3 * projects_hierarchyPlugin Class
5 * Copyright 2006 (c) Fabien Regnier - Sogeti
6 * Copyright 2010-2011, Franck Villaume - Capgemini
7 * Copyright 2012-2013, Franck Villaume - TrivialDev
8 * http://fusionforge.org
10 * This file is part of FusionForge. FusionForge is free software;
11 * you can redistribute it and/or modify it under the terms of the
12 * GNU General Public License as published by the Free Software
13 * Foundation; either version 2 of the Licence, or (at your option)
16 * FusionForge is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 class projects_hierarchyPlugin extends Plugin {
27 function __construct() {
29 $this->name = 'projects-hierarchy';
30 $this->text = _('Project Hierarchy'); // To show in the tabs, use...
31 $this->_addHook('groupisactivecheckbox'); // The "use ..." checkbox in editgroupinfo
32 $this->_addHook('groupisactivecheckboxpost');
33 $this->_addHook('hierarchy_views'); // include specific views
34 $this->_addHook('display_hierarchy'); // to see the tree of projects
35 $this->_addHook('group_delete'); // clean tables on delete
36 $this->_addHook('project_admin_plugins'); // to show up in the admin page fro group
37 $this->_addHook('site_admin_option_hook'); // to provide a link to the site wide administrative pages of plugin
38 $this->_addHook('display_hierarchy_submenu'); // to display a submenu in software map if projects-hierarchy plugin is used
39 $this->_addHook('docmansearch_has_hierarchy'); // used by the search menu in docman
42 function CallHook($hookname, &$params) {
43 global $G_SESSION, $HTML;
46 case "display_hierarchy": {
47 if ($this->displayHierarchy()) {
52 case "hierarchy_views": {
54 require_once $gfplugins.$this->name.'/include/hierarchy_utils.php';
55 $group_id = $params[0];
56 $project = group_get_object($group_id);
57 if ($project->usesPlugin($this->name)) {
62 include($gfplugins.$this->name.'/view/'.$params[1].'_project_link.php');
73 case "group_delete": {
74 $this->remove($params['group_id']);
78 case "site_admin_option_hook": {
79 // Use this to provide a link to the site wide administrative pages for your plugin
80 echo '<li>'.$this->getAdminOptionLink().'</li>';
84 case "project_admin_plugins": {
85 // this displays the link in the project admin options page to it's administration
86 $group_id = $params['group_id'];
87 $group = group_get_object($group_id);
88 if ($group->usesPlugin($this->name)) {
90 echo util_make_link('/plugins/'.$this->name.'/?group_id='.$group_id.'&type=admin&pluginname='.$this->name, _('Hierarchy Admin'), array('class'=>'tabtitle', 'title'=>_('Configure the projets-hierarchy plugin (docman, tree, delegate, globalconf features)')));
96 case "display_hierarchy_submenu": {
97 $globalConf = $this->getGlobalconf();
98 if ($globalConf['tree']) {
99 // Use to display a submenu in software map page if at least one project has a valid relationship
100 $res1 = db_query_params('SELECT g.group_name FROM plugins p, group_plugin gp, groups g WHERE plugin_name = $1 and gp.group_id = g.group_id and p.plugin_id = gp.plugin_id',
103 if (db_numrows($res1) > 0) {
104 $res2 = db_query_params('SELECT count(*) as used FROM plugin_projects_hierarchy_relationship where status = $1',
107 $row = db_fetch_array($res2);
109 $hierarchy_used = true;
113 if (isset($hierarchy_used)) {
114 $hierarMenuTitle[] = _('Per Category');
115 $hierarMenuTitle[] = _('Per Hierarchy');
116 $hierarMenuAttr[] = array('title' => _('Browse per category the available projects. Some projects might not appear here they do not choose any categories'), 'class' => 'tabtitle-nw');
117 $hierarMenuAttr[] = array('title' => _('Browse per hierarchy. Projects can share relationship between projects, as father and sons'), 'class' => 'tabtitle');
118 $hierarMenuUrl[] = '/softwaremap/trove_list.php?cat=c';
119 $hierarMenuUrl[] = '/softwaremap/trove_list.php?cat=h';
120 echo ($HTML->subMenu($hierarMenuTitle, $hierarMenuUrl, $hierarMenuAttr));
126 case "docmansearch_has_hierarchy": {
127 if ($params['includesubprojects']) {
128 $group_id = $params['group_id'];
129 $group = group_get_object($group_id);
130 if ($group->usesPlugin($this->name)) {
131 $arrayChilds= $this->getFamily($group_id, 'child', true, 'validated');
132 foreach ( $arrayChilds as $childId ) {
133 $params['qpa'] = db_construct_qpa($params['qpa'], ' OR group_id = $1', array($childId));
144 function displayHierarchy() {
145 $tree = $this->buildTree();
146 $displayTree = $this->affTree($tree, 0);
153 echo '<link rel="StyleSheet" href="/plugins/projects-hierarchy/dtree.css" type="text/css" />
154 <script type="text/javascript" src="/plugins/projects-hierarchy/dtree.js"></script>';
157 function buildTree() {
158 global $project_name;
159 $res = db_query_params('select p1.group_id as father_id,p1.unix_group_name as father_unix_name,p1.group_name as father_name,p2.group_id as son_id,p2.unix_group_name as son_unix_name,p2.group_name as son_name
160 from groups as p1,groups as p2,plugin_projects_hierarchy_relationship
161 where p1.group_id=plugin_projects_hierarchy_relationship.project_id
162 and p2.group_id=plugin_projects_hierarchy_relationship.sub_project_id
163 and plugin_projects_hierarchy_relationship.status=$1
164 and (select tree from plugin_projects_hierarchy where project_id = p2.group_id)
165 order by father_name, son_name',
168 // construction du tableau associatif
169 // key = name of the father
170 // value = list of sons
172 while ($row = db_fetch_array($res)) {
173 if (forge_check_perm('project_read', $row['father_id']) && forge_check_perm('project_read', $row['son_id'])) {
174 $tree[$row['father_id']][] = $row['son_id'];
175 //get the unix name of the project
176 $project_name[$row['father_id']][0] = $row['father_name'];
177 $project_name[$row['son_id']][0] = $row['son_name'];
178 $project_name[$row['father_id']][1] = $row['father_unix_name'];
179 $project_name[$row['son_id']][1] = $row['son_unix_name'];
185 function affTree($tree, $lvl) {
186 global $project_name;
192 while (list($key, $sons) = each($tree)) {
193 // Really don't know why there is a warning there, and added @
194 if(@!$arbre[$key] != 0){
198 foreach ($sons as $son) {
199 $arbre[$son] = $cpt_pere;
203 if (sizeof($arbre)) {
204 $returnTree .= '<table ><tr><td>';
205 $returnTree .= '<script type="text/javascript">';
206 $returnTree .= 'd = new dTree(\'d\');';
207 $returnTree .= 'd.add(0,-1,\'Project Hierarchy Tree\');';
209 //construction automatique de l'arbre format : (num_fils, num_pere,nom,nom_unix)
210 while (list($key2, $sons2) = each($arbre)) {
211 $returnTree .= "d.add('".$key2."','".$sons2."','".$project_name[$key2][0]."','".util_make_url( '/projects/'.$project_name[$key2][1] .'/', $project_name[$key2][1] ) ."');";
214 $returnTree .= 'document.write(d);';
215 $returnTree .= '</script>';
216 $returnTree .= '</td></tr></table>';
222 * getFamily - find the children or parent group_id of this project.
224 * @param integer group_id to search for
225 * @param string parent or child ?
226 * @param boolean recurcive or not ?
227 * @param string validated or pending or any relation ?
228 * @return array array of arrays with group_id of parent or childs
231 function getFamily($group_id, $order, $deep = false, $status = 'any') {
232 $localFamily = array();
235 $statusCondition = 't';
240 $statusCondition = 'f';
250 $qpa = db_construct_qpa(false, 'SELECT project_id as id FROM plugin_projects_hierarchy_relationship
251 WHERE sub_project_id = $1', array($group_id));
252 if (isset($statusCondition))
253 $qpa = db_construct_qpa($qpa, ' AND status = $1', array($statusCondition));
255 $res = db_query_qpa($qpa);
259 $qpa = db_construct_qpa(false, 'SELECT sub_project_id as id FROM plugin_projects_hierarchy_relationship
260 WHERE project_id = $1', array($group_id));
261 if (isset($statusCondition))
262 $qpa = db_construct_qpa($qpa, ' AND status = $1', array($statusCondition));
264 $res = db_query_qpa($qpa);
272 if ($res && db_numrows($res) > 0) {
273 while ($arr = db_fetch_array($res)) {
274 $localFamily[] = $arr['id'];
279 $nextFamily = array();
280 for ( $i = 0; $i < count($localFamily); $i++) {
281 $nextFamily = $this->getFamily($localFamily[$i], $order, $deep, $status);
284 if (isset($nextFamily) && sizeof($nextFamily))
285 $localFamily = array_merge($localFamily, $nextFamily);
291 * getDocmanStatus - returns the docman status for this project
293 * @param integer group_id
294 * @return boolean true/false
297 function getDocmanStatus($group_id) {
298 $res = db_query_params('SELECT docman FROM plugin_projects_hierarchy WHERE project_id = $1 limit 1',
303 $resArr = db_fetch_array($res);
304 if ($resArr['docman'] == 't')
311 * setDocmanStatus - allow parent to browse your document manager and allow yourself to select your childs to be browsed.
313 * @param integer your groud_id
314 * @param boolean the status to set
315 * @return boolean success or not
317 function setDocmanStatus($group_id, $status = 0) {
318 $res = db_query_params('UPDATE plugin_projects_hierarchy set docman = $1 WHERE project_id = $2',
319 array($status, $group_id));
328 * redirect - encapsulate session_redirect to handle correctly the redirection URL
330 * @param string usually http_referer from $_SERVER
331 * @param string type of feedback : error, warning, feedback
332 * @param string the message of feedback
335 function redirect($http_referer, $type, $message) {
346 $url = util_find_relative_referer($http_referer);
347 if (strpos($url,'?')) {
348 session_redirect($url.'&'.$type.'='.urlencode($message));
350 session_redirect($url.'?'.$type.'='.urlencode($message));
354 * override default groupisactivecheckboxpost function for init value in db
356 function groupisactivecheckboxpost(&$params) {
357 // this code actually activates/deactivates the plugin after the form was submitted in the project edit public info page
358 $group = group_get_object($params['group']);
359 $flag = strtolower('use_'.$this->name);
360 if ( getIntFromRequest($flag) == 1 ) {
361 if ($this->add($group->getID())) {
362 $group->setPluginUse($this->name);
366 if ($this->remove($group->getID())) {
367 $group->setPluginUse($this->name, false);
375 * add - add a new group_id using the plugin
377 * @param integer group_id
378 * @return boolean true on success
381 function add($group_id) {
382 if (!$this->exists($group_id)) {
383 $globalConf = $this->getGlobalconf();
384 $res = db_query_params('INSERT INTO plugin_projects_hierarchy (project_id, tree, docman, delegate) VALUES ($1, $2, $3, $4)', array($group_id, (int)$globalConf['tree'], (int)$globalConf['docman'], (int)$globalConf['delegate']));
392 * remove - remove group_id using the plugin
394 * @param integer group_id
395 * @return boolean true on success
398 function remove($group_id) {
399 if ($this->exists($group_id)) {
401 $res = db_query_params('DELETE FROM plugin_projects_hierarchy where project_id = $1', array($group_id));
407 $res = db_query_params('DELETE FROM plugin_projects_hierarchy_relationship where project_id = $1 OR sub_project_id = $1',
419 * addChild - add a new child to this project
421 * @param integer group_id
422 * @param integer sub_group_id
423 * @return boolean true on success
426 function addChild($project_id, $sub_project_id) {
427 if ($this->exists($project_id) && $this->exists($sub_project_id)) {
428 if (!$this->hasRelation($project_id, $sub_project_id)) {
429 $res = db_query_params('INSERT INTO plugin_projects_hierarchy_relationship (project_id, sub_project_id)
430 VALUES ($1, $2)', array($project_id, $sub_project_id));
441 * removeChild - remove a child to this project
443 * @param integer group_id
444 * @param integer sub_group_id
445 * @return boolean true on success
448 function removeChild($project_id, $sub_project_id) {
449 if ($this->exists($project_id) && $this->exists($sub_project_id)) {
450 if ($this->hasRelation($project_id, $sub_project_id)) {
451 $res = db_query_params('DELETE FROM plugin_projects_hierarchy_relationship
452 WHERE project_id = $1 AND sub_project_id = $2',
453 array($project_id, $sub_project_id));
464 * removeParent - remove a parent to this project
466 * @param integer group_id
467 * @param integer sub_group_id
468 * @return boolean true on success
471 function removeParent($project_id, $sub_project_id) {
472 if ($this->exists($project_id) && $this->exists($sub_project_id)) {
473 if ($this->hasRelation($project_id, $sub_project_id)) {
474 $res = db_query_params('DELETE FROM plugin_projects_hierarchy_relationship
475 WHERE project_id = $1 AND sub_project_id = $2',
476 array($sub_project_id, $project_id));
487 * hasRelation - check if there is a relation child->parent or parent->child between 2 projects
489 * @param integer group_id
490 * @param integer sub_group_id
491 * @return boolean true on success
494 function hasRelation($project_id, $sub_project_id) {
495 if ($this->exists($project_id) && $this->exists($sub_project_id)) {
496 $res = db_query_params('SELECT * FROM plugin_projects_hierarchy_relationship
497 WHERE ( project_id = $1 AND sub_project_id = $2 )
498 OR ( project_id = $2 AND sub_project_id = $1 )',
499 array($project_id, $sub_project_id));
500 if ($res && db_numrows($res) == 1 )
507 * validateRelationship - validate or reject a relation between 2 projects
509 * @param integer group_id
510 * @param integer sub_group_id
511 * @param string type of relation
512 * @param integer status of the relation
513 * @return boolean true on success
516 function validateRelationship($project_id, $sub_project_id, $relation, $status) {
517 if ($this->exists($project_id) && $this->exists($sub_project_id)) {
518 if ($this->hasRelation($project_id, $sub_project_id)) {
520 $qpa = db_construct_qpa(false, 'UPDATE plugin_projects_hierarchy_relationship SET status = $1
521 WHERE ', array($status));
524 $qpa = db_construct_qpa($qpa, 'project_id = $1 AND sub_project_id = $2',
525 array($sub_project_id, $project_id));
529 $qpa = db_construct_qpa($qpa, 'project_id = $1 AND sub_project_id = $2',
530 array($project_id, $sub_project_id));
538 $res = db_query_qpa($qpa);
542 if (db_affected_rows($res))
545 $qpa = db_construct_qpa(false, 'DELETE FROM plugin_projects_hierarchy_relationship WHERE ');
548 $qpa = db_construct_qpa($qpa, 'project_id = $1 AND sub_project_id = $2',
549 array($project_id, $sub_project_id));
553 $qpa = db_construct_qpa($qpa, 'project_id = $1 AND sub_project_id = $2',
554 array($sub_project_id, $project_id));
562 $res = db_query_qpa($qpa);
566 if (db_affected_rows($res))
575 * exists - if this project use the plugin
577 * @param integer group_id
578 * @return boolean true on success
581 function exists($group_id) {
582 $res = db_query_params('SELECT project_id FROM plugin_projects_hierarchy WHERE project_id = $1', array($group_id));
586 if (db_numrows($res))
593 * getAdminOptionLink - return the admin link url
595 * @return string html url
598 function getAdminOptionLink() {
599 return util_make_link('/plugins/'.$this->name.'/?type=globaladmin',_('Global Hierarchy admin'), array('class'=>'tabtitle', 'title'=>_('Configure the projets-hierarchy plugin (docman, tree, delegate, globalconf features)')));
603 * getHeader - initialize header and js
604 * @param string type : user, project (aka group)
605 * @param array params
606 * @return bool success or not
608 function getHeader($type, $params=NULL) {
612 case 'globaladmin': {
613 session_require_global_perm('forge_admin');
615 require_once($gfwww.'admin/admin_utils.php');
616 site_admin_header(array('title'=>_('Site Global Hierarchy Admin'), 'toptab'=>''));
622 site_project_header($params);
631 * getGlobalAdminView - display the global configuration view
633 * @return boolean True
636 function getGlobalAdminView() {
637 global $gfplugins, $use_tooltips;
638 include $gfplugins.$this->name.'/view/admin/viewGlobalConfiguration.php';
643 * getGlobalconf - return the global configuration defined at forge level
645 * @return array the global configuration array
647 function getGlobalconf() {
648 $resGlobConf = db_query_params('SELECT * from plugin_projects_hierarchy_global',array());
653 $row = db_numrows($resGlobConf);
655 if ($row == null || count($row) > 2) {
659 $resArr = db_fetch_array($resGlobConf);
660 $returnArr = array();
662 foreach($resArr as $column => $value) {
664 $returnArr[$column] = true;
666 $returnArr[$column] = false;
674 * getConf - return the configuration defined at project level
676 * @param integer the group id
677 * @return array the configuration array
679 function getConf($project_id) {
680 $resConf = db_query_params('SELECT * from plugin_projects_hierarchy where project_id=$1',array($project_id));
685 $row = db_numrows($resConf);
687 if ($row == null || count($row) > 2) {
691 $resArr = db_fetch_array($resConf);
692 $returnArr = array();
694 foreach($resArr as $column => $value) {
696 $returnArr[$column] = true;
698 $returnArr[$column] = false;
706 function getProjectAdminView() {
707 global $gfplugins, $use_tooltips;
708 include $gfplugins.$this->name.'/view/admin/viewProjectConfiguration.php';
713 * getFooter - display footer
715 function getFooter($type) {
719 case 'globaladmin': {
720 session_require_global_perm('forge_admin');
721 site_admin_footer(array());
726 site_project_footer(array());
734 * updateGlobalConf - update the global configuration in database
736 * @param array configuration array (tree, docman, delegate)
737 * @return bool true on success
739 function updateGlobalConf($confArr) {
740 if (!isset($confArr['tree']) || !isset($confArr['docman']) || !isset($confArr['delegate']))
743 $res = db_query_params('truncate plugin_projects_hierarchy_global',array());
747 $res = db_query_params('insert into plugin_projects_hierarchy_global (tree, docman, delegate)
748 values ($1, $2, $3)',
761 * updateConf - update the configuration in database for this project
763 * @param array configuration array (tree, docman, delegate)
764 * @return bool true on success
766 function updateConf($project_id, $confArr) {
767 if (!isset($confArr['tree']) || !isset($confArr['docman']) || !isset($confArr['delegate']) || !isset($confArr['globalconf']))
770 $res = db_query_params('update plugin_projects_hierarchy
771 set (tree, docman, delegate, globalconf) = ($1, $2, $3, $4)
772 where project_id = $5',
776 $confArr['delegate'],
777 $confArr['globalconf'],
787 * son_box - display a select box for son selection
789 * @param integer group_id
791 * @param string selected value
792 * @return string html box
795 function son_box($group_id, $name, $selected = 'xzxzxz') {
796 $sons = $this->getFamily($group_id, 'child', true, 'any');
797 $parent = $this->getFamily($group_id, 'parent', true, 'any');
798 $family = array_merge($parent, $sons);
799 $son = db_query_params('SELECT group_id, group_name FROM groups
802 AND group_id <> ALL ($3)
803 AND group_id IN (select group_id from group_plugin,plugins where group_plugin.plugin_id = plugins.plugin_id and plugins.plugin_name = $4)
804 AND group_id NOT IN (select sub_project_id from plugin_projects_hierarchy_relationship);',
807 db_int_array_to_any_clause($family),
809 return html_build_select_box($son, $name, $selected, false);
813 * isUsed - is this plugin used by other projects than the current family ?
815 * @return bool yes or no
817 function isUsed($group_id) {
818 $sons = $this->getFamily($group_id, 'child', true, 'any');
819 $parent = $this->getFamily($group_id, 'parent', true, 'any');
820 $family = array_merge($parent, $sons);
822 $res1 = db_query_params('SELECT g.group_name FROM plugins p, group_plugin gp, groups g
823 WHERE plugin_name = $1
824 AND gp.group_id = g.group_id
825 AND p.plugin_id = gp.plugin_id
826 AND g.group_id <> ALL ($2)',
827 array($this->name, db_int_array_to_any_clause($family))
830 // we want at least more than ourself
831 if (db_numrows($res1) > 1) {
840 // c-file-style: "bsd"