diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc index 72685a4..e32db3a 100644 --- a/core/includes/bootstrap.inc +++ b/core/includes/bootstrap.inc @@ -296,6 +296,20 @@ const DRUPAL_PHP_FUNCTION_PATTERN = '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'; /** + * $config_directories key for active directory. + * + * @see config_get_config_directory + */ +const CONFIG_ACTIVE_DIRECTORY = 'active'; + +/** + * $config_directories key for staging directory. + * + * @see config_get_config_directory + */ +const CONFIG_STAGING_DIRECTORY = 'staging'; + +/** * Starts the timer with the specified name. * * If you start and stop the same timer multiple times, the measured intervals @@ -469,23 +483,27 @@ function find_conf_path($http_host, $script_name, $require_settings = TRUE) { } /** - * Returns the path of the configuration directory. + * Returns the path of a configuration directory. + * + * @param string $type + * (optional) The type of config directory to return. Drupal core provides + * 'active' and 'staging'. Defaults to CONFIG_ACTIVE_DIRECTORY. * * @return string * The configuration directory path. */ -function config_get_config_directory() { - global $config_directory_name; +function config_get_config_directory($type = CONFIG_ACTIVE_DIRECTORY) { + global $config_directories; if ($test_prefix = drupal_valid_test_ua()) { // @see Drupal\simpletest\WebTestBase::setUp() - $path = conf_path() . '/files/simpletest/' . substr($test_prefix, 10) . '/config'; + $path = conf_path() . '/files/simpletest/' . substr($test_prefix, 10) . '/config_' . $type; } - elseif (!empty($config_directory_name)) { - $path = conf_path() . '/files/' . $config_directory_name; + elseif (!empty($config_directories[$type])) { + $path = conf_path() . '/files/' . $config_directories[$type]; } else { - throw new \Exception('Missing $config_directory_name.'); + throw new \Exception(format_string('The configuration directory type %type does not exist.', array('%type' => $type))); } return $path; } @@ -678,7 +696,7 @@ function drupal_settings_initialize() { global $base_url, $base_path, $base_root, $script_path; // Export these settings.php variables to the global namespace. - global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url, $config_directory_name; + global $databases, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $db_prefix, $drupal_hash_salt, $is_https, $base_secure_url, $base_insecure_url, $config_directories; $conf = array(); // Make conf_path() available as local variable in settings.php. @@ -1679,7 +1697,9 @@ function watchdog($type, $message, array $variables = array(), $severity = WATCH // It is possible that the error handling will itself trigger an error. In that case, we could // end up in an infinite loop. To avoid that, we implement a simple static semaphore. - if (!$in_error_state && function_exists('module_implements')) { + // During early bootstrap of the installer, module_implements() might be + // loaded already, but the module system might be initialized yet. + if (!$in_error_state && function_exists('module_implements') && module_load_all(NULL)) { $in_error_state = TRUE; // The user object may not exist in all conditions, so 0 is substituted if needed. @@ -2436,7 +2456,7 @@ function drupal_container(Container $new_container = NULL, $reset = FALSE) { // lowest of low level configuration. $container->setParameter('config.storage.options', array( 'Drupal\Core\Config\FileStorage' => array( - 'directory' => config_get_config_directory() . '/active', + 'directory' => config_get_config_directory(CONFIG_ACTIVE_DIRECTORY), ), 'Drupal\Core\Config\CacheStorage' => array( 'backend' => 'Drupal\Core\Cache\DatabaseBackend', @@ -2449,6 +2469,7 @@ function drupal_container(Container $new_container = NULL, $reset = FALSE) { $container->register('config.subscriber.globalconf', 'Drupal\Core\EventSubscriber\ConfigGlobalOverrideSubscriber'); $container->register('dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher') ->addMethodCall('addSubscriber', array(new Reference('config.subscriber.globalconf'))); + // Register configuration object factory. $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory') ->addArgument(new Reference('config.storage')) @@ -2456,7 +2477,7 @@ function drupal_container(Container $new_container = NULL, $reset = FALSE) { // Register configuration state. $container->setParameter('config.state.options', array( - 'directory' => config_get_config_directory() . '/import', + 'directory' => config_get_config_directory(CONFIG_STAGING_DIRECTORY), )); $container->register('config.state', 'Drupal\Core\Config\FileStorage') ->addArgument('%config.state.options%'); diff --git a/core/includes/config.inc b/core/includes/config.inc index c4d6810..c2b0f29 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -135,7 +135,7 @@ function config_sync_changes(array $config_changes, StorageInterface $source_sto * synchronization error, or NULL if there are no changes to synchronize. */ function config_import() { - // Retrieve a list of differences between last known state and active store. + // Retrieve a list of differences between staging and the active store. $source_storage = drupal_container()->get('config.state'); $target_storage = drupal_container()->get('config.storage'); @@ -213,12 +213,12 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou } /** - * Updates the last known state with the active store configuration. + * Updates staging store with the active store configuration. * * @todo config_export() is a misnomer now. Rename to config_state_update(). */ function config_export() { - // Retrieve a list of differences between active store and last known state. + // Retrieve a list of differences between the active store and staging. $source_storage = drupal_container()->get('config.storage'); $target_storage = drupal_container()->get('config.state'); diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc index 3d8d823..cf3a605 100644 --- a/core/includes/install.core.inc +++ b/core/includes/install.core.inc @@ -279,7 +279,7 @@ function install_begin_request(&$install_state) { include_once DRUPAL_ROOT . '/core/includes/session.inc'; // Determine whether the configuration system is ready to operate. - $install_state['config_verified'] = install_verify_config_directory(); + $install_state['config_verified'] = install_verify_config_directory(CONFIG_ACTIVE_DIRECTORY) && install_verify_config_directory(CONFIG_STAGING_DIRECTORY); // If it is not, replace the configuration storage with the InstallStorage // implementation, for the following reasons: @@ -303,9 +303,13 @@ function install_begin_request(&$install_state) { // @todo Move this into a proper Drupal\Core\DependencyInjection\InstallContainerBuilder. if (!$install_state['config_verified']) { $container = new ContainerBuilder(); + + $container->register('dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher'); + $container->register('config.storage', 'Drupal\Core\Config\InstallStorage'); $container->register('config.factory', 'Drupal\Core\Config\ConfigFactory') - ->addArgument(new Reference('config.storage')); + ->addArgument(new Reference('config.storage')) + ->addArgument(new Reference('dispatcher')); drupal_container($container); } @@ -1086,8 +1090,8 @@ function install_settings_form_submit($form, &$form_state) { drupal_rewrite_settings($settings); - // Add the config directory to settings.php. - drupal_install_config_directory(); + // Add the config directories to settings.php. + drupal_install_config_directories(); // We have a valid configuration directory in settings.php. // Reset the service container, so the config.storage service will use the diff --git a/core/includes/install.inc b/core/includes/install.inc index 6602d36..de32df2 100644 --- a/core/includes/install.inc +++ b/core/includes/install.inc @@ -254,32 +254,37 @@ function drupal_rewrite_settings($settings = array()) { * @see install_settings_form_submit() * @see update_prepare_d8_bootstrap() */ -function drupal_install_config_directory() { - global $config_directory_name; +function drupal_install_config_directories() { + global $config_directories; // Add a randomized config directory name to settings.php, unless it was // manually defined in the existing already. - if (empty($config_directory_name)) { - $settings['config_directory_name'] = array( - 'value' => 'config_' . drupal_hash_base64(drupal_random_bytes(55)), + if (empty($config_directories)) { + $settings['config_directories'] = array( + 'value' => array( + CONFIG_ACTIVE_DIRECTORY => 'config/active_' . drupal_hash_base64(drupal_random_bytes(55)), + CONFIG_STAGING_DIRECTORY => 'config/staging_' . drupal_hash_base64(drupal_random_bytes(55)), + ), 'required' => TRUE, ); // Rewrite settings.php, which also sets the value as global variable. drupal_rewrite_settings($settings); } - // Ensure that the config directory exists or can be created, and is writable. - if (!install_ensure_config_directory()) { + // Ensure the config directories exist or can be created, and are writable. + foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $config_type) { // This should never fail, since if the config directory was specified in // settings.php it will have already been created and verified earlier, and // if it wasn't specified in settings.php, it is created here inside the // public files directory, which has already been verified to be writable // itself. But if it somehow fails anyway, the installation cannot proceed. // Bail out using a similar error message as in system_requirements(). - throw new Exception(st('The directory %directory could not be created or could not be made writable. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see the online handbook.', array( - '%directory' => config_get_config_directory(), - '@handbook_url' => 'http://drupal.org/server-permissions', - ))); + if (!install_ensure_config_directory($config_type)) { + throw new Exception(st('The directory %directory could not be created or could not be made writable. To proceed with the installation, either create the directory and modify its permissions manually or ensure that the installer has the permissions to create it automatically. For more information, see the online handbook.', array( + '%directory' => config_get_config_directory($config_type), + '@handbook_url' => 'http://drupal.org/server-permissions', + ))); + } } } @@ -291,38 +296,51 @@ function drupal_install_config_directory() { * since the installer would create the config directory too early in the * installation process otherwise (e.g., when only visiting install.php when * there is a settings.php already, but not actually executing the installation). + * + * @param string $type + * Type of config directory to return. Drupal core provides 'active' and + * 'staging'. + * + * @return bool + * TRUE if the config directory exists and is writable. */ -function install_verify_config_directory() { - global $config_directory_name; - if (empty($config_directory_name)) { +function install_verify_config_directory($type) { + global $config_directories; + if (!isset($config_directories[$type])) { return FALSE; } - $config_directory = config_get_config_directory(); - if (is_dir($config_directory) && is_writable($config_directory)) { - return TRUE; + try { + $config_directory = config_get_config_directory($type); + if (is_dir($config_directory) && is_writable($config_directory)) { + return TRUE; + } + } + catch (\Exception $e) { } return FALSE; } /** * Ensures that the config directory exists and is writable, or can be made so. + * + * @param string $type + * Type of config directory to return. Drupal core provides 'active' and + * 'staging'. + * + * @return bool + * TRUE if the config directory exists and is writable. */ -function install_ensure_config_directory() { +function install_ensure_config_directory($type) { // The config directory must be defined in settings.php. - global $config_directory_name; - if (empty($config_directory_name)) { + global $config_directories; + if (!isset($config_directories[$type])) { return FALSE; } // The logic here is similar to that used by system_requirements() for other // directories that the installer creates. else { - $config_directory = config_get_config_directory(); - $success = file_prepare_directory($config_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); - foreach (array('active', 'import') as $subdir) { - $subdir = $config_directory . '/' . $subdir; - $success = $success && file_prepare_directory($subdir, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); - } - return $success; + $config_directory = config_get_config_directory($type); + return file_prepare_directory($config_directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS); } } diff --git a/core/includes/theme.maintenance.inc b/core/includes/theme.maintenance.inc index 12de2a7..4b3e80c 100644 --- a/core/includes/theme.maintenance.inc +++ b/core/includes/theme.maintenance.inc @@ -122,7 +122,7 @@ function theme_task_list($variables) { } else { $class = $done ? 'done' : ''; - $status = $done ? '(' . t('done') . ')' : ''; + $status = $done ? '(' . $t('done') . ')' : ''; } $output .= ''; diff --git a/core/includes/update.inc b/core/includes/update.inc index 30be3cd..5582bb4 100644 --- a/core/includes/update.inc +++ b/core/includes/update.inc @@ -89,7 +89,7 @@ function update_prepare_d8_bootstrap() { drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE); // Check whether settings.php needs to be rewritten. - $settings_exist = !empty($GLOBALS['config_directory_name']); + $settings_exist = !empty($GLOBALS['config_directories']); // If any of the required settings needs to be written, then settings.php // needs to be writable. @@ -139,11 +139,11 @@ function update_prepare_d8_bootstrap() { // Update the environment for the language bootstrap if needed. update_prepare_d8_language(); - // Ensure the configuration directory exists and is writable or create it. - // If no $config_directory_name has been specified in settings.php and - // created manually already, and the directory cannot be created by the + // 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_directory(); + drupal_install_config_directories(); // Change language column to langcode in url_alias. if (db_table_exists('url_alias') && db_field_exists('url_alias', 'language')) { diff --git a/core/lib/Drupal/Core/Config/CacheStorage.php b/core/lib/Drupal/Core/Config/CacheStorage.php index 1f69c87..a2effc8 100644 --- a/core/lib/Drupal/Core/Config/CacheStorage.php +++ b/core/lib/Drupal/Core/Config/CacheStorage.php @@ -7,6 +7,8 @@ namespace Drupal\Core\Config; +use Drupal\Core\Cache\CacheBackendInterface; + /** * Defines the Cache storage controller. * @@ -91,7 +93,7 @@ public function read($name) { * Implements Drupal\Core\Config\StorageInterface::write(). */ public function write($name, array $data) { - $this->getBackend()->set($name, $data, CACHE_PERMANENT, array('config' => array($name))); + $this->getBackend()->set($name, $data, CacheBackendInterface::CACHE_PERMANENT, array('config' => array($name))); return TRUE; } @@ -104,6 +106,15 @@ public function delete($name) { } /** + * Implements Drupal\Core\Config\StorageInterface::rename(). + */ + public function rename($name, $new_name) { + $this->getBackend()->delete($name); + $this->getBackend()->delete($new_name); + return TRUE; + } + + /** * Implements Drupal\Core\Config\StorageInterface::encode(). */ public static function encode($data) { diff --git a/core/lib/Drupal/Core/Config/CachedFileStorage.php b/core/lib/Drupal/Core/Config/CachedFileStorage.php index 74eb99e..0fc0cfe 100644 --- a/core/lib/Drupal/Core/Config/CachedFileStorage.php +++ b/core/lib/Drupal/Core/Config/CachedFileStorage.php @@ -99,6 +99,15 @@ public function delete($name) { } /** + * Implements Drupal\Core\Config\StorageInterface::rename(). + */ + public function rename($name, $new_name) { + $success = $this->storages['file']->rename($name, $new_name); + $this->storages['cache']->rename($name, $new_name); + return $success; + } + + /** * Implements Drupal\Core\Config\StorageInterface::encode(). * * @todo Remove encode() from StorageInterface. diff --git a/core/lib/Drupal/Core/Config/InstallStorage.php b/core/lib/Drupal/Core/Config/InstallStorage.php index 67cf357..740d583 100644 --- a/core/lib/Drupal/Core/Config/InstallStorage.php +++ b/core/lib/Drupal/Core/Config/InstallStorage.php @@ -67,13 +67,26 @@ public function write($name, array $data) { /** * Overrides Drupal\Core\Config\FileStorage::delete(). + * + * @throws Drupal\Core\Config\StorageException */ public function delete($name) { throw new StorageException('Write operations are not allowed.'); } /** + * Overrides Drupal\Core\Config\FileStorage::rename(). + * + * @throws Drupal\Core\Config\StorageException + */ + public function rename($name, $new_name) { + throw new StorageException('Write operations are not allowed.'); + } + + /** * Implements Drupal\Core\Config\StorageInterface::listAll(). + * + * @throws Drupal\Core\Config\StorageException */ public function listAll($prefix = '') { throw new StorageException('List operation is not supported.'); diff --git a/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php b/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php index 092687f..0fd2ba4 100644 --- a/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/Storage/FileStorageTest.php @@ -25,7 +25,7 @@ public static function getInfo() { function setUp() { parent::setUp(); $this->storage = new FileStorage(); - $this->invalidStorage = new FileStorage(array('directory' => $this->configFileDirectory . '/nonexisting')); + $this->invalidStorage = new FileStorage(array('directory' => $this->configDirectories[CONFIG_ACTIVE_DIRECTORY] . '/nonexisting')); // FileStorage::listAll() requires other configuration data to exist. $this->storage->write('system.performance', config('system.performance')->get()); diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php index 80d53db..253d4df 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php @@ -689,7 +689,7 @@ protected function prepareEnvironment() { // Backup statics and globals. $this->originalContainer = clone drupal_container(); $this->originalLanguage = $language_interface; - $this->originalConfigDirectory = $GLOBALS['config_directory_name']; + $this->originalConfigDirectories = $GLOBALS['config_directories']; // Save further contextual information. $this->originalFileDirectory = variable_get('file_public_path', conf_path() . '/files'); @@ -717,21 +717,26 @@ protected function prepareEnvironment() { file_prepare_directory($this->temp_files_directory, FILE_CREATE_DIRECTORY); $this->generatedTestFiles = FALSE; - // Set a new configuration directory name. - // The child site automatically adjusts the global $config_directory_name to + // Create and set new configuration directories. The child site + // uses drupal_valid_test_ua() to adjust the config directory paths to // a test-prefix-specific directory within the public files directory. // @see config_get_config_directory() - $GLOBALS['config_directory_name'] = 'simpletest/' . substr($this->databasePrefix, 10) . '/config'; - $this->configFileDirectory = $this->originalFileDirectory . '/' . $GLOBALS['config_directory_name']; + $GLOBALS['config_directories'] = array(); + foreach (array(CONFIG_ACTIVE_DIRECTORY, CONFIG_STAGING_DIRECTORY) as $type) { + $GLOBALS['config_directories'][$type] = 'simpletest/' . substr($this->databasePrefix, 10) . '/config_' . $type; + } // Reset and create a new service container. drupal_container(NULL, TRUE); $this->container = drupal_container(); - // Create the new configuration directories. + $this->configDirectories = array(); include_once DRUPAL_ROOT . '/core/includes/install.inc'; - if (!install_ensure_config_directory()) { - return FALSE; + foreach ($GLOBALS['config_directories'] as $type => $path) { + if (!install_ensure_config_directory($type)) { + return FALSE; + } + $this->configDirectories[$type] = $this->originalFileDirectory . '/' . $path; } // Log fatal errors. @@ -790,7 +795,7 @@ protected function tearDown() { // Restore original statics and globals. drupal_container($this->originalContainer); $language_interface = $this->originalLanguage; - $GLOBALS['config_directory_name'] = $this->originalConfigDirectory; + $GLOBALS['config_directories'] = $this->originalConfigDirectories; // Restore original shutdown callbacks. $callbacks = &drupal_register_shutdown_function(); diff --git a/core/modules/system/system.install b/core/modules/system/system.install index fd4e06c..da76231 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -330,14 +330,15 @@ function system_requirements($phase) { // Check the config directory if it is defined in settings.php. If it isn't // defined, the installer will create a valid config directory later, but // during runtime we must always display an error. - if (!empty($GLOBALS['config_directory_name'])) { - $directories[] = config_get_config_directory(); + if (!empty($GLOBALS['config_directories'])) { + $directories[] = config_get_config_directory(CONFIG_ACTIVE_DIRECTORY); + $directories[] = config_get_config_directory(CONFIG_STAGING_DIRECTORY); } elseif ($phase != 'install') { - $requirements['config directory'] = array( - 'title' => $t('Configuration directory'), + $requirements['config directories'] = array( + 'title' => $t('Configuration directories'), 'value' => $t('Not present'), - 'description' => $t('Your %file file must define the $config_directory_name variable as the name of a directory in which configuration files can be written.', array('%file' => conf_path() . '/settings.php')), + 'description' => $t('Your %file file must define the $config_directories variable as an array containing the name of a directories in which configuration files can be written.', array('%file' => conf_path() . '/settings.php')), 'severity' => REQUIREMENT_ERROR, ); } diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php index 2c12638..b063812 100755 --- a/sites/default/default.settings.php +++ b/sites/default/default.settings.php @@ -257,9 +257,14 @@ * @todo Flesh this out, provide more details, etc. * * Example: - * $config_directory_name = '/some/directory/outside/webroot'; + * @code + * $config_directories = array( + * CONFIG_ACTIVE_DIRECTORY => '/some/directory/outside/webroot', + * CONFIG_STAGING_DIRECTORY => '/another/directory/outside/webroot', + * ); + * @endcode */ -$config_directory_name = ''; +$config_directories = array(); /** * Base URL (optional).