Pull merge.
[yaffs-website] / web / core / lib / Drupal / Core / Plugin / Context / ContextDefinition.php
index 1c252e2961011c8570a3b77fd25a7e49d8ed1a96..9d8c81f588714182d5cb4de1ea695b73dd7396d8 100644 (file)
@@ -10,14 +10,17 @@ use Drupal\Core\TypedData\TypedDataTrait;
  */
 class ContextDefinition implements ContextDefinitionInterface {
 
-  use DependencySerializationTrait;
+  use DependencySerializationTrait {
+    __sleep as traitSleep;
+    __wakeup as traitWakeup;
+  }
 
   use TypedDataTrait;
 
   /**
    * The data type of the data.
    *
-   * @return string
+   * @var string
    *   The data type.
    */
   protected $dataType;
@@ -25,7 +28,7 @@ class ContextDefinition implements ContextDefinitionInterface {
   /**
    * The human-readable label.
    *
-   * @return string
+   * @var string
    *   The label.
    */
   protected $label;
@@ -33,7 +36,7 @@ class ContextDefinition implements ContextDefinitionInterface {
   /**
    * The human-readable description.
    *
-   * @return string|null
+   * @var string|null
    *   The description, or NULL if no description is available.
    */
   protected $description;
@@ -67,6 +70,38 @@ class ContextDefinition implements ContextDefinitionInterface {
    */
   protected $constraints = [];
 
+  /**
+   * An EntityContextDefinition instance, for backwards compatibility.
+   *
+   * If this context is created with a data type that starts with 'entity:',
+   * this property will be an instance of EntityContextDefinition, and certain
+   * methods of this object will delegate to their overridden counterparts in
+   * $this->entityContextDefinition.
+   *
+   * This property should be kept private so that it is only accessible to this
+   * class for backwards compatibility reasons. It will be removed in Drupal 9.
+   *
+   * @deprecated
+   *   Constructing a context definition for an entity type (i.e., the data type
+   *   begins with 'entity:') is deprecated in Drupal 8.6.0. Instead, use
+   *   the static factory methods of EntityContextDefinition to create context
+   *   definitions for entity types, or the static ::create() method of this
+   *   class for any other data type. See https://www.drupal.org/node/2976400
+   *   for more information.
+   *
+   * @see ::__construct()
+   * @see ::__sleep()
+   * @see ::__wakeup()
+   * @see ::getConstraintObjects()
+   * @see ::getSampleValues()
+   * @see ::initializeEntityContextDefinition()
+   * @see https://www.drupal.org/node/2932462
+   * @see https://www.drupal.org/node/2976400
+   *
+   * @var \Drupal\Core\Plugin\Context\EntityContextDefinition
+   */
+  private $entityContextDefinition;
+
   /**
    * Creates a new context definition.
    *
@@ -106,6 +141,11 @@ class ContextDefinition implements ContextDefinitionInterface {
     $this->isMultiple = $multiple;
     $this->description = $description;
     $this->defaultValue = $default_value;
+
+    if (strpos($data_type, 'entity:') === 0 && !($this instanceof EntityContextDefinition)) {
+      @trigger_error('Constructing a ContextDefinition object for an entity type is deprecated in Drupal 8.6.0. Use ' . __NAMESPACE__ . '\EntityContextDefinition instead. See https://www.drupal.org/node/2976400 for more information.', E_USER_DEPRECATED);
+      $this->initializeEntityContextDefinition();
+    }
   }
 
   /**
@@ -202,6 +242,11 @@ class ContextDefinition implements ContextDefinitionInterface {
    * {@inheritdoc}
    */
   public function getConstraints() {
+    // If the backwards compatibility layer is present, delegate to that.
+    if ($this->entityContextDefinition) {
+      return $this->entityContextDefinition->getConstraints();
+    }
+
     // @todo Apply defaults.
     return $this->constraints;
   }
@@ -210,6 +255,11 @@ class ContextDefinition implements ContextDefinitionInterface {
    * {@inheritdoc}
    */
   public function getConstraint($constraint_name) {
+    // If the backwards compatibility layer is present, delegate to that.
+    if ($this->entityContextDefinition) {
+      return $this->entityContextDefinition->getConstraint($constraint_name);
+    }
+
     $constraints = $this->getConstraints();
     return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
   }
@@ -218,6 +268,11 @@ class ContextDefinition implements ContextDefinitionInterface {
    * {@inheritdoc}
    */
   public function setConstraints(array $constraints) {
+    // If the backwards compatibility layer is present, delegate to that.
+    if ($this->entityContextDefinition) {
+      $this->entityContextDefinition->setConstraint();
+    }
+
     $this->constraints = $constraints;
     return $this;
   }
@@ -226,6 +281,11 @@ class ContextDefinition implements ContextDefinitionInterface {
    * {@inheritdoc}
    */
   public function addConstraint($constraint_name, $options = NULL) {
+    // If the backwards compatibility layer is present, delegate to that.
+    if ($this->entityContextDefinition) {
+      $this->entityContextDefinition->addConstraint($constraint_name, $options);
+    }
+
     $this->constraints[$constraint_name] = $options;
     return $this;
   }
@@ -252,4 +312,124 @@ class ContextDefinition implements ContextDefinitionInterface {
     return $definition;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function isSatisfiedBy(ContextInterface $context) {
+    $definition = $context->getContextDefinition();
+    // If the data types do not match, this context is invalid unless the
+    // expected data type is any, which means all data types are supported.
+    if ($this->getDataType() != 'any' && $definition->getDataType() != $this->getDataType()) {
+      return FALSE;
+    }
+
+    // Get the value for this context, either directly if possible or by
+    // introspecting the definition.
+    if ($context->hasContextValue()) {
+      $values = [$context->getContextData()];
+    }
+    elseif ($definition instanceof self) {
+      if ($this->entityContextDefinition) {
+        $values = $this->entityContextDefinition->getSampleValues();
+      }
+      else {
+        $values = $definition->getSampleValues();
+      }
+    }
+    else {
+      $values = [];
+    }
+
+    $validator = $this->getTypedDataManager()->getValidator();
+    foreach ($values as $value) {
+      $constraints = array_values($this->getConstraintObjects());
+      $violations = $validator->validate($value, $constraints);
+      foreach ($violations as $delta => $violation) {
+        // Remove any violation that does not correspond to the constraints.
+        if (!in_array($violation->getConstraint(), $constraints)) {
+          $violations->remove($delta);
+        }
+      }
+      // If a value has no violations then the requirement is satisfied.
+      if (!$violations->count()) {
+        return TRUE;
+      }
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Returns typed data objects representing this context definition.
+   *
+   * This should return as many objects as needed to reflect the variations of
+   * the constraints it supports.
+   *
+   * @yield \Drupal\Core\TypedData\TypedDataInterface
+   *   The set of typed data object.
+   */
+  protected function getSampleValues() {
+    yield $this->getTypedDataManager()->create($this->getDataDefinition());
+  }
+
+  /**
+   * Extracts an array of constraints for a context definition object.
+   *
+   * @return \Symfony\Component\Validator\Constraint[]
+   *   A list of applied constraints for the context definition.
+   */
+  protected function getConstraintObjects() {
+    // If the backwards compatibility layer is present, delegate to that.
+    if ($this->entityContextDefinition) {
+      return $this->entityContextDefinition->getConstraintObjects();
+    }
+
+    $constraint_definitions = $this->getConstraints();
+
+    $validation_constraint_manager = $this->getTypedDataManager()->getValidationConstraintManager();
+    $constraints = [];
+    foreach ($constraint_definitions as $constraint_name => $constraint_definition) {
+      $constraints[$constraint_name] = $validation_constraint_manager->create($constraint_name, $constraint_definition);
+    }
+
+    return $constraints;
+  }
+
+  /**
+   * Implements magic __sleep() method.
+   */
+  public function __sleep() {
+    return array_diff($this->traitSleep(), ['entityContextDefinition']);
+  }
+
+  /**
+   * Implements magic __wakeup() method.
+   */
+  public function __wakeup() {
+    $this->traitWakeup();
+
+    if (strpos($this->getDataType(), 'entity:') === 0) {
+      $this->initializeEntityContextDefinition();
+    }
+  }
+
+  /**
+   * Initializes $this->entityContextDefinition for backwards compatibility.
+   *
+   * This method should be kept private so that it is only accessible to this
+   * class for backwards compatibility reasons. It will be removed in Drupal 9.
+   *
+   * @deprecated
+   */
+  private function initializeEntityContextDefinition() {
+    $this->entityContextDefinition = EntityContextDefinition::create()
+      ->setDataType($this->getDataType())
+      ->setLabel($this->getLabel())
+      ->setRequired($this->isRequired())
+      ->setMultiple($this->isMultiple())
+      ->setDescription($this->getDescription())
+      ->setConstraints($this->getConstraints())
+      ->setDefaultValue($this->getDefaultValue());
+  }
+
 }