- <?php
- * @file
- * Backdrop site update API.
- *
- * This file contains functions to perform database and config updates for a
- * Backdrop installation. It is included and used extensively by update.php.
- */
-
- * Minimum schema version of Drupal 7 required for upgrade to Backdrop.
- *
- * Upgrades from Drupal 7 to Backdrop require that Drupal 7 be running the most
- * recent version, or the upgrade could fail. We can't check the Drupal 7
- * version once the update process has begun, so instead we check the schema
- * version of system.module in the system table.
- */
- define('REQUIRED_D7_SCHEMA_VERSION', '7069');
-
- * Disable any items in the {system} table that are not core compatible.
- */
- function update_fix_compatibility() {
- $incompatible = array();
- $result = db_query("SELECT name, type, status FROM {system} WHERE status = 1 AND type IN ('module','theme')");
- foreach ($result as $row) {
- if (update_check_incompatibility($row->name, $row->type)) {
- $incompatible[] = $row->name;
- }
- }
- if (!empty($incompatible)) {
- db_update('system')
- ->fields(array('status' => 0))
- ->condition('name', $incompatible, 'IN')
- ->execute();
- }
- }
-
- * Tests the compatibility of a module or theme.
- */
- function update_check_incompatibility($name, $type = 'module') {
- static $themes, $modules;
-
-
- if (empty($themes) || empty($modules)) {
-
-
-
- $themes = system_rebuild_theme_data();
- $modules = system_rebuild_module_data();
- }
-
- if ($type == 'module' && isset($modules[$name])) {
- $file = $modules[$name];
- }
- elseif ($type == 'theme' && isset($themes[$name])) {
- $file = $themes[$name];
- }
- if (!isset($file)
- || !isset($file->info['backdrop'])
- || $file->info['backdrop'] != BACKDROP_CORE_COMPATIBILITY
- || version_compare(phpversion(), $file->info['php']) < 0) {
- return TRUE;
- }
- return FALSE;
- }
-
- * Performs extra steps required to bootstrap when using a Drupal 7 database.
- *
- * Users who still have a Drupal 7 database (and are in the process of
- * updating to Backdrop) need extra help before a full bootstrap can be
- * achieved. This function does the necessary preliminary work that allows
- * the bootstrap to be successful.
- *
- * No access check has been performed when this function is called, so no
- * irreversible changes to the database are made here.
- */
- function update_prepare_bootstrap() {
-
-
- include_once BACKDROP_ROOT . '/core/includes/install.inc';
- include_once BACKDROP_ROOT . '/core/modules/entity/entity.controller.inc';
- backdrop_bootstrap(BACKDROP_BOOTSTRAP_CONFIGURATION);
-
-
- $settings_exist = !empty($GLOBALS['config_directories']);
- $active_config = config_get_config_storage('active');
- $staging_config = config_get_config_storage('staging');
-
- if (!is_a($active_config, 'ConfigDatabaseStorage')) {
- if (!$settings_exist || !$active_config->isInitialized()) {
- backdrop_install_config_location();
- }
-
-
- if (!$staging_config->isInitialized()) {
- $t = get_t();
- backdrop_set_message($t('The staging configuration directory (%directory) could not be found. Please make sure it exists and is properly referenced in settings.php.',
- array('%directory' => config_get_config_directory('staging'))), 'warning', FALSE);
- }
- }
-
-
-
- if (!$settings_exist) {
- $settings_file = conf_path() . '/settings.php';
- $writable = backdrop_verify_install_file($settings_file, FILE_EXIST | FILE_READABLE | FILE_WRITABLE);
- $requirements['settings file']['title'] = 'Settings file';
- if ($writable) {
- $requirements['settings file'] += array(
- 'value' => 'settings.php is writable.',
- );
- }
- else {
- $requirements['settings file'] += array(
- 'value' => 'settings.php is not writable.',
- 'severity' => REQUIREMENT_ERROR,
- 'description' => 'Backdrop CMS requires write permissions to <em>' . $settings_file . '</em> during the update process. If you are unsure how to grant file permissions, consult the <a href="https://backdropcms.org/installation">Installation Instructions</a> page.',
- );
- }
- update_extra_requirements($requirements);
- }
-
-
- backdrop_bootstrap(BACKDROP_BOOTSTRAP_DATABASE);
-
-
-
-
-
- $system_schema = backdrop_get_installed_schema_version('system');
- if ($system_schema > 7000 || $system_schema < 6000) {
- $has_required_schema = $system_schema >= REQUIRED_D7_SCHEMA_VERSION || $system_schema < 6000;
- $requirements = array(
- 'drupal 7 version' => array(
- 'title' => 'Drupal 7 version',
- 'value' => $has_required_schema ? 'You are running a current version of Drupal 7.' : 'You are not running a current version of Drupal 7',
- 'severity' => $has_required_schema ? REQUIREMENT_OK : REQUIREMENT_ERROR,
- 'description' => $has_required_schema ? '' : 'Please update your Drupal 7 installation to the most recent version before attempting to upgrade to Backdrop',
- ),
- );
- update_extra_requirements($requirements);
-
- if ($has_required_schema) {
-
-
- backdrop_bootstrap(BACKDROP_BOOTSTRAP_VARIABLES);
-
-
- if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) {
- db_drop_index('url_alias', 'alias_language_pid');
- db_drop_index('url_alias', 'source_language_pid');
- $langcode_spec = array(
- 'description' => "The language code this alias is for; if 'und', the alias will be used for unknown languages. Each Backdrop path can have an alias for each supported language.",
- 'type' => 'varchar',
- 'length' => 12,
- 'not null' => TRUE,
- 'default' => '',
- );
- $langcode_indexes = array(
- 'indexes' => array(
- 'alias_langcode_pid' => array('alias', 'langcode', 'pid'),
- 'source_langcode_pid' => array('source', 'langcode', 'pid'),
- ),
- );
- db_change_field('url_alias', 'language', 'langcode', $langcode_spec, $langcode_indexes);
- }
-
-
- if (!db_table_exists('state')) {
- $schema = array(
- 'description' => 'Stores environment-specific state values.',
- 'fields' => array(
- 'name' => array(
- 'description' => 'The name of the state.',
- 'type' => 'varchar',
- 'length' => 128,
- 'not null' => TRUE,
- 'default' => '',
- ),
- 'value' => array(
- 'description' => 'The value of the state.',
- 'type' => 'blob',
- 'not null' => TRUE,
- 'size' => 'big',
- ),
- ),
- 'primary key' => array('name'),
- );
- db_create_table('state', $schema);
- }
-
-
- $role_column = array(
- 'type' => 'varchar',
- 'length' => 64,
- 'description' => 'Primary Key: The name of the role.',
- 'not null' => TRUE,
- 'default' => '',
- );
- if (!db_field_exists('users_roles', 'role')) {
- db_add_field('users_roles', 'role', $role_column);
- }
-
-
-
- update_prepare_language();
- }
- }
- }
-
- * Prepare Backdrop language changes for the bootstrap if needed.
- */
- function update_prepare_language() {
- if (db_table_exists('languages')) {
- $languages = db_select('languages', 'l')
- ->fields('l')
- ->execute();
- $plurals = array();
- $javascript = array();
- $prefixes = array();
- $domains = array();
- foreach ($languages as $language) {
- $plurals[$language->language] = array(
- 'plurals' => $language->plurals,
- 'formula' => $language->formula,
- );
- $javascript[$language->language] = $language->javascript;
- $prefixes[$language->language] = $language->prefix;
- $domains[$language->language] = $language->domain;
- }
- state_set('locale_translation_plurals', $plurals);
- state_set('locale_translation_javascript', $javascript);
- config_set('locale.settings', 'language_negotiation_url_prefixes', $prefixes);
- config_set('locale.settings', 'language_negotiation_url_domains', $domains);
-
-
- db_drop_field('languages', 'plurals');
- db_drop_field('languages', 'formula');
- db_drop_field('languages', 'javascript');
- db_drop_field('languages', 'prefix');
- db_drop_field('languages', 'domain');
- db_drop_field('languages', 'native');
-
-
- db_rename_table('languages', 'language');
-
-
-
-
- $modules = array('language');
- update_module_add_to_system($modules);
- update_module_enable($modules);
-
-
- require_once BACKDROP_ROOT . '/core/modules/language/language.install';
- language_update_1000();
- }
- }
-
- * Adds modules to the system table in a Backdrop core update.
- *
- * @param $modules
- * Array of module names.
- */
- function update_module_add_to_system($modules = array()) {
-
-
- $info_defaults = array(
- 'dependencies' => array(),
- 'description' => '',
- 'package' => 'Other',
- 'version' => NULL,
- 'php' => BACKDROP_MINIMUM_PHP,
- 'files' => array(),
- 'bootstrap' => 0,
- );
- foreach ($modules as $module) {
- $module_info = backdrop_parse_info_file('core/modules/' . $module . '/' . $module . '.info');
- db_insert('system')
- ->fields(array(
- 'filename' => 'core/modules/' . $module . '/' . $module . '.module',
- 'name' => $module,
- 'type' => 'module',
- 'status' => 0,
- 'bootstrap' => 0,
- 'schema_version' => -1,
- 'weight' => 0,
- 'info' => serialize($module_info + $info_defaults),
- ))
- ->execute();
- }
- }
-
- * Checks for disabled dependencies during a Drupal 7 upgrade.
- *
- * @return string
- * A themed message that lists modules that will be enabled, to be displayed
- * in the 'info' page of the update process.
- */
- function update_upgrade_check_dependencies() {
- $status_report = '';
- if (backdrop_get_installed_schema_version('system') > 7000) {
- $modules_to_enable = update_upgrade_modules_to_enable();
-
- if (empty($modules_to_enable)) {
- return '';
- }
-
- $files = system_rebuild_module_data();
- $module_list = '';
- foreach ($modules_to_enable as $module_to_enable => $dependents) {
- $module_list .= '<strong>' . $files[$module_to_enable]->info['name'] . '. ' . t('Required by:') . '</strong>';
- $module_list .= theme('item_list', array('items' => $dependents));
- }
- $status_report = '<h4>' . t('Some modules need to be enabled') . '</h4>';
- $status_report .= '<p>The following Backdrop modules will be enabled in order for the site to be upgraded successfully:</p>';
- $status_report .= $module_list;
- }
- return $status_report;
- }
-
- * Enables needed dependencies.
- */
- function update_upgrade_enable_dependencies() {
- if (backdrop_get_installed_schema_version('system') > 7000 && !state_get('update_upgrade_enable_dependencies', FALSE)) {
- $modules_to_enable = update_upgrade_modules_to_enable();
- if (!empty($modules_to_enable)) {
- update_module_enable(array_keys($modules_to_enable));
- state_set('update_upgrade_enable_dependencies', TRUE);
- }
- }
- }
-
- * Finds out which modules need to be enabled.
- *
- * @return array
- * An array keyed by the machine names of module names needed to be enabled,
- * whose values are arrays of the modules that depend on them.
- */
- function update_upgrade_modules_to_enable() {
-
- $result = db_query("SELECT name FROM {system} WHERE type = :type AND status = :status", array(':type' => 'module', ':status' => 1));
-
-
- $enabled_modules = array_keys($result->fetchAllAssoc('name'));
-
-
-
- $enabled_modules = array_diff($enabled_modules, array('standard', 'minimal'));
-
-
- $files = system_rebuild_module_data();
-
- $modules_to_enable = array();
- foreach ($enabled_modules as $enabled_module) {
- if (!empty($files[$enabled_module]->requires)) {
- $required_modules = array_keys($files[$enabled_module]->requires);
-
- $modules_not_enabled = array_diff($required_modules, $enabled_modules);
- foreach ($modules_not_enabled as $module_not_enabled) {
- $modules_to_enable[$module_not_enabled][] = $files[$enabled_module]->info['name'];
- }
- }
- }
- return $modules_to_enable;
- }
-
- * Perform Drupal 7.x to Backdrop 1.x updates that are required for update.php
- * to function properly.
- *
- * This function runs when update.php is run the first time for Backdrop 1.x,
- * even before updates are selected or performed. It is important
- * that if updates are not ultimately performed that no changes are
- * made which make it impossible to continue using the prior version.
- */
- function update_fix_requirements() {
- if (backdrop_get_installed_schema_version('system') > 7000 && !state_get('update_backdrop_requirements', FALSE)) {
-
-
- if ($key = update_variable_get('drupal_private_key')) {
- state_set('private_key', $key);
- update_variable_del('drupal_private_key');
- }
-
-
- if (!db_query("SELECT name FROM {system} WHERE name = 'views' AND type = 'module' AND status = 1")->fetchField()) {
- $schema_cache_views = backdrop_get_schema_unprocessed('system', 'cache');
- $schema_cache_views['description'] = 'Cache table for Views to store loaded view configurations.';
-
- $schema_cache_views_data = backdrop_get_schema_unprocessed('system', 'cache');
- $schema_cache_views_data['description'] = 'Cache table for views to store pre-rendered queries, results, and display output.';
- $schema_cache_views_data['fields']['serialized']['default'] = 1;
-
- if (!db_table_exists('cache_views')) {
- db_create_table('cache_views', $schema_cache_views);
- }
- if (!db_table_exists('cache_views_data')) {
- db_create_table('cache_views_data', $schema_cache_views_data);
- }
- update_module_enable(array('views'));
- }
-
-
-
- if (!db_query("SELECT name FROM {system} WHERE name = 'file' AND type = 'module' AND status = 1")->fetchField()) {
- update_module_enable(array('file'));
- }
-
-
- db_truncate('cache')->execute();
- db_truncate('cache_bootstrap')->execute();
-
- state_set('update_backdrop_requirements', TRUE);
- }
- }
-
- * Helper function to install a new module in Backdrop 1.x via hook_update_N().
- */
- function update_module_enable(array $modules) {
- foreach ($modules as $module) {
-
-
-
-
- $function = $module . '_schema_0';
- if (function_exists($function)) {
- $schema = $function();
- foreach ($schema as $table => $spec) {
- db_create_table($table, $spec);
- }
- }
-
-
- db_update('system')
- ->condition('type', 'module')
- ->condition('name', $module)
- ->fields(array('schema_version' => 0, 'status' => 1))
- ->execute();
-
-
-
- require_once BACKDROP_ROOT . '/core/includes/module.inc';
- system_list_reset();
-
- }
- }
-
- * Gets the value of a variable from the database during update hooks.
- *
- * Use this during the upgrade path instead of variable_get().
- *
- * @param string $name
- * The name of the variable.
- * @param mixed $default
- * The default value of the variable.
- * @return
- * The value of the variable in the database unserialized, or NULL if not set.
- */
- function update_variable_get($name, $default = NULL) {
- $result = db_query('SELECT value FROM {variable} WHERE name = :name', array(':name' => $name))->fetchField();
- if ($result !== FALSE) {
- return unserialize($result);
- }
- return $default;
- }
-
- * Sets a persistent variable during update hooks.
- *
- * Use this during the upgrade path instead of variable_set().
- *
- * @param string $name
- * The name of the variable.
- * @param mixed $value
- * The value of the variable to be set.
- */
- function update_variable_set($name, $value) {
- db_merge('variable')
- ->key(array(
- 'name' => $name,
- ))
- ->fields(array(
- 'value' => serialize($value),
- ))
- ->execute();
- }
-
- * Delete a variable from the database during update hooks.
- *
- * Use this during the upgrade path instead of variable_del().
- *
- * @param string $name
- * The name of the variable to delete.
- */
- function update_variable_del($name) {
- db_delete('variable')
- ->condition('name', $name)
- ->execute();
- }
-
- * Performs one update and stores the results for display on the results page.
- *
- * If an update function completes successfully, it should return a message
- * as a string indicating success, for example:
- * @code
- * return t('New index added successfully.');
- * @endcode
- *
- * Alternatively, it may return nothing. In that case, no message
- * will be displayed at all.
- *
- * If it fails for whatever reason, it should throw an instance of
- * BackdropUpdateException with an appropriate error message, for example:
- * @code
- * throw new BackdropUpdateException(t('Description of what went wrong'));
- * @endcode
- *
- * If an exception is thrown, the current update and all updates that depend on
- * it will be aborted. The schema version will not be updated in this case, and
- * all the aborted updates will continue to appear on update.php as updates
- * that have not yet been run.
- *
- * If an update function needs to be re-run as part of a batch process, it
- * should accept the $sandbox array by reference as its first parameter
- * and set the #finished property to the percentage completed that it is, as a
- * fraction of 1.
- *
- * @param $module
- * The module whose update will be run.
- * @param $number
- * The update number to run.
- * @param $dependency_map
- * An array whose keys are the names of all update functions that will be
- * performed during this batch process, and whose values are arrays of other
- * update functions that each one depends on.
- * @param $context
- * The batch context array.
- *
- * @see update_resolve_dependencies()
- */
- function update_do_one($module, $number, $dependency_map, &$context) {
- $function = $module . '_update_' . $number;
-
-
-
- if (!empty($context['results']['#abort']) && array_intersect($context['results']['#abort'], array_merge($dependency_map, array($function)))) {
- return;
- }
-
- $ret = array();
- if (function_exists($function)) {
- try {
- $ret['results']['query'] = $function($context['sandbox']);
- $ret['results']['success'] = TRUE;
- }
-
-
-
- catch (Exception $e) {
- watchdog_exception('update', $e);
-
- require_once BACKDROP_ROOT . '/core/includes/errors.inc';
- $variables = _backdrop_decode_exception($e);
-
- $ret['#abort'] = array('success' => FALSE, 'query' => t('%type: !message in %function (line %line of %file).', $variables));
- }
- }
-
- if (isset($context['sandbox']['#finished'])) {
- $context['finished'] = $context['sandbox']['#finished'];
- unset($context['sandbox']['#finished']);
- }
-
- if (!isset($context['results'][$module])) {
- $context['results'][$module] = array();
- }
- if (!isset($context['results'][$module][$number])) {
- $context['results'][$module][$number] = array();
- }
- $context['results'][$module][$number] = array_merge($context['results'][$module][$number], $ret);
-
- if (!empty($ret['#abort'])) {
-
- $context['results']['#abort'][] = $function;
- }
-
-
- if ($context['finished'] == 1 && empty($ret['#abort'])) {
- backdrop_set_installed_schema_version($module, $number);
- }
-
- $context['message'] = 'Updating ' . check_plain($module) . ' module';
- }
-
- * @class Exception class used to throw error if a module update fails.
- */
- class BackdropUpdateException extends Exception { }
-
- * Starts the site update batch process.
- *
- * @param $start
- * An array whose keys contain the names of modules to be updated during the
- * current batch process, and whose values contain the number of the first
- * requested update for that module. The actual updates that are run (and the
- * order they are run in) will depend on the results of passing this data
- * through the update dependency system.
- * @param $redirect
- * Path to redirect to when the batch has finished processing.
- * @param $url
- * URL of the batch processing page (should only be used for separate
- * scripts like update.php).
- * @param $batch
- * Optional parameters to pass into the batch API.
- * @param $redirect_callback
- * (optional) Specify a function to be called to redirect to the progressive
- * processing page.
- *
- * @see update_resolve_dependencies()
- */
- function update_batch($start, $redirect = NULL, $url = NULL, $batch = array(), $redirect_callback = 'backdrop_goto') {
-
-
- $_SESSION['maintenance_mode'] = state_get('maintenance_mode', FALSE);
- if ($_SESSION['maintenance_mode'] == FALSE) {
- state_set('maintenance_mode', TRUE);
- }
-
-
-
- $updates = update_resolve_dependencies($start);
-
-
-
-
-
- $dependency_map = array();
- foreach ($updates as $function => $update) {
- $dependency_map[$function] = !empty($update['reverse_paths']) ? array_keys($update['reverse_paths']) : array();
- }
-
- $operations = array();
- foreach ($updates as $update) {
- if ($update['allowed']) {
-
-
-
- if (isset($start[$update['module']])) {
- backdrop_set_installed_schema_version($update['module'], $update['number'] - 1);
- unset($start[$update['module']]);
- }
-
- $function = $update['module'] . '_update_' . $update['number'];
- $operations[] = array('update_do_one', array($update['module'], $update['number'], $dependency_map[$function]));
- }
- }
- $batch['operations'] = $operations;
- $batch += array(
- 'title' => 'Updating',
- 'init_message' => 'Starting updates',
- 'error_message' => 'An unrecoverable error has occurred. You can find the error message below. It is advised to copy it to the clipboard for reference.',
- 'finished' => 'update_finished',
- 'file' => 'core/includes/update.inc',
- );
- batch_set($batch);
- batch_process($redirect, $url, $redirect_callback);
- }
-
- * Finishes the update process and stores the results for eventual display.
- *
- * After the updates run, all caches are flushed. The update results are
- * stored into the session (for example, to be displayed on the update results
- * page in update.php). Additionally, if the site was off-line, now that the
- * update process is completed, the site is set back online.
- *
- * @param $success
- * Indicate that the batch API tasks were all completed successfully.
- * @param $results
- * An array of all the results that were updated in update_do_one().
- * @param $operations
- * A list of all the operations that had not been completed by the batch API.
- *
- * @see update_batch()
- */
- function update_finished($success, $results, $operations) {
-
- backdrop_flush_all_caches();
-
- $_SESSION['update_results'] = $results;
- $_SESSION['update_success'] = $success;
- $_SESSION['updates_remaining'] = $operations;
-
-
-
- if (isset($_SESSION['maintenance_mode']) && $_SESSION['maintenance_mode'] == FALSE) {
- state_set('maintenance_mode', FALSE);
- unset($_SESSION['maintenance_mode']);
- }
- }
-
- * Returns a list of all the pending site updates.
- *
- * @return
- * An associative array keyed by module name, which contains all information
- * about site updates that need to be run and any updates that are not
- * going to proceed due to missing requirements. The system module will
- * always be listed first.
- *
- * The subarray for each module can contain the following keys:
- * - start: The starting update that is to be processed. If this does not
- * exist then do not process any updates for this module as there are
- * other requirements that need to be resolved.
- * - warning: Any warnings about why this module can not be updated.
- * - pending: An array of all the pending updates for the module including
- * the update number and the description from source code comment for
- * each update function. This array is keyed by the update number.
- */
- function update_get_update_list() {
-
- $ret = array('system' => array());
-
- $modules = backdrop_get_installed_schema_version(NULL, FALSE, TRUE);
- foreach ($modules as $module => $schema_version) {
-
- if ($schema_version == SCHEMA_UNINSTALLED || update_check_incompatibility($module)) {
- continue;
- }
-
- $updates = backdrop_get_schema_versions($module);
- if ($updates !== FALSE) {
-
-
- $last_removed = module_invoke($module, 'update_last_removed');
-
-
-
- $comparison_schema_version = $schema_version;
- if ($last_removed >= 7000 && $last_removed < 9000 && $schema_version < 7000) {
- $comparison_schema_version += 7000;
- }
- if ($comparison_schema_version < $last_removed) {
- $ret[$module]['warning'] = '<em>' . $module . '</em> module can not be updated. Its schema version is ' . $schema_version . '. Updates up to and including ' . $last_removed . ' have been removed in this release. In order to update <em>' . $module . '</em> module, you will first <a href="https://backdropcms.org/upgrade">need to upgrade</a> to the last version in which these updates were available.';
- continue;
- }
-
-
- if ($schema_version >= 7000 && $schema_version < 9000) {
- $schema_version -= 7000;
- }
-
- foreach ($updates as $update) {
-
-
- $compare_version = $update;
- if ($update >= 7000 && $update < 9000) {
- $compare_version -= 7000;
- }
- if ($compare_version > $schema_version) {
-
- $func = new ReflectionFunction($module . '_update_' . $update);
-
- $description = preg_replace(array('#^\s*\/\*\*\s*#m', '#^\s*\*\/\s*$#m', '#^\s*\*\s*#m'), '', $func->getDocComment());
- $ret[$module]['pending'][$update] = $update . ' - ' . trim($description);
- if (!isset($ret[$module]['start'])) {
- $ret[$module]['start'] = $update;
- }
- }
- }
- if (!isset($ret[$module]['start']) && isset($ret[$module]['pending'])) {
- $ret[$module]['start'] = $schema_version;
- }
- }
- }
-
- if (empty($ret['system'])) {
- unset($ret['system']);
- }
- return $ret;
- }
-
- * Resolves dependencies in a set of module updates, and orders them correctly.
- *
- * This function receives a list of requested module updates and determines an
- * appropriate order to run them in such that all update dependencies are met.
- * Any updates whose dependencies cannot be met are included in the returned
- * array but have the key 'allowed' set to FALSE; the calling function should
- * take responsibility for ensuring that these updates are ultimately not
- * performed.
- *
- * In addition, the returned array also includes detailed information about the
- * dependency chain for each update, as provided by the depth-first search
- * algorithm in backdrop_depth_first_search().
- *
- * @param $starting_updates
- * An array whose keys contain the names of modules with updates to be run
- * and whose values contain the number of the first requested update for that
- * module.
- *
- * @return
- * An array whose keys are the names of all update functions within the
- * provided modules that would need to be run in order to fulfill the
- * request, arranged in the order in which the update functions should be
- * run. (This includes the provided starting update for each module and all
- * subsequent updates that are available.) The values are themselves arrays
- * containing all the keys provided by the backdrop_depth_first_search()
- * algorithm, which encode detailed information about the dependency chain
- * for this update function (for example: 'paths', 'reverse_paths', 'weight',
- * and 'component'), as well as the following additional keys:
- * - 'allowed': A boolean which is TRUE when the update function's
- * dependencies are met, and FALSE otherwise. Calling functions should
- * inspect this value before running the update.
- * - 'missing_dependencies': An array containing the names of any other
- * update functions that are required by this one but that are unavailable
- * to be run. This array will be empty when 'allowed' is TRUE.
- * - 'module': The name of the module that this update function belongs to.
- * - 'number': The number of this update function within that module.
- *
- * @see backdrop_depth_first_search()
- */
- function update_resolve_dependencies($starting_updates) {
-
- $update_functions = update_get_update_function_list($starting_updates);
- $graph = update_build_dependency_graph($update_functions);
-
-
- require_once BACKDROP_ROOT . '/core/includes/graph.inc';
- backdrop_depth_first_search($graph);
- backdrop_sort($graph);
-
- foreach ($graph as $function => &$data) {
- $module = $data['module'];
- $number = $data['number'];
-
-
- if (update_is_missing($module, $number, $update_functions) && !update_already_performed($module, $number)) {
- $data['allowed'] = FALSE;
- foreach (array_keys($data['paths']) as $dependent) {
- $graph[$dependent]['allowed'] = FALSE;
- $graph[$dependent]['missing_dependencies'][] = $function;
- }
- }
- elseif (!isset($data['allowed'])) {
- $data['allowed'] = TRUE;
- $data['missing_dependencies'] = array();
- }
-
-
-
- if (!isset($update_functions[$module][$number])) {
- unset($graph[$function]);
- }
- }
-
- return $graph;
- }
-
- * Returns an organized list of update functions for a set of modules.
- *
- * @param $starting_updates
- * An array whose keys contain the names of modules and whose values contain
- * the number of the first requested update for that module.
- *
- * @return
- * An array containing all the update functions that should be run for each
- * module, including the provided starting update and all subsequent updates
- * that are available. The keys of the array contain the module names, and
- * each value is an ordered array of update functions, keyed by the update
- * number.
- *
- * @see update_resolve_dependencies()
- */
- function update_get_update_function_list($starting_updates) {
-
-
- $update_functions = array();
- foreach ($starting_updates as $module => $version) {
- $update_functions[$module] = array();
- $updates = backdrop_get_schema_versions($module);
- if ($updates !== FALSE) {
- $start_key = array_search($version, $updates);
- $total_updates = count($updates);
- for ($n = $start_key; $n < $total_updates; $n++) {
- $update = $updates[$n];
- $update_functions[$module][$update] = $module . '_update_' . $update;
- }
- }
- }
- return $update_functions;
- }
-
- * Constructs a graph which encodes the dependencies between module updates.
- *
- * This function returns an associative array which contains a "directed graph"
- * representation of the dependencies between a provided list of update
- * functions, as well as any outside update functions that they directly depend
- * on but that were not in the provided list. The vertices of the graph
- * represent the update functions themselves, and each edge represents a
- * requirement that the first update function needs to run before the second.
- * For example, consider this graph:
- *
- * system_update_1000 ---> system_update_1001 ---> system_update_1002
- *
- * Visually, this indicates that system_update_1000() must run before
- * system_update_1001(), which in turn must run before system_update_1002().
- *
- * The function takes into account standard dependencies within each module, as
- * shown above (i.e., the fact that each module's updates must run in numerical
- * order), but also finds any cross-module dependencies that are defined by
- * modules which implement hook_update_dependencies(), and builds them into the
- * graph as well.
- *
- * @param $update_functions
- * An organized array of update functions, in the format returned by
- * update_get_update_function_list().
- *
- * @return
- * A multidimensional array representing the dependency graph, suitable for
- * passing in to backdrop_depth_first_search(), but with extra information
- * about each update function also included. Each array key contains the name
- * of an update function, including all update functions from the provided
- * list as well as any outside update functions which they directly depend
- * on. Each value is an associative array containing the following keys:
- * - 'edges': A representation of any other update functions that immediately
- * depend on this one. See backdrop_depth_first_search() for more details on
- * the format.
- * - 'module': The name of the module that this update function belongs to.
- * - 'number': The number of this update function within that module.
- *
- * @see backdrop_depth_first_search()
- * @see update_resolve_dependencies()
- */
- function update_build_dependency_graph($update_functions) {
-
-
- $graph = array();
-
-
- foreach ($update_functions as $module => $functions) {
- $previous_function = NULL;
- foreach ($functions as $number => $function) {
-
-
-
- if ($previous_function) {
- $graph[$previous_function]['edges'][$function] = TRUE;
- }
- $previous_function = $function;
-
-
- $graph[$function]['module'] = $module;
- $graph[$function]['number'] = $number;
- }
- }
-
-
- $update_dependencies = update_retrieve_dependencies();
- foreach ($graph as $function => $data) {
- if (!empty($update_dependencies[$data['module']][$data['number']])) {
- foreach ($update_dependencies[$data['module']][$data['number']] as $module => $number) {
- $dependency = $module . '_update_' . $number;
- $graph[$dependency]['edges'][$function] = TRUE;
- $graph[$dependency]['module'] = $module;
- $graph[$dependency]['number'] = $number;
- }
- }
- }
-
- return $graph;
- }
-
- * Determines if a module update is missing or unavailable.
- *
- * @param $module
- * The name of the module.
- * @param $number
- * The number of the update within that module.
- * @param $update_functions
- * An organized array of update functions, in the format returned by
- * update_get_update_function_list(). This should represent all module
- * updates that are requested to run at the time this function is called.
- *
- * @return
- * TRUE if the provided module update is not installed or is not in the
- * provided list of updates to run; FALSE otherwise.
- */
- function update_is_missing($module, $number, $update_functions) {
- return !isset($update_functions[$module][$number]) || !function_exists($update_functions[$module][$number]);
- }
-
- * Determines if a module update has already been performed.
- *
- * @param $module
- * The name of the module.
- * @param $number
- * The number of the update within that module.
- *
- * @return
- * TRUE if the database schema indicates that the update has already been
- * performed; FALSE otherwise.
- */
- function update_already_performed($module, $number) {
- return $number <= backdrop_get_installed_schema_version($module);
- }
-
- * Invokes hook_update_dependencies() in all installed modules.
- *
- * This function is similar to module_invoke_all(), with the main difference
- * that it does not require that a module be enabled to invoke its hook, only
- * that it be installed. This allows the update system to properly perform
- * updates even on modules that are currently disabled.
- *
- * @return
- * An array of return values obtained by merging the results of the
- * hook_update_dependencies() implementations in all installed modules.
- *
- * @see module_invoke_all()
- * @see hook_update_dependencies()
- */
- function update_retrieve_dependencies() {
- $return = array();
-
-
- $modules = db_query("SELECT name FROM {system} WHERE type = 'module' AND schema_version <> :schema ORDER BY weight ASC, name ASC", array(':schema' => SCHEMA_UNINSTALLED))->fetchCol();
- foreach ($modules as $module) {
- $function = $module . '_update_dependencies';
- if (function_exists($function)) {
- $result = $function();
-
-
-
-
-
-
-
-
- if (isset($result) && is_array($result)) {
- foreach ($result as $module => $module_data) {
- foreach ($module_data as $update => $update_data) {
- foreach ($update_data as $module_dependency => $update_dependency) {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- if (!isset($return[$module][$update][$module_dependency]) || $update_dependency > $return[$module][$update][$module_dependency]) {
- $return[$module][$update][$module_dependency] = $update_dependency;
- }
- }
- }
- }
- }
- }
- }
-
- return $return;
- }