diff --git a/core/includes/config.inc b/core/includes/config.inc index fe1cd79..9439bf8 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -109,8 +109,8 @@ function config_sync() { } try { - config_sync_invoke_sync_hooks($config_changes); - config_sync_save_changes($config_changes); + $remaining_changes = config_sync_invoke_sync_hooks($config_changes); + config_sync_save_changes($remaining_changes); // Flush all caches and reset static variables after a successful import. drupal_flush_all_caches(); } @@ -131,9 +131,9 @@ function config_sync() { * An array of changes to be written. */ function config_sync_save_changes(array $config_changes) { - foreach (array('new', 'changed', 'deleted') as $type) { - foreach ($config_changes[$type] as $name) { - if ($type == 'deleted') { + foreach (array('delete', 'create', 'change') as $op) { + foreach ($config_changes[$op] as $name) { + if ($op == 'delete') { config($name)->delete(); } else { @@ -154,9 +154,6 @@ function config_sync_save_changes(array $config_changes) { * An array of changes to be loaded. */ function config_sync_invoke_sync_hooks(array $config_changes) { - // @todo Lock writes to all stores to prevent other/unintended config - // changes from happening during the import. - $target_storage = new DrupalConfig(new DatabaseStorage(NULL)); $source_storage = new DrupalConfig(new FileStorage(NULL)); @@ -166,11 +163,30 @@ function config_sync_invoke_sync_hooks(array $config_changes) { // supposed to change the configuration that is to be imported. module_invoke_all('config_sync_validate', $config_changes, $target_storage, $source_storage); - // Allow modules to react to the configuration changes. - // Note: module_invoke_all() can only be used as long as it does not allow - // implementations to take $config_changes by reference, since they are not - // supposed to change the configuration that is to be imported. - module_invoke_all('config_sync', $config_changes, $target_storage, $source_storage); + // Allow modules to take over configuration change operations for + // higher-level configuration data. + // First pass deleted, then new, and lastly changed configuration, in order to + // handle dependencies correctly. + $remaining_changes = $config_changes; + foreach (array('delete', 'create', 'change') as $op) { + foreach ($remaining_changes[$op] as $key => $name) { + // Extract owner from configuration object name. + $module = strtok($name, '.'); + // Check whether the module implements Attempt to pass + $handled_by_module = FALSE; + if (module_hook($module, 'config_sync')) { + $old_config = new DrupalConfig(new DatabaseStorage($name)); + $new_config = new DrupalConfig(new FileStorage($name)); + // @todo Passing $name is obsolete as soon as there is a proper ConfigObject. + $handled_by_module = module_invoke($module, 'config_sync', $op, $name, $new_config, $old_config); + } + if (!empty($handled_by_module)) { + unset($remaining_changes[$op][$key]); + } + } + } + + return $remaining_changes; } /** @@ -212,21 +228,21 @@ function config_sync_get_changes() { $disk_config_names = FileStorage::getNamesWithPrefix(); $active_config_names = DatabaseStorage::getNamesWithPrefix(); $config_changes = array( - 'new' => array_diff($disk_config_names, $active_config_names), - 'changed' => array(), - 'deleted' => array_diff($active_config_names, $disk_config_names), + 'create' => array_diff($disk_config_names, $active_config_names), + 'change' => array(), + 'delete' => array_diff($active_config_names, $disk_config_names), ); foreach (array_intersect($disk_config_names, $active_config_names) as $name) { $active_config = config($name); $file_config = new DrupalConfig(new FileStorage($name)); if ($active_config->get() != $file_config->get()) { - $config_changes['changed'][] = $name; + $config_changes['change'][] = $name; } } // Do not trigger subsequent synchronization operations if there are no // changes in either category. - if (empty($config_changes['new']) && empty($config_changes['changed']) && empty($config_changes['deleted'])) { + if (empty($config_changes['create']) && empty($config_changes['change']) && empty($config_changes['delete'])) { return FALSE; } diff --git a/core/modules/image/image.module b/core/modules/image/image.module index c159a47..767768b 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -497,40 +497,30 @@ function image_path_flush($path) { /** * Implements hook_config_sync(). */ -function image_config_sync($config_changes, $target_storage, $source_storage) { - foreach ($config_changes['new'] as $file_name) { - if (strpos($file_name, 'image.style.') === 0) { - $style = $source_storage->load($file_name)->get(); - $style['is_new'] = TRUE; - module_invoke_all('image_style_save', $style); - image_style_flush($style); - } +function image_config_sync($op, $name, $new_config, $old_config) { + // Only image styles require custom handling. Any other module settings can be + // synchronized directly. + if (strpos($name, 'image.style.') !== 0) { + return FALSE; } - foreach ($config_changes['changed'] as $file_name) { - if (strpos($file_name, 'image.style.') === 0) { - $style = $source_storage->load($file_name)->get(); - $style['is_new'] = FALSE; - module_invoke_all('image_style_save', $style); - image_style_flush($style); - } + + if ($op == 'delete') { + // @todo image_style_delete() supports the notion of a "replacement style" + // to be used by other modules instead of the deleted style. Good idea. + // But squeezing that into a "delete" operation is the worst idea ever. + // Regardless of Image module insanity, add a 'replaced' stack to + // config_sync()? And how can that work? If an 'old_ID' key would be a + // standard, wouldn't this belong into 'changed' instead? + $style = $old_config->get(); + return image_style_delete($style); } - foreach ($config_changes['deleted'] as $file_name) { - if (strpos($file_name, 'image.style.') === 0) { - // The style has been deleted, so read the previous configuration from the - // old storage. - $style = $target_storage->load($file_name)->get(); - image_style_flush($style); - - // @todo image_style_delete() supports the notion of a "replacement style" - // to be used by other modules instead of the deleted style. Good idea. - // But squeezing that into a "delete" operation is the worst idea ever. - // Regardless of Image module insanity, add a 'replaced' stack to - // config_sync()? And how can that work? If an 'old_ID' key would be a - // standard, wouldn't this belong into 'changed' instead? - $style['old_name'] = $style['name']; - $style['name'] = ''; - module_invoke_all('image_style_delete', $style); - } + if ($op == 'new') { + $style = $new_config->get(); + return image_style_save($style); + } + if ($op == 'change') { + $style = $new_config->get(); + return image_style_save($style); } }