diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 77458a1..8ec2539 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -885,23 +885,22 @@ function drupal_get_filename($type, $name, $filename = NULL) { elseif (isset($files[$type][$name])) { // nothing } - // Verify that we have an active database connection, before querying - // the database. This is required because this function is called both - // before we have a database connection (i.e. during installation) and - // when a database connection fails. else { - try { - if (function_exists('db_query')) { - $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField(); - if ($file && file_exists(DRUPAL_ROOT . '/' . $file)) { - $files[$type][$name] = $file; + // Verify that we have an keyvalue service before using it. This is required + // because this function is called during installation. + // @todo Inject database connection into KeyValueStore\DatabaseStorage. + if (drupal_container()->hasDefinition('keyvalue') && function_exists('db_query')) { + try { + $file_list = state()->get('system.' . $type . '.files'); + if ($file_list && isset($file_list[$name]) && file_exists(DRUPAL_ROOT . '/' . $file_list[$name])) { + $files[$type][$name] = $file_list[$name]; } } - } - catch (Exception $e) { - // The database table may not exist because Drupal is not yet installed, - // or the database might be down. We have a fallback for this case so we - // hide the error completely. + catch (Exception $e) { + // The keyvalue service raise an exception due because the backend might + // be down. We have a fallback for this case so we hide the error + // completely. + } } // Fallback to searching the filesystem if the database could not find the // file or the file returned by the database is not found. @@ -2461,9 +2460,10 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) { $container ->register('config.storage.staging', 'Drupal\Core\Config\FileStorage') ->addArgument(config_get_config_directory(CONFIG_STAGING_DIRECTORY)); + + // KeyValueStore. $container - ->register('state.storage', 'Drupal\Core\KeyValueStore\DatabaseStorage') - ->addArgument('state'); + ->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory'); } return $container; } @@ -2480,7 +2480,7 @@ function drupal_container(Container $new_container = NULL, $rebuild = FALSE) { * @return Drupal\Core\KeyValueStore\KeyValueStoreInterface */ function state() { - return drupal_container()->get('state.storage'); + return drupal_container()->get('keyvalue')->get('state'); } /** @@ -2499,16 +2499,21 @@ function typed_data() { /** * Returns the test prefix if this is an internal request from SimpleTest. * + * @param string $new_prefix + * Internal use only. A new prefix to be stored. + * * @return * Either the simpletest prefix (the string "simpletest" followed by any * number of digits) or FALSE if the user agent does not contain a valid * HMAC and timestamp. */ -function drupal_valid_test_ua() { +function drupal_valid_test_ua($new_prefix = NULL) { global $drupal_hash_salt; - // No reason to reset this. static $test_prefix; + if (isset($new_prefix)) { + $test_prefix = $new_prefix; + } if (isset($test_prefix)) { return $test_prefix; } diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index dfb41f5..ac94175 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -322,9 +322,9 @@ function install_begin_request(&$install_state) { $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory') ->addArgument(new Reference('config.storage')) ->addArgument(new Reference('dispatcher')); + // @todo State service is gone; needs keyvalue + NullStorage replacement. $container - ->register('state.storage', 'Drupal\Core\KeyValueStore\DatabaseStorage') - ->addArgument('state'); + ->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory'); drupal_container($container); } @@ -1661,15 +1661,12 @@ function install_import_translations_remaining(&$install_state) { * A message informing the user that the installation is complete. */ function install_finished(&$install_state) { + $profile = drupal_get_profile(); // Remember the profile which was used. - variable_set('install_profile', drupal_get_profile()); + variable_set('install_profile', $profile); // Installation profiles are always loaded last. - db_update('system') - ->fields(array('weight' => 1000)) - ->condition('type', 'module') - ->condition('name', drupal_get_profile()) - ->execute(); + module_set_weight($profile, 1000); // Flush all caches to ensure that any full bootstraps during the installer // do not leave stale cached data, and that any content types or other items diff --git a/core/includes/install.inc b/core/includes/install.inc index 3c30c89..c242cbc 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -9,16 +9,6 @@ use Drupal\locale\Gettext; /** - * Indicates that a module has not been installed yet. - */ -const SCHEMA_UNINSTALLED = -1; - -/** - * Indicates that a module has been installed. - */ -const SCHEMA_INSTALLED = 0; - -/** * Requirement severity -- Informational message only. */ const REQUIREMENT_INFO = -1; @@ -420,28 +410,26 @@ function drupal_install_system() { require_once DRUPAL_ROOT . '/' . $system_path . '/system.install'; $system_versions = drupal_get_schema_versions('system'); $system_version = $system_versions ? max($system_versions) : SCHEMA_INSTALLED; - db_insert('system') - ->fields(array('filename', 'name', 'type', 'owner', 'status', 'schema_version', 'bootstrap')) - ->values(array( - 'filename' => $system_path . '/system.module', - 'name' => 'system', - 'type' => 'module', - 'owner' => '', - 'status' => 1, - 'schema_version' => $system_version, - 'bootstrap' => 0, - )) - ->execute(); - - // Clear out module list and hook implementation statics before calling - // system_rebuild_theme_data(). + drupal_container() + ->get('keyvalue') + ->get('system.schema') + ->set('system', $system_version); + + // Enable System module. + // System module needs to be enabled and the system/module lists need to be + // reset first in order to allow config_install_default_config() to invoke + // config import callbacks. + // @todo Installation profiles may override the system.module config object. + // @todo Config\InstallStorage should actually throw an exception on write, why does this work? + config('system.module') + ->set('enabled.system', 0) + ->save(); + + // Clear out module list and hook implementation statics. system_list_reset(); module_list_reset(); module_implements_reset(); - system_rebuild_module_data(); - system_rebuild_theme_data(); - config_install_default_config('module', 'system'); module_invoke('system', 'install'); diff --git a/core/includes/module.inc b/core/includes/module.inc index 5944543..d674866 100644 --- a/core/includes/module.inc +++ b/core/includes/module.inc @@ -132,6 +132,11 @@ function module_list_reset() { * * @see module_list() * @see list_themes() + * + * @todo There are too many layers/levels of caching involved for system_list() + * data. Consider to add a config($name, $cache = TRUE) argument to allow + * callers like this to force-disable any possible configuration storage + * caching, or some other direct access to circumvent it/take it over. */ function system_list($type) { $lists = &drupal_static(__FUNCTION__); @@ -147,15 +152,15 @@ function system_list($type) { $bootstrap_list = $cached->data; } else { - $bootstrap_list = db_query("SELECT name, filename FROM {system} WHERE status = 1 AND bootstrap = 1 AND type = 'module' ORDER BY weight ASC, name ASC")->fetchAllAssoc('name'); + $bootstrap_list = state()->get('system.module.bootstrap') ?: array(); cache('bootstrap')->set('bootstrap_modules', $bootstrap_list); } // To avoid a separate database lookup for the filepath, prime the // drupal_get_filename() static cache for bootstrap modules only. // The rest is stored separately to keep the bootstrap module cache small. - foreach ($bootstrap_list as $module) { - drupal_classloader_register($module->name, dirname($module->filename)); - drupal_get_filename('module', $module->name, $module->filename); + foreach ($bootstrap_list as $name => $filename) { + drupal_classloader_register($name, dirname($filename)); + drupal_get_filename('module', $name, $filename); } // We only return the module names here since module_list() doesn't need // the filename itself. @@ -177,22 +182,48 @@ function system_list($type) { // Drupal installations, which might have modules installed in different // locations in the file system. The ordering here must also be // consistent with the one used in module_implements(). - $result = db_query("SELECT * FROM {system} WHERE type = 'theme' OR (type = 'module' AND status = 1) ORDER BY weight ASC, name ASC"); - foreach ($result as $record) { + $enabled_modules = config('system.module')->get('enabled'); + $module_files = state()->get('system.module.files'); + foreach ($enabled_modules as $name => $weight) { // Build a list of all enabled modules. - if ($record->type == 'module') { - $lists['module_enabled'][$record->name] = $record->name; - } - // Build a list of themes. - if ($record->type == 'theme') { - $record->info = unserialize($record->info); - $lists['theme'][$record->name] = $record; - } + $lists['module_enabled'][$name] = $name; + // Build a list of filenames so drupal_get_filename can use it. + $lists['filepaths'][] = array( + 'type' => 'module', + 'name' => $name, + 'filepath' => $module_files[$name], + ); + } + + // Build a list of themes. + $enabled_themes = config('system.theme')->get('enabled'); + //$theme_files = state()->get('system.theme.files'); + // @todo Themes include all themes, including disabled/uninstalled. This + // system.theme.data state will go away entirely as soon as themes have + // a proper installation status. + // @see http://drupal.org/node/1067408 + $theme_data = state()->get('system.theme.data'); + if (empty($theme_data)) { + // system_list() may be called from _drupal_bootstrap_code() and + // module_load_all(), in which case system.module is not loaded yet. + // Prevent a filesystem scan in drupal_load() and include it directly. + require_once DRUPAL_ROOT . '/core/modules/system/system.module'; + $theme_data = system_rebuild_theme_data(); + } + foreach ($theme_data as $name => $theme) { + $theme->status = (int) isset($enabled_themes[$name]); + $lists['theme'][$name] = $theme; // Build a list of filenames so drupal_get_filename can use it. - if ($record->status) { - $lists['filepaths'][] = array('type' => $record->type, 'name' => $record->name, 'filepath' => $record->filename); + if (isset($enabled_themes[$name])) { + $lists['filepaths'][] = array( + 'type' => 'theme', + 'name' => $name, + 'filepath' => $theme->filename, + ); } } + // @todo Move into list_themes(). Read info for a particular requested + // theme from state instead. foreach ($lists['theme'] as $key => $theme) { if (!empty($theme->info['base theme'])) { // Make a list of the theme's base themes. @@ -240,6 +271,14 @@ function system_list_reset() { drupal_static_reset('list_themes'); cache('bootstrap')->deleteMultiple(array('bootstrap_modules', 'system_list')); cache()->delete('system_info'); + // Remove last known theme data state. + // This causes system_list() to call system_rebuild_theme_data() on its next + // invocation. When enabling a module that implements hook_system_info_alter() + // to inject a new (testing) theme or manipulate an existing theme, then that + // will cause system_list_reset() to be called, but theme data is not + // necessarily rebuilt afterwards. + // @todo Obsolete with proper installation status for themes. + state()->delete('system.theme.data'); } /** @@ -436,26 +475,34 @@ function module_enable($module_list, $enable_dependencies = TRUE) { $modules_installed = array(); $modules_enabled = array(); + $schema_store = drupal_container()->get('keyvalue')->get('system.schema'); + $module_config = config('system.module'); + $disabled_config = config('system.module.disabled'); foreach ($module_list as $module) { // Only process modules that are not already enabled. - $existing = db_query("SELECT status FROM {system} WHERE type = :type AND name = :name", array( - ':type' => 'module', - ':name' => $module)) - ->fetchObject(); - if ($existing->status == 0) { + $enabled = TRUE; + $weight = 0; + if (!$schema_store->get($module)) { + $enabled = FALSE; + } + elseif (!$module_config->get("enabled.$module")) { + $enabled = FALSE; + $weight = $disabled_config->get($module); + } + if (!$enabled) { + $module_config + ->set("enabled.$module", $weight) + ->set('enabled', module_config_sort($module_config->get('enabled'))) + ->save(); + if ($weight) { + $disabled_config + ->clear($module) + ->save(); + } // Load the module's code. drupal_load('module', $module); module_load_install($module); - // Update the database and module list to reflect the new module. This - // needs to be done first so that the module's hook implementations, - // hook_schema() in particular, can be called while it is being - // installed. - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'module') - ->condition('name', $module) - ->execute(); // Refresh the module list to include it. system_list_reset(); module_implements_reset(); @@ -565,15 +612,19 @@ function module_disable($module_list, $disable_dependents = TRUE) { $invoke_modules = array(); + $module_config = config('system.module'); + $disabled_config = config('system.module.disabled'); foreach ($module_list as $module) { if (module_exists($module)) { module_load_install($module); module_invoke($module, 'disable'); - db_update('system') - ->fields(array('status' => 0)) - ->condition('type', 'module') - ->condition('name', $module) - ->execute(); + $disabled_config + ->set($module, $module_config->get($module)) + ->save(); + $module_config + ->clear("enabled.$module") + ->set('enabled', module_config_sort($module_config->get('enabled'))) + ->save(); $invoke_modules[] = $module; watchdog('system', '%module module disabled.', array('%module' => $module), WATCHDOG_INFO); } @@ -642,6 +693,8 @@ function module_uninstall($module_list = array(), $uninstall_dependents = TRUE) } $storage = drupal_container()->get('config.storage'); + $schema_store = drupal_container()->get('keyvalue')->get('system.schema'); + $disabled_config = config('system.module.disabled'); foreach ($module_list as $module) { // Uninstall the module. module_load_install($module); @@ -655,8 +708,11 @@ function module_uninstall($module_list = array(), $uninstall_dependents = TRUE) } watchdog('system', '%module module uninstalled.', array('%module' => $module), WATCHDOG_INFO); - drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED); + $schema_store->delete($module); + $disabled_config->clear($module); } + $disabled_config->save(); + drupal_get_installed_schema_version(NULL, TRUE); if (!empty($module_list)) { // Call hook_module_uninstall to let other modules act @@ -1124,3 +1180,59 @@ function drupal_alter($type, &$data, &$context1 = NULL, &$context2 = NULL) { $function($data, $context1, $context2); } } + +/** + * Sets weight of a particular module. + * + * The weight of uninstalled modules cannot be changed. + * + * @param string $module + * The name of the module (without the .module extension). + * @param int $weight + * An integer representing the weight of the module. + */ +function module_set_weight($module, $weight) { + // Update the module weight in the config file that contains it. + $module_config = config('system.module'); + if ($module_config->get("enabled.$module") !== NULL) { + $module_config + ->set("enabled.$module", $weight) + ->set('enabled', module_config_sort($module_config->get('enabled'))) + ->save(); + return; + } + $disabled_config = config('system.module.disabled'); + if ($disabled_config->get($module) !== NULL) { + $disabled_config + ->set($module, $weight) + ->save(); + return; + } +} + +/** + * Sort module config. + * + * The list of enabled modules is expected to be weight and name order and it + * is faster to do this sort on write than on read. + * + * @param array $data + * An array of module configuration data. + * + * @return array + * An array of module configuration data sorted by weight. + */ +function module_config_sort($data) { + $sort = array(); + foreach ($data as $name => $weight) { + // We can't use the sign directly because + (ASCII 43) is before + // - (ASCII 45). So negative nubmers get a 0, non-negative numbers + // a 1 prefix. + $prefix = (int) ($weight >= 0); + // PHP_INT_MAX is at most 19 characters so make every number equally + // 19 digits long. + $sort[] = $prefix . sprintf('%019d', abs($weight)) . $name; + } + array_multisort($sort, SORT_STRING, $data); + return $data; +} diff --git a/core/includes/schema.inc b/core/includes/schema.inc index 155bb6b..bff33d3 100644 --- a/core/includes/schema.inc +++ b/core/includes/schema.inc @@ -15,6 +15,16 @@ */ /** + * Indicates that a module has not been installed yet. + */ +const SCHEMA_UNINSTALLED = -1; + +/** + * Indicates that a module has been installed. + */ +const SCHEMA_INSTALLED = 0; + +/** * Gets the schema definition of a table, or the whole database schema. * * The returned schema will include any modifications made by any @@ -173,10 +183,8 @@ function drupal_get_installed_schema_version($module, $reset = FALSE, $array = F } if (!$versions) { - $versions = array(); - $result = db_query("SELECT name, schema_version FROM {system} WHERE type = :type", array(':type' => 'module')); - foreach ($result as $row) { - $versions[$row->name] = $row->schema_version; + if (!$versions = drupal_container()->get('keyvalue')->get('system.schema')->getAll()) { + $versions = array(); } } @@ -197,11 +205,7 @@ function drupal_get_installed_schema_version($module, $reset = FALSE, $array = F * The new schema version. */ function drupal_set_installed_schema_version($module, $version) { - db_update('system') - ->fields(array('schema_version' => $version)) - ->condition('name', $module) - ->execute(); - + drupal_container()->get('keyvalue')->get('system.schema')->set($module, $version); // Reset the static cache of module schema versions. drupal_get_installed_schema_version(NULL, TRUE); } diff --git a/core/includes/theme.inc b/core/includes/theme.inc index 20c43ea..06e99fb 100644 --- a/core/includes/theme.inc +++ b/core/includes/theme.inc @@ -1412,17 +1412,17 @@ function theme_render_template($template_file, $variables) { */ function theme_enable($theme_list) { drupal_clear_css_cache(); - + $theme_config = config('system.theme'); + $disabled_themes = config('system.theme.disabled'); foreach ($theme_list as $key) { - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'theme') - ->condition('name', $key) - ->execute(); - + // The value is not used; the weight is ignored for themes currently. + $theme_config->set("enabled.$key", 0); + $disabled_themes->clear($key); // Install default configuration of the theme. config_install_default_config('theme', $key); } + $theme_config->save(); + $disabled_themes->save(); list_themes(TRUE); menu_router_rebuild(); @@ -1449,13 +1449,15 @@ function theme_disable($theme_list) { drupal_clear_css_cache(); + $theme_config = config('system.theme'); + $disabled_themes = config('system.theme.disabled'); foreach ($theme_list as $key) { - db_update('system') - ->fields(array('status' => 0)) - ->condition('type', 'theme') - ->condition('name', $key) - ->execute(); + // The value is not used; the weight is ignored for themes currently. + $theme_config->clear("enabled.$key"); + $disabled_themes->set($key, 0); } + $theme_config->save(); + $disabled_themes->save(); list_themes(TRUE); menu_router_rebuild(); diff --git a/core/includes/update.inc b/core/includes/update.inc index 5d80188..99dd714 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -23,21 +23,24 @@ const REQUIRED_D7_SCHEMA_VERSION = '7069'; /** - * Disable any items in the {system} table that are not core compatible. + * Disables any extensions that are incompatible with the current core version. */ 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; + foreach (array('module', 'theme') as $type) { + $config = config("system.$type"); + $save = FALSE; + foreach ($config->get('enabled') as $name => $weight) { + if (update_check_incompatibility($name, $type)) { + $config->clear("enabled.$name"); + $save = TRUE; + } + } + if ($save) { + if ($type == 'module') { + $config->set('enabled', module_config_sort($config->get('enabled'))); + } + $config->save(); } - } - if (!empty($incompatible)) { - db_update('system') - ->fields(array('status' => 0)) - ->condition('name', $incompatible, 'IN') - ->execute(); } } @@ -116,7 +119,12 @@ function update_prepare_d8_bootstrap() { // running an up-to-date version of Drupal 7 before proceeding. Note this has // to happen AFTER the database bootstraps because of // drupal_get_installed_schema_version(). - $system_schema = drupal_get_installed_schema_version('system'); + try { + $system_schema = drupal_get_installed_schema_version('system'); + } + catch (\Exception $e) { + $system_schema = db_query('SELECT schema_version FROM {system} WHERE name = :system', array(':system' => 'system'))->fetchField(); + } if ($system_schema < 8000) { $has_required_schema = $system_schema >= REQUIRED_D7_SCHEMA_VERSION; $requirements = array( @@ -129,6 +137,10 @@ function update_prepare_d8_bootstrap() { ); update_extra_requirements($requirements); + // @todo update.php stages seem to be completely screwed up; the initial + // requirements check is not supposed to change the system. All of the + // following code seems to have been mistakenly/unknowingly added here and + // does not belong into update_prepare_d8_bootstrap(). if ($has_required_schema) { if (!db_table_exists('key_value')) { $specs = array( @@ -163,18 +175,88 @@ function update_prepare_d8_bootstrap() { // Bootstrap variables so we can update theme while preparing the update // process. drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES); - // Update the dynamic include paths that might be used before running the - // proper update functions. - update_prepare_stored_includes(); - // Update the environment for the language bootstrap if needed. - update_prepare_d8_language(); + // Update the 'language_default' system variable, if configured. + // Required to run before drupal_install_config_directories(), since that + // triggers a call into system_stream_wrappers(), which calls t(), which + // calls into language_default(). + $language_default = variable_get('language_default'); + if (!empty($language_default) && (isset($language_default->langcode) || isset($language_default->language))) { + if (!isset($language_default->langcode)) { + $language_default->langcode = $language_default->language; + } + unset($language_default->language); + // In D8, the 'language_default' is not anymore an object, but an array, + // so make sure that the new value that is saved into this variable is an + // array. + variable_set('language_default', (array) $language_default); + } + + // @todo Race-condition: drupal_install_config_directories() calls into + // install_ensure_config_directory(), which calls into + // file_prepare_directory(), whichs calls into file_get_stream_wrappers(), + // which attempts to invoke hooks with a non-existing module/hook system. + include_once DRUPAL_ROOT . '/core/includes/module.inc'; + $module_list['system']['filename'] = 'core/modules/system/system.module'; + module_list(NULL, $module_list); + require_once DRUPAL_ROOT . '/' . $module_list['system']['filename']; // Ensure the configuration directories exist and are writable, or create // them. If the directories have not been specified in settings.php and // created manually already, and either directory cannot be created by the // web server, an exception will be thrown, halting the update. drupal_install_config_directories(); + $module_config = config('system.module'); + $disabled_modules = config('system.module.disabled'); + $theme_config = config('system.theme'); + $disabled_themes = config('system.theme.disabled'); + $schema_store = drupal_container()->get('keyvalue')->get('system.schema'); + + // Load system.module, because update_prepare_d8_bootstrap() is called in + // the initial minimal update.php bootstrap that performs the core + // requirements check. + require_once DRUPAL_ROOT . '/core/modules/system/system.module'; + + $result = db_query('SELECT name, status, weight, schema_version, type FROM {system} WHERE type = :theme OR (type = :module AND schema_version <> :schema_uninstalled)', array( + ':theme' => 'theme', + ':module' => 'module', + ':schema_uninstalled' => SCHEMA_UNINSTALLED, + )); + $module_data = _system_rebuild_module_data(); + + // Loop through each extension from the system table. + foreach ($result as $record) { + if ($record->type == 'module') { + if ($record->status && isset($module_data[$record->name])) { + $module_config->set('enabled.' . $record->name, $record->weight); + } + else { + $disabled_modules->set($record->name, $record->weight); + } + $schema_store->set($record->name, $record->schema_version); + } + elseif ($record->type == 'theme') { + if ($record->status) { + $theme_config->set('enabled.' . $record->name, 0); + } + else { + $disabled_themes->set($record->name, 0); + } + } + } + $module_config->set('enabled', module_config_sort($module_config->get('enabled')))->save(); + $disabled_modules->save(); + $theme_config->save(); + $disabled_themes->save(); + + // Update the dynamic include paths that might be used before running the + // proper update functions. + update_prepare_stored_includes(); + // Update the environment for the language bootstrap if needed. + update_prepare_d8_language(); + // Prime the classloader. + system_list('module_enabled'); + // Change language column to langcode in url_alias. if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) { db_drop_index('url_alias', 'alias_language_pid'); @@ -259,7 +341,6 @@ function update_prepare_d8_language() { // version of this function to ensure schema conflicts don't happen due to // our updated data. $modules = array('language'); - update_module_add_to_system($modules); update_module_enable($modules); // Rename language column to langcode and set it again as the primary key. @@ -275,19 +356,6 @@ function update_prepare_d8_language() { db_change_field('language', 'language', 'langcode', $langcode_spec, array('primary key' => array('langcode'))); } - // Update the 'language_default' system variable, if configured. - $language_default = variable_get('language_default'); - if (!empty($language_default) && (isset($language_default->langcode) || isset($language_default->language))) { - if (!isset($language_default->langcode)) { - $language_default->langcode = $language_default->language; - } - unset($language_default->language); - // In D8, the 'language_default' is not anymore an object, but an array, - // so make sure that the new value that is saved into this variable is an - // array. - variable_set('language_default', (array) $language_default); - } - // Adds the locked column and saves the special languages. if (!db_field_exists('language', 'locked')) { $locked_spec = array( @@ -331,41 +399,6 @@ function update_prepare_d8_language() { } /** - * Adds modules to the system table in a Drupal core update. - * - * @param $modules - * Array of module names. - */ -function update_module_add_to_system($modules = array()) { - // Insert module data, so we can enable the module. Calling a full module - // list rebuild so early is costly and complex, so we just have a stop-gap. - $info_defaults = array( - 'dependencies' => array(), - 'description' => '', - 'package' => 'Other', - 'version' => NULL, - 'php' => DRUPAL_MINIMUM_PHP, - 'files' => array(), - 'bootstrap' => 0, - ); - foreach ($modules as $module) { - $module_info = drupal_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(); - } -} - -/** * Perform Drupal 7.x to 8.x updates that are required for update.php * to function properly. * @@ -387,6 +420,7 @@ function update_fix_d8_requirements() { * Helper function to install a new module in Drupal 8 via hook_update_N(). */ function update_module_enable(array $modules) { + $schema_store = drupal_container()->get('keyvalue')->get('system.schema'); foreach ($modules as $module) { // Check for initial schema and install it. The schema version of a newly // installed module is always 0. Using 8000 here would be inconsistent @@ -399,13 +433,19 @@ function update_module_enable(array $modules) { db_create_table($table, $spec); } } + // Enable the module with a weight of 0. + $module_config = config('system.module'); + $module_config + ->set("enabled.$module", 0) + ->set('enabled', module_config_sort($module_config->get('enabled'))) + ->save(); + // Ensure the module is not contained in disabled modules. + config('system.module.disabled') + ->clear($module) + ->save(); // Change the schema version from SCHEMA_UNINSTALLED to 0, so any module // updates since the module's inception are executed in a core upgrade. - db_update('system') - ->condition('type', 'module') - ->condition('name', $module) - ->fields(array('schema_version' => 0, 'status' => 1)) - ->execute(); + $schema_store->set($module, 0); // system_list_reset() is in module.inc but that would only be available // once the variable bootstrap is done. @@ -940,8 +980,7 @@ function update_retrieve_dependencies() { $return = array(); // Get a list of installed modules, arranged so that we invoke their hooks in // the same order that module_invoke_all() does. - $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) { + foreach (config('system.module')->get('enabled') as $module => $weight) { $function = $module . '_update_dependencies'; if (function_exists($function)) { $result = $function(); diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index 7c150e9..034dff1 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -346,7 +346,6 @@ public function load() { * The configuration object. */ public function save() { - $this->sortByKey($this->data); $this->storage->write($this->name, $this->data); $this->isNew = FALSE; $this->notify('save'); @@ -370,26 +369,6 @@ public function rename($new_name) { } /** - * Sorts all keys in configuration data. - * - * Ensures that re-inserted keys appear in the same location as before, in - * order to ensure an identical order regardless of storage controller. - * A consistent order is important for any storage that allows any kind of - * diff operation. - * - * @param array $data - * An associative array to sort recursively by key name. - */ - public function sortByKey(array &$data) { - ksort($data); - foreach ($data as &$value) { - if (is_array($value)) { - $this->sortByKey($value); - } - } - } - - /** * Deletes the configuration object. * * @return Drupal\Core\Config\Config diff --git a/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php b/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php new file mode 100644 index 0000000..864697f --- /dev/null +++ b/core/lib/Drupal/Core/KeyValueStore/KeyValueFactory.php @@ -0,0 +1,29 @@ +stores[$collection])) { + $this->stores[$collection] = new DatabaseStorage($collection); + } + return $this->stores[$collection]; + } +} diff --git a/core/modules/action/tests/action_loop_test/action_loop_test.install b/core/modules/action/tests/action_loop_test/action_loop_test.install index 50e3603..7085aed 100644 --- a/core/modules/action/tests/action_loop_test/action_loop_test.install +++ b/core/modules/action/tests/action_loop_test/action_loop_test.install @@ -4,8 +4,5 @@ * Implements hook_install(). */ function action_loop_test_install() { - db_update('system') - ->fields(array('weight' => 1)) - ->condition('name', 'action_loop_test') - ->execute(); + module_set_weight('action_loop_test', 1); } diff --git a/core/modules/block/block.install b/core/modules/block/block.install index 09ba522..ee72e0f 100644 --- a/core/modules/block/block.install +++ b/core/modules/block/block.install @@ -217,10 +217,7 @@ function block_install() { // Block should go first so that other modules can alter its output // during hook_page_alter(). Almost everything on the page is a block, // so before block module runs, there will not be much to alter. - db_update('system') - ->fields(array('weight' => -5)) - ->condition('name', 'block') - ->execute(); + module_set_weight('block', -5); } /** diff --git a/core/modules/block/block.module b/core/modules/block/block.module index 0626c08..2978146 100644 --- a/core/modules/block/block.module +++ b/core/modules/block/block.module @@ -958,12 +958,10 @@ function block_cache_flush() { * Implements hook_rebuild(). */ function block_rebuild() { - // Rehash blocks for active themes. We don't use list_themes() here, - // because if MAINTENANCE_MODE is defined it skips reading the database, - // and we can't tell which themes are active. - $themes = db_query("SELECT name FROM {system} WHERE type = 'theme' AND status = 1"); - foreach ($themes as $theme) { - _block_rehash($theme->name); + foreach (list_themes() as $name => $data) { + if ($data->status) { + _block_rehash($name); + } } } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php index 9eea495..2414728 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigCRUDTest.php @@ -93,7 +93,7 @@ function testCRUD() { } /** - * Tests Drupal\Core\Config\Config::sortByKey(). + * Tests key injection order. */ function testDataKeySort() { $config = config('config_test.keysort'); @@ -112,10 +112,10 @@ function testDataKeySort() { $new_config->set('new', 'Value to be replaced'); $new_config->save(); - // Verify that the data of both objects is in the identical order. + // Verify that the data of the new object is in the reverse order. // assertIdentical() is the required essence of this test; it performs a // strict comparison, which means that keys and values must be identical and - // their order must be identical. - $this->assertIdentical($new_config->get(), $config->get()); + // their order must be as expected. + $this->assertIdentical($new_config->get(), array_reverse($config->get())); } } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php index 9316962..2266732 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigImportTest.php @@ -128,10 +128,10 @@ function testNew() { $staging->write($name, $original_name_data); $original_dynamic_data = array( 'id' => 'new', + 'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651', 'label' => 'New', - 'langcode' => 'und', 'style' => '', - 'uuid' => '30df59bd-7b03-4cf7-bb35-d42fc49f0651', + 'langcode' => 'und', ); $staging->write($dynamic_name, $original_dynamic_data); $this->assertIdentical($staging->exists($name), TRUE, $name . ' found.'); diff --git a/core/modules/field/tests/modules/field_test/field_test.install b/core/modules/field/tests/modules/field_test/field_test.install index 5957561..5355e9b 100644 --- a/core/modules/field/tests/modules/field_test/field_test.install +++ b/core/modules/field/tests/modules/field_test/field_test.install @@ -10,10 +10,7 @@ */ function field_test_install() { // hook_entity_info_alter() needs to be executed as last. - db_update('system') - ->fields(array('weight' => 1)) - ->condition('name', 'field_test') - ->execute(); + module_set_weight('field_test', 1); } /** diff --git a/core/modules/forum/forum.install b/core/modules/forum/forum.install index 8e6fddf..56f8402 100644 --- a/core/modules/forum/forum.install +++ b/core/modules/forum/forum.install @@ -10,10 +10,7 @@ */ function forum_install() { // Set the weight of the forum.module to 1 so it is loaded after the taxonomy.module. - db_update('system') - ->fields(array('weight' => 1)) - ->condition('name', 'forum') - ->execute(); + module_set_weight('forum', 1); // Forum topics are published by default, but do not have any other default // options set (for example, they are not promoted to the front page). // @todo Convert to default module configuration, once Node module's content diff --git a/core/modules/help/lib/Drupal/help/Tests/HelpTest.php b/core/modules/help/lib/Drupal/help/Tests/HelpTest.php index 0bd35e6..7d5d20e 100644 --- a/core/modules/help/lib/Drupal/help/Tests/HelpTest.php +++ b/core/modules/help/lib/Drupal/help/Tests/HelpTest.php @@ -107,12 +107,9 @@ protected function verifyHelp($response = 200) { */ protected function getModuleList() { $modules = array(); - $result = db_query("SELECT name, filename, info FROM {system} WHERE type = 'module' AND status = 1 ORDER BY weight ASC, filename ASC"); - foreach ($result as $module) { - if (file_exists($module->filename) && function_exists($module->name . '_help')) { - $fullname = unserialize($module->info); - $modules[$module->name] = $fullname['name']; - } + $module_data = system_rebuild_module_data(); + foreach (module_implements('help') as $module) { + $modules[$module] = $module_data[$module]->info['name']; } return $modules; } diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php index 2e85afa..112fee8 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleCompareTest.php @@ -47,6 +47,8 @@ function testLocaleCompare() { // modules not hidden. locale_test_system_info_alter() modifies the project // info of the locale_test and locale_test_disabled modules. variable_set('locale_translation_test_system_info_alter', TRUE); + // Reset system lists to reflect changes. + system_list_reset(); // Check if interface translation data is collected from hook_info. drupal_static_reset('locale_translation_project_list'); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 6370033..aeff65e 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -122,6 +122,13 @@ protected $verboseDirectory; /** + * The original database prefix when running inside Simpletest. + * + * @var string + */ + protected $originalPrefix; + + /** * Constructor for Test. * * @param $test_id @@ -742,6 +749,10 @@ protected function prepareEnvironment() { foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) { $GLOBALS['config_directories'][$type]['path'] = 'simpletest/' . substr($this->databasePrefix, 10) . '/config_' . $type; } + if (drupal_valid_test_ua()) { + $this->originalPrefix = drupal_valid_test_ua(); + drupal_valid_test_ua($this->databasePrefix); + } // Reset and create a new service container. $this->container = drupal_container(NULL, TRUE); @@ -829,6 +840,9 @@ protected function tearDown() { drupal_container($this->originalContainer); $language_interface = $this->originalLanguage; $GLOBALS['config_directories'] = $this->originalConfigDirectories; + if (isset($this->originalPrefix)) { + drupal_valid_test_ua($this->originalPrefix); + } // Restore original shutdown callbacks. $callbacks = &drupal_register_shutdown_function(); diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module index dd5ee4d..2388601 100644 --- a/core/modules/simpletest/simpletest.module +++ b/core/modules/simpletest/simpletest.module @@ -323,16 +323,16 @@ function simpletest_test_get_all() { else { // Select all PSR-0 classes in the Tests namespace of all modules. $classes = array(); - $system_list = db_query("SELECT name, filename FROM {system}")->fetchAllKeyed(); - - foreach ($system_list as $name => $filename) { + $module_data = system_rebuild_module_data(); + $all_data = $module_data + system_rebuild_theme_data(); + foreach ($all_data as $name => $data) { // Build directory in which the test files would reside. - $tests_dir = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/Drupal/' . $name . '/Tests'; + $tests_dir = DRUPAL_ROOT . '/' . dirname($data->filename) . '/lib/Drupal/' . $name . '/Tests'; // Scan it for test files if it exists. if (is_dir($tests_dir)) { $files = file_scan_directory($tests_dir, '/.*\.php/'); if (!empty($files)) { - $basedir = DRUPAL_ROOT . '/' . dirname($filename) . '/lib/'; + $basedir = DRUPAL_ROOT . '/' . dirname($data->filename) . '/lib/'; foreach ($files as $file) { // Convert the file name into the namespaced class name. $replacements = array( @@ -357,7 +357,7 @@ function simpletest_test_get_all() { // If this test class requires a non-existing module, skip it. if (!empty($info['dependencies'])) { foreach ($info['dependencies'] as $module) { - if (!drupal_get_filename('module', $module)) { + if (!isset($module_data[$module])) { continue 2; } } @@ -385,12 +385,10 @@ function simpletest_test_get_all() { * Registers namespaces for disabled modules. */ function simpletest_classloader_register() { - // Get the cached test modules list and register a test namespace for each. - $disabled_modules = db_query("SELECT name, filename FROM {system} WHERE status = 0")->fetchAllKeyed(); - if ($disabled_modules) { - foreach ($disabled_modules as $name => $filename) { - drupal_classloader_register($name, dirname($filename)); - } + $all_data = system_rebuild_module_data(); + $all_data += system_rebuild_theme_data(); + foreach ($all_data as $name => $data) { + drupal_classloader_register($name, dirname($data->filename)); } } diff --git a/core/modules/system/config/system.module.yml b/core/modules/system/config/system.module.yml new file mode 100644 index 0000000..696bedc --- /dev/null +++ b/core/modules/system/config/system.module.yml @@ -0,0 +1,2 @@ +enabled: + system: '0' diff --git a/core/modules/system/config/system.theme.yml b/core/modules/system/config/system.theme.yml new file mode 100644 index 0000000..40fad06 --- /dev/null +++ b/core/modules/system/config/system.theme.yml @@ -0,0 +1,2 @@ +enabled: + stark: '0' diff --git a/core/modules/system/lib/Drupal/system/Tests/Database/RangeQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Database/RangeQueryTest.php index 4b15603..f85750b 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Database/RangeQueryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Database/RangeQueryTest.php @@ -34,11 +34,11 @@ public static function getInfo() { */ function testRangeQuery() { // Test if return correct number of rows. - $range_rows = db_query_range("SELECT name FROM {system} ORDER BY name", 2, 3)->fetchAll(); + $range_rows = db_query_range("SELECT name FROM {variable} ORDER BY name", 2, 3)->fetchAll(); $this->assertEqual(count($range_rows), 3, 'Range query work and return correct number of rows.'); // Test if return target data. - $raw_rows = db_query('SELECT name FROM {system} ORDER BY name')->fetchAll(); + $raw_rows = db_query('SELECT name FROM {variable} ORDER BY name')->fetchAll(); $raw_rows = array_slice($raw_rows, 2, 3); $this->assertEqual($range_rows, $raw_rows, 'Range query work and return target data.'); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Database/TemporaryQueryTest.php b/core/modules/system/lib/Drupal/system/Tests/Database/TemporaryQueryTest.php index dab4a70..110f5a8 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Database/TemporaryQueryTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Database/TemporaryQueryTest.php @@ -43,7 +43,7 @@ function testTemporaryQuery() { $this->drupalGet('database_test/db_query_temporary'); $data = json_decode($this->drupalGetContent()); if ($data) { - $this->assertEqual($this->countTableRows("system"), $data->row_count, 'The temporary table contains the correct amount of rows.'); + $this->assertEqual($this->countTableRows("filter"), $data->row_count, 'The temporary table contains the correct amount of rows.'); $this->assertFalse(db_table_exists($data->table_name), 'The temporary table is, indeed, temporary.'); } else { @@ -51,10 +51,10 @@ function testTemporaryQuery() { } // Now try to run two db_query_temporary() in the same request. - $table_name_system = db_query_temporary('SELECT status FROM {system}', array()); + $table_name_system = db_query_temporary('SELECT name FROM {variable}', array()); $table_name_users = db_query_temporary('SELECT uid FROM {users}', array()); - $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), 'A temporary table was created successfully in this request.'); + $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("variable"), 'A temporary table was created successfully in this request.'); $this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), 'A second temporary table was created successfully in this request.'); } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php index 3a54336..f6e6929 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Module/ModuleApiTest.php @@ -49,11 +49,7 @@ function testModuleList() { $this->assertModuleList($module_list, t('After adding a module')); // Try to mess with the module weights. - db_update('system') - ->fields(array('weight' => 20)) - ->condition('name', 'contact') - ->condition('type', 'module') - ->execute(); + module_set_weight('contact', 20); // Reset the module list. system_list_reset(); // Move contact to the end of the array. diff --git a/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php b/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php index 9bdeac4..603af18 100644 --- a/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/System/InfoAlterTest.php @@ -27,13 +27,14 @@ public static function getInfo() { * return freshly altered info. */ function testSystemInfoAlter() { - // Enable our test module. Flush all caches, which we assert is the only - // thing necessary to use the rebuilt {system}.info. + // Enable seven and the test module. Flush all caches, which we assert is + // the only thing necessary to use the rebuilt {system}.info. + theme_enable(array('seven')); module_enable(array('module_test'), FALSE); $this->resetAll(); $this->assertTrue(module_exists('module_test'), t('Test module is enabled.')); - $info = $this->getSystemInfo('seven', 'theme'); + $info = system_get_info('theme', 'seven'); $this->assertTrue(isset($info['regions']['test_region']), t('Altered theme info was added to {system}.info.')); $seven_regions = system_region_list('seven'); $this->assertTrue(isset($seven_regions['test_region']), t('Altered theme info was returned by system_region_list().')); @@ -48,7 +49,7 @@ function testSystemInfoAlter() { $this->resetAll(); $this->assertFalse(module_exists('module_test'), t('Test module is disabled.')); - $info = $this->getSystemInfo('seven', 'theme'); + $info = system_get_info('theme', 'seven'); $this->assertFalse(isset($info['regions']['test_region']), t('Altered theme info was removed from {system}.info.')); $seven_regions = system_region_list('seven'); $this->assertFalse(isset($seven_regions['test_region']), t('Altered theme info was not returned by system_region_list().')); @@ -58,20 +59,4 @@ function testSystemInfoAlter() { $list_themes = list_themes(); $this->assertFalse(isset($list_themes['seven']->info['regions']['test_region']), t('Altered theme info was not returned by list_themes().')); } - - /** - * Returns the info array as it is stored in {system}. - * - * @param $name - * The name of the record in {system}. - * @param $type - * The type of record in {system}. - * - * @return - * Array of info, or FALSE if the record is not found. - */ - function getSystemInfo($name, $type) { - $raw_info = db_query("SELECT info FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField(); - return $raw_info ? unserialize($raw_info) : FALSE; - } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php index 1231af4..f3275a7 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Update/UpdateScriptTest.php @@ -129,10 +129,10 @@ function testThemeSystem() { // Since visiting update.php triggers a rebuild of the theme system from an // unusual maintenance mode environment, we check that this rebuild did not // put any incorrect information about the themes into the database. - $original_theme_data = db_query("SELECT * FROM {system} WHERE type = 'theme' ORDER BY name")->fetchAll(); + $original_theme_data = config('system.theme')->get('enabled'); $this->drupalLogin($this->update_user); $this->drupalGet($this->update_url, array('external' => TRUE)); - $final_theme_data = db_query("SELECT * FROM {system} WHERE type = 'theme' ORDER BY name")->fetchAll(); + $final_theme_data = config('system.theme')->get('enabled'); $this->assertEqual($original_theme_data, $final_theme_data, t('Visiting update.php does not alter the information about themes stored in the database.')); } diff --git a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php index 812bde7..6c32faa 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php +++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/UpgradePathTestBase.php @@ -189,8 +189,7 @@ protected function refreshVariables() { protected function performUpgrade($register_errors = TRUE) { // Load the first update screen. - $update_url = $GLOBALS['base_url'] . '/core/update.php'; - $this->drupalGet($update_url, array('external' => TRUE)); + $this->getUpdatePhp(); if (!$this->assertResponse(200)) { throw new Exception('Initial GET to update.php did not return HTTP 200 status.'); } @@ -230,7 +229,7 @@ protected function performUpgrade($register_errors = TRUE) { } // Check if there still are pending updates. - $this->drupalGet($update_url, array('external' => TRUE)); + $this->getUpdatePhp(); $this->drupalPost(NULL, array(), t('Continue')); if (!$this->assertText(t('No pending updates.'), t('No pending updates at the end of the update process.'))) { throw new Exception('update.php still shows pending updates after execution.'); @@ -264,21 +263,35 @@ protected function performUpgrade($register_errors = TRUE) { } /** - * Force uninstall all modules from a test database, except those listed. + * Gets update.php without calling url(). * - * @param $modules - * The list of modules to keep installed. Required core modules will - * always be kept. + * Required since WebTestBase::drupalGet() calls t(), which calls into + * system_list(), from the parent site/test runner, before update.php is even + * executed. + * + * @see WebTestBase::drupalGet() */ - protected function uninstallModulesExcept(array $modules) { - $required_modules = array('block', 'dblog', 'filter', 'node', 'system', 'update', 'user'); - - $modules = array_merge($required_modules, $modules); + protected function getUpdatePhp() { + $path = $GLOBALS['base_url'] . '/core/update.php'; + $out = $this->curlExec(array(CURLOPT_HTTPGET => TRUE, CURLOPT_URL => $path, CURLOPT_NOBODY => FALSE)); + // Ensure that any changes to variables in the other thread are picked up. + $this->refreshVariables(); - db_delete('system') - ->condition('type', 'module') - ->condition('name', $modules, 'NOT IN') - ->execute(); + // Replace original page output with new output from redirected page(s). + if ($new = $this->checkForMetaRefresh()) { + $out = $new; + } + // @todo WebTestBase::verbose() cannot be called here yet, since Simpletest + // verbose output parameters are not prepared before test execution and + // instead determined at runtime; i.e., file_create_url() calls into + // system_list(), before update.php has upgraded the system list. + // @see http://drupal.org/node/1611430 + /* + $this->verbose('GET request to: ' . $path . + '
Ending URL: ' . $this->getUrl() . + '
' . $out); + */ + return $out; } } diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc index 2d7e194..1c6dc89 100644 --- a/core/modules/system/system.admin.inc +++ b/core/modules/system/system.admin.inc @@ -1252,7 +1252,7 @@ function system_modules_uninstall($form, $form_state = NULL) { $all_modules = system_rebuild_module_data(); $disabled_modules = array(); foreach ($all_modules as $name => $module) { - if (empty($module->status) && $module->schema_version > SCHEMA_UNINSTALLED) { + if (empty($module->status) && drupal_get_installed_schema_version($name) > SCHEMA_UNINSTALLED) { $disabled_modules[$name] = $module; } } diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 8331d55..e2b1eee 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -510,11 +510,6 @@ function system_install() { // Enable the default theme. Can't use theme_enable() this early in // installation. variable_set('theme_default', 'stark'); - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'theme') - ->condition('name', 'stark') - ->execute(); config_install_default_config('theme', 'stark'); // Populate the cron key variable. @@ -1385,75 +1380,6 @@ function system_schema() { ), ); - $schema['system'] = array( - 'description' => "A list of all modules, themes, and theme engines that are or have been installed in Drupal's file system.", - 'fields' => array( - 'filename' => array( - 'description' => 'The path of the primary file for this item, relative to the Drupal root; e.g. modules/node/node.module.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'name' => array( - 'description' => 'The name of the item; e.g. node.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'type' => array( - 'description' => 'The type of the item, either module, theme, or theme_engine.', - 'type' => 'varchar', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ), - 'owner' => array( - 'description' => "A theme's 'parent' . Can be either a theme or an engine.", - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ), - 'status' => array( - 'description' => 'Boolean indicating whether or not this item is enabled.', - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'bootstrap' => array( - 'description' => "Boolean indicating whether this module is loaded during Drupal's early bootstrapping phase (e.g. even before the page cache is consulted).", - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'schema_version' => array( - 'description' => "The module's database schema version number. -1 if the module is not installed (its tables do not exist); 0 or the largest N of the module's hook_update_N() function that has either been run or existed when the module was first installed.", - 'type' => 'int', - 'not null' => TRUE, - 'default' => -1, - 'size' => 'small', - ), - 'weight' => array( - 'description' => "The order in which this module's hooks should be invoked relative to other modules. Equal-weighted modules are ordered by name.", - 'type' => 'int', - 'not null' => TRUE, - 'default' => 0, - ), - 'info' => array( - 'description' => "A serialized array containing information from the module's .info file; keys can include name, description, package, version, core, dependencies, and php.", - 'type' => 'blob', - 'not null' => FALSE, - ), - ), - 'primary key' => array('filename'), - 'indexes' => array( - 'system_list' => array('status', 'bootstrap', 'type', 'weight', 'name'), - 'type_name' => array('type', 'name'), - ), - ); - $schema['url_alias'] = array( 'description' => 'A list of URL aliases for Drupal paths; a user may visit either the source or destination path.', 'fields' => array( diff --git a/core/modules/system/system.module b/core/modules/system/system.module index 4524a49..7b34f3f 100644 --- a/core/modules/system/system.module +++ b/core/modules/system/system.module @@ -2653,116 +2653,6 @@ function system_check_directory($form_element) { } /** - * Retrieves the current status of an array of files in the system table. - * - * @param $files - * An array of files to check. - * @param $type - * The type of the files. - */ -function system_get_files_database(&$files, $type) { - // Extract current files from database. - $result = db_query("SELECT filename, name, type, status, schema_version, weight FROM {system} WHERE type = :type", array(':type' => $type)); - foreach ($result as $file) { - if (isset($files[$file->name]) && is_object($files[$file->name])) { - $file->uri = $file->filename; - foreach ($file as $key => $value) { - if (!isset($files[$file->name]->$key)) { - $files[$file->name]->$key = $value; - } - } - } - } -} - -/** - * Updates the records in the system table based on the files array. - * - * @param $files - * An array of files. - * @param $type - * The type of the files. - */ -function system_update_files_database(&$files, $type) { - $result = db_query("SELECT * FROM {system} WHERE type = :type", array(':type' => $type)); - - // Add all files that need to be deleted to a DatabaseCondition. - $delete = db_or(); - foreach ($result as $file) { - if (isset($files[$file->name]) && is_object($files[$file->name])) { - // Keep the old filename from the database in case the file has moved. - $old_filename = $file->filename; - - $updated_fields = array(); - - // Handle info specially, compare the serialized value. - $serialized_info = serialize($files[$file->name]->info); - if ($serialized_info != $file->info) { - $updated_fields['info'] = $serialized_info; - } - unset($file->info); - - // Scan remaining fields to find only the updated values. - foreach ($file as $key => $value) { - if (isset($files[$file->name]->$key) && $files[$file->name]->$key != $value) { - $updated_fields[$key] = $files[$file->name]->$key; - } - } - - // Update the record. - if (count($updated_fields)) { - db_update('system') - ->fields($updated_fields) - ->condition('filename', $old_filename) - ->execute(); - } - - // Indicate that the file exists already. - $files[$file->name]->exists = TRUE; - } - else { - // File is not found in file system, so delete record from the system table. - $delete->condition('filename', $file->filename); - } - } - - if (count($delete) > 0) { - // Delete all missing files from the system table, but only if the plugin - // has never been installed. - db_delete('system') - ->condition($delete) - ->condition('schema_version', -1) - ->execute(); - } - - // All remaining files are not in the system table, so we need to add them. - $query = db_insert('system')->fields(array('filename', 'name', 'type', 'owner', 'info')); - foreach ($files as &$file) { - if (isset($file->exists)) { - unset($file->exists); - } - else { - $query->values(array( - 'filename' => $file->uri, - 'name' => $file->name, - 'type' => $type, - 'owner' => isset($file->owner) ? $file->owner : '', - 'info' => serialize($file->info), - )); - $file->type = $type; - $file->status = 0; - $file->schema_version = -1; - } - } - $query->execute(); - - // If any module or theme was moved to a new location, we need to reset the - // system_list() cache or we will continue to load the old copy, look for - // schema updates in the wrong place, etc. - system_list_reset(); -} - -/** * Returns an array of information about enabled modules or themes. * * This function returns the information from the {system} table corresponding @@ -2787,9 +2677,9 @@ function system_update_files_database(&$files, $type) { function system_get_info($type, $name = NULL) { $info = array(); if ($type == 'module') { - $result = db_query('SELECT name, info FROM {system} WHERE type = :type AND status = 1', array(':type' => 'module')); - foreach ($result as $record) { - $info[$record->name] = unserialize($record->info); + $data = system_rebuild_module_data(); + foreach (module_list() as $module) { + $info[$module] = $data[$module]->info; } } else { @@ -2927,11 +2817,25 @@ function system_rebuild_module_data() { // reference from system_list_reset() during the rebuild. if (!isset($modules_cache)) { $modules = _system_rebuild_module_data(); + $files = array(); ksort($modules); - system_get_files_database($modules, 'module'); - system_update_files_database($modules, 'module'); + // Add name, status, weight, and schema version. + $enabled_modules = config('system.module')->get('enabled'); + $disabled_modules = config('system.module.disabled')->get(); + $all_modules = $enabled_modules + $disabled_modules; + foreach ($modules as $module => $record) { + $record->name = $module; + $record->weight = isset($all_modules[$module]) ? $all_modules[$module] : 0; + $record->status = (int) isset($enabled_modules[$module]); + $record->schema_version = SCHEMA_UNINSTALLED; + $files[$module] = $record->filename; + } $modules = _module_build_dependencies($modules); $modules_cache = $modules; + + // Store filenames to allow system_list() and drupal_get_filename() to + // retrieve them without having to rebuild or scan the filesystem. + state()->set('system.module.files', $files); } return $modules_cache; } @@ -2947,20 +2851,10 @@ function _system_update_bootstrap_status() { $bootstrap_modules = array(); foreach (bootstrap_hooks() as $hook) { foreach (module_implements($hook) as $module) { - $bootstrap_modules[] = $module; + $bootstrap_modules[$module] = drupal_get_filename('module', $module); } } - $query = db_update('system')->fields(array('bootstrap' => 0)); - if ($bootstrap_modules) { - db_update('system') - ->fields(array('bootstrap' => 1)) - ->condition('name', $bootstrap_modules, 'IN') - ->execute(); - $query->condition('name', $bootstrap_modules, 'NOT IN'); - } - $query->execute(); - // Reset the cached list of bootstrap modules. - system_list_reset(); + state()->set('system.module.bootstrap', $bootstrap_modules); } /** @@ -3092,8 +2986,27 @@ function _system_rebuild_theme_data() { function system_rebuild_theme_data() { $themes = _system_rebuild_theme_data(); ksort($themes); - system_get_files_database($themes, 'theme'); - system_update_files_database($themes, 'theme'); + // @todo This function has no business in determining/setting the status of + // a theme, but various other functions expect it to return themes with a + // $status property. system_list() stores the return value of this function + // in state, and ensures to set/override the $status property for each theme + // based on the current config. Remove this code here when themes have a + // proper installation status. + // @see http://drupal.org/node/1067408 + $enabled_themes = config('system.theme')->get('enabled'); + $files = array(); + foreach ($themes as $name => $theme) { + $theme->status = (int) isset($enabled_themes[$name]); + $files[$name] = $theme->filename; + } + // Replace last known theme data state. + // @todo Obsolete with proper installation status for themes. + state()->set('system.theme.data', $themes); + + // Store filenames to allow system_list() and drupal_get_filename() to + // retrieve them without having to rebuild or scan the filesystem. + state()->set('system.theme.files', $files); + return $themes; } diff --git a/core/modules/system/tests/modules/database_test/database_test.module b/core/modules/system/tests/modules/database_test/database_test.module index 0180b03..93135c9 100644 --- a/core/modules/system/tests/modules/database_test/database_test.module +++ b/core/modules/system/tests/modules/database_test/database_test.module @@ -84,7 +84,7 @@ function database_test_menu() { * table should automatically dropped. */ function database_test_db_query_temporary() { - $table_name = db_query_temporary('SELECT status FROM {system}', array()); + $table_name = db_query_temporary('SELECT status FROM {filter}', array()); return new JsonResponse(array( 'table_name' => $table_name, 'row_count' => db_select($table_name)->countQuery()->execute()->fetchField(), diff --git a/core/modules/system/tests/modules/url_alter_test/url_alter_test.install b/core/modules/system/tests/modules/url_alter_test/url_alter_test.install index 6e09ab5..eb4864f 100644 --- a/core/modules/system/tests/modules/url_alter_test/url_alter_test.install +++ b/core/modules/system/tests/modules/url_alter_test/url_alter_test.install @@ -5,8 +5,5 @@ */ function url_alter_test_install() { // Set the weight of this module to one higher than forum.module. - db_update('system') - ->fields(array('weight' => 2)) - ->condition('name', 'url_alter_test') - ->execute(); + module_set_weight('url_alter_test', 2); } diff --git a/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php b/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php index 27328c0..4f51883 100644 --- a/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php +++ b/core/modules/update/lib/Drupal/update/Tests/UpdateContribTest.php @@ -166,11 +166,7 @@ function testUpdateContribOrder() { */ function testUpdateBaseThemeSecurityUpdate() { // Only enable the subtheme, not the base theme. - db_update('system') - ->fields(array('status' => 1)) - ->condition('type', 'theme') - ->condition('name', 'update_test_subtheme') - ->execute(); + theme_enable(array('update_test_subtheme')); // Define the initial state for core and the subtheme. $system_info = array( @@ -208,11 +204,13 @@ function testUpdateBaseThemeSecurityUpdate() { function testUpdateShowDisabledThemes() { $update_settings = config('update.settings'); // Make sure all the update_test_* themes are disabled. - db_update('system') - ->fields(array('status' => 0)) - ->condition('type', 'theme') - ->condition('name', 'update_test_%', 'LIKE') - ->execute(); + $theme_config = config('system.theme'); + foreach ($theme_config->get('enabled') as $theme => $weight) { + if (preg_match('/^update_test_/', $theme)) { + $theme_config->clear("enabled.$theme"); + } + } + $theme_config->save(); // Define the initial state for core and the test contrib themes. $system_info = array(