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, 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');
42 class SVNPlugin extends SCMPlugin {
43 function __construct() {
44 parent::__construct();
45 $this->name = 'scmsvn';
46 $this->text = _('Subversion');
48 _("This plugin contains the Subversion subsystem of FusionForge. It allows
49 each FusionForge project to have its own Subversion repository, and gives
50 some control over it to the project's administrator.");
51 $this->svn_root_fs = forge_get_config('repos_path',
53 $this->svn_root_dav = '/svn';
54 $this->_addHook('scm_browser_page');
55 $this->_addHook('scm_update_repolist');
56 $this->_addHook('scm_regen_apache_auth');
57 $this->_addHook('scm_generate_snapshots');
58 $this->_addHook('scm_gather_stats');
59 $this->_addHook('get_scm_repo_list');
60 $this->_addHook('get_scm_repo_info');
61 $this->_addHook('parse_scm_repo_activities');
62 $this->_addHook('activity');
64 $this->provides['svn'] = true;
69 function getDefaultServer() {
70 return forge_get_config('default_server', 'scmsvn') ;
73 function printShortStats($params) {
74 $project = $this->checkParams($params);
79 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
80 $result = db_query_params('SELECT sum(updates) AS updates, sum(adds) AS adds FROM stats_cvs_group WHERE group_id=$1',
81 array ($project->getID())) ;
82 $commit_num = db_result($result,0,'updates');
83 $add_num = db_result($result,0,'adds');
90 $params['result'] .= ' (Subversion: '.sprintf(_('<strong>%1$s</strong> updates, <strong>%2$s</strong> adds'), number_format($commit_num, 0), number_format($add_num, 0)).")";
96 . sprintf(_('Documentation for %1$s is available at <a href="%2$s">%2$s</a>.'),
98 'http://svnbook.red-bean.com/')
102 function topModule($project) {
103 // Check toplevel module presence
104 $repo = 'file://' . forge_get_config('repos_path', $this->name).'/'.$project->getUnixName().'/';
107 if (!(exec("svn ls '$repo'", $res) && in_array($module.'/', $res))) {
114 function getInstructionsForAnon($project) {
115 $b = '<h2>' . _('Anonymous Subversion Access') . '</h2>';
117 $b .= _("This project's SVN repository can be checked out through anonymous access with the following command(s).");
121 $module = $this->topModule($project);
122 if (forge_get_config('use_ssh', 'scmsvn')) {
123 $b .= '<span class="tt">svn checkout svn://'.forge_get_config('scm_host').$this->svn_root_fs.'/'.$project->getUnixName().$module.'</span><br />';
125 if (forge_get_config('use_dav', 'scmsvn')) {
126 $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>' ;
132 function getInstructionsForRW($project) {
135 $module = $this->topModule($project);
136 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
137 $b .= '<div id="tabber">';
139 if (forge_get_config('use_ssh', 'scmsvn')) {
140 $b .= '<li><a href="#tabber-ssh">'._('via SSH').'</a></li>';
143 if (forge_get_config('use_dav', 'scmsvn')) {
144 $b .= '<li><a href="#tabber-dav">'._('via DAV').'</a></li>';
148 if (session_loggedin()) {
149 $u = user_get_object(user_getid());
150 $d = $u->getUnixName() ;
151 if (forge_get_config('use_ssh', 'scmsvn')) {
152 $b .= '<div id="tabber-ssh" class="tabbertab" >';
154 $b .= _('SSH must be installed on your client machine.');
156 $b .= _('Enter your site password when prompted.');
158 $b .= '<p><span class="tt">svn checkout svn+ssh://'.$d.'@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $project->getUnixName().$module.'</span></p>' ;
161 if (forge_get_config('use_dav', 'scmsvn')) {
162 $b .= '<div id="tabber-dav" class="tabbertab" >';
164 $b .= _('Enter your site password when prompted.');
166 $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>' ;
170 if (forge_get_config('use_ssh', 'scmsvn')) {
171 $b .= '<div id="tabber-ssh" class="tabbertab" >';
173 $b .= _('SSH must be installed on your client machine.');
175 $b .= _('Substitute <em>developername</em> with the proper value.');
177 $b .= _('Enter your site password when prompted.');
179 $b .= '<p><span class="tt">svn checkout svn+ssh://<i>'._('developername').'</i>@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $project->getUnixName().$module.'</span></p>' ;
182 if (forge_get_config('use_dav', 'scmsvn')) {
183 $b .= '<div id="tabber-dav" class="tabbertab" >';
185 $b .= _('Substitute <em>developername</em> with the proper value.');
187 $b .= _('Enter your site password when prompted.');
189 $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>' ;
197 function getSnapshotPara($project) {
201 function getBrowserLinkBlock($project) {
202 $b = html_e('h2', array(), _('Subversion Repository Browser'));
204 $b .= sprintf(_("Browsing the %s tree gives you a view into the current status of this project's code."), 'Subversion');
206 $b .= _('You may also view the complete histories of any file in the repository.');
209 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID(),
210 sprintf(_('Browse %s Repository'), 'Subversion')
216 function getStatsBlock($project) {
220 $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',
221 array ($project->getID()));
223 if (db_numrows($result) > 0) {
224 $tableHeaders = array(
229 $b .= $HTML->listTableTop($tableHeaders, array(), '', 'repo-history');
232 $total = array('adds' => 0, 'updates' => 0);
234 while($data = db_fetch_array($result)) {
236 $cells[] = array(util_display_user($data['user_name'], $data['user_id'], $data['realname']), 'class' => 'halfwidth');
237 $cells[] = array($data['adds'], 'class' => 'onequarterwidth align-right');
238 $cells[] = array($data['updates'], 'class' => 'onequarterwidth align-right');
239 $b .= $HTML->multiTableRow(array(), $cells);
240 $total['adds'] += $data['adds'];
241 $total['updates'] += $data['updates'];
245 $cells[] = array(html_e('strong', array(), _('Total')._(':')), 'class' => 'halfwidth');
246 $cells[] = array($total['adds'], 'class' => 'onequarterwidth align-right');
247 $cells[] = array($total['updates'], 'class' => 'onequarterwidth align-right');
248 $b .= $HTML->multiTableRow(array(), $cells);
249 $b .= $HTML->listTableBottom();
251 $b .= $HTML->information(_('No history yet'));
257 function printBrowserPage($params) {
260 $project = $this->checkParams($params);
265 if ($project->usesPlugin($this->name)) {
266 $iframe_src = '/scm/viewvc.php?root='.$project->getUnixName();
267 if ($params['commit']) {
268 $iframe_src .= '&view=rev&revision='.$params['commit'];
270 htmlIframe($iframe_src, array('id'=>'scmsvn_iframe'));
274 function createOrUpdateRepo($params) {
275 $project = $this->checkParams($params);
276 if (!$project) return false;
277 if (!$project->isActive()) return false;
278 if (!$project->usesPlugin($this->name)) return false;
280 $repo_prefix = forge_get_config('repos_path', 'scmsvn');
281 if (!is_dir($repo_prefix) && !mkdir($repo_prefix, 0755, true)) {
285 $repo = $repo_prefix . '/' . $project->getUnixName();
287 if (!is_dir ($repo) || !is_file ("$repo/format")) {
288 if (!mkdir($repo, 0700, true)) {
292 system ("svnadmin create $repo", $ret);
296 system ("sed -i '/enable-rep-sharing = false/s/^. //' $repo/db/fsfs.conf") ;
297 // dav/ and dav/activities.d directories are required by old svn clients (eg. svn 1.6.17 on ubuntu 12.04)
298 if (!is_dir ("$repo/dav")) {
301 if (!is_dir ("$repo/dav/activities.d")) {
302 mkdir("$repo/dav/activities.d");
304 system ("svn mkdir -m'Init' file:///$repo/trunk file:///$repo/tags file:///$repo/branches >/dev/null") ;
305 system ("find $repo -type d -print0 | xargs -r -0 chmod g+s") ;
306 // Allow read/write users to modify the SVN repository
307 $rw_unix_group = $project->getUnixName() . '_scmrw';
308 system("chgrp -R $rw_unix_group $repo");
309 // Allow read-only users to enter the (top-level) directory
310 $ro_unix_group = $project->getUnixName() . '_scmro';
311 system("chgrp $ro_unix_group $repo");
312 // open permissions to allow switching private/public easily
313 // see after to restrict the top-level directory
314 system ("chmod -R g+rwX,o+rX-w $repo") ;
317 if ($project->enableAnonSCM()) {
318 system("chmod g+rX-w,o+rX-w $repo") ;
320 system("chmod g+rX-w,o-rwx $repo") ;
324 function updateRepositoryList(&$params) {
327 function regenApacheAuth(&$params) {
328 # Enable /authscm/$user/svn URLs
329 $config_fname = forge_get_config('data_path').'/scmsvn-auth.inc';
330 $config_f = fopen($config_fname.'.new', 'w');
332 $res = db_query_params("SELECT login FROM nss_passwd WHERE status=$1", array('A'));
333 while ($arr = db_fetch_array($res)) {
334 fwrite($config_f, 'Use ScmsvnUser '.$arr['login']."\n");
338 chmod($config_fname.'.new', 0644);
339 rename($config_fname.'.new', $config_fname);
342 function gatherStats($params) {
343 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
344 $adds, $deletes, $updates, $commits, $date_key,
345 $usr_adds, $usr_deletes, $usr_updates, $usr_commits;
349 $project = $this->checkParams($params);
354 if (! $project->usesPlugin($this->name)) {
358 if ($params['mode'] == 'day') {
361 $year = $params['year'];
362 $month = $params['month'];
363 $day = $params['day'];
364 $month_string = sprintf("%04d%02d", $year, $month);
365 $start_time = gmmktime(0, 0, 0, $month, $day, $year);
366 $end_time = $start_time + 86400;
374 $usr_updates = array();
375 $usr_deletes = array();
376 $usr_commits = array();
378 $repo = forge_get_config('repos_path', 'scmsvn') . '/' . $project->getUnixName();
379 if (!is_dir ($repo) || !is_file ("$repo/format")) {
384 $d1 = date('Y-m-d', $start_time - 150000);
385 $d2 = date('Y-m-d', $end_time + 150000);
387 $pipe = popen ("svn log file://$repo --xml -v -q -r '".'{'.$d2.'}:{'.$d1.'}'."' 2> /dev/null", 'r' ) ;
389 // cleaning stats_cvs_* table for the current day
390 $res = db_query_params('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
395 echo "Error while cleaning stats_cvs_group\n" ;
400 $res = db_query_params ('DELETE FROM stats_cvs_user WHERE month=$1 AND day=$2 AND group_id=$3',
401 array ($month_string,
403 $project->getID())) ;
405 echo "Error while cleaning stats_cvs_user\n" ;
410 $xml_parser = xml_parser_create();
411 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
412 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
414 // Analyzing history stream
415 while (!feof($pipe) &&
416 $data = fgets ($pipe, 4096)) {
417 if (!xml_parse ($xml_parser, $data, feof ($pipe))) {
418 $this->setError("Unable to parse XML with error " .
419 xml_error_string(xml_get_error_code($xml_parser)) .
421 xml_get_current_line_number($xml_parser));
428 xml_parser_free($xml_parser);
430 // inserting group results in stats_cvs_groups
431 if ($updates > 0 || $adds > 0 || $deletes > 0 || $commits > 0) {
432 if (!db_query_params('INSERT INTO stats_cvs_group (month,day,group_id,checkouts,commits,adds,updates,deletes) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)',
433 array ($month_string,
441 echo "Error while inserting into stats_cvs_group\n" ;
447 // building the user list
448 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ), array_keys( $usr_deletes ), array_keys( $usr_commits )) );
450 foreach ( $user_list as $user ) {
451 // trying to get user id from user name
452 $u = user_get_object_by_name ($user) ;
454 $user_id = $u->getID();
459 $uc = isset($usr_commits[$user]) ? $usr_commits[$user] : 0 ;
460 $uu = isset($usr_updates[$user]) ? $usr_updates[$user] : 0 ;
461 $ua = isset($usr_adds[$user]) ? $usr_adds[$user] : 0 ;
462 $ud = isset($usr_deletes[$user]) ? $usr_deletes[$user] : 0 ;
463 if ($uu > 0 || $ua > 0 || $uc > 0 || $ud > 0) {
464 if (!db_query_params ('INSERT INTO stats_cvs_user (month,day,group_id,user_id,commits,adds, updates, deletes) VALUES ($1,$2,$3,$4,$5,$6,$7,$8)',
465 array ($month_string,
473 echo "Error while inserting into stats_cvs_user\n" ;
483 function generateSnapshots($params) {
484 $us = forge_get_config('use_scm_snapshots') ;
485 $ut = forge_get_config('use_scm_tarballs') ;
490 $project = $this->checkParams($params);
495 $group_name = $project->getUnixName();
497 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
498 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
500 if (! $project->usesPlugin($this->name)) {
504 if (! $project->enableAnonSCM()) {
505 if (is_file($snapshot)) {
508 if (is_file($tarball)) {
514 $toprepo = forge_get_config('repos_path', 'scmsvn');
515 $repo = $toprepo . '/' . $project->getUnixName();
517 if (!is_dir ($repo) || !is_file ("$repo/format")) {
518 if (is_file($snapshot)) {
521 if (is_file($tarball)) {
527 $tmp = trim (`mktemp -d`) ;
531 $today = date ('Y-m-d') ;
532 $dir = $project->getUnixName ()."-$today" ;
533 system ("mkdir -p $tmp") ;
535 system ("svn ls file://$repo/trunk > /dev/null 2> /dev/null", $code) ;
538 system ("cd $tmp ; svn export file://$repo/trunk $dir > /dev/null 2>&1") ;
539 system ("tar cCf $tmp - $dir |".forge_get_config('compression_method')."> $tmp/snapshot") ;
540 chmod ("$tmp/snapshot", 0644) ;
541 copy ("$tmp/snapshot", $snapshot) ;
542 unlink ("$tmp/snapshot") ;
543 system ("rm -rf $tmp/$dir") ;
545 if (is_file($snapshot)) {
552 system ("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
553 chmod ("$tmp/tarball", 0644) ;
554 copy ("$tmp/tarball", $tarball) ;
555 unlink ("$tmp/tarball") ;
556 system ("rm -rf $tmp") ;
560 function activity($params) {
561 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
562 $adds, $deletes, $updates, $commits, $date_key,
563 $messages, $last_message, $times, $revisions, $users, $xml_parser;
571 $revisions = array();
573 $group_id = $params['group'];
574 $project = group_get_object($group_id);
575 if (! $project->usesPlugin($this->name)) {
579 if (in_array('scmsvn', $params['show']) || (count($params['show']) < 1)) {
580 $start_time = $params['begin'];
581 $end_time = $params['end'];
583 $xml_parser = xml_parser_create();
584 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
585 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
587 // Grab&parse commit log
588 $protocol = forge_get_config('use_ssl', 'scmsvn') ? 'https://' : 'http://';
589 if ($project->enableAnonSCM()) {
590 $server_script = '/anonscm/svnlog';
592 $u = session_get_user();
593 if ($u && !$u->isError()) {
594 $server_script = '/authscm/'.$u->getUnixName().'/svnlog';
599 $script_url = $protocol . forge_get_config('scm_host')
601 .'?unix_group_name='.$project->getUnixName()
603 .'&begin='.$params['begin']
604 .'&end='.$params['end'];
606 curl_setopt($ch, CURLOPT_URL, $script_url);
607 curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curl2xml');
608 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
609 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
610 curl_setopt($ch, CURLOPT_COOKIE, @$_SERVER['HTTP_COOKIE']); // for session validation
611 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
612 curl_setopt($ch, CURLOPT_HTTPHEADER,
613 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
614 $body = curl_exec($ch);
615 if ($body === false) {
616 $this->setError(curl_error($ch));
621 if (!xml_parse($xml_parser, '', true))
622 $this->setError('Unable to parse XML with error '
623 . xml_error_string(xml_get_error_code($xml_parser))
624 . ' on line ' . xml_get_current_line_number($xml_parser));
625 xml_parser_free($xml_parser);
627 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
629 foreach ($messages as $message) {
631 $result['section'] = 'scm';
632 $result['group_id'] = $group_id;
633 $result['ref_id'] = 'browser.php?group_id='.$group_id;
634 $result['description'] = htmlspecialchars($message).' (r'.$revisions[$i].')';
635 $userObject = user_get_object_by_name($users[$i]);
636 if (is_a($userObject, 'FFUser')) {
637 $result['realname'] = util_display_user($userObject->getUnixName(), $userObject->getID(), $userObject->getRealName());
639 $result['realname'] = '';
641 $result['activity_date'] = $times[$i];
642 $result['subref_id'] = '&commit='.$revisions[$i];
643 $params['results'][] = $result;
648 if (!in_array($this->name, $params['ids'])) {
649 $params['ids'][] = $this->name;
650 $params['texts'][] = _('Subversion Commits');
655 // Get latest commits for inclusion in a widget
656 function getCommits($project, $user = null, $nbCommits) {
657 global $commits, $users, $adds, $updates, $messages, $times, $revisions, $deletes, $time_ok, $user_list, $last_message, $notimecheck, $xml_parser;
664 $revisions = array();
667 $user_list = array();
670 $revisionsArr = array();
671 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
672 $xml_parser = xml_parser_create();
673 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
674 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
676 // Grab&parse commit log
677 $protocol = forge_get_config('use_ssl', 'scmsvn') ? 'https://' : 'http://';
678 $u = session_get_user();
679 if ($project->enableAnonSCM())
680 $server_script = '/anonscm/svnlog';
682 $server_script = '/authscm/'.$u->getUnixName().'/svnlog';
684 $userunixname = $user->getUnixName();
685 $params = '&mode=latest_user&user_name='.$userunixname;
687 $params = '&mode=latest';
689 $script_url = $protocol . forge_get_config('scm_host')
691 .'?unix_group_name='.$project->getUnixName()
693 .'&limit='.$nbCommits;
695 curl_setopt($ch, CURLOPT_URL, $script_url);
696 curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curl2xml');
697 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
698 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
699 curl_setopt($ch, CURLOPT_COOKIE, $_SERVER['HTTP_COOKIE']); // for session validation
700 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
701 curl_setopt($ch, CURLOPT_HTTPHEADER,
702 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
703 $body = curl_exec($ch);
704 if ($body === false) {
705 $this->setError(curl_error($ch));
710 if (!xml_parse($xml_parser, '', true))
711 $this->setError('Unable to parse XML with error '
712 . xml_error_string(xml_get_error_code($xml_parser))
713 . ' on line ' . xml_get_current_line_number($xml_parser));
714 xml_parser_free($xml_parser);
716 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
718 foreach ($messages as $message) {
719 if ($user && ($users[$i] == $userunixname)) {
720 $revisionsArr[$i]['pluginName'] = 'scmsvn';
721 $revisionsArr[$i]['description'] = htmlspecialchars($message);
722 $revisionsArr[$i]['commit_id'] = $revisions[$i];
723 $revisionsArr[$i]['date'] = $times[$i];
725 $revisionsArr[$i]['pluginName'] = 'scmsvn';
726 $revisionsArr[$i]['description'] = htmlspecialchars($message);
727 $revisionsArr[$i]['commit_id'] = $revisions[$i];
728 $revisionsArr[$i]['date'] = $times[$i];
734 return $revisionsArr;
737 function get_scm_repo_list(&$params) {
738 if (array_key_exists('group_name',$params)) {
739 $unix_group_name = $params['group_name'];
741 $unix_group_name = '';
743 $protocol = forge_get_config('use_ssl', 'scmsvn')? 'https' : 'http';
744 if (session_loggedin()) {
745 $u = user_get_object(user_getid());
746 $d = $u->getUnixName();
750 if ($unix_group_name) {
751 $res = db_query_params("SELECT unix_group_name, groups.group_id FROM groups
752 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
753 WHERE groups.status=$1 AND group_plugin.plugin_id=$2 AND groups.unix_group_name=$3
754 ORDER BY unix_group_name", array('A', $this->getID(),$unix_group_name));
756 $res = db_query_params("SELECT unix_group_name, groups.group_id FROM groups
757 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
758 WHERE groups.status=$1 AND group_plugin.plugin_id=$2
759 ORDER BY unix_group_name", array('A', $this->getID()));
761 while ($arr = db_fetch_array($res)) {
762 if (!forge_check_perm('scm', $arr['group_id'], 'read')) {
766 if (forge_get_config('use_dav', 'scmsvn')) {
767 $urls[] = $protocol.'://'.forge_get_config('scm_host').'/anonscm/svn/'.$arr['unix_group_name'];
769 if (forge_get_config('use_ssh', 'scmsvn')) {
770 $urls[] = 'svn://'.forge_get_config('scm_host').$this->svn_root_fs.'/'.$arr['unix_group_name'];
772 if (session_loggedin()) {
773 if (forge_get_config('use_dav', 'scmsvn')) {
774 $urls[] = $protocol.'://'.forge_get_config('scm_host').'/authscm/'.$d.'/svn/'.$arr['unix_group_name'];
776 if (forge_get_config('use_ssh', 'scmsvn')) {
777 $urls[] = 'svn+ssh://'.$d.'@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $arr['unix_group_name'];
780 $results[] = array('group_id' => $arr['group_id'],
781 'repository_type' => 'svn',
782 'repository_id' => $arr['unix_group_name'].'/svn/'.$arr['unix_group_name'],
783 'repository_urls' => $urls,
787 foreach ($results as $res) {
788 $params['results'][] = $res;
792 function get_scm_repo_info(&$params) {
793 $rid = $params['repository_id'];
794 $e = explode('/',$rid);
795 if ($e[1] != 'svn') {
799 $p = array('group_name' => $g);
800 $this->get_scm_repo_list($p);
801 foreach ($p['results'] as $r) {
802 if ($r['repository_id'] == $rid) {
803 $params['results'] = $r;
809 function parse_scm_repo_activities(&$params) {
811 $res = db_query_params("SELECT unix_group_name, groups.group_id FROM groups
812 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
813 WHERE groups.status=$1 AND group_plugin.plugin_id=$2
814 ORDER BY unix_group_name", array('A', $this->getID()));
815 while ($arr = db_fetch_array($res)) {
817 $el['rpath'] = $this->svn_root_fs.'/'.$arr['unix_group_name'];
818 $el['rid'] = $arr['unix_group_name'].'/svn/'.$arr['unix_group_name'];
819 $el['gid'] = $arr['group_id'];
823 $lastactivities = array();
824 $res = db_query_params("SELECT repository_id, max(tstamp) AS last FROM scm_activities WHERE plugin_id=$1 GROUP BY repository_id",
825 array($this->getID()));
826 while ($arr = db_fetch_array($res)) {
827 $lastactivities[$arr['repository_id']] = $arr['last'];
830 foreach ($repos as $rdata) {
832 if (array_key_exists($rdata['rid'], $lastactivities)) {
833 $since = '-r {$(date -d @'.$lastactivities[$rdata['rid']].' -Iseconds)}:HEAD';
835 $rpath = $rdata['rpath'];
837 $f = popen("svn log -q 'file:///$rpath' $since 2> /dev/null", "r");
838 while (($l = fgets($f, 4096)) !== false) {
839 if (preg_match("/.*?\|.*\|(?P<tstamp>[-0-9 :+]+)/", $l, $matches)) {
840 $t = strtotime($matches['tstamp']);
841 if (array_key_exists($rdata['rid'], $lastactivities)
842 && $t <= $lastactivities[$rdata['rid']]) {
848 foreach ($tstamps as $t => $v) {
849 $res = db_query_params("INSERT INTO scm_activities (group_id, plugin_id, repository_id, tstamp) VALUES ($1,$2,$3,$4)",
859 // End of class, helper functions now
861 function SVNPluginCharData($parser, $chars) {
862 global $last_tag, $last_user, $last_time, $start_time, $end_time, $usr_commits, $commits,
863 $time_ok, $user_list, $last_message, $messages, $times, $users, $notimecheck;
866 $last_user = preg_replace('/[^a-z0-9_-]/', '', strtolower(trim($chars)));
867 $users[] = $last_user;
868 $usr_commits[$last_user] = isset($usr_commits[$last_user]) ? ($usr_commits[$last_user]+1) : 1 ;
873 $chars = preg_replace('/T(\d\d:\d\d:\d\d)\.\d+Z?$/', ' ${1}', $chars);
874 $last_time = strtotime($chars);
875 if ($start_time <= $last_time && $last_time < $end_time) {
879 if (!isset($notimecheck)) {
880 if ($last_user !== '') // empty in e.g. tags from cvs2svn
881 $usr_commits[$last_user]--;
885 $times[] = $last_time;
889 if ($time_ok === true || isset($notimecheck)) {
890 $messages[count($messages)-1] .= $chars;
892 /* note: there may be more than one msg
893 * (happen when the message contain accents).
900 function SVNPluginStartElement($parser, $name, $attrs) {
901 global $last_user, $last_time, $last_tag, $time_ok, $commits,
902 $adds, $updates, $usr_adds, $usr_updates, $last_message, $messages, $times, $revisions, $deletes, $usr_deletes, $notimecheck;
906 // Make sure we clean up before doing a new log entry
909 $revisions[] = $attrs['REVISION'];
913 if ($time_ok === true || isset($notimecheck)) {
915 if ($attrs['ACTION'] == "M") {
918 $usr_updates[$last_user] = isset($usr_updates[$last_user]) ? ($usr_updates[$last_user]+1) : 1 ;
920 } elseif ($attrs['ACTION'] == "A") {
923 $usr_adds[$last_user] = isset($usr_adds[$last_user]) ? ($usr_adds[$last_user]+1) : 1 ;
925 } elseif ($attrs['ACTION'] == 'D') {
928 $usr_deletes[$last_user] = isset($usr_deletes[$last_user]) ? ($usr_deletes[$last_user]+1) : 1 ;
935 if ($time_ok === true || isset($notimecheck)) {
943 function SVNPluginEndElement($parser, $name) {
948 function curl2xml($ch, $data) {
950 if (!xml_parse($xml_parser, $data, false))
951 exit_error('Unable to parse XML with error '
952 . xml_error_string(xml_get_error_code($xml_parser))
953 . ' on line ' . xml_get_current_line_number($xml_parser),
955 return strlen($data);
960 // c-file-style: "bsd"