CliInstaller.php 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290
  1. <?php
  2. /**
  3. * Core installer command line interface.
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. * http://www.gnu.org/copyleft/gpl.html
  19. *
  20. * @file
  21. * @ingroup Deployment
  22. */
  23. use MediaWiki\Installer\InstallException;
  24. use MediaWiki\MediaWikiServices;
  25. /**
  26. * Class for the core installer command line interface.
  27. *
  28. * @ingroup Deployment
  29. * @since 1.17
  30. */
  31. class CliInstaller extends Installer {
  32. private $specifiedScriptPath = false;
  33. private $optionMap = [
  34. 'dbtype' => 'wgDBtype',
  35. 'dbserver' => 'wgDBserver',
  36. 'dbname' => 'wgDBname',
  37. 'dbuser' => 'wgDBuser',
  38. 'dbpass' => 'wgDBpassword',
  39. 'dbprefix' => 'wgDBprefix',
  40. 'dbtableoptions' => 'wgDBTableOptions',
  41. 'dbport' => 'wgDBport',
  42. 'dbschema' => 'wgDBmwschema',
  43. 'dbpath' => 'wgSQLiteDataDir',
  44. 'server' => 'wgServer',
  45. 'scriptpath' => 'wgScriptPath',
  46. ];
  47. /**
  48. * @param string $siteName
  49. * @param string|null $admin
  50. * @param array $options
  51. * @throws InstallException
  52. */
  53. function __construct( $siteName, $admin = null, array $options = [] ) {
  54. global $wgContLang;
  55. parent::__construct();
  56. if ( isset( $options['scriptpath'] ) ) {
  57. $this->specifiedScriptPath = true;
  58. }
  59. foreach ( $this->optionMap as $opt => $global ) {
  60. if ( isset( $options[$opt] ) ) {
  61. $GLOBALS[$global] = $options[$opt];
  62. $this->setVar( $global, $options[$opt] );
  63. }
  64. }
  65. if ( isset( $options['lang'] ) ) {
  66. global $wgLang, $wgLanguageCode;
  67. $this->setVar( '_UserLang', $options['lang'] );
  68. $wgLanguageCode = $options['lang'];
  69. $this->setVar( 'wgLanguageCode', $wgLanguageCode );
  70. $wgContLang = MediaWikiServices::getInstance()->getContentLanguage();
  71. $wgLang = Language::factory( $options['lang'] );
  72. RequestContext::getMain()->setLanguage( $wgLang );
  73. }
  74. $this->setVar( 'wgSitename', $siteName );
  75. $metaNS = $wgContLang->ucfirst( str_replace( ' ', '_', $siteName ) );
  76. if ( $metaNS == 'MediaWiki' ) {
  77. $metaNS = 'Project';
  78. }
  79. $this->setVar( 'wgMetaNamespace', $metaNS );
  80. if ( $admin ) {
  81. $this->setVar( '_AdminName', $admin );
  82. }
  83. if ( !isset( $options['installdbuser'] ) ) {
  84. $this->setVar( '_InstallUser',
  85. $this->getVar( 'wgDBuser' ) );
  86. $this->setVar( '_InstallPassword',
  87. $this->getVar( 'wgDBpassword' ) );
  88. } else {
  89. $this->setVar( '_InstallUser',
  90. $options['installdbuser'] );
  91. $this->setVar( '_InstallPassword',
  92. $options['installdbpass'] ?? "" );
  93. // Assume that if we're given the installer user, we'll create the account.
  94. $this->setVar( '_CreateDBAccount', true );
  95. }
  96. if ( isset( $options['pass'] ) ) {
  97. $this->setVar( '_AdminPassword', $options['pass'] );
  98. }
  99. // Detect and inject any extension found
  100. if ( isset( $options['extensions'] ) ) {
  101. $status = $this->validateExtensions(
  102. 'extension', 'extensions', $options['extensions'] );
  103. if ( !$status->isOK() ) {
  104. throw new InstallException( $status );
  105. }
  106. $this->setVar( '_Extensions', $status->value );
  107. } elseif ( isset( $options['with-extensions'] ) ) {
  108. $status = $this->findExtensions();
  109. if ( !$status->isOK() ) {
  110. throw new InstallException( $status );
  111. }
  112. $this->setVar( '_Extensions', array_keys( $status->value ) );
  113. }
  114. // Set up the default skins
  115. if ( isset( $options['skins'] ) ) {
  116. $status = $this->validateExtensions( 'skin', 'skins', $options['skins'] );
  117. if ( !$status->isOK() ) {
  118. throw new InstallException( $status );
  119. }
  120. $skins = $status->value;
  121. } else {
  122. $status = $this->findExtensions( 'skins' );
  123. if ( !$status->isOK() ) {
  124. throw new InstallException( $status );
  125. }
  126. $skins = array_keys( $status->value );
  127. }
  128. $this->setVar( '_Skins', $skins );
  129. if ( $skins ) {
  130. $skinNames = array_map( 'strtolower', $skins );
  131. $this->setVar( 'wgDefaultSkin', $this->getDefaultSkin( $skinNames ) );
  132. }
  133. }
  134. private function validateExtensions( $type, $directory, $nameLists ) {
  135. $extensions = [];
  136. $status = new Status;
  137. foreach ( (array)$nameLists as $nameList ) {
  138. foreach ( explode( ',', $nameList ) as $name ) {
  139. $name = trim( $name );
  140. if ( $name === '' ) {
  141. continue;
  142. }
  143. $extStatus = $this->getExtensionInfo( $type, $directory, $name );
  144. if ( $extStatus->isOK() ) {
  145. $extensions[] = $name;
  146. } else {
  147. $status->merge( $extStatus );
  148. }
  149. }
  150. }
  151. $extensions = array_unique( $extensions );
  152. $status->value = $extensions;
  153. return $status;
  154. }
  155. /**
  156. * Main entry point.
  157. */
  158. public function execute() {
  159. // If APC is available, use that as the MainCacheType, instead of nothing.
  160. // This is hacky and should be consolidated with WebInstallerOptions.
  161. // This is here instead of in __construct(), because it should run run after
  162. // doEnvironmentChecks(), which populates '_Caches'.
  163. if ( count( $this->getVar( '_Caches' ) ) ) {
  164. // We detected a CACHE_ACCEL implementation, use it.
  165. $this->setVar( '_MainCacheType', 'accel' );
  166. }
  167. $vars = Installer::getExistingLocalSettings();
  168. if ( $vars ) {
  169. $status = Status::newFatal( "config-localsettings-cli-upgrade" );
  170. $this->showStatusMessage( $status );
  171. return $status;
  172. }
  173. $result = $this->performInstallation(
  174. [ $this, 'startStage' ],
  175. [ $this, 'endStage' ]
  176. );
  177. // PerformInstallation bails on a fatal, so make sure the last item
  178. // completed before giving 'next.' Likewise, only provide back on failure
  179. $lastStepStatus = end( $result );
  180. if ( $lastStepStatus->isOK() ) {
  181. return Status::newGood();
  182. } else {
  183. return $lastStepStatus;
  184. }
  185. }
  186. /**
  187. * Write LocalSettings.php to a given path
  188. *
  189. * @param string $path Full path to write LocalSettings.php to
  190. */
  191. public function writeConfigurationFile( $path ) {
  192. $ls = InstallerOverrides::getLocalSettingsGenerator( $this );
  193. $ls->writeFile( "$path/LocalSettings.php" );
  194. }
  195. public function startStage( $step ) {
  196. // Messages: config-install-database, config-install-tables, config-install-interwiki,
  197. // config-install-stats, config-install-keys, config-install-sysop, config-install-mainpage,
  198. // config-install-extensions
  199. $this->showMessage( "config-install-$step" );
  200. }
  201. public function endStage( $step, $status ) {
  202. $this->showStatusMessage( $status );
  203. $this->showMessage( 'config-install-step-done' );
  204. }
  205. public function showMessage( $msg, ...$params ) {
  206. echo $this->getMessageText( $msg, $params ) . "\n";
  207. flush();
  208. }
  209. public function showError( $msg, ...$params ) {
  210. echo "***{$this->getMessageText( $msg, $params )}***\n";
  211. flush();
  212. }
  213. /**
  214. * @param string $msg
  215. * @param array $params
  216. *
  217. * @return string
  218. */
  219. protected function getMessageText( $msg, $params ) {
  220. $text = wfMessage( $msg, $params )->parse();
  221. $text = preg_replace( '/<a href="(.*?)".*?>(.*?)<\/a>/', '$2 &lt;$1&gt;', $text );
  222. return Sanitizer::stripAllTags( $text );
  223. }
  224. /**
  225. * Dummy
  226. */
  227. public function showHelpBox( $msg /*, ... */ ) {
  228. }
  229. public function showStatusMessage( Status $status ) {
  230. $warnings = array_merge( $status->getWarningsArray(),
  231. $status->getErrorsArray() );
  232. if ( count( $warnings ) !== 0 ) {
  233. foreach ( $warnings as $w ) {
  234. $this->showMessage( ...$w );
  235. }
  236. }
  237. }
  238. public function envCheckPath() {
  239. if ( !$this->specifiedScriptPath ) {
  240. $this->showMessage( 'config-no-cli-uri', $this->getVar( "wgScriptPath" ) );
  241. }
  242. return parent::envCheckPath();
  243. }
  244. protected function envGetDefaultServer() {
  245. // Use a basic value if the user didn't pass in --server
  246. return 'http://localhost';
  247. }
  248. public function dirIsExecutable( $dir, $url ) {
  249. $this->showMessage( 'config-no-cli-uploads-check', $dir );
  250. return false;
  251. }
  252. }