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, 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('data_path').'/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 = '/scmrepos/svn';
52 if (!file_exists($this->svn_root_fs.'/.')) {
53 $this->svn_root_fs = forge_get_config('repos_path',
56 $this->svn_root_dav = '/svn';
57 $this->_addHook('scm_browser_page');
58 $this->_addHook('scm_update_repolist');
59 $this->_addHook('scm_generate_snapshots');
60 $this->_addHook('scm_gather_stats');
61 $this->_addHook('activity');
63 $this->provides['svn'] = true;
68 function getDefaultServer() {
69 return forge_get_config('default_server', 'scmsvn') ;
72 function printShortStats($params) {
73 $project = $this->checkParams($params);
78 if ($project->usesPlugin($this->name) && forge_check_perm('scm', $project->getID(), 'read')) {
79 $result = db_query_params('SELECT sum(updates) AS updates, sum(adds) AS adds FROM stats_cvs_group WHERE group_id=$1',
80 array ($project->getID())) ;
81 $commit_num = db_result($result,0,'updates');
82 $add_num = db_result($result,0,'adds');
89 echo ' (Subversion: '.sprintf(_('<strong>%1$s</strong> updates, <strong>%2$s</strong> adds'), number_format($commit_num, 0), number_format($add_num, 0)).")";
95 . sprintf(_('Documentation for %1$s is available at <a href="%2$s">%2$s</a>.'),
97 'http://svnbook.red-bean.com/')
101 function topModule($project) {
102 // Check toplevel module presence
103 $repo = 'file://' . forge_get_config('repos_path', $this->name).'/'.$project->getUnixName().'/';
106 if (!(exec("svn ls '$repo'", $res) && in_array($module.'/', $res))) {
113 function getInstructionsForAnon($project) {
114 $b = '<h2>' . _('Anonymous Subversion Access') . '</h2>';
116 $b .= _("This project's SVN repository can be checked out through anonymous access with the following command(s).");
120 $module = $this->topModule($project);
121 if (forge_get_config('use_ssh', 'scmsvn')) {
122 $b .= '<tt>svn checkout svn://'.$this->getBoxForProject($project).$this->svn_root_fs.'/'.$project->getUnixName().$module.'</tt><br />';
124 if (forge_get_config('use_dav', 'scmsvn')) {
125 $b .= '<tt>svn checkout --username '.forge_get_config('anonsvn_login', 'scmsvn').' http'.((forge_get_config('use_ssl', 'scmsvn')) ? 's' : '').'://' . $this->getBoxForProject($project). $this->svn_root_dav .'/'. $project->getUnixName() .$module.'</tt><br />';
126 $b .= _('The password is ').forge_get_config('anonsvn_password', 'scmsvn').'<br />';
132 function getInstructionsForRW($project) {
135 $module = $this->topModule($project);
137 if (session_loggedin()) {
138 $u = user_get_object(user_getid());
139 $d = $u->getUnixName() ;
140 if (forge_get_config('use_ssh', 'scmsvn')) {
142 $b .= sprintf(_('Developer %s Access via SSH'), 'Subversion');
145 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
147 $b .= _('SSH must be installed on your client machine.');
149 $b .= _('Enter your site password when prompted.');
151 $b .= '<p><tt>svn checkout svn+ssh://'.$d.'@' . $this->getBoxForProject($project) . $this->svn_root_fs .'/'. $project->getUnixName().$module.'</tt></p>' ;
153 if (forge_get_config('use_dav', 'scmsvn')) {
155 $b .= _('Developer Subversion Access via DAV');
158 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
160 $b .= _('Enter your site password when prompted.');
162 $b .= '<p><tt>svn checkout --username '.$d.' http'.((forge_get_config('use_ssl', 'scmsvn')) ? 's' : '').'://'. $this->getBoxForProject($project) . $this->svn_root_dav .'/'.$project->getUnixName().$module.'</tt></p>' ;
165 if (forge_get_config('use_ssh', 'scmsvn')) {
167 $b .= sprintf(_('Developer %s Access via SSH'), 'Subversion');
170 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
172 $b .= _('SSH must be installed on your client machine.');
174 $b .= _('Substitute <em>developername</em> with the proper value.');
176 $b .= _('Enter your site password when prompted.');
178 $b .= '<p><tt>svn checkout svn+ssh://<i>'._('developername').'</i>@' . $this->getBoxForProject($project) . $this->svn_root_fs .'/'. $project->getUnixName().$module.'</tt></p>' ;
180 if (forge_get_config('use_dav', 'scmsvn')) {
182 $b .= _('Developer Subversion Access via DAV');
185 $b .= sprintf(_('Only project developers can access the %s tree via this method.'), 'Subversion');
187 $b .= _('Substitute <em>developername</em> with the proper value.');
189 $b .= _('Enter your site password when prompted.');
191 $b .= '<p><tt>svn checkout --username <i>'._('developername').'</i> http'.((forge_get_config('use_ssl', 'scmsvn')) ? 's' : '').'://'. $this->getBoxForProject($project) . $this->svn_root_dav .'/'.$project->getUnixName().$module.'</tt></p>' ;
197 function getSnapshotPara($project) {
201 function getBrowserLinkBlock($project) {
203 $b = $HTML->boxMiddle(sprintf(_('%s Repository Browser'), 'Subversion'));
205 $b .= sprintf(_("Browsing the %s tree gives you a view into the current status of this project's code."), 'Subversion');
207 $b .= _('You may also view the complete histories of any file in the repository.');
210 $b .= util_make_link ("/scm/browser.php?group_id=".$project->getID(),
211 sprintf(_('Browse %s Repository'), 'Subversion')
217 function getStatsBlock($project) {
221 $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',
222 array ($project->getID()));
224 if (db_numrows($result) > 0) {
225 $tableHeaders = array(
230 $b .= $HTML->listTableTop($tableHeaders, false, '', 'repo-history');
233 $total = array('adds' => 0, 'updates' => 0);
235 while($data = db_fetch_array($result)) {
237 $cells[] = array(util_make_link_u($data['user_name'], $data['user_id'], $data['realname']), 'class' => 'halfwidth');
238 $cells[] = array($data['adds'], 'class' => 'onequarterwidth align-right');
239 $cells[] = array($data['updates'], 'class' => 'onequarterwidth align-right');
240 $b .= $HTML->multiTableRow(array('class' => $HTML->boxGetAltRowStyle($i, true)), $cells);
241 $total['adds'] += $data['adds'];
242 $total['updates'] += $data['updates'];
246 $cells[] = array(html_e('strong', array(), _('Total')._(':')), 'class' => 'halfwidth');
247 $cells[] = array($total['adds'], 'class' => 'onequarterwidth align-right');
248 $cells[] = array($total['updates'], 'class' => 'onequarterwidth align-right');
249 $b .= $HTML->multiTableRow(array('class' => $HTML->boxGetAltRowStyle($i, true)), $cells);
250 $b .= $HTML->listTableBottom();
256 function printBrowserPage($params) {
259 $project = $this->checkParams($params);
264 if ($project->usesPlugin($this->name)) {
265 $iframe_src = '/scm/viewvc.php?inframe=1&root='.$project->getUnixName();
266 if ($params['commit']) {
267 $iframe_src .= '&view=rev&revision='.$params['commit'];
269 htmlIframe($iframe_src, array('id'=>'scmsvn_iframe'));
273 function createOrUpdateRepo($params) {
274 $project = $this->checkParams($params);
279 if (! $project->usesPlugin($this->name)) {
283 $repo_prefix = forge_get_config('repos_path', 'scmsvn');
284 if (!is_dir($repo_prefix) && !mkdir($repo_prefix, 0755, true)) {
288 $repo = $repo_prefix . '/' . $project->getUnixName();
290 if (!is_dir ($repo) || !is_file ("$repo/format")) {
291 if (!mkdir($repo, 0700, true)) {
295 system ("svnadmin create $repo", $ret);
299 system ("sed -i '/enable-rep-sharing = false/s/^. //' $repo/db/fsfs.conf") ;
300 system ("svn mkdir -m'Init' file:///$repo/trunk file:///$repo/tags file:///$repo/branches >/dev/null") ;
301 if (forge_get_config('use_ssh', 'scmsvn')) {
302 $unix_group = 'scm_' . $project->getUnixName() ;
303 system ("find $repo -type d | xargs -I{} chmod g+s {}") ;
304 // open permissions to allow switching private/public easily
305 // see after to restrict the top-level directory
306 system ("chmod -R g+rwX,o+rX-w $repo") ;
307 system ("chgrp -R $unix_group $repo") ;
309 $unix_user = forge_get_config('apache_user');
310 $unix_group = forge_get_config('apache_group');
311 system ("chmod -R g-rwx,o-rwx $repo") ;
312 system ("chown -R $unix_user:$unix_group $repo") ;
316 if (forge_get_config('use_ssh', 'scmsvn')) {
317 $unix_group = 'scm_' . $project->getUnixName();
318 system("find $repo -type d | xargs -I{} chmod g+s {}");
319 if (forge_get_config('use_dav', 'scmsvn')) {
320 $unix_user = forge_get_config('apache_user');
321 system("chown $unix_user:$unix_group $repo");
323 system("chgrp $unix_group $repo");
325 if ($project->enableAnonSCM()) {
326 system("chmod g+rwX,o+rX-w $repo") ;
328 system("chmod g+rwX,o-rwx $repo") ;
331 $unix_user = forge_get_config('apache_user');
332 $unix_group = forge_get_config('apache_group');
333 system("chown $unix_user:$unix_group $repo") ;
334 system("chmod g-rwx,o-rwx $repo") ;
338 function updateRepositoryList(&$params) {
339 $groups = $this->getGroups();
341 // Update WebDAV stuff
342 if (!forge_get_config('use_dav', 'scmsvn')) {
348 $engine = RBACEngine::getInstance() ;
351 foreach ($groups as $project) {
352 if ( !$project->isActive()) {
355 if ( !$project->usesSCM()) {
358 $access_data .= '[' . $project->getUnixName() . ":/]\n";
360 $users = $engine->getUsersByAllowedAction('scm',$project->getID(),'read');
361 if ($users === false) {
362 $params['output'] .= $engine->getErrorMessage();
365 foreach ($users as $user) {
366 $svnusers[$user->getID()] = $user;
367 if (forge_check_perm_for_user($user,
371 $access_data .= $user->getUnixName() . "= rw\n";
373 $access_data .= $user->getUnixName() . "= r\n";
377 if ($project->enableAnonSCM()) {
378 $anonRole = RoleAnonymous::getInstance();
379 if ($anonRole->hasPermission('scm', $project->getID(), 'write')) {
380 $access_data .= forge_get_config('anonsvn_login', 'scmsvn')." = rw\n";
382 $access_data .= forge_get_config('anonsvn_login', 'scmsvn')." = r\n";
386 $access_data .= "\n";
387 $engine->invalidateRoleCaches(); // caching all roles takes ~1GB RAM for 5K projects/15K users
390 foreach ($svnusers as $user_id => $user) {
391 $password_data .= $user->getUnixName().':'.$user->getUnixPasswd()."\n";
393 $password_data .= forge_get_config('anonsvn_login', 'scmsvn').":".htpasswd_apr1_md5(forge_get_config('anonsvn_password', 'scmsvn'))."\n";
395 $fname = forge_get_config('data_path').'/svnroot-authfile';
396 $f = fopen($fname.'.new', 'w');
397 fwrite($f, $password_data);
399 chmod($fname.'.new', 0644);
400 rename($fname.'.new', $fname);
402 $fname = forge_get_config('data_path').'/svnroot-access';
403 $f = fopen($fname.'.new', 'w');
404 fwrite($f, $access_data);
406 chmod($fname.'.new', 0644);
407 rename($fname.'.new', $fname);
410 function gatherStats($params) {
411 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
412 $adds, $deletes, $updates, $commits, $date_key,
413 $usr_adds, $usr_deletes, $usr_updates, $usr_commits;
417 $project = $this->checkParams($params);
422 if (! $project->usesPlugin($this->name)) {
426 if ($params['mode'] == 'day') {
429 $year = $params['year'];
430 $month = $params['month'];
431 $day = $params['day'];
432 $month_string = sprintf("%04d%02d", $year, $month);
433 $start_time = gmmktime(0, 0, 0, $month, $day, $year);
434 $end_time = $start_time + 86400;
442 $usr_updates = array();
443 $usr_deletes = array();
444 $usr_commits = array();
446 $repo = forge_get_config('repos_path', 'scmsvn') . '/' . $project->getUnixName();
447 if (!is_dir ($repo) || !is_file ("$repo/format")) {
452 $d1 = date('Y-m-d', $start_time - 150000);
453 $d2 = date('Y-m-d', $end_time + 150000);
455 $pipe = popen ("svn log file://$repo --xml -v -q -r '".'{'.$d2.'}:{'.$d1.'}'."' 2> /dev/null", 'r' ) ;
457 // cleaning stats_cvs_* table for the current day
458 $res = db_query_params('DELETE FROM stats_cvs_group WHERE month=$1 AND day=$2 AND group_id=$3',
463 echo "Error while cleaning stats_cvs_group\n" ;
468 $res = db_query_params ('DELETE FROM stats_cvs_user WHERE month=$1 AND day=$2 AND group_id=$3',
469 array ($month_string,
471 $project->getID())) ;
473 echo "Error while cleaning stats_cvs_user\n" ;
478 $xml_parser = xml_parser_create();
479 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
480 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
482 // Analyzing history stream
483 while (!feof($pipe) &&
484 $data = fgets ($pipe, 4096)) {
485 if (!xml_parse ($xml_parser, $data, feof ($pipe))) {
486 $this->setError("Unable to parse XML with error " .
487 xml_error_string(xml_get_error_code($xml_parser)) .
489 xml_get_current_line_number($xml_parser));
496 xml_parser_free($xml_parser);
498 // inserting group results in stats_cvs_groups
499 if ($updates > 0 || $adds > 0 || $deletes > 0 || $commits > 0) {
500 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)',
501 array ($month_string,
509 echo "Error while inserting into stats_cvs_group\n" ;
515 // building the user list
516 $user_list = array_unique( array_merge( array_keys( $usr_adds ), array_keys( $usr_updates ), array_keys( $usr_deletes ), array_keys( $usr_commits )) );
518 foreach ( $user_list as $user ) {
519 // trying to get user id from user name
520 $u = user_get_object_by_name ($user) ;
522 $user_id = $u->getID();
527 $uc = isset($usr_commits[$user]) ? $usr_commits[$user] : 0 ;
528 $uu = isset($usr_updates[$user]) ? $usr_updates[$user] : 0 ;
529 $ua = isset($usr_adds[$user]) ? $usr_adds[$user] : 0 ;
530 $ud = isset($usr_deletes[$user]) ? $usr_deletes[$user] : 0 ;
531 if ($uu > 0 || $ua > 0 || $uc > 0 || $ud > 0) {
532 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)',
533 array ($month_string,
541 echo "Error while inserting into stats_cvs_user\n" ;
551 function generateSnapshots($params) {
552 $us = forge_get_config('use_scm_snapshots') ;
553 $ut = forge_get_config('use_scm_tarballs') ;
558 $project = $this->checkParams($params);
563 $group_name = $project->getUnixName();
565 $snapshot = forge_get_config('scm_snapshots_path').'/'.$group_name.'-scm-latest.tar'.util_get_compressed_file_extension();
566 $tarball = forge_get_config('scm_tarballs_path').'/'.$group_name.'-scmroot.tar'.util_get_compressed_file_extension();
568 if (! $project->usesPlugin($this->name)) {
572 if (! $project->enableAnonSCM()) {
573 if (is_file($snapshot)) {
576 if (is_file($tarball)) {
582 $toprepo = forge_get_config('repos_path', 'scmsvn');
583 $repo = $toprepo . '/' . $project->getUnixName();
585 if (!is_dir ($repo) || !is_file ("$repo/format")) {
586 if (is_file($snapshot)) {
589 if (is_file($tarball)) {
595 $tmp = trim (`mktemp -d`) ;
599 $today = date ('Y-m-d') ;
600 $dir = $project->getUnixName ()."-$today" ;
601 system ("mkdir -p $tmp") ;
603 system ("svn ls file://$repo/trunk > /dev/null 2> /dev/null", $code) ;
606 system ("cd $tmp ; svn export file://$repo/trunk $dir > /dev/null 2>&1") ;
607 system ("tar cCf $tmp - $dir |".forge_get_config('compression_method')."> $tmp/snapshot") ;
608 chmod ("$tmp/snapshot", 0644) ;
609 copy ("$tmp/snapshot", $snapshot) ;
610 unlink ("$tmp/snapshot") ;
611 system ("rm -rf $tmp/$dir") ;
613 if (is_file($snapshot)) {
620 system ("tar cCf $toprepo - ".$project->getUnixName() ."|".forge_get_config('compression_method')."> $tmp/tarball") ;
621 chmod ("$tmp/tarball", 0644) ;
622 copy ("$tmp/tarball", $tarball) ;
623 unlink ("$tmp/tarball") ;
624 system ("rm -rf $tmp") ;
628 function activity($params) {
629 global $last_user, $last_time, $last_tag, $time_ok, $start_time, $end_time,
630 $adds, $deletes, $updates, $commits, $date_key,
631 $messages, $last_message, $times, $revisions, $users;
632 $group_id = $params['group'];
633 $project = group_get_object($group_id);
634 if (! $project->usesPlugin($this->name)) {
638 if (in_array('scmsvn', $params['show']) || (count($params['show']) < 1)) {
639 $repo = forge_get_config('repos_path', 'scmsvn') . '/' . $project->getUnixName();
641 $start_time = $params['begin'];
642 $end_time = $params['end'];
643 $d1 = date('Y-m-d', $start_time - 80000);
644 $d2 = date('Y-m-d', $end_time + 80000);
645 $pipe = popen("svn log file://$repo --xml -v -r '".'{'.$d2.'}:{'.$d1.'}'."' 2> /dev/null", 'r' );
646 $xml_parser = xml_parser_create();
647 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
648 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
649 while (!feof($pipe) && $data = fgets($pipe, 4096)) {
650 if (!xml_parse($xml_parser, $data, feof ($pipe))) {
651 debug("Unable to parse XML with error " .
652 xml_error_string(xml_get_error_code($xml_parser)) .
654 xml_get_current_line_number($xml_parser));
659 xml_parser_free($xml_parser);
660 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
662 foreach ($messages as $message) {
664 $result['section'] = 'scm';
665 $result['group_id'] = $group_id;
666 $result['ref_id'] = 'browser.php?group_id='.$group_id;
667 $result['description'] = htmlspecialchars($message).' (r'.$revisions[$i].')';
668 $userObject = user_get_object_by_name($users[$i]);
669 if (is_a($userObject, 'GFUser')) {
670 $result['realname'] = util_display_user($userObject->getUnixName(), $userObject->getID(), $userObject->getRealName());
672 $result['realname'] = '';
674 $result['activity_date'] = $times[$i];
675 $result['subref_id'] = '&commit='.$revisions[$i];
676 $params['results'][] = $result;
682 $params['ids'][] = $this->name;
683 $params['texts'][] = _('Subversion Commits');
687 function getCommits($project, $user = null, $nbCommits) {
688 global $commits, $users, $adds, $updates, $messages, $times, $revisions, $deletes, $time_ok, $user_list, $last_message, $notimecheck;
695 $revisions = array();
698 $user_list = array();
701 $revisionsArr = array();
702 if ($project->usesPlugin($this->name) && forge_get_config('use_dav', $this->name) && forge_check_perm($this->name, $project->getID(), 'read')) {
703 $repo = forge_get_config('repos_path', $this->name) . '/' . $project->getUnixName();
705 $userunixname = $user->getUnixName();
706 $pipecmd = "svn log file://$repo --xml -v --limit $nbCommits --search \"$userunixname\" 2> /dev/null";
708 $pipecmd = "svn log file://$repo --xml -v --limit $nbCommits 2> /dev/null";
711 $pipe = popen($pipecmd, 'r' );
712 $xml_parser = xml_parser_create();
713 xml_set_element_handler($xml_parser, "SVNPluginStartElement", "SVNPluginEndElement");
714 xml_set_character_data_handler($xml_parser, "SVNPluginCharData");
715 while (!feof($pipe) && $data = fgets($pipe, 4096)) {
716 if (!xml_parse($xml_parser, $data, feof($pipe))) {
717 $this->setError("Unable to parse XML with error " .
718 xml_error_string(xml_get_error_code($xml_parser)) .
720 xml_get_current_line_number($xml_parser));
727 xml_parser_free($xml_parser);
730 if ($adds > 0 || $updates > 0 || $commits > 0 || $deletes > 0) {
732 foreach ($messages as $message) {
733 if ($user && ($users[$i] == $userunixname)) {
734 $revisionsArr[$i]['pluginName'] = 'scmsvn';
735 $revisionsArr[$i]['description'] = htmlspecialchars($message);
736 $revisionsArr[$i]['commit_id'] = $revisions[$i];
737 $revisionsArr[$i]['date'] = $times[$i];
739 $revisionsArr[$i]['pluginName'] = 'scmsvn';
740 $revisionsArr[$i]['description'] = htmlspecialchars($message);
741 $revisionsArr[$i]['commit_id'] = $revisions[$i];
742 $revisionsArr[$i]['date'] = $times[$i];
748 return $revisionsArr;
752 // End of class, helper functions now
754 function SVNPluginCharData($parser, $chars) {
755 global $last_tag, $last_user, $last_time, $start_time, $end_time, $usr_commits, $commits,
756 $time_ok, $user_list, $last_message, $messages, $times, $users, $notimecheck;
759 $last_user = preg_replace('/[^a-z0-9_-]/', '', strtolower(trim($chars)));
760 $users[] = $last_user;
761 $usr_commits[$last_user] = isset($usr_commits[$last_user]) ? ($usr_commits[$last_user]+1) : 1 ;
766 $chars = preg_replace('/T(\d\d:\d\d:\d\d)\.\d+Z?$/', ' ${1}', $chars);
767 $last_time = strtotime($chars);
768 if ($start_time <= $last_time && $last_time < $end_time) {
772 if (!isset($notimecheck)) {
773 if ($last_user !== '') // empty in e.g. tags from cvs2svn
774 $usr_commits[$last_user]--;
778 $times[] = $last_time;
782 if ($time_ok === true || isset($notimecheck)) {
783 $messages[count($messages)-1] .= $chars;
785 /* note: there may be more than one msg
786 * (happen when the message contain accents).
793 function SVNPluginStartElement($parser, $name, $attrs) {
794 global $last_user, $last_time, $last_tag, $time_ok, $commits,
795 $adds, $updates, $usr_adds, $usr_updates, $last_message, $messages, $times, $revisions, $deletes, $usr_deletes, $notimecheck;
799 // Make sure we clean up before doing a new log entry
802 $revisions[] = $attrs['REVISION'];
806 if ($time_ok === true || isset($notimecheck)) {
808 if ($attrs['ACTION'] == "M") {
811 $usr_updates[$last_user] = isset($usr_updates[$last_user]) ? ($usr_updates[$last_user]+1) : 1 ;
813 } elseif ($attrs['ACTION'] == "A") {
816 $usr_adds[$last_user] = isset($usr_adds[$last_user]) ? ($usr_adds[$last_user]+1) : 1 ;
818 } elseif ($attrs['ACTION'] == 'D') {
821 $usr_deletes[$last_user] = isset($usr_deletes[$last_user]) ? ($usr_deletes[$last_user]+1) : 1 ;
828 if ($time_ok === true || isset($notimecheck)) {
836 function SVNPluginEndElement($parser, $name) {
843 // c-file-style: "bsd"