3 * FusionForge Bazaar plugin
5 * Copyright 2009, Roland Mas
6 * Copyright 2013, Franck Villaume - TrivialDev
8 * This file is part of FusionForge.
10 * FusionForge is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published
12 * by the Free Software Foundation; either version 2 of the License,
13 * or (at your option) any later version.
15 * FusionForge is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License along
21 * with this program; if not, write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 forge_define_config_item ('default_server', 'scmbzr', forge_get_config ('web_host')) ;
26 forge_define_config_item ('repos_path', 'scmbzr', forge_get_config('chroot').'/scmrepos/bzr') ;
28 class BzrPlugin extends SCMPlugin {
29 function BzrPlugin () {
32 $this->name = 'scmbzr';
33 $this->text = 'Bazaar';
34 $this->hooks[] = 'scm_generate_snapshots' ;
35 $this->hooks[] = 'scm_browser_page';
36 $this->hooks[] = 'scm_update_repolist' ;
37 $this->hooks[] = 'scm_gather_stats' ;
39 $this->main_branch_names = array () ;
40 $this->main_branch_names[] = 'trunk' ;
41 $this->main_branch_names[] = 'master' ;
42 $this->main_branch_names[] = 'main' ;
43 $this->main_branch_names[] = 'head' ;
44 $this->main_branch_names[] = 'HEAD' ;
49 function getDefaultServer() {
50 return forge_get_config('default_server', 'scmbzr') ;
53 function printShortStats ($params) {
54 $project = $this->checkParams ($params) ;
59 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
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 ' (Bazaar: '.sprintf(_('<strong>%1$s</strong> commits, <strong>%2$s</strong> adds'), number_format($commit_num, 0), number_format($add_num, 0)).")";
74 function getBlurb () {
76 . sprintf(_('Documentation for %1$s is available at <a href="%2$s">%2$s</a>.'),
78 'http://bazaar-vcs.org/Documentation')
82 function getInstructionsForAnon ($project) {
84 $b .= _('Anonymous Bazaar Access');
87 $b .= _('This project\'s Bazaar repository can be checked out through anonymous access with the following command.');
90 $b .= '<tt>bzr checkout '.util_make_url ('/anonscm/bzr/'.$project->getUnixName().'/').'</tt><br />';
95 function getInstructionsForRW ($project) {
97 if (session_loggedin()) {
98 $u =& user_get_object(user_getid()) ;
99 $d = $u->getUnixName() ;
101 $b .= sprintf(_('Developer %s Access via SSH'), 'Bazaar');
104 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Bazaar');
106 $b .= _('SSH must be installed on your client machine.');
108 $b .= _('Enter your site password when prompted.');
110 $b .= '<p><tt>bzr checkout bzr+ssh://'.$d.'@' . $this->getBoxForProject($project) . forge_get_config('repos_path', 'scmbzr') .'/'. $project->getUnixName().'/'._('branchname').'</tt></p>' ;
113 $b .= sprintf(_('Developer %s Access via SSH'), 'Bazaar');
116 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Bazaar');
118 $b .= _('SSH must be installed on your client machine.');
120 $b .= _('Substitute <i>developername</i> with the proper values.');
122 $b .= _('Enter your site password when prompted.');
124 $b .= '<p><tt>bzr checkout bzr+ssh://<i>'._('developername').'</i>@' . $this->getBoxForProject($project) . forge_get_config('repos_path', 'scmbzr') .'/'. $project->getUnixName().'/'._('branchname').'</tt></p>' ;
129 function getSnapshotPara ($project) {
133 function getBrowserLinkBlock ($project) {
135 $b = $HTML->boxMiddle(sprintf(_('%s Repository Browser'), 'Bazaar'));
137 $b .= sprintf(_("Browsing the %s tree gives you a view into the current status of this project's code."), 'Bazaar');
139 $b .= _('You may also view the complete histories of any file in the repository.');
142 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID(),
143 sprintf(_('Browse %s Repository'), 'Bazaar')
149 function getStatsBlock ($project) {
153 function printBrowserPage ($params) {
154 $project = $this->checkParams ($params) ;
159 if ($project->usesPlugin ($this->name)) {
160 if ($this->browserDisplayable ($project)) {
161 print '<iframe src="'.util_make_url ("/scm/loggerhead/".$project->getUnixName()).'" frameborder="0" width=100% height=700></iframe>' ;
166 function createOrUpdateRepo ($params) {
167 $project = $this->checkParams ($params) ;
172 if (! $project->usesPlugin ($this->name)) {
176 $project_name = $project->getUnixName();
178 $repo = forge_get_config('repos_path', 'scmbzr') . '/' . $project->getUnixName() ;
179 $unix_group = 'scm_' . $project_name ;
181 $repo_exists = false ;
182 if (is_dir ($repo)) {
183 $pipe = popen ("bzr info $repo 2>/dev/null", "r") ;
184 $line = fgets ($pipe) ;
187 if (preg_match ("/^Shared repository/", $line) != 0
188 || preg_match ("/^Repository branch/", $line) != 0) {
189 $repo_exists = true ;
194 $tmp_repo = util_mkdtemp('.bzr', $project_name);
195 if ($tmp_repo == false) {
199 system ("bzr init-repo --no-trees $tmp_repo >/dev/null") ;
200 system ("find $tmp_repo/.bzr -type d | xargs chmod g+s") ;
201 system ("chmod -R g+wX,o+rX-w $tmp_repo/.bzr") ;
202 system ("chgrp $unix_group $tmp_repo/.bzr") ;
204 system ("mkdir -p $repo") ;
205 system ("chgrp $unix_group $repo") ;
206 system ("chmod g+s $repo") ;
207 system ("mv $tmp_repo/.bzr $repo/.bzr");
211 if ($project->enableAnonSCM()) {
212 system ("chmod o+rX-w $repo") ;
214 system ("chmod o-rwx $repo") ;
218 function updateRepositoryList ($params) {
219 $groups = $this->getGroups () ;
221 $dir = forge_get_config('data_path').'/plugins/scmbzr/public-repositories' ;
224 mkdir ($dir, 0644, true);
227 $oldlist = array () ;
228 $dh = opendir ($dir) ;
229 while (($file = readdir($dh)) !== false) {
230 if ($file != '.' && $file != '..') {
237 $newlist = array () ;
238 foreach ($groups as $project) {
239 if ($this->browserDisplayable ($project)) {
240 $newlist[] = $project->getUnixName() ;
245 $dellist = array () ;
246 $createlist = array () ;
248 while (count ($oldlist) > 0 && count ($newlist) > 0) {
252 $createlist[] = array_shift ($newlist) ;
254 $dellist[] = array_shift ($oldlist) ;
256 array_shift ($newlist) ;
257 array_shift ($oldlist) ;
260 $dellist = array_merge ($dellist, $oldlist) ;
261 $createlist = array_merge ($createlist, $newlist) ;
263 foreach ($dellist as $del) {
264 unlink ($dir . '/' . $del) ;
266 foreach ($createlist as $create) {
267 symlink (forge_get_config('repos_path', 'scmbzr') . '/' . $create, $dir . '/' . $create) ;
271 function gatherStats ($params) {
272 $project = $this->checkParams ($params) ;
277 if (! $project->usesPlugin ($this->name)) {
281 if ($params['mode'] == 'day') {
284 $year = $params ['year'] ;
285 $month = $params ['month'] ;
286 $day = $params ['day'] ;
287 $month_string = sprintf( "%04d%02d", $year, $month );
288 $start_time = gmmktime( 0, 0, 0, $month, $day, $year);
289 $end_time = $start_time + 86400;
291 $date = sprintf ("%04d-%02d-%02d", $year, $month, $day);
295 $usr_updates = array () ;
296 $usr_adds = array () ;
298 $toprepo = forge_get_config('repos_path', 'scmbzr') ;
299 $repo = $toprepo . '/' . $project->getUnixName() ;
301 $branch = $this->findMainBranch ($project) ;
308 $pipe = popen ("bzr log file://$repo/$branch --long --verbose 2> /dev/null", 'r' ) ;
310 // cleaning stats_cvs_* table for the current day
311 $res = db_query_params ('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
312 array ($month_string,
314 $project->getID())) ;
316 echo "Error while cleaning stats_cvs_group\n" ;
321 $res = db_query_params ('DELETE FROM stats_cvs_user WHERE month=$1 AND day=$2 AND group_id=$3',
322 array ($month_string,
324 $project->getID())) ;
326 echo "Error while cleaning stats_cvs_user\n" ;
331 // Analyzing history stream
332 $sep = '------------------------------------------------------------' ;
339 while (! feof ($pipe) &&
340 $line = rtrim (fgets ($pipe))) {
342 if ($curdate == $date) {
343 $adds = $adds + $curadds ;
344 $updates = $updates + $curupdates ;
346 if ($curdate != '' && $curdate < $date) {
356 } elseif (preg_match( '/^revno: ([0-9]+)$/', $line, $matches)) {
357 $currev = $matches[1] ;
358 } elseif (preg_match( '/^committer: (.*)$/', $line, $matches)) {
359 $curuser = $matches[1] ;
360 } elseif (preg_match( '/^timestamp: ... (\d\d\d\d-\d\d-\d\d)/', $line, $matches)) {
361 $curdate = $matches[1] ;
362 } elseif (preg_match( '/^modified:/', $line, $matches)) {
363 $state = 'modified' ;
364 } elseif (preg_match( '/^renamed:/', $line, $matches)) {
366 } elseif (preg_match( '/^removed:/', $line, $matches)) {
368 } elseif (preg_match( '/^added/', $line, $matches)) {
381 if ($curdate == $date) {
382 $adds = $adds + $curadds ;
383 $updates = $updates + $curupdates ;
386 // inserting group results in stats_cvs_groups
387 if ($updates > 0 || $adds > 0) {
388 if (!db_query_params ('INSERT INTO stats_cvs_group (month,day,group_id,checkouts,commits,adds) VALUES ($1,$2,$3,$4,$5,$6)',
389 array ($month_string,
395 echo "Error while inserting into stats_cvs_group\n" ;
401 // building the user list
402 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ) ) );
404 foreach ( $user_list as $user ) {
405 // trying to get user id from user name
406 $u = &user_get_object_by_name ($user) ;
408 $user_id = $u->getID();
413 $uu = $usr_updates[$user] ? $usr_updates[$user] : 0 ;
414 $ua = $usr_adds[$user] ? $usr_adds[$user] : 0 ;
415 if ($uu > 0 || $ua > 0) {
416 if (!db_query_params ('INSERT INTO stats_cvs_user (month,day,group_id,user_id,commits,adds) VALUES ($1,$2,$3,$4,$5,$6)',
417 array ($month_string,
423 echo "Error while inserting into stats_cvs_user\n" ;
433 function findMainBranch ($project) {
434 $toprepo = forge_get_config('repos_path', 'scmbzr') ;
435 $repo = $toprepo . '/' . $project->getUnixName() ;
439 foreach ($this->main_branch_names as $bname) {
440 system ("bzr ls file://$repo/$bname > /dev/null 2>&1", $code) ;
449 function generateSnapshots ($params) {
453 $project = $this->checkParams ($params) ;
458 $group_name = $project->getUnixName() ;
460 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
461 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
463 if (! $project->usesPlugin ($this->name)) {
467 if (! $project->enableAnonSCM()) {
468 if (file_exists ($snapshot)) unlink ($snapshot) ;
469 if (file_exists ($tarball)) unlink ($tarball) ;
473 $toprepo = forge_get_config('repos_path', 'scmbzr') ;
474 $repo = $toprepo . '/' . $project->getUnixName() ;
476 if (!is_dir ($repo) || !is_file ("$repo/format")) {
477 if (file_exists ($snapshot)) unlink ($snapshot) ;
478 if (file_exists ($tarball)) unlink ($tarball) ;
482 $tmp = trim (`mktemp -d`) ;
486 $today = date ('Y-m-d') ;
487 $branch = $this->findMainBranch ($project) ;
489 system ("bzr export --root=$group_name-scm-$today --format=tar - $repo/$bname |".forge_get_config('compression_method')."> $tmp/snapshot") ;
490 chmod ("$tmp/snapshot", 0644) ;
491 copy ("$tmp/snapshot", $snapshot) ;
492 unlink ("$tmp/snapshot") ;
493 system ("rm -rf $tmp/$dir") ;
498 system ("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
499 chmod ("$tmp/tarball", 0644) ;
500 copy ("$tmp/tarball", $tarball) ;
501 unlink ("$tmp/tarball") ;
502 system ("rm -rf $tmp") ;
508 // c-file-style: "bsd"