5 * Copyright 1999-2001, VA Linux Systems, Inc.
6 * Copyright 2009-2010, Roland Mas
7 * Copyright 2010-2011, Franck Villaume - Capgemini
8 * Copyright 2010-2012, 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('
323 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)',
324 array (htmlspecialchars ($group_name),
326 htmlspecialchars($description),
332 htmlspecialchars($purpose),
334 md5(util_randbytes()),
335 $built_from_template));
336 if (!$res || db_affected_rows($res) < 1) {
337 $this->setError(sprintf(_('ERROR: Could not create group: %s'),db_error()));
342 $id = db_insertid($res, 'groups', 'group_id');
344 $this->setError(sprintf(_('ERROR: Could not get group id: %s'),db_error()));
349 if (!$this->fetchData($id)) {
354 $gjr = new GroupJoinRequest($this);
355 $gjr->create($user->getID(),
356 'Fake GroupJoinRequest to store the creator of a project',
359 $hook_params = array();
360 $hook_params['group'] = $this;
361 $hook_params['group_id'] = $this->getID();
362 $hook_params['group_name'] = $group_name;
363 $hook_params['unix_group_name'] = $unix_name;
364 plugin_hook("group_create", $hook_params);
368 $this->sendNewProjectNotificationEmail();
376 * updateAdmin - Update core properties of group object.
378 * This function require site admin privilege.
380 * @param object User requesting operation (for access control).
381 * @param boolean Whether group is publicly accessible (0/1).
382 * @param int Group type (1-project, 2-foundry).
383 * @param string Machine on which group's home directory located.
384 * @param string Domain which serves group's WWW.
388 function updateAdmin(&$user, $is_public, $type_id, $unix_box, $http_domain) {
389 $perm =& $this->getPermission();
391 if (!$perm || !is_object($perm)) {
392 $this->setError(_('Could not get permission.'));
396 if (!$perm->isSuperUser()) {
397 $this->setError(_('Permission denied.'));
403 $res = db_query_params('
405 SET type_id=$1, unix_box=$2, http_domain=$3
412 if (!$res || db_affected_rows($res) < 1) {
413 $this->setError(_('ERROR: DB: Could not change group properties: %s'),db_error());
418 // Log the audit trail
419 if ($type_id != $this->data_array['type_id']) {
420 $this->addHistory('type_id', $this->data_array['type_id']);
422 if ($unix_box != $this->data_array['unix_box']) {
423 $this->addHistory('unix_box', $this->data_array['unix_box']);
425 if ($http_domain != $this->data_array['http_domain']) {
426 $this->addHistory('http_domain', $this->data_array['http_domain']);
429 if (!$this->fetchData($this->getID())) {
438 * update - Update number of common properties.
440 * Unlike updateAdmin(), this function accessible to project admin.
442 * @param object User requesting operation (for access control).
443 * @param boolean Whether group is publicly accessible (0/1).
444 * @param string Project's license (string ident).
445 * @param int Group type (1-project, 2-foundry).
446 * @param string Machine on which group's home directory located.
447 * @param string Domain which serves group's WWW.
448 * @return int status.
451 function update(&$user, $group_name, $homepage, $short_description, $use_mail, $use_survey, $use_forum,
452 $use_pm, $use_pm_depend_box, $use_scm, $use_news, $use_docman,
453 $new_doc_address, $send_all_docs, $logo_image_id,
454 $use_ftp, $use_tracker, $use_frs, $use_stats, $tags, $use_activity, $is_public) {
456 $perm =& $this->getPermission();
458 if (!$perm || !is_object($perm)) {
459 $this->setError(_('Could not get permission.'));
463 if (!$perm->isAdmin()) {
464 $this->setError(_('Permission denied.'));
468 // Validate some values
469 if ($this->getPublicName() != $group_name) {
470 if (!$this->validateGroupName($group_name)) {
475 if ($new_doc_address) {
476 $invalid_mails = validate_emails($new_doc_address);
477 if (count($invalid_mails) > 0) {
478 $this->setError(sprintf(ngettext('New Doc Address Appeared Invalid: %s', 'New Doc Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
483 // in the database, these all default to '1',
484 // so we have to explicity set 0
497 if (!$use_pm_depend_box) {
498 $use_pm_depend_box = 0;
521 if (!$use_activity) {
524 if (!$send_all_docs) {
528 $homepage = ltrim($homepage);
530 $homepage = util_make_url('/projects/' . $this->getUnixName() . '/');
533 if (strlen(htmlspecialchars($short_description))>255) {
534 $this->setError(_('Error updating project information: Maximum length for Project Description is 255 chars.'));
540 //XXX not yet actived logo_image_id='$logo_image_id',
541 $res = db_query_params('UPDATE groups
544 short_description=$3,
549 use_pm_depend_box=$8,
560 array(htmlspecialchars($group_name),
562 htmlspecialchars($short_description),
579 if (!$res || db_affected_rows($res) < 1) {
580 $this->setError(sprintf(_('Error updating project information: %s'), db_error()));
585 if (!$this->setUseDocman($use_docman)) {
586 $this->setError(sprintf(_('Error updating project information: use_docman %s'), db_error()));
591 if ($this->setTags($tags) === false) {
596 $hook_params = array();
597 $hook_params['group'] = $this;
598 $hook_params['group_id'] = $this->getID();
599 $hook_params['group_homepage'] = $homepage;
600 $hook_params['group_name'] = htmlspecialchars($group_name);
601 $hook_params['group_description'] = htmlspecialchars($short_description);
602 $hook_params['group_ispublic'] = $is_public;
603 if (!plugin_hook("group_update", $hook_params)) {
604 if (!$this->isError()) {
605 $this->setError(_('Error updating project information in plugin_hook group_update'));
611 // Log the audit trail
612 $this->addHistory('Changed Public Info', '');
614 if (!$this->fetchData($this->getID())) {
623 * getID - Simply return the group_id for this object.
625 * @return int group_id.
628 return $this->data_array['group_id'];
632 * getType() - Foundry, project, etc.
634 * @return int The type flag from the database.
637 return $this->data_array['type_id'];
642 * getStatus - the status code.
644 * Statuses char include I,H,A,D,P.
645 * TODO : document what these mean :
652 function getStatus() {
653 return $this->data_array['status'];
657 * setStatus - set the status code.
659 * Statuses include I,H,A,D,P.
660 * TODO : document what these mean :
667 * @param object User requesting operation (for access control).
668 * @param string Status value.
669 * @return boolean success.
672 function setStatus(&$user, $status) {
675 if (!forge_check_global_perm('approve_projects')) {
676 $this->setPermissionDeniedError();
680 // Projects in 'A' status can only go to 'H' or 'D'
681 // Projects in 'D' status can only go to 'A'
682 // Projects in 'P' status can only go to 'A' OR 'D'
683 // Projects in 'I' status can only go to 'P'
684 // Projects in 'H' status can only go to 'A' OR 'D'
685 $allowed_status_changes = array(
686 'AH'=>1,'AD'=>1,'DA'=>1,'PA'=>1,'PD'=>1,
687 'IP'=>1,'HA'=>1,'HD'=>1
690 // Check that status transition is valid
691 if ($this->getStatus() != $status
692 && !array_key_exists($this->getStatus(). $status, $allowed_status_changes)) {
693 $this->setError(_('Invalid Status Change From: ').$this->getStatus(). _(' To: '.$status));
699 $res = db_query_params('UPDATE groups
701 WHERE group_id=$2', array($status, $this->getID()));
703 if (!$res || db_affected_rows($res) < 1) {
704 $this->setError(sprintf(_('ERROR: DB: Could not change group status: %s'),db_error()));
710 // Activate system group, if not yet
711 if (!$SYS->sysCheckGroup($this->getID())) {
712 if (!$SYS->sysCreateGroup($this->getID())) {
713 $this->setError($SYS->getErrorMessage());
718 if (!$this->activateUsers()) {
723 /* Otherwise, the group is not active, and make sure that
724 System group is not active either */
725 } else if ($SYS->sysCheckGroup($this->getID())) {
726 if (!$SYS->sysRemoveGroup($this->getID())) {
727 $this->setError($SYS->getErrorMessage());
733 $hook_params = array();
734 $hook_params['group'] = $this;
735 $hook_params['group_id'] = $this->getID();
736 $hook_params['status'] = $status;
737 plugin_hook("group_setstatus", $hook_params);
741 // Log the audit trail
742 if ($status != $this->getStatus()) {
743 $this->addHistory('Status', $this->getStatus());
746 $this->data_array['status'] = $status;
751 * isProject - Simple boolean test to see if it's a project or not.
753 * @return boolean is_project.
755 function isProject() {
756 if ($this->getType()==1) {
764 * isPublic - Wrapper around RBAC to check if a project is anonymously readable
766 * @return boolean is_public.
768 function isPublic() {
769 $ra = RoleAnonymous::getInstance() ;
770 return $ra->hasPermission('project_read', $this->getID());
774 * isActive - Database field status of 'A' returns true.
776 * @return boolean is_active.
778 function isActive() {
779 if ($this->getStatus()=='A') {
787 * isTemplate - Simply returns the is_template flag from the database.
789 * @return boolean is_template.
791 function isTemplate() {
792 return $this->data_array['is_template'];
796 * setAsTemplate - Set the template status of a project
798 * @param boolean is_template.
800 function setAsTemplate($booleanparam) {
802 $booleanparam = $booleanparam ? 1 : 0;
803 $res = db_query_params('UPDATE groups SET is_template=$1 WHERE group_id=$2',
804 array($booleanparam, $this->getID()));
806 $this->data_array['is_template']=$booleanparam;
816 * getTemplateProject - Return the project template this project is built from
818 * @return object The template project
820 function getTemplateProject() {
821 return group_get_object($this->data_array['built_from_template']);
825 * getUnixName - the unix_name
827 * @return string unix_name.
829 function getUnixName() {
830 return strtolower($this->data_array['unix_group_name']);
834 * getPublicName - the full-length public name.
836 * @return string The group_name.
838 function getPublicName() {
839 return $this->data_array['group_name'];
843 * getRegisterPurpose - the text description of the purpose of this project.
845 * @return string The description.
847 function getRegisterPurpose() {
848 return $this->data_array['register_purpose'];
852 * getDescription - the text description of this project.
854 * @return string The description.
856 function getDescription() {
857 return $this->data_array['short_description'];
861 * getStartDate - the unix time this project was registered.
863 * @return int (unix time) of registration.
865 function getStartDate() {
866 return $this->data_array['register_time'];
870 * getLogoImageID - the id of the logo in the database for this project.
872 * @return int The ID of logo image in db_images table (or 100 if none).
874 function getLogoImageID() {
875 return $this->data_array['logo_image_id'];
879 * getUnixBox - the hostname of the unix box where this project is located.
881 * @return string The name of the unix machine for the group.
883 function getUnixBox() {
884 return $this->data_array['unix_box'];
888 * getSCMBox - the hostname of the scm box where this project is located.
890 * @return string The name of the unix machine for the group.
892 function getSCMBox() {
893 return $this->data_array['scm_box'];
896 * setSCMBox - the hostname of the scm box where this project is located.
898 * @param string The name of the new SCM_BOX
900 function setSCMBox($scm_box) {
902 if ($scm_box == $this->data_array['scm_box']) {
907 $res = db_query_params('UPDATE groups SET scm_box=$1 WHERE group_id=$2', array($scm_box, $this->getID ()));
909 $this->addHistory('scm_box', $this->data_array['scm_box']);
910 $this->data_array['scm_box'] = $scm_box;
915 $this->setError(_("Couldn't insert SCM_BOX to database"));
919 $this->setError(_("SCM Box can't be empty"));
925 * getDomain - the hostname.domain where their web page is located.
927 * @return string The name of the group [web] domain.
929 function getDomain() {
930 return $this->data_array['http_domain'];
934 * getRegistrationPurpose - the text description of the purpose of this project.
936 * @return string The application for project hosting.
938 function getRegistrationPurpose() {
939 return $this->data_array['register_purpose'];
944 * getAdmins() - Get array of Admin user objects.
946 * @return array Array of User objects.
948 function &getAdmins() {
949 $roles = RBACEngine::getInstance()->getRolesByAllowedAction ('project_admin', $this->getID());
953 foreach ($roles as $role) {
954 if (! ($role instanceof RoleExplicit)) {
957 if ($role->getHomeProject() == NULL
958 || $role->getHomeProject()->getID() != $this->getID()) {
962 foreach ($role->getUsers() as $u) {
963 $user_ids[] = $u->getID();
966 return user_get_objects(array_unique($user_ids));
970 Common Group preferences for tools
974 * enableAnonSCM - whether or not this group has opted to enable Anonymous SCM.
976 * @return boolean enable_scm.
978 function enableAnonSCM() {
979 $r = RoleAnonymous::getInstance();
980 return $r->hasPermission('scm', $this->getID(), 'read');
983 function SetUsesAnonSCM($booleanparam) {
985 $booleanparam = $booleanparam ? 1 : 0;
986 $r = RoleAnonymous::getInstance();
987 $r->setSetting('scm', $this->getID(), $booleanparam);
992 * enablePserver - whether or not this group has opted to enable Pserver.
994 * @return boolean enable_pserver.
996 function enablePserver() {
997 if ($this->usesSCM()) {
998 return $this->data_array['enable_pserver'];
1004 function SetUsesPserver($booleanparam) {
1006 $booleanparam = $booleanparam ? 1 : 0;
1007 $res = db_query_params('UPDATE groups SET enable_pserver=$1 WHERE group_id=$2',
1008 array($booleanparam, $this->getID()));
1010 $this->data_array['enable_pserver'] = $booleanparam;
1019 * usesSCM - whether or not this group has opted to use SCM.
1021 * @return boolean uses_scm.
1023 function usesSCM() {
1024 if (forge_get_config('use_scm')) {
1025 return $this->data_array['use_scm'];
1032 * setUseSCM - Set the SCM usage
1034 * @param boolean enabled/disabled
1036 function setUseSCM($booleanparam) {
1038 $booleanparam = $booleanparam ? 1 : 0 ;
1039 $res = db_query_params('UPDATE groups SET use_scm=$1 WHERE group_id=$2',
1040 array($booleanparam, $this->getID()));
1042 $this->data_array['use_scm']=$booleanparam;
1052 * usesMail - whether or not this group has opted to use mailing lists.
1054 * @return boolean uses_mail.
1056 function usesMail() {
1057 if (forge_get_config('use_mail')) {
1058 return $this->data_array['use_mail'];
1065 * setUseMail - Set the mailing-list usage
1067 * @param boolean enabled/disabled
1069 function setUseMail($booleanparam) {
1071 $booleanparam = $booleanparam ? 1 : 0 ;
1072 $res = db_query_params('UPDATE groups SET use_mail=$1 WHERE group_id=$2',
1073 array($booleanparam, $this->getID()));
1075 $this->data_array['use_mail']=$booleanparam;
1085 * usesNews - whether or not this group has opted to use news.
1087 * @return boolean uses_news.
1089 function usesNews() {
1090 if (forge_get_config('use_news')) {
1091 return $this->data_array['use_news'];
1098 * usesActivity - whether or not this group has opted to display Project Activities.
1100 * @return boolean uses_activities.
1102 function usesActivity() {
1103 if (forge_get_config('use_activity')) {
1104 return $this->data_array['use_activity'];
1111 * usesForum - whether or not this group has opted to use discussion forums.
1113 * @return boolean uses_forum.
1115 function usesForum() {
1116 if (forge_get_config('use_forum')) {
1117 return $this->data_array['use_forum'];
1124 * setUseForum - Set the forum usage
1126 * @param boolean enabled/disabled
1128 function setUseForum($booleanparam) {
1130 $booleanparam = $booleanparam ? 1 : 0;
1131 $res = db_query_params('UPDATE groups SET use_forum=$1 WHERE group_id=$2',
1132 array($booleanparam, $this->getID()));
1134 $this->data_array['use_forum']=$booleanparam;
1144 * usesStats - whether or not this group has opted to use stats.
1146 * @return boolean uses_stats.
1148 function usesStats() {
1149 return $this->data_array['use_stats'];
1153 * usesFRS - whether or not this group has opted to use file release system.
1155 * @return boolean uses_frs.
1157 function usesFRS() {
1158 if (forge_get_config('use_frs')) {
1159 return $this->data_array['use_frs'];
1166 * setUseFRS - Set the FRS usage
1168 * @param boolean enabled/disabled
1170 function setUseFRS($booleanparam) {
1172 $booleanparam = $booleanparam ? 1 : 0;
1173 $res = db_query_params('UPDATE groups SET use_frs=$1 WHERE group_id=$2',
1174 array ($booleanparam, $this->getID()));
1176 $this->data_array['use_frs']=$booleanparam;
1186 * usesTracker - whether or not this group has opted to use tracker.
1188 * @return boolean uses_tracker.
1190 function usesTracker() {
1191 if (forge_get_config('use_tracker')) {
1192 return $this->data_array['use_tracker'];
1199 * setUseTracker - Set the tracker usage
1201 * @param boolean enabled/disabled
1203 function setUseTracker ($booleanparam) {
1205 $booleanparam = $booleanparam ? 1 : 0 ;
1206 $res = db_query_params ('UPDATE groups SET use_tracker=$1 WHERE group_id=$2',
1207 array ($booleanparam, $this->getID()));
1209 $this->data_array['use_tracker']=$booleanparam;
1219 * useCreateOnline - whether or not this group has opted to use create online documents option.
1221 * @return boolean use_docman_create_online.
1223 function useCreateOnline() {
1224 if (forge_get_config('use_docman')) {
1225 return $this->data_array['use_docman_create_online'];
1232 * usesDocman - whether or not this group has opted to use docman.
1234 * @return boolean use_docman.
1236 function usesDocman() {
1237 if (forge_get_config('use_docman')) {
1238 return $this->data_array['use_docman'];
1245 * setUseDocman - Set the docman usage
1247 * @param boolean enabled/disabled
1249 function setUseDocman($booleanparam) {
1251 $booleanparam = $booleanparam ? 1 : 0;
1252 $res = db_query_params('UPDATE groups SET use_docman = $1 WHERE group_id = $2',
1253 array($booleanparam, $this->getID()));
1255 // check if / doc_group exists, if not create it
1256 $trashdir = db_query_params('select groupname from doc_groups where groupname = $1 and group_id = $2',
1257 array('.trash', $this->getID()));
1258 if ($trashdir && db_numrows($trashdir) == 0) {
1259 $resinsert = db_query_params('insert into doc_groups (groupname, group_id, stateid) values ($1, $2, $3)',
1260 array('.trash', $this->getID(), '2'));
1266 $this->data_array['use_docman'] = $booleanparam;
1276 * useDocmanSearch - whether or not this group has opted to use docman search engine.
1278 * @return boolean use_docman_search.
1280 function useDocmanSearch() {
1281 if (forge_get_config('use_docman')) {
1282 return $this->data_array['use_docman_search'];
1289 * useWebdav - whether or not this group has opted to use webdav interface.
1291 * @return boolean use_docman_search.
1293 function useWebdav() {
1294 if (forge_get_config('use_webdav')) {
1295 return $this->data_array['use_webdav'];
1302 * usesFTP - whether or not this group has opted to use FTP.
1304 * @return boolean uses_ftp.
1306 function usesFTP() {
1307 if (forge_get_config('use_ftp')) {
1308 return $this->data_array['use_ftp'];
1315 * usesSurvey - whether or not this group has opted to use surveys.
1317 * @return boolean uses_survey.
1319 function usesSurvey() {
1320 if (forge_get_config('use_survey')) {
1321 return $this->data_array['use_survey'];
1328 * usesPM - whether or not this group has opted to Project Manager.
1330 * @return boolean uses_projman.
1333 if (forge_get_config('use_pm')) {
1334 return $this->data_array['use_pm'];
1341 * setUsePM - Set the PM usage
1343 * @param boolean enabled/disabled
1345 function setUsePM($booleanparam) {
1347 $booleanparam = $booleanparam ? 1 : 0;
1348 $res = db_query_params('UPDATE groups SET use_pm=$1 WHERE group_id=$2',
1349 array($booleanparam, $this->getID()));
1351 $this->data_array['use_pm']=$booleanparam;
1361 * getPlugins - get a list of all available group plugins
1363 * @return array array containing plugin_id => plugin_name
1365 function getPlugins() {
1366 if (!isset($this->plugins_data)) {
1367 $this->plugins_data = array();
1368 $res = db_query_params('SELECT group_plugin.plugin_id, plugins.plugin_name
1369 FROM group_plugin, plugins
1370 WHERE group_plugin.group_id=$1
1371 AND group_plugin.plugin_id=plugins.plugin_id', array($this->getID()));
1372 $rows = db_numrows($res);
1374 for ($i=0; $i<$rows; $i++) {
1375 $plugin_id = db_result($res, $i, 'plugin_id');
1376 $this->plugins_data[$plugin_id] = db_result($res, $i, 'plugin_name');
1379 return $this->plugins_data;
1383 * usesPlugin - returns true if the group uses a particular plugin
1385 * @param string name of the plugin
1386 * @return boolean whether plugin is being used or not
1388 function usesPlugin($pluginname) {
1389 $plugins_data = $this->getPlugins();
1390 foreach ($plugins_data as $p_id => $p_name) {
1391 if ($p_name == $pluginname) {
1399 * added for Codendi compatibility
1400 * usesServices - returns true if the group uses a particular plugin or feature
1402 * @param string name of the plugin
1403 * @return boolean whether plugin is being used or not
1405 function usesService($feature) {
1406 $plugins_data = $this->getPlugins();
1407 $pm = plugin_manager_get_object();
1408 foreach ($plugins_data as $p_id => $p_name) {
1409 if ($p_name == $feature) {
1412 if ($pm->getPluginByName($p_name)->provide($feature)) {
1420 * setPluginUse - enables/disables plugins for the group
1422 * @param string name of the plugin
1423 * @param boolean the new state
1424 * @return string database result
1426 function setPluginUse($pluginname, $val=true) {
1427 if ($val == $this->usesPlugin($pluginname)) {
1428 // State is already good, returning
1431 $res = db_query_params('SELECT plugin_id FROM plugins WHERE plugin_name=$1',
1432 array($pluginname));
1433 $rows = db_numrows($res);
1435 // Error: no plugin by that name
1438 $plugin_id = db_result($res,0,'plugin_id');
1440 unset($this->plugins_data);
1442 $res = db_query_params('INSERT INTO group_plugin (group_id, plugin_id) VALUES ($1, $2)',
1443 array($this->getID(),
1447 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1 AND plugin_id=$2',
1448 array($this->getID(),
1452 $this->normalizeAllRoles () ;
1456 * getDocEmailAddress - get email address(es) to send doc notifications to.
1458 * @return string email address.
1460 function getDocEmailAddress() {
1461 return $this->data_array['new_doc_address'];
1465 * DocEmailAll - whether or not this group has opted to use receive notices on all doc updates.
1467 * @return boolean email_on_all_doc_updates.
1469 function docEmailAll() {
1470 return $this->data_array['send_all_docs'];
1475 * getHomePage - The URL for this project's home page.
1477 * @return string homepage URL.
1479 function getHomePage() {
1480 if (!preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*:/",
1481 $this->data_array['homepage'])) {
1482 $this->data_array['homepage'] = util_url_prefix() .
1483 $this->data_array['homepage'];
1485 return $this->data_array['homepage'];
1489 * getTags - Tags of this project.
1491 * @return string List of tags. Comma separated
1493 function getTags() {
1494 $sql = 'SELECT name FROM project_tags WHERE group_id = $1';
1495 $res = db_query_params($sql, array($this->getID()));
1496 return join(', ', util_result_column_to_array($res));
1500 * setTags - Set tags of this project.
1502 * @return string database result.
1504 function setTags($tags) {
1506 $sql = 'DELETE FROM project_tags WHERE group_id=$1';
1507 $res = db_query_params($sql, array($this->getID()));
1509 $this->setError('Deleting old tags: '.db_error());
1513 $inserted = array();
1514 $tags_array = preg_split('/[;,]/', $tags);
1515 foreach ($tags_array as $tag) {
1516 $tag = preg_replace('/[\t\r\n]/', ' ', $tag);
1517 // Allowed caracteres: [A-Z][a-z][0-9] -_&'#+.
1518 if (preg_match('/[^[:alnum:]| |\-|_|\&|\'|#|\+|\.]/', $tag)) {
1519 $this->setError(_('Bad tag name, you only can use the following characters: [A-Z][a-z][0-9]-_&\'#+. and space'));
1524 if ($tag == '' || array_search($tag, $inserted) !== false) continue;
1525 $sql = 'INSERT INTO project_tags (group_id,name) VALUES ($1, $2)';
1526 $res = db_query_params($sql, array($this->getID(), $tag));
1528 $this->setError(_('Setting tags:') . ' ' .
1540 * getPermission - Return a Permission for this Group
1542 * @return object The Permission.
1544 function &getPermission() {
1545 return permission_get_object($this);
1549 function delete($sure, $really_sure, $really_really_sure) {
1550 if (!$sure || !$really_sure || !$really_really_sure) {
1551 $this->setMissingParamsError(_('Please tick all checkboxes.'));
1554 if ($this->getID() == forge_get_config('news_group') ||
1555 $this->getID() == 1 ||
1556 $this->getID() == forge_get_config('stats_group') ||
1557 $this->getID() == forge_get_config('peer_rating_group')) {
1558 $this->setError(_('Cannot Delete System Group'));
1561 $perm = $this->getPermission();
1562 if (!$perm || !is_object($perm)) {
1563 $this->setPermissionDeniedError();
1565 } elseif ($perm->isError()) {
1566 $this->setPermissionDeniedError();
1568 } elseif (!$perm->isSuperUser()) {
1569 $this->setPermissionDeniedError();
1575 // Remove all the members
1577 $members = $this->getMembers();
1578 foreach ($members as $i) {
1579 if(!$this->removeUser($i->getID())) {
1580 $this->setError(_('Could not properly remove member:').' '.$i->getID());
1585 // unlink roles from this project
1586 $ra = RoleAnonymous::getInstance();
1587 $rl = RoleLoggedIn::getInstance();
1588 $ra->unlinkProject($this);
1589 $rl->unlinkProject($this);
1590 // @todo : unlink all the other roles created in the project...
1595 $atf = new ArtifactTypeFactory($this);
1596 $at_arr = $atf->getArtifactTypes();
1597 foreach ($at_arr as $i) {
1598 if (!is_object($i)) {
1601 if (!$i->delete(1,1)) {
1602 $this->setError(_('Could not properly delete the tracker:').' '.$i->getErrorMessage());
1609 $ff = new ForumFactory($this);
1610 $f_arr = $ff->getForums();
1611 foreach ($f_arr as $i) {
1612 if (!is_object($i)) {
1615 if(!$i->delete(1,1)) {
1616 $this->setError(_('Could not properly delete the forum:').' '.$i->getErrorMessage());
1621 // Delete Subprojects
1623 $pgf = new ProjectGroupFactory($this);
1624 $pg_arr = $pgf->getProjectGroups();
1625 foreach ($pg_arr as $i) {
1626 if (!is_object($i)) {
1629 if (!$i->delete(1,1)) {
1630 $this->setError(_('Could not properly delete the ProjectGroup:').' '.$i->getErrorMessage());
1635 // Delete FRS Packages
1637 $res = db_query_params('SELECT * FROM frs_package WHERE group_id=$1',
1638 array($this->getID()));
1640 $this->setError(_('Error FRS Packages: ').db_error());
1645 while ($arr = db_fetch_array($res)) {
1646 $frsp=new FRSPackage($this, $arr['package_id'], $arr);
1647 if (!$frsp->delete(1, 1)) {
1648 $this->setError(_('Could not properly delete the FRSPackage:').' '.$frsp->getErrorMessage());
1655 $news_group=group_get_object(forge_get_config('news_group'));
1656 $res = db_query_params('SELECT forum_id FROM news_bytes WHERE group_id=$1',
1657 array($this->getID()));
1659 $this->setError(_('Error Deleting News: ').db_error());
1664 for ($i=0; $i<db_numrows($res); $i++) {
1665 $Forum = new Forum($news_group,db_result($res,$i,'forum_id'));
1666 if (!$Forum->delete(1,1)) {
1667 $this->setError(_("Could Not Delete News Forum: %d"),$Forum->getID());
1671 $res = db_query_params('DELETE FROM news_bytes WHERE group_id=$1',
1672 array($this->getID()));
1674 $this->setError(_('Error Deleting News: ').db_error());
1682 $res = db_query_params('DELETE FROM doc_data WHERE group_id=$1',
1683 array($this->getID()));
1685 $this->setError(_('Error Deleting Documents: ').db_error());
1690 $res = db_query_params('DELETE FROM doc_groups WHERE group_id=$1',
1691 array($this->getID()));
1693 $this->setError(_('Error Deleting Documents: ').db_error());
1701 $res=db_query_params('DELETE FROM project_tags WHERE group_id=$1', array($this->getID()));
1703 $this->setError(_('Error Deleting Tags: ').db_error());
1709 // Delete group history
1711 $res = db_query_params('DELETE FROM group_history WHERE group_id=$1',
1712 array($this->getID()));
1714 $this->setError(_('Error Deleting Project History: ').db_error());
1720 // Delete group plugins
1722 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1',
1723 array($this->getID()));
1725 $this->setError(_('Error Deleting Project Plugins: ').db_error());
1731 // Delete group cvs stats
1733 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE group_id=$1',
1734 array ($this->getID())) ;
1736 $this->setError(_('Error Deleting SCM Statistics: ').db_error());
1744 $sf = new SurveyFactory($this);
1745 $s_arr =& $sf->getSurveys();
1746 foreach ($s_arr as $i) {
1747 if (!is_object($i)) {
1750 if (!$i->delete()) {
1751 $this->setError(_('Could not properly delete the survey'));
1757 // Delete SurveyQuestions
1759 $sqf = new SurveyQuestionFactory($this);
1760 $sq_arr =& $sqf->getSurveyQuestions();
1761 foreach ($sq_arr as $i) {
1762 if (!is_object($i)) {
1765 if (!$i->delete()) {
1766 $this->setError(_('Could not properly delete the survey questions'));
1772 // Delete Mailing List Factory
1774 $mlf = new MailingListFactory($this);
1775 $ml_arr = $mlf->getMailingLists();
1776 foreach ($ml_arr as $i) {
1777 if (!is_object($i)) {
1780 if (!$i->delete(1,1)) {
1781 $this->setError(_('Could not properly delete the mailing list'));
1789 $res = db_query_params('DELETE FROM trove_group_link WHERE group_id=$1',
1790 array($this->getID()));
1792 $this->setError(_('Error Deleting Trove: ').db_error());
1797 $res = db_query_params('DELETE FROM trove_agg WHERE group_id=$1',
1798 array($this->getID()));
1800 $this->setError(_('Error Deleting Trove: ').db_error());
1808 $res = db_query_params('DELETE FROM project_sums_agg WHERE group_id=$1',
1809 array($this->getID()));
1811 $this->setError(_('Error Deleting Counters: ').db_error());
1816 $res = db_query_params('INSERT INTO deleted_groups (unix_group_name, delete_date, isdeleted) VALUES ($1, $2, $3)',
1817 array($this->getUnixName(),
1821 $this->setError(_('Error Deleting Project:').' '.db_error());
1826 // Delete entry in groups.
1827 $res = db_query_params('DELETE FROM groups WHERE group_id=$1',
1828 array($this->getID()));
1830 $this->setError(_('Error Deleting Project:').' '.db_error());
1837 $hook_params = array();
1838 $hook_params['group'] = $this;
1839 $hook_params['group_id'] = $this->getID();
1840 plugin_hook("group_delete", $hook_params);
1842 if (forge_get_config('upload_dir') != '' && $this->getUnixName()) {
1843 exec('/bin/rm -rf '.forge_get_config('upload_dir').'/'.$this->getUnixName().'/');
1845 if (forge_get_config('ftp_upload_dir') != '' && $this->getUnixName()) {
1846 exec('/bin/rm -rf '.forge_get_config('ftp_upload_dir').'/'.$this->getUnixName().'/');
1851 $res = db_query_params('DELETE FROM rep_group_act_monthly WHERE group_id=$1',
1852 array ($this->getID()));
1853 //echo 'rep_group_act_monthly'.db_error();
1854 $res = db_query_params('DELETE FROM rep_group_act_weekly WHERE group_id=$1',
1855 array ($this->getID()));
1856 //echo 'rep_group_act_weekly'.db_error();
1857 $res = db_query_params('DELETE FROM rep_group_act_daily WHERE group_id=$1',
1858 array ($this->getID()));
1859 //echo 'rep_group_act_daily'.db_error();
1860 unset($this->data_array);
1865 Basic functions to add/remove users to/from a group
1866 and update their permissions
1870 * addUser - controls adding a user to a group.
1872 * @param string Unix name of the user to add OR integer user_id.
1873 * @param int The role_id this user should have.
1874 * @return boolean success.
1877 function addUser($user_identifier,$role_id) {
1880 Admins can add users to groups
1883 if (!forge_check_perm ('project_admin', $this->getID())) {
1884 $this->setPermissionDeniedError();
1890 get user id for this user's unix_name
1892 if (is_int ($user_identifier)) { // user_id or user_name
1893 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_id=$1', array ($user_identifier)) ;
1895 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_name=$1', array ($user_identifier)) ;
1897 if (db_numrows($res_newuser) > 0) {
1899 // make sure user is active
1901 if (db_result($res_newuser,0,'status') != 'A') {
1902 $this->setError(_('User is not active. Only active users can be added.'));
1908 // user was found - set new user_id var
1910 $user_id = db_result($res_newuser,0,'user_id');
1912 $role = new Role($this, $role_id);
1913 if (!$role || !is_object($role)) {
1914 $this->setError(_('Error Getting Role Object'));
1917 } elseif ($role->isError()) {
1918 $this->setError('addUser::roleget::'.$role->getErrorMessage());
1923 $role->addUser(user_get_object($user_id)) ;
1924 if (!$SYS->sysCheckCreateGroup($this->getID())){
1925 $this->setError($SYS->getErrorMessage());
1929 if (!$SYS->sysCheckCreateUser($user_id)) {
1930 $this->setError($SYS->getErrorMessage());
1934 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
1935 $this->setError($SYS->getErrorMessage());
1941 // user doesn't exist
1943 $this->setError(_('ERROR: User does not exist'));
1948 $hook_params['group'] = $this;
1949 $hook_params['group_id'] = $this->getID();
1950 $hook_params['user'] = user_get_object($user_id);
1951 $hook_params['user_id'] = $user_id;
1952 plugin_hook ("group_adduser", $hook_params);
1957 $this->addHistory('Added User',$user_identifier);
1963 * removeUser - controls removing a user from a group.
1965 * Users can remove themselves.
1967 * @param int The ID of the user to remove.
1968 * @return boolean success.
1970 function removeUser($user_id) {
1973 if ($user_id != user_getid()
1974 && !forge_check_perm('project_admin', $this->getID())) {
1975 $this->setPermissionDeniedError();
1981 $user = user_get_object($user_id);
1982 $roles = RBACEngine::getInstance()->getAvailableRolesForUser($user);
1984 foreach ($roles as $role) {
1985 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
1986 $found_role = $role;
1990 if ($found_role == NULL) {
1991 $this->setError(sprintf(_('ERROR: User not removed: %s')));
1995 $found_role->removeUser($user);
1996 if (!$SYS->sysGroupCheckUser($this->getID(), $user_id)) {
1997 $this->setError($SYS->getErrorMessage());
2003 // reassign open artifacts to id=100
2005 $res = db_query_params('UPDATE artifact SET assigned_to=100
2006 WHERE group_artifact_id
2007 IN (SELECT group_artifact_id
2008 FROM artifact_group_list
2009 WHERE group_id=$1 AND status_id=1 AND assigned_to=$2)',
2010 array($this->getID(),
2013 $this->setError(_('ERROR: DB: artifact:').' '.db_error());
2019 // reassign open tasks to id=100
2020 // first have to purge any assignments that would cause
2021 // conflict with existing assignment to 100
2023 $res = db_query_params('DELETE FROM project_assigned_to
2024 WHERE project_task_id IN (SELECT pt.project_task_id
2025 FROM project_task pt, project_group_list pgl, project_assigned_to pat
2026 WHERE pt.group_project_id = pgl.group_project_id
2027 AND pat.project_task_id=pt.project_task_id
2028 AND pt.status_id=1 AND pgl.group_id=$1
2029 AND pat.assigned_to_id=$2)
2030 AND assigned_to_id=100',
2031 array($this->getID(),
2034 $this->setError(sprintf(_('ERROR: DB: project_assigned_to %d: %s'), 1, db_error()));
2038 $res = db_query_params('UPDATE project_assigned_to SET assigned_to_id=100
2039 WHERE project_task_id IN (SELECT pt.project_task_id
2040 FROM project_task pt, project_group_list pgl
2041 WHERE pt.group_project_id = pgl.group_project_id
2042 AND pt.status_id=1 AND pgl.group_id=$1)
2043 AND assigned_to_id=$2',
2044 array($this->getID(),
2047 $this->setError(sprintf(_('ERROR: DB: project_assigned_to %d: %s'), 2, db_error()));
2053 // Remove user from system
2055 if (!$SYS->sysGroupRemoveUser($this->getID(), $user_id)) {
2056 $this->setError($SYS->getErrorMessage());
2061 $hook_params['group'] = $this;
2062 $hook_params['group_id'] = $this->getID();
2063 $hook_params['user'] = user_get_object($user_id);
2064 $hook_params['user_id'] = $user_id;
2065 plugin_hook ("group_removeuser", $hook_params);
2068 $this->addHistory('Removed User',$user_id);
2075 * updateUser - controls updating a user's role in this group.
2077 * @param int The ID of the user.
2078 * @param int The role_id to set this user to.
2079 * @return boolean success.
2081 function updateUser($user_id,$role_id) {
2083 if (!forge_check_perm ('project_admin', $this->getID())) {
2084 $this->setPermissionDeniedError();
2088 $newrole = RBACEngine::getInstance()->getRoleById ($role_id) ;
2089 if (!$newrole || !is_object($newrole)) {
2090 $this->setError(_('Could Not Get Role'));
2092 } elseif ($newrole->isError()) {
2093 $this->setError(sprintf(_('Role: %s'),$role->getErrorMessage()));
2095 } elseif ($newrole->getHomeProject() == NULL
2096 || $newrole->getHomeProject()->getID() != $this->getID()) {
2097 $this->setError(_('Wrong destination role'));
2100 $user = user_get_object ($user_id) ;
2101 $roles = RBACEngine::getInstance()->getAvailableRolesForUser ($user) ;
2102 $found_role = NULL ;
2103 foreach ($roles as $role) {
2104 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2105 $found_role = $role ;
2109 if ($found_role == NULL) {
2110 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2114 $found_role->removeUser ($user) ;
2115 $newrole->addUser ($user) ;
2117 $this->addHistory('Updated User',$user_id);
2122 * addHistory - Makes an audit trail entry for this project.
2124 * @param string The name of the field.
2125 * @param string The Old Value for this $field_name.
2126 * @return database result handle.
2129 function addHistory($field_name, $old_value) {
2130 return db_query_params ('INSERT INTO group_history(group_id,field_name,old_value,mod_by,adddate)
2131 VALUES ($1,$2,$3,$4,$5)',
2132 array ($this->getID(),
2140 * activateUsers - Make sure that group members have unix accounts.
2142 * Setup unix accounts for group members. Can be called even
2143 * if members are already active.
2147 function activateUsers() {
2149 Activate member(s) of the project
2152 $members = $this->getUsers (true) ;
2154 foreach ($members as $member) {
2156 foreach (RBACEngine::getInstance()->getAvailableRolesForUser ($member) as $role) {
2157 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2160 if (!$this->addUser($member->getUnixName(),$role->getID())) {
2171 * getMembers - returns array of User objects for this project
2173 * @return array of User objects for this group.
2175 function getMembers() {
2176 return $this->getUsers (true) ;
2180 * replaceTemplateStrings - fill-in some blanks with project name
2182 * @param string Template string
2183 * @return string String after replacements
2185 function replaceTemplateStrings($string) {
2186 $string = str_replace ('UNIXNAME', $this->getUnixName(), $string) ;
2187 $string = str_replace ('PUBLICNAME', $this->getPublicName(), $string) ;
2188 $string = str_replace ('DESCRIPTION', $this->getDescription(), $string) ;
2193 * approve - Approve pending project.
2195 * @param object The User object who is doing the updating.
2198 function approve(&$user) {
2199 global $gfcommon,$gfwww;
2200 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
2202 if ($this->getStatus()=='A') {
2203 $this->setError(_("Group already active"));
2209 // Step 1: Activate group and create LDAP entries
2210 if (!$this->setStatus($user, 'A')) {
2215 // Switch to system language for item creation
2216 setup_gettext_from_sys_lang();
2218 // Create default roles
2219 $idadmin_group = NULL;
2220 foreach (get_group_join_requests ($this) as $gjr) {
2221 $idadmin_group = $gjr->getUserID();
2224 if ($idadmin_group == NULL) {
2225 $idadmin_group = $user->getID();
2228 $template = $this->getTemplateProject();
2229 $id_mappings = array();
2230 $seen_admin_role = false;
2232 // Copy roles from template project
2233 foreach($template->getRoles() as $oldrole) {
2234 if ($oldrole->getHomeProject() != NULL) {
2235 $role = new Role($this);
2237 // Need to use a different role name so that the permissions aren't set from the hardcoded defaults
2238 $role->create('TEMPORARY ROLE NAME', $data, true);
2239 $role->setName($oldrole->getName());
2240 if ($oldrole->getSetting ('project_admin', $template->getID())) {
2241 $seen_admin_role = true;
2245 $role->linkProject($this);
2247 $id_mappings['role'][$oldrole->getID()] = $role->getID();
2248 // Reuse the project_admin permission
2249 $role->setSetting ('project_admin', $this->getID(), $oldrole->getSetting ('project_admin', $template->getID())) ;
2253 if (!$seen_admin_role) {
2254 $role = new Role($this);
2255 $adminperms = array ('project_admin' => array ($this->getID() => 1)) ;
2256 $role_id = $role->create ('Admin', $adminperms, true) ;
2259 $roles = $this->getRoles() ;
2260 foreach ($roles as $r) {
2261 if ($r->getHomeProject() == NULL) {
2264 if ($r->getSetting ('project_admin', $this->getID())) {
2265 $r->addUser(user_get_object ($idadmin_group));
2269 // Temporarily switch to the submitter's identity
2270 $saved_session = session_get_user();
2271 session_set_internal($idadmin_group);
2274 if (forge_get_config('use_tracker')) {
2275 $this->setUseTracker ($template->usesTracker());
2276 if ($template->usesTracker()) {
2277 $oldatf = new ArtifactTypeFactory($template);
2278 foreach ($oldatf->getArtifactTypes() as $o) {
2279 $t = new ArtifactType ($this) ;
2280 $t->create ($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->emailAll(),$o->getEmailAddress(),$o->getDuePeriod()/86400,0,$o->getSubmitInstructions(),$o->getBrowseInstructions()) ;
2281 $id_mappings['tracker'][$o->getID()] = $t->getID();
2282 $t->cloneFieldsFrom ($o->getID());
2287 if (forge_get_config('use_pm')) {
2288 $this->setUsePM ($template->usesPM());
2289 if ($template->usesPM()) {
2290 $oldpgf = new ProjectGroupFactory($template);
2291 foreach ($oldpgf->getProjectGroups() as $o) {
2292 $pg = new ProjectGroup($this);
2293 $pg->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo());
2294 $id_mappings['pm'][$o->getID()] = $pg->getID();
2299 if (forge_get_config('use_forum')) {
2300 $this->setUseForum($template->usesForum()) ;
2301 if ($template->usesForum()) {
2302 $oldff = new ForumFactory($template) ;
2303 foreach ($oldff->getForums() as $o) {
2304 $f = new Forum($this);
2305 $f->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo(),1);
2306 $id_mappings['forum'][$o->getID()] = $f->getID();
2311 if (forge_get_config('use_docman')) {
2312 $this->setUseDocman($template->usesDocman());
2313 if ($template->usesDocman()) {
2314 $olddgf = new DocumentGroupFactory($template);
2315 // First pass: create all docgroups
2316 $id_mappings['docman_docgroup'][0] = 0;
2317 foreach ($olddgf->getDocumentGroups() as $o) {
2318 $ndgf = new DocumentGroup($this);
2319 // .trash is a reserved directory
2320 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2321 $ndgf->create($this->replaceTemplateStrings($o->getName()));
2322 $id_mappings['docman_docgroup'][$o->getID()] = $ndgf->getID();
2325 // Second pass: restore hierarchy links
2326 foreach ($olddgf->getDocumentGroups() as $o) {
2327 $ndgf = new DocumentGroup($this);
2328 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2329 $ndgf->fetchData($id_mappings['docman_docgroup'][$o->getID()]);
2330 $ndgf->update($ndgf->getName(), $id_mappings['docman_docgroup'][$o->getParentID()]);
2336 if (forge_get_config('use_frs')) {
2337 $this->setUseFRS ($template->usesFRS());
2338 if ($template->usesFRS()) {
2339 foreach (get_frs_packages($template) as $o) {
2340 $newp = new FRSPackage($this);
2341 $nname = $this->replaceTemplateStrings($o->getName());
2342 $newp->create ($nname, $o->isPublic());
2347 if (forge_get_config('use_mail')) {
2348 $this->setUseMail($template->usesMail()) ;
2349 if ($template->usesMail()) {
2350 $oldmlf = new MailingListFactory($template);
2351 foreach ($oldmlf->getMailingLists() as $o) {
2352 $ml = new MailingList($this);
2353 $nname = preg_replace ('/^'.$template->getUnixName().'-/','',$o->getName()) ;
2355 $ndescription = $this->replaceTemplateStrings($o->getDescription()) ;
2356 $ml->create($nname, $ndescription, $o->isPublic());
2362 /* use SCM plugin from template group */
2363 $this->setUseSCM($template->usesSCM());
2365 foreach ($template->getPlugins() as
2366 $plugin_id => $plugin_name) {
2367 $this->setPluginUse($plugin_name);
2370 /* use SCM choice from registration page */
2372 foreach ($template->getPlugins() as
2373 $plugin_id => $plugin_name) {
2374 if (substr($plugin_name, 3) == 'scm' &&
2375 $plugin_name != 'scmhook') {
2376 /* skip copying scm plugins */
2379 /* enable other plugins though */
2380 $this->setPluginUse($plugin_name);
2384 foreach ($template->getRoles() as $oldrole) {
2385 $newrole = RBACEngine::getInstance()->getRoleById ($id_mappings['role'][$oldrole->getID()]) ;
2386 if ($oldrole->getHomeProject() != NULL
2387 && $oldrole->getHomeProject()->getID() == $template->getID()) {
2388 $newrole->setPublic ($oldrole->isPublic()) ;
2390 $oldsettings = $oldrole->getSettingsForProject ($template) ;
2392 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm') ;
2393 foreach ($sections as $section) {
2394 $newrole->setSetting ($section, $this->getID(), $oldsettings[$section][$template->getID()]) ;
2397 $sections = array ('tracker', 'pm', 'forum') ;
2398 foreach ($sections as $section) {
2399 if (isset ($oldsettings[$section])) {
2400 foreach ($oldsettings[$section] as $k => $v) {
2401 // Only copy perms for tools that have been copied
2402 if (isset ($id_mappings[$section][$k])) {
2403 $newrole->setSetting ($section,
2404 $id_mappings[$section][$k],
2412 $lm = new WidgetLayoutManager();
2413 $lm->createDefaultLayoutForProject ($this->getID(), $template->getID()) ;
2415 $params = array () ;
2416 $params['template'] = $template ;
2417 $params['project'] = $this ;
2418 $params['id_mappings'] = $id_mappings ;
2419 plugin_hook_by_reference ('clone_project_from_template', $params) ;
2421 // Disable everything
2422 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',
2423 array ($this->getID())) ;
2426 $this->normalizeAllRoles();
2427 $this->activateUsers();
2429 // Delete fake join request
2430 foreach (get_group_join_requests ($this) as $gjr) {
2431 $gjr->delete(true) ;
2434 // Switch back to user preference
2435 session_set_internal($saved_session->getID());
2436 setup_gettext_from_context();
2440 $this->sendApprovalEmail();
2441 $this->addHistory('Approved', 'x');
2444 // Plugin can make approve operation there
2446 $params[0] = $idadmin_group;
2447 $params[1] = $this->getID();
2448 plugin_hook('group_approved', $params);
2456 * sendApprovalEmail - Send new project email.
2458 * @return boolean success.
2461 function sendApprovalEmail() {
2462 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('project_admin', $this->getID()) ;
2464 if (count($admins) < 1) {
2465 $this->setError(_("Group does not have any administrators."));
2469 // send one email per admin
2470 foreach ($admins as $admin) {
2471 setup_gettext_for_user ($admin) ;
2473 $message=sprintf(_('Your project registration for %4$s has been approved.
2475 Project Full Name: %1$s
2476 Project Unix Name: %2$s
2478 Your DNS will take up to a day to become active on our site.
2479 Your web site is accessible through your shell account. Please read
2480 site documentation (see link below) about intended usage, available
2481 services, and directory layout of the account.
2484 own project page in %4$s while logged in, you will find
2485 additional menu functions to your left labeled \'Project Admin\'.
2487 We highly suggest that you now visit %4$s and create a public
2488 description for your project. This can be done by visiting your project
2489 page while logged in, and selecting \'Project Admin\' from the menus
2490 on the left (or by visiting %3$s
2493 Your project will also not appear in the Trove Software Map (primary
2494 list of projects hosted on %4$s which offers great flexibility in
2495 browsing and search) until you categorize it in the project administration
2496 screens. So that people can find your project, you should do this now.
2497 Visit your project while logged in, and select \'Project Admin\' from the
2500 Enjoy the system, and please tell others about %4$s. Let us know
2501 if there is anything we can do to help you.
2504 htmlspecialchars_decode($this->getPublicName()),
2505 $this->getUnixName(),
2506 util_make_url ('/project/admin/?group_id='.$this->getID()),
2507 forge_get_config ('forge_name'));
2509 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Approved'), forge_get_config ('forge_name')), $message);
2511 setup_gettext_from_context();
2519 * sendRejectionEmail - Send project rejection email.
2521 * This function sends out a rejection message to a user who
2522 * registered a project.
2524 * @param int The id of the response to use.
2525 * @param string The rejection message.
2526 * @return boolean completion status.
2529 function sendRejectionEmail($response_id, $message="zxcv") {
2530 $submitters = array () ;
2531 foreach (get_group_join_requests ($this) as $gjr) {
2532 $submitters[] = user_get_object($gjr->getUserID());
2535 if (count ($submitters) < 1) {
2536 $this->setError(_("Group does not have any administrators."));
2540 foreach ($submitters as $admin) {
2541 setup_gettext_for_user($admin);
2543 $response=sprintf(_('Your project registration for %3$s has been denied.
2545 Project Full Name: %1$s
2546 Project Unix Name: %2$s
2548 Reasons for negative decision:
2550 '), $this->getPublicName(), $this->getUnixName(), forge_get_config('forge_name'));
2552 // Check to see if they want to send a custom rejection response
2553 if ($response_id == 0) {
2554 $response .= $message;
2556 $response .= db_result(
2557 db_query_params('SELECT response_text FROM canned_responses WHERE response_id=$1', array ($response_id)),
2562 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Denied'), forge_get_config ('forge_name')), $response);
2563 setup_gettext_from_context();
2570 * sendNewProjectNotificationEmail - Send new project notification email.
2572 * This function sends out a notification email to the
2573 * SourceForge admin user when a new project is
2576 * @return boolean success.
2579 function sendNewProjectNotificationEmail() {
2580 // Get the user who wants to register the project
2581 $submitters = array();
2582 foreach (get_group_join_requests ($this) as $gjr) {
2583 $submitters[] = user_get_object($gjr->getUserID());
2585 if (count ($submitters) < 1) {
2586 $this->setError(_("Could not find user who has submitted the project."));
2590 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('approve_projects', -1) ;
2592 if (count($admins) < 1) {
2593 $this->setError(_("There is no administrator to send the mail to."));
2597 foreach ($admins as $admin) {
2598 $admin_email = $admin->getEmail () ;
2599 setup_gettext_for_user ($admin) ;
2601 $message = sprintf(_('New %1$s Project Submitted
2603 Project Full Name: %2$s
2604 Submitted Description: %3$s
2606 forge_get_config ('forge_name'),
2607 htmlspecialchars_decode($this->getPublicName()),
2608 htmlspecialchars_decode($this->getRegistrationPurpose()));
2610 foreach ($submitters as $submitter) {
2611 $message .= sprintf(_('Submitter: %1$s (%2$s)
2613 $submitter->getRealName(),
2614 $submitter->getUnixName());
2617 $message .= sprintf (_('
2618 Please visit the following URL to approve or reject this project:
2620 util_make_url ('/admin/approve-pending.php')) ;
2621 util_send_message($admin_email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2622 setup_gettext_from_context();
2626 $email = $submitter->getEmail() ;
2627 setup_gettext_for_user ($submitter) ;
2629 $message=sprintf(_('New %1$s Project Submitted
2631 Project Full Name: %2$s
2632 Submitted Description: %3$s
2634 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'));
2636 util_send_message($email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2637 setup_gettext_from_context();
2646 * validateGroupName - Validate the group name
2648 * @param string Group name.
2650 * @return boolean an error false and set an error is the group name is invalide otherwise return true
2652 function validateGroupName($group_name) {
2653 if (strlen($group_name)<3) {
2654 $this->setError(_('Group name is too short'));
2656 } else if (strlen(htmlspecialchars($group_name))>50) {
2657 $this->setError(_('Group name is too long'));
2659 } else if (group_get_object_by_publicname($group_name)) {
2660 $this->setError(_('Group name already taken'));
2668 * getRolesId - Get Ids of the roles of the group.
2670 * @return array Role ids of this group.
2672 function getRolesId() {
2673 $role_ids = array();
2675 $res = db_query_params('SELECT role_id FROM pfo_role WHERE home_group_id=$1',
2676 array($this->getID()));
2677 while ($arr = db_fetch_array($res)) {
2678 $role_ids[] = $arr['role_id'];
2680 $res = db_query_params('SELECT role_id FROM role_project_refs WHERE group_id=$1',
2681 array($this->getID()));
2682 while ($arr = db_fetch_array($res)) {
2683 $role_ids[] = $arr['role_id'];
2686 return array_unique($role_ids);
2690 * getRoles - Get the roles of the group.
2692 * @return array Roles of this group.
2694 function getRoles() {
2697 $roles = $this->getRolesId();
2698 $engine = RBACEngine::getInstance();
2699 foreach ($roles as $role_id) {
2700 $result[] = $engine->getRoleById ($role_id);
2706 function normalizeAllRoles() {
2707 $roles = $this->getRoles();
2709 foreach ($roles as $r) {
2710 $r->normalizeData();
2715 * getUnixStatus - Status of activation of unix account.
2717 * @return char (N)one, (A)ctive, (S)uspended or (D)eleted
2719 function getUnixStatus() {
2720 return $this->data_array['unix_status'];
2724 * setUnixStatus - Sets status of activation of unix account.
2726 * @param string The unix status.
2732 * @return boolean success.
2734 function setUnixStatus($status) {
2737 $res = db_query_params ('UPDATE groups SET unix_status=$1 WHERE group_id=$2',
2742 $this->setError(sprintf(_('ERROR - Could Not Update Group Unix Status: %s'),db_error()));
2746 if ($status == 'A') {
2747 if (!$SYS->sysCheckCreateGroup($this->getID())) {
2748 $this->setError($SYS->getErrorMessage());
2753 if ($SYS->sysCheckGroup($this->getID())) {
2754 if (!$SYS->sysRemoveGroup($this->getID())) {
2755 $this->setError($SYS->getErrorMessage());
2762 $this->data_array['unix_status']=$status;
2769 * getUsers - Get the users of a group
2771 * @return array of user's objects.
2773 function getUsers($onlylocal = true) {
2774 if (!isset($this->membersArr)) {
2775 $this->membersArr = array () ;
2778 foreach ($this->getRoles() as $role) {
2780 && ($role->getHomeProject() == NULL || $role->getHomeProject()->getID() != $this->getID())) {
2783 foreach ($role->getUsers() as $user) {
2784 $ids[] = $user->getID() ;
2787 $ids = array_unique ($ids) ;
2788 foreach ($ids as $id) {
2789 $u = user_get_object ($id) ;
2790 if ($u->isActive()) {
2791 $this->membersArr[] = $u ;
2795 return $this->membersArr;
2798 function setDocmanCreateOnlineStatus($status) {
2800 /* if we activate search engine, we probably want to reindex */
2801 $res = db_query_params('UPDATE groups SET use_docman_create_online=$1 WHERE group_id=$2',
2802 array($status, $this->getID()));
2805 $this->setError(sprintf(_('ERROR - Could Not Update Group DocmanCreateOnline Status: %s'),db_error()));
2809 $this->data_array['use_docman_create_online']=$status;
2815 function setDocmanWebdav($status) {
2817 /* if we activate search engine, we probably want to reindex */
2818 $res = db_query_params('UPDATE groups SET use_webdav=$1 WHERE group_id=$2',
2823 $this->setError(sprintf(_('ERROR - Could Not Update Group UseWebdab Status: %s'),db_error()));
2827 $this->data_array['use_webdav']=$status;
2833 function setDocmanSearchStatus($status) {
2835 /* if we activate search engine, we probably want to reindex */
2836 $res = db_query_params('UPDATE groups SET use_docman_search=$1, force_docman_reindex=$1 WHERE group_id=$2',
2841 $this->setError(sprintf(_('ERROR - Could Not Update Group UseDocmanSearch Status: %s'),db_error()));
2845 $this->data_array['use_docman_search']=$status;
2851 function setDocmanForceReindexSearch($status) {
2853 /* if we activate search engine, we probably want to reindex */
2854 $res = db_query_params('UPDATE groups SET force_docman_reindex=$1 WHERE group_id=$2',
2859 $this->setError(sprintf(_('ERROR - Could Not Update Group force_docman_reindex %s'),db_error()));
2863 $this->data_array['force_docman_reindex']=$status;
2872 * group_getname() - get the group name
2874 * @param int The group ID
2878 function group_getname ($group_id = 0) {
2879 $grp = group_get_object($group_id);
2881 return $grp->getPublicName();
2888 * group_getunixname() - get the unixname for a group
2890 * @param int The group ID
2894 function group_getunixname ($group_id) {
2895 $grp = group_get_object($group_id);
2897 return $grp->getUnixName();
2904 * group_get_result() - Get the group object result ID.
2906 * @param int The group ID
2910 function &group_get_result($group_id=0) {
2911 $grp = group_get_object($group_id);
2913 return $grp->getData();
2919 function getAllProjectTags($onlyvisible = true) {
2920 $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',
2923 if (!$res || db_numrows($res) == 0) {
2929 while ($arr = db_fetch_array($res)) {
2931 $group_id = $arr[1];
2932 if (!isset($result[$tag])) {
2933 $result[$tag] = array();
2936 if (!$onlyvisible || forge_check_perm('project_read', $group_id)) {
2937 $p = group_get_object($group_id);
2938 $result[$tag][] = array('unix_group_name' => $p->getUnixName(),
2939 'group_id' => $group_id);
2947 * Utility class to compare project based in various criteria (names, unixnames, id, ...)
2950 class ProjectComparator {
2951 var $criterion = 'name' ;
2953 function Compare ($a, $b) {
2954 switch ($this->criterion) {
2957 $namecmp = strcoll ($a->getPublicName(), $b->getPublicName()) ;
2958 if ($namecmp != 0) {
2961 /* If several projects share a same public name */
2962 return strcoll ($a->getUnixName(), $b->getUnixName()) ;
2965 return strcmp ($a->getUnixName(), $b->getUnixName()) ;
2968 $aid = $a->getID() ;
2969 $bid = $b->getID() ;
2973 return ($a < $b) ? -1 : 1;
2979 function sortProjectList (&$list, $criterion='name') {
2980 $cmp = new ProjectComparator () ;
2981 $cmp->criterion = $criterion ;
2983 return usort ($list, array ($cmp, 'Compare')) ;
2988 // c-file-style: "bsd"