4 * Copyright 2011, Franck Villaume - Capgemini
5 * Copyright (C) 2012 Alain Peyrat - Alcatel-Lucent
6 * Copyright 2012, Benoit Debaenst - TrivialDev
7 * Copyright 2012-2014,2017-2018, Franck Villaume - TrivialDev
8 * Copyright 2014, Sylvain Beucler - Inria
9 * Copyright 2014, Philipp Keidel - EDAG Engineering AG
11 * This file is part of FusionForge. FusionForge is free software;
12 * you can redistribute it and/or modify it under the terms of the
13 * GNU General Public License as published by the Free Software
14 * Foundation; either version 2 of the Licence, or (at your option)
17 * FusionForge is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with FusionForge; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 require_once $gfcommon.'include/SysTasksQ.class.php';
29 class scmhookPlugin extends Plugin {
30 public $systask_types = array(
31 'SCMHOOK_UPDATE' => 'updateScmRepo.php',
34 function __construct() {
35 parent::__construct();
36 $this->name = 'scmhook';
37 $this->text = _('Scmhook'); // To show in the tabs, use...
39 _("This plugin contains a set of commit hooks (e-mail notifications,
40 tracker integration, conformity...) that can be enabled for each
41 project independently.");
42 $this->_addHook('groupmenu'); // To put into the project tabs
43 if (forge_get_config('use_scm')) {
44 $this->_addHook('groupisactivecheckbox'); // The "use ..." checkbox in editgroupinfo
46 $this->_addHook('groupisactivecheckboxpost'); //
47 $this->_addHook('artifact_extra_detail');
48 $this->_addHook('task_extra_detail');
51 function CallHook($hookname, &$params) {
53 case 'artifact_extra_detail': {
54 $group_id = $params['group_id'];
55 $group = group_get_object($group_id);
56 if ($group->usesPlugin($this->name)) {
57 $this->artifact_extra_detail($params);
61 case 'task_extra_detail': {
62 $group_id = $params['group_id'];
63 $group = group_get_object($group_id);
64 if ($group->usesPlugin($this->name)) {
65 $this->task_extra_detail($params);
73 function exists($group_id) {
74 $res = db_query_params('SELECT id_group FROM plugin_scmhook WHERE id_group = $1', array($group_id));
78 if (db_numrows($res)) {
84 function remove($group_id) {
85 if ($this->exists($group_id)) {
86 $res = db_query_params('DELETE FROM plugin_scmhook where id_group = $1', array($group_id));
94 function update($params) {
95 $group_id = $params['group_id'];
96 $repository_name = $params['repository_name'];
97 $scm_plugin = $params['scm_plugin'];
99 $group = group_get_object($group_id);
100 if (!$group->usesPlugin($this->name)) {
104 $hooks = $this->getAvailableHooks($group_id);
105 $available_hooknames = array();
106 foreach ($hooks as $hook) {
107 if ($hook->getLabel() == $scm_plugin) {
108 $available_hooknames[] = $hook->getLabel().'_'.$hook->getClassname();
112 $enabled_hooknames = array();
113 if (isset($params['hooks'])) {
114 foreach($params['hooks'] as $value) {
115 if (in_array($value, $available_hooknames) !== FALSE) {
116 $enabled_hooknames[] = preg_replace('/scm[a-z][a-z]+_/','', $value);
121 $existingHooksEnabled = $this->getEnabledHooks($group_id);
122 if (isset($existingHooksEnabled[$scm_plugin][$repository_name])) {
123 $res = db_query_params('UPDATE plugin_scmhook SET hooks = $1, need_update = 1 WHERE id_group = $2 AND repository_name = $3 AND scm_plugin = $4',
124 array(implode('|', $enabled_hooknames), $group_id, $repository_name, $scm_plugin));
126 $res = db_query_params('INSERT INTO plugin_scmhook (hooks, need_update, id_group, repository_name, scm_plugin) VALUES ($1, $2, $3, $4, $5)',
127 array(implode('|', $enabled_hooknames), 1, $group_id, $repository_name, $scm_plugin));
131 foreach($hooks as $hook) {
132 $table = 'plugin_scmhook_'.$hook->getLabel().'_'.strtolower($hook->getClassname());
133 if (db_check_table_exists($table)) {
134 $hook_params = $hook->getParams();
135 if (empty($hook_params)) {
138 if (array_search($hook->getClassname(), $enabled_hooknames) === false) {
141 // Build 3 arrays for inconvenient db_query_params()
143 $sql_cols = array_keys($hook_params);
146 foreach($hook_params as $pconf) {
147 $vals = $params['hooks']['options'];
148 foreach ($vals as $val) {
150 switch($pconf['type']) {
152 if (isset($val['dest'])) {
153 $emails = array_map('trim', explode(',', $val['dest']));
155 $invalid = array_search(false, array_map('validate_email', $emails), $strict) !== false;
157 exit_error($hook->getName() . _(": ") . _("invalid e-mails"). ' ' . $val['dest']);
159 $val = implode(',', $emails);
163 $sql_vars[] = '$'.$i;
167 $sql_cols[] = 'group_id';
168 $sql_vals[] = $group_id;
169 $sql_vars[] = '$'.$i;
170 $sql_cols[] = 'repository_name';
171 $sql_vals[] = $repository_name;
172 $sql_vars[] = '$'.++$i;
174 db_query_params('DELETE FROM '.$table.' WHERE group_id=$1 AND repository_name = $2', array($group_id, $repository_name));
175 db_query_params('INSERT INTO '.$table.' (' . implode(',', $sql_cols)
176 . ') VALUES (' . implode(',', $sql_vars) . ')',
185 $systasksq = new SystasksQ();
186 $systasksq->add($this->getID(), 'SCMHOOK_UPDATE', $group_id, user_getid());
191 function displayScmHook($group_id, $scm) {
193 html_use_tablesorter();
194 echo $HTML->getJavascripts();
195 $hooksAvailable = $this->getAvailableHooks($group_id);
196 $hooksEnabled = $this->getEnabledHooks($group_id);
197 if (count($hooksAvailable)) {
198 echo $HTML->openForm(array('id' => 'scmhook_form', 'action' => '/scm/admin/?group_id='.$group_id, 'method' => 'post'));
199 echo $HTML->html_input('scm_plugin', '', '', 'hidden', $scm);
200 echo '<div id="scmhook">';
201 echo html_e('h2', array(), _('Enable Repository Hooks'));
204 $this->displayScmSvnHook($hooksAvailable, $hooksEnabled, $group_id);
208 $this->displayScmHgHook($hooksAvailable, $hooksEnabled, $group_id);
212 $this->displayScmGitHook($hooksAvailable, $hooksEnabled, $group_id);
216 $this->displayScmCVSHook($hooksAvailable, $hooksEnabled, $group_id);
220 echo $HTML->warning_msg(_('SCM Type not supported yet by scmhook'));
225 echo $HTML->html_input('scmhook_submit', '', '', 'submit', _('Submit'));
226 echo $HTML->closeForm();
228 echo $HTML->information(_('No hooks available'));
232 function getStatusDeploy($group_id) {
233 $res = db_query_params('SELECT need_update FROM plugin_scmhook WHERE id_group = $1', array($group_id));
237 $row = db_fetch_array($res);
238 return $row['need_update'];
241 function getAvailableHooks($group_id) {
242 $available_hooks = array();
243 $listScm = $this->getListLibraryScm();
244 $group = group_get_object($group_id);
245 for ($i = 0; $i < count($listScm); $i++) {
246 if ($group->usesPlugin($listScm[$i])) {
247 $available_hooks = array_merge($available_hooks, $this->getListLibraryHook($listScm[$i]));
250 return $available_hooks;
253 function getEnabledHooks($group_id) {
254 $enabledHooks = array();
255 $res = db_query_params('SELECT hooks, repository_name, scm_plugin FROM plugin_scmhook WHERE id_group = $1', array($group_id));
257 return $enabledHooks;
259 while ($arr = db_fetch_array($res)) {
260 $enabledHooks[$arr['scm_plugin']][$arr['repository_name']] = explode('|', $arr['hooks']);
262 return $enabledHooks;
265 function getListLibraryScm() {
266 return array_values(array_diff(scandir(dirname(__FILE__).'/../library/'), Array('.', '..', '.svn')));
269 function getListLibraryHook($scm) {
270 $listHooks = array_values(array_diff(scandir(dirname(__FILE__).'/../library/'.$scm), array('.', '..', '.svn', 'skel', 'hooks', 'depends', 'cronjobs')));
271 $validHooks = array();
272 foreach ($listHooks as $hook) {
273 if (!stristr($hook,'~')) {
274 include_once dirname(__FILE__).'/../library/'.$scm.'/'.$hook;
275 $hookClassName = preg_replace('/^scm/','', $scm).preg_replace('/.class.php/','', $hook);
276 $hookObject = new $hookClassName;
277 $validHooks[] = $hookObject;
283 function artifact_extra_detail(&$params) {
284 $hooksAvailable = $this->getAvailableHooks($params['group_id']);
285 $hooksEnabled = $this->getEnabledHooks($params['group_id']);
286 foreach ($hooksEnabled as $repos) {
287 foreach ($repos as $repo) {
288 foreach ($hooksAvailable as $hookAvailable) {
289 if (in_array($hookAvailable->getClassname(), $repo)) {
290 if (method_exists($hookAvailable,'artifact_extra_detail')) {
291 $hookAvailable->artifact_extra_detail($params);
299 function task_extra_detail($params) {
300 $hooksAvailable = $this->getAvailableHooks($params['group_id']);
301 $hooksEnabled = $this->getEnabledHooks($params['group_id']);
302 foreach ($hooksEnabled as $repos) {
303 foreach ($repos as $repo) {
304 foreach ($hooksAvailable as $hookAvailable) {
305 if (in_array($hookAvailable->getClassname(), $repo)) {
306 if (method_exists($hookAvailable,'task_extra_detail')) {
307 $hookAvailable->task_extra_detail($params);
316 * override default groupisactivecheckboxpost function for init value in db
318 function groupisactivecheckboxpost(&$params) {
319 // this code actually activates/deactivates the plugin after the form was submitted in the project edit public info page
320 $group = group_get_object($params['group']);
321 $flag = strtolower('use_'.$this->name);
322 if (getIntFromRequest($flag) == 1) {
323 $group->setPluginUse($this->name);
325 $group->setPluginUse($this->name, false);
326 $this->remove($group->getID());
331 function displayScmSvnHook($hooksAvailable, $hooksEnabled, $group_id) {
333 $scm_plugin = plugin_get_object('scmsvn');
334 $groupObject = group_get_object($group_id);
335 $repositories = $scm_plugin->getRepositories($groupObject);
336 // Group available hooks by type
337 $hooks_by_type = array();
339 foreach ($hooksAvailable as $hook) {
340 if ($hook->label == 'scmsvn') {
341 $hooks_by_type[$hook->getHookType()][] = $hook;
345 if (count($hookCount)) {
346 $tabletop = array(_('Repository'));
347 $classth = array('');
348 $titleArr = array('');
350 foreach (array('pre-commit', 'pre-revprop-change', 'post-commit') as $hooktype) {
351 $hooks = $hooks_by_type[$hooktype];
352 foreach ($hooks as $hook) {
353 $tabletop[] = $hook->getName();
354 $classth[] = 'unsortable';
355 $titleArr[] = $hook->getDescription().' ('.$hooktype.')';
359 echo $HTML->listTableTop($tabletop, '', 'sortable_scmhook_scmsvn', 'sortable', $classth, $titleArr);
360 foreach($repositories as $repository) {
362 $cells[][] = $repository.html_e('input', array('type' => 'hidden', 'name' => 'repository['.$repository.'][]'));
363 foreach (array('pre-commit', 'pre-revprop-change', 'post-commit') as $hooktype) {
364 $hooks = $hooks_by_type[$hooktype];
365 foreach ($hooks as $hook) {
366 $attr = array('type' => 'checkbox', 'name' => 'repository['.$repository.'][]', 'value' => $hook->getLabel().'_'.$hook->getClassname());
367 if ((!empty($hook->onlyGlobalAdmin) && !Permission::isGlobalAdmin()) || !$hook->isAvailable()) {
368 $attr = array_merge($attr, array('disabled' => 'disabled'));
369 if (!$hook->isAvailable()) {
370 $attr = array_merge($attr, array('title' => $hook->getDisabledMessage()));
373 if (isset($hooksEnabled['scmsvn'][$repository]) && in_array($hook->getClassname(), $hooksEnabled['scmsvn'][$repository])) {
374 $attr = array_merge($attr, array('checked' => 'checked'));
377 $table = 'plugin_scmhook_scmsvn_'.strtolower($hook->getClassname());
378 if (db_check_table_exists($table)) {
379 $res = db_query_params('SELECT * FROM '.$table.' WHERE group_id = $1 and repository_name = $2', array($group_id, $repository));
380 $values = db_fetch_array($res);
381 foreach ($hook->getParams() as $pname => $pconf) {
382 $val = ($values[$pname] != null) ? $values[$pname] : $pconf['default'];
383 switch($pconf['type']) {
385 $content = html_e('input', array('type' => 'text','title' => $pconf['description'], 'name' => $hook->getLabel().'_'.$hook->getClassname().'['.$repository.']['.$pname.']', 'value' => $val, 'size' => 40));
390 $cells[][] = html_e('input', $attr).$content;
393 echo $HTML->multiTableRow(array(), $cells);
395 echo $HTML->listTableBottom();
399 function displayScmHgHook($hooksAvailable, $hooksEnabled, $group_id) {
401 $scm_plugin = plugin_get_object('scmhg');
402 $groupObject = group_get_object($group_id);
403 $repositories = $scm_plugin->getRepositories($groupObject);
404 $hooksServePushPullBundle = array();
405 foreach ($hooksAvailable as $hook) {
406 if ($hook->label == 'scmhg') {
407 switch ($hook->getHookType()) {
408 case "serve-push-pull-bundle": {
409 $hooksServePushPullBundle[] = $hook;
413 //byebye hook.... we do not know you...
419 if (count($hooksServePushPullBundle)) {
420 $tabletop = array(_('Repository'));
421 $classth = array('');
422 $titleArr = array('');
423 foreach ($hooksServePushPullBundle as $hookServePushPullBundle) {
424 $tabletop[] = $hookServePushPullBundle->getName();
425 $classth[] = 'unsortable';
426 $titleArr[] = $hookServePushPullBundle->getDescription();
429 echo $HTML->listTableTop($tabletop, '', 'sortable_scmhook_scmhg', 'sortable', $classth, $titleArr);
430 foreach($repositories as $repository) {
432 $cells[][] = $repository.html_e('input', array('type' => 'hidden', 'name' => 'repository['.$repository.'][]'));
433 foreach ($hooksServePushPullBundle as $hookServePushPullBundle) {
434 $attr = array('type' => 'checkbox', 'name' => 'repository['.$repository.'][]', 'value' => $hookServePushPullBundle->getLabel().'_'.$hookServePushPullBundle->getClassname());
435 if ((!empty($hookServePushPullBundle->onlyGlobalAdmin) && !Permission::isGlobalAdmin()) || !$hookServePushPullBundle->isAvailable()) {
436 $attr = array_merge($attr, array('disabled' => 'disabled'));
437 if (!$hookServePushPullBundle->isAvailable()) {
438 $attr = array_merge($attr, array('title' => $hookServePushPullBundle->getDisabledMessage()));
441 if (isset($hooksEnabled['scmhg'][$repository]) && in_array($hookServePushPullBundle->getClassname(), $hooksEnabled['scmhg'][$repository])) {
442 $attr = array_merge($attr, array('checked' => 'checked'));
445 $table = 'plugin_scmhook_scmhg_'.strtolower($hookServePushPullBundle->getClassname());
446 if (db_check_table_exists($table)) {
447 $res = db_query_params('SELECT * FROM '.$table.' WHERE group_id = $1 and repository_name = $2', array($group_id, $repository));
448 $values = db_fetch_array($res);
449 foreach ($hookServePushPullBundle->getParams() as $pname => $pconf) {
450 $val = ($values[$pname] != null) ? $values[$pname] : $pconf['default'];
451 switch($pconf['type']) {
453 $content = html_e('input', array('type' => 'text','title' => $pconf['description'], 'name' => $hookServePushPullBundle->getLabel().'_'.$hookServePushPullBundle->getClassname().'['.$repository.']['.$pname.']', 'value' => $val, 'size' => 40));
458 $cells[][] = html_e('input', $attr).$content;
460 echo $HTML->multiTableRow(array(), $cells);
462 echo $HTML->listTableBottom();
466 function displayScmGitHook($hooksAvailable, $hooksEnabled, $group_id) {
468 $scm_plugin = plugin_get_object('scmgit');
469 $groupObject = group_get_object($group_id);
470 $repositories = $scm_plugin->getRepositories($groupObject);
471 $hooksPostReceive = array();
472 foreach ($hooksAvailable as $hook) {
473 if ($hook->label == 'scmgit') {
474 switch ($hook->getHookType()) {
475 case 'post-receive': {
476 $hooksPostReceive[] = $hook;
480 //byebye hook.... we do not know you...
486 if (count($hooksPostReceive)) {
487 $tabletop = array(_('Repository'));
488 $classth = array('');
489 $titleArr = array('');
490 foreach ($hooksPostReceive as $hookPostReceive) {
491 $tabletop[] = $hookPostReceive->getName();
492 $classth[] = 'unsortable';
493 $titleArr[] = $hookPostReceive->getDescription();
496 echo $HTML->listTableTop($tabletop, '', 'sortable_scmhook_scmgit', 'sortable', $classth, $titleArr);
497 foreach($repositories as $repository) {
499 $cells[][] = $repository.html_e('input', array('type' => 'hidden', 'name' => 'repository['.$repository.'][]'));
500 foreach ($hooksPostReceive as $hookPostReceive) {
501 $attr = array('type' => 'checkbox', 'name' => 'repository['.$repository.'][]', 'value' => $hookPostReceive->getLabel().'_'.$hookPostReceive->getClassname());
502 if ((!empty($hookPostReceive->onlyGlobalAdmin) && !Permission::isGlobalAdmin()) || !$hookPostReceive->isAvailable()) {
503 $attr = array_merge($attr, array('disabled' => 'disabled'));
504 if (!$hookPostReceive->isAvailable()) {
505 $attr = array_merge($attr, array('title' => $hookPostReceive->getDisabledMessage()));
508 if (isset($hooksEnabled['scmgit'][$repository]) && in_array($hookPostReceive->getClassname(), $hooksEnabled['scmgit'][$repository])) {
509 $attr = array_merge($attr, array('checked' => 'checked'));
512 $table = 'plugin_scmhook_scmgit_'.strtolower($hookPostReceive->getClassname());
513 if (db_check_table_exists($table)) {
514 $res = db_query_params('SELECT * FROM '.$table.' WHERE group_id = $1 and repository_name = $2', array($group_id, $repository));
515 $values = db_fetch_array($res);
516 foreach ($hookPostReceive->getParams() as $pname => $pconf) {
517 $val = ($values[$pname] != null) ? $values[$pname] : $pconf['default'];
518 switch($pconf['type']) {
520 $content = html_e('input', array('type' => 'text','title' => $pconf['description'], 'name' => $hookPostReceive->getLabel().'_'.$hookPostReceive->getClassname().'['.$repository.']['.$pname.']', 'value' => $val, 'size' => 40));
525 $cells[][] = html_e('input', $attr).$content;
527 echo $HTML->multiTableRow(array(), $cells);
529 echo $HTML->listTableBottom();
533 function displayScmCVSHook($hooksAvailable, $hooksEnabled, $group_id) {
535 $scm_plugin = plugin_get_object('scmcvs');
536 $groupObject = group_get_object($group_id);
537 $repositories = $scm_plugin->getRepositories($groupObject);
538 $hooksPostCommit = array();
539 foreach ($hooksAvailable as $hook) {
540 if ($hook->label == 'scmcvs') {
541 switch ($hook->getHookType()) {
542 case "post-commit": {
543 $hooksPostCommit[] = $hook;
547 //byebye hook.... we do not know you...
553 if (count($hooksPostCommit)) {
554 $tabletop = array(_('Repository'));
555 $classth = array('');
556 $titleArr = array('');
557 foreach ($hooksPostCommit as $hookPostCommit) {
558 $tabletop[] = $hookPostCommit->getName();
559 $classth[] = 'unsortable';
560 $titleArr[] = $hookPostCommit->getDescription();
563 echo $HTML->listTableTop($tabletop, '', 'sortable_scmhook_scmcvs', 'sortable', $classth, $titleArr);
564 foreach($repositories as $repository) {
566 $cells[][] = $repository.html_e('input', array('type' => 'hidden', 'name' => 'repository['.$repository.'][]'));
567 foreach ($hooksPostCommit as $hookPostCommit) {
568 $attr = array('type' => 'checkbox', 'name' => 'repository['.$repository.'][]', 'value' => $hookPostCommit->getLabel().'_'.$hookPostCommit->getClassname());
569 if ((!empty($hookPostCommit->onlyGlobalAdmin) && !Permission::isGlobalAdmin()) || !$hookPostCommit->isAvailable()) {
570 $attr = array_merge($attr, array('disabled' => 'disabled'));
571 if (!$hookPostCommit->isAvailable()) {
572 $attr = array_merge($attr, array('title' => $hookPostCommit->getDisabledMessage()));
575 if (isset($hooksEnabled['scmcvs'][$repository]) && in_array($hookPostCommit->getName(), $hooksEnabled['scmcvs'][$repository])) {
576 $attr = array_merge($attr, array('checked' => 'checked'));
578 $cells[][] = html_e('input', $attr);
580 echo $HTML->multiTableRow(array(), $cells);
582 echo $HTML->listTableBottom();
589 // c-file-style: "bsd"