3 * FusionForge Subversion plugin
5 * Copyright 2003-2010, Roland Mas, Franck Villaume
6 * Copyright 2004, GForge, LLC
7 * Copyright 2010, Alain Peyrat <aljeux@free.fr>
8 * Copyright 2012-2014,2016-2017, Franck Villaume - TrivialDev
9 * Copyright 2013, French Ministry of National Education
11 * This file is part of FusionForge.
13 * FusionForge is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published
15 * by the Free Software Foundation; either version 2 of the License,
16 * or (at your option) any later version.
18 * FusionForge is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
23 * You should have received a copy of the GNU General Public License along
24 * with this program; if not, write to the Free Software Foundation, Inc.,
25 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
28 require_once $gfcommon.'include/plugins_utils.php';
30 forge_define_config_item('default_server', 'scmsvn', forge_get_config ('scm_host'));
31 forge_define_config_item('repos_path', 'scmsvn', forge_get_config('chroot').'/scmrepos/svn');
32 forge_define_config_item('serve_path', 'scmsvn', forge_get_config('repos_path'));
33 forge_define_config_item('use_ssh', 'scmsvn', false);
34 forge_set_config_item_bool('use_ssh', 'scmsvn');
35 forge_define_config_item('use_dav', 'scmsvn', true);
36 forge_set_config_item_bool('use_dav', 'scmsvn');
37 forge_define_config_item('use_ssl', 'scmsvn', true);
38 forge_set_config_item_bool('use_ssl', 'scmsvn');
39 forge_define_config_item('anonsvn_login','scmsvn', 'anonsvn');
40 forge_define_config_item('anonsvn_password','scmsvn', 'anonsvn');
41 forge_define_config_item('ssh_port', 'core', 22);
43 class SVNPlugin extends SCMPlugin {
44 function __construct() {
45 parent::__construct();
46 $this->name = 'scmsvn';
47 $this->text = _('Subversion');
49 _("This plugin contains the Subversion subsystem of FusionForge. It allows
50 each FusionForge project to have its own Subversion repository, and gives
51 some control over it to the project's administrator.");
52 $this->svn_root_fs = forge_get_config('repos_path',
54 $this->svn_root_dav = '/svn';
55 $this->_addHook('scm_browser_page');
56 $this->_addHook('scm_update_repolist');
57 $this->_addHook('scm_regen_apache_auth');
58 $this->_addHook('scm_generate_snapshots');
59 $this->_addHook('scm_gather_stats');
60 $this->_addHook('get_scm_repo_list');
61 $this->_addHook('get_scm_repo_info');
62 $this->_addHook('parse_scm_repo_activities');
63 $this->_addHook('activity');
65 $this->provides['svn'] = true;
70 function getDefaultServer() {
71 return forge_get_config('default_server', 'scmsvn');
74 function printShortStats($params) {
75 $project = $this->checkParams($params);
80 if (forge_check_perm('scm', $project->getID(), 'read')) {
81 $result = db_query_params('SELECT sum(updates) AS updates, sum(adds) AS adds FROM stats_cvs_group WHERE group_id=$1',
82 array($project->getID()));
83 $commit_num = db_result($result,0,'updates');
84 $add_num = db_result($result,0,'adds');
91 $params['result'] .= ' (Subversion: '.sprintf(_('<strong>%1$s</strong> updates, <strong>%2$s</strong> adds'), number_format($commit_num, 0), number_format($add_num, 0)).")";
97 . sprintf(_('Documentation for %1$s is available at <a href="%2$s">%2$s</a>.'),
99 'http://svnbook.red-bean.com/')
103 function topModule($project) {
104 // Check toplevel module presence
105 $repo = 'file://' . forge_get_config('repos_path', $this->name).'/'.$project->getUnixName().'/';
108 if (!(exec("svn ls '$repo'", $res) && in_array($module.'/', $res))) {
115 function getInstructionsForAnon($project) {
116 $b = '<h2>' . _('Anonymous Subversion Access') . '</h2>';
118 $b .= _("This project's SVN repository can be checked out through anonymous access with the following command(s).");
122 $module = $this->topModule($project);
123 if (forge_get_config('use_ssh', 'scmsvn')) {
125 if (forge_get_config('ssh_port') != 22) {
126 $ssh_port = '--config-option="config:tunnels:ssh=ssh -p '.forge_get_config('ssh_port').'"';
128 $b .= '<span class="tt">svn '.$ssh_port.' checkout svn://'.forge_get_config('scm_host').$this->svn_root_fs.'/'.$project->getUnixName().$module.'</span><br />';
130 if (forge_get_config('use_dav', 'scmsvn')) {
131 $b .= '<p><span class="tt">svn checkout http'.((forge_get_config('use_ssl', 'scmsvn')) ? 's' : '').'://'. forge_get_config('scm_host'). '/anonscm/svn/'.$project->getUnixName().$module.'</span></p>' ;
137 function getInstructionsForRW($project) {
140 $module = $this->topModule($project);
141 $b .= html_e('h2', array(), _('Developer Access'));
142 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
143 $b .= '<div id="tabber-svn">';
145 if (forge_get_config('use_ssh', 'scmsvn')) {
146 $b .= '<li><a href="#tabber-svnssh">'._('via SSH').'</a></li>';
149 if (forge_get_config('use_dav', 'scmsvn')) {
150 $b .= '<li><a href="#tabber-svndav">'._('via DAV').'</a></li>';
154 if (session_loggedin()) {
155 $u = user_get_object(user_getid());
156 $d = $u->getUnixName();
157 if (forge_get_config('use_ssh', 'scmsvn')) {
158 $b .= '<div id="tabber-svnssh" class="tabbertab" >';
160 $b .= _('SSH must be installed on your client machine.');
162 $b .= _('Enter your site password when prompted.');
165 if (forge_get_config('ssh_port') != 22) {
166 $ssh_port = '--config-option="config:tunnels:ssh=ssh -p '.forge_get_config('ssh_port').'" ';
168 $b .= '<p><span class="tt">svn '.$ssh_port.'checkout svn+ssh://'.$d.'@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $project->getUnixName().$module.'</span></p>' ;
171 if (forge_get_config('use_dav', 'scmsvn')) {
172 $b .= '<div id="tabber-svndav" class="tabbertab" >';
174 $b .= _('Enter your site password when prompted.');
176 $b .= '<p><span class="tt">svn checkout --username '.$d.' http'.((forge_get_config('use_ssl', 'scmsvn')) ? 's' : '').'://'. forge_get_config('scm_host'). '/authscm/'.$d.'/svn/'.$project->getUnixName().$module.'</span></p>' ;
180 if (forge_get_config('use_ssh', 'scmsvn')) {
181 $b .= '<div id="tabber-svnssh" class="tabbertab" >';
183 $b .= _('SSH must be installed on your client machine.');
185 $b .= _('Substitute <em>developername</em> with the proper value.');
187 $b .= _('Enter your site password when prompted.');
190 if (forge_get_config('ssh_port') != 22) {
191 $ssh_port = '--config-option="config:tunnels:ssh=ssh -p '.forge_get_config('ssh_port').'" ';
193 $b .= '<p><span class="tt">svn '.$ssh_port.'checkout svn+ssh://<i>'._('developername').'</i>@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $project->getUnixName().$module.'</span></p>' ; $b .= '</div>';
195 if (forge_get_config('use_dav', 'scmsvn')) {
196 $b .= '<div id="tabber-svndav" class="tabbertab" >';
198 $b .= _('Substitute <em>developername</em> with the proper value.');
200 $b .= _('Enter your site password when prompted.');
202 $b .= '<p><span class="tt">svn checkout --username <i>'._('developername').'</i> http'.((forge_get_config('use_ssl', 'scmsvn')) ? 's' : '').'://'. forge_get_config('scm_host'). '/authscm/<i>'._('developername').'</i>/svn/'.$project->getUnixName().$module.'</span></p>' ;
210 function getSnapshotPara($project) {
214 function getBrowserLinkBlock($project) {
215 $b = html_e('h2', array(), _('Subversion Repository Browser'));
217 $b .= sprintf(_("Browsing the %s tree gives you a view into the current status of this project's code."), 'Subversion');
219 $b .= _('You may also view the complete histories of any file in the repository.');
222 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID(),
223 sprintf(_('Browse %s Repository'), 'Subversion')
229 function getStatsBlock($project) {
233 $result = db_query_params('SELECT u.realname, u.user_name, u.user_id, sum(updates) as updates, sum(adds) as adds, sum(adds+updates) as combined FROM stats_cvs_user s, users u WHERE group_id=$1 AND s.user_id=u.user_id AND (updates>0 OR adds >0) GROUP BY u.user_id, realname, user_name, u.user_id ORDER BY combined DESC, realname',
234 array($project->getID()));
236 if (db_numrows($result) > 0) {
237 $tableHeaders = array(
242 $b .= $HTML->listTableTop($tableHeaders, array(), '', 'repo-history');
245 $total = array('adds' => 0, 'updates' => 0);
247 while($data = db_fetch_array($result)) {
249 $cells[] = array(util_display_user($data['user_name'], $data['user_id'], $data['realname']), 'class' => 'halfwidth');
250 $cells[] = array($data['adds'], 'class' => 'onequarterwidth align-right');
251 $cells[] = array($data['updates'], 'class' => 'onequarterwidth align-right');
252 $b .= $HTML->multiTableRow(array(), $cells);
253 $total['adds'] += $data['adds'];
254 $total['updates'] += $data['updates'];
258 $cells[] = array(html_e('strong', array(), _('Total')._(':')), 'class' => 'halfwidth');
259 $cells[] = array($total['adds'], 'class' => 'onequarterwidth align-right');
260 $cells[] = array($total['updates'], 'class' => 'onequarterwidth align-right');
261 $b .= $HTML->multiTableRow(array(), $cells);
262 $b .= $HTML->listTableBottom();
264 $b .= $HTML->information(_('No history yet'));
270 function printBrowserPage($params) {
273 $project = $this->checkParams($params);
278 $iframe_src = '/scm/viewvc.php?root='.$project->getUnixName();
279 if ($params['commit']) {
280 $iframe_src .= '&view=rev&revision='.$params['commit'];
282 htmlIframe($iframe_src, array('id'=>'scmsvn_iframe'));
285 function createOrUpdateRepo($params) {
286 $project = $this->checkParams($params);
287 if (!$project) return false;
288 if (!$project->isActive()) return false;
290 $repo_prefix = forge_get_config('repos_path', 'scmsvn');
291 if (!is_dir($repo_prefix) && !mkdir($repo_prefix, 0755, true)) {
295 $repo = $repo_prefix . '/' . $project->getUnixName();
297 if (!is_dir ($repo) || !is_file ("$repo/format")) {
298 if (!mkdir($repo, 0700, true)) {
302 system ("svnadmin create $repo", $ret);
306 system ("sed -i '/enable-rep-sharing = false/s/^. //' $repo/db/fsfs.conf") ;
307 // dav/ and dav/activities.d directories are required by old svn clients (eg. svn 1.6.17 on ubuntu 12.04)
308 if (!is_dir ("$repo/dav")) {
311 if (!is_dir ("$repo/dav/activities.d")) {
312 mkdir("$repo/dav/activities.d");
314 system ("svn mkdir -m'Init' file:///$repo/trunk file:///$repo/tags file:///$repo/branches >/dev/null") ;
315 system ("find $repo -type d -print0 | xargs -r -0 chmod g+s") ;
316 // Allow read/write users to modify the SVN repository
317 $rw_unix_group = $project->getUnixName() . '_scmrw';
318 system("chgrp -R $rw_unix_group $repo");
319 // Allow read-only users to enter the (top-level) directory
320 $ro_unix_group = $project->getUnixName() . '_scmro';
321 system("chgrp $ro_unix_group $repo");
322 // open permissions to allow switching private/public easily
323 // see after to restrict the top-level directory
324 system ("chmod -R g+rwX,o+rX-w $repo") ;
327 if ($project->enableAnonSCM()) {
328 system("chmod g+rX-w,o+rX-w $repo") ;
330 system("chmod g+rX-w,o-rwx $repo") ;
334 function updateRepositoryList(&$params) {
337 function regenApacheAuth(&$params) {
338 # Enable /authscm/$user/svn URLs
339 $config_fname = forge_get_config('data_path').'/scmsvn-auth.inc';
340 $config_f = fopen($config_fname.'.new', 'w');
342 $res = db_query_params("SELECT login FROM nss_passwd WHERE status=$1", array('A'));
343 while ($arr = db_fetch_array($res)) {
344 fwrite($config_f, 'Use ScmsvnUser '.$arr['login']."\n");
348 chmod($config_fname.'.new', 0644);
349 rename($config_fname.'.new', $config_fname);
352 function gatherStats($params) {
353 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
354 $adds, $deletes, $updates, $commits, $date_key,
355 $usr_adds, $usr_deletes, $usr_updates, $usr_commits;
359 $project = $this->checkParams($params);
364 if ($params['mode'] == 'day') {
367 $year = $params['year'];
368 $month = $params['month'];
369 $day = $params['day'];
370 $month_string = sprintf("%04d%02d", $year, $month);
371 $start_time = gmmktime(0, 0, 0, $month, $day, $year);
372 $end_time = $start_time + 86400;
380 $usr_updates = array();
381 $usr_deletes = array();
382 $usr_commits = array();
384 $repo = forge_get_config('repos_path', 'scmsvn') . '/' . $project->getUnixName();
385 if (!is_dir($repo) || !is_file ("$repo/format")) {
390 $d1 = date('Y-m-d', $start_time - 150000);
391 $d2 = date('Y-m-d', $end_time + 150000);
393 $pipe = popen ("svn log file://$repo --xml -v -q -r '".'{'.$d2.'}:{'.$d1.'}'."' 2> /dev/null", 'r' ) ;
395 // cleaning stats_cvs_* table for the current day
396 $res = db_query_params('DELETE FROM stats_cvs_group WHERE month = $1 AND day = $2 AND group_id = $3 AND reponame = $4',
400 $project->getUnixName()));
402 echo "Error while cleaning stats_cvs_group\n";
407 $res = db_query_params('DELETE FROM stats_cvs_user WHERE month = $1 AND day = $2 AND group_id = $3 AND reponame = $4',
411 $project->getUnixName()));
413 echo "Error while cleaning stats_cvs_user\n" ;
418 $xml_parser = xml_parser_create();
419 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
420 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
422 // Analyzing history stream
423 while (!feof($pipe) &&
424 $data = fgets ($pipe, 4096)) {
425 if (!xml_parse ($xml_parser, $data, feof ($pipe))) {
426 $this->setError("Unable to parse XML with error " .
427 xml_error_string(xml_get_error_code($xml_parser)) .
429 xml_get_current_line_number($xml_parser));
436 xml_parser_free($xml_parser);
438 // inserting group results in stats_cvs_groups
439 if ($updates > 0 || $adds > 0 || $deletes > 0 || $commits > 0) {
440 if (!db_query_params('INSERT INTO stats_cvs_group (month, day, group_id, checkouts, commits, adds, updates, deletes, reponame)
441 VALUES ($1, $2, $3, $4, $5, $6, $7, $8)',
450 $project->getUnixName()))) {
451 echo "Error while inserting into stats_cvs_group\n";
457 // building the user list
458 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ), array_keys( $usr_deletes ), array_keys( $usr_commits )) );
460 foreach ($user_list as $user) {
461 // Trying to get user id from user name
462 $u = user_get_object_by_name($user);
464 $user_id = $u->getID();
469 $uc = isset($usr_commits[$user]) ? $usr_commits[$user] : 0 ;
470 $uu = isset($usr_updates[$user]) ? $usr_updates[$user] : 0 ;
471 $ua = isset($usr_adds[$user]) ? $usr_adds[$user] : 0 ;
472 $ud = isset($usr_deletes[$user]) ? $usr_deletes[$user] : 0 ;
473 if ($uu > 0 || $ua > 0 || $uc > 0 || $ud > 0) {
474 if (!db_query_params('INSERT INTO stats_cvs_user (month, day, group_id, user_id, commits, adds, updates, deletes, reponame)
475 VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)',
476 array ($month_string,
484 $project->getUnixName()))) {
485 echo "Error while inserting into stats_cvs_user\n";
495 function generateSnapshots($params) {
496 $us = forge_get_config('use_scm_snapshots') ;
497 $ut = forge_get_config('use_scm_tarballs') ;
502 $project = $this->checkParams($params);
507 $group_name = $project->getUnixName();
509 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
510 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
512 if (!$project->usesPlugin($this->name)) {
516 if (!$project->enableAnonSCM()) {
517 if (is_file($snapshot)) {
520 if (is_file($tarball)) {
526 $toprepo = forge_get_config('repos_path', 'scmsvn');
527 $repo = $toprepo . '/' . $project->getUnixName();
529 if (!is_dir($repo) || !is_file ("$repo/format")) {
530 if (is_file($snapshot)) {
533 if (is_file($tarball)) {
539 $tmp = trim(`mktemp -d`);
543 $today = date('Y-m-d');
544 $dir = $project->getUnixName ()."-$today" ;
545 system("mkdir -p $tmp") ;
547 system ("svn ls file://$repo/trunk > /dev/null 2> /dev/null", $code) ;
550 system ("cd $tmp ; svn export file://$repo/trunk $dir > /dev/null 2>&1") ;
551 system ("tar cCf $tmp - $dir |".forge_get_config('compression_method')."> $tmp/snapshot") ;
552 chmod("$tmp/snapshot", 0644);
553 copy("$tmp/snapshot", $snapshot);
554 unlink("$tmp/snapshot");
555 system ("rm -rf $tmp/$dir") ;
557 if (is_file($snapshot)) {
564 system("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
565 chmod("$tmp/tarball", 0644);
566 copy("$tmp/tarball", $tarball);
567 unlink("$tmp/tarball");
568 system("rm -rf $tmp");
572 function activity($params) {
573 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
574 $adds, $deletes, $updates, $commits, $date_key,
575 $messages, $last_message, $times, $revisions, $users, $xml_parser;
583 $revisions = array();
585 $project = $this->checkParams($params);
590 if (in_array('scmsvn', $params['show']) || (count($params['show']) < 1)) {
591 $start_time = $params['begin'];
592 $end_time = $params['end'];
594 $xml_parser = xml_parser_create();
595 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
596 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
598 // Grab&parse commit log
599 $protocol = forge_get_config('use_ssl', 'scmsvn') ? 'https://' : 'http://';
600 if ($project->enableAnonSCM()) {
601 $server_script = '/anonscm/svnlog';
603 $u = session_get_user();
604 if ($u && !$u->isError()) {
605 $server_script = '/authscm/'.$u->getUnixName().'/svnlog';
610 $script_url = $protocol . forge_get_config('scm_host')
612 .'?unix_group_name='.$project->getUnixName()
614 .'&begin='.$params['begin']
615 .'&end='.$params['end'];
617 curl_setopt($ch, CURLOPT_URL, $script_url);
618 curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curl2xml');
619 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
620 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
621 curl_setopt($ch, CURLOPT_COOKIE, @$_SERVER['HTTP_COOKIE']); // for session validation
622 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
623 curl_setopt($ch, CURLOPT_HTTPHEADER,
624 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
625 $body = curl_exec($ch);
626 if ($body === false) {
627 $this->setError(curl_error($ch));
632 if (!xml_parse($xml_parser, '', true))
633 $this->setError('Unable to parse XML with error '
634 . xml_error_string(xml_get_error_code($xml_parser))
635 . ' on line ' . xml_get_current_line_number($xml_parser));
636 xml_parser_free($xml_parser);
638 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
640 foreach ($messages as $message) {
642 $result['section'] = 'scm';
643 $result['group_id'] = $project->getID();
644 $result['ref_id'] = 'browser.php?group_id='.$project->getID();
645 $result['description'] = htmlspecialchars($message).' (r'.$revisions[$i].')';
646 $userObject = user_get_object_by_name($users[$i]);
647 if (is_a($userObject, 'FFUser')) {
648 $result['realname'] = util_display_user($userObject->getUnixName(), $userObject->getID(), $userObject->getRealName());
650 $result['realname'] = '';
652 $result['activity_date'] = $times[$i];
653 $result['subref_id'] = '&commit='.$revisions[$i];
654 $params['results'][] = $result;
659 if (!in_array($this->name, $params['ids'])) {
660 $params['ids'][] = $this->name;
661 $params['texts'][] = _('Subversion Commits');
666 // Get latest commits for inclusion in a widget
667 function getCommits($project, $user = null, $nbCommits) {
668 global $commits, $users, $adds, $updates, $messages, $times, $revisions, $deletes, $time_ok, $user_list, $last_message, $notimecheck, $xml_parser;
675 $revisions = array();
678 $user_list = array();
681 $revisionsArr = array();
682 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
683 $xml_parser = xml_parser_create();
684 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
685 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
687 // Grab&parse commit log
688 $protocol = forge_get_config('use_ssl', 'scmsvn') ? 'https://' : 'http://';
689 $u = session_get_user();
690 if ($project->enableAnonSCM())
691 $server_script = '/anonscm/svnlog';
693 $server_script = '/authscm/'.$u->getUnixName().'/svnlog';
695 $userunixname = $user->getUnixName();
696 $params = '&mode=latest_user&user_name='.$userunixname;
698 $params = '&mode=latest';
700 $script_url = $protocol . forge_get_config('scm_host')
702 .'?unix_group_name='.$project->getUnixName()
704 .'&limit='.$nbCommits;
706 curl_setopt($ch, CURLOPT_URL, $script_url);
707 curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curl2xml');
708 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
709 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
710 curl_setopt($ch, CURLOPT_COOKIE, $_SERVER['HTTP_COOKIE']); // for session validation
711 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
712 curl_setopt($ch, CURLOPT_HTTPHEADER,
713 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
714 $body = curl_exec($ch);
715 if ($body === false) {
716 $this->setError(curl_error($ch));
721 if (!xml_parse($xml_parser, '', true))
722 $this->setError('Unable to parse XML with error '
723 . xml_error_string(xml_get_error_code($xml_parser))
724 . ' on line ' . xml_get_current_line_number($xml_parser));
725 xml_parser_free($xml_parser);
727 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
729 foreach ($messages as $message) {
730 if ($user && ($users[$i] == $userunixname)) {
731 $revisionsArr[$i]['pluginName'] = 'scmsvn';
732 $revisionsArr[$i]['description'] = htmlspecialchars($message);
733 $revisionsArr[$i]['commit_id'] = $revisions[$i];
734 $revisionsArr[$i]['date'] = $times[$i];
736 $revisionsArr[$i]['pluginName'] = 'scmsvn';
737 $revisionsArr[$i]['description'] = htmlspecialchars($message);
738 $revisionsArr[$i]['commit_id'] = $revisions[$i];
739 $revisionsArr[$i]['date'] = $times[$i];
745 return $revisionsArr;
748 function get_scm_repo_list(&$params) {
749 if (array_key_exists('group_name',$params)) {
750 $unix_group_name = $params['group_name'];
752 $unix_group_name = '';
754 $protocol = forge_get_config('use_ssl', 'scmsvn')? 'https' : 'http';
755 if (session_loggedin()) {
756 $u = user_get_object(user_getid());
757 $d = $u->getUnixName();
761 if ($unix_group_name) {
762 $res = db_query_params("SELECT unix_group_name, groups.group_id FROM groups
763 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
764 WHERE groups.status=$1 AND group_plugin.plugin_id=$2 AND groups.unix_group_name=$3
765 ORDER BY unix_group_name", array('A', $this->getID(),$unix_group_name));
767 $res = db_query_params("SELECT unix_group_name, groups.group_id FROM groups
768 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
769 WHERE groups.status=$1 AND group_plugin.plugin_id=$2
770 ORDER BY unix_group_name", array('A', $this->getID()));
772 while ($arr = db_fetch_array($res)) {
773 if (!forge_check_perm('scm', $arr['group_id'], 'read')) {
777 if (forge_get_config('use_dav', 'scmsvn')) {
778 $urls[] = $protocol.'://'.forge_get_config('scm_host').'/anonscm/svn/'.$arr['unix_group_name'];
780 if (forge_get_config('use_ssh', 'scmsvn')) {
781 $urls[] = 'svn://'.forge_get_config('scm_host').$this->svn_root_fs.'/'.$arr['unix_group_name'];
783 if (session_loggedin()) {
784 if (forge_get_config('use_dav', 'scmsvn')) {
785 $urls[] = $protocol.'://'.forge_get_config('scm_host').'/authscm/'.$d.'/svn/'.$arr['unix_group_name'];
787 if (forge_get_config('use_ssh', 'scmsvn')) {
788 $urls[] = 'svn+ssh://'.$d.'@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $arr['unix_group_name'];
791 $results[] = array('group_id' => $arr['group_id'],
792 'repository_type' => 'svn',
793 'repository_id' => $arr['unix_group_name'].'/svn/'.$arr['unix_group_name'],
794 'repository_urls' => $urls,
798 foreach ($results as $res) {
799 $params['results'][] = $res;
803 function get_scm_repo_info(&$params) {
804 $rid = $params['repository_id'];
805 $e = explode('/',$rid);
806 if ($e[1] != 'svn') {
810 $p = array('group_name' => $g);
811 $this->get_scm_repo_list($p);
812 foreach ($p['results'] as $r) {
813 if ($r['repository_id'] == $rid) {
814 $params['results'] = $r;
820 function parse_scm_repo_activities(&$params) {
822 $res = db_query_params("SELECT unix_group_name, groups.group_id FROM groups
823 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
824 WHERE groups.status=$1 AND group_plugin.plugin_id=$2
825 ORDER BY unix_group_name", array('A', $this->getID()));
826 while ($arr = db_fetch_array($res)) {
828 $el['rpath'] = $this->svn_root_fs.'/'.$arr['unix_group_name'];
829 $el['rid'] = $arr['unix_group_name'].'/svn/'.$arr['unix_group_name'];
830 $el['gid'] = $arr['group_id'];
834 $lastactivities = array();
835 $res = db_query_params("SELECT repository_id, max(tstamp) AS last FROM scm_activities WHERE plugin_id=$1 GROUP BY repository_id",
836 array($this->getID()));
837 while ($arr = db_fetch_array($res)) {
838 $lastactivities[$arr['repository_id']] = $arr['last'];
841 foreach ($repos as $rdata) {
843 if (array_key_exists($rdata['rid'], $lastactivities)) {
844 $since = '-r {$(date -d @'.$lastactivities[$rdata['rid']].' -Iseconds)}:HEAD';
846 $rpath = $rdata['rpath'];
848 $f = popen("svn log -q 'file:///$rpath' $since 2> /dev/null", "r");
849 while (($l = fgets($f, 4096)) !== false) {
850 if (preg_match("/.*?\|.*\|(?P<tstamp>[-0-9 :+]+)/", $l, $matches)) {
851 $t = strtotime($matches['tstamp']);
852 if (array_key_exists($rdata['rid'], $lastactivities)
853 && $t <= $lastactivities[$rdata['rid']]) {
859 foreach ($tstamps as $t => $v) {
860 $res = db_query_params("INSERT INTO scm_activities (group_id, plugin_id, repository_id, tstamp) VALUES ($1,$2,$3,$4)",
870 // End of class, helper functions now
872 function SVNPluginCharData($parser, $chars) {
873 global $last_tag, $last_user, $last_time, $start_time, $end_time, $usr_commits, $commits,
874 $time_ok, $user_list, $last_message, $messages, $times, $users, $notimecheck;
877 $last_user = preg_replace('/[^a-z0-9_-]/', '', strtolower(trim($chars)));
878 $users[] = $last_user;
879 $usr_commits[$last_user] = isset($usr_commits[$last_user]) ? ($usr_commits[$last_user]+1) : 1 ;
884 $chars = preg_replace('/T(\d\d:\d\d:\d\d)\.\d+Z?$/', ' ${1}', $chars);
885 $last_time = strtotime($chars);
886 if ($start_time <= $last_time && $last_time < $end_time) {
890 if (!isset($notimecheck)) {
891 if ($last_user !== '') // empty in e.g. tags from cvs2svn
892 $usr_commits[$last_user]--;
896 $times[] = $last_time;
900 if ($time_ok === true || isset($notimecheck)) {
901 $messages[count($messages)-1] .= $chars;
903 /* note: there may be more than one msg
904 * (happen when the message contain accents).
911 function SVNPluginStartElement($parser, $name, $attrs) {
912 global $last_user, $last_time, $last_tag, $time_ok, $commits,
913 $adds, $updates, $usr_adds, $usr_updates, $last_message, $messages, $times, $revisions, $deletes, $usr_deletes, $notimecheck;
917 // Make sure we clean up before doing a new log entry
920 $revisions[] = $attrs['REVISION'];
924 if ($time_ok === true || isset($notimecheck)) {
926 if ($attrs['ACTION'] == "M") {
929 $usr_updates[$last_user] = isset($usr_updates[$last_user]) ? ($usr_updates[$last_user]+1) : 1 ;
931 } elseif ($attrs['ACTION'] == "A") {
934 $usr_adds[$last_user] = isset($usr_adds[$last_user]) ? ($usr_adds[$last_user]+1) : 1 ;
936 } elseif ($attrs['ACTION'] == 'D') {
939 $usr_deletes[$last_user] = isset($usr_deletes[$last_user]) ? ($usr_deletes[$last_user]+1) : 1 ;
946 if ($time_ok === true || isset($notimecheck)) {
954 function SVNPluginEndElement($parser, $name) {
959 function curl2xml($ch, $data) {
961 if (!xml_parse($xml_parser, $data, false))
962 exit_error('Unable to parse XML with error '
963 . xml_error_string(xml_get_error_code($xml_parser))
964 . ' on line ' . xml_get_current_line_number($xml_parser),
966 return strlen($data);
970 // c-file-style: "bsd"