3 * FusionForge Bazaar plugin
5 * Copyright 2009, Roland Mas
6 * Copyright 2013-2014,2017, 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 require_once $gfcommon.'include/plugins_utils.php';
27 forge_define_config_item('default_server', 'scmbzr', forge_get_config('scm_host'));
28 forge_define_config_item('repos_path', 'scmbzr', forge_get_config('chroot').'/scmrepos/bzr');
30 class BzrPlugin extends SCMPlugin {
31 function __construct() {
33 parent::__construct();
34 $this->name = 'scmbzr';
35 $this->text = _('Bazaar');
37 _("This plugin contains the Bazaar subsystem of FusionForge. It allows each
38 FusionForge project to have its own Bazaar repository, and gives some control
39 over it to the project's administrator.");
40 $this->hooks[] = 'scm_generate_snapshots';
41 $this->hooks[] = 'scm_browser_page';
42 $this->hooks[] = 'scm_update_repolist';
43 $this->hooks[] = 'scm_gather_stats';
44 $this->hooks[] = 'scm_admin_form';
46 $this->main_branch_names = array();
47 $this->main_branch_names[] = 'trunk';
48 $this->main_branch_names[] = 'master';
49 $this->main_branch_names[] = 'main';
50 $this->main_branch_names[] = 'head';
51 $this->main_branch_names[] = 'HEAD';
56 function getDefaultServer() {
57 return forge_get_config('default_server', 'scmbzr');
60 function printShortStats($params) {
61 $project = $this->checkParams($params);
66 if (forge_check_perm('scm', $project->getID(), 'read')) {
67 $result = db_query_params('SELECT sum(updates) AS updates, sum(adds) AS adds FROM stats_cvs_group WHERE group_id=$1',
68 array ($project->getID())) ;
69 $commit_num = db_result($result,0,'updates');
70 $add_num = db_result($result,0,'adds');
77 $params['result'] .= ' (Bazaar: '.sprintf(_('<strong>%1$s</strong> updates, <strong>%2$s</strong> adds'), number_format($commit_num, 0), number_format($add_num, 0)).")";
83 . sprintf(_('Documentation for %1$s is available at <a href="%2$s">%2$s</a>.'),
85 'http://bazaar-vcs.org/Documentation')
89 function getInstructionsForAnon($project) {
91 $b .= _('Anonymous Bazaar Access');
94 $b .= _('This project\'s Bazaar repository can be checked out through anonymous access with the following command.');
97 $b .= html_e('kbd', array(), 'bzr checkout '.util_make_url('/anonscm/bzr/'.$project->getUnixName().'/')).'<br />';
102 function getInstructionsForRW($project) {
104 if (session_loggedin()) {
105 $u = user_get_object(user_getid()) ;
106 $d = $u->getUnixName() ;
108 $b .= sprintf(_('Developer %s Access via SSH'), 'Bazaar');
111 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Bazaar');
113 $b .= _('SSH must be installed on your client machine.');
115 $b .= _('Enter your site password when prompted.');
117 $b .= '<p>'.html_e('kbd', array(), 'bzr checkout bzr+ssh://'.$d.'@' . $this->getBoxForProject($project) . forge_get_config('repos_path', 'scmbzr') .'/'. $project->getUnixName().'/'._('branchname')).'</p>' ;
120 $b .= sprintf(_('Developer %s Access via SSH'), 'Bazaar');
123 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Bazaar');
125 $b .= _('SSH must be installed on your client machine.');
127 $b .= _('Substitute <em>developername</em> with the proper value.');
129 $b .= _('Enter your site password when prompted.');
131 $b .= '<p><kbd>bzr checkout bzr+ssh://<i>'._('developername').'</i>@' . $this->getBoxForProject($project) . forge_get_config('repos_path', 'scmbzr') .'/'. $project->getUnixName().'/'._('branchname').'</kbd></p>' ;
136 function getSnapshotPara ($project) {
140 function getBrowserLinkBlock ($project) {
142 $b = html_e('h2', array(), _('Bazaar Repository Browser'));
143 if ( $project->enableAnonSCM()) {
145 $b .= sprintf(_("Browsing the %s tree gives you a view into the current status of this project's code."), 'Bazaar');
147 $b .= _('You may also view the complete histories of any file in the repository.');
150 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID().'&scm_plugin='.$this->name,
151 sprintf(_('Browse %s Repository'), 'Bazaar')
155 $b .= $HTML->information(_('Only available if anonymous access is allowed'));
160 function getStatsBlock($project) {
164 function printBrowserPage($params) {
165 if ($params['scm_plugin'] != $this->name) {
169 $project = $this->checkParams($params);
174 if ($this->browserDisplayable($project) && $project->enableAnonSCM()) {
175 htmlIframe('/scm/loggerhead/'.$project->getUnixName(),array('id'=>'scmbzr_iframe'));
179 function createOrUpdateRepo ($params) {
180 $project = $this->checkParams ($params) ;
185 $project_name = $project->getUnixName();
187 $repo = forge_get_config('repos_path', 'scmbzr') . '/' . $project->getUnixName() ;
188 $unix_group = $project_name.'_scmrw' ;
190 $repo_exists = false ;
191 if (is_dir ($repo)) {
192 $pipe = popen ("bzr info $repo 2>/dev/null", "r") ;
193 $line = fgets ($pipe) ;
196 if (preg_match ("/^Shared repository/", $line) != 0
197 || preg_match ("/^Repository branch/", $line) != 0) {
198 $repo_exists = true ;
203 $tmp_repo = util_mkdtemp('.bzr', $project_name);
204 if ($tmp_repo == false) {
208 system("bzr init-repo --no-trees $tmp_repo >/dev/null") ;
209 system("find $tmp_repo/.bzr -type d | xargs chmod g+s") ;
210 system("chmod -R g+rwX,o+rX-w $tmp_repo/.bzr") ;
211 system("chgrp -R $unix_group $tmp_repo/.bzr") ;
213 system("mkdir -p $repo") ;
214 system("chgrp $unix_group $repo") ;
215 system("chmod g+ws $repo") ;
216 system("mv $tmp_repo/.bzr $repo/.bzr");
220 if ($project->enableAnonSCM()) {
221 system("chmod o+rX-w $repo") ;
223 system("chmod o-rwx $repo") ;
227 function updateRepositoryList($params) {
228 $groups = $this->getGroups();
230 $dir = forge_get_config('data_path').'/plugins/scmbzr/public-repositories' ;
233 mkdir ($dir, 0644, true);
236 $oldlist = array () ;
237 $dh = opendir ($dir) ;
238 while (($file = readdir($dh)) !== false) {
239 if ($file != '.' && $file != '..') {
246 $newlist = array () ;
247 foreach ($groups as $project) {
248 if ($this->browserDisplayable ($project)) {
249 $newlist[] = $project->getUnixName() ;
254 $dellist = array () ;
255 $createlist = array () ;
257 while (!empty($oldlist) && !empty($newlist)) {
261 $createlist[] = array_shift ($newlist) ;
263 $dellist[] = array_shift ($oldlist) ;
265 array_shift ($newlist) ;
266 array_shift ($oldlist) ;
269 $dellist = array_merge ($dellist, $oldlist) ;
270 $createlist = array_merge ($createlist, $newlist) ;
272 foreach ($dellist as $del) {
273 unlink ($dir . '/' . $del) ;
275 foreach ($createlist as $create) {
276 symlink (forge_get_config('repos_path', 'scmbzr') . '/' . $create, $dir . '/' . $create) ;
280 function gatherStats($params) {
281 $project = $this->checkParams($params);
286 if ($params['mode'] == 'day') {
289 $year = $params ['year'] ;
290 $month = $params ['month'] ;
291 $day = $params ['day'] ;
295 $usr_adds = array () ;
296 $usr_updates = array () ;
297 $usr_deletes = array () ;
298 $usr_commits = array () ;
300 $toprepo = forge_get_config('repos_path', 'scmbzr') ;
301 $repo = $toprepo . '/' . $project->getUnixName() ;
303 $branch = $this->findMainBranch ($project) ;
310 $month_string = sprintf( "%04d%02d", $year, $month );
311 $pipe = popen("bzr log file://$repo/$branch --long --verbose 2> /dev/null", 'r');
313 // cleaning stats_cvs_* table for the current day
314 $res = db_query_params('DELETE FROM stats_cvs_group WHERE month = $1 AND day = $2 AND group_id = $3 AND reponame = $4',
318 $project->getUnixName()));
320 echo "Error while cleaning stats_cvs_group\n";
325 $res = db_query_params('DELETE FROM stats_cvs_user WHERE month = $1 AND day = $2 AND group_id = $3 AND reponame = $4',
326 array ($month_string,
329 $project->getUnixName()));
331 echo "Error while cleaning stats_cvs_user\n" ;
336 // Analyzing history stream
337 $sep = '------------------------------------------------------------' ;
344 $date = sprintf ("%04d-%02d-%02d", $year, $month, $day);
345 while (! feof ($pipe) &&
346 $line = rtrim (fgets ($pipe))) {
348 if ($curdate == $date) {
349 $adds = $adds + $curadds ;
350 $updates = $updates + $curupdates ;
352 if ($curdate != '' && $curdate < $date) {
362 } elseif (preg_match( '/^revno: ([0-9]+)$/', $line, $matches)) {
363 $currev = $matches[1] ;
364 } elseif (preg_match( '/^committer: (.*)$/', $line, $matches)) {
365 $curuser = $matches[1] ;
366 } elseif (preg_match( '/^timestamp: ... (\d\d\d\d-\d\d-\d\d)/', $line, $matches)) {
367 $curdate = $matches[1] ;
368 } elseif (preg_match( '/^modified:/', $line, $matches)) {
369 $state = 'modified' ;
370 } elseif (preg_match( '/^renamed:/', $line, $matches)) {
372 } elseif (preg_match( '/^removed:/', $line, $matches)) {
374 } elseif (preg_match( '/^added/', $line, $matches)) {
391 if ($curdate == $date) {
392 $adds = $adds + $curadds ;
393 $updates = $updates + $curupdates ;
394 $deletes = $deletes + $curdeletes ;
395 $commits = $commits + $curcommits ;
398 // inserting group results in stats_cvs_groups
399 if ($updates > 0 || $adds > 0 || $deletes > 0 || $commits > 0) {
400 if (!db_query_params('INSERT INTO stats_cvs_group (month, day, group_id, checkouts, commits, adds, updates, deletes, reponame)
401 VALUES ($1, $2, $3, $4, $5, $6, $7)',
410 $project->getUnixName()))) {
411 echo "Error while inserting into stats_cvs_group\n" ;
417 // building the user list
418 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ), array_keys( $usr_commits ) ) );
420 foreach ( $user_list as $user ) {
421 // trying to get user id from user name
422 $u = user_get_object_by_name ($user) ;
424 $user_id = $u->getID();
429 $uc = $usr_commits[$user] ? $usr_commits[$user] : 0 ;
430 $uu = $usr_updates[$user] ? $usr_updates[$user] : 0 ;
431 $ua = $usr_adds[$user] ? $usr_adds[$user] : 0 ;
432 $ud = $usr_deletes[$user] ? $usr_deletes[$user] : 0 ;
433 if ($uu > 0 || $ua > 0 || $uc > 0 || $ud > 0) {
434 if (!db_query_params('INSERT INTO stats_cvs_user (month, day, group_id, user_id, commits, adds, updates, deletes, reponame)
435 VALUES ($1,$2,$3,$4,$5,$6)',
444 $project->getUnixName()))) {
445 echo "Error while inserting into stats_cvs_user\n" ;
455 function findMainBranch($project) {
456 $toprepo = forge_get_config('repos_path', 'scmbzr') ;
457 $repo = $toprepo . '/' . $project->getUnixName() ;
461 foreach ($this->main_branch_names as $bname) {
462 system ("bzr ls file://$repo/$bname > /dev/null 2>&1", $code) ;
471 function generateSnapshots($params) {
472 $us = forge_get_config('use_scm_snapshots') ;
473 $ut = forge_get_config('use_scm_tarballs') ;
478 $project = $this->checkParams($params);
483 $group_name = $project->getUnixName() ;
485 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
486 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
488 if (! $project->enableAnonSCM()) {
489 if (file_exists($snapshot)) {
492 if (file_exists($tarball)) {
498 $toprepo = forge_get_config('repos_path', 'scmbzr') ;
499 $repo = $toprepo . '/' . $project->getUnixName() ;
501 if (!is_dir ($repo) || !is_file ("$repo/format")) {
502 if (file_exists ($snapshot)) {
505 if (file_exists ($tarball)) {
511 $tmp = trim (`mktemp -d`) ;
516 $today = date ('Y-m-d') ;
517 $branch = $this->findMainBranch ($project) ;
519 system ("bzr export --root=$group_name-scm-$today --format=tar - $repo/$branch |".forge_get_config('compression_method')."> $tmp/snapshot") ;
520 chmod ("$tmp/snapshot", 0644) ;
521 copy ("$tmp/snapshot", $snapshot) ;
522 unlink ("$tmp/snapshot") ;
523 system ("rm -rf $tmp") ;
530 system ("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
531 chmod ("$tmp/tarball", 0644) ;
532 copy ("$tmp/tarball", $tarball) ;
533 unlink ("$tmp/tarball") ;
534 system ("rm -rf $tmp") ;
538 function scm_admin_form(&$params) {
540 $project = $this->checkParams($params);
544 session_require_perm('project_admin', $params['group_id']);
546 if (forge_get_config('allow_multiple_scm') && ($params['allow_multiple_scm'] > 1)) {
547 echo html_e('div', array('id' => 'tabber-'.$this->name, 'class' => 'tabbertab'),
548 $HTML->information(_('No specific action available')));
555 // c-file-style: "bsd"