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 $max_query_limit Optional Maximum number of rows to limit query length
164 * @return array List of public active projects
166 function get_public_active_projects_asc($max_query_limit = -1) {
168 $res_grp = db_query_params ('
169 SELECT group_id, group_name, unix_group_name, short_description, register_time
171 WHERE status = $1 AND type_id=1 AND is_template=0 AND register_time > 0
172 ORDER BY group_name ASC
177 while ($row_grp = db_fetch_array($res_grp)) {
178 if (!forge_check_perm ('project_read', $row_grp['group_id'])) {
181 $projects[] = $row_grp;
187 class Group extends Error {
189 * Associative array of data from db.
191 * @var array $data_array.
196 * array of User objects.
198 * @var array $membersArr.
203 * Whether the use is an admin/super user of this project.
205 * @var bool $is_admin.
210 * Artifact types result handle.
212 * @var int $types_res.
217 * Associative array of data for plugins.
219 * @var array $plugins_data.
225 * Associative array of data for the group menu.
227 * @var array $menu_data.
232 * Group - Group object constructor - use group_get_object() to instantiate.
234 * @param int|bool $id Required - Id of the group you want to instantiate.
235 * @param int|bool $res Database result from select query OR associative array of all columns.
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 $user The User object.
293 * @param string $group_name The full name of the user.
294 * @param string $unix_name The Unix name of the user.
295 * @param string $description The new group description.
296 * @param string $purpose The purpose of the group.
297 * @param string $unix_box
298 * @param string $scm_box
299 * @param bool $is_public
300 * @param bool $send_mail Whether to send an email or not
301 * @param int $built_from_template The id of the project this new project is based on
302 * @return boolean success or not
304 function create(&$user, $group_name, $unix_name, $description, $purpose, $unix_box = 'shell1',
305 $scm_box = 'cvs1', $is_public = true, $send_mail = true, $built_from_template = 0) {
306 // $user is ignored - anyone can create pending group
309 if ($this->getID()!=0) {
310 $this->setError(_('Group object already exists.'));
312 } elseif (!$this->validateGroupName($group_name)) {
314 } elseif (!account_groupnamevalid($unix_name)) {
315 $this->setError(_('Invalid Unix Name.'));
317 } elseif (!$SYS->sysUseUnixName($unix_name)) {
318 $this->setError(_('Unix name already taken.'));
320 } elseif (db_numrows(db_query_params('SELECT group_id FROM groups WHERE unix_group_name=$1',
321 array($unix_name))) > 0) {
322 $this->setError(_('Unix name already taken.'));
324 } elseif (strlen($purpose)<10) {
325 $this->setError(_('Please describe your Registration Project Purpose and Summarization in a more comprehensive manner.'));
327 } elseif (strlen($purpose)>1500) {
328 $this->setError(_('The Registration Project Purpose and Summarization text is too long. Please make it smaller than 1500 characters.'));
330 } elseif (strlen($description)<10) {
331 $this->setError(_('Describe in a more comprehensive manner your project.'));
335 // Check if sys_use_project_vhost for homepage
336 if (forge_get_config('use_project_vhost')) {
337 $homepage = $unix_name.".".forge_get_config('web_host');
339 $homepage = forge_get_config('web_host')."/www/".$unix_name."/";
344 $res = db_query_params('
359 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)',
360 array(htmlspecialchars($group_name),
362 htmlspecialchars($description),
368 htmlspecialchars($purpose),
370 md5(util_randbytes()),
371 $built_from_template));
372 if (!$res || db_affected_rows($res) < 1) {
373 $this->setError(sprintf(_('Error: Cannot create group: %s'),db_error()));
378 $id = db_insertid($res, 'groups', 'group_id');
380 $this->setError(sprintf(_('Error: Cannot get group id: %s'),db_error()));
385 if (!$this->fetchData($id)) {
390 $gjr = new GroupJoinRequest($this);
391 $gjr->create($user->getID(),
392 'Fake GroupJoinRequest to store the creator of a project',
395 $hook_params = array();
396 $hook_params['group'] = $this;
397 $hook_params['group_id'] = $this->getID();
398 $hook_params['group_name'] = $group_name;
399 $hook_params['unix_group_name'] = $unix_name;
400 plugin_hook("group_create", $hook_params);
404 $this->sendNewProjectNotificationEmail();
412 * updateAdmin - Update core properties of group object.
414 * This function require site admin privilege.
416 * @param object $user User requesting operation (for access control).
417 * @param int $type_id Group type (1-project, 2-foundry).
418 * @param string $unix_box Machine on which group's home directory located.
419 * @param string $http_domain Domain which serves group's WWW.
420 * @return bool status.
423 function updateAdmin(&$user, $type_id, $unix_box, $http_domain) {
424 $perm =& $this->getPermission();
426 if (!$perm || !is_object($perm)) {
427 $this->setError(_('Could not get permission.'));
431 if (!$perm->isSuperUser()) {
432 $this->setError(_('Permission denied.'));
438 $res = db_query_params('
440 SET type_id=$1, unix_box=$2, http_domain=$3
447 if (!$res || db_affected_rows($res) < 1) {
448 $this->setError(_('Error: Cannot change group properties: %s'),db_error());
453 // Log the audit trail
454 if ($type_id != $this->data_array['type_id']) {
455 $this->addHistory('type_id', $this->data_array['type_id']);
457 if ($unix_box != $this->data_array['unix_box']) {
458 $this->addHistory('unix_box', $this->data_array['unix_box']);
460 if ($http_domain != $this->data_array['http_domain']) {
461 $this->addHistory('http_domain', $this->data_array['http_domain']);
464 if (!$this->fetchData($this->getID())) {
473 * update - Update number of common properties.
475 * Unlike updateAdmin(), this function accessible to project admin.
477 * @param object $user User requesting operation (for access control).
478 * @param string $group_name
479 * @param string $homepage
480 * @param string $short_description
481 * @param bool $use_mail
482 * @param bool $use_survey
483 * @param bool $use_forum
484 * @param bool $use_pm
485 * @param bool $use_pm_depend_box
486 * @param bool $use_scm
487 * @param bool $use_news
488 * @param bool $use_docman
489 * @param string $new_doc_address
490 * @param bool $send_all_docs
491 * @param int $logo_image_id
492 * @param bool $use_ftp
493 * @param bool $use_tracker
494 * @param bool $use_frs
495 * @param bool $use_stats
497 * @param bool $use_activity
498 * @param bool $is_public group is publicly accessible
499 * @return int status.
502 function update(&$user, $group_name, $homepage, $short_description, $use_mail, $use_survey, $use_forum,
503 $use_pm, $use_pm_depend_box, $use_scm, $use_news, $use_docman,
504 $new_doc_address, $send_all_docs, $logo_image_id,
505 $use_ftp, $use_tracker, $use_frs, $use_stats, $tags, $use_activity, $is_public) {
507 $perm =& $this->getPermission();
509 if (!$perm || !is_object($perm)) {
510 $this->setError(_('Could not get permission.'));
514 if (!$perm->isAdmin()) {
515 $this->setError(_('Permission denied.'));
519 // Validate some values
520 if ($this->getPublicName() != $group_name) {
521 if (!$this->validateGroupName($group_name)) {
526 if ($new_doc_address) {
527 $invalid_mails = validate_emails($new_doc_address);
528 if (count($invalid_mails) > 0) {
529 $this->setError(sprintf(ngettext('New Doc Address Appeared Invalid: %s', 'New Doc Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
534 // in the database, these all default to '1',
535 // so we have to explicitly set 0
548 if (!$use_pm_depend_box) {
549 $use_pm_depend_box = 0;
572 if (!$use_activity) {
575 if (!$send_all_docs) {
579 $homepage = ltrim($homepage);
581 $homepage = util_make_url('/projects/' . $this->getUnixName() . '/');
584 if (strlen(htmlspecialchars($short_description))<10) {
585 $this->setError(_('Describe in a more comprehensive manner your project.'));
591 //XXX not yet actived logo_image_id='$logo_image_id',
592 $res = db_query_params('UPDATE groups
595 short_description=$3,
600 use_pm_depend_box=$8,
611 array(htmlspecialchars($group_name),
613 htmlspecialchars($short_description),
630 if (!$res || db_affected_rows($res) < 1) {
631 $this->setError(sprintf(_('Error updating project information: %s'), db_error()));
636 if (!$this->setUseDocman($use_docman)) {
637 $this->setError(sprintf(_('Error updating project information: use_docman %s'), db_error()));
642 if ($this->setTags($tags) === false) {
647 // Log the audit trail
648 $this->addHistory('Changed Public Info', '');
650 if (!$this->fetchData($this->getID())) {
655 $hook_params = array();
656 $hook_params['group'] = $this;
657 $hook_params['group_id'] = $this->getID();
658 $hook_params['group_homepage'] = $homepage;
659 $hook_params['group_name'] = htmlspecialchars($group_name);
660 $hook_params['group_description'] = htmlspecialchars($short_description);
661 $hook_params['group_ispublic'] = $is_public;
662 if (!plugin_hook("group_update", $hook_params)) {
663 if (!$this->isError()) {
664 $this->setError(_('Error updating project information in plugin_hook group_update'));
675 * getID - Simply return the group_id for this object.
677 * @return int group_id.
680 return $this->data_array['group_id'];
684 * getType() - Foundry, project, etc.
686 * @return int The type flag from the database.
689 return $this->data_array['type_id'];
694 * getStatus - the status code.
696 * Statuses char include I,H,A,D,P.
703 function getStatus() {
704 return $this->data_array['status'];
708 * setStatus - set the status code.
710 * Statuses include I,H,A,D,P.
717 * @param object $user User requesting operation (for access control).
718 * @param string $status Status value.
719 * @return boolean success.
722 function setStatus(&$user, $status) {
725 if (!forge_check_global_perm_for_user($user, 'approve_projects')) {
726 $this->setPermissionDeniedError();
730 // Projects in 'A' status can only go to 'H' or 'D'
731 // Projects in 'D' status can only go to 'A'
732 // Projects in 'P' status can only go to 'A' OR 'D'
733 // Projects in 'I' status can only go to 'P'
734 // Projects in 'H' status can only go to 'A' OR 'D'
735 $allowed_status_changes = array(
736 'AH'=>1,'AD'=>1,'DA'=>1,'PA'=>1,'PD'=>1,
737 'IP'=>1,'HA'=>1,'HD'=>1
740 // Check that status transition is valid
741 if ($this->getStatus() != $status
742 && !array_key_exists($this->getStatus(). $status, $allowed_status_changes)) {
743 $this->setError(_('Invalid Status Change From: ').$this->getStatus(). _(' To: '.$status));
749 $res = db_query_params('UPDATE groups
751 WHERE group_id=$2', array($status, $this->getID()));
753 if (!$res || db_affected_rows($res) < 1) {
754 $this->setError(sprintf(_('Error: Cannot change group status: %s'),db_error()));
760 // Activate system group, if not yet
761 if (!$SYS->sysCheckGroup($this->getID())) {
762 if (!$SYS->sysCreateGroup($this->getID())) {
763 $this->setError($SYS->getErrorMessage());
768 if (!$this->activateUsers()) {
773 /* Otherwise, the group is not active, and make sure that
774 System group is not active either */
775 } elseif ($SYS->sysCheckGroup($this->getID())) {
776 if (!$SYS->sysRemoveGroup($this->getID())) {
777 $this->setError($SYS->getErrorMessage());
783 $hook_params = array();
784 $hook_params['group'] = $this;
785 $hook_params['group_id'] = $this->getID();
786 $hook_params['status'] = $status;
787 plugin_hook("group_setstatus", $hook_params);
791 // Log the audit trail
792 if ($status != $this->getStatus()) {
793 $this->addHistory(_('Status'), $this->getStatus());
796 $this->data_array['status'] = $status;
801 * isProject - Simple boolean test to see if it's a project or not.
803 * @return boolean is_project.
805 function isProject() {
806 if ($this->getType()==1) {
814 * isPublic - Wrapper around RBAC to check if a project is anonymously readable
816 * @return boolean is_public.
818 function isPublic() {
819 $ra = RoleAnonymous::getInstance();
820 return $ra->hasPermission('project_read', $this->getID());
824 * isActive - Database field status of 'A' returns true.
826 * @return boolean is_active.
828 function isActive() {
829 if ($this->getStatus()=='A') {
837 * isTemplate - Simply returns the is_template flag from the database.
839 * @return boolean is_template.
841 function isTemplate() {
842 return $this->data_array['is_template'];
846 * setAsTemplate - Set the template status of a project
848 * @param boolean $booleanparam is_template.
851 function setAsTemplate($booleanparam) {
853 $booleanparam = $booleanparam ? 1 : 0;
854 $res = db_query_params('UPDATE groups SET is_template=$1 WHERE group_id=$2',
855 array($booleanparam, $this->getID()));
857 $this->data_array['is_template']=$booleanparam;
867 * getTemplateProject - Return the project template this project is built from
869 * @return object The template project
871 function getTemplateProject() {
872 return group_get_object($this->data_array['built_from_template']);
876 * getUnixName - the unix_name
878 * @return string unix_name.
880 function getUnixName() {
881 return strtolower($this->data_array['unix_group_name']);
885 * getPublicName - the full-length public name.
887 * @return string The group_name.
889 function getPublicName() {
890 return $this->data_array['group_name'];
894 * getRegisterPurpose - the text description of the purpose of this project.
896 * @return string The description.
898 function getRegisterPurpose() {
899 return $this->data_array['register_purpose'];
903 * getDescription - the text description of this project.
905 * @return string The description.
907 function getDescription() {
908 return $this->data_array['short_description'];
912 * getStartDate - the unix time this project was registered.
914 * @return int (unix time) of registration.
916 function getStartDate() {
917 return $this->data_array['register_time'];
921 * getLogoImageID - the id of the logo in the database for this project.
923 * @return int The ID of logo image in db_images table (or 100 if none).
925 function getLogoImageID() {
926 return $this->data_array['logo_image_id'];
930 * getUnixBox - the hostname of the unix box where this project is located.
932 * @return string The name of the unix machine for the group.
934 function getUnixBox() {
935 return $this->data_array['unix_box'];
939 * getSCMBox - the hostname of the scm box where this project is located.
941 * @return string The name of the unix machine for the group.
943 function getSCMBox() {
944 return $this->data_array['scm_box'];
947 * setSCMBox - the hostname of the scm box where this project is located.
949 * @param string $scm_box The name of the new SCM_BOX
952 function setSCMBox($scm_box) {
954 if ($scm_box == $this->data_array['scm_box']) {
959 $res = db_query_params('UPDATE groups SET scm_box=$1 WHERE group_id=$2', array($scm_box, $this->getID()));
961 $this->addHistory('scm_box', $this->data_array['scm_box']);
962 $this->data_array['scm_box'] = $scm_box;
967 $this->setError(_("Could not insert SCM_BOX to database"));
971 $this->setError(_("SCM Box cannot be empty"));
977 * getDomain - the hostname.domain where their web page is located.
979 * @return string The name of the group [web] domain.
981 function getDomain() {
982 return $this->data_array['http_domain'];
986 * getRegistrationPurpose - the text description of the purpose of this project.
988 * @return string The application for project hosting.
990 function getRegistrationPurpose() {
991 return $this->data_array['register_purpose'];
995 * getAdmins() - Get array of Admin user objects.
997 * @return array Array of User objects.
999 function &getAdmins() {
1000 $roles = RBACEngine::getInstance()->getRolesByAllowedAction ('project_admin', $this->getID());
1002 $user_ids = array();
1004 foreach ($roles as $role) {
1005 if (! ($role instanceof RoleExplicit)) {
1008 if ($role->getHomeProject() == NULL
1009 || $role->getHomeProject()->getID() != $this->getID()) {
1013 foreach ($role->getUsers() as $u) {
1014 $user_ids[] = $u->getID();
1017 return user_get_objects(array_unique($user_ids));
1021 Common Group preferences for tools
1025 * enableAnonSCM - whether or not this group has opted to enable Anonymous SCM.
1027 * @return boolean enable_scm.
1029 function enableAnonSCM() {
1030 $r = RoleAnonymous::getInstance();
1031 return $r->hasPermission('scm', $this->getID(), 'read');
1034 function SetUsesAnonSCM($booleanparam) {
1036 $booleanparam = $booleanparam ? 1 : 0;
1037 $r = RoleAnonymous::getInstance();
1038 $r->setSetting('scm', $this->getID(), $booleanparam);
1043 * enablePserver - whether or not this group has opted to enable Pserver.
1045 * @return boolean enable_pserver.
1047 function enablePserver() {
1048 if ($this->usesSCM()) {
1049 return $this->data_array['enable_pserver'];
1055 function SetUsesPserver($booleanparam) {
1057 $booleanparam = $booleanparam ? 1 : 0;
1058 $res = db_query_params('UPDATE groups SET enable_pserver=$1 WHERE group_id=$2',
1059 array($booleanparam, $this->getID()));
1061 $this->data_array['enable_pserver'] = $booleanparam;
1071 * usesSCM - whether or not this group has opted to use SCM.
1073 * @return boolean uses_scm.
1075 function usesSCM() {
1076 if (forge_get_config('use_scm')) {
1077 return $this->data_array['use_scm'];
1084 * setUseSCM - Set the SCM usage
1086 * @param boolean $booleanparam enabled/disabled
1089 function setUseSCM($booleanparam) {
1091 $booleanparam = $booleanparam ? 1 : 0;
1092 $res = db_query_params('UPDATE groups SET use_scm=$1 WHERE group_id=$2',
1093 array($booleanparam, $this->getID()));
1095 $this->data_array['use_scm']=$booleanparam;
1105 * usesMail - whether or not this group has opted to use mailing lists.
1107 * @return boolean uses_mail.
1109 function usesMail() {
1110 if (forge_get_config('use_mail')) {
1111 return $this->data_array['use_mail'];
1116 $hook_params = array();
1117 $hook_params['group'] = $this;
1118 $hook_params['group_id'] = $this->getID();
1119 $hook_params['group_homepage'] = $this->getHomePage();
1120 $hook_params['group_name'] = $this->getPublicName();
1121 $hook_params['group_description'] = $this->getDescription();
1122 plugin_hook ("group_update", $hook_params);
1126 * setUseMail - Set the mailing-list usage
1128 * @param boolean $booleanparam enabled/disabled
1131 function setUseMail($booleanparam) {
1133 $booleanparam = $booleanparam ? 1 : 0;
1134 $res = db_query_params('UPDATE groups SET use_mail=$1 WHERE group_id=$2',
1135 array($booleanparam, $this->getID()));
1137 $this->data_array['use_mail']=$booleanparam;
1147 * usesNews - whether or not this group has opted to use news.
1149 * @return boolean uses_news.
1151 function usesNews() {
1152 if (forge_get_config('use_news')) {
1153 return $this->data_array['use_news'];
1160 * usesActivity - whether or not this group has opted to display Project Activities.
1162 * @return boolean uses_activities.
1164 function usesActivity() {
1165 if (forge_get_config('use_activity')) {
1166 return $this->data_array['use_activity'];
1173 * usesForum - whether or not this group has opted to use discussion forums.
1175 * @return boolean uses_forum.
1177 function usesForum() {
1178 if (forge_get_config('use_forum')) {
1179 return $this->data_array['use_forum'];
1186 * setUseForum - Set the forum usage
1188 * @param boolean $booleanparam enabled/disabled
1191 function setUseForum($booleanparam) {
1193 $booleanparam = $booleanparam ? 1 : 0;
1194 $res = db_query_params('UPDATE groups SET use_forum=$1 WHERE group_id=$2',
1195 array($booleanparam, $this->getID()));
1197 $this->data_array['use_forum']=$booleanparam;
1207 * usesStats - whether or not this group has opted to use stats.
1209 * @return boolean uses_stats.
1211 function usesStats() {
1212 return $this->data_array['use_stats'];
1216 * usesFRS - whether or not this group has opted to use file release system.
1218 * @return boolean uses_frs.
1220 function usesFRS() {
1221 if (forge_get_config('use_frs')) {
1222 return $this->data_array['use_frs'];
1229 * setUseFRS - Set the FRS usage
1231 * @param boolean $booleanparam enabled/disabled
1234 function setUseFRS($booleanparam) {
1236 $booleanparam = $booleanparam ? 1 : 0;
1237 $res = db_query_params('UPDATE groups SET use_frs=$1 WHERE group_id=$2',
1238 array($booleanparam, $this->getID()));
1240 $this->data_array['use_frs']=$booleanparam;
1250 * usesTracker - whether or not this group has opted to use tracker.
1252 * @return boolean uses_tracker.
1254 function usesTracker() {
1255 if (forge_get_config('use_tracker')) {
1256 return $this->data_array['use_tracker'];
1263 * setUseTracker - Set the tracker usage
1265 * @param boolean $booleanparam enabled/disabled
1268 function setUseTracker ($booleanparam) {
1270 $booleanparam = $booleanparam ? 1 : 0;
1271 $res = db_query_params ('UPDATE groups SET use_tracker=$1 WHERE group_id=$2',
1272 array($booleanparam, $this->getID()));
1274 $this->data_array['use_tracker']=$booleanparam;
1284 * useCreateOnline - whether or not this group has opted to use create online documents option.
1286 * @return boolean use_docman_create_online.
1288 function useCreateOnline() {
1289 if (forge_get_config('use_docman')) {
1290 return $this->data_array['use_docman_create_online'];
1297 * usesDocman - whether or not this group has opted to use docman.
1299 * @return boolean use_docman.
1301 function usesDocman() {
1302 if (forge_get_config('use_docman')) {
1303 return $this->data_array['use_docman'];
1310 * setUseDocman - Set the docman usage
1312 * @param boolean $booleanparam enabled/disabled
1315 function setUseDocman($booleanparam) {
1317 $booleanparam = $booleanparam ? 1 : 0;
1318 $res = db_query_params('UPDATE groups SET use_docman = $1 WHERE group_id = $2',
1319 array($booleanparam, $this->getID()));
1321 // check if / doc_group exists, if not create it
1322 $trashdir = db_query_params('select groupname from doc_groups where groupname = $1 and group_id = $2',
1323 array('.trash', $this->getID()));
1324 if ($trashdir && db_numrows($trashdir) == 0) {
1325 $resinsert = db_query_params('insert into doc_groups (groupname, group_id, stateid) values ($1, $2, $3)',
1326 array('.trash', $this->getID(), '2'));
1332 $this->data_array['use_docman'] = $booleanparam;
1342 * useDocmanSearch - whether or not this group has opted to use docman search engine.
1344 * @return boolean use_docman_search.
1346 function useDocmanSearch() {
1347 if (forge_get_config('use_docman')) {
1348 return $this->data_array['use_docman_search'];
1355 * useWebdav - whether or not this group has opted to use webdav interface.
1357 * @return boolean use_docman_search.
1359 function useWebdav() {
1360 if (forge_get_config('use_webdav')) {
1361 return $this->data_array['use_webdav'];
1368 * usesFTP - whether or not this group has opted to use FTP.
1370 * @return boolean uses_ftp.
1372 function usesFTP() {
1373 if (forge_get_config('use_ftp')) {
1374 return $this->data_array['use_ftp'];
1381 * usesSurvey - whether or not this group has opted to use surveys.
1383 * @return boolean uses_survey.
1385 function usesSurvey() {
1386 if (forge_get_config('use_survey')) {
1387 return $this->data_array['use_survey'];
1394 * usesPM - whether or not this group has opted to Project Manager.
1396 * @return boolean uses_projman.
1399 if (forge_get_config('use_pm')) {
1400 return $this->data_array['use_pm'];
1407 * setUsePM - Set the PM usage
1409 * @param boolean $booleanparam enabled/disabled
1412 function setUsePM($booleanparam) {
1414 $booleanparam = $booleanparam ? 1 : 0;
1415 $res = db_query_params('UPDATE groups SET use_pm=$1 WHERE group_id=$2',
1416 array($booleanparam, $this->getID()));
1418 $this->data_array['use_pm']=$booleanparam;
1428 * getPlugins - get a list of all available group plugins
1430 * @return array array containing plugin_id => plugin_name
1432 function getPlugins() {
1433 if (!isset($this->plugins_data)) {
1434 $this->plugins_data = array();
1435 $res = db_query_params('SELECT group_plugin.plugin_id, plugins.plugin_name
1436 FROM group_plugin, plugins
1437 WHERE group_plugin.group_id=$1
1438 AND group_plugin.plugin_id=plugins.plugin_id', array($this->getID()));
1439 $rows = db_numrows($res);
1441 for ($i=0; $i<$rows; $i++) {
1442 $plugin_id = db_result($res, $i, 'plugin_id');
1443 $this->plugins_data[$plugin_id] = db_result($res, $i, 'plugin_name');
1446 return $this->plugins_data;
1450 * usesPlugin - returns true if the group uses a particular plugin
1452 * @param string $pluginname name of the plugin
1453 * @return boolean whether plugin is being used or not
1455 function usesPlugin($pluginname) {
1456 $plugins_data = $this->getPlugins();
1457 foreach ($plugins_data as $p_id => $p_name) {
1458 if ($p_name == $pluginname) {
1466 * added for Codendi compatibility
1467 * usesServices - returns true if the group uses a particular plugin or feature
1469 * @param string $feature name of the plugin
1470 * @return boolean whether plugin is being used or not
1472 function usesService($feature) {
1473 $plugins_data = $this->getPlugins();
1474 $pm = plugin_manager_get_object();
1475 foreach ($plugins_data as $p_id => $p_name) {
1476 if ($p_name == $feature) {
1479 if ($pm->getPluginByName($p_name)->provide($feature)) {
1487 * setPluginUse - enables/disables plugins for the group
1489 * @param string $pluginname name of the plugin
1490 * @param boolean $val the new state
1491 * @return string database result
1493 function setPluginUse($pluginname, $val=true) {
1494 if ($val == $this->usesPlugin($pluginname)) {
1495 // State is already good, returning
1498 $res = db_query_params('SELECT plugin_id FROM plugins WHERE plugin_name=$1',
1499 array($pluginname));
1500 $rows = db_numrows($res);
1502 // Error: no plugin by that name
1505 $plugin_id = db_result($res,0,'plugin_id');
1507 unset($this->plugins_data);
1509 $res = db_query_params('INSERT INTO group_plugin (group_id, plugin_id) VALUES ($1, $2)',
1510 array($this->getID(),
1514 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1 AND plugin_id=$2',
1515 array($this->getID(),
1519 $this->normalizeAllRoles();
1523 * getDocEmailAddress - get email address(es) to send doc notifications to.
1525 * @return string email address.
1527 function getDocEmailAddress() {
1528 return $this->data_array['new_doc_address'];
1532 * DocEmailAll - whether or not this group has opted to use receive notices on all doc updates.
1534 * @return boolean email_on_all_doc_updates.
1536 function docEmailAll() {
1537 return $this->data_array['send_all_docs'];
1541 * getHomePage - The URL for this project's home page.
1543 * @return string homepage URL.
1545 function getHomePage() {
1546 if (!preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*:/",
1547 $this->data_array['homepage'])) {
1548 $this->data_array['homepage'] = util_url_prefix() .
1549 $this->data_array['homepage'];
1551 return $this->data_array['homepage'];
1555 * getTags - Tags of this project.
1557 * @return string List of tags. Comma separated
1559 function getTags() {
1560 $sql = 'SELECT name FROM project_tags WHERE group_id = $1';
1561 $res = db_query_params($sql, array($this->getID()));
1562 return join(', ', util_result_column_to_array($res));
1566 * setTags - Set tags of this project.
1568 * @param string $tags
1569 * @return string database result.
1571 function setTags($tags) {
1573 $sql = 'DELETE FROM project_tags WHERE group_id=$1';
1574 $res = db_query_params($sql, array($this->getID()));
1576 $this->setError('Deleting old tags: '.db_error());
1580 $inserted = array();
1581 $tags_array = preg_split('/[;,]/', $tags);
1582 foreach ($tags_array as $tag) {
1583 $tag = preg_replace('/[\t\r\n]/', ' ', $tag);
1584 // Allowed caracteres: [A-Z][a-z][0-9] -_&'#+.
1585 if (preg_match('/[^[:alnum:]| |\-|_|\&|\'|#|\+|\.]/', $tag)) {
1586 $this->setError(_('Bad tag name, you only can use the following characters: [A-Z][a-z][0-9]-_&\'#+. and space'));
1591 if ($tag == '' || array_search($tag, $inserted) !== false) continue;
1592 $sql = 'INSERT INTO project_tags (group_id,name) VALUES ($1, $2)';
1593 $res = db_query_params($sql, array($this->getID(), $tag));
1595 $this->setError(_('Setting tags:') . ' ' .
1607 * getPermission - Return a Permission for this Group
1609 * @return object The Permission.
1611 function &getPermission() {
1612 return permission_get_object($this);
1615 function delete($sure, $really_sure, $really_really_sure) {
1616 if (!$sure || !$really_sure || !$really_really_sure) {
1617 $this->setMissingParamsError(_('Please tick all checkboxes.'));
1620 if ($this->getID() == forge_get_config('news_group') ||
1621 $this->getID() == 1 ||
1622 $this->getID() == forge_get_config('stats_group') ||
1623 $this->getID() == forge_get_config('peer_rating_group')) {
1624 $this->setError(_('Cannot Delete System Group'));
1627 $perm = $this->getPermission();
1628 if (!$perm || !is_object($perm)) {
1629 $this->setPermissionDeniedError();
1631 } elseif ($perm->isError()) {
1632 $this->setPermissionDeniedError();
1634 } elseif (!$perm->isSuperUser()) {
1635 $this->setPermissionDeniedError();
1641 // Remove all the members
1643 $members = $this->getMembers();
1644 foreach ($members as $i) {
1645 if(!$this->removeUser($i->getID())) {
1646 $this->setError(_('Could not properly remove member:').' '.$i->getID());
1651 // unlink roles from this project
1652 foreach ($this->getRoles() as $r) {
1653 if ($r->getHomeProject() == NULL
1654 || $r->getHomeProject()->getID() != $this->getID()) {
1655 $r->unlinkProject($this);
1662 if ($this->usesTracker()) {
1663 $atf = new ArtifactTypeFactory($this);
1664 $at_arr = $atf->getArtifactTypes();
1665 foreach ($at_arr as $i) {
1666 if (!is_object($i)) {
1669 if (!$i->delete(1,1)) {
1670 $this->setError(_('Could not properly delete the tracker:').' '.$i->getErrorMessage());
1679 if ($this->usesForum()) {
1680 $ff = new ForumFactory($this);
1681 $f_arr = $ff->getForums();
1682 foreach ($f_arr as $i) {
1683 if (!is_object($i)) {
1686 if(!$i->delete(1,1)) {
1687 $this->setError(_('Could not properly delete the forum:').' '.$i->getErrorMessage());
1693 // Delete Subprojects
1695 if ($this->usesPM()) {
1696 $pgf = new ProjectGroupFactory($this);
1697 $pg_arr = $pgf->getProjectGroups();
1698 foreach ($pg_arr as $i) {
1699 if (!is_object($i)) {
1702 if (!$i->delete(1,1)) {
1703 $this->setError(_('Could not properly delete the ProjectGroup:').' '.$i->getErrorMessage());
1709 // Delete FRS Packages
1711 $res = db_query_params('SELECT * FROM frs_package WHERE group_id=$1',
1712 array($this->getID()));
1714 $this->setError(_('Error FRS Packages: ').db_error());
1719 while ($arr = db_fetch_array($res)) {
1720 $frsp=new FRSPackage($this, $arr['package_id'], $arr);
1721 if (!$frsp->delete(1, 1)) {
1722 $this->setError(_('Could not properly delete the FRSPackage:').' '.$frsp->getErrorMessage());
1729 $news_group=group_get_object(forge_get_config('news_group'));
1730 $res = db_query_params('SELECT forum_id FROM news_bytes WHERE group_id=$1',
1731 array($this->getID()));
1733 $this->setError(_('Error Deleting News: ').db_error());
1738 for ($i=0; $i<db_numrows($res); $i++) {
1739 $Forum = new Forum($news_group,db_result($res,$i,'forum_id'));
1740 if (!$Forum->delete(1,1)) {
1741 $this->setError(_("Could Not Delete News Forum: %d"),$Forum->getID());
1745 $res = db_query_params('DELETE FROM news_bytes WHERE group_id=$1',
1746 array($this->getID()));
1748 $this->setError(_('Error Deleting News: ').db_error());
1756 $res = db_query_params('DELETE FROM doc_data WHERE group_id=$1',
1757 array($this->getID()));
1759 $this->setError(_('Error Deleting Documents: ').db_error());
1764 $res = db_query_params('DELETE FROM doc_groups WHERE group_id=$1',
1765 array($this->getID()));
1767 $this->setError(_('Error Deleting Documents: ').db_error());
1775 $res=db_query_params('DELETE FROM project_tags WHERE group_id=$1', array($this->getID()));
1777 $this->setError(_('Error Deleting Tags: ').db_error());
1783 // Delete group history
1785 $res = db_query_params('DELETE FROM group_history WHERE group_id=$1',
1786 array($this->getID()));
1788 $this->setError(_('Error Deleting Project History: ').db_error());
1794 // Delete group plugins
1796 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1',
1797 array($this->getID()));
1799 $this->setError(_('Error Deleting Project Plugins: ').db_error());
1805 // Delete group cvs stats
1807 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE group_id=$1',
1808 array($this->getID()));
1810 $this->setError(_('Error Deleting SCM Statistics: ').db_error());
1818 if ($this->usesSurvey()) {
1819 $sf = new SurveyFactory($this);
1820 $s_arr =& $sf->getSurveys();
1821 foreach ($s_arr as $i) {
1822 if (!is_object($i)) {
1825 if (!$i->delete()) {
1826 $this->setError(_('Could not properly delete the survey'));
1832 // Delete SurveyQuestions
1834 $sqf = new SurveyQuestionFactory($this);
1835 $sq_arr = $sqf->getSurveyQuestions();
1836 if (is_array($sq_arr)) {
1837 foreach ($sq_arr as $i) {
1838 if (!is_object($i)) {
1841 if (!$i->delete()) {
1842 $this->setError(_('Could not properly delete the survey questions'));
1850 // Delete Mailing List Factory
1852 if ($this->usesMail()) {
1853 $mlf = new MailingListFactory($this);
1854 $ml_arr = $mlf->getMailingLists();
1855 foreach ($ml_arr as $i) {
1856 if (!is_object($i)) {
1859 if (!$i->delete(1,1)) {
1860 $this->setError(_('Could not properly delete the mailing list'));
1869 $res = db_query_params('DELETE FROM trove_group_link WHERE group_id=$1',
1870 array($this->getID()));
1872 $this->setError(_('Error Deleting Trove: ').db_error());
1877 $res = db_query_params('DELETE FROM trove_agg WHERE group_id=$1',
1878 array($this->getID()));
1880 $this->setError(_('Error Deleting Trove: ').db_error());
1888 $res = db_query_params('DELETE FROM project_sums_agg WHERE group_id=$1',
1889 array($this->getID()));
1891 $this->setError(_('Error Deleting Counters: ').db_error());
1896 $res = db_query_params('INSERT INTO deleted_groups (unix_group_name, delete_date, isdeleted) VALUES ($1, $2, $3)',
1897 array($this->getUnixName(),
1901 $this->setError(_('Error Deleting Project:').' '.db_error());
1906 // Delete entry in groups.
1907 $res = db_query_params('DELETE FROM groups WHERE group_id=$1',
1908 array($this->getID()));
1910 $this->setError(_('Error Deleting Project:').' '.db_error());
1917 $hook_params = array();
1918 $hook_params['group'] = $this;
1919 $hook_params['group_id'] = $this->getID();
1920 plugin_hook("group_delete", $hook_params);
1922 if (forge_get_config('upload_dir') != '' && $this->getUnixName()) {
1923 exec('/bin/rm -rf '.forge_get_config('upload_dir').'/'.$this->getUnixName().'/');
1925 if (forge_get_config('ftp_upload_dir') != '' && $this->getUnixName()) {
1926 exec('/bin/rm -rf '.forge_get_config('ftp_upload_dir').'/'.$this->getUnixName().'/');
1931 db_query_params('DELETE FROM rep_group_act_monthly WHERE group_id=$1',
1932 array($this->getID()));
1933 //echo 'rep_group_act_monthly'.db_error();
1934 db_query_params('DELETE FROM rep_group_act_weekly WHERE group_id=$1',
1935 array($this->getID()));
1936 //echo 'rep_group_act_weekly'.db_error();
1937 db_query_params('DELETE FROM rep_group_act_daily WHERE group_id=$1',
1938 array($this->getID()));
1939 //echo 'rep_group_act_daily'.db_error();
1940 unset($this->data_array);
1945 Basic functions to add/remove users to/from a group
1946 and update their permissions
1950 * addUser - controls adding a user to a group.
1952 * @param string $user_identifier Unix name of the user to add OR integer user_id.
1953 * @param int $role_id The role_id this user should have.
1954 * @return boolean success.
1957 function addUser($user_identifier, $role_id) {
1960 Admins can add users to groups
1963 if (!forge_check_perm ('project_admin', $this->getID())) {
1964 $this->setPermissionDeniedError();
1970 get user id for this user's unix_name
1972 if (is_int ($user_identifier)) { // user_id or user_name
1973 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_id=$1', array($user_identifier));
1975 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_name=$1', array($user_identifier));
1977 if (db_numrows($res_newuser) > 0) {
1979 // make sure user is active
1981 if (db_result($res_newuser,0,'status') != 'A') {
1982 $this->setError(_('User is not active. Only active users can be added.'));
1988 // user was found - set new user_id var
1990 $user_id = db_result($res_newuser,0,'user_id');
1992 $role = new Role($this, $role_id);
1993 if (!$role || !is_object($role)) {
1994 $this->setError(_('Error Getting Role Object'));
1997 } elseif ($role->isError()) {
1998 $this->setError('addUser::roleget::'.$role->getErrorMessage());
2003 $role->addUser(user_get_object($user_id));
2004 if (!$SYS->sysCheckCreateGroup($this->getID())){
2005 $this->setError($SYS->getErrorMessage());
2009 if (!$SYS->sysCheckCreateUser($user_id)) {
2010 $this->setError($SYS->getErrorMessage());
2014 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
2015 $this->setError($SYS->getErrorMessage());
2021 // user doesn't exist
2023 $this->setError(_('That user does not exist.'));
2028 $hook_params['group'] = $this;
2029 $hook_params['group_id'] = $this->getID();
2030 $hook_params['user'] = user_get_object($user_id);
2031 $hook_params['user_id'] = $user_id;
2032 plugin_hook ("group_adduser", $hook_params);
2037 $this->addHistory('Added User',$user_identifier);
2043 * removeUser - controls removing a user from a group.
2045 * Users can remove themselves.
2047 * @param int $user_id The ID of the user to remove.
2048 * @return boolean success.
2050 function removeUser($user_id) {
2053 if ($user_id != user_getid()
2054 && !forge_check_perm('project_admin', $this->getID())) {
2055 $this->setPermissionDeniedError();
2061 $user = user_get_object($user_id);
2062 $roles = RBACEngine::getInstance()->getAvailableRolesForUser($user);
2064 foreach ($roles as $role) {
2065 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2066 $found_role = $role;
2070 if ($found_role == NULL) {
2071 $this->setError(sprintf(_('Error: User not removed: %s')));
2075 $found_role->removeUser($user);
2076 if (!$SYS->sysGroupCheckUser($this->getID(), $user_id)) {
2077 $this->setError($SYS->getErrorMessage());
2083 // reassign open artifacts to id=100
2085 $res = db_query_params('UPDATE artifact SET assigned_to=100
2086 WHERE group_artifact_id
2087 IN (SELECT group_artifact_id
2088 FROM artifact_group_list
2089 WHERE group_id=$1 AND status_id=1 AND assigned_to=$2)',
2090 array($this->getID(),
2093 $this->setError(_('Error: artifact:').' '.db_error());
2099 // reassign open tasks to id=100
2100 // first have to purge any assignments that would cause
2101 // conflict with existing assignment to 100
2103 $res = db_query_params('DELETE FROM project_assigned_to
2104 WHERE project_task_id IN (SELECT pt.project_task_id
2105 FROM project_task pt, project_group_list pgl, project_assigned_to pat
2106 WHERE pt.group_project_id = pgl.group_project_id
2107 AND pat.project_task_id=pt.project_task_id
2108 AND pt.status_id=1 AND pgl.group_id=$1
2109 AND pat.assigned_to_id=$2)
2110 AND assigned_to_id=100',
2111 array($this->getID(),
2114 $this->setError(sprintf(_('Error: project_assigned_to %d: %s'), 1, db_error()));
2118 $res = db_query_params('UPDATE project_assigned_to SET assigned_to_id=100
2119 WHERE project_task_id IN (SELECT pt.project_task_id
2120 FROM project_task pt, project_group_list pgl
2121 WHERE pt.group_project_id = pgl.group_project_id
2122 AND pt.status_id=1 AND pgl.group_id=$1)
2123 AND assigned_to_id=$2',
2124 array($this->getID(),
2127 $this->setError(sprintf(_('Error: project_assigned_to %d: %s'), 2, db_error()));
2133 // Remove user from system
2135 if (!$SYS->sysGroupRemoveUser($this->getID(), $user_id)) {
2136 $this->setError($SYS->getErrorMessage());
2141 $hook_params['group'] = $this;
2142 $hook_params['group_id'] = $this->getID();
2143 $hook_params['user'] = user_get_object($user_id);
2144 $hook_params['user_id'] = $user_id;
2145 plugin_hook ("group_removeuser", $hook_params);
2148 $this->addHistory('Removed User',$user_id);
2155 * updateUser - controls updating a user's role in this group.
2157 * @param int $user_id The ID of the user.
2158 * @param int $role_id The role_id to set this user to.
2159 * @return boolean success.
2161 function updateUser($user_id, $role_id) {
2163 if (!forge_check_perm ('project_admin', $this->getID())) {
2164 $this->setPermissionDeniedError();
2168 $newrole = RBACEngine::getInstance()->getRoleById ($role_id);
2169 if (!$newrole || !is_object($newrole)) {
2170 $this->setError(_('Could Not Get Role'));
2172 } elseif ($newrole->isError()) {
2173 $this->setError(sprintf(_('Role: %s'),$role->getErrorMessage()));
2175 } elseif ($newrole->getHomeProject() == NULL
2176 || $newrole->getHomeProject()->getID() != $this->getID()) {
2177 $this->setError(_('Wrong destination role'));
2180 $user = user_get_object ($user_id);
2181 $roles = RBACEngine::getInstance()->getAvailableRolesForUser ($user);
2183 foreach ($roles as $role) {
2184 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2185 $found_role = $role;
2189 if ($found_role == NULL) {
2190 $this->setError(sprintf(_('Error: User not removed: %s')));
2194 $found_role->removeUser ($user);
2195 $newrole->addUser ($user);
2197 $this->addHistory('Updated User',$user_id);
2202 * addHistory - Makes an audit trail entry for this project.
2204 * @param string $field_name The name of the field.
2205 * @param string $old_value The Old Value for this $field_name.
2206 * @return resource database result handle.
2209 function addHistory($field_name, $old_value) {
2210 return db_query_params ('INSERT INTO group_history(group_id,field_name,old_value,mod_by,adddate)
2211 VALUES ($1,$2,$3,$4,$5)',
2212 array($this->getID(),
2220 * activateUsers - Make sure that group members have unix accounts.
2222 * Setup unix accounts for group members. Can be called even
2223 * if members are already active.
2227 function activateUsers() {
2229 Activate member(s) of the project
2232 $members = $this->getUsers (true);
2234 foreach ($members as $member) {
2235 $user_id = $member->getID();
2237 if (!$SYS->sysCheckCreateGroup($this->getID())){
2238 $this->setError($SYS->getErrorMessage());
2242 if (!$SYS->sysCheckCreateUser($user_id)) {
2243 $this->setError($SYS->getErrorMessage());
2247 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
2248 $this->setError($SYS->getErrorMessage());
2258 * getMembers - returns array of User objects for this project
2260 * @return array of User objects for this group.
2262 function getMembers() {
2263 return $this->getUsers (true);
2267 * replaceTemplateStrings - fill-in some blanks with project name
2269 * @param string $string Template string
2270 * @return string String after replacements
2272 function replaceTemplateStrings($string) {
2273 $string = str_replace ('UNIXNAME', $this->getUnixName(), $string);
2274 $string = str_replace ('PUBLICNAME', $this->getPublicName(), $string);
2275 $string = str_replace ('DESCRIPTION', $this->getDescription(), $string);
2280 * approve - Approve pending project.
2282 * @param object $user The User object who is doing the updating.
2286 function approve(&$user) {
2287 global $gfcommon,$gfwww;
2288 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
2290 if ($this->getStatus()=='A') {
2291 $this->setError(_("Group already active"));
2297 // Step 1: Activate group and create LDAP entries
2298 if (!$this->setStatus($user, 'A')) {
2303 // Switch to system language for item creation
2304 setup_gettext_from_sys_lang();
2306 // Create default roles
2307 $idadmin_group = NULL;
2308 foreach (get_group_join_requests ($this) as $gjr) {
2309 $idadmin_group = $gjr->getUserID();
2312 if ($idadmin_group == NULL) {
2313 $idadmin_group = $user->getID();
2316 $template = $this->getTemplateProject();
2317 $id_mappings = array();
2318 $seen_admin_role = false;
2320 // Copy roles from template project
2321 foreach($template->getRoles() as $oldrole) {
2322 if ($oldrole->getHomeProject() != NULL) {
2323 $role = new Role($this);
2325 // Need to use a different role name so that the permissions aren't set from the hardcoded defaults
2326 $role->create('TEMPORARY ROLE NAME', $data, true);
2327 $role->setName($oldrole->getName());
2328 if ($oldrole->getSetting ('project_admin', $template->getID())) {
2329 $seen_admin_role = true;
2333 $role->linkProject($this);
2335 $id_mappings['role'][$oldrole->getID()] = $role->getID();
2336 // Reuse the project_admin permission
2337 $role->setSetting ('project_admin', $this->getID(), $oldrole->getSetting ('project_admin', $template->getID()));
2341 if (!$seen_admin_role) {
2342 $role = new Role($this);
2343 $adminperms = array('project_admin' => array ($this->getID() => 1));
2344 $role_id = $role->create ('Admin', $adminperms, true);
2347 $roles = $this->getRoles();
2348 foreach ($roles as $r) {
2349 if ($r->getHomeProject() == NULL) {
2352 if ($r->getSetting ('project_admin', $this->getID())) {
2353 $r->addUser(user_get_object ($idadmin_group));
2357 // Temporarily switch to the submitter's identity
2358 $saved_session = session_get_user();
2359 session_set_internal($idadmin_group);
2362 if (forge_get_config('use_tracker')) {
2363 $this->setUseTracker ($template->usesTracker());
2364 if ($template->usesTracker()) {
2365 $oldatf = new ArtifactTypeFactory($template);
2366 foreach ($oldatf->getArtifactTypes() as $o) {
2367 $t = new ArtifactType ($this);
2368 $t->create ($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->emailAll(),$o->getEmailAddress(),$o->getDuePeriod()/86400,0,$o->getSubmitInstructions(),$o->getBrowseInstructions());
2369 $id_mappings['tracker'][$o->getID()] = $t->getID();
2370 $t->cloneFieldsFrom ($o->getID());
2375 if (forge_get_config('use_pm')) {
2376 $this->setUsePM ($template->usesPM());
2377 if ($template->usesPM()) {
2378 $oldpgf = new ProjectGroupFactory($template);
2379 foreach ($oldpgf->getProjectGroups() as $o) {
2380 $pg = new ProjectGroup($this);
2381 $pg->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo());
2382 $id_mappings['pm'][$o->getID()] = $pg->getID();
2387 if (forge_get_config('use_forum')) {
2388 $this->setUseForum($template->usesForum());
2389 if ($template->usesForum()) {
2390 $oldff = new ForumFactory($template);
2391 foreach ($oldff->getForums() as $o) {
2392 $f = new Forum($this);
2393 $f->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo(),1);
2394 $id_mappings['forum'][$o->getID()] = $f->getID();
2399 if (forge_get_config('use_docman')) {
2400 $this->setUseDocman($template->usesDocman());
2401 if ($template->usesDocman()) {
2402 $olddgf = new DocumentGroupFactory($template);
2403 // First pass: create all docgroups
2404 $id_mappings['docman_docgroup'][0] = 0;
2405 foreach ($olddgf->getDocumentGroups() as $o) {
2406 $ndgf = new DocumentGroup($this);
2407 // .trash is a reserved directory
2408 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2409 $ndgf->create($this->replaceTemplateStrings($o->getName()));
2410 $id_mappings['docman_docgroup'][$o->getID()] = $ndgf->getID();
2413 // Second pass: restore hierarchy links
2414 foreach ($olddgf->getDocumentGroups() as $o) {
2415 $ndgf = new DocumentGroup($this);
2416 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2417 $ndgf->fetchData($id_mappings['docman_docgroup'][$o->getID()]);
2418 $ndgf->update($ndgf->getName(), $id_mappings['docman_docgroup'][$o->getParentID()]);
2424 if (forge_get_config('use_frs')) {
2425 $this->setUseFRS ($template->usesFRS());
2426 if ($template->usesFRS()) {
2427 foreach (get_frs_packages($template) as $o) {
2428 $newp = new FRSPackage($this);
2429 $nname = $this->replaceTemplateStrings($o->getName());
2430 $newp->create ($nname, $o->isPublic());
2435 if (forge_get_config('use_mail')) {
2436 $this->setUseMail($template->usesMail());
2437 if ($template->usesMail()) {
2438 $oldmlf = new MailingListFactory($template);
2439 foreach ($oldmlf->getMailingLists() as $o) {
2440 $ml = new MailingList($this);
2441 $nname = preg_replace ('/^'.$template->getUnixName().'-/','',$o->getName());
2443 $ndescription = $this->replaceTemplateStrings($o->getDescription());
2444 $ml->create($nname, $ndescription, $o->isPublic());
2450 /* use SCM plugin from template group */
2451 $this->setUseSCM($template->usesSCM());
2453 foreach ($template->getPlugins() as
2454 $plugin_id => $plugin_name) {
2455 $this->setPluginUse($plugin_name);
2458 /* use SCM choice from registration page */
2460 foreach ($template->getPlugins() as
2461 $plugin_id => $plugin_name) {
2462 if (substr($plugin_name, 3) == 'scm' &&
2463 $plugin_name != 'scmhook') {
2464 /* skip copying scm plugins */
2467 /* enable other plugins though */
2468 $this->setPluginUse($plugin_name);
2472 foreach ($template->getRoles() as $oldrole) {
2473 $newrole = RBACEngine::getInstance()->getRoleById ($id_mappings['role'][$oldrole->getID()]);
2474 if ($oldrole->getHomeProject() != NULL
2475 && $oldrole->getHomeProject()->getID() == $template->getID()) {
2476 $newrole->setPublic ($oldrole->isPublic());
2478 $oldsettings = $oldrole->getSettingsForProject ($template);
2480 $sections = array('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm');
2481 foreach ($sections as $section) {
2482 $newrole->setSetting ($section, $this->getID(), $oldsettings[$section][$template->getID()]);
2485 $sections = array('tracker', 'pm', 'forum');
2486 foreach ($sections as $section) {
2487 if (isset ($oldsettings[$section])) {
2488 foreach ($oldsettings[$section] as $k => $v) {
2489 // Only copy perms for tools that have been copied
2490 if (isset ($id_mappings[$section][$k])) {
2491 $newrole->setSetting ($section,
2492 $id_mappings[$section][$k],
2500 $lm = new WidgetLayoutManager();
2501 $lm->createDefaultLayoutForProject ($this->getID(), $template->getID());
2504 $params['template'] = $template;
2505 $params['project'] = $this;
2506 $params['id_mappings'] = $id_mappings;
2507 plugin_hook_by_reference ('clone_project_from_template', $params);
2509 // Disable everything
2510 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',
2511 array($this->getID()));
2514 $this->normalizeAllRoles();
2515 // empty members cache because the group creator is not yet in cache.
2516 unset($this->membersArr);
2517 $this->activateUsers();
2519 // Delete fake join request
2520 foreach (get_group_join_requests ($this) as $gjr) {
2524 // Switch back to user preference
2525 session_set_internal($saved_session->getID());
2526 setup_gettext_from_context();
2530 $this->sendApprovalEmail();
2531 $this->addHistory(_('Approved'), 'x');
2534 // Plugin can make approve operation there
2537 $params['group'] = $this;
2538 $params['group_id'] = $this->getID();
2539 plugin_hook('group_approved', $params);
2545 * sendApprovalEmail - Send new project email.
2547 * @return boolean success.
2550 function sendApprovalEmail() {
2551 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('project_admin', $this->getID());
2553 if (count($admins) < 1) {
2554 $this->setError(_("Group does not have any administrators."));
2558 // send one email per admin
2559 foreach ($admins as $admin) {
2560 setup_gettext_for_user ($admin);
2562 $message=sprintf(_('Your project registration for %4$s has been approved.
2564 Project Full Name: %1$s
2565 Project Unix Name: %2$s
2567 Your DNS will take up to a day to become active on our site.
2568 Your web site is accessible through your shell account. Please read
2569 site documentation (see link below) about intended usage, available
2570 services, and directory layout of the account.
2573 own project page in %4$s while logged in, you will find
2574 additional menu functions to your left labeled \'Project Admin\'.
2576 We highly suggest that you now visit %4$s and create a public
2577 description for your project. This can be done by visiting your project
2578 page while logged in, and selecting \'Project Admin\' from the menus
2579 on the left (or by visiting %3$s
2582 Your project will also not appear in the Trove Software Map (primary
2583 list of projects hosted on %4$s which offers great flexibility in
2584 browsing and search) until you categorize it in the project administration
2585 screens. So that people can find your project, you should do this now.
2586 Visit your project while logged in, and select \'Project Admin\' from the
2589 Enjoy the system, and please tell others about %4$s. Let us know
2590 if there is anything we can do to help you.
2593 htmlspecialchars_decode($this->getPublicName()),
2594 $this->getUnixName(),
2595 util_make_url ('/project/admin/?group_id='.$this->getID()),
2596 forge_get_config ('forge_name'));
2598 util_send_message($admin->getEmail(), sprintf(_('%s Project Approved'), forge_get_config ('forge_name')), $message);
2600 setup_gettext_from_context();
2607 * sendRejectionEmail - Send project rejection email.
2609 * This function sends out a rejection message to a user who
2610 * registered a project.
2612 * @param int $response_id The id of the response to use.
2613 * @param string $message The rejection message.
2614 * @return bool completion status.
2617 function sendRejectionEmail($response_id, $message="zxcv") {
2618 $submitters = array();
2619 foreach (get_group_join_requests ($this) as $gjr) {
2620 $submitters[] = user_get_object($gjr->getUserID());
2623 if (count ($submitters) < 1) {
2624 $this->setError(_("Group does not have any administrators."));
2628 foreach ($submitters as $admin) {
2629 setup_gettext_for_user($admin);
2631 $response = sprintf(_('Your project registration for %s has been denied.'), forge_get_config('forge_name')) . "\n\n"
2632 . _('Project Full Name')._(': '). $this->getPublicName() . "\n"
2633 . _('Project Unix Name')._(': '). $this->getUnixName() . "\n\n"
2634 . _('Reasons for negative decision')._(': ') . "\n\n";
2636 // Check to see if they want to send a custom rejection response
2637 if ($response_id == 0) {
2638 $response .= $message;
2640 $response .= db_result(
2641 db_query_params('SELECT response_text FROM canned_responses WHERE response_id=$1', array($response_id)),
2646 util_send_message($admin->getEmail(), sprintf(_('%s Project Denied'), forge_get_config ('forge_name')), $response);
2647 setup_gettext_from_context();
2654 * sendNewProjectNotificationEmail - Send new project notification email.
2656 * This function sends out a notification email to the
2657 * SourceForge admin user when a new project is
2660 * @return boolean success.
2663 function sendNewProjectNotificationEmail() {
2664 // Get the user who wants to register the project
2665 $submitters = array();
2666 foreach (get_group_join_requests ($this) as $gjr) {
2667 $submitters[] = user_get_object($gjr->getUserID());
2669 if (count ($submitters) < 1) {
2670 $this->setError(_("Could not find user who has submitted the project."));
2674 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('approve_projects', -1);
2676 if (count($admins) < 1) {
2677 $this->setError(_("There is no administrator to send the mail to."));
2681 foreach ($admins as $admin) {
2682 $admin_email = $admin->getEmail();
2683 setup_gettext_for_user ($admin);
2685 $message = sprintf(_('New %s Project Submitted'), forge_get_config ('forge_name')) . "\n\n"
2686 . _('Project Full Name')._(': ').htmlspecialchars_decode($this->getPublicName()) . "\n"
2687 . _('Submitted Description')._(': ').htmlspecialchars_decode($this->getRegistrationPurpose()) . "\n";
2689 foreach ($submitters as $submitter) {
2690 $message .= _('Submitter')._(': ').$submitter->getRealName().' ('.$submitter->getUnixName().')' . "\n\n";
2694 . _('Please visit the following URL to approve or reject this project')._(': '). "\n"
2695 . util_make_url('/admin/approve-pending.php');
2696 util_send_message($admin_email, sprintf(_('New %s Project Submitted'), forge_get_config('forge_name')), $message);
2697 setup_gettext_from_context();
2700 $email = $submitter->getEmail();
2701 setup_gettext_for_user ($submitter);
2703 $message = sprintf(_('New %s Project Submitted'), forge_get_config ('forge_name')) . "\n\n"
2704 . _('Project Full Name')._(': ') . $this->getPublicName() . "\n"
2705 . _('Submitted Description')._(': ') . util_unconvert_htmlspecialchars($this->getRegistrationPurpose()) . "\n\n"
2706 . sprintf(_('The %s admin team will now examine your project submission. You will be notified of their decision.'),
2707 forge_get_config ('web_host'));
2709 util_send_message($email, sprintf(_('New %s Project Submitted'), forge_get_config ('forge_name')), $message);
2710 setup_gettext_from_context();
2716 * validateGroupName - Validate the group name
2718 * @param string Group name.
2720 * @return boolean an error false and set an error is the group name is invalid otherwise return true
2722 function validateGroupName($group_name) {
2723 if (strlen($group_name)<3) {
2724 $this->setError(_('Group name is too short'));
2726 } elseif (strlen(htmlspecialchars($group_name))>40) {
2727 $this->setError(_('Group name is too long'));
2729 } elseif (group_get_object_by_publicname($group_name)) {
2730 $this->setError(_('Group name already taken'));
2737 * getRolesId - Get Ids of the roles of the group.
2739 * @return array Role ids of this group.
2741 function getRolesId() {
2742 $role_ids = array();
2744 $res = db_query_params('SELECT role_id FROM pfo_role WHERE home_group_id=$1',
2745 array($this->getID()));
2746 while ($arr = db_fetch_array($res)) {
2747 $role_ids[] = $arr['role_id'];
2749 $res = db_query_params('SELECT role_id FROM role_project_refs WHERE group_id=$1',
2750 array($this->getID()));
2751 while ($arr = db_fetch_array($res)) {
2752 $role_ids[] = $arr['role_id'];
2755 return array_unique($role_ids);
2759 * getRoles - Get the roles of the group.
2761 * @return array Roles of this group.
2763 function getRoles() {
2766 $roles = $this->getRolesId();
2767 $engine = RBACEngine::getInstance();
2768 foreach ($roles as $role_id) {
2769 $result[] = $engine->getRoleById ($role_id);
2775 function normalizeAllRoles() {
2776 $roles = $this->getRoles();
2778 foreach ($roles as $r) {
2779 $r->normalizeData();
2784 * getUnixStatus - Status of activation of unix account.
2786 * @return string Values: (N)one, (A)ctive, (S)uspended or (D)eleted
2788 function getUnixStatus() {
2789 return $this->data_array['unix_status'];
2793 * setUnixStatus - Sets status of activation of unix account.
2795 * @param string $status The unix status.
2801 * @return boolean success.
2803 function setUnixStatus($status) {
2806 $res = db_query_params ('UPDATE groups SET unix_status=$1 WHERE group_id=$2',
2811 $this->setError(sprintf(_('Error: Cannot Update Group Unix Status: %s'),db_error()));
2815 if ($status == 'A') {
2816 if (!$SYS->sysCheckCreateGroup($this->getID())) {
2817 $this->setError($SYS->getErrorMessage());
2822 if ($SYS->sysCheckGroup($this->getID())) {
2823 if (!$SYS->sysRemoveGroup($this->getID())) {
2824 $this->setError($SYS->getErrorMessage());
2831 $this->data_array['unix_status']=$status;
2838 * getUsers - Get the users of a group
2840 * @param bool $onlylocal
2841 * @return array of user's objects.
2843 function getUsers($onlylocal = true) {
2844 if (!isset($this->membersArr)) {
2845 $this->membersArr = array();
2848 foreach ($this->getRoles() as $role) {
2850 && ($role->getHomeProject() == NULL || $role->getHomeProject()->getID() != $this->getID())) {
2853 foreach ($role->getUsers() as $user) {
2854 $ids[] = $user->getID();
2857 $ids = array_unique ($ids);
2858 foreach ($ids as $id) {
2859 $u = user_get_object ($id);
2860 if ($u->isActive()) {
2861 $this->membersArr[] = $u;
2865 return $this->membersArr;
2868 function setDocmanCreateOnlineStatus($status) {
2870 /* if we activate search engine, we probably want to reindex */
2871 $res = db_query_params('UPDATE groups SET use_docman_create_online=$1 WHERE group_id=$2',
2872 array($status, $this->getID()));
2875 $this->setError(sprintf(_('Error: Cannot Update Group DocmanCreateOnline Status: %s'),db_error()));
2879 $this->data_array['use_docman_create_online']=$status;
2885 function setDocmanWebdav($status) {
2887 /* if we activate search engine, we probably want to reindex */
2888 $res = db_query_params('UPDATE groups SET use_webdav=$1 WHERE group_id=$2',
2893 $this->setError(sprintf(_('Error: Cannot Update Group UseWebdab Status: %s'),db_error()));
2897 $this->data_array['use_webdav']=$status;
2903 function setDocmanSearchStatus($status) {
2905 /* if we activate search engine, we probably want to reindex */
2906 $res = db_query_params('UPDATE groups SET use_docman_search=$1, force_docman_reindex=$1 WHERE group_id=$2',
2911 $this->setError(sprintf(_('Error: Cannot Update Group UseDocmanSearch Status: %s'),db_error()));
2915 $this->data_array['use_docman_search']=$status;
2921 function setDocmanForceReindexSearch($status) {
2923 /* if we activate search engine, we probably want to reindex */
2924 $res = db_query_params('UPDATE groups SET force_docman_reindex=$1 WHERE group_id=$2',
2929 $this->setError(sprintf(_('Error: Cannot Update Group force_docman_reindex %s'),db_error()));
2933 $this->data_array['force_docman_reindex']=$status;
2941 * group_getname() - get the group name
2943 * @param int $group_id The group ID
2948 function group_getname ($group_id = 0) {
2949 $grp = group_get_object($group_id);
2951 return $grp->getPublicName();
2958 * group_getunixname() - get the unixname for a group
2960 * @param int $group_id The group ID
2965 function group_getunixname ($group_id) {
2966 $grp = group_get_object($group_id);
2968 return $grp->getUnixName();
2975 * group_get_result() - Get the group object result ID.
2977 * @param int $group_id The group ID
2982 function &group_get_result($group_id=0) {
2983 $grp = group_get_object($group_id);
2985 return $grp->getData();
2991 function getAllProjectTags($onlyvisible = true) {
2992 $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',
2995 if (!$res || db_numrows($res) == 0) {
3001 while ($arr = db_fetch_array($res)) {
3003 $group_id = $arr[1];
3004 if (!isset($result[$tag])) {
3005 $result[$tag] = array();
3008 if (!$onlyvisible || forge_check_perm('project_read', $group_id)) {
3009 $p = group_get_object($group_id);
3010 $result[$tag][] = array('unix_group_name' => $p->getUnixName(),
3011 'group_id' => $group_id);
3019 * Utility class to compare project based in various criteria (names, unixnames, id, ...)
3022 class ProjectComparator {
3023 var $criterion = 'name';
3025 function Compare ($a, $b) {
3026 switch ($this->criterion) {
3029 $namecmp = strcoll ($a->getPublicName(), $b->getPublicName());
3030 if ($namecmp != 0) {
3033 /* If several projects share a same public name */
3034 return strcoll ($a->getUnixName(), $b->getUnixName());
3037 return strcmp ($a->getUnixName(), $b->getUnixName());
3045 return ($a < $b) ? -1 : 1;
3051 function sortProjectList (&$list, $criterion='name') {
3052 $cmp = new ProjectComparator();
3053 $cmp->criterion = $criterion;
3055 return usort ($list, array($cmp, 'Compare'));
3060 // c-file-style: "bsd"