Pathologic was missing because of a .git folder inside.
[yaffs-website] / web / modules / contrib / ctools / src / TypedDataResolver.php
1 <?php
2
3 namespace Drupal\ctools;
4
5
6 use Drupal\Core\Field\BaseFieldDefinition;
7 use Drupal\Core\Plugin\Context\Context;
8 use Drupal\Core\Plugin\Context\ContextDefinition;
9 use Drupal\Core\Plugin\Context\ContextDefinitionInterface;
10 use Drupal\Core\Plugin\Context\ContextInterface;
11 use Drupal\Core\StringTranslation\TranslationInterface;
12 use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
13 use Drupal\Core\TypedData\DataReferenceDefinitionInterface;
14 use Drupal\Core\TypedData\DataReferenceInterface;
15 use Drupal\Core\TypedData\ListDataDefinitionInterface;
16 use Drupal\Core\TypedData\ListInterface;
17 use Drupal\Core\TypedData\TypedDataManagerInterface;
18
19 class TypedDataResolver {
20
21   /**
22    * The typed data manager.
23    *
24    * @var \Drupal\Core\TypedData\TypedDataManagerInterface
25    */
26   protected $manager;
27
28   /**
29    * The string translation service.
30    *
31    * @var \Drupal\Core\StringTranslation\TranslationInterface
32    */
33   protected $translation;
34
35   /**
36    * @param \Drupal\Core\TypedData\TypedDataManagerInterface $manager
37    *   The typed data manager.
38    * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
39    *   The string translation service.
40    */
41   public function __construct(TypedDataManagerInterface $manager, TranslationInterface $translation) {
42     $this->manager = $manager;
43     $this->translation = $translation;
44   }
45
46   /**
47    * Convert a property to a context.
48    *
49    * This method will respect the value of contexts as well, so if a context
50    * object is pass that contains a value, the appropriate value will be
51    * extracted and injected into the resulting context object if available.
52    *
53    * @param string $property_path
54    *   The name of the property.
55    * @param \Drupal\Core\Plugin\Context\ContextInterface $context
56    *   The context from which we will extract values if available.
57    *
58    * @return \Drupal\Core\Plugin\Context\Context
59    *   A context object that represents the definition & value of the property.
60    * @throws \Exception
61    */
62   public function getContextFromProperty($property_path, ContextInterface $context) {
63     $value = NULL;
64     $data_definition = NULL;
65     if ($context->hasContextValue()) {
66       /** @var \Drupal\Core\TypedData\ComplexDataInterface $data */
67       $data = $context->getContextData();
68       foreach (explode(':', $property_path) as $name) {
69
70         if ($data instanceof ListInterface) {
71           if (!is_numeric($name)) {
72             // Implicitly default to delta 0 for lists when not specified.
73             $data = $data->first();
74           }
75           else {
76             // If we have a delta, fetch it and continue with the next part.
77             $data = $data->get($name);
78             continue;
79           }
80         }
81
82         // Forward to the target value if this is a data reference.
83         if ($data instanceof DataReferenceInterface) {
84           $data = $data->getTarget();
85         }
86
87         if (!$data->getDataDefinition()->getPropertyDefinition($name)) {
88           throw new \Exception("Unknown property $name in property path $property_path");
89         }
90         $data = $data->get($name);
91       }
92
93       $value = $data->getValue();
94       $data_definition = $data instanceof DataReferenceInterface ? $data->getDataDefinition()->getTargetDefinition() : $data->getDataDefinition();
95     }
96     else {
97       /** @var \Drupal\Core\TypedData\ComplexDataDefinitionInterface $data_definition */
98       $data_definition = $context->getContextDefinition()->getDataDefinition();
99       foreach (explode(':', $property_path) as $name) {
100
101         if ($data_definition instanceof ListDataDefinitionInterface) {
102           $data_definition = $data_definition->getItemDefinition();
103
104           // If the delta was specified explicitly, continue with the next part.
105           if (is_numeric($name)) {
106             continue;
107           }
108         }
109
110         // Forward to the target definition if this is a data reference
111         // definition.
112         if ($data_definition instanceof DataReferenceDefinitionInterface) {
113           $data_definition = $data_definition->getTargetDefinition();
114         }
115
116         if (!$data_definition->getPropertyDefinition($name)) {
117           throw new \Exception("Unknown property $name in property path $property_path");
118         }
119         $data_definition = $data_definition->getPropertyDefinition($name);
120       }
121
122       // Forward to the target definition if this is a data reference
123       // definition.
124       if ($data_definition instanceof DataReferenceDefinitionInterface) {
125         $data_definition = $data_definition->getTargetDefinition();
126       }
127     }
128     $context_definition = new ContextDefinition($data_definition->getDataType(), $data_definition->getLabel(), $data_definition->isRequired(), FALSE, $data_definition->getDescription());
129     return new Context($context_definition, $value);
130   }
131
132   /**
133    * Extracts a context from an array of contexts by a tokenized pattern.
134    *
135    * This is more than simple isset/empty checks on the contexts array. The
136    * pattern could be node:uid:name which will iterate over all provided
137    * contexts in the array for one named 'node', it will then load the data
138    * definition of 'node' and check for a property named 'uid'. This will then
139    * set a new (temporary) context on the array and recursively call itself to
140    * navigate through related properties all the way down until the request
141    * property is located. At that point the property is passed to a
142    * TypedDataResolver which will convert it to an appropriate ContextInterface
143    * object.
144    *
145    * @param $token
146    *   A ":" delimited set of tokens representing
147    * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
148    *   The array of available contexts.
149    *
150    * @return \Drupal\Core\Plugin\Context\ContextInterface
151    *   The requested token as a full Context object.
152    *
153    * @throws \Drupal\ctools\ContextNotFoundException
154    */
155   public function convertTokenToContext($token, $contexts) {
156     // If the requested token is already a context, just return it.
157     if (isset($contexts[$token])) {
158       return $contexts[$token];
159     }
160     else {
161       list($base, $property_path) = explode(':', $token, 2);
162       // A base must always be set. This method recursively calls itself
163       // setting bases for this reason.
164       if (!empty($contexts[$base])) {
165         return $this->getContextFromProperty($property_path,  $contexts[$base]);
166       }
167       // @todo improve this exception message.
168       throw new ContextNotFoundException("The requested context was not found in the supplied array of contexts.");
169     }
170   }
171
172   /**
173    * Provides an administrative label for a tokenized relationship.
174    *
175    * @param string $token
176    *   The token related to a context in the contexts array.
177    * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
178    *  An array of contexts from which to extract our token's label.
179    *
180    * @return \Drupal\Core\StringTranslation\TranslatableMarkup
181    *   The administrative label of $token.
182    */
183   public function getLabelByToken($token, $contexts) {
184     // @todo Optimize this by allowing to limit the desired token?
185     $tokens = $this->getTokensForContexts($contexts);
186     if (isset($tokens[$token])) {
187       return $tokens[$token];
188     }
189   }
190
191   /**
192    * Extracts an array of tokens and labels.
193    *
194    * @param \Drupal\Core\Plugin\Context\ContextInterface[] $contexts
195    *   The array of contexts with which we are currently dealing.
196    *
197    * @return array
198    *   An array of token keys and corresponding labels.
199    */
200   public function getTokensForContexts($contexts) {
201     $tokens = [];
202     foreach ($contexts as $context_id => $context) {
203       $data_definition = $context->getContextDefinition()->getDataDefinition();
204       if ($data_definition instanceof ComplexDataDefinitionInterface) {
205         foreach ($this->getTokensFromComplexData($data_definition) as $token => $label) {
206           $tokens["$context_id:$token"] = $data_definition->getLabel() . ': ' . $label;
207         }
208       }
209     }
210     return $tokens;
211   }
212
213   /**
214    * Returns tokens for a complex data definition.
215    *
216    * @param \Drupal\Core\TypedData\ComplexDataDefinitionInterface $complex_data_definition
217    *
218    * @return array
219    *   An array of token keys and corresponding labels.
220    */
221   protected function getTokensFromComplexData(ComplexDataDefinitionInterface $complex_data_definition) {
222     $tokens = [];
223     // Loop over all properties.
224     foreach ($complex_data_definition->getPropertyDefinitions() as $property_name => $property_definition) {
225
226       // Item definitions do not always have a label. Use the list definition
227       // label if the item does not have one.
228       $property_label = $property_definition->getLabel();
229       if ($property_definition instanceof ListDataDefinitionInterface) {
230         $property_definition = $property_definition->getItemDefinition();
231         $property_label = $property_definition->getLabel() ?: $property_label;
232       }
233
234       // If the property is complex too, recurse to find child properties.
235       if ($property_definition instanceof ComplexDataDefinitionInterface) {
236         $property_tokens = $this->getTokensFromComplexData($property_definition);
237         foreach ($property_tokens as $token => $label) {
238           $tokens[$property_name . ':' . $token] = count($property_tokens) > 1 ? ($property_label . ': ' . $label) : $property_label;
239         }
240       }
241
242       // Only expose references as tokens.
243       // @todo Consider to expose primitive and non-reference typed data
244       //   definitions too, like strings, integers and dates. The current UI
245       //   will not scale to that.
246       if ($property_definition instanceof DataReferenceDefinitionInterface) {
247         $tokens[$property_name] = $property_definition->getLabel();
248       }
249     }
250     return $tokens;
251   }
252
253 }