use Drupal\Component\Render\PlainTextOutput;
use Drupal\Core\Entity\ContentEntityInterface;
+use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\field\Entity\FieldConfig;
+use Drupal\metatag\Entity\MetatagDefaults;
+use Drupal\views\ViewEntityInterface;
/**
* Class MetatagManager.
protected $groupPluginManager;
protected $tagPluginManager;
-
+ protected $metatagDefaults;
protected $tokenService;
/**
* @param MetatagTagPluginManager $tagPluginManager
* @param MetatagToken $token
* @param \Drupal\Core\Logger\LoggerChannelFactoryInterface $channelFactory
+ * @param EntityTypeManager $entityTypeManager
*/
public function __construct(MetatagGroupPluginManager $groupPluginManager,
MetatagTagPluginManager $tagPluginManager,
MetatagToken $token,
- LoggerChannelFactoryInterface $channelFactory) {
+ LoggerChannelFactoryInterface $channelFactory,
+ EntityTypeManager $entityTypeManager) {
$this->groupPluginManager = $groupPluginManager;
$this->tagPluginManager = $tagPluginManager;
$this->tokenService = $token;
$this->logger = $channelFactory->get('metatag');
+ $this->metatagDefaults = $entityTypeManager->getStorage('metatag_defaults');
}
/**
return $tags;
}
+ /**
+ * {@inheritdoc}
+ */
+ public function tagsFromEntityWithDefaults(ContentEntityInterface $entity) {
+ return $this->tagsFromEntity($entity) + $this->defaultTagsFromEntity($entity);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function defaultTagsFromEntity(ContentEntityInterface $entity) {
+ /** @var MetatagDefaults $metatags */
+ $metatags = $this->metatagDefaults->load('global');
+ if (!$metatags) {
+ return NULL;
+ }
+ // Add/overwrite with tags set on the entity type.
+ $entity_type_tags = $this->metatagDefaults->load($entity->getEntityTypeId());
+ if (!is_null($entity_type_tags)) {
+ $metatags->overwriteTags($entity_type_tags->get('tags'));
+ }
+ // Add/overwrite with tags set on the entity bundle.
+ $bundle_metatags = $this->metatagDefaults->load($entity->getEntityTypeId() . '__' . $entity->bundle());
+ if (!is_null($bundle_metatags)) {
+ $metatags->overwriteTags($bundle_metatags->get('tags'));
+ }
+ return $metatags->get('tags');
+ }
+
/**
* Gets the group plugin definitions.
*
* @return array
- * Group definitions
+ * Group definitions.
*/
protected function groupDefinitions() {
return $this->groupPluginManager->getDefinitions();
// Pull the data from the definitions into a new array.
$groups = [];
foreach ($metatag_groups as $group_name => $group_info) {
- $id = $group_info['id'];
- $groups[$id]['label'] = $group_info['label']->render();
- $groups[$id]['description'] = $group_info['description'];
- $groups[$id]['weight'] = $group_info['weight'];
+ $groups[$group_name]['id'] = $group_info['id'];
+ $groups[$group_name]['label'] = $group_info['label']->render();
+ $groups[$group_name]['description'] = $group_info['description'];
+ $groups[$group_name]['weight'] = $group_info['weight'];
}
// Create the 'sort by' array.
// Pull the data from the definitions into a new array.
$tags = [];
foreach ($metatag_tags as $tag_name => $tag_info) {
- $id = $tag_info['id'];
- $tags[$id]['label'] = $tag_info['label']->render();
- $tags[$id]['group'] = $tag_info['group'];
- $tags[$id]['weight'] = $tag_info['weight'];
+ $tags[$tag_name]['id'] = $tag_info['id'];
+ $tags[$tag_name]['label'] = $tag_info['label']->render();
+ $tags[$tag_name]['group'] = $tag_info['group'];
+ $tags[$tag_name]['weight'] = $tag_info['weight'];
}
// Create the 'sort by' array.
$groups = $this->sortedGroups();
$tags = $this->sortedTags();
- foreach ($tags as $tag_id => $tag) {
+ foreach ($tags as $tag_name => $tag) {
$tag_group = $tag['group'];
if (!isset($groups[$tag_group])) {
$tag_group = 'basic';
}
- $groups[$tag_group]['tags'][$tag_id] = $tag;
+ $groups[$tag_group]['tags'][$tag_name] = $tag;
}
return $groups;
* {@inheritdoc}
*/
public function form(array $values, array $element, array $token_types = [], array $included_groups = NULL, array $included_tags = NULL) {
-
// Add the outer fieldset.
$element += [
'#type' => 'details',
$groups_and_tags = $this->sortedGroupsWithTags();
$first = TRUE;
- foreach ($groups_and_tags as $group_id => $group) {
+ foreach ($groups_and_tags as $group_name => $group) {
// Only act on groups that have tags and are in the list of included
// groups (unless that list is null).
- if (isset($group['tags']) && (is_null($included_groups) || in_array($group_id, $included_groups))) {
+ if (isset($group['tags']) && (is_null($included_groups) || in_array($group_name, $included_groups) || in_array($group['id'], $included_groups))) {
// Create the fieldset.
- $element[$group_id]['#type'] = 'details';
- $element[$group_id]['#title'] = $group['label'];
- $element[$group_id]['#description'] = $group['description'];
- $element[$group_id]['#open'] = $first;
+ $element[$group_name]['#type'] = 'details';
+ $element[$group_name]['#title'] = $group['label'];
+ $element[$group_name]['#description'] = $group['description'];
+ $element[$group_name]['#open'] = $first;
$first = FALSE;
- foreach ($group['tags'] as $tag_id => $tag) {
+ foreach ($group['tags'] as $tag_name => $tag) {
// Only act on tags in the included tags list, unless that is null.
- if (is_null($included_tags) || in_array($tag_id, $included_tags)) {
+ if (is_null($included_tags) || in_array($tag_name, $included_tags) || in_array($tag['id'], $included_tags)) {
// Make an instance of the tag.
- $tag = $this->tagPluginManager->createInstance($tag_id);
+ $tag = $this->tagPluginManager->createInstance($tag_name);
// Set the value to the stored value, if any.
- $tag_value = isset($values[$tag_id]) ? $values[$tag_id] : NULL;
+ $tag_value = isset($values[$tag_name]) ? $values[$tag_name] : NULL;
$tag->setValue($tag_value);
// Create the bit of form for this tag.
- $element[$group_id][$tag_id] = $tag->form($element);
+ $element[$group_name][$tag_name] = $tag->form($element);
}
}
}
/**
* Returns a list of the metatags with values from a field.
*
- * @param $entity
- * @param $field_name
+ * @param ContentEntityInterface $entity
+ * @param string $field_name
*/
protected function getFieldTags(ContentEntityInterface $entity, $field_name) {
$tags = [];
return $tags;
}
+ /**
+ *
+ *
+ * @param ContentEntityInterface $entity
+ */
+ public function getDefaultMetatags(ContentEntityInterface $entity = NULL) {
+ // Get general global metatags
+ $metatags = $this->getGlobalMetatags();
+ // If that is empty something went wrong.
+ if (!$metatags) {
+ return;
+ }
+
+ // Check if this is a special page.
+ $special_metatags = $this->getSpecialMetatags();
+
+ // Merge with all globals defaults.
+ if ($special_metatags) {
+ $metatags->set('tags', array_merge($metatags->get('tags'), $special_metatags->get('tags')));
+ }
+
+ // Next check if there is this page is an entity that has meta tags.
+ // @TODO: Think about using other defaults, e.g. views. Maybe use plugins?
+ else {
+ if (is_null($entity)) {
+ $entity = metatag_get_route_entity();
+ }
+
+ if (!empty($entity)) {
+ // Get default metatags for a given entity.
+ $entity_defaults = $this->getEntityDefaultMetatags($entity);
+ if ($entity_defaults != NULL) {
+ $metatags->set('tags', array_merge($metatags->get('tags'), $entity_defaults));
+ }
+ }
+ }
+
+ return $metatags->get('tags');
+ }
+
+ /**
+ *
+ */
+ public function getGlobalMetatags() {
+ return $this->metatagDefaults->load('global');
+ }
+
+ /**
+ *
+ */
+ public function getSpecialMetatags() {
+ $metatags = NULL;
+
+ if (\Drupal::service('path.matcher')->isFrontPage()) {
+ $metatags = $this->metatagDefaults->load('front');
+ }
+ elseif (\Drupal::service('current_route_match')->getRouteName() == 'system.403') {
+ $metatags = $this->metatagDefaults->load('403');
+ }
+ elseif (\Drupal::service('current_route_match')->getRouteName() == 'system.404') {
+ $metatags = $this->metatagDefaults->load('404');
+ }
+
+ return $metatags;
+ }
+
+ /**
+ *
+ */
+ public function getEntityDefaultMetatags(ContentEntityInterface $entity) {
+ $entity_metatags = $this->metatagDefaults->load($entity->getEntityTypeId());
+ $metatags = [];
+ if ($entity_metatags != NULL) {
+ // Merge with global defaults.
+ $metatags = array_merge($metatags, $entity_metatags->get('tags'));
+ }
+
+ // Finally, check if we should apply bundle overrides.
+ $bundle_metatags = $this->metatagDefaults->load($entity->getEntityTypeId() . '__' . $entity->bundle());
+ if ($bundle_metatags != NULL) {
+ // Merge with existing defaults.
+ $metatags = array_merge($metatags, $bundle_metatags->get('tags'));
+ }
+
+ return $metatags;
+ }
+
/**
* Generate the elements that go in the attached array in
* hook_page_attachments.
* The array of tags as plugin_id => value.
* @param object $entity
* Optional entity object to use for token replacements.
+ *
* @return array
* Render array with tag elements.
*/
public function generateElements($tags, $entity = NULL) {
- $metatag_tags = $this->tagPluginManager->getDefinitions();
$elements = [];
+ $tags = $this->generateRawElements($tags, $entity);
+
+ foreach ($tags as $name => $tag) {
+ if (!empty($tag)) {
+ $elements['#attached']['html_head'][] = [
+ $tag,
+ $name,
+ ];
+ }
+ }
+
+ return $elements;
+ }
+
+ /**
+ * Generate the actual meta tag values.
+ *
+ * @param array $tags
+ * The array of tags as plugin_id => value.
+ * @param object $entity
+ * Optional entity object to use for token replacements.
+ *
+ * @return array
+ * Render array with tag elements.
+ */
+ public function generateRawElements($tags, $entity = NULL) {
+ $rawTags = [];
+
+ $metatag_tags = $this->tagPluginManager->getDefinitions();
+
+ // Order the elements by weight first, as some systems like Facebook care.
+ uksort($tags, function ($tag_name_a, $tag_name_b) use ($metatag_tags) {
+ $weight_a = isset($metatag_tags[$tag_name_a]['weight']) ? $metatag_tags[$tag_name_a]['weight'] : 0;
+ $weight_b = isset($metatag_tags[$tag_name_b]['weight']) ? $metatag_tags[$tag_name_b]['weight'] : 0;
+
+ return ($weight_a < $weight_b) ? -1 : 1;
+ });
// Each element of the $values array is a tag with the tag plugin name
// as the key.
// Render any tokens in the value.
$token_replacements = [];
if ($entity) {
- $token_replacements = [$entity->getEntityTypeId() => $entity];
+ // @TODO: This needs a better way of discovering the context.
+ if ($entity instanceof ViewEntityInterface) {
+ // Views tokens require the ViewExecutable, not the config entity.
+ // @todo Can we move this into metatag_views somehow?
+ $token_replacements = ['view' => $entity->getExecutable()];
+ }
+ else {
+ $token_replacements = [$entity->getEntityTypeId() => $entity];
+ }
}
// Set the value as sometimes the data needs massaging, such as when
// @see @Robots::setValue().
$tag->setValue($value);
$langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId();
+
if ($tag->type() === 'image') {
$processed_value = $this->tokenService->replace($tag->value(), $token_replacements, ['langcode' => $langcode]);
}
$output = $tag->output();
if (!empty($output)) {
- $elements['#attached']['html_head'][] = [
- $output,
- $tag_name
- ];
+ $rawTags[$tag_name] = $output;
}
}
}
- return $elements;
+ return $rawTags;
}
/**