3 * FusionForge Mercurial (Hg) plugin
5 * Copyright 2009, Roland Mas
6 * Copyright 2012, Denise Patzker
7 * Copyright 2012-2014,2017-2019,2021, Franck Villaume - TrivialDev
9 * This file is part of FusionForge.
11 * FusionForge is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published
13 * by the Free Software Foundation; either version 2 of the License,
14 * or (at your option) any later version.
16 * FusionForge is distributed in the hope that it will be useful, but
17 * WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * General Public License for more details.
21 * You should have received a copy of the GNU General Public License along
22 * with this program; if not, write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 require_once $gfcommon.'include/plugins_utils.php';
28 forge_define_config_item('default_server', 'scmhg', forge_get_config('scm_host'));
29 forge_define_config_item('repos_path', 'scmhg', forge_get_config('chroot').'/scmrepos/hg');
30 forge_define_config_item('ssh_port', 'core', 22);
32 class HgPlugin extends SCMPlugin {
33 function __construct() {
34 parent::__construct();
35 $this->name = 'scmhg';
36 $this->text = _('Mercurial');
38 _("This plugin contains the Mercurial (Hg) subsystem of FusionForge. It
39 allows each FusionForge project to have its own Mercurial repository,
40 and gives some control over it to the project's administrator.
41 Offer DAV or SSH access.");
42 $this->_addHook('scm_browser_page');
43 $this->_addHook('scm_update_repolist');
44 $this->_addHook('scm_generate_snapshots');
45 $this->_addHook('scm_gather_stats');
46 $this->_addHook('activity');
47 $this->_addHook('scm_admin_form');
48 $this->_addHook('scm_delete_repo');
49 $this->_addHook('scm_add_repo');
50 $this->_addHook('get_scm_repo_list');
55 * getPluginDescription - display the description of this plugin in pluginman admin page
57 * @return string the description
59 function getPluginDescription() {
60 return _('Use Mercurial as Source Code Management tool. Offer DAV or SSH access.');
63 function getDefaultServer() {
64 return forge_get_config('default_server', 'scmhg');
68 return html_e('p', array(), sprintf(_('Documentation for %1$s is available at <a href="%2$s">%2$s</a>.'),
70 'http://hgbook.red-bean.com/')).
71 html_e('p', array(), _('Another short Introduction can be found at <a href="http://hginit.com/">http://hginit.com/</a>'));
74 function getInstructionsForAnon($project) {
76 $b = html_e('h2', array(), _('Anonymous Mercurial Access'));
78 if (forge_get_config('use_dav', 'scmhg')) {
79 $repo_list = $this->getRepositories($project);
80 $protocol = forge_get_config('use_ssl', 'scmhg')? 'https' : 'http';
81 $b .= html_e('p', array(), _("This project's Mercurial repository can be checked out through anonymous access with the following command")._(':'));
82 foreach ($repo_list as $repo_name) {
83 $b .= html_e('kbd', array(), 'hg clone '.$protocol.'://'.$this->getBoxForProject($project).'/anonscm/'.'hg'.'/'.$project->getUnixName().'/'.$repo_name).html_e('br');
86 $b .= $HTML->warning_msg(_('Anonymous browsing not available using ssh access.'));
91 function getInstructionsForRW($project) {
93 $repo_list = $this->getRepositories($project);
96 $b .= html_e('h2', array(), _('Developer Access'));
97 $b .= html_e('p', array(),
98 ngettext('Only project developers can access the Hg repository via this method.',
99 'Only project developers can access the Hg repositories via this method.',
101 $b .= '<div id="tabber-hg">';
102 $liElements = array();
103 if (forge_get_config('use_ssh', 'scmhg')) {
104 $liElements[]['content'] = '<a href="#tabber-hgssh">'._('via SSH').'</a>';
107 if (forge_get_config('use_dav', 'scmhg')) {
108 $liElements[]['content'] = '<a href="#tabber-hgdav">'._('via "DAV"').'</a>';
111 $b .= $HTML->html_list($liElements);
112 if (!isset($configuration)) {
113 return $HTML->error_msg(_('Error')._(': ')._('No access protocol has been allowed for the Hg plugin in scmhg.ini: use_ssh and use_dav are disabled'));
116 if (forge_get_config('ssh_port') != 22) {
117 $ssh_port = ':'.forge_get_config('ssh_port');
119 if (session_loggedin()) {
120 $u = user_get_object(user_getid());
121 $d = $u->getUnixName();
122 if (forge_get_config('use_ssh', 'scmhg')) {
123 $b .= '<div id="tabber-hgssh" class="tabbertab" >';
124 $b .= html_e('p', array(), _('SSH must be installed on your client machine.'));
126 foreach ($repo_list as $repo_name) {
127 // Warning : the ssh uri MUST be this form : ssh://username@scmbox//path/reponame
128 // HAVE YOU SEEN THE // starting the path ? Keep in mind the double /
129 if (forge_get_config('use_shell_limited')) {
130 $htmlRepo .= html_e('kbd', array(), 'hg clone ssh://'.$d.'@'.$this->getBoxForProject($project).$ssh_port.'/hg/'.$project->getUnixName().'/'.$repo_name).html_e('br');
133 $htmlRepo .= html_e('kbd', array(), 'hg clone ssh://'.$d.'@'.$this->getBoxForProject($project).$ssh_port.'/'.forge_get_config('repos_path', 'scmhg').'/'.$project->getUnixName().'/'.$repo_name).html_e('br');
136 $b .= html_e('p', array(), $htmlRepo);
139 if (forge_get_config('use_dav', 'scmhg')) {
140 $b .= '<div id="tabber-hgdav" class="tabbertab" >';
141 $b .= html_e('p', array(), _('Enter your site password when prompted.'));
143 $protocol = forge_get_config('use_ssl', 'scmhg') ? 'https' : 'http';
144 foreach ($repo_list as $repo_name) {
145 $htmlRepo .= html_e('kbd', array(), 'hg clone '.$protocol.'://'.$d.'@'.$this->getBoxForProject($project).'/authscm/'.$d.'/hg/'. $project->getUnixName().'/'.$repo_name).html_e('br');
147 $b .= html_e('p', array(), $htmlRepo);
151 if (forge_get_config('use_ssh', 'scmhg')) {
152 $b .= '<div id="tabber-hgssh" class="tabbertab" >';
153 $b .= html_e('p', array(),
154 ngettext('Only project developers can access the Hg repository via this method.',
155 'Only project developers can access the Hg repositories via this method.',
157 ' '. _('SSH must be installed on your client machine.').
158 ' '. _('Additionally, a public ssh key must be available in the FusionForge settings of the respective user.').
159 ' '. _('Substitute <em>developername</em> with the proper value.'));
161 foreach ($repo_list as $repo_name) {
162 // Warning : the ssh uri MUST be this form : ssh://username@scmbox//path/reponame
163 // HAVE YOU SEEN THE // starting the path ? Keep in mind the double /
164 if (forge_get_config('use_shell_limited')) {
165 $htmlRepo .= html_e('kbd', array(), 'hg clone ssh://'.html_e('em', array(), _('developername'), true, false).'@'.$this->getBoxForProject($project).$ssh_port.'/hg/'.$project->getUnixName().'/'.$repo_name).html_e('br');
167 $htmlRepo .= html_e('kbd', array(), 'hg clone ssh://'.html_e('em', array(), _('developername'), true, false).'@'.$this->getBoxForProject($project).$ssh_port.'/'.forge_get_config('repos_path', 'scmhg').'/'.$project->getUnixName().'/'.$repo_name).html_e('br');
170 $b .= html_e('p', array(), $htmlRepo);
173 if (forge_get_config('use_dav', 'scmhg')) {
174 $protocol = forge_get_config('use_ssl', 'scmhg')? 'https' : 'http';
175 $b .= '<div id="tabber-hgdav" class="tabbertab" >';
176 $b .= html_e('p', array(),
177 ngettext('Only project developers can access the Hg repository via this method.',
178 'Only project developers can access the Hg repositories via this method.',
180 ' '. _('Enter your site password when prompted.').
181 ' '. _('Substitute <em>developername</em> with the proper value.'));
183 foreach ($repo_list as $repo_name) {
184 $htmlRepo .= html_e('kbd', array(), 'hg clone '.$protocol.'://'.html_e('em', array(), _('developername'), true, false).'@'.$this->getBoxForProject($project).'/authscm/'.html_e('em', array(), _('developername'), true, false).'/hg/'.$project->getUnixName().'/'.$repo_name).html_e('br');
186 $b .= html_e('p', array(), $htmlRepo);
194 function getSnapshotPara($project) {
196 $filename = $project->getUnixName().'-scm-latest.tar'.util_get_compressed_file_extension();
197 if (file_exists(forge_get_config('scm_snapshots_path').'/'.$filename)) {
198 $b .= html_e('p', array(), '['.util_make_link('/snapshots.php?group_id='.$project->getID(), _('Download the nightly snapshot')).']');
203 function getBrowserLinkBlock($project) {
205 $b = html_e('h2', array(), _('Mercurial Repository Browser'));
206 $b .= html_e('p', array(), _('Browsing the Mercurial tree gives you a view into the current status of this project\'s code. You may also view the complete histories of any file in the repository.'));
207 $b .= html_e('p', array(), '['.util_make_link('/scm/browser.php?group_id='.$project->getID().'&scm_plugin='.$this->name, _('Browse Hg Repository')).']');
208 $repo_list = $this->getRepositories($project, false);
209 foreach ($repo_list as $repo_name) {
210 $b .= '['.util_make_link('/scm/browser.php?group_id='.$project->getID().'&extra='.$repo_name.'&scm_plugin='.$this->name, _('Browse extra Hg repository')._(': ').$repo_name).']'.html_e('br');
215 function getStatsBlock($project) {
219 $result = db_query_params('SELECT u.realname, u.user_name, u.user_id, sum(updates) as updates, sum(adds) as adds, sum(adds+commits) as combined, reponame FROM stats_cvs_user s, users u WHERE group_id=$1 AND s.user_id=u.user_id AND (commits>0 OR adds >0) GROUP BY u.user_id, realname, user_name, u.user_id, reponame ORDER BY reponame, combined DESC, realname',
220 array($project->getID()));
222 if (db_numrows($result) > 0) {
223 $tableHeaders = array(
228 $b .= $HTML->listTableTop($tableHeaders, array(), '', 'repo-history-'.$this->name);
231 $total = array('adds' => 0, 'updates' => 0);
233 while($data = db_fetch_array($result)) {
235 $cells[] = array(util_display_user($data['user_name'], $data['user_id'], $data['realname']), 'class' => 'halfwidth');
236 $cells[] = array($data['adds'], 'class' => 'onequarterwidth align-right');
237 $cells[] = array($data['updates'], 'class' => 'onequarterwidth align-right');
238 $b .= $HTML->multiTableRow(array(), $cells);
239 $total['adds'] += $data['adds'];
240 $total['updates'] += $data['updates'];
244 $cells[] = array(html_e('strong', array(), _('Total')._(':')), 'class' => 'halfwidth');
245 $cells[] = array($total['adds'], 'class' => 'onequarterwidth align-right');
246 $cells[] = array($total['updates'], 'class' => 'onequarterwidth align-right');
247 $b .= $HTML->multiTableRow(array(), $cells);
248 $b .= $HTML->listTableBottom();
250 $b .= $HTML->warning_msg(_('No history yet.'));
256 function printShortStats($params) {
257 $project = $this->checkParams($params);
261 if (forge_check_perm('scm', $project->getID(), 'read')) {
262 $result = db_query_params('SELECT sum(updates) AS updates, sum(adds) AS adds FROM stats_cvs_group WHERE group_id=$1',
263 array ($project->getID())) ;
264 $update_num = db_result($result,0,'updates');
265 $add_num = db_result($result,0,'adds');
272 $params['result'] .= ' (Mercurial: '.sprintf(_('<strong>%1$s</strong> updates, <strong>%2$s</strong> adds'), number_format($update_num, 0), number_format($add_num, 0)).")";
276 function printBrowserPage($params) {
277 if ($params['scm_plugin'] != $this->name) {
280 $project = $this->checkParams($params);
284 if ($this->browserDisplayable($project)) {
285 (isset($params['extra']) && $params['extra']) ? $extrarepo = $params['extra'] : $extrarepo = $project->getUnixName();
287 $protocol = forge_get_config('use_ssl', 'scmhg')? 'https' : 'http';
288 $box = $this->getBoxForProject($project);
289 if ($project->enableAnonSCM()) {
290 $iframesrc = $protocol.'://'.$box.'/anonscm/scmhg/cgi-bin/'.$project->getUnixName().'/'.$extrarepo;
291 } elseif (session_loggedin()) {
292 $logged_user = user_get_object(user_getid())->getUnixName();
293 $iframesrc = $protocol.'://'.$box.'/authscm/'.$logged_user.'/scmhg/cgi-bin/'.$project->getUnixName().'/'.$extrarepo.'/';
295 if ($params['commit']) {
296 $iframesrc .= '/rev/'.$params['commit'];
298 htmlIframeResizer($iframesrc, array('id'=>'scmhg', 'absolute'=>true), array('minHeight' => 400));
302 function createOrUpdateRepo($params) {
303 $project = $this->checkParams($params);
308 $project_name = $project->getUnixName();
309 $unix_group_ro = $project_name . '_scmro';
310 $unix_group_rw = $project_name . '_scmrw';
312 $root = forge_get_config('repos_path', 'scmhg') . '/' . $project_name;
313 if (!is_dir($root)) {
314 system("mkdir -p $root");
315 system("chgrp $unix_group_ro $root");
317 if ($project->enableAnonSCM()) {
318 system("chmod 2755 $root");
320 system("chmod 2750 $root");
323 /** per project configuration for http **/
324 //get template hgweb.cgi
325 $hgweb = forge_get_config('source_path').'/plugins/scmhg/cgi-bin/hgweb.cgi';
326 $project_hgweb = forge_get_config('source_path').'/www/plugins/scmhg/cgi-bin/'.$project_name;
327 if (!is_file($project_hgweb)) {
328 $lines = file($hgweb);
330 foreach ($lines as $line) {
331 if (preg_match("/\Aconfig = /",$line)) {
332 $repo_config .= 'config = "'.$root.'/config"'."\n";
334 $repo_config .= $line;
337 $f = fopen($project_hgweb, 'w');
338 fwrite($f, $repo_config);
340 $apache_user = forge_get_config('apache_user');
341 $apache_group = forge_get_config('apache_group');
342 system("chown $apache_user:$apache_group $project_hgweb");
343 system("chmod 755 $project_hgweb");
345 if (!is_file("$root/config")) {
346 $f = fopen("$root/config", 'w');
348 $conf .= "/ = ".$root.'/*'."\n";
352 if (!is_dir("$root/$project_name/.hg")) {
353 system("hg init $root/$project_name");
354 $f = fopen("$root/$project_name/.hg/hgrc", 'w');
356 $conf .= "baseurl = /hg/".$project_name."/".$project_name."\n";
357 $conf .= "description = ".$project_name."\n";
358 $conf .= "style = paper\n";
359 $conf .= "allow_push = *\n"; // every user (see Apache configuration) is allowed to push
360 $conf .= "allow_read = *\n"; // every user is allowed to clone and pull
361 if (!forge_get_config('use_ssl', 'scmhg')) {
362 $conf .= "push_ssl = 0\n";
366 system("chgrp -R $unix_group_rw $root/$project_name");
367 system("chmod -R g=rwX,o=rX $root/$project_name");
368 system("chmod 660 $root/$project_name/.hg/hgrc");
371 // Create project-wide secondary repositories
372 $result = db_query_params('SELECT repo_name, description, clone_url FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3',
373 array($project->getID(),
374 SCM_EXTRA_REPO_ACTION_UPDATE,
376 $rows = db_numrows($result);
377 for ($i = 0; $i < $rows; $i++) {
378 $repo_name = db_result($result, $i, 'repo_name');
379 $description = db_result($result, $i, 'description');
380 //no support for cloning from any URL, working dir...
381 $repodir = $root.'/'.$repo_name;
382 if (!is_dir("$repodir/.hg")) {
383 system("hg init $repodir");
384 $f = fopen("$repodir/.hg/hgrc", 'w');
386 $conf .= 'baseurl = /hg/'.$project_name.'/'.$repo_name."\n";
387 $conf .= "description = ".$description."\n";
388 $conf .= "style = paper\n";
389 $conf .= "allow_push = *\n"; // every user (see Apache configuration) is allowed to push
390 $conf .= "allow_read = *\n"; // every user is allowed to clone and pull
391 if (!forge_get_config('use_ssl', 'scmhg')) {
392 $conf .= "push_ssl = 0\n";
396 system("chgrp -R $unix_group_rw $repodir");
397 system("chmod -R g=rwX,o=rX $repodir");
398 system("chmod 660 $repodir/.hg/hgrc");
402 // Delete project-wide secondary repositories
403 $result = db_query_params ('SELECT repo_name FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3',
404 array($project->getID(),
405 SCM_EXTRA_REPO_ACTION_DELETE,
407 $rows = db_numrows ($result);
408 for ($i = 0; $i < $rows; $i++) {
409 $repo_name = db_result($result, $i, 'repo_name');
410 $repodir = $root.'/'.$repo_name;
411 if (util_is_valid_repository_name($repo_name)) {
412 system("rm -rf $repodir");
414 db_query_params ('DELETE FROM scm_secondary_repos WHERE group_id=$1 AND repo_name=$2 AND next_action = $3 AND plugin_id=$4',
415 array($project->getID(),
417 SCM_EXTRA_REPO_ACTION_DELETE,
422 function updateRepositoryList($params) {
423 $groups = $this->getGroups();
424 $unix_group = forge_get_config('apache_group');
425 $unix_user = forge_get_config('apache_user');
426 foreach ($groups as $project) {
427 if ($project->isError() || !$project->isActive() || !$project->usesSCM()) {
430 $repolist = $this->getRepositories($project);
431 foreach ($repolist as $repo_name) {
433 $read = ""; /*pull,clone*/
434 $path = forge_get_config('repos_path', 'scmhg').'/'.$project->getUnixName().'/'.$repo_name.'/.hg';
437 $users = $project->getMembers();
438 foreach ($users as $user) {
439 if (forge_check_perm_for_user($user, 'scm', $project->getID(), 'write')) {
446 $push .= $user->getUnixName();
447 $read .= $user->getUnixName();
450 } elseif (forge_check_perm_for_user($user, 'scm', $project->getID(), 'read')) {
454 $read .= $user->getUnixName();
460 if ($project->enableAnonSCM()) {
464 /*make new hgrc file*/
465 if (is_file($path.'/hgrc')) {
466 $hgrc_val = parse_ini_file($path.'/hgrc', true);
467 if (isset($hgrc_val['web'])) {
468 $hgrc_val['web']['allow_read'] = $read;
469 $hgrc_val['web']['allow_push'] = $push;
471 if (isset($hgrc_val['notify']['test'])) {
472 /* Set the value again, because parse_ini_file() converts boolean values to "" or "1" .
473 This would break the hgrc file.*/
474 $hgrc_val['notify']['test'] = 'false';
476 if (isset($hgrc_val['notify']['template'])) {
477 /*Set value again, because special character are not escaped*/
478 $hgrc_val['notify']['template'] = '"\ndetails: {webroot}/rev/{node|short}\nchangeset: {rev}:{node|short}\nuser: {author}\ndate: {date|date}\ndescription:\n{desc}\n"';
481 foreach ($hgrc_val as $section => $sub) {
482 $hgrc .= '['.$section."]\n";
483 foreach ($sub as $prop => $value) {
484 $hgrc .= "$prop = $value\n";
485 if ($value == end($sub)) {
492 $hgrc .= "baseurl = /hg/".$project->getUnixName().'/'.$repo_name;
493 $hgrc .= "\ndescription = ".$project->getUnixName().'/'.$repo_name;
494 $hgrc .= "\nstyle = paper";
495 $hgrc .= "\nallow_push = ".$push;
496 $hgrc .= "\nallow_read = ".$read;
497 if (!forge_get_config('use_ssl', 'scmhg')) {
498 $hgrc .= "\n".'push_ssl = 0';
502 $f = fopen($path.'/hgrc.new', 'w');
505 rename($path.'/hgrc.new', $path.'/hgrc');
506 system("chown $unix_user:$unix_group $path/hgrc");
507 system("chmod 660 $path/hgrc");
511 function generateSnapshots($params) {
512 $us = forge_get_config('use_scm_snapshots') ;
513 $ut = forge_get_config('use_scm_tarballs') ;
518 $project = $this->checkParams($params);
523 $group_name = $project->getUnixName();
524 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
525 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
527 if (!$project->enableAnonSCM()) {
528 if (is_file($snapshot)) {
531 if (is_file($tarball)) {
537 // TODO: ideally we generate one snapshot per hg repository
538 $toprepo = forge_get_config('repos_path', 'scmhg');
539 $repo = $toprepo . '/' . $project->getUnixName(). $project->getUnixName();
541 if (!is_dir($repo)) {
542 if (is_file($snapshot)) {
545 if (is_file($tarball)) {
551 $tmp = trim(`mktemp -d`);
556 system("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
557 chmod("$tmp/tarball", 0644);
558 copy("$tmp/tarball", $tarball);
559 unlink("$tmp/tarball");
560 system("rm -rf $tmp");
564 function gatherStats($params) {
565 global $last_user, $usr_adds, $usr_deletes, $usr_updates, $updates, $adds;
567 $project = $this->checkParams($params);
572 // since cronjobs are running as root, we need to trust apache user
573 if (!is_file('/root/.hgrc')) {
574 $trustdata = '[trusted]'.PHP_EOL.'users = '.forge_get_config('apache_user').PHP_EOL;
575 $f = fopen('/root/.hgrc', 'w');
576 fwrite($f, $trustdata);
580 if ($params['mode'] == 'day') {
581 $year = $params['year'];
582 $month = $params['month'];
583 $day = $params['day'];
584 $repolist = $this->getRepositories($project);
585 foreach ($repolist as $repo_name) {
586 $this->gatherStatsRepo($project, $repo_name, $year, $month, $day);
591 function gatherStatsRepo($project, $repo_name, $year, $month, $day) {
592 $month_string = sprintf("%04d%02d", $year, $month);
593 $start_time = gmmktime(0, 0, 0, $month, $day, $year);
594 $end_time = $start_time + 86400;
596 $usr_updates = array();
597 $usr_deletes = array();
598 $usr_commits = array();
603 $repo = forge_get_config('repos_path', 'scmhg').'/'.$project->getUnixName().'/'.$repo_name;
604 if (!is_dir($repo) || !is_dir("$repo/.hg")) {
605 // echo "No repository\n";
609 // cleaning stats_cvs_* table for the current day
610 $res = db_query_params('DELETE FROM stats_cvs_group WHERE month = $1 AND day = $2 AND group_id = $3 AND reponame = $4',
616 echo "Error while cleaning stats_cvs_group\n";
621 $res = db_query_params('DELETE FROM stats_cvs_user WHERE month = $1 AND day = $2 AND group_id = $3 AND reponame = $4',
627 echo "Error while cleaning stats_cvs_user\n" ;
632 //switch into scm_repository and take a look at the log informations
633 $cdir = chdir($repo);
635 //show customised log informations
636 $pipe = popen("hg log --style fflog.tmpl -d '$start_time 0 to $end_time 0'", 'r');
638 while (!feof($pipe) && $line = fgets ($pipe)) {
639 //determine between author line and file informations
640 if (preg_match("/(\A[AMD]) .*/", $line, $matches)) {
641 if ($last_user == "") {
644 switch ($matches[1]) {
646 $usr_adds[$last_user]++;
650 $usr_updates[$last_user]++;
654 $usr_deletes[$last_user]++;
658 $result = preg_match("/^(?P<name>.+) <(?P<mail>.+)>/", $line, $matches);
661 $last_user = $matches['name'];
662 $user2email[$last_user] = strtolower($matches['mail']);
663 if (!isset($usr_adds[$last_user])) {
664 $usr_adds[$last_user] = 0;
665 $usr_updates[$last_user] = 0;
666 $usr_deletes[$last_user] = 0;
667 $usr_commits[$last_user] = 0;
670 $usr_commits[$last_user]++;
677 // inserting group results in stats_cvs_groups
678 if ($updates > 0 || $adds > 0 || $deletes > 0 || $commits > 0) {
679 if (!db_query_params('INSERT INTO stats_cvs_group (month, day, group_id, checkouts, commits, adds, updates, deletes, reponame)
680 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)',
690 echo "Error while inserting into stats_cvs_group\n";
696 // building the user list
697 $user_list = array_unique(array_merge(array_keys($usr_adds), array_keys($usr_updates), array_keys($usr_deletes), array_keys($usr_commits)));
699 foreach ($user_list as $user) {
700 // Trying to get user id from user name or email
701 $u = user_get_object_by_name($user);
703 $user_id = $u->getID();
705 $res=db_query_params('SELECT user_id FROM users WHERE lower(realname)=$1 OR email=$2',
706 array(strtolower($user), $user2email[$user]));
707 if ($res && db_numrows($res) > 0) {
708 $user_id = db_result($res,0,'user_id');
714 $uc = isset($usr_commits[$user]) ? $usr_commits[$user] : 0;
715 $uu = isset($usr_updates[$user]) ? $usr_updates[$user] : 0;
716 $ua = isset($usr_adds[$user]) ? $usr_adds[$user] : 0;
717 $ud = isset($usr_deletes[$user]) ? $usr_deletes[$user] : 0;
718 if ($uu > 0 || $ua > 0 || $uc > 0 || $ud > 0) {
719 if (!db_query_params('INSERT INTO stats_cvs_user (month, day, group_id, user_id, commits, adds, updates, deletes, reponame)
720 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)',
730 echo "Error while inserting into stats_cvs_user\n";
739 function activity($params) {
740 $project = $this->checkParams($params);
744 if (isset($params['exclusive_area']) && ($params['exclusive_area'] != $this->name)) {
748 if (in_array('scmhg', $params['show']) || (count($params['show']) < 1)) {
749 if ($project->enableAnonSCM()) {
750 $server_script = '/anonscm/hglog';
751 } elseif (session_loggedin()) {
752 $u = session_get_user();
753 $server_script = '/authscm/'.$u->getUnixName().'/hglog';
758 $protocol = forge_get_config('use_ssl', 'scmhg') ? 'https://' : 'http://';
759 $repo_list = $this->getRepositories($project);
760 foreach ($repo_list as $repo_name) {
761 $script_url = $protocol.$this->getBoxForProject($project)
763 .'?unix_group_name='.$project->getUnixName()
764 .'&repo_name='.$repo_name
766 .'&begin='.$params['begin']
767 .'&end='.$params['end'];
768 $filename = tempnam('/tmp', 'hglog');
769 $f = fopen($filename, 'w');
771 curl_setopt($ch, CURLOPT_URL, $script_url);
772 curl_setopt($ch, CURLOPT_FILE, $f);
773 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, forge_get_config('use_ssl_verification'));
774 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, forge_get_config('use_ssl_verification'));
775 curl_setopt($ch, CURLOPT_COOKIE, @$_SERVER['HTTP_COOKIE']); // for session validation
776 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
777 curl_setopt($ch, CURLOPT_HTTPHEADER,
778 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
779 $body = curl_exec($ch);
780 if ($body === false) {
781 $this->setError(curl_error($ch));
784 fclose($f); // flush buffer
785 $f = fopen($filename, 'r');
788 while (!feof($f) && $data = fgets($f)) {
790 $splitedLine = explode('||', $line);
791 if (sizeof($splitedLine) == 4) {
793 $result['section'] = 'scm';
794 $result['group_id'] = $project->getID();
795 $result['ref_id'] = 'browser.php?group_id='.$project->getID().'&extra='.$repo_name.'&scm_plugin='.$this->name.'&commit='.$splitedLine[3];
796 $result['description'] = htmlspecialchars($splitedLine[2]).' (repository: '.$repo_name.', changeset: '.$splitedLine[3].')';
797 $userObject = user_get_object_by_email($splitedLine[1]);
798 if (is_a($userObject, 'FFUser')) {
799 $result['realname'] = util_display_user($userObject->getUnixName(), $userObject->getID(), $userObject->getRealName());
801 $result['realname'] = '';
803 $splitedDate = explode('-', $splitedLine[0]);
804 $result['activity_date'] = $splitedDate[0];
805 $result['subref_id'] = '';
806 $params['results'][] = $result;
811 if (!in_array($this->name, $params['ids']) && ($project->enableAnonSCM() || session_loggedin())) {
812 $params['ids'][] = $this->name;
813 $params['texts'][] = _('Hg Commits');
818 function scm_add_repo(&$params) {
819 if ($params['scm_plugin'] != $this->name) {
822 $project = $this->checkParams($params);
827 if (!isset($params['repo_name'])) {
831 if ($params['repo_name'] == $project->getUnixName()) {
832 $params['error_msg'] = _('Cannot create a secondary repository with the same name as the primary');
836 if (! util_is_valid_repository_name($params['repo_name'])) {
837 $params['error_msg'] = _('This repository name is not valid');
841 $result = db_query_params('SELECT count(*) AS count FROM scm_secondary_repos WHERE group_id=$1 AND repo_name = $2 AND plugin_id=$3',
842 array($params['group_id'],
843 $params['repo_name'],
846 $params['error_msg'] = db_error();
849 if (db_result($result, 0, 'count')) {
850 $params['error_msg'] = sprintf(_('A repository %s already exists'), $params['repo_name']);
856 if (isset($params['description'])) {
857 $description = $params['description'];
860 $description = "Hg repository $params[repo_name] for project ".$project->getUnixName();
863 $result = db_query_params('INSERT INTO scm_secondary_repos (group_id, repo_name, description, clone_url, plugin_id) VALUES ($1, $2, $3, $4, $5)',
864 array($params['group_id'],
865 $params['repo_name'],
870 $params['error_msg'] = db_error();
877 function scm_admin_form(&$params) {
879 $project = $this->checkParams($params);
884 session_require_perm('project_admin', $params['group_id']);
885 if (forge_get_config('allow_multiple_scm') && ($params['allow_multiple_scm'] > 1)) {
886 echo html_ao('div', array('id' => 'tabber-'.$this->name, 'class' => 'tabbertab'));
889 $project_name = $project->getUnixName();
890 $result = db_query_params('SELECT repo_name, description FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3 ORDER BY repo_name',
891 array($params['group_id'],
892 SCM_EXTRA_REPO_ACTION_UPDATE,
895 $params['error_msg'] = db_error();
898 $existing_repos = array();
899 while ($data = db_fetch_array($result)) {
900 $existing_repos[] = array('repo_name' => $data['repo_name'],
901 'description' => $data['description']);
903 if (empty($existing_repos)) {
904 echo $HTML->information(_('No extra Hg repository for project').' '.$project_name);
906 echo html_e('h2', array(), sprintf(ngettext('Extra Hg repository for project %1$s',
907 'Extra Hg repositories for project %1$s',
908 count($existing_repos)), $project_name));
909 $titleArr = array(_('Repository name'), ('Initial repository description'), _('Delete'));
910 echo $HTML->listTableTop($titleArr);
911 foreach ($existing_repos as $repo) {
913 $cells[][] = html_e('kbd', array(), $repo['repo_name']);
914 $cells[][] = $repo['description'];
915 $deleteForm = $HTML->openForm(array('name' => 'form_delete_repo_'.$repo['repo_name'], 'action' => getStringFromServer('PHP_SELF'), 'method' => 'post'));
916 $deleteForm .= html_e('input', array('type' => 'hidden', 'name' => 'group_id', 'value' => $params['group_id']));
917 $deleteForm .= html_e('input', array('type' => 'hidden', 'name' => 'delete_repository', 'value' => 1));
918 $deleteForm .= html_e('input', array('type' => 'hidden', 'name' => 'repo_name', 'value' => $repo['repo_name']));
919 $deleteForm .= $HTML->html_input('scm_plugin_id', '', '', 'hidden', $this->getID());
920 $deleteForm .= html_e('input', array('type' => 'submit', 'name' => 'submit', 'value' => _('Delete')));
921 $deleteForm .= $HTML->closeForm();
922 $cells[][] = $deleteForm;
923 echo $HTML->multiTableRow(array(), $cells);
925 echo $HTML->listTableBottom();
928 echo html_e('h2', array(), sprintf(_('Create new Hg repository for project %s'), $project_name));
929 echo $HTML->openForm(array('name' => 'form_create_repo_scmhg', 'action' => getStringFromServer('PHP_SELF'), 'method' => 'post'));
930 echo $HTML->html_input('group_id', '', '', 'hidden', $params['group_id']);
931 echo $HTML->html_input('create_repository', '', '', 'hidden', 1);
932 echo $HTML->html_input('scm_plugin', '', '', 'hidden', $this->name);
933 echo $HTML->html_input('repo_name', '', html_e('strong', array(), _('Repository name')._(':')).utils_requiredField(), 'text', '', array('required' => 'required', 'size' => 20));
935 echo $HTML->html_input('description', '', html_e('strong', array(), _('Description')._(':')), 'text', '', array('size' => 60));
937 echo $HTML->html_input('cancel', '', '', 'submit', _('Cancel'), array(), array('style' => 'display: inline-block!important'));
938 echo $HTML->html_input('submit', '', '', 'submit', _('Submit'), array(), array('style' => 'display: inline-block!important'));
939 echo $HTML->closeForm();
940 if ($project->usesPlugin('scmhook')) {
941 $scmhookPlugin = plugin_get_object('scmhook');
942 $scmhookPlugin->displayScmHook($project->getID(), $this->name);
944 if (forge_get_config('allow_multiple_scm') && ($params['allow_multiple_scm'] > 1)) {
945 echo html_ac(html_ap() - 1);
949 function getRepositories($group, $autoinclude = true) {
952 $repoarr[] = $group->getUnixName();
954 $result = db_query_params('SELECT repo_name FROM scm_secondary_repos WHERE group_id = $1 AND next_action = $2 AND plugin_id = $3 ORDER BY repo_name',
955 array($group->getID(),
956 SCM_EXTRA_REPO_ACTION_UPDATE,
958 while ($arr = db_fetch_array($result)) {
959 $repoarr[] = $arr['repo_name'];