summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwebchick2014-06-25 21:56:47 (GMT)
committerwebchick2014-06-25 23:15:36 (GMT)
commit3a1f6fd99947cf156bad762ee2dd5814ba46475f (patch)
treebbd6a311b570b51e3e2b57c5380e4f9d8224a013
parent8a44b5c742feae5e2919d5a21ce49c53cee99341 (diff)
Issue #2281635 by fubhy, er.pushpinderrana, EclipseGc, blueminds, tim.plunkett: \Drupal\Core\Plugin\Context\Context should use a ContextDefinition class instead of arrays.
-rw-r--r--core/lib/Drupal/Component/Plugin/Context/Context.php20
-rw-r--r--core/lib/Drupal/Component/Plugin/Context/ContextDefinitionInterface.php158
-rw-r--r--core/lib/Drupal/Component/Plugin/Context/ContextInterface.php8
-rw-r--r--core/lib/Drupal/Component/Plugin/ContextAwarePluginBase.php63
-rw-r--r--core/lib/Drupal/Component/Plugin/ContextAwarePluginInterface.php14
-rw-r--r--core/lib/Drupal/Core/Annotation/ContextDefinition.php110
-rw-r--r--core/lib/Drupal/Core/Condition/ConditionAccessResolverTrait.php4
-rw-r--r--core/lib/Drupal/Core/Condition/ConditionManager.php8
-rw-r--r--core/lib/Drupal/Core/Condition/ConditionPluginBase.php7
-rw-r--r--core/lib/Drupal/Core/Plugin/Context/Context.php113
-rw-r--r--core/lib/Drupal/Core/Plugin/Context/ContextDefinition.php227
-rw-r--r--core/lib/Drupal/Core/Plugin/Context/ContextDefinitionInterface.php25
-rw-r--r--core/lib/Drupal/Core/Plugin/Context/ContextHandler.php4
-rw-r--r--core/lib/Drupal/Core/Plugin/Context/ContextInterface.php35
-rw-r--r--core/lib/Drupal/Core/Plugin/ContextAwarePluginBase.php47
-rw-r--r--core/lib/Drupal/Core/TypedData/TypedDataTrait.php49
-rw-r--r--core/modules/block/src/BlockBase.php9
-rw-r--r--core/modules/block/src/EventSubscriber/CurrentLanguageContext.php6
-rw-r--r--core/modules/block/src/EventSubscriber/CurrentUserContext.php6
-rw-r--r--core/modules/block/src/EventSubscriber/NodeRouteContext.php5
-rw-r--r--core/modules/language/src/Plugin/Condition/Language.php5
-rw-r--r--core/modules/node/src/Plugin/Condition/NodeType.php5
-rw-r--r--core/modules/system/src/Tests/Plugin/ContextPluginTest.php129
-rw-r--r--core/modules/system/src/Tests/Plugin/DerivativeTest.php4
-rw-r--r--core/modules/system/src/Tests/Plugin/PluginTestBase.php17
-rw-r--r--core/modules/system/tests/modules/plugin_test/src/Plugin/MockBlockManager.php19
-rw-r--r--core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockComplexContextBlock.php1
-rw-r--r--core/modules/system/tests/modules/plugin_test/src/Plugin/plugin_test/mock_block/MockUserNameBlock.php1
-rw-r--r--core/modules/user/src/Plugin/Condition/UserRole.php6
-rw-r--r--core/tests/Drupal/Tests/Core/Condition/ConditionAccessResolverTraitTest.php4
30 files changed, 844 insertions, 265 deletions
diff --git a/core/lib/Drupal/Component/Plugin/Context/Context.php b/core/lib/Drupal/Component/Plugin/Context/Context.php
index b117a72..f301583 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 @@ class Context implements ContextInterface {
* 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 0000000..3d2337b
--- /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 c5cd743..f22202b 100644
--- a/core/lib/Drupal/Component/Plugin/Context/ContextInterface.php
+++ b/core/lib/Drupal/Component/Plugin/Context/ContextInterface.php
@@ -33,13 +33,11 @@ interface ContextInterface {
/**
* 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 f04f07d..b684630 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 @@ abstract class ContextAwarePluginBase extends PluginBase implements ContextAware
}
/**
- * Implements \Drupal\Component\Plugin\ContextAwarePluginInterface::getContextDefinitions().
+ * {@inheritdoc}
*/
public function getContextDefinitions() {
$definition = $this->getPluginDefinition();
@@ -63,50 +63,47 @@ abstract class ContextAwarePluginBase extends PluginBase implements ContextAware
}
/**
- * 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 @@ abstract class ContextAwarePluginBase extends PluginBase implements ContextAware
}
/**
- * 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 f29119d..9cb56a1 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 @@ interface ContextAwarePluginInterface extends PluginInspectionInterface {
* @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);
@@ -90,6 +90,16 @@ interface ContextAwarePluginInterface extends PluginInspectionInterface {
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.
*
* @param string $name
diff --git a/core/lib/Drupal/Core/Annotation/ContextDefinition.php b/core/lib/Drupal/Core/Annotation/ContextDefinition.php
new file mode 100644
index 0000000..25bc272
--- /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 35587eb..528e662 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 @@ trait ConditionAccessResolverTrait {
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 e47dd28..db0fa43 100644
--- a/core/lib/Drupal/Core/Condition/ConditionManager.php
+++ b/core/lib/Drupal/Core/Condition/ConditionManager.php
@@ -50,6 +50,14 @@ class ConditionManager extends DefaultPluginManager implements ExecutableManager
*/
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 221e3b5..a36c770 100644
--- a/core/lib/Drupal/Core/Condition/ConditionPluginBase.php
+++ b/core/lib/Drupal/Core/Condition/ConditionPluginBase.php
@@ -23,6 +23,13 @@ abstract class ConditionPluginBase extends ExecutablePluginBase implements Condi
/**
* {@inheritdoc}
*/
+ public static function contextDefinitions() {
+ return [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function __construct(array $configuration, $plugin_id, $plugin_definition) {
parent::__construct($configuration, $plugin_id, $plugin_definition);
diff --git a/core/lib/Drupal/Core/Plugin/Context/Context.php b/core/lib/Drupal/Core/Plugin/Context/Context.php
index 2d79d6f..0366b71 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 0000000..93a639f
--- /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 0000000..3472c94
--- /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 672c613..100bf29 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 @@ class ContextHandler implements ContextHandlerInterface {
* {@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 0000000..bb38617
--- /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 1f5fc7e..1fec193 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 0000000..737a86a
--- /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 258d23d..813da27 100644
--- a/core/modules/block/src/BlockBase.php
+++ b/core/modules/block/src/BlockBase.php
@@ -13,12 +13,10 @@ use Drupal\Component\Plugin\ContextAwarePluginInterface;
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;
/**
@@ -51,6 +49,13 @@ abstract class BlockBase extends ContextAwarePluginBase implements BlockPluginIn
/**
* {@inheritdoc}
*/
+ public static function contextDefinitions() {
+ return [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function label() {
if (!empty($this->configuration['label'])) {
return $this->configuration['label'];
diff --git a/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php b/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php
index 054d0de..4584172 100644
--- a/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php
+++ b/core/modules/block/src/EventSubscriber/CurrentLanguageContext.php
@@ -9,6 +9,7 @@ namespace Drupal\block\EventSubscriber;
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 @@ class CurrentLanguageContext extends BlockConditionContextSubscriberBase {
* {@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 14565aa..2a5a19e 100644
--- a/core/modules/block/src/EventSubscriber/CurrentUserContext.php
+++ b/core/modules/block/src/EventSubscriber/CurrentUserContext.php
@@ -9,6 +9,7 @@ namespace Drupal\block\EventSubscriber;
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 @@ class CurrentUserContext extends BlockConditionContextSubscriberBase {
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 adadbe8..c1bd2bf 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 @@ class NodeRouteContext extends BlockConditionContextSubscriberBase {
*/
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 @@ class NodeRouteContext extends BlockConditionContextSubscriberBase {
}
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 41dab71..46a99cb 100644
--- a/core/modules/language/src/Plugin/Condition/Language.php
+++ b/core/modules/language/src/Plugin/Condition/Language.php
@@ -17,11 +17,10 @@ use Drupal\Core\Language\LanguageInterface;
* 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 66e57e3..7973e69 100644
--- a/core/modules/node/src/Plugin/Condition/NodeType.php
+++ b/core/modules/node/src/Plugin/Condition/NodeType.php
@@ -16,11 +16,10 @@ use Drupal\Core\Condition\ConditionPluginBase;
* 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 cd7dbfe..b52dd0f 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 @@ class ContextPluginTest extends DrupalUnitTestBase {
// 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 @@ class ContextPluginTest extends DrupalUnitTestBase {
$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 8feeff6..0d634f5 100644
--- a/core/modules/system/src/Tests/Plugin/DerivativeTest.php
+++ b/core/modules/system/src/Tests/Plugin/DerivativeTest.php
@@ -25,11 +25,11 @@ class DerivativeTest extends PluginTestBase {
*/
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 3a92dbd..3508a05 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 @@ abstract class PluginTestBase extends UnitTestBase {
'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 cd3d030..6de31cd 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\PluginManagerBase;
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 @@ class MockBlockManager extends PluginManagerBase {
'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 @@ class MockBlockManager extends PluginManagerBase {
$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 @@ class MockBlockManager extends PluginManagerBase {
'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 1cb657a..8041c3a 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 9150167..07c7a79 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 cd38ee3..872b18a 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 @@ use Drupal\Core\Condition\ConditionPluginBase;
* 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 dc676c5..8081ea8 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 @@ class ConditionAccessResolverTraitTest extends UnitTestCase {
$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);