diff --git a/core/includes/config.inc b/core/includes/config.inc index 1e638ca..51476b7 100644 --- a/core/includes/config.inc +++ b/core/includes/config.inc @@ -154,7 +154,7 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou // Check whether the module implements hook_config_import() and ask it to // handle the configuration change. $handled_by_module = FALSE; - if (module_hook($module, 'config_import')) { + if (module_hook($module, 'config_import_' . $op)) { $old_config = new Config($storage_dispatcher); $old_config ->setName($name) @@ -167,7 +167,7 @@ function config_import_invoke_owner(array $config_changes, StorageInterface $sou $new_config->setData($data); } - $handled_by_module = module_invoke($module, 'config_import', $op, $name, $new_config, $old_config); + $handled_by_module = module_invoke($module, 'config_import_' . $op, $name, $new_config, $old_config); } if (!empty($handled_by_module)) { unset($config_changes[$op][$key]); diff --git a/core/lib/Drupal/Core/Config/NullStorage.php b/core/lib/Drupal/Core/Config/NullStorage.php index 54554c9..fede4f0 100644 --- a/core/lib/Drupal/Core/Config/NullStorage.php +++ b/core/lib/Drupal/Core/Config/NullStorage.php @@ -8,7 +8,18 @@ namespace Drupal\Core\Config; /** - * Defines the Null storage controller. + * Defines a stub storage controller. + * + * This storage is always empty; the controller reads and writes nothing. + * + * The stub implementation is needed for synchronizing configuration during + * installation of a module, in which case all configuration being shipped with + * the module is known to be new. Therefore, the module installation process is + * able to short-circuit the full diff against the active store; the diff would + * yield all currently available configuration as items to remove, since they do + * not exist in the module's default configuration directory. + * + * This also can be used for testing purposes. */ class NullStorage implements StorageInterface { /** diff --git a/core/modules/config/config.api.php b/core/modules/config/config.api.php index 8cfa336..f0c3afa 100644 --- a/core/modules/config/config.api.php +++ b/core/modules/config/config.api.php @@ -13,19 +13,16 @@ */ /** - * Synchronize configuration changes. + * Create configuration upon synchronizing configuration changes. * * This callback is invoked when configuration is synchronized between storages * and allows a module to take over the synchronization of configuration data. * * Modules should implement this callback if they manage configuration data - * (such as image styles, node types, or fields), which needs to be + * (such as image styles, node types, or fields) which needs to be * prepared and passed through module API functions to properly handle a * configuration change. * - * @param string $op - * The operation to perform for the configuration data; one of 'create', - * 'delete', or 'change'. * @param string $name * The name of the configuration object. * @param Drupal\Core\Config\Config $new_config @@ -33,31 +30,78 @@ * @param Drupal\Core\Config\Config $old_config * A configuration object containing the old configuration data. */ -function MODULE_config_import($op, $name, $new_config, $old_config) { +function MODULE_config_import_create($name, $new_config, $old_config) { // Only configurable thingies require custom handling. Any other module // settings can be synchronized directly. if (strpos($name, 'config_test.dynamic.') !== 0) { return FALSE; } + $config_test = new ConfigTest($new_config); + $config_test->save(); + return TRUE; +} - 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. Essential! - // But that is impossible currently, since the config system only knows - // about deleted and added changes. Introduce an 'old_ID' key within - // config objects as a standard? - $config_test = new ConfigTest($old_config); - $config_test->delete(); - } - if ($op == 'create') { - $config_test = new ConfigTest($new_config); - $config_test->save(); +/** + * Update configuration upon synchronizing configuration changes. + * + * This callback is invoked when configuration is synchronized between storages + * and allows a module to take over the synchronization of configuration data. + * + * Modules should implement this callback if they manage configuration data + * (such as image styles, node types, or fields) which needs to be + * prepared and passed through module API functions to properly handle a + * configuration change. + * + * @param string $name + * The name of the configuration object. + * @param Drupal\Core\Config\Config $new_config + * A configuration object containing the new configuration data. + * @param Drupal\Core\Config\Config $old_config + * A configuration object containing the old configuration data. + */ +function MODULE_config_import_change($name, $new_config, $old_config) { + // Only configurable thingies require custom handling. Any other module + // settings can be synchronized directly. + if (strpos($name, 'config_test.dynamic.') !== 0) { + return FALSE; } - if ($op == 'change') { - $config_test = new ConfigTest($new_config); - $config_test->setOriginal($old_config); - $config_test->save(); + $config_test = new ConfigTest($new_config); + $config_test->setOriginal($old_config); + $config_test->save(); + return TRUE; +} + +/** + * Delete configuration upon synchronizing configuration changes. + * + * This callback is invoked when configuration is synchronized between storages + * and allows a module to take over the synchronization of configuration data. + * + * Modules should implement this callback if they manage configuration data + * (such as image styles, node types, or fields) which needs to be + * prepared and passed through module API functions to properly handle a + * configuration change. + * + * @param string $name + * The name of the configuration object. + * @param Drupal\Core\Config\Config $new_config + * A configuration object containing the new configuration data. + * @param Drupal\Core\Config\Config $old_config + * A configuration object containing the old configuration data. + */ +function MODULE_config_import_delete($name, $new_config, $old_config) { + // Only configurable thingies require custom handling. Any other module + // settings can be synchronized directly. + if (strpos($name, 'config_test.dynamic.') !== 0) { + return FALSE; } + // @todo image_style_delete() supports the notion of a "replacement style" + // to be used by other modules instead of the deleted style. Essential! + // But that is impossible currently, since the config system only knows + // about deleted and added changes. Introduce an 'old_ID' key within + // config objects as a standard? + $config_test = new ConfigTest($old_config); + $config_test->delete(); return TRUE; } diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigConfigurableTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigConfigurableTest.php deleted file mode 100644 index 68df5f1..0000000 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigConfigurableTest.php +++ /dev/null @@ -1,80 +0,0 @@ - 'Configurable configuration', - 'description' => 'Tests configurable configuration.', - 'group' => 'Configuration', - ); - } - - function setUp() { - parent::setUp(array('config_test')); - } - - /** - * Tests basic CRUD operations through the UI. - */ - function testCRUD() { - // Create a thingie. - $id = 'thingie'; - $edit = array( - 'id' => $id, - 'name' => 'Thingie', - ); - $this->drupalPost('admin/structure/config_test/add', $edit, 'Save'); - $this->assertResponse(200); - $this->assertText('Thingie'); - - // Update the thingie. - $this->assertLinkByHref('admin/structure/config_test/manage/' . $id); - $edit = array( - 'name' => 'Thongie', - ); - $this->drupalPost('admin/structure/config_test/manage/' . $id, $edit, 'Save'); - $this->assertResponse(200); - $this->assertNoText('Thingie'); - $this->assertText('Thongie'); - - // Delete the thingie. - $this->assertLinkByHref('admin/structure/config_test/manage/' . $id . '/delete'); - $this->drupalPost('admin/structure/config_test/manage/' . $id . '/delete', array(), 'Delete'); - $this->assertResponse(200); - $this->assertNoText('Thingie'); - $this->assertNoText('Thongie'); - - // Re-create a thingie. - $edit = array( - 'id' => $id, - 'name' => 'Thingie', - ); - $this->drupalPost('admin/structure/config_test/add', $edit, 'Save'); - $this->assertResponse(200); - $this->assertText('Thingie'); - - // Rename the thingie's ID/machine name. - $this->assertLinkByHref('admin/structure/config_test/manage/' . $id); - $new_id = 'zingie'; - $edit = array( - 'id' => $new_id, - 'name' => 'Zingie', - ); - $this->drupalPost('admin/structure/config_test/manage/' . $id, $edit, 'Save'); - $this->assertResponse(200); - $this->assertNoText('Thingie'); - $this->assertText('Zingie'); - } -} diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php index 3d13d98..7ec6d8e 100644 --- a/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php +++ b/core/modules/config/lib/Drupal/config/Tests/ConfigInstallTest.php @@ -46,13 +46,5 @@ class ConfigInstallTest extends WebTestBase { // Verify that configuration import callback was invoked for the dynamic // thingie. $this->assertTrue($GLOBALS['hook_config_import']); - - // Verify that config_test API hooks were invoked for the dynamic default - // thingie. - $this->assertTrue($GLOBALS['hook_config_test_load']); - $this->assertTrue($GLOBALS['hook_config_test_presave']); - $this->assertTrue($GLOBALS['hook_config_test_insert']); - $this->assertFalse(isset($GLOBALS['hook_config_test_update'])); - $this->assertFalse(isset($GLOBALS['hook_config_test_delete'])); } } diff --git a/core/modules/config/tests/config_test/config/config_test.dynamic.default.yml b/core/modules/config/tests/config_test/config/config_test.dynamic.default.yml index d285439..3e50e3b 100644 --- a/core/modules/config/tests/config_test/config/config_test.dynamic.default.yml +++ b/core/modules/config/tests/config_test/config/config_test.dynamic.default.yml @@ -1,2 +1,2 @@ id: default -name: Default +label: Default diff --git a/core/modules/config/tests/config_test/config_test.hooks.inc b/core/modules/config/tests/config_test/config_test.hooks.inc deleted file mode 100644 index 56f690f..0000000 --- a/core/modules/config/tests/config_test/config_test.hooks.inc +++ /dev/null @@ -1,45 +0,0 @@ -delete(); - } - if ($op == 'create') { - $config_test = new ConfigTest($new_config); - $config_test->save(); - } - if ($op == 'change') { - $config_test = new ConfigTest($new_config); - $config_test->setOriginal($old_config); - $config_test->save(); - } + $new_config->save(); return TRUE; } /** - * Implements hook_menu(). - */ -function config_test_menu() { - $items['admin/structure/config_test'] = array( - 'title' => 'Test configuration', - 'page callback' => 'config_test_list_page', - 'access callback' => TRUE, - ); - $items['admin/structure/config_test/add'] = array( - 'title' => 'Add test configuration', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('config_test_form'), - 'access callback' => TRUE, - 'type' => MENU_LOCAL_ACTION, - ); - $items['admin/structure/config_test/manage/%config_test'] = array( - 'title' => 'Edit test configuration', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('config_test_form', 4), - 'access callback' => TRUE, - ); - $items['admin/structure/config_test/manage/%config_test/edit'] = array( - 'title' => 'Edit', - 'type' => MENU_DEFAULT_LOCAL_TASK, - 'weight' => -10, - ); - $items['admin/structure/config_test/manage/%config_test/delete'] = array( - 'title' => 'Delete', - 'page callback' => 'drupal_get_form', - 'page arguments' => array('config_test_delete_form', 4), - 'access callback' => TRUE, - 'type' => MENU_LOCAL_TASK, - ); - return $items; -} - -/** - * Loads a ConfigTest object. - * - * @param string $id - * The ID of the ConfigTest object to load. + * Implements MODULE_config_import_change(). */ -function config_test_load($id) { - $config = config('config_test.dynamic.' . $id); - if ($config->isNew()) { +function config_test_config_import_change($name, $new_config, $old_config) { + // Only configurable thingies require custom handling. Any other module + // settings can be synchronized directly. + if (strpos($name, 'config_test.dynamic.') !== 0) { return FALSE; } - return new ConfigTest($config); -} - -/** - * Saves a ConfigTest object. - * - * @param Drupal\config_test\ConfigTest $config_test - * The ConfigTest object to save. - */ -function config_test_save(ConfigTest $config_test) { - return $config_test->save(); -} - -/** - * Deletes a ConfigTest object. - * - * @param string $id - * The ID of the ConfigTest object to delete. - */ -function config_test_delete($id) { - $config = config('config_test.dynamic.' . $id); - $config_test = new ConfigTest($config); - return $config_test->delete(); -} - -/** - * Page callback; Lists available ConfigTest objects. - */ -function config_test_list_page() { - $config_names = config_get_storage_names_with_prefix('config_test.dynamic.'); - $rows = array(); - foreach ($config_names as $config_name) { - $config_test = new ConfigTest(config($config_name)); - $row = array(); - $row['name']['data'] = array( - '#type' => 'link', - '#title' => $config_test->getLabel(), - '#href' => $config_test->getUri(), - ); - $row['delete']['data'] = array( - '#type' => 'link', - '#title' => t('Delete'), - '#href' => $config_test->getUri() . '/delete', - ); - $rows[] = $row; - } - $build = array( - '#theme' => 'table', - '#header' => array('Name', 'Operations'), - '#rows' => $rows, - '#empty' => format_string('No test configuration defined. Add some', array( - '@add-url' => url('admin/structure/config_test/add'), - )), - ); - return $build; -} - -/** - * Form constructor to add or edit a ConfigTest object. - * - * @param Drupal\config_test\ConfigTest $config_test - * (optional) An existing ConfigTest object to edit. If omitted, the form - * creates a new ConfigTest. - */ -function config_test_form($form, &$form_state, ConfigTest $config_test = NULL) { - if (!isset($config_test)) { - $config_test = new ConfigTest(config(NULL)); - } - $form_state['config_test'] = $config_test; - - $form['name'] = array( - '#type' => 'textfield', - '#title' => 'Label', - '#default_value' => $config_test->getLabel(), - '#required' => TRUE, - ); - $form['id'] = array( - '#type' => 'machine_name', - '#default_value' => $config_test->getId(), - '#required' => TRUE, - '#machine_name' => array( - 'exists' => 'config_test_load', - ), - ); - $form['style'] = array( - '#type' => 'select', - '#title' => 'Image style', - '#options' => array(), - '#default_value' => $config_test->get('style'), - '#access' => FALSE, - ); - if (module_exists('image')) { - $form['style']['#access'] = TRUE; - $form['style']['#options'] = image_style_options(); - } - - $form['actions'] = array('#type' => 'actions'); - $form['actions']['submit'] = array('#type' => 'submit', '#value' => 'Save'); + // Set a global value we can check in test code. + $GLOBALS['hook_config_import'] = __FUNCTION__; - return $form; + $new_config->save(); + return TRUE; } /** - * Form submission handler for config_test_form(). + * Implements MODULE_config_import_delete(). */ -function config_test_form_submit($form, &$form_state) { - form_state_values_clean($form_state); - - $config_test = $form_state['config_test']; - - foreach ($form_state['values'] as $key => $value) { - $config_test->set($key, $value); - } - $config_test->save(); - - if (!empty($config_test->original)) { - drupal_set_message(format_string('%label configuration has been updated.', array('%label' => $config_test->getLabel()))); - } - else { - drupal_set_message(format_string('%label configuration has been created.', array('%label' => $config_test->getLabel()))); +function config_test_config_import_delete($name, $new_config, $old_config) { + // Only configurable thingies require custom handling. Any other module + // settings can be synchronized directly. + if (strpos($name, 'config_test.dynamic.') !== 0) { + return FALSE; } + // Set a global value we can check in test code. + $GLOBALS['hook_config_import'] = __FUNCTION__; - $form_state['redirect'] = 'admin/structure/config_test'; -} - -/** - * Form constructor to delete a ConfigTest object. - * - * @param Drupal\config_test\ConfigTest $config_test - * The ConfigTest object to delete. - */ -function config_test_delete_form($form, &$form_state, ConfigTest $config_test) { - $form_state['config_test'] = $config_test; - - $form['id'] = array('#type' => 'value', '#value' => $config_test->getId()); - return confirm_form($form, - format_string('Are you sure you want to delete %label', array('%label' => $config_test->getLabel())), - 'admin/structure/config_test', - NULL, - 'Delete' - ); + $old_config->delete(); + return TRUE; } -/** - * Form submission handler for config_test_delete_form(). - */ -function config_test_delete_form_submit($form, &$form_state) { - $form_state['config_test']->delete(); - $form_state['redirect'] = 'admin/structure/config_test'; -} diff --git a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTest.php b/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTest.php deleted file mode 100644 index 5d95d50..0000000 --- a/core/modules/config/tests/config_test/lib/Drupal/config_test/ConfigTest.php +++ /dev/null @@ -1,187 +0,0 @@ -load($config); - } - - /** - * Implements Drupal\Core\Config\ConfigObjectInterface::getConfigPrefix(). - */ - public function getConfigPrefix() { - return 'config_test.dynamic'; - } - - /** - * Implements ConfigObjectInterface::getName(). - */ - public function getConfigName() { - return $this->getConfigPrefix() . '.' . $this->getId; - } - - /** - * Implements ConfigObjectInterface::getId(). - */ - public function getId() { - return $this->config->get($this->idKey); - } - - /** - * Implements ConfigObjectInterface::isNew(). - */ - public function isNew() { - return isset($this->originalId); - } - - /** - * Implements ConfigObjectInterface::getLabel(). - */ - public function getLabel() { - return $this->config->get($this->labelKey); - } - - /** - * Implements ConfigObjectInterface::getUri(). - */ - public function getUri() { - // @todo Check whether this makes sense. - return 'admin/structure/config_test/manage/' . $this->getId(); - } - - /** - * Implements ConfigObjectInterface::get(). - */ - public function get($property_name, $langcode = NULL) { - return $this->config->get($property_name); - } - - /** - * Implements ConfigObjectInterface::set(). - */ - public function set($property_name, $value, $langcode = NULL) { - return $this->config->set($property_name, $value); - } - - /** - * Implements ConfigObjectInterface::setOriginal(). - */ - public function setOriginal($config) { - $this->original = $config; - $this->originalId = $config->get($this->idKey); - return $this; - } - - /** - * Implements ConfigObjectInterface::load(). - */ - public function load($config) { - $this->config = $config; - $this->originalId = $config->get($this->idKey); - module_invoke_all('config_test_load', $this); - return $this; - } - - /** - * Implements ConfigObjectInterface::save(). - */ - public function save() { - // Provide the original configuration in $config->original, if any. - if (isset($this->originalId)) { - $original_config = config($this->getConfigPrefix() . '.' . $this->originalId); - $this->setOriginal(new $this($original_config)); - } - - // Save the new configuration. - $this->config->setName($this->getConfigPrefix() . '.' . $this->getId()); - module_invoke_all('config_test_presave', $this); - $this->config->save(); - - if (isset($this->originalId)) { - module_invoke_all('config_test_update', $this); - // Delete the original configuration, if it was renamed. - if ($this->originalId !== $this->getId()) { - // Configuration data is emptied out upon delete, so back it up and - // re-inject it. Delete the old configuration data directly; hooks will - // get and will be able to react to the data in $this->original. - $original_data = $original_config->get(); - $original_config->delete(); - $original_config->setData($original_data); - } - } - else { - module_invoke_all('config_test_insert', $this); - } - - return $this; - } - - /** - * Implements ConfigObjectInterface::delete(). - */ - public function delete() { - $this->config->delete(); - module_invoke_all('config_test_delete', $this); - } -} diff --git a/core/modules/image/image.module b/core/modules/image/image.module index 28afd0b..70f5ae5 100644 --- a/core/modules/image/image.module +++ b/core/modules/image/image.module @@ -500,32 +500,47 @@ function image_path_flush($path) { } /** - * Implements hook_config_import(). + * Implements MODULE_config_import_create(). */ -function image_config_import($op, $name, $new_config, $old_config) { +function image_config_import_create($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; } + $style = $new_config->get(); + return image_style_save($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. Essential! - // But that is impossible currently, since the config system only knows - // about deleted and added changes. Introduce an 'old_ID' key within - // config objects as a standard? - $style = $old_config->get(); - return image_style_delete($style); - } - if ($op == 'create') { - $style = $new_config->get(); - return image_style_save($style); +/** + * Implements MODULE_config_import_change(). + */ +function image_config_import_change($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; } - if ($op == 'change') { - $style = $new_config->get(); - return image_style_save($style); + $style = $new_config->get(); + return image_style_save($style); +} + +/** + * Implements MODULE_config_import_delete(). + */ +function image_config_import_delete($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; } + // @todo image_style_delete() supports the notion of a "replacement style" + // to be used by other modules instead of the deleted style. Essential! + // But that is impossible currently, since the config system only knows + // about deleted and added changes. Introduce an 'old_ID' key within + // config objects as a standard? + $style = $old_config->get(); + return image_style_delete($style); } /**