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 // Create users' personal repositories
451 $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',
452 array ($project->getID(),
454 $rows = db_numrows ($result) ;
455 for ($i=0; $i<$rows; $i++) {
456 system ("mkdir -p $root/users") ;
457 $user_name = db_result($result,$i,'user_name');
458 $repodir = $root . '/users/' . $user_name . '.git' ;
460 if (!is_file ("$repodir/HEAD") && !is_dir("$repodir/objects") && !is_dir("$repodir/refs")) {
461 system ("git clone --bare $main_repo $repodir") ;
462 system ("GIT_DIR=\"$repodir\" git update-server-info") ;
463 if (is_file ("$repodir/hooks/post-update.sample")) {
464 rename ("$repodir/hooks/post-update.sample",
465 "$repodir/hooks/post-update") ;
467 if (!is_file ("$repodir/hooks/post-update")) {
468 $f = fopen ("$repodir/hooks/post-update", 'w') ;
469 fwrite ($f, "exec git-update-server-info\n") ;
472 if (is_file ("$repodir/hooks/post-update")) {
473 system ("chmod +x $repodir/hooks/post-update") ;
475 system("echo \"Git repository for user $user_name in project $project_name\" > $repodir/description");
476 system ("chown -R $user_name:$unix_group $repodir") ;
479 if (is_dir ("$root/users")) {
480 if ($project->enableAnonSCM()) {
481 system ("chmod -R g+rX-w,o+rX-w $root/users") ;
483 system ("chmod -R g+rX-w,o-rwx $root/users") ;
486 $params['output'] = $output;
489 function updateRepositoryList($params) {
490 $groups = $this->getGroups();
492 foreach ($groups as $project) {
493 if ($this->browserDisplayable($project)) {
498 $config_dir = forge_get_config('config_path').'/plugins/scmgit';
499 if (!is_dir($config_dir)) {
500 mkdir($config_dir, 0755, true);
502 $fname = $config_dir . '/gitweb.conf' ;
503 $config_f = fopen($fname.'.new', 'w') ;
504 $rootdir = forge_get_config('repos_path', 'scmgit');
505 fwrite($config_f, "\$projectroot = '$rootdir';\n");
506 fwrite($config_f, "\$projects_list = '$config_dir/gitweb.list';\n");
507 fwrite($config_f, "@git_base_url_list = ('". util_make_url('/anonscm/git') . "');\n");
508 fwrite($config_f, "\$logo = '". util_make_url('/plugins/scmgit/git-logo.png') . "';\n");
509 fwrite($config_f, "\$favicon = '". util_make_url('/plugins/scmgit/git-favicon.png')."';\n");
510 fwrite($config_f, "\$stylesheet = '". util_make_url('/plugins/scmgit/gitweb.css')."';\n");
511 fwrite($config_f, "\$javascript = '". util_make_url('/plugins/scmgit/gitweb.js')."';\n");
512 fwrite($config_f, "\$prevent_xss = 'true';\n");
514 chmod ($fname.'.new', 0644) ;
515 rename ($fname.'.new', $fname) ;
517 $fname = $config_dir . '/gitweb.list' ;
519 $f = fopen ($fname.'.new', 'w');
520 foreach ($list as $project) {
521 $repos = $this->getRepositories($rootdir . "/" . $project->getUnixName());
522 foreach ($repos as $repo) {
523 $reldir = substr($repo, strlen($rootdir) + 1);
524 fwrite($f, $reldir . "\n");
528 chmod($fname.'.new', 0644);
529 rename($fname.'.new', $fname);
532 function getRepositories($path) {
533 if (! is_dir($path)) {
537 $entries = scandir($path);
538 foreach ($entries as $entry) {
539 $fullname = $path . "/" . $entry;
540 if (($entry == ".") or ($entry == ".."))
542 if (is_dir($fullname)) {
543 if (is_link($fullname))
545 $result = $this->getRepositories($fullname);
546 $list = array_merge($list, $result);
547 } elseif ($entry == "HEAD") {
554 function gatherStats ($params) {
555 $project = $this->checkParams ($params) ;
560 if (! $project->usesPlugin ($this->name)) {
564 if ($params['mode'] == 'day') {
565 $year = $params ['year'] ;
566 $month = $params ['month'] ;
567 $day = $params ['day'] ;
568 $month_string = sprintf( "%04d%02d", $year, $month );
569 $start_time = gmmktime( 0, 0, 0, $month, $day, $year);
570 $end_time = $start_time + 86400;
572 $usr_adds = array () ;
573 $usr_updates = array () ;
574 $usr_deletes = array () ;
579 $repo = forge_get_config('repos_path', 'scmgit') . '/' . $project->getUnixName() . '/' . $project->getUnixName() . '.git';
580 if (!is_dir ($repo) || !is_dir ("$repo/refs")) {
581 // echo "No repository\n" ;
585 $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' ) ;
589 // cleaning stats_cvs_* table for the current day
590 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
591 array ($month_string,
593 $project->getID())) ;
595 echo "Error while cleaning stats_cvs_group\n" ;
601 while (!feof($pipe) && $data = fgets ($pipe)) {
603 if (strlen($line) > 0) {
604 $result = preg_match("/^(?P<name>.+) <(?P<mail>.+)>/", $line, $matches);
607 $last_user = $matches['name'];
608 $user2email[$last_user] = strtolower($matches['mail']);
609 if (!isset($usr_adds[$last_user])) {
610 $usr_adds[$last_user] = 0;
611 $usr_updates[$last_user] = 0;
612 $usr_deletes[$last_user] = 0;
615 // Short-commit stats line
616 preg_match("/^(?P<mode>[AM])\s+(?P<file>.+)$/", $line, $matches);
617 if ($last_user == "") continue;
618 if ($matches['mode'] == 'A') {
619 $usr_adds[$last_user]++;
621 } elseif ($matches['mode'] == 'M') {
622 $usr_updates[$last_user]++;
624 } elseif ($matches['mode'] == 'D') {
625 $usr_deletes[$last_user]++;
631 // inserting group results in stats_cvs_groups
632 if ($updates > 0 || $adds > 0) {
633 if (!db_query_params ('INSERT INTO stats_cvs_group (month,day,group_id,checkouts,commits,adds) VALUES ($1,$2,$3,$4,$5,$6)',
634 array ($month_string,
640 echo "Error while inserting into stats_cvs_group\n" ;
646 // building the user list
647 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ) ) );
649 foreach ( $user_list as $user ) {
650 // Trying to get user id from user name or email
651 $u = &user_get_object_by_name ($user) ;
653 $user_id = $u->getID();
655 $res=db_query_params('SELECT user_id FROM users WHERE lower(realname)=$1 OR email=$2',
656 array (strtolower($user), $user2email[$user]));
657 if ($res && db_numrows($res) > 0) {
658 $user_id = db_result($res,0,'user_id');
664 $uu = $usr_updates[$user] ? $usr_updates[$user] : 0 ;
665 $ua = $usr_adds[$user] ? $usr_adds[$user] : 0 ;
666 if ($uu > 0 || $ua > 0) {
667 if (!db_query_params ('INSERT INTO stats_cvs_user (month,day,group_id,user_id,commits,adds) VALUES ($1,$2,$3,$4,$5,$6)',
668 array ($month_string,
674 echo "Error while inserting into stats_cvs_user\n" ;
684 function generateSnapshots ($params) {
686 $project = $this->checkParams ($params) ;
691 $group_name = $project->getUnixName() ;
693 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
694 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
696 if (! $project->usesPlugin ($this->name)) {
700 if (! $project->enableAnonSCM()) {
701 if (is_file($snapshot)) {
704 if (is_file($tarball)) {
710 // TODO: ideally we generate one snapshot per git repository
711 $toprepo = forge_get_config('repos_path', 'scmgit') ;
712 $repo = $toprepo . '/' . $project->getUnixName() . '/' . $project->getUnixName() . '.git' ;
714 if (!is_dir ($repo)) {
715 if (is_file($snapshot)) {
718 if (is_file($tarball)) {
724 // Skip empty repo (no HEAD present in repository)
725 $ref = trim(`GIT_DIR=$repo git symbolic-ref HEAD`);
726 if (!file_exists($repo.'/'.$ref)) {
730 $tmp = trim (`mktemp -d`) ;
734 $today = date ('Y-m-d') ;
735 system ("GIT_DIR=\"$repo\" git archive --format=tar --prefix=$group_name-scm-$today/ HEAD |".forge_get_config('compression_method')." > $tmp/snapshot");
736 chmod ("$tmp/snapshot", 0644) ;
737 copy ("$tmp/snapshot", $snapshot) ;
738 unlink ("$tmp/snapshot") ;
740 system ("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
741 chmod ("$tmp/tarball", 0644) ;
742 copy ("$tmp/tarball", $tarball) ;
743 unlink ("$tmp/tarball") ;
744 system ("rm -rf $tmp") ;
748 * widgets - 'widgets' hook handler
749 * @param array $params
752 function widgets($params) {
753 require_once 'common/widget/WidgetLayoutManager.class.php';
754 if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_GROUP) {
755 $params['fusionforge_widgets'][] = 'plugin_scmgit_project_latestcommits';
757 if ($params['owner_type'] == WidgetLayoutManager::OWNER_TYPE_USER) {
758 $params['fusionforge_widgets'][] = 'plugin_scmgit_user_myrepositories';
764 * Process the 'widget_instance' hook to create instances of the widgets
765 * @param array $params
767 function myPageBox($params) {
769 $user = UserManager::instance()->getCurrentUser();
770 require_once 'common/widget/WidgetLayoutManager.class.php';
771 if ($params['widget'] == 'plugin_scmgit_user_myrepositories') {
772 require_once $gfplugins.$this->name.'/common/scmgit_Widget_MyRepositories.class.php';
773 $params['instance'] = new scmgit_Widget_MyRepositories(WidgetLayoutManager::OWNER_TYPE_USER, $user->getId());
777 function weekly(&$params) {
778 $res = db_query_params('SELECT group_id FROM groups WHERE status=$1 AND use_scm=1 ORDER BY group_id DESC',
781 $params['output'] .= 'ScmGit Plugin: Unable to get list of projects using SCM: '.db_error();
785 $params['output'] .= 'ScmGit Plugin: Running "git gc --quiet" on '.db_numrows($res).' repositories.'."\n";
786 while ($row = db_fetch_array($res)) {
787 $project = group_get_object($row['group_id']);
788 if (!$project || !is_object($project)) {
790 } elseif ($project->isError()) {
793 if (!$project->usesPlugin($this->name)) {
797 $project_name = $project->getUnixName();
798 $repo = forge_get_config('repos_path', 'scmgit') . '/' . $project_name . '/' . $project_name .'.git';
801 $params['output'] .= $project_name.': '.`git gc --quiet 2>&1`;
805 // Delete project-wide secondary repositories
806 $result = db_query_params ('SELECT repo_name FROM plugin_scmgit_secondary_repos WHERE group_id=$1 AND next_action = 1',
807 array ($project->getID())) ;
808 $rows = db_numrows ($result) ;
809 for ($i=0; $i<$rows; $i++) {
810 $repo_name = db_result($result,$i,'repo_name');
811 $repodir = $root . '/' . $repo_name . '.git' ;
812 if (util_is_valid_repository_name($repo_name)) {
813 system ("rm -rf $repodir");
815 db_query_params ('DELETE FROM plugin_scmgit_secondary_repos WHERE group_id=$1 AND repo_name=$2 AND next_action = 1',
816 array ($project->getID(),
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"