ContextHandler.php 5.09 KB
Newer Older
1
2
3
4
5
6
7
8
9
<?php

/**
 * @file
 * Contains \Drupal\Core\Plugin\Context\ContextHandler.
 */

namespace Drupal\Core\Plugin\Context;

10
use Drupal\Component\Plugin\ConfigurablePluginInterface;
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
use Drupal\Component\Plugin\ContextAwarePluginInterface;
use Drupal\Component\Plugin\Exception\ContextException;
use Drupal\Component\Utility\String;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\Core\TypedData\DataDefinition;
use Drupal\Core\TypedData\TypedDataManager;

/**
 * Provides methods to handle sets of contexts.
 */
class ContextHandler implements ContextHandlerInterface {

  /**
   * The typed data manager.
   *
   * @var \Drupal\Core\TypedData\TypedDataManager
   */
  protected $typedDataManager;

  /**
   * Constructs a new ContextHandler.
   *
   * @param \Drupal\Core\TypedData\TypedDataManager $typed_data
   *   The typed data manager.
   */
  public function __construct(TypedDataManager $typed_data) {
    $this->typedDataManager = $typed_data;
  }

  /**
   * {@inheritdoc}
   */
  public function filterPluginDefinitionsByContexts(array $contexts, array $definitions) {
    return array_filter($definitions, function ($plugin_definition) use ($contexts) {
      // If this plugin doesn't need any context, it is available to use.
      if (!isset($plugin_definition['context'])) {
        return TRUE;
      }

      // Build an array of requirements out of the contexts specified by the
      // plugin definition.
      $requirements = array();
53
      /** @var $plugin_context \Drupal\Core\Plugin\Context\ContextDefinitionInterface */
54
      foreach ($plugin_definition['context'] as $context_id => $plugin_context) {
55
56
        $definition = $this->typedDataManager->getDefinition($plugin_context->getDataType());
        $definition['type'] = $plugin_context->getDataType();
57
58
59

        // If the plugin specifies additional constraints, add them to the
        // constraints defined by the plugin type.
60
        if ($plugin_constraints = $plugin_context->getConstraints()) {
61
62
63
64
65
          // Ensure the array exists before adding in constraints.
          if (!isset($definition['constraints'])) {
            $definition['constraints'] = array();
          }

66
          $definition['constraints'] += $plugin_constraints;
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
        }

        // Assume the requirement is required if unspecified.
        if (!isset($definition['required'])) {
          $definition['required'] = TRUE;
        }

        // @todo Use context definition objects after
        //   https://drupal.org/node/2281635.
        $requirements[$context_id] = new DataDefinition($definition);
      }

      // Check the set of contexts against the requirements.
      return $this->checkRequirements($contexts, $requirements);
    });
  }

  /**
   * {@inheritdoc}
   */
  public function checkRequirements(array $contexts, array $requirements) {
    foreach ($requirements as $requirement) {
      if ($requirement->isRequired() && !$this->getMatchingContexts($contexts, $requirement)) {
        return FALSE;
      }
    }
    return TRUE;
  }

  /**
   * {@inheritdoc}
   */
  public function getMatchingContexts(array $contexts, DataDefinitionInterface $definition) {
100
101
    return array_filter($contexts, function (ContextInterface $context) use ($definition) {
      $context_definition = $context->getContextDefinition()->getDataDefinition();
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124

      // If the data types do not match, this context is invalid.
      if ($definition->getDataType() != $context_definition->getDataType()) {
        return FALSE;
      }

      // If any constraint does not match, this context is invalid.
      // @todo This is too restrictive, consider only relying on data types.
      foreach ($definition->getConstraints() as $constraint_name => $constraint) {
        if ($context_definition->getConstraint($constraint_name) != $constraint) {
          return FALSE;
        }
      }

      // All contexts with matching data type and contexts are valid.
      return TRUE;
    });
  }

  /**
   * {@inheritdoc}
   */
  public function applyContextMapping(ContextAwarePluginInterface $plugin, $contexts, $mappings = array()) {
125
126
127
128
129
130
    if ($plugin instanceof ConfigurablePluginInterface) {
      $configuration = $plugin->getConfiguration();
      if (isset($configuration['context_mapping'])) {
        $mappings += array_flip($configuration['context_mapping']);
      }
    }
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
    $plugin_contexts = $plugin->getContextDefinitions();
    // Loop through each context and set it on the plugin if it matches one of
    // the contexts expected by the plugin.
    foreach ($contexts as $name => $context) {
      // If this context was given a specific name, use that.
      $assigned_name = isset($mappings[$name]) ? $mappings[$name] : $name;
      if (isset($plugin_contexts[$assigned_name])) {
        // This assignment has been used, remove it.
        unset($mappings[$name]);
        $plugin->setContextValue($assigned_name, $context->getContextValue());
      }
    }

    // If there are any mappings that were not satisfied, throw an exception.
    if (!empty($mappings)) {
      throw new ContextException(String::format('Assigned contexts were not satisfied: @mappings', array('@mappings' => implode(',', $mappings))));
    }
  }

}