Issue #2161591 by pwolanin, beejeebus, sun: Change default active config from file storage to DB storage.
parent
23b38b7294
commit
abc7e15fa6
|
@ -74,16 +74,9 @@ services:
|
|||
factory_method: get
|
||||
factory_service: cache_factory
|
||||
arguments: [discovery]
|
||||
config.cachedstorage.storage:
|
||||
class: Drupal\Core\Config\FileStorage
|
||||
factory_class: Drupal\Core\Config\FileStorageFactory
|
||||
factory_method: getActive
|
||||
config.manager:
|
||||
class: Drupal\Core\Config\ConfigManager
|
||||
arguments: ['@entity.manager', '@config.factory', '@config.typed', '@string_translation', '@config.storage']
|
||||
config.storage:
|
||||
class: Drupal\Core\Config\CachedStorage
|
||||
arguments: ['@config.cachedstorage.storage', '@cache.config']
|
||||
config.factory:
|
||||
class: Drupal\Core\Config\ConfigFactory
|
||||
tags:
|
||||
|
@ -92,6 +85,15 @@ services:
|
|||
config.installer:
|
||||
class: Drupal\Core\Config\ConfigInstaller
|
||||
arguments: ['@config.factory', '@config.storage', '@config.typed', '@config.manager', '@event_dispatcher']
|
||||
config.storage:
|
||||
alias: config.storage.active
|
||||
config.storage.active:
|
||||
class: Drupal\Core\Config\DatabaseStorage
|
||||
arguments: ['@database', 'config']
|
||||
config.storage.file:
|
||||
class: Drupal\Core\Config\FileStorage
|
||||
factory_class: Drupal\Core\Config\FileStorageFactory
|
||||
factory_method: getActive
|
||||
config.storage.staging:
|
||||
class: Drupal\Core\Config\FileStorage
|
||||
factory_class: Drupal\Core\Config\FileStorageFactory
|
||||
|
|
|
@ -438,7 +438,7 @@ function install_begin_request(&$install_state) {
|
|||
// Ensure that the active configuration directory is empty before installation
|
||||
// starts.
|
||||
if ($install_state['config_verified'] && empty($task)) {
|
||||
$config = glob(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY) . '/*.' . FileStorage::getFileExtension());
|
||||
$config = \Drupal::service('config.storage')->listAll();
|
||||
if (!empty($config)) {
|
||||
$task = NULL;
|
||||
throw new AlreadyInstalledException($container->get('string_translation'));
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Component\Utility\Settings;
|
||||
|
||||
/**
|
||||
|
@ -21,13 +22,29 @@ class BootstrapConfigStorageFactory {
|
|||
* A configuration storage implementation.
|
||||
*/
|
||||
public static function get() {
|
||||
$drupal_bootstrap_config_storage = Settings::get('drupal_bootstrap_config_storage');
|
||||
if ($drupal_bootstrap_config_storage && is_callable($drupal_bootstrap_config_storage)) {
|
||||
return call_user_func($drupal_bootstrap_config_storage);
|
||||
}
|
||||
else {
|
||||
return new FileStorage(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY));
|
||||
$bootstrap_config_storage = Settings::get('bootstrap_config_storage');
|
||||
if (!empty($bootstrap_config_storage) && is_callable($bootstrap_config_storage)) {
|
||||
return call_user_func($bootstrap_config_storage);
|
||||
}
|
||||
// Fallback to the DatabaseStorage.
|
||||
return self::getDatabaseStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Database configuration storage implementation.
|
||||
*
|
||||
* @return \Drupal\Core\Config\DatabaseStorage
|
||||
*/
|
||||
public static function getDatabaseStorage() {
|
||||
return new DatabaseStorage(Database::getConnection(), 'config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a File-based configuration storage implementation.
|
||||
*
|
||||
* @return \Drupal\Core\Config\FileStorage
|
||||
*/
|
||||
public static function getFileStorage() {
|
||||
return new FileStorage(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\Core\Config\Config.
|
||||
* Contains \Drupal\Core\Config\Config.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Config;
|
||||
|
@ -215,6 +215,11 @@ class Config extends StorableConfigBase {
|
|||
$this->data[$key] = $this->castValue($key, $value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
foreach ($this->data as $key => $value) {
|
||||
$this->validateValue($key, $value);
|
||||
}
|
||||
}
|
||||
|
||||
$this->storage->write($this->name, $this->data);
|
||||
$this->isNew = FALSE;
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\Core\Config\DatabaseStorage.
|
||||
* Contains \Drupal\Core\Config\DatabaseStorage.
|
||||
*/
|
||||
|
||||
namespace Drupal\Core\Config;
|
||||
|
||||
use Drupal\Core\Database\Database;
|
||||
use Drupal\Core\Database\Connection;
|
||||
use Drupal\Core\Database\SchemaObjectExistsException;
|
||||
|
||||
/**
|
||||
* Defines the Database storage.
|
||||
|
@ -56,24 +57,23 @@ class DatabaseStorage implements StorageInterface {
|
|||
* Implements Drupal\Core\Config\StorageInterface::exists().
|
||||
*/
|
||||
public function exists($name) {
|
||||
return (bool) $this->connection->queryRange('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name = :name', 0, 1, array(
|
||||
':name' => $name,
|
||||
), $this->options)->fetchField();
|
||||
try {
|
||||
return (bool) $this->connection->queryRange('SELECT 1 FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name = :name', 0, 1, array(
|
||||
':name' => $name,
|
||||
), $this->options)->fetchField();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// If we attempt a read without actually having the database or the table
|
||||
// available, just return FALSE so the caller can handle it.
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\StorageInterface::read().
|
||||
*
|
||||
* @throws PDOException
|
||||
* @throws \Drupal\Core\Database\DatabaseExceptionWrapper
|
||||
* Only thrown in case $this->options['throw_exception'] is TRUE.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function read($name) {
|
||||
$data = FALSE;
|
||||
// There are situations, like in the installer, where we may attempt a
|
||||
// read without actually having the database available. In this case,
|
||||
// catch the exception and just return an empty array so the caller can
|
||||
// handle it if need be.
|
||||
try {
|
||||
$raw = $this->connection->query('SELECT data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name = :name', array(':name' => $name), $this->options)->fetchField();
|
||||
if ($raw !== FALSE) {
|
||||
|
@ -81,6 +81,8 @@ class DatabaseStorage implements StorageInterface {
|
|||
}
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// If we attempt a read without actually having the database or the table
|
||||
// available, just return FALSE so the caller can handle it.
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
|
@ -89,10 +91,6 @@ class DatabaseStorage implements StorageInterface {
|
|||
* {@inheritdoc}
|
||||
*/
|
||||
public function readMultiple(array $names) {
|
||||
// There are situations, like in the installer, where we may attempt a
|
||||
// read without actually having the database available. In this case,
|
||||
// catch the exception and just return an empty array so the caller can
|
||||
// handle it if need be.
|
||||
$list = array();
|
||||
try {
|
||||
$list = $this->connection->query('SELECT name, data FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name IN (:names)', array(':names' => $names), $this->options)->fetchAllKeyed();
|
||||
|
@ -100,20 +98,42 @@ class DatabaseStorage implements StorageInterface {
|
|||
$data = $this->decode($data);
|
||||
}
|
||||
}
|
||||
catch (Exception $e) {
|
||||
catch (\Exception $e) {
|
||||
// If we attempt a read without actually having the database or the table
|
||||
// available, just return an empty array so the caller can handle it.
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\StorageInterface::write().
|
||||
*
|
||||
* @throws PDOException
|
||||
*
|
||||
* @todo Ignore slave targets for data manipulation operations.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($name, array $data) {
|
||||
$data = $this->encode($data);
|
||||
try {
|
||||
return $this->doWrite($name, $data);
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
// If there was an exception, try to create the table.
|
||||
if ($this->ensureTableExists()) {
|
||||
return $this->doWrite($name, $data);
|
||||
}
|
||||
// Some other failure that we can not recover from.
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method so we can re-try a write.
|
||||
*
|
||||
* @param string $name
|
||||
* The config name.
|
||||
* @param string $data
|
||||
* The config data, already dumped to a string.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doWrite($name, $data) {
|
||||
$options = array('return' => Database::RETURN_AFFECTED) + $this->options;
|
||||
return (bool) $this->connection->merge($this->table, $options)
|
||||
->key('name', $name)
|
||||
|
@ -121,6 +141,60 @@ class DatabaseStorage implements StorageInterface {
|
|||
->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the config table exists and create it if not.
|
||||
*
|
||||
* @return bool
|
||||
* TRUE if the table was created, FALSE otherwise.
|
||||
*
|
||||
* @throws \Drupal\Core\Config\StorageException
|
||||
* If a database error occurs.
|
||||
*/
|
||||
protected function ensureTableExists() {
|
||||
try {
|
||||
if (!$this->connection->schema()->tableExists($this->table)) {
|
||||
$this->connection->schema()->createTable($this->table, static::schemaDefinition());
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
// If another process has already created the config table, attempting to
|
||||
// recreate it will throw an exception. In this case just catch the
|
||||
// exception and do nothing.
|
||||
catch (SchemaObjectExistsException $e) {
|
||||
return TRUE;
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
throw new StorageException($e->getMessage(), NULL, $e);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the schema for the configuration table.
|
||||
*/
|
||||
protected static function schemaDefinition() {
|
||||
$schema = array(
|
||||
'description' => 'The base table for configuration data.',
|
||||
'fields' => array(
|
||||
'name' => array(
|
||||
'description' => 'Primary Key: Unique config object name.',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'data' => array(
|
||||
'description' => 'A serialized configuration object data.',
|
||||
'type' => 'blob',
|
||||
'not null' => FALSE,
|
||||
'size' => 'big',
|
||||
),
|
||||
),
|
||||
'primary key' => array('name'),
|
||||
);
|
||||
return $schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\StorageInterface::delete().
|
||||
*
|
||||
|
@ -168,29 +242,31 @@ class DatabaseStorage implements StorageInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\StorageInterface::listAll().
|
||||
*
|
||||
* @throws PDOException
|
||||
* @throws \Drupal\Core\Database\DatabaseExceptionWrapper
|
||||
* Only thrown in case $this->options['throw_exception'] is TRUE.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function listAll($prefix = '') {
|
||||
return $this->connection->query('SELECT name FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name LIKE :name', array(
|
||||
':name' => db_like($prefix) . '%',
|
||||
), $this->options)->fetchCol();
|
||||
try {
|
||||
return $this->connection->query('SELECT name FROM {' . $this->connection->escapeTable($this->table) . '} WHERE name LIKE :name', array(
|
||||
':name' => $this->connection->escapeLike($prefix) . '%',
|
||||
), $this->options)->fetchCol();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\StorageInterface::deleteAll().
|
||||
*
|
||||
* @throws PDOException
|
||||
* @throws \Drupal\Core\Database\DatabaseExceptionWrapper
|
||||
* Only thrown in case $this->options['throw_exception'] is TRUE.
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function deleteAll($prefix = '') {
|
||||
$options = array('return' => Database::RETURN_AFFECTED) + $this->options;
|
||||
return (bool) $this->connection->delete($this->table, $options)
|
||||
->condition('name', $prefix . '%', 'LIKE')
|
||||
->execute();
|
||||
try {
|
||||
$options = array('return' => Database::RETURN_AFFECTED) + $this->options;
|
||||
return (bool) $this->connection->delete($this->table, $options)
|
||||
->condition('name', $prefix . '%', 'LIKE')
|
||||
->execute();
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,6 +68,18 @@ class FileStorage implements StorageInterface {
|
|||
return 'yml';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the directory exists and create it if not.
|
||||
*/
|
||||
protected function ensureStorage() {
|
||||
$success = file_prepare_directory($this->directory, FILE_CREATE_DIRECTORY | FILE_MODIFY_PERMISSIONS);
|
||||
$success = $success && file_save_htaccess($this->directory, TRUE, TRUE);
|
||||
if (!$success) {
|
||||
throw new StorageException("Failed to create config directory {$this->directory}");
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\StorageInterface::exists().
|
||||
*/
|
||||
|
@ -105,21 +117,23 @@ class FileStorage implements StorageInterface {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements Drupal\Core\Config\StorageInterface::write().
|
||||
*
|
||||
* @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException
|
||||
* @throws \Drupal\Core\Config\StorageException
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function write($name, array $data) {
|
||||
try {
|
||||
$data = $this->encode($data);
|
||||
}
|
||||
catch(DumpException $e) {
|
||||
throw new UnsupportedDataTypeConfigException(String::format('Invalid data type for used in config: @name', array('@name' => $name)));
|
||||
throw new StorageException(String::format('Invalid data type for used in config: @name', array('@name' => $name)));
|
||||
}
|
||||
|
||||
$target = $this->getFilePath($name);
|
||||
$status = @file_put_contents($target, $data);
|
||||
if ($status === FALSE) {
|
||||
// Try to make sure the directory exists and try witing again.
|
||||
$this->ensureStorage();
|
||||
$status = @file_put_contents($target, $data);
|
||||
}
|
||||
if ($status === FALSE) {
|
||||
throw new StorageException('Failed to write configuration file: ' . $this->getFilePath($name));
|
||||
}
|
||||
|
@ -213,7 +227,7 @@ class FileStorage implements StorageInterface {
|
|||
// glob() silently ignores the error of a non-existing search directory,
|
||||
// even with the GLOB_ERR flag.
|
||||
if (!file_exists($this->directory)) {
|
||||
throw new StorageException($this->directory . '/ not found.');
|
||||
return array();
|
||||
}
|
||||
$extension = '.' . static::getFileExtension();
|
||||
// \GlobIterator on Windows requires an absolute path.
|
||||
|
|
|
@ -131,6 +131,27 @@ abstract class StorableConfigBase extends ConfigBase {
|
|||
return $this->schemaWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the values are allowed data types.
|
||||
*
|
||||
* @throws UnsupportedDataTypeConfigException
|
||||
* If there is any invalid value.
|
||||
*/
|
||||
protected function validateValue($key, $value) {
|
||||
// Minimal validation. Should not try to serialize resources or non-arrays.
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $nested_value_key => $nested_value) {
|
||||
$this->validateValue($key . '.' . $nested_value_key, $nested_value);
|
||||
}
|
||||
}
|
||||
elseif ($value !== NULL && !is_scalar($value)) {
|
||||
throw new UnsupportedDataTypeConfigException(String::format('Invalid data type for config element @name:@key', array(
|
||||
'@name' => $this->getName(),
|
||||
'@key' => $key,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Casts the value to correct data type using the configuration schema.
|
||||
*
|
||||
|
|
|
@ -41,7 +41,7 @@ interface StorageInterface {
|
|||
/**
|
||||
* Reads configuration data from the storage.
|
||||
*
|
||||
* @param array $name
|
||||
* @param array $names
|
||||
* List of names of the configuration objects to load.
|
||||
*
|
||||
* @return array
|
||||
|
@ -60,6 +60,9 @@ interface StorageInterface {
|
|||
*
|
||||
* @return bool
|
||||
* TRUE on success, FALSE in case of an error.
|
||||
*
|
||||
* @throws \Drupal\Core\Config\StorageException
|
||||
* If the back-end storage does not exist and cannot be created.
|
||||
*/
|
||||
public function write($name, array $data);
|
||||
|
||||
|
|
|
@ -28,11 +28,11 @@ class UpdateServiceProvider implements ServiceProviderInterface, ServiceModifier
|
|||
$container
|
||||
->register('lock', 'Drupal\Core\Lock\NullLockBackend');
|
||||
|
||||
// Prevent config from accessing {cache_config}.
|
||||
// @see $conf['cache_classes'], update_prepare_d8_bootstrap()
|
||||
$container
|
||||
->register('config.storage', 'Drupal\Core\Config\FileStorage')
|
||||
->addArgument(config_get_config_directory(CONFIG_ACTIVE_DIRECTORY));
|
||||
// Prevent config from being accessed via a cache wrapper by removing
|
||||
// any existing definition and setting an alias to the actual storage.
|
||||
$container->removeDefinition('config.storage');
|
||||
$container->setAlias('config.storage', 'config.storage.active');
|
||||
|
||||
$container->register('module_handler', 'Drupal\Core\Extension\UpdateModuleHandler')
|
||||
->addArgument('%container.modules%');
|
||||
$container
|
||||
|
|
|
@ -12,6 +12,7 @@ use Drupal\Core\Config\ConfigManagerInterface;
|
|||
use Drupal\Core\Config\StorageInterface;
|
||||
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
|
||||
use Drupal\system\FileDownloadController;
|
||||
use Symfony\Component\Yaml\Dumper;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
|
||||
|
@ -81,13 +82,15 @@ class ConfigController implements ContainerInjectionInterface {
|
|||
* Downloads a tarball of the site configuration.
|
||||
*/
|
||||
public function downloadExport() {
|
||||
file_unmanaged_delete(file_directory_temp() . '/config.tar.gz');
|
||||
|
||||
$dumper = new Dumper();
|
||||
$dumper->setIndentation(2);
|
||||
|
||||
$archiver = new ArchiveTar(file_directory_temp() . '/config.tar.gz', 'gz');
|
||||
$config_dir = config_get_config_directory();
|
||||
$config_files = array();
|
||||
foreach (\Drupal::service('config.storage')->listAll() as $config_name) {
|
||||
$config_files[] = $config_dir . '/' . $config_name . '.yml';
|
||||
foreach (\Drupal::service('config.storage')->listAll() as $name) {
|
||||
$archiver->addString("$name.yml", $dumper->dump(\Drupal::config($name)->get(), PHP_INT_MAX, 0, TRUE));
|
||||
}
|
||||
$archiver->createModify($config_files, '', config_get_config_directory());
|
||||
|
||||
$request = new Request(array('file' => 'config.tar.gz'));
|
||||
return $this->fileDownloadController->download($request, 'temporary');
|
||||
|
|
|
@ -12,6 +12,7 @@ use Drupal\Core\Entity\EntityManagerInterface;
|
|||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Form\FormBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Yaml\Dumper;
|
||||
|
||||
/**
|
||||
* Provides a form for exporting a single configuration file.
|
||||
|
@ -32,6 +33,13 @@ class ConfigSingleExportForm extends FormBase {
|
|||
*/
|
||||
protected $configStorage;
|
||||
|
||||
/**
|
||||
* The YAML dumper.
|
||||
*
|
||||
* @var \Symfony\Component\Yaml\Dumper
|
||||
*/
|
||||
protected $dumper;
|
||||
|
||||
/**
|
||||
* Tracks the valid config entity type definitions.
|
||||
*
|
||||
|
@ -46,10 +54,14 @@ class ConfigSingleExportForm extends FormBase {
|
|||
* The entity manager.
|
||||
* @param \Drupal\Core\Config\StorageInterface $config_storage
|
||||
* The config storage.
|
||||
* @param \Symfony\Component\Yaml\Dumper $dumper
|
||||
* The yaml dumper.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage) {
|
||||
public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage, Dumper $dumper) {
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->configStorage = $config_storage;
|
||||
$this->dumper = $dumper;
|
||||
$this->dumper->setIndentation(2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -58,7 +70,8 @@ class ConfigSingleExportForm extends FormBase {
|
|||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('config.storage')
|
||||
$container->get('config.storage'),
|
||||
new Dumper()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -151,8 +164,7 @@ class ConfigSingleExportForm extends FormBase {
|
|||
$name = $form_state['values']['config_name'];
|
||||
}
|
||||
// Read the raw data for this config name, encode it, and display it.
|
||||
$data = $this->configStorage->read($name);
|
||||
$form['export']['#value'] = $this->configStorage->encode($data);
|
||||
$form['export']['#value'] = $this->dumper->dump($this->configStorage->read($name), PHP_INT_MAX);
|
||||
$form['export']['#description'] = $this->t('The filename is %name.', array('%name' => $name . '.yml'));
|
||||
return $form['export'];
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\Core\Config\StorageInterface;
|
|||
use Drupal\Core\Entity\EntityManagerInterface;
|
||||
use Drupal\Core\Form\ConfirmFormBase;
|
||||
use Symfony\Component\DependencyInjection\ContainerInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Provides a form for importing a single configuration file.
|
||||
|
@ -31,6 +32,13 @@ class ConfigSingleImportForm extends ConfirmFormBase {
|
|||
*/
|
||||
protected $configStorage;
|
||||
|
||||
/**
|
||||
* The YAML component.
|
||||
*
|
||||
* @var \Symfony\Component\Yaml\Yaml
|
||||
*/
|
||||
protected $yaml;
|
||||
|
||||
/**
|
||||
* If the config exists, this is that object. Otherwise, FALSE.
|
||||
*
|
||||
|
@ -52,10 +60,13 @@ class ConfigSingleImportForm extends ConfirmFormBase {
|
|||
* The entity manager.
|
||||
* @param \Drupal\Core\Config\StorageInterface $config_storage
|
||||
* The config storage.
|
||||
* @param \Symfony\Component\Yaml\Yaml $yaml
|
||||
* The YAML component.
|
||||
*/
|
||||
public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage) {
|
||||
public function __construct(EntityManagerInterface $entity_manager, StorageInterface $config_storage, Yaml $yaml) {
|
||||
$this->entityManager = $entity_manager;
|
||||
$this->configStorage = $config_storage;
|
||||
$this->yaml = $yaml;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,7 +75,8 @@ class ConfigSingleImportForm extends ConfirmFormBase {
|
|||
public static function create(ContainerInterface $container) {
|
||||
return new static(
|
||||
$container->get('entity.manager'),
|
||||
$container->get('config.storage')
|
||||
$container->get('config.storage'),
|
||||
new Yaml()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -184,7 +196,7 @@ class ConfigSingleImportForm extends ConfirmFormBase {
|
|||
}
|
||||
|
||||
// Decode the submitted import.
|
||||
$data = $this->configStorage->decode($form_state['values']['import']);
|
||||
$data = $this->yaml->parse($form_state['values']['import']);
|
||||
|
||||
// Validate for config entities.
|
||||
if ($form_state['values']['config_type'] !== 'system.simple') {
|
||||
|
|
|
@ -11,6 +11,7 @@ use Drupal\Component\Utility\String;
|
|||
use Drupal\Core\Config\ConfigNameException;
|
||||
use Drupal\simpletest\DrupalUnitTestBase;
|
||||
use Drupal\Core\Config\FileStorage;
|
||||
use Drupal\Core\Config\DatabaseStorage;
|
||||
use Drupal\Core\Config\UnsupportedDataTypeConfigException;
|
||||
|
||||
/**
|
||||
|
@ -192,10 +193,10 @@ class ConfigCRUDTest extends DrupalUnitTestBase {
|
|||
*/
|
||||
public function testDataTypes() {
|
||||
\Drupal::moduleHandler()->install(array('config_test'));
|
||||
$storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
|
||||
$storage = new DatabaseStorage($this->container->get('database'), 'config');
|
||||
$name = 'config_test.types';
|
||||
$config = $this->container->get('config.factory')->get($name);
|
||||
$original_content = file_get_contents($storage->getFilePath($name));
|
||||
$original_content = file_get_contents(drupal_get_path('module', 'config_test') . "/config/$name.yml");
|
||||
$this->verbose('<pre>' . $original_content . "\n" . var_export($storage->read($name), TRUE));
|
||||
|
||||
// Verify variable data types are intact.
|
||||
|
@ -220,7 +221,7 @@ class ConfigCRUDTest extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($config->get(), $data);
|
||||
// Assert the data against the file storage.
|
||||
$this->assertIdentical($storage->read($name), $data);
|
||||
$this->verbose('<pre>' . file_get_contents($storage->getFilePath($name)) . var_export($storage->read($name), TRUE));
|
||||
$this->verbose('<pre>' . $name . var_export($storage->read($name), TRUE));
|
||||
|
||||
// Set data using config::setData().
|
||||
$config->setData($data)->save();
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
namespace Drupal\config\Tests;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
use Symfony\Component\Yaml\Dumper;
|
||||
|
||||
/**
|
||||
* Tests the user interface for importing/exporting a single configuration.
|
||||
|
@ -22,6 +22,13 @@ class ConfigSingleImportExportTest extends WebTestBase {
|
|||
*/
|
||||
public static $modules = array('config', 'config_test');
|
||||
|
||||
/**
|
||||
* The YAML dumper.
|
||||
*
|
||||
* @var \Symfony\Component\Yaml\Dumper
|
||||
*/
|
||||
protected $dumper;
|
||||
|
||||
public static function getInfo() {
|
||||
return array(
|
||||
'name' => 'Configuration Single Import/Export UI',
|
||||
|
@ -30,6 +37,15 @@ class ConfigSingleImportExportTest extends WebTestBase {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->dumper = new Dumper();
|
||||
$this->dumper->setIndentation(2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests importing a single configuration file.
|
||||
*/
|
||||
|
@ -111,12 +127,11 @@ EOD;
|
|||
*/
|
||||
public function testImportSimpleConfiguration() {
|
||||
$this->drupalLogin($this->drupalCreateUser(array('import configuration')));
|
||||
$yaml = new Yaml();
|
||||
$config = \Drupal::config('system.site')->set('name', 'Test simple import');
|
||||
$edit = array(
|
||||
'config_type' => 'system.simple',
|
||||
'config_name' => $config->getName(),
|
||||
'import' => $yaml->dump($config->get()),
|
||||
'import' => $this->dumper->dump($config->get(), PHP_INT_MAX),
|
||||
);
|
||||
$this->drupalPostForm('admin/config/development/configuration/single/import', $edit, t('Import'));
|
||||
$this->assertRaw(t('Are you sure you want to update the %name @type?', array('%name' => $config->getName(), '@type' => 'simple configuration')));
|
||||
|
@ -152,7 +167,7 @@ EOD;
|
|||
$this->assertFieldByXPath('//select[@name="config_name"]//option[@selected="selected"]', t('Fallback date format'), 'The fallback date format config entity is selected when specified in the URL.');
|
||||
|
||||
$fallback_date = \Drupal::entityManager()->getStorage('date_format')->load('fallback');
|
||||
$data = \Drupal::service('config.storage')->encode($fallback_date->toArray());
|
||||
$data = $this->dumper->dump($fallback_date->toArray(), PHP_INT_MAX);
|
||||
$this->assertFieldByXPath('//textarea[@name="export"]', $data, 'The fallback date format config entity export code is displayed.');
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,10 @@ abstract class ConfigStorageTestBase extends DrupalUnitTestBase {
|
|||
$result = $this->invalidStorage->read($name);
|
||||
$this->assertIdentical($result, FALSE);
|
||||
|
||||
// Listing on a non-existing storage bin returns an empty array.
|
||||
$result = $this->invalidStorage->listAll();
|
||||
$this->assertIdentical($result, array());
|
||||
|
||||
// Deleting all names with prefix deletes the appropriate data and returns
|
||||
// TRUE.
|
||||
$files = array(
|
||||
|
@ -111,15 +115,6 @@ abstract class ConfigStorageTestBase extends DrupalUnitTestBase {
|
|||
$this->assertIdentical($result, TRUE);
|
||||
$this->assertIdentical($names, array());
|
||||
|
||||
// Writing to a non-existing storage bin throws an exception.
|
||||
try {
|
||||
$this->invalidStorage->write($name, array('foo' => 'bar'));
|
||||
$this->fail('Exception not thrown upon writing to a non-existing storage bin.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$class = get_class($e);
|
||||
$this->pass($class . ' thrown upon writing to a non-existing storage bin.');
|
||||
}
|
||||
|
||||
// Deleting from a non-existing storage bin throws an exception.
|
||||
try {
|
||||
|
@ -131,16 +126,6 @@ abstract class ConfigStorageTestBase extends DrupalUnitTestBase {
|
|||
$this->pass($class . ' thrown upon deleting from a non-existing storage bin.');
|
||||
}
|
||||
|
||||
// Listing on a non-existing storage bin throws an exception.
|
||||
try {
|
||||
$this->invalidStorage->listAll();
|
||||
$this->fail('Exception not thrown upon listing from a non-existing storage bin.');
|
||||
}
|
||||
catch (\Exception $e) {
|
||||
$class = get_class($e);
|
||||
$this->pass($class . ' thrown upon listing from a non-existing storage bin.');
|
||||
}
|
||||
|
||||
// Test renaming an object that does not exist throws an exception.
|
||||
try {
|
||||
$this->storage->rename('config_test.storage_does_not_exist', 'config_test.storage_does_not_exist_rename');
|
||||
|
@ -159,6 +144,10 @@ abstract class ConfigStorageTestBase extends DrupalUnitTestBase {
|
|||
$this->pass($class . ' thrown upon renaming a nonexistent storage bin.');
|
||||
}
|
||||
|
||||
// Writing to a non-existing storage bin creates the bin.
|
||||
$this->invalidStorage->write($name, array('foo' => 'bar'));
|
||||
$result = $this->invalidStorage->read($name);
|
||||
$this->assertIdentical($result, array('foo' => 'bar'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
/**
|
||||
* @file
|
||||
* Definition of Drupal\config\Tests\Storage\DatabaseStorageTest.
|
||||
* Contains \Drupal\config\Tests\Storage\DatabaseStorageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\config\Tests\Storage;
|
||||
|
@ -24,28 +24,6 @@ class DatabaseStorageTest extends ConfigStorageTestBase {
|
|||
function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$schema['config'] = array(
|
||||
'description' => 'Database storage for the configuration system.',
|
||||
'fields' => array(
|
||||
'name' => array(
|
||||
'description' => 'The identifier for the configuration entry, such as module.example (the name of the file, minus the file extension).',
|
||||
'type' => 'varchar',
|
||||
'length' => 255,
|
||||
'not null' => TRUE,
|
||||
'default' => '',
|
||||
),
|
||||
'data' => array(
|
||||
'description' => 'The raw data for this configuration entry.',
|
||||
'type' => 'blob',
|
||||
'not null' => TRUE,
|
||||
'size' => 'big',
|
||||
'translatable' => TRUE,
|
||||
),
|
||||
),
|
||||
'primary key' => array('name'),
|
||||
);
|
||||
db_create_table('config', $schema['config']);
|
||||
|
||||
$this->storage = new DatabaseStorage($this->container->get('database'), 'config');
|
||||
$this->invalidStorage = new DatabaseStorage($this->container->get('database'), 'invalid');
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ class FileStorageTest extends ConfigStorageTestBase {
|
|||
|
||||
// FileStorage::listAll() requires other configuration data to exist.
|
||||
$this->storage->write('system.performance', \Drupal::config('system.performance')->get());
|
||||
$this->storage->write('core.extension', array('module' => array()));
|
||||
}
|
||||
|
||||
protected function read($name) {
|
||||
|
|
|
@ -368,7 +368,6 @@ class ConfigTranslationUiTest extends WebTestBase {
|
|||
*/
|
||||
public function testDateFormatTranslation() {
|
||||
$this->drupalLogin($this->admin_user);
|
||||
$file_storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
|
||||
|
||||
$this->drupalGet('admin/config/regional/date-time');
|
||||
|
||||
|
|
|
@ -229,8 +229,9 @@ abstract class DrupalUnitTestBase extends UnitTestBase {
|
|||
$container->register('cache_factory', 'Drupal\Core\Cache\MemoryBackendFactory');
|
||||
|
||||
$container
|
||||
->register('config.storage', 'Drupal\Core\Config\FileStorage')
|
||||
->addArgument($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
|
||||
->register('config.storage.active', 'Drupal\Core\Config\DatabaseStorage')
|
||||
->addArgument(Database::getConnection())
|
||||
->addArgument('config');
|
||||
|
||||
$this->settingsSet('keyvalue_default', 'keyvalue.memory');
|
||||
$container->set('keyvalue.memory', $this->keyValueFactory);
|
||||
|
|
|
@ -600,6 +600,19 @@ ini_set('session.cookie_lifetime', 2000000);
|
|||
*/
|
||||
# $cookie_domain = '.example.com';
|
||||
|
||||
/**
|
||||
* Active configuration settings.
|
||||
*
|
||||
* By default, the active configuration is stored in the database in the
|
||||
* {config} table. To install Drupal with a different active configuration
|
||||
* storage, you need to override the setting here, in addition to overriding
|
||||
* the config.storage.active service definition in a module or profile.
|
||||
*
|
||||
* The 'bootstrap_config_storage' setting needs to be a callable that returns
|
||||
* core.services.yml.
|
||||
*/
|
||||
# $settings['bootstrap_config_storage'] = array('Drupal\Core\Config\BootstrapConfigStorageFactory', 'getFileStorage');
|
||||
|
||||
/**
|
||||
* Configuration overrides.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue