Loading CHANGELOG.txt +2 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,8 @@ Metatag 8.x-1.x-dev, xxxx-xx-xx assertCount(). #3108052 by Berdir, SpadXIII: metatag_get_default_tags() does not revert config override language. #2862747 by JeroenT, DanielVeza, rwohleb, joshua.boltz, henrikakselsen, Vitalyos, Phil Wolstenholme: Tokens to access individual meta tag values. Metatag 8.x-1.14, 2020-08-11 Loading metatag.tokens.inc 0 → 100644 +228 −0 Original line number Diff line number Diff line <?php /** * @file * Metatag token integration. */ use Drupal\Component\Utility\Html; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Render\BubbleableMetadata; /** * Implements hook_token_info(). */ function metatag_token_info() { $info = []; $group_manager = \Drupal::service('plugin.manager.metatag.group'); $tag_manager = \Drupal::service('plugin.manager.metatag.tag'); $tag_definitions = $tag_manager->getDefinitions(); $info['types']['metatag'] = [ 'name' => t('Metatags'), 'description' => t('Tokens related to Metatags.'), 'needs-data' => 'metatag', ]; foreach ($tag_definitions as $tag_id => $tag_definition) { $label = $tag_definition['label']; $description = $tag_definition['description']; $multiple = $tag_definition['multiple']; $metatag_token_name = 'metatag-' . $tag_id; $group = $group_manager->getDefinition($tag_definition['group']); if ($group) { $label = $group['label'] . ': ' . $label; } $info['tokens']['current-page']['metatag'] = [ 'name' => t('Metatags'), 'description' => t('Metatag values for the current page.'), 'type' => 'metatag', ]; $info['tokens']['metatag'][$tag_id] = [ 'name' => Html::escape($label), 'description' => $description, 'type' => $multiple ? "list<$metatag_token_name>" : $metatag_token_name, ]; $info['types'][$metatag_token_name] = [ 'name' => Html::escape($label), 'description' => t('@label tokens.', ['@label' => Html::escape($label)]), 'needs-data' => $metatag_token_name, 'nested' => TRUE, ]; // Tag list token type. if ($multiple) { $info['types']["list<$metatag_token_name>"] = [ 'name' => t('List of @type values', ['@type' => Html::escape($label)]), 'description' => t('Tokens for lists of @type values.', ['@type' => Html::escape($label)]), 'needs-data' => "list<$metatag_token_name>", 'nested' => TRUE, ]; // Show a different token for each tag delta. // Since we don't know how many there will be, we will just show 3. for ($delta = 0; $delta < 3; $delta++) { $info['tokens']["list<$metatag_token_name>"][$delta] = [ 'name' => t('@type type with delta @delta', [ '@type' => Html::escape($label), '@delta' => $delta, ]), 'module' => 'token', 'type' => $metatag_token_name, ]; } } } return $info; } /** * Implements hook_token_info_alter(). */ function metatag_token_info_alter(&$info) { foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type_id => $entity_type) { if (!$entity_type->entityClassImplements(ContentEntityInterface::class)) { continue; } // Make sure a token type exists for this entity. $token_type = \Drupal::service('token.entity_mapper')->getTokenTypeForEntityType($entity_type_id); if (empty($token_type) || !isset($info['types'][$token_type])) { continue; } $fields = \Drupal::entityTypeManager()->getStorage('field_storage_config')->loadByProperties([ 'entity_type' => $entity_type_id, 'type' => 'metatag', ]); foreach ($fields as $field) { $field_token_name = $token_type . '-' . $field->getName(); $info['types'][$field_token_name] = [ 'name' => Html::escape($field->getName()), 'description' => t('@label tokens.', ['@label' => Html::escape($field->getName())]), 'needs-data' => $field_token_name, 'nested' => TRUE, 'type' => 'metatag', 'module' => 'metatag', ]; } } } /** * Implements hook_tokens(). */ function metatag_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) { $replacements = []; switch ($type) { case 'current-page': /** @var \Drupal\token\TokenInterface $token_service */ $token_service = \Drupal::token(); $metatag_tokens = $token_service->findWithPrefix($tokens, 'metatag'); if (!empty($metatag_tokens) && metatag_is_current_route_supported()) { // Add cache contexts to ensure this token functions on a per-path // basis. $bubbleable_metadata->addCacheContexts(['url.site']); $replacements += $token_service->generate('metatag', $metatag_tokens, [], $options, $bubbleable_metadata); } break; case 'entity': if (!empty($data['entity_type']) && !empty($data['entity']) && !empty($data['token_type'])) { /* @var \Drupal\Core\Entity\ContentEntityInterface $entity */ $entity = $data['entity']; if (!($entity instanceof ContentEntityInterface)) { return $replacements; } $metatag_fields = []; foreach ($tokens as $name => $original) { $field_name = explode(':', $name)[0]; if ($entity->hasField($field_name) && $entity->get($field_name)->getFieldDefinition()->getType() === 'metatag') { $metatag_fields[] = $field_name; } } if (!empty($metatag_fields)) { /** @var \Drupal\token\TokenInterface $token_service */ $token_service = \Drupal::token(); $metatag_tokens = []; foreach ($metatag_fields as $metatag_field) { $metatag_tokens += $token_service->findWithPrefix($tokens, $metatag_field); } $replacements += $token_service->generate('metatag', $metatag_tokens, ['entity' => $entity], $options, $bubbleable_metadata); } } break; case 'metatag': $metatag_manager = \Drupal::service('metatag.manager'); $entity = $options['entity'] ?? metatag_get_route_entity(); $tags = metatag_get_default_tags($entity); if ($entity instanceof ContentEntityInterface) { // If content entity does not have an ID the page is likely an "Add" // page, so skip processing for entity which has not been created yet. if (!$entity->id()) { return NULL; } $tags += $metatag_manager->tagsFromEntity($entity); } // Trigger hook_metatags_alter(). // Allow modules to override tags or the entity used for token // replacements. $context = [ 'entity' => &$entity, ]; \Drupal::service('module_handler')->alter('metatags', $tags, $context); // If the entity was changed above, use that for generating the meta tags. if (isset($context['entity'])) { $entity = $context['entity']; } $processed_tags = $metatag_manager->generateTokenValues($tags, $entity); foreach ($tokens as $name => $original) { // For the [metatag:tag_name] token. if (strpos($name, ':') === FALSE) { $tag_name = $name; } // For [metatag:tag_name:0], [metatag:tag_name:0:value] and // [metatag:tag_name:value] tokens. else { [$tag_name, $delta] = explode(':', $name, 2); if (!is_numeric($delta)) { unset($delta); } } // Replace dashes (-) with underscores (_) for e.g. canonical-url. $tag_name = str_replace('-', '_', $tag_name); if (empty($processed_tags[$tag_name])) { continue; } // Render only one delta. if (isset($delta)) { $replacements[$original] = $processed_tags[$tag_name][$delta]; } else { $replacements[$original] = is_array($processed_tags[$tag_name]) ? implode(',', $processed_tags[$tag_name]) : $processed_tags[$tag_name]; } } break; } return $replacements; } src/MetatagManager.php +76 −4 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ namespace Drupal\metatag; use Drupal\Component\Render\PlainTextOutput; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\LanguageInterface; Loading Loading @@ -54,6 +55,13 @@ class MetatagManager implements MetatagManagerInterface { */ protected $logger; /** * Caches processed strings, keyed by tag name. * * @var array */ protected $processedTokenCache = []; /** * Constructor for MetatagManager. * Loading @@ -72,7 +80,8 @@ class MetatagManager implements MetatagManagerInterface { MetatagTagPluginManager $tagPluginManager, MetatagToken $token, LoggerChannelFactoryInterface $channelFactory, EntityTypeManagerInterface $entityTypeManager) { EntityTypeManagerInterface $entityTypeManager ) { $this->groupPluginManager = $groupPluginManager; $this->tagPluginManager = $tagPluginManager; $this->tokenService = $token; Loading Loading @@ -593,6 +602,69 @@ class MetatagManager implements MetatagManagerInterface { return $rawTags; } /** * Generate the actual meta tag values for use as tokens. * * @param array $tags * The array of tags as plugin_id => value. * @param object $entity * Optional entity object to use for token replacements. * * @return array * Array of MetatagTag plugin instances. */ public function generateTokenValues(array $tags, $entity = NULL) { // Ignore the update.php path. $request = \Drupal::request(); if ($request->getBaseUrl() == '/update.php') { return []; } $entity_identifier = '_none'; if ($entity) { $entity_identifier = $entity->getEntityTypeId() . ':' . ($entity->uuid() ?: $entity->id()); } if (!isset($this->processedTokenCache[$entity_identifier])) { $metatag_tags = $this->tagPluginManager->getDefinitions(); // Each element of the $values array is a tag with the tag plugin name as // the key. foreach ($tags as $tag_name => $value) { // Check to ensure there is a matching plugin. if (isset($metatag_tags[$tag_name])) { // Get an instance of the plugin. $tag = $this->tagPluginManager->createInstance($tag_name); // Render any tokens in the value. $token_replacements = []; if ($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()]; } elseif ($entity instanceof ContentEntityInterface) { $token_replacements = [$entity->getEntityTypeId() => $entity]; } } // Set the value as sometimes the data needs massaging, such as when // field defaults are used for the Robots field, which come as an // array that needs to be filtered and converted to a string. // @see Robots::setValue() $tag->setValue($value); $langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); $value = PlainTextOutput::renderFromHtml(htmlspecialchars_decode($this->tokenService->replace($value, $token_replacements, ['langcode' => $langcode]))); $this->processedTokenCache[$entity_identifier][$tag_name] = $tag->multiple() ? explode(',', $value) : $value; } } } return $this->processedTokenCache[$entity_identifier]; } /** * Returns a list of fields handled by Metatag. * Loading tests/src/Functional/MetatagTokenTest.php 0 → 100644 +113 −0 Original line number Diff line number Diff line <?php namespace Drupal\Tests\metatag\Functional; use Drupal\Tests\BrowserTestBase; use Drupal\Tests\field_ui\Traits\FieldUiTestTrait; use Drupal\Tests\token\Functional\TokenTestTrait; /** * Verify that metatag token generation is working. * * @group metatag */ class MetatagTokenTest extends BrowserTestBase { use TokenTestTrait; use FieldUiTestTrait; /** * {@inheritdoc} */ protected static $modules = [ 'block', 'field_ui', 'user', 'token', 'token_module_test', 'metatag', 'metatag_open_graph', ]; /** * {@inheritdoc} */ protected $defaultTheme = 'stark'; /** * {@inheritdoc} */ public function setUp(): void { parent::setUp(); $this->drupalPlaceBlock('system_breadcrumb_block'); $this->drupalPlaceBlock('local_tasks_block'); $this->drupalPlaceBlock('page_title_block'); $this->drupalLogin($this->rootUser); $this->fieldUIAddNewField('/admin/config/people/accounts', 'metatags', 'Metatags', 'metatag'); } /** * Test current-page metatag token generation. */ public function testMetatagCurrentPageTokens() { $user = $this->createUser([]); $this->drupalGet($user->toUrl('edit-form')); $this->submitForm([ 'field_metatags[0][basic][abstract]' => 'My abstract', 'field_metatags[0][open_graph][og_title]' => 'My OG Title', 'field_metatags[0][open_graph][og_image]' => 'Image 1,Image 2', ], 'Save'); $tokens = [ // Test globally configured metatags. '[current-page:metatag:title]' => sprintf('%s | %s', $user->getAccountName(), $this->config('system.site') ->get('name')), '[current-page:metatag:description]' => $this->config('system.site') ->get('name'), '[current-page:metatag:canonical-url]' => $user->toUrl('canonical', ['absolute' => TRUE]) ->toString(), // Test entity overridden metatags. '[current-page:metatag:abstract]' => 'My abstract', // Test metatags provided by a submodule. '[current-page:metatag:og-title]' => 'My OG Title', // Test metatags that can contain multiple values. '[current-page:metatag:og_image]' => 'Image 1,Image 2', '[current-page:metatag:og_image:0]' => 'Image 1', '[current-page:metatag:og_image:1]' => 'Image 2', ]; $this->assertPageTokens($user->toUrl(), $tokens); } /** * Test entity token generation. */ public function testMetatagEntityTokens() { $user = $this->createUser(); $this->drupalGet($user->toUrl('edit-form')); $this->submitForm([ 'field_metatags[0][basic][abstract]' => 'My abstract', 'field_metatags[0][open_graph][og_title]' => 'My OG Title', 'field_metatags[0][open_graph][og_image]' => 'Image 1,Image 2', ], 'Save'); $tokens = [ // Test globally configured metatags. '[user:field_metatags:title]' => sprintf('%s | %s', $user->getAccountName(), $this->config('system.site')->get('name')), '[user:field_metatags:description]' => $this->config('system.site')->get('name'), '[user:field_metatags:canonical-url]' => $user->toUrl('canonical', ['absolute' => TRUE])->toString(), // Test entity overridden metatags. '[user:field_metatags:abstract]' => 'My abstract', // Test metatags provided by a submodule. '[user:field_metatags:og-title]' => 'My OG Title', // Test metatags that can contain multiple values. '[user:field_metatags:og_image]' => 'Image 1,Image 2', '[user:field_metatags:og_image:0]' => 'Image 1', '[user:field_metatags:og_image:1]' => 'Image 2', ]; $this->assertPageTokens($user->toUrl(), $tokens, ['user' => $user]); } } Loading
CHANGELOG.txt +2 −0 Original line number Diff line number Diff line Loading @@ -33,6 +33,8 @@ Metatag 8.x-1.x-dev, xxxx-xx-xx assertCount(). #3108052 by Berdir, SpadXIII: metatag_get_default_tags() does not revert config override language. #2862747 by JeroenT, DanielVeza, rwohleb, joshua.boltz, henrikakselsen, Vitalyos, Phil Wolstenholme: Tokens to access individual meta tag values. Metatag 8.x-1.14, 2020-08-11 Loading
metatag.tokens.inc 0 → 100644 +228 −0 Original line number Diff line number Diff line <?php /** * @file * Metatag token integration. */ use Drupal\Component\Utility\Html; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Render\BubbleableMetadata; /** * Implements hook_token_info(). */ function metatag_token_info() { $info = []; $group_manager = \Drupal::service('plugin.manager.metatag.group'); $tag_manager = \Drupal::service('plugin.manager.metatag.tag'); $tag_definitions = $tag_manager->getDefinitions(); $info['types']['metatag'] = [ 'name' => t('Metatags'), 'description' => t('Tokens related to Metatags.'), 'needs-data' => 'metatag', ]; foreach ($tag_definitions as $tag_id => $tag_definition) { $label = $tag_definition['label']; $description = $tag_definition['description']; $multiple = $tag_definition['multiple']; $metatag_token_name = 'metatag-' . $tag_id; $group = $group_manager->getDefinition($tag_definition['group']); if ($group) { $label = $group['label'] . ': ' . $label; } $info['tokens']['current-page']['metatag'] = [ 'name' => t('Metatags'), 'description' => t('Metatag values for the current page.'), 'type' => 'metatag', ]; $info['tokens']['metatag'][$tag_id] = [ 'name' => Html::escape($label), 'description' => $description, 'type' => $multiple ? "list<$metatag_token_name>" : $metatag_token_name, ]; $info['types'][$metatag_token_name] = [ 'name' => Html::escape($label), 'description' => t('@label tokens.', ['@label' => Html::escape($label)]), 'needs-data' => $metatag_token_name, 'nested' => TRUE, ]; // Tag list token type. if ($multiple) { $info['types']["list<$metatag_token_name>"] = [ 'name' => t('List of @type values', ['@type' => Html::escape($label)]), 'description' => t('Tokens for lists of @type values.', ['@type' => Html::escape($label)]), 'needs-data' => "list<$metatag_token_name>", 'nested' => TRUE, ]; // Show a different token for each tag delta. // Since we don't know how many there will be, we will just show 3. for ($delta = 0; $delta < 3; $delta++) { $info['tokens']["list<$metatag_token_name>"][$delta] = [ 'name' => t('@type type with delta @delta', [ '@type' => Html::escape($label), '@delta' => $delta, ]), 'module' => 'token', 'type' => $metatag_token_name, ]; } } } return $info; } /** * Implements hook_token_info_alter(). */ function metatag_token_info_alter(&$info) { foreach (\Drupal::entityTypeManager()->getDefinitions() as $entity_type_id => $entity_type) { if (!$entity_type->entityClassImplements(ContentEntityInterface::class)) { continue; } // Make sure a token type exists for this entity. $token_type = \Drupal::service('token.entity_mapper')->getTokenTypeForEntityType($entity_type_id); if (empty($token_type) || !isset($info['types'][$token_type])) { continue; } $fields = \Drupal::entityTypeManager()->getStorage('field_storage_config')->loadByProperties([ 'entity_type' => $entity_type_id, 'type' => 'metatag', ]); foreach ($fields as $field) { $field_token_name = $token_type . '-' . $field->getName(); $info['types'][$field_token_name] = [ 'name' => Html::escape($field->getName()), 'description' => t('@label tokens.', ['@label' => Html::escape($field->getName())]), 'needs-data' => $field_token_name, 'nested' => TRUE, 'type' => 'metatag', 'module' => 'metatag', ]; } } } /** * Implements hook_tokens(). */ function metatag_tokens($type, $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) { $replacements = []; switch ($type) { case 'current-page': /** @var \Drupal\token\TokenInterface $token_service */ $token_service = \Drupal::token(); $metatag_tokens = $token_service->findWithPrefix($tokens, 'metatag'); if (!empty($metatag_tokens) && metatag_is_current_route_supported()) { // Add cache contexts to ensure this token functions on a per-path // basis. $bubbleable_metadata->addCacheContexts(['url.site']); $replacements += $token_service->generate('metatag', $metatag_tokens, [], $options, $bubbleable_metadata); } break; case 'entity': if (!empty($data['entity_type']) && !empty($data['entity']) && !empty($data['token_type'])) { /* @var \Drupal\Core\Entity\ContentEntityInterface $entity */ $entity = $data['entity']; if (!($entity instanceof ContentEntityInterface)) { return $replacements; } $metatag_fields = []; foreach ($tokens as $name => $original) { $field_name = explode(':', $name)[0]; if ($entity->hasField($field_name) && $entity->get($field_name)->getFieldDefinition()->getType() === 'metatag') { $metatag_fields[] = $field_name; } } if (!empty($metatag_fields)) { /** @var \Drupal\token\TokenInterface $token_service */ $token_service = \Drupal::token(); $metatag_tokens = []; foreach ($metatag_fields as $metatag_field) { $metatag_tokens += $token_service->findWithPrefix($tokens, $metatag_field); } $replacements += $token_service->generate('metatag', $metatag_tokens, ['entity' => $entity], $options, $bubbleable_metadata); } } break; case 'metatag': $metatag_manager = \Drupal::service('metatag.manager'); $entity = $options['entity'] ?? metatag_get_route_entity(); $tags = metatag_get_default_tags($entity); if ($entity instanceof ContentEntityInterface) { // If content entity does not have an ID the page is likely an "Add" // page, so skip processing for entity which has not been created yet. if (!$entity->id()) { return NULL; } $tags += $metatag_manager->tagsFromEntity($entity); } // Trigger hook_metatags_alter(). // Allow modules to override tags or the entity used for token // replacements. $context = [ 'entity' => &$entity, ]; \Drupal::service('module_handler')->alter('metatags', $tags, $context); // If the entity was changed above, use that for generating the meta tags. if (isset($context['entity'])) { $entity = $context['entity']; } $processed_tags = $metatag_manager->generateTokenValues($tags, $entity); foreach ($tokens as $name => $original) { // For the [metatag:tag_name] token. if (strpos($name, ':') === FALSE) { $tag_name = $name; } // For [metatag:tag_name:0], [metatag:tag_name:0:value] and // [metatag:tag_name:value] tokens. else { [$tag_name, $delta] = explode(':', $name, 2); if (!is_numeric($delta)) { unset($delta); } } // Replace dashes (-) with underscores (_) for e.g. canonical-url. $tag_name = str_replace('-', '_', $tag_name); if (empty($processed_tags[$tag_name])) { continue; } // Render only one delta. if (isset($delta)) { $replacements[$original] = $processed_tags[$tag_name][$delta]; } else { $replacements[$original] = is_array($processed_tags[$tag_name]) ? implode(',', $processed_tags[$tag_name]) : $processed_tags[$tag_name]; } } break; } return $replacements; }
src/MetatagManager.php +76 −4 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ namespace Drupal\metatag; use Drupal\Component\Render\PlainTextOutput; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\LanguageInterface; Loading Loading @@ -54,6 +55,13 @@ class MetatagManager implements MetatagManagerInterface { */ protected $logger; /** * Caches processed strings, keyed by tag name. * * @var array */ protected $processedTokenCache = []; /** * Constructor for MetatagManager. * Loading @@ -72,7 +80,8 @@ class MetatagManager implements MetatagManagerInterface { MetatagTagPluginManager $tagPluginManager, MetatagToken $token, LoggerChannelFactoryInterface $channelFactory, EntityTypeManagerInterface $entityTypeManager) { EntityTypeManagerInterface $entityTypeManager ) { $this->groupPluginManager = $groupPluginManager; $this->tagPluginManager = $tagPluginManager; $this->tokenService = $token; Loading Loading @@ -593,6 +602,69 @@ class MetatagManager implements MetatagManagerInterface { return $rawTags; } /** * Generate the actual meta tag values for use as tokens. * * @param array $tags * The array of tags as plugin_id => value. * @param object $entity * Optional entity object to use for token replacements. * * @return array * Array of MetatagTag plugin instances. */ public function generateTokenValues(array $tags, $entity = NULL) { // Ignore the update.php path. $request = \Drupal::request(); if ($request->getBaseUrl() == '/update.php') { return []; } $entity_identifier = '_none'; if ($entity) { $entity_identifier = $entity->getEntityTypeId() . ':' . ($entity->uuid() ?: $entity->id()); } if (!isset($this->processedTokenCache[$entity_identifier])) { $metatag_tags = $this->tagPluginManager->getDefinitions(); // Each element of the $values array is a tag with the tag plugin name as // the key. foreach ($tags as $tag_name => $value) { // Check to ensure there is a matching plugin. if (isset($metatag_tags[$tag_name])) { // Get an instance of the plugin. $tag = $this->tagPluginManager->createInstance($tag_name); // Render any tokens in the value. $token_replacements = []; if ($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()]; } elseif ($entity instanceof ContentEntityInterface) { $token_replacements = [$entity->getEntityTypeId() => $entity]; } } // Set the value as sometimes the data needs massaging, such as when // field defaults are used for the Robots field, which come as an // array that needs to be filtered and converted to a string. // @see Robots::setValue() $tag->setValue($value); $langcode = \Drupal::languageManager()->getCurrentLanguage(LanguageInterface::TYPE_CONTENT)->getId(); $value = PlainTextOutput::renderFromHtml(htmlspecialchars_decode($this->tokenService->replace($value, $token_replacements, ['langcode' => $langcode]))); $this->processedTokenCache[$entity_identifier][$tag_name] = $tag->multiple() ? explode(',', $value) : $value; } } } return $this->processedTokenCache[$entity_identifier]; } /** * Returns a list of fields handled by Metatag. * Loading
tests/src/Functional/MetatagTokenTest.php 0 → 100644 +113 −0 Original line number Diff line number Diff line <?php namespace Drupal\Tests\metatag\Functional; use Drupal\Tests\BrowserTestBase; use Drupal\Tests\field_ui\Traits\FieldUiTestTrait; use Drupal\Tests\token\Functional\TokenTestTrait; /** * Verify that metatag token generation is working. * * @group metatag */ class MetatagTokenTest extends BrowserTestBase { use TokenTestTrait; use FieldUiTestTrait; /** * {@inheritdoc} */ protected static $modules = [ 'block', 'field_ui', 'user', 'token', 'token_module_test', 'metatag', 'metatag_open_graph', ]; /** * {@inheritdoc} */ protected $defaultTheme = 'stark'; /** * {@inheritdoc} */ public function setUp(): void { parent::setUp(); $this->drupalPlaceBlock('system_breadcrumb_block'); $this->drupalPlaceBlock('local_tasks_block'); $this->drupalPlaceBlock('page_title_block'); $this->drupalLogin($this->rootUser); $this->fieldUIAddNewField('/admin/config/people/accounts', 'metatags', 'Metatags', 'metatag'); } /** * Test current-page metatag token generation. */ public function testMetatagCurrentPageTokens() { $user = $this->createUser([]); $this->drupalGet($user->toUrl('edit-form')); $this->submitForm([ 'field_metatags[0][basic][abstract]' => 'My abstract', 'field_metatags[0][open_graph][og_title]' => 'My OG Title', 'field_metatags[0][open_graph][og_image]' => 'Image 1,Image 2', ], 'Save'); $tokens = [ // Test globally configured metatags. '[current-page:metatag:title]' => sprintf('%s | %s', $user->getAccountName(), $this->config('system.site') ->get('name')), '[current-page:metatag:description]' => $this->config('system.site') ->get('name'), '[current-page:metatag:canonical-url]' => $user->toUrl('canonical', ['absolute' => TRUE]) ->toString(), // Test entity overridden metatags. '[current-page:metatag:abstract]' => 'My abstract', // Test metatags provided by a submodule. '[current-page:metatag:og-title]' => 'My OG Title', // Test metatags that can contain multiple values. '[current-page:metatag:og_image]' => 'Image 1,Image 2', '[current-page:metatag:og_image:0]' => 'Image 1', '[current-page:metatag:og_image:1]' => 'Image 2', ]; $this->assertPageTokens($user->toUrl(), $tokens); } /** * Test entity token generation. */ public function testMetatagEntityTokens() { $user = $this->createUser(); $this->drupalGet($user->toUrl('edit-form')); $this->submitForm([ 'field_metatags[0][basic][abstract]' => 'My abstract', 'field_metatags[0][open_graph][og_title]' => 'My OG Title', 'field_metatags[0][open_graph][og_image]' => 'Image 1,Image 2', ], 'Save'); $tokens = [ // Test globally configured metatags. '[user:field_metatags:title]' => sprintf('%s | %s', $user->getAccountName(), $this->config('system.site')->get('name')), '[user:field_metatags:description]' => $this->config('system.site')->get('name'), '[user:field_metatags:canonical-url]' => $user->toUrl('canonical', ['absolute' => TRUE])->toString(), // Test entity overridden metatags. '[user:field_metatags:abstract]' => 'My abstract', // Test metatags provided by a submodule. '[user:field_metatags:og-title]' => 'My OG Title', // Test metatags that can contain multiple values. '[user:field_metatags:og_image]' => 'Image 1,Image 2', '[user:field_metatags:og_image:0]' => 'Image 1', '[user:field_metatags:og_image:1]' => 'Image 2', ]; $this->assertPageTokens($user->toUrl(), $tokens, ['user' => $user]); } }