5 * Copyright 1999-2001, VA Linux Systems, Inc.
6 * Copyright 2009-2013, Roland Mas
7 * Copyright 2010-2011,2021, Franck Villaume - Capgemini
8 * Copyright 2010-2012, Alain Peyrat - Alcatel-Lucent
9 * Copyright 2012-2017,2021, Franck Villaume - TrivialDev
10 * Copyright 2013, French Ministry of National Education
11 * Copyright 2017, Stéphane-Eymeric Bredthauer - TrivialDev
12 * http://fusionforge.org
14 * This file is part of FusionForge. FusionForge is free software;
15 * you can redistribute it and/or modify it under the terms of the
16 * GNU General Public License as published by the Free Software
17 * Foundation; either version 2 of the Licence, or (at your option)
20 * FusionForge is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
25 * You should have received a copy of the GNU General Public License along
26 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
27 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
30 require_once $gfcommon.'tracker/ArtifactTypes.class.php';
31 require_once $gfcommon.'tracker/ArtifactTypeFactory.class.php';
32 require_once $gfcommon.'tracker/RoadmapFactory.class.php';
33 require_once $gfcommon.'forum/Forum.class.php';
34 require_once $gfcommon.'forum/ForumFactory.class.php';
35 require_once $gfcommon.'pm/ProjectGroup.class.php';
36 require_once $gfcommon.'pm/ProjectGroupFactory.class.php';
37 require_once $gfcommon.'frs/FRSPackage.class.php';
38 require_once $gfcommon.'frs/FRSRelease.class.php';
39 require_once $gfcommon.'docman/DocumentGroup.class.php';
40 require_once $gfcommon.'docman/DocumentGroupFactory.class.php';
41 require_once $gfcommon.'mail/MailingList.class.php';
42 require_once $gfcommon.'mail/MailingListFactory.class.php';
43 require_once $gfcommon.'survey/SurveyFactory.class.php';
44 require_once $gfcommon.'survey/SurveyQuestionFactory.class.php';
45 require_once $gfcommon.'include/gettext.php';
46 require_once $gfcommon.'include/GroupJoinRequest.class.php';
47 require_once $gfcommon.'include/Role.class.php';
48 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
53 * group_get_object() - Get the group object.
55 * group_get_object() is useful so you can pool group objects/save database queries
56 * You should always use this instead of instantiating the object directly.
58 * You can now optionally pass in a db result handle. If you do, it re-uses that query
59 * to instantiate the objects.
61 * IMPORTANT! That db result must contain all fields
62 * from groups table or you will have problems
64 * @param int $group_id Required
65 * @param int|bool $res Result set handle ("SELECT * FROM groups WHERE group_id=xx")
66 * @return Group|bool A group object or false on failure
68 function &group_get_object($group_id, $res = false) {
69 //create a common set of group objects
70 //saves a little wear on the database
72 //automatically checks group_type and
73 //returns appropriate object
76 if (!isset($GROUP_OBJ["_".$group_id."_"])) {
78 //the db result handle was passed in
80 $res = db_query_params('SELECT * FROM groups WHERE group_id=$1', array($group_id));
82 if (!$res || db_numrows($res) < 1) {
83 $GROUP_OBJ["_".$group_id."_"]=false;
88 $GROUP_OBJ["_".$group_id."_"] = new Group($group_id, $res);
91 return $GROUP_OBJ["_".$group_id."_"];
94 function &group_get_objects($id_arr) {
97 // Note: if we don't do this, the result may be corrupted
101 foreach ($id_arr as $id) {
103 // See if this ID already has been fetched in the cache
105 if (!isset($GROUP_OBJ["_".$id."_"])) {
109 if (!empty($fetch)) {
110 $res=db_query_params('SELECT * FROM groups WHERE group_id = ANY ($1)',
111 array(db_int_array_to_any_clause($fetch)));
112 while ($arr = db_fetch_array($res)) {
113 $GROUP_OBJ["_".$arr['group_id']."_"] = new Group($arr['group_id'],$arr);
116 foreach ($id_arr as $id) {
117 $return[] =& $GROUP_OBJ["_".$id."_"];
122 function &group_get_active_projects() {
123 $res = db_query_params('SELECT group_id FROM groups WHERE status=$1',
125 return group_get_objects(util_result_column_to_array($res,0));
128 function &group_get_all_projects() {
129 $res = db_query_params ('SELECT group_id FROM groups',
131 return group_get_objects(util_result_column_to_array($res,0));
134 function &group_get_template_projects() {
135 $res = db_query_params('SELECT group_id FROM groups WHERE is_template=1 AND status != $1',
137 return group_get_objects(util_result_column_to_array($res,0));
140 function &group_get_object_by_name($groupname) {
141 $res = db_query_params('SELECT * FROM groups WHERE unix_group_name=$1', array($groupname));
142 return group_get_object(db_result($res, 0, 'group_id'), $res);
145 function &group_get_objects_by_name($groupname_arr) {
146 $res = db_query_params('SELECT group_id FROM groups WHERE unix_group_name = ANY ($1)',
147 array(db_string_array_to_any_clause($groupname_arr)));
148 $arr =& util_result_column_to_array($res,0);
149 return group_get_objects($arr);
152 function group_get_object_by_publicname($groupname) {
153 $res = db_query_params('SELECT * FROM groups WHERE lower(group_name) LIKE $1',
154 array(htmlspecialchars(html_entity_decode(strtolower($groupname)))));
155 return group_get_object(db_result($res, 0, 'group_id'), $res);
158 function filter_groups_by_read_access($grps) {
159 $filteredgrps = array();
160 foreach ($grps as $g) {
161 if (forge_check_perm ('project_read', $g->getID())) {
162 $filteredgrps[] = $g;
165 return $filteredgrps;
169 * get_public_active_projects_asc() - Get a list of rows for public active projects (initially in trove/full_list)
171 * @param int $max_query_limit Optional Maximum number of rows to limit query length
172 * @param int $offset start to retrieve rows from offset value
173 * @return array List of public active projects
175 function group_get_public_active_projects_asc($max_query_limit = -1, $offset = 0) {
177 if (session_loggedin()) {
179 $userRoles = $LUSER->getRoles();
180 if (count($userRoles)) {
181 foreach ($userRoles as $r) {
182 $role_id .= ', '.$r->getID();
187 $res_grp = db_query_params ('
188 SELECT group_id, group_name, unix_group_name, short_description, register_time
190 WHERE status = $1 AND is_template=0 AND register_time > 0
191 AND group_id in (select ref_id FROM pfo_role_setting WHERE section_name = $2 and perm_val = 1 and role_id IN ('.$role_id.'))
192 ORDER BY group_name ASC
194 array('A', 'project_read'),
195 $max_query_limit, $offset);
197 while ($row_grp = db_fetch_array($res_grp)) {
198 $projects[] = $row_grp;
204 * group_get_readable_projects_using_tag_asc() - Get a list of group_id for active projects (initially in trove/tag_cloud)
206 * @param string $selected_tag Tag to search
207 * @param int $max_query_limit Optional Maximum number of rows to limit query length
208 * @param int $offset start to retrieve rows from offset value
209 * @return array List of public active projects
211 function group_get_readable_projects_using_tag_asc($selected_tag, $max_query_limit = -1, $offset = 0) {
213 if (session_loggedin()) {
215 $userRoles = $LUSER->getRoles();
216 if (count($userRoles)) {
217 foreach ($userRoles as $r) {
218 $role_id .= ', '.$r->getID();
223 $res_grp = db_query_params ('SELECT groups.group_id, group_name, unix_group_name, short_description, register_time
224 FROM project_tags, groups
225 WHERE LOWER(name) = $1
226 AND project_tags.group_id = groups.group_id
227 AND groups.status = $2 AND groups.is_template=0 AND groups.register_time > 0
228 AND groups.group_id in (select ref_id FROM pfo_role_setting WHERE section_name = $3 and perm_val = 1 and role_id IN ('.$role_id.'))
229 ORDER BY groups.group_name ASC',
230 array(strtolower($selected_tag), 'A', 'project_read'),
231 $max_query_limit, $offset);
233 while ($row_grp = db_fetch_array($res_grp)) {
234 $projects[] = $row_grp;
239 class Group extends FFError {
241 * Associative array of data from db.
243 * @var array $data_array.
248 * array of User objects.
250 * @var array $membersArr.
255 * cached return value of getVotes
256 * @var int|bool $votes
261 * cached return value of getVoters
262 * @var int|bool $voters
267 * Group - Group object constructor - use group_get_object() to instantiate.
269 * @param int|bool $id Required - Id of the group you want to instantiate.
270 * @param int|bool $res Database result from select query OR associative array of all columns.
272 function __construct($id = false, $res = false) {
273 parent::__construct();
275 //setting up an empty object
276 //probably going to call create()
280 $this->fetchData($id);
283 // Assoc array was passed in
285 if (is_array($res)) {
286 $this->data_array =& $res;
288 if (db_numrows($res) < 1) {
289 //function in class we extended
290 $this->setError(_('Group Not Found'));
291 $this->data_array = array();
293 //set up an associative array for use by other functions
294 $this->data_array = db_fetch_array_by_row($res, 0);
302 * fetchData - May need to refresh database fields if an update occurred.
304 * @param int $group_id The group_id.
305 * @return bool success or not
307 function fetchData($group_id) {
308 $res = db_query_params ('SELECT * FROM groups WHERE group_id=$1',
310 if (!$res || db_numrows($res) < 1) {
311 $this->setError('fetchData()'._(': ').db_error());
314 $this->data_array = db_fetch_array($res);
319 * create - Create new group.
321 * This method should be called on empty Group object.
322 * It will add an entry for a pending group/project (status 'P')
324 * @param object $user The User object.
325 * @param string $group_name The full name of the user.
326 * @param string $unix_name The Unix name of the user.
327 * @param string $description The new group description.
328 * @param string $purpose The purpose of the group.
329 * @param string $unix_box
330 * @param string $scm_box
331 * @param bool $send_mail Whether to send an email or not
332 * @param int $built_from_template The id of the project this new project is based on
333 * @param int $createtimestamp The Time Stamp of creation to ease import.
334 * @return bool success or not
336 function create(&$user, $group_name, $unix_name, $description, $purpose, $unix_box = 'shell1',
337 $scm_box = 'cvs1', $send_mail = true, $built_from_template = 0, $createtimestamp = null) {
338 // $user is ignored - anyone can create pending group
341 if ($this->getID()!=0) {
342 $this->setError(_('Group object already exists.'));
344 } elseif (!$this->validateGroupName($group_name)) {
345 $this->setError(_('Invalid project name'));
347 } elseif (!account_groupnamevalid($unix_name)) {
348 $this->setError(_('Invalid Unix Name.'));
350 } elseif (!$SYS->sysUseUnixName($unix_name)) {
351 $this->setError(_('Unix name already taken.'));
353 } elseif (strlen($purpose)<10) {
354 $this->setError(_('Please describe your Registration Project Purpose and Summarization in a more comprehensive manner.'));
356 } elseif (strlen($purpose)>1500) {
357 $this->setError(_('The Registration Project Purpose and Summarization text is too long. Please make it smaller than 1500 characters.'));
359 } elseif (strlen($description)<10) {
360 $this->setError(_('Describe in a more comprehensive manner your project.'));
364 // Check if use_project_vhost for homepage
365 if (forge_get_config('use_project_vhost')) {
366 $homepage = $unix_name.'.'.forge_get_config('web_host');
368 $homepage = forge_get_config('web_host')."/www/".$unix_name."/";
372 $createtimestamp = (($createtimestamp) ? $createtimestamp : time());
373 $res = db_query_params('
388 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)',
389 array(htmlspecialchars($group_name),
391 htmlspecialchars($description),
397 htmlspecialchars($purpose),
399 md5(util_randbytes()),
400 $built_from_template));
401 if (!$res || db_affected_rows($res) < 1) {
402 $this->setError(_('Error')._(': ')._('Cannot create group')._(': ').db_error());
407 $id = db_insertid($res, 'groups', 'group_id');
409 $this->setError(_('Error')._(': ')._('Cannot get group id')._(': ').db_error());
414 if (!$this->fetchData($id)) {
419 $gjr = new GroupJoinRequest($this);
420 $gjr->create($user->getID(), 'Fake GroupJoinRequest to store the creator of a project', false);
422 $hook_params = array();
423 $hook_params['group'] = $this;
424 $hook_params['group_id'] = $this->getID();
425 $hook_params['group_name'] = $group_name;
426 $hook_params['unix_group_name'] = $unix_name;
427 $hook_params['createtimestamp'] = $createtimestamp;
428 plugin_hook("group_create", $hook_params);
432 $this->sendNewProjectNotificationEmail();
440 * updateAdmin - Update core properties of group object.
442 * This function require site admin privilege.
444 * @param string $unix_box Machine on which group's home directory located.
445 * @param string $http_domain Domain which serves group's WWW.
446 * @return bool status.
449 function updateAdmin($unix_box, $http_domain) {
450 $perm =& $this->getPermission();
452 if (!$perm || !is_object($perm)) {
453 $this->setError(_('Could not get permission.'));
457 if (!$perm->isSuperUser()) {
458 $this->setError(_('Permission denied.'));
464 $res = db_query_params('
466 SET unix_box=$1, http_domain=$2
472 if (!$res || db_affected_rows($res) < 1) {
473 $this->setError(_('Error')._(': ')._('Cannot change group properties')._(': ').db_error());
478 // Log the audit trail
479 if ($unix_box != $this->data_array['unix_box']) {
480 $this->addHistory('unix_box', $this->data_array['unix_box']);
482 if ($http_domain != $this->data_array['http_domain']) {
483 $this->addHistory('http_domain', $this->data_array['http_domain']);
486 if (!$this->fetchData($this->getID())) {
495 * update - Update number of common properties.
497 * Unlike updateAdmin(), this function accessible to project admin.
499 * @param object $user User requesting operation (for access control).
500 * @param string $group_name
501 * @param string $homepage
502 * @param string $short_description
503 * @param bool $use_mail
504 * @param bool $use_survey
505 * @param bool $use_forum
506 * @param bool $use_pm
507 * @param bool $use_pm_depend_box
508 * @param bool $use_scm
509 * @param bool $use_news
510 * @param bool $use_docman
511 * @param string $new_doc_address
512 * @param bool $send_all_docs
513 * @param int $logo_image_id XXXX UNUSED XXXX -> see getLogoImageID function
514 * @param bool $use_ftp
515 * @param bool $use_tracker
516 * @param bool $use_frs
517 * @param bool $use_stats
518 * @param string $tags
519 * @param bool $use_activity
520 * @param bool $is_public group is publicly accessible
521 * @param string $new_frs_address
522 * @param bool $send_all_frs
523 * @return int status.
526 function update(&$user, $group_name, $homepage, $short_description, $use_mail, $use_survey, $use_forum,
527 $use_pm, $use_pm_depend_box, $use_scm, $use_news, $use_docman,
528 $new_doc_address, $send_all_docs, $logo_image_id,
529 $use_ftp, $use_tracker, $use_frs, $use_stats, $tags, $use_activity, $is_public, $new_frs_address, $send_all_frs) {
531 $perm =& $this->getPermission();
533 if (!$perm || !is_object($perm)) {
534 $this->setError(_('Could not get permission.'));
538 if (!$perm->isAdmin()) {
539 $this->setError(_('Permission denied.'));
543 // Validate some values
544 if ($this->getPublicName() != htmlspecialchars($group_name) && !$this->validateGroupName($group_name)) {
548 if ($new_doc_address) {
549 $invalid_mails = validate_emails($new_doc_address);
550 if (count($invalid_mails) > 0) {
551 $this->setError(sprintf(ngettext('New Doc Address Appeared Invalid: %s', 'New Doc Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
556 if ($new_frs_address) {
557 $invalid_mails = validate_emails($new_frs_address);
558 if (count($invalid_mails) > 0) {
559 $this->setError(sprintf(ngettext('New FRS Address Appeared Invalid: %s', 'New FRS Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
564 // in the database, these all default to '1',
565 // so we have to explicitly set 0
578 if (!$use_pm_depend_box) {
579 $use_pm_depend_box = 0;
602 if (!$use_activity) {
605 if (!$send_all_docs) {
609 if (!$send_all_frs) {
613 $homepage = ltrim($homepage);
615 $homepage = util_make_url('/projects/'.$this->getUnixName().'/');
620 $res = db_query_params('UPDATE groups
623 short_description=$3,
628 use_pm_depend_box=$8,
641 array(htmlspecialchars($group_name),
643 htmlspecialchars($short_description),
662 if (!$res || db_affected_rows($res) < 1) {
663 $this->setError(_('Error updating project information')._(': ').db_error());
668 if (!$this->setUseDocman($use_docman)) {
669 $this->setError(_('Error updating project information use_docman')._(': ').db_error());
674 if ($this->setTags($tags) === false) {
679 // Log the audit trail
680 $this->addHistory('Changed Public Info', '');
682 if (!$this->fetchData($this->getID())) {
687 $hook_params = array();
688 $hook_params['group'] = $this;
689 $hook_params['group_id'] = $this->getID();
690 $hook_params['group_homepage'] = $homepage;
691 $hook_params['group_name'] = htmlspecialchars($group_name);
692 $hook_params['group_description'] = htmlspecialchars($short_description);
693 $hook_params['group_ispublic'] = $is_public;
694 if (!plugin_hook("group_update", $hook_params)) {
695 if (!$this->isError()) {
696 $this->setError(_('Error updating project information in plugin_hook group_update'));
707 * getID - Simply return the group_id for this object.
709 * @return int group_id.
712 return $this->data_array['group_id'];
716 * getStatus - the status code.
718 * Statuses char include I,H,A,D,P.
725 function getStatus() {
726 return $this->data_array['status'];
730 * setStatus - set the status code.
732 * Statuses include I,H,A,D,P.
739 * @param object $user User requesting operation (for access control).
740 * @param string $status Status value.
741 * @return bool success.
744 function setStatus(&$user, $status) {
747 if (!forge_check_global_perm_for_user($user, 'approve_projects')) {
748 $this->setPermissionDeniedError();
752 // Projects in 'A' status can only go to 'H' or 'D'
753 // Projects in 'D' status can only go to 'A'
754 // Projects in 'P' status can only go to 'A' OR 'D'
755 // Projects in 'I' status can only go to 'P'
756 // Projects in 'H' status can only go to 'A' OR 'D'
757 $allowed_status_changes = array(
758 'AH'=>1,'AD'=>1,'DA'=>1,'PA'=>1,'PD'=>1,
759 'IP'=>1,'HA'=>1,'HD'=>1
762 // Check that status transition is valid
763 if ($this->getStatus() != $status
764 && !array_key_exists($this->getStatus(). $status, $allowed_status_changes)) {
765 $this->setError(_('Invalid Status Change From')._(': ').$this->getStatus()._(' To ')._(': ').$status);
771 $res = db_query_params('UPDATE groups
773 WHERE group_id=$2', array($status, $this->getID()));
775 if (!$res || db_affected_rows($res) < 1) {
776 $this->setError(_('Error')._(': ')._('Cannot change group status')._(': ').db_error());
782 // Activate system group, if not yet
783 if (!$SYS->sysCheckGroup($this->getID())) {
784 if (!$SYS->sysCreateGroup($this->getID())) {
785 $this->setError($SYS->getErrorMessage());
790 if (!$this->activateUsers()) {
795 /* Otherwise, the group is not active, and make sure that
796 System group is not active either */
797 } elseif ($SYS->sysCheckGroup($this->getID())) {
798 if (!$SYS->sysRemoveGroup($this->getID())) {
799 $this->setError($SYS->getErrorMessage());
805 $hook_params = array();
806 $hook_params['group'] = $this;
807 $hook_params['group_id'] = $this->getID();
808 $hook_params['status'] = $status;
809 plugin_hook("group_setstatus", $hook_params);
813 // Log the audit trail
814 if ($status != $this->getStatus()) {
815 $this->addHistory(_('Status'), $this->getStatus());
818 $this->data_array['status'] = $status;
823 * isPublic - Wrapper around RBAC to check if a project is anonymously readable
825 * @return boo is_public.
827 function isPublic() {
828 $ra = RoleAnonymous::getInstance();
829 return $ra->hasPermission('project_read', $this->getID());
833 * isActive - Database field status of 'A' returns true.
835 * @return bool is_active.
837 function isActive() {
838 if ($this->getStatus() == 'A') {
845 * isTemplate - Simply returns the is_template flag from the database.
847 * @return bool is_template.
849 function isTemplate() {
850 return $this->data_array['is_template'];
854 * setAsTemplate - Set the template status of a project
856 * @param bool $booleanparam is_template.
859 function setAsTemplate($booleanparam) {
861 $booleanparam = $booleanparam ? 1 : 0;
862 $res = db_query_params('UPDATE groups SET is_template=$1 WHERE group_id=$2',
863 array($booleanparam, $this->getID()));
865 if ($booleanparam != $this->data_array['is_template']) {
866 $this->addHistory('is_template', $this->data_array['is_template']);
868 $this->data_array['is_template'] = $booleanparam;
878 * getTemplateProject - Return the project template this project is built from
880 * @return object The template project
882 function getTemplateProject() {
883 return group_get_object($this->data_array['built_from_template']);
887 * getUnixName - the unix_name
889 * @return string unix_name.
891 function getUnixName() {
892 return strtolower($this->data_array['unix_group_name']);
896 * getPublicName - the full-length public name.
898 * @return string The group_name.
900 function getPublicName() {
901 return $this->data_array['group_name'];
905 * getRegisterPurpose - the text description of the purpose of this project.
907 * @return string The description.
909 function getRegisterPurpose() {
910 return $this->data_array['register_purpose'];
914 * getDescription - the text description of this project.
916 * @return string The description.
918 function getDescription() {
919 return $this->data_array['short_description'];
923 * getStartDate - the unix time this project was registered.
925 * @return int (unix time) of registration.
927 function getStartDate() {
928 return $this->data_array['register_time'];
932 * getLogoImageID - the id of the logo in the database for this project.
934 * @return int The ID of logo image in db_images table (or 100 if none).
936 function getLogoImageID() {
937 if (!isset($this->data_array['logo_image_id'])) {
938 $res = db_query_params('select id from db_images where group_id = $1 and is_logo = $2',
939 array($this->getID(), 1));
940 if ($res && db_numrows($res)) {
941 $this->data_array['logo_image_id'] = db_result($res, 0, 'id');
943 $this->data_array['logo_image_id'] = null;
946 return $this->data_array['logo_image_id'];
950 * getUnixBox - the hostname of the unix box where this project is located.
952 * @return string The name of the unix machine for the group.
954 function getUnixBox() {
955 return $this->data_array['unix_box'];
959 * getSCMBox - the hostname of the scm box where this project is located.
961 * @return string The name of the unix machine for the group.
963 function getSCMBox() {
964 return $this->data_array['scm_box'];
967 * setSCMBox - the hostname of the scm box where this project is located.
969 * @param string $scm_box The name of the new SCM_BOX
972 function setSCMBox($scm_box) {
974 if ($scm_box == $this->data_array['scm_box']) {
979 $res = db_query_params('UPDATE groups SET scm_box=$1 WHERE group_id=$2', array($scm_box, $this->getID()));
981 $this->addHistory('scm_box', $this->data_array['scm_box']);
982 $this->data_array['scm_box'] = $scm_box;
987 $this->setError(_("Could not insert SCM_BOX to database"));
991 $this->setError(_("SCM Box cannot be empty"));
997 * getDomain - the hostname.domain where their web page is located.
999 * @return string The name of the group [web] domain.
1001 function getDomain() {
1002 return $this->data_array['http_domain'];
1006 * getRegistrationPurpose - the text description of the purpose of this project.
1008 * @return string The application for project hosting.
1010 function getRegistrationPurpose() {
1011 return $this->data_array['register_purpose'];
1015 * getAdmins - Get array of Admin user objects.
1017 * @return array Array of User objects.
1019 function &getAdmins() {
1020 $roles = RBACEngine::getInstance()->getRolesByAllowedAction ('project_admin', $this->getID());
1022 $user_ids = array();
1024 foreach ($roles as $role) {
1025 if (! ($role instanceof RoleExplicit)) {
1028 if ($role->getHomeProject() == NULL
1029 || $role->getHomeProject()->getID() != $this->getID()) {
1033 foreach ($role->getUsers() as $u) {
1034 $user_ids[] = $u->getID();
1037 $active_ids = array();
1038 $ids = array_unique ($user_ids);
1039 foreach ($ids as $id) {
1040 $u = user_get_object ($id);
1041 if ($u->isActive()) {
1049 Common Group preferences for tools
1053 * enableAnonSCM - whether or not this group has opted to enable Anonymous SCM.
1055 * @return bool enable_scm.
1057 function enableAnonSCM() {
1058 $r = RoleAnonymous::getInstance();
1059 return $r->hasPermission('scm', $this->getID(), 'read');
1062 function SetUsesAnonSCM($booleanparam) {
1064 $booleanparam = $booleanparam ? 1 : 0;
1065 $r = RoleAnonymous::getInstance();
1066 $r->setSetting('scm', $this->getID(), $booleanparam);
1071 * enablePserver - whether or not this group has opted to enable Pserver.
1073 * @return bool enable_pserver.
1075 function enablePserver() {
1076 if ($this->usesSCM()) {
1077 return $this->data_array['enable_pserver'];
1083 function SetUsesPserver($booleanparam) {
1085 $booleanparam = $booleanparam ? 1 : 0;
1086 $res = db_query_params('UPDATE groups SET enable_pserver=$1 WHERE group_id=$2',
1087 array($booleanparam, $this->getID()));
1089 $this->data_array['enable_pserver'] = $booleanparam;
1099 * usesSCM - whether or not this group has opted to use SCM.
1101 * @return bool uses_scm.
1103 function usesSCM() {
1104 if (forge_get_config('use_scm')) {
1105 return $this->data_array['use_scm'];
1112 * setUseSCM - Set the SCM usage
1114 * @param bool $booleanparam enabled/disabled
1117 function setUseSCM($booleanparam) {
1119 $booleanparam = $booleanparam ? 1 : 0;
1120 $res = db_query_params('UPDATE groups SET use_scm=$1 WHERE group_id=$2',
1121 array($booleanparam, $this->getID()));
1123 $this->data_array['use_scm'] = $booleanparam;
1133 * usesMail - whether or not this group has opted to use mailing lists.
1135 * @return bool uses_mail.
1137 function usesMail() {
1138 if (forge_get_config('use_mail')) {
1139 return $this->data_array['use_mail'];
1146 * setUseMail - Set the mailing-list usage
1148 * @param bool $booleanparam enabled/disabled
1151 function setUseMail($booleanparam) {
1153 $booleanparam = $booleanparam ? 1 : 0;
1154 $res = db_query_params('UPDATE groups SET use_mail=$1 WHERE group_id=$2',
1155 array($booleanparam, $this->getID()));
1157 $this->data_array['use_mail'] = $booleanparam;
1167 * usesNews - whether or not this group has opted to use news.
1169 * @return bool uses_news.
1171 function usesNews() {
1172 if (forge_get_config('use_news')) {
1173 return $this->data_array['use_news'];
1179 function setUseNews($booleanparam) {
1181 $booleanparam = $booleanparam ? 1 : 0;
1182 $res = db_query_params('UPDATE groups SET use_news=$1 WHERE group_id=$2',
1183 array($booleanparam, $this->getID()));
1185 $this->data_array['use_news'] = $booleanparam;
1195 * usesActivity - whether or not this group has opted to display Project Activities.
1197 * @return bool uses_activities.
1199 function usesActivity() {
1200 if (forge_get_config('use_activity')) {
1201 return $this->data_array['use_activity'];
1207 function setUseActivity($booleanparam) {
1209 $booleanparam = $booleanparam ? 1 : 0;
1210 $res = db_query_params('UPDATE groups SET use_activity=$1 WHERE group_id=$2',
1211 array($booleanparam, $this->getID()));
1213 $this->data_array['use_activity'] = $booleanparam;
1223 * usesForum - whether or not this group has opted to use discussion forums.
1225 * @return bool uses_forum.
1227 function usesForum() {
1228 if (forge_get_config('use_forum')) {
1229 return $this->data_array['use_forum'];
1236 * setUseForum - Set the forum usage
1238 * @param bool $booleanparam enabled/disabled
1241 function setUseForum($booleanparam) {
1243 $booleanparam = $booleanparam ? 1 : 0;
1244 $res = db_query_params('UPDATE groups SET use_forum=$1 WHERE group_id=$2',
1245 array($booleanparam, $this->getID()));
1247 $this->data_array['use_forum'] = $booleanparam;
1257 * usesStats - whether or not this group has opted to use stats.
1259 * @return bool uses_stats.
1261 function usesStats() {
1262 return $this->data_array['use_stats'];
1266 * usesFRS - whether or not this group has opted to use file release system.
1268 * @return bool uses_frs.
1270 function usesFRS() {
1271 if (forge_get_config('use_frs')) {
1272 return $this->data_array['use_frs'];
1279 * setUseFRS - Set the FRS usage
1281 * @param bool $booleanparam enabled/disabled
1284 function setUseFRS($booleanparam) {
1286 $booleanparam = $booleanparam ? 1 : 0;
1287 $res = db_query_params('UPDATE groups SET use_frs=$1 WHERE group_id=$2',
1288 array($booleanparam, $this->getID()));
1290 $this->data_array['use_frs']=$booleanparam;
1299 * usesTracker - whether or not this group has opted to use tracker.
1301 * @return bool uses_tracker.
1303 function usesTracker() {
1304 if (forge_get_config('use_tracker')) {
1305 return $this->data_array['use_tracker'];
1311 * setUseTracker - Set the tracker usage
1313 * @param bool $booleanparam enabled/disabled
1316 function setUseTracker($booleanparam) {
1318 $booleanparam = $booleanparam ? 1 : 0;
1319 $res = db_query_params ('UPDATE groups SET use_tracker=$1 WHERE group_id=$2',
1320 array($booleanparam, $this->getID()));
1322 $this->data_array['use_tracker']=$booleanparam;
1331 * useCreateOnline - whether or not this group has opted to use create online documents option.
1333 * @return bool use_docman_create_online.
1335 function useCreateOnline() {
1336 if (forge_get_config('use_docman')) {
1337 return $this->data_array['use_docman_create_online'];
1343 * usesDocman - whether or not this group has opted to use docman.
1345 * @return bool use_docman.
1347 function usesDocman() {
1348 if (forge_get_config('use_docman')) {
1349 return $this->data_array['use_docman'];
1355 * setUseDocman - Set the docman usage
1357 * @param bool $booleanparam enabled/disabled
1360 function setUseDocman($booleanparam) {
1362 $booleanparam = $booleanparam ? 1 : 0;
1363 $res = db_query_params('UPDATE groups SET use_docman = $1 WHERE group_id = $2',
1364 array($booleanparam, $this->getID()));
1366 // check if / doc_group exists, if not create it
1367 $trashdir = db_query_params('select groupname from doc_groups where groupname = $1 and group_id = $2',
1368 array('.trash', $this->getID()));
1369 if ($trashdir && db_numrows($trashdir) == 0) {
1370 $resinsert = db_query_params('insert into doc_groups (groupname, group_id, stateid) values ($1, $2, $3)',
1371 array('.trash', $this->getID(), '2'));
1377 $this->data_array['use_docman'] = $booleanparam;
1386 * useDocmanSearch - whether or not this group has opted to use docman search engine.
1388 * @return bool use_docman_search.
1390 function useDocmanSearch() {
1391 if (forge_get_config('use_docman')) {
1392 return $this->data_array['use_docman_search'];
1398 * useWebdav - whether or not this group has opted to use webdav interface.
1400 * @return bool use_docman_search.
1402 function useWebdav() {
1403 if (forge_get_config('use_webdav')) {
1404 return $this->data_array['use_webdav'];
1410 * usesFTP - whether or not this group has opted to use FTP.
1412 * @return bool uses_ftp.
1414 function usesFTP() {
1415 if (forge_get_config('use_ftp')) {
1416 return $this->data_array['use_ftp'];
1422 * usesSurvey - whether or not this group has opted to use surveys.
1424 * @return bool uses_survey.
1426 function usesSurvey() {
1427 if (forge_get_config('use_survey')) {
1428 return $this->data_array['use_survey'];
1434 * usesPM - whether or not this group has opted to Project Manager.
1436 * @return bool uses_projman.
1439 if (forge_get_config('use_pm')) {
1440 return $this->data_array['use_pm'];
1446 * setUsePM - Set the PM usage
1448 * @param bool $booleanparam enabled/disabled
1451 function setUsePM($booleanparam) {
1453 $booleanparam = $booleanparam ? 1 : 0;
1454 $res = db_query_params('UPDATE groups SET use_pm=$1 WHERE group_id=$2',
1455 array($booleanparam, $this->getID()));
1457 $this->data_array['use_pm']=$booleanparam;
1466 * getPlugins - get a list of all available group plugins
1468 * @return array array containing plugin_id => plugin_name
1470 function getPlugins() {
1471 if (!isset($this->plugins_data)) {
1472 $this->plugins_data = array();
1473 $res = db_query_params('SELECT group_plugin.plugin_id, plugins.plugin_name
1474 FROM group_plugin, plugins
1475 WHERE group_plugin.group_id=$1
1476 AND group_plugin.plugin_id=plugins.plugin_id', array($this->getID()));
1477 $rows = db_numrows($res);
1479 for ($i=0; $i<$rows; $i++) {
1480 $plugin_id = db_result($res, $i, 'plugin_id');
1481 $this->plugins_data[$plugin_id] = db_result($res, $i, 'plugin_name');
1484 return $this->plugins_data;
1488 * usesPlugin - returns true if the group uses a particular plugin
1490 * @param string $pluginname name of the plugin
1491 * @return bool whether plugin is being used or not
1493 function usesPlugin($pluginname) {
1494 $plugins_data = $this->getPlugins();
1495 foreach ($plugins_data as $p_name) {
1496 if ($p_name == $pluginname) {
1504 * added for Codendi compatibility
1505 * usesServices - returns true if the group uses a particular plugin or feature
1507 * @param string $feature name of the plugin
1508 * @return bool whether plugin is being used or not
1510 function usesService($feature) {
1511 $plugins_data = $this->getPlugins();
1512 $pm = plugin_manager_get_object();
1513 foreach ($plugins_data as $p_name) {
1514 if ($p_name == $feature) {
1517 if (is_object($pm->getPluginByName($p_name)) && $pm->getPluginByName($p_name)->provide($feature)) {
1525 * setPluginUse - enables/disables plugins for the group
1527 * @param string $pluginname name of the plugin
1528 * @param bool $val the new state
1529 * @return string database result
1531 function setPluginUse($pluginname, $val=true) {
1532 if ($val == $this->usesPlugin($pluginname)) {
1533 // State is already good, returning
1536 $res = db_query_params('SELECT plugin_id FROM plugins WHERE plugin_name=$1',
1537 array($pluginname));
1538 $rows = db_numrows($res);
1540 // Error: no plugin by that name
1543 $plugin_id = db_result($res,0,'plugin_id');
1545 unset($this->plugins_data);
1547 $res = db_query_params('INSERT INTO group_plugin (group_id, plugin_id) VALUES ($1, $2)',
1548 array($this->getID(),
1551 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1 AND plugin_id=$2',
1552 array($this->getID(),
1555 $this->normalizeAllRoles();
1556 $hook_params = array();
1557 $hook_params['group_id'] = $this->getID();
1558 $hook_params['val'] = $val;
1559 plugin_hook("group_plugin_use", $hook_params);
1564 * getDocEmailAddress - get email address(es) to send doc notifications to.
1566 * @return string email address.
1568 function getDocEmailAddress() {
1569 return $this->data_array['new_doc_address'];
1572 function setDocEmailAddress($email) {
1573 $invalid_mails = validate_emails($email);
1574 if (count($invalid_mails) > 0) {
1575 $this->setError(sprintf(ngettext('New Doc Address Appeared Invalid: %s', 'New Doc Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
1579 $res = db_query_params('UPDATE groups SET new_doc_address = $1 WHERE group_id = $2',
1580 array($email, $this->getID()));
1583 $this->setError(_('Error')._(': ')._('Cannot Update Group new_doc_address')._(': ').db_error());
1587 $this->data_array['new_doc_address'] = $email;
1593 * docEmailAll - whether or not this group has opted to use receive notices on all doc updates.
1595 * @return bool email_on_all_doc_updates.
1597 function docEmailAll() {
1598 return $this->data_array['send_all_docs'];
1601 function setDocEmailAll($status) {
1603 $res = db_query_params('UPDATE groups SET send_all_docs = $1 WHERE group_id = $2',
1604 array($status, $this->getID()));
1607 $this->setError(_('Error')._(': ')._('Cannot Update Group send_all_docs')._(': ').db_error());
1611 $this->data_array['send_all_docs'] = $status;
1617 * getFRSEmailAddress - get email address(es) to send FRS notifications to.
1619 * @return string email address.
1621 function getFRSEmailAddress() {
1622 return $this->data_array['new_frs_address'];
1625 function setFRSEmailAddress($email) {
1626 $invalid_mails = validate_emails($email);
1627 if (count($invalid_mails) > 0) {
1628 $this->setError(sprintf(ngettext('New FRS Address Appeared Invalid: %s', 'New FRS Addresses Appeared Invalid: %s', count($invalid_mails)),implode(',',$invalid_mails)));
1632 $res = db_query_params('UPDATE groups SET new_frs_address = $1 WHERE group_id = $2',
1633 array($email, $this->getID()));
1636 $this->setError(_('Error')._(': ')._('Cannot Update Group new_frs_address')._(': ').db_error());
1640 $this->data_array['new_frs_address'] = $email;
1646 * frsEmailAll - whether or not this group has opted to use receive notices on all frs updates.
1648 * @return bool email_on_all_frs_updates.
1650 function frsEmailAll() {
1651 return $this->data_array['send_all_frs'];
1654 function setFRSEmailAll($status) {
1656 $res = db_query_params('UPDATE groups SET send_all_frs = $1 WHERE group_id = $2',
1657 array($status, $this->getID()));
1660 $this->setError(_('Error')._(': ')._('Cannot Update Group send_frs_docs')._(': ').db_error());
1664 $this->data_array['send_frs_docs'] = $status;
1670 * getHomePage - The URL for this project's home page.
1672 * @return string homepage URL.
1674 function getHomePage() {
1675 if (!preg_match("/^[a-zA-Z][a-zA-Z0-9+.-]*:/", $this->data_array['homepage'])) {
1676 $this->data_array['homepage'] = 'http://'.$this->data_array['homepage'];
1678 return $this->data_array['homepage'];
1682 * setHomepage - the hostname of the website url where this project is located.
1684 * @param string $homepage The name of the new HOMEPAGE
1687 function setHomepage($homepage) {
1688 if ($homepage == $this->data_array['homepage']) {
1693 $res = db_query_params('UPDATE groups SET homepage=$1 WHERE group_id=$2', array($homepage, $this->getID()));
1695 $this->addHistory('homepage', $this->data_array['homepage']);
1696 $this->data_array['homepage'] = $homepage;
1701 $this->setError(_('Could not insert homepage to database'));
1704 $this->setError(_('Homepage cannot be empty'));
1709 * getTags - Tags of this project.
1711 * @return string List of tags. Comma separated
1713 function getTags() {
1714 $sql = 'SELECT name FROM project_tags WHERE group_id = $1';
1715 $res = db_query_params($sql, array($this->getID()));
1716 return join(', ', util_result_column_to_array($res));
1720 * setTags - Set tags of this project.
1722 * @param string $tags
1723 * @return string database result.
1725 function setTags($tags) {
1727 $sql = 'DELETE FROM project_tags WHERE group_id=$1';
1728 $res = db_query_params($sql, array($this->getID()));
1730 $this->setError(_('Deleting old tags')._(': ').db_error());
1734 $inserted = array();
1735 $tags_array = preg_split('/[;,]/', $tags);
1736 foreach ($tags_array as $tag) {
1737 $tag = preg_replace('/[\t\r\n]/', ' ', $tag);
1738 // Allowed caracteres: [A-Z][a-z][0-9] -_&'#+.
1739 if (preg_match('/[^[:alnum:]| |\-|_|\&|\'|#|\+|\.]/', $tag)) {
1740 $this->setError(_('Bad tag name, you only can use the following characters: [A-Z][a-z][0-9]-_&\'#+. and space'));
1745 if ($tag == '' || array_search($tag, $inserted) !== false) {
1748 $sql = 'INSERT INTO project_tags (group_id,name) VALUES ($1, $2)';
1749 $res = db_query_params($sql, array($this->getID(), $tag));
1751 $this->setError(_('Setting tags')._(': ').db_error());
1762 * getPermission - Return a Permission for this Group
1764 * @return object The Permission.
1766 function &getPermission() {
1767 return permission_get_object($this);
1770 function delete($sure, $really_sure, $really_really_sure) {
1771 if (!$sure || !$really_sure || !$really_really_sure) {
1772 $this->setMissingParamsError(_('Please tick all checkboxes.'));
1775 if ($this->getID() == GROUP_IS_NEWS ||
1776 $this->getID() == GROUP_IS_MASTER ||
1777 $this->getID() == GROUP_IS_STATS ||
1778 $this->getID() == GROUP_IS_PEER_RATINGS) {
1779 $this->setError(_('Cannot Delete System Group'));
1782 $perm = $this->getPermission();
1783 if (!$perm || !is_object($perm)) {
1784 $this->setPermissionDeniedError();
1786 } elseif ($perm->isError() || !$perm->isSuperUser()) {
1787 $this->setPermissionDeniedError();
1793 // Remove all the members
1795 $members = $this->getMembers(false);
1796 foreach ($members as $i) {
1797 if(!$this->removeUser($i->getID())) {
1798 $this->setError(_('Could not properly remove member')._(': ').$i->getID());
1803 // unlink roles from this project
1804 foreach ($this->getRoles() as $r) {
1805 if ($r->getHomeProject() == NULL
1806 || $r->getHomeProject()->getID() != $this->getID()) {
1807 $r->unlinkProject($this);
1814 $atf = new ArtifactTypeFactory($this, true);
1815 $at_arr = $atf->getArtifactTypes();
1816 foreach ($at_arr as $i) {
1817 if (!is_object($i)) {
1820 if (!$i->delete(1,1)) {
1821 $this->setError(_('Could not properly delete the tracker')._(': ').$i->getErrorMessage());
1828 $rmf = new RoadmapFactory($this);
1829 $rm_arr = $rmf->getRoadmaps();
1830 foreach ($rm_arr as $i) {
1831 if (!is_object($i)) {
1834 if (!$i->delete()) {
1835 $this->setError(_('Could not properly delete the roadmap')._(': ').$i->getErrorMessage());
1843 $ff = new ForumFactory($this, true);
1844 $f_arr = $ff->getForums();
1845 foreach ($f_arr as $i) {
1846 if (!is_object($i)) {
1849 if(!$i->delete(1,1)) {
1850 $this->setError(_('Could not properly delete the forum')._(': ').$i->getErrorMessage());
1856 // Delete Subprojects
1858 $pgf = new ProjectGroupFactory($this, true);
1859 $pg_arr = $pgf->getProjectGroups();
1860 foreach ($pg_arr as $i) {
1861 if (!is_object($i)) {
1864 if (!$i->delete(1,1)) {
1865 $this->setError(_('Could not properly delete the ProjectGroup')._(': ').$i->getErrorMessage());
1871 // Delete FRS Packages
1873 $res = db_query_params('SELECT * FROM frs_package WHERE group_id=$1',
1874 array($this->getID()));
1876 $this->setError(_('Error FRS Packages')._(': ').db_error());
1881 while ($arr = db_fetch_array($res)) {
1882 $frsp=new FRSPackage($this, $arr['package_id'], $arr);
1883 if (!$frsp->delete(1, 1)) {
1884 $this->setError(_('Could not properly delete the FRSPackage')._(': ').$frsp->getErrorMessage());
1891 $news_group = group_get_object(GROUP_IS_NEWS);
1892 $res = db_query_params('SELECT forum_id FROM news_bytes WHERE group_id=$1',
1893 array($this->getID()));
1895 $this->setError(_('Error Deleting News')._(': ').db_error());
1900 for ($i=0; $i<db_numrows($res); $i++) {
1901 $Forum = new Forum($news_group,db_result($res,$i,'forum_id'));
1902 if (!$Forum->delete(1,1)) {
1903 $this->setError(_('Could not delete News Forum')._(': ').$Forum->getID());
1908 // Delete news forums in group itself
1909 for ($i = 0; $i < db_numrows($res); $i++) {
1910 $Forum = new Forum($this, db_result($res, $i, 'forum_id'));
1911 if (!$Forum->delete(1, 1)) {
1912 $this->setError(_('Could not delete News Forum')._(': ').$Forum->getID());
1917 $res = db_query_params('DELETE FROM news_bytes WHERE group_id=$1',
1918 array($this->getID()));
1920 $this->setError(_('Error Deleting News')._(': ').db_error());
1928 $res = db_query_params('DELETE FROM doc_data WHERE group_id=$1',
1929 array($this->getID()));
1931 $this->setError(_('Error Deleting Documents')._(': ').db_error());
1936 $res = db_query_params('DELETE FROM doc_groups WHERE group_id=$1',
1937 array($this->getID()));
1939 $this->setError(_('Error Deleting Document Groups')._(': ').db_error());
1947 $res=db_query_params('DELETE FROM project_tags WHERE group_id=$1', array($this->getID()));
1949 $this->setError(_('Error Deleting Tags')._(': ').db_error());
1955 // Delete group history
1957 $res = db_query_params('DELETE FROM group_history WHERE group_id=$1',
1958 array($this->getID()));
1960 $this->setError(_('Error Deleting Project History')._(': ').db_error());
1966 // Delete group plugins
1968 $res = db_query_params('DELETE FROM group_plugin WHERE group_id=$1',
1969 array($this->getID()));
1971 $this->setError(_('Error Deleting Project Plugins')._(': ').db_error());
1977 // Delete group cvs stats
1979 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE group_id=$1',
1980 array($this->getID()));
1982 $this->setError(_('Error Deleting SCM Statistics')._(': ').db_error());
1990 $sf = new SurveyFactory($this, true);
1991 $s_arr =& $sf->getSurveys();
1992 foreach ($s_arr as $i) {
1993 if (!is_object($i)) {
1996 if (!$i->delete()) {
1997 $this->setError(_('Could not properly delete the survey'));
2002 // Delete SurveyQuestions
2004 $sqf = new SurveyQuestionFactory($this);
2005 $sq_arr = $sqf->getSurveyQuestions();
2006 if (is_array($sq_arr)) {
2007 foreach ($sq_arr as $i) {
2008 if (!is_object($i)) {
2011 if (!$i->delete()) {
2012 $this->setError(_('Could not properly delete the survey questions'));
2020 // Delete Mailing List Factory
2022 $mlf = new MailingListFactory($this, true);
2023 $ml_arr = $mlf->getMailingLists();
2024 foreach ($ml_arr as $i) {
2025 if (!is_object($i)) {
2028 if (!$i->delete(1,1)) {
2029 $this->setError(_('Could not properly delete the mailing list'));
2037 $res = db_query_params('DELETE FROM trove_group_link WHERE group_id=$1',
2038 array($this->getID()));
2040 $this->setError(_('Error Deleting Trove')._(': ').db_error());
2045 $res = db_query_params('DELETE FROM trove_agg WHERE group_id=$1',
2046 array($this->getID()));
2048 $this->setError(_('Error Deleting Trove')._(': ').db_error());
2056 $res = db_query_params('DELETE FROM project_sums_agg WHERE group_id=$1',
2057 array($this->getID()));
2059 $this->setError(_('Error Deleting Counters')._(': ').db_error());
2064 $res = db_query_params('INSERT INTO deleted_groups (unix_group_name, delete_date, isdeleted) VALUES ($1, $2, $3)',
2065 array($this->getUnixName(),
2069 $this->setError(_('Error Deleting Project')._(': ').db_error());
2074 // Remove users & delete roles from this project
2075 $members = $this->getMembers();
2076 foreach ($members as $userObject) {
2077 $this->removeUser($userObject->getID());
2079 $localRolesId = $this->getRolesId(false);
2080 foreach ($localRolesId as $localRoleId) {
2081 $roleObject = new Role($this, $localRoleId);
2082 $roleObject->delete();
2084 // Delete entry in groups.
2085 $res = db_query_params('DELETE FROM groups WHERE group_id=$1',
2086 array($this->getID()));
2088 $this->setError(_('Error Deleting Project')._(': ').db_error());
2095 $hook_params = array();
2096 $hook_params['group'] = $this;
2097 $hook_params['group_id'] = $this->getID();
2098 plugin_hook("group_delete", $hook_params);
2100 if (forge_get_config('upload_dir') != '' && $this->getUnixName()) {
2101 exec('/bin/rm -rf '.forge_get_config('upload_dir').'/'.$this->getUnixName().'/');
2103 if (forge_get_config('ftp_upload_dir') != '' && $this->getUnixName()) {
2104 exec('/bin/rm -rf '.forge_get_config('ftp_upload_dir').'/'.$this->getUnixName().'/');
2109 db_query_params('DELETE FROM rep_group_act_monthly WHERE group_id=$1', array($this->getID()));
2110 db_query_params('DELETE FROM rep_group_act_weekly WHERE group_id=$1', array($this->getID()));
2111 db_query_params('DELETE FROM rep_group_act_daily WHERE group_id=$1', array($this->getID()));
2112 unset($this->data_array);
2117 Basic functions to add/remove users to/from a group
2118 and update their permissions
2122 * addUser - controls adding a user to a group.
2124 * @param string $user_identifier Unix name of the user to add OR integer user_id.
2125 * @param int $role_id The role_id this user should have.
2126 * @return bool success.
2129 function addUser($user_identifier, $role_id) {
2132 Admins can add users to groups
2135 if (!forge_check_perm ('project_admin', $this->getID())) {
2136 $this->setPermissionDeniedError();
2142 get user id for this user's unix_name
2144 if (is_int ($user_identifier)) { // user_id or user_name
2145 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_id=$1', array($user_identifier));
2147 $res_newuser = db_query_params ('SELECT * FROM users WHERE user_name=$1', array($user_identifier));
2149 if (db_numrows($res_newuser) > 0) {
2151 // make sure user is active
2153 if (db_result($res_newuser,0,'status') != 'A') {
2154 $this->setError(_('User is not active. Only active users can be added.'));
2160 // user was found - set new user_id var
2162 $user_id = db_result($res_newuser,0,'user_id');
2164 $role = new Role($this, $role_id);
2165 if (!$role || !is_object($role)) {
2166 $this->setError(_('Error Getting Role Object'));
2169 } elseif ($role->isError()) {
2170 $this->setError('addUser::roleget::'.$role->getErrorMessage());
2175 $role->addUser(user_get_object($user_id));
2176 if (!$SYS->sysCheckCreateGroup($this->getID())){
2177 $this->setError($SYS->getErrorMessage());
2181 if (!$SYS->sysCheckCreateUser($user_id)) {
2182 $this->setError($SYS->getErrorMessage());
2186 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
2187 $this->setError($SYS->getErrorMessage());
2193 // user doesn't exist
2195 $this->setError(_('That user does not exist.'));
2200 $hook_params['group'] = $this;
2201 $hook_params['group_id'] = $this->getID();
2202 $hook_params['user'] = user_get_object($user_id);
2203 $hook_params['user_id'] = $user_id;
2204 plugin_hook ("group_adduser", $hook_params);
2209 $this->addHistory(_('Added User'), $user_identifier);
2215 $add_u = user_get_object($user_id);
2217 if (is_array($this->membersArr)) {
2218 foreach ($this->membersArr as $u) {
2219 if ($u->getID() == $add_u->getID()) {
2225 $this->membersArr[] = $add_u;
2233 * removeUser - controls removing a user from a group.
2235 * Users can remove themselves.
2237 * @param int $user_id The ID of the user to remove.
2238 * @return bool success.
2240 function removeUser($user_id) {
2243 if ($user_id != user_getid()
2244 && !forge_check_perm('project_admin', $this->getID())) {
2245 $this->setPermissionDeniedError();
2251 $user = user_get_object($user_id);
2252 $roles = RBACEngine::getInstance()->getAvailableRolesForUser($user);
2253 $found_roles = array();
2254 foreach ($roles as $role) {
2255 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2256 $found_roles[] = $role;
2259 if (empty($found_roles)) {
2260 $this->setError(_('Error')._(': ')._('User not removed')._(': ').$user_id);
2264 foreach ($found_roles as $found_role) {
2265 $found_role->removeUser($user);
2266 if (!$SYS->sysGroupCheckUser($this->getID(), $user_id)) {
2267 $this->setError($SYS->getErrorMessage());
2274 // reassign open artifacts to id=100
2276 $res = db_query_params('UPDATE artifact SET assigned_to=100
2277 WHERE group_artifact_id
2278 IN (SELECT group_artifact_id
2279 FROM artifact_group_list
2280 WHERE group_id=$1 AND status_id=1 AND assigned_to=$2)',
2281 array($this->getID(),
2284 $this->setError(_('Error')._(': ')._('artifact')._(': ').db_error());
2290 // reassign open tasks to id=100
2291 // first have to purge any assignments that would cause
2292 // conflict with existing assignment to 100
2294 $res = db_query_params('DELETE FROM project_assigned_to
2295 WHERE project_task_id IN (SELECT pt.project_task_id
2296 FROM project_task pt, project_group_list pgl, project_assigned_to pat
2297 WHERE pt.group_project_id = pgl.group_project_id
2298 AND pat.project_task_id=pt.project_task_id
2299 AND pt.status_id=1 AND pgl.group_id=$1
2300 AND pat.assigned_to_id=$2)
2301 AND assigned_to_id=100',
2302 array($this->getID(),
2305 $this->setError(_('Error')._(': ').sprintf(_('project_assigned_to %d: %s'), 1, db_error()));
2309 $res = db_query_params('UPDATE project_assigned_to SET assigned_to_id=100
2310 WHERE project_task_id IN (SELECT pt.project_task_id
2311 FROM project_task pt, project_group_list pgl
2312 WHERE pt.group_project_id = pgl.group_project_id
2313 AND pt.status_id=1 AND pgl.group_id=$1)
2314 AND assigned_to_id=$2',
2315 array($this->getID(),
2318 $this->setError(_('Error')._(': ').sprintf(_('project_assigned_to %d: %s'), 2, db_error()));
2324 // Remove user from system
2326 if (!$SYS->sysGroupRemoveUser($this->getID(), $user_id)) {
2327 $this->setError($SYS->getErrorMessage());
2332 $hook_params['group'] = $this;
2333 $hook_params['group_id'] = $this->getID();
2334 $hook_params['user'] = user_get_object($user_id);
2335 $hook_params['user_id'] = $user_id;
2336 plugin_hook("group_removeuser", $hook_params);
2339 $this->addHistory(_('Removed User'), $user_id);
2346 $del_u = user_get_object($user_id);
2347 foreach ($this->membersArr as $k => $u) {
2348 if ($u->getID() == $del_u->getID()) {
2349 unset($this->membersArr[$k]);
2358 * updateUser - controls updating a user's role in this group.
2360 * @param int $user_id The ID of the user.
2361 * @param int $role_id The role_id to set this user to.
2362 * @return bool success.
2364 function updateUser($user_id, $role_id) {
2366 if (!forge_check_perm ('project_admin', $this->getID())) {
2367 $this->setPermissionDeniedError();
2371 $newrole = RBACEngine::getInstance()->getRoleById ($role_id);
2372 if (!$newrole || !is_object($newrole)) {
2373 $this->setError(_('Could Not Get Role'));
2375 } elseif ($newrole->isError()) {
2376 $this->setError(_('Role')._(': ').$newrole->getErrorMessage());
2378 } elseif ($newrole->getHomeProject() == NULL
2379 || $newrole->getHomeProject()->getID() != $this->getID()) {
2380 $this->setError(_('Wrong destination role'));
2383 $user = user_get_object ($user_id);
2384 $roles = RBACEngine::getInstance()->getAvailableRolesForUser ($user);
2386 foreach ($roles as $role) {
2387 if ($role->getHomeProject() && $role->getHomeProject()->getID() == $this->getID()) {
2388 $found_role = $role;
2392 if ($found_role == NULL) {
2393 $this->setError(_('Error')._(': ')._('User not removed')._(': ').$user_id);
2397 $found_role->removeUser ($user);
2398 $newrole->addUser ($user);
2400 $this->addHistory(_('Updated User'), $user_id);
2405 * addHistory - Makes an audit trail entry for this project.
2407 * @param string $field_name The name of the field.
2408 * @param string $old_value The Old Value for this $field_name.
2409 * @return resource database result handle.
2412 function addHistory($field_name, $old_value) {
2413 if ($old_value == NULL) {
2417 return db_query_params('INSERT INTO group_history(group_id,field_name,old_value,mod_by,adddate)
2418 VALUES ($1,$2,$3,$4,$5)',
2419 array($this->getID(),
2427 * activateUsers - Make sure that group members have unix accounts.
2429 * Setup unix accounts for group members. Can be called even
2430 * if members are already active.
2434 function activateUsers() {
2436 Activate member(s) of the project
2441 $members = $this->getUsers(true);
2443 foreach ($members as $member) {
2444 $user_id = $member->getID();
2446 if (!$SYS->sysCheckCreateGroup($this->getID())){
2447 $this->setError($SYS->getErrorMessage());
2451 if (!$SYS->sysCheckCreateUser($user_id)) {
2452 $this->setError($SYS->getErrorMessage());
2456 if (!$SYS->sysGroupCheckUser($this->getID(),$user_id)) {
2457 $this->setError($SYS->getErrorMessage());
2467 * getMembers - returns array of User objects for this project
2469 * @param bool $onlyactive Only users with state active, or all users of group
2470 * @return array of User objects for this group.
2472 function getMembers($onlyactive = true) {
2473 return $this->getUsers(true, $onlyactive);
2477 * replaceTemplateStrings - fill-in some blanks with project name
2479 * @param string $string Template string
2480 * @return string String after replacements
2482 function replaceTemplateStrings($string) {
2483 $string = str_replace('UNIXNAME', $this->getUnixName(), $string);
2484 $string = str_replace('PUBLICNAME', $this->getPublicName(), $string);
2485 $string = str_replace('DESCRIPTION', $this->getDescription(), $string);
2490 * approve - Approve pending project.
2492 * @param object $user The User object who is doing the updating.
2496 function approve(&$user) {
2497 global $gfcommon,$gfwww;
2498 require_once $gfcommon.'widget/WidgetLayoutManager.class.php';
2500 if ($this->getStatus()=='A') {
2501 $this->setError(_('Group already active'));
2507 // Step 1: Activate group and create LDAP entries
2508 if (!$this->setStatus($user, 'A')) {
2515 // Switch to system language for item creation
2516 setup_gettext_from_sys_lang();
2518 // Create default roles
2519 $idadmin_group = NULL;
2520 foreach (get_group_join_requests ($this) as $gjr) {
2521 $idadmin_group = $gjr->getUserID();
2524 if ($idadmin_group == NULL) {
2525 $idadmin_group = $user->getID();
2528 $template = $this->getTemplateProject();
2529 $id_mappings = array();
2530 $seen_admin_role = false;
2532 // Copy roles from template project
2533 foreach($template->getRoles() as $oldrole) {
2534 if ($oldrole->getHomeProject() != NULL) {
2535 $role = new Role($this);
2537 // Need to use a different role name so that the permissions aren't set from the hardcoded defaults
2538 $role->create('TEMPORARY ROLE NAME', $data, true);
2539 $role->setName($oldrole->getName());
2540 if ($oldrole->getSetting ('project_admin', $template->getID())) {
2541 $seen_admin_role = true;
2545 $role->linkProject($this);
2547 $id_mappings['role'][$oldrole->getID()] = $role->getID();
2548 // Reuse the project_admin permission
2549 $role->setSetting ('project_admin', $this->getID(), $oldrole->getSetting ('project_admin', $template->getID()));
2553 if (!$seen_admin_role) {
2554 $role = new Role($this);
2555 $adminperms = array('project_admin' => array ($this->getID() => 1));
2556 $role->create ('Admin', $adminperms, true);
2559 $roles = $this->getRoles();
2560 foreach ($roles as $r) {
2561 if ($r->getHomeProject() == NULL) {
2564 if ($r->getSetting ('project_admin', $this->getID())) {
2565 $r->addUser(user_get_object ($idadmin_group));
2569 // Temporarily switch to the submitter's identity
2570 $saved_session = session_get_user();
2571 session_set_internal($idadmin_group);
2574 if (forge_get_config('use_tracker')) {
2575 $this->setUseTracker ($template->usesTracker());
2576 if ($template->usesTracker()) {
2577 $oldatf = new ArtifactTypeFactory($template);
2578 foreach ($oldatf->getArtifactTypes() as $o) {
2579 $t = new ArtifactType ($this);
2580 $t->create ($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->emailAll(),$o->getEmailAddress(),$o->getDuePeriod()/86400,0,$o->getSubmitInstructions(),$o->getBrowseInstructions());
2581 $id_mappings['tracker'][$o->getID()] = $t->getID();
2586 if (forge_get_config('use_pm')) {
2587 $this->setUsePM ($template->usesPM());
2588 if ($template->usesPM()) {
2589 $oldpgf = new ProjectGroupFactory($template);
2590 foreach ($oldpgf->getProjectGroups() as $o) {
2591 $pg = new ProjectGroup($this);
2592 $pg->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo());
2593 $id_mappings['pm'][$o->getID()] = $pg->getID();
2598 if (forge_get_config('use_forum')) {
2599 $this->setUseForum($template->usesForum());
2600 if ($template->usesForum()) {
2601 $oldff = new ForumFactory($template);
2602 foreach ($oldff->getForums() as $o) {
2603 $f = new Forum($this);
2604 $f->create($this->replaceTemplateStrings($o->getName()),$this->replaceTemplateStrings($o->getDescription()),$o->getSendAllPostsTo(),1);
2605 $id_mappings['forum'][$o->getID()] = $f->getID();
2610 if (forge_get_config('use_docman')) {
2611 $this->setUseDocman($template->usesDocman());
2612 if ($template->usesDocman()) {
2613 $olddgf = new DocumentGroupFactory($template);
2614 // First pass: create all docgroups
2615 $id_mappings['docman_docgroup'][0] = 0;
2616 foreach ($olddgf->getDocumentGroups(array(1, 5)) as $o) {
2617 $ndg = new DocumentGroup($this);
2618 // .trash is a reserved directory
2619 if ($o->getName() != '.trash') {
2620 $ndg->create($this->replaceTemplateStrings($o->getName()), 0, 1, null, true);
2621 $id_mappings['docman_docgroup'][$o->getID()] = $ndg->getID();
2624 // Second pass: restore hierarchy links & stateid
2625 foreach ($olddgf->getDocumentGroups(array(1, 5)) as $o) {
2626 $ndgf = new DocumentGroup($this);
2627 if ($o->getName() != '.trash') {
2628 $ndgf->fetchData($id_mappings['docman_docgroup'][$o->getID()]);
2629 $ndgf->update($ndgf->getName(), $id_mappings['docman_docgroup'][$o->getParentID()], 0, $o->getState());
2635 if (forge_get_config('use_frs')) {
2636 $this->setUseFRS ($template->usesFRS());
2637 if ($template->usesFRS()) {
2638 foreach (get_frs_packages($template) as $o) {
2639 $newp = new FRSPackage($this);
2640 $nname = $this->replaceTemplateStrings($o->getName());
2641 $newp->create($nname, $o->isPublic());
2642 $id_mappings['frs'][$o->getID()] = $newp->getID();
2643 foreach(get_frs_releases($o) as $or) {
2644 $newr = new FRSRelease($newp);
2645 $newr->create($this->replaceTemplateStrings($or->getName()), $this->replaceTemplateStrings($or->getNotes()), $this->replaceTemplateStrings($or->getChanges()), $or->getPreformatted());
2646 $id_mappings['frs_release'][$or->getID()] = $newr->getID();
2652 if (forge_get_config('use_mail')) {
2653 $this->setUseMail($template->usesMail());
2654 if ($template->usesMail()) {
2655 $oldmlf = new MailingListFactory($template);
2656 foreach ($oldmlf->getMailingLists() as $o) {
2657 $ml = new MailingList($this);
2658 $nname = preg_replace ('/^'.$template->getUnixName().'-/','',$o->getName());
2660 $ndescription = $this->replaceTemplateStrings($o->getDescription());
2661 $ml->create($nname, $ndescription, $o->isPublic());
2667 /* use SCM plugin from template group */
2668 $this->setUseSCM($template->usesSCM());
2670 foreach ($template->getPlugins() as $plugin_name) {
2671 $this->setPluginUse($plugin_name);
2674 /* use SCM choice from registration page */
2675 foreach ($template->getPlugins() as $plugin_name) {
2676 if (substr($plugin_name, 3) == 'scm' && $plugin_name != 'scmhook') {
2677 /* skip copying scm plugins */
2680 /* enable other plugins though */
2681 $this->setPluginUse($plugin_name);
2685 foreach ($template->getRoles() as $oldrole) {
2686 $newrole = RBACEngine::getInstance()->getRoleById($id_mappings['role'][$oldrole->getID()]);
2687 if ($oldrole->getHomeProject() != NULL
2688 && $oldrole->getHomeProject()->getID() == $template->getID()) {
2689 $newrole->setPublic ($oldrole->isPublic());
2691 $oldsettings = $oldrole->getSettingsForProject ($template);
2693 $sections = array('project_read', 'project_admin', 'scm', 'docman', 'tracker_admin', 'new_tracker', 'forum_admin', 'new_forum', 'pm_admin', 'new_pm', 'frs_admin', 'new_frs', 'members');
2694 foreach ($sections as $section) {
2695 $newrole->setSetting ($section, $this->getID(), $oldsettings[$section][$template->getID()]);
2698 $sections = array('tracker', 'pm', 'forum', 'frs');
2699 foreach ($sections as $section) {
2700 if (isset ($oldsettings[$section])) {
2701 foreach ($oldsettings[$section] as $k => $v) {
2702 // Only copy perms for tools that have been copied
2703 if (isset ($id_mappings[$section][$k])) {
2704 $newrole->setSetting ($section,
2705 $id_mappings[$section][$k],
2713 $lm = new WidgetLayoutManager();
2714 $lm->createDefaultLayoutForProject($this->getID(), $template->getID());
2716 // second computation to clone fields and workflow
2717 if (forge_get_config('use_tracker')) {
2718 if ($template->usesTracker()) {
2719 $oldatf = new ArtifactTypeFactory($template);
2720 foreach ($oldatf->getArtifactTypes() as $o) {
2721 $t = artifactType_get_object($id_mappings['tracker'][$o->getID()]);
2722 $id_mappings['tracker'][$o->getID()] = $t->getID();
2723 $newEFIds = $t->cloneFieldsFrom($o->getID(), $id_mappings);
2724 if (forge_get_config('use_tracker_widget_display')) {
2725 $lm->createDefaultLayoutForTracker($t->getID(), $o->getID(), $newEFIds);
2732 $params['template'] = $template;
2733 $params['project'] = $this;
2734 $params['id_mappings'] = $id_mappings;
2735 plugin_hook_by_reference ('clone_project_from_template', $params);
2737 // Disable everything - except use_scm (manually set in the registration page)
2738 db_query_params ('UPDATE groups SET use_mail = 0, use_survey = 0, use_forum = 0, use_pm = 0, use_pm_depend_box = 0, use_news = 0,
2739 use_docman = 0, use_ftp = 0, use_tracker = 0, use_frs = 0, use_stats = 0 WHERE group_id = $1',
2740 array($this->getID()));
2743 $this->normalizeAllRoles();
2744 // empty members cache because the group creator is not yet in cache.
2745 unset($this->membersArr);
2746 $this->activateUsers();
2748 // Delete fake join request
2749 foreach (get_group_join_requests ($this) as $gjr) {
2753 // Switch back to user preference
2754 session_set_internal($saved_session->getID());
2755 setup_gettext_from_context();
2757 $this->sendApprovalEmail();
2758 $this->addHistory(_('Approved'), 'x');
2761 // Plugin can make approve operation there
2764 $params['group'] = $this;
2765 $params['group_id'] = $this->getID();
2766 plugin_hook('group_approved', $params);
2772 * sendApprovalEmail - Send new project email.
2774 * @return bool success.
2777 function sendApprovalEmail() {
2778 $admins = RBACEngine::getInstance()->getUsersByAllowedAction ('project_admin', $this->getID());
2780 if (empty($admins)) {
2781 $this->setError(_('Group does not have any administrators.'));
2785 // send one email per admin
2786 foreach ($admins as $admin) {
2787 setup_gettext_for_user ($admin);
2789 $message = sprintf(_('Your project registration for %s has been approved.'), forge_get_config ('forge_name')) . "\n\n"
2791 . _('Project Full Name')._(': '). htmlspecialchars_decode($this->getPublicName()) . "\n"
2792 . _('Project Unix Name')._(': '). $this->getUnixName() . "\n\n"
2794 . _('Your DNS will take up to a day to become active on our site. '
2795 .'Your web site is accessible through your shell account. Please read '
2796 .'site documentation (see link below) about intended usage, available '
2797 .'services, and directory layout of the account.') . "\n\n"
2799 . sprintf(_('If you visit your own project page in %s while logged in, '
2800 . 'you will find additional menu functions to your left labeled “Project Admin”.'),
2801 forge_get_config ('forge_name')) . "\n\n"
2803 . sprintf(_('We highly suggest that you now visit %1$s and create a public '
2804 . 'description for your project. This can be done by visiting your project '
2805 . 'page while logged in, and selecting “Project Admin” from the menus '
2806 . 'on the left (or by visiting %2$s after login).'),
2807 forge_get_config ('forge_name'), util_make_url('/project/admin/?group_id='.$this->getID())) . "\n\n"
2809 . sprintf(_('Your project will also not appear in the Trove Software Map (primary '
2810 . 'list of projects hosted on %s which offers great flexibility in '
2811 . 'browsing and search) until you categorize it in the project administration '
2812 . 'screens. So that people can find your project, you should do this now. '
2813 . 'Visit your project while logged in, and select “Project Admin” from the '
2814 . 'menus on the left.'), forge_get_config ('forge_name')) . "\n\n"
2816 . sprintf(_('Enjoy the system, and please tell others about %s. Let us know '
2817 . 'if there is anything we can do to help you.'), forge_get_config ('forge_name')) . "\n\n"
2819 . sprintf(_('-- the %s staff'), forge_get_config ('forge_name')) . "\n";
2821 util_send_message($admin->getEmail(), sprintf(_('%s Project Approved'), forge_get_config ('forge_name')), $message);
2823 setup_gettext_from_context();
2830 * sendRejectionEmail - Send project rejection email.
2832 * This function sends out a rejection message to a user who
2833 * registered a project.
2835 * @param int $response_id The id of the response to use.
2836 * @param string $message The rejection message.
2837 * @return bool completion status.
2840 function sendRejectionEmail($response_id, $message = 'zxcv') {
2841 $submitters = array();
2842 foreach (get_group_join_requests ($this) as $gjr) {
2843 $submitters[] = user_get_object($gjr->getUserID());
2846 if (empty($submitters)) {
2847 $this->setError(_('Group does not have any administrators.'));
2851 foreach ($submitters as $admin) {
2852 setup_gettext_for_user($admin);
2854 $response = sprintf(_('Your project registration for %s has been denied.'), forge_get_config('forge_name')) . "\n\n"
2855 . _('Project Full Name')._(': '). $this->getPublicName() . "\n"
2856 . _('Project Unix Name')._(': '). $this->getUnixName() . "\n\n"
2857 . _('Reasons for negative decision')._(': ') . "\n\n";
2859 // Check to see if they want to send a custom rejection response
2860 if ($response_id == 0) {
2861 $response .= $message;
2863 $response .= db_result(
2864 db_query_params('SELECT response_text FROM canned_responses WHERE response_id=$1', array($response_id)),
2869 util_send_message($admin->getEmail(), sprintf(_('%s Project Denied'), forge_get_config('forge_name')), $response);
2870 setup_gettext_from_context();
2877 * sendNewProjectNotificationEmail - Send new project notification email.
2879 * This function sends out a notification email to the
2880 * SourceForge admin user when a new project is
2883 * @return bool success.
2886 function sendNewProjectNotificationEmail() {
2887 // Get the user who wants to register the project
2888 $submitters = array();
2889 foreach (get_group_join_requests ($this) as $gjr) {
2890 $submitters[] = user_get_object($gjr->getUserID());
2892 if (empty($submitters)) {
2893 $this->setError(_('Could not find user who has submitted the project.'));
2897 $admins = RBACEngine::getInstance()->getUsersByAllowedAction('approve_projects', -1);
2899 if (empty($admins)) {
2900 $this->setError(_('There is no administrator to send the mail to.'));
2904 foreach ($admins as $admin) {
2905 $admin_email = $admin->getEmail();
2906 setup_gettext_for_user ($admin);
2908 $message = sprintf(_('New %s Project Submitted'), forge_get_config('forge_name')) . "\n\n"
2909 . _('Project Full Name')._(': ').htmlspecialchars_decode($this->getPublicName()) . "\n"
2910 . _('Submitted Description')._(': ').htmlspecialchars_decode($this->getRegistrationPurpose()) . "\n";
2912 foreach ($submitters as $submitter) {
2913 $message .= _('Submitter')._(': ').$submitter->getRealName().' ('.$submitter->getUnixName().')' . "\n\n";
2917 . _('Please visit the following URL to approve or reject this project')._(': '). "\n"
2918 . util_make_url('/admin/approve-pending.php');
2919 util_send_message($admin_email, sprintf(_('New %s Project Submitted'), forge_get_config('forge_name')), $message);
2920 setup_gettext_from_context();
2923 $email = $submitter->getEmail();
2924 setup_gettext_for_user ($submitter);
2926 $message = sprintf(_('New %s Project Submitted'), forge_get_config ('forge_name')) . "\n\n"
2927 . _('Project Full Name')._(': ') . $this->getPublicName() . "\n"
2928 . _('Submitted Description')._(': ') . util_unconvert_htmlspecialchars($this->getRegistrationPurpose()) . "\n\n"
2929 . sprintf(_('The %s admin team will now examine your project submission. You will be notified of their decision.'),
2930 forge_get_config ('web_host'));
2932 util_send_message($email, sprintf(_('New %s Project Submitted'), forge_get_config ('forge_name')), $message);
2933 setup_gettext_from_context();
2939 * validateGroupName - Validate the group name
2941 * @param string $group_name Project name.
2943 * @return bool an error false and set an error is the group name is invalid otherwise return true
2945 function validateGroupName($group_name) {
2946 if (strlen($group_name)<3) {
2947 $this->setError(_('Project name is too short'));
2949 } elseif (strlen(htmlspecialchars($group_name))>40) {
2950 $this->setError(_('Project name is too long'));
2952 } elseif (group_get_object_by_publicname($group_name)) {
2953 $this->setError(_('Project name already taken'));
2960 * getRolesId - Get Ids of the roles of the group.
2962 * @param bool all role ids or local role ids only. Default is all role ids
2963 * @return array Role ids of this group.
2965 function getRolesId($global = true) {
2966 $role_ids = array();
2968 $res = db_query_params('SELECT role_id FROM pfo_role WHERE home_group_id=$1',
2969 array($this->getID()));
2970 while ($arr = db_fetch_array($res)) {
2971 $role_ids[] = $arr['role_id'];
2974 $res = db_query_params('SELECT role_id FROM role_project_refs WHERE group_id=$1',
2975 array($this->getID()));
2976 while ($arr = db_fetch_array($res)) {
2977 $role_ids[] = $arr['role_id'];
2981 return array_unique($role_ids);
2985 * getRoles - Get the roles of the group.
2987 * @param bool all roles or local roles only. Default is all roles
2988 * @return array Roles of this group.
2990 function getRoles($global = true) {
2993 $roles = $this->getRolesId($global);
2994 $engine = RBACEngine::getInstance();
2995 foreach ($roles as $role_id) {
2996 $result[] = $engine->getRoleById($role_id);
3002 function normalizeAllRoles() {
3003 $roles = $this->getRoles();
3005 foreach ($roles as $r) {
3006 $r->normalizeData();
3011 * getUnixStatus - Status of activation of unix account.
3013 * @return string Values: (N)one, (A)ctive, (S)uspended or (D)eleted
3015 function getUnixStatus() {
3016 return $this->data_array['unix_status'];
3020 * setUnixStatus - Sets status of activation of unix account.
3022 * @param string $status The unix status.
3028 * @return bool success.
3030 function setUnixStatus($status) {
3033 $res = db_query_params ('UPDATE groups SET unix_status=$1 WHERE group_id=$2',
3038 $this->setError(_('Error')._(': ')._('Cannot Update Group Unix Status')._(': ').db_error());
3042 if ($status == 'A') {
3043 if (!$SYS->sysCheckCreateGroup($this->getID())) {
3044 $this->setError($SYS->getErrorMessage());
3049 if ($SYS->sysCheckGroup($this->getID())) {
3050 if (!$SYS->sysRemoveGroup($this->getID())) {
3051 $this->setError($SYS->getErrorMessage());
3058 $this->data_array['unix_status'] = $status;
3065 * getUsers - Get the users of a group
3067 * @param bool $onlylocal
3068 * @param bool $onlyactive Only users with state active, or all users of group
3069 * @return array user's objects.
3071 function getUsers($onlylocal = true, $onlyactive = true) {
3072 if (!isset($this->membersArr)) {
3073 $this->membersArr = array();
3076 foreach ($this->getRoles() as $role) {
3078 && ($role->getHomeProject() == NULL || $role->getHomeProject()->getID() != $this->getID())) {
3081 foreach ($role->getUsers() as $user) {
3082 $ids[] = $user->getID();
3085 $ids = array_unique ($ids);
3086 foreach ($ids as $id) {
3087 $u = user_get_object ($id);
3088 if (!$onlyactive || $u->isActive()) {
3089 $this->membersArr[] = $u;
3093 return $this->membersArr;
3096 function setDocmanCreateOnlineStatus($status) {
3098 $res = db_query_params('UPDATE groups SET use_docman_create_online = $1 WHERE group_id = $2',
3099 array($status, $this->getID()));
3102 $this->setError(_('Error')._(': ')._('Cannot Update Group DocmanCreateOnline Status')._(': ').db_error());
3106 $this->data_array['use_docman_create_online'] = $status;
3112 function setDocmanWebdav($status) {
3114 $res = db_query_params('UPDATE groups SET use_webdav = $1 WHERE group_id = $2',
3119 $this->setError(_('Error')._(': ')._('Cannot Update Group UseWebdav Status')._(': ').db_error());
3123 $this->data_array['use_webdav'] = $status;
3129 function setDocmanSearchStatus($status) {
3131 /* if we activate search engine, we probably want to reindex */
3132 $res = db_query_params('UPDATE groups SET use_docman_search = $1, force_docman_reindex = $1 WHERE group_id = $2',
3133 array($status, $this->getID()));
3136 $this->setError(_('Error')._(': ')._('Cannot Update Group UseDocmanSearch Status')._(': ').db_error());
3140 $this->data_array['use_docman_search'] = $status;
3146 function setDocmanForceReindexSearch($status) {
3148 $res = db_query_params('UPDATE groups SET force_docman_reindex = $1 WHERE group_id = $2',
3149 array($status, $this->getID()));
3152 $this->setError(_('Error')._(': ')._('Cannot Update Group force_docman_reindex')._(': ').db_error());
3156 $this->data_array['force_docman_reindex'] = $status;
3164 * @param int $unit_set_id the effort unit set id
3167 function setEffortUnitSet($unit_set_id) {
3169 $res = db_query_params ('UPDATE groups SET unit_set_id=$1 WHERE group_id=$2',
3170 array($unit_set_id, $this->getID()));
3172 $this->data_array['unit_set_id'] = $unit_set_id;
3182 * getEffortUnitSet - Get the effort unit set id.
3184 * @return int The id of the effort unit set.
3186 function getEffortUnitSet() {
3187 return $this->data_array['unit_set_id'];
3190 function getWidgetLayoutConfig() {
3191 $lm = new WidgetLayoutManager();
3192 return $lm->getLayout($this->getID(), WidgetLayoutManager::OWNER_TYPE_GROUP);
3196 * castVote - Vote on this project or retract the vote
3197 * @param bool $value true to cast, false to retract
3198 * @return bool success (false sets error message)
3200 function castVote($value = true) {
3201 if (!($uid = user_getid()) || $uid == 100) {
3202 $this->setMissingParamsError(_('User ID not passed'));
3205 if (!$this->canVote()) {
3206 $this->setPermissionDeniedError();
3209 $has_vote = $this->hasVote($uid);
3210 if ($has_vote == $value) {
3211 /* nothing changed */
3215 $res = db_query_params('INSERT INTO group_votes (group_id, user_id) VALUES ($1, $2)',
3216 array($this->getID(), $uid));
3218 $res = db_query_params('DELETE FROM group_votes WHERE group_id = $1 AND user_id = $2',
3219 array($this->getID(), $uid));
3222 $this->setError(db_error());
3229 * hasVote - Check if a user has voted on this group item
3231 * @param int|bool $uid user ID (default: current user)
3232 * @return bool true if a vote exists
3234 function hasVote($uid = false) {
3236 $uid = user_getid();
3238 if (!$uid || $uid == 100) {
3241 $res = db_query_params('SELECT * FROM group_votes WHERE group_id = $1 AND user_id = $2',
3242 array($this->getID(), $uid));
3243 return (db_numrows($res) == 1);
3247 * getVotes - get number of valid cast and potential votes
3249 * @return array|bool (votes, voters, percent)
3251 function getVotes() {
3252 if ($this->votes !== false) {
3253 return $this->votes;
3256 $lvoters = $this->getVoters();
3257 unset($lvoters[0]); /* just in case */
3258 unset($lvoters[100]); /* need users */
3259 if (($numvoters = count($lvoters)) < 1) {
3260 $this->votes = array(0, 0, 0);
3261 return $this->votes;
3264 $res = db_query_params('SELECT COUNT(*) AS count FROM group_votes WHERE group_id = $1 AND user_id = ANY($2)',
3265 array($this->getID(), db_int_array_to_any_clause($lvoters)));
3266 $db_count = db_fetch_array($res);
3267 $numvotes = $db_count['count'];
3269 /* check for invalid values */
3270 if ($numvotes < 0 || $numvoters < $numvotes) {
3271 $this->votes = array(-1, -1, 0);
3273 $this->votes = array($numvotes, $numvoters,
3274 (int)($numvotes * 100 / $numvoters + 0.5));
3276 return $this->votes;
3280 * canVote - check whether the current user can vote on
3281 * items in this project
3283 * @return bool true if they can
3285 function canVote() {
3286 if (in_array(user_getid(), $this->getVoters())) {
3293 * getVoters - get IDs of users that may vote on
3294 * items in this project
3296 * @return array list of user IDs
3298 function getVoters() {
3299 if ($this->voters !== false) {
3300 return $this->voters;
3303 $this->voters = array();
3304 if (($engine = RBACEngine::getInstance())
3305 && ($lvoters = $engine->getUsersByAllowedAction('project_read', $this->getID()))
3306 && (count($lvoters) > 0)) {
3307 foreach ($lvoters as $voter) {
3308 $voter_id = $voter->getID();
3309 $this->voters[$voter_id] = $voter_id;
3312 return $this->voters;
3317 * group_getname() - get the group name
3319 * @param int $group_id The group ID
3324 function group_getname ($group_id = 0) {
3325 $grp = group_get_object($group_id);
3327 return $grp->getPublicName();
3334 * group_getunixname() - get the unixname for a group
3336 * @param int $group_id The group ID
3341 function group_getunixname ($group_id) {
3342 $grp = group_get_object($group_id);
3344 return $grp->getUnixName();
3351 * group_get_result() - Get the group object result ID.
3353 * @param int $group_id The group ID
3358 function &group_get_result($group_id=0) {
3359 $grp = group_get_object($group_id);
3361 return $grp->getData();
3367 function getAllProjectTags($onlyvisible = true) {
3368 $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',
3371 if (!$res || db_numrows($res) == 0) {
3377 while ($arr = db_fetch_array($res)) {
3379 $group_id = $arr[1];
3380 if (!isset($result[$tag])) {
3381 $result[$tag] = array();
3384 if (!$onlyvisible || forge_check_perm('project_read', $group_id)) {
3385 $p = group_get_object($group_id);
3386 $result[$tag][] = array('unix_group_name' => $p->getUnixName(),
3387 'group_id' => $group_id);
3395 * Utility class to compare project based in various criteria (names, unixnames, id, ...)
3398 class ProjectComparator {
3399 var $criterion = 'name';
3401 function Compare ($a, $b) {
3402 switch ($this->criterion) {
3404 return strcmp ($a->getUnixName(), $b->getUnixName());
3411 return ($aid < $bid) ? -1 : 1;
3414 $namecmp = strcoll ($a->getPublicName(), $b->getPublicName());
3415 if ($namecmp != 0) {
3418 /* If several projects share a same public name */
3419 return strcoll ($a->getUnixName(), $b->getUnixName());
3424 function sortProjectList (&$list, $criterion='name') {
3425 $cmp = new ProjectComparator();
3426 $cmp->criterion = $criterion;
3428 return usort ($list, array($cmp, 'Compare'));
3433 // c-file-style: "bsd"