5 * Copyright 1999-2001, VA Linux Systems, Inc.
6 * Copyright 2009-2013, Roland Mas
7 * Copyright 2010-2011, Franck Villaume - Capgemini
8 * Copyright 2010-2012, Alain Peyrat - Alcatel-Lucent
9 * Copyright 2012-2013, Franck Villaume - TrivialDev
10 * Copyright 2013, French Ministry of National Education
11 * http://fusionforge.org
13 * This file is part of FusionForge. FusionForge is free software;
14 * you can redistribute it and/or modify it under the terms of the
15 * GNU General Public License as published by the Free Software
16 * Foundation; either version 2 of the Licence, or (at your option)
19 * FusionForge is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 require_once $gfcommon.'tracker/ArtifactTypes.class.php';
30 require_once $gfcommon.'tracker/ArtifactTypeFactory.class.php';
31 require_once $gfcommon.'forum/Forum.class.php';
32 require_once $gfcommon.'forum/ForumFactory.class.php';
33 require_once $gfcommon.'pm/ProjectGroup.class.php';
34 require_once $gfcommon.'pm/ProjectGroupFactory.class.php';
35 require_once $gfcommon.'include/Role.class.php';
36 require_once $gfcommon.'frs/FRSPackage.class.php';
37 require_once $gfcommon.'docman/DocumentGroup.class.php';
38 require_once $gfcommon.'docman/DocumentGroupFactory.class.php';
39 require_once $gfcommon.'mail/MailingList.class.php';
40 require_once $gfcommon.'mail/MailingListFactory.class.php';
41 require_once $gfcommon.'survey/SurveyFactory.class.php';
42 require_once $gfcommon.'survey/SurveyQuestionFactory.class.php';
43 require_once $gfcommon.'include/gettext.php';
44 require_once $gfcommon.'include/GroupJoinRequest.class.php';
49 * group_get_object() - Get the group object.
51 * group_get_object() is useful so you can pool group objects/save database queries
52 * You should always use this instead of instantiating the object directly.
54 * You can now optionally pass in a db result handle. If you do, it re-uses that query
55 * to instantiate the objects.
57 * IMPORTANT! That db result must contain all fields
58 * from groups table or you will have problems
60 * @param int $group_id Required
61 * @param int|bool $res Result set handle ("SELECT * FROM groups WHERE group_id=xx")
62 * @return Group|bool A group object or false on failure
64 function &group_get_object($group_id, $res = false) {
65 //create a common set of group objects
66 //saves a little wear on the database
68 //automatically checks group_type and
69 //returns appropriate object
72 if (!isset($GROUP_OBJ["_".$group_id."_"])) {
74 //the db result handle was passed in
76 $res = db_query_params('SELECT * FROM groups WHERE group_id=$1', array($group_id)) ;
78 if (!$res || db_numrows($res) < 1) {
79 $GROUP_OBJ["_".$group_id."_"]=false;
82 check group type and set up object
84 if (db_result($res,0,'type_id') == 1) {
86 $GROUP_OBJ["_".$group_id."_"] = new Group($group_id, $res);
89 $GROUP_OBJ["_".$group_id."_"] = false;
93 return $GROUP_OBJ["_".$group_id."_"];
96 function &group_get_objects($id_arr) {
99 // Note: if we don't do this, the result may be corrupted
103 foreach ($id_arr as $id) {
105 // See if this ID already has been fetched in the cache
107 if (!isset($GROUP_OBJ["_".$id."_"])) {
111 if (count($fetch) > 0) {
112 $res=db_query_params('SELECT * FROM groups WHERE group_id = ANY ($1)',
113 array(db_int_array_to_any_clause($fetch)));
114 while ($arr = db_fetch_array($res)) {
115 $GROUP_OBJ["_".$arr['group_id']."_"] = new Group($arr['group_id'],$arr);
118 foreach ($id_arr as $id) {
119 $return[] =& $GROUP_OBJ["_".$id."_"];
124 function &group_get_active_projects() {
125 $res = db_query_params('SELECT group_id FROM groups WHERE status=$1',
127 return group_get_objects(util_result_column_to_array($res,0));
130 function &group_get_all_projects() {
131 $res = db_query_params ('SELECT group_id FROM groups',
133 return group_get_objects(util_result_column_to_array($res,0)) ;
136 function &group_get_template_projects() {
137 $res = db_query_params('SELECT group_id FROM groups WHERE is_template=1 AND status != $1',
139 return group_get_objects(util_result_column_to_array($res,0)) ;
142 function &group_get_object_by_name($groupname) {
143 $res = db_query_params('SELECT * FROM groups WHERE unix_group_name=$1', array($groupname));
144 return group_get_object(db_result($res, 0, 'group_id'), $res);
147 function &group_get_objects_by_name($groupname_arr) {
148 $res = db_query_params('SELECT group_id FROM groups WHERE unix_group_name = ANY ($1)',
149 array(db_string_array_to_any_clause($groupname_arr)));
150 $arr =& util_result_column_to_array($res,0);
151 return group_get_objects($arr);
154 function group_get_object_by_publicname($groupname) {
155 $res = db_query_params('SELECT * FROM groups WHERE lower(group_name) LIKE $1',
156 array(htmlspecialchars(strtolower($groupname))));
157 return group_get_object(db_result($res, 0, 'group_id'), $res);
161 * get_public_active_projects_asc() - Get a list of rows for public active projects (initially in trove/full_list)
163 * @param int Opional Maximum number of rows to limit query length
165 function get_public_active_projects_asc($max_query_limit = -1) {
167 $res_grp = db_query_params ('
168 SELECT group_id, group_name, unix_group_name, short_description, register_time
170 WHERE status = $1 AND type_id=1 AND is_template=0 AND register_time > 0
171 ORDER BY group_name ASC
176 while ($row_grp = db_fetch_array($res_grp)) {
177 if (!forge_check_perm ('project_read', $row_grp['group_id'])) {
180 $projects[] = $row_grp;
186 class Group extends Error {
188 * Associative array of data from db.
190 * @var array $data_array.
195 * array of User objects.
197 * @var array $membersArr.
202 * Whether the use is an admin/super user of this project.
204 * @var bool $is_admin.
209 * Artifact types result handle.
211 * @var int $types_res.
216 * Associative array of data for plugins.
218 * @var array $plugins_data.
224 * Associative array of data for the group menu.
226 * @var array $menu_data.
231 * Group - Group object constructor - use group_get_object() to instantiate.
233 * @param int|bool $id Required - Id of the group you want to instantiate.
234 * @param int|bool $res Database result from select query OR associative array of all columns.
235 * @return boolean Success or not
237 function __construct($id = false, $res = false) {
240 //setting up an empty object
241 //probably going to call create()
245 if (!$this->fetchData($id)) {
250 // Assoc array was passed in
252 if (is_array($res)) {
253 $this->data_array =& $res;
255 if (db_numrows($res) < 1) {
256 //function in class we extended
257 $this->setError(_('Group Not Found'));
258 $this->data_array=array();
261 //set up an associative array for use by other functions
262 $this->data_array = db_fetch_array_by_row($res, 0);
270 * fetchData - May need to refresh database fields if an update occurred.
272 * @param int $group_id The group_id.
273 * @return boolean success or not
275 function fetchData($group_id) {
276 $res = db_query_params ('SELECT * FROM groups WHERE group_id=$1',
278 if (!$res || db_numrows($res) < 1) {
279 $this->setError(sprintf('fetchData():: %s', db_error()));
282 $this->data_array = db_fetch_array($res);
287 * create - Create new group.
289 * This method should be called on empty Group object.
290 * It will add an entry for a pending group/project (status 'P')
292 * @param object The User object.
293 * @param string The full name of the user.
294 * @param string The Unix name of the user.
295 * @param string The new group description.
296 * @param string The purpose of the group.
297 * @param boolean Whether to send an email or not
298 * @param int The id of the project this new project is based on
299 * @return boolean success or not
301 function create(&$user, $group_name, $unix_name, $description, $purpose, $unix_box = 'shell1',
302 $scm_box = 'cvs1', $is_public = 1, $send_mail = true, $built_from_template = 0) {
303 // $user is ignored - anyone can create pending group
306 if ($this->getID()!=0) {
307 $this->setError(_('Group object already exists'));
309 } elseif (!$this->validateGroupName($group_name)) {
311 } elseif (!account_groupnamevalid($unix_name)) {
312 $this->setError(_('Invalid Unix Name.'));
314 } elseif (!$SYS->sysUseUnixName($unix_name)) {
315 $this->setError(_('Unix name already taken'));
317 } elseif (db_numrows(db_query_params('SELECT group_id FROM groups WHERE unix_group_name=$1',
318 array($unix_name))) > 0) {
319 $this->setError(_('Unix name already taken'));
321 } elseif (strlen($purpose)<10) {
322 $this->setError(_('Please describe your Registration Purpose in a more comprehensive manner'));
324 } elseif (strlen($purpose)>1500) {
325 $this->setError(_('The Registration Purpose text is too long. Please make it smaller than 1500 bytes.'));
327 } elseif (strlen($description)<10) {
328 $this->setError(_('Describe in a more comprehensive manner your project.'));
332 // Check if sys_use_project_vhost for homepage
333 if (forge_get_config('use_project_vhost')) {
334 $homepage = $unix_name.".".forge_get_config('web_host');
336 $homepage = forge_get_config('web_host')."/www/".$unix_name."/";
341 $res = db_query_params('
356 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)',
357 array (htmlspecialchars ($group_name),
359 htmlspecialchars($description),
365 htmlspecialchars($purpose),
367 md5(util_randbytes()),
368 $built_from_template));
369 if (!$res || db_affected_rows($res) < 1) {
370 $this->setError(sprintf(_('Error: Cannot create group: %s'),db_error()));
375 $id = db_insertid($res, 'groups', 'group_id');
377 $this->setError(sprintf(_('Error: Cannot get group id: %s'),db_error()));
382 if (!$this->fetchData($id)) {
387 $gjr = new GroupJoinRequest($this);
388 $gjr->create($user->getID(),
389 'Fake GroupJoinRequest to store the creator of a project',
392 $hook_params = array();
393 $hook_params['group'] = $this;
394 $hook_params['group_id'] = $this->getID();
395 $hook_params['group_name'] = $group_name;
396 $hook_params['unix_group_name'] = $unix_name;
397 plugin_hook("group_create", $hook_params);
401 $this->sendNewProjectNotificationEmail();
409 * updateAdmin - Update core properties of group object.
411 * This function require site admin privilege.
413 * @param object User requesting operation (for access control).
414 * @param int Group type (1-project, 2-foundry).
415 * @param string Machine on which group's home directory located.
416 * @param string Domain which serves group's WWW.
420 function updateAdmin(&$user, $type_id, $unix_box, $http_domain) {
421 $perm =& $this->getPermission();
423 if (!$perm || !is_object($perm)) {
424 $this->setError(_('Could not get permission.'));
428 if (!$perm->isSuperUser()) {
429 $this->setError(_('Permission denied.'));
435 $res = db_query_params('
437 SET type_id=$1, unix_box=$2, http_domain=$3
444 if (!$res || db_affected_rows($res) < 1) {
445 $this->setError(_('Error: Cannot change group properties: %s'),db_error());
450 // Log the audit trail
451 if ($type_id != $this->data_array['type_id']) {
452 $this->addHistory('type_id', $this->data_array['type_id']);
454 if ($unix_box != $this->data_array['unix_box']) {
455 $this->addHistory('unix_box', $this->data_array['unix_box']);
457 if ($http_domain != $this->data_array['http_domain']) {
458 $this->addHistory('http_domain', $this->data_array['http_domain']);
461 if (!$this->fetchData($this->getID())) {
470 * update - Update number of common properties.
472 * Unlike updateAdmin(), this function accessible to project admin.
474 * @param object User requesting operation (for access control).
475 * @param boolean Whether group is publicly accessible (0/1).
476 * @param string Project's license (string ident).
477 * @param int Group type (1-project, 2-foundry).
478 * @param string Machine on which group's home directory located.
479 * @param string Domain which serves group's WWW.
480 * @return int status.
483 function update(&$user, $group_name, $homepage, $short_description, $use_mail, $use_survey, $use_forum,
484 $use_pm, $use_pm_depend_box, $use_scm, $use_news, $use_docman,
485 $new_doc_address, $send_all_docs, $logo_image_id,
486 $use_ftp, $use_tracker, $use_frs, $use_stats, $tags, $use_activity, $is_public) {
488 $perm =& $this->getPermission();
490 if (!$perm || !is_object($perm)) {
491 $this->setError(_('Could not get permission.'));
495 if (!$perm->isAdmin()) {
496 $this->setError(_('Permission denied.'));
500 // Validate some values
501 if ($this->getPublicName() != $group_name) {
502 if (!$this->validateGroupName($group_name)) {
507 if ($new_doc_address) {
508 $invalid_mails = validate_emails($new_doc_address);
509 if (count($invalid_mails) > 0) {
510 $this->setError(sprintf(ngettext('New Doc Address Appeared Invalid: %s', 'New Doc Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
515 // in the database, these all default to '1',
516 // so we have to explicitly set 0
529 if (!$use_pm_depend_box) {
530 $use_pm_depend_box = 0;
553 if (!$use_activity) {
556 if (!$send_all_docs) {
560 $homepage = ltrim($homepage);
562 $homepage = util_make_url('/projects/' . $this->getUnixName() . '/');
565 if (strlen(htmlspecialchars($short_description))<10) {
566 $this->setError(_('Describe in a more comprehensive manner your project.'));
572 //XXX not yet actived logo_image_id='$logo_image_id',
573 $res = db_query_params('UPDATE groups
576 short_description=$3,
581 use_pm_depend_box=$8,
592 array(htmlspecialchars($group_name),
594 htmlspecialchars($short_description),
611 if (!$res || db_affected_rows($res) < 1) {
612 $this->setError(sprintf(_('Error updating project information: %s'), db_error()));
617 if (!$this->setUseDocman($use_docman)) {
618 $this->setError(sprintf(_('Error updating project information: use_docman %s'), db_error()));
623 if ($this->setTags($tags) === false) {
628 // Log the audit trail
629 $this->addHistory('Changed Public Info', '');
631 if (!$this->fetchData($this->getID())) {
636 $hook_params = array();
637 $hook_params['group'] = $this;
638 $hook_params['group_id'] = $this->getID();
639 $hook_params['group_homepage'] = $homepage;
640 $hook_params['group_name'] = htmlspecialchars($group_name);
641 $hook_params['group_description'] = htmlspecialchars($short_description);
642 $hook_params['group_ispublic'] = $is_public;
643 if (!plugin_hook("group_update", $hook_params)) {
644 if (!$this->isError()) {
645 $this->setError(_('Error updating project information in plugin_hook group_update'));
656 * getID - Simply return the group_id for this object.
658 * @return int group_id.
661 return $this->data_array['group_id'];
665 * getType() - Foundry, project, etc.
667 * @return int The type flag from the database.
670 return $this->data_array['type_id'];
675 * getStatus - the status code.
677 * Statuses char include I,H,A,D,P.
684 function getStatus() {
685 return $this->data_array['status'];
689 * setStatus - set the status code.
691 * Statuses include I,H,A,D,P.
698 * @param object User requesting operation (for access control).
699 * @param string Status value.
700 * @return boolean success.
703 function setStatus(&$user, $status) {
706 if (!forge_check_global_perm_for_user($user, 'approve_projects')) {
707 $this->setPermissionDeniedError();
711 // Projects in 'A' status can only go to 'H' or 'D'
712 // Projects in 'D' status can only go to 'A'
713 // Projects in 'P' status can only go to 'A' OR 'D'
714 // Projects in 'I' status can only go to 'P'
715 // Projects in 'H' status can only go to 'A' OR 'D'
716 $allowed_status_changes = array(
717 'AH'=>1,'AD'=>1,'DA'=>1,'PA'=>1,'PD'=>1,
718 'IP'=>1,'HA'=>1,'HD'=>1
721 // Check that status transition is valid
722 if ($this->getStatus() != $status
723 && !array_key_exists($this->getStatus(). $status, $allowed_status_changes)) {
724 $this->setError(_('Invalid Status Change From: ').$this->getStatus(). _(' To: '.$status));
730 $res = db_query_params('UPDATE groups
732 WHERE group_id=$2', array($status, $this->getID()));
734 if (!$res || db_affected_rows($res) < 1) {
735 $this->setError(sprintf(_('Error: Cannot change group status: %s'),db_error()));
741 // Activate system group, if not yet
742 if (!$SYS->sysCheckGroup($this->getID())) {
743 if (!$SYS->sysCreateGroup($this->getID())) {
744 $this->setError($SYS->getErrorMessage());
749 if (!$this->activateUsers()) {
754 /* Otherwise, the group is not active, and make sure that
755 System group is not active either */
756 } elseif ($SYS->sysCheckGroup($this->getID())) {
757 if (!$SYS->sysRemoveGroup($this->getID())) {
758 $this->setError($SYS->getErrorMessage());
764 $hook_params = array();
765 $hook_params['group'] = $this;
766 $hook_params['group_id'] = $this->getID();
767 $hook_params['status'] = $status;
768 plugin_hook("group_setstatus", $hook_params);
772 // Log the audit trail
773 if ($status != $this->getStatus()) {
774 $this->addHistory(_('Status'), $this->getStatus());
777 $this->data_array['status'] = $status;
782 * isProject - Simple boolean test to see if it's a project or not.
784 * @return boolean is_project.
786 function isProject() {
787 if ($this->getType()==1) {
795 * isPublic - Wrapper around RBAC to check if a project is anonymously readable
797 * @return boolean is_public.
799 function isPublic() {
800 $ra = RoleAnonymous::getInstance() ;
801 return $ra->hasPermission('project_read', $this->getID());
805 * isActive - Database field status of 'A' returns true.
807 * @return boolean is_active.
809 function isActive() {
810 if ($this->getStatus()=='A') {
818 * isTemplate - Simply returns the is_template flag from the database.
820 * @return boolean is_template.
822 function isTemplate() {
823 return $this->data_array['is_template'];
827 * setAsTemplate - Set the template status of a project
829 * @param boolean is_template.
832 function setAsTemplate($booleanparam) {
834 $booleanparam = $booleanparam ? 1 : 0;
835 $res = db_query_params('UPDATE groups SET is_template=$1 WHERE group_id=$2',
836 array($booleanparam, $this->getID()));
838 $this->data_array['is_template']=$booleanparam;
848 * getTemplateProject - Return the project template this project is built from
850 * @return object The template project
852 function getTemplateProject() {
853 return group_get_object($this->data_array['built_from_template']);
857 * getUnixName - the unix_name
859 * @return string unix_name.
861 function getUnixName() {
862 return strtolower($this->data_array['unix_group_name']);
866 * getPublicName - the full-length public name.
868 * @return string The group_name.
870 function getPublicName() {
871 return $this->data_array['group_name'];
875 * getRegisterPurpose - the text description of the purpose of this project.
877 * @return string The description.
879 function getRegisterPurpose() {
880 return $this->data_array['register_purpose'];
884 * getDescription - the text description of this project.
886 * @return string The description.
888 function getDescription() {
889 return $this->data_array['short_description'];
893 * getStartDate - the unix time this project was registered.
895 * @return int (unix time) of registration.
897 function getStartDate() {
898 return $this->data_array['register_time'];
902 * getLogoImageID - the id of the logo in the database for this project.
904 * @return int The ID of logo image in db_images table (or 100 if none).
906 function getLogoImageID() {
907 return $this->data_array['logo_image_id'];
911 * getUnixBox - the hostname of the unix box where this project is located.
913 * @return string The name of the unix machine for the group.
915 function getUnixBox() {
916 return $this->data_array['unix_box'];
920 * getSCMBox - the hostname of the scm box where this project is located.
922 * @return string The name of the unix machine for the group.
924 function getSCMBox() {
925 return $this->data_array['scm_box'];
928 * setSCMBox - the hostname of the scm box where this project is located.
930 * @param string The name of the new SCM_BOX
933 function setSCMBox($scm_box) {
935 if ($scm_box == $this->data_array['scm_box']) {
940 $res = db_query_params('UPDATE groups SET scm_box=$1 WHERE group_id=$2', array($scm_box, $this->getID ()));
942 $this->addHistory('scm_box', $this->data_array['scm_box']);
943 $this->data_array['scm_box'] = $scm_box;
948 $this->setError(_("Could not insert SCM_BOX to database"));
952 $this->setError(_("SCM Box cannot be empty"));
958 * getDomain - the hostname.domain where their web page is located.
960 * @return string The name of the group [web] domain.
962 function getDomain() {
963 return $this->data_array['http_domain'];
967 * getRegistrationPurpose - the text description of the purpose of this project.
969 * @return string The application for project hosting.
971 function getRegistrationPurpose() {
972 return $this->data_array['register_purpose'];
977 * getAdmins() - Get array of Admin user objects.
979 * @return array Array of User objects.
981 function &getAdmins() {
982 $roles = RBACEngine::getInstance()->getRolesByAllowedAction ('project_admin', $this->getID());
986 foreach ($roles as $role) {
987 if (! ($role instanceof RoleExplicit)) {
990 if ($role->getHomeProject() == NULL
991 || $role->getHomeProject()->getID() != $this->getID()) {
995 foreach ($role->getUsers() as $u) {
996 $user_ids[] = $u->getID();
999 return user_get_objects(array_unique($user_ids));
1003 Common Group preferences for tools
1007 * enableAnonSCM - whether or not this group has opted to enable Anonymous SCM.
1009 * @return boolean enable_scm.
1011 function enableAnonSCM() {
1012 $r = RoleAnonymous::getInstance();
1013 return $r->hasPermission('scm', $this->getID(), 'read');
1016 function SetUsesAnonSCM($booleanparam) {
1018 $booleanparam = $booleanparam ? 1 : 0;
1019 $r = RoleAnonymous::getInstance();
1020 $r->setSetting('scm', $this->getID(), $booleanparam);
1025 * enablePserver - whether or not this group has opted to enable Pserver.
1027 * @return boolean enable_pserver.
1029 function enablePserver() {
1030 if ($this->usesSCM()) {
1031 return $this->data_array['enable_pserver'];
1037 function SetUsesPserver($booleanparam) {
1039 $booleanparam = $booleanparam ? 1 : 0;
1040 $res = db_query_params('UPDATE groups SET enable_pserver=$1 WHERE group_id=$2',
1041 array($booleanparam, $this->getID()));
1043 $this->data_array['enable_pserver'] = $booleanparam;
1052 * usesSCM - whether or not this group has opted to use SCM.
1054 * @return boolean uses_scm.
1056 function usesSCM() {
1057 if (forge_get_config('use_scm')) {
1058 return $this->data_array['use_scm'];
1065 * setUseSCM - Set the SCM usage
1067 * @param boolean enabled/disabled
1070 function setUseSCM($booleanparam) {
1072 $booleanparam = $booleanparam ? 1 : 0 ;
1073 $res = db_query_params('UPDATE groups SET use_scm=$1 WHERE group_id=$2',
1074 array($booleanparam, $this->getID()));
1076 $this->data_array['use_scm']=$booleanparam;
1086 * usesMail - whether or not this group has opted to use mailing lists.
1088 * @return boolean uses_mail.
1090 function usesMail() {
1091 if (forge_get_config('use_mail')) {
1092 return $this->data_array['use_mail'];
1097 $hook_params = array ();
1098 $hook_params['group'] = $this;
1099 $hook_params['group_id'] = $this->getID();
1100 $hook_params['group_homepage'] = $this->getHomePage();
1101 $hook_params['group_name'] = $this->getPublicName();
1102 $hook_params['group_description'] = $this->getDescription();
1103 plugin_hook ("group_update", $hook_params);
1107 * setUseMail - Set the mailing-list usage
1109 * @param boolean enabled/disabled
1112 function setUseMail($booleanparam) {
1114 $booleanparam = $booleanparam ? 1 : 0 ;
1115 $res = db_query_params('UPDATE groups SET use_mail=$1 WHERE group_id=$2',
1116 array($booleanparam, $this->getID()));
1118 $this->data_array['use_mail']=$booleanparam;
1128 * usesNews - whether or not this group has opted to use news.
1130 * @return boolean uses_news.
1132 function usesNews() {
1133 if (forge_get_config('use_news')) {
1134 return $this->data_array['use_news'];
1141 * usesActivity - whether or not this group has opted to display Project Activities.
1143 * @return boolean uses_activities.
1145 function usesActivity() {
1146 if (forge_get_config('use_activity')) {
1147 return $this->data_array['use_activity'];
1154 * usesForum - whether or not this group has opted to use discussion forums.
1156 * @return boolean uses_forum.
1158 function usesForum() {
1159 if (forge_get_config('use_forum')) {
1160 return $this->data_array['use_forum'];
1167 * setUseForum - Set the forum usage
1169 * @param boolean enabled/disabled
1172 function setUseForum($booleanparam) {
1174 $booleanparam = $booleanparam ? 1 : 0;
1175 $res = db_query_params('UPDATE groups SET use_forum=$1 WHERE group_id=$2',
1176 array($booleanparam, $this->getID()));
1178 $this->data_array['use_forum']=$booleanparam;
1188 * usesStats - whether or not this group has opted to use stats.
1190 * @return boolean uses_stats.
1192 function usesStats() {
1193 return $this->data_array['use_stats'];
1197 * usesFRS - whether or not this group has opted to use file release system.
1199 * @return boolean uses_frs.
1201 function usesFRS() {
1202 if (forge_get_config('use_frs')) {
1203 return $this->data_array['use_frs'];
1210 * setUseFRS - Set the FRS usage
1212 * @param boolean enabled/disabled
1215 function setUseFRS($booleanparam) {
1217 $booleanparam = $booleanparam ? 1 : 0;
1218 $res = db_query_params('UPDATE groups SET use_frs=$1 WHERE group_id=$2',
1219 array ($booleanparam, $this->getID()));
1221 $this->data_array['use_frs']=$booleanparam;
1231 * usesTracker - whether or not this group has opted to use tracker.
1233 * @return boolean uses_tracker.
1235 function usesTracker() {
1236 if (forge_get_config('use_tracker')) {
1237 return $this->data_array['use_tracker'];
1244 * setUseTracker - Set the tracker usage
1246 * @param boolean enabled/disabled
1249 function setUseTracker ($booleanparam) {
1251 $booleanparam = $booleanparam ? 1 : 0 ;
1252 $res = db_query_params ('UPDATE groups SET use_tracker=$1 WHERE group_id=$2',
1253 array ($booleanparam, $this->getID()));
1255 $this->data_array['use_tracker']=$booleanparam;
1265 * useCreateOnline - whether or not this group has opted to use create online documents option.
1267 * @return boolean use_docman_create_online.
1269 function useCreateOnline() {
1270 if (forge_get_config('use_docman')) {
1271 return $this->data_array['use_docman_create_online'];
1278 * usesDocman - whether or not this group has opted to use docman.
1280 * @return boolean use_docman.
1282 function usesDocman() {
1283 if (forge_get_config('use_docman')) {
1284 return $this->data_array['use_docman'];
1291 * setUseDocman - Set the docman usage
1293 * @param boolean enabled/disabled
1296 function setUseDocman($booleanparam) {
1298 $booleanparam = $booleanparam ? 1 : 0;
1299 $res = db_query_params('UPDATE groups SET use_docman = $1 WHERE group_id = $2',
1300 array($booleanparam, $this->getID()));
1302 // check if / doc_group exists, if not create it
1303 $trashdir = db_query_params('select groupname from doc_groups where groupname = $1 and group_id = $2',
1304 array('.trash', $this->getID()));
1305 if ($trashdir && db_numrows($trashdir) == 0) {
1306 $resinsert = db_query_params('insert into doc_groups (groupname, group_id, stateid) values ($1, $2, $3)',
1307 array('.trash', $this->getID(), '2'));
1313 $this->data_array['use_docman'] = $booleanparam;
1323 * useDocmanSearch - whether or not this group has opted to use docman search engine.
1325 * @return boolean use_docman_search.
1327 function useDocmanSearch() {
1328 if (forge_get_config('use_docman')) {
1329 return $this->data_array['use_docman_search'];
1336 * useWebdav - whether or not this group has opted to use webdav interface.
1338 * @return boolean use_docman_search.
1340 function useWebdav() {
1341 if (forge_get_config('use_webdav')) {
1342 return $this->data_array['use_webdav'];
1349 * usesFTP - whether or not this group has opted to use FTP.
1351 * @return boolean uses_ftp.
1353 function usesFTP() {
1354 if (forge_get_config('use_ftp')) {
1355 return $this->data_array['use_ftp'];
1362 * usesSurvey - whether or not this group has opted to use surveys.
1364 * @return boolean uses_survey.
1366 function usesSurvey() {
1367 if (forge_get_config('use_survey')) {
1368 return $this->data_array['use_survey'];
1375 * usesPM - whether or not this group has opted to Project Manager.
1377 * @return boolean uses_projman.
1380 if (forge_get_config('use_pm')) {
1381 return $this->data_array['use_pm'];
1388 * setUsePM - Set the PM usage
1390 * @param boolean enabled/disabled
1393 function setUsePM($booleanparam) {
1395 $booleanparam = $booleanparam ? 1 : 0;
1396 $res = db_query_params('UPDATE groups SET use_pm=$1 WHERE group_id=$2',
1397 array($booleanparam, $this->getID()));
1399 $this->data_array['use_pm']=$booleanparam;
1409 * getPlugins - get a list of all available group plugins
1411 * @return array array containing plugin_id => plugin_name
1413 function getPlugins() {
1414 if (!isset($this->plugins_data)) {
1415 $this->plugins_data = array();
1416 $res = db_query_params('SELECT group_plugin.plugin_id, plugins.plugin_name
1417 FROM group_plugin, plugins
1418 WHERE group_plugin.group_id=$1
1419 AND group_plugin.plugin_id=plugins.plugin_id', array($this->getID()));
1420 $rows = db_numrows($res);
1422 for ($i=0; $i<$rows; $i++) {
1423 $plugin_id = db_result($res, $i, 'plugin_id');
1424 $this->plugins_data[$plugin_id] = db_result($res, $i, 'plugin_name');
1427 return $this->plugins_data;
1431 * usesPlugin - returns true if the group uses a particular plugin
1433 * @param string $pluginname name of the plugin
1434 * @return boolean whether plugin is being used or not
1436 function usesPlugin($pluginname) {
1437 $plugins_data = $this->getPlugins();
1438 foreach ($plugins_data as $p_id => $p_name) {
1439 if ($p_name == $pluginname) {
1447 * added for Codendi compatibility
1448 * usesServices - returns true if the group uses a particular plugin or feature
1450 * @param string name of the plugin
1451 * @return boolean whether plugin is being used or not
1453 function usesService($feature) {
1454 $plugins_data = $this->getPlugins();
1455 $pm = plugin_manager_get_object();
1456 foreach ($plugins_data as $p_id => $p_name) {
1457 if ($p_name == $feature) {
1460 if ($pm->getPluginByName($p_name)->provide($feature)) {
1468 * setPluginUse - enables/disables plugins for the group
1470 * @param string name of the plugin
1471 * @param boolean the new state
1472 * @return string database result
1474 function setPluginUse($pluginname, $val=true) {
1475 if ($val == $this->usesPlugin($pluginname)) {
1476 // State is already good, returning
1479 $res = db_query_params('SELECT plugin_id FROM plugins WHERE plugin_name=$1',
1480 array($pluginname));
1481 $rows = db_numrows($res);
1483 // Error: no plugin by that name
1486 $plugin_id = db_result($res,0,'plugin_id');
1488 unset($this->plugins_data);
1490 $res = db_query_params('INSERT INTO group_plugin (group_id, plugin_id) VALUES ($1, $2)',
1491 array($this->getID(),
1495 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1 AND plugin_id=$2',
1496 array($this->getID(),
1500 $this->normalizeAllRoles () ;
1504 * getDocEmailAddress - get email address(es) to send doc notifications to.
1506 * @return string email address.
1508 function getDocEmailAddress() {
1509 return $this->data_array['new_doc_address'];
1513 * DocEmailAll - whether or not this group has opted to use receive notices on all doc updates.
1515 * @return boolean email_on_all_doc_updates.
1517 function docEmailAll() {
1518 return $this->data_array['send_all_docs'];
1523 * getHomePage - The URL for this project's home page.
1525 * @return string homepage URL.
1527 function getHomePage() {
1528 if (!preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*:/",
1529 $this->data_array['homepage'])) {
1530 $this->data_array['homepage'] = util_url_prefix() .
1531 $this->data_array['homepage'];
1533 return $this->data_array['homepage'];
1537 * getTags - Tags of this project.
1539 * @return string List of tags. Comma separated
1541 function getTags() {
1542 $sql = 'SELECT name FROM project_tags WHERE group_id = $1';
1543 $res = db_query_params($sql, array($this->getID()));
1544 return join(', ', util_result_column_to_array($res));
1548 * setTags - Set tags of this project.
1550 * @return string database result.
1552 function setTags($tags) {
1554 $sql = 'DELETE FROM project_tags WHERE group_id=$1';
1555 $res = db_query_params($sql, array($this->getID()));
1557 $this->setError('Deleting old tags: '.db_error());
1561 $inserted = array();
1562 $tags_array = preg_split('/[;,]/', $tags);
1563 foreach ($tags_array as $tag) {
1564 $tag = preg_replace('/[\t\r\n]/', ' ', $tag);
1565 // Allowed caracteres: [A-Z][a-z][0-9] -_&'#+.
1566 if (preg_match('/[^[:alnum:]| |\-|_|\&|\'|#|\+|\.]/', $tag)) {
1567 $this->setError(_('Bad tag name, you only can use the following characters: [A-Z][a-z][0-9]-_&\'#+. and space'));
1572 if ($tag == '' || array_search($tag, $inserted) !== false) continue;
1573 $sql = 'INSERT INTO project_tags (group_id,name) VALUES ($1, $2)';
1574 $res = db_query_params($sql, array($this->getID(), $tag));
1576 $this->setError(_('Setting tags:') . ' ' .
1588 * getPermission - Return a Permission for this Group
1590 * @return object The Permission.
1592 function &getPermission() {
1593 return permission_get_object($this);
1597 function delete($sure, $really_sure, $really_really_sure) {
1598 if (!$sure || !$really_sure || !$really_really_sure) {
1599 $this->setMissingParamsError(_('Please tick all checkboxes.'));
1602 if ($this->getID() == forge_get_config('news_group') ||
1603 $this->getID() == 1 ||
1604 $this->getID() == forge_get_config('stats_group') ||
1605 $this->getID() == forge_get_config('peer_rating_group')) {
1606 $this->setError(_('Cannot Delete System Group'));
1609 $perm = $this->getPermission();
1610 if (!$perm || !is_object($perm)) {
1611 $this->setPermissionDeniedError();
1613 } elseif ($perm->isError()) {
1614 $this->setPermissionDeniedError();
1616 } elseif (!$perm->isSuperUser()) {
1617 $this->setPermissionDeniedError();
1623 // Remove all the members
1625 $members = $this->getMembers();
1626 foreach ($members as $i) {
1627 if(!$this->removeUser($i->getID())) {
1628 $this->setError(_('Could not properly remove member:').' '.$i->getID());
1633 // unlink roles from this project
1634 foreach ($this->getRoles() as $r) {
1635 if ($r->getHomeProject() == NULL
1636 || $r->getHomeProject()->getID() != $this->getID()) {
1637 $r->unlinkProject($this);
1644 if ($this->usesTracker()) {
1645 $atf = new ArtifactTypeFactory($this);
1646 $at_arr = $atf->getArtifactTypes();
1647 foreach ($at_arr as $i) {
1648 if (!is_object($i)) {
1651 if (!$i->delete(1,1)) {
1652 $this->setError(_('Could not properly delete the tracker:').' '.$i->getErrorMessage());
1661 if ($this->usesForum()) {
1662 $ff = new ForumFactory($this);
1663 $f_arr = $ff->getForums();
1664 foreach ($f_arr as $i) {
1665 if (!is_object($i)) {
1668 if(!$i->delete(1,1)) {
1669 $this->setError(_('Could not properly delete the forum:').' '.$i->getErrorMessage());
1675 // Delete Subprojects
1677 if ($this->usesPM()) {
1678 $pgf = new ProjectGroupFactory($this);
1679 $pg_arr = $pgf->getProjectGroups();
1680 foreach ($pg_arr as $i) {
1681 if (!is_object($i)) {
1684 if (!$i->delete(1,1)) {
1685 $this->setError(_('Could not properly delete the ProjectGroup:').' '.$i->getErrorMessage());
1691 // Delete FRS Packages
1693 $res = db_query_params('SELECT * FROM frs_package WHERE group_id=$1',
1694 array($this->getID()));
1696 $this->setError(_('Error FRS Packages: ').db_error());
1701 while ($arr = db_fetch_array($res)) {
1702 $frsp=new FRSPackage($this, $arr['package_id'], $arr);
1703 if (!$frsp->delete(1, 1)) {
1704 $this->setError(_('Could not properly delete the FRSPackage:').' '.$frsp->getErrorMessage());
1711 $news_group=group_get_object(forge_get_config('news_group'));
1712 $res = db_query_params('SELECT forum_id FROM news_bytes WHERE group_id=$1',
1713 array($this->getID()));
1715 $this->setError(_('Error Deleting News: ').db_error());
1720 for ($i=0; $i<db_numrows($res); $i++) {
1721 $Forum = new Forum($news_group,db_result($res,$i,'forum_id'));
1722 if (!$Forum->delete(1,1)) {
1723 $this->setError(_("Could Not Delete News Forum: %d"),$Forum->getID());
1727 $res = db_query_params('DELETE FROM news_bytes WHERE group_id=$1',
1728 array($this->getID()));
1730 $this->setError(_('Error Deleting News: ').db_error());
1738 $res = db_query_params('DELETE FROM doc_data WHERE group_id=$1',
1739 array($this->getID()));
1741 $this->setError(_('Error Deleting Documents: ').db_error());
1746 $res = db_query_params('DELETE FROM doc_groups WHERE group_id=$1',
1747 array($this->getID()));
1749 $this->setError(_('Error Deleting Documents: ').db_error());
1757 $res=db_query_params('DELETE FROM project_tags WHERE group_id=$1', array($this->getID()));
1759 $this->setError(_('Error Deleting Tags: ').db_error());
1765 // Delete group history
1767 $res = db_query_params('DELETE FROM group_history WHERE group_id=$1',
1768 array($this->getID()));
1770 $this->setError(_('Error Deleting Project History: ').db_error());
1776 // Delete group plugins
1778 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1',
1779 array($this->getID()));
1781 $this->setError(_('Error Deleting Project Plugins: ').db_error());
1787 // Delete group cvs stats
1789 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE group_id=$1',
1790 array ($this->getID())) ;
1792 $this->setError(_('Error Deleting SCM Statistics: ').db_error());
1800 if ($this->usesSurvey()) {
1801 $sf = new SurveyFactory($this);
1802 $s_arr =& $sf->getSurveys();
1803 foreach ($s_arr as $i) {
1804 if (!is_object($i)) {
1807 if (!$i->delete()) {
1808 $this->setError(_('Could not properly delete the survey'));
1814 // Delete SurveyQuestions
1816 $sqf = new SurveyQuestionFactory($this);
1817 $sq_arr = $sqf->getSurveyQuestions();
1818 if (is_array($sq_arr)) {
1819 foreach ($sq_arr as $i) {
1820 if (!is_object($i)) {
1823 if (!$i->delete()) {
1824 $this->setError(_('Could not properly delete the survey questions'));
1832 // Delete Mailing List Factory
1834 if ($this->usesMail()) {
1835 $mlf = new MailingListFactory($this);
1836 $ml_arr = $mlf->getMailingLists();
1837 foreach ($ml_arr as $i) {
1838 if (!is_object($i)) {
1841 if (!$i->delete(1,1)) {
1842 $this->setError(_('Could not properly delete the mailing list'));
1851 $res = db_query_params('DELETE FROM trove_group_link WHERE group_id=$1',
1852 array($this->getID()));
1854 $this->setError(_('Error Deleting Trove: ').db_error());
1859 $res = db_query_params('DELETE FROM trove_agg WHERE group_id=$1',
1860 array($this->getID()));
1862 $this->setError(_('Error Deleting Trove: ').db_error());
1870 $res = db_query_params('DELETE FROM project_sums_agg WHERE group_id=$1',
1871 array($this->getID()));
1873 $this->setError(_('Error Deleting Counters: ').db_error());
1878 $res = db_query_params('INSERT INTO deleted_groups (unix_group_name, delete_date, isdeleted) VALUES ($1, $2, $3)',
1879 array($this->getUnixName(),
1883 $this->setError(_('Error Deleting Project:').' '.db_error());
1888 // Delete entry in groups.
1889 $res = db_query_params('DELETE FROM groups WHERE group_id=$1',
1890 array($this->getID()));
1892 $this->setError(_('Error Deleting Project:').' '.db_error());
1899 $hook_params = array();
1900 $hook_params['group'] = $this;
1901 $hook_params['group_id'] = $this->getID();
1902 plugin_hook("group_delete", $hook_params);
1904 if (forge_get_config('upload_dir') != '' && $this->getUnixName()) {
1905 exec('/bin/rm -rf '.forge_get_config('upload_dir').'/'.$this->getUnixName().'/');
1907 if (forge_get_config('ftp_upload_dir') != '' && $this->getUnixName()) {
1908 exec('/bin/rm -rf '.forge_get_config('ftp_upload_dir').'/'.$this->getUnixName().'/');
1913 $res = db_query_params('DELETE FROM rep_group_act_monthly WHERE group_id=$1',
1914 array ($this->getID()));
1915 //echo 'rep_group_act_monthly'.db_error();
1916 $res = db_query_params('DELETE FROM rep_group_act_weekly WHERE group_id=$1',
1917 array ($this->getID()));
1918 //echo 'rep_group_act_weekly'.db_error();
1919 $res = db_query_params('DELETE FROM rep_group_act_daily WHERE group_id=$1',
1920 array ($this->getID()));
1921 //echo 'rep_group_act_daily'.db_error();
1922 unset($this->data_array);
1927 Basic functions to add/remove users to/from a group
1928 and update their permissions
1932 * addUser - controls adding a user to a group.
1934 * @param string Unix name of the user to add OR integer user_id.
1935 * @param int The role_id this user should have.
1936 * @return boolean success.
1939 function addUser($user_identifier,$role_id) {
1942 Admins can add users to groups
1945 if (!forge_check_perm ('project_admin', $this->getID())) {
1946 $this->setPermissionDeniedError();
1952 get user id for this user's unix_name
1954 if (is_int ($user_identifier)) { // user_id or user_name
1955 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_id=$1', array ($user_identifier)) ;
1957 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_name=$1', array ($user_identifier)) ;
1959 if (db_numrows($res_newuser) > 0) {
1961 // make sure user is active
1963 if (db_result($res_newuser,0,'status') != 'A') {
1964 $this->setError(_('User is not active. Only active users can be added.'));
1970 // user was found - set new user_id var
1972 $user_id = db_result($res_newuser,0,'user_id');
1974 $role = new Role($this, $role_id);
1975 if (!$role || !is_object($role)) {
1976 $this->setError(_('Error Getting Role Object'));
1979 } elseif ($role->isError()) {
1980 $this->setError('addUser::roleget::'.$role->getErrorMessage());
1985 $role->addUser(user_get_object($user_id)) ;
1986 if (!$SYS->sysCheckCreateGroup($this->getID())){
1987 $this->setError($SYS->getErrorMessage());
1991 if (!$SYS->sysCheckCreateUser($user_id)) {
1992 $this->setError($SYS->getErrorMessage());
1996 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
1997 $this->setError($SYS->getErrorMessage());
2003 // user doesn't exist
2005 $this->setError(_('Error: User does not exist'));
2010 $hook_params['group'] = $this;
2011 $hook_params['group_id'] = $this->getID();
2012 $hook_params['user'] = user_get_object($user_id);
2013 $hook_params['user_id'] = $user_id;
2014 plugin_hook ("group_adduser", $hook_params);
2019 $this->addHistory('Added User',$user_identifier);
2025 * removeUser - controls removing a user from a group.
2027 * Users can remove themselves.
2029 * @param int The ID of the user to remove.
2030 * @return boolean success.
2032 function removeUser($user_id) {
2035 if ($user_id != user_getid()
2036 && !forge_check_perm('project_admin', $this->getID())) {
2037 $this->setPermissionDeniedError();
2043 $user = user_get_object($user_id);
2044 $roles = RBACEngine::getInstance()->getAvailableRolesForUser($user);
2046 foreach ($roles as $role) {
2047 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2048 $found_role = $role;
2052 if ($found_role == NULL) {
2053 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2057 $found_role->removeUser($user);
2058 if (!$SYS->sysGroupCheckUser($this->getID(), $user_id)) {
2059 $this->setError($SYS->getErrorMessage());
2065 // reassign open artifacts to id=100
2067 $res = db_query_params('UPDATE artifact SET assigned_to=100
2068 WHERE group_artifact_id
2069 IN (SELECT group_artifact_id
2070 FROM artifact_group_list
2071 WHERE group_id=$1 AND status_id=1 AND assigned_to=$2)',
2072 array($this->getID(),
2075 $this->setError(_('Error: artifact:').' '.db_error());
2081 // reassign open tasks to id=100
2082 // first have to purge any assignments that would cause
2083 // conflict with existing assignment to 100
2085 $res = db_query_params('DELETE FROM project_assigned_to
2086 WHERE project_task_id IN (SELECT pt.project_task_id
2087 FROM project_task pt, project_group_list pgl, project_assigned_to pat
2088 WHERE pt.group_project_id = pgl.group_project_id
2089 AND pat.project_task_id=pt.project_task_id
2090 AND pt.status_id=1 AND pgl.group_id=$1
2091 AND pat.assigned_to_id=$2)
2092 AND assigned_to_id=100',
2093 array($this->getID(),
2096 $this->setError(sprintf(_('Error: project_assigned_to %d: %s'), 1, db_error()));
2100 $res = db_query_params('UPDATE project_assigned_to SET assigned_to_id=100
2101 WHERE project_task_id IN (SELECT pt.project_task_id
2102 FROM project_task pt, project_group_list pgl
2103 WHERE pt.group_project_id = pgl.group_project_id
2104 AND pt.status_id=1 AND pgl.group_id=$1)
2105 AND assigned_to_id=$2',
2106 array($this->getID(),
2109 $this->setError(sprintf(_('Error: project_assigned_to %d: %s'), 2, db_error()));
2115 // Remove user from system
2117 if (!$SYS->sysGroupRemoveUser($this->getID(), $user_id)) {
2118 $this->setError($SYS->getErrorMessage());
2123 $hook_params['group'] = $this;
2124 $hook_params['group_id'] = $this->getID();
2125 $hook_params['user'] = user_get_object($user_id);
2126 $hook_params['user_id'] = $user_id;
2127 plugin_hook ("group_removeuser", $hook_params);
2130 $this->addHistory('Removed User',$user_id);
2137 * updateUser - controls updating a user's role in this group.
2139 * @param int The ID of the user.
2140 * @param int The role_id to set this user to.
2141 * @return boolean success.
2143 function updateUser($user_id,$role_id) {
2145 if (!forge_check_perm ('project_admin', $this->getID())) {
2146 $this->setPermissionDeniedError();
2150 $newrole = RBACEngine::getInstance()->getRoleById ($role_id) ;
2151 if (!$newrole || !is_object($newrole)) {
2152 $this->setError(_('Could Not Get Role'));
2154 } elseif ($newrole->isError()) {
2155 $this->setError(sprintf(_('Role: %s'),$role->getErrorMessage()));
2157 } elseif ($newrole->getHomeProject() == NULL
2158 || $newrole->getHomeProject()->getID() != $this->getID()) {
2159 $this->setError(_('Wrong destination role'));
2162 $user = user_get_object ($user_id) ;
2163 $roles = RBACEngine::getInstance()->getAvailableRolesForUser ($user) ;
2164 $found_role = NULL ;
2165 foreach ($roles as $role) {
2166 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2167 $found_role = $role ;
2171 if ($found_role == NULL) {
2172 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2176 $found_role->removeUser ($user) ;
2177 $newrole->addUser ($user) ;
2179 $this->addHistory('Updated User',$user_id);
2184 * addHistory - Makes an audit trail entry for this project.
2186 * @param string The name of the field.
2187 * @param string The Old Value for this $field_name.
2188 * @return database result handle.
2191 function addHistory($field_name, $old_value) {
2192 return db_query_params ('INSERT INTO group_history(group_id,field_name,old_value,mod_by,adddate)
2193 VALUES ($1,$2,$3,$4,$5)',
2194 array ($this->getID(),
2202 * activateUsers - Make sure that group members have unix accounts.
2204 * Setup unix accounts for group members. Can be called even
2205 * if members are already active.
2209 function activateUsers() {
2211 Activate member(s) of the project
2214 $members = $this->getUsers (true) ;
2216 foreach ($members as $member) {
2218 foreach (RBACEngine::getInstance()->getAvailableRolesForUser ($member) as $role) {
2219 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2223 foreach ($roles as $role) {
2224 if (!$this->addUser($member->getUnixName(),$role->getID())) {
2235 * getMembers - returns array of User objects for this project
2237 * @return array of User objects for this group.
2239 function getMembers() {
2240 return $this->getUsers (true) ;
2244 * replaceTemplateStrings - fill-in some blanks with project name
2246 * @param string Template string
2247 * @return string String after replacements
2249 function replaceTemplateStrings($string) {
2250 $string = str_replace ('UNIXNAME', $this->getUnixName(), $string) ;
2251 $string = str_replace ('PUBLICNAME', $this->getPublicName(), $string) ;
2252 $string = str_replace ('DESCRIPTION', $this->getDescription(), $string) ;
2257 * approve - Approve pending project.
2259 * @param User $user The User object who is doing the updating.
2263 function approve(&$user) {
2264 global $gfcommon,$gfwww;
2265 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
2267 if ($this->getStatus()=='A') {
2268 $this->setError(_("Group already active"));
2274 // Step 1: Activate group and create LDAP entries
2275 if (!$this->setStatus($user, 'A')) {
2280 // Switch to system language for item creation
2281 setup_gettext_from_sys_lang();
2283 // Create default roles
2284 $idadmin_group = NULL;
2285 foreach (get_group_join_requests ($this) as $gjr) {
2286 $idadmin_group = $gjr->getUserID();
2289 if ($idadmin_group == NULL) {
2290 $idadmin_group = $user->getID();
2293 $template = $this->getTemplateProject();
2294 $id_mappings = array();
2295 $seen_admin_role = false;
2297 // Copy roles from template project
2298 foreach($template->getRoles() as $oldrole) {
2299 if ($oldrole->getHomeProject() != NULL) {
2300 $role = new Role($this);
2302 // Need to use a different role name so that the permissions aren't set from the hardcoded defaults
2303 $role->create('TEMPORARY ROLE NAME', $data, true);
2304 $role->setName($oldrole->getName());
2305 if ($oldrole->getSetting ('project_admin', $template->getID())) {
2306 $seen_admin_role = true;
2310 $role->linkProject($this);
2312 $id_mappings['role'][$oldrole->getID()] = $role->getID();
2313 // Reuse the project_admin permission
2314 $role->setSetting ('project_admin', $this->getID(), $oldrole->getSetting ('project_admin', $template->getID())) ;
2318 if (!$seen_admin_role) {
2319 $role = new Role($this);
2320 $adminperms = array ('project_admin' => array ($this->getID() => 1)) ;
2321 $role_id = $role->create ('Admin', $adminperms, true) ;
2324 $roles = $this->getRoles() ;
2325 foreach ($roles as $r) {
2326 if ($r->getHomeProject() == NULL) {
2329 if ($r->getSetting ('project_admin', $this->getID())) {
2330 $r->addUser(user_get_object ($idadmin_group));
2334 // Temporarily switch to the submitter's identity
2335 $saved_session = session_get_user();
2336 session_set_internal($idadmin_group);
2339 if (forge_get_config('use_tracker')) {
2340 $this->setUseTracker ($template->usesTracker());
2341 if ($template->usesTracker()) {
2342 $oldatf = new ArtifactTypeFactory($template);
2343 foreach ($oldatf->getArtifactTypes() as $o) {
2344 $t = new ArtifactType ($this) ;
2345 $t->create ($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->emailAll(),$o->getEmailAddress(),$o->getDuePeriod()/86400,0,$o->getSubmitInstructions(),$o->getBrowseInstructions()) ;
2346 $id_mappings['tracker'][$o->getID()] = $t->getID();
2347 $t->cloneFieldsFrom ($o->getID());
2352 if (forge_get_config('use_pm')) {
2353 $this->setUsePM ($template->usesPM());
2354 if ($template->usesPM()) {
2355 $oldpgf = new ProjectGroupFactory($template);
2356 foreach ($oldpgf->getProjectGroups() as $o) {
2357 $pg = new ProjectGroup($this);
2358 $pg->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo());
2359 $id_mappings['pm'][$o->getID()] = $pg->getID();
2364 if (forge_get_config('use_forum')) {
2365 $this->setUseForum($template->usesForum()) ;
2366 if ($template->usesForum()) {
2367 $oldff = new ForumFactory($template) ;
2368 foreach ($oldff->getForums() as $o) {
2369 $f = new Forum($this);
2370 $f->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo(),1);
2371 $id_mappings['forum'][$o->getID()] = $f->getID();
2376 if (forge_get_config('use_docman')) {
2377 $this->setUseDocman($template->usesDocman());
2378 if ($template->usesDocman()) {
2379 $olddgf = new DocumentGroupFactory($template);
2380 // First pass: create all docgroups
2381 $id_mappings['docman_docgroup'][0] = 0;
2382 foreach ($olddgf->getDocumentGroups() as $o) {
2383 $ndgf = new DocumentGroup($this);
2384 // .trash is a reserved directory
2385 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2386 $ndgf->create($this->replaceTemplateStrings($o->getName()));
2387 $id_mappings['docman_docgroup'][$o->getID()] = $ndgf->getID();
2390 // Second pass: restore hierarchy links
2391 foreach ($olddgf->getDocumentGroups() as $o) {
2392 $ndgf = new DocumentGroup($this);
2393 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2394 $ndgf->fetchData($id_mappings['docman_docgroup'][$o->getID()]);
2395 $ndgf->update($ndgf->getName(), $id_mappings['docman_docgroup'][$o->getParentID()]);
2401 if (forge_get_config('use_frs')) {
2402 $this->setUseFRS ($template->usesFRS());
2403 if ($template->usesFRS()) {
2404 foreach (get_frs_packages($template) as $o) {
2405 $newp = new FRSPackage($this);
2406 $nname = $this->replaceTemplateStrings($o->getName());
2407 $newp->create ($nname, $o->isPublic());
2412 if (forge_get_config('use_mail')) {
2413 $this->setUseMail($template->usesMail()) ;
2414 if ($template->usesMail()) {
2415 $oldmlf = new MailingListFactory($template);
2416 foreach ($oldmlf->getMailingLists() as $o) {
2417 $ml = new MailingList($this);
2418 $nname = preg_replace ('/^'.$template->getUnixName().'-/','',$o->getName()) ;
2420 $ndescription = $this->replaceTemplateStrings($o->getDescription()) ;
2421 $ml->create($nname, $ndescription, $o->isPublic());
2427 /* use SCM plugin from template group */
2428 $this->setUseSCM($template->usesSCM());
2430 foreach ($template->getPlugins() as
2431 $plugin_id => $plugin_name) {
2432 $this->setPluginUse($plugin_name);
2435 /* use SCM choice from registration page */
2437 foreach ($template->getPlugins() as
2438 $plugin_id => $plugin_name) {
2439 if (substr($plugin_name, 3) == 'scm' &&
2440 $plugin_name != 'scmhook') {
2441 /* skip copying scm plugins */
2444 /* enable other plugins though */
2445 $this->setPluginUse($plugin_name);
2449 foreach ($template->getRoles() as $oldrole) {
2450 $newrole = RBACEngine::getInstance()->getRoleById ($id_mappings['role'][$oldrole->getID()]) ;
2451 if ($oldrole->getHomeProject() != NULL
2452 && $oldrole->getHomeProject()->getID() == $template->getID()) {
2453 $newrole->setPublic ($oldrole->isPublic()) ;
2455 $oldsettings = $oldrole->getSettingsForProject ($template) ;
2457 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm') ;
2458 foreach ($sections as $section) {
2459 $newrole->setSetting ($section, $this->getID(), $oldsettings[$section][$template->getID()]) ;
2462 $sections = array ('tracker', 'pm', 'forum') ;
2463 foreach ($sections as $section) {
2464 if (isset ($oldsettings[$section])) {
2465 foreach ($oldsettings[$section] as $k => $v) {
2466 // Only copy perms for tools that have been copied
2467 if (isset ($id_mappings[$section][$k])) {
2468 $newrole->setSetting ($section,
2469 $id_mappings[$section][$k],
2477 $lm = new WidgetLayoutManager();
2478 $lm->createDefaultLayoutForProject ($this->getID(), $template->getID()) ;
2480 $params = array () ;
2481 $params['template'] = $template ;
2482 $params['project'] = $this ;
2483 $params['id_mappings'] = $id_mappings ;
2484 plugin_hook_by_reference ('clone_project_from_template', $params) ;
2486 // Disable everything
2487 db_query_params ('UPDATE groups SET use_mail=0, use_survey=0, use_forum=0, use_pm=0, use_pm_depend_box=0, use_scm=0, use_news=0, use_docman=0, use_ftp=0, use_tracker=0, use_frs=0, use_stats=0 WHERE group_id=$1',
2488 array ($this->getID())) ;
2491 $this->normalizeAllRoles();
2492 // empty members cache because the group creator is not yet in cache.
2493 unset($this->membersArr);
2494 $this->activateUsers();
2496 // Delete fake join request
2497 foreach (get_group_join_requests ($this) as $gjr) {
2498 $gjr->delete(true) ;
2501 // Switch back to user preference
2502 session_set_internal($saved_session->getID());
2503 setup_gettext_from_context();
2507 $this->sendApprovalEmail();
2508 $this->addHistory(_('Approved'), 'x');
2511 // Plugin can make approve operation there
2513 $params = array () ;
2514 $params['group'] = $this ;
2515 $params['group_id'] = $this->getID();
2516 plugin_hook('group_approved', $params);
2524 * sendApprovalEmail - Send new project email.
2526 * @return boolean success.
2529 function sendApprovalEmail() {
2530 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('project_admin', $this->getID()) ;
2532 if (count($admins) < 1) {
2533 $this->setError(_("Group does not have any administrators."));
2537 // send one email per admin
2538 foreach ($admins as $admin) {
2539 setup_gettext_for_user ($admin) ;
2541 $message = sprintf(_('Your project registration for %s has been approved.'), forge_get_config ('forge_name')) . "\n\n"
2543 . _('Project Full Name')._(': '). htmlspecialchars_decode($this->getPublicName()) . "\n"
2544 . _('Project Unix Name')._(': '). $this->getUnixName() . "\n\n"
2546 . sprintf(_('If you visit your own project page in %s while logged in, '
2547 . 'you will find additional menu functions to your left labeled “Project Admin”.'),
2548 forge_get_config ('forge_name')) . "\n\n"
2550 . sprintf(_('We highly suggest that you now visit %1$s and create a public '
2551 . 'description for your project. This can be done by visiting your project '
2552 . 'page while logged in, and selecting “Project Admin” from the menus '
2553 . 'on the left (or by visiting %2$s after login).'),
2554 forge_get_config ('forge_name'), util_make_url('/project/admin/?group_id='.$this->getID())) . "\n\n"
2556 . sprintf(_('Your project will also not appear in the Trove Software Map (primary '
2557 . 'list of projects hosted on %s which offers great flexibility in '
2558 . 'browsing and search) until you categorize it in the project administration '
2559 . 'screens. So that people can find your project, you should do this now. '
2560 . 'Visit your project while logged in, and select “Project Admin” from the '
2561 . 'menus on the left.'), forge_get_config ('forge_name')) . "\n\n"
2563 . sprintf(_('Enjoy the system, and please tell others about %s. Let us know '
2564 . 'if there is anything we can do to help you.'), forge_get_config ('forge_name')) . "\n\n"
2566 . sprintf(_('-- the %s staff'), forge_get_config ('forge_name')) . "\n" ;
2568 util_send_message($admin->getEmail(), sprintf(_('%s Project Approved'), forge_get_config ('forge_name')), $message);
2570 setup_gettext_from_context();
2577 * sendRejectionEmail - Send project rejection email.
2579 * This function sends out a rejection message to a user who
2580 * registered a project.
2582 * @param int The id of the response to use.
2583 * @param string The rejection message.
2584 * @return boolean completion status.
2587 function sendRejectionEmail($response_id, $message="zxcv") {
2588 $submitters = array () ;
2589 foreach (get_group_join_requests ($this) as $gjr) {
2590 $submitters[] = user_get_object($gjr->getUserID());
2593 if (count ($submitters) < 1) {
2594 $this->setError(_("Group does not have any administrators."));
2598 foreach ($submitters as $admin) {
2599 setup_gettext_for_user($admin);
2601 $response = sprintf(_('Your project registration for %s has been denied.'), forge_get_config('forge_name')) . "\n\n"
2602 . _('Project Full Name')._(': '). $this->getPublicName() . "\n"
2603 . _('Project Unix Name')._(': '). $this->getUnixName() . "\n\n"
2604 . _('Reasons for negative decision')._(': ') . "\n\n";
2606 // Check to see if they want to send a custom rejection response
2607 if ($response_id == 0) {
2608 $response .= $message;
2610 $response .= db_result(
2611 db_query_params('SELECT response_text FROM canned_responses WHERE response_id=$1', array ($response_id)),
2616 util_send_message($admin->getEmail(), sprintf(_('%s Project Denied'), forge_get_config ('forge_name')), $response);
2617 setup_gettext_from_context();
2624 * sendNewProjectNotificationEmail - Send new project notification email.
2626 * This function sends out a notification email to the
2627 * SourceForge admin user when a new project is
2630 * @return boolean success.
2633 function sendNewProjectNotificationEmail() {
2634 // Get the user who wants to register the project
2635 $submitters = array();
2636 foreach (get_group_join_requests ($this) as $gjr) {
2637 $submitters[] = user_get_object($gjr->getUserID());
2639 if (count ($submitters) < 1) {
2640 $this->setError(_("Could not find user who has submitted the project."));
2644 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('approve_projects', -1) ;
2646 if (count($admins) < 1) {
2647 $this->setError(_("There is no administrator to send the mail to."));
2651 foreach ($admins as $admin) {
2652 $admin_email = $admin->getEmail () ;
2653 setup_gettext_for_user ($admin) ;
2655 $message = sprintf(_('New %s Project Submitted'), forge_get_config ('forge_name')) . "\n\n"
2656 . _('Project Full Name')._(': ').htmlspecialchars_decode($this->getPublicName()) . "\n"
2657 . _('Submitted Description')._(': ').htmlspecialchars_decode($this->getRegistrationPurpose()) . "\n"
2658 . _('Submitter')._(': ').$submitter->getRealName().' ('.$submitter->getUnixName().')' . "\n\n"
2659 . _('Please visit the following URL to approve or reject this project')._(': '). "\n"
2660 . util_make_url('/admin/approve-pending.php');
2661 util_send_message($admin_email, sprintf(_('New %s Project Submitted'), forge_get_config('forge_name')), $message);
2662 setup_gettext_from_context();
2665 $email = $submitter->getEmail() ;
2666 setup_gettext_for_user ($submitter) ;
2668 $message = sprintf(_('New %s Project Submitted'), forge_get_config ('forge_name')) . "\n\n"
2669 . _('Project Full Name')._(': ') . $this->getPublicName() . "\n"
2670 . _('Submitted Description')._(': ') . util_unconvert_htmlspecialchars($this->getRegistrationPurpose()) . "\n\n"
2671 . sprintf(_('The %s admin team will now examine your project submission. You will be notified of their decision.'),
2672 forge_get_config ('forge_name'));
2674 util_send_message($email, sprintf(_('New %s Project Submitted'), forge_get_config ('forge_name')), $message);
2675 setup_gettext_from_context();
2681 * validateGroupName - Validate the group name
2683 * @param string Group name.
2685 * @return boolean an error false and set an error is the group name is invalid otherwise return true
2687 function validateGroupName($group_name) {
2688 if (strlen($group_name)<3) {
2689 $this->setError(_('Group name is too short'));
2691 } elseif (strlen(htmlspecialchars($group_name))>50) {
2692 $this->setError(_('Group name is too long'));
2694 } elseif (group_get_object_by_publicname($group_name)) {
2695 $this->setError(_('Group name already taken'));
2703 * getRolesId - Get Ids of the roles of the group.
2705 * @return array Role ids of this group.
2707 function getRolesId() {
2708 $role_ids = array();
2710 $res = db_query_params('SELECT role_id FROM pfo_role WHERE home_group_id=$1',
2711 array($this->getID()));
2712 while ($arr = db_fetch_array($res)) {
2713 $role_ids[] = $arr['role_id'];
2715 $res = db_query_params('SELECT role_id FROM role_project_refs WHERE group_id=$1',
2716 array($this->getID()));
2717 while ($arr = db_fetch_array($res)) {
2718 $role_ids[] = $arr['role_id'];
2721 return array_unique($role_ids);
2725 * getRoles - Get the roles of the group.
2727 * @return array Roles of this group.
2729 function getRoles() {
2732 $roles = $this->getRolesId();
2733 $engine = RBACEngine::getInstance();
2734 foreach ($roles as $role_id) {
2735 $result[] = $engine->getRoleById ($role_id);
2741 function normalizeAllRoles() {
2742 $roles = $this->getRoles();
2744 foreach ($roles as $r) {
2745 $r->normalizeData();
2750 * getUnixStatus - Status of activation of unix account.
2752 * @return string Values: (N)one, (A)ctive, (S)uspended or (D)eleted
2754 function getUnixStatus() {
2755 return $this->data_array['unix_status'];
2759 * setUnixStatus - Sets status of activation of unix account.
2761 * @param string $status The unix status.
2767 * @return boolean success.
2769 function setUnixStatus($status) {
2772 $res = db_query_params ('UPDATE groups SET unix_status=$1 WHERE group_id=$2',
2777 $this->setError(sprintf(_('Error: Cannot Update Group Unix Status: %s'),db_error()));
2781 if ($status == 'A') {
2782 if (!$SYS->sysCheckCreateGroup($this->getID())) {
2783 $this->setError($SYS->getErrorMessage());
2788 if ($SYS->sysCheckGroup($this->getID())) {
2789 if (!$SYS->sysRemoveGroup($this->getID())) {
2790 $this->setError($SYS->getErrorMessage());
2797 $this->data_array['unix_status']=$status;
2804 * getUsers - Get the users of a group
2806 * @return array of user's objects.
2808 function getUsers($onlylocal = true) {
2809 if (!isset($this->membersArr)) {
2810 $this->membersArr = array () ;
2813 foreach ($this->getRoles() as $role) {
2815 && ($role->getHomeProject() == NULL || $role->getHomeProject()->getID() != $this->getID())) {
2818 foreach ($role->getUsers() as $user) {
2819 $ids[] = $user->getID() ;
2822 $ids = array_unique ($ids) ;
2823 foreach ($ids as $id) {
2824 $u = user_get_object ($id) ;
2825 if ($u->isActive()) {
2826 $this->membersArr[] = $u ;
2830 return $this->membersArr;
2833 function setDocmanCreateOnlineStatus($status) {
2835 /* if we activate search engine, we probably want to reindex */
2836 $res = db_query_params('UPDATE groups SET use_docman_create_online=$1 WHERE group_id=$2',
2837 array($status, $this->getID()));
2840 $this->setError(sprintf(_('Error: Cannot Update Group DocmanCreateOnline Status: %s'),db_error()));
2844 $this->data_array['use_docman_create_online']=$status;
2850 function setDocmanWebdav($status) {
2852 /* if we activate search engine, we probably want to reindex */
2853 $res = db_query_params('UPDATE groups SET use_webdav=$1 WHERE group_id=$2',
2858 $this->setError(sprintf(_('Error: Cannot Update Group UseWebdab Status: %s'),db_error()));
2862 $this->data_array['use_webdav']=$status;
2868 function setDocmanSearchStatus($status) {
2870 /* if we activate search engine, we probably want to reindex */
2871 $res = db_query_params('UPDATE groups SET use_docman_search=$1, force_docman_reindex=$1 WHERE group_id=$2',
2876 $this->setError(sprintf(_('Error: Cannot Update Group UseDocmanSearch Status: %s'),db_error()));
2880 $this->data_array['use_docman_search']=$status;
2886 function setDocmanForceReindexSearch($status) {
2888 /* if we activate search engine, we probably want to reindex */
2889 $res = db_query_params('UPDATE groups SET force_docman_reindex=$1 WHERE group_id=$2',
2894 $this->setError(sprintf(_('Error: Cannot Update Group force_docman_reindex %s'),db_error()));
2898 $this->data_array['force_docman_reindex']=$status;
2907 * group_getname() - get the group name
2909 * @param int The group ID
2914 function group_getname ($group_id = 0) {
2915 $grp = group_get_object($group_id);
2917 return $grp->getPublicName();
2924 * group_getunixname() - get the unixname for a group
2926 * @param int The group ID
2931 function group_getunixname ($group_id) {
2932 $grp = group_get_object($group_id);
2934 return $grp->getUnixName();
2941 * group_get_result() - Get the group object result ID.
2943 * @param int The group ID
2948 function &group_get_result($group_id=0) {
2949 $grp = group_get_object($group_id);
2951 return $grp->getData();
2957 function getAllProjectTags($onlyvisible = true) {
2958 $res = db_query_params('SELECT project_tags.name, groups.group_id FROM groups, project_tags WHERE groups.group_id = project_tags.group_id AND groups.status = $1 ORDER BY project_tags.name, groups.group_id',
2961 if (!$res || db_numrows($res) == 0) {
2967 while ($arr = db_fetch_array($res)) {
2969 $group_id = $arr[1];
2970 if (!isset($result[$tag])) {
2971 $result[$tag] = array();
2974 if (!$onlyvisible || forge_check_perm('project_read', $group_id)) {
2975 $p = group_get_object($group_id);
2976 $result[$tag][] = array('unix_group_name' => $p->getUnixName(),
2977 'group_id' => $group_id);
2985 * Utility class to compare project based in various criteria (names, unixnames, id, ...)
2988 class ProjectComparator {
2989 var $criterion = 'name' ;
2991 function Compare ($a, $b) {
2992 switch ($this->criterion) {
2995 $namecmp = strcoll ($a->getPublicName(), $b->getPublicName()) ;
2996 if ($namecmp != 0) {
2999 /* If several projects share a same public name */
3000 return strcoll ($a->getUnixName(), $b->getUnixName()) ;
3003 return strcmp ($a->getUnixName(), $b->getUnixName()) ;
3006 $aid = $a->getID() ;
3007 $bid = $b->getID() ;
3011 return ($a < $b) ? -1 : 1;
3017 function sortProjectList (&$list, $criterion='name') {
3018 $cmp = new ProjectComparator () ;
3019 $cmp->criterion = $criterion ;
3021 return usort ($list, array ($cmp, 'Compare')) ;
3026 // c-file-style: "bsd"