diff --git a/core/lib/Drupal/Component/Plugin/DefaultPluginBag.php b/core/lib/Drupal/Component/Plugin/DefaultPluginBag.php index d8651e6..ba87dfb 100644 --- a/core/lib/Drupal/Component/Plugin/DefaultPluginBag.php +++ b/core/lib/Drupal/Component/Plugin/DefaultPluginBag.php @@ -101,11 +101,7 @@ public function sortHelper($aID, $bID) { } /** - * Returns the current configuration of all plugins in this bag. - * - * @return array - * An associative array keyed by instance ID, whose values are up-to-date - * plugin configurations. + * {@inheritdoc} */ public function getConfiguration() { $instances = array(); @@ -132,6 +128,16 @@ public function getConfiguration() { /** * {@inheritdoc} */ + public function setConfiguration($configuration) { + foreach ($configuration as $instance_id => $instance_configuration) { + $this->setInstanceConfiguration($instance_id, $instance_configuration); + } + return $this; + } + + /** + * {@inheritdoc} + */ public function setInstanceIds(array $instance_ids) { parent::setInstanceIds($instance_ids); // Ensure the new order matches the original order. @@ -149,7 +155,7 @@ public function setInstanceIds(array $instance_ids) { * @param array $configuration * The plugin configuration to set. */ - public function setConfiguration($instance_id, array $configuration) { + public function setInstanceConfiguration($instance_id, array $configuration) { $this->configurations[$instance_id] = $configuration; $instance = $this->get($instance_id); if ($instance instanceof ConfigurablePluginInterface) { diff --git a/core/lib/Drupal/Component/Plugin/DefaultSinglePluginBag.php b/core/lib/Drupal/Component/Plugin/DefaultSinglePluginBag.php index 468b803..469b567 100644 --- a/core/lib/Drupal/Component/Plugin/DefaultSinglePluginBag.php +++ b/core/lib/Drupal/Component/Plugin/DefaultSinglePluginBag.php @@ -7,8 +7,6 @@ namespace Drupal\Component\Plugin; -use Drupal\Component\Utility\MapArray; - /** * Provides a default plugin bag for a plugin type. * @@ -35,18 +33,27 @@ class DefaultSinglePluginBag extends PluginBag { protected $configuration; /** + * The instance ID used for this plugin bag. + * + * @var string + */ + protected $instanceId; + + /** * Constructs a new DefaultSinglePluginBag object. * * @param \Drupal\Component\Plugin\PluginManagerInterface $manager * The manager to be used for instantiating plugins. - * @param array $instance_ids - * The IDs of the plugin instances with which we are dealing. + * @param string $instance_id + * The ID of the plugin instance. * @param array $configuration * An array of configuration. */ - public function __construct(PluginManagerInterface $manager, array $instance_ids, array $configuration) { + public function __construct(PluginManagerInterface $manager, $instance_id, array $configuration) { $this->manager = $manager; - $this->instanceIDs = MapArray::copyValuesToKeys($instance_ids); + $this->instanceId = $instance_id; + // This is still needed by the parent PluginBag class. + $this->instanceIDs = array($instance_id => $instance_id); $this->configuration = $configuration; } @@ -57,4 +64,29 @@ protected function initializePlugin($instance_id) { $this->set($instance_id, $this->manager->createInstance($instance_id, $this->configuration)); } + /** + * {@inheritdoc} + */ + public function getConfiguration() { + $plugin = $this->get($this->instanceId); + if ($plugin instanceof ConfigurablePluginInterface) { + return $plugin->getConfiguration(); + } + else { + return $this->configuration; + } + } + + /** + * {@inheritdoc} + */ + public function setConfiguration($configuration) { + $plugin = $this->get($this->instanceId); + if ($plugin instanceof ConfigurablePluginInterface) { + $plugin->setConfiguration($configuration); + } + $this->configuration = $configuration; + return $this; + } + } diff --git a/core/lib/Drupal/Component/Plugin/PluginBag.php b/core/lib/Drupal/Component/Plugin/PluginBag.php index 960eb52..977cab2 100644 --- a/core/lib/Drupal/Component/Plugin/PluginBag.php +++ b/core/lib/Drupal/Component/Plugin/PluginBag.php @@ -35,6 +35,24 @@ abstract protected function initializePlugin($instance_id); /** + * Returns the current configuration of all plugins in this bag. + * + * @return array + * An array of up-to-date plugin configuration. + */ + abstract public function getConfiguration(); + + /** + * Sets the configuration for all plugins in this bag. + * + * @param array $configuration + * An array of up-to-date plugin configuration. + * + * @return $this + */ + abstract public function setConfiguration($configuration); + + /** * Clears all instantiated plugins. */ public function clear() { diff --git a/core/lib/Drupal/Core/Action/ActionInterface.php b/core/lib/Drupal/Core/Action/ActionInterface.php index 0620416..a53d7bb 100644 --- a/core/lib/Drupal/Core/Action/ActionInterface.php +++ b/core/lib/Drupal/Core/Action/ActionInterface.php @@ -7,6 +7,7 @@ namespace Drupal\Core\Action; +use Drupal\Component\Plugin\PluginInspectionInterface; use Drupal\Core\Executable\ExecutableInterface; /** @@ -15,7 +16,7 @@ * @see \Drupal\Core\Annotation\Action * @see \Drupal\Core\Action\ActionManager */ -interface ActionInterface extends ExecutableInterface { +interface ActionInterface extends ExecutableInterface, PluginInspectionInterface { /** * Executes the plugin for an array of objects. diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php index e8dcff4..42ad63e 100644 --- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php +++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php @@ -28,6 +28,20 @@ protected $originalId; /** + * The name of the property that is used to store plugin configuration. + * + * This is needed when the entity utilizes a PluginBag, to dictate where the + * plugin configuration should be stored. + * + * @todo Move this to a trait along with + * \Drupal\Core\Config\Entity\EntityWithPluginBagInterface, and give it a + * default value of 'configuration'. + * + * @var string + */ + protected $pluginConfigKey; + + /** * The enabled/disabled status of the configuration entity. * * @var bool @@ -101,6 +115,15 @@ public function get($property_name) { * {@inheritdoc} */ public function set($property_name, $value) { + // @todo When \Drupal\Core\Config\Entity\EntityWithPluginBagInterface moves + // to a trait, switch to class_uses() instead. + if ($this instanceof EntityWithPluginBagInterface) { + if ($property_name == $this->pluginConfigKey) { + // If external code updates the settings, pass it along to the plugin. + $this->getPluginBag()->setConfiguration($value); + } + } + $this->{$property_name} = $value; } @@ -192,6 +215,14 @@ public function getExportProperties() { public function preSave(EntityStorageControllerInterface $storage_controller) { parent::preSave($storage_controller); + // @todo When \Drupal\Core\Config\Entity\EntityWithPluginBagInterface moves + // to a trait, switch to class_uses() instead. + if ($this instanceof EntityWithPluginBagInterface) { + // Any changes to the plugin configuration must be saved to the entity's + // copy as well. + $this->set($this->pluginConfigKey, $this->getPluginBag()->getConfiguration()); + } + // Ensure this entity's UUID does not exist with a different ID, regardless // of whether it's new or updated. $matching_entities = $storage_controller->getQuery() diff --git a/core/lib/Drupal/Core/Config/Entity/EntityWithPluginBagInterface.php b/core/lib/Drupal/Core/Config/Entity/EntityWithPluginBagInterface.php new file mode 100644 index 0000000..44c5958 --- /dev/null +++ b/core/lib/Drupal/Core/Config/Entity/EntityWithPluginBagInterface.php @@ -0,0 +1,28 @@ + $value) { - $entity->set($key, $value); - } + $this->copyFormValuesToEntity($entity, $form_state); // Invoke all specified builders for copying form values to entity // properties. @@ -381,6 +373,26 @@ public function buildEntity(array $form, array &$form_state) { } /** + * Copies top-level form values to entity properties + * + * This should not change existing entity properties that are not being edited + * by this form. + * + * @param \Drupal\Core\Entity\EntityInterface $entity + * The entity the current form should operate upon. + * @param array $form_state + * An associative array containing the current state of the form. + */ + protected function copyFormValuesToEntity(EntityInterface $entity, array $form_state) { + // @todo: This relies on a method that only exists for config and content + // entities, in a different way. Consider moving this logic to a config + // entity specific implementation. + foreach ($form_state['values'] as $key => $value) { + $entity->set($key, $value); + } + } + + /** * {@inheritdoc} */ public function getEntity() { diff --git a/core/modules/block/lib/Drupal/block/BlockBase.php b/core/modules/block/lib/Drupal/block/BlockBase.php index 48e469b..d278663 100644 --- a/core/modules/block/lib/Drupal/block/BlockBase.php +++ b/core/modules/block/lib/Drupal/block/BlockBase.php @@ -108,6 +108,10 @@ public function buildConfigurationForm(array $form, array &$form_state) { '#default_value' => ($this->configuration['label_display'] === BlockInterface::BLOCK_LABEL_VISIBLE), '#return_value' => BlockInterface::BLOCK_LABEL_VISIBLE, ); + $form['cache'] = array( + '#type' => 'value', + '#value' => $this->configuration['cache'], + ); // Add plugin-specific settings for this block type. $form += $this->blockForm($form, $form_state); diff --git a/core/modules/block/lib/Drupal/block/BlockPluginBag.php b/core/modules/block/lib/Drupal/block/BlockPluginBag.php index 9bd948d..51f927a 100644 --- a/core/modules/block/lib/Drupal/block/BlockPluginBag.php +++ b/core/modules/block/lib/Drupal/block/BlockPluginBag.php @@ -25,10 +25,19 @@ class BlockPluginBag extends DefaultSinglePluginBag { protected $blockId; /** - * {@inheritdoc} + * Constructs a new BlockPluginBag. + * + * @param \Drupal\Component\Plugin\PluginManagerInterface $manager + * The manager to be used for instantiating plugins. + * @param string $instance_id + * The ID of the plugin instance. + * @param array $configuration + * An array of configuration. + * @param string $block_id + * The unique ID of the block entity using this plugin. */ - public function __construct(PluginManagerInterface $manager, array $instance_ids, array $configuration, $block_id) { - parent::__construct($manager, $instance_ids, $configuration); + public function __construct(PluginManagerInterface $manager, $instance_id, array $configuration, $block_id) { + parent::__construct($manager, $instance_id, $configuration); $this->blockId = $block_id; } diff --git a/core/modules/block/lib/Drupal/block/Entity/Block.php b/core/modules/block/lib/Drupal/block/Entity/Block.php index 9437e77..b6ab85d 100644 --- a/core/modules/block/lib/Drupal/block/Entity/Block.php +++ b/core/modules/block/lib/Drupal/block/Entity/Block.php @@ -10,6 +10,7 @@ use Drupal\Core\Config\Entity\ConfigEntityBase; use Drupal\block\BlockPluginBag; use Drupal\block\BlockInterface; +use Drupal\Core\Config\Entity\EntityWithPluginBagInterface; use Drupal\Core\Entity\EntityStorageControllerInterface; /** @@ -41,7 +42,7 @@ * } * ) */ -class Block extends ConfigEntityBase implements BlockInterface { +class Block extends ConfigEntityBase implements BlockInterface, EntityWithPluginBagInterface { /** * The ID of the block. @@ -93,6 +94,11 @@ class Block extends ConfigEntityBase implements BlockInterface { protected $pluginBag; /** + * {@inheritdoc} + */ + protected $pluginConfigKey = 'settings'; + + /** * The visibility settings. * * @var array @@ -100,19 +106,20 @@ class Block extends ConfigEntityBase implements BlockInterface { protected $visibility; /** - * Overrides \Drupal\Core\Config\Entity\ConfigEntityBase::__construct(); + * {@inheritdoc} */ - public function __construct(array $values, $entity_type) { - parent::__construct($values, $entity_type); - - $this->pluginBag = new BlockPluginBag(\Drupal::service('plugin.manager.block'), array($this->plugin), $this->get('settings'), $this->id()); + public function getPlugin() { + return $this->getPluginBag()->get($this->plugin); } /** * {@inheritdoc} */ - public function getPlugin() { - return $this->pluginBag->get($this->plugin); + public function getPluginBag() { + if (!$this->pluginBag) { + $this->pluginBag = new BlockPluginBag(\Drupal::service('plugin.manager.block'), $this->plugin, $this->get('settings'), $this->id()); + } + return $this->pluginBag; } /** @@ -149,15 +156,6 @@ public function getExportProperties() { } /** - * {@inheritdoc} - */ - public function preSave(EntityStorageControllerInterface $storage_controller) { - parent::preSave($storage_controller); - - $this->set('settings', $this->getPlugin()->getConfiguration()); - } - - /** * Sorts active blocks by weight; sorts inactive blocks by name. */ public static function sort($a, $b) { diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php index 3f22caa..a694b4e 100644 --- a/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php +++ b/core/modules/block/lib/Drupal/block/Tests/BlockInterfaceTest.php @@ -83,6 +83,10 @@ public function testBlockInterface() { '#default_value' => TRUE, '#return_value' => 'visible', ), + 'cache' => array( + '#type' => 'value', + '#value' => DRUPAL_NO_CACHE, + ), 'display_message' => array( '#type' => 'textfield', '#title' => t('Display message'), diff --git a/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php b/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php index 6666c86..37f0471 100644 --- a/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php +++ b/core/modules/filter/lib/Drupal/filter/Entity/FilterFormat.php @@ -9,6 +9,7 @@ use Drupal\Core\Cache\Cache; use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\Core\Config\Entity\EntityWithPluginBagInterface; use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\filter\FilterFormatInterface; use Drupal\filter\FilterBag; @@ -44,7 +45,7 @@ * } * ) */ -class FilterFormat extends ConfigEntityBase implements FilterFormatInterface { +class FilterFormat extends ConfigEntityBase implements FilterFormatInterface, EntityWithPluginBagInterface { /** * Unique machine name of the format. @@ -136,6 +137,11 @@ class FilterFormat extends ConfigEntityBase implements FilterFormatInterface { /** * {@inheritdoc} */ + protected $pluginConfigKey = 'filters'; + + /** + * {@inheritdoc} + */ public function id() { return $this->format; } @@ -144,12 +150,20 @@ public function id() { * {@inheritdoc} */ public function filters($instance_id = NULL) { + $filter_bag = $this->getPluginBag(); + if (isset($instance_id)) { + return $filter_bag->get($instance_id); + } + return $filter_bag; + } + + /** + * {@inheritdoc} + */ + public function getPluginBag() { if (!isset($this->filterBag)) { $this->filterBag = new FilterBag(\Drupal::service('plugin.manager.filter'), $this->filters); } - if (isset($instance_id)) { - return $this->filterBag->get($instance_id); - } return $this->filterBag; } @@ -159,7 +173,7 @@ public function filters($instance_id = NULL) { public function setFilterConfig($instance_id, array $configuration) { $this->filters[$instance_id] = $configuration; if (isset($this->filterBag)) { - $this->filterBag->setConfiguration($instance_id, $configuration); + $this->filterBag->setInstanceConfiguration($instance_id, $configuration); } return $this; } @@ -169,9 +183,13 @@ public function setFilterConfig($instance_id, array $configuration) { */ public function getExportProperties() { $properties = parent::getExportProperties(); - // Sort and export the configuration of all filters. - $properties['filters'] = $this->filters()->sort()->getConfiguration(); - + // @todo Make self::$weight and self::$cache protected and add them here. + $names = array( + 'filters', + ); + foreach ($names as $name) { + $properties[$name] = $this->get($name); + } return $properties; } @@ -195,6 +213,9 @@ public function disable() { * {@inheritdoc} */ public function preSave(EntityStorageControllerInterface $storage_controller) { + // Ensure the filters have been sorted before saving. + $this->filters()->sort(); + parent::preSave($storage_controller); $this->name = trim($this->label()); diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtml.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtml.php index a49f5f4..a7c73c4 100644 --- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtml.php +++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtml.php @@ -18,8 +18,8 @@ * type = Drupal\filter\Plugin\FilterInterface::TYPE_HTML_RESTRICTOR, * settings = { * "allowed_html" = "
    1. ", - * "filter_html_help" = 1, - * "filter_html_nofollow" = 0 + * "filter_html_help" = TRUE, + * "filter_html_nofollow" = FALSE * }, * weight = -10 * ) diff --git a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php index 65e8f6d..5e2c726 100644 --- a/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php +++ b/core/modules/image/lib/Drupal/image/Entity/ImageStyle.php @@ -8,6 +8,7 @@ namespace Drupal\image\Entity; use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\Core\Config\Entity\EntityWithPluginBagInterface; use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\image\ImageEffectBag; use Drupal\image\ImageEffectInterface; @@ -45,7 +46,7 @@ * } * ) */ -class ImageStyle extends ConfigEntityBase implements ImageStyleInterface { +class ImageStyle extends ConfigEntityBase implements ImageStyleInterface, EntityWithPluginBagInterface { /** * The name of the image style to use as replacement upon delete. @@ -90,6 +91,11 @@ class ImageStyle extends ConfigEntityBase implements ImageStyleInterface { protected $effectsBag; /** + * {@inheritdoc} + */ + protected $pluginConfigKey = 'effects'; + + /** * Overrides Drupal\Core\Entity\Entity::id(). */ public function id() { @@ -358,6 +364,13 @@ public function getEffects() { /** * {@inheritdoc} */ + public function getPluginBag() { + return $this->getEffects(); + } + + /** + * {@inheritdoc} + */ public function saveImageEffect(array $configuration) { $effect_id = $this->getEffects()->updateConfiguration($configuration); $this->save(); @@ -369,7 +382,12 @@ public function saveImageEffect(array $configuration) { */ public function getExportProperties() { $properties = parent::getExportProperties(); - $properties['effects'] = $this->getEffects()->getConfiguration(); + $names = array( + 'effects', + ); + foreach ($names as $name) { + $properties[$name] = $this->get($name); + } return $properties; } @@ -394,4 +412,5 @@ public function setName($name) { $this->set('name', $name); return $this; } + } diff --git a/core/modules/image/lib/Drupal/image/Form/ImageStyleEditForm.php b/core/modules/image/lib/Drupal/image/Form/ImageStyleEditForm.php index c00b009..a589feb 100644 --- a/core/modules/image/lib/Drupal/image/Form/ImageStyleEditForm.php +++ b/core/modules/image/lib/Drupal/image/Form/ImageStyleEditForm.php @@ -7,6 +7,7 @@ namespace Drupal\image\Form; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\image\ConfigurableImageEffectInterface; use Drupal\image\ImageEffectManager; @@ -248,4 +249,16 @@ protected function updateEffectWeights(array $effects) { } } + /** + * {@inheritdoc} + */ + protected function copyFormValuesToEntity(EntityInterface $entity, array $form_state) { + foreach ($form_state['values'] as $key => $value) { + // Do not copy effects here, see self::updateEffectWeights(). + if ($key != 'effects') { + $entity->set($key, $value); + } + } + } + } diff --git a/core/modules/image/lib/Drupal/image/ImageEffectBag.php b/core/modules/image/lib/Drupal/image/ImageEffectBag.php index f75c7cc..97201ec 100644 --- a/core/modules/image/lib/Drupal/image/ImageEffectBag.php +++ b/core/modules/image/lib/Drupal/image/ImageEffectBag.php @@ -42,7 +42,7 @@ public function updateConfiguration(array $configuration) { $configuration['uuid'] = $uuid_generator->generate(); } $instance_id = $configuration['uuid']; - $this->setConfiguration($instance_id, $configuration); + $this->setInstanceConfiguration($instance_id, $configuration); $this->addInstanceId($instance_id); return $instance_id; } diff --git a/core/modules/search/lib/Drupal/search/Entity/SearchPage.php b/core/modules/search/lib/Drupal/search/Entity/SearchPage.php index c640abe..66cf1ec 100644 --- a/core/modules/search/lib/Drupal/search/Entity/SearchPage.php +++ b/core/modules/search/lib/Drupal/search/Entity/SearchPage.php @@ -8,6 +8,7 @@ namespace Drupal\search\Entity; use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\Core\Config\Entity\EntityWithPluginBagInterface; use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\Component\Plugin\ConfigurablePluginInterface; use Drupal\search\Plugin\SearchIndexingInterface; @@ -49,7 +50,7 @@ * } * ) */ -class SearchPage extends ConfigEntityBase implements SearchPageInterface { +class SearchPage extends ConfigEntityBase implements SearchPageInterface, EntityWithPluginBagInterface { /** * The name (plugin ID) of the search page entity. @@ -112,17 +113,23 @@ class SearchPage extends ConfigEntityBase implements SearchPageInterface { /** * {@inheritdoc} */ - public function __construct(array $values, $entity_type) { - parent::__construct($values, $entity_type); - - $this->pluginBag = new SearchPluginBag($this->searchPluginManager(), array($this->plugin), $this->configuration, $this->id()); - } + protected $pluginConfigKey = 'configuration'; /** * {@inheritdoc} */ public function getPlugin() { - return $this->pluginBag->get($this->plugin); + return $this->getPluginBag()->get($this->plugin); + } + + /** + * {@inheritdoc} + */ + public function getPluginBag() { + if (!$this->pluginBag) { + $this->pluginBag = new SearchPluginBag($this->searchPluginManager(), $this->plugin, $this->configuration, $this->id()); + } + return $this->pluginBag; } /** @@ -130,7 +137,7 @@ public function getPlugin() { */ public function setPlugin($plugin_id) { $this->plugin = $plugin_id; - $this->pluginBag->addInstanceID($plugin_id); + $this->getPluginBag()->addInstanceID($plugin_id); } /** @@ -194,19 +201,6 @@ public function postCreate(EntityStorageControllerInterface $storage_controller) /** * {@inheritdoc} */ - public function preSave(EntityStorageControllerInterface $storage_controller) { - parent::preSave($storage_controller); - - $plugin = $this->getPlugin(); - // If this plugin has any configuration, ensure that it is set. - if ($plugin instanceof ConfigurablePluginInterface) { - $this->set('configuration', $plugin->getConfiguration()); - } - } - - /** - * {@inheritdoc} - */ public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) { parent::postSave($storage_controller, $update); $this->routeBuilder()->setRebuildNeeded(); diff --git a/core/modules/search/lib/Drupal/search/Plugin/SearchPluginBag.php b/core/modules/search/lib/Drupal/search/Plugin/SearchPluginBag.php index 0f8596e..7bcfa5f 100644 --- a/core/modules/search/lib/Drupal/search/Plugin/SearchPluginBag.php +++ b/core/modules/search/lib/Drupal/search/Plugin/SearchPluginBag.php @@ -23,10 +23,19 @@ class SearchPluginBag extends DefaultSinglePluginBag { protected $searchPageId; /** - * {@inheritdoc} + * Constructs a new SearchPluginBag. + * + * @param \Drupal\Component\Plugin\PluginManagerInterface $manager + * The manager to be used for instantiating plugins. + * @param string $instance_id + * The ID of the plugin instance. + * @param array $configuration + * An array of configuration. + * @param string $search_page_id + * The unique ID of the search page using this plugin. */ - public function __construct(PluginManagerInterface $manager, array $instance_ids, array $configuration, $search_page_id) { - parent::__construct($manager, $instance_ids, $configuration); + public function __construct(PluginManagerInterface $manager, $instance_id, array $configuration, $search_page_id) { + parent::__construct($manager, $instance_id, $configuration); $this->searchPageId = $search_page_id; } diff --git a/core/modules/search/tests/Drupal/search/Tests/SearchPluginBagTest.php b/core/modules/search/tests/Drupal/search/Tests/SearchPluginBagTest.php index 5ee70f0..c2cf9af 100644 --- a/core/modules/search/tests/Drupal/search/Tests/SearchPluginBagTest.php +++ b/core/modules/search/tests/Drupal/search/Tests/SearchPluginBagTest.php @@ -56,7 +56,7 @@ public static function getInfo() { */ protected function setUp() { $this->pluginManager = $this->getMock('Drupal\Component\Plugin\PluginManagerInterface'); - $this->searchPluginBag = new SearchPluginBag($this->pluginManager, array('banana'), array('id' => 'banana', 'color' => 'yellow'), 'fruit_stand'); + $this->searchPluginBag = new SearchPluginBag($this->pluginManager, 'banana', array('id' => 'banana', 'color' => 'yellow'), 'fruit_stand'); } /** diff --git a/core/modules/system/lib/Drupal/system/Entity/Action.php b/core/modules/system/lib/Drupal/system/Entity/Action.php index 7df6352..3b8992f 100644 --- a/core/modules/system/lib/Drupal/system/Entity/Action.php +++ b/core/modules/system/lib/Drupal/system/Entity/Action.php @@ -8,6 +8,8 @@ namespace Drupal\system\Entity; use Drupal\Core\Config\Entity\ConfigEntityBase; +use Drupal\Core\Config\Entity\EntityWithPluginBagInterface; +use Drupal\Core\Entity\Entity; use Drupal\Core\Entity\EntityStorageControllerInterface; use Drupal\system\ActionConfigEntityInterface; use Drupal\Core\Action\ActionBag; @@ -28,7 +30,7 @@ * } * ) */ -class Action extends ConfigEntityBase implements ActionConfigEntityInterface { +class Action extends ConfigEntityBase implements ActionConfigEntityInterface, EntityWithPluginBagInterface { /** * The name (plugin ID) of the action. @@ -82,17 +84,23 @@ class Action extends ConfigEntityBase implements ActionConfigEntityInterface { /** * {@inheritdoc} */ - public function __construct(array $values, $entity_type) { - parent::__construct($values, $entity_type); + protected $pluginConfigKey = 'configuration'; - $this->pluginBag = new ActionBag(\Drupal::service('plugin.manager.action'), array($this->plugin), $this->configuration); + /** + * {@inheritdoc} + */ + public function getPluginBag() { + if (!$this->pluginBag) { + $this->pluginBag = new ActionBag(\Drupal::service('plugin.manager.action'), $this->plugin, $this->configuration); + } + return $this->pluginBag; } /** * {@inheritdoc} */ public function getPlugin() { - return $this->pluginBag->get($this->plugin); + return $this->getPluginBag()->get($this->plugin); } /** @@ -100,7 +108,7 @@ public function getPlugin() { */ public function setPlugin($plugin_id) { $this->plugin = $plugin_id; - $this->pluginBag->addInstanceId($plugin_id); + $this->getPluginBag()->addInstanceId($plugin_id); } /** @@ -159,17 +167,4 @@ public function getExportProperties() { return $properties; } - /** - * {@inheritdoc} - */ - public function preSave(EntityStorageControllerInterface $storage_controller) { - parent::preSave($storage_controller); - - $plugin = $this->getPlugin(); - // If this plugin has any configuration, ensure that it is set. - if ($plugin instanceof ConfigurablePluginInterface) { - $this->set('configuration', $plugin->getConfiguration()); - } - } - } diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/ConfigEntityImportTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/ConfigEntityImportTest.php new file mode 100644 index 0000000..2d3def7 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Entity/ConfigEntityImportTest.php @@ -0,0 +1,244 @@ + 'Configuration entity import', + 'description' => 'Tests ConfigEntity importing.', + 'group' => 'Configuration', + ); + } + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->copyConfig($this->container->get('config.storage'), $this->container->get('config.storage.staging')); + } + + /** + * Runs test methods for each module within a single test run. + */ + public function testConfigUpdateImport() { + $this->doActionUpdate(); + $this->doBlockUpdate(); + $this->doFilterFormatUpdate(); + $this->doImageStyleUpdate(); + $this->doSearchPageUpdate(); + } + /** + * Tests updating a action during import. + */ + protected function doActionUpdate() { + // Create a test action with a known label. + $name = 'system.action.apple'; + $entity = entity_create('action', array( + 'id' => 'apple', + 'plugin' => 'action_message_action', + )); + $entity->save(); + + $this->checkSinglePluginConfigSync($entity, 'configuration', 'message', ''); + + // Read the existing data, and prepare an altered version in staging. + $custom_data = $original_data = $this->container->get('config.storage')->read($name); + $custom_data['configuration']['message'] = 'Granny Smith'; + $this->assertConfigUpdateImport($name, $original_data, $custom_data); + + } + + /** + * Tests updating a block during import. + */ + protected function doBlockUpdate() { + // Create a test block with a known label. + $name = 'block.block.apple'; + $block = $this->drupalPlaceBlock('system_powered_by_block', array( + 'id' => 'apple', + 'label' => 'Red Delicious', + )); + + $this->checkSinglePluginConfigSync($block, 'settings', 'label', 'Red Delicious'); + + // Read the existing data, and prepare an altered version in staging. + $custom_data = $original_data = $this->container->get('config.storage')->read($name); + $custom_data['settings']['label'] = 'Granny Smith'; + $this->assertConfigUpdateImport($name, $original_data, $custom_data); + } + + /** + * Tests updating a filter format during import. + */ + protected function doFilterFormatUpdate() { + // Create a test filter format with a known label. + $name = 'filter.format.plain_text'; + + /** @var $entity \Drupal\filter\Entity\FilterFormat */ + $entity = entity_load('filter_format', 'plain_text'); + $plugin_bag = $entity->getPluginBag(); + + $filters = $entity->get('filters'); + $this->assertIdentical(72, $filters['filter_url']['settings']['filter_url_length']); + + $filters['filter_url']['settings']['filter_url_length'] = 100; + $entity->set('filters', $filters); + $entity->save(); + $this->assertIdentical($filters, $entity->get('filters')); + $this->assertIdentical($filters, $plugin_bag->getConfiguration()); + + $filters['filter_url']['settings']['filter_url_length'] = -100; + $entity->getPluginBag()->setConfiguration($filters); + $entity->save(); + $this->assertIdentical($filters, $entity->get('filters')); + $this->assertIdentical($filters, $plugin_bag->getConfiguration()); + + // Read the existing data, and prepare an altered version in staging. + $custom_data = $original_data = $this->container->get('config.storage')->read($name); + $custom_data['filters']['filter_url']['settings']['filter_url_length'] = 100; + $this->assertConfigUpdateImport($name, $original_data, $custom_data); + } + + /** + * Tests updating an image style during import. + */ + protected function doImageStyleUpdate() { + // Create a test image style with a known label. + $name = 'image.style.thumbnail'; + + /** @var $entity \Drupal\image\Entity\ImageStyle */ + $entity = entity_load('image_style', 'thumbnail'); + $plugin_bag = $entity->getPluginBag(); + + $effects = $entity->get('effects'); + $effect_id = key($effects); + $this->assertIdentical(100, $effects[$effect_id]['data']['height']); + + $effects[$effect_id]['data']['height'] = 50; + $entity->set('effects', $effects); + $entity->save(); + // Ensure the entity and plugin have the correct configuration. + $this->assertIdentical($effects, $entity->get('effects')); + $this->assertIdentical($effects, $plugin_bag->getConfiguration()); + + $effects[$effect_id]['data']['height'] = -50; + $entity->getPluginBag()->setConfiguration($effects); + $entity->save(); + // Ensure the entity and plugin have the correct configuration. + $this->assertIdentical($effects, $entity->get('effects')); + $this->assertIdentical($effects, $plugin_bag->getConfiguration()); + + // Read the existing data, and prepare an altered version in staging. + $custom_data = $original_data = $this->container->get('config.storage')->read($name); + $effect_name = key($original_data['effects']); + + $custom_data['effects'][$effect_name]['data']['upscale'] = FALSE; + $this->assertConfigUpdateImport($name, $original_data, $custom_data); + } + + /** + * Tests updating a search page during import. + */ + protected function doSearchPageUpdate() { + // Create a test search page with a known label. + $name = 'search.page.apple'; + $entity = entity_create('search_page', array( + 'id' => 'apple', + 'plugin' => 'search_extra_type_search', + )); + $entity->save(); + + $this->checkSinglePluginConfigSync($entity, 'configuration', 'boost', 'bi'); + + // Read the existing data, and prepare an altered version in staging. + $custom_data = $original_data = $this->container->get('config.storage')->read($name); + $custom_data['configuration']['boost'] = 'asdf'; + $this->assertConfigUpdateImport($name, $original_data, $custom_data); + } + + /** + * Tests that a single set of plugin config stays in sync. + * + * @param \Drupal\Core\Config\Entity\EntityWithPluginBagInterface $entity + * The entity. + * @param string $config_key + * Where the plugin config is stored. + * @param string $setting_key + * The setting within the plugin config to change. + * @param mixed $expected + * The expected default value of the plugin config setting. + */ + protected function checkSinglePluginConfigSync(EntityWithPluginBagInterface $entity, $config_key, $setting_key, $expected) { + $plugin_bag = $entity->getPluginBag(); + $settings = $entity->get($config_key); + + // Ensure the default config exists. + $this->assertIdentical($expected, $settings[$setting_key]); + + // Change the plugin config by setting it on the entity. + $settings[$setting_key] = $this->randomString(); + $entity->set($config_key, $settings); + $entity->save(); + $this->assertIdentical($settings, $entity->get($config_key)); + $this->assertIdentical($settings, $plugin_bag->getConfiguration()); + + // Change the plugin config by setting it on the plugin. + $settings[$setting_key] = $this->randomString(); + $plugin_bag->setConfiguration($settings); + $entity->save(); + $this->assertIdentical($settings, $entity->get($config_key)); + $this->assertIdentical($settings, $plugin_bag->getConfiguration()); + } + + /** + * Asserts that config entities are updated during import. + * + * @param string $name + * The name of the config object. + * @param array $original_data + * The original data stored in the config object. + * @param array $custom_data + * The new data to store in the config object. + */ + public function assertConfigUpdateImport($name, $original_data, $custom_data) { + $this->container->get('config.storage.staging')->write($name, $custom_data); + + // Verify the active configuration still returns the default values. + $config = $this->container->get('config.factory')->get($name); + $this->assertIdentical($config->get(), $original_data); + + // Import. + $this->configImporter()->import(); + + // Verify the values were updated. + $this->container->get('config.factory')->reset($name); + $config = $this->container->get('config.factory')->get($name); + $this->assertIdentical($config->get(), $custom_data); + } + +} diff --git a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/TestPluginBag.php b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/TestPluginBag.php index a4d06d2..c389308 100644 --- a/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/TestPluginBag.php +++ b/core/modules/system/tests/modules/plugin_test/lib/Drupal/plugin_test/Plugin/TestPluginBag.php @@ -42,4 +42,18 @@ protected function initializePlugin($instance_id) { $this->pluginInstances[$instance_id] = $this->manager->createInstance($instance_id, array()); } + /** + * {@inheritdoc} + */ + public function getConfiguration() { + return array(); + } + + /** + * {@inheritdoc} + */ + public function setConfiguration($configuration) { + return $this; + } + } diff --git a/core/tests/Drupal/Tests/Component/Plugin/ConfigurablePluginBagTest.php b/core/tests/Drupal/Tests/Component/Plugin/ConfigurablePluginBagTest.php index d970be7..735dd7b 100644 --- a/core/tests/Drupal/Tests/Component/Plugin/ConfigurablePluginBagTest.php +++ b/core/tests/Drupal/Tests/Component/Plugin/ConfigurablePluginBagTest.php @@ -61,7 +61,7 @@ public function testConfigurableGetConfiguration() { public function testConfigurableSetConfiguration() { $this->setupPluginBag($this->exactly(3)); $this->defaultPluginBag->getConfiguration(); - $this->defaultPluginBag->setConfiguration('apple', array('value' => 'pineapple')); + $this->defaultPluginBag->setInstanceConfiguration('apple', array('value' => 'pineapple')); $expected = $this->config; $expected['apple'] = array('value' => 'pineapple'); diff --git a/core/tests/Drupal/Tests/Component/Plugin/DefaultPluginBagTest.php b/core/tests/Drupal/Tests/Component/Plugin/DefaultPluginBagTest.php index b38e2dc..1dc33d9 100644 --- a/core/tests/Drupal/Tests/Component/Plugin/DefaultPluginBagTest.php +++ b/core/tests/Drupal/Tests/Component/Plugin/DefaultPluginBagTest.php @@ -140,18 +140,18 @@ public function testRemoveInstanceId() { } /** - * Tests the setConfiguration() method. + * Tests the setInstanceConfiguration() method. * - * @see \Drupal\Component\Plugin\DefaultPluginBag::setConfiguration() + * @see \Drupal\Component\Plugin\DefaultPluginBag::setInstanceConfiguration() */ - public function testSetConfiguration() { + public function testSetInstanceConfiguration() { $this->setupPluginBag($this->exactly(3)); $expected = array( 'id' => 'cherry', 'key' => 'value', 'custom' => 'bananas', ); - $this->defaultPluginBag->setConfiguration('cherry', $expected); + $this->defaultPluginBag->setInstanceConfiguration('cherry', $expected); $config = $this->defaultPluginBag->getConfiguration(); $this->assertSame($expected, $config['cherry']); } @@ -203,7 +203,7 @@ public function testSet() { $this->setupPluginBag($this->exactly(4)); $instance = $this->pluginManager->createInstance('cherry', $this->config['cherry']); $this->defaultPluginBag->set('cherry2', $instance); - $this->defaultPluginBag->setConfiguration('cherry2', $this->config['cherry']); + $this->defaultPluginBag->setInstanceConfiguration('cherry2', $this->config['cherry']); $expected = array( 'banana', diff --git a/core/tests/Drupal/Tests/Component/Plugin/DefaultSinglePluginBagTest.php b/core/tests/Drupal/Tests/Component/Plugin/DefaultSinglePluginBagTest.php index 85718f8..65bba15 100644 --- a/core/tests/Drupal/Tests/Component/Plugin/DefaultSinglePluginBagTest.php +++ b/core/tests/Drupal/Tests/Component/Plugin/DefaultSinglePluginBagTest.php @@ -41,7 +41,7 @@ protected function setupPluginBag(\PHPUnit_Framework_MockObject_Matcher_InvokedR ->method('createInstance') ->will($this->returnValue($this->pluginInstances['apple'])); - $this->defaultPluginBag = new DefaultSinglePluginBag($this->pluginManager, array_keys($this->pluginInstances), array('id' => 'apple', 'key' => 'value')); + $this->defaultPluginBag = new DefaultSinglePluginBag($this->pluginManager, 'apple', array('id' => 'apple', 'key' => 'value')); } /**