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-2013, 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
59 * @param int $group_id Required
60 * @param int|bool $res Result set handle ("SELECT * FROM groups WHERE group_id=xx")
61 * @return Group|bool 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);
154 * get_public_active_projects_asc() - Get a list of rows for public active projects (initially in trove/full_list)
156 * @param int Opional Maximum number of rows to limit query length
158 function get_public_active_projects_asc($max_query_limit = -1) {
160 $res_grp = db_query_params ('
161 SELECT group_id, group_name, unix_group_name, short_description, register_time
163 WHERE status = $1 AND type_id=1 AND group_id>4 AND register_time > 0
164 ORDER BY group_name ASC
169 while ($row_grp = db_fetch_array($res_grp)) {
170 if (!forge_check_perm ('project_read', $row_grp['group_id'])) {
173 $projects[] = $row_grp;
179 class Group extends Error {
181 * Associative array of data from db.
183 * @var array $data_array.
188 * array of User objects.
190 * @var array $membersArr.
195 * Whether the use is an admin/super user of this project.
197 * @var bool $is_admin.
202 * Artifact types result handle.
204 * @var int $types_res.
209 * Associative array of data for plugins.
211 * @var array $plugins_data.
217 * Associative array of data for the group menu.
219 * @var array $menu_data.
224 * Group - Group object constructor - use group_get_object() to instantiate.
226 * @param int|bool $id Required - Id of the group you want to instantiate.
227 * @param int|bool $res Database result from select query OR associative array of all columns.
228 * @return boolean Success or not
230 function __construct($id = false, $res = false) {
233 //setting up an empty object
234 //probably going to call create()
238 if (!$this->fetchData($id)) {
243 // Assoc array was passed in
245 if (is_array($res)) {
246 $this->data_array =& $res;
248 if (db_numrows($res) < 1) {
249 //function in class we extended
250 $this->setError(_('Group Not Found'));
251 $this->data_array=array();
254 //set up an associative array for use by other functions
255 $this->data_array = db_fetch_array_by_row($res, 0);
263 * fetchData - May need to refresh database fields if an update occurred.
265 * @param int $group_id The group_id.
266 * @return boolean success or not
268 function fetchData($group_id) {
269 $res = db_query_params ('SELECT * FROM groups WHERE group_id=$1',
271 if (!$res || db_numrows($res) < 1) {
272 $this->setError(sprintf(_('fetchData():: %s'),db_error()));
275 $this->data_array = db_fetch_array($res);
280 * create - Create new group.
282 * This method should be called on empty Group object.
283 * It will add an entry for a pending group/project (status 'P')
285 * @param object The User object.
286 * @param string The full name of the user.
287 * @param string The Unix name of the user.
288 * @param string The new group description.
289 * @param string The purpose of the group.
290 * @param boolean Whether to send an email or not
291 * @param int The id of the project this new project is based on
292 * @return boolean success or not
294 function create(&$user, $group_name, $unix_name, $description, $purpose, $unix_box = 'shell1',
295 $scm_box = 'cvs1', $is_public = 1, $send_mail = true, $built_from_template = 0) {
296 // $user is ignored - anyone can create pending group
299 if ($this->getID()!=0) {
300 $this->setError(_('Group::create: Group object already exists'));
302 } elseif (!$this->validateGroupName($group_name)) {
304 } elseif (!account_groupnamevalid($unix_name)) {
305 $this->setError(_('Invalid Unix name'));
307 } elseif (!$SYS->sysUseUnixName($unix_name)) {
308 $this->setError(_('Unix name already taken'));
310 } elseif (db_numrows(db_query_params('SELECT group_id FROM groups WHERE unix_group_name=$1',
311 array($unix_name))) > 0) {
312 $this->setError(_('Unix name already taken'));
314 } elseif (strlen($purpose)<10) {
315 $this->setError(_('Please describe your Registration Purpose in a more comprehensive manner'));
317 } elseif (strlen($purpose)>1500) {
318 $this->setError(_('The Registration Purpose text is too long. Please make it smaller than 1500 bytes.'));
320 } elseif (strlen($description)<10) {
321 $this->setError(_('Describe in a more comprehensive manner your project.'));
325 // Check if sys_use_project_vhost for homepage
326 if (forge_get_config('use_project_vhost')) {
327 $homepage = $unix_name.".".forge_get_config('web_host');
329 $homepage = forge_get_config('web_host')."/www/".$unix_name."/";
334 $res = db_query_params('
349 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)',
350 array (htmlspecialchars ($group_name),
352 htmlspecialchars($description),
358 htmlspecialchars($purpose),
360 md5(util_randbytes()),
361 $built_from_template));
362 if (!$res || db_affected_rows($res) < 1) {
363 $this->setError(sprintf(_('ERROR: Could not create group: %s'),db_error()));
368 $id = db_insertid($res, 'groups', 'group_id');
370 $this->setError(sprintf(_('ERROR: Could not get group id: %s'),db_error()));
375 if (!$this->fetchData($id)) {
380 $gjr = new GroupJoinRequest($this);
381 $gjr->create($user->getID(),
382 'Fake GroupJoinRequest to store the creator of a project',
385 $hook_params = array();
386 $hook_params['group'] = $this;
387 $hook_params['group_id'] = $this->getID();
388 $hook_params['group_name'] = $group_name;
389 $hook_params['unix_group_name'] = $unix_name;
390 plugin_hook("group_create", $hook_params);
394 $this->sendNewProjectNotificationEmail();
402 * updateAdmin - Update core properties of group object.
404 * This function require site admin privilege.
406 * @param object User requesting operation (for access control).
407 * @param boolean Whether group is publicly accessible (0/1).
408 * @param int Group type (1-project, 2-foundry).
409 * @param string Machine on which group's home directory located.
410 * @param string Domain which serves group's WWW.
414 function updateAdmin(&$user, $is_public, $type_id, $unix_box, $http_domain) {
415 $perm =& $this->getPermission();
417 if (!$perm || !is_object($perm)) {
418 $this->setError(_('Could not get permission.'));
422 if (!$perm->isSuperUser()) {
423 $this->setError(_('Permission denied.'));
429 $res = db_query_params('
431 SET type_id=$1, unix_box=$2, http_domain=$3
438 if (!$res || db_affected_rows($res) < 1) {
439 $this->setError(_('ERROR: DB: Could not change group properties: %s'),db_error());
444 // Log the audit trail
445 if ($type_id != $this->data_array['type_id']) {
446 $this->addHistory('type_id', $this->data_array['type_id']);
448 if ($unix_box != $this->data_array['unix_box']) {
449 $this->addHistory('unix_box', $this->data_array['unix_box']);
451 if ($http_domain != $this->data_array['http_domain']) {
452 $this->addHistory('http_domain', $this->data_array['http_domain']);
455 if (!$this->fetchData($this->getID())) {
464 * update - Update number of common properties.
466 * Unlike updateAdmin(), this function accessible to project admin.
468 * @param object User requesting operation (for access control).
469 * @param boolean Whether group is publicly accessible (0/1).
470 * @param string Project's license (string ident).
471 * @param int Group type (1-project, 2-foundry).
472 * @param string Machine on which group's home directory located.
473 * @param string Domain which serves group's WWW.
474 * @return int status.
477 function update(&$user, $group_name, $homepage, $short_description, $use_mail, $use_survey, $use_forum,
478 $use_pm, $use_pm_depend_box, $use_scm, $use_news, $use_docman,
479 $new_doc_address, $send_all_docs, $logo_image_id,
480 $use_ftp, $use_tracker, $use_frs, $use_stats, $tags, $use_activity, $is_public) {
482 $perm =& $this->getPermission();
484 if (!$perm || !is_object($perm)) {
485 $this->setError(_('Could not get permission.'));
489 if (!$perm->isAdmin()) {
490 $this->setError(_('Permission denied.'));
494 // Validate some values
495 if ($this->getPublicName() != $group_name) {
496 if (!$this->validateGroupName($group_name)) {
501 if ($new_doc_address) {
502 $invalid_mails = validate_emails($new_doc_address);
503 if (count($invalid_mails) > 0) {
504 $this->setError(sprintf(ngettext('New Doc Address Appeared Invalid: %s', 'New Doc Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
509 // in the database, these all default to '1',
510 // so we have to explicitly set 0
523 if (!$use_pm_depend_box) {
524 $use_pm_depend_box = 0;
547 if (!$use_activity) {
550 if (!$send_all_docs) {
554 $homepage = ltrim($homepage);
556 $homepage = util_make_url('/projects/' . $this->getUnixName() . '/');
559 if (strlen(htmlspecialchars($short_description))<10) {
560 $this->setError(_('Describe in a more comprehensive manner your project.'));
566 //XXX not yet actived logo_image_id='$logo_image_id',
567 $res = db_query_params('UPDATE groups
570 short_description=$3,
575 use_pm_depend_box=$8,
586 array(htmlspecialchars($group_name),
588 htmlspecialchars($short_description),
605 if (!$res || db_affected_rows($res) < 1) {
606 $this->setError(sprintf(_('Error updating project information: %s'), db_error()));
611 if (!$this->setUseDocman($use_docman)) {
612 $this->setError(sprintf(_('Error updating project information: use_docman %s'), db_error()));
617 if ($this->setTags($tags) === false) {
622 $hook_params = array();
623 $hook_params['group'] = $this;
624 $hook_params['group_id'] = $this->getID();
625 $hook_params['group_homepage'] = $homepage;
626 $hook_params['group_name'] = htmlspecialchars($group_name);
627 $hook_params['group_description'] = htmlspecialchars($short_description);
628 $hook_params['group_ispublic'] = $is_public;
629 if (!plugin_hook("group_update", $hook_params)) {
630 if (!$this->isError()) {
631 $this->setError(_('Error updating project information in plugin_hook group_update'));
637 // Log the audit trail
638 $this->addHistory('Changed Public Info', '');
640 if (!$this->fetchData($this->getID())) {
649 * getID - Simply return the group_id for this object.
651 * @return int group_id.
654 return $this->data_array['group_id'];
658 * getType() - Foundry, project, etc.
660 * @return int The type flag from the database.
663 return $this->data_array['type_id'];
668 * getStatus - the status code.
670 * Statuses char include I,H,A,D,P.
671 * TODO : document what these mean :
678 function getStatus() {
679 return $this->data_array['status'];
683 * setStatus - set the status code.
685 * Statuses include I,H,A,D,P.
686 * TODO : document what these mean :
693 * @param object User requesting operation (for access control).
694 * @param string Status value.
695 * @return boolean success.
698 function setStatus(&$user, $status) {
701 if (!forge_check_global_perm('approve_projects')) {
702 $this->setPermissionDeniedError();
706 // Projects in 'A' status can only go to 'H' or 'D'
707 // Projects in 'D' status can only go to 'A'
708 // Projects in 'P' status can only go to 'A' OR 'D'
709 // Projects in 'I' status can only go to 'P'
710 // Projects in 'H' status can only go to 'A' OR 'D'
711 $allowed_status_changes = array(
712 'AH'=>1,'AD'=>1,'DA'=>1,'PA'=>1,'PD'=>1,
713 'IP'=>1,'HA'=>1,'HD'=>1
716 // Check that status transition is valid
717 if ($this->getStatus() != $status
718 && !array_key_exists($this->getStatus(). $status, $allowed_status_changes)) {
719 $this->setError(_('Invalid Status Change From: ').$this->getStatus(). _(' To: '.$status));
725 $res = db_query_params('UPDATE groups
727 WHERE group_id=$2', array($status, $this->getID()));
729 if (!$res || db_affected_rows($res) < 1) {
730 $this->setError(sprintf(_('ERROR: DB: Could not change group status: %s'),db_error()));
736 // Activate system group, if not yet
737 if (!$SYS->sysCheckGroup($this->getID())) {
738 if (!$SYS->sysCreateGroup($this->getID())) {
739 $this->setError($SYS->getErrorMessage());
744 if (!$this->activateUsers()) {
749 /* Otherwise, the group is not active, and make sure that
750 System group is not active either */
751 } elseif ($SYS->sysCheckGroup($this->getID())) {
752 if (!$SYS->sysRemoveGroup($this->getID())) {
753 $this->setError($SYS->getErrorMessage());
759 $hook_params = array();
760 $hook_params['group'] = $this;
761 $hook_params['group_id'] = $this->getID();
762 $hook_params['status'] = $status;
763 plugin_hook("group_setstatus", $hook_params);
767 // Log the audit trail
768 if ($status != $this->getStatus()) {
769 $this->addHistory('Status', $this->getStatus());
772 $this->data_array['status'] = $status;
777 * isProject - Simple boolean test to see if it's a project or not.
779 * @return boolean is_project.
781 function isProject() {
782 if ($this->getType()==1) {
790 * isPublic - Wrapper around RBAC to check if a project is anonymously readable
792 * @return boolean is_public.
794 function isPublic() {
795 $ra = RoleAnonymous::getInstance() ;
796 return $ra->hasPermission('project_read', $this->getID());
800 * isActive - Database field status of 'A' returns true.
802 * @return boolean is_active.
804 function isActive() {
805 if ($this->getStatus()=='A') {
813 * isTemplate - Simply returns the is_template flag from the database.
815 * @return boolean is_template.
817 function isTemplate() {
818 return $this->data_array['is_template'];
822 * setAsTemplate - Set the template status of a project
824 * @param boolean is_template.
826 function setAsTemplate($booleanparam) {
828 $booleanparam = $booleanparam ? 1 : 0;
829 $res = db_query_params('UPDATE groups SET is_template=$1 WHERE group_id=$2',
830 array($booleanparam, $this->getID()));
832 $this->data_array['is_template']=$booleanparam;
842 * getTemplateProject - Return the project template this project is built from
844 * @return object The template project
846 function getTemplateProject() {
847 return group_get_object($this->data_array['built_from_template']);
851 * getUnixName - the unix_name
853 * @return string unix_name.
855 function getUnixName() {
856 return strtolower($this->data_array['unix_group_name']);
860 * getPublicName - the full-length public name.
862 * @return string The group_name.
864 function getPublicName() {
865 return $this->data_array['group_name'];
869 * getRegisterPurpose - the text description of the purpose of this project.
871 * @return string The description.
873 function getRegisterPurpose() {
874 return $this->data_array['register_purpose'];
878 * getDescription - the text description of this project.
880 * @return string The description.
882 function getDescription() {
883 return $this->data_array['short_description'];
887 * getStartDate - the unix time this project was registered.
889 * @return int (unix time) of registration.
891 function getStartDate() {
892 return $this->data_array['register_time'];
896 * getLogoImageID - the id of the logo in the database for this project.
898 * @return int The ID of logo image in db_images table (or 100 if none).
900 function getLogoImageID() {
901 return $this->data_array['logo_image_id'];
905 * getUnixBox - the hostname of the unix box where this project is located.
907 * @return string The name of the unix machine for the group.
909 function getUnixBox() {
910 return $this->data_array['unix_box'];
914 * getSCMBox - the hostname of the scm box where this project is located.
916 * @return string The name of the unix machine for the group.
918 function getSCMBox() {
919 return $this->data_array['scm_box'];
922 * setSCMBox - the hostname of the scm box where this project is located.
924 * @param string The name of the new SCM_BOX
926 function setSCMBox($scm_box) {
928 if ($scm_box == $this->data_array['scm_box']) {
933 $res = db_query_params('UPDATE groups SET scm_box=$1 WHERE group_id=$2', array($scm_box, $this->getID ()));
935 $this->addHistory('scm_box', $this->data_array['scm_box']);
936 $this->data_array['scm_box'] = $scm_box;
941 $this->setError(_("Couldn't insert SCM_BOX to database"));
945 $this->setError(_("SCM Box can't be empty"));
951 * getDomain - the hostname.domain where their web page is located.
953 * @return string The name of the group [web] domain.
955 function getDomain() {
956 return $this->data_array['http_domain'];
960 * getRegistrationPurpose - the text description of the purpose of this project.
962 * @return string The application for project hosting.
964 function getRegistrationPurpose() {
965 return $this->data_array['register_purpose'];
970 * getAdmins() - Get array of Admin user objects.
972 * @return array Array of User objects.
974 function &getAdmins() {
975 $roles = RBACEngine::getInstance()->getRolesByAllowedAction ('project_admin', $this->getID());
979 foreach ($roles as $role) {
980 if (! ($role instanceof RoleExplicit)) {
983 if ($role->getHomeProject() == NULL
984 || $role->getHomeProject()->getID() != $this->getID()) {
988 foreach ($role->getUsers() as $u) {
989 $user_ids[] = $u->getID();
992 return user_get_objects(array_unique($user_ids));
996 Common Group preferences for tools
1000 * enableAnonSCM - whether or not this group has opted to enable Anonymous SCM.
1002 * @return boolean enable_scm.
1004 function enableAnonSCM() {
1005 $r = RoleAnonymous::getInstance();
1006 return $r->hasPermission('scm', $this->getID(), 'read');
1009 function SetUsesAnonSCM($booleanparam) {
1011 $booleanparam = $booleanparam ? 1 : 0;
1012 $r = RoleAnonymous::getInstance();
1013 $r->setSetting('scm', $this->getID(), $booleanparam);
1018 * enablePserver - whether or not this group has opted to enable Pserver.
1020 * @return boolean enable_pserver.
1022 function enablePserver() {
1023 if ($this->usesSCM()) {
1024 return $this->data_array['enable_pserver'];
1030 function SetUsesPserver($booleanparam) {
1032 $booleanparam = $booleanparam ? 1 : 0;
1033 $res = db_query_params('UPDATE groups SET enable_pserver=$1 WHERE group_id=$2',
1034 array($booleanparam, $this->getID()));
1036 $this->data_array['enable_pserver'] = $booleanparam;
1045 * usesSCM - whether or not this group has opted to use SCM.
1047 * @return boolean uses_scm.
1049 function usesSCM() {
1050 if (forge_get_config('use_scm')) {
1051 return $this->data_array['use_scm'];
1058 * setUseSCM - Set the SCM usage
1060 * @param boolean enabled/disabled
1062 function setUseSCM($booleanparam) {
1064 $booleanparam = $booleanparam ? 1 : 0 ;
1065 $res = db_query_params('UPDATE groups SET use_scm=$1 WHERE group_id=$2',
1066 array($booleanparam, $this->getID()));
1068 $this->data_array['use_scm']=$booleanparam;
1078 * usesMail - whether or not this group has opted to use mailing lists.
1080 * @return boolean uses_mail.
1082 function usesMail() {
1083 if (forge_get_config('use_mail')) {
1084 return $this->data_array['use_mail'];
1091 * setUseMail - Set the mailing-list usage
1093 * @param boolean enabled/disabled
1095 function setUseMail($booleanparam) {
1097 $booleanparam = $booleanparam ? 1 : 0 ;
1098 $res = db_query_params('UPDATE groups SET use_mail=$1 WHERE group_id=$2',
1099 array($booleanparam, $this->getID()));
1101 $this->data_array['use_mail']=$booleanparam;
1111 * usesNews - whether or not this group has opted to use news.
1113 * @return boolean uses_news.
1115 function usesNews() {
1116 if (forge_get_config('use_news')) {
1117 return $this->data_array['use_news'];
1124 * usesActivity - whether or not this group has opted to display Project Activities.
1126 * @return boolean uses_activities.
1128 function usesActivity() {
1129 if (forge_get_config('use_activity')) {
1130 return $this->data_array['use_activity'];
1137 * usesForum - whether or not this group has opted to use discussion forums.
1139 * @return boolean uses_forum.
1141 function usesForum() {
1142 if (forge_get_config('use_forum')) {
1143 return $this->data_array['use_forum'];
1150 * setUseForum - Set the forum usage
1152 * @param boolean enabled/disabled
1154 function setUseForum($booleanparam) {
1156 $booleanparam = $booleanparam ? 1 : 0;
1157 $res = db_query_params('UPDATE groups SET use_forum=$1 WHERE group_id=$2',
1158 array($booleanparam, $this->getID()));
1160 $this->data_array['use_forum']=$booleanparam;
1170 * usesStats - whether or not this group has opted to use stats.
1172 * @return boolean uses_stats.
1174 function usesStats() {
1175 return $this->data_array['use_stats'];
1179 * usesFRS - whether or not this group has opted to use file release system.
1181 * @return boolean uses_frs.
1183 function usesFRS() {
1184 if (forge_get_config('use_frs')) {
1185 return $this->data_array['use_frs'];
1192 * setUseFRS - Set the FRS usage
1194 * @param boolean enabled/disabled
1196 function setUseFRS($booleanparam) {
1198 $booleanparam = $booleanparam ? 1 : 0;
1199 $res = db_query_params('UPDATE groups SET use_frs=$1 WHERE group_id=$2',
1200 array ($booleanparam, $this->getID()));
1202 $this->data_array['use_frs']=$booleanparam;
1212 * usesTracker - whether or not this group has opted to use tracker.
1214 * @return boolean uses_tracker.
1216 function usesTracker() {
1217 if (forge_get_config('use_tracker')) {
1218 return $this->data_array['use_tracker'];
1225 * setUseTracker - Set the tracker usage
1227 * @param boolean enabled/disabled
1229 function setUseTracker ($booleanparam) {
1231 $booleanparam = $booleanparam ? 1 : 0 ;
1232 $res = db_query_params ('UPDATE groups SET use_tracker=$1 WHERE group_id=$2',
1233 array ($booleanparam, $this->getID()));
1235 $this->data_array['use_tracker']=$booleanparam;
1245 * useCreateOnline - whether or not this group has opted to use create online documents option.
1247 * @return boolean use_docman_create_online.
1249 function useCreateOnline() {
1250 if (forge_get_config('use_docman')) {
1251 return $this->data_array['use_docman_create_online'];
1258 * usesDocman - whether or not this group has opted to use docman.
1260 * @return boolean use_docman.
1262 function usesDocman() {
1263 if (forge_get_config('use_docman')) {
1264 return $this->data_array['use_docman'];
1271 * setUseDocman - Set the docman usage
1273 * @param boolean enabled/disabled
1275 function setUseDocman($booleanparam) {
1277 $booleanparam = $booleanparam ? 1 : 0;
1278 $res = db_query_params('UPDATE groups SET use_docman = $1 WHERE group_id = $2',
1279 array($booleanparam, $this->getID()));
1281 // check if / doc_group exists, if not create it
1282 $trashdir = db_query_params('select groupname from doc_groups where groupname = $1 and group_id = $2',
1283 array('.trash', $this->getID()));
1284 if ($trashdir && db_numrows($trashdir) == 0) {
1285 $resinsert = db_query_params('insert into doc_groups (groupname, group_id, stateid) values ($1, $2, $3)',
1286 array('.trash', $this->getID(), '2'));
1292 $this->data_array['use_docman'] = $booleanparam;
1302 * useDocmanSearch - whether or not this group has opted to use docman search engine.
1304 * @return boolean use_docman_search.
1306 function useDocmanSearch() {
1307 if (forge_get_config('use_docman')) {
1308 return $this->data_array['use_docman_search'];
1315 * useWebdav - whether or not this group has opted to use webdav interface.
1317 * @return boolean use_docman_search.
1319 function useWebdav() {
1320 if (forge_get_config('use_webdav')) {
1321 return $this->data_array['use_webdav'];
1328 * usesFTP - whether or not this group has opted to use FTP.
1330 * @return boolean uses_ftp.
1332 function usesFTP() {
1333 if (forge_get_config('use_ftp')) {
1334 return $this->data_array['use_ftp'];
1341 * usesSurvey - whether or not this group has opted to use surveys.
1343 * @return boolean uses_survey.
1345 function usesSurvey() {
1346 if (forge_get_config('use_survey')) {
1347 return $this->data_array['use_survey'];
1354 * usesPM - whether or not this group has opted to Project Manager.
1356 * @return boolean uses_projman.
1359 if (forge_get_config('use_pm')) {
1360 return $this->data_array['use_pm'];
1367 * setUsePM - Set the PM usage
1369 * @param boolean enabled/disabled
1371 function setUsePM($booleanparam) {
1373 $booleanparam = $booleanparam ? 1 : 0;
1374 $res = db_query_params('UPDATE groups SET use_pm=$1 WHERE group_id=$2',
1375 array($booleanparam, $this->getID()));
1377 $this->data_array['use_pm']=$booleanparam;
1387 * getPlugins - get a list of all available group plugins
1389 * @return array array containing plugin_id => plugin_name
1391 function getPlugins() {
1392 if (!isset($this->plugins_data)) {
1393 $this->plugins_data = array();
1394 $res = db_query_params('SELECT group_plugin.plugin_id, plugins.plugin_name
1395 FROM group_plugin, plugins
1396 WHERE group_plugin.group_id=$1
1397 AND group_plugin.plugin_id=plugins.plugin_id', array($this->getID()));
1398 $rows = db_numrows($res);
1400 for ($i=0; $i<$rows; $i++) {
1401 $plugin_id = db_result($res, $i, 'plugin_id');
1402 $this->plugins_data[$plugin_id] = db_result($res, $i, 'plugin_name');
1405 return $this->plugins_data;
1409 * usesPlugin - returns true if the group uses a particular plugin
1411 * @param string $pluginname name of the plugin
1412 * @return boolean whether plugin is being used or not
1414 function usesPlugin($pluginname) {
1415 $plugins_data = $this->getPlugins();
1416 foreach ($plugins_data as $p_id => $p_name) {
1417 if ($p_name == $pluginname) {
1425 * added for Codendi compatibility
1426 * usesServices - returns true if the group uses a particular plugin or feature
1428 * @param string name of the plugin
1429 * @return boolean whether plugin is being used or not
1431 function usesService($feature) {
1432 $plugins_data = $this->getPlugins();
1433 $pm = plugin_manager_get_object();
1434 foreach ($plugins_data as $p_id => $p_name) {
1435 if ($p_name == $feature) {
1438 if ($pm->getPluginByName($p_name)->provide($feature)) {
1446 * setPluginUse - enables/disables plugins for the group
1448 * @param string name of the plugin
1449 * @param boolean the new state
1450 * @return string database result
1452 function setPluginUse($pluginname, $val=true) {
1453 if ($val == $this->usesPlugin($pluginname)) {
1454 // State is already good, returning
1457 $res = db_query_params('SELECT plugin_id FROM plugins WHERE plugin_name=$1',
1458 array($pluginname));
1459 $rows = db_numrows($res);
1461 // Error: no plugin by that name
1464 $plugin_id = db_result($res,0,'plugin_id');
1466 unset($this->plugins_data);
1468 $res = db_query_params('INSERT INTO group_plugin (group_id, plugin_id) VALUES ($1, $2)',
1469 array($this->getID(),
1473 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1 AND plugin_id=$2',
1474 array($this->getID(),
1478 $this->normalizeAllRoles () ;
1482 * getDocEmailAddress - get email address(es) to send doc notifications to.
1484 * @return string email address.
1486 function getDocEmailAddress() {
1487 return $this->data_array['new_doc_address'];
1491 * DocEmailAll - whether or not this group has opted to use receive notices on all doc updates.
1493 * @return boolean email_on_all_doc_updates.
1495 function docEmailAll() {
1496 return $this->data_array['send_all_docs'];
1501 * getHomePage - The URL for this project's home page.
1503 * @return string homepage URL.
1505 function getHomePage() {
1506 if (!preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*:/",
1507 $this->data_array['homepage'])) {
1508 $this->data_array['homepage'] = util_url_prefix() .
1509 $this->data_array['homepage'];
1511 return $this->data_array['homepage'];
1515 * getTags - Tags of this project.
1517 * @return string List of tags. Comma separated
1519 function getTags() {
1520 $sql = 'SELECT name FROM project_tags WHERE group_id = $1';
1521 $res = db_query_params($sql, array($this->getID()));
1522 return join(', ', util_result_column_to_array($res));
1526 * setTags - Set tags of this project.
1528 * @return string database result.
1530 function setTags($tags) {
1532 $sql = 'DELETE FROM project_tags WHERE group_id=$1';
1533 $res = db_query_params($sql, array($this->getID()));
1535 $this->setError('Deleting old tags: '.db_error());
1539 $inserted = array();
1540 $tags_array = preg_split('/[;,]/', $tags);
1541 foreach ($tags_array as $tag) {
1542 $tag = preg_replace('/[\t\r\n]/', ' ', $tag);
1543 // Allowed caracteres: [A-Z][a-z][0-9] -_&'#+.
1544 if (preg_match('/[^[:alnum:]| |\-|_|\&|\'|#|\+|\.]/', $tag)) {
1545 $this->setError(_('Bad tag name, you only can use the following characters: [A-Z][a-z][0-9]-_&\'#+. and space'));
1550 if ($tag == '' || array_search($tag, $inserted) !== false) continue;
1551 $sql = 'INSERT INTO project_tags (group_id,name) VALUES ($1, $2)';
1552 $res = db_query_params($sql, array($this->getID(), $tag));
1554 $this->setError(_('Setting tags:') . ' ' .
1566 * getPermission - Return a Permission for this Group
1568 * @return object The Permission.
1570 function &getPermission() {
1571 return permission_get_object($this);
1575 function delete($sure, $really_sure, $really_really_sure) {
1576 if (!$sure || !$really_sure || !$really_really_sure) {
1577 $this->setMissingParamsError(_('Please tick all checkboxes.'));
1580 if ($this->getID() == forge_get_config('news_group') ||
1581 $this->getID() == 1 ||
1582 $this->getID() == forge_get_config('stats_group') ||
1583 $this->getID() == forge_get_config('peer_rating_group')) {
1584 $this->setError(_('Cannot Delete System Group'));
1587 $perm = $this->getPermission();
1588 if (!$perm || !is_object($perm)) {
1589 $this->setPermissionDeniedError();
1591 } elseif ($perm->isError()) {
1592 $this->setPermissionDeniedError();
1594 } elseif (!$perm->isSuperUser()) {
1595 $this->setPermissionDeniedError();
1601 // Remove all the members
1603 $members = $this->getMembers();
1604 foreach ($members as $i) {
1605 if(!$this->removeUser($i->getID())) {
1606 $this->setError(_('Could not properly remove member:').' '.$i->getID());
1611 // unlink roles from this project
1612 foreach ($this->getRoles() as $r) {
1613 if ($r->getHomeProject() == NULL
1614 || $r->getHomeProject()->getID() != $this->getID()) {
1615 $r->unlinkProject($this);
1622 if ($this->usesTracker()) {
1623 $atf = new ArtifactTypeFactory($this);
1624 $at_arr = $atf->getArtifactTypes();
1625 foreach ($at_arr as $i) {
1626 if (!is_object($i)) {
1629 if (!$i->delete(1,1)) {
1630 $this->setError(_('Could not properly delete the tracker:').' '.$i->getErrorMessage());
1639 if ($this->usesForum()) {
1640 $ff = new ForumFactory($this);
1641 $f_arr = $ff->getForums();
1642 foreach ($f_arr as $i) {
1643 if (!is_object($i)) {
1646 if(!$i->delete(1,1)) {
1647 $this->setError(_('Could not properly delete the forum:').' '.$i->getErrorMessage());
1653 // Delete Subprojects
1655 if ($this->usesPM()) {
1656 $pgf = new ProjectGroupFactory($this);
1657 $pg_arr = $pgf->getProjectGroups();
1658 foreach ($pg_arr as $i) {
1659 if (!is_object($i)) {
1662 if (!$i->delete(1,1)) {
1663 $this->setError(_('Could not properly delete the ProjectGroup:').' '.$i->getErrorMessage());
1669 // Delete FRS Packages
1671 $res = db_query_params('SELECT * FROM frs_package WHERE group_id=$1',
1672 array($this->getID()));
1674 $this->setError(_('Error FRS Packages: ').db_error());
1679 while ($arr = db_fetch_array($res)) {
1680 $frsp=new FRSPackage($this, $arr['package_id'], $arr);
1681 if (!$frsp->delete(1, 1)) {
1682 $this->setError(_('Could not properly delete the FRSPackage:').' '.$frsp->getErrorMessage());
1689 $news_group=group_get_object(forge_get_config('news_group'));
1690 $res = db_query_params('SELECT forum_id FROM news_bytes WHERE group_id=$1',
1691 array($this->getID()));
1693 $this->setError(_('Error Deleting News: ').db_error());
1698 for ($i=0; $i<db_numrows($res); $i++) {
1699 $Forum = new Forum($news_group,db_result($res,$i,'forum_id'));
1700 if (!$Forum->delete(1,1)) {
1701 $this->setError(_("Could Not Delete News Forum: %d"),$Forum->getID());
1705 $res = db_query_params('DELETE FROM news_bytes WHERE group_id=$1',
1706 array($this->getID()));
1708 $this->setError(_('Error Deleting News: ').db_error());
1716 $res = db_query_params('DELETE FROM doc_data WHERE group_id=$1',
1717 array($this->getID()));
1719 $this->setError(_('Error Deleting Documents: ').db_error());
1724 $res = db_query_params('DELETE FROM doc_groups WHERE group_id=$1',
1725 array($this->getID()));
1727 $this->setError(_('Error Deleting Documents: ').db_error());
1735 $res=db_query_params('DELETE FROM project_tags WHERE group_id=$1', array($this->getID()));
1737 $this->setError(_('Error Deleting Tags: ').db_error());
1743 // Delete group history
1745 $res = db_query_params('DELETE FROM group_history WHERE group_id=$1',
1746 array($this->getID()));
1748 $this->setError(_('Error Deleting Project History: ').db_error());
1754 // Delete group plugins
1756 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1',
1757 array($this->getID()));
1759 $this->setError(_('Error Deleting Project Plugins: ').db_error());
1765 // Delete group cvs stats
1767 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE group_id=$1',
1768 array ($this->getID())) ;
1770 $this->setError(_('Error Deleting SCM Statistics: ').db_error());
1778 if ($this->usesSurvey()) {
1779 $sf = new SurveyFactory($this);
1780 $s_arr =& $sf->getSurveys();
1781 foreach ($s_arr as $i) {
1782 if (!is_object($i)) {
1785 if (!$i->delete()) {
1786 $this->setError(_('Could not properly delete the survey'));
1792 // Delete SurveyQuestions
1794 $sqf = new SurveyQuestionFactory($this);
1795 $sq_arr = $sqf->getSurveyQuestions();
1796 if (is_array($sq_arr)) {
1797 foreach ($sq_arr as $i) {
1798 if (!is_object($i)) {
1801 if (!$i->delete()) {
1802 $this->setError(_('Could not properly delete the survey questions'));
1810 // Delete Mailing List Factory
1812 if ($this->usesMail()) {
1813 $mlf = new MailingListFactory($this);
1814 $ml_arr = $mlf->getMailingLists();
1815 foreach ($ml_arr as $i) {
1816 if (!is_object($i)) {
1819 if (!$i->delete(1,1)) {
1820 $this->setError(_('Could not properly delete the mailing list'));
1829 $res = db_query_params('DELETE FROM trove_group_link WHERE group_id=$1',
1830 array($this->getID()));
1832 $this->setError(_('Error Deleting Trove: ').db_error());
1837 $res = db_query_params('DELETE FROM trove_agg WHERE group_id=$1',
1838 array($this->getID()));
1840 $this->setError(_('Error Deleting Trove: ').db_error());
1848 $res = db_query_params('DELETE FROM project_sums_agg WHERE group_id=$1',
1849 array($this->getID()));
1851 $this->setError(_('Error Deleting Counters: ').db_error());
1856 $res = db_query_params('INSERT INTO deleted_groups (unix_group_name, delete_date, isdeleted) VALUES ($1, $2, $3)',
1857 array($this->getUnixName(),
1861 $this->setError(_('Error Deleting Project:').' '.db_error());
1866 // Delete entry in groups.
1867 $res = db_query_params('DELETE FROM groups WHERE group_id=$1',
1868 array($this->getID()));
1870 $this->setError(_('Error Deleting Project:').' '.db_error());
1877 $hook_params = array();
1878 $hook_params['group'] = $this;
1879 $hook_params['group_id'] = $this->getID();
1880 plugin_hook("group_delete", $hook_params);
1882 if (forge_get_config('upload_dir') != '' && $this->getUnixName()) {
1883 exec('/bin/rm -rf '.forge_get_config('upload_dir').'/'.$this->getUnixName().'/');
1885 if (forge_get_config('ftp_upload_dir') != '' && $this->getUnixName()) {
1886 exec('/bin/rm -rf '.forge_get_config('ftp_upload_dir').'/'.$this->getUnixName().'/');
1891 $res = db_query_params('DELETE FROM rep_group_act_monthly WHERE group_id=$1',
1892 array ($this->getID()));
1893 //echo 'rep_group_act_monthly'.db_error();
1894 $res = db_query_params('DELETE FROM rep_group_act_weekly WHERE group_id=$1',
1895 array ($this->getID()));
1896 //echo 'rep_group_act_weekly'.db_error();
1897 $res = db_query_params('DELETE FROM rep_group_act_daily WHERE group_id=$1',
1898 array ($this->getID()));
1899 //echo 'rep_group_act_daily'.db_error();
1900 unset($this->data_array);
1905 Basic functions to add/remove users to/from a group
1906 and update their permissions
1910 * addUser - controls adding a user to a group.
1912 * @param string Unix name of the user to add OR integer user_id.
1913 * @param int The role_id this user should have.
1914 * @return boolean success.
1917 function addUser($user_identifier,$role_id) {
1920 Admins can add users to groups
1923 if (!forge_check_perm ('project_admin', $this->getID())) {
1924 $this->setPermissionDeniedError();
1930 get user id for this user's unix_name
1932 if (is_int ($user_identifier)) { // user_id or user_name
1933 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_id=$1', array ($user_identifier)) ;
1935 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_name=$1', array ($user_identifier)) ;
1937 if (db_numrows($res_newuser) > 0) {
1939 // make sure user is active
1941 if (db_result($res_newuser,0,'status') != 'A') {
1942 $this->setError(_('User is not active. Only active users can be added.'));
1948 // user was found - set new user_id var
1950 $user_id = db_result($res_newuser,0,'user_id');
1952 $role = new Role($this, $role_id);
1953 if (!$role || !is_object($role)) {
1954 $this->setError(_('Error Getting Role Object'));
1957 } elseif ($role->isError()) {
1958 $this->setError('addUser::roleget::'.$role->getErrorMessage());
1963 $role->addUser(user_get_object($user_id)) ;
1964 if (!$SYS->sysCheckCreateGroup($this->getID())){
1965 $this->setError($SYS->getErrorMessage());
1969 if (!$SYS->sysCheckCreateUser($user_id)) {
1970 $this->setError($SYS->getErrorMessage());
1974 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
1975 $this->setError($SYS->getErrorMessage());
1981 // user doesn't exist
1983 $this->setError(_('ERROR: User does not exist'));
1988 $hook_params['group'] = $this;
1989 $hook_params['group_id'] = $this->getID();
1990 $hook_params['user'] = user_get_object($user_id);
1991 $hook_params['user_id'] = $user_id;
1992 plugin_hook ("group_adduser", $hook_params);
1997 $this->addHistory('Added User',$user_identifier);
2003 * removeUser - controls removing a user from a group.
2005 * Users can remove themselves.
2007 * @param int The ID of the user to remove.
2008 * @return boolean success.
2010 function removeUser($user_id) {
2013 if ($user_id != user_getid()
2014 && !forge_check_perm('project_admin', $this->getID())) {
2015 $this->setPermissionDeniedError();
2021 $user = user_get_object($user_id);
2022 $roles = RBACEngine::getInstance()->getAvailableRolesForUser($user);
2024 foreach ($roles as $role) {
2025 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2026 $found_role = $role;
2030 if ($found_role == NULL) {
2031 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2035 $found_role->removeUser($user);
2036 if (!$SYS->sysGroupCheckUser($this->getID(), $user_id)) {
2037 $this->setError($SYS->getErrorMessage());
2043 // reassign open artifacts to id=100
2045 $res = db_query_params('UPDATE artifact SET assigned_to=100
2046 WHERE group_artifact_id
2047 IN (SELECT group_artifact_id
2048 FROM artifact_group_list
2049 WHERE group_id=$1 AND status_id=1 AND assigned_to=$2)',
2050 array($this->getID(),
2053 $this->setError(_('ERROR: DB: artifact:').' '.db_error());
2059 // reassign open tasks to id=100
2060 // first have to purge any assignments that would cause
2061 // conflict with existing assignment to 100
2063 $res = db_query_params('DELETE FROM project_assigned_to
2064 WHERE project_task_id IN (SELECT pt.project_task_id
2065 FROM project_task pt, project_group_list pgl, project_assigned_to pat
2066 WHERE pt.group_project_id = pgl.group_project_id
2067 AND pat.project_task_id=pt.project_task_id
2068 AND pt.status_id=1 AND pgl.group_id=$1
2069 AND pat.assigned_to_id=$2)
2070 AND assigned_to_id=100',
2071 array($this->getID(),
2074 $this->setError(sprintf(_('ERROR: DB: project_assigned_to %d: %s'), 1, db_error()));
2078 $res = db_query_params('UPDATE project_assigned_to SET assigned_to_id=100
2079 WHERE project_task_id IN (SELECT pt.project_task_id
2080 FROM project_task pt, project_group_list pgl
2081 WHERE pt.group_project_id = pgl.group_project_id
2082 AND pt.status_id=1 AND pgl.group_id=$1)
2083 AND assigned_to_id=$2',
2084 array($this->getID(),
2087 $this->setError(sprintf(_('ERROR: DB: project_assigned_to %d: %s'), 2, db_error()));
2093 // Remove user from system
2095 if (!$SYS->sysGroupRemoveUser($this->getID(), $user_id)) {
2096 $this->setError($SYS->getErrorMessage());
2101 $hook_params['group'] = $this;
2102 $hook_params['group_id'] = $this->getID();
2103 $hook_params['user'] = user_get_object($user_id);
2104 $hook_params['user_id'] = $user_id;
2105 plugin_hook ("group_removeuser", $hook_params);
2108 $this->addHistory('Removed User',$user_id);
2115 * updateUser - controls updating a user's role in this group.
2117 * @param int The ID of the user.
2118 * @param int The role_id to set this user to.
2119 * @return boolean success.
2121 function updateUser($user_id,$role_id) {
2123 if (!forge_check_perm ('project_admin', $this->getID())) {
2124 $this->setPermissionDeniedError();
2128 $newrole = RBACEngine::getInstance()->getRoleById ($role_id) ;
2129 if (!$newrole || !is_object($newrole)) {
2130 $this->setError(_('Could Not Get Role'));
2132 } elseif ($newrole->isError()) {
2133 $this->setError(sprintf(_('Role: %s'),$role->getErrorMessage()));
2135 } elseif ($newrole->getHomeProject() == NULL
2136 || $newrole->getHomeProject()->getID() != $this->getID()) {
2137 $this->setError(_('Wrong destination role'));
2140 $user = user_get_object ($user_id) ;
2141 $roles = RBACEngine::getInstance()->getAvailableRolesForUser ($user) ;
2142 $found_role = NULL ;
2143 foreach ($roles as $role) {
2144 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2145 $found_role = $role ;
2149 if ($found_role == NULL) {
2150 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2154 $found_role->removeUser ($user) ;
2155 $newrole->addUser ($user) ;
2157 $this->addHistory('Updated User',$user_id);
2162 * addHistory - Makes an audit trail entry for this project.
2164 * @param string The name of the field.
2165 * @param string The Old Value for this $field_name.
2166 * @return database result handle.
2169 function addHistory($field_name, $old_value) {
2170 return db_query_params ('INSERT INTO group_history(group_id,field_name,old_value,mod_by,adddate)
2171 VALUES ($1,$2,$3,$4,$5)',
2172 array ($this->getID(),
2180 * activateUsers - Make sure that group members have unix accounts.
2182 * Setup unix accounts for group members. Can be called even
2183 * if members are already active.
2187 function activateUsers() {
2189 Activate member(s) of the project
2192 $members = $this->getUsers (true) ;
2194 foreach ($members as $member) {
2196 foreach (RBACEngine::getInstance()->getAvailableRolesForUser ($member) as $role) {
2197 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2200 if (!$this->addUser($member->getUnixName(),$role->getID())) {
2211 * getMembers - returns array of User objects for this project
2213 * @return array of User objects for this group.
2215 function getMembers() {
2216 return $this->getUsers (true) ;
2220 * replaceTemplateStrings - fill-in some blanks with project name
2222 * @param string Template string
2223 * @return string String after replacements
2225 function replaceTemplateStrings($string) {
2226 $string = str_replace ('UNIXNAME', $this->getUnixName(), $string) ;
2227 $string = str_replace ('PUBLICNAME', $this->getPublicName(), $string) ;
2228 $string = str_replace ('DESCRIPTION', $this->getDescription(), $string) ;
2233 * approve - Approve pending project.
2235 * @param object The User object who is doing the updating.
2238 function approve(&$user) {
2239 global $gfcommon,$gfwww;
2240 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
2242 if ($this->getStatus()=='A') {
2243 $this->setError(_("Group already active"));
2249 // Step 1: Activate group and create LDAP entries
2250 if (!$this->setStatus($user, 'A')) {
2255 // Switch to system language for item creation
2256 setup_gettext_from_sys_lang();
2258 // Create default roles
2259 $idadmin_group = NULL;
2260 foreach (get_group_join_requests ($this) as $gjr) {
2261 $idadmin_group = $gjr->getUserID();
2264 if ($idadmin_group == NULL) {
2265 $idadmin_group = $user->getID();
2268 $template = $this->getTemplateProject();
2269 $id_mappings = array();
2270 $seen_admin_role = false;
2272 // Copy roles from template project
2273 foreach($template->getRoles() as $oldrole) {
2274 if ($oldrole->getHomeProject() != NULL) {
2275 $role = new Role($this);
2277 // Need to use a different role name so that the permissions aren't set from the hardcoded defaults
2278 $role->create('TEMPORARY ROLE NAME', $data, true);
2279 $role->setName($oldrole->getName());
2280 if ($oldrole->getSetting ('project_admin', $template->getID())) {
2281 $seen_admin_role = true;
2285 $role->linkProject($this);
2287 $id_mappings['role'][$oldrole->getID()] = $role->getID();
2288 // Reuse the project_admin permission
2289 $role->setSetting ('project_admin', $this->getID(), $oldrole->getSetting ('project_admin', $template->getID())) ;
2293 if (!$seen_admin_role) {
2294 $role = new Role($this);
2295 $adminperms = array ('project_admin' => array ($this->getID() => 1)) ;
2296 $role_id = $role->create ('Admin', $adminperms, true) ;
2299 $roles = $this->getRoles() ;
2300 foreach ($roles as $r) {
2301 if ($r->getHomeProject() == NULL) {
2304 if ($r->getSetting ('project_admin', $this->getID())) {
2305 $r->addUser(user_get_object ($idadmin_group));
2309 // Temporarily switch to the submitter's identity
2310 $saved_session = session_get_user();
2311 session_set_internal($idadmin_group);
2314 if (forge_get_config('use_tracker')) {
2315 $this->setUseTracker ($template->usesTracker());
2316 if ($template->usesTracker()) {
2317 $oldatf = new ArtifactTypeFactory($template);
2318 foreach ($oldatf->getArtifactTypes() as $o) {
2319 $t = new ArtifactType ($this) ;
2320 $t->create ($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->emailAll(),$o->getEmailAddress(),$o->getDuePeriod()/86400,0,$o->getSubmitInstructions(),$o->getBrowseInstructions()) ;
2321 $id_mappings['tracker'][$o->getID()] = $t->getID();
2322 $t->cloneFieldsFrom ($o->getID());
2327 if (forge_get_config('use_pm')) {
2328 $this->setUsePM ($template->usesPM());
2329 if ($template->usesPM()) {
2330 $oldpgf = new ProjectGroupFactory($template);
2331 foreach ($oldpgf->getProjectGroups() as $o) {
2332 $pg = new ProjectGroup($this);
2333 $pg->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo());
2334 $id_mappings['pm'][$o->getID()] = $pg->getID();
2339 if (forge_get_config('use_forum')) {
2340 $this->setUseForum($template->usesForum()) ;
2341 if ($template->usesForum()) {
2342 $oldff = new ForumFactory($template) ;
2343 foreach ($oldff->getForums() as $o) {
2344 $f = new Forum($this);
2345 $f->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo(),1);
2346 $id_mappings['forum'][$o->getID()] = $f->getID();
2351 if (forge_get_config('use_docman')) {
2352 $this->setUseDocman($template->usesDocman());
2353 if ($template->usesDocman()) {
2354 $olddgf = new DocumentGroupFactory($template);
2355 // First pass: create all docgroups
2356 $id_mappings['docman_docgroup'][0] = 0;
2357 foreach ($olddgf->getDocumentGroups() as $o) {
2358 $ndgf = new DocumentGroup($this);
2359 // .trash is a reserved directory
2360 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2361 $ndgf->create($this->replaceTemplateStrings($o->getName()));
2362 $id_mappings['docman_docgroup'][$o->getID()] = $ndgf->getID();
2365 // Second pass: restore hierarchy links
2366 foreach ($olddgf->getDocumentGroups() as $o) {
2367 $ndgf = new DocumentGroup($this);
2368 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2369 $ndgf->fetchData($id_mappings['docman_docgroup'][$o->getID()]);
2370 $ndgf->update($ndgf->getName(), $id_mappings['docman_docgroup'][$o->getParentID()]);
2376 if (forge_get_config('use_frs')) {
2377 $this->setUseFRS ($template->usesFRS());
2378 if ($template->usesFRS()) {
2379 foreach (get_frs_packages($template) as $o) {
2380 $newp = new FRSPackage($this);
2381 $nname = $this->replaceTemplateStrings($o->getName());
2382 $newp->create ($nname, $o->isPublic());
2387 if (forge_get_config('use_mail')) {
2388 $this->setUseMail($template->usesMail()) ;
2389 if ($template->usesMail()) {
2390 $oldmlf = new MailingListFactory($template);
2391 foreach ($oldmlf->getMailingLists() as $o) {
2392 $ml = new MailingList($this);
2393 $nname = preg_replace ('/^'.$template->getUnixName().'-/','',$o->getName()) ;
2395 $ndescription = $this->replaceTemplateStrings($o->getDescription()) ;
2396 $ml->create($nname, $ndescription, $o->isPublic());
2402 /* use SCM plugin from template group */
2403 $this->setUseSCM($template->usesSCM());
2405 foreach ($template->getPlugins() as
2406 $plugin_id => $plugin_name) {
2407 $this->setPluginUse($plugin_name);
2410 /* use SCM choice from registration page */
2412 foreach ($template->getPlugins() as
2413 $plugin_id => $plugin_name) {
2414 if (substr($plugin_name, 3) == 'scm' &&
2415 $plugin_name != 'scmhook') {
2416 /* skip copying scm plugins */
2419 /* enable other plugins though */
2420 $this->setPluginUse($plugin_name);
2424 foreach ($template->getRoles() as $oldrole) {
2425 $newrole = RBACEngine::getInstance()->getRoleById ($id_mappings['role'][$oldrole->getID()]) ;
2426 if ($oldrole->getHomeProject() != NULL
2427 && $oldrole->getHomeProject()->getID() == $template->getID()) {
2428 $newrole->setPublic ($oldrole->isPublic()) ;
2430 $oldsettings = $oldrole->getSettingsForProject ($template) ;
2432 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm') ;
2433 foreach ($sections as $section) {
2434 $newrole->setSetting ($section, $this->getID(), $oldsettings[$section][$template->getID()]) ;
2437 $sections = array ('tracker', 'pm', 'forum') ;
2438 foreach ($sections as $section) {
2439 if (isset ($oldsettings[$section])) {
2440 foreach ($oldsettings[$section] as $k => $v) {
2441 // Only copy perms for tools that have been copied
2442 if (isset ($id_mappings[$section][$k])) {
2443 $newrole->setSetting ($section,
2444 $id_mappings[$section][$k],
2452 $lm = new WidgetLayoutManager();
2453 $lm->createDefaultLayoutForProject ($this->getID(), $template->getID()) ;
2455 $params = array () ;
2456 $params['template'] = $template ;
2457 $params['project'] = $this ;
2458 $params['id_mappings'] = $id_mappings ;
2459 plugin_hook_by_reference ('clone_project_from_template', $params) ;
2461 // Disable everything
2462 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',
2463 array ($this->getID())) ;
2466 $this->normalizeAllRoles();
2467 $this->activateUsers();
2469 // Delete fake join request
2470 foreach (get_group_join_requests ($this) as $gjr) {
2471 $gjr->delete(true) ;
2474 // Switch back to user preference
2475 session_set_internal($saved_session->getID());
2476 setup_gettext_from_context();
2480 $this->sendApprovalEmail();
2481 $this->addHistory('Approved', 'x');
2484 // Plugin can make approve operation there
2486 $params[0] = $idadmin_group;
2487 $params[1] = $this->getID();
2488 plugin_hook('group_approved', $params);
2496 * sendApprovalEmail - Send new project email.
2498 * @return boolean success.
2501 function sendApprovalEmail() {
2502 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('project_admin', $this->getID()) ;
2504 if (count($admins) < 1) {
2505 $this->setError(_("Group does not have any administrators."));
2509 // send one email per admin
2510 foreach ($admins as $admin) {
2511 setup_gettext_for_user ($admin) ;
2513 $message=sprintf(_('Your project registration for %4$s has been approved.
2515 Project Full Name: %1$s
2516 Project Unix Name: %2$s
2518 Your DNS will take up to a day to become active on our site.
2519 Your web site is accessible through your shell account. Please read
2520 site documentation (see link below) about intended usage, available
2521 services, and directory layout of the account.
2524 own project page in %4$s while logged in, you will find
2525 additional menu functions to your left labeled \'Project Admin\'.
2527 We highly suggest that you now visit %4$s and create a public
2528 description for your project. This can be done by visiting your project
2529 page while logged in, and selecting \'Project Admin\' from the menus
2530 on the left (or by visiting %3$s
2533 Your project will also not appear in the Trove Software Map (primary
2534 list of projects hosted on %4$s which offers great flexibility in
2535 browsing and search) until you categorize it in the project administration
2536 screens. So that people can find your project, you should do this now.
2537 Visit your project while logged in, and select \'Project Admin\' from the
2540 Enjoy the system, and please tell others about %4$s. Let us know
2541 if there is anything we can do to help you.
2544 htmlspecialchars_decode($this->getPublicName()),
2545 $this->getUnixName(),
2546 util_make_url ('/project/admin/?group_id='.$this->getID()),
2547 forge_get_config ('forge_name'));
2549 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Approved'), forge_get_config ('forge_name')), $message);
2551 setup_gettext_from_context();
2559 * sendRejectionEmail - Send project rejection email.
2561 * This function sends out a rejection message to a user who
2562 * registered a project.
2564 * @param int The id of the response to use.
2565 * @param string The rejection message.
2566 * @return boolean completion status.
2569 function sendRejectionEmail($response_id, $message="zxcv") {
2570 $submitters = array () ;
2571 foreach (get_group_join_requests ($this) as $gjr) {
2572 $submitters[] = user_get_object($gjr->getUserID());
2575 if (count ($submitters) < 1) {
2576 $this->setError(_("Group does not have any administrators."));
2580 foreach ($submitters as $admin) {
2581 setup_gettext_for_user($admin);
2583 $response=sprintf(_('Your project registration for %3$s has been denied.
2585 Project Full Name: %1$s
2586 Project Unix Name: %2$s
2588 Reasons for negative decision:
2590 '), $this->getPublicName(), $this->getUnixName(), forge_get_config('forge_name'));
2592 // Check to see if they want to send a custom rejection response
2593 if ($response_id == 0) {
2594 $response .= $message;
2596 $response .= db_result(
2597 db_query_params('SELECT response_text FROM canned_responses WHERE response_id=$1', array ($response_id)),
2602 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Denied'), forge_get_config ('forge_name')), $response);
2603 setup_gettext_from_context();
2610 * sendNewProjectNotificationEmail - Send new project notification email.
2612 * This function sends out a notification email to the
2613 * SourceForge admin user when a new project is
2616 * @return boolean success.
2619 function sendNewProjectNotificationEmail() {
2620 // Get the user who wants to register the project
2621 $submitters = array();
2622 foreach (get_group_join_requests ($this) as $gjr) {
2623 $submitters[] = user_get_object($gjr->getUserID());
2625 if (count ($submitters) < 1) {
2626 $this->setError(_("Could not find user who has submitted the project."));
2630 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('approve_projects', -1) ;
2632 if (count($admins) < 1) {
2633 $this->setError(_("There is no administrator to send the mail to."));
2637 foreach ($admins as $admin) {
2638 $admin_email = $admin->getEmail () ;
2639 setup_gettext_for_user ($admin) ;
2641 $message = sprintf(_('New %1$s Project Submitted
2643 Project Full Name: %2$s
2644 Submitted Description: %3$s
2646 forge_get_config ('forge_name'),
2647 htmlspecialchars_decode($this->getPublicName()),
2648 htmlspecialchars_decode($this->getRegistrationPurpose()));
2650 foreach ($submitters as $submitter) {
2651 $message .= sprintf(_('Submitter: %1$s (%2$s)
2653 $submitter->getRealName(),
2654 $submitter->getUnixName());
2657 $message .= sprintf (_('
2658 Please visit the following URL to approve or reject this project:
2660 util_make_url ('/admin/approve-pending.php')) ;
2661 util_send_message($admin_email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2662 setup_gettext_from_context();
2666 $email = $submitter->getEmail() ;
2667 setup_gettext_for_user ($submitter) ;
2669 $message=sprintf(_('New %1$s Project Submitted
2671 Project Full Name: %2$s
2672 Submitted Description: %3$s
2674 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'));
2676 util_send_message($email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2677 setup_gettext_from_context();
2686 * validateGroupName - Validate the group name
2688 * @param string Group name.
2690 * @return boolean an error false and set an error is the group name is invalide otherwise return true
2692 function validateGroupName($group_name) {
2693 if (strlen($group_name)<3) {
2694 $this->setError(_('Group name is too short'));
2696 } elseif (strlen(htmlspecialchars($group_name))>50) {
2697 $this->setError(_('Group name is too long'));
2699 } elseif (group_get_object_by_publicname($group_name)) {
2700 $this->setError(_('Group name already taken'));
2708 * getRolesId - Get Ids of the roles of the group.
2710 * @return array Role ids of this group.
2712 function getRolesId() {
2713 $role_ids = array();
2715 $res = db_query_params('SELECT role_id FROM pfo_role WHERE home_group_id=$1',
2716 array($this->getID()));
2717 while ($arr = db_fetch_array($res)) {
2718 $role_ids[] = $arr['role_id'];
2720 $res = db_query_params('SELECT role_id FROM role_project_refs WHERE group_id=$1',
2721 array($this->getID()));
2722 while ($arr = db_fetch_array($res)) {
2723 $role_ids[] = $arr['role_id'];
2726 return array_unique($role_ids);
2730 * getRoles - Get the roles of the group.
2732 * @return array Roles of this group.
2734 function getRoles() {
2737 $roles = $this->getRolesId();
2738 $engine = RBACEngine::getInstance();
2739 foreach ($roles as $role_id) {
2740 $result[] = $engine->getRoleById ($role_id);
2746 function normalizeAllRoles() {
2747 $roles = $this->getRoles();
2749 foreach ($roles as $r) {
2750 $r->normalizeData();
2755 * getUnixStatus - Status of activation of unix account.
2757 * @return char (N)one, (A)ctive, (S)uspended or (D)eleted
2759 function getUnixStatus() {
2760 return $this->data_array['unix_status'];
2764 * setUnixStatus - Sets status of activation of unix account.
2766 * @param string The unix status.
2772 * @return boolean success.
2774 function setUnixStatus($status) {
2777 $res = db_query_params ('UPDATE groups SET unix_status=$1 WHERE group_id=$2',
2782 $this->setError(sprintf(_('ERROR - Could Not Update Group Unix Status: %s'),db_error()));
2786 if ($status == 'A') {
2787 if (!$SYS->sysCheckCreateGroup($this->getID())) {
2788 $this->setError($SYS->getErrorMessage());
2793 if ($SYS->sysCheckGroup($this->getID())) {
2794 if (!$SYS->sysRemoveGroup($this->getID())) {
2795 $this->setError($SYS->getErrorMessage());
2802 $this->data_array['unix_status']=$status;
2809 * getUsers - Get the users of a group
2811 * @return array of user's objects.
2813 function getUsers($onlylocal = true) {
2814 if (!isset($this->membersArr)) {
2815 $this->membersArr = array () ;
2818 foreach ($this->getRoles() as $role) {
2820 && ($role->getHomeProject() == NULL || $role->getHomeProject()->getID() != $this->getID())) {
2823 foreach ($role->getUsers() as $user) {
2824 $ids[] = $user->getID() ;
2827 $ids = array_unique ($ids) ;
2828 foreach ($ids as $id) {
2829 $u = user_get_object ($id) ;
2830 if ($u->isActive()) {
2831 $this->membersArr[] = $u ;
2835 return $this->membersArr;
2838 function setDocmanCreateOnlineStatus($status) {
2840 /* if we activate search engine, we probably want to reindex */
2841 $res = db_query_params('UPDATE groups SET use_docman_create_online=$1 WHERE group_id=$2',
2842 array($status, $this->getID()));
2845 $this->setError(sprintf(_('ERROR - Could Not Update Group DocmanCreateOnline Status: %s'),db_error()));
2849 $this->data_array['use_docman_create_online']=$status;
2855 function setDocmanWebdav($status) {
2857 /* if we activate search engine, we probably want to reindex */
2858 $res = db_query_params('UPDATE groups SET use_webdav=$1 WHERE group_id=$2',
2863 $this->setError(sprintf(_('ERROR - Could Not Update Group UseWebdab Status: %s'),db_error()));
2867 $this->data_array['use_webdav']=$status;
2873 function setDocmanSearchStatus($status) {
2875 /* if we activate search engine, we probably want to reindex */
2876 $res = db_query_params('UPDATE groups SET use_docman_search=$1, force_docman_reindex=$1 WHERE group_id=$2',
2881 $this->setError(sprintf(_('ERROR - Could Not Update Group UseDocmanSearch Status: %s'),db_error()));
2885 $this->data_array['use_docman_search']=$status;
2891 function setDocmanForceReindexSearch($status) {
2893 /* if we activate search engine, we probably want to reindex */
2894 $res = db_query_params('UPDATE groups SET force_docman_reindex=$1 WHERE group_id=$2',
2899 $this->setError(sprintf(_('ERROR - Could Not Update Group force_docman_reindex %s'),db_error()));
2903 $this->data_array['force_docman_reindex']=$status;
2912 * group_getname() - get the group name
2914 * @param int The group ID
2918 function group_getname ($group_id = 0) {
2919 $grp = group_get_object($group_id);
2921 return $grp->getPublicName();
2928 * group_getunixname() - get the unixname for a group
2930 * @param int The group ID
2934 function group_getunixname ($group_id) {
2935 $grp = group_get_object($group_id);
2937 return $grp->getUnixName();
2944 * group_get_result() - Get the group object result ID.
2946 * @param int The group ID
2950 function &group_get_result($group_id=0) {
2951 $grp = group_get_object($group_id);
2953 return $grp->getData();
2959 function getAllProjectTags($onlyvisible = true) {
2960 $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',
2963 if (!$res || db_numrows($res) == 0) {
2969 while ($arr = db_fetch_array($res)) {
2971 $group_id = $arr[1];
2972 if (!isset($result[$tag])) {
2973 $result[$tag] = array();
2976 if (!$onlyvisible || forge_check_perm('project_read', $group_id)) {
2977 $p = group_get_object($group_id);
2978 $result[$tag][] = array('unix_group_name' => $p->getUnixName(),
2979 'group_id' => $group_id);
2987 * Utility class to compare project based in various criteria (names, unixnames, id, ...)
2990 class ProjectComparator {
2991 var $criterion = 'name' ;
2993 function Compare ($a, $b) {
2994 switch ($this->criterion) {
2997 $namecmp = strcoll ($a->getPublicName(), $b->getPublicName()) ;
2998 if ($namecmp != 0) {
3001 /* If several projects share a same public name */
3002 return strcoll ($a->getUnixName(), $b->getUnixName()) ;
3005 return strcmp ($a->getUnixName(), $b->getUnixName()) ;
3008 $aid = $a->getID() ;
3009 $bid = $b->getID() ;
3013 return ($a < $b) ? -1 : 1;
3019 function sortProjectList (&$list, $criterion='name') {
3020 $cmp = new ProjectComparator () ;
3021 $cmp->criterion = $criterion ;
3023 return usort ($list, array ($cmp, 'Compare')) ;
3028 // c-file-style: "bsd"