cms/drupal/includes/install.core.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     1 <?php
       
     2 
       
     3 /**
       
     4  * @file
       
     5  * API functions for installing Drupal.
       
     6  */
       
     7 
       
     8 /**
       
     9  * Do not run the task during the current installation request.
       
    10  *
       
    11  * This can be used to skip running an installation task when certain
       
    12  * conditions are met, even though the task may still show on the list of
       
    13  * installation tasks presented to the user. For example, the Drupal installer
       
    14  * uses this flag to skip over the database configuration form when valid
       
    15  * database connection information is already available from settings.php. It
       
    16  * also uses this flag to skip language import tasks when the installation is
       
    17  * being performed in English.
       
    18  */
       
    19 define('INSTALL_TASK_SKIP', 1);
       
    20 
       
    21 /**
       
    22  * Run the task on each installation request until the database is set up.
       
    23  *
       
    24  * This is primarily used by the Drupal installer for bootstrap-related tasks.
       
    25  */
       
    26 define('INSTALL_TASK_RUN_IF_REACHED', 2);
       
    27 
       
    28 /**
       
    29  * Global flag to indicate that a task should be run on each installation
       
    30  * request that reaches it, until the database is set up and we are able to
       
    31  * record the fact that it already ran.
       
    32  *
       
    33  * This is the default method for running tasks and should be used for most
       
    34  * tasks that occur after the database is set up; these tasks will then run
       
    35  * once and be marked complete once they are successfully finished. For
       
    36  * example, the Drupal installer uses this flag for the batch installation of
       
    37  * modules on the new site, and also for the configuration form that collects
       
    38  * basic site information and sets up the site maintenance account.
       
    39  */
       
    40 define('INSTALL_TASK_RUN_IF_NOT_COMPLETED', 3);
       
    41 
       
    42 /**
       
    43  * Installs Drupal either interactively or via an array of passed-in settings.
       
    44  *
       
    45  * The Drupal installation happens in a series of steps, which may be spread
       
    46  * out over multiple page requests. Each request begins by trying to determine
       
    47  * the last completed installation step (also known as a "task"), if one is
       
    48  * available from a previous request. Control is then passed to the task
       
    49  * handler, which processes the remaining tasks that need to be run until (a)
       
    50  * an error is thrown, (b) a new page needs to be displayed, or (c) the
       
    51  * installation finishes (whichever happens first).
       
    52  *
       
    53  * @param $settings
       
    54  *   An optional array of installation settings. Leave this empty for a normal,
       
    55  *   interactive, browser-based installation intended to occur over multiple
       
    56  *   page requests. Alternatively, if an array of settings is passed in, the
       
    57  *   installer will attempt to use it to perform the installation in a single
       
    58  *   page request (optimized for the command line) and not send any output
       
    59  *   intended for the web browser. See install_state_defaults() for a list of
       
    60  *   elements that are allowed to appear in this array.
       
    61  *
       
    62  * @see install_state_defaults()
       
    63  */
       
    64 function install_drupal($settings = array()) {
       
    65   global $install_state;
       
    66   // Initialize the installation state with the settings that were passed in,
       
    67   // as well as a boolean indicating whether or not this is an interactive
       
    68   // installation.
       
    69   $interactive = empty($settings);
       
    70   $install_state = $settings + array('interactive' => $interactive) + install_state_defaults();
       
    71   try {
       
    72     // Begin the page request. This adds information about the current state of
       
    73     // the Drupal installation to the passed-in array.
       
    74     install_begin_request($install_state);
       
    75     // Based on the installation state, run the remaining tasks for this page
       
    76     // request, and collect any output.
       
    77     $output = install_run_tasks($install_state);
       
    78   }
       
    79   catch (Exception $e) {
       
    80     // When an installation error occurs, either send the error to the web
       
    81     // browser or pass on the exception so the calling script can use it.
       
    82     if ($install_state['interactive']) {
       
    83       install_display_output($e->getMessage(), $install_state);
       
    84     }
       
    85     else {
       
    86       throw $e;
       
    87     }
       
    88   }
       
    89   // All available tasks for this page request are now complete. Interactive
       
    90   // installations can send output to the browser or redirect the user to the
       
    91   // next page.
       
    92   if ($install_state['interactive']) {
       
    93     if ($install_state['parameters_changed']) {
       
    94       // Redirect to the correct page if the URL parameters have changed.
       
    95       install_goto(install_redirect_url($install_state));
       
    96     }
       
    97     elseif (isset($output)) {
       
    98       // Display a page only if some output is available. Otherwise it is
       
    99       // possible that we are printing a JSON page and theme output should
       
   100       // not be shown.
       
   101       install_display_output($output, $install_state);
       
   102     }
       
   103   }
       
   104 }
       
   105 
       
   106 /**
       
   107  * Returns an array of default settings for the global installation state.
       
   108  *
       
   109  * The installation state is initialized with these settings at the beginning
       
   110  * of each page request. They may evolve during the page request, but they are
       
   111  * initialized again once the next request begins.
       
   112  *
       
   113  * Non-interactive Drupal installations can override some of these default
       
   114  * settings by passing in an array to the installation script, most notably
       
   115  * 'parameters' (which contains one-time parameters such as 'profile' and
       
   116  * 'locale' that are normally passed in via the URL) and 'forms' (which can
       
   117  * be used to programmatically submit forms during the installation; the keys
       
   118  * of each element indicate the name of the installation task that the form
       
   119  * submission is for, and the values are used as the $form_state['values']
       
   120  * array that is passed on to the form submission via drupal_form_submit()).
       
   121  *
       
   122  * @see drupal_form_submit()
       
   123  */
       
   124 function install_state_defaults() {
       
   125   $defaults = array(
       
   126     // The current task being processed.
       
   127     'active_task' => NULL,
       
   128     // The last task that was completed during the previous installation
       
   129     // request.
       
   130     'completed_task' => NULL,
       
   131     // This becomes TRUE only when Drupal's system module is installed.
       
   132     'database_tables_exist' => FALSE,
       
   133     // An array of forms to be programmatically submitted during the
       
   134     // installation. The keys of each element indicate the name of the
       
   135     // installation task that the form submission is for, and the values are
       
   136     // used as the $form_state['values'] array that is passed on to the form
       
   137     // submission via drupal_form_submit().
       
   138     'forms' => array(),
       
   139     // This becomes TRUE only at the end of the installation process, after
       
   140     // all available tasks have been completed and Drupal is fully installed.
       
   141     // It is used by the installer to store correct information in the database
       
   142     // about the completed installation, as well as to inform theme functions
       
   143     // that all tasks are finished (so that the task list can be displayed
       
   144     // correctly).
       
   145     'installation_finished' => FALSE,
       
   146     // Whether or not this installation is interactive. By default this will
       
   147     // be set to FALSE if settings are passed in to install_drupal().
       
   148     'interactive' => TRUE,
       
   149     // An array of available languages for the installation.
       
   150     'locales' => array(),
       
   151     // An array of parameters for the installation, pre-populated by the URL
       
   152     // or by the settings passed in to install_drupal(). This is primarily
       
   153     // used to store 'profile' (the name of the chosen installation profile)
       
   154     // and 'locale' (the name of the chosen installation language), since
       
   155     // these settings need to persist from page request to page request before
       
   156     // the database is available for storage.
       
   157     'parameters' => array(),
       
   158     // Whether or not the parameters have changed during the current page
       
   159     // request. For interactive installations, this will trigger a page
       
   160     // redirect.
       
   161     'parameters_changed' => FALSE,
       
   162     // An array of information about the chosen installation profile. This will
       
   163     // be filled in based on the profile's .info file.
       
   164     'profile_info' => array(),
       
   165     // An array of available installation profiles.
       
   166     'profiles' => array(),
       
   167     // An array of server variables that will be substituted into the global
       
   168     // $_SERVER array via drupal_override_server_variables(). Used by
       
   169     // non-interactive installations only.
       
   170     'server' => array(),
       
   171     // This becomes TRUE only when a valid database connection can be
       
   172     // established.
       
   173     'settings_verified' => FALSE,
       
   174     // Installation tasks can set this to TRUE to force the page request to
       
   175     // end (even if there is no themable output), in the case of an interactive
       
   176     // installation. This is needed only rarely; for example, it would be used
       
   177     // by an installation task that prints JSON output rather than returning a
       
   178     // themed page. The most common example of this is during batch processing,
       
   179     // but the Drupal installer automatically takes care of setting this
       
   180     // parameter properly in that case, so that individual installation tasks
       
   181     // which implement the batch API do not need to set it themselves.
       
   182     'stop_page_request' => FALSE,
       
   183     // Installation tasks can set this to TRUE to indicate that the task should
       
   184     // be run again, even if it normally wouldn't be. This can be used, for
       
   185     // example, if a single task needs to be spread out over multiple page
       
   186     // requests, or if it needs to perform some validation before allowing
       
   187     // itself to be marked complete. The most common examples of this are batch
       
   188     // processing and form submissions, but the Drupal installer automatically
       
   189     // takes care of setting this parameter properly in those cases, so that
       
   190     // individual installation tasks which implement the batch API or form API
       
   191     // do not need to set it themselves.
       
   192     'task_not_complete' => FALSE,
       
   193     // A list of installation tasks which have already been performed during
       
   194     // the current page request.
       
   195     'tasks_performed' => array(),
       
   196   );
       
   197   return $defaults;
       
   198 }
       
   199 
       
   200 /**
       
   201  * Begins an installation request, modifying the installation state as needed.
       
   202  *
       
   203  * This function performs commands that must run at the beginning of every page
       
   204  * request. It throws an exception if the installation should not proceed.
       
   205  *
       
   206  * @param $install_state
       
   207  *   An array of information about the current installation state. This is
       
   208  *   modified with information gleaned from the beginning of the page request.
       
   209  */
       
   210 function install_begin_request(&$install_state) {
       
   211   // Add any installation parameters passed in via the URL.
       
   212   $install_state['parameters'] += $_GET;
       
   213 
       
   214   // Validate certain core settings that are used throughout the installation.
       
   215   if (!empty($install_state['parameters']['profile'])) {
       
   216     $install_state['parameters']['profile'] = preg_replace('/[^a-zA-Z_0-9]/', '', $install_state['parameters']['profile']);
       
   217   }
       
   218   if (!empty($install_state['parameters']['locale'])) {
       
   219     $install_state['parameters']['locale'] = preg_replace('/[^a-zA-Z_0-9\-]/', '', $install_state['parameters']['locale']);
       
   220   }
       
   221 
       
   222   // Allow command line scripts to override server variables used by Drupal.
       
   223   require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
       
   224   if (!$install_state['interactive']) {
       
   225     drupal_override_server_variables($install_state['server']);
       
   226   }
       
   227 
       
   228   // The user agent header is used to pass a database prefix in the request when
       
   229   // running tests. However, for security reasons, it is imperative that no
       
   230   // installation be permitted using such a prefix.
       
   231   if (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'], "simpletest") !== FALSE) {
       
   232     header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
       
   233     exit;
       
   234   }
       
   235 
       
   236   drupal_bootstrap(DRUPAL_BOOTSTRAP_CONFIGURATION);
       
   237 
       
   238   // This must go after drupal_bootstrap(), which unsets globals!
       
   239   global $conf;
       
   240 
       
   241   require_once DRUPAL_ROOT . '/modules/system/system.install';
       
   242   require_once DRUPAL_ROOT . '/includes/common.inc';
       
   243   require_once DRUPAL_ROOT . '/includes/file.inc';
       
   244   require_once DRUPAL_ROOT . '/includes/install.inc';
       
   245   require_once DRUPAL_ROOT . '/' . variable_get('path_inc', 'includes/path.inc');
       
   246 
       
   247   // Load module basics (needed for hook invokes).
       
   248   include_once DRUPAL_ROOT . '/includes/module.inc';
       
   249   include_once DRUPAL_ROOT . '/includes/session.inc';
       
   250 
       
   251   // Set up $language, so t() caller functions will still work.
       
   252   drupal_language_initialize();
       
   253 
       
   254   include_once DRUPAL_ROOT . '/includes/entity.inc';
       
   255   require_once DRUPAL_ROOT . '/includes/ajax.inc';
       
   256   $module_list['system']['filename'] = 'modules/system/system.module';
       
   257   $module_list['user']['filename'] = 'modules/user/user.module';
       
   258   module_list(TRUE, FALSE, FALSE, $module_list);
       
   259   drupal_load('module', 'system');
       
   260   drupal_load('module', 'user');
       
   261 
       
   262   // Load the cache infrastructure using a "fake" cache implementation that
       
   263   // does not attempt to write to the database. We need this during the initial
       
   264   // part of the installer because the database is not available yet. We
       
   265   // continue to use it even when the database does become available, in order
       
   266   // to preserve consistency between interactive and command-line installations
       
   267   // (the latter complete in one page request and therefore are forced to
       
   268   // continue using the cache implementation they started with) and also
       
   269   // because any data put in the cache during the installer is inherently
       
   270   // suspect, due to the fact that Drupal is not fully set up yet.
       
   271   require_once DRUPAL_ROOT . '/includes/cache.inc';
       
   272   require_once DRUPAL_ROOT . '/includes/cache-install.inc';
       
   273   $conf['cache_default_class'] = 'DrupalFakeCache';
       
   274 
       
   275   // Prepare for themed output. We need to run this at the beginning of the
       
   276   // page request to avoid a different theme accidentally getting set. (We also
       
   277   // need to run it even in the case of command-line installations, to prevent
       
   278   // any code in the installer that happens to initialize the theme system from
       
   279   // accessing the database before it is set up yet.)
       
   280   drupal_maintenance_theme();
       
   281 
       
   282   // Check existing settings.php.
       
   283   $install_state['settings_verified'] = install_verify_settings();
       
   284 
       
   285   if ($install_state['settings_verified']) {
       
   286     // Initialize the database system. Note that the connection
       
   287     // won't be initialized until it is actually requested.
       
   288     require_once DRUPAL_ROOT . '/includes/database/database.inc';
       
   289 
       
   290     // Verify the last completed task in the database, if there is one.
       
   291     $task = install_verify_completed_task();
       
   292   }
       
   293   else {
       
   294     $task = NULL;
       
   295 
       
   296     // Do not install over a configured settings.php. Check the 'db_url'
       
   297     // variable in addition to 'databases', since previous versions of Drupal
       
   298     // used that (and we do not want to allow installations on an existing site
       
   299     // whose settings file has not yet been updated).
       
   300     if (!empty($GLOBALS['databases']) || !empty($GLOBALS['db_url'])) {
       
   301       throw new Exception(install_already_done_error());
       
   302     }
       
   303   }
       
   304 
       
   305   // Modify the installation state as appropriate.
       
   306   $install_state['completed_task'] = $task;
       
   307   $install_state['database_tables_exist'] = !empty($task);
       
   308 }
       
   309 
       
   310 /**
       
   311  * Runs all tasks for the current installation request.
       
   312  *
       
   313  * In the case of an interactive installation, all tasks will be attempted
       
   314  * until one is reached that has output which needs to be displayed to the
       
   315  * user, or until a page redirect is required. Otherwise, tasks will be
       
   316  * attempted until the installation is finished.
       
   317  *
       
   318  * @param $install_state
       
   319  *   An array of information about the current installation state. This is
       
   320  *   passed along to each task, so it can be modified if necessary.
       
   321  *
       
   322  * @return
       
   323  *   HTML output from the last completed task.
       
   324  */
       
   325 function install_run_tasks(&$install_state) {
       
   326   do {
       
   327     // Obtain a list of tasks to perform. The list of tasks itself can be
       
   328     // dynamic (e.g., some might be defined by the installation profile,
       
   329     // which is not necessarily known until the earlier tasks have run),
       
   330     // so we regenerate the remaining tasks based on the installation state,
       
   331     // each time through the loop.
       
   332     $tasks_to_perform = install_tasks_to_perform($install_state);
       
   333     // Run the first task on the list.
       
   334     reset($tasks_to_perform);
       
   335     $task_name = key($tasks_to_perform);
       
   336     $task = array_shift($tasks_to_perform);
       
   337     $install_state['active_task'] = $task_name;
       
   338     $original_parameters = $install_state['parameters'];
       
   339     $output = install_run_task($task, $install_state);
       
   340     $install_state['parameters_changed'] = ($install_state['parameters'] != $original_parameters);
       
   341     // Store this task as having been performed during the current request,
       
   342     // and save it to the database as completed, if we need to and if the
       
   343     // database is in a state that allows us to do so. Also mark the
       
   344     // installation as 'done' when we have run out of tasks.
       
   345     if (!$install_state['task_not_complete']) {
       
   346       $install_state['tasks_performed'][] = $task_name;
       
   347       $install_state['installation_finished'] = empty($tasks_to_perform);
       
   348       if ($install_state['database_tables_exist'] && ($task['run'] == INSTALL_TASK_RUN_IF_NOT_COMPLETED || $install_state['installation_finished'])) {
       
   349         variable_set('install_task', $install_state['installation_finished'] ? 'done' : $task_name);
       
   350       }
       
   351     }
       
   352     // Stop when there are no tasks left. In the case of an interactive
       
   353     // installation, also stop if we have some output to send to the browser,
       
   354     // the URL parameters have changed, or an end to the page request was
       
   355     // specifically called for.
       
   356     $finished = empty($tasks_to_perform) || ($install_state['interactive'] && (isset($output) || $install_state['parameters_changed'] || $install_state['stop_page_request']));
       
   357   } while (!$finished);
       
   358   return $output;
       
   359 }
       
   360 
       
   361 /**
       
   362  * Runs an individual installation task.
       
   363  *
       
   364  * @param $task
       
   365  *   An array of information about the task to be run as returned by
       
   366  *   hook_install_tasks().
       
   367  * @param $install_state
       
   368  *   An array of information about the current installation state. This is
       
   369  *   passed in by reference so that it can be modified by the task.
       
   370  *
       
   371  * @return
       
   372  *   The output of the task function, if there is any.
       
   373  */
       
   374 function install_run_task($task, &$install_state) {
       
   375   $function = $task['function'];
       
   376 
       
   377   if ($task['type'] == 'form') {
       
   378     require_once DRUPAL_ROOT . '/includes/form.inc';
       
   379     if ($install_state['interactive']) {
       
   380       // For interactive forms, build the form and ensure that it will not
       
   381       // redirect, since the installer handles its own redirection only after
       
   382       // marking the form submission task complete.
       
   383       $form_state = array(
       
   384         // We need to pass $install_state by reference in order for forms to
       
   385         // modify it, since the form API will use it in call_user_func_array(),
       
   386         // which requires that referenced variables be passed explicitly.
       
   387         'build_info' => array('args' => array(&$install_state)),
       
   388         'no_redirect' => TRUE,
       
   389       );
       
   390       $form = drupal_build_form($function, $form_state);
       
   391       // If a successful form submission did not occur, the form needs to be
       
   392       // rendered, which means the task is not complete yet.
       
   393       if (empty($form_state['executed'])) {
       
   394         $install_state['task_not_complete'] = TRUE;
       
   395         return drupal_render($form);
       
   396       }
       
   397       // Otherwise, return nothing so the next task will run in the same
       
   398       // request.
       
   399       return;
       
   400     }
       
   401     else {
       
   402       // For non-interactive forms, submit the form programmatically with the
       
   403       // values taken from the installation state. Throw an exception if any
       
   404       // errors were encountered.
       
   405       $form_state = array(
       
   406         'values' => !empty($install_state['forms'][$function]) ? $install_state['forms'][$function] : array(),
       
   407         // We need to pass $install_state by reference in order for forms to
       
   408         // modify it, since the form API will use it in call_user_func_array(),
       
   409         // which requires that referenced variables be passed explicitly.
       
   410         'build_info' => array('args' => array(&$install_state)),
       
   411       );
       
   412       drupal_form_submit($function, $form_state);
       
   413       $errors = form_get_errors();
       
   414       if (!empty($errors)) {
       
   415         throw new Exception(implode("\n", $errors));
       
   416       }
       
   417     }
       
   418   }
       
   419 
       
   420   elseif ($task['type'] == 'batch') {
       
   421     // Start a new batch based on the task function, if one is not running
       
   422     // already.
       
   423     $current_batch = variable_get('install_current_batch');
       
   424     if (!$install_state['interactive'] || !$current_batch) {
       
   425       $batch = $function($install_state);
       
   426       if (empty($batch)) {
       
   427         // If the task did some processing and decided no batch was necessary,
       
   428         // there is nothing more to do here.
       
   429         return;
       
   430       }
       
   431       batch_set($batch);
       
   432       // For interactive batches, we need to store the fact that this batch
       
   433       // task is currently running. Otherwise, we need to make sure the batch
       
   434       // will complete in one page request.
       
   435       if ($install_state['interactive']) {
       
   436         variable_set('install_current_batch', $function);
       
   437       }
       
   438       else {
       
   439         $batch =& batch_get();
       
   440         $batch['progressive'] = FALSE;
       
   441       }
       
   442       // Process the batch. For progressive batches, this will redirect.
       
   443       // Otherwise, the batch will complete.
       
   444       batch_process(install_redirect_url($install_state), install_full_redirect_url($install_state));
       
   445     }
       
   446     // If we are in the middle of processing this batch, keep sending back
       
   447     // any output from the batch process, until the task is complete.
       
   448     elseif ($current_batch == $function) {
       
   449       include_once DRUPAL_ROOT . '/includes/batch.inc';
       
   450       $output = _batch_page();
       
   451       // The task is complete when we try to access the batch page and receive
       
   452       // FALSE in return, since this means we are at a URL where we are no
       
   453       // longer requesting a batch ID.
       
   454       if ($output === FALSE) {
       
   455         // Return nothing so the next task will run in the same request.
       
   456         variable_del('install_current_batch');
       
   457         return;
       
   458       }
       
   459       else {
       
   460         // We need to force the page request to end if the task is not
       
   461         // complete, since the batch API sometimes prints JSON output
       
   462         // rather than returning a themed page.
       
   463         $install_state['task_not_complete'] = $install_state['stop_page_request'] = TRUE;
       
   464         return $output;
       
   465       }
       
   466     }
       
   467   }
       
   468 
       
   469   else {
       
   470     // For normal tasks, just return the function result, whatever it is.
       
   471     return $function($install_state);
       
   472   }
       
   473 }
       
   474 
       
   475 /**
       
   476  * Returns a list of tasks to perform during the current installation request.
       
   477  *
       
   478  * Note that the list of tasks can change based on the installation state as
       
   479  * the page request evolves (for example, if an installation profile hasn't
       
   480  * been selected yet, we don't yet know which profile tasks need to be run).
       
   481  *
       
   482  * You can override this using hook_install_tasks() or
       
   483  * hook_install_tasks_alter().
       
   484  *
       
   485  * @param $install_state
       
   486  *   An array of information about the current installation state.
       
   487  *
       
   488  * @return
       
   489  *   A list of tasks to be performed, with associated metadata as returned by
       
   490  *   hook_install_tasks().
       
   491  */
       
   492 function install_tasks_to_perform($install_state) {
       
   493   // Start with a list of all currently available tasks.
       
   494   $tasks = install_tasks($install_state);
       
   495   foreach ($tasks as $name => $task) {
       
   496     // Remove any tasks that were already performed or that never should run.
       
   497     // Also, if we started this page request with an indication of the last
       
   498     // task that was completed, skip that task and all those that come before
       
   499     // it, unless they are marked as always needing to run.
       
   500     if ($task['run'] == INSTALL_TASK_SKIP || in_array($name, $install_state['tasks_performed']) || (!empty($install_state['completed_task']) && empty($completed_task_found) && $task['run'] != INSTALL_TASK_RUN_IF_REACHED)) {
       
   501       unset($tasks[$name]);
       
   502     }
       
   503     if (!empty($install_state['completed_task']) && $name == $install_state['completed_task']) {
       
   504       $completed_task_found = TRUE;
       
   505     }
       
   506   }
       
   507   return $tasks;
       
   508 }
       
   509 
       
   510 /**
       
   511  * Returns a list of all tasks the installer currently knows about.
       
   512  *
       
   513  * This function will return tasks regardless of whether or not they are
       
   514  * intended to run on the current page request. However, the list can change
       
   515  * based on the installation state (for example, if an installation profile
       
   516  * hasn't been selected yet, we don't yet know which profile tasks will be
       
   517  * available).
       
   518  *
       
   519  * @param $install_state
       
   520  *   An array of information about the current installation state.
       
   521  *
       
   522  * @return
       
   523  *   A list of tasks, with associated metadata.
       
   524  */
       
   525 function install_tasks($install_state) {
       
   526   // Determine whether translation import tasks will need to be performed.
       
   527   $needs_translations = count($install_state['locales']) > 1 && !empty($install_state['parameters']['locale']) && $install_state['parameters']['locale'] != 'en';
       
   528 
       
   529   // Start with the core installation tasks that run before handing control
       
   530   // to the installation profile.
       
   531   $tasks = array(
       
   532     'install_select_profile' => array(
       
   533       'display_name' => st('Choose profile'),
       
   534       'display' => count($install_state['profiles']) != 1,
       
   535       'run' => INSTALL_TASK_RUN_IF_REACHED,
       
   536     ),
       
   537     'install_select_locale' => array(
       
   538       'display_name' => st('Choose language'),
       
   539       'run' => INSTALL_TASK_RUN_IF_REACHED,
       
   540     ),
       
   541     'install_load_profile' => array(
       
   542       'run' => INSTALL_TASK_RUN_IF_REACHED,
       
   543     ),
       
   544     'install_verify_requirements' => array(
       
   545       'display_name' => st('Verify requirements'),
       
   546     ),
       
   547     'install_settings_form' => array(
       
   548       'display_name' => st('Set up database'),
       
   549       'type' => 'form',
       
   550       'run' => $install_state['settings_verified'] ? INSTALL_TASK_SKIP : INSTALL_TASK_RUN_IF_NOT_COMPLETED,
       
   551     ),
       
   552     'install_system_module' => array(
       
   553     ),
       
   554     'install_bootstrap_full' => array(
       
   555       'run' => INSTALL_TASK_RUN_IF_REACHED,
       
   556     ),
       
   557     'install_profile_modules' => array(
       
   558       'display_name' => count($install_state['profiles']) == 1 ? st('Install site') : st('Install profile'),
       
   559       'type' => 'batch',
       
   560     ),
       
   561     'install_import_locales' => array(
       
   562       'display_name' => st('Set up translations'),
       
   563       'display' => $needs_translations,
       
   564       'type' => 'batch',
       
   565       'run' => $needs_translations ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
       
   566     ),
       
   567     'install_configure_form' => array(
       
   568       'display_name' => st('Configure site'),
       
   569       'type' => 'form',
       
   570     ),
       
   571   );
       
   572 
       
   573   // Now add any tasks defined by the installation profile.
       
   574   if (!empty($install_state['parameters']['profile'])) {
       
   575     // Load the profile install file, because it is not always loaded when
       
   576     // hook_install_tasks() is invoked (e.g. batch processing).
       
   577     $profile_install_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.install';
       
   578     if (file_exists($profile_install_file)) {
       
   579       include_once $profile_install_file;
       
   580     }
       
   581     $function = $install_state['parameters']['profile'] . '_install_tasks';
       
   582     if (function_exists($function)) {
       
   583       $result = $function($install_state);
       
   584       if (is_array($result)) {
       
   585         $tasks += $result;
       
   586       }
       
   587     }
       
   588   }
       
   589 
       
   590   // Finish by adding the remaining core tasks.
       
   591   $tasks += array(
       
   592     'install_import_locales_remaining' => array(
       
   593       'display_name' => st('Finish translations'),
       
   594       'display' => $needs_translations,
       
   595       'type' => 'batch',
       
   596       'run' => $needs_translations ? INSTALL_TASK_RUN_IF_NOT_COMPLETED : INSTALL_TASK_SKIP,
       
   597     ),
       
   598     'install_finished' => array(
       
   599       'display_name' => st('Finished'),
       
   600     ),
       
   601   );
       
   602 
       
   603   // Allow the installation profile to modify the full list of tasks.
       
   604   if (!empty($install_state['parameters']['profile'])) {
       
   605     $profile_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.profile';
       
   606     if (file_exists($profile_file)) {
       
   607       include_once $profile_file;
       
   608       $function = $install_state['parameters']['profile'] . '_install_tasks_alter';
       
   609       if (function_exists($function)) {
       
   610         $function($tasks, $install_state);
       
   611       }
       
   612     }
       
   613   }
       
   614 
       
   615   // Fill in default parameters for each task before returning the list.
       
   616   foreach ($tasks as $task_name => &$task) {
       
   617     $task += array(
       
   618       'display_name' => NULL,
       
   619       'display' => !empty($task['display_name']),
       
   620       'type' => 'normal',
       
   621       'run' => INSTALL_TASK_RUN_IF_NOT_COMPLETED,
       
   622       'function' => $task_name,
       
   623     );
       
   624   }
       
   625   return $tasks;
       
   626 }
       
   627 
       
   628 /**
       
   629  * Returns a list of tasks that should be displayed to the end user.
       
   630  *
       
   631  * The output of this function is a list suitable for sending to
       
   632  * theme_task_list().
       
   633  *
       
   634  * @param $install_state
       
   635  *   An array of information about the current installation state.
       
   636  *
       
   637  * @return
       
   638  *   A list of tasks, with keys equal to the machine-readable task name and
       
   639  *   values equal to the name that should be displayed.
       
   640  *
       
   641  * @see theme_task_list()
       
   642  */
       
   643 function install_tasks_to_display($install_state) {
       
   644   $displayed_tasks = array();
       
   645   foreach (install_tasks($install_state) as $name => $task) {
       
   646     if ($task['display']) {
       
   647       $displayed_tasks[$name] = $task['display_name'];
       
   648     }
       
   649   }
       
   650   return $displayed_tasks;
       
   651 }
       
   652 
       
   653 /**
       
   654  * Returns the URL that should be redirected to during an installation request.
       
   655  *
       
   656  * The output of this function is suitable for sending to install_goto().
       
   657  *
       
   658  * @param $install_state
       
   659  *   An array of information about the current installation state.
       
   660  *
       
   661  * @return
       
   662  *   The URL to redirect to.
       
   663  *
       
   664  * @see install_full_redirect_url()
       
   665  */
       
   666 function install_redirect_url($install_state) {
       
   667   return 'install.php?' . drupal_http_build_query($install_state['parameters']);
       
   668 }
       
   669 
       
   670 /**
       
   671  * Returns the complete URL redirected to during an installation request.
       
   672  *
       
   673  * @param $install_state
       
   674  *   An array of information about the current installation state.
       
   675  *
       
   676  * @return
       
   677  *   The complete URL to redirect to.
       
   678  *
       
   679  * @see install_redirect_url()
       
   680  */
       
   681 function install_full_redirect_url($install_state) {
       
   682   global $base_url;
       
   683   return $base_url . '/' . install_redirect_url($install_state);
       
   684 }
       
   685 
       
   686 /**
       
   687  * Displays themed installer output and ends the page request.
       
   688  *
       
   689  * Installation tasks should use drupal_set_title() to set the desired page
       
   690  * title, but otherwise this function takes care of theming the overall page
       
   691  * output during every step of the installation.
       
   692  *
       
   693  * @param $output
       
   694  *   The content to display on the main part of the page.
       
   695  * @param $install_state
       
   696  *   An array of information about the current installation state.
       
   697  */
       
   698 function install_display_output($output, $install_state) {
       
   699   drupal_page_header();
       
   700 
       
   701   // Prevent install.php from being indexed when installed in a sub folder.
       
   702   // robots.txt rules are not read if the site is within domain.com/subfolder
       
   703   // resulting in /subfolder/install.php being found through search engines.
       
   704   // When settings.php is writeable this can be used via an external database
       
   705   // leading a malicious user to gain php access to the server.
       
   706   $noindex_meta_tag = array(
       
   707     '#tag' => 'meta',
       
   708     '#attributes' => array(
       
   709       'name' => 'robots',
       
   710       'content' => 'noindex, nofollow',
       
   711     ),
       
   712   );
       
   713   drupal_add_html_head($noindex_meta_tag, 'install_meta_robots');
       
   714 
       
   715   // Only show the task list if there is an active task; otherwise, the page
       
   716   // request has ended before tasks have even been started, so there is nothing
       
   717   // meaningful to show.
       
   718   if (isset($install_state['active_task'])) {
       
   719     // Let the theming function know when every step of the installation has
       
   720     // been completed.
       
   721     $active_task = $install_state['installation_finished'] ? NULL : $install_state['active_task'];
       
   722     drupal_add_region_content('sidebar_first', theme('task_list', array('items' => install_tasks_to_display($install_state), 'active' => $active_task)));
       
   723   }
       
   724   print theme('install_page', array('content' => $output));
       
   725   exit;
       
   726 }
       
   727 
       
   728 /**
       
   729  * Verifies the requirements for installing Drupal.
       
   730  *
       
   731  * @param $install_state
       
   732  *   An array of information about the current installation state.
       
   733  *
       
   734  * @return
       
   735  *   A themed status report, or an exception if there are requirement errors.
       
   736  *   If there are only requirement warnings, a themed status report is shown
       
   737  *   initially, but the user is allowed to bypass it by providing 'continue=1'
       
   738  *   in the URL. Otherwise, no output is returned, so that the next task can be
       
   739  *   run in the same page request.
       
   740  */
       
   741 function install_verify_requirements(&$install_state) {
       
   742   // Check the installation requirements for Drupal and this profile.
       
   743   $requirements = install_check_requirements($install_state);
       
   744 
       
   745   // Verify existence of all required modules.
       
   746   $requirements += drupal_verify_profile($install_state);
       
   747 
       
   748   // Check the severity of the requirements reported.
       
   749   $severity = drupal_requirements_severity($requirements);
       
   750 
       
   751   // If there are errors, always display them. If there are only warnings, skip
       
   752   // them if the user has provided a URL parameter acknowledging the warnings
       
   753   // and indicating a desire to continue anyway. See drupal_requirements_url().
       
   754   if ($severity == REQUIREMENT_ERROR || ($severity == REQUIREMENT_WARNING && empty($install_state['parameters']['continue']))) {
       
   755     if ($install_state['interactive']) {
       
   756       drupal_set_title(st('Requirements problem'));
       
   757       $status_report = theme('status_report', array('requirements' => $requirements));
       
   758       $status_report .= st('Check the error messages and <a href="!url">proceed with the installation</a>.', array('!url' => check_url(drupal_requirements_url($severity))));
       
   759       return $status_report;
       
   760     }
       
   761     else {
       
   762       // Throw an exception showing any unmet requirements.
       
   763       $failures = array();
       
   764       foreach ($requirements as $requirement) {
       
   765         // Skip warnings altogether for non-interactive installations; these
       
   766         // proceed in a single request so there is no good opportunity (and no
       
   767         // good method) to warn the user anyway.
       
   768         if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
       
   769           $failures[] = $requirement['title'] . ': ' . $requirement['value'] . "\n\n" . $requirement['description'];
       
   770         }
       
   771       }
       
   772       if (!empty($failures)) {
       
   773         throw new Exception(implode("\n\n", $failures));
       
   774       }
       
   775     }
       
   776   }
       
   777 }
       
   778 
       
   779 /**
       
   780  * Installation task; install the Drupal system module.
       
   781  *
       
   782  * @param $install_state
       
   783  *   An array of information about the current installation state.
       
   784  */
       
   785 function install_system_module(&$install_state) {
       
   786   // Install system.module.
       
   787   drupal_install_system();
       
   788 
       
   789   // Call file_ensure_htaccess() to ensure that all of Drupal's standard
       
   790   // directories (e.g., the public and private files directories) have
       
   791   // appropriate .htaccess files. These directories will have already been
       
   792   // created by this point in the installer, since Drupal creates them during
       
   793   // the install_verify_requirements() task. Note that we cannot call
       
   794   // file_ensure_htaccess() any earlier than this, since it relies on
       
   795   // system.module in order to work.
       
   796   file_ensure_htaccess();
       
   797 
       
   798   // Enable the user module so that sessions can be recorded during the
       
   799   // upcoming bootstrap step.
       
   800   module_enable(array('user'), FALSE);
       
   801 
       
   802   // Save the list of other modules to install for the upcoming tasks.
       
   803   // variable_set() can be used now that system.module is installed.
       
   804   $modules = $install_state['profile_info']['dependencies'];
       
   805 
       
   806   // The installation profile is also a module, which needs to be installed
       
   807   // after all the dependencies have been installed.
       
   808   $modules[] = drupal_get_profile();
       
   809 
       
   810   variable_set('install_profile_modules', array_diff($modules, array('system')));
       
   811   $install_state['database_tables_exist'] = TRUE;
       
   812 
       
   813   // Prevent the hook_requirements() check from telling us to convert the
       
   814   // database to utf8mb4.
       
   815   $connection = Database::getConnection();
       
   816   if ($connection->utf8mb4IsConfigurable() && $connection->utf8mb4IsActive()) {
       
   817     variable_set('drupal_all_databases_are_utf8mb4', TRUE);
       
   818   }
       
   819 }
       
   820 
       
   821 /**
       
   822  * Verifies and returns the last installation task that was completed.
       
   823  *
       
   824  * @return
       
   825  *   The last completed task, if there is one. An exception is thrown if Drupal
       
   826  *   is already installed.
       
   827  */
       
   828 function install_verify_completed_task() {
       
   829   try {
       
   830     if ($result = db_query("SELECT value FROM {variable} WHERE name = :name", array('name' => 'install_task'))) {
       
   831       $task = unserialize($result->fetchField());
       
   832     }
       
   833   }
       
   834   // Do not trigger an error if the database query fails, since the database
       
   835   // might not be set up yet.
       
   836   catch (Exception $e) {
       
   837   }
       
   838   if (isset($task)) {
       
   839     if ($task == 'done') {
       
   840       throw new Exception(install_already_done_error());
       
   841     }
       
   842     return $task;
       
   843   }
       
   844 }
       
   845 
       
   846 /**
       
   847  * Verifies the existing settings in settings.php.
       
   848  */
       
   849 function install_verify_settings() {
       
   850   global $databases;
       
   851 
       
   852   // Verify existing settings (if any).
       
   853   if (!empty($databases) && install_verify_pdo()) {
       
   854     $database = $databases['default']['default'];
       
   855     drupal_static_reset('conf_path');
       
   856     $settings_file = './' . conf_path(FALSE) . '/settings.php';
       
   857     $errors = install_database_errors($database, $settings_file);
       
   858     if (empty($errors)) {
       
   859       return TRUE;
       
   860     }
       
   861   }
       
   862   return FALSE;
       
   863 }
       
   864 
       
   865 /**
       
   866  * Verifies the PDO library.
       
   867  */
       
   868 function install_verify_pdo() {
       
   869   // PDO was moved to PHP core in 5.2.0, but the old extension (targeting 5.0
       
   870   // and 5.1) is still available from PECL, and can still be built without
       
   871   // errors. To verify that the correct version is in use, we check the
       
   872   // PDO::ATTR_DEFAULT_FETCH_MODE constant, which is not available in the
       
   873   // PECL extension.
       
   874   return extension_loaded('pdo') && defined('PDO::ATTR_DEFAULT_FETCH_MODE');
       
   875 }
       
   876 
       
   877 /**
       
   878  * Form constructor for a form to configure and rewrite settings.php.
       
   879  *
       
   880  * @param $install_state
       
   881  *   An array of information about the current installation state.
       
   882  *
       
   883  * @see install_settings_form_validate()
       
   884  * @see install_settings_form_submit()
       
   885  * @ingroup forms
       
   886  */
       
   887 function install_settings_form($form, &$form_state, &$install_state) {
       
   888   global $databases;
       
   889   $profile = $install_state['parameters']['profile'];
       
   890   $install_locale = $install_state['parameters']['locale'];
       
   891 
       
   892   drupal_static_reset('conf_path');
       
   893   $conf_path = './' . conf_path(FALSE);
       
   894   $settings_file = $conf_path . '/settings.php';
       
   895   $database = isset($databases['default']['default']) ? $databases['default']['default'] : array();
       
   896 
       
   897   drupal_set_title(st('Database configuration'));
       
   898 
       
   899   $drivers = drupal_get_database_types();
       
   900   $drivers_keys = array_keys($drivers);
       
   901 
       
   902   $form['driver'] = array(
       
   903     '#type' => 'radios',
       
   904     '#title' => st('Database type'),
       
   905     '#required' => TRUE,
       
   906     '#default_value' => !empty($database['driver']) ? $database['driver'] : current($drivers_keys),
       
   907     '#description' => st('The type of database your @drupal data will be stored in.', array('@drupal' => drupal_install_profile_distribution_name())),
       
   908   );
       
   909   if (count($drivers) == 1) {
       
   910     $form['driver']['#disabled'] = TRUE;
       
   911     $form['driver']['#description'] .= ' ' . st('Your PHP configuration only supports a single database type, so it has been automatically selected.');
       
   912   }
       
   913 
       
   914   // Add driver specific configuration options.
       
   915   foreach ($drivers as $key => $driver) {
       
   916     $form['driver']['#options'][$key] = $driver->name();
       
   917 
       
   918     $form['settings'][$key] = $driver->getFormOptions($database);
       
   919     $form['settings'][$key]['#prefix'] = '<h2 class="js-hide">' . st('@driver_name settings', array('@driver_name' => $driver->name())) . '</h2>';
       
   920     $form['settings'][$key]['#type'] = 'container';
       
   921     $form['settings'][$key]['#tree'] = TRUE;
       
   922     $form['settings'][$key]['advanced_options']['#parents'] = array($key);
       
   923     $form['settings'][$key]['#states'] = array(
       
   924       'visible' => array(
       
   925         ':input[name=driver]' => array('value' => $key),
       
   926       )
       
   927     );
       
   928   }
       
   929 
       
   930   $form['actions'] = array('#type' => 'actions');
       
   931   $form['actions']['save'] = array(
       
   932     '#type' => 'submit',
       
   933     '#value' => st('Save and continue'),
       
   934     '#limit_validation_errors' => array(
       
   935       array('driver'),
       
   936       array(isset($form_state['input']['driver']) ? $form_state['input']['driver'] : current($drivers_keys)),
       
   937     ),
       
   938     '#submit' => array('install_settings_form_submit'),
       
   939   );
       
   940 
       
   941   $form['errors'] = array();
       
   942   $form['settings_file'] = array('#type' => 'value', '#value' => $settings_file);
       
   943 
       
   944   return $form;
       
   945 }
       
   946 
       
   947 /**
       
   948  * Form validation handler for install_settings_form().
       
   949  *
       
   950  * @see install_settings_form_submit()
       
   951  */
       
   952 function install_settings_form_validate($form, &$form_state) {
       
   953   $driver = $form_state['values']['driver'];
       
   954   $database = $form_state['values'][$driver];
       
   955   $database['driver'] = $driver;
       
   956 
       
   957   // TODO: remove when PIFR will be updated to use 'db_prefix' instead of
       
   958   // 'prefix' in the database settings form.
       
   959   $database['prefix'] = $database['db_prefix'];
       
   960   unset($database['db_prefix']);
       
   961 
       
   962   $form_state['storage']['database'] = $database;
       
   963   $errors = install_database_errors($database, $form_state['values']['settings_file']);
       
   964   foreach ($errors as $name => $message) {
       
   965     form_set_error($name, $message);
       
   966   }
       
   967 }
       
   968 
       
   969 /**
       
   970  * Checks a database connection and returns any errors.
       
   971  */
       
   972 function install_database_errors($database, $settings_file) {
       
   973   global $databases;
       
   974   $errors = array();
       
   975 
       
   976   // Check database type.
       
   977   $database_types = drupal_get_database_types();
       
   978   $driver = $database['driver'];
       
   979   if (!isset($database_types[$driver])) {
       
   980     $errors['driver'] = st("In your %settings_file file you have configured @drupal to use a %driver server, however your PHP installation currently does not support this database type.", array('%settings_file' => $settings_file, '@drupal' => drupal_install_profile_distribution_name(), '%driver' => $driver));
       
   981   }
       
   982   else {
       
   983     // Run driver specific validation
       
   984     $errors += $database_types[$driver]->validateDatabaseSettings($database);
       
   985 
       
   986     // Run tasks associated with the database type. Any errors are caught in the
       
   987     // calling function.
       
   988     $databases['default']['default'] = $database;
       
   989     // Just changing the global doesn't get the new information processed.
       
   990     // We tell tell the Database class to re-parse $databases.
       
   991     Database::parseConnectionInfo();
       
   992 
       
   993     try {
       
   994       db_run_tasks($driver);
       
   995     }
       
   996     catch (DatabaseTaskException $e) {
       
   997       // These are generic errors, so we do not have any specific key of the
       
   998       // database connection array to attach them to; therefore, we just put
       
   999       // them in the error array with standard numeric keys.
       
  1000       $errors[$driver . '][0'] = $e->getMessage();
       
  1001     }
       
  1002   }
       
  1003   return $errors;
       
  1004 }
       
  1005 
       
  1006 /**
       
  1007  * Form submission handler for install_settings_form().
       
  1008  *
       
  1009  * @see install_settings_form_validate()
       
  1010  */
       
  1011 function install_settings_form_submit($form, &$form_state) {
       
  1012   global $install_state;
       
  1013 
       
  1014   // Update global settings array and save.
       
  1015   $settings['databases'] = array(
       
  1016     'value'    => array('default' => array('default' => $form_state['storage']['database'])),
       
  1017     'required' => TRUE,
       
  1018   );
       
  1019   $settings['drupal_hash_salt'] = array(
       
  1020     'value'    => drupal_random_key(),
       
  1021     'required' => TRUE,
       
  1022   );
       
  1023   drupal_rewrite_settings($settings);
       
  1024   // Indicate that the settings file has been verified, and check the database
       
  1025   // for the last completed task, now that we have a valid connection. This
       
  1026   // last step is important since we want to trigger an error if the new
       
  1027   // database already has Drupal installed.
       
  1028   $install_state['settings_verified'] = TRUE;
       
  1029   $install_state['completed_task'] = install_verify_completed_task();
       
  1030 }
       
  1031 
       
  1032 /**
       
  1033  * Finds all .profile files.
       
  1034  */
       
  1035 function install_find_profiles() {
       
  1036   return file_scan_directory('./profiles', '/\.profile$/', array('key' => 'name'));
       
  1037 }
       
  1038 
       
  1039 /**
       
  1040  * Selects which profile to install.
       
  1041  *
       
  1042  * @param $install_state
       
  1043  *   An array of information about the current installation state. The chosen
       
  1044  *   profile will be added here, if it was not already selected previously, as
       
  1045  *   will a list of all available profiles.
       
  1046  *
       
  1047  * @return
       
  1048  *   For interactive installations, a form allowing the profile to be selected,
       
  1049  *   if the user has a choice that needs to be made. Otherwise, an exception is
       
  1050  *   thrown if a profile cannot be chosen automatically.
       
  1051  */
       
  1052 function install_select_profile(&$install_state) {
       
  1053   $install_state['profiles'] += install_find_profiles();
       
  1054   if (empty($install_state['parameters']['profile'])) {
       
  1055     // Try to find a profile.
       
  1056     $profile = _install_select_profile($install_state['profiles']);
       
  1057     if (empty($profile)) {
       
  1058       // We still don't have a profile, so display a form for selecting one.
       
  1059       // Only do this in the case of interactive installations, since this is
       
  1060       // not a real form with submit handlers (the database isn't even set up
       
  1061       // yet), rather just a convenience method for setting parameters in the
       
  1062       // URL.
       
  1063       if ($install_state['interactive']) {
       
  1064         include_once DRUPAL_ROOT . '/includes/form.inc';
       
  1065         drupal_set_title(st('Select an installation profile'));
       
  1066         $form = drupal_get_form('install_select_profile_form', $install_state['profiles']);
       
  1067         return drupal_render($form);
       
  1068       }
       
  1069       else {
       
  1070         throw new Exception(install_no_profile_error());
       
  1071       }
       
  1072     }
       
  1073     else {
       
  1074       $install_state['parameters']['profile'] = $profile;
       
  1075     }
       
  1076   }
       
  1077 }
       
  1078 
       
  1079 /**
       
  1080  * Selects an installation profile.
       
  1081  *
       
  1082  * A profile will be selected if:
       
  1083  * - Only one profile is available,
       
  1084  * - A profile was submitted through $_POST,
       
  1085  * - Exactly one of the profiles is marked as "exclusive".
       
  1086  * If multiple profiles are marked as "exclusive" then no profile will be
       
  1087  * selected.
       
  1088  *
       
  1089  * @param array $profiles
       
  1090  *   An associative array of profiles with the machine-readable names as keys.
       
  1091  *
       
  1092  * @return
       
  1093  *   The machine-readable name of the selected profile or NULL if no profile was
       
  1094  *   selected.
       
  1095  */
       
  1096 function _install_select_profile($profiles) {
       
  1097   if (sizeof($profiles) == 0) {
       
  1098     throw new Exception(install_no_profile_error());
       
  1099   }
       
  1100   // Don't need to choose profile if only one available.
       
  1101   if (sizeof($profiles) == 1) {
       
  1102     $profile = array_pop($profiles);
       
  1103     // TODO: is this right?
       
  1104     require_once DRUPAL_ROOT . '/' . $profile->uri;
       
  1105     return $profile->name;
       
  1106   }
       
  1107   else {
       
  1108     foreach ($profiles as $profile) {
       
  1109       if (!empty($_POST['profile']) && ($_POST['profile'] == $profile->name)) {
       
  1110         return $profile->name;
       
  1111       }
       
  1112     }
       
  1113   }
       
  1114   // Check for a profile marked as "exclusive" and ensure that only one
       
  1115   // profile is marked as such.
       
  1116   $exclusive_profile = NULL;
       
  1117   foreach ($profiles as $profile) {
       
  1118     $profile_info = install_profile_info($profile->name);
       
  1119     if (!empty($profile_info['exclusive'])) {
       
  1120       if (empty($exclusive_profile)) {
       
  1121         $exclusive_profile = $profile->name;
       
  1122       }
       
  1123       else {
       
  1124         // We found a second "exclusive" profile. There's no way to choose
       
  1125         // between them, so we ignore the property.
       
  1126         return;
       
  1127       }
       
  1128     }
       
  1129   }
       
  1130   return $exclusive_profile;
       
  1131 }
       
  1132 
       
  1133 /**
       
  1134  * Form constructor for the profile selection form.
       
  1135  *
       
  1136  * @param $form_state
       
  1137  *   Array of metadata about state of form processing.
       
  1138  * @param $profile_files
       
  1139  *   Array of .profile files, as returned from file_scan_directory().
       
  1140  *
       
  1141  * @ingroup forms
       
  1142  */
       
  1143 function install_select_profile_form($form, &$form_state, $profile_files) {
       
  1144   $profiles = array();
       
  1145   $names = array();
       
  1146 
       
  1147   foreach ($profile_files as $profile) {
       
  1148     // TODO: is this right?
       
  1149     include_once DRUPAL_ROOT . '/' . $profile->uri;
       
  1150 
       
  1151     $details = install_profile_info($profile->name);
       
  1152     // Don't show hidden profiles. This is used by to hide the testing profile,
       
  1153     // which only exists to speed up test runs.
       
  1154     if ($details['hidden'] === TRUE) {
       
  1155       continue;
       
  1156     }
       
  1157     $profiles[$profile->name] = $details;
       
  1158 
       
  1159     // Determine the name of the profile; default to file name if defined name
       
  1160     // is unspecified.
       
  1161     $name = isset($details['name']) ? $details['name'] : $profile->name;
       
  1162     $names[$profile->name] = $name;
       
  1163   }
       
  1164 
       
  1165   // Display radio buttons alphabetically by human-readable name, but always
       
  1166   // put the core profiles first (if they are present in the filesystem).
       
  1167   natcasesort($names);
       
  1168   if (isset($names['minimal'])) {
       
  1169     // If the expert ("Minimal") core profile is present, put it in front of
       
  1170     // any non-core profiles rather than including it with them alphabetically,
       
  1171     // since the other profiles might be intended to group together in a
       
  1172     // particular way.
       
  1173     $names = array('minimal' => $names['minimal']) + $names;
       
  1174   }
       
  1175   if (isset($names['standard'])) {
       
  1176     // If the default ("Standard") core profile is present, put it at the very
       
  1177     // top of the list. This profile will have its radio button pre-selected,
       
  1178     // so we want it to always appear at the top.
       
  1179     $names = array('standard' => $names['standard']) + $names;
       
  1180   }
       
  1181 
       
  1182   foreach ($names as $profile => $name) {
       
  1183     $form['profile'][$name] = array(
       
  1184       '#type' => 'radio',
       
  1185       '#value' => 'standard',
       
  1186       '#return_value' => $profile,
       
  1187       '#title' => $name,
       
  1188       '#description' => isset($profiles[$profile]['description']) ? $profiles[$profile]['description'] : '',
       
  1189       '#parents' => array('profile'),
       
  1190     );
       
  1191   }
       
  1192   $form['actions'] = array('#type' => 'actions');
       
  1193   $form['actions']['submit'] =  array(
       
  1194     '#type' => 'submit',
       
  1195     '#value' => st('Save and continue'),
       
  1196   );
       
  1197   return $form;
       
  1198 }
       
  1199 
       
  1200 /**
       
  1201  * Find all .po files for the current profile.
       
  1202  */
       
  1203 function install_find_locales($profilename) {
       
  1204   $locales = file_scan_directory('./profiles/' . $profilename . '/translations', '/\.po$/', array('recurse' => FALSE));
       
  1205   array_unshift($locales, (object) array('name' => 'en'));
       
  1206   foreach ($locales as $key => $locale) {
       
  1207     // The locale (file name) might be drupal-7.2.cs.po instead of cs.po.
       
  1208     $locales[$key]->langcode = preg_replace('!^(.+\.)?([^\.]+)$!', '\2', $locale->name);
       
  1209     // Language codes cannot exceed 12 characters to fit into the {languages}
       
  1210     // table.
       
  1211     if (strlen($locales[$key]->langcode) > 12) {
       
  1212       unset($locales[$key]);
       
  1213     }
       
  1214   }
       
  1215   return $locales;
       
  1216 }
       
  1217 
       
  1218 /**
       
  1219  * Installation task; select which locale to use for the current profile.
       
  1220  *
       
  1221  * @param $install_state
       
  1222  *   An array of information about the current installation state. The chosen
       
  1223  *   locale will be added here, if it was not already selected previously, as
       
  1224  *   will a list of all available locales.
       
  1225  *
       
  1226  * @return
       
  1227  *   For interactive installations, a form or other page output allowing the
       
  1228  *   locale to be selected or providing information about locale selection, if
       
  1229  *   a locale has not been chosen. Otherwise, an exception is thrown if a
       
  1230  *   locale cannot be chosen automatically.
       
  1231  */
       
  1232 function install_select_locale(&$install_state) {
       
  1233   // Find all available locales.
       
  1234   $profilename = $install_state['parameters']['profile'];
       
  1235   $locales = install_find_locales($profilename);
       
  1236   $install_state['locales'] += $locales;
       
  1237 
       
  1238   if (!empty($_POST['locale'])) {
       
  1239     foreach ($locales as $locale) {
       
  1240       if ($_POST['locale'] == $locale->langcode) {
       
  1241         $install_state['parameters']['locale'] = $locale->langcode;
       
  1242         return;
       
  1243       }
       
  1244     }
       
  1245   }
       
  1246 
       
  1247   if (empty($install_state['parameters']['locale'])) {
       
  1248     // If only the built-in (English) language is available, and we are
       
  1249     // performing an interactive installation, inform the user that the
       
  1250     // installer can be localized. Otherwise we assume the user knows what he
       
  1251     // is doing.
       
  1252     if (count($locales) == 1) {
       
  1253       if ($install_state['interactive']) {
       
  1254         drupal_set_title(st('Choose language'));
       
  1255         if (!empty($install_state['parameters']['localize'])) {
       
  1256           $output = '<p>Follow these steps to translate Drupal into your language:</p>';
       
  1257           $output .= '<ol>';
       
  1258           $output .= '<li>Download a translation from the <a href="http://localize.drupal.org/download" target="_blank">translation server</a>.</li>';
       
  1259           $output .= '<li>Place it into the following directory:
       
  1260 <pre>
       
  1261 /profiles/' . $profilename . '/translations/
       
  1262 </pre></li>';
       
  1263           $output .= '</ol>';
       
  1264           $output .= '<p>For more information on installing Drupal in different languages, visit the <a href="http://drupal.org/localize" target="_blank">drupal.org handbook page</a>.</p>';
       
  1265           $output .= '<p>How should the installation continue?</p>';
       
  1266           $output .= '<ul>';
       
  1267           $output .= '<li><a href="install.php?profile=' . $profilename . '">Reload the language selection page after adding translations</a></li>';
       
  1268           $output .= '<li><a href="install.php?profile=' . $profilename . '&amp;locale=en">Continue installation in English</a></li>';
       
  1269           $output .= '</ul>';
       
  1270         }
       
  1271         else {
       
  1272           include_once DRUPAL_ROOT . '/includes/form.inc';
       
  1273           $elements = drupal_get_form('install_select_locale_form', $locales, $profilename);
       
  1274           $output = drupal_render($elements);
       
  1275         }
       
  1276         return $output;
       
  1277       }
       
  1278       // One language, but not an interactive installation. Assume the user
       
  1279       // knows what he is doing.
       
  1280       $locale = current($locales);
       
  1281       $install_state['parameters']['locale'] = $locale->name;
       
  1282       return;
       
  1283     }
       
  1284     else {
       
  1285       // Allow profile to pre-select the language, skipping the selection.
       
  1286       $function = $profilename . '_profile_details';
       
  1287       if (function_exists($function)) {
       
  1288         $details = $function();
       
  1289         if (isset($details['language'])) {
       
  1290           foreach ($locales as $locale) {
       
  1291             if ($details['language'] == $locale->name) {
       
  1292               $install_state['parameters']['locale'] = $locale->name;
       
  1293               return;
       
  1294             }
       
  1295           }
       
  1296         }
       
  1297       }
       
  1298 
       
  1299       // We still don't have a locale, so display a form for selecting one.
       
  1300       // Only do this in the case of interactive installations, since this is
       
  1301       // not a real form with submit handlers (the database isn't even set up
       
  1302       // yet), rather just a convenience method for setting parameters in the
       
  1303       // URL.
       
  1304       if ($install_state['interactive']) {
       
  1305         drupal_set_title(st('Choose language'));
       
  1306         include_once DRUPAL_ROOT . '/includes/form.inc';
       
  1307         $elements = drupal_get_form('install_select_locale_form', $locales, $profilename);
       
  1308         return drupal_render($elements);
       
  1309       }
       
  1310       else {
       
  1311         throw new Exception(st('Sorry, you must select a language to continue the installation.'));
       
  1312       }
       
  1313     }
       
  1314   }
       
  1315 }
       
  1316 
       
  1317 /**
       
  1318  * Form constructor for the language selection form.
       
  1319  *
       
  1320  * @ingroup forms
       
  1321  */
       
  1322 function install_select_locale_form($form, &$form_state, $locales, $profilename) {
       
  1323   include_once DRUPAL_ROOT . '/includes/iso.inc';
       
  1324   $languages = _locale_get_predefined_list();
       
  1325   foreach ($locales as $locale) {
       
  1326     $name = $locale->langcode;
       
  1327     if (isset($languages[$name])) {
       
  1328       $name = $languages[$name][0] . (isset($languages[$name][1]) ? ' ' . st('(@language)', array('@language' => $languages[$name][1])) : '');
       
  1329     }
       
  1330     $form['locale'][$locale->langcode] = array(
       
  1331       '#type' => 'radio',
       
  1332       '#return_value' => $locale->langcode,
       
  1333       '#default_value' => $locale->langcode == 'en' ? 'en' : '',
       
  1334       '#title' => $name . ($locale->langcode == 'en' ? ' ' . st('(built-in)') : ''),
       
  1335       '#parents' => array('locale')
       
  1336     );
       
  1337   }
       
  1338   if (count($locales) == 1) {
       
  1339     $form['help'] = array(
       
  1340       '#markup' => '<p><a href="install.php?profile=' . $profilename . '&amp;localize=true">' . st('Learn how to install Drupal in other languages') . '</a></p>',
       
  1341     );
       
  1342   }
       
  1343   $form['actions'] = array('#type' => 'actions');
       
  1344   $form['actions']['submit'] =  array(
       
  1345     '#type' => 'submit',
       
  1346     '#value' => st('Save and continue'),
       
  1347   );
       
  1348   return $form;
       
  1349 }
       
  1350 
       
  1351 /**
       
  1352  * Indicates that there are no profiles available.
       
  1353  */
       
  1354 function install_no_profile_error() {
       
  1355   drupal_set_title(st('No profiles available'));
       
  1356   return st('We were unable to find any installation profiles. Installation 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.');
       
  1357 }
       
  1358 
       
  1359 /**
       
  1360  * Indicates that Drupal has already been installed.
       
  1361  */
       
  1362 function install_already_done_error() {
       
  1363   global $base_url;
       
  1364 
       
  1365   drupal_set_title(st('Drupal already installed'));
       
  1366   return 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><li>View your <a href="@base-url">existing site</a>.</li></ul>', array('@base-url' => $base_url));
       
  1367 }
       
  1368 
       
  1369 /**
       
  1370  * Loads information about the chosen profile during installation.
       
  1371  *
       
  1372  * @param $install_state
       
  1373  *   An array of information about the current installation state. The loaded
       
  1374  *   profile information will be added here, or an exception will be thrown if
       
  1375  *   the profile cannot be loaded.
       
  1376  */
       
  1377 function install_load_profile(&$install_state) {
       
  1378   $profile_file = DRUPAL_ROOT . '/profiles/' . $install_state['parameters']['profile'] . '/' . $install_state['parameters']['profile'] . '.profile';
       
  1379   if (file_exists($profile_file)) {
       
  1380     include_once $profile_file;
       
  1381     $install_state['profile_info'] = install_profile_info($install_state['parameters']['profile'], $install_state['parameters']['locale']);
       
  1382   }
       
  1383   else {
       
  1384     throw new Exception(st('Sorry, the profile you have chosen cannot be loaded.'));
       
  1385   }
       
  1386 }
       
  1387 
       
  1388 /**
       
  1389  * Performs a full bootstrap of Drupal during installation.
       
  1390  *
       
  1391  * @param $install_state
       
  1392  *   An array of information about the current installation state.
       
  1393  */
       
  1394 function install_bootstrap_full(&$install_state) {
       
  1395   drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
       
  1396 }
       
  1397 
       
  1398 /**
       
  1399  * Installs required modules via a batch process.
       
  1400  *
       
  1401  * @param $install_state
       
  1402  *   An array of information about the current installation state.
       
  1403  *
       
  1404  * @return
       
  1405  *   The batch definition.
       
  1406  */
       
  1407 function install_profile_modules(&$install_state) {
       
  1408   $modules = variable_get('install_profile_modules', array());
       
  1409   $files = system_rebuild_module_data();
       
  1410   variable_del('install_profile_modules');
       
  1411 
       
  1412   // Always install required modules first. Respect the dependencies between
       
  1413   // the modules.
       
  1414   $required = array();
       
  1415   $non_required = array();
       
  1416   // Although the profile module is marked as required, it needs to go after
       
  1417   // every dependency, including non-required ones. So clear its required
       
  1418   // flag for now to allow it to install late.
       
  1419   $files[$install_state['parameters']['profile']]->info['required'] = FALSE;
       
  1420   // Add modules that other modules depend on.
       
  1421   foreach ($modules as $module) {
       
  1422     if ($files[$module]->requires) {
       
  1423       $modules = array_merge($modules, array_keys($files[$module]->requires));
       
  1424     }
       
  1425   }
       
  1426   $modules = array_unique($modules);
       
  1427   foreach ($modules as $module) {
       
  1428     if (!empty($files[$module]->info['required'])) {
       
  1429       $required[$module] = $files[$module]->sort;
       
  1430     }
       
  1431     else {
       
  1432       $non_required[$module] = $files[$module]->sort;
       
  1433     }
       
  1434   }
       
  1435   arsort($required);
       
  1436   arsort($non_required);
       
  1437 
       
  1438   $operations = array();
       
  1439   foreach ($required + $non_required as $module => $weight) {
       
  1440     $operations[] = array('_install_module_batch', array($module, $files[$module]->info['name']));
       
  1441   }
       
  1442   $batch = array(
       
  1443     'operations' => $operations,
       
  1444     'title' => st('Installing @drupal', array('@drupal' => drupal_install_profile_distribution_name())),
       
  1445     'error_message' => st('The installation has encountered an error.'),
       
  1446     'finished' => '_install_profile_modules_finished',
       
  1447   );
       
  1448   return $batch;
       
  1449 }
       
  1450 
       
  1451 /**
       
  1452  * Imports languages via a batch process during installation.
       
  1453  *
       
  1454  * @param $install_state
       
  1455  *   An array of information about the current installation state.
       
  1456  *
       
  1457  * @return
       
  1458  *   The batch definition, if there are language files to import.
       
  1459  */
       
  1460 function install_import_locales(&$install_state) {
       
  1461   include_once DRUPAL_ROOT . '/includes/locale.inc';
       
  1462   $install_locale = $install_state['parameters']['locale'];
       
  1463 
       
  1464   include_once DRUPAL_ROOT . '/includes/iso.inc';
       
  1465   $predefined = _locale_get_predefined_list();
       
  1466   if (!isset($predefined[$install_locale])) {
       
  1467     // Drupal does not know about this language, so we prefill its values with
       
  1468     // our best guess. The user will be able to edit afterwards.
       
  1469     locale_add_language($install_locale, $install_locale, $install_locale, LANGUAGE_LTR, '', '', TRUE, TRUE);
       
  1470   }
       
  1471   else {
       
  1472     // A known predefined language, details will be filled in properly.
       
  1473     locale_add_language($install_locale, NULL, NULL, NULL, '', '', TRUE, TRUE);
       
  1474   }
       
  1475 
       
  1476   // Collect files to import for this language.
       
  1477   $batch = locale_batch_by_language($install_locale, NULL);
       
  1478   if (!empty($batch)) {
       
  1479     // Remember components we cover in this batch set.
       
  1480     variable_set('install_locale_batch_components', $batch['#components']);
       
  1481     return $batch;
       
  1482   }
       
  1483 }
       
  1484 
       
  1485 /**
       
  1486  * Form constructor for a form to configure the new site.
       
  1487  *
       
  1488  * @param $install_state
       
  1489  *   An array of information about the current installation state.
       
  1490  *
       
  1491  * @see install_configure_form_validate()
       
  1492  * @see install_configure_form_submit()
       
  1493  * @ingroup forms
       
  1494  */
       
  1495 function install_configure_form($form, &$form_state, &$install_state) {
       
  1496   drupal_set_title(st('Configure site'));
       
  1497 
       
  1498   // Warn about settings.php permissions risk
       
  1499   $settings_dir = conf_path();
       
  1500   $settings_file = $settings_dir . '/settings.php';
       
  1501   // Check that $_POST is empty so we only show this message when the form is
       
  1502   // first displayed, not on the next page after it is submitted. (We do not
       
  1503   // want to repeat it multiple times because it is a general warning that is
       
  1504   // not related to the rest of the installation process; it would also be
       
  1505   // especially out of place on the last page of the installer, where it would
       
  1506   // distract from the message that the Drupal installation has completed
       
  1507   // successfully.)
       
  1508   if (empty($_POST) && (!drupal_verify_install_file(DRUPAL_ROOT . '/' . $settings_file, FILE_EXIST|FILE_READABLE|FILE_NOT_WRITABLE) || !drupal_verify_install_file(DRUPAL_ROOT . '/' . $settings_dir, FILE_NOT_WRITABLE, 'dir'))) {
       
  1509     drupal_set_message(st('All necessary changes to %dir and %file have been made, so you should remove write permissions to them now in order to avoid security risks. If you are unsure how to do so, consult the <a href="@handbook_url">online handbook</a>.', array('%dir' => $settings_dir, '%file' => $settings_file, '@handbook_url' => 'http://drupal.org/server-permissions')), 'warning');
       
  1510   }
       
  1511 
       
  1512   drupal_add_js(drupal_get_path('module', 'system') . '/system.js');
       
  1513   // Add JavaScript time zone detection.
       
  1514   drupal_add_js('misc/timezone.js');
       
  1515   // We add these strings as settings because JavaScript translation does not
       
  1516   // work during installation.
       
  1517   drupal_add_js(array('copyFieldValue' => array('edit-site-mail' => array('edit-account-mail'))), 'setting');
       
  1518   drupal_add_js('jQuery(function () { Drupal.cleanURLsInstallCheck(); });', 'inline');
       
  1519   // Add JS to show / hide the 'Email administrator about site updates' elements
       
  1520   drupal_add_js('jQuery(function () { Drupal.hideEmailAdministratorCheckbox() });', 'inline');
       
  1521   // Build menu to allow clean URL check.
       
  1522   menu_rebuild();
       
  1523 
       
  1524   // Cache a fully-built schema. This is necessary for any invocation of
       
  1525   // index.php because: (1) setting cache table entries requires schema
       
  1526   // information, (2) that occurs during bootstrap before any module are
       
  1527   // loaded, so (3) if there is no cached schema, drupal_get_schema() will
       
  1528   // try to generate one but with no loaded modules will return nothing.
       
  1529   //
       
  1530   // This logically could be done during the 'install_finished' task, but the
       
  1531   // clean URL check requires it now.
       
  1532   drupal_get_schema(NULL, TRUE);
       
  1533 
       
  1534   // Return the form.
       
  1535   return _install_configure_form($form, $form_state, $install_state);
       
  1536 }
       
  1537 
       
  1538 /**
       
  1539  * Installation task; import remaining languages via a batch process.
       
  1540  *
       
  1541  * @param $install_state
       
  1542  *   An array of information about the current installation state.
       
  1543  *
       
  1544  * @return
       
  1545  *   The batch definition, if there are language files to import.
       
  1546  */
       
  1547 function install_import_locales_remaining(&$install_state) {
       
  1548   include_once DRUPAL_ROOT . '/includes/locale.inc';
       
  1549   // Collect files to import for this language. Skip components already covered
       
  1550   // in the initial batch set.
       
  1551   $install_locale = $install_state['parameters']['locale'];
       
  1552   $batch = locale_batch_by_language($install_locale, NULL, variable_get('install_locale_batch_components', array()));
       
  1553   // Remove temporary variable.
       
  1554   variable_del('install_locale_batch_components');
       
  1555   return $batch;
       
  1556 }
       
  1557 
       
  1558 /**
       
  1559  * Finishes importing files at end of installation.
       
  1560  *
       
  1561  * @param $install_state
       
  1562  *   An array of information about the current installation state.
       
  1563  *
       
  1564  * @return
       
  1565  *   A message informing the user that the installation is complete.
       
  1566  */
       
  1567 function install_finished(&$install_state) {
       
  1568   drupal_set_title(st('@drupal installation complete', array('@drupal' => drupal_install_profile_distribution_name())), PASS_THROUGH);
       
  1569   $messages = drupal_set_message();
       
  1570   $output = '<p>' . st('Congratulations, you installed @drupal!', array('@drupal' => drupal_install_profile_distribution_name())) . '</p>';
       
  1571   $output .= '<p>' . (isset($messages['error']) ? st('Review the messages above before visiting <a href="@url">your new site</a>.', array('@url' => url(''))) : st('<a href="@url">Visit your new site</a>.', array('@url' => url('')))) . '</p>';
       
  1572 
       
  1573   // Flush all caches to ensure that any full bootstraps during the installer
       
  1574   // do not leave stale cached data, and that any content types or other items
       
  1575   // registered by the installation profile are registered correctly.
       
  1576   drupal_flush_all_caches();
       
  1577 
       
  1578   // Remember the profile which was used.
       
  1579   variable_set('install_profile', drupal_get_profile());
       
  1580 
       
  1581   // Installation profiles are always loaded last
       
  1582   db_update('system')
       
  1583     ->fields(array('weight' => 1000))
       
  1584     ->condition('type', 'module')
       
  1585     ->condition('name', drupal_get_profile())
       
  1586     ->execute();
       
  1587 
       
  1588   // Cache a fully-built schema.
       
  1589   drupal_get_schema(NULL, TRUE);
       
  1590 
       
  1591   // Run cron to populate update status tables (if available) so that users
       
  1592   // will be warned if they've installed an out of date Drupal version.
       
  1593   // Will also trigger indexing of profile-supplied content or feeds.
       
  1594   drupal_cron_run();
       
  1595 
       
  1596   return $output;
       
  1597 }
       
  1598 
       
  1599 /**
       
  1600  * Implements callback_batch_operation().
       
  1601  *
       
  1602  * Performs batch installation of modules.
       
  1603  */
       
  1604 function _install_module_batch($module, $module_name, &$context) {
       
  1605   // Install and enable the module right away, so that the module will be
       
  1606   // loaded by drupal_bootstrap in subsequent batch requests, and other
       
  1607   // modules possibly depending on it can safely perform their installation
       
  1608   // steps.
       
  1609   module_enable(array($module), FALSE);
       
  1610   $context['results'][] = $module;
       
  1611   $context['message'] = st('Installed %module module.', array('%module' => $module_name));
       
  1612 }
       
  1613 
       
  1614 /**
       
  1615  * Implements callback_batch_finished().
       
  1616  *
       
  1617  * 'Finished' callback for module installation batch.
       
  1618  */
       
  1619 function _install_profile_modules_finished($success, $results, $operations) {
       
  1620   // Flush all caches to complete the module installation process. Subsequent
       
  1621   // installation tasks will now have full access to the profile's modules.
       
  1622   drupal_flush_all_caches();
       
  1623 }
       
  1624 
       
  1625 /**
       
  1626  * Checks installation requirements and reports any errors.
       
  1627  */
       
  1628 function install_check_requirements($install_state) {
       
  1629   $profile = $install_state['parameters']['profile'];
       
  1630 
       
  1631   // Check the profile requirements.
       
  1632   $requirements = drupal_check_profile($profile);
       
  1633 
       
  1634   // If Drupal is not set up already, we need to create a settings file.
       
  1635   if (!$install_state['settings_verified']) {
       
  1636     $writable = FALSE;
       
  1637     $conf_path = './' . conf_path(FALSE, TRUE);
       
  1638     $settings_file = $conf_path . '/settings.php';
       
  1639     $default_settings_file = './sites/default/default.settings.php';
       
  1640     $file = $conf_path;
       
  1641     $exists = FALSE;
       
  1642     // Verify that the directory exists.
       
  1643     if (drupal_verify_install_file($conf_path, FILE_EXIST, 'dir')) {
       
  1644       // Check if a settings.php file already exists.
       
  1645       $file = $settings_file;
       
  1646       if (drupal_verify_install_file($settings_file, FILE_EXIST)) {
       
  1647         // If it does, make sure it is writable.
       
  1648         $writable = drupal_verify_install_file($settings_file, FILE_READABLE|FILE_WRITABLE);
       
  1649         $exists = TRUE;
       
  1650       }
       
  1651     }
       
  1652 
       
  1653     // If default.settings.php does not exist, or is not readable, throw an
       
  1654     // error.
       
  1655     if (!drupal_verify_install_file($default_settings_file, FILE_EXIST|FILE_READABLE)) {
       
  1656       $requirements['default settings file exists'] = array(
       
  1657         'title'       => st('Default settings file'),
       
  1658         'value'       => st('The default settings file does not exist.'),
       
  1659         'severity'    => REQUIREMENT_ERROR,
       
  1660         'description' => st('The @drupal installer requires that the %default-file file not be modified in any way from the original download.', array('@drupal' => drupal_install_profile_distribution_name(), '%default-file' => $default_settings_file)),
       
  1661       );
       
  1662     }
       
  1663     // Otherwise, if settings.php does not exist yet, we can try to copy
       
  1664     // default.settings.php to create it.
       
  1665     elseif (!$exists) {
       
  1666       $copied = drupal_verify_install_file($conf_path, FILE_EXIST|FILE_WRITABLE, 'dir') && @copy($default_settings_file, $settings_file);
       
  1667       if ($copied) {
       
  1668         // If the new settings file has the same owner as default.settings.php,
       
  1669         // this means default.settings.php is owned by the webserver user.
       
  1670         // This is an inherent security weakness because it allows a malicious
       
  1671         // webserver process to append arbitrary PHP code and then execute it.
       
  1672         // However, it is also a common configuration on shared hosting, and
       
  1673         // there is nothing Drupal can do to prevent it. In this situation,
       
  1674         // having settings.php also owned by the webserver does not introduce
       
  1675         // any additional security risk, so we keep the file in place.
       
  1676         if (fileowner($default_settings_file) === fileowner($settings_file)) {
       
  1677           $writable = drupal_verify_install_file($settings_file, FILE_READABLE|FILE_WRITABLE);
       
  1678           $exists = TRUE;
       
  1679         }
       
  1680         // If settings.php and default.settings.php have different owners, this
       
  1681         // probably means the server is set up "securely" (with the webserver
       
  1682         // running as its own user, distinct from the user who owns all the
       
  1683         // Drupal PHP files), although with either a group or world writable
       
  1684         // sites directory. Keeping settings.php owned by the webserver would
       
  1685         // therefore introduce a security risk. It would also cause a usability
       
  1686         // problem, since site owners who do not have root access to the file
       
  1687         // system would be unable to edit their settings file later on. We
       
  1688         // therefore must delete the file we just created and force the
       
  1689         // administrator to log on to the server and create it manually.
       
  1690         else {
       
  1691           $deleted = @drupal_unlink($settings_file);
       
  1692           // We expect deleting the file to be successful (since we just
       
  1693           // created it ourselves above), but if it fails somehow, we set a
       
  1694           // variable so we can display a one-time error message to the
       
  1695           // administrator at the bottom of the requirements list. We also try
       
  1696           // to make the file writable, to eliminate any conflicting error
       
  1697           // messages in the requirements list.
       
  1698           $exists = !$deleted;
       
  1699           if ($exists) {
       
  1700             $settings_file_ownership_error = TRUE;
       
  1701             $writable = drupal_verify_install_file($settings_file, FILE_READABLE|FILE_WRITABLE);
       
  1702           }
       
  1703         }
       
  1704       }
       
  1705     }
       
  1706 
       
  1707     // If settings.php does not exist, throw an error.
       
  1708     if (!$exists) {
       
  1709       $requirements['settings file exists'] = array(
       
  1710         'title'       => st('Settings file'),
       
  1711         'value'       => st('The settings file does not exist.'),
       
  1712         'severity'    => REQUIREMENT_ERROR,
       
  1713         'description' => st('The @drupal installer requires that you create a settings file as part of the installation process. Copy the %default_file file to %file. More details about installing Drupal are available in <a href="@install_txt">INSTALL.txt</a>.', array('@drupal' => drupal_install_profile_distribution_name(), '%file' => $file, '%default_file' => $default_settings_file, '@install_txt' => base_path() . 'INSTALL.txt')),
       
  1714       );
       
  1715     }
       
  1716     else {
       
  1717       $requirements['settings file exists'] = array(
       
  1718         'title'       => st('Settings file'),
       
  1719         'value'       => st('The %file file exists.', array('%file' => $file)),
       
  1720       );
       
  1721       // If settings.php is not writable, throw an error.
       
  1722       if (!$writable) {
       
  1723         $requirements['settings file writable'] = array(
       
  1724           'title'       => st('Settings file'),
       
  1725           'value'       => st('The settings file is not writable.'),
       
  1726           'severity'    => REQUIREMENT_ERROR,
       
  1727           'description' => st('The @drupal installer requires write permissions to %file during the installation process. If you are unsure how to grant file permissions, consult the <a href="@handbook_url">online handbook</a>.', array('@drupal' => drupal_install_profile_distribution_name(), '%file' => $file, '@handbook_url' => 'http://drupal.org/server-permissions')),
       
  1728         );
       
  1729       }
       
  1730       else {
       
  1731         $requirements['settings file'] = array(
       
  1732           'title'       => st('Settings file'),
       
  1733           'value'       => st('The settings file is writable.'),
       
  1734         );
       
  1735       }
       
  1736       if (!empty($settings_file_ownership_error)) {
       
  1737         $requirements['settings file ownership'] = array(
       
  1738           'title'       => st('Settings file'),
       
  1739           'value'       => st('The settings file is owned by the web server.'),
       
  1740           'severity'    => REQUIREMENT_ERROR,
       
  1741           'description' => st('The @drupal installer failed to create a settings file with proper file ownership. Log on to your web server, remove the existing %file file, and create a new one by copying the %default_file file to %file. More details about installing Drupal are available in <a href="@install_txt">INSTALL.txt</a>. If you have problems with the file permissions on your server, consult the <a href="@handbook_url">online handbook</a>.', array('@drupal' => drupal_install_profile_distribution_name(), '%file' => $file, '%default_file' => $default_settings_file, '@install_txt' => base_path() . 'INSTALL.txt', '@handbook_url' => 'http://drupal.org/server-permissions')),
       
  1742         );
       
  1743       }
       
  1744     }
       
  1745   }
       
  1746   return $requirements;
       
  1747 }
       
  1748 
       
  1749 /**
       
  1750  * Form constructor for a site configuration form.
       
  1751  *
       
  1752  * @param $install_state
       
  1753  *   An array of information about the current installation state.
       
  1754  *
       
  1755  * @see install_configure_form()
       
  1756  * @see install_configure_form_validate()
       
  1757  * @see install_configure_form_submit()
       
  1758  * @ingroup forms
       
  1759  */
       
  1760 function _install_configure_form($form, &$form_state, &$install_state) {
       
  1761   include_once DRUPAL_ROOT . '/includes/locale.inc';
       
  1762 
       
  1763   $form['site_information'] = array(
       
  1764     '#type' => 'fieldset',
       
  1765     '#title' => st('Site information'),
       
  1766     '#collapsible' => FALSE,
       
  1767   );
       
  1768   $form['site_information']['site_name'] = array(
       
  1769     '#type' => 'textfield',
       
  1770     '#title' => st('Site name'),
       
  1771     '#required' => TRUE,
       
  1772     '#weight' => -20,
       
  1773   );
       
  1774   $form['site_information']['site_mail'] = array(
       
  1775     '#type' => 'textfield',
       
  1776     '#title' => st('Site e-mail address'),
       
  1777     '#default_value' => ini_get('sendmail_from'),
       
  1778     '#description' => st("Automated e-mails, such as registration information, will be sent from this address. Use an address ending in your site's domain to help prevent these e-mails from being flagged as spam."),
       
  1779     '#required' => TRUE,
       
  1780     '#weight' => -15,
       
  1781   );
       
  1782   $form['admin_account'] = array(
       
  1783     '#type' => 'fieldset',
       
  1784     '#title' => st('Site maintenance account'),
       
  1785     '#collapsible' => FALSE,
       
  1786   );
       
  1787 
       
  1788   $form['admin_account']['account']['#tree'] = TRUE;
       
  1789   $form['admin_account']['account']['name'] = array('#type' => 'textfield',
       
  1790     '#title' => st('Username'),
       
  1791     '#maxlength' => USERNAME_MAX_LENGTH,
       
  1792     '#description' => st('Spaces are allowed; punctuation is not allowed except for periods, hyphens, and underscores.'),
       
  1793     '#required' => TRUE,
       
  1794     '#weight' => -10,
       
  1795     '#attributes' => array('class' => array('username')),
       
  1796   );
       
  1797 
       
  1798   $form['admin_account']['account']['mail'] = array('#type' => 'textfield',
       
  1799     '#title' => st('E-mail address'),
       
  1800     '#maxlength' => EMAIL_MAX_LENGTH,
       
  1801     '#required' => TRUE,
       
  1802     '#weight' => -5,
       
  1803   );
       
  1804   $form['admin_account']['account']['pass'] = array(
       
  1805     '#type' => 'password_confirm',
       
  1806     '#required' => TRUE,
       
  1807     '#size' => 25,
       
  1808     '#weight' => 0,
       
  1809   );
       
  1810 
       
  1811   $form['server_settings'] = array(
       
  1812     '#type' => 'fieldset',
       
  1813     '#title' => st('Server settings'),
       
  1814     '#collapsible' => FALSE,
       
  1815   );
       
  1816 
       
  1817   $countries = country_get_list();
       
  1818   $form['server_settings']['site_default_country'] = array(
       
  1819     '#type' => 'select',
       
  1820     '#title' => st('Default country'),
       
  1821     '#empty_value' => '',
       
  1822     '#default_value' => variable_get('site_default_country', NULL),
       
  1823     '#options' => $countries,
       
  1824     '#description' => st('Select the default country for the site.'),
       
  1825     '#weight' => 0,
       
  1826   );
       
  1827 
       
  1828   $form['server_settings']['date_default_timezone'] = array(
       
  1829     '#type' => 'select',
       
  1830     '#title' => st('Default time zone'),
       
  1831     '#default_value' => date_default_timezone_get(),
       
  1832     '#options' => system_time_zones(),
       
  1833     '#description' => st('By default, dates in this site will be displayed in the chosen time zone.'),
       
  1834     '#weight' => 5,
       
  1835     '#attributes' => array('class' => array('timezone-detect')),
       
  1836   );
       
  1837 
       
  1838   $form['server_settings']['clean_url'] = array(
       
  1839     '#type' => 'hidden',
       
  1840     '#default_value' => 0,
       
  1841     '#attributes' => array('id' => 'edit-clean-url', 'class' => array('install')),
       
  1842   );
       
  1843 
       
  1844   $form['update_notifications'] = array(
       
  1845     '#type' => 'fieldset',
       
  1846     '#title' => st('Update notifications'),
       
  1847     '#collapsible' => FALSE,
       
  1848   );
       
  1849   $form['update_notifications']['update_status_module'] = array(
       
  1850     '#type' => 'checkboxes',
       
  1851     '#options' => array(
       
  1852       1 => st('Check for updates automatically'),
       
  1853       2 => st('Receive e-mail notifications'),
       
  1854     ),
       
  1855     '#default_value' => array(1, 2),
       
  1856     '#description' => st('The system will notify you when updates and important security releases are available for installed components. Anonymous information about your site is sent to <a href="@drupal">Drupal.org</a>.', array('@drupal' => 'http://drupal.org')),
       
  1857     '#weight' => 15,
       
  1858   );
       
  1859 
       
  1860   $form['actions'] = array('#type' => 'actions');
       
  1861   $form['actions']['submit'] = array(
       
  1862     '#type' => 'submit',
       
  1863     '#value' => st('Save and continue'),
       
  1864     '#weight' => 15,
       
  1865   );
       
  1866 
       
  1867   return $form;
       
  1868 }
       
  1869 
       
  1870 /**
       
  1871  * Form validation handler for install_configure_form().
       
  1872  *
       
  1873  * @see install_configure_form_submit()
       
  1874  */
       
  1875 function install_configure_form_validate($form, &$form_state) {
       
  1876   if ($error = user_validate_name($form_state['values']['account']['name'])) {
       
  1877     form_error($form['admin_account']['account']['name'], $error);
       
  1878   }
       
  1879   if ($error = user_validate_mail($form_state['values']['account']['mail'])) {
       
  1880     form_error($form['admin_account']['account']['mail'], $error);
       
  1881   }
       
  1882   if ($error = user_validate_mail($form_state['values']['site_mail'])) {
       
  1883     form_error($form['site_information']['site_mail'], $error);
       
  1884   }
       
  1885 }
       
  1886 
       
  1887 /**
       
  1888  * Form submission handler for install_configure_form().
       
  1889  *
       
  1890  * @see install_configure_form_validate()
       
  1891  */
       
  1892 function install_configure_form_submit($form, &$form_state) {
       
  1893   global $user;
       
  1894 
       
  1895   variable_set('site_name', $form_state['values']['site_name']);
       
  1896   variable_set('site_mail', $form_state['values']['site_mail']);
       
  1897   variable_set('date_default_timezone', $form_state['values']['date_default_timezone']);
       
  1898   variable_set('site_default_country', $form_state['values']['site_default_country']);
       
  1899 
       
  1900   // Enable update.module if this option was selected.
       
  1901   if ($form_state['values']['update_status_module'][1]) {
       
  1902     module_enable(array('update'), FALSE);
       
  1903 
       
  1904     // Add the site maintenance account's email address to the list of
       
  1905     // addresses to be notified when updates are available, if selected.
       
  1906     if ($form_state['values']['update_status_module'][2]) {
       
  1907       variable_set('update_notify_emails', array($form_state['values']['account']['mail']));
       
  1908     }
       
  1909   }
       
  1910 
       
  1911   // We precreated user 1 with placeholder values. Let's save the real values.
       
  1912   $account = user_load(1);
       
  1913   $merge_data = array('init' => $form_state['values']['account']['mail'], 'roles' => !empty($account->roles) ? $account->roles : array(), 'status' => 1, 'timezone' => $form_state['values']['date_default_timezone']);
       
  1914   user_save($account, array_merge($form_state['values']['account'], $merge_data));
       
  1915   // Load global $user and perform final login tasks.
       
  1916   $user = user_load(1);
       
  1917   user_login_finalize();
       
  1918 
       
  1919   if (isset($form_state['values']['clean_url'])) {
       
  1920     variable_set('clean_url', $form_state['values']['clean_url']);
       
  1921   }
       
  1922 
       
  1923   // Record when this install ran.
       
  1924   variable_set('install_time', $_SERVER['REQUEST_TIME']);
       
  1925 }