
| Current Path : /var/www/html/strat/web/modules/contrib/token/ |
Linux ift1.ift-informatik.de 5.4.0-216-generic #236-Ubuntu SMP Fri Apr 11 19:53:21 UTC 2025 x86_64 |
| Current File : /var/www/html/strat/web/modules/contrib/token/token.module |
<?php
/**
* @file
* Enhances the token API in core: adds a browsable UI, missing tokens, etc.
*/
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Hook\Attribute\LegacyHook;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Entity\EntityTypeInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Menu\MenuLinkInterface;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\menu_link_content\MenuLinkContentInterface;
use Drupal\node\Entity\Node;
use Drupal\node\NodeInterface;
use Drupal\token\Hook\TokenEntityHooks;
use Drupal\token\Hook\TokenFormHooks;
use Drupal\token\Hook\TokenHooks;
use Drupal\token\Hook\TokenThemeHooks;
use Drupal\token\Hook\TokenTokenInfoHooks;
use Drupal\token\Hook\TokenTokensHooks;
/**
* Implements hook_help().
*/
#[LegacyHook]
function token_help($route_name, RouteMatchInterface $route_match) {
return \Drupal::service(TokenHooks::class)->help($route_name, $route_match);
}
/**
* Implements hook_theme().
*/
#[LegacyHook]
function token_theme() {
return \Drupal::service(TokenThemeHooks::class)->theme();
}
/**
* Implements hook_block_view_alter().
*/
#[LegacyHook]
function token_block_view_alter(&$build, BlockPluginInterface $block) {
\Drupal::service(TokenHooks::class)->blockViewAlter($build, $block);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
#[LegacyHook]
function token_form_block_form_alter(&$form, FormStateInterface $form_state) {
\Drupal::service(TokenFormHooks::class)->formBlockFormAlter($form, $form_state);
}
/**
* Implements hook_field_info_alter().
*/
#[LegacyHook]
function token_field_info_alter(&$info) {
\Drupal::service(TokenEntityHooks::class)->fieldInfoAlter($info);
}
/**
* Implements hook_ENTITY_TYPE_insert().
*/
#[LegacyHook]
function token_date_format_insert() {
Drupal::service(TokenEntityHooks::class)->entitySaveClearCache();
}
/**
* Implements hook_ENTITY_TYPE_delete().
*/
#[LegacyHook]
function token_date_format_delete() {
Drupal::service(TokenEntityHooks::class)->entitySaveClearCache();
}
/**
* Implements hook_ENTITY_TYPE_presave().
*/
#[LegacyHook]
function token_field_config_presave($instance) {
Drupal::service(TokenEntityHooks::class)->entitySaveClearCache();
}
/**
* Implements hook_ENTITY_TYPE_delete().
*/
#[LegacyHook]
function token_field_config_delete($instance) {
Drupal::service(TokenEntityHooks::class)->entitySaveClearCache();
}
/**
* Clear token caches and static variables.
*/
function token_clear_cache() {
\Drupal::token()->resetInfo();
\Drupal::service('token.entity_mapper')->resetInfo();
drupal_static_reset('token_menu_link_load_all_parents');
drupal_static_reset('token_book_link_load');
}
/**
* Implements hook_entity_type_alter().
*/
#[LegacyHook]
function token_entity_type_alter(array &$entity_types) {
\Drupal::service(TokenEntityHooks::class)->entityTypeAlter($entity_types);
}
/**
* Return the module responsible for a token.
*
* @param string $type
* The token type.
* @param string $name
* The token name.
*
* @return mixed
* The value of $info['tokens'][$type][$name]['module'] from token info, or
* NULL if the value does not exist.
*
* @deprecated in token:8.x-1.x and is removed from token:2.0.0. Use the
* token.module_provider service instead.
*/
function _token_module($type, $name) {
return \Drupal::service('token.module_provider')->getTokenModule($type, $name);
}
/**
* Validate a form element that should have tokens in it.
*
* Form elements that want to add this validation should have the #token_types
* parameter defined.
*
* For example:
* @code
* $form['my_node_text_element'] = [
* '#type' => 'textfield',
* '#title' => t('Some text to token-ize that has a node context.'),
* '#default_value' => 'The title of this node is [node:title].',
* '#element_validate' => ['token_element_validate'],
* '#token_types' => ['node'],
* '#min_tokens' => 1,
* '#max_tokens' => 10,
* ];
* @endcode
*/
function token_element_validate($element, FormStateInterface $form_state) {
$value = $element['#value'] ?? $element['#default_value'];
if (empty($value)) {
// Empty value needs no further validation since the element should depend
// on using the '#required' FAPI property.
return $element;
}
$tokens = \Drupal::token()->scan($value);
$title = empty($element['#title']) ? $element['#parents'][0] : $element['#title'];
// Validate if an element must have a minimum number of tokens.
if (isset($element['#min_tokens']) && count($tokens) < $element['#min_tokens']) {
$error = \Drupal::translation()->formatPlural($element['#min_tokens'], '%name must contain at least one token.', '%name must contain at least @count tokens.', ['%name' => $title]);
$form_state->setError($element, $error);
}
// Validate if an element must have a maximum number of tokens.
if (isset($element['#max_tokens']) && count($tokens) > $element['#max_tokens']) {
$error = \Drupal::translation()->formatPlural($element['#max_tokens'], '%name must contain at most one token.', '%name must contain at most @count tokens.', ['%name' => $title]);
$form_state->setError($element, $error);
}
// Check if the field defines specific token types.
if (isset($element['#token_types'])) {
$invalid_tokens = \Drupal::token()->getInvalidTokensByContext($tokens, $element['#token_types']);
if ($invalid_tokens) {
$form_state->setError($element, t('%name is using the following invalid tokens: @invalid-tokens.', ['%name' => $title, '@invalid-tokens' => implode(', ', $invalid_tokens)]));
}
}
return $element;
}
/**
* Implements hook_form_FORM_ID_alter().
*/
#[LegacyHook]
function token_form_field_config_edit_form_alter(&$form, FormStateInterface $form_state) {
\Drupal::service(TokenFormHooks::class)->formFieldConfigEditFormAlter($form, $form_state);
}
/**
* Implements hook_form_BASE_FORM_ID_alter().
*
* Alters the configure action form to add token context validation and
* adds the token tree for a better token UI and selection.
*/
#[LegacyHook]
function token_form_action_form_alter(&$form, $form_state) {
\Drupal::service(TokenFormHooks::class)->formActionFormAlter($form, $form_state);
}
/**
* Implements hook_form_FORM_ID_alter().
*/
#[LegacyHook]
function token_form_user_admin_settings_alter(&$form, FormStateInterface $form_state) {
\Drupal::service(TokenFormHooks::class)->formUserAdminSettingsAlter($form, $form_state);
}
/**
* Prepare a string for use as a valid token name.
*
* @param $name
* The token name to clean.
*
* @return
* The cleaned token name.
*/
function token_clean_token_name($name) {
static $names = [];
if (!isset($names[$name])) {
$cleaned_name = strtr($name, [' ' => '-', '_' => '-', '/' => '-', '[' => '-', ']' => '']);
$cleaned_name = preg_replace('/[^\w\-]/i', '', $cleaned_name);
$cleaned_name = trim($cleaned_name, '-');
$names[$name] = $cleaned_name;
}
return $names[$name];
}
/**
* Do not use this function yet. Its API has not been finalized.
*/
function token_render_array(array $array, array $options = []) {
$rendered = [];
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
foreach (token_element_children($array) as $key) {
$value = $array[$key];
$rendered[] = is_array($value) ? $renderer->renderInIsolation($value) : (string) $value;
}
$join = $options['join'] ?? ', ';
return implode($join, $rendered);
}
/**
* Do not use this function yet. Its API has not been finalized.
*/
function token_render_array_value($value, array $options = []) {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = \Drupal::service('renderer');
$rendered = is_array($value) ? $renderer->renderInIsolation($value) : (string) $value;
return $rendered;
}
/**
* Loads menu link titles for all parents of a menu link plugin ID.
*
* @param string $plugin_id
* The menu link plugin ID.
* @param string $langcode
* The language code.
*
* @return string[]
* List of menu link parent titles.
*/
function token_menu_link_load_all_parents($plugin_id, $langcode) {
$cache = &drupal_static(__FUNCTION__, []);
if (!isset($cache[$plugin_id][$langcode])) {
$cache[$plugin_id][$langcode] = [];
/** @var \Drupal\Core\Menu\MenuLinkManagerInterface $menu_link_manager */
$menu_link_manager = \Drupal::service('plugin.manager.menu.link');
$parent_ids = $menu_link_manager->getParentIds($plugin_id);
// Remove the current plugin ID from the parents.
unset($parent_ids[$plugin_id]);
foreach ($parent_ids as $parent_id) {
$parent = $menu_link_manager->createInstance($parent_id);
$cache[$plugin_id][$langcode] = [$parent_id => token_menu_link_translated_title($parent, $langcode)] + $cache[$plugin_id][$langcode];
}
}
return $cache[$plugin_id][$langcode];
}
/**
* Returns the translated link of a menu title.
*
* If the underlying entity is a content menu item, load it to get the
* translated menu item title.
*
* @todo Remove this when there is a better way to get a translated menu
* item title in core: https://www.drupal.org/node/2795143
*
* @param \Drupal\Core\Menu\MenuLinkInterface $menu_link
* The menu link.
* @param string|null $langcode
* (optional) The langcode, defaults to the current language.
*
* @return string
* The menu link title.
*/
function token_menu_link_translated_title(MenuLinkInterface $menu_link, $langcode = NULL) {
$metadata = $menu_link->getMetaData();
if (isset($metadata['entity_id']) && $menu_link->getProvider() == 'menu_link_content') {
/** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */
$entity = \Drupal::entityTypeManager()->getStorage('menu_link_content')->load($metadata['entity_id']);
if (!empty($entity)) {
$entity = \Drupal::service('entity.repository')->getTranslationFromContext($entity, $langcode);
return $entity->getTitle();
}
}
return $menu_link->getTitle();
}
/**
* Loads all the parents of the term in the specified language.
*
* @param int $tid
* The term id.
* @param string $langcode
* The language code.
*
* @return string[]
* The term parents collection.
*/
function token_taxonomy_term_load_all_parents($tid, $langcode) {
$cache = &drupal_static(__FUNCTION__, []);
if (!is_numeric($tid)) {
return [];
}
if (!isset($cache[$langcode][$tid])) {
$cache[$langcode][$tid] = [];
/** @var \Drupal\taxonomy\TermStorageInterface $term_storage */
$term_storage = \Drupal::entityTypeManager()->getStorage('taxonomy_term');
$parents = $term_storage->loadAllParents($tid);
// Remove this term from the array.
array_shift($parents);
$parents = array_reverse($parents);
foreach ($parents as $term) {
$translation = \Drupal::service('entity.repository')->getTranslationFromContext($term, $langcode);
$cache[$langcode][$tid][$term->id()] = $translation->label();
}
}
return $cache[$langcode][$tid];
}
/**
*
*/
function token_element_children(&$elements, $sort = FALSE) {
// Do not attempt to sort elements which have already been sorted.
$sort = isset($elements['#sorted']) ? !$elements['#sorted'] : $sort;
// Filter out properties from the element, leaving only children.
$children = [];
$sortable = FALSE;
foreach ($elements as $key => $value) {
if (is_int($key) || $key === '' || $key[0] !== '#') {
$children[$key] = $value;
if (is_array($value) && isset($value['#weight'])) {
$sortable = TRUE;
}
}
}
// Sort the children if necessary.
if ($sort && $sortable) {
uasort($children, 'Drupal\Component\Utility\SortArray::sortByWeightProperty');
// Put the sorted children back into $elements in the correct order, to
// preserve sorting if the same element is passed through
// element_children() twice.
foreach ($children as $key => $child) {
unset($elements[$key]);
$elements[$key] = $child;
}
$elements['#sorted'] = TRUE;
}
return array_keys($children);
}
/**
* Loads all the parents of the book page.
*
* @param array $book
* The book data. The 'nid' key points to the current page of the book.
* The 'p1' ... 'p9' keys point to parents of the page, if they exist, with 'p1'
* pointing to the book itself and the last defined pX to the current page.
*
* @return string[]
* List of node titles of the book parents.
*/
function token_book_load_all_parents(array $book) {
$cache = &drupal_static(__FUNCTION__, []);
if (empty($book['nid'])) {
return [];
}
$nid = $book['nid'];
if (!isset($cache[$nid])) {
$cache[$nid] = [];
$i = 1;
while ($book["p$i"] != $nid) {
$cache[$nid][] = Node::load($book["p$i"])->getTitle();
$i++;
}
}
return $cache[$nid];
}
/**
* Implements hook_entity_base_field_info().
*/
#[LegacyHook]
function token_entity_base_field_info(EntityTypeInterface $entity_type) {
return \Drupal::service(TokenEntityHooks::class)->entityBaseFieldInfo($entity_type);
}
/**
* Implements hook_form_BASE_FORM_ID_alter() for node_form.
*
* Populates menu_link field on nodes from the menu item on unsaved nodes.
*
* @see menu_ui_form_node_form_submit()
* @see token_entity_base_field_info()
*/
#[LegacyHook]
function token_form_node_form_alter(&$form, FormStateInterface $form_state) {
\Drupal::service(TokenFormHooks::class)->formNodeFormAlter($form, $form_state);
}
/**
* Entity builder.
*/
function token_node_menu_link_submit($entity_type, NodeInterface $node, &$form, FormStateInterface $form_state) {
// Entity builders run twice, once during validation and again during
// submission, so we only run this code after validation has been performed.
if (!$form_state->isValueEmpty('menu') && $form_state->getTemporaryValue('entity_validated')) {
// Don't create a menu link if the node is not being saved.
$triggering_element = $form_state->getTriggeringElement();
if (!$triggering_element || !isset($triggering_element['#submit']) || !in_array('::save', $triggering_element['#submit'])) {
return;
}
$values = $form_state->getValue('menu');
$needs_save = FALSE;
if (empty($values['enabled']) || empty(trim($values['title']))) {
return;
}
if (!empty($values['menu_parent'])) {
[$menu_name, $parent] = explode(':', $values['menu_parent'], 2);
$values['menu_name'] = $menu_name;
$values['parent'] = $parent;
}
// Construct an unsaved entity.
if (!empty($values['entity_id'])) {
$entity = \Drupal::service('entity.repository')->getActive('menu_link_content', $values['entity_id']);
if ($entity->isTranslatable() && $node->isTranslatable()) {
if (!$entity->hasTranslation($node->language()->getId())) {
$entity = $entity->addTranslation($node->language()->getId(), $entity->toArray());
}
else {
$entity = $entity->getTranslation($node->language()->getId());
}
}
else {
// Ensure the entity matches the node language.
$entity = $entity->getUntranslated();
if ($entity->language()->getId() != $node->language()->getId()) {
$entity->set($entity->getEntityType()->getKey('langcode'), $node->language()->getId());
$needs_save = TRUE;
}
}
}
else {
// Create a new menu_link_content entity.
if ($node->isNew()) {
$entity = MenuLinkContent::create([
// Lets just reference the UUID for now, the link is not important for
// token generation.
'link' => ['uri' => 'internal:/node/' . $node->uuid()],
'langcode' => $node->language()->getId(),
]);
}
else {
$entity = MenuLinkContent::create([
'link' => ['uri' => 'entity:node/' . $node->id()],
'langcode' => $node->language()->getId(),
]);
}
$entity->enabled->value = 1;
$needs_save = TRUE;
}
// Check if any value changed and only save if necessary.
if ($entity->get('title')->value != trim($values['title'])) {
$entity->set('title', trim($values['title']));
$needs_save = TRUE;
}
if ($entity->get('description')->value != trim($values['description'])) {
$entity->set('description', trim($values['description']));
$needs_save = TRUE;
}
if ($entity->get('menu_name')->value != $values['menu_name']) {
$entity->set('menu_name', $values['menu_name']);
$needs_save = TRUE;
}
if ($entity->get('parent')->value != $values['parent']) {
$entity->set('parent', $values['parent']);
$needs_save = TRUE;
}
if ($entity->get('weight')->value != ($values['weight'] ?? 0)) {
$entity->set('weight', $values['weight'] ?? 0);
$needs_save = TRUE;
}
if ($entity->isNew()) {
// @todo The menu link doesn't need to be changed in a workspace context.
// Fix this in https://www.drupal.org/project/drupal/issues/3511204.
if (!$node->isDefaultRevision() && $node->hasLinkTemplate('latest-version')) {
// If a new menu link is created while saving the node as a pending draft
// (non-default revision), store it as a link to the latest version.
// That ensures that there is a regular, valid link target that is
// only visible to users with permission to view the latest version.
$entity->get('link')->uri = 'internal:/node/' . $node->id() . '/latest';
$needs_save = TRUE;
}
}
else {
if ($entity->isDefaultRevision() != $node->isDefaultRevision()) {
$entity->isDefaultRevision($node->isDefaultRevision());
$needs_save = TRUE;
}
if (!$entity->isDefaultRevision()) {
$entity->setNewRevision(TRUE);
$needs_save = TRUE;
}
elseif ($entity->get('link')->uri !== 'entity:node/' . $node->id()) {
$entity->get('link')->uri = 'entity:node/' . $node->id();
$needs_save = TRUE;
}
}
if ($needs_save) {
$entity->isDefaultRevision($node->isDefaultRevision());
$entity->save();
$node->menu_link = $entity;
// Leave this for _menu_ui_node_save() to pick up so we don't end up with
// duplicate menu-links.
$form_state->setValue(['menu', 'entity_id'], $entity->id());
}
}
}
/**
* Implements hook_ENTITY_TYPE_insert for node entities.
*/
#[LegacyHook]
function token_node_insert(NodeInterface $node) {
Drupal::service(TokenEntityHooks::class)->nodeInsert($node);
}
/**
* Implements hook_ENTITY_TYPE_presave() for menu_link_content.
*/
#[LegacyHook]
function token_menu_link_content_presave(MenuLinkContentInterface $menu_link_content) {
Drupal::service(TokenEntityHooks::class)->menuLinkContentPresave($menu_link_content);
}
/**
* Implements hook_token_info_alter().
*/
#[LegacyHook]
function token_token_info_alter(&$info) {
\Drupal::service(TokenTokenInfoHooks::class)->tokenInfoAlter($info);
}
/**
* Implements hook_token_info().
*/
#[LegacyHook]
function token_token_info() {
return \Drupal::service(TokenTokenInfoHooks::class)->tokenInfo();
}
/**
* Implements hook_tokens().
*/
#[LegacyHook]
function token_tokens($type, array $tokens, array $data, array $options, BubbleableMetadata $bubbleable_metadata) {
return \Drupal::service(TokenTokensHooks::class)->tokens($type, $tokens, $data, $options, $bubbleable_metadata);
}
/**
* Returns the token view display for the given entity if enabled.
*
* @param \Drupal\Core\Entity\EntityInterface $entity
* The entity.
*
* @return \Drupal\Core\Entity\Display\EntityViewDisplayInterface|null
* The view display or null.
*/
function token_get_token_view_display(EntityInterface $entity) {
$view_mode_name = $entity->getEntityTypeId() . '.' . $entity->bundle() . '.token';
$view_display = \Drupal::entityTypeManager()->getStorage('entity_view_display')->load($view_mode_name);
return ($view_display && $view_display->status()) ? $view_display : NULL;
}
/**
* Theme a link to a token tree shown as a dialog.
*/
function template_preprocess_token_tree_link(&$variables) {
\Drupal::service(TokenThemeHooks::class)->preprocessTokenTreeLink($variables);
}