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.
831 function setAsTemplate($booleanparam) {
833 $booleanparam = $booleanparam ? 1 : 0;
834 $res = db_query_params('UPDATE groups SET is_template=$1 WHERE group_id=$2',
835 array($booleanparam, $this->getID()));
837 $this->data_array['is_template']=$booleanparam;
847 * getTemplateProject - Return the project template this project is built from
849 * @return object The template project
851 function getTemplateProject() {
852 return group_get_object($this->data_array['built_from_template']);
856 * getUnixName - the unix_name
858 * @return string unix_name.
860 function getUnixName() {
861 return strtolower($this->data_array['unix_group_name']);
865 * getPublicName - the full-length public name.
867 * @return string The group_name.
869 function getPublicName() {
870 return $this->data_array['group_name'];
874 * getRegisterPurpose - the text description of the purpose of this project.
876 * @return string The description.
878 function getRegisterPurpose() {
879 return $this->data_array['register_purpose'];
883 * getDescription - the text description of this project.
885 * @return string The description.
887 function getDescription() {
888 return $this->data_array['short_description'];
892 * getStartDate - the unix time this project was registered.
894 * @return int (unix time) of registration.
896 function getStartDate() {
897 return $this->data_array['register_time'];
901 * getLogoImageID - the id of the logo in the database for this project.
903 * @return int The ID of logo image in db_images table (or 100 if none).
905 function getLogoImageID() {
906 return $this->data_array['logo_image_id'];
910 * getUnixBox - the hostname of the unix box where this project is located.
912 * @return string The name of the unix machine for the group.
914 function getUnixBox() {
915 return $this->data_array['unix_box'];
919 * getSCMBox - the hostname of the scm box where this project is located.
921 * @return string The name of the unix machine for the group.
923 function getSCMBox() {
924 return $this->data_array['scm_box'];
927 * setSCMBox - the hostname of the scm box where this project is located.
929 * @param string The name of the new SCM_BOX
931 function setSCMBox($scm_box) {
933 if ($scm_box == $this->data_array['scm_box']) {
938 $res = db_query_params('UPDATE groups SET scm_box=$1 WHERE group_id=$2', array($scm_box, $this->getID ()));
940 $this->addHistory('scm_box', $this->data_array['scm_box']);
941 $this->data_array['scm_box'] = $scm_box;
946 $this->setError(_("Could not insert SCM_BOX to database"));
950 $this->setError(_("SCM Box cannot be empty"));
956 * getDomain - the hostname.domain where their web page is located.
958 * @return string The name of the group [web] domain.
960 function getDomain() {
961 return $this->data_array['http_domain'];
965 * getRegistrationPurpose - the text description of the purpose of this project.
967 * @return string The application for project hosting.
969 function getRegistrationPurpose() {
970 return $this->data_array['register_purpose'];
975 * getAdmins() - Get array of Admin user objects.
977 * @return array Array of User objects.
979 function &getAdmins() {
980 $roles = RBACEngine::getInstance()->getRolesByAllowedAction ('project_admin', $this->getID());
984 foreach ($roles as $role) {
985 if (! ($role instanceof RoleExplicit)) {
988 if ($role->getHomeProject() == NULL
989 || $role->getHomeProject()->getID() != $this->getID()) {
993 foreach ($role->getUsers() as $u) {
994 $user_ids[] = $u->getID();
997 return user_get_objects(array_unique($user_ids));
1001 Common Group preferences for tools
1005 * enableAnonSCM - whether or not this group has opted to enable Anonymous SCM.
1007 * @return boolean enable_scm.
1009 function enableAnonSCM() {
1010 $r = RoleAnonymous::getInstance();
1011 return $r->hasPermission('scm', $this->getID(), 'read');
1014 function SetUsesAnonSCM($booleanparam) {
1016 $booleanparam = $booleanparam ? 1 : 0;
1017 $r = RoleAnonymous::getInstance();
1018 $r->setSetting('scm', $this->getID(), $booleanparam);
1023 * enablePserver - whether or not this group has opted to enable Pserver.
1025 * @return boolean enable_pserver.
1027 function enablePserver() {
1028 if ($this->usesSCM()) {
1029 return $this->data_array['enable_pserver'];
1035 function SetUsesPserver($booleanparam) {
1037 $booleanparam = $booleanparam ? 1 : 0;
1038 $res = db_query_params('UPDATE groups SET enable_pserver=$1 WHERE group_id=$2',
1039 array($booleanparam, $this->getID()));
1041 $this->data_array['enable_pserver'] = $booleanparam;
1050 * usesSCM - whether or not this group has opted to use SCM.
1052 * @return boolean uses_scm.
1054 function usesSCM() {
1055 if (forge_get_config('use_scm')) {
1056 return $this->data_array['use_scm'];
1063 * setUseSCM - Set the SCM usage
1065 * @param boolean enabled/disabled
1067 function setUseSCM($booleanparam) {
1069 $booleanparam = $booleanparam ? 1 : 0 ;
1070 $res = db_query_params('UPDATE groups SET use_scm=$1 WHERE group_id=$2',
1071 array($booleanparam, $this->getID()));
1073 $this->data_array['use_scm']=$booleanparam;
1083 * usesMail - whether or not this group has opted to use mailing lists.
1085 * @return boolean uses_mail.
1087 function usesMail() {
1088 if (forge_get_config('use_mail')) {
1089 return $this->data_array['use_mail'];
1094 $hook_params = array ();
1095 $hook_params['group'] = $this;
1096 $hook_params['group_id'] = $this->getID();
1097 $hook_params['group_homepage'] = $this->getHomePage();
1098 $hook_params['group_name'] = $this->getPublicName();
1099 $hook_params['group_description'] = $this->getDescription();
1100 plugin_hook ("group_update", $hook_params);
1104 * setUseMail - Set the mailing-list usage
1106 * @param boolean enabled/disabled
1108 function setUseMail($booleanparam) {
1110 $booleanparam = $booleanparam ? 1 : 0 ;
1111 $res = db_query_params('UPDATE groups SET use_mail=$1 WHERE group_id=$2',
1112 array($booleanparam, $this->getID()));
1114 $this->data_array['use_mail']=$booleanparam;
1124 * usesNews - whether or not this group has opted to use news.
1126 * @return boolean uses_news.
1128 function usesNews() {
1129 if (forge_get_config('use_news')) {
1130 return $this->data_array['use_news'];
1137 * usesActivity - whether or not this group has opted to display Project Activities.
1139 * @return boolean uses_activities.
1141 function usesActivity() {
1142 if (forge_get_config('use_activity')) {
1143 return $this->data_array['use_activity'];
1150 * usesForum - whether or not this group has opted to use discussion forums.
1152 * @return boolean uses_forum.
1154 function usesForum() {
1155 if (forge_get_config('use_forum')) {
1156 return $this->data_array['use_forum'];
1163 * setUseForum - Set the forum usage
1165 * @param boolean enabled/disabled
1167 function setUseForum($booleanparam) {
1169 $booleanparam = $booleanparam ? 1 : 0;
1170 $res = db_query_params('UPDATE groups SET use_forum=$1 WHERE group_id=$2',
1171 array($booleanparam, $this->getID()));
1173 $this->data_array['use_forum']=$booleanparam;
1183 * usesStats - whether or not this group has opted to use stats.
1185 * @return boolean uses_stats.
1187 function usesStats() {
1188 return $this->data_array['use_stats'];
1192 * usesFRS - whether or not this group has opted to use file release system.
1194 * @return boolean uses_frs.
1196 function usesFRS() {
1197 if (forge_get_config('use_frs')) {
1198 return $this->data_array['use_frs'];
1205 * setUseFRS - Set the FRS usage
1207 * @param boolean enabled/disabled
1209 function setUseFRS($booleanparam) {
1211 $booleanparam = $booleanparam ? 1 : 0;
1212 $res = db_query_params('UPDATE groups SET use_frs=$1 WHERE group_id=$2',
1213 array ($booleanparam, $this->getID()));
1215 $this->data_array['use_frs']=$booleanparam;
1225 * usesTracker - whether or not this group has opted to use tracker.
1227 * @return boolean uses_tracker.
1229 function usesTracker() {
1230 if (forge_get_config('use_tracker')) {
1231 return $this->data_array['use_tracker'];
1238 * setUseTracker - Set the tracker usage
1240 * @param boolean enabled/disabled
1242 function setUseTracker ($booleanparam) {
1244 $booleanparam = $booleanparam ? 1 : 0 ;
1245 $res = db_query_params ('UPDATE groups SET use_tracker=$1 WHERE group_id=$2',
1246 array ($booleanparam, $this->getID()));
1248 $this->data_array['use_tracker']=$booleanparam;
1258 * useCreateOnline - whether or not this group has opted to use create online documents option.
1260 * @return boolean use_docman_create_online.
1262 function useCreateOnline() {
1263 if (forge_get_config('use_docman')) {
1264 return $this->data_array['use_docman_create_online'];
1271 * usesDocman - whether or not this group has opted to use docman.
1273 * @return boolean use_docman.
1275 function usesDocman() {
1276 if (forge_get_config('use_docman')) {
1277 return $this->data_array['use_docman'];
1284 * setUseDocman - Set the docman usage
1286 * @param boolean enabled/disabled
1288 function setUseDocman($booleanparam) {
1290 $booleanparam = $booleanparam ? 1 : 0;
1291 $res = db_query_params('UPDATE groups SET use_docman = $1 WHERE group_id = $2',
1292 array($booleanparam, $this->getID()));
1294 // check if / doc_group exists, if not create it
1295 $trashdir = db_query_params('select groupname from doc_groups where groupname = $1 and group_id = $2',
1296 array('.trash', $this->getID()));
1297 if ($trashdir && db_numrows($trashdir) == 0) {
1298 $resinsert = db_query_params('insert into doc_groups (groupname, group_id, stateid) values ($1, $2, $3)',
1299 array('.trash', $this->getID(), '2'));
1305 $this->data_array['use_docman'] = $booleanparam;
1315 * useDocmanSearch - whether or not this group has opted to use docman search engine.
1317 * @return boolean use_docman_search.
1319 function useDocmanSearch() {
1320 if (forge_get_config('use_docman')) {
1321 return $this->data_array['use_docman_search'];
1328 * useWebdav - whether or not this group has opted to use webdav interface.
1330 * @return boolean use_docman_search.
1332 function useWebdav() {
1333 if (forge_get_config('use_webdav')) {
1334 return $this->data_array['use_webdav'];
1341 * usesFTP - whether or not this group has opted to use FTP.
1343 * @return boolean uses_ftp.
1345 function usesFTP() {
1346 if (forge_get_config('use_ftp')) {
1347 return $this->data_array['use_ftp'];
1354 * usesSurvey - whether or not this group has opted to use surveys.
1356 * @return boolean uses_survey.
1358 function usesSurvey() {
1359 if (forge_get_config('use_survey')) {
1360 return $this->data_array['use_survey'];
1367 * usesPM - whether or not this group has opted to Project Manager.
1369 * @return boolean uses_projman.
1372 if (forge_get_config('use_pm')) {
1373 return $this->data_array['use_pm'];
1380 * setUsePM - Set the PM usage
1382 * @param boolean enabled/disabled
1384 function setUsePM($booleanparam) {
1386 $booleanparam = $booleanparam ? 1 : 0;
1387 $res = db_query_params('UPDATE groups SET use_pm=$1 WHERE group_id=$2',
1388 array($booleanparam, $this->getID()));
1390 $this->data_array['use_pm']=$booleanparam;
1400 * getPlugins - get a list of all available group plugins
1402 * @return array array containing plugin_id => plugin_name
1404 function getPlugins() {
1405 if (!isset($this->plugins_data)) {
1406 $this->plugins_data = array();
1407 $res = db_query_params('SELECT group_plugin.plugin_id, plugins.plugin_name
1408 FROM group_plugin, plugins
1409 WHERE group_plugin.group_id=$1
1410 AND group_plugin.plugin_id=plugins.plugin_id', array($this->getID()));
1411 $rows = db_numrows($res);
1413 for ($i=0; $i<$rows; $i++) {
1414 $plugin_id = db_result($res, $i, 'plugin_id');
1415 $this->plugins_data[$plugin_id] = db_result($res, $i, 'plugin_name');
1418 return $this->plugins_data;
1422 * usesPlugin - returns true if the group uses a particular plugin
1424 * @param string $pluginname name of the plugin
1425 * @return boolean whether plugin is being used or not
1427 function usesPlugin($pluginname) {
1428 $plugins_data = $this->getPlugins();
1429 foreach ($plugins_data as $p_id => $p_name) {
1430 if ($p_name == $pluginname) {
1438 * added for Codendi compatibility
1439 * usesServices - returns true if the group uses a particular plugin or feature
1441 * @param string name of the plugin
1442 * @return boolean whether plugin is being used or not
1444 function usesService($feature) {
1445 $plugins_data = $this->getPlugins();
1446 $pm = plugin_manager_get_object();
1447 foreach ($plugins_data as $p_id => $p_name) {
1448 if ($p_name == $feature) {
1451 if ($pm->getPluginByName($p_name)->provide($feature)) {
1459 * setPluginUse - enables/disables plugins for the group
1461 * @param string name of the plugin
1462 * @param boolean the new state
1463 * @return string database result
1465 function setPluginUse($pluginname, $val=true) {
1466 if ($val == $this->usesPlugin($pluginname)) {
1467 // State is already good, returning
1470 $res = db_query_params('SELECT plugin_id FROM plugins WHERE plugin_name=$1',
1471 array($pluginname));
1472 $rows = db_numrows($res);
1474 // Error: no plugin by that name
1477 $plugin_id = db_result($res,0,'plugin_id');
1479 unset($this->plugins_data);
1481 $res = db_query_params('INSERT INTO group_plugin (group_id, plugin_id) VALUES ($1, $2)',
1482 array($this->getID(),
1486 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1 AND plugin_id=$2',
1487 array($this->getID(),
1491 $this->normalizeAllRoles () ;
1495 * getDocEmailAddress - get email address(es) to send doc notifications to.
1497 * @return string email address.
1499 function getDocEmailAddress() {
1500 return $this->data_array['new_doc_address'];
1504 * DocEmailAll - whether or not this group has opted to use receive notices on all doc updates.
1506 * @return boolean email_on_all_doc_updates.
1508 function docEmailAll() {
1509 return $this->data_array['send_all_docs'];
1514 * getHomePage - The URL for this project's home page.
1516 * @return string homepage URL.
1518 function getHomePage() {
1519 if (!preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*:/",
1520 $this->data_array['homepage'])) {
1521 $this->data_array['homepage'] = util_url_prefix() .
1522 $this->data_array['homepage'];
1524 return $this->data_array['homepage'];
1528 * getTags - Tags of this project.
1530 * @return string List of tags. Comma separated
1532 function getTags() {
1533 $sql = 'SELECT name FROM project_tags WHERE group_id = $1';
1534 $res = db_query_params($sql, array($this->getID()));
1535 return join(', ', util_result_column_to_array($res));
1539 * setTags - Set tags of this project.
1541 * @return string database result.
1543 function setTags($tags) {
1545 $sql = 'DELETE FROM project_tags WHERE group_id=$1';
1546 $res = db_query_params($sql, array($this->getID()));
1548 $this->setError('Deleting old tags: '.db_error());
1552 $inserted = array();
1553 $tags_array = preg_split('/[;,]/', $tags);
1554 foreach ($tags_array as $tag) {
1555 $tag = preg_replace('/[\t\r\n]/', ' ', $tag);
1556 // Allowed caracteres: [A-Z][a-z][0-9] -_&'#+.
1557 if (preg_match('/[^[:alnum:]| |\-|_|\&|\'|#|\+|\.]/', $tag)) {
1558 $this->setError(_('Bad tag name, you only can use the following characters: [A-Z][a-z][0-9]-_&\'#+. and space'));
1563 if ($tag == '' || array_search($tag, $inserted) !== false) continue;
1564 $sql = 'INSERT INTO project_tags (group_id,name) VALUES ($1, $2)';
1565 $res = db_query_params($sql, array($this->getID(), $tag));
1567 $this->setError(_('Setting tags:') . ' ' .
1579 * getPermission - Return a Permission for this Group
1581 * @return object The Permission.
1583 function &getPermission() {
1584 return permission_get_object($this);
1588 function delete($sure, $really_sure, $really_really_sure) {
1589 if (!$sure || !$really_sure || !$really_really_sure) {
1590 $this->setMissingParamsError(_('Please tick all checkboxes.'));
1593 if ($this->getID() == forge_get_config('news_group') ||
1594 $this->getID() == 1 ||
1595 $this->getID() == forge_get_config('stats_group') ||
1596 $this->getID() == forge_get_config('peer_rating_group')) {
1597 $this->setError(_('Cannot Delete System Group'));
1600 $perm = $this->getPermission();
1601 if (!$perm || !is_object($perm)) {
1602 $this->setPermissionDeniedError();
1604 } elseif ($perm->isError()) {
1605 $this->setPermissionDeniedError();
1607 } elseif (!$perm->isSuperUser()) {
1608 $this->setPermissionDeniedError();
1614 // Remove all the members
1616 $members = $this->getMembers();
1617 foreach ($members as $i) {
1618 if(!$this->removeUser($i->getID())) {
1619 $this->setError(_('Could not properly remove member:').' '.$i->getID());
1624 // unlink roles from this project
1625 foreach ($this->getRoles() as $r) {
1626 if ($r->getHomeProject() == NULL
1627 || $r->getHomeProject()->getID() != $this->getID()) {
1628 $r->unlinkProject($this);
1635 if ($this->usesTracker()) {
1636 $atf = new ArtifactTypeFactory($this);
1637 $at_arr = $atf->getArtifactTypes();
1638 foreach ($at_arr as $i) {
1639 if (!is_object($i)) {
1642 if (!$i->delete(1,1)) {
1643 $this->setError(_('Could not properly delete the tracker:').' '.$i->getErrorMessage());
1652 if ($this->usesForum()) {
1653 $ff = new ForumFactory($this);
1654 $f_arr = $ff->getForums();
1655 foreach ($f_arr as $i) {
1656 if (!is_object($i)) {
1659 if(!$i->delete(1,1)) {
1660 $this->setError(_('Could not properly delete the forum:').' '.$i->getErrorMessage());
1666 // Delete Subprojects
1668 if ($this->usesPM()) {
1669 $pgf = new ProjectGroupFactory($this);
1670 $pg_arr = $pgf->getProjectGroups();
1671 foreach ($pg_arr as $i) {
1672 if (!is_object($i)) {
1675 if (!$i->delete(1,1)) {
1676 $this->setError(_('Could not properly delete the ProjectGroup:').' '.$i->getErrorMessage());
1682 // Delete FRS Packages
1684 $res = db_query_params('SELECT * FROM frs_package WHERE group_id=$1',
1685 array($this->getID()));
1687 $this->setError(_('Error FRS Packages: ').db_error());
1692 while ($arr = db_fetch_array($res)) {
1693 $frsp=new FRSPackage($this, $arr['package_id'], $arr);
1694 if (!$frsp->delete(1, 1)) {
1695 $this->setError(_('Could not properly delete the FRSPackage:').' '.$frsp->getErrorMessage());
1702 $news_group=group_get_object(forge_get_config('news_group'));
1703 $res = db_query_params('SELECT forum_id FROM news_bytes WHERE group_id=$1',
1704 array($this->getID()));
1706 $this->setError(_('Error Deleting News: ').db_error());
1711 for ($i=0; $i<db_numrows($res); $i++) {
1712 $Forum = new Forum($news_group,db_result($res,$i,'forum_id'));
1713 if (!$Forum->delete(1,1)) {
1714 $this->setError(_("Could Not Delete News Forum: %d"),$Forum->getID());
1718 $res = db_query_params('DELETE FROM news_bytes WHERE group_id=$1',
1719 array($this->getID()));
1721 $this->setError(_('Error Deleting News: ').db_error());
1729 $res = db_query_params('DELETE FROM doc_data WHERE group_id=$1',
1730 array($this->getID()));
1732 $this->setError(_('Error Deleting Documents: ').db_error());
1737 $res = db_query_params('DELETE FROM doc_groups WHERE group_id=$1',
1738 array($this->getID()));
1740 $this->setError(_('Error Deleting Documents: ').db_error());
1748 $res=db_query_params('DELETE FROM project_tags WHERE group_id=$1', array($this->getID()));
1750 $this->setError(_('Error Deleting Tags: ').db_error());
1756 // Delete group history
1758 $res = db_query_params('DELETE FROM group_history WHERE group_id=$1',
1759 array($this->getID()));
1761 $this->setError(_('Error Deleting Project History: ').db_error());
1767 // Delete group plugins
1769 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1',
1770 array($this->getID()));
1772 $this->setError(_('Error Deleting Project Plugins: ').db_error());
1778 // Delete group cvs stats
1780 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE group_id=$1',
1781 array ($this->getID())) ;
1783 $this->setError(_('Error Deleting SCM Statistics: ').db_error());
1791 if ($this->usesSurvey()) {
1792 $sf = new SurveyFactory($this);
1793 $s_arr =& $sf->getSurveys();
1794 foreach ($s_arr as $i) {
1795 if (!is_object($i)) {
1798 if (!$i->delete()) {
1799 $this->setError(_('Could not properly delete the survey'));
1805 // Delete SurveyQuestions
1807 $sqf = new SurveyQuestionFactory($this);
1808 $sq_arr = $sqf->getSurveyQuestions();
1809 if (is_array($sq_arr)) {
1810 foreach ($sq_arr as $i) {
1811 if (!is_object($i)) {
1814 if (!$i->delete()) {
1815 $this->setError(_('Could not properly delete the survey questions'));
1823 // Delete Mailing List Factory
1825 if ($this->usesMail()) {
1826 $mlf = new MailingListFactory($this);
1827 $ml_arr = $mlf->getMailingLists();
1828 foreach ($ml_arr as $i) {
1829 if (!is_object($i)) {
1832 if (!$i->delete(1,1)) {
1833 $this->setError(_('Could not properly delete the mailing list'));
1842 $res = db_query_params('DELETE FROM trove_group_link WHERE group_id=$1',
1843 array($this->getID()));
1845 $this->setError(_('Error Deleting Trove: ').db_error());
1850 $res = db_query_params('DELETE FROM trove_agg WHERE group_id=$1',
1851 array($this->getID()));
1853 $this->setError(_('Error Deleting Trove: ').db_error());
1861 $res = db_query_params('DELETE FROM project_sums_agg WHERE group_id=$1',
1862 array($this->getID()));
1864 $this->setError(_('Error Deleting Counters: ').db_error());
1869 $res = db_query_params('INSERT INTO deleted_groups (unix_group_name, delete_date, isdeleted) VALUES ($1, $2, $3)',
1870 array($this->getUnixName(),
1874 $this->setError(_('Error Deleting Project:').' '.db_error());
1879 // Delete entry in groups.
1880 $res = db_query_params('DELETE FROM groups WHERE group_id=$1',
1881 array($this->getID()));
1883 $this->setError(_('Error Deleting Project:').' '.db_error());
1890 $hook_params = array();
1891 $hook_params['group'] = $this;
1892 $hook_params['group_id'] = $this->getID();
1893 plugin_hook("group_delete", $hook_params);
1895 if (forge_get_config('upload_dir') != '' && $this->getUnixName()) {
1896 exec('/bin/rm -rf '.forge_get_config('upload_dir').'/'.$this->getUnixName().'/');
1898 if (forge_get_config('ftp_upload_dir') != '' && $this->getUnixName()) {
1899 exec('/bin/rm -rf '.forge_get_config('ftp_upload_dir').'/'.$this->getUnixName().'/');
1904 $res = db_query_params('DELETE FROM rep_group_act_monthly WHERE group_id=$1',
1905 array ($this->getID()));
1906 //echo 'rep_group_act_monthly'.db_error();
1907 $res = db_query_params('DELETE FROM rep_group_act_weekly WHERE group_id=$1',
1908 array ($this->getID()));
1909 //echo 'rep_group_act_weekly'.db_error();
1910 $res = db_query_params('DELETE FROM rep_group_act_daily WHERE group_id=$1',
1911 array ($this->getID()));
1912 //echo 'rep_group_act_daily'.db_error();
1913 unset($this->data_array);
1918 Basic functions to add/remove users to/from a group
1919 and update their permissions
1923 * addUser - controls adding a user to a group.
1925 * @param string Unix name of the user to add OR integer user_id.
1926 * @param int The role_id this user should have.
1927 * @return boolean success.
1930 function addUser($user_identifier,$role_id) {
1933 Admins can add users to groups
1936 if (!forge_check_perm ('project_admin', $this->getID())) {
1937 $this->setPermissionDeniedError();
1943 get user id for this user's unix_name
1945 if (is_int ($user_identifier)) { // user_id or user_name
1946 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_id=$1', array ($user_identifier)) ;
1948 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_name=$1', array ($user_identifier)) ;
1950 if (db_numrows($res_newuser) > 0) {
1952 // make sure user is active
1954 if (db_result($res_newuser,0,'status') != 'A') {
1955 $this->setError(_('User is not active. Only active users can be added.'));
1961 // user was found - set new user_id var
1963 $user_id = db_result($res_newuser,0,'user_id');
1965 $role = new Role($this, $role_id);
1966 if (!$role || !is_object($role)) {
1967 $this->setError(_('Error Getting Role Object'));
1970 } elseif ($role->isError()) {
1971 $this->setError('addUser::roleget::'.$role->getErrorMessage());
1976 $role->addUser(user_get_object($user_id)) ;
1977 if (!$SYS->sysCheckCreateGroup($this->getID())){
1978 $this->setError($SYS->getErrorMessage());
1982 if (!$SYS->sysCheckCreateUser($user_id)) {
1983 $this->setError($SYS->getErrorMessage());
1987 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
1988 $this->setError($SYS->getErrorMessage());
1994 // user doesn't exist
1996 $this->setError(_('Error: User does not exist'));
2001 $hook_params['group'] = $this;
2002 $hook_params['group_id'] = $this->getID();
2003 $hook_params['user'] = user_get_object($user_id);
2004 $hook_params['user_id'] = $user_id;
2005 plugin_hook ("group_adduser", $hook_params);
2010 $this->addHistory('Added User',$user_identifier);
2016 * removeUser - controls removing a user from a group.
2018 * Users can remove themselves.
2020 * @param int The ID of the user to remove.
2021 * @return boolean success.
2023 function removeUser($user_id) {
2026 if ($user_id != user_getid()
2027 && !forge_check_perm('project_admin', $this->getID())) {
2028 $this->setPermissionDeniedError();
2034 $user = user_get_object($user_id);
2035 $roles = RBACEngine::getInstance()->getAvailableRolesForUser($user);
2037 foreach ($roles as $role) {
2038 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2039 $found_role = $role;
2043 if ($found_role == NULL) {
2044 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2048 $found_role->removeUser($user);
2049 if (!$SYS->sysGroupCheckUser($this->getID(), $user_id)) {
2050 $this->setError($SYS->getErrorMessage());
2056 // reassign open artifacts to id=100
2058 $res = db_query_params('UPDATE artifact SET assigned_to=100
2059 WHERE group_artifact_id
2060 IN (SELECT group_artifact_id
2061 FROM artifact_group_list
2062 WHERE group_id=$1 AND status_id=1 AND assigned_to=$2)',
2063 array($this->getID(),
2066 $this->setError(_('Error: artifact:').' '.db_error());
2072 // reassign open tasks to id=100
2073 // first have to purge any assignments that would cause
2074 // conflict with existing assignment to 100
2076 $res = db_query_params('DELETE FROM project_assigned_to
2077 WHERE project_task_id IN (SELECT pt.project_task_id
2078 FROM project_task pt, project_group_list pgl, project_assigned_to pat
2079 WHERE pt.group_project_id = pgl.group_project_id
2080 AND pat.project_task_id=pt.project_task_id
2081 AND pt.status_id=1 AND pgl.group_id=$1
2082 AND pat.assigned_to_id=$2)
2083 AND assigned_to_id=100',
2084 array($this->getID(),
2087 $this->setError(sprintf(_('Error: project_assigned_to %d: %s'), 1, db_error()));
2091 $res = db_query_params('UPDATE project_assigned_to SET assigned_to_id=100
2092 WHERE project_task_id IN (SELECT pt.project_task_id
2093 FROM project_task pt, project_group_list pgl
2094 WHERE pt.group_project_id = pgl.group_project_id
2095 AND pt.status_id=1 AND pgl.group_id=$1)
2096 AND assigned_to_id=$2',
2097 array($this->getID(),
2100 $this->setError(sprintf(_('Error: project_assigned_to %d: %s'), 2, db_error()));
2106 // Remove user from system
2108 if (!$SYS->sysGroupRemoveUser($this->getID(), $user_id)) {
2109 $this->setError($SYS->getErrorMessage());
2114 $hook_params['group'] = $this;
2115 $hook_params['group_id'] = $this->getID();
2116 $hook_params['user'] = user_get_object($user_id);
2117 $hook_params['user_id'] = $user_id;
2118 plugin_hook ("group_removeuser", $hook_params);
2121 $this->addHistory('Removed User',$user_id);
2128 * updateUser - controls updating a user's role in this group.
2130 * @param int The ID of the user.
2131 * @param int The role_id to set this user to.
2132 * @return boolean success.
2134 function updateUser($user_id,$role_id) {
2136 if (!forge_check_perm ('project_admin', $this->getID())) {
2137 $this->setPermissionDeniedError();
2141 $newrole = RBACEngine::getInstance()->getRoleById ($role_id) ;
2142 if (!$newrole || !is_object($newrole)) {
2143 $this->setError(_('Could Not Get Role'));
2145 } elseif ($newrole->isError()) {
2146 $this->setError(sprintf(_('Role: %s'),$role->getErrorMessage()));
2148 } elseif ($newrole->getHomeProject() == NULL
2149 || $newrole->getHomeProject()->getID() != $this->getID()) {
2150 $this->setError(_('Wrong destination role'));
2153 $user = user_get_object ($user_id) ;
2154 $roles = RBACEngine::getInstance()->getAvailableRolesForUser ($user) ;
2155 $found_role = NULL ;
2156 foreach ($roles as $role) {
2157 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2158 $found_role = $role ;
2162 if ($found_role == NULL) {
2163 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2167 $found_role->removeUser ($user) ;
2168 $newrole->addUser ($user) ;
2170 $this->addHistory('Updated User',$user_id);
2175 * addHistory - Makes an audit trail entry for this project.
2177 * @param string The name of the field.
2178 * @param string The Old Value for this $field_name.
2179 * @return database result handle.
2182 function addHistory($field_name, $old_value) {
2183 return db_query_params ('INSERT INTO group_history(group_id,field_name,old_value,mod_by,adddate)
2184 VALUES ($1,$2,$3,$4,$5)',
2185 array ($this->getID(),
2193 * activateUsers - Make sure that group members have unix accounts.
2195 * Setup unix accounts for group members. Can be called even
2196 * if members are already active.
2200 function activateUsers() {
2202 Activate member(s) of the project
2205 $members = $this->getUsers (true) ;
2207 foreach ($members as $member) {
2209 foreach (RBACEngine::getInstance()->getAvailableRolesForUser ($member) as $role) {
2210 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2214 foreach ($roles as $role) {
2215 if (!$this->addUser($member->getUnixName(),$role->getID())) {
2226 * getMembers - returns array of User objects for this project
2228 * @return array of User objects for this group.
2230 function getMembers() {
2231 return $this->getUsers (true) ;
2235 * replaceTemplateStrings - fill-in some blanks with project name
2237 * @param string Template string
2238 * @return string String after replacements
2240 function replaceTemplateStrings($string) {
2241 $string = str_replace ('UNIXNAME', $this->getUnixName(), $string) ;
2242 $string = str_replace ('PUBLICNAME', $this->getPublicName(), $string) ;
2243 $string = str_replace ('DESCRIPTION', $this->getDescription(), $string) ;
2248 * approve - Approve pending project.
2250 * @param object The User object who is doing the updating.
2253 function approve(&$user) {
2254 global $gfcommon,$gfwww;
2255 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
2257 if ($this->getStatus()=='A') {
2258 $this->setError(_("Group already active"));
2264 // Step 1: Activate group and create LDAP entries
2265 if (!$this->setStatus($user, 'A')) {
2270 // Switch to system language for item creation
2271 setup_gettext_from_sys_lang();
2273 // Create default roles
2274 $idadmin_group = NULL;
2275 foreach (get_group_join_requests ($this) as $gjr) {
2276 $idadmin_group = $gjr->getUserID();
2279 if ($idadmin_group == NULL) {
2280 $idadmin_group = $user->getID();
2283 $template = $this->getTemplateProject();
2284 $id_mappings = array();
2285 $seen_admin_role = false;
2287 // Copy roles from template project
2288 foreach($template->getRoles() as $oldrole) {
2289 if ($oldrole->getHomeProject() != NULL) {
2290 $role = new Role($this);
2292 // Need to use a different role name so that the permissions aren't set from the hardcoded defaults
2293 $role->create('TEMPORARY ROLE NAME', $data, true);
2294 $role->setName($oldrole->getName());
2295 if ($oldrole->getSetting ('project_admin', $template->getID())) {
2296 $seen_admin_role = true;
2300 $role->linkProject($this);
2302 $id_mappings['role'][$oldrole->getID()] = $role->getID();
2303 // Reuse the project_admin permission
2304 $role->setSetting ('project_admin', $this->getID(), $oldrole->getSetting ('project_admin', $template->getID())) ;
2308 if (!$seen_admin_role) {
2309 $role = new Role($this);
2310 $adminperms = array ('project_admin' => array ($this->getID() => 1)) ;
2311 $role_id = $role->create ('Admin', $adminperms, true) ;
2314 $roles = $this->getRoles() ;
2315 foreach ($roles as $r) {
2316 if ($r->getHomeProject() == NULL) {
2319 if ($r->getSetting ('project_admin', $this->getID())) {
2320 $r->addUser(user_get_object ($idadmin_group));
2324 // Temporarily switch to the submitter's identity
2325 $saved_session = session_get_user();
2326 session_set_internal($idadmin_group);
2329 if (forge_get_config('use_tracker')) {
2330 $this->setUseTracker ($template->usesTracker());
2331 if ($template->usesTracker()) {
2332 $oldatf = new ArtifactTypeFactory($template);
2333 foreach ($oldatf->getArtifactTypes() as $o) {
2334 $t = new ArtifactType ($this) ;
2335 $t->create ($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->emailAll(),$o->getEmailAddress(),$o->getDuePeriod()/86400,0,$o->getSubmitInstructions(),$o->getBrowseInstructions()) ;
2336 $id_mappings['tracker'][$o->getID()] = $t->getID();
2337 $t->cloneFieldsFrom ($o->getID());
2342 if (forge_get_config('use_pm')) {
2343 $this->setUsePM ($template->usesPM());
2344 if ($template->usesPM()) {
2345 $oldpgf = new ProjectGroupFactory($template);
2346 foreach ($oldpgf->getProjectGroups() as $o) {
2347 $pg = new ProjectGroup($this);
2348 $pg->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo());
2349 $id_mappings['pm'][$o->getID()] = $pg->getID();
2354 if (forge_get_config('use_forum')) {
2355 $this->setUseForum($template->usesForum()) ;
2356 if ($template->usesForum()) {
2357 $oldff = new ForumFactory($template) ;
2358 foreach ($oldff->getForums() as $o) {
2359 $f = new Forum($this);
2360 $f->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo(),1);
2361 $id_mappings['forum'][$o->getID()] = $f->getID();
2366 if (forge_get_config('use_docman')) {
2367 $this->setUseDocman($template->usesDocman());
2368 if ($template->usesDocman()) {
2369 $olddgf = new DocumentGroupFactory($template);
2370 // First pass: create all docgroups
2371 $id_mappings['docman_docgroup'][0] = 0;
2372 foreach ($olddgf->getDocumentGroups() as $o) {
2373 $ndgf = new DocumentGroup($this);
2374 // .trash is a reserved directory
2375 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2376 $ndgf->create($this->replaceTemplateStrings($o->getName()));
2377 $id_mappings['docman_docgroup'][$o->getID()] = $ndgf->getID();
2380 // Second pass: restore hierarchy links
2381 foreach ($olddgf->getDocumentGroups() as $o) {
2382 $ndgf = new DocumentGroup($this);
2383 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2384 $ndgf->fetchData($id_mappings['docman_docgroup'][$o->getID()]);
2385 $ndgf->update($ndgf->getName(), $id_mappings['docman_docgroup'][$o->getParentID()]);
2391 if (forge_get_config('use_frs')) {
2392 $this->setUseFRS ($template->usesFRS());
2393 if ($template->usesFRS()) {
2394 foreach (get_frs_packages($template) as $o) {
2395 $newp = new FRSPackage($this);
2396 $nname = $this->replaceTemplateStrings($o->getName());
2397 $newp->create ($nname, $o->isPublic());
2402 if (forge_get_config('use_mail')) {
2403 $this->setUseMail($template->usesMail()) ;
2404 if ($template->usesMail()) {
2405 $oldmlf = new MailingListFactory($template);
2406 foreach ($oldmlf->getMailingLists() as $o) {
2407 $ml = new MailingList($this);
2408 $nname = preg_replace ('/^'.$template->getUnixName().'-/','',$o->getName()) ;
2410 $ndescription = $this->replaceTemplateStrings($o->getDescription()) ;
2411 $ml->create($nname, $ndescription, $o->isPublic());
2417 /* use SCM plugin from template group */
2418 $this->setUseSCM($template->usesSCM());
2420 foreach ($template->getPlugins() as
2421 $plugin_id => $plugin_name) {
2422 $this->setPluginUse($plugin_name);
2425 /* use SCM choice from registration page */
2427 foreach ($template->getPlugins() as
2428 $plugin_id => $plugin_name) {
2429 if (substr($plugin_name, 3) == 'scm' &&
2430 $plugin_name != 'scmhook') {
2431 /* skip copying scm plugins */
2434 /* enable other plugins though */
2435 $this->setPluginUse($plugin_name);
2439 foreach ($template->getRoles() as $oldrole) {
2440 $newrole = RBACEngine::getInstance()->getRoleById ($id_mappings['role'][$oldrole->getID()]) ;
2441 if ($oldrole->getHomeProject() != NULL
2442 && $oldrole->getHomeProject()->getID() == $template->getID()) {
2443 $newrole->setPublic ($oldrole->isPublic()) ;
2445 $oldsettings = $oldrole->getSettingsForProject ($template) ;
2447 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm') ;
2448 foreach ($sections as $section) {
2449 $newrole->setSetting ($section, $this->getID(), $oldsettings[$section][$template->getID()]) ;
2452 $sections = array ('tracker', 'pm', 'forum') ;
2453 foreach ($sections as $section) {
2454 if (isset ($oldsettings[$section])) {
2455 foreach ($oldsettings[$section] as $k => $v) {
2456 // Only copy perms for tools that have been copied
2457 if (isset ($id_mappings[$section][$k])) {
2458 $newrole->setSetting ($section,
2459 $id_mappings[$section][$k],
2467 $lm = new WidgetLayoutManager();
2468 $lm->createDefaultLayoutForProject ($this->getID(), $template->getID()) ;
2470 $params = array () ;
2471 $params['template'] = $template ;
2472 $params['project'] = $this ;
2473 $params['id_mappings'] = $id_mappings ;
2474 plugin_hook_by_reference ('clone_project_from_template', $params) ;
2476 // Disable everything
2477 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',
2478 array ($this->getID())) ;
2481 $this->normalizeAllRoles();
2482 // empty members cache because the group creator is not yet in cache.
2483 unset($this->membersArr);
2484 $this->activateUsers();
2486 // Delete fake join request
2487 foreach (get_group_join_requests ($this) as $gjr) {
2488 $gjr->delete(true) ;
2491 // Switch back to user preference
2492 session_set_internal($saved_session->getID());
2493 setup_gettext_from_context();
2497 $this->sendApprovalEmail();
2498 $this->addHistory(_('Approved'), 'x');
2501 // Plugin can make approve operation there
2503 $params = array () ;
2504 $params['group'] = $this ;
2505 $params['group_id'] = $this->getID();
2506 plugin_hook('group_approved', $params);
2514 * sendApprovalEmail - Send new project email.
2516 * @return boolean success.
2519 function sendApprovalEmail() {
2520 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('project_admin', $this->getID()) ;
2522 if (count($admins) < 1) {
2523 $this->setError(_("Group does not have any administrators."));
2527 // send one email per admin
2528 foreach ($admins as $admin) {
2529 setup_gettext_for_user ($admin) ;
2531 $message=sprintf(_('Your project registration for %4$s has been approved.
2533 Project Full Name: %1$s
2534 Project Unix Name: %2$s
2536 Your DNS will take up to a day to become active on our site.
2537 Your web site is accessible through your shell account. Please read
2538 site documentation (see link below) about intended usage, available
2539 services, and directory layout of the account.
2542 own project page in %4$s while logged in, you will find
2543 additional menu functions to your left labeled \'Project Admin\'.
2545 We highly suggest that you now visit %4$s and create a public
2546 description for your project. This can be done by visiting your project
2547 page while logged in, and selecting \'Project Admin\' from the menus
2548 on the left (or by visiting %3$s
2551 Your project will also not appear in the Trove Software Map (primary
2552 list of projects hosted on %4$s which offers great flexibility in
2553 browsing and search) until you categorize it in the project administration
2554 screens. So that people can find your project, you should do this now.
2555 Visit your project while logged in, and select \'Project Admin\' from the
2558 Enjoy the system, and please tell others about %4$s. Let us know
2559 if there is anything we can do to help you.
2562 htmlspecialchars_decode($this->getPublicName()),
2563 $this->getUnixName(),
2564 util_make_url ('/project/admin/?group_id='.$this->getID()),
2565 forge_get_config ('forge_name'));
2567 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Approved'), forge_get_config ('forge_name')), $message);
2569 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 %3$s has been denied.
2603 Project Full Name: %1$s
2604 Project Unix Name: %2$s
2606 Reasons for negative decision:
2608 '), $this->getPublicName(), $this->getUnixName(), forge_get_config('forge_name'));
2610 // Check to see if they want to send a custom rejection response
2611 if ($response_id == 0) {
2612 $response .= $message;
2614 $response .= db_result(
2615 db_query_params('SELECT response_text FROM canned_responses WHERE response_id=$1', array ($response_id)),
2620 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Denied'), forge_get_config ('forge_name')), $response);
2621 setup_gettext_from_context();
2628 * sendNewProjectNotificationEmail - Send new project notification email.
2630 * This function sends out a notification email to the
2631 * SourceForge admin user when a new project is
2634 * @return boolean success.
2637 function sendNewProjectNotificationEmail() {
2638 // Get the user who wants to register the project
2639 $submitters = array();
2640 foreach (get_group_join_requests ($this) as $gjr) {
2641 $submitters[] = user_get_object($gjr->getUserID());
2643 if (count ($submitters) < 1) {
2644 $this->setError(_("Could not find user who has submitted the project."));
2648 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('approve_projects', -1) ;
2650 if (count($admins) < 1) {
2651 $this->setError(_("There is no administrator to send the mail to."));
2655 foreach ($admins as $admin) {
2656 $admin_email = $admin->getEmail () ;
2657 setup_gettext_for_user ($admin) ;
2659 $message = sprintf(_('New %1$s Project Submitted
2661 Project Full Name: %2$s
2662 Submitted Description: %3$s
2664 forge_get_config ('forge_name'),
2665 htmlspecialchars_decode($this->getPublicName()),
2666 htmlspecialchars_decode($this->getRegistrationPurpose()));
2668 foreach ($submitters as $submitter) {
2669 $message .= sprintf(_('Submitter: %1$s (%2$s)
2671 $submitter->getRealName(),
2672 $submitter->getUnixName());
2675 $message .= sprintf (_('
2676 Please visit the following URL to approve or reject this project:
2678 util_make_url ('/admin/approve-pending.php')) ;
2679 util_send_message($admin_email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2680 setup_gettext_from_context();
2684 $email = $submitter->getEmail() ;
2685 setup_gettext_for_user ($submitter) ;
2687 $message=sprintf(_('New %1$s Project Submitted
2689 Project Full Name: %2$s
2690 Submitted Description: %3$s
2692 The %1$s admin team will now examine your project submission. You will be notified of their decision.'), forge_get_config ('forge_name'), $this->getPublicName(), util_unconvert_htmlspecialchars($this->getRegistrationPurpose()), forge_get_config('web_host'));
2694 util_send_message($email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2695 setup_gettext_from_context();
2704 * validateGroupName - Validate the group name
2706 * @param string Group name.
2708 * @return boolean an error false and set an error is the group name is invalide otherwise return true
2710 function validateGroupName($group_name) {
2711 if (strlen($group_name)<3) {
2712 $this->setError(_('Group name is too short'));
2714 } elseif (strlen(htmlspecialchars($group_name))>50) {
2715 $this->setError(_('Group name is too long'));
2717 } elseif (group_get_object_by_publicname($group_name)) {
2718 $this->setError(_('Group name already taken'));
2726 * getRolesId - Get Ids of the roles of the group.
2728 * @return array Role ids of this group.
2730 function getRolesId() {
2731 $role_ids = array();
2733 $res = db_query_params('SELECT role_id FROM pfo_role WHERE home_group_id=$1',
2734 array($this->getID()));
2735 while ($arr = db_fetch_array($res)) {
2736 $role_ids[] = $arr['role_id'];
2738 $res = db_query_params('SELECT role_id FROM role_project_refs WHERE group_id=$1',
2739 array($this->getID()));
2740 while ($arr = db_fetch_array($res)) {
2741 $role_ids[] = $arr['role_id'];
2744 return array_unique($role_ids);
2748 * getRoles - Get the roles of the group.
2750 * @return array Roles of this group.
2752 function getRoles() {
2755 $roles = $this->getRolesId();
2756 $engine = RBACEngine::getInstance();
2757 foreach ($roles as $role_id) {
2758 $result[] = $engine->getRoleById ($role_id);
2764 function normalizeAllRoles() {
2765 $roles = $this->getRoles();
2767 foreach ($roles as $r) {
2768 $r->normalizeData();
2773 * getUnixStatus - Status of activation of unix account.
2775 * @return char (N)one, (A)ctive, (S)uspended or (D)eleted
2777 function getUnixStatus() {
2778 return $this->data_array['unix_status'];
2782 * setUnixStatus - Sets status of activation of unix account.
2784 * @param string $status The unix status.
2790 * @return boolean success.
2792 function setUnixStatus($status) {
2795 $res = db_query_params ('UPDATE groups SET unix_status=$1 WHERE group_id=$2',
2800 $this->setError(sprintf(_('Error: Cannot Update Group Unix Status: %s'),db_error()));
2804 if ($status == 'A') {
2805 if (!$SYS->sysCheckCreateGroup($this->getID())) {
2806 $this->setError($SYS->getErrorMessage());
2811 if ($SYS->sysCheckGroup($this->getID())) {
2812 if (!$SYS->sysRemoveGroup($this->getID())) {
2813 $this->setError($SYS->getErrorMessage());
2820 $this->data_array['unix_status']=$status;
2827 * getUsers - Get the users of a group
2829 * @return array of user's objects.
2831 function getUsers($onlylocal = true) {
2832 if (!isset($this->membersArr)) {
2833 $this->membersArr = array () ;
2836 foreach ($this->getRoles() as $role) {
2838 && ($role->getHomeProject() == NULL || $role->getHomeProject()->getID() != $this->getID())) {
2841 foreach ($role->getUsers() as $user) {
2842 $ids[] = $user->getID() ;
2845 $ids = array_unique ($ids) ;
2846 foreach ($ids as $id) {
2847 $u = user_get_object ($id) ;
2848 if ($u->isActive()) {
2849 $this->membersArr[] = $u ;
2853 return $this->membersArr;
2856 function setDocmanCreateOnlineStatus($status) {
2858 /* if we activate search engine, we probably want to reindex */
2859 $res = db_query_params('UPDATE groups SET use_docman_create_online=$1 WHERE group_id=$2',
2860 array($status, $this->getID()));
2863 $this->setError(sprintf(_('Error: Cannot Update Group DocmanCreateOnline Status: %s'),db_error()));
2867 $this->data_array['use_docman_create_online']=$status;
2873 function setDocmanWebdav($status) {
2875 /* if we activate search engine, we probably want to reindex */
2876 $res = db_query_params('UPDATE groups SET use_webdav=$1 WHERE group_id=$2',
2881 $this->setError(sprintf(_('Error: Cannot Update Group UseWebdab Status: %s'),db_error()));
2885 $this->data_array['use_webdav']=$status;
2891 function setDocmanSearchStatus($status) {
2893 /* if we activate search engine, we probably want to reindex */
2894 $res = db_query_params('UPDATE groups SET use_docman_search=$1, force_docman_reindex=$1 WHERE group_id=$2',
2899 $this->setError(sprintf(_('Error: Cannot Update Group UseDocmanSearch Status: %s'),db_error()));
2903 $this->data_array['use_docman_search']=$status;
2909 function setDocmanForceReindexSearch($status) {
2911 /* if we activate search engine, we probably want to reindex */
2912 $res = db_query_params('UPDATE groups SET force_docman_reindex=$1 WHERE group_id=$2',
2917 $this->setError(sprintf(_('Error: Cannot Update Group force_docman_reindex %s'),db_error()));
2921 $this->data_array['force_docman_reindex']=$status;
2930 * group_getname() - get the group name
2932 * @param int The group ID
2936 function group_getname ($group_id = 0) {
2937 $grp = group_get_object($group_id);
2939 return $grp->getPublicName();
2946 * group_getunixname() - get the unixname for a group
2948 * @param int The group ID
2952 function group_getunixname ($group_id) {
2953 $grp = group_get_object($group_id);
2955 return $grp->getUnixName();
2962 * group_get_result() - Get the group object result ID.
2964 * @param int The group ID
2968 function &group_get_result($group_id=0) {
2969 $grp = group_get_object($group_id);
2971 return $grp->getData();
2977 function getAllProjectTags($onlyvisible = true) {
2978 $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',
2981 if (!$res || db_numrows($res) == 0) {
2987 while ($arr = db_fetch_array($res)) {
2989 $group_id = $arr[1];
2990 if (!isset($result[$tag])) {
2991 $result[$tag] = array();
2994 if (!$onlyvisible || forge_check_perm('project_read', $group_id)) {
2995 $p = group_get_object($group_id);
2996 $result[$tag][] = array('unix_group_name' => $p->getUnixName(),
2997 'group_id' => $group_id);
3005 * Utility class to compare project based in various criteria (names, unixnames, id, ...)
3008 class ProjectComparator {
3009 var $criterion = 'name' ;
3011 function Compare ($a, $b) {
3012 switch ($this->criterion) {
3015 $namecmp = strcoll ($a->getPublicName(), $b->getPublicName()) ;
3016 if ($namecmp != 0) {
3019 /* If several projects share a same public name */
3020 return strcoll ($a->getUnixName(), $b->getUnixName()) ;
3023 return strcmp ($a->getUnixName(), $b->getUnixName()) ;
3026 $aid = $a->getID() ;
3027 $bid = $b->getID() ;
3031 return ($a < $b) ? -1 : 1;
3037 function sortProjectList (&$list, $criterion='name') {
3038 $cmp = new ProjectComparator () ;
3039 $cmp->criterion = $criterion ;
3041 return usort ($list, array ($cmp, 'Compare')) ;
3046 // c-file-style: "bsd"