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 plugin_scmgit_secondary_repos WHERE group_id=$1 AND next_action = 0 ORDER BY repo_name',
81 array ($project->getID())) ;
82 $rows = db_numrows ($result) ;
83 for ($i=0; $i<$rows; $i++) {
84 $repo_list[] = db_result($result,$i,'repo_name');
87 $b = '<h2>' . ngettext('Anonymous Access to the Git repository',
88 'Anonymous Access to the Git repositories',
89 count($repo_list)) . '</h2>';
92 $b .= ngettext('This project\'s Git repository can be checked out through anonymous access with the following command.',
93 'This project\'s Git repositories can be checked out through anonymous access with the following commands.',
98 foreach ($repo_list as $repo_name) {
100 $b .= '<tt>git clone '.util_make_url ('/anonscm/git/'.$project->getUnixName().'/'.$repo_name.'.git').'</tt><br />';
104 $result = db_query_params('SELECT u.user_id, u.user_name, u.realname FROM plugin_scmgit_personal_repos p, users u WHERE p.group_id=$1 AND u.user_id=p.user_id AND u.unix_status=$2',
105 array ($project->getID(),
107 $rows = db_numrows($result);
111 $b .= ngettext('Developer\'s repository',
112 'Developer\'s repositories',
116 $b .= ngettext('One of this project\'s members also has a personal Git repository that can be checked out anonymously.',
117 'Some of this project\'s members also have personal Git repositories that can be checked out anonymously.',
121 for ($i=0; $i<$rows; $i++) {
122 $user_id = db_result($result,$i,'user_id');
123 $user_name = db_result($result,$i,'user_name');
124 $real_name = db_result($result,$i,'realname');
125 $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 />';
133 function getInstructionsForRW($project) {
134 $repo_list = array($project->getUnixName());
136 $result = db_query_params ('SELECT repo_name FROM plugin_scmgit_secondary_repos WHERE group_id=$1 AND next_action = 0 ORDER BY repo_name',
137 array ($project->getID())) ;
138 $rows = db_numrows ($result) ;
139 for ($i=0; $i<$rows; $i++) {
140 $repo_list[] = db_result($result,$i,'repo_name');
143 if (session_loggedin()) {
144 $u = user_get_object(user_getid());
145 $d = $u->getUnixName();
148 if (forge_get_config('use_ssh', 'scmgit')) {
150 $b = '<h2>' . ngettext('Developer Access to the Git repository via SSH',
151 'Developer Access to the Git repositories via SSH',
152 count($repo_list)) . '</h2>';
155 $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.',
156 '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.',
160 foreach ($repo_list as $repo_name) {
161 $b .= '<p><tt>git clone git+ssh://'.$d.'@' . $project->getSCMBox() . '/'. forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/'. $repo_name .'.git</tt></p>' ;
166 if (forge_get_config('use_dav', 'scmgit')) {
167 $protocol = forge_get_config('use_ssl', 'scmgit')? 'https' : 'http';
169 $b = '<h2>' . ngettext('Developer Access to the Git repository via HTTP',
170 'Developer Access to the Git repositories via HTTP',
171 count($repo_list)) . '</h2>';
175 $b .= ngettext('Only project developers can access the GIT repository via this method. Enter your site password when prompted.',
176 'Only project developers can access the GIT repositories via this method. Enter your site password when prompted.',
180 foreach ($repo_list as $repo_name) {
181 $b .= '<p><tt>git clone '.$protocol.'://'.$d.'@' . $project->getSCMBox() . '/'. forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/'. $repo_name .'.git</tt></p>' ;
187 if ($validSetup == 0) {
188 $b = '<p class="warning">'._('Missing configuration for access in scmgit.ini : use_ssh and use_dav disabled').'</p>';
191 if (forge_get_config('use_ssh', 'scmgit')) {
193 $b = '<h2>' . ngettext('Developer Access to the Git repository via SSH',
194 'Developer Access to the Git repositories via SSH',
195 count($repo_list)) . '</h2>';
199 $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.',
200 '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.',
204 foreach ($repo_list as $repo_name) {
205 $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>' ;
209 if (forge_get_config('use_dav', 'scmgit')) {
210 $protocol = forge_get_config('use_ssl', 'scmgit')? 'https' : 'http';
212 $b = '<h2>' . ngettext('Developer Access to the Git repository via HTTP',
213 'Developer Access to the Git repositories via HTTP',
214 count($repo_list)) . '</h2>';
217 $b .= ngettext('Only project developers can access the GIT repository via this method. Enter your site password when prompted.',
218 'Only project developers can access the GIT repositories via this method. Enter your site password when prompted.',
222 foreach ($repo_list as $repo_name) {
223 $b .= '<p><tt>git clone '.$protocol.'://<i>'._('developername').'</i>@' . $project->getSCMBox() . '/'. forge_get_config('repos_path', 'scmgit') .'/'. $project->getUnixName() .'/'. $repo_name .'.git</tt></p>' ;
229 if (session_loggedin()) {
230 $u =& user_get_object(user_getid()) ;
231 if ($u->getUnixStatus() == 'A') {
232 $result = db_query_params('SELECT * FROM plugin_scmgit_personal_repos p WHERE p.group_id=$1 AND p.user_id=$2',
233 array ($project->getID(),
235 if ($result && db_numrows ($result) > 0) {
237 $b .= _('Access to your personal repository');
240 $b .= _('You have a personal repository for this project, accessible through SSH with the following method. Enter your site password when prompted.');
242 $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>' ;
244 $glist = $u->getGroups();
245 foreach ($glist as $g) {
246 if ($g->getID() == $project->getID()) {
248 $b .= _('Request a personal repository');
251 $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).');
254 $b .= sprintf (_('<a href="%s">Request a personal repository</a>.'),
255 util_make_url ('/plugins/scmgit/index.php?func=request-personal-repo&group_id='.$project->getID()));
265 function getSnapshotPara($project) {
268 $filename = $project->getUnixName().'-scm-latest.tar'.util_get_compressed_file_extension();
269 if (file_exists(forge_get_config('scm_snapshots_path').'/'.$filename)) {
271 $b .= util_make_link("/snapshots.php?group_id=".$project->getID(),
272 _('Download the nightly snapshot')
279 function printBrowserPage($params) {
280 $project = $this->checkParams($params);
285 if ($project->usesPlugin($this->name)) {
286 if ($params['user_id']) {
287 $user = user_get_object($params['user_id']);
288 echo $project->getUnixName().'/users/'.$user->getUnixName();
289 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>' ;
290 } elseif ($this->browserDisplayable($project)) {
291 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>' ;
296 function getBrowserLinkBlock($project) {
298 $b = $HTML->boxMiddle(_('Git Repository Browser'));
300 $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.');
303 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID(),
304 _('Browse Git Repository')
310 function getStatsBlock ($project) {
314 $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',
315 array ($project->getID()));
317 if (db_numrows($result) > 0) {
318 // $b .= $HTML->boxMiddle(_('Repository Statistics'));
320 $tableHeaders = array(
325 $b .= $HTML->listTableTop($tableHeaders, false, '', 'repo-history');
328 $total = array('adds' => 0, 'commits' => 0);
330 while($data = db_fetch_array($result)) {
331 $b .= '<tr '. $HTML->boxGetAltRowStyle($i) .'>';
332 $b .= '<td class="halfwidth">' ;
333 $b .= util_make_link_u ($data['user_name'], $data['user_id'], $data['realname']) ;
334 $b .= '</td><td class="onequarterwidth align-right">'.$data['adds']. '</td>'.
335 '<td class="onequarterwidth align-right">'.$data['commits'].'</td></tr>';
336 $total['adds'] += $data['adds'];
337 $total['commits'] += $data['commits'];
340 $b .= '<tr '. $HTML->boxGetAltRowStyle($i) .'>';
341 $b .= '<td class="halfwidth"><strong>'._('Total').':</strong></td>'.
342 '<td class="onequarterwidth align-right"><strong>'.$total['adds']. '</strong></td>'.
343 '<td class="onequarterwidth align-right"><strong>'.$total['commits'].'</strong></td>';
345 $b .= $HTML->listTableBottom();
351 function createOrUpdateRepo($params) {
352 $project = $this->checkParams($params);
357 if (! $project->usesPlugin($this->name)) {
361 $project_name = $project->getUnixName();
362 $root = forge_get_config('repos_path', 'scmgit') . '/' . $project_name;
363 if (!is_dir($root)) {
364 system("mkdir -p $root");
368 // Create main repository
369 $main_repo = $root . '/' . $project_name . '.git' ;
370 if (!is_file("$main_repo/HEAD") && !is_dir("$main_repo/objects") && !is_dir("$main_repo/refs")) {
371 exec("GIT_DIR=\"$main_repo\" git init --bare --shared=group", $result) ;
372 $output .= join("<br />", $result);
374 exec("GIT_DIR=\"$main_repo\" git update-server-info", $result) ;
375 $output .= join("<br />", $result);
376 if (is_file("$main_repo/hooks/post-update.sample")) {
377 rename("$main_repo/hooks/post-update.sample",
378 "$main_repo/hooks/post-update");
380 if (!is_file("$main_repo/hooks/post-update")) {
381 $f = fopen("$main_repo/hooks/post-update", 'w');
382 fwrite($f, "exec git-update-server-info\n");
385 if (is_file ("$main_repo/hooks/post-update")) {
386 system ("chmod +x $main_repo/hooks/post-update") ;
388 system ("echo \"Git repository for $project_name\" > $main_repo/description") ;
389 system ("find $main_repo -type d | xargs chmod g+s") ;
391 if (forge_get_config('use_ssh','scmgit')) {
392 $unix_group = 'scm_' . $project_name ;
393 system ("chgrp -R $unix_group $root") ;
394 system ("chmod g+s $root") ;
395 if ($project->enableAnonSCM()) {
396 system ("chmod g+wX,o+rX-w $root") ;
397 system ("chmod -R g+rwX,o+rX-w $main_repo") ;
399 system ("chmod g+wX,o-rwx $root") ;
400 system ("chmod -R g+rwX,o-rwx $main_repo") ;
403 $unix_user = forge_get_config('apache_user');
404 $unix_group = forge_get_config('apache_group');
405 system ("chown -R $unix_user:$unix_group $main_repo") ;
406 system ("chmod -R g-rwx,o-rwx $main_repo") ;
409 // Create project-wide secondary repositories
410 $result = db_query_params ('SELECT repo_name, description, clone_url FROM plugin_scmgit_secondary_repos WHERE group_id=$1 AND next_action = 0',
411 array ($project->getID())) ;
412 $rows = db_numrows ($result) ;
413 for ($i=0; $i<$rows; $i++) {
414 $repo_name = db_result($result,$i,'repo_name');
415 $description = db_result($result,$i,'description');
416 $clone_url = db_result($result,$i,'clone_url');
417 $repodir = $root . '/' . $repo_name . '.git' ;
418 if (!is_file ("$repodir/HEAD") && !is_dir("$repodir/objects") && !is_dir("$repodir/refs")) {
419 if ($clone_url != '') {
420 system ("cd $root;git clone --bare $clone_url $repodir") ;
422 system ("GIT_DIR=\"$repodir\" git init --bare --shared=group") ;
424 system ("GIT_DIR=\"$repodir\" git update-server-info") ;
425 if (is_file ("$repodir/hooks/post-update.sample")) {
426 rename ("$repodir/hooks/post-update.sample",
427 "$repodir/hooks/post-update") ;
429 if (!is_file ("$repodir/hooks/post-update")) {
430 $f = fopen ("$repodir/hooks/post-update") ;
431 fwrite ($f, "exec git-update-server-info\n") ;
434 if (is_file ("$repodir/hooks/post-update")) {
435 system ("chmod +x $repodir/hooks/post-update") ;
437 $f = fopen("$repodir/description", "w");
438 fwrite($f, $description."\n");
440 system ("chgrp -R $unix_group $repodir") ;
441 system ("chmod g+s $root") ;
442 if ($project->enableAnonSCM()) {
443 system ("chmod -R g+wX,o+rX-w $main_repo") ;
445 system ("chmod -R g+wX,o-rwx $main_repo") ;
450 // Delete project-wide secondary repositories
451 $result = db_query_params ('SELECT repo_name FROM plugin_scmgit_secondary_repos WHERE group_id=$1 AND next_action = 1',
452 array ($project->getID())) ;
453 $rows = db_numrows ($result) ;
454 for ($i=0; $i<$rows; $i++) {
455 $repo_name = db_result($result,$i,'repo_name');
456 $repodir = $root . '/' . $repo_name . '.git' ;
457 if (util_is_valid_repository_name($repo_name)) {
458 system ("rm -rf $repodir");
460 db_query_params ('DELETE FROM plugin_scmgit_secondary_repos WHERE group_id=$1 AND repo_name=$2 AND next_action = 1',
461 array ($project->getID(),
465 // Create users' personal repositories
466 $result = db_query_params ('SELECT u.user_name FROM plugin_scmgit_personal_repos p, users u WHERE p.group_id=$1 AND u.user_id=p.user_id AND u.unix_status=$2',
467 array ($project->getID(),
469 $rows = db_numrows ($result) ;
470 for ($i=0; $i<$rows; $i++) {
471 system ("mkdir -p $root/users") ;
472 $user_name = db_result($result,$i,'user_name');
473 $repodir = $root . '/users/' . $user_name . '.git' ;
475 if (!is_file ("$repodir/HEAD") && !is_dir("$repodir/objects") && !is_dir("$repodir/refs")) {
476 system ("git clone --bare $main_repo $repodir") ;
477 system ("GIT_DIR=\"$repodir\" git update-server-info") ;
478 if (is_file ("$repodir/hooks/post-update.sample")) {
479 rename ("$repodir/hooks/post-update.sample",
480 "$repodir/hooks/post-update") ;
482 if (!is_file ("$repodir/hooks/post-update")) {
483 $f = fopen ("$repodir/hooks/post-update", 'w') ;
484 fwrite ($f, "exec git-update-server-info\n") ;
487 if (is_file ("$repodir/hooks/post-update")) {
488 system ("chmod +x $repodir/hooks/post-update") ;
490 system("echo \"Git repository for user $user_name in project $project_name\" > $repodir/description");
491 system ("chown -R $user_name:$unix_group $repodir") ;
494 if (is_dir ("$root/users")) {
495 if ($project->enableAnonSCM()) {
496 system ("chmod -R g+rX-w,o+rX-w $root/users") ;
498 system ("chmod -R g+rX-w,o-rwx $root/users") ;
501 $params['output'] = $output;
504 function updateRepositoryList($params) {
505 $groups = $this->getGroups();
507 foreach ($groups as $project) {
508 if ($this->browserDisplayable($project)) {
513 $config_dir = forge_get_config('config_path').'/plugins/scmgit';
514 if (!is_dir($config_dir)) {
515 mkdir($config_dir, 0755, true);
517 $fname = $config_dir . '/gitweb.conf' ;
518 $config_f = fopen($fname.'.new', 'w') ;
519 $rootdir = forge_get_config('repos_path', 'scmgit');
520 fwrite($config_f, "\$projectroot = '$rootdir';\n");
521 fwrite($config_f, "\$projects_list = '$config_dir/gitweb.list';\n");
522 fwrite($config_f, "@git_base_url_list = ('". util_make_url('/anonscm/git') . "');\n");
523 fwrite($config_f, "\$logo = '". util_make_url('/plugins/scmgit/git-logo.png') . "';\n");
524 fwrite($config_f, "\$favicon = '". util_make_url('/plugins/scmgit/git-favicon.png')."';\n");
525 fwrite($config_f, "\$stylesheet = '". util_make_url('/plugins/scmgit/gitweb.css')."';\n");
526 fwrite($config_f, "\$javascript = '". util_make_url('/plugins/scmgit/gitweb.js')."';\n");
527 fwrite($config_f, "\$prevent_xss = 'true';\n");
529 chmod ($fname.'.new', 0644) ;
530 rename ($fname.'.new', $fname) ;
532 $fname = $config_dir . '/gitweb.list' ;
534 $f = fopen ($fname.'.new', 'w');
535 foreach ($list as $project) {
536 $repos = $this->getRepositories($rootdir . "/" . $project->getUnixName());
537 foreach ($repos as $repo) {
538 $reldir = substr($repo, strlen($rootdir) + 1);
539 fwrite($f, $reldir . "\n");
543 chmod($fname.'.new', 0644);
544 rename($fname.'.new', $fname);
547 function getRepositories($path) {
548 if (! is_dir($path)) {
552 $entries = scandir($path);
553 foreach ($entries as $entry) {
554 $fullname = $path . "/" . $entry;
555 if (($entry == ".") or ($entry == ".."))
557 if (is_dir($fullname)) {
558 if (is_link($fullname))
560 $result = $this->getRepositories($fullname);
561 $list = array_merge($list, $result);
562 } elseif ($entry == "HEAD") {
569 function gatherStats ($params) {
570 $project = $this->checkParams ($params) ;
575 if (! $project->usesPlugin ($this->name)) {
579 if ($params['mode'] == 'day') {
580 $year = $params ['year'] ;
581 $month = $params ['month'] ;
582 $day = $params ['day'] ;
583 $month_string = sprintf( "%04d%02d", $year, $month );
584 $start_time = gmmktime( 0, 0, 0, $month, $day, $year);
585 $end_time = $start_time + 86400;
587 $usr_adds = array () ;
588 $usr_updates = array () ;
589 $usr_deletes = array () ;
594 $repo = forge_get_config('repos_path', 'scmgit') . '/' . $project->getUnixName() . '/' . $project->getUnixName() . '.git';
595 if (!is_dir ($repo) || !is_dir ("$repo/refs")) {
596 // echo "No repository\n" ;
600 $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' ) ;
604 // cleaning stats_cvs_* table for the current day
605 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
606 array ($month_string,
608 $project->getID())) ;
610 echo "Error while cleaning stats_cvs_group\n" ;
616 while (!feof($pipe) && $data = fgets ($pipe)) {
618 if (strlen($line) > 0) {
619 $result = preg_match("/^(?P<name>.+) <(?P<mail>.+)>/", $line, $matches);
622 $last_user = $matches['name'];
623 $user2email[$last_user] = strtolower($matches['mail']);
624 if (!isset($usr_adds[$last_user])) {
625 $usr_adds[$last_user] = 0;
626 $usr_updates[$last_user] = 0;
627 $usr_deletes[$last_user] = 0;
630 // Short-commit stats line
631 preg_match("/^(?P<mode>[AM])\s+(?P<file>.+)$/", $line, $matches);
632 if ($last_user == "") continue;
633 if ($matches['mode'] == 'A') {
634 $usr_adds[$last_user]++;
636 } elseif ($matches['mode'] == 'M') {
637 $usr_updates[$last_user]++;
639 } elseif ($matches['mode'] == 'D') {
640 $usr_deletes[$last_user]++;
646 // inserting group results in stats_cvs_groups
647 if ($updates > 0 || $adds > 0) {
648 if (!db_query_params ('INSERT INTO stats_cvs_group (month,day,group_id,checkouts,commits,adds) VALUES ($1,$2,$3,$4,$5,$6)',
649 array ($month_string,
655 echo "Error while inserting into stats_cvs_group\n" ;
661 // building the user list
662 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ) ) );
664 foreach ( $user_list as $user ) {
665 // Trying to get user id from user name or email
666 $u = &user_get_object_by_name ($user) ;
668 $user_id = $u->getID();
670 $res=db_query_params('SELECT user_id FROM users WHERE lower(realname)=$1 OR email=$2',
671 array (strtolower($user), $user2email[$user]));
672 if ($res && db_numrows($res) > 0) {
673 $user_id = db_result($res,0,'user_id');
679 $uu = $usr_updates[$user] ? $usr_updates[$user] : 0 ;
680 $ua = $usr_adds[$user] ? $usr_adds[$user] : 0 ;
681 if ($uu > 0 || $ua > 0) {
682 if (!db_query_params ('INSERT INTO stats_cvs_user (month,day,group_id,user_id,commits,adds) VALUES ($1,$2,$3,$4,$5,$6)',
683 array ($month_string,
689 echo "Error while inserting into stats_cvs_user\n" ;
699 function generateSnapshots ($params) {
701 $project = $this->checkParams ($params) ;
706 $group_name = $project->getUnixName() ;
708 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
709 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
711 if (! $project->usesPlugin ($this->name)) {
715 if (! $project->enableAnonSCM()) {
716 if (is_file($snapshot)) {
719 if (is_file($tarball)) {
725 // TODO: ideally we generate one snapshot per git repository
726 $toprepo = forge_get_config('repos_path', 'scmgit') ;
727 $repo = $toprepo . '/' . $project->getUnixName() . '/' . $project->getUnixName() . '.git' ;
729 if (!is_dir ($repo)) {
730 if (is_file($snapshot)) {
733 if (is_file($tarball)) {
739 // Skip empty repo (no HEAD present in repository)
740 $ref = trim(`GIT_DIR=$repo git symbolic-ref HEAD`);
741 if (!file_exists($repo.'/'.$ref)) {
745 $tmp = trim (`mktemp -d`) ;
749 $today = date ('Y-m-d') ;
750 system ("GIT_DIR=\"$repo\" git archive --format=tar --prefix=$group_name-scm-$today/ HEAD |".forge_get_config('compression_method')." > $tmp/snapshot");
751 chmod ("$tmp/snapshot", 0644) ;
752 copy ("$tmp/snapshot", $snapshot) ;
753 unlink ("$tmp/snapshot") ;
755 system ("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
756 chmod ("$tmp/tarball", 0644) ;
757 copy ("$tmp/tarball", $tarball) ;
758 unlink ("$tmp/tarball") ;
759 system ("rm -rf $tmp") ;
763 * widgets - 'widgets' hook handler
764 * @param array $params
767 function widgets($params) {
768 require_once 'common/widget/WidgetLayoutManager.class.php';
769 if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_GROUP) {
770 $params['fusionforge_widgets'][] = 'plugin_scmgit_project_latestcommits';
772 if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_USER) {
773 $params['fusionforge_widgets'][] = 'plugin_scmgit_user_myrepositories';
779 * Process the 'widget_instance' hook to create instances of the widgets
780 * @param array $params
782 function myPageBox($params) {
784 $user = UserManager::instance()->getCurrentUser();
785 require_once 'common/widget/WidgetLayoutManager.class.php';
786 if ($params['widget'] == 'plugin_scmgit_user_myrepositories') {
787 require_once $gfplugins.$this->name.'/common/scmgit_Widget_MyRepositories.class.php';
788 $params['instance'] = new scmgit_Widget_MyRepositories(WidgetLayoutManager::OWNER_TYPE_USER, $user->getId());
792 function weekly(&$params) {
793 $res = db_query_params('SELECT group_id FROM groups WHERE status=$1 AND use_scm=1 ORDER BY group_id DESC',
796 $params['output'] .= 'ScmGit Plugin: Unable to get list of projects using SCM: '.db_error();
800 $params['output'] .= 'ScmGit Plugin: Running "git gc --quiet" on '.db_numrows($res).' repositories.'."\n";
801 while ($row = db_fetch_array($res)) {
802 $project = group_get_object($row['group_id']);
803 if (!$project || !is_object($project)) {
805 } elseif ($project->isError()) {
808 if (!$project->usesPlugin($this->name)) {
812 $project_name = $project->getUnixName();
813 $repo = forge_get_config('repos_path', 'scmgit') . '/' . $project_name . '/' . $project_name .'.git';
816 $params['output'] .= $project_name.': '.`git gc --quiet 2>&1`;
821 function activity($params) {
822 $group_id = $params['group'];
823 $project = group_get_object($group_id);
824 if (! $project->usesPlugin($this->name)) {
827 if (in_array('scmgit', $params['show'])) {
828 $start_time = $params['begin'];
829 $end_time = $params['end'];
830 $repo = forge_get_config('repos_path', 'scmgit') . '/' . $project->getUnixName() . '/' . $project->getUnixName() . '.git';
831 $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' );
832 while (!feof($pipe) && $data = fgets($pipe)) {
834 $splitedLine = explode('||', $line);
835 if (sizeof($splitedLine) == 4) {
837 $result['section'] = 'scm';
838 $result['group_id'] = $group_id;
839 $result['ref_id'] = 'browser.php?group_id='.$group_id;
840 $result['description'] = $splitedLine[2].' (commit '.$splitedLine[3].')';
841 $result['realname'] = '';
842 $splitedDate = explode(' ', $splitedLine[0]);
843 $result['activity_date'] = $splitedDate[0];
844 $result['subref_id'] = '';
845 $params['results'][] = $result;
849 $params['ids'][] = $this->name;
850 $params['texts'][] = _('Git Commits');
854 function scm_add_repo(&$params) {
855 $project = $this->checkParams($params);
859 if (! $project->usesPlugin ($this->name)) {
863 if (!isset($params['repo_name'])) {
867 if ($params['repo_name'] == $project->getUnixName()) {
868 $params['error_msg'] = _('Cannot create a secondary repository with the same name as the primary');
872 if (! util_is_valid_repository_name($params['repo_name'])) {
873 $params['error_msg'] = _('This repository name is not valid');
877 $result = db_query_params('SELECT count(*) AS count FROM plugin_scmgit_secondary_repos WHERE group_id=$1 AND repo_name = $2',
878 array ($params['group_id'], $params['repo_name']));
880 $params['error_msg'] = db_error();
883 if (db_result($result, 0, 'count')) {
884 $params['error_msg'] = sprintf(_('A repository %s already exists'), $params['repo_name']);
890 if (isset($params['clone'])) {
891 $url = $params['clone'];
895 } elseif (preg_match('|^git://|', $url) || preg_match('|^https?://|', $url)) {
898 } elseif ($url == $project->getUnixName()) {
900 } elseif (($result = db_query_params('SELECT count(*) AS count FROM plugin_scmgit_secondary_repos WHERE group_id=$1 AND repo_name = $2', array ($project->getID(), $url)))
901 && db_result($result, 0, 'count')) {
902 // Local repo: try to clone from an existing repo in same project
906 $params['error_msg'] = _('Invalid URL from which to clone');
911 if (isset($params['description'])) {
912 $description = $params['description'];
914 if ($clone && !$description) {
915 $description = sprintf(_('Clone of %s'), $params['clone']);
918 $description = "Git repository $params[repo_name] for project ".$project->getUnixName();
921 $result = db_query_params ('INSERT INTO plugin_scmgit_secondary_repos (group_id, repo_name, description, clone_url) VALUES ($1, $2, $3, $4)',
922 array ($params['group_id'], $params['repo_name'], $description, $clone));
924 $params['error_msg'] = db_error();
928 plugin_hook ("scm_admin_update", $params);
932 function scm_delete_repo(&$params) {
933 $project = $this->checkParams($params);
937 if (! $project->usesPlugin ($this->name)) {
941 if (!isset($params['repo_name'])) {
945 $result = db_query_params('SELECT count(*) AS count FROM plugin_scmgit_secondary_repos WHERE group_id=$1 AND repo_name = $2',
946 array ($params['group_id'], $params['repo_name']));
948 $params['error_msg'] = db_error();
951 if (db_result($result, 0, 'count') == 0) {
952 $params['error_msg'] = sprintf(_('No repository %s exists'), $params['repo_name']);
956 $result = db_query_params ('UPDATE plugin_scmgit_secondary_repos SET next_action = 1 WHERE group_id=$1 AND repo_name=$2',
957 array ($params['group_id'], $params['repo_name']));
959 $params['error_msg'] = db_error();
963 plugin_hook ("scm_admin_update", $params);
967 function scm_admin_buttons(&$params) {
968 $project = $this->checkParams($params);
972 if (! $project->usesPlugin ($this->name)) {
979 '/scm/admin/?group_id='.$params['group_id'].'&form_create_repo=1',
981 array('icon' => html_image('ic/scm_repo_add.png'))
985 function scm_admin_form(&$params) {
986 $project = $this->checkParams($params);
990 if (! $project->usesPlugin ($this->name)) {
994 session_require_perm('project_admin', $params['group_id']);
996 $project_name = $project->getUnixName();
998 $select_repo = '<select name="frontpage">' . "\n";
999 $result = db_query_params('SELECT repo_name, description, clone_url FROM plugin_scmgit_secondary_repos WHERE group_id=$1 AND next_action = 0 ORDER BY repo_name',
1000 array ($params['group_id']));
1002 $params['error_msg'] = db_error();
1005 $existing_repos = array();
1006 while($data = db_fetch_array($result)) {
1007 $existing_repos[] = array('repo_name' => $data['repo_name'],
1008 'description' => $data['description'],
1009 'clone_url' => $data['clone_url']);
1011 if (count($existing_repos) == 0) {
1012 printf('<h2>'._('No extra Git repository for project %1$s').'</h2>', $project_name);
1014 $t = sprintf(ngettext('Extra Git repository for project %1$s',
1015 'Extra Git repositories for project %1$s',
1016 count($existing_repos)), $project_name);
1017 print '<h2>'.$t.'</h2>';
1018 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>';
1019 foreach ($existing_repos as $repo) {
1020 print "<tr><td><tt>$repo[repo_name]</tt></td><td>$repo[description]</td><td>$repo[clone_url]</td><td>";
1022 <form name="form_delete_repo_<?php echo $repo['repo_name']?>"
1023 action="<?php echo getStringFromServer('PHP_SELF'); ?>" method="post">
1024 <input type="hidden" name="group_id" value="<?php echo $params['group_id'] ?>" />
1025 <input type="hidden" name="delete_repository" value="1" />
1026 <input type="hidden" name="repo_name" value="<?php echo $repo['repo_name']?>" />
1027 <input type="submit" name="submit" value="<?php echo _('Delete') ?>" />
1030 print "</td></tr>\n";
1032 print '</tbody></table>';
1035 printf('<h2>'._('Create new Git repository for project %1$s').'</h2>', $project_name);
1038 <form name="form_create_repo"
1039 action="<?php echo getStringFromServer('PHP_SELF'); ?>" method="post">
1040 <input type="hidden" name="group_id" value="<?php echo $params['group_id'] ?>" />
1041 <input type="hidden" name="create_repository" value="1" />
1042 <p><strong><?php echo _('Repository name:') ?></strong><?php echo utils_requiredField(); ?><br />
1043 <input type="text" required="required" size="20" name="repo_name" value="" /></p>
1044 <p><strong><?php echo _('Description:'); ?></strong><br />
1045 <input type="text" size="60" name="description" value="" /></p>
1046 <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 />
1047 <input type="text" size="60" name="clone" value="<?php echo $project_name; ?>" /></p>
1048 <input type="submit" name="cancel" value="<?php echo _('Cancel') ?>" />
1049 <input type="submit" name="submit" value="<?php echo _('Submit') ?>" />
1059 // c-file-style: "bsd"