Commit 9fbe7440 authored by catch's avatar catch

Issue #1608842 by chx, alexpott, sun, effulgentsia, beejeebus, tim.plunkett et...

Issue #1608842 by chx, alexpott, sun, effulgentsia, beejeebus, tim.plunkett et al: Replace list of bootstrap modules, enabled modules, enabled themes, and default theme with config.
parent 16eac3be
......@@ -890,28 +890,27 @@ 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 raised an exception 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.
if (!isset($files[$type][$name])) {
// We have a consistent directory naming: modules, themes...
// We have consistent directory naming: modules, themes...
$dir = $type . 's';
if ($type == 'theme_engine') {
$dir = 'themes/engines';
......@@ -2466,9 +2465,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));
// Register the KeyValueStore factory.
$container
->register('state.storage', 'Drupal\Core\KeyValueStore\DatabaseStorage')
->addArgument('state');
->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory');
}
return $container;
}
......@@ -2485,7 +2485,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');
}
/**
......@@ -2504,16 +2504,22 @@ 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. Passed in by tests that use
* the test runner from within a test.
*
* @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;
}
......
......@@ -322,9 +322,6 @@ function install_begin_request(&$install_state) {
$container->register('config.factory', 'Drupal\Core\Config\ConfigFactory')
->addArgument(new Reference('config.storage'))
->addArgument(new Reference('dispatcher'));
$container
->register('state.storage', 'Drupal\Core\KeyValueStore\DatabaseStorage')
->addArgument('state');
drupal_container($container);
}
......@@ -1661,15 +1658,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
......
......@@ -8,16 +8,6 @@
use Drupal\Core\Database\Database;
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.
*/
......@@ -420,28 +410,24 @@ 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);
// 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.
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');
......
This diff is collapsed.
......@@ -14,6 +14,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.
*
......@@ -156,7 +166,7 @@ function drupal_get_schema_versions($module) {
* @param string $module
* A module name.
* @param bool $reset
* Set to TRUE after modifying the system table.
* Set to TRUE after installing or uninstalling an extension.
* @param bool $array
* Set to TRUE if you want to get information about all modules in the
* system.
......@@ -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);
}
......
......@@ -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();
......
This diff is collapsed.
<?php
/**
* @file
* Contains Drupal\Core\KeyValueStore\KeyValueFactory.
*/
namespace Drupal\Core\KeyValueStore;
/**
* Defines the key/value store factory.
*/
class KeyValueFactory {
/**
* Instantiated stores, keyed by collection name.
*
* @var array
*/
protected $stores = array();
/**
* Constructs a new key/value store for a given collection name.
*
* @param string $collection
* The name of the collection holding key and value pairs.
*
* @return Drupal\Core\KeyValueStore\DatabaseStorage
* A key/value store implementation for the given $collection.
*/
public function get($collection) {
if (!isset($this->stores[$collection])) {
$this->stores[$collection] = new DatabaseStorage($collection);
}
return $this->stores[$collection];
}
}
......@@ -72,7 +72,7 @@ public static function canUpdate($project_name) {
* Overrides Drupal\Core\Updater\Updater::postInstall().
*/
public function postInstall() {
// Update the system table.
// Update the theme info.
clearstatcache();
system_rebuild_theme_data();
}
......
......@@ -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);
}
......@@ -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);
}
/**
......
......@@ -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);
}
}
}
......
......@@ -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);
}
/**
......
......@@ -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
......
......@@ -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;
}
......
......@@ -48,8 +48,11 @@ function testLocaleCompare() {
// info of the locale_test and locale_test_disabled modules.
variable_set('locale_translation_test_system_info_alter', TRUE);
// Check if interface translation data is collected from hook_info.
// Reset static system list caches to reflect info changes.
drupal_static_reset('locale_translation_project_list');
system_list_reset();
// Check if interface translation data is collected from hook_info.
$projects = locale_translation_project_list();
$this->assertEqual($projects['locale_test']['info']['interface translation server pattern'], 'core/modules/locale/test/modules/locale_test/%project-%version.%language.po', 'Interface translation parameter found in project info.');
$this->assertEqual($projects['locale_test']['name'] , 'locale_test', format_string('%key found in project info.', array('%key' => 'interface translation project')));
......
......@@ -121,6 +121,13 @@ abstract class TestBase {
*/
protected $verboseDirectory;
/**
* The original database prefix when running inside Simpletest.
*
* @var string
*/
protected $originalPrefix;
/**
* Constructor for Test.
*
......@@ -722,6 +729,13 @@ protected function prepareEnvironment() {
global $user, $conf;
$language_interface = language(LANGUAGE_TYPE_INTERFACE);
// When running the test runner within a test, back up the original database
// prefix and re-set the new/nested prefix in drupal_valid_test_ua().
if (drupal_valid_test_ua()) {
$this->originalPrefix = drupal_valid_test_ua();
drupal_valid_test_ua($this->databasePrefix);
}
// Backup current in-memory configuration.
$this->originalConf = $conf;
......@@ -858,6 +872,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();
......
......@@ -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));
}
}
......
......@@ -7,12 +7,10 @@
namespace Drupal\system\Tests\Database;
use Drupal\simpletest\WebTestBase;
/**
* Range query tests.
*/
class RangeQueryTest extends WebTestBase {
class RangeQueryTest extends DatabaseTestBase {
/**
* Modules to enable.
......@@ -34,12 +32,12 @@ 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 {test} ORDER BY name", 1, 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 = array_slice($raw_rows, 2, 3);
$this->assertEqual($range_rows, $raw_rows, 'Range query work and return target data.');
$raw_rows = db_query('SELECT name FROM {test} ORDER BY name')->fetchAll();
$raw_rows = array_slice($raw_rows, 1, 3);
$this->assertEqual($range_rows, $raw_rows);
}
}
......@@ -7,12 +7,10 @@
namespace Drupal\system\Tests\Database;
use Drupal\simpletest\WebTestBase;
/**
* Temporary query tests.
*/
class TemporaryQueryTest extends WebTestBase {
class TemporaryQueryTest extends DatabaseTestBase {
/**
* Modules to enable.
......@@ -43,7 +41,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('test'), $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 +49,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_users = db_query_temporary('SELECT uid FROM {users}', array());
$table_name_test = db_query_temporary('SELECT name FROM {test}', array());
$table_name_task = db_query_temporary('SELECT pid FROM {test_task}', 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_users), $this->countTableRows("users"), 'A second temporary table was created successfully in this request.');
$this->assertEqual($this->countTableRows($table_name_test), $this->countTableRows('test'), 'A temporary table was created successfully in this request.');
$this->assertEqual($this->countTableRows($table_name_task), $this->countTableRows('test_task'), 'A second temporary table was created successfully in this request.');
}
}
......@@ -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.
......
<?php
/**
* @file
* Contains Drupal\system\Tests\Module\ModuleEnable.
*/
namespace Drupal\system\Tests\Module;
use Drupal\simpletest\WebTestBase;
/**
* Tests module_enable().
*/
class ModuleEnable extends WebTestBase {
public static function getInfo() {
return array(
'name' => 'Module enable',
'description' => 'Tests module_enable().',
'group' => 'Module',
);
}
/**
* Tests enabling User module once more.
*
* Regression: The installer might enable a module twice due to automatic
* dependency resolution. A bug caused the stored weight for User module to
* be an array.
*/
function testEnableUserTwice() {
module_enable(array('user'), FALSE);
$this->assertIdentical(config('system.module')->get('enabled.user'), '0');
}
/**
* Tests recorded schema versions of early installed modules in the installer.
*/
function testRequiredModuleSchemaVersions() {
$version = drupal_get_installed_schema_version('system', TRUE);
$this->assertTrue($version > 0, 'System module version is > 0.');
$version = drupal_get_installed_schema_version('user', TRUE);
$this->assertTrue($version > 0, 'User module version is > 0.');
}
}
<
......@@ -22,19 +22,21 @@ public static function getInfo() {
}
/**
* Tests that {system}.info is rebuilt after a module that implements
* Tests that theme .info data is rebuild after enabling a module.
*
* Tests that info data is rebuilt after a module that implements
* hook_system_info_alter() is enabled. Also tests if core *_list() functions
* 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.
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');
$this->assertTrue(isset($info['regions']['test_region']), t('Altered theme info was added to {system}.info.'));
// Verify that the rebuilt and altered theme info is returned.
$info = system_get_info('theme', 'seven');
$this->assertTrue(isset($info['regions']['test_region']), t('Altered theme info was returned by system_get_info().'));
$seven_regions = system_region_list('seven');
$this->assertTrue(isset($seven_regions['test_region']), t('Altered theme info was returned by system_region_list().'));
$system_list_themes = system_list('theme');
......@@ -43,13 +45,12 @@ function testSystemInfoAlter() {
$list_themes = list_themes();
$this->assertTrue(isset($list_themes['seven']->info['regions']['test_region']), t('Altered theme info was returned by list_themes().'));
// Disable the module and verify that {system}.info is rebuilt without it.
// Disable the module and verify that rebuilt .info does not contain it.
module_disable(array('module_test'), FALSE);
$this->resetAll();
$this->assertFalse(module_exists('module_test'), t('Test module is disabled.'));
$info = $this->getSystemInfo('seven', 'theme');
$this->assertFalse(isset($info['regions']['test_region']), t('Altered theme info was removed from {system}.info.'));
$info = system_get_info('theme', 'seven');
$this->assertFalse(isset($info['regions']['test_region']), t('Altered theme info was not returned by system_get_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().'));
$system_list_themes = system_list('theme');
......@@ -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}.
*