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 * Copyright 2013, French Ministry of National Education
11 * http://fusionforge.org
13 * This file is part of FusionForge. FusionForge is free software;
14 * you can redistribute it and/or modify it under the terms of the
15 * GNU General Public License as published by the Free Software
16 * Foundation; either version 2 of the Licence, or (at your option)
19 * FusionForge is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License along
25 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 require_once $gfcommon.'tracker/ArtifactTypes.class.php';
30 require_once $gfcommon.'tracker/ArtifactTypeFactory.class.php';
31 require_once $gfcommon.'forum/Forum.class.php';
32 require_once $gfcommon.'forum/ForumFactory.class.php';
33 require_once $gfcommon.'pm/ProjectGroup.class.php';
34 require_once $gfcommon.'pm/ProjectGroupFactory.class.php';
35 require_once $gfcommon.'include/Role.class.php';
36 require_once $gfcommon.'frs/FRSPackage.class.php';
37 require_once $gfcommon.'docman/DocumentGroup.class.php';
38 require_once $gfcommon.'docman/DocumentGroupFactory.class.php';
39 require_once $gfcommon.'mail/MailingList.class.php';
40 require_once $gfcommon.'mail/MailingListFactory.class.php';
41 require_once $gfcommon.'survey/SurveyFactory.class.php';
42 require_once $gfcommon.'survey/SurveyQuestionFactory.class.php';
43 require_once $gfcommon.'include/gettext.php';
44 require_once $gfcommon.'include/GroupJoinRequest.class.php';
49 * group_get_object() - Get the group object.
51 * group_get_object() is useful so you can pool group objects/save database queries
52 * You should always use this instead of instantiating the object directly.
54 * You can now optionally pass in a db result handle. If you do, it re-uses that query
55 * to instantiate the objects.
57 * IMPORTANT! That db result must contain all fields
58 * from groups table or you will have problems
60 * @param int $group_id Required
61 * @param int|bool $res Result set handle ("SELECT * FROM groups WHERE group_id=xx")
62 * @return Group|bool A group object or false on failure
64 function &group_get_object($group_id, $res = false) {
65 //create a common set of group objects
66 //saves a little wear on the database
68 //automatically checks group_type and
69 //returns appropriate object
72 if (!isset($GROUP_OBJ["_".$group_id."_"])) {
74 //the db result handle was passed in
76 $res = db_query_params('SELECT * FROM groups WHERE group_id=$1', array($group_id)) ;
78 if (!$res || db_numrows($res) < 1) {
79 $GROUP_OBJ["_".$group_id."_"]=false;
82 check group type and set up object
84 if (db_result($res,0,'type_id') == 1) {
86 $GROUP_OBJ["_".$group_id."_"] = new Group($group_id, $res);
89 $GROUP_OBJ["_".$group_id."_"] = false;
93 return $GROUP_OBJ["_".$group_id."_"];
96 function &group_get_objects($id_arr) {
99 // Note: if we don't do this, the result may be corrupted
103 foreach ($id_arr as $id) {
105 // See if this ID already has been fetched in the cache
107 if (!isset($GROUP_OBJ["_".$id."_"])) {
111 if (count($fetch) > 0) {
112 $res=db_query_params('SELECT * FROM groups WHERE group_id = ANY ($1)',
113 array(db_int_array_to_any_clause($fetch)));
114 while ($arr = db_fetch_array($res)) {
115 $GROUP_OBJ["_".$arr['group_id']."_"] = new Group($arr['group_id'],$arr);
118 foreach ($id_arr as $id) {
119 $return[] =& $GROUP_OBJ["_".$id."_"];
124 function &group_get_active_projects() {
125 $res = db_query_params('SELECT group_id FROM groups WHERE status=$1',
127 return group_get_objects(util_result_column_to_array($res,0));
130 function &group_get_template_projects() {
131 $res=db_query_params ('SELECT group_id FROM groups WHERE is_template=1 AND status != $1',
133 return group_get_objects (util_result_column_to_array($res,0)) ;
136 function &group_get_object_by_name($groupname) {
137 $res = db_query_params('SELECT * FROM groups WHERE unix_group_name=$1', array($groupname));
138 return group_get_object(db_result($res, 0, 'group_id'), $res);
141 function &group_get_objects_by_name($groupname_arr) {
142 $res = db_query_params('SELECT group_id FROM groups WHERE unix_group_name = ANY ($1)',
143 array(db_string_array_to_any_clause($groupname_arr)));
144 $arr =& util_result_column_to_array($res,0);
145 return group_get_objects($arr);
148 function &group_get_object_by_publicname($groupname) {
149 $res = db_query_params('SELECT * FROM groups WHERE lower(group_name) LIKE $1',
150 array(htmlspecialchars(strtolower($groupname))));
151 return group_get_object(db_result($res, 0, 'group_id'), $res);
155 * get_public_active_projects_asc() - Get a list of rows for public active projects (initially in trove/full_list)
157 * @param int Opional Maximum number of rows to limit query length
159 function get_public_active_projects_asc($max_query_limit = -1) {
161 $res_grp = db_query_params ('
162 SELECT group_id, group_name, unix_group_name, short_description, register_time
164 WHERE status = $1 AND type_id=1 AND group_id>4 AND register_time > 0
165 ORDER BY group_name ASC
170 while ($row_grp = db_fetch_array($res_grp)) {
171 if (!forge_check_perm ('project_read', $row_grp['group_id'])) {
174 $projects[] = $row_grp;
180 class Group extends Error {
182 * Associative array of data from db.
184 * @var array $data_array.
189 * array of User objects.
191 * @var array $membersArr.
196 * Whether the use is an admin/super user of this project.
198 * @var bool $is_admin.
203 * Artifact types result handle.
205 * @var int $types_res.
210 * Associative array of data for plugins.
212 * @var array $plugins_data.
218 * Associative array of data for the group menu.
220 * @var array $menu_data.
225 * Group - Group object constructor - use group_get_object() to instantiate.
227 * @param int|bool $id Required - Id of the group you want to instantiate.
228 * @param int|bool $res Database result from select query OR associative array of all columns.
229 * @return boolean Success or not
231 function __construct($id = false, $res = false) {
234 //setting up an empty object
235 //probably going to call create()
239 if (!$this->fetchData($id)) {
244 // Assoc array was passed in
246 if (is_array($res)) {
247 $this->data_array =& $res;
249 if (db_numrows($res) < 1) {
250 //function in class we extended
251 $this->setError(_('Group Not Found'));
252 $this->data_array=array();
255 //set up an associative array for use by other functions
256 $this->data_array = db_fetch_array_by_row($res, 0);
264 * fetchData - May need to refresh database fields if an update occurred.
266 * @param int $group_id The group_id.
267 * @return boolean success or not
269 function fetchData($group_id) {
270 $res = db_query_params ('SELECT * FROM groups WHERE group_id=$1',
272 if (!$res || db_numrows($res) < 1) {
273 $this->setError(sprintf(_('fetchData():: %s'),db_error()));
276 $this->data_array = db_fetch_array($res);
281 * create - Create new group.
283 * This method should be called on empty Group object.
284 * It will add an entry for a pending group/project (status 'P')
286 * @param object The User object.
287 * @param string The full name of the user.
288 * @param string The Unix name of the user.
289 * @param string The new group description.
290 * @param string The purpose of the group.
291 * @param boolean Whether to send an email or not
292 * @param int The id of the project this new project is based on
293 * @return boolean success or not
295 function create(&$user, $group_name, $unix_name, $description, $purpose, $unix_box = 'shell1',
296 $scm_box = 'cvs1', $is_public = 1, $send_mail = true, $built_from_template = 0) {
297 // $user is ignored - anyone can create pending group
300 if ($this->getID()!=0) {
301 $this->setError(_('Group::create: Group object already exists'));
303 } elseif (!$this->validateGroupName($group_name)) {
305 } elseif (!account_groupnamevalid($unix_name)) {
306 $this->setError(_('Invalid Unix name'));
308 } elseif (!$SYS->sysUseUnixName($unix_name)) {
309 $this->setError(_('Unix name already taken'));
311 } elseif (db_numrows(db_query_params('SELECT group_id FROM groups WHERE unix_group_name=$1',
312 array($unix_name))) > 0) {
313 $this->setError(_('Unix name already taken'));
315 } elseif (strlen($purpose)<10) {
316 $this->setError(_('Please describe your Registration Purpose in a more comprehensive manner'));
318 } elseif (strlen($purpose)>1500) {
319 $this->setError(_('The Registration Purpose text is too long. Please make it smaller than 1500 bytes.'));
321 } elseif (strlen($description)<10) {
322 $this->setError(_('Describe in a more comprehensive manner your project.'));
326 // Check if sys_use_project_vhost for homepage
327 if (forge_get_config('use_project_vhost')) {
328 $homepage = $unix_name.".".forge_get_config('web_host');
330 $homepage = forge_get_config('web_host')."/www/".$unix_name."/";
335 $res = db_query_params('
350 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)',
351 array (htmlspecialchars ($group_name),
353 htmlspecialchars($description),
359 htmlspecialchars($purpose),
361 md5(util_randbytes()),
362 $built_from_template));
363 if (!$res || db_affected_rows($res) < 1) {
364 $this->setError(sprintf(_('ERROR: Could not create group: %s'),db_error()));
369 $id = db_insertid($res, 'groups', 'group_id');
371 $this->setError(sprintf(_('ERROR: Could not get group id: %s'),db_error()));
376 if (!$this->fetchData($id)) {
381 $gjr = new GroupJoinRequest($this);
382 $gjr->create($user->getID(),
383 'Fake GroupJoinRequest to store the creator of a project',
386 $hook_params = array();
387 $hook_params['group'] = $this;
388 $hook_params['group_id'] = $this->getID();
389 $hook_params['group_name'] = $group_name;
390 $hook_params['unix_group_name'] = $unix_name;
391 plugin_hook("group_create", $hook_params);
395 $this->sendNewProjectNotificationEmail();
403 * updateAdmin - Update core properties of group object.
405 * This function require site admin privilege.
407 * @param object User requesting operation (for access control).
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, $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.
677 function getStatus() {
678 return $this->data_array['status'];
682 * setStatus - set the status code.
684 * Statuses include I,H,A,D,P.
691 * @param object User requesting operation (for access control).
692 * @param string Status value.
693 * @return boolean success.
696 function setStatus(&$user, $status) {
699 if (!forge_check_global_perm_for_user($user, 'approve_projects')) {
700 $this->setPermissionDeniedError();
704 // Projects in 'A' status can only go to 'H' or 'D'
705 // Projects in 'D' status can only go to 'A'
706 // Projects in 'P' status can only go to 'A' OR 'D'
707 // Projects in 'I' status can only go to 'P'
708 // Projects in 'H' status can only go to 'A' OR 'D'
709 $allowed_status_changes = array(
710 'AH'=>1,'AD'=>1,'DA'=>1,'PA'=>1,'PD'=>1,
711 'IP'=>1,'HA'=>1,'HD'=>1
714 // Check that status transition is valid
715 if ($this->getStatus() != $status
716 && !array_key_exists($this->getStatus(). $status, $allowed_status_changes)) {
717 $this->setError(_('Invalid Status Change From: ').$this->getStatus(). _(' To: '.$status));
723 $res = db_query_params('UPDATE groups
725 WHERE group_id=$2', array($status, $this->getID()));
727 if (!$res || db_affected_rows($res) < 1) {
728 $this->setError(sprintf(_('ERROR: DB: Could not change group status: %s'),db_error()));
734 // Activate system group, if not yet
735 if (!$SYS->sysCheckGroup($this->getID())) {
736 if (!$SYS->sysCreateGroup($this->getID())) {
737 $this->setError($SYS->getErrorMessage());
742 if (!$this->activateUsers()) {
747 /* Otherwise, the group is not active, and make sure that
748 System group is not active either */
749 } elseif ($SYS->sysCheckGroup($this->getID())) {
750 if (!$SYS->sysRemoveGroup($this->getID())) {
751 $this->setError($SYS->getErrorMessage());
757 $hook_params = array();
758 $hook_params['group'] = $this;
759 $hook_params['group_id'] = $this->getID();
760 $hook_params['status'] = $status;
761 plugin_hook("group_setstatus", $hook_params);
765 // Log the audit trail
766 if ($status != $this->getStatus()) {
767 $this->addHistory('Status', $this->getStatus());
770 $this->data_array['status'] = $status;
775 * isProject - Simple boolean test to see if it's a project or not.
777 * @return boolean is_project.
779 function isProject() {
780 if ($this->getType()==1) {
788 * isPublic - Wrapper around RBAC to check if a project is anonymously readable
790 * @return boolean is_public.
792 function isPublic() {
793 $ra = RoleAnonymous::getInstance() ;
794 return $ra->hasPermission('project_read', $this->getID());
798 * isActive - Database field status of 'A' returns true.
800 * @return boolean is_active.
802 function isActive() {
803 if ($this->getStatus()=='A') {
811 * isTemplate - Simply returns the is_template flag from the database.
813 * @return boolean is_template.
815 function isTemplate() {
816 return $this->data_array['is_template'];
820 * setAsTemplate - Set the template status of a project
822 * @param boolean is_template.
824 function setAsTemplate($booleanparam) {
826 $booleanparam = $booleanparam ? 1 : 0;
827 $res = db_query_params('UPDATE groups SET is_template=$1 WHERE group_id=$2',
828 array($booleanparam, $this->getID()));
830 $this->data_array['is_template']=$booleanparam;
840 * getTemplateProject - Return the project template this project is built from
842 * @return object The template project
844 function getTemplateProject() {
845 return group_get_object($this->data_array['built_from_template']);
849 * getUnixName - the unix_name
851 * @return string unix_name.
853 function getUnixName() {
854 return strtolower($this->data_array['unix_group_name']);
858 * getPublicName - the full-length public name.
860 * @return string The group_name.
862 function getPublicName() {
863 return $this->data_array['group_name'];
867 * getRegisterPurpose - the text description of the purpose of this project.
869 * @return string The description.
871 function getRegisterPurpose() {
872 return $this->data_array['register_purpose'];
876 * getDescription - the text description of this project.
878 * @return string The description.
880 function getDescription() {
881 return $this->data_array['short_description'];
885 * getStartDate - the unix time this project was registered.
887 * @return int (unix time) of registration.
889 function getStartDate() {
890 return $this->data_array['register_time'];
894 * getLogoImageID - the id of the logo in the database for this project.
896 * @return int The ID of logo image in db_images table (or 100 if none).
898 function getLogoImageID() {
899 return $this->data_array['logo_image_id'];
903 * getUnixBox - the hostname of the unix box where this project is located.
905 * @return string The name of the unix machine for the group.
907 function getUnixBox() {
908 return $this->data_array['unix_box'];
912 * getSCMBox - the hostname of the scm box where this project is located.
914 * @return string The name of the unix machine for the group.
916 function getSCMBox() {
917 return $this->data_array['scm_box'];
920 * setSCMBox - the hostname of the scm box where this project is located.
922 * @param string The name of the new SCM_BOX
924 function setSCMBox($scm_box) {
926 if ($scm_box == $this->data_array['scm_box']) {
931 $res = db_query_params('UPDATE groups SET scm_box=$1 WHERE group_id=$2', array($scm_box, $this->getID ()));
933 $this->addHistory('scm_box', $this->data_array['scm_box']);
934 $this->data_array['scm_box'] = $scm_box;
939 $this->setError(_("Couldn't insert SCM_BOX to database"));
943 $this->setError(_("SCM Box can't be empty"));
949 * getDomain - the hostname.domain where their web page is located.
951 * @return string The name of the group [web] domain.
953 function getDomain() {
954 return $this->data_array['http_domain'];
958 * getRegistrationPurpose - the text description of the purpose of this project.
960 * @return string The application for project hosting.
962 function getRegistrationPurpose() {
963 return $this->data_array['register_purpose'];
968 * getAdmins() - Get array of Admin user objects.
970 * @return array Array of User objects.
972 function &getAdmins() {
973 $roles = RBACEngine::getInstance()->getRolesByAllowedAction ('project_admin', $this->getID());
977 foreach ($roles as $role) {
978 if (! ($role instanceof RoleExplicit)) {
981 if ($role->getHomeProject() == NULL
982 || $role->getHomeProject()->getID() != $this->getID()) {
986 foreach ($role->getUsers() as $u) {
987 $user_ids[] = $u->getID();
990 return user_get_objects(array_unique($user_ids));
994 Common Group preferences for tools
998 * enableAnonSCM - whether or not this group has opted to enable Anonymous SCM.
1000 * @return boolean enable_scm.
1002 function enableAnonSCM() {
1003 $r = RoleAnonymous::getInstance();
1004 return $r->hasPermission('scm', $this->getID(), 'read');
1007 function SetUsesAnonSCM($booleanparam) {
1009 $booleanparam = $booleanparam ? 1 : 0;
1010 $r = RoleAnonymous::getInstance();
1011 $r->setSetting('scm', $this->getID(), $booleanparam);
1016 * enablePserver - whether or not this group has opted to enable Pserver.
1018 * @return boolean enable_pserver.
1020 function enablePserver() {
1021 if ($this->usesSCM()) {
1022 return $this->data_array['enable_pserver'];
1028 function SetUsesPserver($booleanparam) {
1030 $booleanparam = $booleanparam ? 1 : 0;
1031 $res = db_query_params('UPDATE groups SET enable_pserver=$1 WHERE group_id=$2',
1032 array($booleanparam, $this->getID()));
1034 $this->data_array['enable_pserver'] = $booleanparam;
1043 * usesSCM - whether or not this group has opted to use SCM.
1045 * @return boolean uses_scm.
1047 function usesSCM() {
1048 if (forge_get_config('use_scm')) {
1049 return $this->data_array['use_scm'];
1056 * setUseSCM - Set the SCM usage
1058 * @param boolean enabled/disabled
1060 function setUseSCM($booleanparam) {
1062 $booleanparam = $booleanparam ? 1 : 0 ;
1063 $res = db_query_params('UPDATE groups SET use_scm=$1 WHERE group_id=$2',
1064 array($booleanparam, $this->getID()));
1066 $this->data_array['use_scm']=$booleanparam;
1076 * usesMail - whether or not this group has opted to use mailing lists.
1078 * @return boolean uses_mail.
1080 function usesMail() {
1081 if (forge_get_config('use_mail')) {
1082 return $this->data_array['use_mail'];
1089 * setUseMail - Set the mailing-list usage
1091 * @param boolean enabled/disabled
1093 function setUseMail($booleanparam) {
1095 $booleanparam = $booleanparam ? 1 : 0 ;
1096 $res = db_query_params('UPDATE groups SET use_mail=$1 WHERE group_id=$2',
1097 array($booleanparam, $this->getID()));
1099 $this->data_array['use_mail']=$booleanparam;
1109 * usesNews - whether or not this group has opted to use news.
1111 * @return boolean uses_news.
1113 function usesNews() {
1114 if (forge_get_config('use_news')) {
1115 return $this->data_array['use_news'];
1122 * usesActivity - whether or not this group has opted to display Project Activities.
1124 * @return boolean uses_activities.
1126 function usesActivity() {
1127 if (forge_get_config('use_activity')) {
1128 return $this->data_array['use_activity'];
1135 * usesForum - whether or not this group has opted to use discussion forums.
1137 * @return boolean uses_forum.
1139 function usesForum() {
1140 if (forge_get_config('use_forum')) {
1141 return $this->data_array['use_forum'];
1148 * setUseForum - Set the forum usage
1150 * @param boolean enabled/disabled
1152 function setUseForum($booleanparam) {
1154 $booleanparam = $booleanparam ? 1 : 0;
1155 $res = db_query_params('UPDATE groups SET use_forum=$1 WHERE group_id=$2',
1156 array($booleanparam, $this->getID()));
1158 $this->data_array['use_forum']=$booleanparam;
1168 * usesStats - whether or not this group has opted to use stats.
1170 * @return boolean uses_stats.
1172 function usesStats() {
1173 return $this->data_array['use_stats'];
1177 * usesFRS - whether or not this group has opted to use file release system.
1179 * @return boolean uses_frs.
1181 function usesFRS() {
1182 if (forge_get_config('use_frs')) {
1183 return $this->data_array['use_frs'];
1190 * setUseFRS - Set the FRS usage
1192 * @param boolean enabled/disabled
1194 function setUseFRS($booleanparam) {
1196 $booleanparam = $booleanparam ? 1 : 0;
1197 $res = db_query_params('UPDATE groups SET use_frs=$1 WHERE group_id=$2',
1198 array ($booleanparam, $this->getID()));
1200 $this->data_array['use_frs']=$booleanparam;
1210 * usesTracker - whether or not this group has opted to use tracker.
1212 * @return boolean uses_tracker.
1214 function usesTracker() {
1215 if (forge_get_config('use_tracker')) {
1216 return $this->data_array['use_tracker'];
1223 * setUseTracker - Set the tracker usage
1225 * @param boolean enabled/disabled
1227 function setUseTracker ($booleanparam) {
1229 $booleanparam = $booleanparam ? 1 : 0 ;
1230 $res = db_query_params ('UPDATE groups SET use_tracker=$1 WHERE group_id=$2',
1231 array ($booleanparam, $this->getID()));
1233 $this->data_array['use_tracker']=$booleanparam;
1243 * useCreateOnline - whether or not this group has opted to use create online documents option.
1245 * @return boolean use_docman_create_online.
1247 function useCreateOnline() {
1248 if (forge_get_config('use_docman')) {
1249 return $this->data_array['use_docman_create_online'];
1256 * usesDocman - whether or not this group has opted to use docman.
1258 * @return boolean use_docman.
1260 function usesDocman() {
1261 if (forge_get_config('use_docman')) {
1262 return $this->data_array['use_docman'];
1269 * setUseDocman - Set the docman usage
1271 * @param boolean enabled/disabled
1273 function setUseDocman($booleanparam) {
1275 $booleanparam = $booleanparam ? 1 : 0;
1276 $res = db_query_params('UPDATE groups SET use_docman = $1 WHERE group_id = $2',
1277 array($booleanparam, $this->getID()));
1279 // check if / doc_group exists, if not create it
1280 $trashdir = db_query_params('select groupname from doc_groups where groupname = $1 and group_id = $2',
1281 array('.trash', $this->getID()));
1282 if ($trashdir && db_numrows($trashdir) == 0) {
1283 $resinsert = db_query_params('insert into doc_groups (groupname, group_id, stateid) values ($1, $2, $3)',
1284 array('.trash', $this->getID(), '2'));
1290 $this->data_array['use_docman'] = $booleanparam;
1300 * useDocmanSearch - whether or not this group has opted to use docman search engine.
1302 * @return boolean use_docman_search.
1304 function useDocmanSearch() {
1305 if (forge_get_config('use_docman')) {
1306 return $this->data_array['use_docman_search'];
1313 * useWebdav - whether or not this group has opted to use webdav interface.
1315 * @return boolean use_docman_search.
1317 function useWebdav() {
1318 if (forge_get_config('use_webdav')) {
1319 return $this->data_array['use_webdav'];
1326 * usesFTP - whether or not this group has opted to use FTP.
1328 * @return boolean uses_ftp.
1330 function usesFTP() {
1331 if (forge_get_config('use_ftp')) {
1332 return $this->data_array['use_ftp'];
1339 * usesSurvey - whether or not this group has opted to use surveys.
1341 * @return boolean uses_survey.
1343 function usesSurvey() {
1344 if (forge_get_config('use_survey')) {
1345 return $this->data_array['use_survey'];
1352 * usesPM - whether or not this group has opted to Project Manager.
1354 * @return boolean uses_projman.
1357 if (forge_get_config('use_pm')) {
1358 return $this->data_array['use_pm'];
1365 * setUsePM - Set the PM usage
1367 * @param boolean enabled/disabled
1369 function setUsePM($booleanparam) {
1371 $booleanparam = $booleanparam ? 1 : 0;
1372 $res = db_query_params('UPDATE groups SET use_pm=$1 WHERE group_id=$2',
1373 array($booleanparam, $this->getID()));
1375 $this->data_array['use_pm']=$booleanparam;
1385 * getPlugins - get a list of all available group plugins
1387 * @return array array containing plugin_id => plugin_name
1389 function getPlugins() {
1390 if (!isset($this->plugins_data)) {
1391 $this->plugins_data = array();
1392 $res = db_query_params('SELECT group_plugin.plugin_id, plugins.plugin_name
1393 FROM group_plugin, plugins
1394 WHERE group_plugin.group_id=$1
1395 AND group_plugin.plugin_id=plugins.plugin_id', array($this->getID()));
1396 $rows = db_numrows($res);
1398 for ($i=0; $i<$rows; $i++) {
1399 $plugin_id = db_result($res, $i, 'plugin_id');
1400 $this->plugins_data[$plugin_id] = db_result($res, $i, 'plugin_name');
1403 return $this->plugins_data;
1407 * usesPlugin - returns true if the group uses a particular plugin
1409 * @param string $pluginname name of the plugin
1410 * @return boolean whether plugin is being used or not
1412 function usesPlugin($pluginname) {
1413 $plugins_data = $this->getPlugins();
1414 foreach ($plugins_data as $p_id => $p_name) {
1415 if ($p_name == $pluginname) {
1423 * added for Codendi compatibility
1424 * usesServices - returns true if the group uses a particular plugin or feature
1426 * @param string name of the plugin
1427 * @return boolean whether plugin is being used or not
1429 function usesService($feature) {
1430 $plugins_data = $this->getPlugins();
1431 $pm = plugin_manager_get_object();
1432 foreach ($plugins_data as $p_id => $p_name) {
1433 if ($p_name == $feature) {
1436 if ($pm->getPluginByName($p_name)->provide($feature)) {
1444 * setPluginUse - enables/disables plugins for the group
1446 * @param string name of the plugin
1447 * @param boolean the new state
1448 * @return string database result
1450 function setPluginUse($pluginname, $val=true) {
1451 if ($val == $this->usesPlugin($pluginname)) {
1452 // State is already good, returning
1455 $res = db_query_params('SELECT plugin_id FROM plugins WHERE plugin_name=$1',
1456 array($pluginname));
1457 $rows = db_numrows($res);
1459 // Error: no plugin by that name
1462 $plugin_id = db_result($res,0,'plugin_id');
1464 unset($this->plugins_data);
1466 $res = db_query_params('INSERT INTO group_plugin (group_id, plugin_id) VALUES ($1, $2)',
1467 array($this->getID(),
1471 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1 AND plugin_id=$2',
1472 array($this->getID(),
1476 $this->normalizeAllRoles () ;
1480 * getDocEmailAddress - get email address(es) to send doc notifications to.
1482 * @return string email address.
1484 function getDocEmailAddress() {
1485 return $this->data_array['new_doc_address'];
1489 * DocEmailAll - whether or not this group has opted to use receive notices on all doc updates.
1491 * @return boolean email_on_all_doc_updates.
1493 function docEmailAll() {
1494 return $this->data_array['send_all_docs'];
1499 * getHomePage - The URL for this project's home page.
1501 * @return string homepage URL.
1503 function getHomePage() {
1504 if (!preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*:/",
1505 $this->data_array['homepage'])) {
1506 $this->data_array['homepage'] = util_url_prefix() .
1507 $this->data_array['homepage'];
1509 return $this->data_array['homepage'];
1513 * getTags - Tags of this project.
1515 * @return string List of tags. Comma separated
1517 function getTags() {
1518 $sql = 'SELECT name FROM project_tags WHERE group_id = $1';
1519 $res = db_query_params($sql, array($this->getID()));
1520 return join(', ', util_result_column_to_array($res));
1524 * setTags - Set tags of this project.
1526 * @return string database result.
1528 function setTags($tags) {
1530 $sql = 'DELETE FROM project_tags WHERE group_id=$1';
1531 $res = db_query_params($sql, array($this->getID()));
1533 $this->setError('Deleting old tags: '.db_error());
1537 $inserted = array();
1538 $tags_array = preg_split('/[;,]/', $tags);
1539 foreach ($tags_array as $tag) {
1540 $tag = preg_replace('/[\t\r\n]/', ' ', $tag);
1541 // Allowed caracteres: [A-Z][a-z][0-9] -_&'#+.
1542 if (preg_match('/[^[:alnum:]| |\-|_|\&|\'|#|\+|\.]/', $tag)) {
1543 $this->setError(_('Bad tag name, you only can use the following characters: [A-Z][a-z][0-9]-_&\'#+. and space'));
1548 if ($tag == '' || array_search($tag, $inserted) !== false) continue;
1549 $sql = 'INSERT INTO project_tags (group_id,name) VALUES ($1, $2)';
1550 $res = db_query_params($sql, array($this->getID(), $tag));
1552 $this->setError(_('Setting tags:') . ' ' .
1564 * getPermission - Return a Permission for this Group
1566 * @return object The Permission.
1568 function &getPermission() {
1569 return permission_get_object($this);
1573 function delete($sure, $really_sure, $really_really_sure) {
1574 if (!$sure || !$really_sure || !$really_really_sure) {
1575 $this->setMissingParamsError(_('Please tick all checkboxes.'));
1578 if ($this->getID() == forge_get_config('news_group') ||
1579 $this->getID() == 1 ||
1580 $this->getID() == forge_get_config('stats_group') ||
1581 $this->getID() == forge_get_config('peer_rating_group')) {
1582 $this->setError(_('Cannot Delete System Group'));
1585 $perm = $this->getPermission();
1586 if (!$perm || !is_object($perm)) {
1587 $this->setPermissionDeniedError();
1589 } elseif ($perm->isError()) {
1590 $this->setPermissionDeniedError();
1592 } elseif (!$perm->isSuperUser()) {
1593 $this->setPermissionDeniedError();
1599 // Remove all the members
1601 $members = $this->getMembers();
1602 foreach ($members as $i) {
1603 if(!$this->removeUser($i->getID())) {
1604 $this->setError(_('Could not properly remove member:').' '.$i->getID());
1609 // unlink roles from this project
1610 foreach ($this->getRoles() as $r) {
1611 if ($r->getHomeProject() == NULL
1612 || $r->getHomeProject()->getID() != $this->getID()) {
1613 $r->unlinkProject($this);
1620 if ($this->usesTracker()) {
1621 $atf = new ArtifactTypeFactory($this);
1622 $at_arr = $atf->getArtifactTypes();
1623 foreach ($at_arr as $i) {
1624 if (!is_object($i)) {
1627 if (!$i->delete(1,1)) {
1628 $this->setError(_('Could not properly delete the tracker:').' '.$i->getErrorMessage());
1637 if ($this->usesForum()) {
1638 $ff = new ForumFactory($this);
1639 $f_arr = $ff->getForums();
1640 foreach ($f_arr as $i) {
1641 if (!is_object($i)) {
1644 if(!$i->delete(1,1)) {
1645 $this->setError(_('Could not properly delete the forum:').' '.$i->getErrorMessage());
1651 // Delete Subprojects
1653 if ($this->usesPM()) {
1654 $pgf = new ProjectGroupFactory($this);
1655 $pg_arr = $pgf->getProjectGroups();
1656 foreach ($pg_arr as $i) {
1657 if (!is_object($i)) {
1660 if (!$i->delete(1,1)) {
1661 $this->setError(_('Could not properly delete the ProjectGroup:').' '.$i->getErrorMessage());
1667 // Delete FRS Packages
1669 $res = db_query_params('SELECT * FROM frs_package WHERE group_id=$1',
1670 array($this->getID()));
1672 $this->setError(_('Error FRS Packages: ').db_error());
1677 while ($arr = db_fetch_array($res)) {
1678 $frsp=new FRSPackage($this, $arr['package_id'], $arr);
1679 if (!$frsp->delete(1, 1)) {
1680 $this->setError(_('Could not properly delete the FRSPackage:').' '.$frsp->getErrorMessage());
1687 $news_group=group_get_object(forge_get_config('news_group'));
1688 $res = db_query_params('SELECT forum_id FROM news_bytes WHERE group_id=$1',
1689 array($this->getID()));
1691 $this->setError(_('Error Deleting News: ').db_error());
1696 for ($i=0; $i<db_numrows($res); $i++) {
1697 $Forum = new Forum($news_group,db_result($res,$i,'forum_id'));
1698 if (!$Forum->delete(1,1)) {
1699 $this->setError(_("Could Not Delete News Forum: %d"),$Forum->getID());
1703 $res = db_query_params('DELETE FROM news_bytes WHERE group_id=$1',
1704 array($this->getID()));
1706 $this->setError(_('Error Deleting News: ').db_error());
1714 $res = db_query_params('DELETE FROM doc_data WHERE group_id=$1',
1715 array($this->getID()));
1717 $this->setError(_('Error Deleting Documents: ').db_error());
1722 $res = db_query_params('DELETE FROM doc_groups WHERE group_id=$1',
1723 array($this->getID()));
1725 $this->setError(_('Error Deleting Documents: ').db_error());
1733 $res=db_query_params('DELETE FROM project_tags WHERE group_id=$1', array($this->getID()));
1735 $this->setError(_('Error Deleting Tags: ').db_error());
1741 // Delete group history
1743 $res = db_query_params('DELETE FROM group_history WHERE group_id=$1',
1744 array($this->getID()));
1746 $this->setError(_('Error Deleting Project History: ').db_error());
1752 // Delete group plugins
1754 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1',
1755 array($this->getID()));
1757 $this->setError(_('Error Deleting Project Plugins: ').db_error());
1763 // Delete group cvs stats
1765 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE group_id=$1',
1766 array ($this->getID())) ;
1768 $this->setError(_('Error Deleting SCM Statistics: ').db_error());
1776 if ($this->usesSurvey()) {
1777 $sf = new SurveyFactory($this);
1778 $s_arr =& $sf->getSurveys();
1779 foreach ($s_arr as $i) {
1780 if (!is_object($i)) {
1783 if (!$i->delete()) {
1784 $this->setError(_('Could not properly delete the survey'));
1790 // Delete SurveyQuestions
1792 $sqf = new SurveyQuestionFactory($this);
1793 $sq_arr = $sqf->getSurveyQuestions();
1794 if (is_array($sq_arr)) {
1795 foreach ($sq_arr as $i) {
1796 if (!is_object($i)) {
1799 if (!$i->delete()) {
1800 $this->setError(_('Could not properly delete the survey questions'));
1808 // Delete Mailing List Factory
1810 if ($this->usesMail()) {
1811 $mlf = new MailingListFactory($this);
1812 $ml_arr = $mlf->getMailingLists();
1813 foreach ($ml_arr as $i) {
1814 if (!is_object($i)) {
1817 if (!$i->delete(1,1)) {
1818 $this->setError(_('Could not properly delete the mailing list'));
1827 $res = db_query_params('DELETE FROM trove_group_link WHERE group_id=$1',
1828 array($this->getID()));
1830 $this->setError(_('Error Deleting Trove: ').db_error());
1835 $res = db_query_params('DELETE FROM trove_agg WHERE group_id=$1',
1836 array($this->getID()));
1838 $this->setError(_('Error Deleting Trove: ').db_error());
1846 $res = db_query_params('DELETE FROM project_sums_agg WHERE group_id=$1',
1847 array($this->getID()));
1849 $this->setError(_('Error Deleting Counters: ').db_error());
1854 $res = db_query_params('INSERT INTO deleted_groups (unix_group_name, delete_date, isdeleted) VALUES ($1, $2, $3)',
1855 array($this->getUnixName(),
1859 $this->setError(_('Error Deleting Project:').' '.db_error());
1864 // Delete entry in groups.
1865 $res = db_query_params('DELETE FROM groups WHERE group_id=$1',
1866 array($this->getID()));
1868 $this->setError(_('Error Deleting Project:').' '.db_error());
1875 $hook_params = array();
1876 $hook_params['group'] = $this;
1877 $hook_params['group_id'] = $this->getID();
1878 plugin_hook("group_delete", $hook_params);
1880 if (forge_get_config('upload_dir') != '' && $this->getUnixName()) {
1881 exec('/bin/rm -rf '.forge_get_config('upload_dir').'/'.$this->getUnixName().'/');
1883 if (forge_get_config('ftp_upload_dir') != '' && $this->getUnixName()) {
1884 exec('/bin/rm -rf '.forge_get_config('ftp_upload_dir').'/'.$this->getUnixName().'/');
1889 $res = db_query_params('DELETE FROM rep_group_act_monthly WHERE group_id=$1',
1890 array ($this->getID()));
1891 //echo 'rep_group_act_monthly'.db_error();
1892 $res = db_query_params('DELETE FROM rep_group_act_weekly WHERE group_id=$1',
1893 array ($this->getID()));
1894 //echo 'rep_group_act_weekly'.db_error();
1895 $res = db_query_params('DELETE FROM rep_group_act_daily WHERE group_id=$1',
1896 array ($this->getID()));
1897 //echo 'rep_group_act_daily'.db_error();
1898 unset($this->data_array);
1903 Basic functions to add/remove users to/from a group
1904 and update their permissions
1908 * addUser - controls adding a user to a group.
1910 * @param string Unix name of the user to add OR integer user_id.
1911 * @param int The role_id this user should have.
1912 * @return boolean success.
1915 function addUser($user_identifier,$role_id) {
1918 Admins can add users to groups
1921 if (!forge_check_perm ('project_admin', $this->getID())) {
1922 $this->setPermissionDeniedError();
1928 get user id for this user's unix_name
1930 if (is_int ($user_identifier)) { // user_id or user_name
1931 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_id=$1', array ($user_identifier)) ;
1933 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_name=$1', array ($user_identifier)) ;
1935 if (db_numrows($res_newuser) > 0) {
1937 // make sure user is active
1939 if (db_result($res_newuser,0,'status') != 'A') {
1940 $this->setError(_('User is not active. Only active users can be added.'));
1946 // user was found - set new user_id var
1948 $user_id = db_result($res_newuser,0,'user_id');
1950 $role = new Role($this, $role_id);
1951 if (!$role || !is_object($role)) {
1952 $this->setError(_('Error Getting Role Object'));
1955 } elseif ($role->isError()) {
1956 $this->setError('addUser::roleget::'.$role->getErrorMessage());
1961 $role->addUser(user_get_object($user_id)) ;
1962 if (!$SYS->sysCheckCreateGroup($this->getID())){
1963 $this->setError($SYS->getErrorMessage());
1967 if (!$SYS->sysCheckCreateUser($user_id)) {
1968 $this->setError($SYS->getErrorMessage());
1972 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
1973 $this->setError($SYS->getErrorMessage());
1979 // user doesn't exist
1981 $this->setError(_('ERROR: User does not exist'));
1986 $hook_params['group'] = $this;
1987 $hook_params['group_id'] = $this->getID();
1988 $hook_params['user'] = user_get_object($user_id);
1989 $hook_params['user_id'] = $user_id;
1990 plugin_hook ("group_adduser", $hook_params);
1995 $this->addHistory('Added User',$user_identifier);
2001 * removeUser - controls removing a user from a group.
2003 * Users can remove themselves.
2005 * @param int The ID of the user to remove.
2006 * @return boolean success.
2008 function removeUser($user_id) {
2011 if ($user_id != user_getid()
2012 && !forge_check_perm('project_admin', $this->getID())) {
2013 $this->setPermissionDeniedError();
2019 $user = user_get_object($user_id);
2020 $roles = RBACEngine::getInstance()->getAvailableRolesForUser($user);
2022 foreach ($roles as $role) {
2023 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2024 $found_role = $role;
2028 if ($found_role == NULL) {
2029 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2033 $found_role->removeUser($user);
2034 if (!$SYS->sysGroupCheckUser($this->getID(), $user_id)) {
2035 $this->setError($SYS->getErrorMessage());
2041 // reassign open artifacts to id=100
2043 $res = db_query_params('UPDATE artifact SET assigned_to=100
2044 WHERE group_artifact_id
2045 IN (SELECT group_artifact_id
2046 FROM artifact_group_list
2047 WHERE group_id=$1 AND status_id=1 AND assigned_to=$2)',
2048 array($this->getID(),
2051 $this->setError(_('ERROR: DB: artifact:').' '.db_error());
2057 // reassign open tasks to id=100
2058 // first have to purge any assignments that would cause
2059 // conflict with existing assignment to 100
2061 $res = db_query_params('DELETE FROM project_assigned_to
2062 WHERE project_task_id IN (SELECT pt.project_task_id
2063 FROM project_task pt, project_group_list pgl, project_assigned_to pat
2064 WHERE pt.group_project_id = pgl.group_project_id
2065 AND pat.project_task_id=pt.project_task_id
2066 AND pt.status_id=1 AND pgl.group_id=$1
2067 AND pat.assigned_to_id=$2)
2068 AND assigned_to_id=100',
2069 array($this->getID(),
2072 $this->setError(sprintf(_('ERROR: DB: project_assigned_to %d: %s'), 1, db_error()));
2076 $res = db_query_params('UPDATE project_assigned_to SET assigned_to_id=100
2077 WHERE project_task_id IN (SELECT pt.project_task_id
2078 FROM project_task pt, project_group_list pgl
2079 WHERE pt.group_project_id = pgl.group_project_id
2080 AND pt.status_id=1 AND pgl.group_id=$1)
2081 AND assigned_to_id=$2',
2082 array($this->getID(),
2085 $this->setError(sprintf(_('ERROR: DB: project_assigned_to %d: %s'), 2, db_error()));
2091 // Remove user from system
2093 if (!$SYS->sysGroupRemoveUser($this->getID(), $user_id)) {
2094 $this->setError($SYS->getErrorMessage());
2099 $hook_params['group'] = $this;
2100 $hook_params['group_id'] = $this->getID();
2101 $hook_params['user'] = user_get_object($user_id);
2102 $hook_params['user_id'] = $user_id;
2103 plugin_hook ("group_removeuser", $hook_params);
2106 $this->addHistory('Removed User',$user_id);
2113 * updateUser - controls updating a user's role in this group.
2115 * @param int The ID of the user.
2116 * @param int The role_id to set this user to.
2117 * @return boolean success.
2119 function updateUser($user_id,$role_id) {
2121 if (!forge_check_perm ('project_admin', $this->getID())) {
2122 $this->setPermissionDeniedError();
2126 $newrole = RBACEngine::getInstance()->getRoleById ($role_id) ;
2127 if (!$newrole || !is_object($newrole)) {
2128 $this->setError(_('Could Not Get Role'));
2130 } elseif ($newrole->isError()) {
2131 $this->setError(sprintf(_('Role: %s'),$role->getErrorMessage()));
2133 } elseif ($newrole->getHomeProject() == NULL
2134 || $newrole->getHomeProject()->getID() != $this->getID()) {
2135 $this->setError(_('Wrong destination role'));
2138 $user = user_get_object ($user_id) ;
2139 $roles = RBACEngine::getInstance()->getAvailableRolesForUser ($user) ;
2140 $found_role = NULL ;
2141 foreach ($roles as $role) {
2142 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2143 $found_role = $role ;
2147 if ($found_role == NULL) {
2148 $this->setError(sprintf(_('ERROR: User not removed: %s')));
2152 $found_role->removeUser ($user) ;
2153 $newrole->addUser ($user) ;
2155 $this->addHistory('Updated User',$user_id);
2160 * addHistory - Makes an audit trail entry for this project.
2162 * @param string The name of the field.
2163 * @param string The Old Value for this $field_name.
2164 * @return database result handle.
2167 function addHistory($field_name, $old_value) {
2168 return db_query_params ('INSERT INTO group_history(group_id,field_name,old_value,mod_by,adddate)
2169 VALUES ($1,$2,$3,$4,$5)',
2170 array ($this->getID(),
2178 * activateUsers - Make sure that group members have unix accounts.
2180 * Setup unix accounts for group members. Can be called even
2181 * if members are already active.
2185 function activateUsers() {
2187 Activate member(s) of the project
2190 $members = $this->getUsers (true) ;
2192 foreach ($members as $member) {
2194 foreach (RBACEngine::getInstance()->getAvailableRolesForUser ($member) as $role) {
2195 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2198 if (!$this->addUser($member->getUnixName(),$role->getID())) {
2209 * getMembers - returns array of User objects for this project
2211 * @return array of User objects for this group.
2213 function getMembers() {
2214 return $this->getUsers (true) ;
2218 * replaceTemplateStrings - fill-in some blanks with project name
2220 * @param string Template string
2221 * @return string String after replacements
2223 function replaceTemplateStrings($string) {
2224 $string = str_replace ('UNIXNAME', $this->getUnixName(), $string) ;
2225 $string = str_replace ('PUBLICNAME', $this->getPublicName(), $string) ;
2226 $string = str_replace ('DESCRIPTION', $this->getDescription(), $string) ;
2231 * approve - Approve pending project.
2233 * @param object The User object who is doing the updating.
2236 function approve(&$user) {
2237 global $gfcommon,$gfwww;
2238 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
2240 if ($this->getStatus()=='A') {
2241 $this->setError(_("Group already active"));
2247 // Step 1: Activate group and create LDAP entries
2248 if (!$this->setStatus($user, 'A')) {
2253 // Switch to system language for item creation
2254 setup_gettext_from_sys_lang();
2256 // Create default roles
2257 $idadmin_group = NULL;
2258 foreach (get_group_join_requests ($this) as $gjr) {
2259 $idadmin_group = $gjr->getUserID();
2262 if ($idadmin_group == NULL) {
2263 $idadmin_group = $user->getID();
2266 $template = $this->getTemplateProject();
2267 $id_mappings = array();
2268 $seen_admin_role = false;
2270 // Copy roles from template project
2271 foreach($template->getRoles() as $oldrole) {
2272 if ($oldrole->getHomeProject() != NULL) {
2273 $role = new Role($this);
2275 // Need to use a different role name so that the permissions aren't set from the hardcoded defaults
2276 $role->create('TEMPORARY ROLE NAME', $data, true);
2277 $role->setName($oldrole->getName());
2278 if ($oldrole->getSetting ('project_admin', $template->getID())) {
2279 $seen_admin_role = true;
2283 $role->linkProject($this);
2285 $id_mappings['role'][$oldrole->getID()] = $role->getID();
2286 // Reuse the project_admin permission
2287 $role->setSetting ('project_admin', $this->getID(), $oldrole->getSetting ('project_admin', $template->getID())) ;
2291 if (!$seen_admin_role) {
2292 $role = new Role($this);
2293 $adminperms = array ('project_admin' => array ($this->getID() => 1)) ;
2294 $role_id = $role->create ('Admin', $adminperms, true) ;
2297 $roles = $this->getRoles() ;
2298 foreach ($roles as $r) {
2299 if ($r->getHomeProject() == NULL) {
2302 if ($r->getSetting ('project_admin', $this->getID())) {
2303 $r->addUser(user_get_object ($idadmin_group));
2307 // Temporarily switch to the submitter's identity
2308 $saved_session = session_get_user();
2309 session_set_internal($idadmin_group);
2312 if (forge_get_config('use_tracker')) {
2313 $this->setUseTracker ($template->usesTracker());
2314 if ($template->usesTracker()) {
2315 $oldatf = new ArtifactTypeFactory($template);
2316 foreach ($oldatf->getArtifactTypes() as $o) {
2317 $t = new ArtifactType ($this) ;
2318 $t->create ($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->emailAll(),$o->getEmailAddress(),$o->getDuePeriod()/86400,0,$o->getSubmitInstructions(),$o->getBrowseInstructions()) ;
2319 $id_mappings['tracker'][$o->getID()] = $t->getID();
2320 $t->cloneFieldsFrom ($o->getID());
2325 if (forge_get_config('use_pm')) {
2326 $this->setUsePM ($template->usesPM());
2327 if ($template->usesPM()) {
2328 $oldpgf = new ProjectGroupFactory($template);
2329 foreach ($oldpgf->getProjectGroups() as $o) {
2330 $pg = new ProjectGroup($this);
2331 $pg->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo());
2332 $id_mappings['pm'][$o->getID()] = $pg->getID();
2337 if (forge_get_config('use_forum')) {
2338 $this->setUseForum($template->usesForum()) ;
2339 if ($template->usesForum()) {
2340 $oldff = new ForumFactory($template) ;
2341 foreach ($oldff->getForums() as $o) {
2342 $f = new Forum($this);
2343 $f->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo(),1);
2344 $id_mappings['forum'][$o->getID()] = $f->getID();
2349 if (forge_get_config('use_docman')) {
2350 $this->setUseDocman($template->usesDocman());
2351 if ($template->usesDocman()) {
2352 $olddgf = new DocumentGroupFactory($template);
2353 // First pass: create all docgroups
2354 $id_mappings['docman_docgroup'][0] = 0;
2355 foreach ($olddgf->getDocumentGroups() as $o) {
2356 $ndgf = new DocumentGroup($this);
2357 // .trash is a reserved directory
2358 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2359 $ndgf->create($this->replaceTemplateStrings($o->getName()));
2360 $id_mappings['docman_docgroup'][$o->getID()] = $ndgf->getID();
2363 // Second pass: restore hierarchy links
2364 foreach ($olddgf->getDocumentGroups() as $o) {
2365 $ndgf = new DocumentGroup($this);
2366 if ($o->getName() != '.trash' && $o->getParentID() == 0) {
2367 $ndgf->fetchData($id_mappings['docman_docgroup'][$o->getID()]);
2368 $ndgf->update($ndgf->getName(), $id_mappings['docman_docgroup'][$o->getParentID()]);
2374 if (forge_get_config('use_frs')) {
2375 $this->setUseFRS ($template->usesFRS());
2376 if ($template->usesFRS()) {
2377 foreach (get_frs_packages($template) as $o) {
2378 $newp = new FRSPackage($this);
2379 $nname = $this->replaceTemplateStrings($o->getName());
2380 $newp->create ($nname, $o->isPublic());
2385 if (forge_get_config('use_mail')) {
2386 $this->setUseMail($template->usesMail()) ;
2387 if ($template->usesMail()) {
2388 $oldmlf = new MailingListFactory($template);
2389 foreach ($oldmlf->getMailingLists() as $o) {
2390 $ml = new MailingList($this);
2391 $nname = preg_replace ('/^'.$template->getUnixName().'-/','',$o->getName()) ;
2393 $ndescription = $this->replaceTemplateStrings($o->getDescription()) ;
2394 $ml->create($nname, $ndescription, $o->isPublic());
2400 /* use SCM plugin from template group */
2401 $this->setUseSCM($template->usesSCM());
2403 foreach ($template->getPlugins() as
2404 $plugin_id => $plugin_name) {
2405 $this->setPluginUse($plugin_name);
2408 /* use SCM choice from registration page */
2410 foreach ($template->getPlugins() as
2411 $plugin_id => $plugin_name) {
2412 if (substr($plugin_name, 3) == 'scm' &&
2413 $plugin_name != 'scmhook') {
2414 /* skip copying scm plugins */
2417 /* enable other plugins though */
2418 $this->setPluginUse($plugin_name);
2422 foreach ($template->getRoles() as $oldrole) {
2423 $newrole = RBACEngine::getInstance()->getRoleById ($id_mappings['role'][$oldrole->getID()]) ;
2424 if ($oldrole->getHomeProject() != NULL
2425 && $oldrole->getHomeProject()->getID() == $template->getID()) {
2426 $newrole->setPublic ($oldrole->isPublic()) ;
2428 $oldsettings = $oldrole->getSettingsForProject ($template) ;
2430 $sections = array ('project_read', 'project_admin', 'frs', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm') ;
2431 foreach ($sections as $section) {
2432 $newrole->setSetting ($section, $this->getID(), $oldsettings[$section][$template->getID()]) ;
2435 $sections = array ('tracker', 'pm', 'forum') ;
2436 foreach ($sections as $section) {
2437 if (isset ($oldsettings[$section])) {
2438 foreach ($oldsettings[$section] as $k => $v) {
2439 // Only copy perms for tools that have been copied
2440 if (isset ($id_mappings[$section][$k])) {
2441 $newrole->setSetting ($section,
2442 $id_mappings[$section][$k],
2450 $lm = new WidgetLayoutManager();
2451 $lm->createDefaultLayoutForProject ($this->getID(), $template->getID()) ;
2453 $params = array () ;
2454 $params['template'] = $template ;
2455 $params['project'] = $this ;
2456 $params['id_mappings'] = $id_mappings ;
2457 plugin_hook_by_reference ('clone_project_from_template', $params) ;
2459 // Disable everything
2460 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',
2461 array ($this->getID())) ;
2464 $this->normalizeAllRoles();
2465 $this->activateUsers();
2467 // Delete fake join request
2468 foreach (get_group_join_requests ($this) as $gjr) {
2469 $gjr->delete(true) ;
2472 // Switch back to user preference
2473 session_set_internal($saved_session->getID());
2474 setup_gettext_from_context();
2478 $this->sendApprovalEmail();
2479 $this->addHistory('Approved', 'x');
2482 // Plugin can make approve operation there
2484 $params[0] = $idadmin_group;
2485 $params[1] = $this->getID();
2486 plugin_hook('group_approved', $params);
2494 * sendApprovalEmail - Send new project email.
2496 * @return boolean success.
2499 function sendApprovalEmail() {
2500 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('project_admin', $this->getID()) ;
2502 if (count($admins) < 1) {
2503 $this->setError(_("Group does not have any administrators."));
2507 // send one email per admin
2508 foreach ($admins as $admin) {
2509 setup_gettext_for_user ($admin) ;
2511 $message=sprintf(_('Your project registration for %4$s has been approved.
2513 Project Full Name: %1$s
2514 Project Unix Name: %2$s
2516 Your DNS will take up to a day to become active on our site.
2517 Your web site is accessible through your shell account. Please read
2518 site documentation (see link below) about intended usage, available
2519 services, and directory layout of the account.
2522 own project page in %4$s while logged in, you will find
2523 additional menu functions to your left labeled \'Project Admin\'.
2525 We highly suggest that you now visit %4$s and create a public
2526 description for your project. This can be done by visiting your project
2527 page while logged in, and selecting \'Project Admin\' from the menus
2528 on the left (or by visiting %3$s
2531 Your project will also not appear in the Trove Software Map (primary
2532 list of projects hosted on %4$s which offers great flexibility in
2533 browsing and search) until you categorize it in the project administration
2534 screens. So that people can find your project, you should do this now.
2535 Visit your project while logged in, and select \'Project Admin\' from the
2538 Enjoy the system, and please tell others about %4$s. Let us know
2539 if there is anything we can do to help you.
2542 htmlspecialchars_decode($this->getPublicName()),
2543 $this->getUnixName(),
2544 util_make_url ('/project/admin/?group_id='.$this->getID()),
2545 forge_get_config ('forge_name'));
2547 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Approved'), forge_get_config ('forge_name')), $message);
2549 setup_gettext_from_context();
2557 * sendRejectionEmail - Send project rejection email.
2559 * This function sends out a rejection message to a user who
2560 * registered a project.
2562 * @param int The id of the response to use.
2563 * @param string The rejection message.
2564 * @return boolean completion status.
2567 function sendRejectionEmail($response_id, $message="zxcv") {
2568 $submitters = array () ;
2569 foreach (get_group_join_requests ($this) as $gjr) {
2570 $submitters[] = user_get_object($gjr->getUserID());
2573 if (count ($submitters) < 1) {
2574 $this->setError(_("Group does not have any administrators."));
2578 foreach ($submitters as $admin) {
2579 setup_gettext_for_user($admin);
2581 $response=sprintf(_('Your project registration for %3$s has been denied.
2583 Project Full Name: %1$s
2584 Project Unix Name: %2$s
2586 Reasons for negative decision:
2588 '), $this->getPublicName(), $this->getUnixName(), forge_get_config('forge_name'));
2590 // Check to see if they want to send a custom rejection response
2591 if ($response_id == 0) {
2592 $response .= $message;
2594 $response .= db_result(
2595 db_query_params('SELECT response_text FROM canned_responses WHERE response_id=$1', array ($response_id)),
2600 util_send_message($admin->getEmail(), sprintf(_('%1$s Project Denied'), forge_get_config ('forge_name')), $response);
2601 setup_gettext_from_context();
2608 * sendNewProjectNotificationEmail - Send new project notification email.
2610 * This function sends out a notification email to the
2611 * SourceForge admin user when a new project is
2614 * @return boolean success.
2617 function sendNewProjectNotificationEmail() {
2618 // Get the user who wants to register the project
2619 $submitters = array();
2620 foreach (get_group_join_requests ($this) as $gjr) {
2621 $submitters[] = user_get_object($gjr->getUserID());
2623 if (count ($submitters) < 1) {
2624 $this->setError(_("Could not find user who has submitted the project."));
2628 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('approve_projects', -1) ;
2630 if (count($admins) < 1) {
2631 $this->setError(_("There is no administrator to send the mail to."));
2635 foreach ($admins as $admin) {
2636 $admin_email = $admin->getEmail () ;
2637 setup_gettext_for_user ($admin) ;
2639 $message = sprintf(_('New %1$s Project Submitted
2641 Project Full Name: %2$s
2642 Submitted Description: %3$s
2644 forge_get_config ('forge_name'),
2645 htmlspecialchars_decode($this->getPublicName()),
2646 htmlspecialchars_decode($this->getRegistrationPurpose()));
2648 foreach ($submitters as $submitter) {
2649 $message .= sprintf(_('Submitter: %1$s (%2$s)
2651 $submitter->getRealName(),
2652 $submitter->getUnixName());
2655 $message .= sprintf (_('
2656 Please visit the following URL to approve or reject this project:
2658 util_make_url ('/admin/approve-pending.php')) ;
2659 util_send_message($admin_email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2660 setup_gettext_from_context();
2664 $email = $submitter->getEmail() ;
2665 setup_gettext_for_user ($submitter) ;
2667 $message=sprintf(_('New %1$s Project Submitted
2669 Project Full Name: %2$s
2670 Submitted Description: %3$s
2672 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'));
2674 util_send_message($email, sprintf(_('New %1$s Project Submitted'), forge_get_config ('forge_name')), $message);
2675 setup_gettext_from_context();
2684 * validateGroupName - Validate the group name
2686 * @param string Group name.
2688 * @return boolean an error false and set an error is the group name is invalide otherwise return true
2690 function validateGroupName($group_name) {
2691 if (strlen($group_name)<3) {
2692 $this->setError(_('Group name is too short'));
2694 } elseif (strlen(htmlspecialchars($group_name))>50) {
2695 $this->setError(_('Group name is too long'));
2697 } elseif (group_get_object_by_publicname($group_name)) {
2698 $this->setError(_('Group name already taken'));
2706 * getRolesId - Get Ids of the roles of the group.
2708 * @return array Role ids of this group.
2710 function getRolesId() {
2711 $role_ids = array();
2713 $res = db_query_params('SELECT role_id FROM pfo_role WHERE home_group_id=$1',
2714 array($this->getID()));
2715 while ($arr = db_fetch_array($res)) {
2716 $role_ids[] = $arr['role_id'];
2718 $res = db_query_params('SELECT role_id FROM role_project_refs WHERE group_id=$1',
2719 array($this->getID()));
2720 while ($arr = db_fetch_array($res)) {
2721 $role_ids[] = $arr['role_id'];
2724 return array_unique($role_ids);
2728 * getRoles - Get the roles of the group.
2730 * @return array Roles of this group.
2732 function getRoles() {
2735 $roles = $this->getRolesId();
2736 $engine = RBACEngine::getInstance();
2737 foreach ($roles as $role_id) {
2738 $result[] = $engine->getRoleById ($role_id);
2744 function normalizeAllRoles() {
2745 $roles = $this->getRoles();
2747 foreach ($roles as $r) {
2748 $r->normalizeData();
2753 * getUnixStatus - Status of activation of unix account.
2755 * @return char (N)one, (A)ctive, (S)uspended or (D)eleted
2757 function getUnixStatus() {
2758 return $this->data_array['unix_status'];
2762 * setUnixStatus - Sets status of activation of unix account.
2764 * @param string The unix status.
2770 * @return boolean success.
2772 function setUnixStatus($status) {
2775 $res = db_query_params ('UPDATE groups SET unix_status=$1 WHERE group_id=$2',
2780 $this->setError(sprintf(_('ERROR - Could Not Update Group Unix Status: %s'),db_error()));
2784 if ($status == 'A') {
2785 if (!$SYS->sysCheckCreateGroup($this->getID())) {
2786 $this->setError($SYS->getErrorMessage());
2791 if ($SYS->sysCheckGroup($this->getID())) {
2792 if (!$SYS->sysRemoveGroup($this->getID())) {
2793 $this->setError($SYS->getErrorMessage());
2800 $this->data_array['unix_status']=$status;
2807 * getUsers - Get the users of a group
2809 * @return array of user's objects.
2811 function getUsers($onlylocal = true) {
2812 if (!isset($this->membersArr)) {
2813 $this->membersArr = array () ;
2816 foreach ($this->getRoles() as $role) {
2818 && ($role->getHomeProject() == NULL || $role->getHomeProject()->getID() != $this->getID())) {
2821 foreach ($role->getUsers() as $user) {
2822 $ids[] = $user->getID() ;
2825 $ids = array_unique ($ids) ;
2826 foreach ($ids as $id) {
2827 $u = user_get_object ($id) ;
2828 if ($u->isActive()) {
2829 $this->membersArr[] = $u ;
2833 return $this->membersArr;
2836 function setDocmanCreateOnlineStatus($status) {
2838 /* if we activate search engine, we probably want to reindex */
2839 $res = db_query_params('UPDATE groups SET use_docman_create_online=$1 WHERE group_id=$2',
2840 array($status, $this->getID()));
2843 $this->setError(sprintf(_('ERROR - Could Not Update Group DocmanCreateOnline Status: %s'),db_error()));
2847 $this->data_array['use_docman_create_online']=$status;
2853 function setDocmanWebdav($status) {
2855 /* if we activate search engine, we probably want to reindex */
2856 $res = db_query_params('UPDATE groups SET use_webdav=$1 WHERE group_id=$2',
2861 $this->setError(sprintf(_('ERROR - Could Not Update Group UseWebdab Status: %s'),db_error()));
2865 $this->data_array['use_webdav']=$status;
2871 function setDocmanSearchStatus($status) {
2873 /* if we activate search engine, we probably want to reindex */
2874 $res = db_query_params('UPDATE groups SET use_docman_search=$1, force_docman_reindex=$1 WHERE group_id=$2',
2879 $this->setError(sprintf(_('ERROR - Could Not Update Group UseDocmanSearch Status: %s'),db_error()));
2883 $this->data_array['use_docman_search']=$status;
2889 function setDocmanForceReindexSearch($status) {
2891 /* if we activate search engine, we probably want to reindex */
2892 $res = db_query_params('UPDATE groups SET force_docman_reindex=$1 WHERE group_id=$2',
2897 $this->setError(sprintf(_('ERROR - Could Not Update Group force_docman_reindex %s'),db_error()));
2901 $this->data_array['force_docman_reindex']=$status;
2910 * group_getname() - get the group name
2912 * @param int The group ID
2916 function group_getname ($group_id = 0) {
2917 $grp = group_get_object($group_id);
2919 return $grp->getPublicName();
2926 * group_getunixname() - get the unixname for a group
2928 * @param int The group ID
2932 function group_getunixname ($group_id) {
2933 $grp = group_get_object($group_id);
2935 return $grp->getUnixName();
2942 * group_get_result() - Get the group object result ID.
2944 * @param int The group ID
2948 function &group_get_result($group_id=0) {
2949 $grp = group_get_object($group_id);
2951 return $grp->getData();
2957 function getAllProjectTags($onlyvisible = true) {
2958 $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',
2961 if (!$res || db_numrows($res) == 0) {
2967 while ($arr = db_fetch_array($res)) {
2969 $group_id = $arr[1];
2970 if (!isset($result[$tag])) {
2971 $result[$tag] = array();
2974 if (!$onlyvisible || forge_check_perm('project_read', $group_id)) {
2975 $p = group_get_object($group_id);
2976 $result[$tag][] = array('unix_group_name' => $p->getUnixName(),
2977 'group_id' => $group_id);
2985 * Utility class to compare project based in various criteria (names, unixnames, id, ...)
2988 class ProjectComparator {
2989 var $criterion = 'name' ;
2991 function Compare ($a, $b) {
2992 switch ($this->criterion) {
2995 $namecmp = strcoll ($a->getPublicName(), $b->getPublicName()) ;
2996 if ($namecmp != 0) {
2999 /* If several projects share a same public name */
3000 return strcoll ($a->getUnixName(), $b->getUnixName()) ;
3003 return strcmp ($a->getUnixName(), $b->getUnixName()) ;
3006 $aid = $a->getID() ;
3007 $bid = $b->getID() ;
3011 return ($a < $b) ? -1 : 1;
3017 function sortProjectList (&$list, $criterion='name') {
3018 $cmp = new ProjectComparator () ;
3019 $cmp->criterion = $criterion ;
3021 return usort ($list, array ($cmp, 'Compare')) ;
3026 // c-file-style: "bsd"