Commit a2f9a607 authored by alexpott's avatar alexpott

Issue #2033383 by tim.plunkett: Provide a default plugin bag.

parent 1d4d6ed2
<?php
/**
* @file
* Contains \Drupal\Component\Plugin\ConfigurablePluginInterface.
*/
namespace Drupal\Component\Plugin;
/**
* Provides an interface for a configurable plugin.
*/
interface ConfigurablePluginInterface {
/**
* Returns this plugin's configuration.
*
* @return array
* An array of this plugin's configuration.
*/
public function getConfiguration();
/**
* Sets the configuration for this plugin instance.
*
* @param array $configuration
* An associative array containing the plugin's configuration.
*/
public function setConfiguration(array $configuration);
}
<?php
/**
* @file
* Contains \Drupal\Component\Plugin\DefaultPluginBag.
*/
namespace Drupal\Component\Plugin;
use Drupal\Component\Plugin\Exception\PluginException;
use Drupal\Component\Utility\MapArray;
use Drupal\Component\Utility\String;
/**
* Provides a default plugin bag for a plugin type.
*
* A plugin bag is used to contain plugins that will be lazily instantiated. The
* configurations of each potential plugin are passed in, and the configuration
* key containing the plugin ID is specified by self::$pluginKey.
*/
class DefaultPluginBag extends PluginBag {
/**
* The manager used to instantiate the plugins.
*
* @var \Drupal\Component\Plugin\PluginManagerInterface
*/
protected $manager;
/**
* The initial configuration for each plugin in the bag.
*
* @var array
* An associative array containing the initial configuration for each plugin
* in the bag, keyed by plugin instance ID.
*/
protected $configurations = array();
/**
* The key within the plugin configuration that contains the plugin ID.
*
* @var string
*/
protected $pluginKey = 'id';
/**
* Constructs a new DefaultPluginBag object.
*
* @param \Drupal\Component\Plugin\PluginManagerInterface $manager
* The manager to be used for instantiating plugins.
* @param array $configurations
* (optional) An associative array containing the initial configuration for
* each plugin in the bag, keyed by plugin instance ID.
*/
public function __construct(PluginManagerInterface $manager, array $configurations = array()) {
$this->manager = $manager;
$this->configurations = $configurations;
if (!empty($configurations)) {
$this->instanceIDs = MapArray::copyValuesToKeys(array_keys($configurations));
}
}
/**
* {@inheritdoc}
*/
protected function initializePlugin($instance_id) {
$configuration = $this->configurations[$instance_id];
if (!isset($configuration[$this->pluginKey])) {
throw new PluginException(String::format("Unknown plugin ID '@instance'.", array('@instance' => $instance_id)));
}
$this->pluginInstances[$instance_id] = $this->manager->createInstance($configuration[$this->pluginKey], $configuration);
$this->addInstanceID($instance_id);
}
/**
* Sorts all plugin instances in this bag.
*
* @return self
* Returns the plugin bag.
*/
public function sort() {
uasort($this->instanceIDs, array($this, 'sortHelper'));
return $this;
}
/**
* Provides uasort() callback to sort plugins.
*/
public function sortHelper($aID, $bID) {
$a = $this->get($aID);
$b = $this->get($bID);
return strnatcasecmp($a->getPluginId(), $b->getPluginId());
}
/**
* 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.
*/
public function getConfiguration() {
$instances = array();
$this->rewind();
foreach ($this as $instance_id => $instance) {
if ($instance instanceof ConfigurablePluginInterface) {
$instances[$instance_id] = $instance->getConfiguration();
}
else {
$instances[$instance_id] = $this->configurations[$instance_id];
}
}
return $instances;
}
/**
* Updates the configuration for a plugin instance.
*
* If there is no plugin instance yet, a new will be instantiated. Otherwise,
* the existing instance is updated with the new configuration.
*
* @param string $instance_id
* The ID of a plugin to set the configuration for.
* @param array $configuration
* The plugin configuration to set.
*/
public function setConfiguration($instance_id, array $configuration) {
$this->configurations[$instance_id] = $configuration;
$instance = $this->get($instance_id);
if ($instance instanceof ConfigurablePluginInterface) {
$instance->setConfiguration($configuration);
}
}
}
<?php
/**
* @file
* Contains \Drupal\Component\Plugin\DefaultSinglePluginBag.
*/
namespace Drupal\Component\Plugin;
use Drupal\Component\Utility\MapArray;
/**
* Provides a default plugin bag for a plugin type.
*
* A plugin bag usually stores multiple plugins, and is used to lazily
* instantiate them. When only one plugin is needed, it is still best practice
* to encapsulate all of the instantiation logic in a plugin bag. This class can
* be used directly, or subclassed to add further exception handling in
* self::initializePlugin().
*/
class DefaultSinglePluginBag extends PluginBag {
/**
* The manager used to instantiate the plugins.
*
* @var \Drupal\Component\Plugin\PluginManagerInterface
*/
protected $manager;
/**
* An array of configuration to instantiate the plugin with.
*
* @var array
*/
protected $configuration;
/**
* 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 array $configuration
* An array of configuration.
*/
public function __construct(PluginManagerInterface $manager, array $instance_ids, array $configuration) {
$this->manager = $manager;
$this->instanceIDs = MapArray::copyValuesToKeys($instance_ids);
$this->configuration = $configuration;
}
/**
* {@inheritdoc}
*/
protected function initializePlugin($instance_id) {
$this->pluginInstances[$instance_id] = $this->manager->createInstance($instance_id, $this->configuration);
}
}
......@@ -7,46 +7,20 @@
namespace Drupal\Core\Action;
use Drupal\Component\Plugin\PluginBag;
use Drupal\Component\Plugin\PluginManagerInterface;
use Drupal\Component\Plugin\DefaultSinglePluginBag;
/**
* Provides a container for lazily loading Action plugins.
*/
class ActionBag extends PluginBag {
/**
* The manager used to instantiate the plugins.
*
* @var \Drupal\Component\Plugin\PluginManagerInterface
*/
protected $manager;
/**
* Constructs a new ActionBag 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 array $configuration
* An array of configuration.
*/
public function __construct(PluginManagerInterface $manager, array $instance_ids, array $configuration) {
$this->manager = $manager;
$this->instanceIDs = drupal_map_assoc($instance_ids);
$this->configuration = $configuration;
}
class ActionBag extends DefaultSinglePluginBag {
/**
* {@inheritdoc}
*
* @return \Drupal\Core\Action\ActionInterface
*/
protected function initializePlugin($instance_id) {
if (isset($this->pluginInstances[$instance_id])) {
return;
}
$this->pluginInstances[$instance_id] = $this->manager->createInstance($instance_id, $this->configuration);
public function &get($instance_id) {
return parent::get($instance_id);
}
}
......@@ -7,13 +7,14 @@
namespace Drupal\Core\Action;
use Drupal\Core\Action\ConfigurableActionInterface;
use Drupal\Component\Plugin\ConfigurablePluginInterface;
use Drupal\Core\Action\ActionBase;
use Drupal\Core\Plugin\PluginFormInterface;
/**
* Provides a base implementation for a configurable Action plugin.
*/
abstract class ConfigurableActionBase extends ActionBase implements ConfigurableActionInterface {
abstract class ConfigurableActionBase extends ActionBase implements ConfigurablePluginInterface, PluginFormInterface {
/**
* {@inheritdoc}
......@@ -43,7 +44,14 @@ public function getConfiguration() {
/**
* {@inheritdoc}
*/
public function validate(array &$form, array &$form_state) {
public function setConfiguration(array $configuration) {
$this->configuration = $configuration;
}
/**
* {@inheritdoc}
*/
public function validateConfigurationForm(array &$form, array &$form_state) {
}
}
......@@ -23,7 +23,7 @@ class AlterDecorator implements DiscoveryInterface {
/**
* The Discovery object being decorated.
*
* @var Drupal\Component\Plugin\Discovery\DiscoveryInterface
* @var \Drupal\Component\Plugin\Discovery\DiscoveryInterface
*/
protected $decorated;
......@@ -32,7 +32,7 @@ class AlterDecorator implements DiscoveryInterface {
*
* It uses the DiscoveryInterface object it should decorate.
*
* @param Drupal\Component\Plugin\Discovery\DiscoveryInterface $decorated
* @param \Drupal\Component\Plugin\Discovery\DiscoveryInterface $decorated
* The object implementing DiscoveryInterface that is being decorated.
* @param string $hook
* The name of the alter hook that will be used by this discovery instance.
......@@ -56,7 +56,7 @@ public function getDefinition($plugin_id) {
*/
public function getDefinitions() {
$definitions = $this->decorated->getDefinitions();
drupal_alter($this->hook, $definitions);
\Drupal::moduleHandler()->alter($this->hook, $definitions);
return $definitions;
}
......
......@@ -2,28 +2,15 @@
/**
* @file
* Contains \Drupal\Core\Action\ConfigurableActionInterface.
* Contains \Drupal\Core\Plugin\PluginFormInterface.
*/
namespace Drupal\Core\Action;
use Drupal\Core\Action\ActionInterface;
namespace Drupal\Core\Plugin;
/**
* Provides an interface for an Action plugin.
*
* @see \Drupal\Core\Annotation\Operation
* @see \Drupal\Core\Action\OperationManager
* Provides an interface for a plugin that contains a form.
*/
interface ConfigurableActionInterface extends ActionInterface {
/**
* Returns this plugin's configuration.
*
* @return array
* An array of this action plugin's configuration.
*/
public function getConfiguration();
interface PluginFormInterface {
/**
* Form constructor.
......@@ -36,7 +23,7 @@ public function getConfiguration();
* @return array
* The form structure.
*/
public function form(array $form, array &$form_state);
public function buildConfigurationForm(array $form, array &$form_state);
/**
* Form validation handler.
......@@ -46,16 +33,21 @@ public function form(array $form, array &$form_state);
* @param array $form_state
* An associative array containing the current state of the form.
*/
public function validate(array &$form, array &$form_state);
public function validateConfigurationForm(array &$form, array &$form_state);
/**
* Form submission handler.
*
* To properly store submitted form values store them in $this->configuration.
* @code
* $this->configuration['some_value'] = $form_state['values']['some_value'];
* @endcode
*
* @param array $form
* An associative array containing the structure of the form.
* @param array $form_state
* An associative array containing the current state of the form.
*/
public function submit(array &$form, array &$form_state);
public function submitConfigurationForm(array &$form, array &$form_state);
}
......@@ -10,8 +10,8 @@
use Drupal\Core\Entity\EntityControllerInterface;
use Drupal\Core\Entity\EntityFormController;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Action\ConfigurableActionInterface;
use Drupal\Core\Entity\EntityStorageControllerInterface;
use Drupal\Core\Plugin\PluginFormInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
......@@ -97,8 +97,8 @@ public function form(array $form, array &$form_state) {
'#value' => $this->entity->getType(),
);
if ($this->plugin instanceof ConfigurableActionInterface) {
$form += $this->plugin->form($form, $form_state);
if ($this->plugin instanceof PluginFormInterface) {
$form += $this->plugin->buildConfigurationForm($form, $form_state);
}
return parent::form($form, $form_state);
......@@ -133,8 +133,8 @@ protected function actions(array $form, array &$form_state) {
public function validate(array $form, array &$form_state) {
parent::validate($form, $form_state);
if ($this->plugin instanceof ConfigurableActionInterface) {
$this->plugin->validate($form, $form_state);
if ($this->plugin instanceof PluginFormInterface) {
$this->plugin->validateConfigurationForm($form, $form_state);
}
}
......@@ -144,8 +144,8 @@ public function validate(array $form, array &$form_state) {
public function submit(array $form, array &$form_state) {
parent::submit($form, $form_state);
if ($this->plugin instanceof ConfigurableActionInterface) {
$this->plugin->submit($form, $form_state);
if ($this->plugin instanceof PluginFormInterface) {
$this->plugin->submitConfigurationForm($form, $form_state);
}
return $this->entity;
}
......
......@@ -57,7 +57,7 @@ public function getFormID() {
public function buildForm(array $form, array &$form_state) {
$actions = array();
foreach ($this->manager->getDefinitions() as $id => $definition) {
if (is_subclass_of($definition['class'], '\Drupal\Core\Action\ConfigurableActionInterface')) {
if (is_subclass_of($definition['class'], '\Drupal\Core\Plugin\PluginFormInterface')) {
$key = Crypt::hashBase64($id);
$actions[$key] = $definition['label'] . '...';
}
......
......@@ -116,7 +116,7 @@ protected function getDefaultConfiguration() {
/**
* {@inheritdoc}
*/
public function form(array $form, array &$form_state) {
public function buildConfigurationForm(array $form, array &$form_state) {
$form['recipient'] = array(
'#type' => 'textfield',
'#title' => t('Recipient'),
......@@ -145,7 +145,7 @@ public function form(array $form, array &$form_state) {
/**
* {@inheritdoc}
*/
public function validate(array &$form, array &$form_state) {
public function validateConfigurationForm(array &$form, array &$form_state) {
if (!valid_email_address($form_state['values']['recipient']) && strpos($form_state['values']['recipient'], ':mail') === FALSE) {
// We want the literal %author placeholder to be emphasized in the error message.
form_set_error('recipient', t('Enter a valid email address or use a token e-mail address such as %author.', array('%author' => '[node:author:mail]')));
......@@ -155,7 +155,7 @@ public function validate(array &$form, array &$form_state) {
/**
* {@inheritdoc}
*/
public function submit(array &$form, array &$form_state) {
public function submitConfigurationForm(array &$form, array &$form_state) {
$this->configuration['recipient'] = $form_state['values']['recipient'];
$this->configuration['subject'] = $form_state['values']['subject'];
$this->configuration['message'] = $form_state['values']['message'];
......
......@@ -96,7 +96,7 @@ protected function getDefaultConfiguration() {
/**
* {@inheritdoc}
*/
public function form(array $form, array &$form_state) {
public function buildConfigurationForm(array $form, array &$form_state) {
$form['url'] = array(
'#type' => 'textfield',
'#title' => t('URL'),
......@@ -110,7 +110,7 @@ public function form(array $form, array &$form_state) {
/**
* {@inheritdoc}
*/
public function submit(array &$form, array &$form_state) {
public function submitConfigurationForm(array &$form, array &$form_state) {
$this->configuration['url'] = $form_state['values']['url'];
}
......
......@@ -70,7 +70,7 @@ protected function getDefaultConfiguration() {
/**
* {@inheritdoc}
*/
public function form(array $form, array &$form_state) {
public function buildConfigurationForm(array $form, array &$form_state) {
$form['message'] = array(
'#type' => 'textarea',
'#title' => t('Message'),
......@@ -85,7 +85,7 @@ public function form(array $form, array &$form_state) {
/**
* {@inheritdoc}
*/
public function submit(array &$form, array &$form_state) {
public function submitConfigurationForm(array &$form, array &$form_state) {
$this->configuration['message'] = $form_state['values']['message'];
unset($this->configuration['node']);
}
......
......@@ -50,50 +50,28 @@ public function settings() {
}
/**
* Returns the configuration data for the block plugin.
*
* @return array
* The plugin configuration array from PluginBase::$configuration.
*
* @todo This doesn't belong here. Move this into a new base class in
* http://drupal.org/node/1764380.
* @todo This does not return a config object, so the name is confusing.
*
* @see \Drupal\Component\Plugin\PluginBase::$configuration
* {@inheritdoc}
*/
public function getConfig() {
public function getConfiguration() {
return $this->configuration;
}
/**
* Sets a particular value in the block settings.
*
* @param string $key
* The key of PluginBase::$configuration to set.
* @param mixed $value
* The value to set for the provided key.
*
* @todo This doesn't belong here. Move this into a new base class in
* http://drupal.org/node/1764380.
* @todo This does not set a value in config(), so the name is confusing.
*
* @see \Drupal\Component\Plugin\PluginBase::$configuration
* {@inheritdoc}
*/
public function setConfiguration(array $configuration) {
$this->configuration = $configuration;
}
/**
* {@inheritdoc}
*/
public function setConfig($key, $value) {
public function setConfigurationValue($key, $value) {
$this->configuration[$key] = $value;
}
/**
* Indicates whether block-specific criteria allow access to the block.
*
* Blocks with access restrictions that should always be applied,
* regardless of user-configured settings, should implement this method
* with that access control logic.
*
* @return bool
* FALSE to deny access to the block, or TRUE to allow access.
*
* @see hook_block_access()
* {@inheritdoc}
*/
public function access() {
// By default, the block is visible unless user-configured rules indicate
......@@ -102,7 +80,7 @@ public function access() {
}
/**
* Implements \Drupal\block\BlockPluginInterface::form().
* {@inheritdoc}
*
* Creates a generic configuration form for all block types. Individual
* block plugins can add elements to this form by overriding
......@@ -111,7 +89,7 @@ public function access() {
*
* @see \Drupal\block\BlockBase::blockForm()
*/
public function form($form, &$form_state) {
public function buildConfigurationForm(array $form, array &$form_state) {
$definition = $this->getPluginDefinition();
$form['module'] = array(
'#type' => 'value',
......@@ -138,27 +116,14 @@ public function form($form, &$form_state) {
}
/**
* Returns the configuration form elements specific to this block plugin.
*
* Blocks that need to add form elements to the normal block configuration
* form should implement this method.
*
* @param array $form
* The form definition array for the block configuration form.
* @param array $form_state
* An array containing the current state of the configuration form.
*
* @return array $form
* The renderable form array representing the entire configuration form.
*
* @see \Drupal\block\BlockBase::form()
* {@inheritdoc}
*/
public function blockForm($form, &$form_state) {
return array();
}
/**
* Implements \Drupal\block\BlockPluginInterface::validate().
* {@inheritdoc}
*
* Most block plugins should not override this method. To add validation
* for a specific block type, override BlockBase::blockValdiate().
......@@ -167,30 +132,17 @@ public function blockForm($form, &$form_state) {
*
* @see \Drupal\block\BlockBase::blockValidate()
*/
public function validate($form, &$form_state) {
public function validateConfigurationForm(array &$form, array &$form_state) {
$this->blockValidate($form, $form_state);
}
/**
* Adds block type-specific validation for the block form.
*
* Note that this method takes the form structure and form state arrays for
* the full block configuration form as arguments, not just the elements
* defined in BlockBase::blockForm().
*
* @param array $form
* The form definition array for the full block configuration form.
* @param array $form_state
* An array containing the current state of the configuration form.
*
* @see \Drupal\block\BlockBase::blockForm()
* @see \Drupal\block\BlockBase::blockSubmit()
* @see \Drupal\block\BlockBase::validate()
* {@inheritdoc}
*/
public function blockValidate($form, &$form_state) {}
/**
* Implements \Drupal\block\BlockPluginInterface::submit().
* {@inheritdoc}
*
* Most block plugins should not override this method. To add submission
* handling for a specific block type, override BlockBase::blockSubmit().
......@@ -199,7 +151,7 @@ public function blockValidate($form, &$form_state) {}
*
* @see \Drupal\block\BlockBase::blockSubmit()
*/
public function submit($form, &$form_state) {
public function submitConfigurationForm(array &$form, array &$form_state) {
if (!form_get_errors()) {
$this->configuration['label'] = $form_state['values']['label'];
$this->configuration['label_display'] = $form_state['values']['label_display'];
......@@ -209,20 +161,8 @@ public function submit($form, &$form_state) {
}
/**
* Adds block type-specific submission handling for the block form.
*
* Note that this method takes the form structure and form state arrays for
* the full block configuration form as arguments, not just the elements
* defined in BlockBase::blockForm().
*
* @param array $form
* The form definition array for the full block configuration form.
* @param array $form_state
* An array containing the current state of the configuration form.
*
* @see \Drupal\block\BlockBase::blockForm()
* @see \Drupal\block\BlockBase::blockValidate()
* @see \Drupal\block\BlockBase::submit()
* {@inheritdoc}
*/
public function blockSubmit($form, &$form_state) {}
}
......@@ -25,7 +25,7 @@ public function form(array $form, array &$form_state) {
'#type' => 'value',
'#value' => $entity->id(),
);