/** * {@inheritdoc} */ public function filter(DataDefinitionInterface $definition, $value, array $arguments, BubbleableMetadata $bubbleable_metadata = NULL) { if ($definition->getDataType() != 'timestamp') { // Convert the date to an timestamp. $value = $this->getTypedDataManager()->create($definition, $value)->getDateTime()->getTimestamp(); } $arguments += [0 => 'medium', 1 => '', 2 => NULL, 3 => NULL]; if ($arguments[0] != 'custom' && $bubbleable_metadata) { $config = $this->dateFormatStorage->load($arguments[0]); if (!$config) { throw new \InvalidArgumentException("Unknown date format {$arguments['0']} given."); } $bubbleable_metadata->addCacheableDependency($config); } return $this->dateFormatter->format($value, $arguments[0], $arguments[1], $arguments[2], $arguments[3]); }
/** * @covers ::addCacheableDependency * @dataProvider providerTestMerge * * This only tests at a high level, because it reuses existing logic. Detailed * tests exist for the existing logic: * * @see \Drupal\Tests\Core\Cache\CacheTest::testMergeTags() * @see \Drupal\Tests\Core\Cache\CacheTest::testMergeMaxAges() * @see \Drupal\Tests\Core\Cache\CacheContextsTest */ public function testAddCacheableDependency(BubbleableMetadata $a, $b, BubbleableMetadata $expected) { $cache_contexts_manager = $this->getMockBuilder('Drupal\\Core\\Cache\\Context\\CacheContextsManager')->disableOriginalConstructor()->getMock(); $cache_contexts_manager->method('assertValidTokens')->willReturn(TRUE); $container = new ContainerBuilder(); $container->set('cache_contexts_manager', $cache_contexts_manager); \Drupal::setContainer($container); $this->assertEquals($expected, $a->addCacheableDependency($b)); }
/** * Provide replacement values for placeholder tokens. * * This hook is invoked when someone calls * \Drupal\Core\Utility\Token::replace(). That function first scans the text for * [type:token] patterns, and splits the needed tokens into groups by type. * Then hook_tokens() is invoked on each token-type group, allowing your module * to respond by providing replacement text for any of the tokens in the group * that your module knows how to process. * * A module implementing this hook should also implement hook_token_info() in * order to list its available tokens on editing screens. * * @param $type * The machine-readable name of the type (group) of token being replaced, such * as 'node', 'user', or another type defined by a hook_token_info() * implementation. * @param $tokens * An array of tokens to be replaced. The keys are the machine-readable token * names, and the values are the raw [type:token] strings that appeared in the * original text. * @param array $data * An associative array of data objects to be used when generating replacement * values, as supplied in the $data parameter to * \Drupal\Core\Utility\Token::replace(). * @param array $options * An associative array of options for token replacement; see * \Drupal\Core\Utility\Token::replace() for possible values. * @param \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata * The bubbleable metadata. Prior to invoking this hook, * \Drupal\Core\Utility\Token::generate() collects metadata for all of the * data objects in $data. For any data sources not in $data, but that are * used by the token replacement logic, such as global configuration (e.g., * 'system.site') and related objects (e.g., $node->getOwner()), * implementations of this hook must add the corresponding metadata. * For example: * @code * $bubbleable_metadata->addCacheableDependency(\Drupal::config('system.site')); * $bubbleable_metadata->addCacheableDependency($node->getOwner()); * @endcode * * Additionally, implementations of this hook, must forward * $bubbleable_metadata to the chained tokens that they invoke. * For example: * @code * if ($created_tokens = $token_service->findWithPrefix($tokens, 'created')) { * $replacements = $token_service->generate('date', $created_tokens, array('date' => $node->getCreatedTime()), $options, $bubbleable_metadata); * } * @endcode * * @return array * An associative array of replacement values, keyed by the raw [type:token] * strings from the original text. The returned values must be either plain * text strings, or an object implementing MarkupInterface if they are * HTML-formatted. * * @see hook_token_info() * @see hook_tokens_alter() */ function hook_tokens($type, $tokens, array $data, array $options, \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata) { $token_service = \Drupal::token(); $url_options = array('absolute' => TRUE); if (isset($options['langcode'])) { $url_options['language'] = \Drupal::languageManager()->getLanguage($options['langcode']); $langcode = $options['langcode']; } else { $langcode = NULL; } $replacements = array(); if ($type == 'node' && !empty($data['node'])) { /** @var \Drupal\node\NodeInterface $node */ $node = $data['node']; foreach ($tokens as $name => $original) { switch ($name) { // Simple key values on the node. case 'nid': $replacements[$original] = $node->nid; break; case 'title': $replacements[$original] = $node->getTitle(); break; case 'edit-url': $replacements[$original] = $node->url('edit-form', $url_options); break; // Default values for the chained tokens handled below. // Default values for the chained tokens handled below. case 'author': $account = $node->getOwner() ? $node->getOwner() : User::load(0); $replacements[$original] = $account->label(); $bubbleable_metadata->addCacheableDependency($account); break; case 'created': $replacements[$original] = format_date($node->getCreatedTime(), 'medium', '', NULL, $langcode); break; } } if ($author_tokens = $token_service->findWithPrefix($tokens, 'author')) { $replacements = $token_service->generate('user', $author_tokens, array('user' => $node->getOwner()), $options, $bubbleable_metadata); } if ($created_tokens = $token_service->findWithPrefix($tokens, 'created')) { $replacements = $token_service->generate('date', $created_tokens, array('date' => $node->getCreatedTime()), $options, $bubbleable_metadata); } } return $replacements; }
/** * Adds the bubbleable metadata of the given data. * * @param \Drupal\Core\TypedData\TypedDataInterface $data * The data of which to add the metadata. * @param \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata * The bubbleable metadata to which to add the data. */ protected function addBubbleableMetadata(TypedDataInterface $data, BubbleableMetadata $bubbleable_metadata) { if ($data instanceof PrimitiveInterface) { // Primitives do not have any metadata attached. return; } $value = $data->getValue(); if ($value instanceof CacheableDependencyInterface || $value instanceof AttachmentsInterface) { $bubbleable_metadata->addCacheableDependency($value); } }
/** * Generates replacement values for a list of tokens. * * @param string $type * The type of token being replaced. 'node', 'user', and 'date' are common. * @param array $tokens * An array of tokens to be replaced, keyed by the literal text of the token * as it appeared in the source text. * @param array $data * An array of keyed objects. For simple replacement scenarios: 'node', * 'user', and others are common keys, with an accompanying node or user * object being the value. Some token types, like 'site', do not require * any explicit information from $data and can be replaced even if it is * empty. * @param array $options * A keyed array of settings and flags to control the token replacement * process. Supported options are: * - langcode: A language code to be used when generating locale-sensitive * tokens. * - callback: A callback function that will be used to post-process the * array of token replacements after they are generated. Can be used when * modules require special formatting of token text, for example URL * encoding or truncation to a specific length. * @param \Drupal\Core\Render\BubbleableMetadata $bubbleable_metadata * The bubbleable metadata. This is passed to the token replacement * implementations so that they can attach their metadata. * * @return array * An associative array of replacement values, keyed by the original 'raw' * tokens that were found in the source text. For example: * $results['[node:title]'] = 'My new node'; * * @see hook_tokens() * @see hook_tokens_alter() */ public function generate($type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) { foreach ($data as $object) { if ($object instanceof CacheableDependencyInterface || $object instanceof AttachmentsInterface) { $bubbleable_metadata->addCacheableDependency($object); } } $replacements = $this->moduleHandler->invokeAll('tokens', [$type, $tokens, $data, $options, $bubbleable_metadata]); // Allow other modules to alter the replacements. $context = array('type' => $type, 'tokens' => $tokens, 'data' => $data, 'options' => $options); $this->moduleHandler->alter('tokens', $replacements, $context, $bubbleable_metadata); return $replacements; }