/SourceSVN/SourceSVN.php
PHP | 461 lines | 341 code | 89 blank | 31 comment | 83 complexity | 31113d6bf5ceaac44945b91ccf703b5d MD5 | raw file
- <?php
- # Copyright (c) 2010 John Reese
- # Licensed under the MIT license
- if ( false === include_once( config_get( 'plugin_path' ) . 'Source/MantisSourcePlugin.class.php' ) ) {
- return;
- }
- class SourceSVNPlugin extends MantisSourcePlugin {
- public function register() {
- $this->name = lang_get( 'plugin_SourceSVN_title' );
- $this->description = lang_get( 'plugin_SourceSVN_description' );
- $this->version = '0.16';
- $this->requires = array(
- 'MantisCore' => '1.2.0',
- 'Source' => '0.16',
- );
- $this->author = 'John Reese';
- $this->contact = 'jreese@leetcode.net';
- $this->url = 'http://leetcode.net';
- }
- public function config() {
- return array(
- 'svnpath' => '',
- 'svnargs' => '',
- 'svnssl' => false,
- 'winstart' => false,
- );
- }
- public function errors() {
- return array(
- 'SVNPathInvalid' => 'Path to Subversion binary invalid or inaccessible',
- );
- }
- public $type = 'svn';
- public $configuration = true;
- public function show_type() {
- return lang_get( 'plugin_SourceSVN_svn' );
- }
- public function show_changeset( $p_repo, $p_changeset ) {
- return "$p_changeset->branch r$p_changeset->revision";
- }
- public function show_file( $p_repo, $p_changeset, $p_file ) {
- return $p_file->action . ' - ' . $p_file->filename;
- }
- public function url_repo( $p_repo, $p_changeset=null ) {}
- public function url_changeset( $p_repo, $p_changeset ) {}
- public function url_file( $p_repo, $p_changeset, $p_file ) {}
- public function url_diff( $p_repo, $p_changeset, $p_file ) {}
- public function update_repo_form( $p_repo ) {
- $t_svn_username = isset( $p_repo->info['svn_username'] ) ? $p_repo->info['svn_username'] : '';
- $t_svn_password = isset( $p_repo->info['svn_password'] ) ? $p_repo->info['svn_password'] : '';
- $t_standard_repo = isset( $p_repo->info['standard_repo'] ) ? $p_repo->info['standard_repo'] : '';
- $t_trunk_path = isset( $p_repo->info['trunk_path'] ) ? $p_repo->info['trunk_path'] : '';
- $t_branch_path = isset( $p_repo->info['branch_path'] ) ? $p_repo->info['branch_path'] : '';
- $t_tag_path = isset( $p_repo->info['tag_path'] ) ? $p_repo->info['tag_path'] : '';
- $t_ignore_paths = isset( $p_repo->info['ignore_paths'] ) ? $p_repo->info['ignore_paths'] : '';
- ?>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_svn_username' ) ?></td>
- <td><input name="svn_username" maxlength="250" size="40" value="<?php echo string_attribute( $t_svn_username ) ?>"/></td>
- </tr>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_svn_password' ) ?></td>
- <td><input name="svn_password" maxlength="250" size="40" value="<?php echo string_attribute( $t_svn_password ) ?>"/></td>
- </tr>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_standard_repo' ) ?></td>
- <td><input name="standard_repo" type="checkbox" <?php echo ($t_standard_repo ? 'checked="checked"' : '') ?>/></td>
- </tr>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_trunk_path' ) ?></td>
- <td><input name="trunk_path" maxlength="250" size="40" value="<?php echo string_attribute( $t_trunk_path ) ?>"/></td>
- </tr>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_branch_path' ) ?></td>
- <td><input name="branch_path" maxlength="250" size="40" value="<?php echo string_attribute( $t_branch_path ) ?>"/></td>
- </tr>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_tag_path' ) ?></td>
- <td><input name="tag_path" maxlength="250" size="40" value="<?php echo string_attribute( $t_tag_path ) ?>"/></td>
- </tr>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_ignore_paths' ) ?></td>
- <td><input name="ignore_paths" type="checkbox" <?php echo ($t_ignore_paths ? 'checked="checked"' : '') ?>/></td>
- </tr>
- <?php
- }
- public function update_repo( $p_repo ) {
- $p_repo->info['svn_username'] = gpc_get_string( 'svn_username' );
- $p_repo->info['svn_password'] = gpc_get_string( 'svn_password' );
- $p_repo->info['standard_repo'] = gpc_get_bool( 'standard_repo', false );
- $p_repo->info['trunk_path'] = gpc_get_string( 'trunk_path' );
- $p_repo->info['branch_path'] = gpc_get_string( 'branch_path' );
- $p_repo->info['tag_path'] = gpc_get_string( 'tag_path' );
- $p_repo->info['ignore_paths'] = gpc_get_bool( 'ignore_paths', false );
- return $p_repo;
- }
- private static $config_form_handled = false;
- public function update_config_form() {
- # Prevent more than one SVN class from outputting form elements.
- if ( !SourceSVNPlugin::$config_form_handled ) {
- SourceSVNPlugin::$config_form_handled = true;
- $t_svnpath = config_get( 'plugin_SourceSVN_svnpath', '' );
- $t_svnargs = config_get( 'plugin_SourceSVN_svnargs', '' );
- $t_svnssl = config_get( 'plugin_SourceSVN_svnssl', '' );
- $t_winstart = config_get( 'plugin_SourceSVN_winstart', '' );
- ?>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_svnpath' ) ?></td>
- <td><input name="plugin_SourceSVN_svnpath" value="<?php echo string_attribute( $t_svnpath ) ?>" size="40"/></td>
- </tr>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_svnargs' ) ?></td>
- <td><input name="plugin_SourceSVN_svnargs" value="<?php echo string_attribute( $t_svnargs ) ?>" size="40"/></td>
- </tr>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_svnssl' ) ?></td>
- <td><input name="plugin_SourceSVN_svnssl" type="checkbox" <?php if ( $t_svnssl ) echo 'checked="checked"' ?>/></td>
- </tr>
- <tr <?php echo helper_alternate_class() ?>>
- <td class="category"><?php echo lang_get( 'plugin_SourceSVN_winstart' ) ?></td>
- <td><input name="plugin_SourceSVN_winstart" type="checkbox" <?php if ( $t_winstart ) echo 'checked="checked"' ?>/></td>
- </tr>
- <?php
- }
- }
- public function update_config() {
- # Prevent more than one SVN class from handling form elements.
- if ( !SourceSVNPlugin::$config_form_handled ) {
- SourceSVNPlugin::$config_form_handled = true;
- $f_svnpath = gpc_get_string( 'plugin_SourceSVN_svnpath', '' );
- $t_svnpath = config_get( 'plugin_SourceSVN_svnpath', '' );
- $f_svnpath = rtrim( $f_svnpath, DIRECTORY_SEPARATOR );
- if ( $f_svnpath != $t_svnpath ) {
- if ( is_blank( $f_svnpath ) ) {
- config_delete( 'plugin_SourceSVN_svnpath' );
- } else {
- # be sure that the path is valid
- if ( ( $t_binary = SourceSVNPlugin::svn_binary( $f_svnpath, true ) ) != 'svn' ) {
- config_set( 'plugin_SourceSVN_svnpath', $f_svnpath );
- } else {
- plugin_error( 'SVNPathInvalid', ERROR );
- }
- }
- }
- $f_svnargs = gpc_get_string( 'plugin_SourceSVN_svnargs', '' );
- if ( $f_svnargs != config_get( 'plugin_SourceSVN_svnargs', '' ) ) {
- config_set( 'plugin_SourceSVN_svnargs', $f_svnargs );
- }
- $f_svnssl = gpc_get_bool( 'plugin_SourceSVN_svnssl', false );
- if ( $f_svnssl != config_get( 'plugin_SourceSVN_svnssl', false ) ) {
- config_set( 'plugin_SourceSVN_svnssl', $f_svnssl );
- }
- $f_winstart = gpc_get_bool( 'plugin_SourceSVN_winstart', false );
- if ( $f_winstart != config_get( 'plugin_SourceSVN_winstart', false ) ) {
- config_set( 'plugin_SourceSVN_winstart', $f_winstart );
- }
- }
- }
- public function commit( $p_repo, $p_data ) {
- if ( preg_match( '/(\d+)/', $p_data, $p_matches ) ) {
- $svn = $this->svn_call( $p_repo );
- $t_url = $p_repo->url;
- $t_revision = $p_matches[1];
- $t_svnlog = explode( "\n", shell_exec( "$svn log -v $t_url -r$t_revision" ) );
- if ( SourceChangeset::exists( $p_repo->id, $t_revision ) ) {
- echo "Revision $t_revision already committed!\n";
- return null;
- }
- return $this->process_svn_log( $p_repo, $t_svnlog );
- }
- }
- public function import_full( $p_repo ) {
- $this->check_svn();
- $svn = $this->svn_call( $p_repo );
- $t_changeset_table = plugin_table( 'changeset', 'Source' );
- $t_max_query = "SELECT revision FROM $t_changeset_table
- WHERE repo_id=" . db_param() . '
- ORDER BY CAST( revision AS DECIMAL ) DESC';
- $t_db_revision = db_result( db_query_bound( $t_max_query, array( $p_repo->id ), 1 ) );
- $t_url = $p_repo->url;
- $t_rev = ( false === $t_db_revision ? 0 : $t_db_revision + 1 );
- echo "<pre>";
- while( true ) {
- echo "Requesting svn log for {$p_repo->name} starting with revision {$t_rev}...\n";
- $t_svnlog = explode( "\n", shell_exec( "$svn log -v -r $t_rev:HEAD --limit 200 $t_url" ) );
- $t_changesets = $this->process_svn_log( $p_repo, $t_svnlog );
- # if an array is returned, processing is done
- if ( is_array( $t_changesets ) ) {
- echo "</pre>";
- return $t_changesets;
- # if a number is returned, repeat from given revision
- } else if ( is_numeric( $t_changesets ) ) {
- $t_rev = $t_changesets + 1;
- }
- }
- echo "</pre>";
- }
- public function import_latest( $p_repo ) {
- return $this->import_full( $p_repo );
- }
- private function check_svn() {
- $svn = self::svn_binary();
- if ( is_blank( shell_exec( "$svn help" ) ) ) {
- trigger_error( ERROR_GENERIC, ERROR );
- }
- }
- private function svn_call( $p_repo=null ) {
- static $s_call;
- # Get a full binary call, including configured parameters
- if ( is_null( $s_call ) ) {
- $s_call = self::svn_binary() . ' --non-interactive';
- if ( config_get( 'plugin_SourceSVN_svnssl', false ) ) {
- $s_call .= ' --trust-server-cert';
- }
- $t_svnargs = config_get( 'plugin_SourceSVN_svnargs', '' );
- if ( !is_blank( $t_svnargs ) ) {
- $s_call .= " $t_svnargs";
- }
- }
- # If not given a repo, just return the base SVN binary
- if ( is_null( $p_repo ) ) {
- return $s_call;
- }
- $t_call = $s_call;
- # With a repo, add arguments for repo info
- $t_username = escapeshellarg($p_repo->info['svn_username']);
- $t_password = escapeshellarg($p_repo->info['svn_password']);
- if ( !is_blank( $t_username ) ) {
- $t_call .= ' --username ' . $t_username;
- }
- if ( !is_blank( $t_password ) ) {
- $t_call .= ' --password ' . $t_password;
- }
- # Done
- return $t_call;
- }
- /**
- * Generate, validate, and cache the SVN binary path.
- * @param string Path to SVN
- * @param boolean Reset cached value
- * @return string SVN binary
- */
- public static function svn_binary( $p_path=null, $p_reset=false ) {
- static $s_binary;
- if ( is_null( $s_binary ) || $p_reset ) {
- if ( is_null( $p_path ) ) {
- $t_path = config_get( 'plugin_SourceSVN_svnpath', '' );
- } else {
- $t_path = $p_path;
- }
- if ( !is_blank( $t_path ) && is_dir( $t_path ) ) {
- # Linux / UNIX paths
- $t_binary = $t_path . DIRECTORY_SEPARATOR . 'svn';
- if ( is_file( $t_binary ) && is_executable( $t_binary ) ) {
- return $s_binary = $t_binary;
- }
- # Windows paths
- $t_binary = $t_path . DIRECTORY_SEPARATOR . 'svn.exe';
- if ( is_file( $t_binary ) && is_executable( $t_binary ) ) {
- if ( config_get( 'plugin_SourceSVN_winstart', '' ) ) {
- return "start /B /D \"{$t_path}\" svn.exe";
- } else {
- return $s_binary = $t_binary;
- }
- }
- }
- # Generic pathless call
- $s_binary = 'svn';
- }
- return $s_binary;
- }
- private function process_svn_log( $p_repo, $p_svnlog ) {
- $t_state = 0;
- $t_svnline = str_pad( '', 72, '-' );
- $t_changesets = array();
- $t_changeset = null;
- $t_comments = '';
- $t_count = 0;
- $t_trunk_path = $p_repo->info['trunk_path'];
- $t_branch_path = $p_repo->info['branch_path'];
- $t_tag_path = $p_repo->info['tag_path'];
- $t_ignore_paths = $p_repo->info['ignore_paths'];
- $t_discarded = false;
- echo "Processing svn log...\n";
- foreach( $p_svnlog as $t_line ) {
- # starting state, do nothing
- if ( 0 == $t_state ) {
- if ( $t_line == $t_svnline ) {
- $t_state = 1;
- }
- # Changeset info
- } elseif ( 1 == $t_state && preg_match( '/^r([0-9]+) \| ([^|]+) \| ([0-9\-]+) ([0-9:]+)/', $t_line, $t_matches ) ) {
- if ( !is_null( $t_changeset ) ) {
- if ( !is_blank( $t_changeset->branch ) ) {
- $t_changeset->save();
- $t_changesets[] = $t_changeset;
- } else {
- $t_discarded = $t_changeset->revision;
- }
- }
- $t_changeset = new SourceChangeset( $p_repo->id, $t_matches[1], '', $t_matches[3] . ' ' . $t_matches[4], $t_matches[2], '' );
- $t_state = 2;
- # Changed paths
- } elseif ( 2 == $t_state ) {
- if ( strlen( $t_line ) == 0 ) {
- $t_state = 3;
- } else {
- if ( preg_match( '/^\s+([a-zA-Z])\s+([^\(]+)(?: \(from [^\)]+\))?/', $t_line, $t_matches ) ) {
- switch( $t_matches[1] ) {
- case 'A': $t_action = 'add'; break;
- case 'D': $t_action = 'rm'; break;
- case 'M': $t_action = 'mod'; break;
- case 'R': $t_action = 'mv'; break;
- default: $t_action = $t_matches[1];
- }
- $t_file = new SourceFile( $t_changeset->id, '', trim( $t_matches[2] ), $t_action );
- $t_changeset->files[] = $t_file;
- # Branch-checking
- if ( is_blank( $t_changeset->branch) ) {
- # Look for standard trunk/branches/tags information
- if ( $p_repo->info['standard_repo'] ) {
- if ( preg_match( '@/(?:(trunk)|(?:branches|tags)/([^/]+))@i', $t_file->filename, $t_matches ) ) {
- if ( !is_blank( $t_matches[1] ) ) {
- $t_changeset->branch = $t_matches[1];
- } else {
- $t_changeset->branch = $t_matches[2];
- }
- }
- } else {
- # Look for non-standard trunk path
- if ( !is_blank( $t_trunk_path ) && preg_match( '@^/*(' . $t_trunk_path . ')@i', $t_file->filename, $t_matches ) ) {
- $t_changeset->branch = $t_matches[1];
- # Look for non-standard branch path
- } else if ( !is_blank( $t_branch_path ) && preg_match( '@^/*(?:' . $t_branch_path . ')/([^/]+)@i', $t_file->filename, $t_matches ) ) {
- $t_changeset->branch = $t_matches[1];
- # Look for non-standard tag path
- } else if ( !is_blank( $t_tag_path ) && preg_match( '@^/*(?:' . $t_tag_path . ')/([^/]+)@i', $t_file->filename, $t_matches ) ) {
- $t_changeset->branch = $t_matches[1];
- # Fall back to just using the root folder as the branch name
- } else if ( !$t_ignore_paths && preg_match( '@/([^/]+)@', $t_file->filename, $t_matches ) ) {
- $t_changeset->branch = $t_matches[1];
- }
- }
- }
- }
- }
- # Changeset comments
- } elseif ( 3 == $t_state ) {
- if ( $t_line == $t_svnline ) {
- $t_state = 1;
- } else {
- if ( !is_blank($t_changeset->message) ) {
- $t_changeset->message .= "\n$t_line";
- } else {
- $t_changeset->message .= $t_line;
- }
- }
- # Should only happen at the end...
- } else {
- break;
- }
- }
- if ( !is_null( $t_changeset ) ) {
- echo "Parsed to revision {$t_changeset->revision}.\n";
- $t_changeset->save();
- $t_changesets[] = $t_changeset;
- } else {
- echo "No revisions parsed.\n";
- }
- return $t_changesets;
- }
- }