Updated all the contrib modules to their latest versions.
[yaffs-website] / web / modules / contrib / token / src / TreeBuilder.php
1 <?php
2
3 namespace Drupal\token;
4
5 use Drupal\Core\Cache\Cache;
6 use Drupal\Core\Cache\CacheBackendInterface;
7 use Drupal\Core\Language\LanguageManagerInterface;
8 use Drupal\Core\Render\BubbleableMetadata;
9
10 class TreeBuilder implements TreeBuilderInterface {
11
12   /**
13    * @var \Drupal\token\Token
14    */
15   protected $tokenService;
16
17   /**
18    * @var \Drupal\token\TokenEntityMapperInterface
19    */
20   protected $entityMapper;
21
22   /**
23    * @var \Drupal\Core\Language\LanguageManagerInterface
24    */
25   protected $languageManager;
26
27   /**
28    * @var \Drupal\Core\Cache\CacheBackendInterface
29    */
30   protected $cacheBackend;
31
32   /**
33    * Cache already built trees.
34    *
35    * @var array
36    */
37   protected $builtTrees;
38
39   public function __construct(TokenInterface $token_service, TokenEntityMapperInterface $entity_mapper, CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager) {
40     $this->tokenService = $token_service;
41     $this->entityMapper = $entity_mapper;
42     $this->cacheBackend = $cache_backend;
43     $this->languageManager = $language_manager;
44   }
45
46   /**
47    * {@inheritdoc}
48    */
49   public function buildRenderable(array $token_types, array $options = []) {
50     // Set default options.
51     $options += [
52       'global_types' => TRUE,
53       'click_insert' => TRUE,
54       'show_restricted' => FALSE,
55       'show_nested' => FALSE,
56       'recursion_limit' => 3,
57     ];
58
59     $info = $this->tokenService->getInfo();
60     if ($options['global_types']) {
61       $token_types = array_merge($token_types, $this->tokenService->getGlobalTokenTypes());
62     }
63
64     $element = [
65       /*'#cache' => [
66         'cid' => 'tree-rendered:' . hash('sha256', serialize(['token_types' => $token_types, 'global_types' => NULL] + $variables)),
67         'tags' => [Token::TOKEN_INFO_CACHE_TAG],
68       ],*/
69     ];
70
71     // @todo Find a way to use the render cache for this.
72     /*if ($cached_output = token_render_cache_get($element)) {
73       return $cached_output;
74     }*/
75
76     $tree_options = [
77       'flat' => TRUE,
78       'restricted' => $options['show_restricted'],
79       'nested' => $options['show_nested'],
80       'depth' => $options['recursion_limit'],
81     ];
82
83     $token_tree = [];
84     foreach ($info['types'] as $type => $type_info) {
85       if (!in_array($type, $token_types)) {
86         continue;
87       }
88
89       $token_tree[$type] = $type_info;
90       $token_tree[$type]['tokens'] = $this->buildTree($type, $tree_options);
91     }
92
93     $element += [
94       '#type' => 'token_tree_table',
95       '#token_tree' => $token_tree,
96       '#show_restricted' => $options['show_restricted'],
97       '#show_nested' => $options['show_nested'],
98       '#click_insert' => $options['click_insert'],
99       '#columns' => ['name', 'token', 'description'],
100       '#empty' => t('No tokens available'),
101     ];
102
103     return $element;
104   }
105
106   /**
107    * {@inheritdoc}
108    */
109   public function buildAllRenderable(array $options = []) {
110     $info = $this->tokenService->getInfo();
111     $token_types = array_keys($info['types']);
112
113     // Disable merging in global types as we will be adding in all token types
114     // explicitly. There is no difference in leaving this set to TRUE except for
115     // an additional method call which is unnecessary.
116     $options['global_types'] = FALSE;
117     return $this->buildRenderable($token_types, $options);
118   }
119
120   /**
121    * {@inheritdoc}
122    */
123   public function buildTree($token_type, array $options = []) {
124     $options += [
125       'restricted' => FALSE,
126       'depth' => 4,
127       'data' => [],
128       'values' => FALSE,
129       'flat' => FALSE,
130     ];
131
132     // Do not allow past the maximum token information depth.
133     $options['depth'] = min($options['depth'], static::MAX_DEPTH);
134
135     // If $token_type is an entity, make sure we are using the actual token type.
136     if ($entity_token_type = $this->entityMapper->getTokenTypeForEntityType($token_type)) {
137       $token_type = $entity_token_type;
138     }
139
140     $langcode = $this->languageManager->getCurrentLanguage()->getId();
141     $tree_cid = "token_tree:{$token_type}:{$langcode}:{$options['depth']}";
142
143     // If we do not have this base tree in the static cache, check the cache
144     // otherwise generate and store it in the cache.
145     if (!isset($this->builtTrees[$tree_cid])) {
146       if ($cache = $this->cacheBackend->get($tree_cid)) {
147         $this->builtTrees[$tree_cid] = $cache->data;
148       }
149       else {
150         $options['parents'] = [];
151         $this->builtTrees[$tree_cid] = $this->getTokenData($token_type, $options);
152         $this->cacheBackend->set($tree_cid, $this->builtTrees[$tree_cid], Cache::PERMANENT, [Token::TOKEN_INFO_CACHE_TAG]);
153       }
154     }
155
156     $tree = $this->builtTrees[$tree_cid];
157
158     // If the user has requested a flat tree, convert it.
159     if (!empty($options['flat'])) {
160       $tree = $this->flattenTree($tree);
161     }
162
163     // Fill in token values.
164     if (!empty($options['values'])) {
165       $token_values = [];
166       foreach ($tree as $token => $token_info) {
167         if (!empty($token_info['dynamic']) || !empty($token_info['restricted'])) {
168           continue;
169         }
170         elseif (!isset($token_info['value'])) {
171           $token_values[$token_info['token']] = $token;
172         }
173       }
174       if (!empty($token_values)) {
175         $token_values = $this->tokenService->generate($token_type, $token_values, $options['data'], [], new BubbleableMetadata());
176         foreach ($token_values as $token => $replacement) {
177           $tree[$token]['value'] = $replacement;
178         }
179       }
180     }
181
182     return $tree;
183   }
184
185   /**
186    * {@inheritdoc}
187    */
188   public function flattenTree(array $tree) {
189     $result = [];
190     foreach ($tree as $token => $token_info) {
191       $result[$token] = $token_info;
192       if (isset($token_info['children']) && is_array($token_info['children'])) {
193         $result += $this->flattenTree($token_info['children']);
194       }
195     }
196     return $result;
197   }
198
199   /**
200    * Generate a token tree.
201    *
202    * @param string $token_type
203    *   The token type.
204    * @param array $options
205    *   An associative array of additional options. See documentation for
206    *   TreeBuilderInterface::buildTree() for more information.
207    *
208    * @return array
209    *   The token data for the specified $token_type.
210    *
211    * @internal
212    */
213   protected function getTokenData($token_type, array $options) {
214     $options += [
215       'parents' => [],
216     ];
217
218     $info = $this->tokenService->getInfo();
219     if ($options['depth'] <= 0 || !isset($info['types'][$token_type]) || !isset($info['tokens'][$token_type])) {
220       return [];
221     }
222
223     $tree = [];
224     foreach ($info['tokens'][$token_type] as $token => $token_info) {
225       // Build the raw token string.
226       $token_parents = $options['parents'];
227       if (empty($token_parents)) {
228         // If the parents array is currently empty, assume the token type is its
229         // parent.
230         $token_parents[] = $token_type;
231       }
232       elseif (in_array($token, array_slice($token_parents, 1), TRUE)) {
233         // Prevent duplicate recursive tokens. For example, this will prevent
234         // the tree from generating the following tokens or deeper:
235         // [comment:parent:parent]
236         // [comment:parent:root:parent]
237         continue;
238       }
239
240       $token_parents[] = $token;
241       if (!empty($token_info['dynamic'])) {
242         $token_parents[] = '?';
243       }
244       $raw_token = '[' . implode(':', $token_parents) . ']';
245       $tree[$raw_token] = $token_info;
246       $tree[$raw_token]['raw token'] = $raw_token;
247
248       // Add the token's real name (leave out the base token type).
249       $tree[$raw_token]['token'] = implode(':', array_slice($token_parents, 1));
250
251       // Add the token's parent as its raw token value.
252       if (!empty($options['parents'])) {
253         $tree[$raw_token]['parent'] = '[' . implode(':', $options['parents']) . ']';
254       }
255
256       // Fetch the child tokens.
257       if (!empty($token_info['type'])) {
258         $child_options = $options;
259         $child_options['depth']--;
260         $child_options['parents'] = $token_parents;
261         $tree[$raw_token]['children'] = $this->getTokenData($token_info['type'], $child_options);
262       }
263     }
264
265     return $tree;
266   }
267 }