2 /** FusionForge Darcs plugin
4 * Copyright 2009, Roland Mas
6 * This file is part of FusionForge.
8 * FusionForge is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published
10 * by the Free Software Foundation; either version 2 of the License,
11 * or (at your option) any later version.
13 * FusionForge is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with FusionForge; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
24 class DarcsPlugin extends SCMPlugin {
25 function DarcsPlugin () {
28 $this->name = 'scmdarcs';
29 $this->text = 'Darcs';
30 $this->hooks[] = 'scm_generate_snapshots' ;
31 $this->hooks[] = 'scm_update_repolist' ;
32 $this->hooks[] = 'scm_browser_page' ;
33 $this->hooks[] = 'scm_gather_stats' ;
35 require_once $gfconfig.'plugins/scmdarcs/config.php' ;
37 $this->default_darcs_server = $default_darcs_server ;
38 if (isset ($darcs_root)) {
39 $this->darcs_root = $darcs_root;
41 $this->darcs_root = $GLOBALS['sys_chroot'].'/scmrepos/darcs' ;
47 function getDefaultServer() {
48 return $this->default_darcs_server ;
51 function printShortStats ($params) {
52 $project = $this->checkParams ($params) ;
57 if ($project->usesPlugin($this->name)) {
58 $result = db_query_params('SELECT sum(commits) AS commits, sum(adds) AS adds FROM stats_cvs_group WHERE group_id=$1',
59 array ($project->getID())) ;
60 $commit_num = db_result($result,0,'commits');
61 $add_num = db_result($result,0,'adds');
68 echo ' (Darcs: '.sprintf(_('<strong>%1$s</strong> commits, <strong>%2$s</strong> adds'), number_format($commit_num, 0), number_format($add_num, 0)).")";
72 function getBlurb () {
73 return _('<p>Documentation for Darcs is available <a href="http://darcs.net/">here</a>.</p>') ;
76 function getInstructionsForAnon ($project) {
77 $b = _('<p><b>Anonymous Darcs Access</b></p><p>This project\'s Darcs repository can be checked out through anonymous access with the following command.</p>');
79 $b .= '<tt>darcs get '.util_make_url ('/anonscm/darcs/'.$project->getUnixName().'/').'</tt><br />';
84 function getInstructionsForRW ($project) {
85 $b = _('<p><b>Developer Darcs Access via SSH</b></p><p>Only project developers can access the Darcs tree via this method. SSH must be installed on your client machine. Substitute <i>developername</i> with the proper values. Enter your site password when prompted.</p>');
86 $b .= '<p><tt>darcs get '.$project->getSCMBox() . ':'. $this->darcs_root .'/'. $project->getUnixName().'/ .</tt></p>' ;
90 function getSnapshotPara ($project) {
91 global $sys_scm_snapshots_path ;
93 $filename = $project->getUnixName().'-scm-latest.tar.gz';
94 if (file_exists($sys_scm_snapshots_path.'/'.$filename)) {
96 $b .= util_make_link ("/snapshots.php?group_id=".$project->getID(),
97 _('Download the nightly snapshot')
104 function getBrowserLinkBlock ($project) {
106 $b = $HTML->boxMiddle(_('Darcs Repository Browser'));
107 $b .= _('<p>Browsing the Darcs 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.</p>');
109 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID(),
110 _('Browse Darcs Repository')
116 function getStatsBlock ($project) {
120 $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',
121 array ($project->getID()));
123 if (db_numrows($result) > 0) {
124 $b .= $HTML->boxMiddle(_('Repository Statistics'));
126 $tableHeaders = array(
131 $b .= $HTML->listTableTop($tableHeaders);
134 $total = array('adds' => 0, 'commits' => 0);
136 while($data = db_fetch_array($result)) {
137 $b .= '<tr '. $HTML->boxGetAltRowStyle($i) .'>';
138 $b .= '<td width="50%">' ;
139 $b .= util_make_link_u ($data['user_name'], $data['user_id'], $data['realname']) ;
140 $b .= '</td><td width="25%" align="right">'.$data['adds']. '</td>'.
141 '<td width="25%" align="right">'.$data['commits'].'</td></tr>';
142 $total['adds'] += $data['adds'];
143 $total['commits'] += $data['commits'];
146 $b .= '<tr '. $HTML->boxGetAltRowStyle($i) .'>';
147 $b .= '<td width="50%"><strong>'._('Total').':</strong></td>'.
148 '<td width="25%" align="right"><strong>'.$total['adds']. '</strong></td>'.
149 '<td width="25%" align="right"><strong>'.$total['commits'].'</strong></td>';
151 $b .= $HTML->listTableBottom();
152 $b .= '<hr size="1" />';
158 function printBrowserPage ($params) {
161 $project = $this->checkParams ($params) ;
166 if ($project->usesPlugin ($this->name)) {
167 if ($this->browserDisplayable ($project)) {
168 print '<iframe src="'.util_make_url ("/plugins/scmdarcs/cgi-bin/darcsweb.cgi?r=".$project->getUnixName()).'" frameborder="no" width=100% height=700></iframe>' ;
173 function createOrUpdateRepo ($params) {
174 $project = $this->checkParams ($params) ;
179 if (! $project->usesPlugin ($this->name)) {
183 $repo = $this->darcs_root . '/' . $project->getUnixName() ;
184 $unix_group = 'scm_' . $project->getUnixName() ;
186 if (!is_dir ($repo."/_darcs")) {
187 system ("mkdir -p $repo") ;
188 system ("cd $repo ; darcs init >/dev/null") ;
189 system ("find $repo -type d | xargs chmod g+s") ;
192 system ("chgrp -R $unix_group $repo") ;
193 if ($project->enableAnonSCM()) {
194 system ("chmod -R g+wX,o+rX-w $repo") ;
196 system ("chmod -R g+wX,o-rwx $repo") ;
200 function updateRepositoryList ($params) {
201 $groups = $this->getGroups () ;
203 foreach ($groups as $project) {
204 if ($this->browserDisplayable ($project)) {
209 $fname = '/etc/gforge/plugins/scmdarcs/config.py' ;
211 $f = fopen ($fname.'.new', 'w') ;
213 fwrite ($f, "class base:\n"
214 ."\tdarcslogo = '".util_make_url ('/plugins/scmdarcs/darcsweb/darcs.png')."'\n"
215 ."\tdarcsfav = '".util_make_url ('/plugins/scmdarcs/darcsweb/minidarcs.png')."'\n"
216 ."\tcssfile = '".util_make_url ('/plugins/scmdarcs/darcsweb/style.css')."'\n"
219 foreach ($list as $project) {
220 $classname = str_replace ('-', '_',
221 'repo_' . $project->getUnixName()) ;
223 $repo = $this->darcs_root . '/' . $project->getUnixName() ;
224 fwrite ($f, "class $classname:\n"
225 ."\treponame = '".$project->getUnixName()."'\n"
226 ."\t".'repodesc = """'.$project->getPublicName().'"""'."\n"
227 ."\trepodir = '$repo'\n"
228 ."\trepourl = '" . util_make_url ('/anonscm/darcs/'.$project->getUnixName().'/') . "'\n"
229 ."\trepoprojurl = '" . util_make_url ('/projects/'.$project->getUnixName().'/') . "'\n"
230 ."\trepoencoding = 'utf8'\n"
234 chmod ($fname.'.new', 0644) ;
235 rename ($fname.'.new', $fname) ;
238 function generateSnapshots ($params) {
239 global $sys_scm_tarballs_path ;
241 $project = $this->checkParams ($params) ;
246 $group_name = $project->getUnixName() ;
248 $tarball = $sys_scm_tarballs_path.'/'.$group_name.'-scmroot.tar.gz';
250 if (! $project->usesPlugin ($this->name)) {
254 if (! $project->enableAnonSCM()) {
259 $toprepo = $this->darcs_root ;
260 $repo = $toprepo . '/' . $project->getUnixName() ;
262 if (!is_dir ($repo)) {
267 $tmp = trim (`mktemp -d`) ;
271 $today = date ('Y-m-d') ;
272 $dir = $project->getUnixName ()."-$today" ;
273 system ("mkdir -p $tmp/$dir") ;
274 system ("cd $tmp ; darcs $repo $dir > /dev/null 2>&1") ;
275 system ("tar czCf $tmp $tmp/snapshot.tar.gz $dir") ;
276 chmod ("$tmp/snapshot.tar.gz", 0644) ;
277 copy ("$tmp/snapshot.tar.gz", $snapshot) ;
278 unlink ("$tmp/snapshot.tar.gz") ;
279 system ("rm -rf $tmp/$dir") ;
281 system ("tar czCf $toprepo $tmp/tarball.tar.gz " . $project->getUnixName()) ;
282 chmod ("$tmp/tarball.tar.gz", 0644) ;
283 copy ("$tmp/tarball.tar.gz", $tarball) ;
284 unlink ("$tmp/tarball.tar.gz") ;
285 system ("rm -rf $tmp") ;
288 function gatherStats ($params) {
289 global $adds, $deletes, $updates, $commits,
290 $usr_adds, $usr_deletes, $usr_updates;
292 $project = $this->checkParams ($params) ;
297 if (! $project->usesPlugin ($this->name)) {
301 if ($params['mode'] == 'day') {
304 $year = $params ['year'] ;
305 $month = $params ['month'] ;
306 $day = $params ['day'] ;
307 $month_string = sprintf( "%04d%02d", $year, $month );
308 $start_time = gmmktime( 0, 0, 0, $month, $day, $year);
309 $end_time = $start_time + 86400;
314 $usr_adds = array () ;
315 $usr_updates = array () ;
316 $usr_deletes = array ();
318 $repo = $this->darcs_root . '/' . $project->getUnixName() ;
319 if (!is_dir ($repo) || !is_dir ("$repo/_darcs")) {
320 echo "No repository\n" ;
325 $from_date = date("c", $start_time);
326 $to_date = date("c", $end_time);
327 $pipe = popen("darcs changes --repodir='$repo' "
328 ."--match 'date \"between $from_date and $to_date\"' "
331 // cleaning stats_cvs_* table for the current day
332 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
333 array ($month_string,
335 $project->getID())) ;
337 echo "Error while cleaning stats_cvs_group\n" ;
342 $res = db_query_params ('DELETE FROM stats_cvs_user WHERE month=$1 AND day=$2 AND group_id=$3',
343 array ($month_string,
345 $project->getID())) ;
347 echo "Error while cleaning stats_cvs_user\n" ;
352 $xml_parser = xml_parser_create();
353 xml_set_element_handler($xml_parser, "DarcsPluginStartElement", "DarcsPluginEndElement");
355 // Analyzing history stream
356 while (!feof($pipe) &&
357 $data = fgets ($pipe, 4096)) {
359 if (!xml_parse ($xml_parser, $data, feof ($pipe))) {
360 debug("Unable to parse XML with error " .
361 xml_error_string(xml_get_error_code($xml_parser)) .
363 xml_get_current_line_number($xml_parser));
370 xml_parser_free ($xml_parser);
372 // inserting group results in stats_cvs_groups
374 if (!db_query_params ('INSERT INTO stats_cvs_group (month,day,group_id,checkouts,commits,adds) VALUES ($1,$2,$3,$4,$5,$6)',
375 array ($month_string,
381 echo "Error while inserting into stats_cvs_group\n" ;
386 // build map for email -> login
388 $email_login = array();
389 $email_login_fn = $repo."/_darcs/email-login.txt";
390 if (!file_exists($email_login_fn))
392 $email_login_fn = $repo."/.email-login.txt";
394 if (!file_exists($email_login_fn))
396 unset($email_login_fn);
399 if (isset($email_login_fn))
401 $fh = fopen($email_login_fn, 'r');
404 $a = explode(" ", fgets($fh));
407 $email_login[$a[0]] = rtrim($a[1]);
413 // building the user list
414 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ) ) );
416 foreach ( $user_list as $user ) {
417 // trying to get user id from darcs user name
419 $tmp_email = explode("<", $id, 2);
420 if (isset($tmp_email[1]))
422 $tmp_email = explode(">", $tmp_email[1]);
425 if (isset($email_login[$id]))
427 $id = $email_login[$id];
430 $u = &user_get_object_by_name ($id) ;
432 $user_id = $u->getID();
437 if (!db_query_params ('INSERT INTO stats_cvs_user (month,day,group_id,user_id,commits,adds) VALUES ($1,$2,$3,$4,$5,$6)',
438 array ($month_string,
442 $usr_updates[$user] ? $usr_updates[$user] : 0,
443 $usr_adds[$user] ? $usr_adds[$user] : 0))) {
444 echo "Error while inserting into stats_cvs_user\n" ;
455 function DarcsPluginStartElement($parser, $name, $attrs) {
456 global $last_user, $commits,
457 $adds, $updates, $deletes,
458 $usr_adds, $usr_updates, $usr_deletes;
461 $last_user = $attrs['AUTHOR'];
465 case "REMOVE_DIRECTORY":
468 $usr_deletes[$last_user]++;
475 $usr_updates[$last_user]++;
479 case "ADD_DIRECTORY":
482 $usr_adds[$last_user]++;
488 function DarcsPluginEndElement ($parser, $name) {
492 // c-file-style: "bsd"