diff --git a/core/lib/Drupal/Component/Plugin/Context/Context.php b/core/lib/Drupal/Component/Plugin/Context/Context.php index b117a729cd393249e7bd078a614943bdf14cfe53..f30158333211da1c812d7dac30aeb637af27ae29 100644 --- a/core/lib/Drupal/Component/Plugin/Context/Context.php +++ b/core/lib/Drupal/Component/Plugin/Context/Context.php @@ -26,14 +26,17 @@ class Context implements ContextInterface { /** * The definition to which a context must conform. * - * @var array + * @var \Drupal\Component\Plugin\Context\ContextDefinitionInterface */ protected $contextDefinition; /** * Sets the contextDefinition for us without needing to call the setter. + * + * @param \Drupal\Component\Plugin\Context\ContextDefinitionInterface $context_definition + * The context definition. */ - public function __construct(array $context_definition) { + public function __construct(ContextDefinitionInterface $context_definition) { $this->contextDefinition = $context_definition; } @@ -48,13 +51,22 @@ public function setContextValue($value) { * Implements \Drupal\Component\Plugin\Context\ContextInterface::getContextValue(). */ public function getContextValue() { + // Support optional contexts. + if (!isset($this->contextValue)) { + $definition = $this->getContextDefinition(); + if ($definition->isRequired()) { + $type = $definition->getDataType(); + throw new ContextException(sprintf("The %s context is required and not present.", $type)); + } + return NULL; + } return $this->contextValue; } /** - * Implements \Drupal\Component\Plugin\Context\ContextInterface::setContextDefinition(). + * {@inheritdoc} */ - public function setContextDefinition(array $context_definition) { + public function setContextDefinition(ContextDefinitionInterface $context_definition) { $this->contextDefinition = $context_definition; } diff --git a/core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php b/core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..3d2337bcd458e56f80cf4d91f6ed1d063fb2fdf4 --- /dev/null +++ b/core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php @@ -0,0 +1,158 @@ +<?php + +/** + * @file + * Contains \Drupal\Component\Plugin\Context\ContextDefinitionInterface. + */ + +namespace Drupal\Component\Plugin\Context; + +/** + * Interface for context definitions. + */ +interface ContextDefinitionInterface { + + /** + * Returns a human readable label. + * + * @return string + * The label. + */ + public function getLabel(); + + /** + * Sets the human readable label. + * + * @param string $label + * The label to set. + * + * @return $this + */ + public function setLabel($label); + + /** + * Returns a human readable description. + * + * @return string|null + * The description, or NULL if no description is available. + */ + public function getDescription(); + + /** + * Sets the human readable description. + * + * @param string|null $description + * The description to set. + * + * @return $this + */ + public function setDescription($description); + + /** + * Returns the data type needed by the context. + * + * If the context is multiple-valued, this represents the type of each value. + * + * @return string + * The data type. + */ + public function getDataType(); + + /** + * Sets the data type needed by the context. + * + * @param string $data_type + * The data type to set. + * + * @return $this + */ + public function setDataType($data_type); + + /** + * Returns whether the data is multi-valued, i.e. a list of data items. + * + * @return bool + * Whether the data is multi-valued; i.e. a list of data items. + */ + public function isMultiple(); + + /** + * Sets whether the data is multi-valued. + * + * @param bool $multiple + * (optional) Whether the data is multi-valued. Defaults to TRUE. + * + * @return $this + */ + public function setMultiple($multiple = TRUE); + + /** + * Determines whether the context is required. + * + * For required data a non-NULL value is mandatory. + * + * @return bool + * Whether a data value is required. + */ + public function isRequired(); + + /** + * Sets whether the data is required. + * + * @param bool $required + * (optional) Whether the data is multi-valued. Defaults to TRUE. + * + * @return $this + */ + public function setRequired($required = TRUE); + + /** + * Returns an array of validation constraints. + * + * @return array + * An array of validation constraint definitions, keyed by constraint name. + * Each constraint definition can be used for instantiating + * \Symfony\Component\Validator\Constraint objects. + */ + public function getConstraints(); + + /** + * Sets the array of validation constraints. + * + * NOTE: This will override any previously set constraints. In most cases + * ContextDefinitionInterface::addConstraint() should be used instead. + * + * @param array $constraints + * The array of constraints. + * + * @return $this + * + * @see self::addConstraint() + */ + public function setConstraints(array $constraints); + + /** + * Adds a validation constraint. + * + * @param string $constraint_name + * The name of the constraint to add, i.e. its plugin id. + * @param array|null $options + * The constraint options as required by the constraint plugin, or NULL. + * + * @return $this + */ + public function addConstraint($constraint_name, $options = NULL); + + /** + * Returns a validation constraint. + * + * @param string $constraint_name + * The name of the the constraint, i.e. its plugin id. + * + * @return array + * A validation constraint definition which can be used for instantiating a + * \Symfony\Component\Validator\Constraint object. + */ + public function getConstraint($constraint_name); + +} diff --git a/core/lib/Drupal/Component/Plugin/Context/ContextInterface.php b/core/lib/Drupal/Component/Plugin/Context/ContextInterface.php index c5cd7434f7f8082ac52c1a878f292aabb6209293..f22202b3f0d873424566b81f2c9f231fc843969a 100644 --- a/core/lib/Drupal/Component/Plugin/Context/ContextInterface.php +++ b/core/lib/Drupal/Component/Plugin/Context/ContextInterface.php @@ -33,13 +33,11 @@ public function getContextValue(); /** * Sets the definition that the context must conform to. * - * @param array $contextDefinition + * @param \Drupal\Component\Plugin\Context\ContextDefinitionInterface $context_definition * A defining characteristic representation of the context against which - * that context can be validated. This is typically an array having a - * class name set under the 'class' key, but it could be extended to support - * other notations. + * that context can be validated. */ - public function setContextDefinition(array $contextDefinition); + public function setContextDefinition(ContextDefinitionInterface $context_definition); /** * Gets the provided definition that the context must conform to. diff --git a/core/lib/Drupal/Component/Plugin/ContextAwarePluginBase.php b/core/lib/Drupal/Component/Plugin/ContextAwarePluginBase.php index f04f07d4a39d37795943cdda6c3c9bdb46bb8262..b6846305fdab5e401f8e521907463a9603ba0357 100644 --- a/core/lib/Drupal/Component/Plugin/ContextAwarePluginBase.php +++ b/core/lib/Drupal/Component/Plugin/ContextAwarePluginBase.php @@ -7,10 +7,10 @@ namespace Drupal\Component\Plugin; -use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\Component\Plugin\Context\ContextInterface; +use Drupal\Component\Plugin\Exception\ContextException; use Drupal\Component\Plugin\Context\Context; use Symfony\Component\Validator\ConstraintViolationList; -use Drupal\Component\Plugin\Discovery\DiscoveryInterface; /** * Base class for plugins that are context aware. @@ -20,7 +20,7 @@ abstract class ContextAwarePluginBase extends PluginBase implements ContextAware /** * The data objects representing the context of this plugin. * - * @var array + * @var \Drupal\Component\Plugin\Context\ContextInterface[] */ protected $context; @@ -55,7 +55,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContextDefinitions(). + * {@inheritdoc} */ public function getContextDefinitions() { $definition = $this->getPluginDefinition(); @@ -63,50 +63,47 @@ public function getContextDefinitions() { } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContextDefinition(). + * {@inheritdoc} */ public function getContextDefinition($name) { $definition = $this->getPluginDefinition(); if (empty($definition['context'][$name])) { - throw new PluginException("The $name context is not a valid context."); + throw new ContextException(sprintf("The %s context is not a valid context.", $name)); } return $definition['context'][$name]; } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContexts(). + * {@inheritdoc} */ public function getContexts() { - $definitions = $this->getContextDefinitions(); - if ($definitions && empty($this->context)) { - throw new PluginException("There are no set contexts."); - } - $contexts = array(); - foreach (array_keys($definitions) as $name) { - if (empty($this->context[$name])) { - throw new PluginException("The $name context is not yet set."); - } - $contexts[$name] = $this->context[$name]; + // Make sure all context objects are initialized. + foreach ($this->getContextDefinitions() as $name => $definition) { + $this->getContext($name); } - return $contexts; + return $this->context; } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContext(). + * {@inheritdoc} */ public function getContext($name) { - // Check for a valid context definition. - $this->getContextDefinition($name); // Check for a valid context value. if (!isset($this->context[$name])) { - throw new PluginException("The $name context is not yet set."); + $this->context[$name] = new Context($this->getContextDefinition($name)); } - return $this->context[$name]; } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContextValues(). + * {@inheritdoc} + */ + public function setContext($name, ContextInterface $context) { + $this->context[$name] = $context; + } + + /** + * {@inheritdoc} */ public function getContextValues() { $values = array(); @@ -117,36 +114,30 @@ public function getContextValues() { } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContextValue(). + * {@inheritdoc} */ public function getContextValue($name) { return $this->getContext($name)->getContextValue(); } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::setContextValue(). + * {@inheritdoc} */ public function setContextValue($name, $value) { - $context_definition = $this->getContextDefinition($name); - $this->context[$name] = new Context($context_definition); - $this->context[$name]->setContextValue($value); + $this->getContext($name)->setContextValue($value); return $this; } /** - * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::valdidateContexts(). + * {@inheritdoc} */ public function validateContexts() { $violations = new ConstraintViolationList(); // @todo: Implement symfony validator API to let the validator traverse // and set property paths accordingly. - foreach ($this->getContextDefinitions() as $name => $definition) { - // Validate any set values. - if (isset($this->context[$name])) { - $violations->addAll($this->context[$name]->validate()); - } - // @todo: If no value is set, make sure any mapping is validated. + foreach ($this->getContexts() as $context) { + $violations->addAll($context->validate()); } return $violations; } diff --git a/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php b/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php index f29119da6f8f1980f617d60fb2827ad0359c5551..9cb56a1881fbbb5bf6d1a900b26865e835648f23 100644 --- a/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php +++ b/core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php @@ -7,7 +7,7 @@ namespace Drupal\Component\Plugin; -use Drupal\Component\Plugin\Exception\PluginException; +use \Drupal\Component\Plugin\Context\ContextInterface; /** * Interface for defining context aware plugins. @@ -36,7 +36,7 @@ public function getContextDefinitions(); * @throws \Drupal\Component\Plugin\Exception\PluginException * If the requested context is not defined. * - * @return array + * @return \Drupal\Component\Plugin\Context\ContextDefinitionInterface. * The definition against which the context value must validate. */ public function getContextDefinition($name); @@ -89,6 +89,16 @@ public function getContextValues(); */ public function getContextValue($name); + /** + * Set a context on this plugin. + * + * @param string $name + * The name of the context in the plugin configuration. + * @param \Drupal\Component\Plugin\Context\ContextInterface $context + * The context object to set. + */ + public function setContext($name, ContextInterface $context); + /** * Sets the value for a defined context. * diff --git a/core/lib/Drupal/Core/Annotation/ContextDefinition.php b/core/lib/Drupal/Core/Annotation/ContextDefinition.php new file mode 100644 index 0000000000000000000000000000000000000000..25bc27278a163232dba1196b10593fa5faca9203 --- /dev/null +++ b/core/lib/Drupal/Core/Annotation/ContextDefinition.php @@ -0,0 +1,110 @@ +<?php +/** + * @file + * Contains \Drupal\Core\Annotation\ContextDefinition. + */ + +namespace Drupal\Core\Annotation; + +/** + * @defgroup plugin_context context definition plugin metadata + * + * @{ + * When providing plugin annotations, contexts can be defined to support UI + * interactions through providing limits, and mapping contexts to appropriate + * plugins. Context definitions can be provided as such: + * @code + * context = { + * "node" = @ContextDefinition("entity:node") + * } + * @endcode + * Remove spaces after @ in your actual plugin - these are put into this sample + * code so that it is not recognized as an annotation. + * + * To add a label to a context definition use the "label" key: + * @code + * context = { + * "node" = @ContextDefinition("entity:node", label = @Translation("Node")) + * } + * @endcode + * + * Contexts are required unless otherwise specified. To make an optional + * context use the "required" key: + * @code + * context = { + * "node" = @ContextDefinition("entity:node", required = FALSE, label = @Translation("Node")) + * } + * @endcode + * + * To define multiple contexts, simply provide different key names in the + * context array: + * @code + * context = { + * "artist" = @ContextDefinition("entity:node", label = @Translation("Artist")), + * "album" = @ContextDefinition("entity:node", label = @Translation("Album")) + * } + * @endcode + * @} + */ +use Drupal\Component\Annotation\Plugin; + +/** + * Defines a context definition annotation object. + * + * Some plugins require various data contexts in order to function. This class + * supports that need by allowing the contexts to be easily defined within an + * annotation and return a ContextDefinitionInterface implementing class. + * + * @Annotation + * + * @ingroup plugin_context + */ +class ContextDefinition extends Plugin { + + /** + * The ContextDefinitionInterface object. + * + * @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface + */ + protected $definition; + + /** + * Constructs a new context definition object. + * + * @param array $values + * An associative array with the following keys: + * - value: The required data type. + * - label: (optional) The UI label of this context definition. + * - required: (optional) Whether the context definition is required. + * - multiple: (optional) Whether the context definition is multivalue. + * - description: (optional) The UI description of this context definition. + * - class: (optional) A custom ContextDefinitionInterface class. + * + * @throws \Exception + * Thrown when the class key is specified with a non + * ContextDefinitionInterface implementing class. + */ + public function __construct(array $values) { + $values += array( + 'required' => TRUE, + 'multiple' => FALSE, + 'label' => NULL, + 'description' => NULL, + ); + if (isset($values['class']) && !in_array('Drupal\Core\Plugin\Context\ContextDefinitionInterface', class_implements($values['class']))) { + throw new \Exception('ContextDefinition class must implement \Drupal\Core\Plugin\Context\ContextDefinitionInterface.'); + } + $class = isset($values['class']) ? $values['class'] : 'Drupal\Core\Plugin\Context\ContextDefinition'; + $this->definition = new $class($values['value'], $values['label'], $values['required'], $values['multiple'], $values['description']); + } + + /** + * Returns the value of an annotation. + * + * @return \Drupal\Core\Plugin\Context\ContextDefinitionInterface + */ + public function get() { + return $this->definition; + } + +} diff --git a/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php b/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php index 35587ebb4507896b9b57a1e6b6dbbfebd16ade31..528e662674d304ad0ea097f56c064344bb4df317 100644 --- a/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php +++ b/core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php @@ -7,7 +7,7 @@ namespace Drupal\Core\Condition; -use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\Component\Plugin\Exception\ContextException; /** * Resolves a set of conditions. @@ -30,7 +30,7 @@ protected function resolveConditions($conditions, $condition_logic) { try { $pass = $condition->execute(); } - catch (PluginException $e) { + catch (ContextException $e) { // If a condition is missing context, consider that a fail. $pass = FALSE; } diff --git a/core/lib/Drupal/Core/Condition/ConditionManager.php b/core/lib/Drupal/Core/Condition/ConditionManager.php index e47dd287229d0661e36705be98fc717bc03a2566..db0fa43bb49ca07d8662ce3c21f8b745549db8e7 100644 --- a/core/lib/Drupal/Core/Condition/ConditionManager.php +++ b/core/lib/Drupal/Core/Condition/ConditionManager.php @@ -50,6 +50,14 @@ public function __construct(\Traversable $namespaces, CacheBackendInterface $cac */ public function createInstance($plugin_id, array $configuration = array()) { $plugin = $this->factory->createInstance($plugin_id, $configuration); + + // If we receive any context values via config set it into the plugin. + if (!empty($configuration['context'])) { + foreach ($configuration['context'] as $name => $context) { + $plugin->setContextValue($name, $context); + } + } + return $plugin->setExecutableManager($this); } diff --git a/core/lib/Drupal/Core/Condition/ConditionPluginBase.php b/core/lib/Drupal/Core/Condition/ConditionPluginBase.php index 221e3b5f0e8c57be8bfe8dfae40219de923f860d..a36c7707571ca7760322b6e7c2409c5b26662ef0 100644 --- a/core/lib/Drupal/Core/Condition/ConditionPluginBase.php +++ b/core/lib/Drupal/Core/Condition/ConditionPluginBase.php @@ -20,6 +20,13 @@ */ abstract class ConditionPluginBase extends ExecutablePluginBase implements ConditionInterface { + /** + * {@inheritdoc} + */ + public static function contextDefinitions() { + return []; + } + /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Plugin/Context/Context.php b/core/lib/Drupal/Core/Plugin/Context/Context.php index 2d79d6f441dfab8f0638d6879622e39ce961655e..0366b717f2ee94afad05d7f35bb4f411a8a5ef2c 100644 --- a/core/lib/Drupal/Core/Plugin/Context/Context.php +++ b/core/lib/Drupal/Core/Plugin/Context/Context.php @@ -8,84 +8,99 @@ namespace Drupal\Core\Plugin\Context; use Drupal\Component\Plugin\Context\Context as ComponentContext; -use Drupal\Core\TypedData\ComplexDataInterface; -use Drupal\Core\TypedData\DataDefinition; -use Drupal\Core\TypedData\ListInterface; +use Drupal\Component\Plugin\Exception\ContextException; +use Drupal\Component\Utility\String; +use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\TypedData\TypedDataInterface; +use Drupal\Core\TypedData\TypedDataTrait; /** * A Drupal specific context wrapper class. - * - * The validate method is specifically overridden in order to support typed - * data definitions instead of just class names in the contextual definitions - * of plugins that extend ContextualPluginBase. */ -class Context extends ComponentContext { +class Context extends ComponentContext implements ContextInterface { + + use TypedDataTrait; /** - * Overrides \Drupal\Component\Plugin\Context\Context::getContextValue(). + * The data associated with the context. + * + * @var \Drupal\Core\TypedData\TypedDataInterface + */ + protected $contextData; + + /** + * The definition to which a context must conform. + * + * @var \Drupal\Core\Plugin\Context\ContextDefinitionInterface + */ + protected $contextDefinition; + + /** + * {@inheritdoc} */ public function getContextValue() { - $typed_value = parent::getContextValue(); - // If the typed data is complex, pass it on as typed data. Else pass on its - // plain value, such that e.g. a string will be directly returned as PHP - // string. - $is_complex = $typed_value instanceof ComplexDataInterface; - if (!$is_complex && $typed_value instanceof ListInterface) { - $is_complex = $typed_value[0] instanceof ComplexDataInterface; + if (!isset($this->contextData)) { + $definition = $this->getContextDefinition(); + if ($definition->isRequired()) { + $type = $definition->getDataType(); + throw new ContextException(String::format("The @type context is required and not present.", array('@type' => $type))); + } + return NULL; } - if ($typed_value instanceof TypedDataInterface && !$is_complex) { - return $typed_value->getValue(); + // Special case entities. + // @todo: Remove once entities do not implemented TypedDataInterface. + if ($this->contextData instanceof ContentEntityInterface) { + return $this->contextData; } - return $typed_value; + return $this->contextData->getValue(); } /** - * Implements \Drupal\Component\Plugin\Context\ContextInterface::setContextValue(). + * {@inheritdoc} */ public function setContextValue($value) { - // Make sure the value set is a typed data object. - if (!empty($this->contextDefinition['type']) && !$value instanceof TypedDataInterface) { - $value = \Drupal::typedDataManager()->create(new DataDefinition($this->contextDefinition), $value); + if ($value instanceof TypedDataInterface) { + return $this->setContextData($value); + } + else { + return $this->setContextData($this->getTypedDataManager()->create($this->contextDefinition->getDataDefinition(), $value)); } - parent::setContextValue($value); } /** - * Gets the context value as typed data object. - * - * parent::getContextValue() does not do all the processing required to - * return plain value of a TypedData object. This class overrides that method - * to return the appropriate values from TypedData objects, but the object - * itself can be useful as well, so this method is provided to allow for - * access to the TypedData object. Since parent::getContextValue() already - * does all the processing we need, we simply proxy to it here. - * - * @return \Drupal\Core\TypedData\TypedDataInterface + * {@inheritdoc} */ - public function getTypedContext() { - return parent::getContextValue(); + public function getConstraints() { + return $this->contextDefinition->getConstraints(); } /** - * Implements \Drupal\Component\Plugin\Context\ContextInterface::getConstraints(). + * {@inheritdoc} */ - public function getConstraints() { - if (!empty($this->contextDefinition['type'])) { - // If we do have typed data, leverage it for getting constraints. - return $this->getTypedContext()->getConstraints(); - } - return parent::getConstraints(); + public function getContextData() { + return $this->contextData; + } + + /** + * {@inheritdoc} + */ + public function setContextData(TypedDataInterface $data) { + $this->contextData = $data; + return $this; } /** - * Overrides \Drupal\Component\Plugin\Context\Context::getConstraints(). + * {@inheritdoc} + */ + public function getContextDefinition() { + return $this->contextDefinition; + } + + /** + * {@inheritdoc} */ public function validate() { - // If the context is typed data, defer to its validation. - if (!empty($this->contextDefinition['type'])) { - return $this->getTypedContext()->validate(); - } - return parent::validate(); + return $this->getContextData()->validate(); } + } diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php new file mode 100644 index 0000000000000000000000000000000000000000..93a639f6eec07963f6adbaeba62e3b94987495a8 --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php @@ -0,0 +1,227 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Plugin\Context\ContextDefinition. + */ + +namespace Drupal\Core\Plugin\Context; + +use Drupal\Core\TypedData\TypedDataTrait; + +/** + * Defines a class for context definitions. + */ +class ContextDefinition implements ContextDefinitionInterface { + + use TypedDataTrait; + + /** + * The data type of the data. + * + * @return string + * The data type. + */ + protected $dataType; + + /** + * The human-readable label. + * + * @return string + * The label. + */ + protected $label; + + /** + * The human-readable description. + * + * @return string|null + * The description, or NULL if no description is available. + */ + protected $description; + + /** + * Whether the data is multi-valued, i.e. a list of data items. + * + * @var bool + */ + protected $isMultiple = FALSE; + + /** + * Determines whether a data value is required. + * + * @var bool + * Whether a data value is required. + */ + protected $isRequired = TRUE; + + /** + * An array of constraints. + * + * @var array[] + */ + protected $constraints = []; + + /** + * Creates a new context definition. + * + * @param string $data_type + * The data type for which to create the context definition. Defaults to + * 'any'. + * + * @return static + * The created context definition object. + */ + public static function create($data_type = 'any') { + return new static( + $data_type + ); + } + + /** + * Constructs a new context definition object. + * + * @param string $data_type + * The required data type. + * @param mixed string|null $label + * The label of this context definition for the UI. + * @param bool $required + * Whether the context definition is required. + * @param bool $multiple + * Whether the context definition is multivalue. + * @param mixed string|null $description + * The description of this context definition for the UI. + */ + public function __construct($data_type = 'any', $label = NULL, $required = TRUE, $multiple = FALSE, $description = NULL) { + $this->dataType = $data_type; + $this->label = $label; + $this->isRequired = $required; + $this->isMultiple = $multiple; + $this->description = $description; + } + + /** + * {@inheritdoc} + */ + public function getDataType() { + return $this->dataType; + } + + /** + * {@inheritdoc} + */ + public function setDataType($data_type) { + $this->dataType = $data_type; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getLabel() { + return $this->label; + } + + /** + * {@inheritdoc} + */ + public function setLabel($label) { + $this->label = $label; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getDescription() { + return $this->description; + } + + /** + * {@inheritdoc} + */ + public function setDescription($description) { + $this->description = $description; + return $this; + } + + /** + * {@inheritdoc} + */ + public function isMultiple() { + return $this->isMultiple; + } + + /** + * {@inheritdoc} + */ + public function setMultiple($multiple = TRUE) { + $this->isMultiple = $multiple; + return $this; + } + + /** + * {@inheritdoc} + */ + public function isRequired() { + return $this->isRequired; + } + + /** + * {@inheritdoc} + */ + public function setRequired($required = TRUE) { + $this->isRequired = $required; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getConstraints() { + // @todo Apply defaults. + return $this->constraints; + } + + /** + * {@inheritdoc} + */ + public function getConstraint($constraint_name) { + $constraints = $this->getConstraints(); + return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL; + } + + /** + * {@inheritdoc} + */ + public function setConstraints(array $constraints) { + $this->constraints = $constraints; + return $this; + } + + /** + * {@inheritdoc} + */ + public function addConstraint($constraint_name, $options = NULL) { + $this->constraints[$constraint_name] = $options; + return $this; + } + + /** + * {@inheritdoc} + */ + public function getDataDefinition() { + if ($this->isMultiple()) { + $definition = $this->getTypedDataManager()->createListDataDefinition($this->getDataType()); + } + else { + $definition = $this->getTypedDataManager()->createDataDefinition($this->getDataType()); + } + $definition->setLabel($this->getLabel()) + ->setDescription($this->getDescription()) + ->setRequired($this->isRequired()) + ->setConstraints($this->getConstraints()); + return $definition; + } + +} diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextDefinitionInterface.php b/core/lib/Drupal/Core/Plugin/Context/ContextDefinitionInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..3472c94d7f5a835528b960527f82cc18911d0f00 --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Context/ContextDefinitionInterface.php @@ -0,0 +1,25 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Plugin\Context\ContextDefinitionInterface. + */ + +namespace Drupal\Core\Plugin\Context; + +use Drupal\Component\Plugin\Context\ContextDefinitionInterface as ComponentContextDefinitionInterface; + +/** + * Interface for context definitions. + */ +interface ContextDefinitionInterface extends ComponentContextDefinitionInterface { + + /** + * Returns the data definition of the defined context. + * + * @return \Drupal\Core\TypedData\DataDefinitionInterface + * The data definition object. + */ + public function getDataDefinition(); + +} diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php index 672c61366a5f358da604ebf71ea12c2c73239e43..100bf2901dae8575109ad14b42d5c2858dc6b8b0 100644 --- a/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php +++ b/core/lib/Drupal/Core/Plugin/Context/ContextHandler.php @@ -8,7 +8,7 @@ namespace Drupal\Core\Plugin\Context; use Drupal\Component\Plugin\ConfigurablePluginInterface; -use Drupal\Component\Plugin\Context\ContextInterface; +use Drupal\Component\Plugin\Context\ContextInterface as ComponentContextInterface; use Drupal\Component\Plugin\ContextAwarePluginInterface; use Drupal\Component\Plugin\Exception\ContextException; use Drupal\Component\Utility\String; @@ -97,7 +97,7 @@ public function checkRequirements(array $contexts, array $requirements) { * {@inheritdoc} */ public function getMatchingContexts(array $contexts, DataDefinitionInterface $definition) { - return array_filter($contexts, function (ContextInterface $context) use ($definition) { + return array_filter($contexts, function (ComponentContextInterface $context) use ($definition) { // @todo getContextDefinition() should return a DataDefinitionInterface. $context_definition = new DataDefinition($context->getContextDefinition()); diff --git a/core/lib/Drupal/Core/Plugin/Context/ContextInterface.php b/core/lib/Drupal/Core/Plugin/Context/ContextInterface.php new file mode 100644 index 0000000000000000000000000000000000000000..bb3861737af08c7777a26b879bd1447b701534b4 --- /dev/null +++ b/core/lib/Drupal/Core/Plugin/Context/ContextInterface.php @@ -0,0 +1,35 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\Plugin\Context\ContextInterface. + */ + +namespace Drupal\Core\Plugin\Context; + +use Drupal\Component\Plugin\Context\ContextInterface as ComponentContextInterface; +use Drupal\Core\TypedData\TypedDataInterface; + +/** + * Interface for context. + */ +interface ContextInterface extends ComponentContextInterface { + + /** + * Gets the context value as typed data object. + * + * @return \Drupal\Core\TypedData\TypedDataInterface + */ + public function getContextData(); + + /** + * Sets the context value as typed data object. + * + * @param \Drupal\Core\TypedData\TypedDataInterface $data + * The context value as a typed data object. + * + * @return $this + */ + public function setContextData(TypedDataInterface $data); + +} diff --git a/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php b/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php index 1f5fc7e8d944f427ed67a22d6ee94bf308ce9d1e..1fec19384259ab74f001056b06b3f819a4a1002e 100644 --- a/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php +++ b/core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php @@ -8,48 +8,45 @@ namespace Drupal\Core\Plugin; use Drupal\Component\Plugin\ContextAwarePluginBase as ComponentContextAwarePluginBase; +use Drupal\Component\Plugin\Exception\ContextException; use Drupal\Core\DependencyInjection\DependencySerializationTrait; use Drupal\Core\Plugin\Context\Context; -use Drupal\Component\Plugin\Discovery\DiscoveryInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; +use Drupal\Core\TypedData\TypedDataTrait; +use Drupal\Component\Plugin\Context\ContextInterface as ComponentContextInterface; +use Drupal\Core\Plugin\Context\ContextInterface; /** - * Drupal specific class for plugins that use context. - * - * This class specifically overrides setContextValue to use the core version of - * the Context class. This code is exactly the same as what is in Component - * ContextAwarePluginBase but it is using a different Context class. + * Base class for plugins that are context aware. */ abstract class ContextAwarePluginBase extends ComponentContextAwarePluginBase { + use TypedDataTrait; use StringTranslationTrait; use DependencySerializationTrait; /** - * Override of \Drupal\Component\Plugin\ContextAwarePluginBase::__construct(). + * {@inheritdoc} + * + * This code is identical to the Component in order to pick up a different + * Context class. */ - public function __construct(array $configuration, $plugin_id, $plugin_definition) { - $context = array(); - if (isset($configuration['context'])) { - $context = $configuration['context']; - unset($configuration['context']); - } - parent::__construct($configuration, $plugin_id, $plugin_definition); - foreach ($context as $key => $value) { - $context_definition = $this->getContextDefinition($key); - $this->context[$key] = new Context($context_definition); - $this->context[$key]->setContextValue($value); + public function getContext($name) { + // Check for a valid context value. + if (!isset($this->context[$name])) { + $this->context[$name] = new Context($this->getContextDefinition($name)); } + return $this->context[$name]; } /** - * Override of \Drupal\Component\Plugin\ContextAwarePluginBase::setContextValue(). + * {@inheritdoc} */ - public function setContextValue($name, $value) { - $context_definition = $this->getContextDefinition($name); - // Use the Drupal specific context class. - $this->context[$name] = new Context($context_definition); - $this->context[$name]->setContextValue($value); - return $this; + public function setContext($name, ComponentContextInterface $context) { + // Check that the context passed is an instance of our extended interface. + if (!$context instanceof ContextInterface) { + throw new ContextException("Passed $name context must be an instance of \\Drupal\\Core\\Plugin\\Context\\ContextInterface"); + } + parent::setContext($name, $context); } } diff --git a/core/lib/Drupal/Core/TypedData/TypedDataTrait.php b/core/lib/Drupal/Core/TypedData/TypedDataTrait.php new file mode 100644 index 0000000000000000000000000000000000000000..737a86a25e44595e9bcbea381ba0d4fc9ada3c8a --- /dev/null +++ b/core/lib/Drupal/Core/TypedData/TypedDataTrait.php @@ -0,0 +1,49 @@ +<?php + +/** + * @file + * Contains \Drupal\Core\TypedData\TypedDataTrait. + */ + +namespace Drupal\Core\TypedData; + +/** + * Wrapper methods for classes that needs typed data manager object. + */ +trait TypedDataTrait { + + /** + * The typed data manager used for creating the data types. + * + * @var \Drupal\Core\TypedData\TypedDataManager + */ + protected $typedDataManager; + + /** + * Sets the typed data manager. + * + * @param \Drupal\Core\TypedData\TypedDataManager $typed_data_manager + * The typed data manager. + * + * @return $this + */ + public function setTypedDataManager(TypedDataManager $typed_data_manager) { + $this->typedDataManager = $typed_data_manager; + return $this; + } + + /** + * Gets the typed data manager. + * + * @return \Drupal\Core\TypedData\TypedDataManager + * The typed data manager. + */ + public function getTypedDataManager() { + if (empty($this->typedDataManager)) { + $this->typedDataManager = \Drupal::typedDataManager(); + } + + return $this->typedDataManager; + } + +} diff --git a/core/modules/block/src/BlockBase.php b/core/modules/block/src/BlockBase.php index 258d23d7d13114a7422a96d1bf2d8e56c7142a84..813da27f085010cf8131a3360bab9314a5dda6e3 100644 --- a/core/modules/block/src/BlockBase.php +++ b/core/modules/block/src/BlockBase.php @@ -13,12 +13,10 @@ use Drupal\Core\Condition\ConditionAccessResolverTrait; use Drupal\Core\Condition\ConditionPluginBag; use Drupal\Core\Plugin\ContextAwarePluginBase; -use Drupal\block\BlockInterface; use Drupal\Component\Utility\Unicode; use Drupal\Component\Utility\NestedArray; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Cache\Cache; -use Drupal\Core\Cache\CacheableInterface; use Drupal\Core\Session\AccountInterface; /** @@ -48,6 +46,13 @@ abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginIn */ protected $conditionPluginManager; + /** + * {@inheritdoc} + */ + public static function contextDefinitions() { + return []; + } + /** * {@inheritdoc} */ diff --git a/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php b/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php index 054d0de805a255d9cc6e4497fe6a6f231338ba98..458417295c0f8382dc8b2744136c4be3bfe76352 100644 --- a/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php +++ b/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php @@ -9,6 +9,7 @@ use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\StringTranslation\StringTranslationTrait; /** @@ -39,10 +40,7 @@ public function __construct(LanguageManagerInterface $language_manager) { * {@inheritdoc} */ protected function determineBlockContext() { - $context = new Context(array( - 'type' => 'language', - 'label' => $this->t('Current language'), - )); + $context = new Context(new ContextDefinition('language', $this->t('Current language'))); $context->setContextValue($this->languageManager->getCurrentLanguage()); $this->addContext('language', $context); } diff --git a/core/modules/block/src/EventSubscriber/CurrentUserContext.php b/core/modules/block/src/EventSubscriber/CurrentUserContext.php index 14565aad913997bf22a649cc5ca41b2ab0399a13..2a5a19ebc85112afcf0d155721271a35ca2d6bd4 100644 --- a/core/modules/block/src/EventSubscriber/CurrentUserContext.php +++ b/core/modules/block/src/EventSubscriber/CurrentUserContext.php @@ -9,6 +9,7 @@ use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\Session\AccountInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; @@ -52,10 +53,7 @@ public function __construct(AccountInterface $account, EntityManagerInterface $e protected function determineBlockContext() { $current_user = $this->userStorage->load($this->account->id()); - $context = new Context(array( - 'type' => 'entity:user', - 'label' => $this->t('Current user'), - )); + $context = new Context(new ContextDefinition('entity:user', $this->t('Current user'))); $context->setContextValue($current_user); $this->addContext('current_user', $context); } diff --git a/core/modules/block/src/EventSubscriber/NodeRouteContext.php b/core/modules/block/src/EventSubscriber/NodeRouteContext.php index adadbe828c12d02efa086f593f931baa1bea1ce1..c1bd2bf273e87a426388c3c7cbd066c6ba9f600e 100644 --- a/core/modules/block/src/EventSubscriber/NodeRouteContext.php +++ b/core/modules/block/src/EventSubscriber/NodeRouteContext.php @@ -8,6 +8,7 @@ namespace Drupal\block\EventSubscriber; use Drupal\Core\Plugin\Context\Context; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\node\Entity\Node; @@ -38,7 +39,7 @@ public function __construct(RouteMatchInterface $route_match) { */ protected function determineBlockContext() { if (($route_object = $this->routeMatch->getRouteObject()) && ($route_contexts = $route_object->getOption('parameters')) && isset($route_contexts['node'])) { - $context = new Context($route_contexts['node']); + $context = new Context(new ContextDefinition($route_contexts['node']['type'])); if ($node = $this->routeMatch->getParameter('node')) { $context->setContextValue($node); } @@ -46,7 +47,7 @@ protected function determineBlockContext() { } elseif ($this->routeMatch->getRouteName() == 'node.add') { $node_type = $this->routeMatch->getParameter('node_type'); - $context = new Context(array('type' => 'entity:node')); + $context = new Context(new ContextDefinition('entity:node')); $context->setContextValue(Node::create(array('type' => $node_type->id()))); $this->addContext('node', $context); } diff --git a/core/modules/language/src/Plugin/Condition/Language.php b/core/modules/language/src/Plugin/Condition/Language.php index 41dab719c5f655256a451d06245a88097f266cb5..46a99cb11b822178762089cd642a4a86a70b9667 100644 --- a/core/modules/language/src/Plugin/Condition/Language.php +++ b/core/modules/language/src/Plugin/Condition/Language.php @@ -17,11 +17,10 @@ * id = "language", * label = @Translation("Language"), * context = { - * "language" = { - * "type" = "language" - * } + * "language" = @ContextDefinition("language", label = @Translation("Language")) * } * ) + * */ class Language extends ConditionPluginBase { diff --git a/core/modules/node/src/Plugin/Condition/NodeType.php b/core/modules/node/src/Plugin/Condition/NodeType.php index 66e57e3120c931be45518456a2d009af2df8e27e..7973e69593e4980aa06874582b299d2b659325bd 100644 --- a/core/modules/node/src/Plugin/Condition/NodeType.php +++ b/core/modules/node/src/Plugin/Condition/NodeType.php @@ -16,11 +16,10 @@ * id = "node_type", * label = @Translation("Node Bundle"), * context = { - * "node" = { - * "type" = "entity:node" - * } + * "node" = @ContextDefinition("entity:node", label = @Translation("Node")) * } * ) + * */ class NodeType extends ConditionPluginBase { diff --git a/core/modules/system/src/Tests/Plugin/ContextPluginTest.php b/core/modules/system/src/Tests/Plugin/ContextPluginTest.php index cd7dbfe8a0b1c49dcbb2f41e43f949332c04443a..b52dd0f58028fc15e98ae706138dc5accf4aa592 100644 --- a/core/modules/system/src/Tests/Plugin/ContextPluginTest.php +++ b/core/modules/system/src/Tests/Plugin/ContextPluginTest.php @@ -7,14 +7,15 @@ namespace Drupal\system\Tests\Plugin; -use Drupal\simpletest\DrupalUnitTestBase; +use Drupal\Component\Plugin\Exception\ContextException; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\plugin_test\Plugin\MockBlockManager; -use Drupal\Component\Plugin\Exception\PluginException; +use Drupal\simpletest\KernelTestBase; /** * Tests that context aware plugins function correctly. */ -class ContextPluginTest extends DrupalUnitTestBase { +class ContextPluginTest extends KernelTestBase { public static $modules = array('system', 'user', 'node', 'field', 'filter', 'text'); @@ -36,50 +37,31 @@ function testContext() { // Create a node, add it as context, catch the exception. $node = entity_create('node', array('title' => $name, 'type' => 'page')); - // Try to get a valid context that has not been set. + // Try to get context that is missing its definition. try { - $plugin->getContext('user'); + $plugin->getContextDefinition('not_exists'); $this->fail('The user context should not yet be set.'); } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The user context is not yet set.'); + catch (ContextException $e) { + $this->assertEqual($e->getMessage(), 'The not_exists context is not a valid context.'); } - // Try to get an invalid context. - try { - $plugin->getContext('node'); - $this->fail('The node context should not be a valid context.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The node context is not a valid context.'); - } + // Test the getContextDefinitions() method. + $user_context_definition = ContextDefinition::create('entity:user')->setLabel(t('User')); + $this->assertEqual($plugin->getContextDefinitions()['user']->getLabel(), $user_context_definition->getLabel()); - // Try to get a valid context value that has not been set. - try { - $plugin->getContextValue('user'); - $this->fail('The user context should not yet be set.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The user context is not yet set.'); - } + // Test the getContextDefinition() method for a valid context. + $this->assertEqual($plugin->getContextDefinition('user')->getLabel(), $user_context_definition->getLabel()); - // Try to call a method of the plugin that requires context before it has - // been set. - try { - $plugin->getTitle(); - $this->fail('The user context should not yet be set.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The user context is not yet set.'); - } + // Try to get a context with valid definition. + $this->assertNotNull($plugin->getContext('user'), 'Succeeded to get a context with a valid definition.'); - // Try to get a context value that is not valid. + // Try to get a value of a valid context, while this value has not been set. try { - $plugin->getContextValue('node'); - $this->fail('The node context should not be a valid context.'); + $plugin->getContextValue('user'); } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The node context is not a valid context.'); + catch(ContextException $e) { + $this->assertIdentical("The entity:user context is required and not present.", $e->getMessage(), 'Requesting a non-set value of a required context should throw a context exception.'); } // Try to pass the wrong class type as a context value. @@ -87,81 +69,22 @@ function testContext() { $violations = $plugin->validateContexts(); $this->assertTrue(!empty($violations), 'The provided context value does not pass validation.'); - // Set an appropriate context value appropriately and check to make sure - // its methods work as expected. + // Set an appropriate context value and check to make sure its methods work + // as expected. $user = entity_create('user', array('name' => $name)); $plugin->setContextValue('user', $user); - $this->assertEqual($user->label(), $plugin->getTitle()); - - // Test the getContextDefinitions() method. - $this->assertIdentical($plugin->getContextDefinitions(), array('user' => array('class' => 'Drupal\user\UserInterface'))); - - // Test the getContextDefinition() method for a valid context. - $this->assertEqual($plugin->getContextDefinition('user'), array('class' => 'Drupal\user\UserInterface')); - - // Test the getContextDefinition() method for an invalid context. - try { - $plugin->getContextDefinition('node'); - $this->fail('The node context should not be a valid context.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The node context is not a valid context.'); - } - - // Test typed data context plugins. - $typed_data_plugin = $manager->createInstance('string_context'); - // Try to get a valid context value that has not been set. - try { - $typed_data_plugin->getContextValue('string'); - $this->fail('The string context should not yet be set.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The string context is not yet set.'); - } - - // Try to call a method of the plugin that requires a context value before - // it has been set. - try { - $typed_data_plugin->getTitle(); - $this->fail('The string context should not yet be set.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The string context is not yet set.'); - } + $this->assertEqual($plugin->getContextValue('user')->getName(), $user->getName()); + $this->assertEqual($user->label(), $plugin->getTitle()); - // Set the context value appropriately and check the title. - $typed_data_plugin->setContextValue('string', $name); - $this->assertEqual($name, $typed_data_plugin->getTitle()); + // Test Optional context handling. + $plugin = $manager->createInstance('user_name_optional'); + $this->assertNull($plugin->getContextValue('user'), 'Requesting a non-set value of a valid context should return NULL.'); // Test Complex compound context handling. $complex_plugin = $manager->createInstance('complex_context'); - - // With no contexts set, try to get the contexts. - try { - $complex_plugin->getContexts(); - $this->fail('There should not be any contexts set yet.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'There are no set contexts.'); - } - - // With no contexts set, try to get the context values. - $values = $complex_plugin->getContextValues(); - $this->assertIdentical(array_filter($values), array(), 'There are no set contexts.'); - - // Set the user context value. $complex_plugin->setContextValue('user', $user); - // With only the user context set, try to get the contexts. - try { - $complex_plugin->getContexts(); - $this->fail('The node context should not yet be set.'); - } - catch (PluginException $e) { - $this->assertEqual($e->getMessage(), 'The node context is not yet set.'); - } - // With only the user context set, try to get the context values. $values = $complex_plugin->getContextValues(); $this->assertNull($values['node'], 'The node context is not yet set.'); diff --git a/core/modules/system/src/Tests/Plugin/DerivativeTest.php b/core/modules/system/src/Tests/Plugin/DerivativeTest.php index 8feeff62d887d72462995b65998b957f7d91d10d..0d634f57da705766b401978cba5b1441b25067bc 100644 --- a/core/modules/system/src/Tests/Plugin/DerivativeTest.php +++ b/core/modules/system/src/Tests/Plugin/DerivativeTest.php @@ -25,11 +25,11 @@ public static function getInfo() { */ function testDerivativeDecorator() { // Ensure that getDefinitions() returns the expected definitions. - $this->assertIdentical($this->mockBlockManager->getDefinitions(), $this->mockBlockExpectedDefinitions); + $this->assertEqual($this->mockBlockManager->getDefinitions(), $this->mockBlockExpectedDefinitions); // Ensure that getDefinition() returns the expected definition. foreach ($this->mockBlockExpectedDefinitions as $id => $definition) { - $this->assertIdentical($this->mockBlockManager->getDefinition($id), $definition); + $this->assertEqual($this->mockBlockManager->getDefinition($id), $definition); } // Ensure that NULL is returned as the definition of a non-existing base diff --git a/core/modules/system/src/Tests/Plugin/PluginTestBase.php b/core/modules/system/src/Tests/Plugin/PluginTestBase.php index 3a92dbdc22a9c6875c6864f689f5a08068783f8b..3508a053c555bfe62a80bbe8de32fae0a4fa3e2a 100644 --- a/core/modules/system/src/Tests/Plugin/PluginTestBase.php +++ b/core/modules/system/src/Tests/Plugin/PluginTestBase.php @@ -7,6 +7,7 @@ namespace Drupal\system\Tests\Plugin; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\simpletest\UnitTestBase; use Drupal\plugin_test\Plugin\TestPluginManager; use Drupal\plugin_test\Plugin\MockBlockManager; @@ -82,22 +83,26 @@ public function setUp() { 'label' => 'User name', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', 'context' => array( - 'user' => array('class' => 'Drupal\user\UserInterface') + 'user' => new ContextDefinition('entity:user', 'User'), + ), + ), + 'user_name_optional' => array( + 'label' => 'User name optional', + 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', + 'context' => array( + 'user' => new ContextDefinition('entity:user', 'User', FALSE), ), ), 'string_context' => array( 'label' => 'String typed data', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\TypedDataStringBlock', - 'context' => array( - 'string' => array('type' => 'string'), - ), ), 'complex_context' => array( 'label' => 'Complex context', 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockComplexContextBlock', 'context' => array( - 'user' => array('class' => 'Drupal\user\UserInterface'), - 'node' => array('class' => 'Drupal\node\NodeInterface'), + 'user' => new ContextDefinition('entity:user', 'User'), + 'node' => new ContextDefinition('entity:node', 'Node'), ), ), ); diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php index cd3d0306ae211ca03f5bd9d7f786c080627e47f5..6de31cd8655ed29fae04c20e31434271e2b4a4bf 100644 --- a/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php +++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php @@ -11,6 +11,7 @@ use Drupal\Component\Plugin\Discovery\StaticDiscovery; use Drupal\Component\Plugin\Discovery\DerivativeDiscoveryDecorator; use Drupal\Component\Plugin\Factory\ReflectionFactory; +use Drupal\Core\Plugin\Context\ContextDefinition; /** * Defines a plugin manager used by Plugin API derivative unit tests. @@ -77,7 +78,16 @@ public function __construct() { 'label' => t('User name'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', 'context' => array( - 'user' => array('class' => 'Drupal\user\UserInterface') + 'user' => new ContextDefinition('entity:user', t('User')), + ), + )); + + // An optional context version of the previous block plugin. + $this->discovery->setDefinition('user_name_optional', array( + 'label' => t('User name optional'), + 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockUserNameBlock', + 'context' => array( + 'user' => new ContextDefinition('entity:user', t('User'), FALSE), ), )); @@ -85,9 +95,6 @@ public function __construct() { $this->discovery->setDefinition('string_context', array( 'label' => t('String typed data'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\TypedDataStringBlock', - 'context' => array( - 'string' => array('type' => 'string'), - ), )); // A complex context plugin that requires both a user and node for context. @@ -95,8 +102,8 @@ public function __construct() { 'label' => t('Complex context'), 'class' => 'Drupal\plugin_test\Plugin\plugin_test\mock_block\MockComplexContextBlock', 'context' => array( - 'user' => array('class' => 'Drupal\user\UserInterface'), - 'node' => array('class' => 'Drupal\node\NodeInterface'), + 'user' => new ContextDefinition('entity:user', t('User')), + 'node' => new ContextDefinition('entity:node', t('Node')), ), )); diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockComplexContextBlock.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockComplexContextBlock.php index 1cb657a4a61da8dd62cae6cac2e5a825ff61d3be..8041c3a59812b57c548eb4a2055159844a725baa 100644 --- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockComplexContextBlock.php +++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockComplexContextBlock.php @@ -7,6 +7,7 @@ namespace Drupal\plugin_test\Plugin\plugin_test\mock_block; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\Plugin\ContextAwarePluginBase; /** diff --git a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockUserNameBlock.php b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockUserNameBlock.php index 9150167e5278a7840965326fb72d2ff547ac3788..07c7a798347dadd76dd3491530beb07ce6bccaca 100644 --- a/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockUserNameBlock.php +++ b/core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockUserNameBlock.php @@ -7,6 +7,7 @@ namespace Drupal\plugin_test\Plugin\plugin_test\mock_block; +use Drupal\Core\Plugin\Context\ContextDefinition; use Drupal\Core\Plugin\ContextAwarePluginBase; /** diff --git a/core/modules/user/src/Plugin/Condition/UserRole.php b/core/modules/user/src/Plugin/Condition/UserRole.php index cd38ee3686f510539d812d67e4c7f2c35970588b..872b18a7b1d8f9f780702c0c32e812f0c95daadc 100644 --- a/core/modules/user/src/Plugin/Condition/UserRole.php +++ b/core/modules/user/src/Plugin/Condition/UserRole.php @@ -8,6 +8,7 @@ namespace Drupal\user\Plugin\Condition; use Drupal\Core\Condition\ConditionPluginBase; +use Drupal\Core\Plugin\Context\ContextDefinition; /** * Provides a 'User Role' condition. @@ -16,11 +17,10 @@ * id = "user_role", * label = @Translation("User Role"), * context = { - * "user" = { - * "type" = "entity:user" - * } + * "user" = @ContextDefinition("entity:user", label = @Translation("User")) * } * ) + * */ class UserRole extends ConditionPluginBase { diff --git a/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php b/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php index dc676c5641c61898f938c4c88d02e43793f459b0..8081ea8c7ae02394bb4419d7a8ea9dde9a181e02 100644 --- a/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php +++ b/core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php @@ -7,8 +7,8 @@ namespace Drupal\Tests\Core\Condition; +use Drupal\Component\Plugin\Exception\ContextException; use Drupal\Core\Condition\ConditionAccessResolverTrait; -use Drupal\Component\Plugin\Exception\PluginException; use Drupal\Tests\UnitTestCase; /** @@ -58,7 +58,7 @@ public function providerTestResolveConditions() { $condition_exception = $this->getMock('Drupal\Core\Condition\ConditionInterface'); $condition_exception->expects($this->any()) ->method('execute') - ->will($this->throwException(new PluginException())); + ->will($this->throwException(new ContextException())); $conditions = array(); $data[] = array($conditions, 'and', TRUE);