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 * Copyright 2012, Franck Villaume - TrivialDev
10 * http://fusionforge.org
12 * This file is part of FusionForge. FusionForge is free software;
13 * you can redistribute it and/or modify it under the terms of the
14 * GNU General Public License as published by the Free Software
15 * Foundation; either version 2 of the Licence, or (at your option)
18 * FusionForge is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 require_once $gfcommon.'tracker/ArtifactTypes.class.php';
29 require_once $gfcommon.'tracker/ArtifactTypeFactory.class.php';
30 require_once $gfcommon.'forum/Forum.class.php';
31 require_once $gfcommon.'forum/ForumFactory.class.php';
32 require_once $gfcommon.'pm/ProjectGroup.class.php';
33 require_once $gfcommon.'pm/ProjectGroupFactory.class.php';
34 require_once $gfcommon.'include/Role.class.php';
35 require_once $gfcommon.'frs/FRSPackage.class.php';
36 require_once $gfcommon.'docman/DocumentGroup.class.php';
37 require_once $gfcommon.'docman/DocumentGroupFactory.class.php';
38 require_once $gfcommon.'mail/MailingList.class.php';
39 require_once $gfcommon.'mail/MailingListFactory.class.php';
40 require_once $gfcommon.'survey/SurveyFactory.class.php';
41 require_once $gfcommon.'survey/SurveyQuestionFactory.class.php';
42 require_once $gfcommon.'include/gettext.php';
43 require_once $gfcommon.'include/GroupJoinRequest.class.php';
48 * group_get_object() - Get the group object.
50 * group_get_object() is useful so you can pool group objects/save database queries
51 * You should always use this instead of instantiating the object directly.
53 * You can now optionally pass in a db result handle. If you do, it re-uses that query
54 * to instantiate the objects.
56 * IMPORTANT! That db result must contain all fields
57 * from groups table or you will have problems
60 * @param int Result set handle ("SELECT * FROM groups WHERE group_id=xx")
61 * @return object a group object or false on failure
63 function &group_get_object($group_id, $res = false) {
64 //create a common set of group objects
65 //saves a little wear on the database
67 //automatically checks group_type and
68 //returns appropriate object
71 if (!isset($GROUP_OBJ["_".$group_id."_"])) {
73 //the db result handle was passed in
75 $res = db_query_params('SELECT * FROM groups WHERE group_id=$1', array($group_id)) ;
77 if (!$res || db_numrows($res) < 1) {
78 $GROUP_OBJ["_".$group_id."_"]=false;
81 check group type and set up object
83 if (db_result($res,0,'type_id') == 1) {
85 $GROUP_OBJ["_".$group_id."_"] = new Group($group_id, $res);
88 $GROUP_OBJ["_".$group_id."_"] = false;
92 return $GROUP_OBJ["_".$group_id."_"];
95 function &group_get_objects($id_arr) {
98 // Note: if we don't do this, the result may be corrupted
102 foreach ($id_arr as $id) {
104 // See if this ID already has been fetched in the cache
106 if (!isset($GROUP_OBJ["_".$id."_"])) {
110 if (count($fetch) > 0) {
111 $res=db_query_params('SELECT * FROM groups WHERE group_id = ANY ($1)',
112 array(db_int_array_to_any_clause($fetch)));
113 while ($arr = db_fetch_array($res)) {
114 $GROUP_OBJ["_".$arr['group_id']."_"] = new Group($arr['group_id'],$arr);
117 foreach ($id_arr as $id) {
118 $return[] =& $GROUP_OBJ["_".$id."_"];
123 function &group_get_active_projects() {
124 $res = db_query_params('SELECT group_id FROM groups WHERE status=$1',
126 return group_get_objects(util_result_column_to_array($res,0));
129 function &group_get_template_projects() {
130 $res=db_query_params ('SELECT group_id FROM groups WHERE is_template=1 AND status != $1',
132 return group_get_objects (util_result_column_to_array($res,0)) ;
135 function &group_get_object_by_name($groupname) {
136 $res = db_query_params('SELECT * FROM groups WHERE unix_group_name=$1', array($groupname));
137 return group_get_object(db_result($res, 0, 'group_id'), $res);
140 function &group_get_objects_by_name($groupname_arr) {
141 $res = db_query_params('SELECT group_id FROM groups WHERE unix_group_name = ANY ($1)',
142 array(db_string_array_to_any_clause($groupname_arr)));
143 $arr =& util_result_column_to_array($res,0);
144 return group_get_objects($arr);
147 function &group_get_object_by_publicname($groupname) {
148 $res = db_query_params('SELECT * FROM groups WHERE lower(group_name) LIKE $1',
149 array(htmlspecialchars(strtolower($groupname))));
150 return group_get_object(db_result($res, 0, 'group_id'), $res);
153 class Group extends Error {
155 * Associative array of data from db.
157 * @var array $data_array.
162 * array of User objects.
164 * @var array $membersArr.
169 * Whether the use is an admin/super user of this project.
171 * @var bool $is_admin.
176 * Artifact types result handle.
178 * @var int $types_res.
183 * Associative array of data for plugins.
185 * @var array $plugins_data.
191 * Associative array of data for the group menu.
193 * @var array $menu_data.
198 * Group - Group object constructor - use group_get_object() to instantiate.
200 * @param int Required - group_id of the group you want to instantiate.
201 * @param int Database result from select query OR associative array of all columns.
202 * @return boolean success or not
204 function Group($id = false, $res = false) {
207 //setting up an empty object
208 //probably going to call create()
212 if (!$this->fetchData($id)) {
217 // Assoc array was passed in
219 if (is_array($res)) {
220 $this->data_array =& $res;
222 if (db_numrows($res) < 1) {
223 //function in class we extended
224 $this->setError(_('Group Not Found'));
225 $this->data_array=array();
228 //set up an associative array for use by other functions
229 $this->data_array = db_fetch_array_by_row($res, 0);
237 * fetchData - May need to refresh database fields if an update occurred.
239 * @param int The group_id.
240 * @return boolean success or not
242 function fetchData($group_id) {
243 $res = db_query_params ('SELECT * FROM groups WHERE group_id=$1',
245 if (!$res || db_numrows($res) < 1) {
246 $this->setError(sprintf(_('fetchData():: %s'),db_error()));
249 $this->data_array = db_fetch_array($res);
254 * create - Create new group.
256 * This method should be called on empty Group object.
257 * It will add an entry for a pending group/project (status 'P')
259 * @param object The User object.
260 * @param string The full name of the user.
261 * @param string The Unix name of the user.
262 * @param string The new group description.
263 * @param string The purpose of the group.
264 * @param boolean Whether to send an email or not
265 * @param int The id of the project this new project is based on
266 * @return boolean success or not
268 function create(&$user, $group_name, $unix_name, $description, $purpose, $unix_box = 'shell1',
269 $scm_box = 'cvs1', $is_public = 1, $send_mail = true, $built_from_template = 0) {
270 // $user is ignored - anyone can create pending group
273 if ($this->getID()!=0) {
274 $this->setError(_('Group::create: Group object already exists'));
276 } else if (!$this->validateGroupName($group_name)) {
278 } else if (!account_groupnamevalid($unix_name)) {
279 $this->setError(_('Invalid Unix name'));
281 } else if (!$SYS->sysUseUnixName($unix_name)) {
282 $this->setError(_('Unix name already taken'));
284 } else if (db_numrows(db_query_params('SELECT group_id FROM groups WHERE unix_group_name=$1',
285 array($unix_name))) > 0) {
286 $this->setError(_('Unix name already taken'));
288 } else if (strlen($purpose)<10) {
289 $this->setError(_('Please describe your Registration Purpose in a more comprehensive manner'));
291 } else if (strlen($purpose)>1500) {
292 $this->setError(_('The Registration Purpose text is too long. Please make it smaller than 1500 bytes.'));
294 } else if (strlen($description)<10) {
295 $this->setError(_('Describe in a more comprehensive manner your project.'));
299 // Check if sys_use_project_vhost for homepage
300 if (forge_get_config('use_project_vhost')) {
301 $homepage = $unix_name.".".forge_get_config('web_host');
303 $homepage = forge_get_config('web_host')."/www/".$unix_name."/";
308 $res = db_query_params('
324 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)',
325 array (htmlspecialchars ($group_name),
327 htmlspecialchars($description),
328 $unix_name.".".forge_get_config('web_host'),
333 htmlspecialchars($purpose),
336 md5(util_randbytes()),
337 $built_from_template));
338 if (!$res || db_affected_rows($res) < 1) {
339 $this->setError(sprintf(_('ERROR: Could not create group: %s'),db_error()));
344 $id = db_insertid($res, 'groups', 'group_id');
346 $this->setError(sprintf(_('ERROR: Could not get group id: %s'),db_error()));
351 if (!$this->fetchData($id)) {
356 $gjr = new GroupJoinRequest($this);
357 $gjr->create($user->getID(),
358 'Fake GroupJoinRequest to store the creator of a project',
361 $hook_params = array();
362 $hook_params['group'] = $this;
363 $hook_params['group_id'] = $this->getID();
364 $hook_params['group_name'] = $group_name;
365 $hook_params['unix_group_name'] = $unix_name;
366 plugin_hook("group_create", $hook_params);
370 $this->sendNewProjectNotificationEmail();
378 * updateAdmin - Update core properties of group object.
380 * This function require site admin privilege.
382 * @param object User requesting operation (for access control).
383 * @param boolean Whether group is publicly accessible (0/1).
384 * @param int Group type (1-project, 2-foundry).
385 * @param string Machine on which group's home directory located.
386 * @param string Domain which serves group's WWW.
390 function updateAdmin(&$user, $is_public, $type_id, $unix_box, $http_domain) {
391 $perm =& $this->getPermission();
393 if (!$perm || !is_object($perm)) {
394 $this->setError(_('Could not get permission.'));
398 if (!$perm->isSuperUser()) {
399 $this->setError(_('Permission denied.'));
405 $res = db_query_params('
407 SET type_id=$1, unix_box=$2, http_domain=$3
414 if (!$res || db_affected_rows($res) < 1) {
415 $this->setError(_('ERROR: DB: Could not change group properties: %s'),db_error());
420 // Log the audit trail
421 if ($type_id != $this->data_array['type_id']) {
422 $this->addHistory('type_id', $this->data_array['type_id']);
424 if ($unix_box != $this->data_array['unix_box']) {
425 $this->addHistory('unix_box', $this->data_array['unix_box']);
427 if ($http_domain != $this->data_array['http_domain']) {
428 $this->addHistory('http_domain', $this->data_array['http_domain']);
431 if (!$this->fetchData($this->getID())) {
440 * update - Update number of common properties.
442 * Unlike updateAdmin(), this function accessible to project admin.
444 * @param object User requesting operation (for access control).
445 * @param boolean Whether group is publicly accessible (0/1).
446 * @param string Project's license (string ident).
447 * @param int Group type (1-project, 2-foundry).
448 * @param string Machine on which group's home directory located.
449 * @param string Domain which serves group's WWW.
450 * @return int status.
453 function update(&$user, $group_name, $homepage, $short_description, $use_mail, $use_survey, $use_forum,
454 $use_pm, $use_pm_depend_box, $use_scm, $use_news, $use_docman,
455 $new_doc_address, $send_all_docs, $logo_image_id,
456 $use_ftp, $use_tracker, $use_frs, $use_stats, $tags, $is_public) {
458 $perm =& $this->getPermission();
460 if (!$perm || !is_object($perm)) {
461 $this->setError(_('Could not get permission.'));
465 if (!$perm->isAdmin()) {
466 $this->setError(_('Permission denied.'));
470 // Validate some values
471 if ($this->getPublicName() != $group_name) {
472 if (!$this->validateGroupName($group_name)) {
477 if ($new_doc_address) {
478 $invalid_mails = validate_emails($new_doc_address);
479 if (count($invalid_mails) > 0) {
480 $this->setError(sprintf(ngettext('New Doc Address Appeared Invalid: %s', 'New Doc Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
485 // in the database, these all default to '1',
486 // so we have to explicity set 0
499 if (!$use_pm_depend_box) {
500 $use_pm_depend_box = 0;
523 if (!$send_all_docs) {
527 $homepage = ltrim($homepage);
529 $homepage = util_make_url('/projects/' . $this->getUnixName() . '/');
532 if (strlen(htmlspecialchars($short_description))>255) {
533 $this->setError(_('Error updating project information: Maximum length for Project Description is 255 chars.'));
539 //XXX not yet actived logo_image_id='$logo_image_id',
540 $res = db_query_params('UPDATE groups
543 short_description=$3,
548 use_pm_depend_box=$8,
558 array(htmlspecialchars($group_name),
560 htmlspecialchars($short_description),
577 $this->setError(sprintf(_('Error updating project information: %s'), db_error()));
582 if (!$this->setUseDocman($use_docman)) {
583 $this->setError(sprintf(_('Error updating project information: use_docman %s'), db_error()));
588 if ($this->setTags($tags) === false) {
593 $hook_params = array();
594 $hook_params['group'] = $this;
595 $hook_params['group_id'] = $this->getID();
596 $hook_params['group_homepage'] = $homepage;
597 $hook_params['group_name'] = htmlspecialchars($group_name);
598 $hook_params['group_description'] = htmlspecialchars($short_description);
599 $hook_params['group_ispublic'] = $is_public;
600 if (!plugin_hook("group_update", $hook_params)) {
601 if (!$this->isError()) {
602 $this->setError(_('Error updating project information in plugin_hook group_update'));
608 // Log the audit trail
609 $this->addHistory('Changed Public Info', '');
611 if (!$this->fetchData($this->getID())) {
620 * getID - Simply return the group_id for this object.
622 * @return int group_id.
625 return $this->data_array['group_id'];
629 * getType() - Foundry, project, etc.
631 * @return int The type flag from the database.
634 return $this->data_array['type_id'];
639 * getStatus - the status code.
641 * Statuses char include I,H,A,D,P.
642 * TODO : document what these mean :
649 function getStatus() {
650 return $this->data_array['status'];
654 * setStatus - set the status code.
656 * Statuses include I,H,A,D,P.
657 * TODO : document what these mean :
664 * @param object User requesting operation (for access control).
665 * @param string Status value.
666 * @return boolean success.
669 function setStatus(&$user, $status) {
672 if (!forge_check_global_perm('approve_projects')) {
673 $this->setPermissionDeniedError();
677 // Projects in 'A' status can only go to 'H' or 'D'
678 // Projects in 'D' status can only go to 'A'
679 // Projects in 'P' status can only go to 'A' OR 'D'
680 // Projects in 'I' status can only go to 'P'
681 // Projects in 'H' status can only go to 'A' OR 'D'
682 $allowed_status_changes = array(
683 'AH'=>1,'AD'=>1,'DA'=>1,'PA'=>1,'PD'=>1,
684 'IP'=>1,'HA'=>1,'HD'=>1
687 // Check that status transition is valid
688 if ($this->getStatus() != $status
689 && !array_key_exists($this->getStatus(). $status,$allowed_status_changes)) {
690 $this->setError(_('Invalid Status Change'));
696 $res = db_query_params('UPDATE groups
698 WHERE group_id=$2', array($status, $this->getID()));
700 if (!$res || db_affected_rows($res) < 1) {
701 $this->setError(sprintf(_('ERROR: DB: Could not change group status: %s'),db_error()));
707 // Activate system group, if not yet
708 if (!$SYS->sysCheckGroup($this->getID())) {
709 if (!$SYS->sysCreateGroup($this->getID())) {
710 $this->setError($SYS->getErrorMessage());
715 if (!$this->activateUsers()) {
720 /* Otherwise, the group is not active, and make sure that
721 System group is not active either */
722 } else if ($SYS->sysCheckGroup($this->getID())) {
723 if (!$SYS->sysRemoveGroup($this->getID())) {
724 $this->setError($SYS->getErrorMessage());
730 $hook_params = array();
731 $hook_params['group'] = $this;
732 $hook_params['group_id'] = $this->getID();
733 $hook_params['status'] = $status;
734 plugin_hook("group_setstatus", $hook_params);
738 // Log the audit trail
739 if ($status != $this->getStatus()) {
740 $this->addHistory('Status', $this->getStatus());
743 $this->data_array['status'] = $status;
748 * isProject - Simple boolean test to see if it's a project or not.
750 * @return boolean is_project.
752 function isProject() {
753 if ($this->getType()==1) {
761 * isPublic - Wrapper around RBAC to check if a project is anonymously readable
763 * @return boolean is_public.
765 function isPublic() {
766 $ra = RoleAnonymous::getInstance() ;
767 return $ra->hasPermission('project_read', $this->getID());
771 * isActive - Database field status of 'A' returns true.
773 * @return boolean is_active.
775 function isActive() {
776 if ($this->getStatus()=='A') {
784 * isTemplate - Simply returns the is_template flag from the database.
786 * @return boolean is_template.
788 function isTemplate() {
789 return $this->data_array['is_template'];
793 * setAsTemplate - Set the template status of a project
795 * @param boolean is_template.
797 function setAsTemplate($booleanparam) {
799 $booleanparam = $booleanparam ? 1 : 0;
800 $res = db_query_params('UPDATE groups SET is_template=$1 WHERE group_id=$2',
801 array($booleanparam, $this->getID()));
803 $this->data_array['is_template']=$booleanparam;
813 * getTemplateProject - Return the project template this project is built from
815 * @return object The template project
817 function getTemplateProject() {
818 return group_get_object($this->data_array['built_from_template']);
822 * getUnixName - the unix_name
824 * @return string unix_name.
826 function getUnixName() {
827 return strtolower($this->data_array['unix_group_name']);
831 * getPublicName - the full-length public name.
833 * @return string The group_name.
835 function getPublicName() {
836 return $this->data_array['group_name'];
840 * getRegisterPurpose - the text description of the purpose of this project.
842 * @return string The description.
844 function getRegisterPurpose() {
845 return $this->data_array['register_purpose'];
849 * getDescription - the text description of this project.
851 * @return string The description.
853 function getDescription() {
854 return $this->data_array['short_description'];
858 * getStartDate - the unix time this project was registered.
860 * @return int (unix time) of registration.
862 function getStartDate() {
863 return $this->data_array['register_time'];
867 * getLogoImageID - the id of the logo in the database for this project.
869 * @return int The ID of logo image in db_images table (or 100 if none).
871 function getLogoImageID() {
872 return $this->data_array['logo_image_id'];
876 * getUnixBox - the hostname of the unix box where this project is located.
878 * @return string The name of the unix machine for the group.
880 function getUnixBox() {
881 return $this->data_array['unix_box'];
885 * getSCMBox - the hostname of the scm box where this project is located.
887 * @return string The name of the unix machine for the group.
889 function getSCMBox() {
890 return $this->data_array['scm_box'];
893 * setSCMBox - the hostname of the scm box where this project is located.
895 * @param string The name of the new SCM_BOX
897 function setSCMBox($scm_box) {
899 if ($scm_box == $this->data_array['scm_box']) {
904 $res = db_query_params('UPDATE groups SET scm_box=$1 WHERE group_id=$2', array($scm_box, $this->getID ()));
906 $this->addHistory('scm_box', $this->data_array['scm_box']);
907 $this->data_array['scm_box'] = $scm_box;
912 $this->setError(_("Couldn't insert SCM_BOX to database"));
916 $this->setError(_("SCM Box can't be empty"));
922 * getDomain - the hostname.domain where their web page is located.
924 * @return string The name of the group [web] domain.
926 function getDomain() {
927 return $this->data_array['http_domain'];
931 * getRegistrationPurpose - the text description of the purpose of this project.
933 * @return string The application for project hosting.
935 function getRegistrationPurpose() {
936 return $this->data_array['register_purpose'];
941 * getAdmins() - Get array of Admin user objects.
943 * @return array Array of User objects.
945 function &getAdmins() {
946 $roles = RBACEngine::getInstance()->getRolesByAllowedAction ('project_admin', $this->getID());
950 foreach ($roles as $role) {
951 if (! ($role instanceof RoleExplicit)) {
954 if ($role->getHomeProject() == NULL
955 || $role->getHomeProject()->getID() != $this->getID()) {
959 foreach ($role->getUsers() as $u) {
960 $user_ids[] = $u->getID();
963 return user_get_objects(array_unique($user_ids));
967 Common Group preferences for tools
971 * enableAnonSCM - whether or not this group has opted to enable Anonymous SCM.
973 * @return boolean enable_scm.
975 function enableAnonSCM() {
976 $r = RoleAnonymous::getInstance();
977 return $r->hasPermission('scm', $this->getID(), 'read');
980 function SetUsesAnonSCM($booleanparam) {
982 $booleanparam = $booleanparam ? 1 : 0;
983 $r = RoleAnonymous::getInstance();
984 $r->setSetting('scm', $this->getID(), $booleanparam);
989 * enablePserver - whether or not this group has opted to enable Pserver.
991 * @return boolean enable_pserver.
993 function enablePserver() {
994 if ($this->usesSCM()) {
995 return $this->data_array['enable_pserver'];
1001 function SetUsesPserver($booleanparam) {
1003 $booleanparam = $booleanparam ? 1 : 0;
1004 $res = db_query_params('UPDATE groups SET enable_pserver=$1 WHERE group_id=$2',
1005 array($booleanparam, $this->getID()));
1007 $this->data_array['enable_pserver'] = $booleanparam;
1016 * usesSCM - whether or not this group has opted to use SCM.
1018 * @return boolean uses_scm.
1020 function usesSCM() {
1021 if (forge_get_config('use_scm')) {
1022 return $this->data_array['use_scm'];
1029 * setUseSCM - Set the SCM usage
1031 * @param boolean enabled/disabled
1033 function setUseSCM($booleanparam) {
1035 $booleanparam = $booleanparam ? 1 : 0 ;
1036 $res = db_query_params('UPDATE groups SET use_scm=$1 WHERE group_id=$2',
1037 array($booleanparam, $this->getID()));
1039 $this->data_array['use_scm']=$booleanparam;
1049 * usesMail - whether or not this group has opted to use mailing lists.
1051 * @return boolean uses_mail.
1053 function usesMail() {
1054 if (forge_get_config('use_mail')) {
1055 return $this->data_array['use_mail'];
1062 * setUseMail - Set the mailing-list usage
1064 * @param boolean enabled/disabled
1066 function setUseMail($booleanparam) {
1068 $booleanparam = $booleanparam ? 1 : 0 ;
1069 $res = db_query_params('UPDATE groups SET use_mail=$1 WHERE group_id=$2',
1070 array($booleanparam, $this->getID()));
1072 $this->data_array['use_mail']=$booleanparam;
1082 * usesNews - whether or not this group has opted to use news.
1084 * @return boolean uses_news.
1086 function usesNews() {
1087 if (forge_get_config('use_news')) {
1088 return $this->data_array['use_news'];
1095 * usesForum - whether or not this group has opted to use discussion forums.
1097 * @return boolean uses_forum.
1099 function usesForum() {
1100 if (forge_get_config('use_forum')) {
1101 return $this->data_array['use_forum'];
1108 * setUseForum - Set the forum usage
1110 * @param boolean enabled/disabled
1112 function setUseForum($booleanparam) {
1114 $booleanparam = $booleanparam ? 1 : 0;
1115 $res = db_query_params('UPDATE groups SET use_forum=$1 WHERE group_id=$2',
1116 array($booleanparam, $this->getID()));
1118 $this->data_array['use_forum']=$booleanparam;
1128 * usesStats - whether or not this group has opted to use stats.
1130 * @return boolean uses_stats.
1132 function usesStats() {
1133 return $this->data_array['use_stats'];
1137 * usesFRS - whether or not this group has opted to use file release system.
1139 * @return boolean uses_frs.
1141 function usesFRS() {
1142 if (forge_get_config('use_frs')) {
1143 return $this->data_array['use_frs'];
1150 * setUseFRS - Set the FRS usage
1152 * @param boolean enabled/disabled
1154 function setUseFRS($booleanparam) {
1156 $booleanparam = $booleanparam ? 1 : 0;
1157 $res = db_query_params('UPDATE groups SET use_frs=$1 WHERE group_id=$2',
1158 array ($booleanparam, $this->getID()));
1160 $this->data_array['use_frs']=$booleanparam;
1170 * usesTracker - whether or not this group has opted to use tracker.
1172 * @return boolean uses_tracker.
1174 function usesTracker() {
1175 if (forge_get_config('use_tracker')) {
1176 return $this->data_array['use_tracker'];
1183 * setUseTracker - Set the tracker usage
1185 * @param boolean enabled/disabled
1187 function setUseTracker ($booleanparam) {
1189 $booleanparam = $booleanparam ? 1 : 0 ;
1190 $res = db_query_params ('UPDATE groups SET use_tracker=$1 WHERE group_id=$2',
1191 array ($booleanparam, $this->getID()));
1193 $this->data_array['use_tracker']=$booleanparam;
1203 * useCreateOnline - whether or not this group has opted to use create online documents option.
1205 * @return boolean use_docman_create_online.
1207 function useCreateOnline() {
1208 if (forge_get_config('use_docman')) {
1209 return $this->data_array['use_docman_create_online'];
1216 * usesDocman - whether or not this group has opted to use docman.
1218 * @return boolean use_docman.
1220 function usesDocman() {
1221 if (forge_get_config('use_docman')) {
1222 return $this->data_array['use_docman'];
1229 * setUseDocman - Set the docman usage
1231 * @param boolean enabled/disabled
1233 function setUseDocman($booleanparam) {
1235 $booleanparam = $booleanparam ? 1 : 0;
1236 $res = db_query_params('UPDATE groups SET use_docman = $1 WHERE group_id = $2',
1237 array($booleanparam, $this->getID()));
1239 // check if / doc_group exists, if not create it
1240 $trashdir = db_query_params('select groupname from doc_groups where groupname = $1 and group_id = $2',
1241 array('.trash', $this->getID()));
1242 if ($trashdir && db_numrows($trashdir) == 0) {
1243 $resinsert = db_query_params('insert into doc_groups (groupname, group_id, stateid) values ($1, $2, $3)',
1244 array('.trash', $this->getID(), '2'));
1250 $this->data_array['use_docman'] = $booleanparam;
1260 * useDocmanSearch - whether or not this group has opted to use docman search engine.
1262 * @return boolean use_docman_search.
1264 function useDocmanSearch() {
1265 if (forge_get_config('use_docman')) {
1266 return $this->data_array['use_docman_search'];
1273 * useWebdav - whether or not this group has opted to use webdav interface.
1275 * @return boolean use_docman_search.
1277 function useWebdav() {
1278 if (forge_get_config('use_webdav')) {
1279 return $this->data_array['use_webdav'];
1286 * usesFTP - whether or not this group has opted to use FTP.
1288 * @return boolean uses_ftp.
1290 function usesFTP() {
1291 if (forge_get_config('use_ftp')) {
1292 return $this->data_array['use_ftp'];
1299 * usesSurvey - whether or not this group has opted to use surveys.
1301 * @return boolean uses_survey.
1303 function usesSurvey() {
1304 if (forge_get_config('use_survey')) {
1305 return $this->data_array['use_survey'];
1312 * usesPM - whether or not this group has opted to Project Manager.
1314 * @return boolean uses_projman.
1317 if (forge_get_config('use_pm')) {
1318 return $this->data_array['use_pm'];
1325 * setUsePM - Set the PM usage
1327 * @param boolean enabled/disabled
1329 function setUsePM($booleanparam) {
1331 $booleanparam = $booleanparam ? 1 : 0;
1332 $res = db_query_params('UPDATE groups SET use_pm=$1 WHERE group_id=$2',
1333 array($booleanparam, $this->getID()));
1335 $this->data_array['use_pm']=$booleanparam;
1345 * getPlugins - get a list of all available group plugins
1347 * @return array array containing plugin_id => plugin_name
1349 function getPlugins() {
1350 if (!isset($this->plugins_data)) {
1351 $this->plugins_data = array();
1352 $res = db_query_params('SELECT group_plugin.plugin_id, plugins.plugin_name
1353 FROM group_plugin, plugins
1354 WHERE group_plugin.group_id=$1
1355 AND group_plugin.plugin_id=plugins.plugin_id', array($this->getID()));
1356 $rows = db_numrows($res);
1358 for ($i=0; $i<$rows; $i++) {
1359 $plugin_id = db_result($res, $i, 'plugin_id');
1360 $this->plugins_data[$plugin_id] = db_result($res, $i, 'plugin_name');
1363 return $this->plugins_data;
1367 * usesPlugin - returns true if the group uses a particular plugin
1369 * @param string name of the plugin
1370 * @return boolean whether plugin is being used or not
1372 function usesPlugin($pluginname) {
1373 $plugins_data = $this->getPlugins();
1374 foreach ($plugins_data as $p_id => $p_name) {
1375 if ($p_name == $pluginname) {
1383 * added for Codendi compatibility
1384 * usesServices - returns true if the group uses a particular plugin or feature
1386 * @param string name of the plugin
1387 * @return boolean whether plugin is being used or not
1389 function usesService($feature) {
1390 $plugins_data = $this->getPlugins();
1391 $pm = plugin_manager_get_object();
1392 foreach ($plugins_data as $p_id => $p_name) {
1393 if ($p_name == $feature) {
1396 if ($pm->getPluginByName($p_name)->provide($feature)) {
1404 * setPluginUse - enables/disables plugins for the group
1406 * @param string name of the plugin
1407 * @param boolean the new state
1408 * @return string database result
1410 function setPluginUse($pluginname, $val=true) {
1411 if ($val == $this->usesPlugin($pluginname)) {
1412 // State is already good, returning
1415 $res = db_query_params('SELECT plugin_id FROM plugins WHERE plugin_name=$1',
1416 array($pluginname));
1417 $rows = db_numrows($res);
1419 // Error: no plugin by that name
1422 $plugin_id = db_result($res,0,'plugin_id');
1424 unset($this->plugins_data);
1426 $res = db_query_params('INSERT INTO group_plugin (group_id, plugin_id) VALUES ($1, $2)',
1427 array($this->getID(),
1431 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1 AND plugin_id=$2',
1432 array($this->getID(),
1436 $this->normalizeAllRoles () ;
1440 * getDocEmailAddress - get email address(es) to send doc notifications to.
1442 * @return string email address.
1444 function getDocEmailAddress() {
1445 return $this->data_array['new_doc_address'];
1449 * DocEmailAll - whether or not this group has opted to use receive notices on all doc updates.
1451 * @return boolean email_on_all_doc_updates.
1453 function docEmailAll() {
1454 return $this->data_array['send_all_docs'];
1459 * getHomePage - The URL for this project's home page.
1461 * @return string homepage URL.
1463 function getHomePage() {
1464 if (!preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*:/",
1465 $this->data_array['homepage'])) {
1466 $this->data_array['homepage'] = util_url_prefix() .
1467 $this->data_array['homepage'];
1469 return $this->data_array['homepage'];
1473 * getTags - Tags of this project.
1475 * @return string List of tags. Comma separated
1477 function getTags() {
1478 $sql = 'SELECT name FROM project_tags WHERE group_id = $1';
1479 $res = db_query_params($sql, array($this->getID()));
1480 return join(', ', util_result_column_to_array($res));
1484 * setTags - Set tags of this project.
1486 * @return string database result.
1488 function setTags($tags) {
1490 $sql = 'DELETE FROM project_tags WHERE group_id=$1';
1491 $res = db_query_params($sql, array($this->getID()));
1493 $this->setError('Deleting old tags: '.db_error());
1497 $inserted = array();
1498 $tags_array = preg_split('/[;,]/', $tags);
1499 foreach ($tags_array as $tag) {
1500 $tag = preg_replace('/[\t\r\n]/', ' ', $tag);
1501 // Allowed caracteres: [A-Z][a-z][0-9] -_&'#+.
1502 if (preg_match('/[^[:alnum:]| |\-|_|\&|\'|#|\+|\.]/', $tag)) {
1503 $this->setError(_('Bad tag name, you only can use the following characters: [A-Z][a-z][0-9]-_&\'#+. and space'));
1508 if ($tag == '' || array_search($tag, $inserted) !== false) continue;
1509 $sql = 'INSERT INTO project_tags (group_id,name) VALUES ($1, $2)';
1510 $res = db_query_params($sql, array($this->getID(), $tag));
1512 $this->setError(_('Setting tags:') . ' ' .
1524 * getPermission - Return a Permission for this Group
1526 * @return object The Permission.
1528 function &getPermission() {
1529 return permission_get_object($this);
1533 function delete($sure, $really_sure, $really_really_sure) {
1534 if (!$sure || !$really_sure || !$really_really_sure) {
1535 $this->setMissingParamsError(_('Please tick all checkboxes.'));
1538 if ($this->getID() == forge_get_config('news_group') ||
1539 $this->getID() == 1 ||
1540 $this->getID() == forge_get_config('stats_group') ||
1541 $this->getID() == forge_get_config('peer_rating_group')) {
1542 $this->setError(_('Cannot Delete System Group'));
1545 $perm = $this->getPermission();
1546 if (!$perm || !is_object($perm)) {
1547 $this->setPermissionDeniedError();
1549 } elseif ($perm->isError()) {
1550 $this->setPermissionDeniedError();
1552 } elseif (!$perm->isSuperUser()) {
1553 $this->setPermissionDeniedError();
1559 // Remove all the members
1561 $members = $this->getMembers();
1562 foreach ($members as $i) {
1563 if(!$this->removeUser($i->getID())) {
1564 $this->setError(_('Could not properly remove member:').' '.$i->getID());
1568 // Failsafe until user_group table is gone
1569 $res = db_query_params('DELETE FROM user_group WHERE group_id=$1',
1570 array($this->getID()));
1572 // unlink roles from this project
1573 $ra = RoleAnonymous::getInstance();
1574 $rl = RoleLoggedIn::getInstance();
1575 $ra->unlinkProject($this);
1576 $rl->unlinkProject($this);
1577 // @todo : unlink all the other roles created in the project...
1582 $atf = new ArtifactTypeFactory($this);
1583 $at_arr = $atf->getArtifactTypes();
1584 foreach ($at_arr as $i) {
1585 if (!is_object($i)) {
1588 if (!$i->delete(1,1)) {
1589 $this->setError(_('Could not properly delete the tracker:').' '.$i->getErrorMessage());
1596 $ff = new ForumFactory($this);
1597 $f_arr = $ff->getForums();
1598 foreach ($f_arr as $i) {
1599 if (!is_object($i)) {
1602 if(!$i->delete(1,1)) {
1603 $this->setError(_('Could not properly delete the forum:').' '.$i->getErrorMessage());
1608 // Delete Subprojects
1610 $pgf = new ProjectGroupFactory($this);
1611 $pg_arr = $pgf->getProjectGroups();
1612 foreach ($pg_arr as $i) {
1613 if (!is_object($i)) {
1616 if (!$i->delete(1,1)) {
1617 $this->setError(_('Could not properly delete the ProjectGroup:').' '.$i->getErrorMessage());
1622 // Delete FRS Packages
1624 $res = db_query_params('SELECT * FROM frs_package WHERE group_id=$1',
1625 array($this->getID()));
1627 $this->setError(_('Error FRS Packages: ').db_error());
1632 while ($arr = db_fetch_array($res)) {
1633 $frsp=new FRSPackage($this, $arr['package_id'], $arr);
1634 if (!$frsp->delete(1, 1)) {
1635 $this->setError(_('Could not properly delete the FRSPackage:').' '.$frsp->getErrorMessage());
1642 $news_group=group_get_object(forge_get_config('news_group'));
1643 $res = db_query_params('SELECT forum_id FROM news_bytes WHERE group_id=$1',
1644 array($this->getID()));
1646 $this->setError(_('Error Deleting News: ').db_error());
1651 for ($i=0; $i<db_numrows($res); $i++) {
1652 $Forum = new Forum($news_group,db_result($res,$i,'forum_id'));
1653 if (!$Forum->delete(1,1)) {
1654 $this->setError(_("Could Not Delete News Forum: %d"),$Forum->getID());
1658 $res = db_query_params('DELETE FROM news_bytes WHERE group_id=$1',
1659 array($this->getID()));
1661 $this->setError(_('Error Deleting News: ').db_error());
1669 $res = db_query_params('DELETE FROM doc_data WHERE group_id=$1',
1670 array($this->getID()));
1672 $this->setError(_('Error Deleting Documents: ').db_error());
1677 $res = db_query_params('DELETE FROM doc_groups WHERE group_id=$1',
1678 array($this->getID()));
1680 $this->setError(_('Error Deleting Documents: ').db_error());
1688 $res=db_query_params('DELETE FROM project_tags WHERE group_id=$1', array($this->getID()));
1690 $this->setError(_('Error Deleting Tags: ').db_error());
1696 // Delete group history
1698 $res = db_query_params('DELETE FROM group_history WHERE group_id=$1',
1699 array($this->getID()));
1701 $this->setError(_('Error Deleting Project History: ').db_error());
1707 // Delete group plugins
1709 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1',
1710 array($this->getID()));
1712 $this->setError(_('Error Deleting Project Plugins: ').db_error());
1718 // Delete group cvs stats
1720 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE group_id=$1',
1721 array ($this->getID())) ;
1723 $this->setError(_('Error Deleting SCM Statistics: ').db_error());
1731 $sf = new SurveyFactory($this);
1732 $s_arr =& $sf->getSurveys();
1733 foreach ($s_arr as $i) {
1734 if (!is_object($i)) {
1737 if (!$i->delete()) {
1738 $this->setError(_('Could not properly delete the survey'));
1744 // Delete SurveyQuestions
1746 $sqf = new SurveyQuestionFactory($this);
1747 $sq_arr =& $sqf->getSurveyQuestions();
1748 foreach ($sq_arr as $i) {
1749 if (!is_object($i)) {
1752 if (!$i->delete()) {
1753 $this->setError(_('Could not properly delete the survey questions'));
1759 // Delete Mailing List Factory
1761 $mlf = new MailingListFactory($this);
1762 $ml_arr = $mlf->getMailingLists();
1763 foreach ($ml_arr as $i) {
1764 if (!is_object($i)) {
1767 if (!$i->delete(1,1)) {
1768 $this->setError(_('Could not properly delete the mailing list'));
1776 $res = db_query_params('DELETE FROM trove_group_link WHERE group_id=$1',
1777 array($this->getID()));
1779 $this->setError(_('Error Deleting Trove: ').db_error());
1784 $res = db_query_params('DELETE FROM trove_agg WHERE group_id=$1',
1785 array($this->getID()));
1787 $this->setError(_('Error Deleting Trove: ').db_error());
1795 $res = db_query_params('DELETE FROM project_sums_agg WHERE group_id=$1',
1796 array($this->getID()));
1798 $this->setError(_('Error Deleting Counters: ').db_error());
1803 $res = db_query_params('INSERT INTO deleted_groups (unix_group_name, delete_date, isdeleted) VALUES ($1, $2, $3)',
1804 array($this->getUnixName(),
1808 $this->setError(_('Error Deleting Project:').' '.db_error());
1813 // Delete entry in groups.
1814 $res = db_query_params('DELETE FROM groups WHERE group_id=$1',
1815 array($this->getID()));
1817 $this->setError(_('Error Deleting Project:').' '.db_error());
1824 $hook_params = array();
1825 $hook_params['group'] = $this;
1826 $hook_params['group_id'] = $this->getID();
1827 plugin_hook("group_delete", $hook_params);
1829 if (forge_get_config('upload_dir') != '' && $this->getUnixName()) {
1830 exec('/bin/rm -rf '.forge_get_config('upload_dir').'/'.$this->getUnixName().'/');
1832 if (forge_get_config('ftp_upload_dir') != '' && $this->getUnixName()) {
1833 exec('/bin/rm -rf '.forge_get_config('ftp_upload_dir').'/'.$this->getUnixName().'/');
1838 $res = db_query_params('DELETE FROM rep_group_act_monthly WHERE group_id=$1',
1839 array ($this->getID()));
1840 //echo 'rep_group_act_monthly'.db_error();
1841 $res = db_query_params('DELETE FROM rep_group_act_weekly WHERE group_id=$1',
1842 array ($this->getID()));
1843 //echo 'rep_group_act_weekly'.db_error();
1844 $res = db_query_params('DELETE FROM rep_group_act_daily WHERE group_id=$1',
1845 array ($this->getID()));
1846 //echo 'rep_group_act_daily'.db_error();
1847 unset($this->data_array);
1852 Basic functions to add/remove users to/from a group
1853 and update their permissions
1857 * addUser - controls adding a user to a group.
1859 * @param string Unix name of the user to add OR integer user_id.
1860 * @param int The role_id this user should have.
1861 * @return boolean success.
1864 function addUser($user_identifier,$role_id) {
1867 Admins can add users to groups
1870 if (!forge_check_perm ('project_admin', $this->getID())) {
1871 $this->setPermissionDeniedError();
1877 get user id for this user's unix_name
1879 if (is_int ($user_identifier)) { // user_id or user_name
1880 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_id=$1', array ($user_identifier)) ;
1882 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_name=$1', array ($user_identifier)) ;
1884 if (db_numrows($res_newuser) > 0) {
1886 // make sure user is active
1888 if (db_result($res_newuser,0,'status') != 'A') {
1889 $this->setError(_('User is not active. Only active users can be added.'));
1895 // user was found - set new user_id var
1897 $user_id = db_result($res_newuser,0,'user_id');
1899 $role = new Role($this, $role_id);
1900 if (!$role || !is_object($role)) {
1901 $this->setError(_('Error Getting Role Object'));
1904 } elseif ($role->isError()) {
1905 $this->setError('addUser::roleget::'.$role->getErrorMessage());
1910 $role->addUser(user_get_object($user_id)) ;
1911 if (!$SYS->sysCheckCreateGroup($this->getID())){
1912 $this->setError($SYS->getErrorMessage());
1916 if (!$SYS->sysCheckCreateUser($user_id)) {
1917 $this->setError($SYS->getErrorMessage());
1921 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
1922 $this->setError($SYS->getErrorMessage());
1928 // user doesn't exist
1930 $this->setError(_('ERROR: User does not exist'));
1935 $hook_params['group'] = $this;
1936 $hook_params['group_id'] = $this->getID();
1937 $hook_params['user'] = user_get_object($user_id);
1938 $hook_params['user_id'] = $user_id;
1939 plugin_hook ("group_adduser", $hook_params);
1944 $this->addHistory('Added User',$user_identifier);
1950 * removeUser - controls removing a user from a group.
1952 * Users can remove themselves.
1954 * @param int The ID of the user to remove.
1955 * @return boolean success.
1957 function removeUser($user_id) {
1960 if ($user_id != user_getid()
1961 && !forge_check_perm('project_admin', $this->getID())) {
1962 $this->setPermissionDeniedError();
1968 $user = user_get_object($user_id);
1969 $roles = RBACEngine::getInstance()->getAvailableRolesForUser($user);
1971 foreach ($roles as $role) {
1972 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
1973 $found_role = $role;
1977 if ($found_role == NULL) {
1978 $this->setError(sprintf(_('ERROR: User not removed: %s')));
1982 $found_role->removeUser($user);
1983 if (!$SYS->sysGroupCheckUser($this->getID(), $user_id)) {
1984 $this->setError($SYS->getErrorMessage());
1990 // reassign open artifacts to id=100
1992 $res = db_query_params('UPDATE artifact SET assigned_to=100
1993 WHERE group_artifact_id
1994 IN (SELECT group_artifact_id
1995 FROM artifact_group_list
1996 WHERE group_id=$1 AND status_id=1 AND assigned_to=$2)',
1997 array($this->getID(),
2000 $this->setError(_('ERROR: DB: artifact:').' '.db_error());
2006 // reassign open tasks to id=100
2007 // first have to purge any assignments that would cause
2008 // conflict with existing assignment to 100
2010 $res = db_query_params('DELETE FROM project_assigned_to
2011 WHERE project_task_id IN (SELECT pt.project_task_id
2012 FROM project_task pt, project_group_list pgl, project_assigned_to pat
2013 WHERE pt.group_project_id = pgl.group_project_id
2014 AND pat.project_task_id=pt.project_task_id
2015 AND pt.status_id=1 AND pgl.group_id=$1
2016 AND pat.assigned_to_id=$2)
2017 AND assigned_to_id=100',
2018 array($this->getID(),
2021 $this->setError(sprintf(_('ERROR: DB: project_assigned_to %d: %s'), 1, db_error()));
2025 $res = db_query_params('UPDATE project_assigned_to SET assigned_to_id=100
2026 WHERE project_task_id IN (SELECT pt.project_task_id
2027 FROM project_task pt, project_group_list pgl
2028 WHERE pt.group_project_id = pgl.group_project_id
2029 AND pt.status_id=1 AND pgl.group_id=$1)
2030 AND assigned_to_id=$2',
2031 array($this->getID(),
2034 $this->setError(sprintf(_('ERROR: DB: project_assigned_to %d: %s'), 2, db_error()));
2040 // Remove user from system
2042 if (!$SYS->sysGroupRemoveUser($this->getID(), $user_id)) {
2043 $this->setError($SYS->getErrorMessage());
2048 $hook_params['group'] = $this;
2049 $hook_params['group_id'] = $this->getID();
2050 $hook_params['user'] = user_get_object($user_id);
2051 $hook_params['user_id'] = $user_id;
2052 plugin_hook ("group_removeuser", $hook_params);
2055 $this->addHistory('Removed User',$user_id);
2062 * updateUser - controls updating a user's role in this group.
2064 * @param int The ID of the user.
2065 * @param int The role_id to set this user to.
2066 * @return boolean success.
2068 function updateUser($user_id,$role_id) {
2070 if (!forge_check_perm ('project_admin', $this->getID())) {
2071 $this->setPermissionDeniedError();
2075 $newrole = RBACEngine::getInstance()->getRoleById ($role_id) ;
2076 if (!$newrole || !is_object($newrole)) {
2077 $this->setError(_('Could Not Get Role'));
2079 } elseif ($newrole->isError()) {
2080 $this->setError(sprintf(_('Role: %s'),$role->getErrorMessage()));
2082 } elseif ($newrole->getHomeProject() == NULL
2083 || $newrole->getHomeProject()->getID() != $this->getID()) {
2084 $this->setError(_('Wrong destination role'));
2087 $user = user_get_object ($user_id) ;
2088 $roles = RBACEngine::getInstance()->getAvailableRolesForUser ($user) ;
2089 $found_role = NULL ;
2090 foreach ($roles as $role) {
2091 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2092 $found_role = $role ;
2096 if ($found_role == NULL) {
2097 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2101 $found_role->removeUser ($user) ;
2102 $newrole->addUser ($user) ;
2104 $this->addHistory('Updated User',$user_id);
2109 * addHistory - Makes an audit trail entry for this project.
2111 * @param string The name of the field.
2112 * @param string The Old Value for this $field_name.
2113 * @return database result handle.
2116 function addHistory($field_name, $old_value) {
2117 return db_query_params ('INSERT INTO group_history(group_id,field_name,old_value,mod_by,adddate)
2118 VALUES ($1,$2,$3,$4,$5)',
2119 array ($this->getID(),
2127 * activateUsers - Make sure that group members have unix accounts.
2129 * Setup unix accounts for group members. Can be called even
2130 * if members are already active.
2134 function activateUsers() {
2136 Activate member(s) of the project
2139 $members = $this->getUsers (true) ;
2141 foreach ($members as $member) {
2143 foreach (RBACEngine::getInstance()->getAvailableRolesForUser ($member) as $role) {
2144 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2147 if (!$this->addUser($member->getUnixName(),$role->getID())) {
2158 * getMembers - returns array of User objects for this project
2160 * @return array of User objects for this group.
2162 function getMembers() {
2163 return $this->getUsers (true) ;
2167 * replaceTemplateStrings - fill-in some blanks with project name
2169 * @param string Template string
2170 * @return string String after replacements
2172 function replaceTemplateStrings($string) {
2173 $string = str_replace ('UNIXNAME', $this->getUnixName(), $string) ;
2174 $string = str_replace ('PUBLICNAME', $this->getPublicName(), $string) ;
2175 $string = str_replace ('DESCRIPTION', $this->getDescription(), $string) ;
2180 * approve - Approve pending project.
2182 * @param object The User object who is doing the updating.
2185 function approve(&$user) {
2186 global $gfcommon,$gfwww;
2187 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
2189 if ($this->getStatus()=='A') {
2190 $this->setError(_("Group already active"));
2196 // Step 1: Activate group and create LDAP entries
2197 if (!$this->setStatus($user, 'A')) {
2202 // Switch to system language for item creation
2203 setup_gettext_from_sys_lang();
2205 // Create default roles
2206 $idadmin_group = NULL;
2207 foreach (get_group_join_requests ($this) as $gjr) {
2208 $idadmin_group = $gjr->getUserID();
2211 if ($idadmin_group == NULL) {
2212 $idadmin_group = $user->getID();
2215 $template = $this->getTemplateProject();
2216 $id_mappings = array();
2217 $seen_admin_role = false;
2219 // Copy roles from template project
2220 foreach($template->getRoles() as $oldrole) {
2221 if ($oldrole->getHomeProject() != NULL) {
2222 $role = new Role($this);
2224 // Need to use a different role name so that the permissions aren't set from the hardcoded defaults
2225 $role->create('TEMPORARY ROLE NAME', $data, true);
2226 $role->setName($oldrole->getName());
2227 if ($oldrole->getSetting ('project_admin', $template->getID())) {
2228 $seen_admin_role = true;
2232 $role->linkProject($this);
2234 $id_mappings['role'][$oldrole->getID()] = $role->getID();
2235 // Reuse the project_admin permission
2236 $role->setSetting ('project_admin', $this->getID(), $oldrole->getSetting ('project_admin', $template->getID())) ;
2240 if (!$seen_admin_role) {
2241 $role = new Role($this);
2242 $adminperms = array ('project_admin' => array ($this->getID() => 1)) ;
2243 $role_id = $role->create ('Admin', $adminperms, true) ;
2246 $roles = $this->getRoles() ;
2247 foreach ($roles as $r) {
2248 if ($r->getHomeProject() == NULL) {
2251 if ($r->getSetting ('project_admin', $this->getID())) {
2252 $r->addUser(user_get_object ($idadmin_group));
2256 // Temporarily switch to the submitter's identity
2257 $saved_session = session_get_user();
2258 session_set_internal($idadmin_group);
2261 if (forge_get_config('use_tracker')) {
2262 $this->setUseTracker ($template->usesTracker());
2263 if ($template->usesTracker()) {
2264 $oldatf = new ArtifactTypeFactory($template);
2265 foreach ($oldatf->getArtifactTypes() as $o) {
2266 $t = new ArtifactType ($this) ;
2267 $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()) ;
2268 $id_mappings['tracker'][$o->getID()] = $t->getID();
2269 $t->cloneFieldsFrom ($o->getID());
2274 if (forge_get_config('use_pm')) {
2275 $this->setUsePM ($template->usesPM());
2276 if ($template->usesPM()) {
2277 $oldpgf = new ProjectGroupFactory($template);
2278 foreach ($oldpgf->getProjectGroups() as $o) {
2279 $pg = new ProjectGroup($this);
2280 $pg->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->isPublic(),$o->getSendAllPostsTo());
2281 $id_mappings['pm'][$o->getID()] = $pg->getID();
2286 if (forge_get_config('use_forum')) {
2287 $this->setUseForum($template->usesForum()) ;
2288 if ($template->usesForum()) {
2289 $oldff = new ForumFactory($template) ;
2290 foreach ($oldff->getForums() as $o) {
2291 $f = new Forum($this);
2292 $f->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),0,$o->getSendAllPostsTo(),1,0,0);
2293 $id_mappings['forum'][$o->getID()] = $f->getID();
2298 if (forge_get_config('use_docman')) {
2299 $this->setUseDocman($template->usesDocman());
2300 if ($template->usesDocman()) {
2301 $olddgf = new DocumentGroupFactory($template);
2302 // First pass: create all docgroups
2303 $id_mappings['docman_docgroup'][0] = 0;
2304 foreach ($olddgf->getDocumentGroups() as $o) {
2305 $ndgf = new DocumentGroup($this);
2306 // .trash is a reserved directory
2307 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2308 $ndgf->create($this->replaceTemplateStrings($o->getName()));
2309 $id_mappings['docman_docgroup'][$o->getID()] = $ndgf->getID();
2312 // Second pass: restore hierarchy links
2313 foreach ($olddgf->getDocumentGroups() as $o) {
2314 $ndgf = new DocumentGroup($this);
2315 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2316 $ndgf->fetchData($id_mappings['docman_docgroup'][$o->getID()]);
2317 $ndgf->update($ndgf->getName(), $id_mappings['docman_docgroup'][$o->getParentID()]);
2323 if (forge_get_config('use_frs')) {
2324 $this->setUseFRS ($template->usesFRS());
2325 if ($template->usesFRS()) {
2326 foreach (get_frs_packages($template) as $o) {
2327 $newp = new FRSPackage($this);
2328 $nname = $this->replaceTemplateStrings($o->getName());
2329 $newp->create ($nname, $o->isPublic());
2334 if (forge_get_config('use_mail')) {
2335 $this->setUseMail($template->usesMail()) ;
2336 if ($template->usesMail()) {
2337 $oldmlf = new MailingListFactory($template);
2338 foreach ($oldmlf->getMailingLists() as $o) {
2339 $ml = new MailingList($this);
2340 $nname = preg_replace ('/^'.$template->getUnixName().'-/','',$o->getName()) ;
2342 $ndescription = $this->replaceTemplateStrings($o->getDescription()) ;
2343 $ml->create($nname, $ndescription, $o->isPublic());
2349 /* use SCM plugin from template group */
2350 $this->setUseSCM($template->usesSCM());
2352 foreach ($template->getPlugins() as
2353 $plugin_id => $plugin_name) {
2354 $this->setPluginUse($plugin_name);
2357 /* use SCM choice from registration page */
2359 foreach ($template->getPlugins() as
2360 $plugin_id => $plugin_name) {
2361 if (substr($plugin_name, 3) == 'scm' &&
2362 $plugin_name != 'scmhook') {
2363 /* skip copying scm plugins */
2366 /* enable other plugins though */
2367 $this->setPluginUse($plugin_name);
2371 foreach ($template->getRoles() as $oldrole) {
2372 $newrole = RBACEngine::getInstance()->getRoleById ($id_mappings['role'][$oldrole->getID()]) ;
2373 if ($oldrole->getHomeProject() != NULL
2374 && $oldrole->getHomeProject()->getID() == $template->getID()) {
2375 $newrole->setPublic ($oldrole->isPublic()) ;
2377 $oldsettings = $oldrole->getSettingsForProject ($template) ;
2379 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm') ;
2380 foreach ($sections as $section) {
2381 $newrole->setSetting ($section, $this->getID(), $oldsettings[$section][$template->getID()]) ;
2384 $sections = array ('tracker', 'pm', 'forum') ;
2385 foreach ($sections as $section) {
2386 if (isset ($oldsettings[$section])) {
2387 foreach ($oldsettings[$section] as $k => $v) {
2388 // Only copy perms for tools that have been copied
2389 if (isset ($id_mappings[$section][$k])) {
2390 $newrole->setSetting ($section,
2391 $id_mappings[$section][$k],
2399 $lm = new WidgetLayoutManager();
2400 $lm->createDefaultLayoutForProject ($this->getID(), $template->getID()) ;
2402 $params = array () ;
2403 $params['template'] = $template ;
2404 $params['project'] = $this ;
2405 $params['id_mappings'] = $id_mappings ;
2406 plugin_hook_by_reference ('clone_project_from_template', $params) ;
2408 // Disable everything
2409 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',
2410 array ($this->getID())) ;
2413 $this->normalizeAllRoles();
2414 $this->activateUsers();
2416 // Delete fake join request
2417 foreach (get_group_join_requests ($this) as $gjr) {
2418 $gjr->delete(true) ;
2421 // Switch back to user preference
2422 session_set_internal($saved_session->getID());
2423 setup_gettext_from_context();
2427 $this->sendApprovalEmail();
2428 $this->addHistory('Approved', 'x');
2431 // Plugin can make approve operation there
2433 $params[0] = $idadmin_group;
2434 $params[1] = $this->getID();
2435 plugin_hook('group_approved', $params);
2443 * sendApprovalEmail - Send new project email.
2445 * @return boolean success.
2448 function sendApprovalEmail() {
2449 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('project_admin', $this->getID()) ;
2451 if (count($admins) < 1) {
2452 $this->setError(_("Group does not have any administrators."));
2456 // send one email per admin
2457 foreach ($admins as $admin) {
2458 setup_gettext_for_user ($admin) ;
2460 $message=sprintf(_('Your project registration for %4$s has been approved.
2462 Project Full Name: %1$s
2463 Project Unix Name: %2$s
2465 Your DNS will take up to a day to become active on our site.
2466 Your web site is accessible through your shell account. Please read
2467 site documentation (see link below) about intended usage, available
2468 services, and directory layout of the account.
2471 own project page in %4$s while logged in, you will find
2472 additional menu functions to your left labeled \'Project Admin\'.
2474 We highly suggest that you now visit %4$s and create a public
2475 description for your project. This can be done by visiting your project
2476 page while logged in, and selecting \'Project Admin\' from the menus
2477 on the left (or by visiting %3$s
2480 Your project will also not appear in the Trove Software Map (primary
2481 list of projects hosted on %4$s which offers great flexibility in
2482 browsing and search) until you categorize it in the project administration
2483 screens. So that people can find your project, you should do this now.
2484 Visit your project while logged in, and select \'Project Admin\' from the
2487 Enjoy the system, and please tell others about %4$s. Let us know
2488 if there is anything we can do to help you.
2491 htmlspecialchars_decode($this->getPublicName()),
2492 $this->getUnixName(),
2493 util_make_url ('/project/admin/?group_id='.$this->getID()),
2494 forge_get_config ('forge_name'));
2496 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Approved'), forge_get_config ('forge_name')), $message);
2498 setup_gettext_from_context();
2506 * sendRejectionEmail - Send project rejection email.
2508 * This function sends out a rejection message to a user who
2509 * registered a project.
2511 * @param int The id of the response to use.
2512 * @param string The rejection message.
2513 * @return boolean completion status.
2516 function sendRejectionEmail($response_id, $message="zxcv") {
2517 $submitters = array () ;
2518 foreach (get_group_join_requests ($this) as $gjr) {
2519 $submitters[] = user_get_object($gjr->getUserID());
2522 if (count ($submitters) < 1) {
2523 $this->setError(_("Group does not have any administrators."));
2527 foreach ($submitters as $admin) {
2528 setup_gettext_for_user($admin);
2530 $response=sprintf(_('Your project registration for %3$s has been denied.
2532 Project Full Name: %1$s
2533 Project Unix Name: %2$s
2535 Reasons for negative decision:
2537 '), $this->getPublicName(), $this->getUnixName(), forge_get_config('forge_name'));
2539 // Check to see if they want to send a custom rejection response
2540 if ($response_id == 0) {
2541 $response .= $message;
2543 $response .= db_result(
2544 db_query_params('SELECT response_text FROM canned_responses WHERE response_id=$1', array ($response_id)),
2549 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Denied'), forge_get_config ('forge_name')), $response);
2550 setup_gettext_from_context();
2557 * sendNewProjectNotificationEmail - Send new project notification email.
2559 * This function sends out a notification email to the
2560 * SourceForge admin user when a new project is
2563 * @return boolean success.
2566 function sendNewProjectNotificationEmail() {
2567 // Get the user who wants to register the project
2568 $submitters = array();
2569 foreach (get_group_join_requests ($this) as $gjr) {
2570 $submitters[] = user_get_object($gjr->getUserID());
2572 if (count ($submitters) < 1) {
2573 $this->setError(_("Could not find user who has submitted the project."));
2577 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('approve_projects', -1) ;
2579 if (count($admins) < 1) {
2580 $this->setError(_("There is no administrator to send the mail to."));
2584 foreach ($admins as $admin) {
2585 $admin_email = $admin->getEmail () ;
2586 setup_gettext_for_user ($admin) ;
2588 $message = sprintf(_('New %1$s Project Submitted
2590 Project Full Name: %2$s
2591 Submitted Description: %3$s
2593 forge_get_config ('forge_name'),
2594 htmlspecialchars_decode($this->getPublicName()),
2595 htmlspecialchars_decode($this->getRegistrationPurpose()));
2597 foreach ($submitters as $submitter) {
2598 $message .= sprintf(_('Submitter: %1$s (%2$s)
2600 $submitter->getRealName(),
2601 $submitter->getUnixName());
2604 $message .= sprintf (_('
2605 Please visit the following URL to approve or reject this project:
2607 util_make_url ('/admin/approve-pending.php')) ;
2608 util_send_message($admin_email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2609 setup_gettext_from_context();
2613 $email = $submitter->getEmail() ;
2614 setup_gettext_for_user ($submitter) ;
2616 $message=sprintf(_('New %1$s Project Submitted
2618 Project Full Name: %2$s
2619 Submitted Description: %3$s
2621 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'));
2623 util_send_message($email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2624 setup_gettext_from_context();
2633 * validateGroupName - Validate the group name
2635 * @param string Group name.
2637 * @return boolean an error false and set an error is the group name is invalide otherwise return true
2639 function validateGroupName($group_name) {
2640 if (strlen($group_name)<3) {
2641 $this->setError(_('Group name is too short'));
2643 } else if (strlen(htmlspecialchars($group_name))>50) {
2644 $this->setError(_('Group name is too long'));
2646 } else if (group_get_object_by_publicname($group_name)) {
2647 $this->setError(_('Group name already taken'));
2655 * getRolesId - Get Ids of the roles of the group.
2657 * @return array Role ids of this group.
2659 function getRolesId() {
2660 $role_ids = array();
2662 $res = db_query_params('SELECT role_id FROM pfo_role WHERE home_group_id=$1',
2663 array($this->getID()));
2664 while ($arr = db_fetch_array($res)) {
2665 $role_ids[] = $arr['role_id'];
2667 $res = db_query_params('SELECT role_id FROM role_project_refs WHERE group_id=$1',
2668 array($this->getID()));
2669 while ($arr = db_fetch_array($res)) {
2670 $role_ids[] = $arr['role_id'];
2673 return array_unique($role_ids);
2677 * getRoles - Get the roles of the group.
2679 * @return array Roles of this group.
2681 function getRoles() {
2684 $roles = $this->getRolesId();
2685 $engine = RBACEngine::getInstance();
2686 foreach ($roles as $role_id) {
2687 $result[] = $engine->getRoleById ($role_id);
2693 function normalizeAllRoles() {
2694 $roles = $this->getRoles();
2696 foreach ($roles as $r) {
2697 $r->normalizeData();
2702 * getUnixStatus - Status of activation of unix account.
2704 * @return char (N)one, (A)ctive, (S)uspended or (D)eleted
2706 function getUnixStatus() {
2707 return $this->data_array['unix_status'];
2711 * setUnixStatus - Sets status of activation of unix account.
2713 * @param string The unix status.
2719 * @return boolean success.
2721 function setUnixStatus($status) {
2724 $res = db_query_params ('UPDATE groups SET unix_status=$1 WHERE group_id=$2',
2729 $this->setError(sprintf(_('ERROR - Could Not Update Group Unix Status: %s'),db_error()));
2733 if ($status == 'A') {
2734 if (!$SYS->sysCheckCreateGroup($this->getID())) {
2735 $this->setError($SYS->getErrorMessage());
2740 if ($SYS->sysCheckGroup($this->getID())) {
2741 if (!$SYS->sysRemoveGroup($this->getID())) {
2742 $this->setError($SYS->getErrorMessage());
2749 $this->data_array['unix_status']=$status;
2756 * getUsers - Get the users of a group
2758 * @return array of user's objects.
2760 function getUsers($onlylocal = true) {
2761 if (!isset($this->membersArr)) {
2762 $this->membersArr = array () ;
2765 foreach ($this->getRoles() as $role) {
2767 && ($role->getHomeProject() == NULL || $role->getHomeProject()->getID() != $this->getID())) {
2770 foreach ($role->getUsers() as $user) {
2771 $ids[] = $user->getID() ;
2774 $ids = array_unique ($ids) ;
2775 foreach ($ids as $id) {
2776 $u = user_get_object ($id) ;
2777 if ($u->isActive()) {
2778 $this->membersArr[] = $u ;
2782 return $this->membersArr;
2785 function setDocmanCreateOnlineStatus($status) {
2787 /* if we activate search engine, we probably want to reindex */
2788 $res = db_query_params('UPDATE groups SET use_docman_create_online=$1 WHERE group_id=$2',
2789 array($status, $this->getID()));
2792 $this->setError(sprintf(_('ERROR - Could Not Update Group DocmanCreateOnline Status: %s'),db_error()));
2796 $this->data_array['use_docman_create_online']=$status;
2802 function setDocmanWebdav($status) {
2804 /* if we activate search engine, we probably want to reindex */
2805 $res = db_query_params('UPDATE groups SET use_webdav=$1 WHERE group_id=$2',
2810 $this->setError(sprintf(_('ERROR - Could Not Update Group UseWebdab Status: %s'),db_error()));
2814 $this->data_array['use_webdav']=$status;
2820 function setDocmanSearchStatus($status) {
2822 /* if we activate search engine, we probably want to reindex */
2823 $res = db_query_params('UPDATE groups SET use_docman_search=$1, force_docman_reindex=$1 WHERE group_id=$2',
2828 $this->setError(sprintf(_('ERROR - Could Not Update Group UseDocmanSearch Status: %s'),db_error()));
2832 $this->data_array['use_docman_search']=$status;
2838 function setDocmanForceReindexSearch($status) {
2840 /* if we activate search engine, we probably want to reindex */
2841 $res = db_query_params('UPDATE groups SET force_docman_reindex=$1 WHERE group_id=$2',
2846 $this->setError(sprintf(_('ERROR - Could Not Update Group force_docman_reindex %s'),db_error()));
2850 $this->data_array['force_docman_reindex']=$status;
2856 function setStorageAPI($type) {
2860 function getStorageAPI() {
2866 * group_getname() - get the group name
2868 * @param int The group ID
2872 function group_getname ($group_id = 0) {
2873 $grp = group_get_object($group_id);
2875 return $grp->getPublicName();
2882 * group_getunixname() - get the unixname for a group
2884 * @param int The group ID
2888 function group_getunixname ($group_id) {
2889 $grp = group_get_object($group_id);
2891 return $grp->getUnixName();
2898 * group_get_result() - Get the group object result ID.
2900 * @param int The group ID
2904 function &group_get_result($group_id=0) {
2905 $grp = group_get_object($group_id);
2907 return $grp->getData();
2913 function getAllProjectTags($onlyvisible = true) {
2914 $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',
2917 if (!$res || db_numrows($res) == 0) {
2923 while ($arr = db_fetch_array($res)) {
2925 $group_id = $arr[1];
2926 if (!isset($result[$tag])) {
2927 $result[$tag] = array();
2930 if (!$onlyvisible || forge_check_perm('project_read', $group_id)) {
2931 $p = group_get_object($group_id);
2932 $result[$tag][] = array('unix_group_name' => $p->getUnixName(),
2933 'group_id' => $group_id);
2941 * Utility class to compare project based in various criteria (names, unixnames, id, ...)
2944 class ProjectComparator {
2945 var $criterion = 'name' ;
2947 function Compare ($a, $b) {
2948 switch ($this->criterion) {
2951 $namecmp = strcoll ($a->getPublicName(), $b->getPublicName()) ;
2952 if ($namecmp != 0) {
2955 /* If several projects share a same public name */
2956 return strcoll ($a->getUnixName(), $b->getUnixName()) ;
2959 return strcmp ($a->getUnixName(), $b->getUnixName()) ;
2962 $aid = $a->getID() ;
2963 $bid = $b->getID() ;
2967 return ($a < $b) ? -1 : 1;
2973 function sortProjectList (&$list, $criterion='name') {
2974 $cmp = new ProjectComparator () ;
2975 $cmp->criterion = $criterion ;
2977 return usort ($list, array ($cmp, 'Compare')) ;
2982 // c-file-style: "bsd"