3 * FusionForge Mercurial (Hg) plugin
5 * Copyright 2009, Roland Mas
6 * Copyright 2012, Denise Patzker
7 * Copyright 2012-2014,2017-2019, 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.'://<i>'.$d.'</i>@'.$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 ' '. _('Substitute <em>developername</em> with the proper value.'));
160 foreach ($repo_list as $repo_name) {
161 // Warning : the ssh uri MUST be this form : ssh://username@scmbox//path/reponame
162 // HAVE YOU SEEN THE // starting the path ? Keep in mind the double /
163 if (forge_get_config('use_shell_limited')) {
164 $htmlRepo .= html_e('kbd', array(), 'hg clone ssh://'.html_e('i', array(), _('developername'), true, false).'@'.$this->getBoxForProject($project).$ssh_port.'/hg/'.$project->getUnixName().'/'.$repo_name).html_e('br');
166 $htmlRepo .= html_e('kbd', array(), 'hg clone ssh://'.html_e('i', array(), _('developername'), true, false).'@'.$this->getBoxForProject($project).$ssh_port.'/'.forge_get_config('repos_path', 'scmhg').'/'.$project->getUnixName().'/'.$repo_name).html_e('br');
169 $b .= html_e('p', array(), $htmlRepo);
172 if (forge_get_config('use_dav', 'scmhg')) {
173 $protocol = forge_get_config('use_ssl', 'scmhg')? 'https' : 'http';
174 $b .= '<div id="tabber-hgdav" class="tabbertab" >';
175 $b .= html_e('p', array(),
176 ngettext('Only project developers can access the Hg repository via this method.',
177 'Only project developers can access the Hg repositories via this method.',
179 ' '. _('Enter your site password when prompted.'));
181 foreach ($repo_list as $repo_name) {
182 $htmlRepo .= html_e('kbd', array(), 'hg clone '.$protocol.'://'.html_e('i', array(), _('developername'), true, false).'@'.$this->getBoxForProject($project).'/authscm/'.html_e('i', array(), _('developername'), true, false).'/hg/'.$project->getUnixName().'/'.$repo_name).html_e('br');
184 $b .= html_e('p', array(), $htmlRepo);
192 function getSnapshotPara($project) {
194 $filename = $project->getUnixName().'-scm-latest.tar'.util_get_compressed_file_extension();
195 if (file_exists(forge_get_config('scm_snapshots_path').'/'.$filename)) {
196 $b .= html_e('p', array(), '['.util_make_link('/snapshots.php?group_id='.$project->getID(), _('Download the nightly snapshot')).']');
201 function getBrowserLinkBlock($project) {
203 $b = html_e('h2', array(), _('Mercurial Repository Browser'));
204 $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.'));
205 $b .= html_e('p', array(), '['.util_make_link('/scm/browser.php?group_id='.$project->getID().'&scm_plugin='.$this->name, _('Browse Hg Repository')).']');
206 $repo_list = $this->getRepositories($project, false);
207 foreach ($repo_list as $repo_name) {
208 $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');
213 function getStatsBlock($project) {
217 $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',
218 array($project->getID()));
220 if (db_numrows($result) > 0) {
221 $tableHeaders = array(
226 $b .= $HTML->listTableTop($tableHeaders, array(), '', 'repo-history-'.$this->name);
229 $total = array('adds' => 0, 'updates' => 0);
231 while($data = db_fetch_array($result)) {
233 $cells[] = array(util_display_user($data['user_name'], $data['user_id'], $data['realname']), 'class' => 'halfwidth');
234 $cells[] = array($data['adds'], 'class' => 'onequarterwidth align-right');
235 $cells[] = array($data['updates'], 'class' => 'onequarterwidth align-right');
236 $b .= $HTML->multiTableRow(array(), $cells);
237 $total['adds'] += $data['adds'];
238 $total['updates'] += $data['updates'];
242 $cells[] = array(html_e('strong', array(), _('Total')._(':')), 'class' => 'halfwidth');
243 $cells[] = array($total['adds'], 'class' => 'onequarterwidth align-right');
244 $cells[] = array($total['updates'], 'class' => 'onequarterwidth align-right');
245 $b .= $HTML->multiTableRow(array(), $cells);
246 $b .= $HTML->listTableBottom();
248 $b .= $HTML->warning_msg(_('No history yet.'));
254 function printShortStats($params) {
255 $project = $this->checkParams($params);
259 if (forge_check_perm('scm', $project->getID(), 'read')) {
260 $result = db_query_params('SELECT sum(updates) AS updates, sum(adds) AS adds FROM stats_cvs_group WHERE group_id=$1',
261 array ($project->getID())) ;
262 $update_num = db_result($result,0,'updates');
263 $add_num = db_result($result,0,'adds');
270 $params['result'] .= ' (Mercurial: '.sprintf(_('<strong>%1$s</strong> updates, <strong>%2$s</strong> adds'), number_format($update_num, 0), number_format($add_num, 0)).")";
274 function printBrowserPage($params) {
275 if ($params['scm_plugin'] != $this->name) {
278 $project = $this->checkParams($params);
282 if ($this->browserDisplayable($project)) {
283 (isset($params['extra']) && $params['extra']) ? $extrarepo = $params['extra'] : $extrarepo = $project->getUnixName();
285 $protocol = forge_get_config('use_ssl', 'scmhg')? 'https' : 'http';
286 $box = $this->getBoxForProject($project);
287 if ($project->enableAnonSCM()) {
288 $iframesrc = $protocol.'://'.$box.'/anonscm/scmhg/cgi-bin/'.$project->getUnixName().'/'.$extrarepo;
289 } elseif (session_loggedin()) {
290 $logged_user = user_get_object(user_getid())->getUnixName();
291 $iframesrc = $protocol.'://'.$box.'/authscm/'.$logged_user.'/scmhg/cgi-bin/'.$project->getUnixName().'/'.$extrarepo.'/';
293 if ($params['commit']) {
294 $iframesrc .= '/rev/'.$params['commit'];
296 htmlIframeResizer($iframesrc, array('id'=>'scmhg', 'absolute'=>true), array('minHeight' => 400));
300 function createOrUpdateRepo($params) {
301 $project = $this->checkParams($params);
306 $project_name = $project->getUnixName();
307 $unix_group_ro = $project_name . '_scmro';
308 $unix_group_rw = $project_name . '_scmrw';
310 $root = forge_get_config('repos_path', 'scmhg') . '/' . $project_name;
311 if (!is_dir($root)) {
312 system("mkdir -p $root");
313 system("chgrp $unix_group_ro $root");
315 if ($project->enableAnonSCM()) {
316 system("chmod 2755 $root");
318 system("chmod 2750 $root");
321 /** per project configuration for http **/
322 //get template hgweb.cgi
323 $hgweb = forge_get_config('source_path').'/plugins/scmhg/cgi-bin/hgweb.cgi';
324 $project_hgweb = forge_get_config('source_path').'/www/plugins/scmhg/cgi-bin/'.$project_name;
325 if (!is_file($project_hgweb)) {
326 $lines = file($hgweb);
328 foreach ($lines as $line) {
329 if (preg_match("/\Aconfig = /",$line)) {
330 $repo_config .= 'config = "'.$root.'/config"'."\n";
332 $repo_config .= $line;
335 $f = fopen($project_hgweb, 'w');
336 fwrite($f, $repo_config);
338 $apache_user = forge_get_config('apache_user');
339 $apache_group = forge_get_config('apache_group');
340 system("chown $apache_user:$apache_group $project_hgweb");
341 system("chmod 755 $project_hgweb");
343 if (!is_file("$root/config")) {
344 $f = fopen("$root/config", 'w');
346 $conf .= "/ = ".$root.'/*'."\n";
350 if (!is_dir("$root/$project_name/.hg")) {
351 system("hg init $root/$project_name");
352 $f = fopen("$root/$project_name/.hg/hgrc", 'w');
354 $conf .= "baseurl = /hg/".$project_name."/".$project_name."\n";
355 $conf .= "description = ".$project_name."\n";
356 $conf .= "style = paper\n";
357 $conf .= "allow_push = *\n"; // every user (see Apache configuration) is allowed to push
358 $conf .= "allow_read = *\n"; // every user is allowed to clone and pull
359 if (!forge_get_config('use_ssl', 'scmhg')) {
360 $conf .= "push_ssl = 0\n";
364 system("chgrp -R $unix_group_rw $root/$project_name");
365 system("chmod -R g=rwX,o=rX $root/$project_name");
366 system("chmod 660 $root/$project_name/.hg/hgrc");
369 // Create project-wide secondary repositories
370 $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',
371 array($project->getID(),
372 SCM_EXTRA_REPO_ACTION_UPDATE,
374 $rows = db_numrows($result);
375 for ($i = 0; $i < $rows; $i++) {
376 $repo_name = db_result($result, $i, 'repo_name');
377 $description = db_result($result, $i, 'description');
378 //no support for cloning from any URL, working dir...
379 $repodir = $root.'/'.$repo_name;
380 if (!is_dir("$repodir/.hg")) {
381 system("hg init $repodir");
382 $f = fopen("$repodir/.hg/hgrc", 'w');
384 $conf .= 'baseurl = /hg/'.$project_name.'/'.$repo_name."\n";
385 $conf .= "description = ".$description."\n";
386 $conf .= "style = paper\n";
387 $conf .= "allow_push = *\n"; // every user (see Apache configuration) is allowed to push
388 $conf .= "allow_read = *\n"; // every user is allowed to clone and pull
389 if (!forge_get_config('use_ssl', 'scmhg')) {
390 $conf .= "push_ssl = 0\n";
394 system("chgrp -R $unix_group_rw $repodir");
395 system("chmod -R g=rwX,o=rX $repodir");
396 system("chmod 660 $repodir/.hg/hgrc");
400 // Delete project-wide secondary repositories
401 $result = db_query_params ('SELECT repo_name FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3',
402 array($project->getID(),
403 SCM_EXTRA_REPO_ACTION_DELETE,
405 $rows = db_numrows ($result);
406 for ($i = 0; $i < $rows; $i++) {
407 $repo_name = db_result($result, $i, 'repo_name');
408 $repodir = $root.'/'.$repo_name;
409 if (util_is_valid_repository_name($repo_name)) {
410 system("rm -rf $repodir");
412 db_query_params ('DELETE FROM scm_secondary_repos WHERE group_id=$1 AND repo_name=$2 AND next_action = $3 AND plugin_id=$4',
413 array($project->getID(),
415 SCM_EXTRA_REPO_ACTION_DELETE,
420 function updateRepositoryList($params) {
421 $groups = $this->getGroups();
422 $unix_group = forge_get_config('apache_group');
423 $unix_user = forge_get_config('apache_user');
424 foreach ($groups as $project) {
425 if ($project->isError() || !$project->isActive() || !$project->usesSCM()) {
428 $repolist = $this->getRepositories($project);
429 foreach ($repolist as $repo_name) {
431 $read = ""; /*pull,clone*/
432 $path = forge_get_config('repos_path', 'scmhg').'/'.$project->getUnixName().'/'.$repo_name.'/.hg';
435 $users = $project->getMembers();
436 foreach ($users as $user) {
437 if (forge_check_perm_for_user($user, 'scm', $project->getID(), 'write')) {
444 $push .= $user->getUnixName();
445 $read .= $user->getUnixName();
448 } elseif (forge_check_perm_for_user($user, 'scm', $project->getID(), 'read')) {
452 $read .= $user->getUnixName();
458 if ($project->enableAnonSCM()) {
462 /*make new hgrc file*/
463 if (is_file($path.'/hgrc')) {
464 $hgrc_val = parse_ini_file($path.'/hgrc', true);
465 if (isset($hgrc_val['web'])) {
466 $hgrc_val['web']['allow_read'] = $read;
467 $hgrc_val['web']['allow_push'] = $push;
469 if (isset($hgrc_val['notify']['test'])) {
470 /* Set the value again, because parse_ini_file() converts boolean values to "" or "1" .
471 This would break the hgrc file.*/
472 $hgrc_val['notify']['test'] = 'false';
474 if (isset($hgrc_val['notify']['template'])) {
475 /*Set value again, because special character are not escaped*/
476 $hgrc_val['notify']['template'] = '"\ndetails: {webroot}/rev/{node|short}\nchangeset: {rev}:{node|short}\nuser: {author}\ndate: {date|date}\ndescription:\n{desc}\n"';
479 foreach ($hgrc_val as $section => $sub) {
480 $hgrc .= '['.$section."]\n";
481 foreach ($sub as $prop => $value) {
482 $hgrc .= "$prop = $value\n";
483 if ($value == end($sub)) {
490 $hgrc .= "baseurl = /hg/".$project->getUnixName().'/'.$repo_name;
491 $hgrc .= "\ndescription = ".$project->getUnixName().'/'.$repo_name;
492 $hgrc .= "\nstyle = paper";
493 $hgrc .= "\nallow_push = ".$push;
494 $hgrc .= "\nallow_read = ".$read;
495 if (!forge_get_config('use_ssl', 'scmhg')) {
496 $hgrc .= "\n".'push_ssl = 0';
500 $f = fopen($path.'/hgrc.new', 'w');
503 rename($path.'/hgrc.new', $path.'/hgrc');
504 system("chown $unix_user:$unix_group $path/hgrc");
505 system("chmod 660 $path/hgrc");
509 function generateSnapshots($params) {
510 $us = forge_get_config('use_scm_snapshots') ;
511 $ut = forge_get_config('use_scm_tarballs') ;
516 $project = $this->checkParams($params);
521 $group_name = $project->getUnixName();
522 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
523 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
525 if (!$project->enableAnonSCM()) {
526 if (is_file($snapshot)) {
529 if (is_file($tarball)) {
535 // TODO: ideally we generate one snapshot per hg repository
536 $toprepo = forge_get_config('repos_path', 'scmhg');
537 $repo = $toprepo . '/' . $project->getUnixName(). $project->getUnixName();
539 if (!is_dir($repo)) {
540 if (is_file($snapshot)) {
543 if (is_file($tarball)) {
549 $tmp = trim(`mktemp -d`);
554 system("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
555 chmod("$tmp/tarball", 0644);
556 copy("$tmp/tarball", $tarball);
557 unlink("$tmp/tarball");
558 system("rm -rf $tmp");
562 function gatherStats($params) {
563 global $last_user, $usr_adds, $usr_deletes, $usr_updates, $updates, $adds;
565 $project = $this->checkParams($params);
570 // since cronjobs are running as root, we need to trust apache user
571 if (!is_file('/root/.hgrc')) {
572 $trustdata = '[trusted]'.PHP_EOL.'users = '.forge_get_config('apache_user').PHP_EOL;
573 $f = fopen('/root/.hgrc', 'w');
574 fwrite($f, $trustdata);
578 if ($params['mode'] == 'day') {
579 $year = $params['year'];
580 $month = $params['month'];
581 $day = $params['day'];
582 $repolist = $this->getRepositories($project);
583 foreach ($repolist as $repo_name) {
584 $this->gatherStatsRepo($project, $repo_name, $year, $month, $day);
589 function gatherStatsRepo($project, $repo_name, $year, $month, $day) {
590 $month_string = sprintf("%04d%02d", $year, $month);
591 $start_time = gmmktime(0, 0, 0, $month, $day, $year);
592 $end_time = $start_time + 86400;
594 $usr_updates = array();
595 $usr_deletes = array();
596 $usr_commits = array();
601 $repo = forge_get_config('repos_path', 'scmhg').'/'.$project->getUnixName().'/'.$repo_name;
602 if (!is_dir($repo) || !is_dir("$repo/.hg")) {
603 // echo "No repository\n";
607 // cleaning stats_cvs_* table for the current day
608 $res = db_query_params('DELETE FROM stats_cvs_group WHERE month = $1 AND day = $2 AND group_id = $3 AND reponame = $4',
614 echo "Error while cleaning stats_cvs_group\n";
619 $res = db_query_params('DELETE FROM stats_cvs_user WHERE month = $1 AND day = $2 AND group_id = $3 AND reponame = $4',
625 echo "Error while cleaning stats_cvs_user\n" ;
630 //switch into scm_repository and take a look at the log informations
631 $cdir = chdir($repo);
633 //show customised log informations
634 $pipe = popen("hg log --style fflog.tmpl -d '$start_time 0 to $end_time 0'", 'r');
636 while (!feof($pipe) && $line = fgets ($pipe)) {
637 //determine between author line and file informations
638 if (preg_match("/(\A[AMD]) .*/", $line, $matches)) {
639 if ($last_user == "") {
642 switch ($matches[1]) {
644 $usr_adds[$last_user]++;
648 $usr_updates[$last_user]++;
652 $usr_deletes[$last_user]++;
656 $result = preg_match("/^(?P<name>.+) <(?P<mail>.+)>/", $line, $matches);
659 $last_user = $matches['name'];
660 $user2email[$last_user] = strtolower($matches['mail']);
661 if (!isset($usr_adds[$last_user])) {
662 $usr_adds[$last_user] = 0;
663 $usr_updates[$last_user] = 0;
664 $usr_deletes[$last_user] = 0;
665 $usr_commits[$last_user] = 0;
668 $usr_commits[$last_user]++;
675 // inserting group results in stats_cvs_groups
676 if ($updates > 0 || $adds > 0 || $deletes > 0 || $commits > 0) {
677 if (!db_query_params('INSERT INTO stats_cvs_group (month, day, group_id, checkouts, commits, adds, updates, deletes, reponame)
678 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)',
688 echo "Error while inserting into stats_cvs_group\n";
694 // building the user list
695 $user_list = array_unique(array_merge(array_keys($usr_adds), array_keys($usr_updates), array_keys($usr_deletes), array_keys($usr_commits)));
697 foreach ($user_list as $user) {
698 // Trying to get user id from user name or email
699 $u = user_get_object_by_name($user);
701 $user_id = $u->getID();
703 $res=db_query_params('SELECT user_id FROM users WHERE lower(realname)=$1 OR email=$2',
704 array(strtolower($user), $user2email[$user]));
705 if ($res && db_numrows($res) > 0) {
706 $user_id = db_result($res,0,'user_id');
712 $uc = isset($usr_commits[$user]) ? $usr_commits[$user] : 0;
713 $uu = isset($usr_updates[$user]) ? $usr_updates[$user] : 0;
714 $ua = isset($usr_adds[$user]) ? $usr_adds[$user] : 0;
715 $ud = isset($usr_deletes[$user]) ? $usr_deletes[$user] : 0;
716 if ($uu > 0 || $ua > 0 || $uc > 0 || $ud > 0) {
717 if (!db_query_params('INSERT INTO stats_cvs_user (month, day, group_id, user_id, commits, adds, updates, deletes, reponame)
718 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)',
728 echo "Error while inserting into stats_cvs_user\n";
737 function activity($params) {
738 $project = $this->checkParams($params);
742 if (isset($params['exclusive_area']) && ($params['exclusive_area'] != $this->name)) {
746 if (in_array('scmhg', $params['show']) || (count($params['show']) < 1)) {
747 if ($project->enableAnonSCM()) {
748 $server_script = '/anonscm/hglog';
749 } elseif (session_loggedin()) {
750 $u = session_get_user();
751 $server_script = '/authscm/'.$u->getUnixName().'/hglog';
756 $protocol = forge_get_config('use_ssl', 'scmhg') ? 'https://' : 'http://';
757 $repo_list = $this->getRepositories($project);
758 foreach ($repo_list as $repo_name) {
759 $script_url = $protocol.$this->getBoxForProject($project)
761 .'?unix_group_name='.$project->getUnixName()
762 .'&repo_name='.$repo_name
764 .'&begin='.$params['begin']
765 .'&end='.$params['end'];
766 $filename = tempnam('/tmp', 'hglog');
767 $f = fopen($filename, 'w');
769 curl_setopt($ch, CURLOPT_URL, $script_url);
770 curl_setopt($ch, CURLOPT_FILE, $f);
771 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, forge_get_config('use_ssl_verification'));
772 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, forge_get_config('use_ssl_verification'));
773 curl_setopt($ch, CURLOPT_COOKIE, @$_SERVER['HTTP_COOKIE']); // for session validation
774 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
775 curl_setopt($ch, CURLOPT_HTTPHEADER,
776 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
777 $body = curl_exec($ch);
778 if ($body === false) {
779 $this->setError(curl_error($ch));
782 fclose($f); // flush buffer
783 $f = fopen($filename, 'r');
786 while (!feof($f) && $data = fgets($f)) {
788 $splitedLine = explode('||', $line);
789 if (sizeof($splitedLine) == 4) {
791 $result['section'] = 'scm';
792 $result['group_id'] = $project->getID();
793 $result['ref_id'] = 'browser.php?group_id='.$project->getID().'&extra='.$repo_name.'&scm_plugin='.$this->name.'&commit='.$splitedLine[3];
794 $result['description'] = htmlspecialchars($splitedLine[2]).' (repository: '.$repo_name.', changeset: '.$splitedLine[3].')';
795 $userObject = user_get_object_by_email($splitedLine[1]);
796 if (is_a($userObject, 'FFUser')) {
797 $result['realname'] = util_display_user($userObject->getUnixName(), $userObject->getID(), $userObject->getRealName());
799 $result['realname'] = '';
801 $splitedDate = explode('-', $splitedLine[0]);
802 $result['activity_date'] = $splitedDate[0];
803 $result['subref_id'] = '';
804 $params['results'][] = $result;
809 if (!in_array($this->name, $params['ids']) && ($project->enableAnonSCM() || session_loggedin())) {
810 $params['ids'][] = $this->name;
811 $params['texts'][] = _('Hg Commits');
816 function scm_add_repo(&$params) {
817 if ($params['scm_plugin'] != $this->name) {
820 $project = $this->checkParams($params);
825 if (!isset($params['repo_name'])) {
829 if ($params['repo_name'] == $project->getUnixName()) {
830 $params['error_msg'] = _('Cannot create a secondary repository with the same name as the primary');
834 if (! util_is_valid_repository_name($params['repo_name'])) {
835 $params['error_msg'] = _('This repository name is not valid');
839 $result = db_query_params('SELECT count(*) AS count FROM scm_secondary_repos WHERE group_id=$1 AND repo_name = $2 AND plugin_id=$3',
840 array($params['group_id'],
841 $params['repo_name'],
844 $params['error_msg'] = db_error();
847 if (db_result($result, 0, 'count')) {
848 $params['error_msg'] = sprintf(_('A repository %s already exists'), $params['repo_name']);
854 if (isset($params['description'])) {
855 $description = $params['description'];
858 $description = "Hg repository $params[repo_name] for project ".$project->getUnixName();
861 $result = db_query_params('INSERT INTO scm_secondary_repos (group_id, repo_name, description, clone_url, plugin_id) VALUES ($1, $2, $3, $4, $5)',
862 array($params['group_id'],
863 $params['repo_name'],
868 $params['error_msg'] = db_error();
875 function scm_admin_form(&$params) {
877 $project = $this->checkParams($params);
882 session_require_perm('project_admin', $params['group_id']);
883 if (forge_get_config('allow_multiple_scm') && ($params['allow_multiple_scm'] > 1)) {
884 echo html_ao('div', array('id' => 'tabber-'.$this->name, 'class' => 'tabbertab'));
887 $project_name = $project->getUnixName();
888 $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',
889 array($params['group_id'],
890 SCM_EXTRA_REPO_ACTION_UPDATE,
893 $params['error_msg'] = db_error();
896 $existing_repos = array();
897 while ($data = db_fetch_array($result)) {
898 $existing_repos[] = array('repo_name' => $data['repo_name'],
899 'description' => $data['description']);
901 if (empty($existing_repos)) {
902 echo $HTML->information(_('No extra Hg repository for project').' '.$project_name);
904 echo html_e('h2', array(), sprintf(ngettext('Extra Hg repository for project %1$s',
905 'Extra Hg repositories for project %1$s',
906 count($existing_repos)), $project_name));
907 $titleArr = array(_('Repository name'), ('Initial repository description'), _('Delete'));
908 echo $HTML->listTableTop($titleArr);
909 foreach ($existing_repos as $repo) {
911 $cells[][] = html_e('kbd', array(), $repo['repo_name']);
912 $cells[][] = $repo['description'];
913 $deleteForm = $HTML->openForm(array('name' => 'form_delete_repo_'.$repo['repo_name'], 'action' => getStringFromServer('PHP_SELF'), 'method' => 'post'));
914 $deleteForm .= html_e('input', array('type' => 'hidden', 'name' => 'group_id', 'value' => $params['group_id']));
915 $deleteForm .= html_e('input', array('type' => 'hidden', 'name' => 'delete_repository', 'value' => 1));
916 $deleteForm .= html_e('input', array('type' => 'hidden', 'name' => 'repo_name', 'value' => $repo['repo_name']));
917 $deleteForm .= $HTML->html_input('scm_plugin_id', '', '', 'hidden', $this->getID());
918 $deleteForm .= html_e('input', array('type' => 'submit', 'name' => 'submit', 'value' => _('Delete')));
919 $deleteForm .= $HTML->closeForm();
920 $cells[][] = $deleteForm;
921 echo $HTML->multiTableRow(array(), $cells);
923 echo $HTML->listTableBottom();
926 echo html_e('h2', array(), sprintf(_('Create new Hg repository for project %s'), $project_name));
927 echo $HTML->openForm(array('name' => 'form_create_repo_scmhg', 'action' => getStringFromServer('PHP_SELF'), 'method' => 'post'));
928 echo $HTML->html_input('group_id', '', '', 'hidden', $params['group_id']);
929 echo $HTML->html_input('create_repository', '', '', 'hidden', 1);
930 echo $HTML->html_input('scm_plugin', '', '', 'hidden', $this->name);
931 echo $HTML->html_input('repo_name', '', html_e('strong', array(), _('Repository name')._(':')).utils_requiredField(), 'text', '', array('required' => 'required', 'size' => 20));
933 echo $HTML->html_input('description', '', html_e('strong', array(), _('Description')._(':')), 'text', '', array('size' => 60));
935 echo $HTML->html_input('cancel', '', '', 'submit', _('Cancel'), array(), array('style' => 'display: inline-block!important'));
936 echo $HTML->html_input('submit', '', '', 'submit', _('Submit'), array(), array('style' => 'display: inline-block!important'));
937 echo $HTML->closeForm();
938 if ($project->usesPlugin('scmhook')) {
939 $scmhookPlugin = plugin_get_object('scmhook');
940 $scmhookPlugin->displayScmHook($project->getID(), $this->name);
942 if (forge_get_config('allow_multiple_scm') && ($params['allow_multiple_scm'] > 1)) {
943 echo html_ac(html_ap() - 1);
947 function getRepositories($group, $autoinclude = true) {
950 $repoarr[] = $group->getUnixName();
952 $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',
953 array($group->getID(),
954 SCM_EXTRA_REPO_ACTION_UPDATE,
956 while ($arr = db_fetch_array($result)) {
957 $repoarr[] = $arr['repo_name'];