PageRenderTime 58ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/SourceCgit/SourceCgit.php

https://github.com/cocox/source-integration
PHP | 372 lines | 240 code | 76 blank | 56 comment | 26 complexity | fe03df50bcf4c45d82ed94e76788e4e4 MD5 | raw file
  1. <?php
  2. /**
  3. * This script assumes cgit's virtual-root option is set
  4. */
  5. # Copyright (c) 2011 asm89
  6. # Licensed under the MIT license
  7. if ( !defined('testing') ) {
  8. if ( false === include_once( config_get( 'plugin_path' ) . 'Source/MantisSourcePlugin.class.php' ) ) {
  9. return;
  10. }
  11. require_once( config_get( 'core_path' ) . 'url_api.php' );
  12. }
  13. class SourceCgitPlugin extends MantisSourcePlugin {
  14. public function register() {
  15. $this->name = plugin_lang_get( 'title' );
  16. $this->description = plugin_lang_get( 'description' );
  17. $this->version = '0.16';
  18. $this->requires = array(
  19. 'MantisCore' => '1.2.0',
  20. 'Source' => '0.16',
  21. );
  22. $this->author = 'Alexander';
  23. $this->contact = 'iam.asm89@gmail.com';
  24. $this->url = 'http://leetcode.net/';
  25. }
  26. public $type = 'cgit';
  27. public function show_type() {
  28. return plugin_lang_get( 'cgit' );
  29. }
  30. public function show_changeset( $p_repo, $p_changeset ) {
  31. $t_ref = substr( $p_changeset->revision, 0, 8 );
  32. $t_branch = $p_changeset->branch;
  33. return "$t_branch $t_ref";
  34. }
  35. public function show_file( $p_repo, $p_changeset, $p_file ) {
  36. return "$p_file->action - $p_file->filename";
  37. }
  38. private function uri_base( $p_repo ) {
  39. $t_uri_base = $p_repo->info['cgit_root'] . '' . $p_repo->info['cgit_project'] . '/';
  40. return $t_uri_base;
  41. }
  42. public function url_repo( $p_repo, $t_changeset=null ) {
  43. return $this->uri_base( $p_repo ) . ( $t_changeset ? 'commit/?id=' . $t_changeset->revision : '' );
  44. }
  45. public function url_commit ( $p_repo, $commit_rev) {
  46. return $this->uri_base( $p_repo ) . 'commit/?id=' . $commit_rev;
  47. }
  48. public function url_changeset( $p_repo, $p_changeset ) {
  49. return $this->uri_base( $p_repo ) . ( $p_changeset ? 'commit/?id=' . $p_changeset->revision : '' );
  50. }
  51. public function url_file( $p_repo, $p_changeset, $p_file ) {
  52. return $this->uri_base( $p_repo ) . 'tree/' . $p_file->filename . '?id=' . $p_changeset->revision;
  53. }
  54. public function url_diff( $p_repo, $p_changeset, $p_file ) {
  55. return $this->uri_base( $p_repo ) . 'diff/' . $p_file->filename . '?id=' . $p_changeset->revision;
  56. }
  57. public function update_repo_form( $p_repo ) {
  58. $t_gitweb_root = null;
  59. $t_gitweb_project = null;
  60. if ( isset( $p_repo->info['cgit_root'] ) ) {
  61. $t_cgit_root = $p_repo->info['cgit_root'];
  62. }
  63. if ( isset( $p_repo->info['cgit_project'] ) ) {
  64. $t_cgit_project = $p_repo->info['cgit_project'];
  65. }
  66. if ( isset( $p_repo->info['master_branch'] ) ) {
  67. $t_master_branch = $p_repo->info['master_branch'];
  68. } else {
  69. $t_master_branch = 'master';
  70. }
  71. ?>
  72. <tr <?php echo helper_alternate_class() ?>>
  73. <td class="category"><?php echo plugin_lang_get( 'cgit_root' ) ?></td>
  74. <td><input name="cgit_root" maxlength="250" size="40" value="<?php echo string_attribute( $t_cgit_root ) ?>"/></td>
  75. </tr>
  76. <tr <?php echo helper_alternate_class() ?>>
  77. <td class="category"><?php echo plugin_lang_get( 'cgit_project' ) ?></td>
  78. <td><input name="cgit_project" maxlength="250" size="40" value="<?php echo string_attribute( $t_cgit_project ) ?>"/></td>
  79. </tr>
  80. <tr <?php echo helper_alternate_class() ?>>
  81. <td class="category"><?php echo plugin_lang_get( 'master_branch' ) ?></td>
  82. <td><input name="master_branch" maxlength="250" size="40" value="<?php echo string_attribute( $t_master_branch ) ?>"/></td>
  83. </tr>
  84. <?php
  85. }
  86. public function update_repo( $p_repo ) {
  87. $f_cgit_root = gpc_get_string( 'cgit_root' );
  88. $f_cgit_project = gpc_get_string( 'cgit_project' );
  89. $f_master_branch = gpc_get_string( 'master_branch' );
  90. $p_repo->info['cgit_root'] = $f_cgit_root;
  91. $p_repo->info['cgit_project'] = $f_cgit_project;
  92. $p_repo->info['master_branch'] = $f_master_branch;
  93. return $p_repo;
  94. }
  95. public function precommit( ) {
  96. # TODO: Implement real commit sequence.
  97. return;
  98. }
  99. public function commit( $p_repo, $p_data ) {
  100. # The -d option from curl requires you to encode your own data.
  101. # Once it reaches here it is decoded. Hence we split by a space
  102. # were as the curl command uses a '+' character instead.
  103. # i.e. DATA=`echo $INPUT | sed -e 's/ /+/g'`
  104. list ( , $t_commit_id, $t_branch) = split(' ', $p_data);
  105. list ( , , $t_branch) = split('/', $t_branch);
  106. if ($t_branch != $p_repo->info['master_branch'])
  107. {
  108. return;
  109. }
  110. return $this->import_commits($p_repo, null, $t_commit_id, $t_branch);
  111. }
  112. public function import_full( $p_repo ) {
  113. echo '<pre>';
  114. $t_branch = $p_repo->info['master_branch'];
  115. if ( is_blank( $t_branch ) ) {
  116. $t_branch = 'master';
  117. }
  118. $t_branches = array_map( 'trim', explode( ',', $t_branch ) );
  119. $t_changesets = array();
  120. $t_changeset_table = plugin_table( 'changeset', 'Source' );
  121. foreach( $t_branches as $t_branch ) {
  122. $t_query = "SELECT parent FROM $t_changeset_table
  123. WHERE repo_id=" . db_param() . ' AND branch=' . db_param() .
  124. 'ORDER BY timestamp ASC';
  125. $t_result = db_query_bound( $t_query, array( $p_repo->id, $t_branch ), 1 );
  126. $t_commits = array( $t_branch );
  127. if ( db_num_rows( $t_result ) > 0 ) {
  128. $t_parent = db_result( $t_result );
  129. echo "Oldest '$t_branch' branch parent: '$t_parent'\n";
  130. if ( !empty( $t_parent ) ) {
  131. $t_commits[] = $t_parent;
  132. }
  133. }
  134. $t_changesets = array_merge( $t_changesets, $this->import_commits( $p_repo, $this->uri_base( $p_repo ), $t_commits, $t_branch ) );
  135. }
  136. echo '</pre>';
  137. return $t_changesets;
  138. }
  139. public function import_latest( $p_repo ) {
  140. return $this->import_full( $p_repo );
  141. }
  142. private function import_commits( $p_repo, $p_uri_base, $p_commit_ids, $p_branch='' ) {
  143. static $s_parents = array();
  144. static $s_counter = 0;
  145. if ( is_array( $p_commit_ids ) ) {
  146. $s_parents = array_merge( $s_parents, $p_commit_ids );
  147. } else {
  148. $s_parents[] = $p_commit_ids;
  149. }
  150. $t_changesets = array();
  151. while( count( $s_parents ) > 0 && $s_counter < 200 ) {
  152. $t_commit_id = array_shift( $s_parents );
  153. echo "Retrieving $t_commit_id ... ";
  154. $t_commit_url = $this->url_commit( $p_repo, $t_commit_id);
  155. $t_input = url_get( $t_commit_url );
  156. if ( false === $t_input ) {
  157. echo "failed.\n";
  158. continue;
  159. }
  160. list( $t_changeset, $t_commit_parents ) = $this->commit_changeset( $p_repo, $t_input, $p_branch );
  161. if ( !is_null( $t_changeset ) ) {
  162. $t_changesets[] = $t_changeset;
  163. }
  164. $s_parents = array_merge( $s_parents, $t_commit_parents );
  165. }
  166. $s_counter = 0;
  167. return $t_changesets;
  168. }
  169. /**
  170. * Parses the revision from a cgit page.
  171. *
  172. * @param string $p_input cgit html page
  173. * @return string the revision
  174. */
  175. public function commit_revision( $p_input ) {
  176. preg_match( "#<tr><th>commit</th><td colspan='2' class='sha1'><a href='/(.*?)/commit/\?id=([a-f0-9]*)'>([a-f0-9]*)</a>#", $p_input, $t_matches);
  177. return $t_matches[2];
  178. }
  179. /**
  180. * Parses the author and comitter from a cgit page.
  181. *
  182. * @param string $p_input cgit html page
  183. * @return array author / committer
  184. */
  185. public function commit_author( $p_input ) {
  186. preg_match( "#<tr><th>author</th><td>(.*?)<(.*?)></td><td class='right'>(.*?)</td>#", $p_input, $t_matches);
  187. $t_commit['author'] = trim($t_matches[1]);
  188. $t_commit['author_email'] = $t_matches[2];
  189. $t_commit['date'] = date( 'Y-m-d H:i:s', strtotime( $t_matches[3] ) );
  190. if( preg_match( "#<tr><th>committer</th><td>(.*?)<(.*?)></td><td class='right'>(.*?)</td>#", $p_input, $t_matches) ) {
  191. $t_commit['committer'] = trim($t_matches[1]);
  192. $t_commit['committer_email'] = $t_matches[2];
  193. }
  194. return $t_commit;
  195. }
  196. /**
  197. * Parses the parent commits from a cgit page.
  198. *
  199. * @param string $p_input cgit html page
  200. * @return array the parents
  201. */
  202. public function commit_parents( $p_input ) {
  203. $t_parents = array();
  204. if( preg_match_all( "#<tr><th>parent</th><td colspan='2' class='sha1'><a href='/(.*?)/commit/\?id=([a-f0-9]*)'>([a-f0-9]*)</a>#", $p_input, $t_matches) ) {
  205. foreach( $t_matches[2] as $t_match ) {
  206. $t_parents[] = $t_commit['parent'] = $t_match;
  207. }
  208. }
  209. return $t_parents;
  210. }
  211. /**
  212. * Parses the message from a cgit page.
  213. *
  214. * @param string $p_input cgit html page
  215. * @return string
  216. */
  217. public function commit_message( $p_input ) {
  218. preg_match( "#<div class='commit-subject'>(.*?)(<a class=|</div>)#", $p_input, $t_matches);
  219. $t_message = trim( str_replace( '<br/>', PHP_EOL, $t_matches[1] ) );
  220. # Strip ref links and signoff spans from commit message
  221. $t_message = preg_replace( array(
  222. '@<a[^>]*>([^<]*)<\/a>@',
  223. '@<span[^>]*>([^<]*<[^>]*>[^<]*)<\/span>@', #finds <span..>signed-off by <email></span>
  224. ), '$1', $t_message );
  225. return $t_message;
  226. }
  227. /**
  228. * Parses the commit file from a cgit page.
  229. *
  230. * @param string $p_input cgit html page
  231. * @return array files
  232. */
  233. public function commit_files( $p_input ) {
  234. $t_files = array();
  235. preg_match_all( "#<td class='mode'>(.*?)</td><td class='(.*?)'><a href='(.*?)\?id=([0-9a-f]*)'>(.*?)</a>#", $p_input, $t_matches, PREG_SET_ORDER);
  236. foreach( $t_matches as $t_file_matches ) {
  237. $t_file = array();
  238. $t_file['filename'] = $t_file_matches[5];
  239. $t_file['revision'] = $t_file_matches[4];
  240. if ( 'add' == $t_file_matches[2] ) {
  241. $t_file['action'] = 'add';
  242. } else if ( 'del' == $t_file_matches[2] ) {
  243. $t_file['action'] = 'rm';
  244. } else {
  245. $t_file['action'] = 'mod';
  246. }
  247. $t_files[] = $t_file;
  248. }
  249. return $t_files;
  250. }
  251. /**
  252. * Cleans the input html.
  253. *
  254. * @param string $p_input cgit html page
  255. * @return string
  256. */
  257. public function clean_input( $p_input ) {
  258. return str_replace( array(PHP_EOL, '&lt;', '&gt;', '&nbsp;'), array('', '<', '>', ' '), $p_input );
  259. }
  260. private function commit_changeset( $p_repo, $p_input, $p_branch='' ) {
  261. // Clean the input
  262. $t_input = $this->clean_input( $p_input );
  263. // Get the revision
  264. $t_commit['revision'] = $this->commit_revision( $t_input );;
  265. echo "processing $t_commit[revision] ... ";
  266. // Only process if it doesn't exist yet
  267. if ( !SourceChangeset::exists( $p_repo->id, $t_commit['revision'] ) ) {
  268. // Author and committer data
  269. $t_commit = array_merge($this->commit_author( $t_input ), $t_commit);
  270. // Commit parents
  271. $t_parents = $this->commit_parents( $t_input );
  272. // Set the last parent as the main parent of this commit
  273. $t_commit['parent'] = $t_parents[ count($t_parents) - 1 ];
  274. // Set the message
  275. $t_commit['message'] = $this->commit_message( $t_input );
  276. # Parse for changed file data
  277. $t_commit['files'] = $this->commit_files( $t_input );
  278. // Create the changeset
  279. $t_changeset = new SourceChangeset( $p_repo->id, $t_commit['revision'], $p_branch,
  280. $t_commit['date'], $t_commit['author'], $t_commit['message'], 0,
  281. ( isset( $t_commit['parent'] ) ? $t_commit['parent'] : '' ) );
  282. $t_changeset->author_email = $t_commit['author'];
  283. $t_changeset->committer = $t_commit['committer'];
  284. $t_changeset->committer_email = $t_commit['committer_email'];
  285. foreach( $t_commit['files'] as $t_file ) {
  286. $t_changeset->files[] = new SourceFile( 0, $t_file['revision'], $t_file['filename'], $t_file['action'] );
  287. }
  288. $t_changeset->save();
  289. echo "saved.\n";
  290. return array( $t_changeset, $t_parents );
  291. } else {
  292. echo "already exists.\n";
  293. return array( null, array() );
  294. }
  295. }
  296. }