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 SVNPlugin() {
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);
134 if (session_loggedin()) {
135 $u = user_get_object(user_getid());
136 $d = $u->getUnixName() ;
137 if (forge_get_config('use_ssh', 'scmsvn')) {
139 $b .= sprintf(_('Developer %s Access via SSH'), 'Subversion');
142 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
144 $b .= _('SSH must be installed on your client machine.');
146 $b .= _('Enter your site password when prompted.');
148 $b .= '<p><tt>svn checkout svn+ssh://'.$d.'@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $project->getUnixName().$module.'</tt></p>' ;
150 if (forge_get_config('use_dav', 'scmsvn')) {
152 $b .= _('Developer Subversion Access via DAV');
155 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
157 $b .= _('Enter your site password when prompted.');
159 $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>' ;
162 if (forge_get_config('use_ssh', 'scmsvn')) {
164 $b .= sprintf(_('Developer %s Access via SSH'), 'Subversion');
167 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
169 $b .= _('SSH must be installed on your client machine.');
171 $b .= _('Substitute <em>developername</em> with the proper value.');
173 $b .= _('Enter your site password when prompted.');
175 $b .= '<p><tt>svn checkout svn+ssh://<i>'._('developername').'</i>@' . forge_get_config('scm_host') . $this->svn_root_fs .'/'. $project->getUnixName().$module.'</tt></p>' ;
177 if (forge_get_config('use_dav', 'scmsvn')) {
179 $b .= _('Developer Subversion Access via DAV');
182 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
184 $b .= _('Substitute <em>developername</em> with the proper value.');
186 $b .= _('Enter your site password when prompted.');
188 $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>' ;
194 function getSnapshotPara($project) {
198 function getBrowserLinkBlock($project) {
199 $b = html_e('h2', array(), _('Subversion Repository Browser'));
201 $b .= sprintf(_("Browsing the %s tree gives you a view into the current status of this project's code."), 'Subversion');
203 $b .= _('You may also view the complete histories of any file in the repository.');
206 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID(),
207 sprintf(_('Browse %s Repository'), 'Subversion')
213 function getStatsBlock($project) {
217 $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',
218 array ($project->getID()));
220 if (db_numrows($result) > 0) {
221 $tableHeaders = array(
226 $b .= $HTML->listTableTop($tableHeaders, false, '', 'repo-history');
229 $total = array('adds' => 0, 'updates' => 0);
231 while($data = db_fetch_array($result)) {
233 $cells[] = array(util_make_link_u($data['user_name'], $data['user_id'], $data['realname']), 'class' => 'halfwidth');
234 $cells[] = array($data['adds'], 'class' => 'onequarterwidth align-right');
235 $cells[] = array($data['updates'], 'class' => 'onequarterwidth align-right');
236 $b .= $HTML->multiTableRow(array('class' => $HTML->boxGetAltRowStyle($i, true)), $cells);
237 $total['adds'] += $data['adds'];
238 $total['updates'] += $data['updates'];
242 $cells[] = array(html_e('strong', array(), _('Total')._(':')), 'class' => 'halfwidth');
243 $cells[] = array($total['adds'], 'class' => 'onequarterwidth align-right');
244 $cells[] = array($total['updates'], 'class' => 'onequarterwidth align-right');
245 $b .= $HTML->multiTableRow(array('class' => $HTML->boxGetAltRowStyle($i, true)), $cells);
246 $b .= $HTML->listTableBottom();
248 $b .= $HTML->information(_('No history yet'));
254 function printBrowserPage($params) {
257 $project = $this->checkParams($params);
262 if ($project->usesPlugin($this->name)) {
263 $iframe_src = '/scm/viewvc.php?root='.$project->getUnixName();
264 if ($params['commit']) {
265 $iframe_src .= '&view=rev&revision='.$params['commit'];
267 htmlIframe($iframe_src, array('id'=>'scmsvn_iframe'));
271 function createOrUpdateRepo($params) {
272 $project = $this->checkParams($params);
273 if (!$project) return false;
274 if (!$project->isActive()) return false;
275 if (!$project->usesPlugin($this->name)) return false;
277 $repo_prefix = forge_get_config('repos_path', 'scmsvn');
278 if (!is_dir($repo_prefix) && !mkdir($repo_prefix, 0755, true)) {
282 $repo = $repo_prefix . '/' . $project->getUnixName();
284 if (!is_dir ($repo) || !is_file ("$repo/format")) {
285 if (!mkdir($repo, 0700, true)) {
289 system ("svnadmin create $repo", $ret);
293 system ("sed -i '/enable-rep-sharing = false/s/^. //' $repo/db/fsfs.conf") ;
294 // dav/ directory is required by old svn clients (eg. svn 1.6.17 on ubuntu 12.04)
295 if (!is_dir ("$repo/dav")) {
298 system ("svn mkdir -m'Init' file:///$repo/trunk file:///$repo/tags file:///$repo/branches >/dev/null") ;
299 system ("find $repo -type d -print0 | xargs -r -0 chmod g+s") ;
300 // Allow read/write users to modify the SVN repository
301 $rw_unix_group = $project->getUnixName() . '_scmrw';
302 system("chgrp -R $rw_unix_group $repo");
303 // Allow read-only users to enter the (top-level) directory
304 $ro_unix_group = $project->getUnixName() . '_scmro';
305 system("chgrp $ro_unix_group $repo");
306 // open permissions to allow switching private/public easily
307 // see after to restrict the top-level directory
308 system ("chmod -R g+rwX,o+rX-w $repo") ;
311 if ($project->enableAnonSCM()) {
312 system("chmod g+rX-w,o+rX-w $repo") ;
314 system("chmod g+rX-w,o-rwx $repo") ;
318 function updateRepositoryList(&$params) {
321 function regenApacheAuth(&$params) {
322 # Enable /authscm/$user/svn URLs
323 $config_fname = forge_get_config('data_path').'/scmsvn-auth.inc';
324 $config_f = fopen($config_fname.'.new', 'w');
326 $res = db_query_params("SELECT login, passwd FROM nss_passwd WHERE status=$1", array('A'));
327 while ($arr = db_fetch_array($res)) {
328 fwrite($config_f, 'Use ScmsvnUser '.$arr['login']."\n");
332 chmod($config_fname.'.new', 0644);
333 rename($config_fname.'.new', $config_fname);
336 function gatherStats($params) {
337 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
338 $adds, $deletes, $updates, $commits, $date_key,
339 $usr_adds, $usr_deletes, $usr_updates, $usr_commits;
343 $project = $this->checkParams($params);
348 if (! $project->usesPlugin($this->name)) {
352 if ($params['mode'] == 'day') {
355 $year = $params['year'];
356 $month = $params['month'];
357 $day = $params['day'];
358 $month_string = sprintf("%04d%02d", $year, $month);
359 $start_time = gmmktime(0, 0, 0, $month, $day, $year);
360 $end_time = $start_time + 86400;
368 $usr_updates = array();
369 $usr_deletes = array();
370 $usr_commits = array();
372 $repo = forge_get_config('repos_path', 'scmsvn') . '/' . $project->getUnixName();
373 if (!is_dir ($repo) || !is_file ("$repo/format")) {
378 $d1 = date('Y-m-d', $start_time - 150000);
379 $d2 = date('Y-m-d', $end_time + 150000);
381 $pipe = popen ("svn log file://$repo --xml -v -q -r '".'{'.$d2.'}:{'.$d1.'}'."' 2> /dev/null", 'r' ) ;
383 // cleaning stats_cvs_* table for the current day
384 $res = db_query_params('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
389 echo "Error while cleaning stats_cvs_group\n" ;
394 $res = db_query_params ('DELETE FROM stats_cvs_user WHERE month=$1 AND day=$2 AND group_id=$3',
395 array ($month_string,
397 $project->getID())) ;
399 echo "Error while cleaning stats_cvs_user\n" ;
404 $xml_parser = xml_parser_create();
405 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
406 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
408 // Analyzing history stream
409 while (!feof($pipe) &&
410 $data = fgets ($pipe, 4096)) {
411 if (!xml_parse ($xml_parser, $data, feof ($pipe))) {
412 $this->setError("Unable to parse XML with error " .
413 xml_error_string(xml_get_error_code($xml_parser)) .
415 xml_get_current_line_number($xml_parser));
422 xml_parser_free($xml_parser);
424 // inserting group results in stats_cvs_groups
425 if ($updates > 0 || $adds > 0 || $deletes > 0 || $commits > 0) {
426 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)',
427 array ($month_string,
435 echo "Error while inserting into stats_cvs_group\n" ;
441 // building the user list
442 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ), array_keys( $usr_deletes ), array_keys( $usr_commits )) );
444 foreach ( $user_list as $user ) {
445 // trying to get user id from user name
446 $u = user_get_object_by_name ($user) ;
448 $user_id = $u->getID();
453 $uc = isset($usr_commits[$user]) ? $usr_commits[$user] : 0 ;
454 $uu = isset($usr_updates[$user]) ? $usr_updates[$user] : 0 ;
455 $ua = isset($usr_adds[$user]) ? $usr_adds[$user] : 0 ;
456 $ud = isset($usr_deletes[$user]) ? $usr_deletes[$user] : 0 ;
457 if ($uu > 0 || $ua > 0 || $uc > 0 || $ud > 0) {
458 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)',
459 array ($month_string,
467 echo "Error while inserting into stats_cvs_user\n" ;
477 function generateSnapshots($params) {
478 $us = forge_get_config('use_scm_snapshots') ;
479 $ut = forge_get_config('use_scm_tarballs') ;
484 $project = $this->checkParams($params);
489 $group_name = $project->getUnixName();
491 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
492 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
494 if (! $project->usesPlugin($this->name)) {
498 if (! $project->enableAnonSCM()) {
499 if (is_file($snapshot)) {
502 if (is_file($tarball)) {
508 $toprepo = forge_get_config('repos_path', 'scmsvn');
509 $repo = $toprepo . '/' . $project->getUnixName();
511 if (!is_dir ($repo) || !is_file ("$repo/format")) {
512 if (is_file($snapshot)) {
515 if (is_file($tarball)) {
521 $tmp = trim (`mktemp -d`) ;
525 $today = date ('Y-m-d') ;
526 $dir = $project->getUnixName ()."-$today" ;
527 system ("mkdir -p $tmp") ;
529 system ("svn ls file://$repo/trunk > /dev/null 2> /dev/null", $code) ;
532 system ("cd $tmp ; svn export file://$repo/trunk $dir > /dev/null 2>&1") ;
533 system ("tar cCf $tmp - $dir |".forge_get_config('compression_method')."> $tmp/snapshot") ;
534 chmod ("$tmp/snapshot", 0644) ;
535 copy ("$tmp/snapshot", $snapshot) ;
536 unlink ("$tmp/snapshot") ;
537 system ("rm -rf $tmp/$dir") ;
539 if (is_file($snapshot)) {
546 system ("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
547 chmod ("$tmp/tarball", 0644) ;
548 copy ("$tmp/tarball", $tarball) ;
549 unlink ("$tmp/tarball") ;
550 system ("rm -rf $tmp") ;
554 function activity($params) {
555 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
556 $adds, $deletes, $updates, $commits, $date_key,
557 $messages, $last_message, $times, $revisions, $users, $xml_parser;
558 $group_id = $params['group'];
559 $project = group_get_object($group_id);
560 if (! $project->usesPlugin($this->name)) {
564 if (in_array('scmsvn', $params['show']) || (count($params['show']) < 1)) {
565 $start_time = $params['begin'];
566 $end_time = $params['end'];
568 $xml_parser = xml_parser_create();
569 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
570 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
572 // Grab&parse commit log
573 $protocol = forge_get_config('use_ssl', 'scmsvn') ? 'https://' : 'http://';
574 if ($project->enableAnonSCM()) {
575 $server_script = '/anonscm/svnlog';
577 $u = session_get_user();
578 if ($u && !$u->isError()) {
579 $server_script = '/authscm/'.$u->getUnixName().'/svnlog';
584 $script_url = $protocol . forge_get_config('scm_host')
586 .'?unix_group_name='.$project->getUnixName()
588 .'&begin='.$params['begin']
589 .'&end='.$params['end'];
591 curl_setopt($ch, CURLOPT_URL, $script_url);
592 curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curl2xml');
593 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
594 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
595 curl_setopt($ch, CURLOPT_COOKIE, @$_SERVER['HTTP_COOKIE']); // for session validation
596 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
597 curl_setopt($ch, CURLOPT_HTTPHEADER,
598 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
599 $body = curl_exec($ch);
600 if ($body === false) {
601 $this->setError(curl_error($ch));
606 if (!xml_parse($xml_parser, '', true))
607 $this->setError('Unable to parse XML with error '
608 . xml_error_string(xml_get_error_code($xml_parser))
609 . ' on line ' . xml_get_current_line_number($xml_parser));
610 xml_parser_free($xml_parser);
612 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
614 foreach ($messages as $message) {
616 $result['section'] = 'scm';
617 $result['group_id'] = $group_id;
618 $result['ref_id'] = 'browser.php?group_id='.$group_id;
619 $result['description'] = htmlspecialchars($message).' (r'.$revisions[$i].')';
620 $userObject = user_get_object_by_name($users[$i]);
621 if (is_a($userObject, 'GFUser')) {
622 $result['realname'] = util_display_user($userObject->getUnixName(), $userObject->getID(), $userObject->getRealName());
624 $result['realname'] = '';
626 $result['activity_date'] = $times[$i];
627 $result['subref_id'] = '&commit='.$revisions[$i];
628 $params['results'][] = $result;
633 $params['ids'][] = $this->name;
634 $params['texts'][] = _('Subversion Commits');
638 // Get latest commits for inclusion in a widget
639 function getCommits($project, $user = null, $nbCommits) {
640 global $commits, $users, $adds, $updates, $messages, $times, $revisions, $deletes, $time_ok, $user_list, $last_message, $notimecheck, $xml_parser;
647 $revisions = array();
650 $user_list = array();
653 $revisionsArr = array();
654 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
655 $xml_parser = xml_parser_create();
656 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
657 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
659 // Grab&parse commit log
660 $protocol = forge_get_config('use_ssl', 'scmsvn') ? 'https://' : 'http://';
661 $u = session_get_user();
662 if ($project->enableAnonSCM())
663 $server_script = '/anonscm/svnlog';
665 $server_script = '/authscm/'.$u->getUnixName().'/svnlog';
667 $userunixname = $user->getUnixName();
668 $params = '&mode=latest_user&user_name='.$userunixname;
670 $params = '&mode=latest';
672 $script_url = $protocol . forge_get_config('scm_host')
674 .'?unix_group_name='.$project->getUnixName()
676 .'&limit='.$nbCommits;
678 curl_setopt($ch, CURLOPT_URL, $script_url);
679 curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'curl2xml');
680 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
681 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
682 curl_setopt($ch, CURLOPT_COOKIE, $_SERVER['HTTP_COOKIE']); // for session validation
683 curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); // for session validation
684 curl_setopt($ch, CURLOPT_HTTPHEADER,
685 array('X-Forwarded-For: '.$_SERVER['REMOTE_ADDR'])); // for session validation
686 $body = curl_exec($ch);
687 if ($body === false) {
688 $this->setError(curl_error($ch));
693 if (!xml_parse($xml_parser, '', true))
694 $this->setError('Unable to parse XML with error '
695 . xml_error_string(xml_get_error_code($xml_parser))
696 . ' on line ' . xml_get_current_line_number($xml_parser));
697 xml_parser_free($xml_parser);
699 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
701 foreach ($messages as $message) {
702 if ($user && ($users[$i] == $userunixname)) {
703 $revisionsArr[$i]['pluginName'] = 'scmsvn';
704 $revisionsArr[$i]['description'] = htmlspecialchars($message);
705 $revisionsArr[$i]['commit_id'] = $revisions[$i];
706 $revisionsArr[$i]['date'] = $times[$i];
708 $revisionsArr[$i]['pluginName'] = 'scmsvn';
709 $revisionsArr[$i]['description'] = htmlspecialchars($message);
710 $revisionsArr[$i]['commit_id'] = $revisions[$i];
711 $revisionsArr[$i]['date'] = $times[$i];
717 return $revisionsArr;
721 // End of class, helper functions now
723 function SVNPluginCharData($parser, $chars) {
724 global $last_tag, $last_user, $last_time, $start_time, $end_time, $usr_commits, $commits,
725 $time_ok, $user_list, $last_message, $messages, $times, $users, $notimecheck;
728 $last_user = preg_replace('/[^a-z0-9_-]/', '', strtolower(trim($chars)));
729 $users[] = $last_user;
730 $usr_commits[$last_user] = isset($usr_commits[$last_user]) ? ($usr_commits[$last_user]+1) : 1 ;
735 $chars = preg_replace('/T(\d\d:\d\d:\d\d)\.\d+Z?$/', ' ${1}', $chars);
736 $last_time = strtotime($chars);
737 if ($start_time <= $last_time && $last_time < $end_time) {
741 if (!isset($notimecheck)) {
742 if ($last_user !== '') // empty in e.g. tags from cvs2svn
743 $usr_commits[$last_user]--;
747 $times[] = $last_time;
751 if ($time_ok === true || isset($notimecheck)) {
752 $messages[count($messages)-1] .= $chars;
754 /* note: there may be more than one msg
755 * (happen when the message contain accents).
762 function SVNPluginStartElement($parser, $name, $attrs) {
763 global $last_user, $last_time, $last_tag, $time_ok, $commits,
764 $adds, $updates, $usr_adds, $usr_updates, $last_message, $messages, $times, $revisions, $deletes, $usr_deletes, $notimecheck;
768 // Make sure we clean up before doing a new log entry
771 $revisions[] = $attrs['REVISION'];
775 if ($time_ok === true || isset($notimecheck)) {
777 if ($attrs['ACTION'] == "M") {
780 $usr_updates[$last_user] = isset($usr_updates[$last_user]) ? ($usr_updates[$last_user]+1) : 1 ;
782 } elseif ($attrs['ACTION'] == "A") {
785 $usr_adds[$last_user] = isset($usr_adds[$last_user]) ? ($usr_adds[$last_user]+1) : 1 ;
787 } elseif ($attrs['ACTION'] == 'D') {
790 $usr_deletes[$last_user] = isset($usr_deletes[$last_user]) ? ($usr_deletes[$last_user]+1) : 1 ;
797 if ($time_ok === true || isset($notimecheck)) {
805 function SVNPluginEndElement($parser, $name) {
810 function curl2xml($ch, $data) {
812 if (!xml_parse($xml_parser, $data, false))
813 exit_error('Unable to parse XML with error '
814 . xml_error_string(xml_get_error_code($xml_parser))
815 . ' on line ' . xml_get_current_line_number($xml_parser),
817 return strlen($data);
822 // c-file-style: "bsd"