diff --git a/core/includes/config.inc b/core/includes/config.inc
index 8c2772b..429ced7 100644
--- a/core/includes/config.inc
+++ b/core/includes/config.inc
@@ -1,6 +1,14 @@
");
+ $xml_object = new SimpleXMLElement("");
config_array_to_xml($data, $xml_object);
// Pretty print the result.
@@ -240,3 +251,208 @@ function config_array_to_xml($array, &$xml_object) {
}
}
}
+
+/**
+ * Reload config from disc and write new settings to the active store.
+ */
+function config_reload() {
+ $config_changes = config_get_changes_from_disc();
+ if (empty($config_changes)) {
+ return;
+ }
+
+ $lock_attempts = 0;
+ $lock_acquired = FALSE;
+ do {
+ if (!$lock_acquired = lock_acquire(__FUNCTION__, 5)) {
+ lock_wait(__FUNCTION__, 5);
+ continue;
+ }
+
+ try {
+ config_reload_run_hooks($config_changes);
+ config_reload_save_changes($config_changes);
+ }
+ catch (ConfigException $e) {
+ watchdog_exception('config_reload', $e);
+ config_reload_run_error_hooks($config_changes);
+ lock_release(__FUNCTION__);
+ return;
+ }
+ } while ($lock_acquired === FALSE && ++$lock_attempts < 5);
+
+ if ($lock_acquired) {
+ lock_release(__FUNCTION__);
+ return TRUE;
+ }
+ else {
+ watchdog('config_reload', 'Failed to get lock while trying to reload config.');
+ return FALSE;
+ }
+}
+
+/**
+ * Writes an array of config file changes to the active store.
+ *
+ * @param $config_changes
+ * An array of changes to be written.
+ */
+function config_reload_save_changes($config_changes) {
+ foreach (array('new', 'changed', 'deleted') as $type) {
+ foreach ($config_changes[$type] as $name) {
+ if ($type == 'deleted') {
+ config($name)->delete();
+ }
+ else {
+ // Get the active store object, set the new data from file, then
+ // save, which will also update the .sig file.
+ $active_store_config = config($name);
+ $active_store_config->setData(config($name, 'Drupal\Core\Config\DrupalConfigFile')->get());
+ $active_store_config->save();
+ }
+ }
+ }
+}
+
+/**
+ * Runs hook_config_reload_validate() and hook_config_reload() implementations.
+ *
+ * @param $config_changes
+ * An array of changes to be loaded.
+ */
+function config_reload_run_hooks($config_changes) {
+ $active_config_tree = config_tree();
+ $file_config_tree = config_tree('Drupal\Core\Config\DrupalConfigFile');
+
+ foreach (config_sort_module_reload_dependencies(module_implements('config_reload_validate')) as $module) {
+ $function = $module . '_config_reload_validate';
+ $function($config_changes, $active_config_tree, $file_config_tree);
+ }
+
+ // We allow modules to signal that they would like to be rerun after all
+ // other modules by returning CONFIG_DEFER_RELOAD. Loop until there are no
+ // modules left that indicate they would like to be rerun, checking that we're
+ // not stuck rerunning the same list of modules over and over at each cycle.
+ $modules = config_sort_module_reload_dependencies(module_implements('config_reload'));
+ do {
+ $initial_module_list = $modules;
+ $modules = array();
+ foreach ($initial_module_list as $module) {
+ $function = $module . '_config_reload';
+ if ($function($config_changes, $file_config_tree, $active_config_tree) === CONFIG_DEFER_RELOAD) {
+ $modules[] = $module;
+ }
+ }
+ } while ($modules && $modules != $initial_module_list);
+
+ // If there are modules left that haven't run their reload hook, then we hit
+ // an infinite loop.
+ if ($modules) {
+ throw new ConfigException("Dependency loop detected while reloading configuration from disc:");
+ }
+}
+
+/**
+ * Runs hook_config_reload_error() implementations.
+ *
+ * During a reload run, modules may make changes that cannot be rolled back.
+ * This hook allows modules to react to an error that occurs after they have
+ * made such changes, and make sure that the state of configuration in the
+ * active store is correct.
+ *
+ * @param $config_changes
+ * An array of changes to be loaded.
+ */
+function config_reload_run_error_hooks($config_changes) {
+ $active_config_tree = config_tree();
+ $file_config_tree = config_tree('Drupal\Core\Config\DrupalConfigFile');
+
+ $modules = config_sort_module_reload_dependencies(module_implements('config_reload_error'));
+ foreach ($modules as $module) {
+ $function = $module . '_config_reload_error';
+ try {
+ $function($config_changes, $active_config_tree, $file_config_tree);
+ }
+ catch (ConfigException $e) {
+ // Just keep going, because we need to allow all modules to react even if
+ // some of them are behaving badly.
+ }
+ }
+}
+
+/**
+ * Load all the config names! From disc.
+ */
+function config_get_names_from_disc() {
+ $config_names = array();
+ foreach (glob(config_get_config_directory() . '/' . '*.xml') as $key => $file) {
+ $parts = explode('/', $file);
+ $file = array_pop($parts);
+ $name = str_replace('.xml', '', $file);
+ $config_names[] = $name;
+ }
+ return $config_names;
+}
+
+/**
+ * Returns a DrupalConfigTree object with the given storage backend.
+ *
+ * @param $storage_class
+ * A storage class.
+ * @return
+ * A DrupalConfigTree object.
+ */
+function config_tree($storage_class = NULL) {
+ if ($storage_class === NULL) {
+ $storage_class = variable_get('config_default_storage', 'Drupal\Core\Config\DrupalVerifiedStorageSQL');
+ }
+ return new DrupalConfigTree($storage_class);
+}
+
+/**
+ * Sort the given list of modules based on dependency.
+ *
+ * @param $modules
+ * A list of modules.
+ * @return
+ * The list of modules sorted by dependency.
+ */
+function config_sort_module_reload_dependencies($modules) {
+ // Get all module data so we can find find weights and sort.
+ $module_data = system_rebuild_module_data();
+
+ $sorted_modules = array();
+ foreach ($modules as $module) {
+ $sorted_modules[$module] = $module_data[$module]->sort;
+ }
+ arsort($sorted_modules);
+ return array_keys($sorted_modules);
+}
+
+/**
+ * Returns a list of changes on disc compared to the active store.
+ *
+ * @return
+ * The list of files changed on disc compared to the active store.
+ */
+function config_get_changes_from_disc() {
+ $disc_config_names = config_get_names_from_disc();
+ $active_config_names = config_get_verified_storage_names_with_prefix();
+ $config_changes = array(
+ 'new' => array_diff($disc_config_names, $active_config_names),
+ 'changed' => array(),
+ 'deleted' => array_diff($active_config_names, $disc_config_names),
+ );
+ foreach (array_intersect($disc_config_names, $active_config_names) as $name) {
+ $active_config = config($name);
+ $file_config = config($name, 'Drupal\Core\Config\DrupalConfigFile');
+ if ($active_config->get() != $file_config->get()) {
+ $config_changes['changed'][] = $name;
+ }
+ }
+ if (empty($config_changes['new']) && empty($config_changes['changed']) && empty($config_changes['deleted'])) {
+ return FALSE;
+ }
+ return $config_changes;
+}
+
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 07e25a0..941b667 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1035,7 +1035,7 @@ function install_settings_form_submit($form, &$form_state) {
$config_path = conf_path() . '/files/' . $settings['config_directory_name']['value'];
if (!file_prepare_directory($config_path, FILE_CREATE_DIRECTORY)) {
// How best to handle errors here?
- };
+ }
// Write out a .htaccess file that will protect the config directory from
// prying eyes.
diff --git a/core/includes/module.inc b/core/includes/module.inc
index 60c4035..4243cce 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -463,7 +463,7 @@ function module_enable($module_list, $enable_dependencies = TRUE) {
$versions = drupal_get_schema_versions($module);
$version = $versions ? max($versions) : SCHEMA_INSTALLED;
- // Copy any default configuration data to the system config directory/
+ // Copy any default configuration data to the active store.
config_install_default_config($module);
// If the module has no current updates, but has some that were
diff --git a/core/lib/Drupal/Core/Config/DrupalConfig.php b/core/lib/Drupal/Core/Config/DrupalConfig.php
index 54397e7..6207daa 100644
--- a/core/lib/Drupal/Core/Config/DrupalConfig.php
+++ b/core/lib/Drupal/Core/Config/DrupalConfig.php
@@ -97,6 +97,10 @@ class DrupalConfig {
}
}
+ public function setData(array $data) {
+ $this->data = $data;
+ }
+
/**
* Sets value in this config object.
*
diff --git a/core/lib/Drupal/Core/Config/DrupalConfigFile.php b/core/lib/Drupal/Core/Config/DrupalConfigFile.php
new file mode 100644
index 0000000..763ee7d
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/DrupalConfigFile.php
@@ -0,0 +1,144 @@
+name = $name;
+ }
+
+ /**
+ * Checks whether the XML configuration file already exists on disk.
+ *
+ * @return
+ * Boolean based on file's existence.
+ */
+ protected function exists() {
+ return file_exists($this->getFilePath());
+ }
+
+ /**
+ * Returns the path to the XML configuration file.
+ *
+ * @return
+ * @todo
+ */
+ public function getFilePath() {
+ return config_get_config_directory() . '/' . $this->name . '.xml';
+ }
+
+ /**
+ * Writes the contents of the configuration file to disk.
+ *
+ * @param $data
+ * The data to be written to the file.
+ *
+ * @throws
+ * Exception
+ */
+ public function write($data) {
+ if (!file_put_contents($this->getFilePath(), $data)) {
+ throw new \Exception('Failed to write configuration file: ' . $this->getFilePath());
+ }
+ }
+
+ /**
+ * Returns the contents of the configuration file.
+ *
+ * @return
+ * @todo
+ */
+ public function read() {
+ if ($this->exists()) {
+ return file_get_contents($this->getFilePath());
+ }
+ throw new \Exception('Failed to read configuration file: ' . $this->getFilePath());
+ }
+
+ /**
+ * Deletes a configuration file.
+ */
+ public function delete() {
+ return @drupal_unlink($this->getFilePath());
+ }
+
+ /**
+ * Copies the configuration data from the verified storage into a file.
+ */
+ public function copyToFile() {
+ // TODO: no-op to keep the interface happy.
+ }
+
+ /**
+ * Copies the configuration data from the file into the verified storage.
+ */
+ public function copyFromFile() {
+ // TODO: no-op to keep the interface happy.
+ }
+
+ /**
+ * Deletes the configuration data file.
+ */
+ public function deleteFile() {
+ return $this->delete();
+ }
+
+ /**
+ * Checks whether the file and the verified storage is in sync.
+ *
+ * @return
+ * TRUE if the file and the verified storage contains the same data, FALSE
+ * if not.
+ */
+ public function isOutOfSync() {
+ return FALSE;
+ }
+
+ /**
+ * Writes the configuration data into the active storage but not the file.
+ *
+ * @param $data
+ * The configuration data to write into active storage.
+ */
+ public function writeToActive($data) {
+ // TODO: no-op to keep the interface happy.
+ }
+
+ /**
+ * Writes the configuration data into the file.
+ *
+ * @param $data
+ * The configuration data to write into the file.
+ */
+ public function writeToFile($data) {
+ return $this->write($data);
+ }
+
+ /**
+ * Gets names starting with this prefix.
+ *
+ * @param $prefix
+ * The prefix of the files we are searching for.
+ *
+ * @return
+ * An array of file names under a branch.
+ *
+ * @see config_get_signed_file_storage_names_with_prefix()
+ */
+ public static function getNamesWithPrefix($prefix) {
+ return config_get_signed_file_storage_names_with_prefix($prefix);
+ }
+}
+
diff --git a/core/lib/Drupal/Core/Config/DrupalConfigTree.php b/core/lib/Drupal/Core/Config/DrupalConfigTree.php
new file mode 100644
index 0000000..0d551ac
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/DrupalConfigTree.php
@@ -0,0 +1,18 @@
+storage_class = $storage_class;
+ }
+
+ public function get($name) {
+ return config($name, $this->storage_class);
+ }
+
+}
+
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index bcccbf7..8b2d9f2 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -527,6 +527,36 @@ function image_style_save($style) {
}
/**
+ * Implements hook_config_reload().
+ */
+function image_config_reload($config_changes, $files_config_tree, $active_config_tree) {
+ foreach ($config_changes['new'] as $file_name) {
+ if (strpos($file_name, 'image.styles.') === 0) {
+ $style = $files_config_tree->get($file_name)->get();
+ $style['is_new'] = TRUE;
+ module_invoke_all('image_style_save', $style);
+ image_style_flush($style);
+ }
+ }
+ foreach ($config_changes['changed'] as $file_name) {
+ if (strpos($file_name, 'image.styles.') === 0) {
+ $style = $files_config_tree->get($file_name)->get();
+ $style['is_new'] = FALSE;
+ module_invoke_all('image_style_save', $style);
+ image_style_flush($style);
+ }
+ }
+ foreach ($config_changes['deleted'] as $file_name) {
+ if (strpos($file_name, 'image.styles.') === 0) {
+ image_style_flush($style);
+ $style['old_name'] = $style['name'];
+ $style['name'] = '';
+ module_invoke_all('image_style_delete', $style);
+ }
+ }
+}
+
+/**
* Delete an image style.
*
* @param $style
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 4d75dca..babaf68 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -3203,3 +3203,41 @@ function system_actions_remove_orphans() {
actions_synchronize(TRUE);
drupal_goto('admin/config/system/actions/manage');
}
+
+/**
+ * Reload config from disc form.
+ */
+function system_admin_config_reload_form($form, &$form_state) {
+ $config_changes = config_get_changes_from_disc();
+ if ($config_changes['new'] || $config_changes['changed'] || $config_changes['deleted']) {
+ return array(
+ 'changed_files' => array(
+ '#markup' => '
' . print_r($config_changes, TRUE) . '
',
+ ),
+ 'reload' => array(
+ '#type' => 'submit',
+ '#value' => 'Reload config from disc',
+ ),
+ );
+ }
+ else {
+ return array(
+ 'no_changes' => array(
+ '#markup' => 'There are no changes on disc to reload.'
+ ),
+ );
+ }
+}
+
+/**
+ * Reload config from disc form submit handler.
+ */
+function system_admin_config_reload_form_submit($form, &$form_state) {
+ if (config_reload()) {
+ drupal_set_message('Configuration successfully reloaded from disc.');
+ }
+ else {
+ drupal_set_message('There was an error reloading configuration from disc.', 'error');
+ }
+}
+
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 29c56c5..993b3e5 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -745,6 +745,18 @@ function system_menu() {
'file' => 'system.admin.inc',
);
+ // Config system reload.
+ $items['admin/config/config/reload'] = array(
+ 'title' => 'Configuration reload',
+ 'description' => 'Reload configuration from disc.',
+ 'position' => 'left',
+ 'weight' => -10,
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('system_admin_config_reload_form'),
+ 'access arguments' => array('reload configuration from disc'),
+ 'file' => 'system.admin.inc',
+ );
+
// Media settings.
$items['admin/config/media'] = array(
'title' => 'Media',