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('activity');
61 $this->provides['svn'] = true;
66 function getDefaultServer() {
67 return forge_get_config('default_server', 'scmsvn') ;
70 function printShortStats($params) {
71 $project = $this->checkParams($params);
76 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
77 $result = db_query_params('SELECT sum(updates) AS updates, sum(adds) AS adds FROM stats_cvs_group WHERE group_id=$1',
78 array ($project->getID())) ;
79 $commit_num = db_result($result,0,'updates');
80 $add_num = db_result($result,0,'adds');
87 $params['result'] .= ' (Subversion: '.sprintf(_('<strong>%1$s</strong> updates, <strong>%2$s</strong> adds'), number_format($commit_num, 0), number_format($add_num, 0)).")";
93 . sprintf(_('Documentation for %1$s is available at <a href="%2$s">%2$s</a>.'),
95 'http://svnbook.red-bean.com/')
99 function topModule($project) {
100 // Check toplevel module presence
101 $repo = 'file://' . forge_get_config('repos_path', $this->name).'/'.$project->getUnixName().'/';
104 if (!(exec("svn ls '$repo'", $res) && in_array($module.'/', $res))) {
111 function getInstructionsForAnon($project) {
112 $b = '<h2>' . _('Anonymous Subversion Access') . '</h2>';
114 $b .= _("This project's SVN repository can be checked out through anonymous access with the following command(s).");
118 $module = $this->topModule($project);
119 if (forge_get_config('use_ssh', 'scmsvn')) {
120 $b .= '<tt>svn checkout svn://'.forge_get_config('scm_host').$this->svn_root_fs.'/'.$project->getUnixName().$module.'</tt><br />';
122 if (forge_get_config('use_dav', 'scmsvn')) {
123 $b .= '<p><tt>svn checkout http'.((forge_get_config('use_ssl', 'scmsvn')) ? 's' : '').'://'. forge_get_config('scm_host'). '/anonscm/svn/'.$project->getUnixName().$module.'</tt></p>' ;
129 function getInstructionsForRW($project) {
132 $module = $this->topModule($project);
133 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
134 $b .= '<div id="tabber">';
136 if (forge_get_config('use_ssh', 'scmsvn')) {
137 $b .= '<li><a href="#tabber-ssh">'._('via SSH').'</a></li>';
140 if (forge_get_config('use_dav', 'scmsvn')) {
141 $b .= '<li><a href="#tabber-dav">'._('via DAV').'</a></li>';
145 if (session_loggedin()) {
146 $u = user_get_object(user_getid());
147 $d = $u->getUnixName() ;
148 if (forge_get_config('use_ssh', 'scmsvn')) {
149 $b .= '<div id="tabber-ssh" class="tabbertab" >';
151 $b .= _('SSH must be installed on your client machine.');
153 $b .= _('Enter your site password when prompted.');
155 $b .= '<p><tt>svn checkout svn+ssh://'.$d.'@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $project->getUnixName().$module.'</tt></p>' ;
158 if (forge_get_config('use_dav', 'scmsvn')) {
159 $b .= '<div id="tabber-dav" class="tabbertab" >';
161 $b .= _('Enter your site password when prompted.');
163 $b .= '<p><tt>svn checkout --username '.$d.' http'.((forge_get_config('use_ssl', 'scmsvn')) ? 's' : '').'://'. forge_get_config('scm_host'). '/authscm/'.$d.'/svn/'.$project->getUnixName().$module.'</tt></p>' ;
167 if (forge_get_config('use_ssh', 'scmsvn')) {
168 $b .= '<div id="tabber-ssh" class="tabbertab" >';
170 $b .= _('SSH must be installed on your client machine.');
172 $b .= _('Substitute <em>developername</em> with the proper value.');
174 $b .= _('Enter your site password when prompted.');
176 $b .= '<p><tt>svn checkout svn+ssh://<i>'._('developername').'</i>@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $project->getUnixName().$module.'</tt></p>' ;
179 if (forge_get_config('use_dav', 'scmsvn')) {
180 $b .= '<div id="tabber-dav" class="tabbertab" >';
182 $b .= _('Substitute <em>developername</em> with the proper value.');
184 $b .= _('Enter your site password when prompted.');
186 $b .= '<p><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.'</tt></p>' ;
191 $b .= '<script type="text/javascript">//<![CDATA[
192 jQuery(document).ready(function() {
193 jQuery("#tabber").tabs();
199 function getSnapshotPara($project) {
203 function getBrowserLinkBlock($project) {
204 $b = html_e('h2', array(), _('Subversion Repository Browser'));
206 $b .= sprintf(_("Browsing the %s tree gives you a view into the current status of this project's code."), 'Subversion');
208 $b .= _('You may also view the complete histories of any file in the repository.');
211 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID(),
212 sprintf(_('Browse %s Repository'), 'Subversion')
218 function getStatsBlock($project) {
222 $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',
223 array ($project->getID()));
225 if (db_numrows($result) > 0) {
226 $tableHeaders = array(
231 $b .= $HTML->listTableTop($tableHeaders, false, '', 'repo-history');
234 $total = array('adds' => 0, 'updates' => 0);
236 while($data = db_fetch_array($result)) {
238 $cells[] = array(util_make_link_u($data['user_name'], $data['user_id'], $data['realname']), 'class' => 'halfwidth');
239 $cells[] = array($data['adds'], 'class' => 'onequarterwidth align-right');
240 $cells[] = array($data['updates'], 'class' => 'onequarterwidth align-right');
241 $b .= $HTML->multiTableRow(array('class' => $HTML->boxGetAltRowStyle($i, true)), $cells);
242 $total['adds'] += $data['adds'];
243 $total['updates'] += $data['updates'];
247 $cells[] = array(html_e('strong', array(), _('Total')._(':')), 'class' => 'halfwidth');
248 $cells[] = array($total['adds'], 'class' => 'onequarterwidth align-right');
249 $cells[] = array($total['updates'], 'class' => 'onequarterwidth align-right');
250 $b .= $HTML->multiTableRow(array('class' => $HTML->boxGetAltRowStyle($i, true)), $cells);
251 $b .= $HTML->listTableBottom();
253 $b .= $HTML->information(_('No history yet'));
259 function printBrowserPage($params) {
262 $project = $this->checkParams($params);
267 if ($project->usesPlugin($this->name)) {
268 $iframe_src = '/scm/viewvc.php?root='.$project->getUnixName();
269 if ($params['commit']) {
270 $iframe_src .= '&view=rev&revision='.$params['commit'];
272 htmlIframe($iframe_src, array('id'=>'scmsvn_iframe'));
276 function createOrUpdateRepo($params) {
277 $project = $this->checkParams($params);
278 if (!$project) return false;
279 if (!$project->isActive()) return false;
280 if (!$project->usesPlugin($this->name)) return false;
282 $repo_prefix = forge_get_config('repos_path', 'scmsvn');
283 if (!is_dir($repo_prefix) && !mkdir($repo_prefix, 0755, true)) {
287 $repo = $repo_prefix . '/' . $project->getUnixName();
289 if (!is_dir ($repo) || !is_file ("$repo/format")) {
290 if (!mkdir($repo, 0700, true)) {
294 system ("svnadmin create $repo", $ret);
298 system ("sed -i '/enable-rep-sharing = false/s/^. //' $repo/db/fsfs.conf") ;
299 // dav/ and dav/activities.d directories are required by old svn clients (eg. svn 1.6.17 on ubuntu 12.04)
300 if (!is_dir ("$repo/dav")) {
303 if (!is_dir ("$repo/dav/activities.d")) {
304 mkdir("$repo/dav/activities.d");
306 system ("svn mkdir -m'Init' file:///$repo/trunk file:///$repo/tags file:///$repo/branches >/dev/null") ;
307 system ("find $repo -type d -print0 | xargs -r -0 chmod g+s") ;
308 // Allow read/write users to modify the SVN repository
309 $rw_unix_group = $project->getUnixName() . '_scmrw';
310 system("chgrp -R $rw_unix_group $repo");
311 // Allow read-only users to enter the (top-level) directory
312 $ro_unix_group = $project->getUnixName() . '_scmro';
313 system("chgrp $ro_unix_group $repo");
314 // open permissions to allow switching private/public easily
315 // see after to restrict the top-level directory
316 system ("chmod -R g+rwX,o+rX-w $repo") ;
319 if ($project->enableAnonSCM()) {
320 system("chmod g+rX-w,o+rX-w $repo") ;
322 system("chmod g+rX-w,o-rwx $repo") ;
326 function updateRepositoryList(&$params) {
329 function regenApacheAuth(&$params) {
330 # Enable /authscm/$user/svn URLs
331 $config_fname = forge_get_config('data_path').'/scmsvn-auth.inc';
332 $config_f = fopen($config_fname.'.new', 'w');
334 $res = db_query_params("SELECT login, passwd FROM nss_passwd WHERE status=$1", array('A'));
335 while ($arr = db_fetch_array($res)) {
336 fwrite($config_f, 'Use ScmsvnUser '.$arr['login']."\n");
340 chmod($config_fname.'.new', 0644);
341 rename($config_fname.'.new', $config_fname);
344 function gatherStats($params) {
345 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
346 $adds, $deletes, $updates, $commits, $date_key,
347 $usr_adds, $usr_deletes, $usr_updates, $usr_commits;
351 $project = $this->checkParams($params);
356 if (! $project->usesPlugin($this->name)) {
360 if ($params['mode'] == 'day') {
363 $year = $params['year'];
364 $month = $params['month'];
365 $day = $params['day'];
366 $month_string = sprintf("%04d%02d", $year, $month);
367 $start_time = gmmktime(0, 0, 0, $month, $day, $year);
368 $end_time = $start_time + 86400;
376 $usr_updates = array();
377 $usr_deletes = array();
378 $usr_commits = array();
380 $repo = forge_get_config('repos_path', 'scmsvn') . '/' . $project->getUnixName();
381 if (!is_dir ($repo) || !is_file ("$repo/format")) {
386 $d1 = date('Y-m-d', $start_time - 150000);
387 $d2 = date('Y-m-d', $end_time + 150000);
389 $pipe = popen ("svn log file://$repo --xml -v -q -r '".'{'.$d2.'}:{'.$d1.'}'."' 2> /dev/null", 'r' ) ;
391 // cleaning stats_cvs_* table for the current day
392 $res = db_query_params('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
397 echo "Error while cleaning stats_cvs_group\n" ;
402 $res = db_query_params ('DELETE FROM stats_cvs_user WHERE month=$1 AND day=$2 AND group_id=$3',
403 array ($month_string,
405 $project->getID())) ;
407 echo "Error while cleaning stats_cvs_user\n" ;
412 $xml_parser = xml_parser_create();
413 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
414 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
416 // Analyzing history stream
417 while (!feof($pipe) &&
418 $data = fgets ($pipe, 4096)) {
419 if (!xml_parse ($xml_parser, $data, feof ($pipe))) {
420 $this->setError("Unable to parse XML with error " .
421 xml_error_string(xml_get_error_code($xml_parser)) .
423 xml_get_current_line_number($xml_parser));
430 xml_parser_free($xml_parser);
432 // inserting group results in stats_cvs_groups
433 if ($updates > 0 || $adds > 0 || $deletes > 0 || $commits > 0) {
434 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)',
435 array ($month_string,
443 echo "Error while inserting into stats_cvs_group\n" ;
449 // building the user list
450 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ), array_keys( $usr_deletes ), array_keys( $usr_commits )) );
452 foreach ( $user_list as $user ) {
453 // trying to get user id from user name
454 $u = user_get_object_by_name ($user) ;
456 $user_id = $u->getID();
461 $uc = isset($usr_commits[$user]) ? $usr_commits[$user] : 0 ;
462 $uu = isset($usr_updates[$user]) ? $usr_updates[$user] : 0 ;
463 $ua = isset($usr_adds[$user]) ? $usr_adds[$user] : 0 ;
464 $ud = isset($usr_deletes[$user]) ? $usr_deletes[$user] : 0 ;
465 if ($uu > 0 || $ua > 0 || $uc > 0 || $ud > 0) {
466 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)',
467 array ($month_string,
475 echo "Error while inserting into stats_cvs_user\n" ;
485 function generateSnapshots($params) {
486 $us = forge_get_config('use_scm_snapshots') ;
487 $ut = forge_get_config('use_scm_tarballs') ;
492 $project = $this->checkParams($params);
497 $group_name = $project->getUnixName();
499 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
500 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
502 if (! $project->usesPlugin($this->name)) {
506 if (! $project->enableAnonSCM()) {
507 if (is_file($snapshot)) {
510 if (is_file($tarball)) {
516 $toprepo = forge_get_config('repos_path', 'scmsvn');
517 $repo = $toprepo . '/' . $project->getUnixName();
519 if (!is_dir ($repo) || !is_file ("$repo/format")) {
520 if (is_file($snapshot)) {
523 if (is_file($tarball)) {
529 $tmp = trim (`mktemp -d`) ;
533 $today = date ('Y-m-d') ;
534 $dir = $project->getUnixName ()."-$today" ;
535 system ("mkdir -p $tmp") ;
537 system ("svn ls file://$repo/trunk > /dev/null 2> /dev/null", $code) ;
540 system ("cd $tmp ; svn export file://$repo/trunk $dir > /dev/null 2>&1") ;
541 system ("tar cCf $tmp - $dir |".forge_get_config('compression_method')."> $tmp/snapshot") ;
542 chmod ("$tmp/snapshot", 0644) ;
543 copy ("$tmp/snapshot", $snapshot) ;
544 unlink ("$tmp/snapshot") ;
545 system ("rm -rf $tmp/$dir") ;
547 if (is_file($snapshot)) {
554 system ("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
555 chmod ("$tmp/tarball", 0644) ;
556 copy ("$tmp/tarball", $tarball) ;
557 unlink ("$tmp/tarball") ;
558 system ("rm -rf $tmp") ;
562 function activity($params) {
563 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
564 $adds, $deletes, $updates, $commits, $date_key,
565 $messages, $last_message, $times, $revisions, $users, $xml_parser;
573 $revisions = array();
575 $group_id = $params['group'];
576 $project = group_get_object($group_id);
577 if (! $project->usesPlugin($this->name)) {
581 if (in_array('scmsvn', $params['show']) || (count($params['show']) < 1)) {
582 $start_time = $params['begin'];
583 $end_time = $params['end'];
585 $xml_parser = xml_parser_create();
586 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
587 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
589 // Grab&parse commit log
590 $protocol = forge_get_config('use_ssl', 'scmsvn') ? 'https://' : 'http://';
591 if ($project->enableAnonSCM()) {
592 $server_script = '/anonscm/svnlog';
594 $u = session_get_user();
595 if ($u && !$u->isError()) {
596 $server_script = '/authscm/'.$u->getUnixName().'/svnlog';
601 $script_url = $protocol . forge_get_config('scm_host')
603 .'?unix_group_name='.$project->getUnixName()
605 .'&begin='.$params['begin']
606 .'&end='.$params['end'];
608 curl_setopt($ch, CURLOPT_URL, $script_url);
609 curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curl2xml');
610 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
611 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
612 curl_setopt($ch, CURLOPT_COOKIE, @$_SERVER['HTTP_COOKIE']); // for session validation
613 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
614 curl_setopt($ch, CURLOPT_HTTPHEADER,
615 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
616 $body = curl_exec($ch);
617 if ($body === false) {
618 $this->setError(curl_error($ch));
623 if (!xml_parse($xml_parser, '', true))
624 $this->setError('Unable to parse XML with error '
625 . xml_error_string(xml_get_error_code($xml_parser))
626 . ' on line ' . xml_get_current_line_number($xml_parser));
627 xml_parser_free($xml_parser);
629 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
631 foreach ($messages as $message) {
633 $result['section'] = 'scm';
634 $result['group_id'] = $group_id;
635 $result['ref_id'] = 'browser.php?group_id='.$group_id;
636 $result['description'] = htmlspecialchars($message).' (r'.$revisions[$i].')';
637 $userObject = user_get_object_by_name($users[$i]);
638 if (is_a($userObject, 'FFUser')) {
639 $result['realname'] = util_display_user($userObject->getUnixName(), $userObject->getID(), $userObject->getRealName());
641 $result['realname'] = '';
643 $result['activity_date'] = $times[$i];
644 $result['subref_id'] = '&commit='.$revisions[$i];
645 $params['results'][] = $result;
650 if (!in_array($this->name, $params['ids'])) {
651 $params['ids'][] = $this->name;
652 $params['texts'][] = _('Subversion Commits');
657 // Get latest commits for inclusion in a widget
658 function getCommits($project, $user = null, $nbCommits) {
659 global $commits, $users, $adds, $updates, $messages, $times, $revisions, $deletes, $time_ok, $user_list, $last_message, $notimecheck, $xml_parser;
666 $revisions = array();
669 $user_list = array();
672 $revisionsArr = array();
673 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
674 $xml_parser = xml_parser_create();
675 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
676 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
678 // Grab&parse commit log
679 $protocol = forge_get_config('use_ssl', 'scmsvn') ? 'https://' : 'http://';
680 $u = session_get_user();
681 if ($project->enableAnonSCM())
682 $server_script = '/anonscm/svnlog';
684 $server_script = '/authscm/'.$u->getUnixName().'/svnlog';
686 $userunixname = $user->getUnixName();
687 $params = '&mode=latest_user&user_name='.$userunixname;
689 $params = '&mode=latest';
691 $script_url = $protocol . forge_get_config('scm_host')
693 .'?unix_group_name='.$project->getUnixName()
695 .'&limit='.$nbCommits;
697 curl_setopt($ch, CURLOPT_URL, $script_url);
698 curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curl2xml');
699 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
700 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
701 curl_setopt($ch, CURLOPT_COOKIE, $_SERVER['HTTP_COOKIE']); // for session validation
702 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
703 curl_setopt($ch, CURLOPT_HTTPHEADER,
704 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
705 $body = curl_exec($ch);
706 if ($body === false) {
707 $this->setError(curl_error($ch));
712 if (!xml_parse($xml_parser, '', true))
713 $this->setError('Unable to parse XML with error '
714 . xml_error_string(xml_get_error_code($xml_parser))
715 . ' on line ' . xml_get_current_line_number($xml_parser));
716 xml_parser_free($xml_parser);
718 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
720 foreach ($messages as $message) {
721 if ($user && ($users[$i] == $userunixname)) {
722 $revisionsArr[$i]['pluginName'] = 'scmsvn';
723 $revisionsArr[$i]['description'] = htmlspecialchars($message);
724 $revisionsArr[$i]['commit_id'] = $revisions[$i];
725 $revisionsArr[$i]['date'] = $times[$i];
727 $revisionsArr[$i]['pluginName'] = 'scmsvn';
728 $revisionsArr[$i]['description'] = htmlspecialchars($message);
729 $revisionsArr[$i]['commit_id'] = $revisions[$i];
730 $revisionsArr[$i]['date'] = $times[$i];
736 return $revisionsArr;
740 // End of class, helper functions now
742 function SVNPluginCharData($parser, $chars) {
743 global $last_tag, $last_user, $last_time, $start_time, $end_time, $usr_commits, $commits,
744 $time_ok, $user_list, $last_message, $messages, $times, $users, $notimecheck;
747 $last_user = preg_replace('/[^a-z0-9_-]/', '', strtolower(trim($chars)));
748 $users[] = $last_user;
749 $usr_commits[$last_user] = isset($usr_commits[$last_user]) ? ($usr_commits[$last_user]+1) : 1 ;
754 $chars = preg_replace('/T(\d\d:\d\d:\d\d)\.\d+Z?$/', ' ${1}', $chars);
755 $last_time = strtotime($chars);
756 if ($start_time <= $last_time && $last_time < $end_time) {
760 if (!isset($notimecheck)) {
761 if ($last_user !== '') // empty in e.g. tags from cvs2svn
762 $usr_commits[$last_user]--;
766 $times[] = $last_time;
770 if ($time_ok === true || isset($notimecheck)) {
771 $messages[count($messages)-1] .= $chars;
773 /* note: there may be more than one msg
774 * (happen when the message contain accents).
781 function SVNPluginStartElement($parser, $name, $attrs) {
782 global $last_user, $last_time, $last_tag, $time_ok, $commits,
783 $adds, $updates, $usr_adds, $usr_updates, $last_message, $messages, $times, $revisions, $deletes, $usr_deletes, $notimecheck;
787 // Make sure we clean up before doing a new log entry
790 $revisions[] = $attrs['REVISION'];
794 if ($time_ok === true || isset($notimecheck)) {
796 if ($attrs['ACTION'] == "M") {
799 $usr_updates[$last_user] = isset($usr_updates[$last_user]) ? ($usr_updates[$last_user]+1) : 1 ;
801 } elseif ($attrs['ACTION'] == "A") {
804 $usr_adds[$last_user] = isset($usr_adds[$last_user]) ? ($usr_adds[$last_user]+1) : 1 ;
806 } elseif ($attrs['ACTION'] == 'D') {
809 $usr_deletes[$last_user] = isset($usr_deletes[$last_user]) ? ($usr_deletes[$last_user]+1) : 1 ;
816 if ($time_ok === true || isset($notimecheck)) {
824 function SVNPluginEndElement($parser, $name) {
829 function curl2xml($ch, $data) {
831 if (!xml_parse($xml_parser, $data, false))
832 exit_error('Unable to parse XML with error '
833 . xml_error_string(xml_get_error_code($xml_parser))
834 . ' on line ' . xml_get_current_line_number($xml_parser),
836 return strlen($data);
841 // c-file-style: "bsd"