Drupal PHP Cross Reference Content Management Systems

Source: /includes/install.inc - 1320 lines - 43333 bytes - Summary - Text - Print

   1  <?php
   2  
   3  /**
   4  * @file
   5  * API functions for installing modules and themes.
   6  */
   7  
   8  /**
   9   * Indicates that a module has not been installed yet.
  10   */
  11  define('SCHEMA_UNINSTALLED', -1);
  12  
  13  /**
  14   * Indicates that a module has been installed.
  15   */
  16  define('SCHEMA_INSTALLED', 0);
  17  
  18  /**
  19   * Requirement severity -- Informational message only.
  20   */
  21  define('REQUIREMENT_INFO', -1);
  22  
  23  /**
  24   * Requirement severity -- Requirement successfully met.
  25   */
  26  define('REQUIREMENT_OK', 0);
  27  
  28  /**
  29   * Requirement severity -- Warning condition; proceed but flag warning.
  30   */
  31  define('REQUIREMENT_WARNING', 1);
  32  
  33  /**
  34   * Requirement severity -- Error condition; abort installation.
  35   */
  36  define('REQUIREMENT_ERROR', 2);
  37  
  38  /**
  39   * File permission check -- File exists.
  40   */
  41  define('FILE_EXIST', 1);
  42  
  43  /**
  44   * File permission check -- File is readable.
  45   */
  46  define('FILE_READABLE', 2);
  47  
  48  /**
  49   * File permission check -- File is writable.
  50   */
  51  define('FILE_WRITABLE', 4);
  52  
  53  /**
  54   * File permission check -- File is executable.
  55   */
  56  define('FILE_EXECUTABLE', 8);
  57  
  58  /**
  59   * File permission check -- File does not exist.
  60   */
  61  define('FILE_NOT_EXIST', 16);
  62  
  63  /**
  64   * File permission check -- File is not readable.
  65   */
  66  define('FILE_NOT_READABLE', 32);
  67  
  68  /**
  69   * File permission check -- File is not writable.
  70   */
  71  define('FILE_NOT_WRITABLE', 64);
  72  
  73  /**
  74   * File permission check -- File is not executable.
  75   */
  76  define('FILE_NOT_EXECUTABLE', 128);
  77  
  78  /**
  79   * Loads .install files for installed modules to initialize the update system.
  80   */
  81  function drupal_load_updates() {
  82    foreach (drupal_get_installed_schema_version(NULL, FALSE, TRUE) as $module => $schema_version) {
  83      if ($schema_version > -1) {
  84        module_load_install($module);
  85      }
  86    }
  87  }
  88  
  89  /**
  90   * Returns an array of available schema versions for a module.
  91   *
  92   * @param $module
  93   *   A module name.
  94   * @return
  95   *   If the module has updates, an array of available updates sorted by version.
  96   *   Otherwise, FALSE.
  97   */
  98  function drupal_get_schema_versions($module) {
  99    $updates = &drupal_static(__FUNCTION__, NULL);
 100    if (!isset($updates[$module])) {
 101      $updates = array();
 102  
 103      foreach (module_list() as $loaded_module) {
 104        $updates[$loaded_module] = array();
 105      }
 106  
 107      // Prepare regular expression to match all possible defined hook_update_N().
 108      $regexp = '/^(?P<module>.+)_update_(?P<version>\d+)$/';
 109      $functions = get_defined_functions();
 110      // Narrow this down to functions ending with an integer, since all
 111      // hook_update_N() functions end this way, and there are other
 112      // possible functions which match '_update_'. We use preg_grep() here
 113      // instead of foreaching through all defined functions, since the loop
 114      // through all PHP functions can take significant page execution time
 115      // and this function is called on every administrative page via
 116      // system_requirements().
 117      foreach (preg_grep('/_\d+$/', $functions['user']) as $function) {
 118        // If this function is a module update function, add it to the list of
 119        // module updates.
 120        if (preg_match($regexp, $function, $matches)) {
 121          $updates[$matches['module']][] = $matches['version'];
 122        }
 123      }
 124      // Ensure that updates are applied in numerical order.
 125      foreach ($updates as &$module_updates) {
 126        sort($module_updates, SORT_NUMERIC);
 127      }
 128    }
 129    return empty($updates[$module]) ? FALSE : $updates[$module];
 130  }
 131  
 132  /**
 133   * Returns the currently installed schema version for a module.
 134   *
 135   * @param $module
 136   *   A module name.
 137   * @param $reset
 138   *   Set to TRUE after modifying the system table.
 139   * @param $array
 140   *   Set to TRUE if you want to get information about all modules in the
 141   *   system.
 142   * @return
 143   *   The currently installed schema version, or SCHEMA_UNINSTALLED if the
 144   *   module is not installed.
 145   */
 146  function drupal_get_installed_schema_version($module, $reset = FALSE, $array = FALSE) {
 147    static $versions = array();
 148  
 149    if ($reset) {
 150      $versions = array();
 151    }
 152  
 153    if (!$versions) {
 154      $versions = array();
 155      $result = db_query("SELECT name, schema_version FROM {system} WHERE type = :type", array(':type' => 'module'));
 156      foreach ($result as $row) {
 157        $versions[$row->name] = $row->schema_version;
 158      }
 159    }
 160  
 161    if ($array) {
 162      return $versions;
 163    }
 164    else {
 165      return isset($versions[$module]) ? $versions[$module] : SCHEMA_UNINSTALLED;
 166    }
 167  }
 168  
 169  /**
 170   * Update the installed version information for a module.
 171   *
 172   * @param $module
 173   *   A module name.
 174   * @param $version
 175   *   The new schema version.
 176   */
 177  function drupal_set_installed_schema_version($module, $version) {
 178    db_update('system')
 179      ->fields(array('schema_version' => $version))
 180      ->condition('name', $module)
 181      ->execute();
 182  
 183    // Reset the static cache of module schema versions.
 184    drupal_get_installed_schema_version(NULL, TRUE);
 185  }
 186  
 187  /**
 188   * Loads the installation profile, extracting its defined distribution name.
 189   *
 190   * @return
 191   *   The distribution name defined in the profile's .info file. Defaults to
 192   *   "Drupal" if none is explicitly provided by the installation profile.
 193   *
 194   * @see install_profile_info()
 195   */
 196  function drupal_install_profile_distribution_name() {
 197    // During installation, the profile information is stored in the global
 198    // installation state (it might not be saved anywhere yet).
 199    if (drupal_installation_attempted()) {
 200      global $install_state;
 201      return $install_state['profile_info']['distribution_name'];
 202    }
 203    // At all other times, we load the profile via standard methods.
 204    else {
 205      $profile = drupal_get_profile();
 206      $info = system_get_info('module', $profile);
 207      return $info['distribution_name'];
 208    }
 209  }
 210  
 211  /**
 212   * Detects the base URL using the PHP $_SERVER variables.
 213   *
 214   * @param $file
 215   *   The name of the file calling this function so we can strip it out of
 216   *   the URI when generating the base_url.
 217   *
 218   * @return
 219   *   The auto-detected $base_url that should be configured in settings.php
 220   */
 221  function drupal_detect_baseurl($file = 'install.php') {
 222    $proto = $_SERVER['HTTPS'] ? 'https://' : 'http://';
 223    $host = $_SERVER['SERVER_NAME'];
 224    $port = ($_SERVER['SERVER_PORT'] == 80 ? '' : ':' . $_SERVER['SERVER_PORT']);
 225    $uri = preg_replace("/\?.*/", '', $_SERVER['REQUEST_URI']);
 226    $dir = str_replace("/$file", '', $uri);
 227  
 228    return "$proto$host$port$dir";
 229  }
 230  
 231  /**
 232   * Detects all supported databases that are compiled into PHP.
 233   *
 234   * @return
 235   *  An array of database types compiled into PHP.
 236   */
 237  function drupal_detect_database_types() {
 238    $databases = drupal_get_database_types();
 239  
 240    foreach ($databases as $driver => $installer) {
 241      $databases[$driver] = $installer->name();
 242    }
 243  
 244    return $databases;
 245  }
 246  
 247  /**
 248   * Returns all supported database installer objects that are compiled into PHP.
 249   *
 250   * @return
 251   *  An array of database installer objects compiled into PHP.
 252   */
 253  function drupal_get_database_types() {
 254    $databases = array();
 255  
 256    // We define a driver as a directory in /includes/database that in turn
 257    // contains a database.inc file. That allows us to drop in additional drivers
 258    // without modifying the installer.
 259    // Because we have no registry yet, we need to also include the install.inc
 260    // file for the driver explicitly.
 261    require_once  DRUPAL_ROOT . '/includes/database/database.inc';
 262    foreach (file_scan_directory(DRUPAL_ROOT . '/includes/database', '/^[a-z]*$/i', array('recurse' => FALSE)) as $file) {
 263      if (file_exists($file->uri . '/database.inc') && file_exists($file->uri . '/install.inc')) {
 264        $drivers[$file->filename] = $file->uri;
 265      }
 266    }
 267  
 268    foreach ($drivers as $driver => $file) {
 269      $installer = db_installer_object($driver);
 270      if ($installer->installable()) {
 271        $databases[$driver] = $installer;
 272      }
 273    }
 274  
 275    // Usability: unconditionally put the MySQL driver on top.
 276    if (isset($databases['mysql'])) {
 277      $mysql_database = $databases['mysql'];
 278      unset($databases['mysql']);
 279      $databases = array('mysql' => $mysql_database) + $databases;
 280    }
 281  
 282    return $databases;
 283  }
 284  
 285  /**
 286   * Database installer structure.
 287   *
 288   * Defines basic Drupal requirements for databases.
 289   */
 290  abstract class DatabaseTasks {
 291  
 292    /**
 293     * Structure that describes each task to run.
 294     *
 295     * @var array
 296     *
 297     * Each value of the tasks array is an associative array defining the function
 298     * to call (optional) and any arguments to be passed to the function.
 299     */
 300    protected $tasks = array(
 301      array(
 302        'function'    => 'checkEngineVersion',
 303        'arguments'   => array(),
 304      ),
 305      array(
 306        'arguments'   => array(
 307          'CREATE TABLE {drupal_install_test} (id int NULL)',
 308          'Drupal can use CREATE TABLE database commands.',
 309          'Failed to <strong>CREATE</strong> a test table on your database server with the command %query. The server reports the following message: %error.<p>Are you sure the configured username has the necessary permissions to create tables in the database?</p>',
 310          TRUE,
 311        ),
 312      ),
 313      array(
 314        'arguments'   => array(
 315          'INSERT INTO {drupal_install_test} (id) VALUES (1)',
 316          'Drupal can use INSERT database commands.',
 317          'Failed to <strong>INSERT</strong> a value into a test table on your database server. We tried inserting a value with the command %query and the server reported the following error: %error.',
 318        ),
 319      ),
 320      array(
 321        'arguments'   => array(
 322          'UPDATE {drupal_install_test} SET id = 2',
 323          'Drupal can use UPDATE database commands.',
 324          'Failed to <strong>UPDATE</strong> a value in a test table on your database server. We tried updating a value with the command %query and the server reported the following error: %error.',
 325        ),
 326      ),
 327      array(
 328        'arguments'   => array(
 329          'DELETE FROM {drupal_install_test}',
 330          'Drupal can use DELETE database commands.',
 331          'Failed to <strong>DELETE</strong> a value from a test table on your database server. We tried deleting a value with the command %query and the server reported the following error: %error.',
 332        ),
 333      ),
 334      array(
 335        'arguments'   => array(
 336          'DROP TABLE {drupal_install_test}',
 337          'Drupal can use DROP TABLE database commands.',
 338          'Failed to <strong>DROP</strong> a test table from your database server. We tried dropping a table with the command %query and the server reported the following error %error.',
 339        ),
 340      ),
 341    );
 342  
 343    /**
 344     * Results from tasks.
 345     *
 346     * @var array
 347     */
 348    protected $results = array();
 349  
 350    /**
 351     * Ensure the PDO driver is supported by the version of PHP in use.
 352     */
 353    protected function hasPdoDriver() {
 354      return in_array($this->pdoDriver, PDO::getAvailableDrivers());
 355    }
 356  
 357    /**
 358     * Assert test as failed.
 359     */
 360    protected function fail($message) {
 361      $this->results[$message] = FALSE;
 362    }
 363  
 364    /**
 365     * Assert test as a pass.
 366     */
 367    protected function pass($message) {
 368      $this->results[$message] = TRUE;
 369    }
 370  
 371    /**
 372     * Check whether Drupal is installable on the database.
 373     */
 374    public function installable() {
 375      return $this->hasPdoDriver() && empty($this->error);
 376    }
 377  
 378    /**
 379     * Return the human-readable name of the driver.
 380     */
 381    abstract public function name();
 382  
 383    /**
 384     * Return the minimum required version of the engine.
 385     *
 386     * @return
 387     *   A version string. If not NULL, it will be checked against the version
 388     *   reported by the Database engine using version_compare().
 389     */
 390    public function minimumVersion() {
 391      return NULL;
 392    }
 393  
 394    /**
 395     * Run database tasks and tests to see if Drupal can run on the database.
 396     */
 397    public function runTasks() {
 398      // We need to establish a connection before we can run tests.
 399      if ($this->connect()) {
 400        foreach ($this->tasks as $task) {
 401          if (!isset($task['function'])) {
 402            $task['function'] = 'runTestQuery';
 403          }
 404          if (method_exists($this, $task['function'])) {
 405            // Returning false is fatal. No other tasks can run.
 406            if (FALSE === call_user_func_array(array($this, $task['function']), $task['arguments'])) {
 407              break;
 408            }
 409          }
 410          else {
 411            throw new DatabaseTaskException(st("Failed to run all tasks against the database server. The task %task wasn't found.", array('%task' => $task['function'])));
 412          }
 413        }
 414      }
 415      // Check for failed results and compile message
 416      $message = '';
 417      foreach ($this->results as $result => $success) {
 418        if (!$success) {
 419          $message .= '<p class="error">' . $result  . '</p>';
 420        }
 421      }
 422      if (!empty($message)) {
 423        $message = '<p>In order for Drupal to work, and to continue with the installation process, you must resolve all issues reported below. For more help with configuring your database server, see the <a href="http://drupal.org/getting-started/install">installation handbook</a>. If you are unsure what any of this means you should probably contact your hosting provider.</p>' . $message;
 424        throw new DatabaseTaskException($message);
 425      }
 426    }
 427  
 428    /**
 429     * Check if we can connect to the database.
 430     */
 431    protected function connect() {
 432      try {
 433        // This doesn't actually test the connection.
 434        db_set_active();
 435        // Now actually do a check.
 436        Database::getConnection();
 437        $this->pass('Drupal can CONNECT to the database ok.');
 438      }
 439      catch (Exception $e) {
 440        $this->fail(st('Failed to connect to your database server. The server reports the following message: %error.<ul><li>Is the database server running?</li><li>Does the database exist, and have you entered the correct database name?</li><li>Have you entered the correct username and password?</li><li>Have you entered the correct database hostname?</li></ul>', array('%error' => $e->getMessage())));
 441        return FALSE;
 442      }
 443      return TRUE;
 444    }
 445  
 446    /**
 447     * Run SQL tests to ensure the database can execute commands with the current user.
 448     */
 449    protected function runTestQuery($query, $pass, $fail, $fatal = FALSE) {
 450      try {
 451        db_query($query);
 452        $this->pass(st($pass));
 453      }
 454      catch (Exception $e) {
 455        $this->fail(st($fail, array('%query' => $query, '%error' => $e->getMessage(), '%name' => $this->name())));
 456        return !$fatal;
 457      }
 458    }
 459  
 460    /**
 461     * Check the engine version.
 462     */
 463    protected function checkEngineVersion() {
 464      if ($this->minimumVersion() && version_compare(Database::getConnection()->version(), $this->minimumVersion(), '<')) {
 465        $this->fail(st("The database version %version is less than the minimum required version %minimum_version.", array('%version' => Database::getConnection()->version(), '%minimum_version' => $this->minimumVersion())));
 466      }
 467    }
 468  
 469    /**
 470     * Return driver specific configuration options.
 471     *
 472     * @param $database
 473     *  An array of driver specific configuration options.
 474     *
 475     * @return
 476     *   The options form array.
 477     */
 478    public function getFormOptions($database) {
 479      $form['database'] = array(
 480        '#type' => 'textfield',
 481        '#title' => st('Database name'),
 482        '#default_value' => empty($database['database']) ? '' : $database['database'],
 483        '#size' => 45,
 484        '#required' => TRUE,
 485        '#description' => st('The name of the database your @drupal data will be stored in. It must exist on your server before @drupal can be installed.', array('@drupal' => drupal_install_profile_distribution_name())),
 486      );
 487  
 488      $form['username'] = array(
 489        '#type' => 'textfield',
 490        '#title' => st('Database username'),
 491        '#default_value' => empty($database['username']) ? '' : $database['username'],
 492        '#required' => TRUE,
 493        '#size' => 45,
 494      );
 495  
 496      $form['password'] = array(
 497        '#type' => 'password',
 498        '#title' => st('Database password'),
 499        '#default_value' => empty($database['password']) ? '' : $database['password'],
 500        '#required' => FALSE,
 501        '#size' => 45,
 502      );
 503  
 504      $form['advanced_options'] = array(
 505        '#type' => 'fieldset',
 506        '#title' => st('Advanced options'),
 507        '#collapsible' => TRUE,
 508        '#collapsed' => TRUE,
 509        '#description' => st("These options are only necessary for some sites. If you're not sure what you should enter here, leave the default settings or check with your hosting provider."),
 510        '#weight' => 10,
 511      );
 512  
 513      $profile = drupal_get_profile();
 514      $db_prefix = ($profile == 'standard') ? 'drupal_' : $profile . '_';
 515      $form['advanced_options']['db_prefix'] = array(
 516        '#type' => 'textfield',
 517        '#title' => st('Table prefix'),
 518        '#default_value' => '',
 519        '#size' => 45,
 520        '#description' => st('If more than one application will be sharing this database, enter a table prefix such as %prefix for your @drupal site here.', array('@drupal' => drupal_install_profile_distribution_name(), '%prefix' => $db_prefix)),
 521        '#weight' => 10,
 522      );
 523  
 524      $form['advanced_options']['host'] = array(
 525        '#type' => 'textfield',
 526        '#title' => st('Database host'),
 527        '#default_value' => empty($database['host']) ? 'localhost' : $database['host'],
 528        '#size' => 45,
 529        // Hostnames can be 255 characters long.
 530        '#maxlength' => 255,
 531        '#required' => TRUE,
 532        '#description' => st('If your database is located on a different server, change this.'),
 533      );
 534  
 535      $form['advanced_options']['port'] = array(
 536        '#type' => 'textfield',
 537        '#title' => st('Database port'),
 538        '#default_value' => empty($database['port']) ? '' : $database['port'],
 539        '#size' => 45,
 540        // The maximum port number is 65536, 5 digits.
 541        '#maxlength' => 5,
 542        '#description' => st('If your database server is listening to a non-standard port, enter its number.'),
 543      );
 544  
 545      return $form;
 546    }
 547  
 548    /**
 549     * Validates driver specific configuration settings.
 550     *
 551     * Checks to ensure correct basic database settings and that a proper
 552     * connection to the database can be established.
 553     *
 554     * @param $database
 555     *   An array of driver specific configuration options.
 556     *
 557     * @return
 558     *   An array of driver configuration errors, keyed by form element name.
 559     */
 560    public function validateDatabaseSettings($database) {
 561      $errors = array();
 562  
 563      // Verify the table prefix.
 564      if (!empty($database['prefix']) && is_string($database['prefix']) && !preg_match('/^[A-Za-z0-9_.]+$/', $database['prefix'])) {
 565        $errors[$database['driver'] . '][advanced_options][db_prefix'] = st('The database table prefix you have entered, %prefix, is invalid. The table prefix can only contain alphanumeric characters, periods, or underscores.', array('%prefix' => $database['prefix']));
 566      }
 567  
 568      // Verify the database port.
 569      if (!empty($database['port']) && !is_numeric($database['port'])) {
 570        $errors[$database['driver'] . '][advanced_options][port'] =  st('Database port must be a number.');
 571      }
 572  
 573      return $errors;
 574    }
 575  
 576  }
 577  
 578  /**
 579   * Exception thrown if the database installer fails.
 580   */
 581  class DatabaseTaskException extends Exception {
 582  }
 583  
 584  /**
 585   * Replaces values in settings.php with values in the submitted array.
 586   *
 587   * @param $settings
 588   *   An array of settings that need to be updated.
 589   */
 590  function drupal_rewrite_settings($settings = array(), $prefix = '') {
 591    $default_settings = 'sites/default/default.settings.php';
 592    drupal_static_reset('conf_path');
 593    $settings_file = conf_path(FALSE) . '/' . $prefix . 'settings.php';
 594  
 595    // Build list of setting names and insert the values into the global namespace.
 596    $keys = array();
 597    foreach ($settings as $setting => $data) {
 598      $GLOBALS[$setting] = $data['value'];
 599      $keys[] = $setting;
 600    }
 601  
 602    $buffer = NULL;
 603    $first = TRUE;
 604    if ($fp = fopen(DRUPAL_ROOT . '/' . $default_settings, 'r')) {
 605      // Step line by line through settings.php.
 606      while (!feof($fp)) {
 607        $line = fgets($fp);
 608        if ($first && substr($line, 0, 5) != '<?php') {
 609          $buffer = "<?php\n\n";
 610        }
 611        $first = FALSE;
 612        // Check for constants.
 613        if (substr($line, 0, 7) == 'define(') {
 614          preg_match('/define\(\s*[\'"]([A-Z_-]+)[\'"]\s*,(.*?)\);/', $line, $variable);
 615          if (in_array($variable[1], $keys)) {
 616            $setting = $settings[$variable[1]];
 617            $buffer .= str_replace($variable[2], " '" . $setting['value'] . "'", $line);
 618            unset($settings[$variable[1]]);
 619            unset($settings[$variable[2]]);
 620          }
 621          else {
 622            $buffer .= $line;
 623          }
 624        }
 625        // Check for variables.
 626        elseif (substr($line, 0, 1) == '$') {
 627          preg_match('/\$([^ ]*) /', $line, $variable);
 628          if (in_array($variable[1], $keys)) {
 629            // Write new value to settings.php in the following format:
 630            //    $'setting' = 'value'; // 'comment'
 631            $setting = $settings[$variable[1]];
 632            $buffer .= '$' . $variable[1] . " = " . var_export($setting['value'], TRUE) . ";" . (!empty($setting['comment']) ? ' // ' . $setting['comment'] . "\n" : "\n");
 633            unset($settings[$variable[1]]);
 634          }
 635          else {
 636            $buffer .= $line;
 637          }
 638        }
 639        else {
 640          $buffer .= $line;
 641        }
 642      }
 643      fclose($fp);
 644  
 645      // Add required settings that were missing from settings.php.
 646      foreach ($settings as $setting => $data) {
 647        if ($data['required']) {
 648          $buffer .= "\$$setting = " . var_export($data['value'], TRUE) . ";\n";
 649        }
 650      }
 651  
 652      $fp = fopen(DRUPAL_ROOT . '/' . $settings_file, 'w');
 653      if ($fp && fwrite($fp, $buffer) === FALSE) {
 654        throw new Exception(st('Failed to modify %settings. Verify the file permissions.', array('%settings' => $settings_file)));
 655      }
 656    }
 657    else {
 658      throw new Exception(st('Failed to open %settings. Verify the file permissions.', array('%settings' => $default_settings)));
 659    }
 660  }
 661  
 662  /**
 663   * Verifies an installation profile for installation.
 664   *
 665   * @param $install_state
 666   *   An array of information about the current installation state.
 667   *
 668   * @return
 669   *   The list of modules to install.
 670   */
 671  function drupal_verify_profile($install_state) {
 672    $profile = $install_state['parameters']['profile'];
 673    $locale = $install_state['parameters']['locale'];
 674  
 675    include_once  DRUPAL_ROOT . '/includes/file.inc';
 676    include_once  DRUPAL_ROOT . '/includes/common.inc';
 677  
 678    $profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile";
 679  
 680    if (!isset($profile) || !file_exists($profile_file)) {
 681      throw new Exception(install_no_profile_error());
 682    }
 683    $info = $install_state['profile_info'];
 684  
 685    // Get a list of modules that exist in Drupal's assorted subdirectories.
 686    $present_modules = array();
 687    foreach (drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0) as $present_module) {
 688      $present_modules[] = $present_module->name;
 689    }
 690  
 691    // The installation profile is also a module, which needs to be installed
 692    // after all the other dependencies have been installed.
 693    $present_modules[] = drupal_get_profile();
 694  
 695    // Verify that all of the profile's required modules are present.
 696    $missing_modules = array_diff($info['dependencies'], $present_modules);
 697  
 698    $requirements = array();
 699  
 700    if (count($missing_modules)) {
 701      $modules = array();
 702      foreach ($missing_modules as $module) {
 703        $modules[] = '<span class="admin-missing">' . drupal_ucfirst($module) . '</span>';
 704      }
 705      $requirements['required_modules'] = array(
 706        'title'       => st('Required modules'),
 707        'value'       => st('Required modules not found.'),
 708        'severity'    => REQUIREMENT_ERROR,
 709        'description' => st('The following modules are required but were not found. Move them into the appropriate modules subdirectory, such as <em>sites/all/modules</em>. Missing modules: !modules', array('!modules' => implode(', ', $modules))),
 710      );
 711    }
 712    return $requirements;
 713  }
 714  
 715  /**
 716   * Installs the system module.
 717   *
 718   * Separated from the installation of other modules so core system
 719   * functions can be made available while other modules are installed.
 720   */
 721  function drupal_install_system() {
 722    $system_path = drupal_get_path('module', 'system');
 723    require_once DRUPAL_ROOT . '/' . $system_path . '/system.install';
 724    module_invoke('system', 'install');
 725  
 726    $system_versions = drupal_get_schema_versions('system');
 727    $system_version = $system_versions ? max($system_versions) : SCHEMA_INSTALLED;
 728    db_insert('system')
 729      ->fields(array('filename', 'name', 'type', 'owner', 'status', 'schema_version', 'bootstrap'))
 730      ->values(array(
 731          'filename' => $system_path . '/system.module',
 732          'name' => 'system',
 733          'type' => 'module',
 734          'owner' => '',
 735          'status' => 1,
 736          'schema_version' => $system_version,
 737          'bootstrap' => 0,
 738        ))
 739      ->execute();
 740    system_rebuild_module_data();
 741  }
 742  
 743  /**
 744   * Uninstalls a given list of modules.
 745   *
 746   * @param $module_list
 747   *   The modules to uninstall.
 748   * @param $uninstall_dependents
 749   *   If TRUE, the function will check that all modules which depend on the
 750   *   passed-in module list either are already uninstalled or contained in the
 751   *   list, and it will ensure that the modules are uninstalled in the correct
 752   *   order. This incurs a significant performance cost, so use FALSE if you
 753   *   know $module_list is already complete and in the correct order.
 754   *
 755   * @return
 756   *   FALSE if one or more dependent modules are missing from the list, TRUE
 757   *   otherwise.
 758   */
 759  function drupal_uninstall_modules($module_list = array(), $uninstall_dependents = TRUE) {
 760    if ($uninstall_dependents) {
 761      // Get all module data so we can find dependents and sort.
 762      $module_data = system_rebuild_module_data();
 763      // Create an associative array with weights as values.
 764      $module_list = array_flip(array_values($module_list));
 765  
 766      $profile = drupal_get_profile();
 767      while (list($module) = each($module_list)) {
 768        if (!isset($module_data[$module]) || drupal_get_installed_schema_version($module) == SCHEMA_UNINSTALLED) {
 769          // This module doesn't exist or is already uninstalled, skip it.
 770          unset($module_list[$module]);
 771          continue;
 772        }
 773        $module_list[$module] = $module_data[$module]->sort;
 774  
 775        // If the module has any dependents which are not already uninstalled and
 776        // not included in the passed-in list, abort. It is not safe to uninstall
 777        // them automatically because uninstalling a module is a destructive
 778        // operation.
 779        foreach (array_keys($module_data[$module]->required_by) as $dependent) {
 780          if (!isset($module_list[$dependent]) && drupal_get_installed_schema_version($dependent) != SCHEMA_UNINSTALLED && $dependent != $profile) {
 781            return FALSE;
 782          }
 783        }
 784      }
 785  
 786      // Sort the module list by pre-calculated weights.
 787      asort($module_list);
 788      $module_list = array_keys($module_list);
 789    }
 790  
 791    foreach ($module_list as $module) {
 792      // Uninstall the module.
 793      module_load_install($module);
 794      module_invoke($module, 'uninstall');
 795      drupal_uninstall_schema($module);
 796  
 797      watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO);
 798      drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
 799    }
 800  
 801    if (!empty($module_list)) {
 802      // Call hook_module_uninstall to let other modules act
 803      module_invoke_all('modules_uninstalled', $module_list);
 804    }
 805  
 806    return TRUE;
 807  }
 808  
 809  /**
 810   * Verifies the state of the specified file.
 811   *
 812   * @param $file
 813   *   The file to check for.
 814   * @param $mask
 815   *   An optional bitmask created from various FILE_* constants.
 816   * @param $type
 817   *   The type of file. Can be file (default), dir, or link.
 818   *
 819   * @return
 820   *   TRUE on success or FALSE on failure. A message is set for the latter.
 821   */
 822  function drupal_verify_install_file($file, $mask = NULL, $type = 'file') {
 823    $return = TRUE;
 824    // Check for files that shouldn't be there.
 825    if (isset($mask) && ($mask & FILE_NOT_EXIST) && file_exists($file)) {
 826      return FALSE;
 827    }
 828    // Verify that the file is the type of file it is supposed to be.
 829    if (isset($type) && file_exists($file)) {
 830      $check = 'is_' . $type;
 831      if (!function_exists($check) || !$check($file)) {
 832        $return = FALSE;
 833      }
 834    }
 835  
 836    // Verify file permissions.
 837    if (isset($mask)) {
 838      $masks = array(FILE_EXIST, FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
 839      foreach ($masks as $current_mask) {
 840        if ($mask & $current_mask) {
 841          switch ($current_mask) {
 842            case FILE_EXIST:
 843              if (!file_exists($file)) {
 844                if ($type == 'dir') {
 845                  drupal_install_mkdir($file, $mask);
 846                }
 847                if (!file_exists($file)) {
 848                  $return = FALSE;
 849                }
 850              }
 851              break;
 852            case FILE_READABLE:
 853              if (!is_readable($file) && !drupal_install_fix_file($file, $mask)) {
 854                $return = FALSE;
 855              }
 856              break;
 857            case FILE_WRITABLE:
 858              if (!is_writable($file) && !drupal_install_fix_file($file, $mask)) {
 859                $return = FALSE;
 860              }
 861              break;
 862            case FILE_EXECUTABLE:
 863              if (!is_executable($file) && !drupal_install_fix_file($file, $mask)) {
 864                $return = FALSE;
 865              }
 866              break;
 867            case FILE_NOT_READABLE:
 868              if (is_readable($file) && !drupal_install_fix_file($file, $mask)) {
 869                $return = FALSE;
 870              }
 871              break;
 872            case FILE_NOT_WRITABLE:
 873              if (is_writable($file) && !drupal_install_fix_file($file, $mask)) {
 874                $return = FALSE;
 875              }
 876              break;
 877            case FILE_NOT_EXECUTABLE:
 878              if (is_executable($file) && !drupal_install_fix_file($file, $mask)) {
 879                $return = FALSE;
 880              }
 881              break;
 882          }
 883        }
 884      }
 885    }
 886    return $return;
 887  }
 888  
 889  /**
 890   * Creates a directory with the specified permissions.
 891   *
 892   * @param $file
 893   *  The name of the directory to create;
 894   * @param $mask
 895   *  The permissions of the directory to create.
 896   * @param $message
 897   *  (optional) Whether to output messages. Defaults to TRUE.
 898   *
 899   * @return
 900   *  TRUE/FALSE whether or not the directory was successfully created.
 901   */
 902  function drupal_install_mkdir($file, $mask, $message = TRUE) {
 903    $mod = 0;
 904    $masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
 905    foreach ($masks as $m) {
 906      if ($mask & $m) {
 907        switch ($m) {
 908          case FILE_READABLE:
 909            $mod |= 0444;
 910            break;
 911          case FILE_WRITABLE:
 912            $mod |= 0222;
 913            break;
 914          case FILE_EXECUTABLE:
 915            $mod |= 0111;
 916            break;
 917        }
 918      }
 919    }
 920  
 921    if (@drupal_mkdir($file, $mod)) {
 922      return TRUE;
 923    }
 924    else {
 925      return FALSE;
 926    }
 927  }
 928  
 929  /**
 930   * Attempts to fix file permissions.
 931   *
 932   * The general approach here is that, because we do not know the security
 933   * setup of the webserver, we apply our permission changes to all three
 934   * digits of the file permission (i.e. user, group and all).
 935   *
 936   * To ensure that the values behave as expected (and numbers don't carry
 937   * from one digit to the next) we do the calculation on the octal value
 938   * using bitwise operations. This lets us remove, for example, 0222 from
 939   * 0700 and get the correct value of 0500.
 940   *
 941   * @param $file
 942   *  The name of the file with permissions to fix.
 943   * @param $mask
 944   *  The desired permissions for the file.
 945   * @param $message
 946   *  (optional) Whether to output messages. Defaults to TRUE.
 947   *
 948   * @return
 949   *  TRUE/FALSE whether or not we were able to fix the file's permissions.
 950   */
 951  function drupal_install_fix_file($file, $mask, $message = TRUE) {
 952    // If $file does not exist, fileperms() issues a PHP warning.
 953    if (!file_exists($file)) {
 954      return FALSE;
 955    }
 956  
 957    $mod = fileperms($file) & 0777;
 958    $masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
 959  
 960    // FILE_READABLE, FILE_WRITABLE, and FILE_EXECUTABLE permission strings
 961    // can theoretically be 0400, 0200, and 0100 respectively, but to be safe
 962    // we set all three access types in case the administrator intends to
 963    // change the owner of settings.php after installation.
 964    foreach ($masks as $m) {
 965      if ($mask & $m) {
 966        switch ($m) {
 967          case FILE_READABLE:
 968            if (!is_readable($file)) {
 969              $mod |= 0444;
 970            }
 971            break;
 972          case FILE_WRITABLE:
 973            if (!is_writable($file)) {
 974              $mod |= 0222;
 975            }
 976            break;
 977          case FILE_EXECUTABLE:
 978            if (!is_executable($file)) {
 979              $mod |= 0111;
 980            }
 981            break;
 982          case FILE_NOT_READABLE:
 983            if (is_readable($file)) {
 984              $mod &= ~0444;
 985            }
 986            break;
 987          case FILE_NOT_WRITABLE:
 988            if (is_writable($file)) {
 989              $mod &= ~0222;
 990            }
 991            break;
 992          case FILE_NOT_EXECUTABLE:
 993            if (is_executable($file)) {
 994              $mod &= ~0111;
 995            }
 996            break;
 997        }
 998      }
 999    }
1000  
1001    // chmod() will work if the web server is running as owner of the file.
1002    // If PHP safe_mode is enabled the currently executing script must also
1003    // have the same owner.
1004    if (@chmod($file, $mod)) {
1005      return TRUE;
1006    }
1007    else {
1008      return FALSE;
1009    }
1010  }
1011  
1012  /**
1013   * Sends the user to a different installer page.
1014   *
1015   * This issues an on-site HTTP redirect. Messages (and errors) are erased.
1016   *
1017   * @param $path
1018   *   An installer path.
1019   */
1020  function install_goto($path) {
1021    global $base_url;
1022    include_once  DRUPAL_ROOT . '/includes/common.inc';
1023    header('Location: ' . $base_url . '/' . $path);
1024    header('Cache-Control: no-cache'); // Not a permanent redirect.
1025    drupal_exit();
1026  }
1027  
1028  /**
1029   * Returns the URL of the current script, with modified query parameters.
1030   *
1031   * This function can be called by low-level scripts (such as install.php and
1032   * update.php) and returns the URL of the current script. Existing query
1033   * parameters are preserved by default, but new ones can optionally be merged
1034   * in.
1035   *
1036   * This function is used when the script must maintain certain query parameters
1037   * over multiple page requests in order to work correctly. In such cases (for
1038   * example, update.php, which requires the 'continue=1' parameter to remain in
1039   * the URL throughout the update process if there are any requirement warnings
1040   * that need to be bypassed), using this function to generate the URL for links
1041   * to the next steps of the script ensures that the links will work correctly.
1042   *
1043   * @param $query
1044   *   (optional) An array of query parameters to merge in to the existing ones.
1045   *
1046   * @return
1047   *   The URL of the current script, with query parameters modified by the
1048   *   passed-in $query. The URL is not sanitized, so it still needs to be run
1049   *   through check_url() if it will be used as an HTML attribute value.
1050   *
1051   * @see drupal_requirements_url()
1052   */
1053  function drupal_current_script_url($query = array()) {
1054    $uri = $_SERVER['SCRIPT_NAME'];
1055    $query = array_merge(drupal_get_query_parameters(), $query);
1056    if (!empty($query)) {
1057      $uri .= '?' . drupal_http_build_query($query);
1058    }
1059    return $uri;
1060  }
1061  
1062  /**
1063   * Returns a URL for proceeding to the next page after a requirements problem.
1064   *
1065   * This function can be called by low-level scripts (such as install.php and
1066   * update.php) and returns a URL that can be used to attempt to proceed to the
1067   * next step of the script.
1068   *
1069   * @param $severity
1070   *   The severity of the requirements problem, as returned by
1071   *   drupal_requirements_severity().
1072   *
1073   * @return
1074   *   A URL for attempting to proceed to the next step of the script. The URL is
1075   *   not sanitized, so it still needs to be run through check_url() if it will
1076   *   be used as an HTML attribute value.
1077   *
1078   * @see drupal_current_script_url()
1079   */
1080  function drupal_requirements_url($severity) {
1081    $query = array();
1082    // If there are no errors, only warnings, append 'continue=1' to the URL so
1083    // the user can bypass this screen on the next page load.
1084    if ($severity == REQUIREMENT_WARNING) {
1085      $query['continue'] = 1;
1086    }
1087    return drupal_current_script_url($query);
1088  }
1089  
1090  /**
1091   * Translates a string when some systems are not available.
1092   *
1093   * Used during the install process, when database, theme, and localization
1094   * system is possibly not yet available.
1095   *
1096   * Use t() if your code will never run during the Drupal installation phase.
1097   * Use st() if your code will only run during installation and never any other
1098   * time. Use get_t() if your code could run in either circumstance.
1099   *
1100   * @see t()
1101   * @see get_t()
1102   * @ingroup sanitization
1103   */
1104  function st($string, array $args = array(), array $options = array()) {
1105    static $locale_strings = NULL;
1106    global $install_state;
1107  
1108    if (empty($options['context'])) {
1109      $options['context'] = '';
1110    }
1111  
1112    if (!isset($locale_strings)) {
1113      $locale_strings = array();
1114      if (isset($install_state['parameters']['profile']) && isset($install_state['parameters']['locale'])) {
1115        // If the given locale was selected, there should be at least one .po file
1116        // with its name ending in {$install_state['parameters']['locale']}.po
1117        // This might or might not be the entire filename. It is also possible
1118        // that multiple files end with the same extension, even if unlikely.
1119        $po_files = file_scan_directory('./profiles/' . $install_state['parameters']['profile'] . '/translations', '/'. $install_state['parameters']['locale'] .'\.po$/', array('recurse' => FALSE));
1120        if (count($po_files)) {
1121          require_once  DRUPAL_ROOT . '/includes/locale.inc';
1122          foreach ($po_files as $po_file) {
1123            _locale_import_read_po('mem-store', $po_file);
1124          }
1125          $locale_strings = _locale_import_one_string('mem-report');
1126        }
1127      }
1128    }
1129  
1130    require_once  DRUPAL_ROOT . '/includes/theme.inc';
1131    // Transform arguments before inserting them
1132    foreach ($args as $key => $value) {
1133      switch ($key[0]) {
1134        // Escaped only
1135        case '@':
1136          $args[$key] = check_plain($value);
1137          break;
1138        // Escaped and placeholder
1139        case '%':
1140        default:
1141          $args[$key] = '<em>' . check_plain($value) . '</em>';
1142          break;
1143        // Pass-through
1144        case '!':
1145      }
1146    }
1147    return strtr((!empty($locale_strings[$options['context']][$string]) ? $locale_strings[$options['context']][$string] : $string), $args);
1148  }
1149  
1150  /**
1151   * Checks an installation profile's requirements.
1152   *
1153   * @param $profile
1154   *   Name of installation profile to check.
1155   * @return
1156   *   Array of the installation profile's requirements.
1157   */
1158  function drupal_check_profile($profile) {
1159    include_once  DRUPAL_ROOT . '/includes/file.inc';
1160  
1161    $profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile";
1162  
1163    if (!isset($profile) || !file_exists($profile_file)) {
1164      throw new Exception(install_no_profile_error());
1165    }
1166  
1167    $info = install_profile_info($profile);
1168  
1169    // Collect requirement testing results.
1170    $requirements = array();
1171    foreach ($info['dependencies'] as $module) {
1172      module_load_install($module);
1173      $function = $module . '_requirements';
1174      if (function_exists($function)) {
1175        $requirements = array_merge($requirements, $function('install'));
1176      }
1177    }
1178    return $requirements;
1179  }
1180  
1181  /**
1182   * Extracts the highest severity from the requirements array.
1183   *
1184   * @param $requirements
1185   *   An array of requirements, in the same format as is returned by
1186   *   hook_requirements().
1187   *
1188   * @return
1189   *   The highest severity in the array.
1190   */
1191  function drupal_requirements_severity(&$requirements) {
1192    $severity = REQUIREMENT_OK;
1193    foreach ($requirements as $requirement) {
1194      if (isset($requirement['severity'])) {
1195        $severity = max($severity, $requirement['severity']);
1196      }
1197    }
1198    return $severity;
1199  }
1200  
1201  /**
1202   * Checks a module's requirements.
1203   *
1204   * @param $module
1205   *   Machine name of module to check.
1206   *
1207   * @return
1208   *   TRUE or FALSE, depending on whether the requirements are met.
1209   */
1210  function drupal_check_module($module) {
1211    module_load_install($module);
1212    if (module_hook($module, 'requirements')) {
1213      // Check requirements
1214      $requirements = module_invoke($module, 'requirements', 'install');
1215      if (is_array($requirements) && drupal_requirements_severity($requirements) == REQUIREMENT_ERROR) {
1216        // Print any error messages
1217        foreach ($requirements as $requirement) {
1218          if (isset($requirement['severity']) && $requirement['severity'] == REQUIREMENT_ERROR) {
1219            $message = $requirement['description'];
1220            if (isset($requirement['value']) && $requirement['value']) {
1221              $message .= ' (' . t('Currently using !item !version', array('!item' => $requirement['title'], '!version' => $requirement['value'])) . ')';
1222            }
1223            drupal_set_message($message, 'error');
1224          }
1225        }
1226        return FALSE;
1227      }
1228    }
1229    return TRUE;
1230  }
1231  
1232  /**
1233   * Retrieves information about an installation profile from its .info file.
1234   *
1235   * The information stored in a profile .info file is similar to that stored in
1236   * a normal Drupal module .info file. For example:
1237   * - name: The real name of the installation profile for display purposes.
1238   * - description: A brief description of the profile.
1239   * - dependencies: An array of shortnames of other modules that this install
1240   *   profile requires.
1241   *
1242   * Additional, less commonly-used information that can appear in a profile.info
1243   * file but not in a normal Drupal module .info file includes:
1244   * - distribution_name: The name of the Drupal distribution that is being
1245   *   installed, to be shown throughout the installation process. Defaults to
1246   *   'Drupal'.
1247   *
1248   * Note that this function does an expensive file system scan to get info file
1249   * information for dependencies. If you only need information from the info
1250   * file itself, use system_get_info().
1251   *
1252   * Example of .info file:
1253   * @code
1254   *    name = Minimal
1255   *    description = Start fresh, with only a few modules enabled.
1256   *    dependencies[] = block
1257   *    dependencies[] = dblog
1258   * @endcode
1259   *
1260   * @param $profile
1261   *   Name of profile.
1262   * @param $locale
1263   *   Name of locale used (if any).
1264   *
1265   * @return
1266   *   The info array.
1267   */
1268  function install_profile_info($profile, $locale = 'en') {
1269    $cache = &drupal_static(__FUNCTION__, array());
1270  
1271    if (!isset($cache[$profile])) {
1272      // Set defaults for module info.
1273      $defaults = array(
1274        'dependencies' => array(),
1275        'description' => '',
1276        'distribution_name' => 'Drupal',
1277        'version' => NULL,
1278        'hidden' => FALSE,
1279        'php' => DRUPAL_MINIMUM_PHP,
1280      );
1281      $info = drupal_parse_info_file("profiles/$profile/$profile.info") + $defaults;
1282      $info['dependencies'] = array_unique(array_merge(
1283        drupal_required_modules(),
1284        $info['dependencies'],
1285        ($locale != 'en' && !empty($locale) ? array('locale') : array()))
1286      );
1287  
1288      // drupal_required_modules() includes the current profile as a dependency.
1289      // Since a module can't depend on itself we remove that element of the array.
1290      array_shift($info['dependencies']);
1291  
1292      $cache[$profile] = $info;
1293    }
1294    return $cache[$profile];
1295  }
1296  
1297  /**
1298   * Ensures the environment for a Drupal database on a predefined connection.
1299   *
1300   * This will run tasks that check that Drupal can perform all of the functions
1301   * on a database, that Drupal needs. Tasks include simple checks like CREATE
1302   * TABLE to database specific functions like stored procedures and client
1303   * encoding.
1304   */
1305  function db_run_tasks($driver) {
1306    db_installer_object($driver)->runTasks();
1307    return TRUE;
1308  }
1309  
1310  /**
1311   * Returns a database installer object.
1312   *
1313   * @param $driver
1314   *   The name of the driver.
1315   */
1316  function db_installer_object($driver) {
1317    Database::loadDriverFile($driver, array('install.inc'));
1318    $task_class = 'DatabaseTasks_' . $driver;
1319    return new $task_class();
1320  }

title

Description

title

Description

title

Description

title

title

Body