5 * Copyright 1999-2001, VA Linux Systems, Inc.
6 * Copyright 2009-2010, Roland Mas
7 * Copyright 2010-2011, Franck Villaume - Capgemini
8 * Copyright 2010-2011, Alain Peyrat - Alcatel-Lucent
9 * http://fusionforge.org
11 * This file is part of FusionForge. FusionForge is free software;
12 * you can redistribute it and/or modify it under the terms of the
13 * GNU General Public License as published by the Free Software
14 * Foundation; either version 2 of the Licence, or (at your option)
17 * FusionForge is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 require_once $gfcommon.'tracker/ArtifactTypes.class.php';
28 require_once $gfcommon.'tracker/ArtifactTypeFactory.class.php';
29 require_once $gfcommon.'forum/Forum.class.php';
30 require_once $gfcommon.'forum/ForumFactory.class.php';
31 require_once $gfcommon.'pm/ProjectGroup.class.php';
32 require_once $gfcommon.'pm/ProjectGroupFactory.class.php';
33 require_once $gfcommon.'include/Role.class.php';
34 require_once $gfcommon.'frs/FRSPackage.class.php';
35 require_once $gfcommon.'docman/DocumentGroup.class.php';
36 require_once $gfcommon.'docman/DocumentGroupFactory.class.php';
37 require_once $gfcommon.'mail/MailingList.class.php';
38 require_once $gfcommon.'mail/MailingListFactory.class.php';
39 require_once $gfcommon.'survey/SurveyFactory.class.php';
40 require_once $gfcommon.'survey/SurveyQuestionFactory.class.php';
41 require_once $gfcommon.'include/gettext.php';
42 require_once $gfcommon.'include/GroupJoinRequest.class.php';
47 * group_get_object() - Get the group object.
49 * group_get_object() is useful so you can pool group objects/save database queries
50 * You should always use this instead of instantiating the object directly.
52 * You can now optionally pass in a db result handle. If you do, it re-uses that query
53 * to instantiate the objects.
55 * IMPORTANT! That db result must contain all fields
56 * from groups table or you will have problems
59 * @param int Result set handle ("SELECT * FROM groups WHERE group_id=xx")
60 * @return object a group object or false on failure
62 function &group_get_object($group_id, $res = false) {
63 //create a common set of group objects
64 //saves a little wear on the database
66 //automatically checks group_type and
67 //returns appropriate object
70 if (!isset($GROUP_OBJ["_".$group_id."_"])) {
72 //the db result handle was passed in
74 $res = db_query_params('SELECT * FROM groups WHERE group_id=$1', array($group_id)) ;
76 if (!$res || db_numrows($res) < 1) {
77 $GROUP_OBJ["_".$group_id."_"]=false;
80 check group type and set up object
82 if (db_result($res,0,'type_id') == 1) {
84 $GROUP_OBJ["_".$group_id."_"] = new Group($group_id, $res);
87 $GROUP_OBJ["_".$group_id."_"] = false;
91 return $GROUP_OBJ["_".$group_id."_"];
94 function &group_get_objects($id_arr) {
97 // Note: if we don't do this, the result may be corrupted
101 foreach ($id_arr as $id) {
103 // See if this ID already has been fetched in the cache
105 if (!isset($GROUP_OBJ["_".$id."_"])) {
109 if (count($fetch) > 0) {
110 $res=db_query_params('SELECT * FROM groups WHERE group_id = ANY ($1)',
111 array(db_int_array_to_any_clause($fetch)));
112 while ($arr = db_fetch_array($res)) {
113 $GROUP_OBJ["_".$arr['group_id']."_"] = new Group($arr['group_id'],$arr);
116 foreach ($id_arr as $id) {
117 $return[] =& $GROUP_OBJ["_".$id."_"];
122 function &group_get_active_projects() {
123 $res = db_query_params('SELECT group_id FROM groups WHERE status=$1',
125 return group_get_objects(util_result_column_to_array($res,0));
128 function &group_get_template_projects() {
129 $res=db_query_params ('SELECT group_id FROM groups WHERE is_template=1 AND status != $1',
131 return group_get_objects (util_result_column_to_array($res,0)) ;
134 function &group_get_object_by_name($groupname) {
135 $res = db_query_params('SELECT * FROM groups WHERE unix_group_name=$1', array($groupname));
136 return group_get_object(db_result($res, 0, 'group_id'), $res);
139 function &group_get_objects_by_name($groupname_arr) {
140 $res = db_query_params('SELECT group_id FROM groups WHERE unix_group_name = ANY ($1)',
141 array(db_string_array_to_any_clause($groupname_arr)));
142 $arr =& util_result_column_to_array($res,0);
143 return group_get_objects($arr);
146 function &group_get_object_by_publicname($groupname) {
147 $res = db_query_params('SELECT * FROM groups WHERE lower(group_name) LIKE $1',
148 array(htmlspecialchars(strtolower($groupname))));
149 return group_get_object(db_result($res, 0, 'group_id'), $res);
152 class Group extends Error {
154 * Associative array of data from db.
156 * @var array $data_array.
161 * array of User objects.
163 * @var array $membersArr.
168 * Whether the use is an admin/super user of this project.
170 * @var bool $is_admin.
175 * Artifact types result handle.
177 * @var int $types_res.
182 * Associative array of data for plugins.
184 * @var array $plugins_data.
190 * Associative array of data for the group menu.
192 * @var array $menu_data.
197 * Group - Group object constructor - use group_get_object() to instantiate.
199 * @param int Required - group_id of the group you want to instantiate.
200 * @param int Database result from select query OR associative array of all columns.
201 * @return boolean success or not
203 function Group($id = false, $res = false) {
206 //setting up an empty object
207 //probably going to call create()
211 if (!$this->fetchData($id)) {
216 // Assoc array was passed in
218 if (is_array($res)) {
219 $this->data_array =& $res;
221 if (db_numrows($res) < 1) {
222 //function in class we extended
223 $this->setError(_('Group Not Found'));
224 $this->data_array=array();
227 //set up an associative array for use by other functions
228 $this->data_array = db_fetch_array_by_row($res, 0);
236 * fetchData - May need to refresh database fields if an update occurred.
238 * @param int The group_id.
239 * @return boolean success or not
241 function fetchData($group_id) {
242 $res = db_query_params ('SELECT * FROM groups WHERE group_id=$1',
244 if (!$res || db_numrows($res) < 1) {
245 $this->setError(sprintf(_('fetchData():: %s'),db_error()));
248 $this->data_array = db_fetch_array($res);
253 * create - Create new group.
255 * This method should be called on empty Group object.
256 * It will add an entry for a pending group/project (status 'P')
258 * @param object The User object.
259 * @param string The full name of the user.
260 * @param string The Unix name of the user.
261 * @param string The new group description.
262 * @param string The purpose of the group.
263 * @param boolean Whether to send an email or not
264 * @param int The id of the project this new project is based on
265 * @return boolean success or not
267 function create(&$user, $group_name, $unix_name, $description, $purpose, $unix_box = 'shell1',
268 $scm_box = 'cvs1', $is_public = 1, $send_mail = true, $built_from_template = 0) {
269 // $user is ignored - anyone can create pending group
272 if ($this->getID()!=0) {
273 $this->setError(_('Group::create: Group object already exists'));
275 } else if (!$this->validateGroupName($group_name)) {
277 } else if (!account_groupnamevalid($unix_name)) {
278 $this->setError(_('Invalid Unix name'));
280 } else if (!$SYS->sysUseUnixName($unix_name)) {
281 $this->setError(_('Unix name already taken'));
283 } else if (db_numrows(db_query_params('SELECT group_id FROM groups WHERE unix_group_name=$1',
284 array($unix_name))) > 0) {
285 $this->setError(_('Unix name already taken'));
287 } else if (strlen($purpose)<10) {
288 $this->setError(_('Please describe your Registration Purpose in a more comprehensive manner'));
290 } else if (strlen($purpose)>1500) {
291 $this->setError(_('The Registration Purpose text is too long. Please make it smaller than 1500 bytes.'));
293 } else if (strlen($description)<10) {
294 $this->setError(_('Describe in a more comprehensive manner your project.'));
296 } else if (strlen($description)>255) {
297 $this->setError(_('Your project description is too long. Please make it smaller than 256 bytes.'));
301 // Check if sys_use_project_vhost for homepage
302 if (forge_get_config('use_project_vhost')) {
303 $homepage = $unix_name.".".forge_get_config('web_host');
305 $homepage = forge_get_config('web_host')."/www/".$unix_name."/";
310 $res = db_query_params('
326 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)',
327 array (htmlspecialchars ($group_name),
329 htmlspecialchars($description),
330 $unix_name.".".forge_get_config('web_host'),
335 htmlspecialchars($purpose),
338 md5(util_randbytes()),
339 $built_from_template));
340 if (!$res || db_affected_rows($res) < 1) {
341 $this->setError(sprintf(_('ERROR: Could not create group: %s'),db_error()));
346 $id = db_insertid($res, 'groups', 'group_id');
348 $this->setError(sprintf(_('ERROR: Could not get group id: %s'),db_error()));
353 if (!$this->fetchData($id)) {
358 $gjr = new GroupJoinRequest($this);
359 $gjr->create($user->getID(),
360 'Fake GroupJoinRequest to store the creator of a project',
363 $hook_params = array();
364 $hook_params['group'] = $this;
365 $hook_params['group_id'] = $this->getID();
366 $hook_params['group_name'] = $group_name;
367 $hook_params['unix_group_name'] = $unix_name;
368 plugin_hook("group_create", $hook_params);
372 $this->sendNewProjectNotificationEmail();
380 * updateAdmin - Update core properties of group object.
382 * This function require site admin privilege.
384 * @param object User requesting operation (for access control).
385 * @param boolean Whether group is publicly accessible (0/1).
386 * @param int Group type (1-project, 2-foundry).
387 * @param string Machine on which group's home directory located.
388 * @param string Domain which serves group's WWW.
392 function updateAdmin(&$user, $is_public, $type_id, $unix_box, $http_domain) {
393 $perm =& $this->getPermission();
395 if (!$perm || !is_object($perm)) {
396 $this->setError(_('Could not get permission.'));
400 if (!$perm->isSuperUser()) {
401 $this->setError(_('Permission denied.'));
407 $res = db_query_params('
409 SET type_id=$1, unix_box=$2, http_domain=$3
416 if (!$res || db_affected_rows($res) < 1) {
417 $this->setError(_('ERROR: DB: Could not change group properties: %s'),db_error());
422 // Log the audit trail
423 if ($type_id != $this->data_array['type_id']) {
424 $this->addHistory('type_id', $this->data_array['type_id']);
426 if ($unix_box != $this->data_array['unix_box']) {
427 $this->addHistory('unix_box', $this->data_array['unix_box']);
429 if ($http_domain != $this->data_array['http_domain']) {
430 $this->addHistory('http_domain', $this->data_array['http_domain']);
433 if (!$this->fetchData($this->getID())) {
442 * update - Update number of common properties.
444 * Unlike updateAdmin(), this function accessible to project admin.
446 * @param object User requesting operation (for access control).
447 * @param boolean Whether group is publicly accessible (0/1).
448 * @param string Project's license (string ident).
449 * @param int Group type (1-project, 2-foundry).
450 * @param string Machine on which group's home directory located.
451 * @param string Domain which serves group's WWW.
452 * @return int status.
455 function update(&$user, $group_name, $homepage, $short_description, $use_mail, $use_survey, $use_forum,
456 $use_pm, $use_pm_depend_box, $use_scm, $use_news, $use_docman,
457 $new_doc_address, $send_all_docs, $logo_image_id,
458 $use_ftp, $use_tracker, $use_frs, $use_stats, $tags, $is_public) {
460 $perm =& $this->getPermission();
462 if (!$perm || !is_object($perm)) {
463 $this->setError(_('Could not get permission.'));
467 if (!$perm->isAdmin()) {
468 $this->setError(_('Permission denied.'));
472 // Validate some values
473 if ($this->getPublicName() != $group_name) {
474 if (!$this->validateGroupName($group_name)) {
479 if ($new_doc_address) {
480 $invalid_mails = validate_emails($new_doc_address);
481 if (count($invalid_mails) > 0) {
482 $this->setError(sprintf(ngettext('New Doc Address Appeared Invalid: %s', 'New Doc Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
487 // in the database, these all default to '1',
488 // so we have to explicity set 0
501 if (!$use_pm_depend_box) {
502 $use_pm_depend_box = 0;
525 if (!$send_all_docs) {
529 $homepage = ltrim($homepage);
531 $homepage = util_make_url('/projects/' . $this->getUnixName() . '/');
534 if (strlen(htmlspecialchars($short_description))>255) {
535 $this->setError(_('Error updating project information: Maximum length for Project Description is 255 chars.'));
541 //XXX not yet actived logo_image_id='$logo_image_id',
542 $res = db_query_params('UPDATE groups
545 short_description=$3,
550 use_pm_depend_box=$8,
560 array(htmlspecialchars($group_name),
562 htmlspecialchars($short_description),
579 $this->setError(sprintf(_('Error updating project information: %s'), db_error()));
584 if (!$this->setUseDocman($use_docman)) {
585 $this->setError(sprintf(_('Error updating project information: use_docman %s'), db_error()));
590 if ($this->setTags($tags) === false) {
595 $hook_params = array();
596 $hook_params['group'] = $this;
597 $hook_params['group_id'] = $this->getID();
598 $hook_params['group_homepage'] = $homepage;
599 $hook_params['group_name'] = htmlspecialchars($group_name);
600 $hook_params['group_description'] = htmlspecialchars($short_description);
601 $hook_params['group_ispublic'] = $is_public;
602 if (!plugin_hook("group_update", $hook_params)) {
603 if (!$this->isError()) {
604 $this->setError(_('Error updating project information in plugin_hook group_update'));
610 // Log the audit trail
611 $this->addHistory('Changed Public Info', '');
613 if (!$this->fetchData($this->getID())) {
622 * getID - Simply return the group_id for this object.
624 * @return int group_id.
627 return $this->data_array['group_id'];
631 * getType() - Foundry, project, etc.
633 * @return int The type flag from the database.
636 return $this->data_array['type_id'];
641 * getStatus - the status code.
643 * Statuses char include I,H,A,D,P.
644 * TODO : document what these mean :
651 function getStatus() {
652 return $this->data_array['status'];
656 * setStatus - set the status code.
658 * Statuses include I,H,A,D,P.
659 * TODO : document what these mean :
666 * @param object User requesting operation (for access control).
667 * @param string Status value.
668 * @return boolean success.
671 function setStatus(&$user, $status) {
674 if (!forge_check_global_perm('approve_projects')) {
675 $this->setPermissionDeniedError();
679 // Projects in 'A' status can only go to 'H' or 'D'
680 // Projects in 'D' status can only go to 'A'
681 // Projects in 'P' status can only go to 'A' OR 'D'
682 // Projects in 'I' status can only go to 'P'
683 // Projects in 'H' status can only go to 'A' OR 'D'
684 $allowed_status_changes = array(
685 'AH'=>1,'AD'=>1,'DA'=>1,'PA'=>1,'PD'=>1,
686 'IP'=>1,'HA'=>1,'HD'=>1
689 // Check that status transition is valid
690 if ($this->getStatus() != $status
691 && !$allowed_status_changes[$this->getStatus().$status]) {
692 $this->setError(_('Invalid Status Change'));
698 $res = db_query_params('UPDATE groups
700 WHERE group_id=$2', array($status, $this->getID()));
702 if (!$res || db_affected_rows($res) < 1) {
703 $this->setError(sprintf(_('ERROR: DB: Could not change group status: %s'),db_error()));
709 // Activate system group, if not yet
710 if (!$SYS->sysCheckGroup($this->getID())) {
711 if (!$SYS->sysCreateGroup($this->getID())) {
712 $this->setError($SYS->getErrorMessage());
717 if (!$this->activateUsers()) {
722 /* Otherwise, the group is not active, and make sure that
723 System group is not active either */
724 } else if ($SYS->sysCheckGroup($this->getID())) {
725 if (!$SYS->sysRemoveGroup($this->getID())) {
726 $this->setError($SYS->getErrorMessage());
732 $hook_params = array();
733 $hook_params['group'] = $this;
734 $hook_params['group_id'] = $this->getID();
735 $hook_params['status'] = $status;
736 plugin_hook("group_setstatus", $hook_params);
740 // Log the audit trail
741 if ($status != $this->getStatus()) {
742 $this->addHistory('Status', $this->getStatus());
745 $this->data_array['status'] = $status;
750 * isProject - Simple boolean test to see if it's a project or not.
752 * @return boolean is_project.
754 function isProject() {
755 if ($this->getType()==1) {
763 * isPublic - Wrapper around RBAC to check if a project is anonymously readable
765 * @return boolean is_public.
767 function isPublic() {
768 $ra = RoleAnonymous::getInstance() ;
769 return $ra->hasPermission('project_read', $this->getID());
773 * isActive - Database field status of 'A' returns true.
775 * @return boolean is_active.
777 function isActive() {
778 if ($this->getStatus()=='A') {
786 * isTemplate - Simply returns the is_template flag from the database.
788 * @return boolean is_template.
790 function isTemplate() {
791 return $this->data_array['is_template'];
795 * setAsTemplate - Set the template status of a project
797 * @param boolean is_template.
799 function setAsTemplate($booleanparam) {
801 $booleanparam = $booleanparam ? 1 : 0;
802 $res = db_query_params('UPDATE groups SET is_template=$1 WHERE group_id=$2',
803 array($booleanparam, $this->getID()));
805 $this->data_array['is_template']=$booleanparam;
815 * getTemplateProject - Return the project template this project is built from
817 * @return object The template project
819 function getTemplateProject() {
820 return group_get_object($this->data_array['built_from_template']);
824 * getUnixName - the unix_name
826 * @return string unix_name.
828 function getUnixName() {
829 return strtolower($this->data_array['unix_group_name']);
833 * getPublicName - the full-length public name.
835 * @return string The group_name.
837 function getPublicName() {
838 return $this->data_array['group_name'];
842 * getRegisterPurpose - the text description of the purpose of this project.
844 * @return string The description.
846 function getRegisterPurpose() {
847 return $this->data_array['register_purpose'];
851 * getDescription - the text description of this project.
853 * @return string The description.
855 function getDescription() {
856 return $this->data_array['short_description'];
860 * getStartDate - the unix time this project was registered.
862 * @return int (unix time) of registration.
864 function getStartDate() {
865 return $this->data_array['register_time'];
869 * getLogoImageID - the id of the logo in the database for this project.
871 * @return int The ID of logo image in db_images table (or 100 if none).
873 function getLogoImageID() {
874 return $this->data_array['logo_image_id'];
878 * getUnixBox - the hostname of the unix box where this project is located.
880 * @return string The name of the unix machine for the group.
882 function getUnixBox() {
883 return $this->data_array['unix_box'];
887 * getSCMBox - the hostname of the scm box where this project is located.
889 * @return string The name of the unix machine for the group.
891 function getSCMBox() {
892 return $this->data_array['scm_box'];
895 * setSCMBox - the hostname of the scm box where this project is located.
897 * @param string The name of the new SCM_BOX
899 function setSCMBox($scm_box) {
901 if ($scm_box == $this->data_array['scm_box']) {
906 $res = db_query_params('UPDATE groups SET scm_box=$1 WHERE group_id=$2', array($scm_box, $this->getID ()));
908 $this->addHistory('scm_box', $this->data_array['scm_box']);
909 $this->data_array['scm_box'] = $scm_box;
914 $this->setError(_("Couldn't insert SCM_BOX to database"));
918 $this->setError(_("SCM Box can't be empty"));
924 * getDomain - the hostname.domain where their web page is located.
926 * @return string The name of the group [web] domain.
928 function getDomain() {
929 return $this->data_array['http_domain'];
933 * getRegistrationPurpose - the text description of the purpose of this project.
935 * @return string The application for project hosting.
937 function getRegistrationPurpose() {
938 return $this->data_array['register_purpose'];
943 * getAdmins() - Get array of Admin user objects.
945 * @return array Array of User objects.
947 function &getAdmins() {
948 $roles = RBACEngine::getInstance()->getRolesByAllowedAction ('project_admin', $this->getID());
952 foreach ($roles as $role) {
953 if (! ($role instanceof RoleExplicit)) {
956 if ($role->getHomeProject() == NULL
957 || $role->getHomeProject()->getID() != $this->getID()) {
961 foreach ($role->getUsers() as $u) {
962 $user_ids[] = $u->getID();
965 return user_get_objects(array_unique($user_ids));
969 Common Group preferences for tools
973 * enableAnonSCM - whether or not this group has opted to enable Anonymous SCM.
975 * @return boolean enable_scm.
977 function enableAnonSCM() {
978 $r = RoleAnonymous::getInstance();
979 return $r->hasPermission('scm', $this->getID(), 'read');
982 function SetUsesAnonSCM($booleanparam) {
984 $booleanparam = $booleanparam ? 1 : 0;
985 $r = RoleAnonymous::getInstance();
986 $r->setSetting('scm', $this->getID(), $booleanparam);
991 * enablePserver - whether or not this group has opted to enable Pserver.
993 * @return boolean enable_pserver.
995 function enablePserver() {
996 if ($this->usesSCM()) {
997 return $this->data_array['enable_pserver'];
1003 function SetUsesPserver($booleanparam) {
1005 $booleanparam = $booleanparam ? 1 : 0;
1006 $res = db_query_params('UPDATE groups SET enable_pserver=$1 WHERE group_id=$2',
1007 array($booleanparam, $this->getID()));
1009 $this->data_array['enable_pserver'] = $booleanparam;
1018 * usesSCM - whether or not this group has opted to use SCM.
1020 * @return boolean uses_scm.
1022 function usesSCM() {
1023 if (forge_get_config('use_scm')) {
1024 return $this->data_array['use_scm'];
1031 * setUseSCM - Set the SCM usage
1033 * @param boolean enabled/disabled
1035 function setUseSCM($booleanparam) {
1037 $booleanparam = $booleanparam ? 1 : 0 ;
1038 $res = db_query_params('UPDATE groups SET use_scm=$1 WHERE group_id=$2',
1039 array($booleanparam, $this->getID()));
1041 $this->data_array['use_scm']=$booleanparam;
1051 * usesMail - whether or not this group has opted to use mailing lists.
1053 * @return boolean uses_mail.
1055 function usesMail() {
1056 if (forge_get_config('use_mail')) {
1057 return $this->data_array['use_mail'];
1064 * setUseMail - Set the mailing-list usage
1066 * @param boolean enabled/disabled
1068 function setUseMail($booleanparam) {
1070 $booleanparam = $booleanparam ? 1 : 0 ;
1071 $res = db_query_params('UPDATE groups SET use_mail=$1 WHERE group_id=$2',
1072 array($booleanparam, $this->getID()));
1074 $this->data_array['use_mail']=$booleanparam;
1084 * usesNews - whether or not this group has opted to use news.
1086 * @return boolean uses_news.
1088 function usesNews() {
1089 if (forge_get_config('use_news')) {
1090 return $this->data_array['use_news'];
1097 * usesForum - whether or not this group has opted to use discussion forums.
1099 * @return boolean uses_forum.
1101 function usesForum() {
1102 if (forge_get_config('use_forum')) {
1103 return $this->data_array['use_forum'];
1110 * setUseForum - Set the forum usage
1112 * @param boolean enabled/disabled
1114 function setUseForum($booleanparam) {
1116 $booleanparam = $booleanparam ? 1 : 0;
1117 $res = db_query_params('UPDATE groups SET use_forum=$1 WHERE group_id=$2',
1118 array($booleanparam, $this->getID()));
1120 $this->data_array['use_forum']=$booleanparam;
1130 * usesStats - whether or not this group has opted to use stats.
1132 * @return boolean uses_stats.
1134 function usesStats() {
1135 return $this->data_array['use_stats'];
1139 * usesFRS - whether or not this group has opted to use file release system.
1141 * @return boolean uses_frs.
1143 function usesFRS() {
1144 if (forge_get_config('use_frs')) {
1145 return $this->data_array['use_frs'];
1152 * setUseFRS - Set the FRS usage
1154 * @param boolean enabled/disabled
1156 function setUseFRS($booleanparam) {
1158 $booleanparam = $booleanparam ? 1 : 0;
1159 $res = db_query_params('UPDATE groups SET use_frs=$1 WHERE group_id=$2',
1160 array ($booleanparam, $this->getID()));
1162 $this->data_array['use_frs']=$booleanparam;
1172 * usesTracker - whether or not this group has opted to use tracker.
1174 * @return boolean uses_tracker.
1176 function usesTracker() {
1177 if (forge_get_config('use_tracker')) {
1178 return $this->data_array['use_tracker'];
1185 * setUseTracker - Set the tracker usage
1187 * @param boolean enabled/disabled
1189 function setUseTracker ($booleanparam) {
1191 $booleanparam = $booleanparam ? 1 : 0 ;
1192 $res = db_query_params ('UPDATE groups SET use_tracker=$1 WHERE group_id=$2',
1193 array ($booleanparam, $this->getID()));
1195 $this->data_array['use_tracker']=$booleanparam;
1205 * useCreateOnline - whether or not this group has opted to use create online documents option.
1207 * @return boolean use_docman_create_online.
1209 function useCreateOnline() {
1210 if (forge_get_config('use_docman')) {
1211 return $this->data_array['use_docman_create_online'];
1218 * usesDocman - whether or not this group has opted to use docman.
1220 * @return boolean use_docman.
1222 function usesDocman() {
1223 if (forge_get_config('use_docman')) {
1224 return $this->data_array['use_docman'];
1231 * setUseDocman - Set the docman usage
1233 * @param boolean enabled/disabled
1235 function setUseDocman($booleanparam) {
1237 $booleanparam = $booleanparam ? 1 : 0;
1238 $res = db_query_params('UPDATE groups SET use_docman = $1 WHERE group_id = $2',
1239 array($booleanparam, $this->getID()));
1241 // check if / doc_group exists, if not create it
1242 $trashdir = db_query_params('select groupname from doc_groups where groupname = $1 and group_id = $2',
1243 array('.trash', $this->getID()));
1244 if ($trashdir && db_numrows($trashdir) == 0) {
1245 $resinsert = db_query_params('insert into doc_groups (groupname, group_id, stateid) values ($1, $2, $3)',
1246 array('.trash', $this->getID(), '2'));
1252 $this->data_array['use_docman'] = $booleanparam;
1262 * useDocmanSearch - whether or not this group has opted to use docman search engine.
1264 * @return boolean use_docman_search.
1266 function useDocmanSearch() {
1267 if (forge_get_config('use_docman')) {
1268 return $this->data_array['use_docman_search'];
1275 * useWebdav - whether or not this group has opted to use webdav interface.
1277 * @return boolean use_docman_search.
1279 function useWebdav() {
1280 if (forge_get_config('use_webdav')) {
1281 return $this->data_array['use_webdav'];
1288 * usesFTP - whether or not this group has opted to use FTP.
1290 * @return boolean uses_ftp.
1292 function usesFTP() {
1293 if (forge_get_config('use_ftp')) {
1294 return $this->data_array['use_ftp'];
1301 * usesSurvey - whether or not this group has opted to use surveys.
1303 * @return boolean uses_survey.
1305 function usesSurvey() {
1306 if (forge_get_config('use_survey')) {
1307 return $this->data_array['use_survey'];
1314 * usesPM - whether or not this group has opted to Project Manager.
1316 * @return boolean uses_projman.
1319 if (forge_get_config('use_pm')) {
1320 return $this->data_array['use_pm'];
1327 * setUsePM - Set the PM usage
1329 * @param boolean enabled/disabled
1331 function setUsePM($booleanparam) {
1333 $booleanparam = $booleanparam ? 1 : 0;
1334 $res = db_query_params('UPDATE groups SET use_pm=$1 WHERE group_id=$2',
1335 array($booleanparam, $this->getID()));
1337 $this->data_array['use_pm']=$booleanparam;
1347 * getPlugins - get a list of all available group plugins
1349 * @return array array containing plugin_id => plugin_name
1351 function getPlugins() {
1352 if (!isset($this->plugins_data)) {
1353 $this->plugins_data = array();
1354 $res = db_query_params('SELECT group_plugin.plugin_id, plugins.plugin_name
1355 FROM group_plugin, plugins
1356 WHERE group_plugin.group_id=$1
1357 AND group_plugin.plugin_id=plugins.plugin_id', array($this->getID()));
1358 $rows = db_numrows($res);
1360 for ($i=0; $i<$rows; $i++) {
1361 $plugin_id = db_result($res, $i, 'plugin_id');
1362 $this->plugins_data[$plugin_id] = db_result($res, $i, 'plugin_name');
1365 return $this->plugins_data;
1369 * usesPlugin - returns true if the group uses a particular plugin
1371 * @param string name of the plugin
1372 * @return boolean whether plugin is being used or not
1374 function usesPlugin($pluginname) {
1375 $plugins_data = $this->getPlugins();
1376 foreach ($plugins_data as $p_id => $p_name) {
1377 if ($p_name == $pluginname) {
1385 * added for Codendi compatibility
1386 * usesServices - returns true if the group uses a particular plugin or feature
1388 * @param string name of the plugin
1389 * @return boolean whether plugin is being used or not
1391 function usesService($feature) {
1392 $plugins_data = $this->getPlugins();
1393 $pm = plugin_manager_get_object();
1394 foreach ($plugins_data as $p_id => $p_name) {
1395 if ($p_name == $feature) {
1398 if ($pm->getPluginByName($p_name)->provide($feature)) {
1406 * setPluginUse - enables/disables plugins for the group
1408 * @param string name of the plugin
1409 * @param boolean the new state
1410 * @return string database result
1412 function setPluginUse($pluginname, $val=true) {
1413 if ($val == $this->usesPlugin($pluginname)) {
1414 // State is already good, returning
1417 $res = db_query_params('SELECT plugin_id FROM plugins WHERE plugin_name=$1',
1418 array($pluginname));
1419 $rows = db_numrows($res);
1421 // Error: no plugin by that name
1424 $plugin_id = db_result($res,0,'plugin_id');
1426 unset($this->plugins_data);
1428 $res = db_query_params('INSERT INTO group_plugin (group_id, plugin_id) VALUES ($1, $2)',
1429 array($this->getID(),
1433 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1 AND plugin_id=$2',
1434 array($this->getID(),
1441 * getDocEmailAddress - get email address(es) to send doc notifications to.
1443 * @return string email address.
1445 function getDocEmailAddress() {
1446 return $this->data_array['new_doc_address'];
1450 * DocEmailAll - whether or not this group has opted to use receive notices on all doc updates.
1452 * @return boolean email_on_all_doc_updates.
1454 function docEmailAll() {
1455 return $this->data_array['send_all_docs'];
1460 * getHomePage - The URL for this project's home page.
1462 * @return string homepage URL.
1464 function getHomePage() {
1465 return $this->data_array['homepage'];
1469 * getTags - Tags of this project.
1471 * @return string List of tags. Comma separated
1473 function getTags() {
1474 $sql = 'SELECT name FROM project_tags WHERE group_id = $1';
1475 $res = db_query_params($sql, array($this->getID()));
1476 return join(', ', util_result_column_to_array($res));
1480 * setTags - Set tags of this project.
1482 * @return string database result.
1484 function setTags($tags) {
1486 $sql = 'DELETE FROM project_tags WHERE group_id=$1';
1487 $res = db_query_params($sql, array($this->getID()));
1489 $this->setError('Deleting old tags: '.db_error());
1493 $inserted = array();
1494 $tags_array = preg_split('/[;,]/', $tags);
1495 foreach ($tags_array as $tag) {
1496 $tag = preg_replace('/[\t\r\n]/', ' ', $tag);
1497 // Allowed caracteres: [A-Z][a-z][0-9] -_&'#+.
1498 if (preg_match('/[^[:alnum:]| |\-|_|\&|\'|#|\+|\.]/', $tag)) {
1499 $this->setError(_('Bad tag name, you only can use the following characters: [A-Z][a-z][0-9]-_&\'#+. and space'));
1504 if ($tag == '' || array_search($tag, $inserted) !== false) continue;
1505 $sql = 'INSERT INTO project_tags (group_id,name) VALUES ($1, $2)';
1506 $res = db_query_params($sql, array($this->getID(), $tag));
1508 $this->setError(_('Setting tags:') . ' ' .
1520 * getPermission - Return a Permission for this Group
1522 * @return object The Permission.
1524 function &getPermission() {
1525 return permission_get_object($this);
1529 function delete($sure, $really_sure, $really_really_sure) {
1530 if (!$sure || !$really_sure || !$really_really_sure) {
1531 $this->setMissingParamsError(_('Please tick all checkboxes.'));
1534 if ($this->getID() == forge_get_config('news_group') ||
1535 $this->getID() == 1 ||
1536 $this->getID() == forge_get_config('stats_group') ||
1537 $this->getID() == forge_get_config('peer_rating_group')) {
1538 $this->setError(_('Cannot Delete System Group'));
1541 $perm =& $this->getPermission();
1542 if (!$perm || !is_object($perm)) {
1543 $this->setPermissionDeniedError();
1545 } elseif ($perm->isError()) {
1546 $this->setPermissionDeniedError();
1548 } elseif (!$perm->isSuperUser()) {
1549 $this->setPermissionDeniedError();
1555 // Remove all the members
1557 $members =& $this->getMembers();
1558 foreach ($members as $i) {
1559 if(!$this->removeUser($i->getID())) {
1560 $this->setError(_('Could not properly remove member:').' '.$i->getID());
1564 // Failsafe until user_group table is gone
1565 $res = db_query_params('DELETE FROM user_group WHERE group_id=$1',
1566 array($this->getID()));
1568 // unlink roles from this project
1569 $ra = RoleAnonymous::getInstance();
1570 $rl = RoleLoggedIn::getInstance();
1571 $ra->unlinkProject($this);
1572 $rl->unlinkProject($this);
1573 // @todo : unlink all the other roles created in the project...
1578 $atf = new ArtifactTypeFactory($this);
1579 $at_arr =& $atf->getArtifactTypes();
1580 foreach ($at_arr as $i) {
1581 if (!is_object($i)) {
1584 if (!$i->delete(1,1)) {
1585 $this->setError(_('Could not properly delete the tracker:').' '.$i->getErrorMessage());
1592 $ff = new ForumFactory($this);
1593 $f_arr =& $ff->getForums();
1594 foreach ($f_arr as $i) {
1595 if (!is_object($i)) {
1598 if(!$i->delete(1,1)) {
1599 $this->setError(_('Could not properly delete the forum:').' '.$i->getErrorMessage());
1604 // Delete Subprojects
1606 $pgf = new ProjectGroupFactory($this);
1607 $pg_arr =& $pgf->getProjectGroups();
1608 foreach ($pg_arr as $i) {
1609 if (!is_object($i)) {
1612 if (!$i->delete(1,1)) {
1613 $this->setError(_('Could not properly delete the ProjectGroup:').' '.$i->getErrorMessage());
1618 // Delete FRS Packages
1620 $res = db_query_params('SELECT * FROM frs_package WHERE group_id=$1',
1621 array($this->getID()));
1622 while ($arr = db_fetch_array($res)) {
1623 $frsp=new FRSPackage($this, $arr['package_id'], $arr);
1624 if (!$frsp->delete(1, 1)) {
1625 $this->setError(_('Could not properly delete the FRSPackage:').' '.$frsp->getErrorMessage());
1632 $news_group=group_get_object(forge_get_config('news_group'));
1633 $res = db_query_params('SELECT forum_id FROM news_bytes WHERE group_id=$1',
1634 array($this->getID()));
1636 $this->setError(_('Error Deleting News: ').db_error());
1641 for ($i=0; $i<db_numrows($res); $i++) {
1642 $Forum = new Forum($news_group,db_result($res,$i,'forum_id'));
1643 if (!$Forum->delete(1,1)) {
1644 $this->setError(_("Could Not Delete News Forum: %d"),$Forum->getID());
1648 $res = db_query_params('DELETE FROM news_bytes WHERE group_id=$1',
1649 array($this->getID()));
1651 $this->setError(_('Error Deleting News: ').db_error());
1659 $res = db_query_params('DELETE FROM doc_data WHERE group_id=$1',
1660 array($this->getID()));
1662 $this->setError(_('Error Deleting Documents: ').db_error());
1667 $res = db_query_params('DELETE FROM doc_groups WHERE group_id=$1',
1668 array($this->getID()));
1670 $this->setError(_('Error Deleting Documents: ').db_error());
1678 $res=db_query_params('DELETE FROM project_tags WHERE group_id=$1', array($this->getID()));
1680 $this->setError(_('Error Deleting Tags: ').db_error());
1686 // Delete group history
1688 $res = db_query_params('DELETE FROM group_history WHERE group_id=$1',
1689 array($this->getID()));
1691 $this->setError(_('Error Deleting Project History: ').db_error());
1697 // Delete group plugins
1699 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1',
1700 array($this->getID()));
1702 $this->setError(_('Error Deleting Project Plugins: ').db_error());
1708 // Delete group cvs stats
1710 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE group_id=$1',
1711 array ($this->getID())) ;
1713 $this->setError(_('Error Deleting SCM Statistics: ').db_error());
1721 $sf = new SurveyFactory($this);
1722 $s_arr =& $sf->getSurveys();
1723 foreach ($s_arr as $i) {
1724 if (!is_object($i)) {
1727 if (!$i->delete()) {
1728 $this->setError(_('Could not properly delete the survey'));
1734 // Delete SurveyQuestions
1736 $sqf = new SurveyQuestionFactory($this);
1737 $sq_arr =& $sqf->getSurveyQuestions();
1738 foreach ($sq_arr as $i) {
1739 if (!is_object($i)) {
1742 if (!$i->delete()) {
1743 $this->setError(_('Could not properly delete the survey questions'));
1749 // Delete Mailing List Factory
1751 $mlf = new MailingListFactory($this);
1752 $ml_arr = $mlf->getMailingLists();
1753 foreach ($ml_arr as $i) {
1754 if (!is_object($i)) {
1757 if (!$i->delete(1,1)) {
1758 $this->setError(_('Could not properly delete the mailing list'));
1766 $res = db_query_params('DELETE FROM trove_group_link WHERE group_id=$1',
1767 array($this->getID()));
1769 $this->setError(_('Error Deleting Trove: ').db_error());
1774 $res = db_query_params('DELETE FROM trove_agg WHERE group_id=$1',
1775 array($this->getID()));
1777 $this->setError(_('Error Deleting Trove: ').db_error());
1785 $res = db_query_params('DELETE FROM project_sums_agg WHERE group_id=$1',
1786 array($this->getID()));
1788 $this->setError(_('Error Deleting Counters: ').db_error());
1793 $res = db_query_params('INSERT INTO deleted_groups (unix_group_name, delete_date, isdeleted) VALUES ($1, $2, $3)',
1794 array($this->getUnixName(),
1798 $this->setError(_('Error Deleting Project:').' '.db_error());
1803 $res = db_query_params('DELETE FROM groups WHERE group_id=$1',
1804 array($this->getID()));
1806 $this->setError(_('Error Deleting Project:').' '.db_error());
1813 $hook_params = array();
1814 $hook_params['group'] = $this;
1815 $hook_params['group_id'] = $this->getID();
1816 plugin_hook("group_delete", $hook_params);
1818 if (forge_get_config('upload_dir') != '' && $this->getUnixName()) {
1819 exec('/bin/rm -rf '.forge_get_config('upload_dir').'/'.$this->getUnixName().'/');
1821 if (forge_get_config('ftp_upload_dir') != '' && $this->getUnixName()) {
1822 exec('/bin/rm -rf '.forge_get_config('ftp_upload_dir').'/'.$this->getUnixName().'/');
1827 $res = db_query_params('DELETE FROM rep_group_act_monthly WHERE group_id=$1',
1828 array ($this->getID()));
1829 //echo 'rep_group_act_monthly'.db_error();
1830 $res = db_query_params('DELETE FROM rep_group_act_weekly WHERE group_id=$1',
1831 array ($this->getID()));
1832 //echo 'rep_group_act_weekly'.db_error();
1833 $res = db_query_params('DELETE FROM rep_group_act_daily WHERE group_id=$1',
1834 array ($this->getID()));
1835 //echo 'rep_group_act_daily'.db_error();
1836 unset($this->data_array);
1841 Basic functions to add/remove users to/from a group
1842 and update their permissions
1846 * addUser - controls adding a user to a group.
1848 * @param string Unix name of the user to add OR integer user_id.
1849 * @param int The role_id this user should have.
1850 * @return boolean success.
1853 function addUser($user_identifier,$role_id) {
1856 Admins can add users to groups
1859 if (!forge_check_perm ('project_admin', $this->getID())) {
1860 $this->setPermissionDeniedError();
1866 get user id for this user's unix_name
1868 if (is_int ($user_identifier)) { // user_id or user_name
1869 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_id=$1', array ($user_identifier)) ;
1871 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_name=$1', array ($user_identifier)) ;
1873 if (db_numrows($res_newuser) > 0) {
1875 // make sure user is active
1877 if (db_result($res_newuser,0,'status') != 'A') {
1878 $this->setError(_('User is not active. Only active users can be added.'));
1884 // user was found - set new user_id var
1886 $user_id = db_result($res_newuser,0,'user_id');
1888 $role = new Role($this, $role_id);
1889 if (!$role || !is_object($role)) {
1890 $this->setError(_('Error Getting Role Object'));
1893 } elseif ($role->isError()) {
1894 $this->setError('addUser::roleget::'.$role->getErrorMessage());
1899 $role->addUser(user_get_object($user_id)) ;
1900 if (!$SYS->sysCheckCreateGroup($this->getID())){
1901 $this->setError($SYS->getErrorMessage());
1905 if (!$SYS->sysCheckCreateUser($user_id)) {
1906 $this->setError($SYS->getErrorMessage());
1910 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
1911 $this->setError($SYS->getErrorMessage());
1917 // user doesn't exist
1919 $this->setError(_('ERROR: User does not exist'));
1924 $hook_params['group'] = $this;
1925 $hook_params['group_id'] = $this->getID();
1926 $hook_params['user'] = user_get_object($user_id);
1927 $hook_params['user_id'] = $user_id;
1928 plugin_hook ("group_adduser", $hook_params);
1933 $this->addHistory('Added User',$user_identifier);
1939 * removeUser - controls removing a user from a group.
1941 * Users can remove themselves.
1943 * @param int The ID of the user to remove.
1944 * @return boolean success.
1946 function removeUser($user_id) {
1949 if ($user_id != user_getid()
1950 && !forge_check_perm('project_admin', $this->getID())) {
1951 $this->setPermissionDeniedError();
1957 $user = user_get_object($user_id);
1958 $roles = RBACEngine::getInstance()->getAvailableRolesForUser($user);
1960 foreach ($roles as $role) {
1961 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
1962 $found_role = $role;
1966 if ($found_role == NULL) {
1967 $this->setError(sprintf(_('ERROR: User not removed: %s')));
1971 $found_role->removeUser($user);
1972 if (!$SYS->sysGroupCheckUser($this->getID(), $user_id)) {
1973 $this->setError($SYS->getErrorMessage());
1979 // reassign open artifacts to id=100
1981 $res = db_query_params('UPDATE artifact SET assigned_to=100
1982 WHERE group_artifact_id
1983 IN (SELECT group_artifact_id
1984 FROM artifact_group_list
1985 WHERE group_id=$1 AND status_id=1 AND assigned_to=$2)',
1986 array($this->getID(),
1989 $this->setError(_('ERROR: DB: artifact:').' '.db_error());
1995 // reassign open tasks to id=100
1996 // first have to purge any assignments that would cause
1997 // conflict with existing assignment to 100
1999 $res = db_query_params('DELETE FROM project_assigned_to
2000 WHERE project_task_id IN (SELECT pt.project_task_id
2001 FROM project_task pt, project_group_list pgl, project_assigned_to pat
2002 WHERE pt.group_project_id = pgl.group_project_id
2003 AND pat.project_task_id=pt.project_task_id
2004 AND pt.status_id=1 AND pgl.group_id=$1
2005 AND pat.assigned_to_id=$2)
2006 AND assigned_to_id=100',
2007 array($this->getID(),
2010 $this->setError(sprintf(_('ERROR: DB: project_assigned_to %d: %s'), 1, db_error()));
2014 $res = db_query_params('UPDATE project_assigned_to SET assigned_to_id=100
2015 WHERE project_task_id IN (SELECT pt.project_task_id
2016 FROM project_task pt, project_group_list pgl
2017 WHERE pt.group_project_id = pgl.group_project_id
2018 AND pt.status_id=1 AND pgl.group_id=$1)
2019 AND assigned_to_id=$2',
2020 array($this->getID(),
2023 $this->setError(sprintf(_('ERROR: DB: project_assigned_to %d: %s'), 2, db_error()));
2029 // Remove user from system
2031 if (!$SYS->sysGroupRemoveUser($this->getID(), $user_id)) {
2032 $this->setError($SYS->getErrorMessage());
2037 $hook_params['group'] = $this;
2038 $hook_params['group_id'] = $this->getID();
2039 $hook_params['user'] = user_get_object($user_id);
2040 $hook_params['user_id'] = $user_id;
2041 plugin_hook ("group_removeuser", $hook_params);
2044 $this->addHistory('Removed User',$user_id);
2051 * updateUser - controls updating a user's role in this group.
2053 * @param int The ID of the user.
2054 * @param int The role_id to set this user to.
2055 * @return boolean success.
2057 function updateUser($user_id,$role_id) {
2060 if (!forge_check_perm ('project_admin', $this->getID())) {
2061 $this->setPermissionDeniedError();
2065 $newrole = RBACEngine::getInstance()->getRoleById ($role_id) ;
2066 if (!$newrole || !is_object($newrole)) {
2067 $this->setError(_('Could Not Get Role'));
2069 } elseif ($newrole->isError()) {
2070 $this->setError(sprintf(_('Role: %s'),$role->getErrorMessage()));
2072 } elseif ($newrole->getHomeProject() == NULL
2073 || $newrole->getHomeProject()->getID() != $this->getID()) {
2074 $this->setError(_('Wrong destination role'));
2077 $user = user_get_object ($user_id) ;
2078 $roles = RBACEngine::getInstance()->getAvailableRolesForUser ($user) ;
2079 $found_role = NULL ;
2080 foreach ($roles as $role) {
2081 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2082 $found_role = $role ;
2086 if ($found_role == NULL) {
2087 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2091 $found_role->removeUser ($user) ;
2092 $newrole->addUser ($user) ;
2094 $this->addHistory('Updated User',$user_id);
2099 * addHistory - Makes an audit trail entry for this project.
2101 * @param string The name of the field.
2102 * @param string The Old Value for this $field_name.
2103 * @return database result handle.
2106 function addHistory($field_name, $old_value) {
2107 return db_query_params ('INSERT INTO group_history(group_id,field_name,old_value,mod_by,adddate)
2108 VALUES ($1,$2,$3,$4,$5)',
2109 array ($this->getID(),
2117 * activateUsers - Make sure that group members have unix accounts.
2119 * Setup unix accounts for group members. Can be called even
2120 * if members are already active.
2124 function activateUsers() {
2126 Activate member(s) of the project
2129 $members = $this->getUsers (true) ;
2131 foreach ($members as $member) {
2133 foreach (RBACEngine::getInstance()->getAvailableRolesForUser ($member) as $role) {
2134 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2137 if (!$this->addUser($member->getUnixName(),$role->getID())) {
2148 * getMembers - returns array of User objects for this project
2150 * @return array of User objects for this group.
2152 function getMembers() {
2153 return $this->getUsers (true) ;
2157 * replaceTemplateStrings - fill-in some blanks with project name
2159 * @param string Template string
2160 * @return string String after replacements
2162 function replaceTemplateStrings($string) {
2163 $string = str_replace ('UNIXNAME', $this->getUnixName(), $string) ;
2164 $string = str_replace ('PUBLICNAME', $this->getPublicName(), $string) ;
2165 $string = str_replace ('DESCRIPTION', $this->getDescription(), $string) ;
2170 * approve - Approve pending project.
2172 * @param object The User object who is doing the updating.
2175 function approve(&$user) {
2176 global $gfcommon,$gfwww;
2177 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
2179 if ($this->getStatus()=='A') {
2180 $this->setError(_("Group already active"));
2186 // Step 1: Activate group and create LDAP entries
2187 if (!$this->setStatus($user, 'A')) {
2192 // Switch to system language for item creation
2193 setup_gettext_from_sys_lang();
2195 // Create default roles
2196 $idadmin_group = NULL;
2197 foreach (get_group_join_requests ($this) as $gjr) {
2198 $idadmin_group = $gjr->getUserID();
2201 if ($idadmin_group == NULL) {
2202 $idadmin_group = $user->getID();
2205 $template = $this->getTemplateProject();
2206 $id_mappings = array();
2207 $seen_admin_role = false;
2209 // Copy roles from template project
2210 foreach($template->getRoles() as $oldrole) {
2211 if ($oldrole->getHomeProject() != NULL) {
2212 $role = new Role($this);
2214 // Need to use a different role name so that the permissions aren't set from the hardcoded defaults
2215 $role->create('TEMPORARY ROLE NAME', $data, true);
2216 $role->setName($oldrole->getName());
2217 if ($oldrole->getSetting ('project_admin', $template->getID())) {
2218 $seen_admin_role = true;
2222 $role->linkProject($this);
2224 $id_mappings['role'][$oldrole->getID()] = $role->getID();
2225 // Reuse the project_admin permission
2226 $role->setSetting ('project_admin', $this->getID(), $oldrole->getSetting ('project_admin', $template->getID())) ;
2230 if (!$seen_admin_role) {
2231 $role = new Role($this);
2232 $adminperms = array ('project_admin' => array ($this->getID() => 1)) ;
2233 $role_id = $role->create ('Admin', $adminperms, true) ;
2236 $roles = $this->getRoles() ;
2237 foreach ($roles as $r) {
2238 if ($r->getHomeProject() == NULL) {
2241 if ($r->getSetting ('project_admin', $this->getID())) {
2242 $r->addUser(user_get_object ($idadmin_group));
2246 // Temporarily switch to the submitter's identity
2247 $saved_session = session_get_user();
2248 session_set_internal($idadmin_group);
2251 if (forge_get_config('use_tracker')) {
2252 $this->setUseTracker ($template->usesTracker());
2253 if ($template->usesTracker()) {
2254 $oldatf = new ArtifactTypeFactory($template);
2255 foreach ($oldatf->getArtifactTypes() as $o) {
2256 $t = new ArtifactType ($this) ;
2257 $t->create ($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->isPublic(),$o->allowsAnon(),$o->emailAll(),$o->getEmailAddress(),$o->getDuePeriod()/86400,0,$o->getSubmitInstructions(),$o->getBrowseInstructions()) ;
2258 $id_mappings['tracker'][$o->getID()] = $t->getID();
2259 $t->cloneFieldsFrom ($o->getID());
2264 if (forge_get_config('use_pm')) {
2265 $this->setUsePM ($template->usesPM());
2266 if ($template->usesPM()) {
2267 $oldpgf = new ProjectGroupFactory($template);
2268 foreach ($oldpgf->getProjectGroups() as $o) {
2269 $pg = new ProjectGroup($this);
2270 $pg->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->isPublic(),$o->getSendAllPostsTo());
2271 $id_mappings['pm'][$o->getID()] = $pg->getID();
2276 if (forge_get_config('use_forum')) {
2277 $this->setUseForum($template->usesForum()) ;
2278 if ($template->usesForum()) {
2279 $oldff = new ForumFactory($template) ;
2280 foreach ($oldff->getForums() as $o) {
2281 $f = new Forum($this);
2282 $f->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),0,$o->getSendAllPostsTo(),1,0,0);
2283 $id_mappings['forum'][$o->getID()] = $f->getID();
2288 if (forge_get_config('use_docman')) {
2289 $this->setUseDocman($template->usesDocman());
2290 if ($template->usesDocman()) {
2291 $olddgf = new DocumentGroupFactory($template);
2292 // First pass: create all docgroups
2293 $id_mappings['docman_docgroup'][0] = 0;
2294 foreach ($olddgf->getDocumentGroups() as $o) {
2295 $ndgf = new DocumentGroup($this);
2296 // .trash is a reserved directory
2297 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2298 $ndgf->create($this->replaceTemplateStrings($o->getName()));
2299 $id_mappings['docman_docgroup'][$o->getID()] = $ndgf->getID();
2302 // Second pass: restore hierarchy links
2303 foreach ($olddgf->getDocumentGroups() as $o) {
2304 $ndgf = new DocumentGroup($this);
2305 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2306 $ndgf->fetchData($id_mappings['docman_docgroup'][$o->getID()]);
2307 $ndgf->update($ndgf->getName(), $id_mappings['docman_docgroup'][$o->getParentID()]);
2313 if (forge_get_config('use_frs')) {
2314 $this->setUseFRS ($template->usesFRS());
2315 if ($template->usesFRS()) {
2316 foreach (get_frs_packages($template) as $o) {
2317 $newp = new FRSPackage($this);
2318 $nname = $this->replaceTemplateStrings($o->getName());
2319 $newp->create ($nname, $o->isPublic());
2324 if (forge_get_config('use_mail')) {
2325 $this->setUseMail($template->usesMail()) ;
2326 if ($template->usesMail()) {
2327 $oldmlf = new MailingListFactory($template);
2328 foreach ($oldmlf->getMailingLists() as $o) {
2329 $ml = new MailingList($this);
2330 $nname = preg_replace ('/^'.$template->getUnixName().'-/','',$o->getName()) ;
2332 $ndescription = $this->replaceTemplateStrings($o->getDescription()) ;
2333 $ml->create($nname, $ndescription, $o->isPublic());
2338 $this->setUseSCM ($template->usesSCM()) ;
2340 foreach ($template->getPlugins() as $plugin_id => $plugin_name) {
2341 $this->setPluginUse ($plugin_name) ;
2344 foreach ($template->getRoles() as $oldrole) {
2345 $newrole = RBACEngine::getInstance()->getRoleById ($id_mappings['role'][$oldrole->getID()]) ;
2346 if ($oldrole->getHomeProject() != NULL
2347 && $oldrole->getHomeProject()->getID() == $template->getID()) {
2348 $newrole->setPublic ($oldrole->isPublic()) ;
2350 $oldsettings = $oldrole->getSettingsForProject ($template) ;
2352 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm') ;
2353 foreach ($sections as $section) {
2354 $newrole->setSetting ($section, $this->getID(), $oldsettings[$section][$template->getID()]) ;
2357 $sections = array ('tracker', 'pm', 'forum') ;
2358 foreach ($sections as $section) {
2359 if (isset ($oldsettings[$section])) {
2360 foreach ($oldsettings[$section] as $k => $v) {
2361 // Only copy perms for tools that have been copied
2362 if (isset ($id_mappings[$section][$k])) {
2363 $newrole->setSetting ($section,
2364 $id_mappings[$section][$k],
2372 $lm = new WidgetLayoutManager();
2373 $lm->createDefaultLayoutForProject ($this->getID(), $template->getID()) ;
2375 $params = array () ;
2376 $params['template'] = $template ;
2377 $params['project'] = $this ;
2378 $params['id_mappings'] = $id_mappings ;
2379 plugin_hook_by_reference ('clone_project_from_template', $params) ;
2381 // Disable everything
2382 $res = 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',
2384 array ($this->getID())) ;
2387 $this->normalizeAllRoles();
2388 $this->activateUsers();
2390 // Switch back to user preference
2391 session_set_internal($saved_session->getID());
2392 setup_gettext_from_context();
2396 $this->sendApprovalEmail();
2397 $this->addHistory('Approved', 'x');
2400 // Plugin can make approve operation there
2402 $params[0] = $idadmin_group;
2403 $params[1] = $this->getID();
2404 plugin_hook('group_approved', $params);
2412 * sendApprovalEmail - Send new project email.
2414 * @return boolean success.
2417 function sendApprovalEmail() {
2418 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('project_admin', $this->getID()) ;
2420 if (count($admins) < 1) {
2421 $this->setError(_("Group does not have any administrators."));
2425 // send one email per admin
2426 foreach ($admins as $admin) {
2427 setup_gettext_for_user ($admin) ;
2429 $message=sprintf(_('Your project registration for %4$s has been approved.
2431 Project Full Name: %1$s
2432 Project Unix Name: %2$s
2434 Your DNS will take up to a day to become active on our site.
2435 Your web site is accessible through your shell account. Please read
2436 site documentation (see link below) about intended usage, available
2437 services, and directory layout of the account.
2440 own project page in %4$s while logged in, you will find
2441 additional menu functions to your left labeled \'Project Admin\'.
2443 We highly suggest that you now visit %4$s and create a public
2444 description for your project. This can be done by visiting your project
2445 page while logged in, and selecting \'Project Admin\' from the menus
2446 on the left (or by visiting %3$s
2449 Your project will also not appear in the Trove Software Map (primary
2450 list of projects hosted on %4$s which offers great flexibility in
2451 browsing and search) until you categorize it in the project administration
2452 screens. So that people can find your project, you should do this now.
2453 Visit your project while logged in, and select \'Project Admin\' from the
2456 Enjoy the system, and please tell others about %4$s. Let us know
2457 if there is anything we can do to help you.
2460 htmlspecialchars_decode($this->getPublicName()),
2461 $this->getUnixName(),
2462 util_make_url ('/project/admin/?group_id='.$this->getID()),
2463 forge_get_config ('forge_name'));
2465 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Approved'), forge_get_config ('forge_name')), $message);
2467 setup_gettext_from_context();
2475 * sendRejectionEmail - Send project rejection email.
2477 * This function sends out a rejection message to a user who
2478 * registered a project.
2480 * @param int The id of the response to use.
2481 * @param string The rejection message.
2482 * @return boolean completion status.
2485 function sendRejectionEmail($response_id, $message="zxcv") {
2486 $submitters = array () ;
2487 foreach (get_group_join_requests ($this) as $gjr) {
2488 $submitters[] = user_get_object($gjr->getUserID());
2491 if (count ($submitters) < 1) {
2492 $this->setError(_("Group does not have any administrators."));
2496 foreach ($submitters as $admin) {
2497 setup_gettext_for_user($admin);
2499 $response=sprintf(_('Your project registration for %3$s has been denied.
2501 Project Full Name: %1$s
2502 Project Unix Name: %2$s
2504 Reasons for negative decision:
2506 '), $this->getPublicName(), $this->getUnixName(), forge_get_config('forge_name'));
2508 // Check to see if they want to send a custom rejection response
2509 if ($response_id == 0) {
2510 $response .= $message;
2512 $response .= db_result(
2513 db_query_params('SELECT response_text FROM canned_responses WHERE response_id=$1', array ($response_id)),
2518 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Denied'), forge_get_config ('forge_name')), $response);
2519 setup_gettext_from_context();
2526 * sendNewProjectNotificationEmail - Send new project notification email.
2528 * This function sends out a notification email to the
2529 * SourceForge admin user when a new project is
2532 * @return boolean success.
2535 function sendNewProjectNotificationEmail() {
2536 // Get the user who wants to register the project
2537 $submitters = array();
2538 foreach (get_group_join_requests ($this) as $gjr) {
2539 $submitters[] = user_get_object($gjr->getUserID());
2541 if (count ($submitters) < 1) {
2542 $this->setError(_("Could not find user who has submitted the project."));
2546 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('approve_projects', -1) ;
2548 if (count($admins) < 1) {
2549 $this->setError(_("There is no administrator to send the mail to."));
2553 foreach ($admins as $admin) {
2554 $admin_email = $admin->getEmail () ;
2555 setup_gettext_for_user ($admin) ;
2557 foreach ($submitters as $u) {
2558 $submitter_names[] = $u->getRealName() ;
2561 $message = sprintf(_('New %1$s Project Submitted
2563 Project Full Name: %2$s
2564 Submitted Description: %3$s
2566 forge_get_config ('forge_name'),
2567 htmlspecialchars_decode($this->getPublicName()),
2568 htmlspecialchars_decode($this->getRegistrationPurpose()));
2570 foreach ($submitters as $submitter) {
2571 $message .= sprintf(_('Submitter: %1$s (%2$s)
2573 $submitter->getRealName(),
2574 $submitter->getUnixName());
2577 $message .= sprintf (_('
2578 Please visit the following URL to approve or reject this project:
2580 util_make_url ('/admin/approve-pending.php')) ;
2581 util_send_message($admin_email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2582 setup_gettext_from_context();
2586 $email = $submitter->getEmail() ;
2587 setup_gettext_for_user ($submitter) ;
2589 $message=sprintf(_('New %1$s Project Submitted
2591 Project Full Name: %2$s
2592 Submitted Description: %3$s
2594 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'));
2596 util_send_message($email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2597 setup_gettext_from_context();
2606 * validateGroupName - Validate the group name
2608 * @param string Group name.
2610 * @return boolean an error false and set an error is the group name is invalide otherwise return true
2612 function validateGroupName($group_name) {
2613 if (strlen($group_name)<3) {
2614 $this->setError(_('Group name is too short'));
2616 } else if (strlen(htmlspecialchars($group_name))>50) {
2617 $this->setError(_('Group name is too long'));
2619 } else if ($group=group_get_object_by_publicname($group_name)) {
2620 $this->setError(_('Group name already taken'));
2628 * getRolesId - Get Ids of the roles of the group.
2630 * @return array Role ids of this group.
2632 function getRolesId() {
2633 $role_ids = array();
2635 $res = db_query_params('SELECT role_id FROM pfo_role WHERE home_group_id=$1',
2636 array($this->getID()));
2637 while ($arr = db_fetch_array($res)) {
2638 $role_ids[] = $arr['role_id'];
2640 $res = db_query_params('SELECT role_id FROM role_project_refs WHERE group_id=$1',
2641 array($this->getID()));
2642 while ($arr = db_fetch_array($res)) {
2643 $role_ids[] = $arr['role_id'];
2646 return array_unique($role_ids);
2650 * getRoles - Get the roles of the group.
2652 * @return array Roles of this group.
2654 function getRoles() {
2657 $roles = $this->getRolesId();
2658 $engine = RBACEngine::getInstance();
2659 foreach ($roles as $role_id) {
2660 $result[] = $engine->getRoleById ($role_id);
2666 function normalizeAllRoles() {
2667 $roles = $this->getRoles();
2669 foreach ($roles as $r) {
2670 $r->normalizeData();
2675 * getUnixStatus - Status of activation of unix account.
2677 * @return char (N)one, (A)ctive, (S)uspended or (D)eleted
2679 function getUnixStatus() {
2680 return $this->data_array['unix_status'];
2684 * setUnixStatus - Sets status of activation of unix account.
2686 * @param string The unix status.
2692 * @return boolean success.
2694 function setUnixStatus($status) {
2697 $res = db_query_params ('UPDATE groups SET unix_status=$1 WHERE group_id=$2',
2702 $this->setError(sprintf(_('ERROR - Could Not Update Group Unix Status: %s'),db_error()));
2706 if ($status == 'A') {
2707 if (!$SYS->sysCheckCreateGroup($this->getID())) {
2708 $this->setError($SYS->getErrorMessage());
2713 if ($SYS->sysCheckGroup($this->getID())) {
2714 if (!$SYS->sysRemoveGroup($this->getID())) {
2715 $this->setError($SYS->getErrorMessage());
2722 $this->data_array['unix_status']=$status;
2729 * getUsers - Get the users of a group
2731 * @return array of user's objects.
2733 function getUsers($onlylocal = true) {
2734 if (!isset($this->membersArr)) {
2735 $this->membersArr = array () ;
2738 foreach ($this->getRoles() as $role) {
2740 && ($role->getHomeProject() == NULL || $role->getHomeProject()->getID() != $this->getID())) {
2743 foreach ($role->getUsers() as $user) {
2744 $ids[] = $user->getID() ;
2747 $ids = array_unique ($ids) ;
2748 foreach ($ids as $id) {
2749 $u = user_get_object ($id) ;
2750 if ($u->isActive()) {
2751 $this->membersArr[] = $u ;
2755 return $this->membersArr;
2758 function setDocmanCreateOnlineStatus($status) {
2760 /* if we activate search engine, we probably want to reindex */
2761 $res = db_query_params('UPDATE groups SET use_docman_create_online=$1 WHERE group_id=$2',
2762 array($status, $this->getID()));
2765 $this->setError(sprintf(_('ERROR - Could Not Update Group DocmanCreateOnline Status: %s'),db_error()));
2769 $this->data_array['use_docman_create_online']=$status;
2775 function setDocmanWebdav($status) {
2777 /* if we activate search engine, we probably want to reindex */
2778 $res = db_query_params('UPDATE groups SET use_webdav=$1 WHERE group_id=$2',
2783 $this->setError(sprintf(_('ERROR - Could Not Update Group UseWebdab Status: %s'),db_error()));
2787 $this->data_array['use_webdav']=$status;
2793 function setDocmanSearchStatus($status) {
2795 /* if we activate search engine, we probably want to reindex */
2796 $res = db_query_params('UPDATE groups SET use_docman_search=$1, force_docman_reindex=$1 WHERE group_id=$2',
2801 $this->setError(sprintf(_('ERROR - Could Not Update Group UseDocmanSearch Status: %s'),db_error()));
2805 $this->data_array['use_docman_search']=$status;
2811 function setDocmanForceReindexSearch($status) {
2813 /* if we activate search engine, we probably want to reindex */
2814 $res = db_query_params('UPDATE groups SET force_docman_reindex=$1 WHERE group_id=$2',
2819 $this->setError(sprintf(_('ERROR - Could Not Update Group force_docman_reindex %s'),db_error()));
2823 $this->data_array['force_docman_reindex']=$status;
2829 function setStorageAPI($type) {
2833 function getStorageAPI() {
2839 * group_getname() - get the group name
2841 * @param int The group ID
2845 function group_getname ($group_id = 0) {
2846 $grp = group_get_object($group_id);
2848 return $grp->getPublicName();
2855 * group_getunixname() - get the unixname for a group
2857 * @param int The group ID
2861 function group_getunixname ($group_id) {
2862 $grp = group_get_object($group_id);
2864 return $grp->getUnixName();
2871 * group_get_result() - Get the group object result ID.
2873 * @param int The group ID
2877 function &group_get_result($group_id=0) {
2878 $grp = group_get_object($group_id);
2880 return $grp->getData();
2886 function getAllProjectTags($onlyvisible = true) {
2887 $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',
2890 if (!$res || db_numrows($res) == 0) {
2896 while ($arr = db_fetch_array($res)) {
2898 $group_id = $arr[1];
2899 if (!isset($result[$tag])) {
2900 $result[$tag] = array();
2903 if (!$onlyvisible || forge_check_perm('project_read', $group_id)) {
2904 $p = group_get_object($group_id);
2905 $result[$tag][] = array('unix_group_name' => $p->getUnixName(),
2906 'group_id' => $group_id);
2914 * Utility class to compare project based in various criteria (names, unixnames, id, ...)
2917 class ProjectComparator {
2918 var $criterion = 'name' ;
2920 function Compare ($a, $b) {
2921 switch ($this->criterion) {
2924 $namecmp = strcoll ($a->getPublicName(), $b->getPublicName()) ;
2925 if ($namecmp != 0) {
2928 /* If several projects share a same public name */
2929 return strcoll ($a->getUnixName(), $b->getUnixName()) ;
2932 return strcmp ($a->getUnixName(), $b->getUnixName()) ;
2935 $aid = $a->getID() ;
2936 $bid = $b->getID() ;
2940 return ($a < $b) ? -1 : 1;
2946 function sortProjectList (&$list, $criterion='name') {
2947 $cmp = new ProjectComparator () ;
2948 $cmp->criterion = $criterion ;
2950 return usort ($list, array ($cmp, 'Compare')) ;
2955 // c-file-style: "bsd"