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 ($project->usesPlugin($this->name) && 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 .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
142 $b .= '<div id="tabber">';
144 if (forge_get_config('use_ssh', 'scmsvn')) {
145 $b .= '<li><a href="#tabber-ssh">'._('via SSH').'</a></li>';
148 if (forge_get_config('use_dav', 'scmsvn')) {
149 $b .= '<li><a href="#tabber-dav">'._('via DAV').'</a></li>';
153 if (session_loggedin()) {
154 $u = user_get_object(user_getid());
155 $d = $u->getUnixName();
156 if (forge_get_config('use_ssh', 'scmsvn')) {
157 $b .= '<div id="tabber-ssh" class="tabbertab" >';
159 $b .= _('SSH must be installed on your client machine.');
161 $b .= _('Enter your site password when prompted.');
164 if (forge_get_config('ssh_port') != 22) {
165 $ssh_port = '--config-option="config:tunnels:ssh=ssh -p '.forge_get_config('ssh_port').'" ';
167 $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>' ;
170 if (forge_get_config('use_dav', 'scmsvn')) {
171 $b .= '<div id="tabber-dav" class="tabbertab" >';
173 $b .= _('Enter your site password when prompted.');
175 $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>' ;
179 if (forge_get_config('use_ssh', 'scmsvn')) {
180 $b .= '<div id="tabber-ssh" class="tabbertab" >';
182 $b .= _('SSH must be installed on your client machine.');
184 $b .= _('Substitute <em>developername</em> with the proper value.');
186 $b .= _('Enter your site password when prompted.');
189 if (forge_get_config('ssh_port') != 22) {
190 $ssh_port = '--config-option="config:tunnels:ssh=ssh -p '.forge_get_config('ssh_port').'" ';
192 $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>';
194 if (forge_get_config('use_dav', 'scmsvn')) {
195 $b .= '<div id="tabber-dav" class="tabbertab" >';
197 $b .= _('Substitute <em>developername</em> with the proper value.');
199 $b .= _('Enter your site password when prompted.');
201 $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>' ;
209 function getSnapshotPara($project) {
213 function getBrowserLinkBlock($project) {
214 $b = html_e('h2', array(), _('Subversion Repository Browser'));
216 $b .= sprintf(_("Browsing the %s tree gives you a view into the current status of this project's code."), 'Subversion');
218 $b .= _('You may also view the complete histories of any file in the repository.');
221 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID(),
222 sprintf(_('Browse %s Repository'), 'Subversion')
228 function getStatsBlock($project) {
232 $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',
233 array($project->getID()));
235 if (db_numrows($result) > 0) {
236 $tableHeaders = array(
241 $b .= $HTML->listTableTop($tableHeaders, array(), '', 'repo-history');
244 $total = array('adds' => 0, 'updates' => 0);
246 while($data = db_fetch_array($result)) {
248 $cells[] = array(util_display_user($data['user_name'], $data['user_id'], $data['realname']), 'class' => 'halfwidth');
249 $cells[] = array($data['adds'], 'class' => 'onequarterwidth align-right');
250 $cells[] = array($data['updates'], 'class' => 'onequarterwidth align-right');
251 $b .= $HTML->multiTableRow(array(), $cells);
252 $total['adds'] += $data['adds'];
253 $total['updates'] += $data['updates'];
257 $cells[] = array(html_e('strong', array(), _('Total')._(':')), 'class' => 'halfwidth');
258 $cells[] = array($total['adds'], 'class' => 'onequarterwidth align-right');
259 $cells[] = array($total['updates'], 'class' => 'onequarterwidth align-right');
260 $b .= $HTML->multiTableRow(array(), $cells);
261 $b .= $HTML->listTableBottom();
263 $b .= $HTML->information(_('No history yet'));
269 function printBrowserPage($params) {
272 $project = $this->checkParams($params);
277 if ($project->usesPlugin($this->name)) {
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'));
286 function createOrUpdateRepo($params) {
287 $project = $this->checkParams($params);
288 if (!$project) return false;
289 if (!$project->isActive()) return false;
290 if (!$project->usesPlugin($this->name)) return false;
292 $repo_prefix = forge_get_config('repos_path', 'scmsvn');
293 if (!is_dir($repo_prefix) && !mkdir($repo_prefix, 0755, true)) {
297 $repo = $repo_prefix . '/' . $project->getUnixName();
299 if (!is_dir ($repo) || !is_file ("$repo/format")) {
300 if (!mkdir($repo, 0700, true)) {
304 system ("svnadmin create $repo", $ret);
308 system ("sed -i '/enable-rep-sharing = false/s/^. //' $repo/db/fsfs.conf") ;
309 // dav/ and dav/activities.d directories are required by old svn clients (eg. svn 1.6.17 on ubuntu 12.04)
310 if (!is_dir ("$repo/dav")) {
313 if (!is_dir ("$repo/dav/activities.d")) {
314 mkdir("$repo/dav/activities.d");
316 system ("svn mkdir -m'Init' file:///$repo/trunk file:///$repo/tags file:///$repo/branches >/dev/null") ;
317 system ("find $repo -type d -print0 | xargs -r -0 chmod g+s") ;
318 // Allow read/write users to modify the SVN repository
319 $rw_unix_group = $project->getUnixName() . '_scmrw';
320 system("chgrp -R $rw_unix_group $repo");
321 // Allow read-only users to enter the (top-level) directory
322 $ro_unix_group = $project->getUnixName() . '_scmro';
323 system("chgrp $ro_unix_group $repo");
324 // open permissions to allow switching private/public easily
325 // see after to restrict the top-level directory
326 system ("chmod -R g+rwX,o+rX-w $repo") ;
329 if ($project->enableAnonSCM()) {
330 system("chmod g+rX-w,o+rX-w $repo") ;
332 system("chmod g+rX-w,o-rwx $repo") ;
336 function updateRepositoryList(&$params) {
339 function regenApacheAuth(&$params) {
340 # Enable /authscm/$user/svn URLs
341 $config_fname = forge_get_config('data_path').'/scmsvn-auth.inc';
342 $config_f = fopen($config_fname.'.new', 'w');
344 $res = db_query_params("SELECT login FROM nss_passwd WHERE status=$1", array('A'));
345 while ($arr = db_fetch_array($res)) {
346 fwrite($config_f, 'Use ScmsvnUser '.$arr['login']."\n");
350 chmod($config_fname.'.new', 0644);
351 rename($config_fname.'.new', $config_fname);
354 function gatherStats($params) {
355 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
356 $adds, $deletes, $updates, $commits, $date_key,
357 $usr_adds, $usr_deletes, $usr_updates, $usr_commits;
361 $project = $this->checkParams($params);
366 if (!$project->usesPlugin($this->name)) {
370 if ($params['mode'] == 'day') {
373 $year = $params['year'];
374 $month = $params['month'];
375 $day = $params['day'];
376 $month_string = sprintf("%04d%02d", $year, $month);
377 $start_time = gmmktime(0, 0, 0, $month, $day, $year);
378 $end_time = $start_time + 86400;
386 $usr_updates = array();
387 $usr_deletes = array();
388 $usr_commits = array();
390 $repo = forge_get_config('repos_path', 'scmsvn') . '/' . $project->getUnixName();
391 if (!is_dir($repo) || !is_file ("$repo/format")) {
396 $d1 = date('Y-m-d', $start_time - 150000);
397 $d2 = date('Y-m-d', $end_time + 150000);
399 $pipe = popen ("svn log file://$repo --xml -v -q -r '".'{'.$d2.'}:{'.$d1.'}'."' 2> /dev/null", 'r' ) ;
401 // cleaning stats_cvs_* table for the current day
402 $res = db_query_params('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
407 echo "Error while cleaning stats_cvs_group\n";
412 $res = db_query_params ('DELETE FROM stats_cvs_user WHERE month=$1 AND day=$2 AND group_id=$3',
413 array ($month_string,
417 echo "Error while cleaning stats_cvs_user\n" ;
422 $xml_parser = xml_parser_create();
423 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
424 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
426 // Analyzing history stream
427 while (!feof($pipe) &&
428 $data = fgets ($pipe, 4096)) {
429 if (!xml_parse ($xml_parser, $data, feof ($pipe))) {
430 $this->setError("Unable to parse XML with error " .
431 xml_error_string(xml_get_error_code($xml_parser)) .
433 xml_get_current_line_number($xml_parser));
440 xml_parser_free($xml_parser);
442 // inserting group results in stats_cvs_groups
443 if ($updates > 0 || $adds > 0 || $deletes > 0 || $commits > 0) {
444 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)',
453 echo "Error while inserting into stats_cvs_group\n";
459 // building the user list
460 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ), array_keys( $usr_deletes ), array_keys( $usr_commits )) );
462 foreach ($user_list as $user) {
463 // Trying to get user id from user name
464 $u = user_get_object_by_name($user);
466 $user_id = $u->getID();
471 $uc = isset($usr_commits[$user]) ? $usr_commits[$user] : 0 ;
472 $uu = isset($usr_updates[$user]) ? $usr_updates[$user] : 0 ;
473 $ua = isset($usr_adds[$user]) ? $usr_adds[$user] : 0 ;
474 $ud = isset($usr_deletes[$user]) ? $usr_deletes[$user] : 0 ;
475 if ($uu > 0 || $ua > 0 || $uc > 0 || $ud > 0) {
476 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)',
477 array ($month_string,
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 $group_id = $params['group'];
586 $project = group_get_object($group_id);
587 if (!$project->usesPlugin($this->name)) {
591 if (in_array('scmsvn', $params['show']) || (count($params['show']) < 1)) {
592 $start_time = $params['begin'];
593 $end_time = $params['end'];
595 $xml_parser = xml_parser_create();
596 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
597 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
599 // Grab&parse commit log
600 $protocol = forge_get_config('use_ssl', 'scmsvn') ? 'https://' : 'http://';
601 if ($project->enableAnonSCM()) {
602 $server_script = '/anonscm/svnlog';
604 $u = session_get_user();
605 if ($u && !$u->isError()) {
606 $server_script = '/authscm/'.$u->getUnixName().'/svnlog';
611 $script_url = $protocol . forge_get_config('scm_host')
613 .'?unix_group_name='.$project->getUnixName()
615 .'&begin='.$params['begin']
616 .'&end='.$params['end'];
618 curl_setopt($ch, CURLOPT_URL, $script_url);
619 curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curl2xml');
620 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
621 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
622 curl_setopt($ch, CURLOPT_COOKIE, @$_SERVER['HTTP_COOKIE']); // for session validation
623 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
624 curl_setopt($ch, CURLOPT_HTTPHEADER,
625 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
626 $body = curl_exec($ch);
627 if ($body === false) {
628 $this->setError(curl_error($ch));
633 if (!xml_parse($xml_parser, '', true))
634 $this->setError('Unable to parse XML with error '
635 . xml_error_string(xml_get_error_code($xml_parser))
636 . ' on line ' . xml_get_current_line_number($xml_parser));
637 xml_parser_free($xml_parser);
639 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
641 foreach ($messages as $message) {
643 $result['section'] = 'scm';
644 $result['group_id'] = $group_id;
645 $result['ref_id'] = 'browser.php?group_id='.$group_id;
646 $result['description'] = htmlspecialchars($message).' (r'.$revisions[$i].')';
647 $userObject = user_get_object_by_name($users[$i]);
648 if (is_a($userObject, 'FFUser')) {
649 $result['realname'] = util_display_user($userObject->getUnixName(), $userObject->getID(), $userObject->getRealName());
651 $result['realname'] = '';
653 $result['activity_date'] = $times[$i];
654 $result['subref_id'] = '&commit='.$revisions[$i];
655 $params['results'][] = $result;
660 if (!in_array($this->name, $params['ids'])) {
661 $params['ids'][] = $this->name;
662 $params['texts'][] = _('Subversion Commits');
667 // Get latest commits for inclusion in a widget
668 function getCommits($project, $user = null, $nbCommits) {
669 global $commits, $users, $adds, $updates, $messages, $times, $revisions, $deletes, $time_ok, $user_list, $last_message, $notimecheck, $xml_parser;
676 $revisions = array();
679 $user_list = array();
682 $revisionsArr = array();
683 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
684 $xml_parser = xml_parser_create();
685 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
686 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
688 // Grab&parse commit log
689 $protocol = forge_get_config('use_ssl', 'scmsvn') ? 'https://' : 'http://';
690 $u = session_get_user();
691 if ($project->enableAnonSCM())
692 $server_script = '/anonscm/svnlog';
694 $server_script = '/authscm/'.$u->getUnixName().'/svnlog';
696 $userunixname = $user->getUnixName();
697 $params = '&mode=latest_user&user_name='.$userunixname;
699 $params = '&mode=latest';
701 $script_url = $protocol . forge_get_config('scm_host')
703 .'?unix_group_name='.$project->getUnixName()
705 .'&limit='.$nbCommits;
707 curl_setopt($ch, CURLOPT_URL, $script_url);
708 curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curl2xml');
709 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
710 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
711 curl_setopt($ch, CURLOPT_COOKIE, $_SERVER['HTTP_COOKIE']); // for session validation
712 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
713 curl_setopt($ch, CURLOPT_HTTPHEADER,
714 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
715 $body = curl_exec($ch);
716 if ($body === false) {
717 $this->setError(curl_error($ch));
722 if (!xml_parse($xml_parser, '', true))
723 $this->setError('Unable to parse XML with error '
724 . xml_error_string(xml_get_error_code($xml_parser))
725 . ' on line ' . xml_get_current_line_number($xml_parser));
726 xml_parser_free($xml_parser);
728 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
730 foreach ($messages as $message) {
731 if ($user && ($users[$i] == $userunixname)) {
732 $revisionsArr[$i]['pluginName'] = 'scmsvn';
733 $revisionsArr[$i]['description'] = htmlspecialchars($message);
734 $revisionsArr[$i]['commit_id'] = $revisions[$i];
735 $revisionsArr[$i]['date'] = $times[$i];
737 $revisionsArr[$i]['pluginName'] = 'scmsvn';
738 $revisionsArr[$i]['description'] = htmlspecialchars($message);
739 $revisionsArr[$i]['commit_id'] = $revisions[$i];
740 $revisionsArr[$i]['date'] = $times[$i];
746 return $revisionsArr;
749 function get_scm_repo_list(&$params) {
750 if (array_key_exists('group_name',$params)) {
751 $unix_group_name = $params['group_name'];
753 $unix_group_name = '';
755 $protocol = forge_get_config('use_ssl', 'scmsvn')? 'https' : 'http';
756 if (session_loggedin()) {
757 $u = user_get_object(user_getid());
758 $d = $u->getUnixName();
762 if ($unix_group_name) {
763 $res = db_query_params("SELECT unix_group_name, groups.group_id FROM groups
764 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
765 WHERE groups.status=$1 AND group_plugin.plugin_id=$2 AND groups.unix_group_name=$3
766 ORDER BY unix_group_name", array('A', $this->getID(),$unix_group_name));
768 $res = db_query_params("SELECT unix_group_name, groups.group_id FROM groups
769 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
770 WHERE groups.status=$1 AND group_plugin.plugin_id=$2
771 ORDER BY unix_group_name", array('A', $this->getID()));
773 while ($arr = db_fetch_array($res)) {
774 if (!forge_check_perm('scm', $arr['group_id'], 'read')) {
778 if (forge_get_config('use_dav', 'scmsvn')) {
779 $urls[] = $protocol.'://'.forge_get_config('scm_host').'/anonscm/svn/'.$arr['unix_group_name'];
781 if (forge_get_config('use_ssh', 'scmsvn')) {
782 $urls[] = 'svn://'.forge_get_config('scm_host').$this->svn_root_fs.'/'.$arr['unix_group_name'];
784 if (session_loggedin()) {
785 if (forge_get_config('use_dav', 'scmsvn')) {
786 $urls[] = $protocol.'://'.forge_get_config('scm_host').'/authscm/'.$d.'/svn/'.$arr['unix_group_name'];
788 if (forge_get_config('use_ssh', 'scmsvn')) {
789 $urls[] = 'svn+ssh://'.$d.'@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $arr['unix_group_name'];
792 $results[] = array('group_id' => $arr['group_id'],
793 'repository_type' => 'svn',
794 'repository_id' => $arr['unix_group_name'].'/svn/'.$arr['unix_group_name'],
795 'repository_urls' => $urls,
799 foreach ($results as $res) {
800 $params['results'][] = $res;
804 function get_scm_repo_info(&$params) {
805 $rid = $params['repository_id'];
806 $e = explode('/',$rid);
807 if ($e[1] != 'svn') {
811 $p = array('group_name' => $g);
812 $this->get_scm_repo_list($p);
813 foreach ($p['results'] as $r) {
814 if ($r['repository_id'] == $rid) {
815 $params['results'] = $r;
821 function parse_scm_repo_activities(&$params) {
823 $res = db_query_params("SELECT unix_group_name, groups.group_id FROM groups
824 JOIN group_plugin ON (groups.group_id=group_plugin.group_id)
825 WHERE groups.status=$1 AND group_plugin.plugin_id=$2
826 ORDER BY unix_group_name", array('A', $this->getID()));
827 while ($arr = db_fetch_array($res)) {
829 $el['rpath'] = $this->svn_root_fs.'/'.$arr['unix_group_name'];
830 $el['rid'] = $arr['unix_group_name'].'/svn/'.$arr['unix_group_name'];
831 $el['gid'] = $arr['group_id'];
835 $lastactivities = array();
836 $res = db_query_params("SELECT repository_id, max(tstamp) AS last FROM scm_activities WHERE plugin_id=$1 GROUP BY repository_id",
837 array($this->getID()));
838 while ($arr = db_fetch_array($res)) {
839 $lastactivities[$arr['repository_id']] = $arr['last'];
842 foreach ($repos as $rdata) {
844 if (array_key_exists($rdata['rid'], $lastactivities)) {
845 $since = '-r {$(date -d @'.$lastactivities[$rdata['rid']].' -Iseconds)}:HEAD';
847 $rpath = $rdata['rpath'];
849 $f = popen("svn log -q 'file:///$rpath' $since 2> /dev/null", "r");
850 while (($l = fgets($f, 4096)) !== false) {
851 if (preg_match("/.*?\|.*\|(?P<tstamp>[-0-9 :+]+)/", $l, $matches)) {
852 $t = strtotime($matches['tstamp']);
853 if (array_key_exists($rdata['rid'], $lastactivities)
854 && $t <= $lastactivities[$rdata['rid']]) {
860 foreach ($tstamps as $t => $v) {
861 $res = db_query_params("INSERT INTO scm_activities (group_id, plugin_id, repository_id, tstamp) VALUES ($1,$2,$3,$4)",
871 // End of class, helper functions now
873 function SVNPluginCharData($parser, $chars) {
874 global $last_tag, $last_user, $last_time, $start_time, $end_time, $usr_commits, $commits,
875 $time_ok, $user_list, $last_message, $messages, $times, $users, $notimecheck;
878 $last_user = preg_replace('/[^a-z0-9_-]/', '', strtolower(trim($chars)));
879 $users[] = $last_user;
880 $usr_commits[$last_user] = isset($usr_commits[$last_user]) ? ($usr_commits[$last_user]+1) : 1 ;
885 $chars = preg_replace('/T(\d\d:\d\d:\d\d)\.\d+Z?$/', ' ${1}', $chars);
886 $last_time = strtotime($chars);
887 if ($start_time <= $last_time && $last_time < $end_time) {
891 if (!isset($notimecheck)) {
892 if ($last_user !== '') // empty in e.g. tags from cvs2svn
893 $usr_commits[$last_user]--;
897 $times[] = $last_time;
901 if ($time_ok === true || isset($notimecheck)) {
902 $messages[count($messages)-1] .= $chars;
904 /* note: there may be more than one msg
905 * (happen when the message contain accents).
912 function SVNPluginStartElement($parser, $name, $attrs) {
913 global $last_user, $last_time, $last_tag, $time_ok, $commits,
914 $adds, $updates, $usr_adds, $usr_updates, $last_message, $messages, $times, $revisions, $deletes, $usr_deletes, $notimecheck;
918 // Make sure we clean up before doing a new log entry
921 $revisions[] = $attrs['REVISION'];
925 if ($time_ok === true || isset($notimecheck)) {
927 if ($attrs['ACTION'] == "M") {
930 $usr_updates[$last_user] = isset($usr_updates[$last_user]) ? ($usr_updates[$last_user]+1) : 1 ;
932 } elseif ($attrs['ACTION'] == "A") {
935 $usr_adds[$last_user] = isset($usr_adds[$last_user]) ? ($usr_adds[$last_user]+1) : 1 ;
937 } elseif ($attrs['ACTION'] == 'D') {
940 $usr_deletes[$last_user] = isset($usr_deletes[$last_user]) ? ($usr_deletes[$last_user]+1) : 1 ;
947 if ($time_ok === true || isset($notimecheck)) {
955 function SVNPluginEndElement($parser, $name) {
960 function curl2xml($ch, $data) {
962 if (!xml_parse($xml_parser, $data, false))
963 exit_error('Unable to parse XML with error '
964 . xml_error_string(xml_get_error_code($xml_parser))
965 . ' on line ' . xml_get_current_line_number($xml_parser),
967 return strlen($data);
971 // c-file-style: "bsd"