install.php 30.9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<?php
// $Id$

require_once './includes/install.inc';

/**
 * The Drupal installation happens in a series of steps. We begin by verifying
 * that the current environment meets our minimum requirements. We then go
 * on to verify that settings.php is properly configured. From there we
 * connect to the configured database and verify that it meets our minimum
 * requirements. Finally we can allow the user to select an installation
 * profile and complete the installation process.
 *
 * @param $phase
 *   The installation phase we should proceed to.
 */
function install_main() {
drumm's avatar
drumm committed
18
  global $profile, $install_locale;
19 20 21
  require_once './includes/bootstrap.inc';
  drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
  require_once './modules/system/system.install';
22
  require_once './includes/file.inc';
23

24 25 26
  // Ensure correct page headers are sent (e.g. caching)
  drupal_page_header();

27 28 29 30 31 32 33 34
  // Check existing settings.php.
  $verify = install_verify_settings();

  if ($verify) {
    // Establish a connection to the database.
    require_once './includes/database.inc';
    db_set_active();
    // Check if Drupal is installed.
35 36
    $task = install_verify_drupal();
    if ($task == 'done') {
37 38 39
      install_already_done_error();
    }
  }
40 41 42
  else {
    $task = NULL;
  }
43 44 45 46 47 48 49 50 51 52 53 54 55 56

  // Load module basics (needed for hook invokes).
  include_once './includes/module.inc';
  $module_list['system']['filename'] = 'modules/system/system.module';
  $module_list['filter']['filename'] = 'modules/filter/filter.module';
  module_list(TRUE, FALSE, FALSE, $module_list);
  drupal_load('module', 'system');
  drupal_load('module', 'filter');

  // Decide which profile to use.
  if (!empty($_GET['profile'])) {
    $profile = preg_replace('/[^a-zA-Z_0-9]/', '', $_GET['profile']);
  }
  elseif ($profile = install_select_profile()) {
57
    install_goto("install.php?profile=$profile");
58 59
  }
  else {
60
    install_no_profile_error();
61
  }
drumm's avatar
drumm committed
62

63 64 65
  // Load the profile.
  require_once "./profiles/$profile/$profile.profile";

drumm's avatar
drumm committed
66 67 68 69 70 71 72 73
  // Locale selection
  if (!empty($_GET['locale'])) {
    $install_locale = preg_replace('/[^a-zA-Z_0-9]/', '', $_GET['locale']);
  }
  elseif (($install_locale = install_select_locale($profile)) !== FALSE) {
    install_goto("install.php?profile=$profile&locale=$install_locale");
  }

74 75 76 77
  // Tasks come after the database is set up
  if (!$task) {
    // Check the installation requirements for Drupal and this profile.
    install_check_requirements($profile);
78

79 80 81 82 83
    // Verify existence of all required modules.
    $modules = drupal_verify_profile($profile, $install_locale);
    if (!$modules) {
      install_missing_modules_error($profile);
    }
84

85 86 87 88 89
    // Change the settings.php information if verification failed earlier.
    // Note: will trigger a redirect if database credentials change.
    if (!$verify) {
      install_change_settings($profile, $install_locale);
    }
90

91 92
    // Perform actual installation defined in the profile.
    drupal_install_profile($profile, $modules);
93

94 95 96 97 98 99 100 101
    // Warn about settings.php permissions risk
    $settings_file = './'. conf_path() .'/settings.php';
    if (!drupal_verify_install_file($settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE)) {
      drupal_set_message(st('All necessary changes to %file have been made, so you should now remove write permissions to this file. Failure to remove write permissions to this file is a security risk.', array('%file' => $settings_file)), 'error');
    }
    else {
      drupal_set_message(st('All necessary changes to %file have been made. It has been set to read-only for security.', array('%file' => $settings_file)));
    }
102
  }
103

104 105
  // The database is set up, turn to further tasks.
  install_tasks($profile, $task);
106 107 108 109 110 111
}

/**
 * Verify if Drupal is installed.
 */
function install_verify_drupal() {
112 113 114 115 116
  // Read the variable manually using the @ so we don't trigger an error if it fails.
  $result = @db_query("SELECT value FROM {variable} WHERE name = 'install_task'");
  if ($result) {
    return unserialize(db_result($result));
  }
117 118 119 120 121 122 123 124 125
}

/**
 * Verify existing settings.php
 */
function install_verify_settings() {
  global $db_prefix, $db_type, $db_url;

  // Verify existing settings (if any).
126
  if (!empty($db_url)) {
127 128 129
    // We need this because we want to run form_get_errors.
    include_once './includes/form.inc';

130
    $url = parse_url(is_array($db_url) ? $db_url['default'] : $db_url);
131 132 133
    $db_user = urldecode($url['user']);
    $db_pass = urldecode($url['pass']);
    $db_host = urldecode($url['host']);
134
    $db_port = isset($url['port']) ? urldecode($url['port']) : '';
135 136 137
    $db_path = ltrim(urldecode($url['path']), '/');
    $settings_file = './'. conf_path() .'/settings.php';

138
    _install_settings_form_validate($db_prefix, $db_type, $db_user, $db_pass, $db_host, $db_port, $db_path, $settings_file);
139 140 141 142 143 144 145 146 147 148
    if (!form_get_errors()) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Configure and rewrite settings.php.
 */
149 150
function install_change_settings($profile = 'default', $install_locale = '') {
  global $db_url, $db_type, $db_prefix;
151

152
  $url = parse_url(is_array($db_url) ? $db_url['default'] : $db_url);
153 154 155
  $db_user = urldecode($url['user']);
  $db_pass = urldecode($url['pass']);
  $db_host = urldecode($url['host']);
156
  $db_port = isset($url['port']) ? urldecode($url['port']) : '';
157
  $db_path = ltrim(urldecode($url['path']), '/');
158 159
  $conf_path = './'. conf_path();
  $settings_file = $conf_path .'/settings.php';
160 161 162

  // We always need this because we want to run form_get_errors.
  include_once './includes/form.inc';
163
  drupal_maintenance_theme();
164
  install_task_list('database');
165 166 167

  // The existing database settings are not working, so we need write access
  // to settings.php to change them.
168
  $writable = FALSE;
169 170 171 172 173
  $file = $conf_path;
  // Verify the directory exists.
  if (drupal_verify_install_file($conf_path, FILE_EXIST, 'dir')) {
    // Check to see if a settings.php already exists
    if (drupal_verify_install_file($settings_file, FILE_EXIST)) {
174 175
      // If it does, make sure it is writable
      $writable = drupal_verify_install_file($settings_file, FILE_READABLE|FILE_WRITABLE);
176 177 178 179
      $file = $settings_file;
    }
    else {
      // If not, makes sure the directory is.
180
      $writable = drupal_verify_install_file($conf_path, FILE_READABLE|FILE_WRITABLE, 'dir');
181 182 183
    }
  }

184
  if (!$writable) {
185
    drupal_set_message(st('The @drupal installer requires write permissions to %file during the installation process.', array('@drupal' => drupal_install_profile_name(), '%file' => $file)), 'error');
186

drumm's avatar
drumm committed
187
    drupal_set_title(st('Drupal database setup'));
188 189 190 191
    print theme('install_page', '');
    exit;
  }

192
  $output = drupal_get_form('install_settings_form', $profile, $install_locale, $settings_file, $db_url, $db_type, $db_prefix, $db_user, $db_pass, $db_host, $db_port, $db_path);
drumm's avatar
drumm committed
193
  drupal_set_title(st('Database configuration'));
194 195 196
  print theme('install_page', $output);
  exit;
}
197

198

199 200 201
/**
 * Form API array definition for install_settings.
 */
202
function install_settings_form($profile, $install_locale, $settings_file, $db_url, $db_type, $db_prefix, $db_user, $db_pass, $db_host, $db_port, $db_path) {
203 204 205
  if (empty($db_host)) {
    $db_host = 'localhost';
  }
206 207 208
  $db_types = drupal_detect_database_types();
  if (count($db_types) == 0) {
    $form['no_db_types'] = array(
drumm's avatar
drumm committed
209
      '#value' => st('Your web server does not appear to support any common database types. Check with your hosting provider to see if they offer any databases that <a href="@drupal-databases">Drupal supports</a>.', array('@drupal-databases' => 'http://drupal.org/node/270#database')),
210 211 212 213 214
    );
  }
  else {
    $form['basic_options'] = array(
      '#type' => 'fieldset',
drumm's avatar
drumm committed
215
      '#title' => st('Basic options'),
216
      '#description' => '<p>'. st('To set up your @drupal database, enter the following information.', array('@drupal' => drupal_install_profile_name())) .'</p>',
217 218 219 220
    );

    if (count($db_types) > 1) {
      // Database type
221
      $db_types = drupal_detect_database_types();
222 223
      $form['basic_options']['db_type'] = array(
        '#type' => 'radios',
drumm's avatar
drumm committed
224
        '#title' => st('Database type'),
225
        '#required' => TRUE,
226 227
        '#options' => $db_types,
        '#default_value' => ($db_type ? $db_type : current($db_types)),
228
        '#description' => st('The type of database your @drupal data will be stored in.', array('@drupal' => drupal_install_profile_name())),
229
      );
230
      $db_path_description = st('The name of the database your @drupal data will be stored in. It must exist on your server before @drupal can be installed.', array('@drupal' => drupal_install_profile_name()));
231 232 233
    }
    else {
      if (count($db_types) == 1) {
234
        $db_types = array_values($db_types);
235 236 237 238
        $form['basic_options']['db_type'] = array(
          '#type' => 'hidden',
          '#value' => $db_types[0],
        );
239
        $db_path_description = st('The name of the %db_type database your @drupal data will be stored in. It must exist on your server before @drupal can be installed.', array('%db_type' => $db_types[0], '@drupal' => drupal_install_profile_name()));
240 241 242 243 244 245
      }
    }

    // Database name
    $form['basic_options']['db_path'] = array(
      '#type' => 'textfield',
drumm's avatar
drumm committed
246
      '#title' => st('Database name'),
247 248 249 250 251 252 253 254 255 256
      '#default_value' => $db_path,
      '#size' => 45,
      '#maxlength' => 45,
      '#required' => TRUE,
      '#description' => $db_path_description
    );

    // Database username
    $form['basic_options']['db_user'] = array(
      '#type' => 'textfield',
drumm's avatar
drumm committed
257
      '#title' => st('Database username'),
258 259 260 261 262 263 264 265 266
      '#default_value' => $db_user,
      '#size' => 45,
      '#maxlength' => 45,
      '#required' => TRUE,
    );

    // Database username
    $form['basic_options']['db_pass'] = array(
      '#type' => 'password',
drumm's avatar
drumm committed
267
      '#title' => st('Database password'),
268 269 270 271 272 273 274
      '#default_value' => $db_pass,
      '#size' => 45,
      '#maxlength' => 45,
    );

    $form['advanced_options'] = array(
      '#type' => 'fieldset',
drumm's avatar
drumm committed
275
      '#title' => st('Advanced options'),
276 277
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
278
      '#description' => st("These options are only necessary for some sites. If you're not sure what you should enter here, leave the default settings or check with your hosting provider.")
279 280 281 282 283
    );

    // Database host
    $form['advanced_options']['db_host'] = array(
      '#type' => 'textfield',
drumm's avatar
drumm committed
284
      '#title' => st('Database host'),
285 286 287 288
      '#default_value' => $db_host,
      '#size' => 45,
      '#maxlength' => 45,
      '#required' => TRUE,
drumm's avatar
drumm committed
289
      '#description' => st('If your database is located on a different server, change this.'),
290 291
    );

292 293 294 295 296 297 298 299 300 301
    // Database port
    $form['advanced_options']['db_port'] = array(
      '#type' => 'textfield',
      '#title' => st('Database port'),
      '#default_value' => $db_port,
      '#size' => 45,
      '#maxlength' => 45,
      '#description' => st('If your database server is listening to a non-standard port, enter its number.'),
    );

302
    // Table prefix
303 304
    $form['advanced_options']['db_prefix'] = array(
      '#type' => 'textfield',
305
      '#title' => st('Table prefix'),
306 307 308
      '#default_value' => $db_prefix,
      '#size' => 45,
      '#maxlength' => 45,
309
      '#description' => st('If more than one @drupal website will be sharing this database, enter a table prefix for your @drupal site here.', array('@drupal' => drupal_install_profile_name())),
310 311 312 313
    );

    $form['save'] = array(
      '#type' => 'submit',
drumm's avatar
drumm committed
314
      '#value' => st('Save configuration'),
315 316 317 318 319
    );

    $form['errors'] = array();
    $form['settings_file'] = array('#type' => 'value', '#value' => $settings_file);
    $form['_db_url'] = array('#type' => 'value');
drumm's avatar
drumm committed
320
    $form['#action'] = "install.php?profile=$profile" . ($install_locale ? "&locale=$install_locale" : '');
321 322
    $form['#redirect'] = NULL;
  }
323
  return $form;
324 325 326 327
}
/**
 * Form API validate for install_settings form.
 */
328
function install_settings_form_validate($form_id, $form_values, $form) {
329
  global $db_url;
330
  _install_settings_form_validate($form_values['db_prefix'], $form_values['db_type'], $form_values['db_user'], $form_values['db_pass'], $form_values['db_host'], $form_values['db_port'], $form_values['db_path'], $form_values['settings_file'], $form);
331 332 333 334 335
}

/**
 * Helper function for install_settings_validate.
 */
336
function _install_settings_form_validate($db_prefix, $db_type, $db_user, $db_pass, $db_host, $db_port, $db_path, $settings_file, $form = NULL) {
337 338
  global $db_url;

339
  // Verify the table prefix
340 341
  if (!empty($db_prefix) && is_string($db_prefix) && !preg_match('/^[A-Za-z0-9_.]+$/', $db_prefix)) {
    form_set_error('db_prefix', st('The database table prefix you have entered, %db_prefix, is invalid. The table prefix can only contain alphanumeric characters, underscores or dots.', array('%db_prefix' => $db_prefix)), 'error');
342 343
  }

344 345 346 347
  if (!empty($db_port) && !is_numeric($db_port)) {
    form_set_error('db_port', st('Database port must be a number.'));
  }

348 349
  // Check database type
  if (!isset($form)) {
350 351
    $_db_url = is_array($db_url) ? $db_url['default'] : $db_url;
    $db_type = substr($_db_url, 0, strpos($_db_url, '://'));
352 353 354
  }
  $databases = drupal_detect_database_types();
  if (!in_array($db_type, $databases)) {
355
    form_set_error('db_type', st("In your %settings_file file you have configured @drupal to use a %db_type server, however your PHP installation currently does not support this database type.", array('%settings_file' => $settings_file, '@drupal' => drupal_install_profile_name(), '%db_type' => $db_type)));
356 357 358
  }
  else {
    // Verify
359
    $db_url = $db_type .'://'. urlencode($db_user) . ($db_pass ? ':'. urlencode($db_pass) : '') .'@'. ($db_host ? urlencode($db_host) : 'localhost') . ($db_port ? ":$db_port" : '') .'/'. urlencode($db_path);
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379
    if (isset($form)) {
      form_set_value($form['_db_url'], $db_url);
    }
    $success = array();

    $function = 'drupal_test_'. $db_type;
    if (!$function($db_url, $success)) {
      if (isset($success['CONNECT'])) {
        form_set_error('db_type', st('In order for Drupal to work and to proceed with the installation process you must resolve all permission issues reported above. We were able to verify that we have permission for the following commands: %commands. For more help with configuring your database server, see the <a href="http://drupal.org/node/258">Installation and upgrading handbook</a>. If you are unsure what any of this means you should probably contact your hosting provider.', array('%commands' => implode($success, ', '))));
      }
      else {
        form_set_error('db_type', '');
      }
    }
  }
}

/**
 * Form API submit for install_settings form.
 */
380
function install_settings_form_submit($form_id, $form_values) {
drumm's avatar
drumm committed
381
  global $profile, $install_locale;
382 383 384 385 386 387 388 389 390 391 392 393 394

  // Update global settings array and save
  $settings['db_url'] = array(
    'value'    => $form_values['_db_url'],
    'required' => TRUE,
  );
  $settings['db_prefix'] = array(
    'value'    => $form_values['db_prefix'],
    'required' => TRUE,
  );
  drupal_rewrite_settings($settings);

  // Continue to install profile step
drumm's avatar
drumm committed
395
  install_goto("install.php?profile=$profile" . ($install_locale ? "&locale=$install_locale" : ''));
396 397 398
}

/**
399 400 401
 * Find all .profile files.
 */
function install_find_profiles() {
Dries's avatar
Dries committed
402
  return file_scan_directory('./profiles', '\.profile$', array('.', '..', 'CVS'), 0, TRUE, 'name', 0);
403 404 405 406
}

/**
 * Allow admin to select which profile to install.
407 408 409 410 411 412 413
 *
 * @return
 *   The selected profile.
 */
function install_select_profile() {
  include_once './includes/form.inc';

414
  $profiles = install_find_profiles();
415 416 417 418 419 420 421 422
  // Don't need to choose profile if only one available.
  if (sizeof($profiles) == 1) {
    $profile = array_pop($profiles);
    require_once $profile->filename;
    return $profile->name;
  }
  elseif (sizeof($profiles) > 1) {
    foreach ($profiles as $profile) {
423
      if ($_POST['profile'] == $profile->name) {
424 425 426
        return $profile->name;
      }
    }
427 428

    drupal_maintenance_theme();
429
    install_task_list('profile');
430

drumm's avatar
drumm committed
431
    drupal_set_title(st('Select an installation profile'));
432
    print theme('install_page', drupal_get_form('install_select_profile_form', $profiles));
433 434 435 436
    exit;
  }
}

437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
function install_select_profile_form($profiles) {
  foreach ($profiles as $profile) {
    include_once($profile->filename);
    // Load profile details.
    $function = $profile->name .'_profile_details';
    if (function_exists($function)) {
      $details = $function();
    }
    // If set, used defined name. Otherwise use file name.
    $name = isset($details['name']) ? $details['name'] : $profile->name;
    $form['profile'][$name] = array(
      '#type' => 'radio',
      '#value' => 'default',
      '#return_value' => $profile->name,
      '#title' => $name,
      '#description' => isset($details['description']) ? $details['description'] : '',
      '#parents' => array('profile'),
    );
  }
  $form['submit'] =  array(
    '#type' => 'submit',
drumm's avatar
drumm committed
458 459 460 461 462 463
    '#value' => st('Save configuration'),
  );
  return $form;
}

/**
464 465 466 467 468 469 470 471 472 473
 * Find all .po files for the current profile.
 */
function install_find_locales($profilename) {
  $locales = file_scan_directory('./profiles/'. $profilename, '\.po$', array('.', '..', 'CVS'), 0, FALSE);
  array_unshift($locales, (object) array('name' => 'en'));
  return $locales;
}

/**
 * Allow admin to select which locale to use for the current profile.
drumm's avatar
drumm committed
474 475 476 477 478 479 480 481
 *
 * @return
 *   The selected language.
 */
function install_select_locale($profilename) {
  include_once './includes/file.inc';
  include_once './includes/form.inc';

482 483
  // Find all available locales.
  $locales = install_find_locales($profilename);
drumm's avatar
drumm committed
484 485 486 487

  // Don't need to choose locale if only one (English) is available.
  if (sizeof($locales) == 1) {
    return FALSE;
488 489
  }
  else {
drumm's avatar
drumm committed
490 491 492 493 494 495 496
    foreach ($locales as $locale) {
      if ($_POST['locale'] == $locale->name) {
        return $locale->name;
      }
    }

    drupal_maintenance_theme();
497
    install_task_list('locale');
drumm's avatar
drumm committed
498 499 500 501 502 503 504 505 506

    drupal_set_title(st('Choose your preferred language'));
    print theme('install_page', drupal_get_form('install_select_locale_form', $locales));
    exit;
  }
}

function install_select_locale_form($locales) {
  include_once './includes/locale.inc';
507
  $languages = _locale_get_predefined_list();
drumm's avatar
drumm committed
508 509 510 511
  foreach ($locales as $locale) {
    // Try to use verbose locale name
    $name = $locale->name;
    if (isset($languages[$name])) {
512
      $name = $languages[$name][0] . (isset($languages[$name][1]) ? ' '. st('(@language)', array('@language' => $languages[$name][1])) : '');
drumm's avatar
drumm committed
513 514 515 516 517
    }
    $form['locale'][$locale->name] = array(
      '#type' => 'radio',
      '#return_value' => $locale->name,
      '#default_value' => ($locale->name == 'en' ? TRUE : FALSE),
518
      '#title' => $name . ($locale->name == 'en' ? ' '. st('(built-in)') : ''),
drumm's avatar
drumm committed
519 520 521 522 523 524
      '#parents' => array('locale')
    );
  }
  $form['submit'] =  array(
    '#type' => 'submit',
    '#value' => st('Save configuration'),
525 526 527 528
  );
  return $form;
}

529 530 531 532 533
/**
 * Show an error page when there are no profiles available.
 */
function install_no_profile_error() {
  drupal_maintenance_theme();
534
  install_task_list('profile');
drumm's avatar
drumm committed
535
  drupal_set_title(st('No profiles available'));
536
  print theme('install_page', '<p>'. st('We were unable to find any installer profiles. Installer profiles tell us what modules to enable and what schema to install in the database. A profile is necessary to continue with the installation process.') .'</p>');
537 538 539 540 541 542 543 544
  exit;
}


/**
 * Show an error page when Drupal has already been installed.
 */
function install_already_done_error() {
545 546
  global $base_url;

547
  drupal_maintenance_theme();
drumm's avatar
drumm committed
548 549
  drupal_set_title(st('Drupal already installed'));
  print theme('install_page', st('<ul><li>To start over, you must empty your existing database.</li><li>To install to a different database, edit the appropriate <em>settings.php</em> file in the <em>sites</em> folder.</li><li>To upgrade an existing installation, proceed to the <a href="@base-url/update.php">update script</a>.</li></ul>', array('@base-url' => $base_url)));
550 551 552
  exit;
}

553 554 555 556 557 558 559
/**
 * Show an error page when Drupal is missing required modules.
 */
function install_missing_modules_error($profile) {
  global $base_url;

  drupal_maintenance_theme();
560
  install_task_list('requirements');
561 562 563 564 565
  drupal_set_title(st('Modules missing'));
  print theme('install_page', '<p>'. st('One or more required modules are missing. Please check the error messages and <a href="!url">try again</a>.', array('!url' => "install.php?profile=$profile")) .'</p>');
  exit;
}

566
/**
567
 * Tasks performed after the database is initialized. Called from install.php.
568
 */
569
function install_tasks($profile, $task) {
570
  global $base_url;
571
  $output = '';
572 573 574 575 576 577

  // Bootstrap newly installed Drupal, while preserving existing messages.
  $messages = $_SESSION['messages'];
  drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
  $_SESSION['messages'] = $messages;

578
  // Build a page for a final task.
579
  drupal_maintenance_theme();
580 581 582
  if (empty($task)) {
    variable_set('install_task', 'configure');
    $task = 'configure';
583 584
  }

585 586
  if ($task == 'configure') {
    drupal_set_title(st('Configure site'));
587 588
    // Build menu to allow clean URL check.
    menu_rebuild();
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605

    // We break the form up so we can tell when it's been successfully
    // submitted.
    $form = drupal_retrieve_form('install_configure_form');

    // In order to find out if the form was successfully submitted or not,
    // we do a little song and dance to set the form to 'programmed' and check
    // to make sure this is really the form being submitted. It'd better be.
    if ($_POST && $_POST['form_id'] == 'install_configure_form') {
      $form['#programmed'] = TRUE;
      $form['#post'] = $_POST;
    }

    if (!drupal_process_form('install_configure_form', $form)) {
      $output = drupal_render_form('install_configure_form', $form);
      install_task_list('configure');
    }
606
  }
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637

  // If we have no output, then install.php is done and now we turn to
  // our profile to run it's own tasks.
  if (empty($output)) {
    // Profile might define more tasks.
    $function = $profile .'_profile_final';
    if (function_exists($function)) {
      // More tasks are required by this profile.
      // $task is sent through as a reference and may be changed!
      $output = $function($task);
    }

    // Safety: if the profile doesn't do anything, catch it.
    if ($task == 'configure') {
      $task = 'finished';
    }

    // Display default 'finished' page to user. A custom finished page
    // can be displayed by skipping this step and going to 'done' directly.
    if ($task == 'finished') {
      drupal_set_title(st('@drupal installation complete', array('@drupal' => drupal_install_profile_name())));
      $page = '<p>'. st('Congratulations, @drupal has been successfully installed.', array('@drupal' => drupal_install_profile_name())) .'</p>';
      $page .= $output;
      $messages = drupal_set_message();
      $page .= '<p>'. (isset($messages['error']) ? st('Please review the messages above before continuing on to <a href="@url">your new site</a>.', array('@url' => url(''))) : st('You may now visit <a href="@url">your new site</a>.', array('@url' => url('')))) .'</p>';
      $output = $page;
      $task = 'done';
    }

    // The end of the install process. Remember profile used.
    if ($task == 'done') {
638 639
      // Rebuild menu to get content type links registered by the profile,
      // and possibly any other menu items created through the tasks.
640
      menu_rebuild();
641 642 643 644 645 646 647
      variable_set('install_profile', $profile);
    }

    // Set task for user, and remember the task in the database.
    install_task_list($task);
    variable_set('install_task', $task);

648 649 650 651 652
  }
  // Output page.
  print theme('maintenance_page', $output);
}

653 654 655 656 657 658 659 660 661 662
/**
 * Page to check installation requirements and report any errors.
 */
function install_check_requirements($profile) {
  $requirements = drupal_check_profile($profile);
  $severity = drupal_requirements_severity($requirements);

  // If there are issues, report them.
  if ($severity == REQUIREMENT_ERROR) {
    drupal_maintenance_theme();
663
    install_task_list('requirements');
664 665 666 667 668 669 670

    foreach ($requirements as $requirement) {
      if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
        drupal_set_message($requirement['description'] .' ('. st('Currently using !item !version', array('!item' => $requirement['title'], '!version' => $requirement['value'])) .')', 'error');
      }
    }

671
    drupal_set_title(st('Incompatible environment'));
672 673 674 675 676
    print theme('install_page', '');
    exit;
  }
}

677 678 679 680 681 682 683 684 685
/**
 * Add the installation task list to the current page.
 */
function install_task_list($active = NULL) {
  // Default list of tasks.
  $tasks = array(
    'profile' => st('Choose profile'),
    'locale' => st('Choose language'),
    'requirements' => st('Verify requirements'),
686
    'database' => st('Setup database'),
687
    'configure' => st('Configure site'),
688
  );
Dries's avatar
Dries committed
689

690
  $profiles = install_find_profiles();
691
  // Remove profiles if only one profile exists.
692
  if (count($profiles) == 1) {
693 694 695 696
    unset($tasks['profile']);
  }

  // Remove locale if no install profiles use them.
697 698
  $profile = isset($_GET['profile']) && isset($profiles[$_GET['profile']]) ? $_GET['profile'] : '.';
  if (count(install_find_locales($profile)) == 1) {
699 700 701
    unset($tasks['locale']);
  }

702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
  if ($profile) {
    $function = $profile .'_profile_task_list';
    if (function_exists($function)) {
      $result = $function();
      if (is_array($result)) {
        $tasks += $result;
      }
    }
  }

  // Add finished step as the last task.
  $tasks += array('finished' => st('Finished'));

  // Let the theming function know that 'finished' and 'done'
  // include everything, so every step is completed.
  if (in_array($active, array('finished', 'done'))) {
    $active = NULL;
  }
720 721 722
  drupal_set_content('left', theme_task_list($tasks, $active));
}

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861
function install_configure_form() {
  // This is necessary to add the task to the $_GET args so the install
  // system will know that it is done and we've taken over.

  $form['intro'] = array(
    '#value' => st('To configure your web site, please provide the following information.'),
    '#weight' => -10,
  );
  $form['site_information'] = array(
    '#type' => 'fieldset',
    '#title' => st('Site information'),
    '#collapsible' => FALSE,
  );
  $form['site_information']['site_name'] = array(
    '#type' => 'textfield',
    '#title' => st('Site name'),
    '#default_value' => variable_get('site_name', 'Drupal'),
    '#required' => TRUE,
    '#weight' => -20,
  );
  $form['site_information']['site_mail'] = array(
    '#type' => 'textfield',
    '#title' => st('Site e-mail address'),
    '#default_value' => variable_get('site_mail', ini_get('sendmail_from')),
    '#description' => st('A valid e-mail address to be used as the "From" address by the auto-mailer during registration, new password requests, notifications, etc.  To lessen the likelihood of e-mail being marked as spam, this e-mail address should use the same domain as the website.'),
    '#required' => TRUE,
    '#weight' => -15,
  );
  $form['admin_account'] = array(
    '#type' => 'fieldset',
    '#title' => st('Administrator account'),
    '#collapsible' => FALSE,
  );
  $form['admin_account']['account']['#tree'] = TRUE;
  $form['admin_account']['markup'] = array(
    '#value' => '<p class="description">'. st('The administrator account has complete access to the site; it will automatically be granted all permissions and can perform any administrative activity. This will be the only account that can perform certain activities, so keep its credentials safe.') .'</p>',
    '#weight' => -10,
  );

  $form['admin_account']['account']['name'] = array('#type' => 'textfield',
    '#title' => st('Username'),
    '#maxlength' => USERNAME_MAX_LENGTH,
    '#description' => st('Spaces are allowed; punctuation is not allowed except for periods, hyphens, and underscores.'),
    '#required' => TRUE,
    '#weight' => -10,
  );

  $form['admin_account']['account']['mail'] = array('#type' => 'textfield',
    '#title' => st('E-mail address'),
    '#maxlength' => EMAIL_MAX_LENGTH,
    '#description' => st('All e-mails from the system will be sent to this address. The e-mail address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by e-mail.'),
    '#required' => TRUE,
    '#weight' => -5,
  );
  $form['admin_account']['account']['pass'] = array(
    '#type' => 'password_confirm',
    '#required' => TRUE,
    '#size' => 25,
    '#weight' => 0,
  );

  $zones = _system_zonelist();

  $form['server_settings'] = array(
    '#type' => 'fieldset',
    '#title' => st('Server settings'),
    '#collapsible' => FALSE,
  );
  $form['server_settings']['date_default_timezone'] = array(
    '#type' => 'select',
    '#title' => st('Default time zone'),
    '#default_value' => variable_get('date_default_timezone', 0),
    '#options' => $zones,
    '#description' => st('By default, dates in this site will be displayed in the chosen time zone.'),
    '#weight' => 5,
  );

  drupal_add_js(drupal_get_path('module', 'system') .'/system.js', 'module');
  drupal_add_js(array('cleanURL' => array('success' => st('Your server has been successfully tested to support this feature.'), 'failure' => st('Your system configuration does not currently support this feature. The <a href="http://drupal.org/node/15365">handbook page on Clean URLs</a> has additional troubleshooting information.'), 'testing' => st('Testing clean URLs...'))), 'setting');
  drupal_add_js('
// Global Killswitch
if (Drupal.jsEnabled) {
  $(document).ready(function() {
    Drupal.cleanURLsInstallCheck();
    Drupal.installDefaultTimezone();
  });
}', 'inline');

  $form['server_settings']['clean_url'] = array(
    '#type' => 'radios',
    '#title' => st('Clean URLs'),
    '#default_value' => variable_get('clean_url', 0),
    '#options' => array(st('Disabled'), st('Enabled')),
    '#description' => st('This option makes Drupal emit "clean" URLs (i.e. without <code>?q=</code> in the URL).'),
    '#disabled' => TRUE,
    '#prefix' => '<div id="clean-url" class="install">',
    '#suffix' => '</div>',
    '#weight' => 10,
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => st('Submit'),
    '#weight' => 15,
  );
  $hook_form_alter = $_GET['profile'] .'_form_alter';
  if (function_exists($hook_form_alter)) {
    $form = $hook_form_alter($form, 'install_configure');
  }
  return $form;
}

function install_configure_form_validate($form_id, $form_values, $form) {
  if ($error = user_validate_name($form_values['account']['name'])) {
    form_error($form['admin_account']['account']['name'], $error);
  }
  if ($error = user_validate_mail($form_values['account']['mail'])) {
    form_error($form['admin_account']['account']['mail'], $error);
  }
  if ($error = user_validate_mail($form_values['site_mail'])) {
    form_error($form['site_information']['site_mail'], $error);
  }
}

function install_configure_form_submit($form_id, $form_values) {
  variable_set('site_name', $form_values['site_name']);
  variable_set('site_mail', $form_values['site_mail']);
  variable_set('date_default_timezone', $form_values['date_default_timezone']);
  // Turn this off temporarily so that we can pass a password through.
  variable_set('user_email_verification', FALSE);
  user_register_submit('user_register', $form_values['account']);
  variable_set('user_email_verification', TRUE);
  if (isset($form_values['clean_url'])) {
    variable_set('clean_url', $form_values['clean_url']);
  }

  return 'finished';
}

862
install_main();