|
1 <?php |
|
2 |
|
3 /** |
|
4 * @file |
|
5 * Drupal placeholder/token replacement system. |
|
6 * |
|
7 * API functions for replacing placeholders in text with meaningful values. |
|
8 * |
|
9 * For example: When configuring automated emails, an administrator enters |
|
10 * standard text for the email. Variables like the title of a node and the date |
|
11 * the email was sent can be entered as placeholders like [node:title] and |
|
12 * [date:short]. When a Drupal module prepares to send the email, it can call |
|
13 * the token_replace() function, passing in the text. The token system will |
|
14 * scan the text for placeholder tokens, give other modules an opportunity to |
|
15 * replace them with meaningful text, then return the final product to the |
|
16 * original module. |
|
17 * |
|
18 * Tokens follow the form: [$type:$name], where $type is a general class of |
|
19 * tokens like 'node', 'user', or 'comment' and $name is the name of a given |
|
20 * placeholder. For example, [node:title] or [node:created:since]. |
|
21 * |
|
22 * In addition to raw text containing placeholders, modules may pass in an array |
|
23 * of objects to be used when performing the replacement. The objects should be |
|
24 * keyed by the token type they correspond to. For example: |
|
25 * |
|
26 * @code |
|
27 * // Load a node and a user, then replace tokens in the text. |
|
28 * $text = 'On [date:short], [user:name] read [node:title].'; |
|
29 * $node = node_load(1); |
|
30 * $user = user_load(1); |
|
31 * |
|
32 * // [date:...] tokens use the current date automatically. |
|
33 * $data = array('node' => $node, 'user' => $user); |
|
34 * return token_replace($text, $data); |
|
35 * @endcode |
|
36 * |
|
37 * Some tokens may be chained in the form of [$type:$pointer:$name], where $type |
|
38 * is a normal token type, $pointer is a reference to another token type, and |
|
39 * $name is the name of a given placeholder. For example, [node:author:mail]. In |
|
40 * that example, 'author' is a pointer to the 'user' account that created the |
|
41 * node, and 'mail' is a placeholder available for any 'user'. |
|
42 * |
|
43 * @see token_replace() |
|
44 * @see hook_tokens() |
|
45 * @see hook_token_info() |
|
46 */ |
|
47 |
|
48 /** |
|
49 * Replaces all tokens in a given string with appropriate values. |
|
50 * |
|
51 * @param $text |
|
52 * A string potentially containing replaceable tokens. |
|
53 * @param $data |
|
54 * (optional) An array of keyed objects. For simple replacement scenarios |
|
55 * 'node', 'user', and others are common keys, with an accompanying node or |
|
56 * user object being the value. Some token types, like 'site', do not require |
|
57 * any explicit information from $data and can be replaced even if it is |
|
58 * empty. |
|
59 * @param $options |
|
60 * (optional) A keyed array of settings and flags to control the token |
|
61 * replacement process. Supported options are: |
|
62 * - language: A language object to be used when generating locale-sensitive |
|
63 * tokens. |
|
64 * - callback: A callback function that will be used to post-process the array |
|
65 * of token replacements after they are generated. For example, a module |
|
66 * using tokens in a text-only email might provide a callback to strip HTML |
|
67 * entities from token values before they are inserted into the final text. |
|
68 * - clear: A boolean flag indicating that tokens should be removed from the |
|
69 * final text if no replacement value can be generated. |
|
70 * - sanitize: A boolean flag indicating that tokens should be sanitized for |
|
71 * display to a web browser. Defaults to TRUE. Developers who set this |
|
72 * option to FALSE assume responsibility for running filter_xss(), |
|
73 * check_plain() or other appropriate scrubbing functions before displaying |
|
74 * data to users. |
|
75 * |
|
76 * @return |
|
77 * Text with tokens replaced. |
|
78 */ |
|
79 function token_replace($text, array $data = array(), array $options = array()) { |
|
80 $text_tokens = token_scan($text); |
|
81 if (empty($text_tokens)) { |
|
82 return $text; |
|
83 } |
|
84 |
|
85 $replacements = array(); |
|
86 foreach ($text_tokens as $type => $tokens) { |
|
87 $replacements += token_generate($type, $tokens, $data, $options); |
|
88 if (!empty($options['clear'])) { |
|
89 $replacements += array_fill_keys($tokens, ''); |
|
90 } |
|
91 } |
|
92 |
|
93 // Optionally alter the list of replacement values. |
|
94 if (!empty($options['callback']) && function_exists($options['callback'])) { |
|
95 $function = $options['callback']; |
|
96 $function($replacements, $data, $options); |
|
97 } |
|
98 |
|
99 $tokens = array_keys($replacements); |
|
100 $values = array_values($replacements); |
|
101 |
|
102 return str_replace($tokens, $values, $text); |
|
103 } |
|
104 |
|
105 /** |
|
106 * Builds a list of all token-like patterns that appear in the text. |
|
107 * |
|
108 * @param $text |
|
109 * The text to be scanned for possible tokens. |
|
110 * |
|
111 * @return |
|
112 * An associative array of discovered tokens, grouped by type. |
|
113 */ |
|
114 function token_scan($text) { |
|
115 // Matches tokens with the following pattern: [$type:$name] |
|
116 // $type and $name may not contain [ ] characters. |
|
117 // $type may not contain : or whitespace characters, but $name may. |
|
118 preg_match_all('/ |
|
119 \[ # [ - pattern start |
|
120 ([^\s\[\]:]*) # match $type not containing whitespace : [ or ] |
|
121 : # : - separator |
|
122 ([^\[\]]*) # match $name not containing [ or ] |
|
123 \] # ] - pattern end |
|
124 /x', $text, $matches); |
|
125 |
|
126 $types = $matches[1]; |
|
127 $tokens = $matches[2]; |
|
128 |
|
129 // Iterate through the matches, building an associative array containing |
|
130 // $tokens grouped by $types, pointing to the version of the token found in |
|
131 // the source text. For example, $results['node']['title'] = '[node:title]'; |
|
132 $results = array(); |
|
133 for ($i = 0; $i < count($tokens); $i++) { |
|
134 $results[$types[$i]][$tokens[$i]] = $matches[0][$i]; |
|
135 } |
|
136 |
|
137 return $results; |
|
138 } |
|
139 |
|
140 /** |
|
141 * Generates replacement values for a list of tokens. |
|
142 * |
|
143 * @param $type |
|
144 * The type of token being replaced. 'node', 'user', and 'date' are common. |
|
145 * @param $tokens |
|
146 * An array of tokens to be replaced, keyed by the literal text of the token |
|
147 * as it appeared in the source text. |
|
148 * @param $data |
|
149 * (optional) An array of keyed objects. For simple replacement scenarios |
|
150 * 'node', 'user', and others are common keys, with an accompanying node or |
|
151 * user object being the value. Some token types, like 'site', do not require |
|
152 * any explicit information from $data and can be replaced even if it is |
|
153 * empty. |
|
154 * @param $options |
|
155 * (optional) A keyed array of settings and flags to control the token |
|
156 * replacement process. Supported options are: |
|
157 * - language: A language object to be used when generating locale-sensitive |
|
158 * tokens. |
|
159 * - callback: A callback function that will be used to post-process the |
|
160 * array of token replacements after they are generated. Can be used when |
|
161 * modules require special formatting of token text, for example URL |
|
162 * encoding or truncation to a specific length. |
|
163 * - sanitize: A boolean flag indicating that tokens should be sanitized for |
|
164 * display to a web browser. Developers who set this option to FALSE assume |
|
165 * responsibility for running filter_xss(), check_plain() or other |
|
166 * appropriate scrubbing functions before displaying data to users. |
|
167 * |
|
168 * @return |
|
169 * An associative array of replacement values, keyed by the original 'raw' |
|
170 * tokens that were found in the source text. For example: |
|
171 * $results['[node:title]'] = 'My new node'; |
|
172 * |
|
173 * @see hook_tokens() |
|
174 * @see hook_tokens_alter() |
|
175 */ |
|
176 function token_generate($type, array $tokens, array $data = array(), array $options = array()) { |
|
177 $options += array('sanitize' => TRUE); |
|
178 $replacements = module_invoke_all('tokens', $type, $tokens, $data, $options); |
|
179 |
|
180 // Allow other modules to alter the replacements. |
|
181 $context = array( |
|
182 'type' => $type, |
|
183 'tokens' => $tokens, |
|
184 'data' => $data, |
|
185 'options' => $options, |
|
186 ); |
|
187 drupal_alter('tokens', $replacements, $context); |
|
188 |
|
189 return $replacements; |
|
190 } |
|
191 |
|
192 /** |
|
193 * Returns a list of tokens that begin with a specific prefix. |
|
194 * |
|
195 * Used to extract a group of 'chained' tokens (such as [node:author:name]) |
|
196 * from the full list of tokens found in text. For example: |
|
197 * @code |
|
198 * $data = array( |
|
199 * 'author:name' => '[node:author:name]', |
|
200 * 'title' => '[node:title]', |
|
201 * 'created' => '[node:created]', |
|
202 * ); |
|
203 * $results = token_find_with_prefix($data, 'author'); |
|
204 * $results == array('name' => '[node:author:name]'); |
|
205 * @endcode |
|
206 * |
|
207 * @param $tokens |
|
208 * A keyed array of tokens, and their original raw form in the source text. |
|
209 * @param $prefix |
|
210 * A textual string to be matched at the beginning of the token. |
|
211 * @param $delimiter |
|
212 * An optional string containing the character that separates the prefix from |
|
213 * the rest of the token. Defaults to ':'. |
|
214 * |
|
215 * @return |
|
216 * An associative array of discovered tokens, with the prefix and delimiter |
|
217 * stripped from the key. |
|
218 */ |
|
219 function token_find_with_prefix(array $tokens, $prefix, $delimiter = ':') { |
|
220 $results = array(); |
|
221 foreach ($tokens as $token => $raw) { |
|
222 $parts = explode($delimiter, $token, 2); |
|
223 if (count($parts) == 2 && $parts[0] == $prefix) { |
|
224 $results[$parts[1]] = $raw; |
|
225 } |
|
226 } |
|
227 return $results; |
|
228 } |
|
229 |
|
230 /** |
|
231 * Returns metadata describing supported tokens. |
|
232 * |
|
233 * The metadata array contains token type, name, and description data as well |
|
234 * as an optional pointer indicating that the token chains to another set of |
|
235 * tokens. |
|
236 * |
|
237 * For example: |
|
238 * @code |
|
239 * $data['types']['node'] = array( |
|
240 * 'name' => t('Nodes'), |
|
241 * 'description' => t('Tokens related to node objects.'), |
|
242 * ); |
|
243 * $data['tokens']['node']['title'] = array( |
|
244 * 'name' => t('Title'), |
|
245 * 'description' => t('The title of the current node.'), |
|
246 * ); |
|
247 * $data['tokens']['node']['author'] = array( |
|
248 * 'name' => t('Author'), |
|
249 * 'description' => t('The author of the current node.'), |
|
250 * 'type' => 'user', |
|
251 * ); |
|
252 * @endcode |
|
253 * |
|
254 * @return |
|
255 * An associative array of token information, grouped by token type. |
|
256 */ |
|
257 function token_info() { |
|
258 $data = &drupal_static(__FUNCTION__); |
|
259 if (!isset($data)) { |
|
260 $data = module_invoke_all('token_info'); |
|
261 drupal_alter('token_info', $data); |
|
262 } |
|
263 return $data; |
|
264 } |