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.
236 function __construct($id = false, $res = false) {
239 //setting up an empty object
240 //probably going to call create()
244 if (!$this->fetchData($id)) {
249 // Assoc array was passed in
251 if (is_array($res)) {
252 $this->data_array =& $res;
254 if (db_numrows($res) < 1) {
255 //function in class we extended
256 $this->setError(_('Group Not Found'));
257 $this->data_array=array();
260 //set up an associative array for use by other functions
261 $this->data_array = db_fetch_array_by_row($res, 0);
269 * fetchData - May need to refresh database fields if an update occurred.
271 * @param int $group_id The group_id.
272 * @return boolean success or not
274 function fetchData($group_id) {
275 $res = db_query_params ('SELECT * FROM groups WHERE group_id=$1',
277 if (!$res || db_numrows($res) < 1) {
278 $this->setError(sprintf('fetchData():: %s', db_error()));
281 $this->data_array = db_fetch_array($res);
286 * create - Create new group.
288 * This method should be called on empty Group object.
289 * It will add an entry for a pending group/project (status 'P')
291 * @param object $user The User object.
292 * @param string $group_name The full name of the user.
293 * @param string $unix_name The Unix name of the user.
294 * @param string $description The new group description.
295 * @param string $purpose The purpose of the group.
296 * @param string $unix_box
297 * @param string $scm_box
298 * @param bool $is_public
299 * @param bool $send_mail Whether to send an email or not
300 * @param int $built_from_template The id of the project this new project is based on
301 * @return boolean success or not
303 function create(&$user, $group_name, $unix_name, $description, $purpose, $unix_box = 'shell1',
304 $scm_box = 'cvs1', $is_public = true, $send_mail = true, $built_from_template = 0) {
305 // $user is ignored - anyone can create pending group
308 if ($this->getID()!=0) {
309 $this->setError(_('Group object already exists.'));
311 } elseif (!$this->validateGroupName($group_name)) {
313 } elseif (!account_groupnamevalid($unix_name)) {
314 $this->setError(_('Invalid Unix Name.'));
316 } elseif (!$SYS->sysUseUnixName($unix_name)) {
317 $this->setError(_('Unix name already taken.'));
319 } elseif (db_numrows(db_query_params('SELECT group_id FROM groups WHERE unix_group_name=$1',
320 array($unix_name))) > 0) {
321 $this->setError(_('Unix name already taken.'));
323 } elseif (strlen($purpose)<10) {
324 $this->setError(_('Please describe your Registration Project Purpose and Summarization in a more comprehensive manner.'));
326 } elseif (strlen($purpose)>1500) {
327 $this->setError(_('The Registration Project Purpose and Summarization text is too long. Please make it smaller than 1500 characters.'));
329 } elseif (strlen($description)<10) {
330 $this->setError(_('Describe in a more comprehensive manner your project.'));
334 // Check if sys_use_project_vhost for homepage
335 if (forge_get_config('use_project_vhost')) {
336 $homepage = $unix_name.".".forge_get_config('web_host');
338 $homepage = forge_get_config('web_host')."/www/".$unix_name."/";
343 $res = db_query_params('
358 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)',
359 array(htmlspecialchars($group_name),
361 htmlspecialchars($description),
367 htmlspecialchars($purpose),
369 md5(util_randbytes()),
370 $built_from_template));
371 if (!$res || db_affected_rows($res) < 1) {
372 $this->setError(sprintf(_('Error: Cannot create group: %s'),db_error()));
377 $id = db_insertid($res, 'groups', 'group_id');
379 $this->setError(sprintf(_('Error: Cannot get group id: %s'),db_error()));
384 if (!$this->fetchData($id)) {
389 $gjr = new GroupJoinRequest($this);
390 $gjr->create($user->getID(),
391 'Fake GroupJoinRequest to store the creator of a project',
394 $hook_params = array();
395 $hook_params['group'] = $this;
396 $hook_params['group_id'] = $this->getID();
397 $hook_params['group_name'] = $group_name;
398 $hook_params['unix_group_name'] = $unix_name;
399 plugin_hook("group_create", $hook_params);
403 $this->sendNewProjectNotificationEmail();
411 * updateAdmin - Update core properties of group object.
413 * This function require site admin privilege.
415 * @param object $user User requesting operation (for access control).
416 * @param int $type_id Group type (1-project, 2-foundry).
417 * @param string $unix_box Machine on which group's home directory located.
418 * @param string $http_domain Domain which serves group's WWW.
419 * @return bool status.
422 function updateAdmin(&$user, $type_id, $unix_box, $http_domain) {
423 $perm =& $this->getPermission();
425 if (!$perm || !is_object($perm)) {
426 $this->setError(_('Could not get permission.'));
430 if (!$perm->isSuperUser()) {
431 $this->setError(_('Permission denied.'));
437 $res = db_query_params('
439 SET type_id=$1, unix_box=$2, http_domain=$3
446 if (!$res || db_affected_rows($res) < 1) {
447 $this->setError(_('Error: Cannot change group properties: %s'),db_error());
452 // Log the audit trail
453 if ($type_id != $this->data_array['type_id']) {
454 $this->addHistory('type_id', $this->data_array['type_id']);
456 if ($unix_box != $this->data_array['unix_box']) {
457 $this->addHistory('unix_box', $this->data_array['unix_box']);
459 if ($http_domain != $this->data_array['http_domain']) {
460 $this->addHistory('http_domain', $this->data_array['http_domain']);
463 if (!$this->fetchData($this->getID())) {
472 * update - Update number of common properties.
474 * Unlike updateAdmin(), this function accessible to project admin.
476 * @param object $user User requesting operation (for access control).
477 * @param string $group_name
478 * @param string $homepage
479 * @param string $short_description
480 * @param bool $use_mail
481 * @param bool $use_survey
482 * @param bool $use_forum
483 * @param bool $use_pm
484 * @param bool $use_pm_depend_box
485 * @param bool $use_scm
486 * @param bool $use_news
487 * @param bool $use_docman
488 * @param string $new_doc_address
489 * @param bool $send_all_docs
490 * @param int $logo_image_id
491 * @param bool $use_ftp
492 * @param bool $use_tracker
493 * @param bool $use_frs
494 * @param bool $use_stats
496 * @param bool $use_activity
497 * @param bool $is_public group is publicly accessible
498 * @return int status.
501 function update(&$user, $group_name, $homepage, $short_description, $use_mail, $use_survey, $use_forum,
502 $use_pm, $use_pm_depend_box, $use_scm, $use_news, $use_docman,
503 $new_doc_address, $send_all_docs, $logo_image_id,
504 $use_ftp, $use_tracker, $use_frs, $use_stats, $tags, $use_activity, $is_public) {
506 $perm =& $this->getPermission();
508 if (!$perm || !is_object($perm)) {
509 $this->setError(_('Could not get permission.'));
513 if (!$perm->isAdmin()) {
514 $this->setError(_('Permission denied.'));
518 // Validate some values
519 if ($this->getPublicName() != $group_name) {
520 if (!$this->validateGroupName($group_name)) {
525 if ($new_doc_address) {
526 $invalid_mails = validate_emails($new_doc_address);
527 if (count($invalid_mails) > 0) {
528 $this->setError(sprintf(ngettext('New Doc Address Appeared Invalid: %s', 'New Doc Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
533 // in the database, these all default to '1',
534 // so we have to explicitly set 0
547 if (!$use_pm_depend_box) {
548 $use_pm_depend_box = 0;
571 if (!$use_activity) {
574 if (!$send_all_docs) {
578 $homepage = ltrim($homepage);
580 $homepage = util_make_url('/projects/' . $this->getUnixName() . '/');
583 if (strlen(htmlspecialchars($short_description))<10) {
584 $this->setError(_('Describe in a more comprehensive manner your project.'));
590 //XXX not yet actived logo_image_id='$logo_image_id',
591 $res = db_query_params('UPDATE groups
594 short_description=$3,
599 use_pm_depend_box=$8,
610 array(htmlspecialchars($group_name),
612 htmlspecialchars($short_description),
629 if (!$res || db_affected_rows($res) < 1) {
630 $this->setError(sprintf(_('Error updating project information: %s'), db_error()));
635 if (!$this->setUseDocman($use_docman)) {
636 $this->setError(sprintf(_('Error updating project information: use_docman %s'), db_error()));
641 if ($this->setTags($tags) === false) {
646 // Log the audit trail
647 $this->addHistory('Changed Public Info', '');
649 if (!$this->fetchData($this->getID())) {
654 $hook_params = array();
655 $hook_params['group'] = $this;
656 $hook_params['group_id'] = $this->getID();
657 $hook_params['group_homepage'] = $homepage;
658 $hook_params['group_name'] = htmlspecialchars($group_name);
659 $hook_params['group_description'] = htmlspecialchars($short_description);
660 $hook_params['group_ispublic'] = $is_public;
661 if (!plugin_hook("group_update", $hook_params)) {
662 if (!$this->isError()) {
663 $this->setError(_('Error updating project information in plugin_hook group_update'));
674 * getID - Simply return the group_id for this object.
676 * @return int group_id.
679 return $this->data_array['group_id'];
683 * getType() - Foundry, project, etc.
685 * @return int The type flag from the database.
688 return $this->data_array['type_id'];
693 * getStatus - the status code.
695 * Statuses char include I,H,A,D,P.
702 function getStatus() {
703 return $this->data_array['status'];
707 * setStatus - set the status code.
709 * Statuses include I,H,A,D,P.
716 * @param object $user User requesting operation (for access control).
717 * @param string $status Status value.
718 * @return boolean success.
721 function setStatus(&$user, $status) {
724 if (!forge_check_global_perm_for_user($user, 'approve_projects')) {
725 $this->setPermissionDeniedError();
729 // Projects in 'A' status can only go to 'H' or 'D'
730 // Projects in 'D' status can only go to 'A'
731 // Projects in 'P' status can only go to 'A' OR 'D'
732 // Projects in 'I' status can only go to 'P'
733 // Projects in 'H' status can only go to 'A' OR 'D'
734 $allowed_status_changes = array(
735 'AH'=>1,'AD'=>1,'DA'=>1,'PA'=>1,'PD'=>1,
736 'IP'=>1,'HA'=>1,'HD'=>1
739 // Check that status transition is valid
740 if ($this->getStatus() != $status
741 && !array_key_exists($this->getStatus(). $status, $allowed_status_changes)) {
742 $this->setError(_('Invalid Status Change From: ').$this->getStatus(). _(' To: '.$status));
748 $res = db_query_params('UPDATE groups
750 WHERE group_id=$2', array($status, $this->getID()));
752 if (!$res || db_affected_rows($res) < 1) {
753 $this->setError(sprintf(_('Error: Cannot change group status: %s'),db_error()));
759 // Activate system group, if not yet
760 if (!$SYS->sysCheckGroup($this->getID())) {
761 if (!$SYS->sysCreateGroup($this->getID())) {
762 $this->setError($SYS->getErrorMessage());
767 if (!$this->activateUsers()) {
772 /* Otherwise, the group is not active, and make sure that
773 System group is not active either */
774 } elseif ($SYS->sysCheckGroup($this->getID())) {
775 if (!$SYS->sysRemoveGroup($this->getID())) {
776 $this->setError($SYS->getErrorMessage());
782 $hook_params = array();
783 $hook_params['group'] = $this;
784 $hook_params['group_id'] = $this->getID();
785 $hook_params['status'] = $status;
786 plugin_hook("group_setstatus", $hook_params);
790 // Log the audit trail
791 if ($status != $this->getStatus()) {
792 $this->addHistory(_('Status'), $this->getStatus());
795 $this->data_array['status'] = $status;
800 * isProject - Simple boolean test to see if it's a project or not.
802 * @return boolean is_project.
804 function isProject() {
805 if ($this->getType()==1) {
813 * isPublic - Wrapper around RBAC to check if a project is anonymously readable
815 * @return boolean is_public.
817 function isPublic() {
818 $ra = RoleAnonymous::getInstance();
819 return $ra->hasPermission('project_read', $this->getID());
823 * isActive - Database field status of 'A' returns true.
825 * @return boolean is_active.
827 function isActive() {
828 if ($this->getStatus()=='A') {
836 * isTemplate - Simply returns the is_template flag from the database.
838 * @return boolean is_template.
840 function isTemplate() {
841 return $this->data_array['is_template'];
845 * setAsTemplate - Set the template status of a project
847 * @param boolean $booleanparam is_template.
850 function setAsTemplate($booleanparam) {
852 $booleanparam = $booleanparam ? 1 : 0;
853 $res = db_query_params('UPDATE groups SET is_template=$1 WHERE group_id=$2',
854 array($booleanparam, $this->getID()));
856 $this->data_array['is_template']=$booleanparam;
866 * getTemplateProject - Return the project template this project is built from
868 * @return object The template project
870 function getTemplateProject() {
871 return group_get_object($this->data_array['built_from_template']);
875 * getUnixName - the unix_name
877 * @return string unix_name.
879 function getUnixName() {
880 return strtolower($this->data_array['unix_group_name']);
884 * getPublicName - the full-length public name.
886 * @return string The group_name.
888 function getPublicName() {
889 return $this->data_array['group_name'];
893 * getRegisterPurpose - the text description of the purpose of this project.
895 * @return string The description.
897 function getRegisterPurpose() {
898 return $this->data_array['register_purpose'];
902 * getDescription - the text description of this project.
904 * @return string The description.
906 function getDescription() {
907 return $this->data_array['short_description'];
911 * getStartDate - the unix time this project was registered.
913 * @return int (unix time) of registration.
915 function getStartDate() {
916 return $this->data_array['register_time'];
920 * getLogoImageID - the id of the logo in the database for this project.
922 * @return int The ID of logo image in db_images table (or 100 if none).
924 function getLogoImageID() {
925 return $this->data_array['logo_image_id'];
929 * getUnixBox - the hostname of the unix box where this project is located.
931 * @return string The name of the unix machine for the group.
933 function getUnixBox() {
934 return $this->data_array['unix_box'];
938 * getSCMBox - the hostname of the scm box where this project is located.
940 * @return string The name of the unix machine for the group.
942 function getSCMBox() {
943 return $this->data_array['scm_box'];
946 * setSCMBox - the hostname of the scm box where this project is located.
948 * @param string $scm_box The name of the new SCM_BOX
951 function setSCMBox($scm_box) {
953 if ($scm_box == $this->data_array['scm_box']) {
958 $res = db_query_params('UPDATE groups SET scm_box=$1 WHERE group_id=$2', array($scm_box, $this->getID()));
960 $this->addHistory('scm_box', $this->data_array['scm_box']);
961 $this->data_array['scm_box'] = $scm_box;
966 $this->setError(_("Could not insert SCM_BOX to database"));
970 $this->setError(_("SCM Box cannot be empty"));
976 * getDomain - the hostname.domain where their web page is located.
978 * @return string The name of the group [web] domain.
980 function getDomain() {
981 return $this->data_array['http_domain'];
985 * getRegistrationPurpose - the text description of the purpose of this project.
987 * @return string The application for project hosting.
989 function getRegistrationPurpose() {
990 return $this->data_array['register_purpose'];
994 * getAdmins() - Get array of Admin user objects.
996 * @return array Array of User objects.
998 function &getAdmins() {
999 $roles = RBACEngine::getInstance()->getRolesByAllowedAction ('project_admin', $this->getID());
1001 $user_ids = array();
1003 foreach ($roles as $role) {
1004 if (! ($role instanceof RoleExplicit)) {
1007 if ($role->getHomeProject() == NULL
1008 || $role->getHomeProject()->getID() != $this->getID()) {
1012 foreach ($role->getUsers() as $u) {
1013 $user_ids[] = $u->getID();
1016 return user_get_objects(array_unique($user_ids));
1020 Common Group preferences for tools
1024 * enableAnonSCM - whether or not this group has opted to enable Anonymous SCM.
1026 * @return boolean enable_scm.
1028 function enableAnonSCM() {
1029 $r = RoleAnonymous::getInstance();
1030 return $r->hasPermission('scm', $this->getID(), 'read');
1033 function SetUsesAnonSCM($booleanparam) {
1035 $booleanparam = $booleanparam ? 1 : 0;
1036 $r = RoleAnonymous::getInstance();
1037 $r->setSetting('scm', $this->getID(), $booleanparam);
1042 * enablePserver - whether or not this group has opted to enable Pserver.
1044 * @return boolean enable_pserver.
1046 function enablePserver() {
1047 if ($this->usesSCM()) {
1048 return $this->data_array['enable_pserver'];
1054 function SetUsesPserver($booleanparam) {
1056 $booleanparam = $booleanparam ? 1 : 0;
1057 $res = db_query_params('UPDATE groups SET enable_pserver=$1 WHERE group_id=$2',
1058 array($booleanparam, $this->getID()));
1060 $this->data_array['enable_pserver'] = $booleanparam;
1069 * usesSCM - whether or not this group has opted to use SCM.
1071 * @return boolean uses_scm.
1073 function usesSCM() {
1074 if (forge_get_config('use_scm')) {
1075 return $this->data_array['use_scm'];
1082 * setUseSCM - Set the SCM usage
1084 * @param boolean $booleanparam enabled/disabled
1087 function setUseSCM($booleanparam) {
1089 $booleanparam = $booleanparam ? 1 : 0;
1090 $res = db_query_params('UPDATE groups SET use_scm=$1 WHERE group_id=$2',
1091 array($booleanparam, $this->getID()));
1093 $this->data_array['use_scm']=$booleanparam;
1103 * usesMail - whether or not this group has opted to use mailing lists.
1105 * @return boolean uses_mail.
1107 function usesMail() {
1108 if (forge_get_config('use_mail')) {
1109 return $this->data_array['use_mail'];
1114 $hook_params = array();
1115 $hook_params['group'] = $this;
1116 $hook_params['group_id'] = $this->getID();
1117 $hook_params['group_homepage'] = $this->getHomePage();
1118 $hook_params['group_name'] = $this->getPublicName();
1119 $hook_params['group_description'] = $this->getDescription();
1120 plugin_hook ("group_update", $hook_params);
1124 * setUseMail - Set the mailing-list usage
1126 * @param boolean $booleanparam enabled/disabled
1129 function setUseMail($booleanparam) {
1131 $booleanparam = $booleanparam ? 1 : 0;
1132 $res = db_query_params('UPDATE groups SET use_mail=$1 WHERE group_id=$2',
1133 array($booleanparam, $this->getID()));
1135 $this->data_array['use_mail']=$booleanparam;
1145 * usesNews - whether or not this group has opted to use news.
1147 * @return boolean uses_news.
1149 function usesNews() {
1150 if (forge_get_config('use_news')) {
1151 return $this->data_array['use_news'];
1158 * usesActivity - whether or not this group has opted to display Project Activities.
1160 * @return boolean uses_activities.
1162 function usesActivity() {
1163 if (forge_get_config('use_activity')) {
1164 return $this->data_array['use_activity'];
1171 * usesForum - whether or not this group has opted to use discussion forums.
1173 * @return boolean uses_forum.
1175 function usesForum() {
1176 if (forge_get_config('use_forum')) {
1177 return $this->data_array['use_forum'];
1184 * setUseForum - Set the forum usage
1186 * @param boolean $booleanparam enabled/disabled
1189 function setUseForum($booleanparam) {
1191 $booleanparam = $booleanparam ? 1 : 0;
1192 $res = db_query_params('UPDATE groups SET use_forum=$1 WHERE group_id=$2',
1193 array($booleanparam, $this->getID()));
1195 $this->data_array['use_forum']=$booleanparam;
1205 * usesStats - whether or not this group has opted to use stats.
1207 * @return boolean uses_stats.
1209 function usesStats() {
1210 return $this->data_array['use_stats'];
1214 * usesFRS - whether or not this group has opted to use file release system.
1216 * @return boolean uses_frs.
1218 function usesFRS() {
1219 if (forge_get_config('use_frs')) {
1220 return $this->data_array['use_frs'];
1227 * setUseFRS - Set the FRS usage
1229 * @param boolean $booleanparam enabled/disabled
1232 function setUseFRS($booleanparam) {
1234 $booleanparam = $booleanparam ? 1 : 0;
1235 $res = db_query_params('UPDATE groups SET use_frs=$1 WHERE group_id=$2',
1236 array($booleanparam, $this->getID()));
1238 $this->data_array['use_frs']=$booleanparam;
1248 * usesTracker - whether or not this group has opted to use tracker.
1250 * @return boolean uses_tracker.
1252 function usesTracker() {
1253 if (forge_get_config('use_tracker')) {
1254 return $this->data_array['use_tracker'];
1261 * setUseTracker - Set the tracker usage
1263 * @param boolean $booleanparam enabled/disabled
1266 function setUseTracker ($booleanparam) {
1268 $booleanparam = $booleanparam ? 1 : 0;
1269 $res = db_query_params ('UPDATE groups SET use_tracker=$1 WHERE group_id=$2',
1270 array($booleanparam, $this->getID()));
1272 $this->data_array['use_tracker']=$booleanparam;
1282 * useCreateOnline - whether or not this group has opted to use create online documents option.
1284 * @return boolean use_docman_create_online.
1286 function useCreateOnline() {
1287 if (forge_get_config('use_docman')) {
1288 return $this->data_array['use_docman_create_online'];
1295 * usesDocman - whether or not this group has opted to use docman.
1297 * @return boolean use_docman.
1299 function usesDocman() {
1300 if (forge_get_config('use_docman')) {
1301 return $this->data_array['use_docman'];
1308 * setUseDocman - Set the docman usage
1310 * @param boolean $booleanparam enabled/disabled
1313 function setUseDocman($booleanparam) {
1315 $booleanparam = $booleanparam ? 1 : 0;
1316 $res = db_query_params('UPDATE groups SET use_docman = $1 WHERE group_id = $2',
1317 array($booleanparam, $this->getID()));
1319 // check if / doc_group exists, if not create it
1320 $trashdir = db_query_params('select groupname from doc_groups where groupname = $1 and group_id = $2',
1321 array('.trash', $this->getID()));
1322 if ($trashdir && db_numrows($trashdir) == 0) {
1323 $resinsert = db_query_params('insert into doc_groups (groupname, group_id, stateid) values ($1, $2, $3)',
1324 array('.trash', $this->getID(), '2'));
1330 $this->data_array['use_docman'] = $booleanparam;
1340 * useDocmanSearch - whether or not this group has opted to use docman search engine.
1342 * @return boolean use_docman_search.
1344 function useDocmanSearch() {
1345 if (forge_get_config('use_docman')) {
1346 return $this->data_array['use_docman_search'];
1353 * useWebdav - whether or not this group has opted to use webdav interface.
1355 * @return boolean use_docman_search.
1357 function useWebdav() {
1358 if (forge_get_config('use_webdav')) {
1359 return $this->data_array['use_webdav'];
1366 * usesFTP - whether or not this group has opted to use FTP.
1368 * @return boolean uses_ftp.
1370 function usesFTP() {
1371 if (forge_get_config('use_ftp')) {
1372 return $this->data_array['use_ftp'];
1379 * usesSurvey - whether or not this group has opted to use surveys.
1381 * @return boolean uses_survey.
1383 function usesSurvey() {
1384 if (forge_get_config('use_survey')) {
1385 return $this->data_array['use_survey'];
1392 * usesPM - whether or not this group has opted to Project Manager.
1394 * @return boolean uses_projman.
1397 if (forge_get_config('use_pm')) {
1398 return $this->data_array['use_pm'];
1405 * setUsePM - Set the PM usage
1407 * @param boolean $booleanparam enabled/disabled
1410 function setUsePM($booleanparam) {
1412 $booleanparam = $booleanparam ? 1 : 0;
1413 $res = db_query_params('UPDATE groups SET use_pm=$1 WHERE group_id=$2',
1414 array($booleanparam, $this->getID()));
1416 $this->data_array['use_pm']=$booleanparam;
1426 * getPlugins - get a list of all available group plugins
1428 * @return array array containing plugin_id => plugin_name
1430 function getPlugins() {
1431 if (!isset($this->plugins_data)) {
1432 $this->plugins_data = array();
1433 $res = db_query_params('SELECT group_plugin.plugin_id, plugins.plugin_name
1434 FROM group_plugin, plugins
1435 WHERE group_plugin.group_id=$1
1436 AND group_plugin.plugin_id=plugins.plugin_id', array($this->getID()));
1437 $rows = db_numrows($res);
1439 for ($i=0; $i<$rows; $i++) {
1440 $plugin_id = db_result($res, $i, 'plugin_id');
1441 $this->plugins_data[$plugin_id] = db_result($res, $i, 'plugin_name');
1444 return $this->plugins_data;
1448 * usesPlugin - returns true if the group uses a particular plugin
1450 * @param string $pluginname name of the plugin
1451 * @return boolean whether plugin is being used or not
1453 function usesPlugin($pluginname) {
1454 $plugins_data = $this->getPlugins();
1455 foreach ($plugins_data as $p_id => $p_name) {
1456 if ($p_name == $pluginname) {
1464 * added for Codendi compatibility
1465 * usesServices - returns true if the group uses a particular plugin or feature
1467 * @param string name of the plugin
1468 * @return boolean whether plugin is being used or not
1470 function usesService($feature) {
1471 $plugins_data = $this->getPlugins();
1472 $pm = plugin_manager_get_object();
1473 foreach ($plugins_data as $p_id => $p_name) {
1474 if ($p_name == $feature) {
1477 if ($pm->getPluginByName($p_name)->provide($feature)) {
1485 * setPluginUse - enables/disables plugins for the group
1487 * @param string $pluginname name of the plugin
1488 * @param boolean $val the new state
1489 * @return string database result
1491 function setPluginUse($pluginname, $val=true) {
1492 if ($val == $this->usesPlugin($pluginname)) {
1493 // State is already good, returning
1496 $res = db_query_params('SELECT plugin_id FROM plugins WHERE plugin_name=$1',
1497 array($pluginname));
1498 $rows = db_numrows($res);
1500 // Error: no plugin by that name
1503 $plugin_id = db_result($res,0,'plugin_id');
1505 unset($this->plugins_data);
1507 $res = db_query_params('INSERT INTO group_plugin (group_id, plugin_id) VALUES ($1, $2)',
1508 array($this->getID(),
1512 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1 AND plugin_id=$2',
1513 array($this->getID(),
1517 $this->normalizeAllRoles();
1521 * getDocEmailAddress - get email address(es) to send doc notifications to.
1523 * @return string email address.
1525 function getDocEmailAddress() {
1526 return $this->data_array['new_doc_address'];
1530 * DocEmailAll - whether or not this group has opted to use receive notices on all doc updates.
1532 * @return boolean email_on_all_doc_updates.
1534 function docEmailAll() {
1535 return $this->data_array['send_all_docs'];
1539 * getHomePage - The URL for this project's home page.
1541 * @return string homepage URL.
1543 function getHomePage() {
1544 if (!preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*:/",
1545 $this->data_array['homepage'])) {
1546 $this->data_array['homepage'] = util_url_prefix() .
1547 $this->data_array['homepage'];
1549 return $this->data_array['homepage'];
1553 * getTags - Tags of this project.
1555 * @return string List of tags. Comma separated
1557 function getTags() {
1558 $sql = 'SELECT name FROM project_tags WHERE group_id = $1';
1559 $res = db_query_params($sql, array($this->getID()));
1560 return join(', ', util_result_column_to_array($res));
1564 * setTags - Set tags of this project.
1566 * @param string $tags
1567 * @return string database result.
1569 function setTags($tags) {
1571 $sql = 'DELETE FROM project_tags WHERE group_id=$1';
1572 $res = db_query_params($sql, array($this->getID()));
1574 $this->setError('Deleting old tags: '.db_error());
1578 $inserted = array();
1579 $tags_array = preg_split('/[;,]/', $tags);
1580 foreach ($tags_array as $tag) {
1581 $tag = preg_replace('/[\t\r\n]/', ' ', $tag);
1582 // Allowed caracteres: [A-Z][a-z][0-9] -_&'#+.
1583 if (preg_match('/[^[:alnum:]| |\-|_|\&|\'|#|\+|\.]/', $tag)) {
1584 $this->setError(_('Bad tag name, you only can use the following characters: [A-Z][a-z][0-9]-_&\'#+. and space'));
1589 if ($tag == '' || array_search($tag, $inserted) !== false) continue;
1590 $sql = 'INSERT INTO project_tags (group_id,name) VALUES ($1, $2)';
1591 $res = db_query_params($sql, array($this->getID(), $tag));
1593 $this->setError(_('Setting tags:') . ' ' .
1605 * getPermission - Return a Permission for this Group
1607 * @return object The Permission.
1609 function &getPermission() {
1610 return permission_get_object($this);
1613 function delete($sure, $really_sure, $really_really_sure) {
1614 if (!$sure || !$really_sure || !$really_really_sure) {
1615 $this->setMissingParamsError(_('Please tick all checkboxes.'));
1618 if ($this->getID() == forge_get_config('news_group') ||
1619 $this->getID() == 1 ||
1620 $this->getID() == forge_get_config('stats_group') ||
1621 $this->getID() == forge_get_config('peer_rating_group')) {
1622 $this->setError(_('Cannot Delete System Group'));
1625 $perm = $this->getPermission();
1626 if (!$perm || !is_object($perm)) {
1627 $this->setPermissionDeniedError();
1629 } elseif ($perm->isError()) {
1630 $this->setPermissionDeniedError();
1632 } elseif (!$perm->isSuperUser()) {
1633 $this->setPermissionDeniedError();
1639 // Remove all the members
1641 $members = $this->getMembers();
1642 foreach ($members as $i) {
1643 if(!$this->removeUser($i->getID())) {
1644 $this->setError(_('Could not properly remove member:').' '.$i->getID());
1649 // unlink roles from this project
1650 foreach ($this->getRoles() as $r) {
1651 if ($r->getHomeProject() == NULL
1652 || $r->getHomeProject()->getID() != $this->getID()) {
1653 $r->unlinkProject($this);
1660 if ($this->usesTracker()) {
1661 $atf = new ArtifactTypeFactory($this);
1662 $at_arr = $atf->getArtifactTypes();
1663 foreach ($at_arr as $i) {
1664 if (!is_object($i)) {
1667 if (!$i->delete(1,1)) {
1668 $this->setError(_('Could not properly delete the tracker:').' '.$i->getErrorMessage());
1677 if ($this->usesForum()) {
1678 $ff = new ForumFactory($this);
1679 $f_arr = $ff->getForums();
1680 foreach ($f_arr as $i) {
1681 if (!is_object($i)) {
1684 if(!$i->delete(1,1)) {
1685 $this->setError(_('Could not properly delete the forum:').' '.$i->getErrorMessage());
1691 // Delete Subprojects
1693 if ($this->usesPM()) {
1694 $pgf = new ProjectGroupFactory($this);
1695 $pg_arr = $pgf->getProjectGroups();
1696 foreach ($pg_arr as $i) {
1697 if (!is_object($i)) {
1700 if (!$i->delete(1,1)) {
1701 $this->setError(_('Could not properly delete the ProjectGroup:').' '.$i->getErrorMessage());
1707 // Delete FRS Packages
1709 $res = db_query_params('SELECT * FROM frs_package WHERE group_id=$1',
1710 array($this->getID()));
1712 $this->setError(_('Error FRS Packages: ').db_error());
1717 while ($arr = db_fetch_array($res)) {
1718 $frsp=new FRSPackage($this, $arr['package_id'], $arr);
1719 if (!$frsp->delete(1, 1)) {
1720 $this->setError(_('Could not properly delete the FRSPackage:').' '.$frsp->getErrorMessage());
1727 $news_group=group_get_object(forge_get_config('news_group'));
1728 $res = db_query_params('SELECT forum_id FROM news_bytes WHERE group_id=$1',
1729 array($this->getID()));
1731 $this->setError(_('Error Deleting News: ').db_error());
1736 for ($i=0; $i<db_numrows($res); $i++) {
1737 $Forum = new Forum($news_group,db_result($res,$i,'forum_id'));
1738 if (!$Forum->delete(1,1)) {
1739 $this->setError(_("Could Not Delete News Forum: %d"),$Forum->getID());
1743 $res = db_query_params('DELETE FROM news_bytes WHERE group_id=$1',
1744 array($this->getID()));
1746 $this->setError(_('Error Deleting News: ').db_error());
1754 $res = db_query_params('DELETE FROM doc_data WHERE group_id=$1',
1755 array($this->getID()));
1757 $this->setError(_('Error Deleting Documents: ').db_error());
1762 $res = db_query_params('DELETE FROM doc_groups WHERE group_id=$1',
1763 array($this->getID()));
1765 $this->setError(_('Error Deleting Documents: ').db_error());
1773 $res=db_query_params('DELETE FROM project_tags WHERE group_id=$1', array($this->getID()));
1775 $this->setError(_('Error Deleting Tags: ').db_error());
1781 // Delete group history
1783 $res = db_query_params('DELETE FROM group_history WHERE group_id=$1',
1784 array($this->getID()));
1786 $this->setError(_('Error Deleting Project History: ').db_error());
1792 // Delete group plugins
1794 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1',
1795 array($this->getID()));
1797 $this->setError(_('Error Deleting Project Plugins: ').db_error());
1803 // Delete group cvs stats
1805 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE group_id=$1',
1806 array($this->getID()));
1808 $this->setError(_('Error Deleting SCM Statistics: ').db_error());
1816 if ($this->usesSurvey()) {
1817 $sf = new SurveyFactory($this);
1818 $s_arr =& $sf->getSurveys();
1819 foreach ($s_arr as $i) {
1820 if (!is_object($i)) {
1823 if (!$i->delete()) {
1824 $this->setError(_('Could not properly delete the survey'));
1830 // Delete SurveyQuestions
1832 $sqf = new SurveyQuestionFactory($this);
1833 $sq_arr = $sqf->getSurveyQuestions();
1834 if (is_array($sq_arr)) {
1835 foreach ($sq_arr as $i) {
1836 if (!is_object($i)) {
1839 if (!$i->delete()) {
1840 $this->setError(_('Could not properly delete the survey questions'));
1848 // Delete Mailing List Factory
1850 if ($this->usesMail()) {
1851 $mlf = new MailingListFactory($this);
1852 $ml_arr = $mlf->getMailingLists();
1853 foreach ($ml_arr as $i) {
1854 if (!is_object($i)) {
1857 if (!$i->delete(1,1)) {
1858 $this->setError(_('Could not properly delete the mailing list'));
1867 $res = db_query_params('DELETE FROM trove_group_link WHERE group_id=$1',
1868 array($this->getID()));
1870 $this->setError(_('Error Deleting Trove: ').db_error());
1875 $res = db_query_params('DELETE FROM trove_agg WHERE group_id=$1',
1876 array($this->getID()));
1878 $this->setError(_('Error Deleting Trove: ').db_error());
1886 $res = db_query_params('DELETE FROM project_sums_agg WHERE group_id=$1',
1887 array($this->getID()));
1889 $this->setError(_('Error Deleting Counters: ').db_error());
1894 $res = db_query_params('INSERT INTO deleted_groups (unix_group_name, delete_date, isdeleted) VALUES ($1, $2, $3)',
1895 array($this->getUnixName(),
1899 $this->setError(_('Error Deleting Project:').' '.db_error());
1904 // Delete entry in groups.
1905 $res = db_query_params('DELETE FROM groups WHERE group_id=$1',
1906 array($this->getID()));
1908 $this->setError(_('Error Deleting Project:').' '.db_error());
1915 $hook_params = array();
1916 $hook_params['group'] = $this;
1917 $hook_params['group_id'] = $this->getID();
1918 plugin_hook("group_delete", $hook_params);
1920 if (forge_get_config('upload_dir') != '' && $this->getUnixName()) {
1921 exec('/bin/rm -rf '.forge_get_config('upload_dir').'/'.$this->getUnixName().'/');
1923 if (forge_get_config('ftp_upload_dir') != '' && $this->getUnixName()) {
1924 exec('/bin/rm -rf '.forge_get_config('ftp_upload_dir').'/'.$this->getUnixName().'/');
1929 $res = db_query_params('DELETE FROM rep_group_act_monthly WHERE group_id=$1',
1930 array($this->getID()));
1931 //echo 'rep_group_act_monthly'.db_error();
1932 $res = db_query_params('DELETE FROM rep_group_act_weekly WHERE group_id=$1',
1933 array($this->getID()));
1934 //echo 'rep_group_act_weekly'.db_error();
1935 $res = db_query_params('DELETE FROM rep_group_act_daily WHERE group_id=$1',
1936 array($this->getID()));
1937 //echo 'rep_group_act_daily'.db_error();
1938 unset($this->data_array);
1943 Basic functions to add/remove users to/from a group
1944 and update their permissions
1948 * addUser - controls adding a user to a group.
1950 * @param string $user_identifier Unix name of the user to add OR integer user_id.
1951 * @param int $role_id The role_id this user should have.
1952 * @return boolean success.
1955 function addUser($user_identifier, $role_id) {
1958 Admins can add users to groups
1961 if (!forge_check_perm ('project_admin', $this->getID())) {
1962 $this->setPermissionDeniedError();
1968 get user id for this user's unix_name
1970 if (is_int ($user_identifier)) { // user_id or user_name
1971 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_id=$1', array($user_identifier));
1973 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_name=$1', array($user_identifier));
1975 if (db_numrows($res_newuser) > 0) {
1977 // make sure user is active
1979 if (db_result($res_newuser,0,'status') != 'A') {
1980 $this->setError(_('User is not active. Only active users can be added.'));
1986 // user was found - set new user_id var
1988 $user_id = db_result($res_newuser,0,'user_id');
1990 $role = new Role($this, $role_id);
1991 if (!$role || !is_object($role)) {
1992 $this->setError(_('Error Getting Role Object'));
1995 } elseif ($role->isError()) {
1996 $this->setError('addUser::roleget::'.$role->getErrorMessage());
2001 $role->addUser(user_get_object($user_id));
2002 if (!$SYS->sysCheckCreateGroup($this->getID())){
2003 $this->setError($SYS->getErrorMessage());
2007 if (!$SYS->sysCheckCreateUser($user_id)) {
2008 $this->setError($SYS->getErrorMessage());
2012 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
2013 $this->setError($SYS->getErrorMessage());
2019 // user doesn't exist
2021 $this->setError(_('That user does not exist.'));
2026 $hook_params['group'] = $this;
2027 $hook_params['group_id'] = $this->getID();
2028 $hook_params['user'] = user_get_object($user_id);
2029 $hook_params['user_id'] = $user_id;
2030 plugin_hook ("group_adduser", $hook_params);
2035 $this->addHistory('Added User',$user_identifier);
2041 * removeUser - controls removing a user from a group.
2043 * Users can remove themselves.
2045 * @param int $user_id The ID of the user to remove.
2046 * @return boolean success.
2048 function removeUser($user_id) {
2051 if ($user_id != user_getid()
2052 && !forge_check_perm('project_admin', $this->getID())) {
2053 $this->setPermissionDeniedError();
2059 $user = user_get_object($user_id);
2060 $roles = RBACEngine::getInstance()->getAvailableRolesForUser($user);
2062 foreach ($roles as $role) {
2063 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2064 $found_role = $role;
2068 if ($found_role == NULL) {
2069 $this->setError(sprintf(_('Error: User not removed: %s')));
2073 $found_role->removeUser($user);
2074 if (!$SYS->sysGroupCheckUser($this->getID(), $user_id)) {
2075 $this->setError($SYS->getErrorMessage());
2081 // reassign open artifacts to id=100
2083 $res = db_query_params('UPDATE artifact SET assigned_to=100
2084 WHERE group_artifact_id
2085 IN (SELECT group_artifact_id
2086 FROM artifact_group_list
2087 WHERE group_id=$1 AND status_id=1 AND assigned_to=$2)',
2088 array($this->getID(),
2091 $this->setError(_('Error: artifact:').' '.db_error());
2097 // reassign open tasks to id=100
2098 // first have to purge any assignments that would cause
2099 // conflict with existing assignment to 100
2101 $res = db_query_params('DELETE FROM project_assigned_to
2102 WHERE project_task_id IN (SELECT pt.project_task_id
2103 FROM project_task pt, project_group_list pgl, project_assigned_to pat
2104 WHERE pt.group_project_id = pgl.group_project_id
2105 AND pat.project_task_id=pt.project_task_id
2106 AND pt.status_id=1 AND pgl.group_id=$1
2107 AND pat.assigned_to_id=$2)
2108 AND assigned_to_id=100',
2109 array($this->getID(),
2112 $this->setError(sprintf(_('Error: project_assigned_to %d: %s'), 1, db_error()));
2116 $res = db_query_params('UPDATE project_assigned_to SET assigned_to_id=100
2117 WHERE project_task_id IN (SELECT pt.project_task_id
2118 FROM project_task pt, project_group_list pgl
2119 WHERE pt.group_project_id = pgl.group_project_id
2120 AND pt.status_id=1 AND pgl.group_id=$1)
2121 AND assigned_to_id=$2',
2122 array($this->getID(),
2125 $this->setError(sprintf(_('Error: project_assigned_to %d: %s'), 2, db_error()));
2131 // Remove user from system
2133 if (!$SYS->sysGroupRemoveUser($this->getID(), $user_id)) {
2134 $this->setError($SYS->getErrorMessage());
2139 $hook_params['group'] = $this;
2140 $hook_params['group_id'] = $this->getID();
2141 $hook_params['user'] = user_get_object($user_id);
2142 $hook_params['user_id'] = $user_id;
2143 plugin_hook ("group_removeuser", $hook_params);
2146 $this->addHistory('Removed User',$user_id);
2153 * updateUser - controls updating a user's role in this group.
2155 * @param int $user_id The ID of the user.
2156 * @param int $role_id The role_id to set this user to.
2157 * @return boolean success.
2159 function updateUser($user_id, $role_id) {
2161 if (!forge_check_perm ('project_admin', $this->getID())) {
2162 $this->setPermissionDeniedError();
2166 $newrole = RBACEngine::getInstance()->getRoleById ($role_id);
2167 if (!$newrole || !is_object($newrole)) {
2168 $this->setError(_('Could Not Get Role'));
2170 } elseif ($newrole->isError()) {
2171 $this->setError(sprintf(_('Role: %s'),$role->getErrorMessage()));
2173 } elseif ($newrole->getHomeProject() == NULL
2174 || $newrole->getHomeProject()->getID() != $this->getID()) {
2175 $this->setError(_('Wrong destination role'));
2178 $user = user_get_object ($user_id);
2179 $roles = RBACEngine::getInstance()->getAvailableRolesForUser ($user);
2181 foreach ($roles as $role) {
2182 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2183 $found_role = $role;
2187 if ($found_role == NULL) {
2188 $this->setError(sprintf(_('Error: User not removed: %s')));
2192 $found_role->removeUser ($user);
2193 $newrole->addUser ($user);
2195 $this->addHistory('Updated User',$user_id);
2200 * addHistory - Makes an audit trail entry for this project.
2202 * @param string $field_name The name of the field.
2203 * @param string $old_value The Old Value for this $field_name.
2204 * @return resource database result handle.
2207 function addHistory($field_name, $old_value) {
2208 return db_query_params ('INSERT INTO group_history(group_id,field_name,old_value,mod_by,adddate)
2209 VALUES ($1,$2,$3,$4,$5)',
2210 array($this->getID(),
2218 * activateUsers - Make sure that group members have unix accounts.
2220 * Setup unix accounts for group members. Can be called even
2221 * if members are already active.
2225 function activateUsers() {
2227 Activate member(s) of the project
2230 $members = $this->getUsers (true);
2232 foreach ($members as $member) {
2234 foreach (RBACEngine::getInstance()->getAvailableRolesForUser ($member) as $role) {
2235 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2239 foreach ($roles as $role) {
2240 if (!$this->addUser($member->getUnixName(),$role->getID())) {
2251 * getMembers - returns array of User objects for this project
2253 * @return array of User objects for this group.
2255 function getMembers() {
2256 return $this->getUsers (true);
2260 * replaceTemplateStrings - fill-in some blanks with project name
2262 * @param string Template string
2263 * @return string String after replacements
2265 function replaceTemplateStrings($string) {
2266 $string = str_replace ('UNIXNAME', $this->getUnixName(), $string);
2267 $string = str_replace ('PUBLICNAME', $this->getPublicName(), $string);
2268 $string = str_replace ('DESCRIPTION', $this->getDescription(), $string);
2273 * approve - Approve pending project.
2275 * @param $user The User object who is doing the updating.
2279 function approve(&$user) {
2280 global $gfcommon,$gfwww;
2281 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
2283 if ($this->getStatus()=='A') {
2284 $this->setError(_("Group already active"));
2290 // Step 1: Activate group and create LDAP entries
2291 if (!$this->setStatus($user, 'A')) {
2296 // Switch to system language for item creation
2297 setup_gettext_from_sys_lang();
2299 // Create default roles
2300 $idadmin_group = NULL;
2301 foreach (get_group_join_requests ($this) as $gjr) {
2302 $idadmin_group = $gjr->getUserID();
2305 if ($idadmin_group == NULL) {
2306 $idadmin_group = $user->getID();
2309 $template = $this->getTemplateProject();
2310 $id_mappings = array();
2311 $seen_admin_role = false;
2313 // Copy roles from template project
2314 foreach($template->getRoles() as $oldrole) {
2315 if ($oldrole->getHomeProject() != NULL) {
2316 $role = new Role($this);
2318 // Need to use a different role name so that the permissions aren't set from the hardcoded defaults
2319 $role->create('TEMPORARY ROLE NAME', $data, true);
2320 $role->setName($oldrole->getName());
2321 if ($oldrole->getSetting ('project_admin', $template->getID())) {
2322 $seen_admin_role = true;
2326 $role->linkProject($this);
2328 $id_mappings['role'][$oldrole->getID()] = $role->getID();
2329 // Reuse the project_admin permission
2330 $role->setSetting ('project_admin', $this->getID(), $oldrole->getSetting ('project_admin', $template->getID()));
2334 if (!$seen_admin_role) {
2335 $role = new Role($this);
2336 $adminperms = array('project_admin' => array ($this->getID() => 1));
2337 $role_id = $role->create ('Admin', $adminperms, true);
2340 $roles = $this->getRoles();
2341 foreach ($roles as $r) {
2342 if ($r->getHomeProject() == NULL) {
2345 if ($r->getSetting ('project_admin', $this->getID())) {
2346 $r->addUser(user_get_object ($idadmin_group));
2350 // Temporarily switch to the submitter's identity
2351 $saved_session = session_get_user();
2352 session_set_internal($idadmin_group);
2355 if (forge_get_config('use_tracker')) {
2356 $this->setUseTracker ($template->usesTracker());
2357 if ($template->usesTracker()) {
2358 $oldatf = new ArtifactTypeFactory($template);
2359 foreach ($oldatf->getArtifactTypes() as $o) {
2360 $t = new ArtifactType ($this);
2361 $t->create ($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->emailAll(),$o->getEmailAddress(),$o->getDuePeriod()/86400,0,$o->getSubmitInstructions(),$o->getBrowseInstructions());
2362 $id_mappings['tracker'][$o->getID()] = $t->getID();
2363 $t->cloneFieldsFrom ($o->getID());
2368 if (forge_get_config('use_pm')) {
2369 $this->setUsePM ($template->usesPM());
2370 if ($template->usesPM()) {
2371 $oldpgf = new ProjectGroupFactory($template);
2372 foreach ($oldpgf->getProjectGroups() as $o) {
2373 $pg = new ProjectGroup($this);
2374 $pg->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo());
2375 $id_mappings['pm'][$o->getID()] = $pg->getID();
2380 if (forge_get_config('use_forum')) {
2381 $this->setUseForum($template->usesForum());
2382 if ($template->usesForum()) {
2383 $oldff = new ForumFactory($template);
2384 foreach ($oldff->getForums() as $o) {
2385 $f = new Forum($this);
2386 $f->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo(),1);
2387 $id_mappings['forum'][$o->getID()] = $f->getID();
2392 if (forge_get_config('use_docman')) {
2393 $this->setUseDocman($template->usesDocman());
2394 if ($template->usesDocman()) {
2395 $olddgf = new DocumentGroupFactory($template);
2396 // First pass: create all docgroups
2397 $id_mappings['docman_docgroup'][0] = 0;
2398 foreach ($olddgf->getDocumentGroups() as $o) {
2399 $ndgf = new DocumentGroup($this);
2400 // .trash is a reserved directory
2401 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2402 $ndgf->create($this->replaceTemplateStrings($o->getName()));
2403 $id_mappings['docman_docgroup'][$o->getID()] = $ndgf->getID();
2406 // Second pass: restore hierarchy links
2407 foreach ($olddgf->getDocumentGroups() as $o) {
2408 $ndgf = new DocumentGroup($this);
2409 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2410 $ndgf->fetchData($id_mappings['docman_docgroup'][$o->getID()]);
2411 $ndgf->update($ndgf->getName(), $id_mappings['docman_docgroup'][$o->getParentID()]);
2417 if (forge_get_config('use_frs')) {
2418 $this->setUseFRS ($template->usesFRS());
2419 if ($template->usesFRS()) {
2420 foreach (get_frs_packages($template) as $o) {
2421 $newp = new FRSPackage($this);
2422 $nname = $this->replaceTemplateStrings($o->getName());
2423 $newp->create ($nname, $o->isPublic());
2428 if (forge_get_config('use_mail')) {
2429 $this->setUseMail($template->usesMail());
2430 if ($template->usesMail()) {
2431 $oldmlf = new MailingListFactory($template);
2432 foreach ($oldmlf->getMailingLists() as $o) {
2433 $ml = new MailingList($this);
2434 $nname = preg_replace ('/^'.$template->getUnixName().'-/','',$o->getName());
2436 $ndescription = $this->replaceTemplateStrings($o->getDescription());
2437 $ml->create($nname, $ndescription, $o->isPublic());
2443 /* use SCM plugin from template group */
2444 $this->setUseSCM($template->usesSCM());
2446 foreach ($template->getPlugins() as
2447 $plugin_id => $plugin_name) {
2448 $this->setPluginUse($plugin_name);
2451 /* use SCM choice from registration page */
2453 foreach ($template->getPlugins() as
2454 $plugin_id => $plugin_name) {
2455 if (substr($plugin_name, 3) == 'scm' &&
2456 $plugin_name != 'scmhook') {
2457 /* skip copying scm plugins */
2460 /* enable other plugins though */
2461 $this->setPluginUse($plugin_name);
2465 foreach ($template->getRoles() as $oldrole) {
2466 $newrole = RBACEngine::getInstance()->getRoleById ($id_mappings['role'][$oldrole->getID()]);
2467 if ($oldrole->getHomeProject() != NULL
2468 && $oldrole->getHomeProject()->getID() == $template->getID()) {
2469 $newrole->setPublic ($oldrole->isPublic());
2471 $oldsettings = $oldrole->getSettingsForProject ($template);
2473 $sections = array('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm');
2474 foreach ($sections as $section) {
2475 $newrole->setSetting ($section, $this->getID(), $oldsettings[$section][$template->getID()]);
2478 $sections = array('tracker', 'pm', 'forum');
2479 foreach ($sections as $section) {
2480 if (isset ($oldsettings[$section])) {
2481 foreach ($oldsettings[$section] as $k => $v) {
2482 // Only copy perms for tools that have been copied
2483 if (isset ($id_mappings[$section][$k])) {
2484 $newrole->setSetting ($section,
2485 $id_mappings[$section][$k],
2493 $lm = new WidgetLayoutManager();
2494 $lm->createDefaultLayoutForProject ($this->getID(), $template->getID());
2497 $params['template'] = $template;
2498 $params['project'] = $this;
2499 $params['id_mappings'] = $id_mappings;
2500 plugin_hook_by_reference ('clone_project_from_template', $params);
2502 // Disable everything
2503 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',
2504 array($this->getID()));
2507 $this->normalizeAllRoles();
2508 // empty members cache because the group creator is not yet in cache.
2509 unset($this->membersArr);
2510 $this->activateUsers();
2512 // Delete fake join request
2513 foreach (get_group_join_requests ($this) as $gjr) {
2517 // Switch back to user preference
2518 session_set_internal($saved_session->getID());
2519 setup_gettext_from_context();
2523 $this->sendApprovalEmail();
2524 $this->addHistory(_('Approved'), 'x');
2527 // Plugin can make approve operation there
2530 $params['group'] = $this;
2531 $params['group_id'] = $this->getID();
2532 plugin_hook('group_approved', $params);
2538 * sendApprovalEmail - Send new project email.
2540 * @return boolean success.
2543 function sendApprovalEmail() {
2544 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('project_admin', $this->getID());
2546 if (count($admins) < 1) {
2547 $this->setError(_("Group does not have any administrators."));
2551 // send one email per admin
2552 foreach ($admins as $admin) {
2553 setup_gettext_for_user ($admin);
2555 $message=sprintf(_('Your project registration for %4$s has been approved.
2557 Project Full Name: %1$s
2558 Project Unix Name: %2$s
2560 Your DNS will take up to a day to become active on our site.
2561 Your web site is accessible through your shell account. Please read
2562 site documentation (see link below) about intended usage, available
2563 services, and directory layout of the account.
2566 own project page in %4$s while logged in, you will find
2567 additional menu functions to your left labeled \'Project Admin\'.
2569 We highly suggest that you now visit %4$s and create a public
2570 description for your project. This can be done by visiting your project
2571 page while logged in, and selecting \'Project Admin\' from the menus
2572 on the left (or by visiting %3$s
2575 Your project will also not appear in the Trove Software Map (primary
2576 list of projects hosted on %4$s which offers great flexibility in
2577 browsing and search) until you categorize it in the project administration
2578 screens. So that people can find your project, you should do this now.
2579 Visit your project while logged in, and select \'Project Admin\' from the
2582 Enjoy the system, and please tell others about %4$s. Let us know
2583 if there is anything we can do to help you.
2586 htmlspecialchars_decode($this->getPublicName()),
2587 $this->getUnixName(),
2588 util_make_url ('/project/admin/?group_id='.$this->getID()),
2589 forge_get_config ('forge_name'));
2591 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Approved'), forge_get_config ('forge_name')), $message);
2593 setup_gettext_from_context();
2600 * sendRejectionEmail - Send project rejection email.
2602 * This function sends out a rejection message to a user who
2603 * registered a project.
2605 * @param int $response_id The id of the response to use.
2606 * @param string $message The rejection message.
2607 * @return bool completion status.
2610 function sendRejectionEmail($response_id, $message="zxcv") {
2611 $submitters = array();
2612 foreach (get_group_join_requests ($this) as $gjr) {
2613 $submitters[] = user_get_object($gjr->getUserID());
2616 if (count ($submitters) < 1) {
2617 $this->setError(_("Group does not have any administrators."));
2621 foreach ($submitters as $admin) {
2622 setup_gettext_for_user($admin);
2624 $response = sprintf(_('Your project registration for %s has been denied.'), forge_get_config('forge_name')) . "\n\n"
2625 . _('Project Full Name')._(': '). $this->getPublicName() . "\n"
2626 . _('Project Unix Name')._(': '). $this->getUnixName() . "\n\n"
2627 . _('Reasons for negative decision')._(': ') . "\n\n";
2629 // Check to see if they want to send a custom rejection response
2630 if ($response_id == 0) {
2631 $response .= $message;
2633 $response .= db_result(
2634 db_query_params('SELECT response_text FROM canned_responses WHERE response_id=$1', array($response_id)),
2639 util_send_message($admin->getEmail(), sprintf(_('%s Project Denied'), forge_get_config ('forge_name')), $response);
2640 setup_gettext_from_context();
2647 * sendNewProjectNotificationEmail - Send new project notification email.
2649 * This function sends out a notification email to the
2650 * SourceForge admin user when a new project is
2653 * @return boolean success.
2656 function sendNewProjectNotificationEmail() {
2657 // Get the user who wants to register the project
2658 $submitters = array();
2659 foreach (get_group_join_requests ($this) as $gjr) {
2660 $submitters[] = user_get_object($gjr->getUserID());
2662 if (count ($submitters) < 1) {
2663 $this->setError(_("Could not find user who has submitted the project."));
2667 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('approve_projects', -1);
2669 if (count($admins) < 1) {
2670 $this->setError(_("There is no administrator to send the mail to."));
2674 foreach ($admins as $admin) {
2675 $admin_email = $admin->getEmail();
2676 setup_gettext_for_user ($admin);
2678 $message = sprintf(_('New %1$s Project Submitted
2680 Project Full Name: %2$s
2681 Submitted Description: %3$s
2683 forge_get_config ('forge_name'),
2684 htmlspecialchars_decode($this->getPublicName()),
2685 htmlspecialchars_decode($this->getRegistrationPurpose()));
2687 foreach ($submitters as $submitter) {
2688 $message .= sprintf(_('Submitter: %1$s (%2$s)
2690 $submitter->getRealName(),
2691 $submitter->getUnixName());
2695 . _('Please visit the following URL to approve or reject this project')._(': '). "\n"
2696 . util_make_url('/admin/approve-pending.php');
2697 util_send_message($admin_email, sprintf(_('New %s Project Submitted'), forge_get_config('forge_name')), $message);
2698 setup_gettext_from_context();
2701 $email = $submitter->getEmail();
2702 setup_gettext_for_user ($submitter);
2704 $message = sprintf(_('New %1$s Project Submitted
2706 Project Full Name: %2$s
2707 Submitted Description: %3$s
2709 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'));
2711 util_send_message($email, sprintf(_('New %s Project Submitted'), forge_get_config ('forge_name')), $message);
2712 setup_gettext_from_context();
2718 * validateGroupName - Validate the group name
2720 * @param string Group name.
2722 * @return boolean an error false and set an error is the group name is invalid otherwise return true
2724 function validateGroupName($group_name) {
2725 if (strlen($group_name)<3) {
2726 $this->setError(_('Group name is too short'));
2728 } elseif (strlen(htmlspecialchars($group_name))>40) {
2729 $this->setError(_('Group name is too long'));
2731 } elseif (group_get_object_by_publicname($group_name)) {
2732 $this->setError(_('Group name already taken'));
2739 * getRolesId - Get Ids of the roles of the group.
2741 * @return array Role ids of this group.
2743 function getRolesId() {
2744 $role_ids = array();
2746 $res = db_query_params('SELECT role_id FROM pfo_role WHERE home_group_id=$1',
2747 array($this->getID()));
2748 while ($arr = db_fetch_array($res)) {
2749 $role_ids[] = $arr['role_id'];
2751 $res = db_query_params('SELECT role_id FROM role_project_refs WHERE group_id=$1',
2752 array($this->getID()));
2753 while ($arr = db_fetch_array($res)) {
2754 $role_ids[] = $arr['role_id'];
2757 return array_unique($role_ids);
2761 * getRoles - Get the roles of the group.
2763 * @return array Roles of this group.
2765 function getRoles() {
2768 $roles = $this->getRolesId();
2769 $engine = RBACEngine::getInstance();
2770 foreach ($roles as $role_id) {
2771 $result[] = $engine->getRoleById ($role_id);
2777 function normalizeAllRoles() {
2778 $roles = $this->getRoles();
2780 foreach ($roles as $r) {
2781 $r->normalizeData();
2786 * getUnixStatus - Status of activation of unix account.
2788 * @return string Values: (N)one, (A)ctive, (S)uspended or (D)eleted
2790 function getUnixStatus() {
2791 return $this->data_array['unix_status'];
2795 * setUnixStatus - Sets status of activation of unix account.
2797 * @param string $status The unix status.
2803 * @return boolean success.
2805 function setUnixStatus($status) {
2808 $res = db_query_params ('UPDATE groups SET unix_status=$1 WHERE group_id=$2',
2813 $this->setError(sprintf(_('Error: Cannot Update Group Unix Status: %s'),db_error()));
2817 if ($status == 'A') {
2818 if (!$SYS->sysCheckCreateGroup($this->getID())) {
2819 $this->setError($SYS->getErrorMessage());
2824 if ($SYS->sysCheckGroup($this->getID())) {
2825 if (!$SYS->sysRemoveGroup($this->getID())) {
2826 $this->setError($SYS->getErrorMessage());
2833 $this->data_array['unix_status']=$status;
2840 * getUsers - Get the users of a group
2842 * @return array of user's objects.
2844 function getUsers($onlylocal = true) {
2845 if (!isset($this->membersArr)) {
2846 $this->membersArr = array();
2849 foreach ($this->getRoles() as $role) {
2851 && ($role->getHomeProject() == NULL || $role->getHomeProject()->getID() != $this->getID())) {
2854 foreach ($role->getUsers() as $user) {
2855 $ids[] = $user->getID();
2858 $ids = array_unique ($ids);
2859 foreach ($ids as $id) {
2860 $u = user_get_object ($id);
2861 if ($u->isActive()) {
2862 $this->membersArr[] = $u;
2866 return $this->membersArr;
2869 function setDocmanCreateOnlineStatus($status) {
2871 /* if we activate search engine, we probably want to reindex */
2872 $res = db_query_params('UPDATE groups SET use_docman_create_online=$1 WHERE group_id=$2',
2873 array($status, $this->getID()));
2876 $this->setError(sprintf(_('Error: Cannot Update Group DocmanCreateOnline Status: %s'),db_error()));
2880 $this->data_array['use_docman_create_online']=$status;
2886 function setDocmanWebdav($status) {
2888 /* if we activate search engine, we probably want to reindex */
2889 $res = db_query_params('UPDATE groups SET use_webdav=$1 WHERE group_id=$2',
2894 $this->setError(sprintf(_('Error: Cannot Update Group UseWebdab Status: %s'),db_error()));
2898 $this->data_array['use_webdav']=$status;
2904 function setDocmanSearchStatus($status) {
2906 /* if we activate search engine, we probably want to reindex */
2907 $res = db_query_params('UPDATE groups SET use_docman_search=$1, force_docman_reindex=$1 WHERE group_id=$2',
2912 $this->setError(sprintf(_('Error: Cannot Update Group UseDocmanSearch Status: %s'),db_error()));
2916 $this->data_array['use_docman_search']=$status;
2922 function setDocmanForceReindexSearch($status) {
2924 /* if we activate search engine, we probably want to reindex */
2925 $res = db_query_params('UPDATE groups SET force_docman_reindex=$1 WHERE group_id=$2',
2930 $this->setError(sprintf(_('Error: Cannot Update Group force_docman_reindex %s'),db_error()));
2934 $this->data_array['force_docman_reindex']=$status;
2942 * group_getname() - get the group name
2944 * @param int $group_id The group ID
2949 function group_getname ($group_id = 0) {
2950 $grp = group_get_object($group_id);
2952 return $grp->getPublicName();
2959 * group_getunixname() - get the unixname for a group
2961 * @param int $group_id The group ID
2966 function group_getunixname ($group_id) {
2967 $grp = group_get_object($group_id);
2969 return $grp->getUnixName();
2976 * group_get_result() - Get the group object result ID.
2978 * @param int $group_id The group ID
2983 function &group_get_result($group_id=0) {
2984 $grp = group_get_object($group_id);
2986 return $grp->getData();
2992 function getAllProjectTags($onlyvisible = true) {
2993 $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',
2996 if (!$res || db_numrows($res) == 0) {
3002 while ($arr = db_fetch_array($res)) {
3004 $group_id = $arr[1];
3005 if (!isset($result[$tag])) {
3006 $result[$tag] = array();
3009 if (!$onlyvisible || forge_check_perm('project_read', $group_id)) {
3010 $p = group_get_object($group_id);
3011 $result[$tag][] = array('unix_group_name' => $p->getUnixName(),
3012 'group_id' => $group_id);
3020 * Utility class to compare project based in various criteria (names, unixnames, id, ...)
3023 class ProjectComparator {
3024 var $criterion = 'name';
3026 function Compare ($a, $b) {
3027 switch ($this->criterion) {
3030 $namecmp = strcoll ($a->getPublicName(), $b->getPublicName());
3031 if ($namecmp != 0) {
3034 /* If several projects share a same public name */
3035 return strcoll ($a->getUnixName(), $b->getUnixName());
3038 return strcmp ($a->getUnixName(), $b->getUnixName());
3046 return ($a < $b) ? -1 : 1;
3052 function sortProjectList (&$list, $criterion='name') {
3053 $cmp = new ProjectComparator();
3054 $cmp->criterion = $criterion;
3056 return usort ($list, array($cmp, 'Compare'));
3061 // c-file-style: "bsd"