Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 310 and 400] [Versions 311 and 400] [Versions 39 and 400]

   1  <?php
   2  
   3  // This file is part of Moodle - http://moodle.org/
   4  //
   5  // Moodle 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 3 of the License, or
   8  // (at your option) any later version.
   9  //
  10  // Moodle 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
  16  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  17  
  18  /**
  19   * This script creates config.php file and prepares database.
  20   *
  21   * This script is not intended for beginners!
  22   * Potential problems:
  23   * - su to apache account or sudo before execution
  24   * - not compatible with Windows platform
  25   *
  26   * @package    core
  27   * @subpackage cli
  28   * @copyright  2009 Petr Skoda (http://skodak.org)
  29   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   */
  31  
  32  define('CLI_SCRIPT', true);
  33  
  34  // extra execution prevention - we can not just require config.php here
  35  if (isset($_SERVER['REMOTE_ADDR'])) {
  36      exit(1);
  37  }
  38  
  39  // Force OPcache reset if used, we do not want any stale caches
  40  // when preparing test environment.
  41  if (function_exists('opcache_reset')) {
  42      opcache_reset();
  43  }
  44  
  45  $help =
  46  "Command line Moodle installer, creates config.php and initializes database.
  47  Please note you must execute this script with the same uid as apache
  48  or use chmod/chown after installation.
  49  
  50  Site defaults may be changed via local/defaults.php.
  51  
  52  Options:
  53  --chmod=OCTAL-MODE    Permissions of new directories created within dataroot.
  54                        Default is 2777. You may want to change it to 2770
  55                        or 2750 or 750. See chmod man page for details.
  56  --lang=CODE           Installation and default site language.
  57  --wwwroot=URL         Web address for the Moodle site,
  58                        required in non-interactive mode.
  59  --dataroot=DIR        Location of the moodle data folder,
  60                        must not be web accessible. Default is moodledata
  61                        in the parent directory.
  62  --dbtype=TYPE         Database type. Default is mysqli
  63  --dbhost=HOST         Database host. Default is localhost
  64  --dbname=NAME         Database name. Default is moodle
  65  --dbuser=USERNAME     Database user. Default is root
  66  --dbpass=PASSWORD     Database password. Default is blank
  67  --dbport=NUMBER       Use database port.
  68  --dbsocket=PATH       Use database socket, 1 means default. Available for some databases only.
  69  --prefix=STRING       Table prefix for above database tables. Default is mdl_
  70  --fullname=STRING     The fullname of the site
  71  --shortname=STRING    The shortname of the site
  72  --summary=STRING      The summary to be displayed on the front page
  73  --adminuser=USERNAME  Username for the moodle admin account. Default is admin
  74  --adminpass=PASSWORD  Password for the moodle admin account,
  75                        required in non-interactive mode.
  76  --adminemail=STRING   Email address for the moodle admin account.
  77  --sitepreset=STRING   Admin site preset to be applied during the installation process.
  78  --supportemail=STRING Email address for support and help.
  79  --upgradekey=STRING   The upgrade key to be set in the config.php, leave empty to not set it.
  80  --non-interactive     No interactive questions, installation fails if any
  81                        problem encountered.
  82  --agree-license       Indicates agreement with software license,
  83                        required in non-interactive mode.
  84  --allow-unstable      Install even if the version is not marked as stable yet,
  85                        required in non-interactive mode.
  86  --skip-database       Stop the installation before installing the database.
  87  -h, --help            Print out this help
  88  
  89  Example:
  90  \$sudo -u www-data /usr/bin/php admin/cli/install.php --lang=cs
  91  "; //TODO: localize, mark as needed in install - to be translated later when everything is finished
  92  
  93  
  94  // distro specific customisation
  95  $distrolibfile = __DIR__.'/../../install/distrolib.php';
  96  $distro = null;
  97  if (file_exists($distrolibfile)) {
  98      require_once($distrolibfile);
  99      if (function_exists('distro_get_config')) {
 100          $distro = distro_get_config();
 101      }
 102  }
 103  
 104  // Nothing to do if config.php exists
 105  $configfile = __DIR__.'/../../config.php';
 106  if (file_exists($configfile)) {
 107      require($configfile);
 108      require_once($CFG->libdir.'/clilib.php');
 109      list($options, $unrecognized) = cli_get_params(array('help'=>false), array('h'=>'help'));
 110  
 111      if ($options['help']) {
 112          echo $help;
 113          echo "\n\n";
 114      }
 115  
 116      if ($DB->get_manager()->table_exists('config')) {
 117          cli_error(get_string('clialreadyinstalled', 'install'));
 118      } else {
 119          cli_error(get_string('clialreadyconfigured', 'install'));
 120      }
 121  }
 122  
 123  $olddir = getcwd();
 124  
 125  // change directory so that includes below work properly
 126  chdir(dirname($_SERVER['argv'][0]));
 127  
 128  // Servers should define a default timezone in php.ini, but if they don't then make sure something is defined.
 129  if (!function_exists('date_default_timezone_set') or !function_exists('date_default_timezone_get')) {
 130      fwrite(STDERR, "Timezone functions are not available.\n");
 131      exit(1);
 132  }
 133  date_default_timezone_set(@date_default_timezone_get());
 134  
 135  // make sure PHP errors are displayed - helps with diagnosing of problems
 136  @error_reporting(E_ALL);
 137  @ini_set('display_errors', '1');
 138  // we need a lot of memory
 139  @ini_set('memory_limit', '128M');
 140  
 141  /** Used by library scripts to check they are being called by Moodle */
 142  define('MOODLE_INTERNAL', true);
 143  
 144  // Disables all caching.
 145  define('CACHE_DISABLE_ALL', true);
 146  
 147  define('PHPUNIT_TEST', false);
 148  
 149  define('IGNORE_COMPONENT_CACHE', true);
 150  
 151  // Check that PHP is of a sufficient version as soon as possible.
 152  require_once (__DIR__.'/../../lib/phpminimumversionlib.php');
 153  moodle_require_minimum_php_version();
 154  
 155  // set up configuration
 156  global $CFG;
 157  $CFG = new stdClass();
 158  $CFG->lang                 = 'en';
 159  $CFG->dirroot              = dirname(dirname(__DIR__));
 160  $CFG->libdir               = "$CFG->dirroot/lib";
 161  $CFG->wwwroot              = "http://localhost";
 162  $CFG->httpswwwroot         = $CFG->wwwroot;
 163  $CFG->docroot              = 'http://docs.moodle.org';
 164  $CFG->running_installer    = true;
 165  $CFG->early_install_lang   = true;
 166  $CFG->ostype               = (stristr(PHP_OS, 'win') && !stristr(PHP_OS, 'darwin')) ? 'WINDOWS' : 'UNIX';
 167  $CFG->dboptions            = array();
 168  $CFG->debug                = (E_ALL | E_STRICT);
 169  $CFG->debugdisplay         = true;
 170  $CFG->debugdeveloper       = true;
 171  
 172  $parts = explode('/', str_replace('\\', '/', dirname(__DIR__)));
 173  $CFG->admin                = array_pop($parts);
 174  
 175  //point pear include path to moodles lib/pear so that includes and requires will search there for files before anywhere else
 176  //the problem is that we need specific version of quickforms and hacked excel files :-(
 177  ini_set('include_path', $CFG->libdir.'/pear' . PATH_SEPARATOR . ini_get('include_path'));
 178  
 179  require_once($CFG->libdir.'/classes/component.php');
 180  require_once($CFG->libdir.'/classes/text.php');
 181  require_once($CFG->libdir.'/classes/string_manager.php');
 182  require_once($CFG->libdir.'/classes/string_manager_install.php');
 183  require_once($CFG->libdir.'/classes/string_manager_standard.php');
 184  require_once($CFG->libdir.'/installlib.php');
 185  require_once($CFG->libdir.'/clilib.php');
 186  require_once($CFG->libdir.'/setuplib.php');
 187  require_once($CFG->libdir.'/weblib.php');
 188  require_once($CFG->libdir.'/dmllib.php');
 189  require_once($CFG->libdir.'/moodlelib.php');
 190  require_once($CFG->libdir.'/deprecatedlib.php');
 191  require_once($CFG->libdir.'/adminlib.php');
 192  require_once($CFG->libdir.'/componentlib.class.php');
 193  require_once($CFG->dirroot.'/cache/lib.php');
 194  
 195  // Register our classloader, in theory somebody might want to replace it to load other hacked core classes.
 196  // Required because the database checks below lead to session interaction which is going to lead us to requiring autoloaded classes.
 197  if (defined('COMPONENT_CLASSLOADER')) {
 198      spl_autoload_register(COMPONENT_CLASSLOADER);
 199  } else {
 200      spl_autoload_register('core_component::classloader');
 201  }
 202  
 203  require($CFG->dirroot.'/version.php');
 204  $CFG->target_release = $release;
 205  
 206  \core\session\manager::init_empty_session();
 207  global $SESSION;
 208  global $USER;
 209  
 210  global $COURSE;
 211  $COURSE = new stdClass();
 212  $COURSE->id = 1;
 213  
 214  global $SITE;
 215  $SITE = $COURSE;
 216  define('SITEID', 1);
 217  
 218  //Database types
 219  $databases = array('mysqli' => moodle_database::get_driver_instance('mysqli', 'native'),
 220                     'auroramysql' => moodle_database::get_driver_instance('auroramysql', 'native'),
 221                     'mariadb'=> moodle_database::get_driver_instance('mariadb', 'native'),
 222                     'pgsql'  => moodle_database::get_driver_instance('pgsql',  'native'),
 223                     'oci'    => moodle_database::get_driver_instance('oci',    'native'),
 224                     'sqlsrv' => moodle_database::get_driver_instance('sqlsrv', 'native'), // MS SQL*Server PHP driver
 225                    );
 226  foreach ($databases as $type=>$database) {
 227      if ($database->driver_installed() !== true) {
 228          unset($databases[$type]);
 229      }
 230  }
 231  if (empty($databases)) {
 232      $defaultdb = '';
 233  } else {
 234      reset($databases);
 235      $defaultdb = key($databases);
 236  }
 237  
 238  // now get cli options
 239  list($options, $unrecognized) = cli_get_params(
 240      array(
 241          'chmod'             => isset($distro->directorypermissions) ? sprintf('%04o',$distro->directorypermissions) : '2777', // let distros set dir permissions
 242          'lang'              => $CFG->lang,
 243          'wwwroot'           => '',
 244          'dataroot'          => empty($distro->dataroot) ? str_replace('\\', '/', dirname(dirname(dirname(__DIR__))).'/moodledata'): $distro->dataroot, // initialised later after including libs or by distro
 245          'dbtype'            => empty($distro->dbtype) ? $defaultdb : $distro->dbtype, // let distro skip dbtype selection
 246          'dbhost'            => empty($distro->dbhost) ? 'localhost' : $distro->dbhost, // let distros set dbhost
 247          'dbname'            => 'moodle',
 248          'dbuser'            => empty($distro->dbuser) ? 'root' : $distro->dbuser, // let distros set dbuser
 249          'dbpass'            => '',
 250          'dbport'            => '',
 251          'dbsocket'          => '',
 252          'prefix'            => 'mdl_',
 253          'fullname'          => '',
 254          'shortname'         => '',
 255          'summary'           => '',
 256          'adminuser'         => 'admin',
 257          'adminpass'         => '',
 258          'adminemail'        => '',
 259          'sitepreset'        => '',
 260          'supportemail'      => '',
 261          'upgradekey'        => '',
 262          'non-interactive'   => false,
 263          'agree-license'     => false,
 264          'allow-unstable'    => false,
 265          'skip-database'     => false,
 266          'help'              => false
 267      ),
 268      array(
 269          'h' => 'help'
 270      )
 271  );
 272  
 273  $interactive = empty($options['non-interactive']);
 274  $skipdatabase = $options['skip-database'];
 275  
 276  // set up language
 277  $lang = clean_param($options['lang'], PARAM_SAFEDIR);
 278  $languages = get_string_manager()->get_list_of_translations();
 279  if (array_key_exists($lang, $languages)) {
 280      $CFG->lang = $lang;
 281  }
 282  
 283  // Set up site admin preset.
 284  $sitepreset = clean_param($options['sitepreset'], PARAM_RAW);
 285  if (!empty($sitepreset)) {
 286      $CFG->setsitepresetduringinstall = $sitepreset;
 287  }
 288  
 289  if ($unrecognized) {
 290      $unrecognized = implode("\n  ", $unrecognized);
 291      cli_error(get_string('cliunknowoption', 'admin', $unrecognized));
 292  }
 293  
 294  if ($options['help']) {
 295      echo $help;
 296      die;
 297  }
 298  
 299  //Print header
 300  cli_logo();
 301  echo PHP_EOL;
 302  echo get_string('cliinstallheader', 'install', $CFG->target_release)."\n";
 303  
 304  //Fist select language
 305  if ($interactive) {
 306      cli_separator();
 307      // Do not put the langs into columns because it is not compatible with RTL.
 308      $default = $CFG->lang;
 309      cli_heading(get_string('chooselanguagehead', 'install'));
 310      if (array_key_exists($default, $languages)) {
 311          echo $default.' - '.$languages[$default]."\n";
 312      }
 313      if ($default !== 'en') {
 314          echo 'en - English (en)'."\n";
 315      }
 316      echo '? - '.get_string('availablelangs', 'install')."\n";
 317      $prompt = get_string('clitypevaluedefault', 'admin', $CFG->lang);
 318      $error = '';
 319      do {
 320          echo $error;
 321          $input = cli_input($prompt, $default);
 322  
 323          if ($input === '?') {
 324              echo implode("\n", $languages)."\n";
 325              $error = "\n";
 326  
 327          } else {
 328              $input = clean_param($input, PARAM_SAFEDIR);
 329  
 330              if (!array_key_exists($input, $languages)) {
 331                  $error = get_string('cliincorrectvalueretry', 'admin')."\n";
 332              } else {
 333                  $error = '';
 334              }
 335          }
 336      } while ($error !== '');
 337      $CFG->lang = $input;
 338  } else {
 339      // already selected and verified
 340  }
 341  
 342  // Set directorypermissions first
 343  $chmod = octdec(clean_param($options['chmod'], PARAM_INT));
 344  if ($interactive) {
 345      cli_separator();
 346      cli_heading(get_string('datarootpermission', 'install'));
 347      $prompt = get_string('clitypevaluedefault', 'admin', decoct($chmod));
 348      $error = '';
 349      do {
 350          echo $error;
 351          $input = cli_input($prompt, decoct($chmod));
 352          $input = octdec(clean_param($input, PARAM_INT));
 353          if (empty($input)) {
 354              $error = get_string('cliincorrectvalueretry', 'admin')."\n";
 355          } else {
 356              $error = '';
 357          }
 358      } while ($error !== '');
 359      $chmod = $input;
 360  
 361  } else {
 362      if (empty($chmod)) {
 363          $a = (object)array('option' => 'chmod', 'value' => decoct($chmod));
 364          cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 365      }
 366  }
 367  $CFG->directorypermissions = $chmod;
 368  $CFG->filepermissions      = ($CFG->directorypermissions & 0666);
 369  $CFG->umaskpermissions     = (($CFG->directorypermissions & 0777) ^ 0777);
 370  
 371  //We need wwwroot before we test dataroot
 372  $wwwroot = clean_param($options['wwwroot'], PARAM_URL);
 373  $wwwroot = trim($wwwroot, '/');
 374  if ($interactive) {
 375      cli_separator();
 376      cli_heading(get_string('wwwroot', 'install'));
 377      if (strpos($wwwroot, 'http') === 0) {
 378          $prompt = get_string('clitypevaluedefault', 'admin', $wwwroot);
 379      } else {
 380          $wwwroot = null;
 381          $prompt = get_string('clitypevalue', 'admin');
 382      }
 383      $error = '';
 384      do {
 385          echo $error;
 386          $input = cli_input($prompt, $wwwroot);
 387          $input = clean_param($input, PARAM_URL);
 388          $input = trim($input, '/');
 389          if (strpos($input, 'http') !== 0) {
 390              $error = get_string('cliincorrectvalueretry', 'admin')."\n";
 391          } else {
 392              $error = '';
 393          }
 394      } while ($error !== '');
 395      $wwwroot = $input;
 396  
 397  } else {
 398      if (strpos($wwwroot, 'http') !== 0) {
 399          $a = (object)array('option'=>'wwwroot', 'value'=>$wwwroot);
 400          cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 401      }
 402  }
 403  $CFG->wwwroot       = $wwwroot;
 404  $CFG->httpswwwroot  = $CFG->wwwroot;
 405  
 406  //We need dataroot before lang download
 407  $CFG->dataroot = $options['dataroot'];
 408  if ($interactive) {
 409      cli_separator();
 410      $i=0;
 411      while(is_dataroot_insecure()) {
 412          $parrent = dirname($CFG->dataroot);
 413          $i++;
 414          if ($parrent == '/' or $parrent == '.' or preg_match('/^[a-z]:\\\?$/i', $parrent) or ($i > 100)) {
 415              $CFG->dataroot = ''; //can not find secure location for dataroot
 416              break;
 417          }
 418          $CFG->dataroot = dirname($parrent).'/moodledata';
 419      }
 420      cli_heading(get_string('dataroot', 'install'));
 421      $error = '';
 422      do {
 423          if ($CFG->dataroot !== '') {
 424              $prompt = get_string('clitypevaluedefault', 'admin', $CFG->dataroot);
 425          } else {
 426              $prompt = get_string('clitypevalue', 'admin');
 427          }
 428          echo $error;
 429          $CFG->dataroot = cli_input($prompt, $CFG->dataroot);
 430          if ($CFG->dataroot === '') {
 431              $error = get_string('cliincorrectvalueretry', 'admin')."\n";
 432          } else if (is_dataroot_insecure()) {
 433              $CFG->dataroot = '';
 434              $error = get_string('pathsunsecuredataroot', 'install')."\n";
 435          } else {
 436              if (install_init_dataroot($CFG->dataroot, $CFG->directorypermissions)) {
 437                  $error = '';
 438              } else {
 439                  $a = (object)array('dataroot' => $CFG->dataroot);
 440                  $error = get_string('pathserrcreatedataroot', 'install', $a)."\n";
 441              }
 442          }
 443  
 444      } while ($error !== '');
 445  
 446  } else {
 447      if (is_dataroot_insecure()) {
 448          cli_error(get_string('pathsunsecuredataroot', 'install'));
 449      }
 450      if (!install_init_dataroot($CFG->dataroot, $CFG->directorypermissions)) {
 451          $a = (object)array('dataroot' => $CFG->dataroot);
 452          cli_error(get_string('pathserrcreatedataroot', 'install', $a));
 453      }
 454  }
 455  $CFG->tempdir       = $CFG->dataroot.'/temp';
 456  $CFG->backuptempdir = $CFG->tempdir.'/backup';
 457  $CFG->cachedir      = $CFG->dataroot.'/cache';
 458  $CFG->localcachedir = $CFG->dataroot.'/localcache';
 459  
 460  // download required lang packs
 461  if ($CFG->lang !== 'en') {
 462      $installer = new lang_installer($CFG->lang);
 463      $results = $installer->run();
 464      foreach ($results as $langcode => $langstatus) {
 465          if ($langstatus === lang_installer::RESULT_DOWNLOADERROR) {
 466              $a       = new stdClass();
 467              $a->url  = $installer->lang_pack_url($langcode);
 468              $a->dest = $CFG->dataroot.'/lang';
 469              cli_problem(get_string('remotedownloaderror', 'error', $a));
 470          }
 471      }
 472  }
 473  
 474  // switch the string_manager instance to stop using install/lang/
 475  $CFG->early_install_lang = false;
 476  $CFG->langotherroot      = $CFG->dataroot.'/lang';
 477  $CFG->langlocalroot      = $CFG->dataroot.'/lang';
 478  get_string_manager(true);
 479  
 480  // make sure we are installing stable release or require a confirmation
 481  if (isset($maturity)) {
 482      if (($maturity < MATURITY_STABLE) and !$options['allow-unstable']) {
 483          $maturitylevel = get_string('maturity'.$maturity, 'admin');
 484  
 485          if ($interactive) {
 486              cli_separator();
 487              cli_heading(get_string('notice'));
 488              echo get_string('maturitycorewarning', 'admin', $maturitylevel) . PHP_EOL;
 489              echo get_string('morehelp') . ': ' . get_docs_url('admin/versions') . PHP_EOL;
 490              echo get_string('continue') . PHP_EOL;
 491              $prompt = get_string('cliyesnoprompt', 'admin');
 492              $input = cli_input($prompt, '', array(get_string('clianswerno', 'admin'), get_string('cliansweryes', 'admin')));
 493              if ($input == get_string('clianswerno', 'admin')) {
 494                  exit(1);
 495              }
 496          } else {
 497              cli_problem(get_string('maturitycorewarning', 'admin', $maturitylevel));
 498              cli_error(get_string('maturityallowunstable', 'admin'));
 499          }
 500      }
 501  }
 502  
 503  // ask for db type - show only drivers available
 504  if ($interactive) {
 505      $options['dbtype'] = strtolower($options['dbtype']);
 506      cli_separator();
 507      cli_heading(get_string('databasetypehead', 'install'));
 508      foreach ($databases as $type=>$database) {
 509          echo " $type \n";
 510      }
 511      if (!empty($databases[$options['dbtype']])) {
 512          $prompt = get_string('clitypevaluedefault', 'admin', $options['dbtype']);
 513      } else {
 514          $prompt = get_string('clitypevalue', 'admin');
 515      }
 516      $CFG->dbtype = cli_input($prompt, $options['dbtype'], array_keys($databases));
 517  
 518  } else {
 519      if (empty($databases[$options['dbtype']])) {
 520          $a = (object)array('option'=>'dbtype', 'value'=>$options['dbtype']);
 521          cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 522      }
 523      $CFG->dbtype = $options['dbtype'];
 524  }
 525  $database = $databases[$CFG->dbtype];
 526  
 527  
 528  // We cannot do any validation until all DB connection data is provided.
 529  $hintdatabase = '';
 530  do {
 531      echo $hintdatabase;
 532  
 533      // Ask for db host.
 534      if ($interactive) {
 535          cli_separator();
 536          cli_heading(get_string('databasehost', 'install'));
 537          if ($options['dbhost'] !== '') {
 538              $prompt = get_string('clitypevaluedefault', 'admin', $options['dbhost']);
 539          } else {
 540              $prompt = get_string('clitypevalue', 'admin');
 541          }
 542          $CFG->dbhost = cli_input($prompt, $options['dbhost']);
 543  
 544      } else {
 545          $CFG->dbhost = $options['dbhost'];
 546      }
 547  
 548      // Ask for db name.
 549      if ($interactive) {
 550          cli_separator();
 551          cli_heading(get_string('databasename', 'install'));
 552          if ($options['dbname'] !== '') {
 553              $prompt = get_string('clitypevaluedefault', 'admin', $options['dbname']);
 554          } else {
 555              $prompt = get_string('clitypevalue', 'admin');
 556          }
 557          $CFG->dbname = cli_input($prompt, $options['dbname']);
 558  
 559      } else {
 560          $CFG->dbname = $options['dbname'];
 561      }
 562  
 563      // Ask for db prefix.
 564      if ($interactive) {
 565          cli_separator();
 566          cli_heading(get_string('dbprefix', 'install'));
 567          //TODO: solve somehow the prefix trouble for oci.
 568          if ($options['prefix'] !== '') {
 569              $prompt = get_string('clitypevaluedefault', 'admin', $options['prefix']);
 570          } else {
 571              $prompt = get_string('clitypevalue', 'admin');
 572          }
 573          $CFG->prefix = cli_input($prompt, $options['prefix']);
 574  
 575      } else {
 576          $CFG->prefix = $options['prefix'];
 577      }
 578  
 579      // Ask for db port.
 580      if ($interactive) {
 581          cli_separator();
 582          cli_heading(get_string('databaseport', 'install'));
 583          $prompt = get_string('clitypevaluedefault', 'admin', $options['dbport']);
 584          $CFG->dboptions['dbport'] = (int) cli_input($prompt, $options['dbport']);
 585  
 586      } else {
 587          $CFG->dboptions['dbport'] = (int) $options['dbport'];
 588      }
 589      if ($CFG->dboptions['dbport'] <= 0) {
 590          $CFG->dboptions['dbport'] = '';
 591      }
 592  
 593      // Ask for db socket.
 594      if ($CFG->ostype === 'WINDOWS') {
 595          $CFG->dboptions['dbsocket'] = '';
 596  
 597      } else if ($interactive and empty($CFG->dboptions['dbport'])) {
 598          cli_separator();
 599          cli_heading(get_string('databasesocket', 'install'));
 600          $prompt = get_string('clitypevaluedefault', 'admin', $options['dbsocket']);
 601          $CFG->dboptions['dbsocket'] = cli_input($prompt, $options['dbsocket']);
 602  
 603      } else {
 604          $CFG->dboptions['dbsocket'] = $options['dbsocket'];
 605      }
 606  
 607      // Ask for db user.
 608      if ($interactive) {
 609          cli_separator();
 610          cli_heading(get_string('databaseuser', 'install'));
 611          if ($options['dbuser'] !== '') {
 612              $prompt = get_string('clitypevaluedefault', 'admin', $options['dbuser']);
 613          } else {
 614              $prompt = get_string('clitypevalue', 'admin');
 615          }
 616          $CFG->dbuser = cli_input($prompt, $options['dbuser']);
 617  
 618      } else {
 619          $CFG->dbuser = $options['dbuser'];
 620      }
 621  
 622      // Ask for db password.
 623      if ($interactive) {
 624          cli_separator();
 625          cli_heading(get_string('databasepass', 'install'));
 626  
 627          if ($options['dbpass'] !== '') {
 628              $prompt = get_string('clitypevaluedefault', 'admin', $options['dbpass']);
 629          } else {
 630              $prompt = get_string('clitypevalue', 'admin');
 631          }
 632  
 633          $CFG->dbpass = cli_input($prompt, $options['dbpass']);
 634          if (function_exists('distro_pre_create_db')) { // Hook for distros needing to do something before DB creation.
 635              $distro = distro_pre_create_db($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix,
 636                      array('dbpersist' => 0, 'dbport' => $CFG->dboptions['dbport'], 'dbsocket' => $CFG->dboptions['dbsocket']),
 637                      $distro);
 638          }
 639          $hintdatabase = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix,
 640                  array('dbpersist' => 0, 'dbport' => $CFG->dboptions['dbport'], 'dbsocket' => $CFG->dboptions['dbsocket']));
 641  
 642      } else {
 643          $CFG->dbpass = $options['dbpass'];
 644          $hintdatabase = install_db_validate($database, $CFG->dbhost, $CFG->dbuser, $CFG->dbpass, $CFG->dbname, $CFG->prefix,
 645                  array('dbpersist' => 0, 'dbport' => $CFG->dboptions['dbport'], 'dbsocket' => $CFG->dboptions['dbsocket']));
 646          if ($hintdatabase !== '') {
 647              cli_error(get_string('dbconnectionerror', 'install'));
 648          }
 649      }
 650  } while ($hintdatabase !== '');
 651  
 652  // If --skip-database option is provided, we do not need to ask for site fullname, shortname, adminuser, adminpass, adminemail.
 653  // These fields will be requested during the database install part.
 654  if (!$skipdatabase) {
 655      // Ask for fullname.
 656      if ($interactive) {
 657          cli_separator();
 658          cli_heading(get_string('fullsitename', 'moodle'));
 659  
 660          if ($options['fullname'] !== '') {
 661              $prompt = get_string('clitypevaluedefault', 'admin', $options['fullname']);
 662          } else {
 663              $prompt = get_string('clitypevalue', 'admin');
 664          }
 665  
 666          do {
 667              $options['fullname'] = cli_input($prompt, $options['fullname']);
 668          } while (empty($options['fullname']));
 669      } else {
 670          if (empty($options['fullname'])) {
 671              $a = (object)['option' => 'fullname', 'value' => $options['fullname']];
 672              cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 673          }
 674      }
 675  
 676      // Ask for shortname.
 677      if ($interactive) {
 678          cli_separator();
 679          cli_heading(get_string('shortsitename', 'moodle'));
 680  
 681          if ($options['shortname'] !== '') {
 682              $prompt = get_string('clitypevaluedefault', 'admin', $options['shortname']);
 683          } else {
 684              $prompt = get_string('clitypevalue', 'admin');
 685          }
 686  
 687          do {
 688              $options['shortname'] = cli_input($prompt, $options['shortname']);
 689          } while (empty($options['shortname']));
 690      } else {
 691          if (empty($options['shortname'])) {
 692              $a = (object)['option' => 'shortname', 'value' => $options['shortname']];
 693              cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 694          }
 695      }
 696  
 697      // Ask for admin user name.
 698      if ($interactive) {
 699          cli_separator();
 700          cli_heading(get_string('cliadminusername', 'install'));
 701          if (!empty($options['adminuser'])) {
 702              $prompt = get_string('clitypevaluedefault', 'admin', $options['adminuser']);
 703          } else {
 704              $prompt = get_string('clitypevalue', 'admin');
 705          }
 706          do {
 707              $options['adminuser'] = cli_input($prompt, $options['adminuser']);
 708          } while (empty($options['adminuser']) or $options['adminuser'] === 'guest');
 709      } else {
 710          if ((empty($options['adminuser']) || $options['adminuser'] === 'guest')) {
 711              $a = (object)['option' => 'adminuser', 'value' => $options['adminuser']];
 712              cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 713          }
 714      }
 715  
 716      // Ask for admin user password.
 717      if ($interactive) {
 718          cli_separator();
 719          cli_heading(get_string('cliadminpassword', 'install'));
 720          $prompt = get_string('clitypevalue', 'admin');
 721          do {
 722              $options['adminpass'] = cli_input($prompt);
 723          } while (empty($options['adminpass']) or $options['adminpass'] === 'admin');
 724      } else {
 725          if ((empty($options['adminpass']) or $options['adminpass'] === 'admin')) {
 726              $a = (object)['option' => 'adminpass', 'value' => $options['adminpass']];
 727              cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 728          }
 729      }
 730  
 731      // Ask for the admin email address.
 732      if ($interactive) {
 733          cli_separator();
 734          cli_heading(get_string('cliadminemail', 'install'));
 735          $prompt = get_string('clitypevaluedefault', 'admin', $options['adminemail']);
 736          $options['adminemail'] = cli_input($prompt, $options['adminemail']);
 737      }
 738  
 739      // Validate that the address provided was an e-mail address.
 740      if (!empty($options['adminemail']) && !validate_email($options['adminemail'])) {
 741          $a = (object)['option' => 'adminemail', 'value' => $options['adminemail']];
 742          cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 743      }
 744  
 745      // Ask for the support email address.
 746      if ($interactive) {
 747          cli_separator();
 748          cli_heading(get_string('clisupportemail', 'install'));
 749          $prompt = get_string('clitypevaluedefault', 'admin', $options['supportemail']);
 750          $options['supportemail'] = cli_input($prompt, $options['supportemail']);
 751      }
 752  
 753      // Validate that the support email address provided is valid.
 754      if (!empty($options['supportemail']) && !validate_email($options['supportemail'])) {
 755          $a = (object)['option' => 'supportemail', 'value' => $options['supportemail']];
 756          cli_error(get_string('cliincorrectvalueerror', 'admin', $a));
 757      }
 758  }
 759  
 760  // Ask for the upgrade key.
 761  if ($interactive) {
 762      cli_separator();
 763      cli_heading(get_string('upgradekeyset', 'admin'));
 764      if ($options['upgradekey'] !== '') {
 765          $prompt = get_string('clitypevaluedefault', 'admin', $options['upgradekey']);
 766          $options['upgradekey'] = cli_input($prompt, $options['upgradekey']);
 767      } else {
 768          $prompt = get_string('clitypevalue', 'admin');
 769          $options['upgradekey'] = cli_input($prompt);
 770      }
 771  }
 772  
 773  // Set the upgrade key if it was provided.
 774  if ($options['upgradekey'] !== '') {
 775      $CFG->upgradekey = $options['upgradekey'];
 776  }
 777  
 778  // The user does not also need to pass agree-license when --skip-database is provided as the user will need to accept
 779  // the license again in the database install part.
 780  if (!$skipdatabase) {
 781      if ($interactive) {
 782          if (!$options['agree-license']) {
 783              cli_separator();
 784              cli_heading(get_string('copyrightnotice'));
 785              echo "Moodle  - Modular Object-Oriented Dynamic Learning Environment\n";
 786              echo get_string('gpl3')."\n\n";
 787              echo get_string('doyouagree')."\n";
 788              $prompt = get_string('cliyesnoprompt', 'admin');
 789              $input = cli_input($prompt, '', array(get_string('clianswerno', 'admin'), get_string('cliansweryes', 'admin')));
 790              if ($input == get_string('clianswerno', 'admin')) {
 791                  exit(1);
 792              }
 793          }
 794      } else {
 795          if (!$options['agree-license'] && !$skipdatabase) {
 796              cli_error(get_string('climustagreelicense', 'install'));
 797          }
 798      }
 799  }
 800  
 801  // Finally we have all info needed for config.php
 802  $configphp = install_generate_configphp($database, $CFG);
 803  umask(0137);
 804  if (($fh = fopen($configfile, 'w')) !== false) {
 805      fwrite($fh, $configphp);
 806      fclose($fh);
 807  }
 808  
 809  if (!file_exists($configfile)) {
 810      cli_error('Can not create config file.');
 811  }
 812  
 813  // remember selected language
 814  $installlang = $CFG->lang;
 815  // return back to original dir before executing setup.php which changes the dir again
 816  chdir($olddir);
 817  // We have config.php, it is a real php script from now on :-)
 818  require($configfile);
 819  
 820  // use selected language
 821  $CFG->lang = $installlang;
 822  $SESSION->lang = $CFG->lang;
 823  
 824  require("$CFG->dirroot/version.php");
 825  
 826  // Test environment first.
 827  require_once($CFG->libdir . '/environmentlib.php');
 828  list($envstatus, $environment_results) = check_moodle_environment(normalize_version($release), ENV_SELECT_RELEASE);
 829  if (!$envstatus) {
 830      $errors = environment_get_errors($environment_results);
 831      cli_heading(get_string('environment', 'admin'));
 832      foreach ($errors as $error) {
 833          list($info, $report) = $error;
 834          echo "!! $info !!\n$report\n\n";
 835      }
 836      exit(1);
 837  }
 838  
 839  // Test plugin dependencies.
 840  $failed = array();
 841  if (!core_plugin_manager::instance()->all_plugins_ok($version, $failed)) {
 842      cli_problem(get_string('pluginscheckfailed', 'admin', array('pluginslist' => implode(', ', array_unique($failed)))));
 843      cli_error(get_string('pluginschecktodo', 'admin'));
 844  }
 845  
 846  if (!$skipdatabase) {
 847      install_cli_database($options, $interactive);
 848      // This needs to happen at the end to ensure it occurs after all caches
 849      // have been purged for the last time.
 850      // This will build a cached version of the current theme for the user
 851      // to immediately start browsing the site.
 852      require_once($CFG->libdir.'/upgradelib.php');
 853      upgrade_themes();
 854  } else {
 855      echo get_string('cliskipdatabase', 'install')."\n";
 856  }
 857  
 858  echo get_string('cliinstallfinished', 'install')."\n";
 859  exit(0); // 0 means success