View source
<?php
namespace Drupal\token;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\StringTranslation\StringTranslationTrait;
class TreeBuilder implements TreeBuilderInterface {
use StringTranslationTrait;
protected $tokenService;
protected $entityMapper;
protected $languageManager;
protected $cacheBackend;
protected $builtTrees;
public function __construct(TokenInterface $token_service, TokenEntityMapperInterface $entity_mapper, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager) {
$this->tokenService = $token_service;
$this->entityMapper = $entity_mapper;
$this->cacheBackend = $cache_backend;
$this->languageManager = $language_manager;
}
public function buildRenderable(array $token_types, array $options = []) {
$options += [
'global_types' => TRUE,
'click_insert' => TRUE,
'show_restricted' => FALSE,
'show_nested' => FALSE,
'recursion_limit' => 3,
];
$info = $this->tokenService
->getInfo();
if ($options['global_types']) {
$token_types = array_merge($token_types, $this->tokenService
->getGlobalTokenTypes());
}
$element = [];
$tree_options = [
'flat' => TRUE,
'restricted' => $options['show_restricted'],
'nested' => $options['show_nested'],
'depth' => $options['recursion_limit'],
];
$token_tree = [];
foreach ($info['types'] as $type => $type_info) {
if (!in_array($type, $token_types)) {
continue;
}
$token_tree[$type] = $type_info;
$token_tree[$type]['tokens'] = $this
->buildTree($type, $tree_options);
}
$element += [
'#type' => 'token_tree_table',
'#token_tree' => $token_tree,
'#show_restricted' => $options['show_restricted'],
'#show_nested' => $options['show_nested'],
'#click_insert' => $options['click_insert'],
'#columns' => [
'name',
'token',
'description',
],
'#empty' => $this
->t('No tokens available'),
];
return $element;
}
public function buildAllRenderable(array $options = []) {
$info = $this->tokenService
->getInfo();
$token_types = array_keys($info['types']);
$options['global_types'] = FALSE;
return $this
->buildRenderable($token_types, $options);
}
public function buildTree($token_type, array $options = []) {
$options += [
'restricted' => FALSE,
'depth' => 4,
'data' => [],
'values' => FALSE,
'flat' => FALSE,
];
$options['depth'] = min($options['depth'], static::MAX_DEPTH);
if ($entity_token_type = $this->entityMapper
->getTokenTypeForEntityType($token_type)) {
$token_type = $entity_token_type;
}
$langcode = $this->languageManager
->getCurrentLanguage()
->getId();
$tree_cid = "token_tree:{$token_type}:{$langcode}:{$options['depth']}";
if (!isset($this->builtTrees[$tree_cid])) {
if ($cache = $this->cacheBackend
->get($tree_cid)) {
$this->builtTrees[$tree_cid] = $cache->data;
}
else {
$options['parents'] = [];
$this->builtTrees[$tree_cid] = $this
->getTokenData($token_type, $options);
$this->cacheBackend
->set($tree_cid, $this->builtTrees[$tree_cid], Cache::PERMANENT, [
Token::TOKEN_INFO_CACHE_TAG,
]);
}
}
$tree = $this->builtTrees[$tree_cid];
if (!empty($options['flat'])) {
$tree = $this
->flattenTree($tree);
}
if (!empty($options['values'])) {
$token_values = [];
foreach ($tree as $token => $token_info) {
if (!empty($token_info['dynamic']) || !empty($token_info['restricted'])) {
continue;
}
elseif (!isset($token_info['value'])) {
$token_values[$token_info['token']] = $token;
}
}
if (!empty($token_values)) {
$token_values = $this->tokenService
->generate($token_type, $token_values, $options['data'], [], new BubbleableMetadata());
foreach ($token_values as $token => $replacement) {
$tree[$token]['value'] = $replacement;
}
}
}
return $tree;
}
public function flattenTree(array $tree) {
$result = [];
foreach ($tree as $token => $token_info) {
$result[$token] = $token_info;
if (isset($token_info['children']) && is_array($token_info['children'])) {
$result += $this
->flattenTree($token_info['children']);
}
}
return $result;
}
protected function getTokenData($token_type, array $options) {
$options += [
'parents' => [],
];
$info = $this->tokenService
->getInfo();
if ($options['depth'] <= 0 || !isset($info['types'][$token_type]) || !isset($info['tokens'][$token_type])) {
return [];
}
$tree = [];
foreach ($info['tokens'][$token_type] as $token => $token_info) {
$token_parents = $options['parents'];
if (empty($token_parents)) {
$token_parents[] = $token_type;
}
elseif ($token !== 'entity' && in_array($token, array_slice($token_parents, 1), TRUE)) {
continue;
}
$token_parents[] = $token;
if (!empty($token_info['dynamic'])) {
$token_parents[] = '?';
}
$raw_token = '[' . implode(':', $token_parents) . ']';
$tree[$raw_token] = $token_info;
$tree[$raw_token]['raw token'] = $raw_token;
$tree[$raw_token]['token'] = implode(':', array_slice($token_parents, 1));
if (!empty($options['parents'])) {
$tree[$raw_token]['parent'] = '[' . implode(':', $options['parents']) . ']';
}
if (!empty($token_info['type'])) {
$child_options = $options;
$child_options['depth']--;
$child_options['parents'] = $token_parents;
$tree[$raw_token]['children'] = $this
->getTokenData($token_info['type'], $child_options);
}
}
return $tree;
}
}