PageRenderTime 44ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/extensions/ExtensionDistributor/ExtensionDistributor_body.php

https://github.com/ChuguluGames/mediawiki-svn
PHP | 341 lines | 275 code | 52 blank | 14 comment | 32 complexity | e7d2613de95f23949d2c3d43ff397d48 MD5 | raw file
  1. <?php
  2. /**
  3. * Special page that allows users to download extensions as tar archives.
  4. *
  5. * @author Tim Starling
  6. */
  7. class ExtensionDistributorPage extends SpecialPage {
  8. protected $extensionList; // cached list of extensions
  9. public function __construct() {
  10. parent::__construct( 'ExtensionDistributor' );
  11. }
  12. public function execute( $subpage ) {
  13. global $wgExtDistTarDir, $wgExtDistWorkingCopy, $wgOut, $wgRequest;
  14. $this->setHeaders();
  15. if ( !$wgExtDistTarDir || !$wgExtDistWorkingCopy ) {
  16. $wgOut->addWikiMsg( 'extdist-not-configured' );
  17. return;
  18. }
  19. if ( $subpage ) {
  20. $parts = explode( '/', $subpage, 2 );
  21. if ( count( $parts ) == 1 ) {
  22. $parts[] = false;
  23. }
  24. list( $extension, $version ) = $parts;
  25. } else {
  26. $extension = $wgRequest->getVal( 'extdist_extension' );
  27. $version = $wgRequest->getVal( 'extdist_version' );
  28. }
  29. if ( !$extension ) {
  30. $this->showExtensionSelector();
  31. return;
  32. }
  33. $extensions = $this->getExtensionList();
  34. if ( !in_array( $extension, $extensions['trunk'] ) ) {
  35. $wgOut->addWikiMsg( 'extdist-no-such-extension', $extension );
  36. $this->showExtensionSelector();
  37. return;
  38. }
  39. if ( !$version ) {
  40. $this->showVersionSelector( $extension );
  41. return;
  42. }
  43. if ( !isset( $extensions[$version] ) || !in_array( $extension, $extensions[$version] ) ) {
  44. $wgOut->addWikiMsg( 'extdist-no-such-version', $extension, $version );
  45. return;
  46. }
  47. $this->doDownload( $extension, $version );
  48. }
  49. protected function getExtensionList() {
  50. global $wgExtDistWorkingCopy, $wgExtDistBranches;
  51. if ( isset( $this->extensionList ) ) {
  52. return $this->extensionList;
  53. }
  54. $this->extensionList = array();
  55. foreach ( $wgExtDistBranches as $branchPath => $branch ) {
  56. $wc = "$wgExtDistWorkingCopy/$branchPath/extensions";
  57. $dir = opendir( $wc );
  58. if ( !$dir ) {
  59. return false;
  60. }
  61. $this->extensionList[$branchPath] = array();
  62. while ( false !== ( $file = readdir( $dir ) ) ) {
  63. if ( substr( $file, 0, 1 ) == '.' ) {
  64. continue;
  65. }
  66. if ( !is_dir( "$wc/$file" ) ) {
  67. continue;
  68. }
  69. if ( file_exists( "$wc/$file/NO-DIST" ) ) {
  70. continue;
  71. }
  72. $this->extensionList[$branchPath][] = $file;
  73. }
  74. natcasesort( $this->extensionList[$branchPath] );
  75. }
  76. return $this->extensionList;
  77. }
  78. protected function getBranchName( $path ) {
  79. global $wgExtDistBranches;
  80. if ( !isset( $wgExtDistBranches[$path] ) ) {
  81. return false;
  82. }
  83. if ( isset( $wgExtDistBranches[$path]['msgName'] ) ) {
  84. return wfMsg( $wgExtDistBranches[$path]['msgName'] );
  85. } else {
  86. return $wgExtDistBranches[$path]['name'];
  87. }
  88. }
  89. protected function showExtensionSelector() {
  90. global $wgOut;
  91. $extensions = $this->getExtensionList();
  92. if ( $extensions === false ) {
  93. $wgOut->addWikiMsg( 'extdist-wc-missing' );
  94. return;
  95. }
  96. if ( !$extensions['trunk'] ) {
  97. $wgOut->addWikiMsg( 'extdist-wc-empty' );
  98. return;
  99. }
  100. $wgOut->addWikiMsg( 'extdist-choose-extension' );
  101. $wgOut->addHTML(
  102. Xml::openElement( 'form', array(
  103. 'action' => $this->getTitle()->getLocalUrl(),
  104. 'method' => 'GET' ) ) .
  105. "<select name=\"extdist_extension\">\n" .
  106. "<option value=''></option>\n"
  107. );
  108. foreach ( $extensions['trunk'] as $extension ) {
  109. $wgOut->addHTML( Xml::element( 'option', array( 'value' => $extension ), $extension ) . "\n" );
  110. }
  111. $wgOut->addHTML(
  112. Xml::closeElement( 'select' ) . ' ' .
  113. Xml::submitButton( wfMsg( 'extdist-submit-extension' ), array( 'name' => 'extdist_submit' ) ) .
  114. Xml::closeElement( 'form' ) . "\n"
  115. );
  116. }
  117. protected function showVersionSelector( $extensionName ) {
  118. global $wgOut, $wgExtDistBranches;
  119. $extensions = $this->getExtensionList();
  120. $versions = array();
  121. foreach ( $wgExtDistBranches as $branchPath => $branch ) {
  122. if ( !in_array( $extensionName, $extensions[$branchPath] ) ) {
  123. continue;
  124. }
  125. if ( isset( $branch['msgName'] ) ) {
  126. $branchName = wfMsg( $branch['msgName'] );
  127. } else {
  128. $branchName = $branch['name'];
  129. }
  130. $versions[$branchPath] = $branchName;
  131. }
  132. if ( !$versions ) {
  133. $wgOut->addWikiMsg( 'extdist-no-versions', $extensionName );
  134. $this->showExtensionSelector();
  135. return;
  136. }
  137. $wgOut->addWikiMsg( 'extdist-choose-version', $extensionName );
  138. $wgOut->addHTML(
  139. Xml::openElement( 'form', array(
  140. 'action' => $this->getTitle()->getLocalUrl(),
  141. 'method' => 'GET' ) ) .
  142. Xml::element( 'input' , array( 'type' => 'hidden',
  143. 'name' => 'extdist_extension', 'value' => $extensionName ) ) .
  144. "<select name=\"extdist_version\">\n" );
  145. $selected = 0;
  146. foreach ( $versions as $branchPath => $branchName ) {
  147. $wgOut->addHTML( Xml::option( $branchName, $branchPath, ($selected == 1) ) );
  148. $selected++;
  149. }
  150. $wgOut->addHTML(
  151. Xml::closeElement( 'select' ) . ' ' .
  152. Xml::submitButton( wfMsg( 'extdist-submit-version' ), array( 'name' => 'extdist_submit' ) ) .
  153. Xml::closeElement( 'form' ) . "\n"
  154. );
  155. }
  156. protected function doDownload( $extension, $version ) {
  157. global $wgExtDistWorkingCopy, $wgExtDistTarDir, $wgExtDistBranches,
  158. $wgOut, $wgExtDistTarUrl, $wgExtDistRemoteClient;
  159. if ( $wgExtDistRemoteClient ) {
  160. $rev = $this->updateAndGetRevisionRemote( $extension, $version );
  161. } else {
  162. $rev = $this->updateAndGetRevisionLocal( $extension, $version );
  163. }
  164. if ( $rev === false ) {
  165. // Error already displayed
  166. return;
  167. }
  168. // Determine tar name.
  169. $cleanName = str_replace( '/', '_', $extension );
  170. $versionName = $wgExtDistBranches[$version]['tarLabel'];
  171. $tarName = "$cleanName-$versionName-r$rev.tar.gz";
  172. $tarFile = "$wgExtDistTarDir/$tarName";
  173. // Create the archive if it doesn't exist.
  174. if ( !file_exists( $tarFile ) ) {
  175. // Does the tar file need ExtensionFunctions.php?
  176. $dir = "$wgExtDistWorkingCopy/$version/extensions/$extension";
  177. $retval = - 1;
  178. $files = call_user_func_array( 'wfEscapeShellArg', glob( "$dir/*.php" ) );
  179. wfShellExec( "grep -q '\bExtensionFunctions' " . $files, $retval );
  180. $needEF = !$retval;
  181. // Create the archive.
  182. $cmd = 'tar -czf ' . wfEscapeShellArg( $tarFile ) .
  183. ' --exclude \'*/.svn*\'' .
  184. ' -C ' . wfEscapeShellArg( "$wgExtDistWorkingCopy/$version/extensions" ) .
  185. ' ' . wfEscapeShellArg( $extension ) .
  186. ( $needEF ? ' ExtensionFunctions.php' : '' ) .
  187. ' 2>&1';
  188. $retval = - 1;
  189. $result = wfShellExec( $cmd, $retval );
  190. if ( $retval ) {
  191. $wgOut->addWikiMsg( 'extdist-tar-error', $retval );
  192. $wgOut->addHTML( '<pre>' . htmlspecialchars( $result ) . '</pre>' );
  193. return;
  194. }
  195. }
  196. $url = "$wgExtDistTarUrl/$tarName";
  197. // Show a message
  198. $wgOut->addWikiMsg( 'extdist-created', $extension, "r$rev",
  199. $this->getBranchName( $version ), $url, $tarName );
  200. $wgOut->addHTML( '<p><br /><big>' .
  201. '<a href="' . $this->getTitle()->escapeLocalUrl() . '">' .
  202. htmlspecialchars( wfMsg( 'extdist-want-more' ) ) . '</a></big></p>' );
  203. // Redirect to the file
  204. header( 'Refresh: 5;url=' . $url );
  205. }
  206. protected function updateAndGetRevisionLocal( $extension, $version ) {
  207. global $wgExtDistWorkingCopy, $wgOut;
  208. // svn up
  209. $dir = "$wgExtDistWorkingCopy/$version/extensions/$extension";
  210. $cmd = "svn up --non-interactive " . wfEscapeShellArg( $dir ) . " 2>&1";
  211. $retval = - 1;
  212. $result = wfShellExec( $cmd, $retval );
  213. if ( $retval ) {
  214. $wgOut->addWikiMsg( 'extdist-svn-error', $result );
  215. return false;
  216. }
  217. // Determine last changed revision
  218. $cmd = "svn info --non-interactive --xml " . wfEscapeShellArg( $dir );
  219. $retval = - 1;
  220. $result = wfShellExec( $cmd, $retval );
  221. if ( $retval ) {
  222. $wgOut->addWikiMsg( 'extdist-svn-error', $result );
  223. return false;
  224. }
  225. $sx = new SimpleXMLElement( $result );
  226. $rev = $sx->entry->commit['revision'];
  227. if ( !$rev || strpos( $rev, '/' ) !== false || strpos( $rev, "\000" ) !== false ) {
  228. $wgOut->addWikiMsg( 'extdist-svn-parse-error', $result );
  229. return false;
  230. }
  231. return $rev;
  232. }
  233. protected function updateAndGetRevisionRemote( $extension, $version ) {
  234. global $wgExtDistRemoteClient, $wgOut;
  235. $cmd = json_encode( array( 'extension' => $extension, 'version' => $version ) );
  236. $cmd = str_replace( "\000", '', $cmd );
  237. list( $host, $port ) = explode( ':', $wgExtDistRemoteClient, 2 );
  238. $sock = fsockopen( $host, $port );
  239. if ( !$sock ) {
  240. $wgOut->addWikiMsg( 'extdist-no-remote' );
  241. return false;
  242. }
  243. $encResponse = '';
  244. fwrite( $sock, $cmd . "\000\000\000" );
  245. while ( $sock && !feof( $sock ) ) {
  246. $encResponse .= fread( $sock, 8192 );
  247. }
  248. fclose( $sock );
  249. $response = json_decode( $encResponse );
  250. if ( isset( $response->error ) ) {
  251. $info = isset( $response->errorInfo ) ? $response->errorInfo : '';
  252. $wgOut->addWikiMsg( $response->error, $info );
  253. return false;
  254. }
  255. if ( !isset( $response->revision ) ) {
  256. $wgOut->addWikiMsg( 'extdist-remote-invalid-response' );
  257. return false;
  258. }
  259. return $response->revision;
  260. }
  261. }