9123713c53f38a34cb49041c769981a079d4de0c
[yaffs-website] / web / modules / contrib / metatag / src / MetatagManager.php
1 <?php
2
3 namespace Drupal\metatag;
4
5 use Drupal\Component\Render\PlainTextOutput;
6 use Drupal\Core\Entity\ContentEntityInterface;
7 use Drupal\Core\Language\LanguageInterface;
8 use Drupal\Core\Logger\LoggerChannelFactoryInterface;
9 use Drupal\field\Entity\FieldConfig;
10
11 /**
12  * Class MetatagManager.
13  *
14  * @package Drupal\metatag
15  */
16 class MetatagManager implements MetatagManagerInterface {
17
18   protected $groupPluginManager;
19   protected $tagPluginManager;
20
21   protected $tokenService;
22
23   /**
24    * Metatag logging channel.
25    *
26    * @var \Drupal\Core\Logger\LoggerChannelInterface
27    */
28   protected $logger;
29
30   /**
31    * Constructor for MetatagManager.
32    *
33    * @param MetatagGroupPluginManager $groupPluginManager
34    * @param MetatagTagPluginManager $tagPluginManager
35    * @param MetatagToken $token
36    * @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $channelFactory
37    */
38   public function __construct(MetatagGroupPluginManager $groupPluginManager,
39                               MetatagTagPluginManager $tagPluginManager,
40                               MetatagToken $token,
41                               LoggerChannelFactoryInterface $channelFactory) {
42     $this->groupPluginManager = $groupPluginManager;
43     $this->tagPluginManager = $tagPluginManager;
44     $this->tokenService = $token;
45     $this->logger = $channelFactory->get('metatag');
46   }
47
48   /**
49    * {@inheritdoc}
50    */
51   public function tagsFromEntity(ContentEntityInterface $entity) {
52     $tags = [];
53
54     $fields = $this->getFields($entity);
55
56     /* @var FieldConfig $field_info */
57     foreach ($fields as $field_name => $field_info) {
58       // Get the tags from this field.
59       $tags = $this->getFieldTags($entity, $field_name);
60     }
61
62     return $tags;
63   }
64
65   /**
66    * Gets the group plugin definitions.
67    *
68    * @return array
69    *   Group definitions
70    */
71   protected function groupDefinitions() {
72     return $this->groupPluginManager->getDefinitions();
73   }
74
75   /**
76    * Gets the tag plugin definitions.
77    *
78    * @return array
79    *   Tag definitions
80    */
81   protected function tagDefinitions() {
82     return $this->tagPluginManager->getDefinitions();
83   }
84
85   /**
86    * {@inheritdoc}
87    */
88   public function sortedGroups() {
89     $metatag_groups = $this->groupDefinitions();
90
91     // Pull the data from the definitions into a new array.
92     $groups = [];
93     foreach ($metatag_groups as $group_name => $group_info) {
94       $id  = $group_info['id'];
95       $groups[$id]['label'] = $group_info['label']->render();
96       $groups[$id]['description'] = $group_info['description'];
97       $groups[$id]['weight'] = $group_info['weight'];
98     }
99
100     // Create the 'sort by' array.
101     $sort_by = [];
102     foreach ($groups as $group) {
103       $sort_by[] = $group['weight'];
104     }
105
106     // Sort the groups by weight.
107     array_multisort($sort_by, SORT_ASC, $groups);
108
109     return $groups;
110   }
111
112   /**
113    * {@inheritdoc}
114    */
115   public function sortedTags() {
116     $metatag_tags = $this->tagDefinitions();
117
118     // Pull the data from the definitions into a new array.
119     $tags = [];
120     foreach ($metatag_tags as $tag_name => $tag_info) {
121       $id  = $tag_info['id'];
122       $tags[$id]['label'] = $tag_info['label']->render();
123       $tags[$id]['group'] = $tag_info['group'];
124       $tags[$id]['weight'] = $tag_info['weight'];
125     }
126
127     // Create the 'sort by' array.
128     $sort_by = [];
129     foreach ($tags as $key => $tag) {
130       $sort_by['group'][$key] = $tag['group'];
131       $sort_by['weight'][$key] = $tag['weight'];
132     }
133
134     // Sort the tags by weight.
135     array_multisort($sort_by['group'], SORT_ASC, $sort_by['weight'], SORT_ASC, $tags);
136
137     return $tags;
138   }
139
140   /**
141    * {@inheritdoc}
142    */
143   public function sortedGroupsWithTags() {
144     $groups = $this->sortedGroups();
145     $tags = $this->sortedTags();
146
147     foreach ($tags as $tag_id => $tag) {
148       $tag_group = $tag['group'];
149
150       if (!isset($groups[$tag_group])) {
151         // If the tag is claiming a group that has no matching plugin, log an
152         // error and force it to the basic group.
153         $this->logger->error("Undefined group '%group' on tag '%tag'", ['%group' => $tag_group, '%tag' => $tag_id]);
154         $tag['group'] = 'basic';
155         $tag_group = 'basic';
156       }
157
158       $groups[$tag_group]['tags'][$tag_id] = $tag;
159     }
160
161     return $groups;
162   }
163
164   /**
165    * {@inheritdoc}
166    */
167   public function form(array $values, array $element, array $token_types = [], array $included_groups = NULL, array $included_tags = NULL) {
168
169     // Add the outer fieldset.
170     $element += [
171       '#type' => 'details',
172     ];
173
174     $element += $this->tokenService->tokenBrowser($token_types);
175
176     $groups_and_tags = $this->sortedGroupsWithTags();
177
178     $first = TRUE;
179     foreach ($groups_and_tags as $group_id => $group) {
180       // Only act on groups that have tags and are in the list of included
181       // groups (unless that list is null).
182       if (isset($group['tags']) && (is_null($included_groups) || in_array($group_id, $included_groups))) {
183         // Create the fieldset.
184         $element[$group_id]['#type'] = 'details';
185         $element[$group_id]['#title'] = $group['label'];
186         $element[$group_id]['#description'] = $group['description'];
187         $element[$group_id]['#open'] = $first;
188         $first = FALSE;
189
190         foreach ($group['tags'] as $tag_id => $tag) {
191           // Only act on tags in the included tags list, unless that is null.
192           if (is_null($included_tags) || in_array($tag_id, $included_tags)) {
193             // Make an instance of the tag.
194             $tag = $this->tagPluginManager->createInstance($tag_id);
195
196             // Set the value to the stored value, if any.
197             $tag_value = isset($values[$tag_id]) ? $values[$tag_id] : NULL;
198             $tag->setValue($tag_value);
199
200             // Create the bit of form for this tag.
201             $element[$group_id][$tag_id] = $tag->form($element);
202           }
203         }
204       }
205     }
206
207     return $element;
208   }
209
210   /**
211    * Returns a list of the metatag fields on an entity.
212    */
213   protected function getFields(ContentEntityInterface $entity) {
214     $field_list = [];
215
216     if ($entity instanceof ContentEntityInterface) {
217       // Get a list of the metatag field types.
218       $field_types = $this->fieldTypes();
219
220       // Get a list of the field definitions on this entity.
221       $definitions = $entity->getFieldDefinitions();
222
223       // Iterate through all the fields looking for ones in our list.
224       foreach ($definitions as $field_name => $definition) {
225         // Get the field type, ie: metatag.
226         $field_type = $definition->getType();
227
228         // Check the field type against our list of fields.
229         if (isset($field_type) && in_array($field_type, $field_types)) {
230           $field_list[$field_name] = $definition;
231         }
232       }
233     }
234
235     return $field_list;
236   }
237
238   /**
239    * Returns a list of the metatags with values from a field.
240    *
241    * @param $entity
242    * @param $field_name
243    */
244   protected function getFieldTags(ContentEntityInterface $entity, $field_name) {
245     $tags = [];
246     foreach ($entity->{$field_name} as $item) {
247       // Get serialized value and break it into an array of tags with values.
248       $serialized_value = $item->get('value')->getValue();
249       if (!empty($serialized_value)) {
250         $tags += unserialize($serialized_value);
251       }
252     }
253
254     return $tags;
255   }
256
257   /**
258    * Generate the elements that go in the attached array in
259    * hook_page_attachments.
260    *
261    * @param array $tags
262    *   The array of tags as plugin_id => value.
263    * @param object $entity
264    *   Optional entity object to use for token replacements.
265    * @return array
266    *   Render array with tag elements.
267    */
268   public function generateElements($tags, $entity = NULL) {
269     $metatag_tags = $this->tagPluginManager->getDefinitions();
270     $elements = [];
271
272     // Each element of the $values array is a tag with the tag plugin name
273     // as the key.
274     foreach ($tags as $tag_name => $value) {
275       // Check to ensure there is a matching plugin.
276       if (isset($metatag_tags[$tag_name])) {
277         // Get an instance of the plugin.
278         $tag = $this->tagPluginManager->createInstance($tag_name);
279
280         // Render any tokens in the value.
281         $token_replacements = [];
282         if ($entity) {
283           $token_replacements = [$entity->getEntityTypeId() => $entity];
284         }
285
286         // Set the value as sometimes the data needs massaging, such as when
287         // field defaults are used for the Robots field, which come as an array
288         // that needs to be filtered and converted to a string.
289         // @see @Robots::setValue().
290         $tag->setValue($value);
291         $langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
292         if ($tag->type() === 'image') {
293           $processed_value = $this->tokenService->replace($tag->value(), $token_replacements, ['langcode' => $langcode]);
294         }
295         else {
296           $processed_value = PlainTextOutput::renderFromHtml(htmlspecialchars_decode($this->tokenService->replace($tag->value(), $token_replacements, ['langcode' => $langcode])));
297         }
298
299         // Now store the value with processed tokens back into the plugin.
300         $tag->setValue($processed_value);
301
302         // Have the tag generate the output based on the value we gave it.
303         $output = $tag->output();
304
305         if (!empty($output)) {
306           $elements['#attached']['html_head'][] = [
307             $output,
308             $tag_name
309           ];
310         }
311       }
312     }
313
314     return $elements;
315   }
316
317   /**
318    * Returns a list of fields handled by Metatag.
319    *
320    * @return array
321    */
322   protected function fieldTypes() {
323     //@TODO: Either get this dynamically from field plugins or forget it and just hardcode metatag where this is called.
324     return ['metatag'];
325   }
326
327 }