3 * FusionForge Git plugin
5 * Copyright 2009, Roland Mas
6 * Copyright 2009, Mehdi Dogguy <mehdi@debian.org>
7 * Copyright 2012, Franck Villaume - TrivialDev
8 * http://fusionforge.org
10 * This file is part of FusionForge.
12 * FusionForge is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published
14 * by the Free Software Foundation; either version 2 of the License,
15 * or (at your option) any later version.
17 * FusionForge is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
22 * You should have received a copy of the GNU General Public License along
23 * with this program; if not, write to the Free Software Foundation, Inc.,
24 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 forge_define_config_item('default_server', 'scmgit', forge_get_config ('web_host')) ;
28 forge_define_config_item('repos_path', 'scmgit', forge_get_config('chroot').'/scmrepos/git') ;
30 class GitPlugin extends SCMPlugin {
31 function GitPlugin() {
33 $this->name = 'scmgit';
35 $this->_addHook('scm_browser_page');
36 $this->_addHook('scm_update_repolist');
37 $this->_addHook('scm_generate_snapshots');
38 $this->_addHook('scm_gather_stats');
39 $this->_addHook('scm_admin_form');
40 $this->_addHook('scm_add_repo');
41 $this->_addHook('scm_delete_repo');
42 $this->_addHook('widget_instance', 'myPageBox', false);
43 $this->_addHook('widgets', 'widgets', false);
44 $this->_addHook('activity');
45 $this->_addHook('weekly');
49 function getDefaultServer() {
50 return forge_get_config('default_server', 'scmgit') ;
53 function printShortStats ($params) {
54 $project = $this->checkParams($params);
59 if ($project->usesPlugin($this->name)) {
60 $result = db_query_params('SELECT sum(commits) AS commits, sum(adds) AS adds FROM stats_cvs_group WHERE group_id=$1',
61 array ($project->getID())) ;
62 $commit_num = db_result($result,0,'commits');
63 $add_num = db_result($result,0,'adds');
70 echo ' (Git: '.sprintf(_('<strong>%1$s</strong> commits, <strong>%2$s</strong> adds'), number_format($commit_num, 0), number_format($add_num, 0)).")";
75 return '<p>' . _('Documentation for Git is available at <a href="http://git-scm.com/">http://git-scm.com/</a>.') . '</p>';
78 function getInstructionsForAnon($project) {
79 $repo_list = array($project->getUnixName());
80 $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',
81 array ($project->getID(),
82 SCM_EXTRA_REPO_ACTION_UPDATE,
84 $rows = db_numrows ($result) ;
85 for ($i=0; $i<$rows; $i++) {
86 $repo_list[] = db_result($result,$i,'repo_name');
89 $b = '<h2>' . ngettext('Anonymous Access to the Git repository',
90 'Anonymous Access to the Git repositories',
91 count($repo_list)) . '</h2>';
94 $b .= ngettext('This project\'s Git repository can be checked out through anonymous access with the following command.',
95 'This project\'s Git repositories can be checked out through anonymous access with the following commands.',
100 foreach ($repo_list as $repo_name) {
102 $b .= '<tt>git clone '.util_make_url ('/anonscm/git/'.$project->getUnixName().'/'.$repo_name.'.git').'</tt><br />';
106 $result = db_query_params('SELECT u.user_id, u.user_name, u.realname FROM scm_personal_repos p, users u WHERE p.group_id=$1 AND u.user_id=p.user_id AND u.unix_status=$2 AND plugin_id=$3',
107 array ($project->getID(),
110 $rows = db_numrows($result);
114 $b .= ngettext('Developer\'s repository',
115 'Developer\'s repositories',
119 $b .= ngettext('One of this project\'s members also has a personal Git repository that can be checked out anonymously.',
120 'Some of this project\'s members also have personal Git repositories that can be checked out anonymously.',
124 for ($i=0; $i<$rows; $i++) {
125 $user_id = db_result($result,$i,'user_id');
126 $user_name = db_result($result,$i,'user_name');
127 $real_name = db_result($result,$i,'realname');
128 $b .= '<tt>git clone '.util_make_url('/anonscm/git/'.$project->getUnixName().'/users/'.$user_name.'.git').'</tt> ('.util_make_link_u ($user_name, $user_id, $real_name).') ['.util_make_link('/scm/browser.php?group_id='.$project->getID().'&user_id='.$user_id, _('Browse Git Repository')).']<br />';
136 function getInstructionsForRW($project) {
137 $repo_list = array($project->getUnixName());
139 $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',
140 array ($project->getID(),
141 SCM_EXTRA_REPO_ACTION_UPDATE,
143 $rows = db_numrows ($result) ;
144 for ($i=0; $i<$rows; $i++) {
145 $repo_list[] = db_result($result,$i,'repo_name');
148 if (session_loggedin()) {
149 $u = user_get_object(user_getid());
150 $d = $u->getUnixName();
153 if (forge_get_config('use_ssh', 'scmgit')) {
155 $b = '<h2>' . ngettext('Developer Access to the Git repository via SSH',
156 'Developer Access to the Git repositories via SSH',
157 count($repo_list)) . '</h2>';
160 $b .= ngettext('Only project developers can access the GIT repository via this method. SSH must be installed on your client machine. Enter your site password when prompted.',
161 'Only project developers can access the GIT repositories via this method. SSH must be installed on your client machine. Enter your site password when prompted.',
165 foreach ($repo_list as $repo_name) {
166 $b .= '<p><tt>git clone git+ssh://'.$d.'@' . $project->getSCMBox() . '/'. forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/'. $repo_name .'.git</tt></p>' ;
171 if (forge_get_config('use_dav', 'scmgit')) {
172 $protocol = forge_get_config('use_ssl', 'scmgit')? 'https' : 'http';
174 $b = '<h2>' . ngettext('Developer Access to the Git repository via HTTP',
175 'Developer Access to the Git repositories via HTTP',
176 count($repo_list)) . '</h2>';
180 $b .= ngettext('Only project developers can access the GIT repository via this method. Enter your site password when prompted.',
181 'Only project developers can access the GIT repositories via this method. Enter your site password when prompted.',
185 foreach ($repo_list as $repo_name) {
186 $b .= '<p><tt>git clone '.$protocol.'://'.$d.'@' . $project->getSCMBox() . '/'. forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/'. $repo_name .'.git</tt></p>' ;
192 if ($validSetup == 0) {
193 $b = '<p class="warning">'._('Missing configuration for access in scmgit.ini : use_ssh and use_dav disabled').'</p>';
196 if (forge_get_config('use_ssh', 'scmgit')) {
198 $b = '<h2>' . ngettext('Developer Access to the Git repository via SSH',
199 'Developer Access to the Git repositories via SSH',
200 count($repo_list)) . '</h2>';
204 $b .= ngettext('Only project developers can access the GIT repository via this method. SSH must be installed on your client machine. Substitute <i>developername</i> with the proper value. Enter your site password when prompted.',
205 'Only project developers can access the GIT repositories via this method. SSH must be installed on your client machine. Substitute <i>developername</i> with the proper value. Enter your site password when prompted.',
209 foreach ($repo_list as $repo_name) {
210 $b .= '<p><tt>git clone git+ssh://<i>'._('developername').'</i>@' . $project->getSCMBox() . '/'. forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/'. $repo_name .'.git</tt></p>' ;
214 if (forge_get_config('use_dav', 'scmgit')) {
215 $protocol = forge_get_config('use_ssl', 'scmgit')? 'https' : 'http';
217 $b = '<h2>' . ngettext('Developer Access to the Git repository via HTTP',
218 'Developer Access to the Git repositories via HTTP',
219 count($repo_list)) . '</h2>';
222 $b .= ngettext('Only project developers can access the GIT repository via this method. Enter your site password when prompted.',
223 'Only project developers can access the GIT repositories via this method. Enter your site password when prompted.',
227 foreach ($repo_list as $repo_name) {
228 $b .= '<p><tt>git clone '.$protocol.'://<i>'._('developername').'</i>@' . $project->getSCMBox() . '/'. forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/'. $repo_name .'.git</tt></p>' ;
234 if (session_loggedin()) {
235 $u =& user_get_object(user_getid()) ;
236 if ($u->getUnixStatus() == 'A') {
237 $result = db_query_params('SELECT * FROM scm_personal_repos p WHERE p.group_id=$1 AND p.user_id=$2 AND plugin_id=$3',
238 array ($project->getID(),
241 if ($result && db_numrows ($result) > 0) {
243 $b .= _('Access to your personal repository');
246 $b .= _('You have a personal repository for this project, accessible through SSH with the following method. Enter your site password when prompted.');
248 $b .= '<p><tt>git clone git+ssh://'.$u->getUnixName().'@' . $this->getBoxForProject($project) . '/'. forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/users/'. $u->getUnixName() .'.git</tt></p>' ;
250 $glist = $u->getGroups();
251 foreach ($glist as $g) {
252 if ($g->getID() == $project->getID()) {
254 $b .= _('Request a personal repository');
257 $b .= _('You can clone the project repository into a personal one into which you alone will be able to write. Other members of the project will only have read access. Access for non-members will follow the same rules as for the project\'s main repository. Note that the personal repository may take some time before it is created (less than an hour in most situations).');
260 $b .= sprintf (_('<a href="%s">Request a personal repository</a>.'),
261 util_make_url ('/plugins/scmgit/index.php?func=request-personal-repo&group_id='.$project->getID()));
271 function getSnapshotPara($project) {
274 $filename = $project->getUnixName().'-scm-latest.tar'.util_get_compressed_file_extension();
275 if (file_exists(forge_get_config('scm_snapshots_path').'/'.$filename)) {
277 $b .= util_make_link("/snapshots.php?group_id=".$project->getID(),
278 _('Download the nightly snapshot')
285 function printBrowserPage($params) {
286 $project = $this->checkParams($params);
291 if ($project->usesPlugin($this->name)) {
292 if ($params['user_id']) {
293 $user = user_get_object($params['user_id']);
294 echo $project->getUnixName().'/users/'.$user->getUnixName();
295 print '<iframe src="'.util_make_url("/plugins/scmgit/cgi-bin/gitweb.cgi?p=".$project->getUnixName().'/users/'.$user->getUnixName().'.git').'" frameborder="0" width=100% height=700></iframe>' ;
296 } elseif ($this->browserDisplayable($project)) {
297 print '<iframe src="'.util_make_url("/plugins/scmgit/cgi-bin/gitweb.cgi?p=".$project->getUnixName().'/'.$project->getUnixName().'.git').'" frameborder="0" width=100% height=700></iframe>' ;
302 function getBrowserLinkBlock($project) {
304 $b = $HTML->boxMiddle(_('Git Repository Browser'));
306 $b .= _('Browsing the Git 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.');
309 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID(),
310 _('Browse Git Repository')
316 function getStatsBlock ($project) {
320 $result = db_query_params('SELECT u.realname, u.user_name, u.user_id, sum(commits) as commits, sum(adds) as adds, sum(adds+commits) as combined 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 ORDER BY combined DESC, realname',
321 array ($project->getID()));
323 if (db_numrows($result) > 0) {
324 // $b .= $HTML->boxMiddle(_('Repository Statistics'));
326 $tableHeaders = array(
331 $b .= $HTML->listTableTop($tableHeaders, false, '', 'repo-history');
334 $total = array('adds' => 0, 'commits' => 0);
336 while($data = db_fetch_array($result)) {
337 $b .= '<tr '. $HTML->boxGetAltRowStyle($i) .'>';
338 $b .= '<td class="halfwidth">' ;
339 $b .= util_make_link_u ($data['user_name'], $data['user_id'], $data['realname']) ;
340 $b .= '</td><td class="onequarterwidth align-right">'.$data['adds']. '</td>'.
341 '<td class="onequarterwidth align-right">'.$data['commits'].'</td></tr>';
342 $total['adds'] += $data['adds'];
343 $total['commits'] += $data['commits'];
346 $b .= '<tr '. $HTML->boxGetAltRowStyle($i) .'>';
347 $b .= '<td class="halfwidth"><strong>'._('Total').':</strong></td>'.
348 '<td class="onequarterwidth align-right"><strong>'.$total['adds']. '</strong></td>'.
349 '<td class="onequarterwidth align-right"><strong>'.$total['commits'].'</strong></td>';
351 $b .= $HTML->listTableBottom();
357 function createOrUpdateRepo($params) {
358 $project = $this->checkParams($params);
363 if (! $project->usesPlugin($this->name)) {
367 $project_name = $project->getUnixName();
368 $root = forge_get_config('repos_path', 'scmgit') . '/' . $project_name;
369 if (!is_dir($root)) {
370 system("mkdir -p $root");
374 // Create main repository
375 $main_repo = $root . '/' . $project_name . '.git' ;
376 if (!is_file("$main_repo/HEAD") && !is_dir("$main_repo/objects") && !is_dir("$main_repo/refs")) {
377 exec("GIT_DIR=\"$main_repo\" git init --bare --shared=group", $result) ;
378 $output .= join("<br />", $result);
380 exec("GIT_DIR=\"$main_repo\" git update-server-info", $result) ;
381 $output .= join("<br />", $result);
382 if (is_file("$main_repo/hooks/post-update.sample")) {
383 rename("$main_repo/hooks/post-update.sample",
384 "$main_repo/hooks/post-update");
386 if (!is_file("$main_repo/hooks/post-update")) {
387 $f = fopen("$main_repo/hooks/post-update", 'w');
388 fwrite($f, "exec git-update-server-info\n");
391 if (is_file ("$main_repo/hooks/post-update")) {
392 system ("chmod +x $main_repo/hooks/post-update") ;
394 system ("echo \"Git repository for $project_name\" > $main_repo/description") ;
395 system ("find $main_repo -type d | xargs chmod g+s") ;
397 if (forge_get_config('use_ssh','scmgit')) {
398 $unix_group = 'scm_' . $project_name ;
399 system ("chgrp -R $unix_group $root") ;
400 system ("chmod g+s $root") ;
401 if ($project->enableAnonSCM()) {
402 system ("chmod g+wX,o+rX-w $root") ;
403 system ("chmod -R g+rwX,o+rX-w $main_repo") ;
405 system ("chmod g+wX,o-rwx $root") ;
406 system ("chmod -R g+rwX,o-rwx $main_repo") ;
409 $unix_user = forge_get_config('apache_user');
410 $unix_group = forge_get_config('apache_group');
411 system ("chown -R $unix_user:$unix_group $main_repo") ;
412 system ("chmod -R g-rwx,o-rwx $main_repo") ;
415 // Create project-wide secondary repositories
416 $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',
417 array ($project->getID(),
418 SCM_EXTRA_REPO_ACTION_UPDATE,
420 $rows = db_numrows ($result) ;
421 for ($i=0; $i<$rows; $i++) {
422 $repo_name = db_result($result,$i,'repo_name');
423 $description = db_result($result,$i,'description');
424 $clone_url = db_result($result,$i,'clone_url');
425 $repodir = $root . '/' . $repo_name . '.git' ;
426 if (!is_file ("$repodir/HEAD") && !is_dir("$repodir/objects") && !is_dir("$repodir/refs")) {
427 if ($clone_url != '') {
428 system ("cd $root;git clone --bare $clone_url $repodir") ;
430 system ("GIT_DIR=\"$repodir\" git init --bare --shared=group") ;
432 system ("GIT_DIR=\"$repodir\" git update-server-info") ;
433 if (is_file ("$repodir/hooks/post-update.sample")) {
434 rename ("$repodir/hooks/post-update.sample",
435 "$repodir/hooks/post-update") ;
437 if (!is_file ("$repodir/hooks/post-update")) {
438 $f = fopen ("$repodir/hooks/post-update") ;
439 fwrite ($f, "exec git-update-server-info\n") ;
442 if (is_file ("$repodir/hooks/post-update")) {
443 system ("chmod +x $repodir/hooks/post-update") ;
445 $f = fopen("$repodir/description", "w");
446 fwrite($f, $description."\n");
448 system ("chgrp -R $unix_group $repodir") ;
449 system ("chmod g+s $root") ;
450 if ($project->enableAnonSCM()) {
451 system ("chmod -R g+wX,o+rX-w $main_repo") ;
453 system ("chmod -R g+wX,o-rwx $main_repo") ;
458 // Delete project-wide secondary repositories
459 $result = db_query_params ('SELECT repo_name FROM scm_secondary_repos WHERE group_id=$1 AND next_action = $2 AND plugin_id=$3',
460 array ($project->getID(),
461 SCM_EXTRA_REPO_ACTION_DELETE,
463 $rows = db_numrows ($result) ;
464 for ($i=0; $i<$rows; $i++) {
465 $repo_name = db_result($result,$i,'repo_name');
466 $repodir = $root . '/' . $repo_name . '.git' ;
467 if (util_is_valid_repository_name($repo_name)) {
468 system ("rm -rf $repodir");
470 db_query_params ('DELETE FROM scm_secondary_repos WHERE group_id=$1 AND repo_name=$2 AND next_action = $3 AND plugin_id=$4',
471 array ($project->getID(),
473 SCM_EXTRA_REPO_ACTION_DELETE,
477 // Create users' personal repositories
478 $result = db_query_params ('SELECT u.user_name FROM scm_personal_repos p, users u WHERE p.group_id=$1 AND u.user_id=p.user_id AND u.unix_status=$2 AND plugin_id=$3',
479 array ($project->getID(),
482 $rows = db_numrows ($result) ;
483 for ($i=0; $i<$rows; $i++) {
484 system ("mkdir -p $root/users") ;
485 $user_name = db_result($result,$i,'user_name');
486 $repodir = $root . '/users/' . $user_name . '.git' ;
488 if (!is_file ("$repodir/HEAD") && !is_dir("$repodir/objects") && !is_dir("$repodir/refs")) {
489 system ("git clone --bare $main_repo $repodir") ;
490 system ("GIT_DIR=\"$repodir\" git update-server-info") ;
491 if (is_file ("$repodir/hooks/post-update.sample")) {
492 rename ("$repodir/hooks/post-update.sample",
493 "$repodir/hooks/post-update") ;
495 if (!is_file ("$repodir/hooks/post-update")) {
496 $f = fopen ("$repodir/hooks/post-update", 'w') ;
497 fwrite ($f, "exec git-update-server-info\n") ;
500 if (is_file ("$repodir/hooks/post-update")) {
501 system ("chmod +x $repodir/hooks/post-update") ;
503 system("echo \"Git repository for user $user_name in project $project_name\" > $repodir/description");
504 system ("chown -R $user_name:$unix_group $repodir") ;
507 if (is_dir ("$root/users")) {
508 if ($project->enableAnonSCM()) {
509 system ("chmod -R g+rX-w,o+rX-w $root/users") ;
511 system ("chmod -R g+rX-w,o-rwx $root/users") ;
514 $params['output'] = $output;
517 function updateRepositoryList($params) {
518 $groups = $this->getGroups();
520 foreach ($groups as $project) {
521 if ($this->browserDisplayable($project)) {
526 $config_dir = forge_get_config('config_path').'/plugins/scmgit';
527 if (!is_dir($config_dir)) {
528 mkdir($config_dir, 0755, true);
530 $fname = $config_dir . '/gitweb.conf' ;
531 $config_f = fopen($fname.'.new', 'w') ;
532 $rootdir = forge_get_config('repos_path', 'scmgit');
533 fwrite($config_f, "\$projectroot = '$rootdir';\n");
534 fwrite($config_f, "\$projects_list = '$config_dir/gitweb.list';\n");
535 fwrite($config_f, "@git_base_url_list = ('". util_make_url('/anonscm/git') . "');\n");
536 fwrite($config_f, "\$logo = '". util_make_url('/plugins/scmgit/git-logo.png') . "';\n");
537 fwrite($config_f, "\$favicon = '". util_make_url('/plugins/scmgit/git-favicon.png')."';\n");
538 fwrite($config_f, "\$stylesheet = '". util_make_url('/plugins/scmgit/gitweb.css')."';\n");
539 fwrite($config_f, "\$javascript = '". util_make_url('/plugins/scmgit/gitweb.js')."';\n");
540 fwrite($config_f, "\$prevent_xss = 'true';\n");
542 chmod ($fname.'.new', 0644) ;
543 rename ($fname.'.new', $fname) ;
545 $fname = $config_dir . '/gitweb.list' ;
547 $f = fopen ($fname.'.new', 'w');
548 foreach ($list as $project) {
549 $repos = $this->getRepositories($rootdir . "/" . $project->getUnixName());
550 foreach ($repos as $repo) {
551 $reldir = substr($repo, strlen($rootdir) + 1);
552 fwrite($f, $reldir . "\n");
556 chmod($fname.'.new', 0644);
557 rename($fname.'.new', $fname);
560 function getRepositories($path) {
561 if (! is_dir($path)) {
565 $entries = scandir($path);
566 foreach ($entries as $entry) {
567 $fullname = $path . "/" . $entry;
568 if (($entry == ".") or ($entry == ".."))
570 if (is_dir($fullname)) {
571 if (is_link($fullname))
573 $result = $this->getRepositories($fullname);
574 $list = array_merge($list, $result);
575 } elseif ($entry == "HEAD") {
582 function gatherStats ($params) {
583 $project = $this->checkParams ($params) ;
588 if (! $project->usesPlugin ($this->name)) {
592 if ($params['mode'] == 'day') {
593 $year = $params ['year'] ;
594 $month = $params ['month'] ;
595 $day = $params ['day'] ;
596 $month_string = sprintf( "%04d%02d", $year, $month );
597 $start_time = gmmktime( 0, 0, 0, $month, $day, $year);
598 $end_time = $start_time + 86400;
600 $usr_adds = array () ;
601 $usr_updates = array () ;
602 $usr_deletes = array () ;
607 $repo = forge_get_config('repos_path', 'scmgit') . '/' . $project->getUnixName() . '/' . $project->getUnixName() . '.git';
608 if (!is_dir ($repo) || !is_dir ("$repo/refs")) {
609 // echo "No repository\n" ;
613 $pipe = popen ("GIT_DIR=\"$repo\" git log --since=@$start_time --until=@$end_time --all --pretty='format:%n%an <%ae>' --name-status 2>/dev/null", 'r' ) ;
617 // cleaning stats_cvs_* table for the current day
618 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
619 array ($month_string,
621 $project->getID())) ;
623 echo "Error while cleaning stats_cvs_group\n" ;
629 while (!feof($pipe) && $data = fgets ($pipe)) {
631 if (strlen($line) > 0) {
632 $result = preg_match("/^(?P<name>.+) <(?P<mail>.+)>/", $line, $matches);
635 $last_user = $matches['name'];
636 $user2email[$last_user] = strtolower($matches['mail']);
637 if (!isset($usr_adds[$last_user])) {
638 $usr_adds[$last_user] = 0;
639 $usr_updates[$last_user] = 0;
640 $usr_deletes[$last_user] = 0;
643 // Short-commit stats line
644 preg_match("/^(?P<mode>[AM])\s+(?P<file>.+)$/", $line, $matches);
645 if ($last_user == "") continue;
646 if ($matches['mode'] == 'A') {
647 $usr_adds[$last_user]++;
649 } elseif ($matches['mode'] == 'M') {
650 $usr_updates[$last_user]++;
652 } elseif ($matches['mode'] == 'D') {
653 $usr_deletes[$last_user]++;
659 // inserting group results in stats_cvs_groups
660 if ($updates > 0 || $adds > 0) {
661 if (!db_query_params ('INSERT INTO stats_cvs_group (month,day,group_id,checkouts,commits,adds) VALUES ($1,$2,$3,$4,$5,$6)',
662 array ($month_string,
668 echo "Error while inserting into stats_cvs_group\n" ;
674 // building the user list
675 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ) ) );
677 foreach ( $user_list as $user ) {
678 // Trying to get user id from user name or email
679 $u = &user_get_object_by_name ($user) ;
681 $user_id = $u->getID();
683 $res=db_query_params('SELECT user_id FROM users WHERE lower(realname)=$1 OR email=$2',
684 array (strtolower($user), $user2email[$user]));
685 if ($res && db_numrows($res) > 0) {
686 $user_id = db_result($res,0,'user_id');
692 $uu = $usr_updates[$user] ? $usr_updates[$user] : 0 ;
693 $ua = $usr_adds[$user] ? $usr_adds[$user] : 0 ;
694 if ($uu > 0 || $ua > 0) {
695 if (!db_query_params ('INSERT INTO stats_cvs_user (month,day,group_id,user_id,commits,adds) VALUES ($1,$2,$3,$4,$5,$6)',
696 array ($month_string,
702 echo "Error while inserting into stats_cvs_user\n" ;
712 function generateSnapshots ($params) {
714 $project = $this->checkParams ($params) ;
719 $group_name = $project->getUnixName() ;
721 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
722 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
724 if (! $project->usesPlugin ($this->name)) {
728 if (! $project->enableAnonSCM()) {
729 if (is_file($snapshot)) {
732 if (is_file($tarball)) {
738 // TODO: ideally we generate one snapshot per git repository
739 $toprepo = forge_get_config('repos_path', 'scmgit') ;
740 $repo = $toprepo . '/' . $project->getUnixName() . '/' . $project->getUnixName() . '.git' ;
742 if (!is_dir ($repo)) {
743 if (is_file($snapshot)) {
746 if (is_file($tarball)) {
752 // Skip empty repo (no HEAD present in repository)
753 $ref = trim(`GIT_DIR=$repo git symbolic-ref HEAD`);
754 if (!file_exists($repo.'/'.$ref)) {
758 $tmp = trim (`mktemp -d`) ;
762 $today = date ('Y-m-d') ;
763 system ("GIT_DIR=\"$repo\" git archive --format=tar --prefix=$group_name-scm-$today/ HEAD |".forge_get_config('compression_method')." > $tmp/snapshot");
764 chmod ("$tmp/snapshot", 0644) ;
765 copy ("$tmp/snapshot", $snapshot) ;
766 unlink ("$tmp/snapshot") ;
768 system ("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
769 chmod ("$tmp/tarball", 0644) ;
770 copy ("$tmp/tarball", $tarball) ;
771 unlink ("$tmp/tarball") ;
772 system ("rm -rf $tmp") ;
776 * widgets - 'widgets' hook handler
777 * @param array $params
780 function widgets($params) {
781 require_once 'common/widget/WidgetLayoutManager.class.php';
782 if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_GROUP) {
783 $params['fusionforge_widgets'][] = 'plugin_scmgit_project_latestcommits';
785 if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_USER) {
786 $params['fusionforge_widgets'][] = 'plugin_scmgit_user_myrepositories';
792 * Process the 'widget_instance' hook to create instances of the widgets
793 * @param array $params
795 function myPageBox($params) {
797 $user = UserManager::instance()->getCurrentUser();
798 require_once 'common/widget/WidgetLayoutManager.class.php';
799 if ($params['widget'] == 'plugin_scmgit_user_myrepositories') {
800 require_once $gfplugins.$this->name.'/common/scmgit_Widget_MyRepositories.class.php';
801 $params['instance'] = new scmgit_Widget_MyRepositories(WidgetLayoutManager::OWNER_TYPE_USER, $user->getId());
805 function weekly(&$params) {
806 $res = db_query_params('SELECT group_id FROM groups WHERE status=$1 AND use_scm=1 ORDER BY group_id DESC',
809 $params['output'] .= 'ScmGit Plugin: Unable to get list of projects using SCM: '.db_error();
813 $params['output'] .= 'ScmGit Plugin: Running "git gc --quiet" on '.db_numrows($res).' repositories.'."\n";
814 while ($row = db_fetch_array($res)) {
815 $project = group_get_object($row['group_id']);
816 if (!$project || !is_object($project)) {
818 } elseif ($project->isError()) {
821 if (!$project->usesPlugin($this->name)) {
825 $project_name = $project->getUnixName();
826 $repo = forge_get_config('repos_path', 'scmgit') . '/' . $project_name . '/' . $project_name .'.git';
829 $params['output'] .= $project_name.': '.`git gc --quiet 2>&1`;
834 function activity($params) {
835 $group_id = $params['group'];
836 $project = group_get_object($group_id);
837 if (! $project->usesPlugin($this->name)) {
840 if (in_array('scmgit', $params['show'])) {
841 $start_time = $params['begin'];
842 $end_time = $params['end'];
843 $repo = forge_get_config('repos_path', 'scmgit') . '/' . $project->getUnixName() . '/' . $project->getUnixName() . '.git';
844 $pipe = popen("GIT_DIR=\"$repo\" git log --date=raw --since=@$start_time --until=@$end_time --all --pretty='format:%ad||%an||%s||%h' --name-status", 'r' );
845 while (!feof($pipe) && $data = fgets($pipe)) {
847 $splitedLine = explode('||', $line);
848 if (sizeof($splitedLine) == 4) {
850 $result['section'] = 'scm';
851 $result['group_id'] = $group_id;
852 $result['ref_id'] = 'browser.php?group_id='.$group_id;
853 $result['description'] = $splitedLine[2].' (commit '.$splitedLine[3].')';
854 $result['realname'] = '';
855 $splitedDate = explode(' ', $splitedLine[0]);
856 $result['activity_date'] = $splitedDate[0];
857 $result['subref_id'] = '';
858 $params['results'][] = $result;
862 $params['ids'][] = $this->name;
863 $params['texts'][] = _('Git Commits');
867 function scm_add_repo(&$params) {
868 $project = $this->checkParams($params);
872 if (! $project->usesPlugin ($this->name)) {
876 if (!isset($params['repo_name'])) {
880 if ($params['repo_name'] == $project->getUnixName()) {
881 $params['error_msg'] = _('Cannot create a secondary repository with the same name as the primary');
885 if (! util_is_valid_repository_name($params['repo_name'])) {
886 $params['error_msg'] = _('This repository name is not valid');
890 $result = db_query_params('SELECT count(*) AS count FROM scm_secondary_repos WHERE group_id=$1 AND repo_name = $2 AND plugin_id=$3',
891 array ($params['group_id'],
892 $params['repo_name'],
895 $params['error_msg'] = db_error();
898 if (db_result($result, 0, 'count')) {
899 $params['error_msg'] = sprintf(_('A repository %s already exists'), $params['repo_name']);
905 if (isset($params['clone'])) {
906 $url = $params['clone'];
910 } elseif (preg_match('|^git://|', $url) || preg_match('|^https?://|', $url)) {
913 } elseif ($url == $project->getUnixName()) {
915 } elseif (($result = db_query_params('SELECT count(*) AS count FROM scm_secondary_repos WHERE group_id=$1 AND repo_name = $2 AND plugin_id=$3',
916 array ($project->getID(),
919 && db_result($result, 0, 'count')) {
920 // Local repo: try to clone from an existing repo in same project
924 $params['error_msg'] = _('Invalid URL from which to clone');
929 if (isset($params['description'])) {
930 $description = $params['description'];
932 if ($clone && !$description) {
933 $description = sprintf(_('Clone of %s'), $params['clone']);
936 $description = "Git repository $params[repo_name] for project ".$project->getUnixName();
939 $result = db_query_params ('INSERT INTO scm_secondary_repos (group_id, repo_name, description, clone_url, plugin_id) VALUES ($1, $2, $3, $4, $5)',
940 array ($params['group_id'],
941 $params['repo_name'],
946 $params['error_msg'] = db_error();
950 plugin_hook ("scm_admin_update", $params);
954 function scm_admin_form(&$params) {
955 $project = $this->checkParams($params);
959 if (! $project->usesPlugin ($this->name)) {
963 session_require_perm('project_admin', $params['group_id']);
965 $project_name = $project->getUnixName();
967 $select_repo = '<select name="frontpage">' . "\n";
968 $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 ORDER BY repo_name',
969 array ($params['group_id'],
970 SCM_EXTRA_REPO_ACTION_UPDATE,
973 $params['error_msg'] = db_error();
976 $existing_repos = array();
977 while($data = db_fetch_array($result)) {
978 $existing_repos[] = array('repo_name' => $data['repo_name'],
979 'description' => $data['description'],
980 'clone_url' => $data['clone_url']);
982 if (count($existing_repos) == 0) {
983 printf('<h2>'._('No extra Git repository for project %1$s').'</h2>', $project_name);
985 $t = sprintf(ngettext('Extra Git repository for project %1$s',
986 'Extra Git repositories for project %1$s',
987 count($existing_repos)), $project_name);
988 print '<h2>'.$t.'</h2>';
989 print '<table><thead><tr><th>'._('Repository name').'</th><th>'._('Initial repository description').'</th><th>'._('Initial clone URL (if any)').'</th><th>'._('Delete').'</th></tr></thead><tbody>';
990 foreach ($existing_repos as $repo) {
991 print "<tr><td><tt>$repo[repo_name]</tt></td><td>$repo[description]</td><td>$repo[clone_url]</td><td>";
993 <form name="form_delete_repo_<?php echo $repo['repo_name']?>"
994 action="<?php echo getStringFromServer('PHP_SELF'); ?>" method="post">
995 <input type="hidden" name="group_id" value="<?php echo $params['group_id'] ?>" />
996 <input type="hidden" name="delete_repository" value="1" />
997 <input type="hidden" name="repo_name" value="<?php echo $repo['repo_name']?>" />
998 <input type="submit" name="submit" value="<?php echo _('Delete') ?>" />
1001 print "</td></tr>\n";
1003 print '</tbody></table>';
1006 printf('<h2>'._('Create new Git repository for project %1$s').'</h2>', $project_name);
1009 <form name="form_create_repo"
1010 action="<?php echo getStringFromServer('PHP_SELF'); ?>" method="post">
1011 <input type="hidden" name="group_id" value="<?php echo $params['group_id'] ?>" />
1012 <input type="hidden" name="create_repository" value="1" />
1013 <p><strong><?php echo _('Repository name:') ?></strong><?php echo utils_requiredField(); ?><br />
1014 <input type="text" required="required" size="20" name="repo_name" value="" /></p>
1015 <p><strong><?php echo _('Description:'); ?></strong><br />
1016 <input type="text" size="60" name="description" value="" /></p>
1017 <p><strong><?php echo _('Initial clone URL (or name of an existing repository in this project; leave empty to start with an empty repository):') ?></strong><br />
1018 <input type="text" size="60" name="clone" value="<?php echo $project_name; ?>" /></p>
1019 <input type="submit" name="cancel" value="<?php echo _('Cancel') ?>" />
1020 <input type="submit" name="submit" value="<?php echo _('Submit') ?>" />
1030 // c-file-style: "bsd"