source: sipes/modules_contrib/token/token.module @ 6e81fb4

stableversion-3.0
Last change on this file since 6e81fb4 was d5990f8, checked in by José Gregorio Puentes <jpuentes@…>, 8 años ago

se actualizo el modulo

  • Propiedad mode establecida a 100755
File size: 32.3 KB
Línea 
1<?php
2
3/**
4 * @file
5 * The Token API module.
6 *
7 * The Token module provides an API for providing tokens to other modules.
8 * Tokens are small bits of text that can be placed into larger documents
9 * via simple placeholders, like %site-name or [user].
10 *
11 * @ingroup token
12 */
13
14/**
15 * The default token prefix string.
16 */
17define('TOKEN_PREFIX', '[');
18
19/**
20 * The default token suffix string.
21 */
22define('TOKEN_SUFFIX', ']');
23
24/**
25 * Implements hook_help().
26 */
27function token_help($path, $arg) {
28  if ($path == 'admin/help#token') {
29    $output = '<dl>';
30    $output .= '<dt>' . t('List of the currently available tokens on this site') . '</dt>';
31    $output .= '<dd>' . theme('token_tree', 'all', TRUE, FALSE) . '</dd>';
32    $output .= '</dl>';
33    return $output;
34  }
35}
36
37/**
38 * Return an array of the core modules supported by token.module.
39 */
40function _token_core_supported_modules() {
41  return array('node', 'user', 'taxonomy', 'comment', 'menu', 'book');
42}
43
44/**
45 * Implements hook_menu().
46 */
47function token_menu() {
48  $items = array();
49
50  // Devel token pages.
51  if (module_exists('devel')) {
52    $items['node/%node/devel/token'] = array(
53      'title' => 'Tokens',
54      'page callback' => 'token_devel_token_object',
55      'page arguments' => array('node', 1),
56      'access arguments' => array('access devel information'),
57      'type' => MENU_LOCAL_TASK,
58      'file' => 'token.pages.inc',
59      'weight' => 5,
60    );
61    $items['user/%user/devel/token'] = array(
62      'title' => 'Tokens',
63      'page callback' => 'token_devel_token_object',
64      'page arguments' => array('user', 1),
65      'access arguments' => array('access devel information'),
66      'type' => MENU_LOCAL_TASK,
67      'file' => 'token.pages.inc',
68      'weight' => 5,
69    );
70  }
71
72  return $items;
73}
74
75/**
76 * Implements hook_theme().
77 */
78function token_theme() {
79  return array(
80    'token_help' => array(
81      'arguments' => array('type' => 'all', 'prefix' => TOKEN_PREFIX, 'suffix' => TOKEN_SUFFIX),
82      'file' => 'token.pages.inc',
83    ),
84    'token_tree' => array(
85      'arguments' => array('token_types' => array(), 'global_types' => TRUE , 'click_insert' => TRUE),
86      'file' => 'token.pages.inc',
87    ),
88  );
89}
90
91/**
92 * Implements hook_token_values().
93 */
94function token_token_values($type, $object = NULL) {
95  global $user;
96  $values = array();
97
98  switch ($type) {
99    case 'global':
100      // Current user tokens.
101      $values['user-name']    = $user->uid ? $user->name : variable_get('anonymous', t('Anonymous'));
102      $values['user-id']      = $user->uid ? $user->uid : 0;
103      $values['user-mail']    = $user->uid ? $user->mail : '';
104
105      // Site information tokens.
106      $values['site-url']     = url('<front>', array('absolute' => TRUE));
107      $values['site-name']    = check_plain(variable_get('site_name', t('Drupal')));
108      $values['site-slogan']  = check_plain(variable_get('site_slogan', ''));
109      $values['site-mission'] = filter_xss_admin(variable_get('site_mission', ''));
110      $values['site-mail']    = variable_get('site_mail', '');
111      $values += token_get_date_token_values(NULL, 'site-date-');
112
113      // Current page tokens.
114      $values['current-page-title'] = drupal_get_title();
115      $alias = drupal_get_path_alias($_GET['q']);
116      $values['current-page-path-raw'] = $alias;
117      $values['current-page-path'] = check_plain($alias);
118      $values['current-page-url'] = url($_GET['q'], array('absolute' => TRUE));
119
120      $page = isset($_GET['page']) ? $_GET['page'] : '';
121      $pager_page_array = explode(',', $page);
122      $page = $pager_page_array[0];
123      $values['current-page-number'] = (int) $page + 1;
124
125      // Backwards compatability for renamed tokens.
126      $values['site-date'] = $values['site-date-small'];
127      $values['page-number'] = $values['current-page-number'];
128
129      break;
130  }
131  return $values;
132}
133
134/**
135 * Implements hook_token_list().
136 */
137function token_token_list($type = 'all') {
138  $tokens = array();
139
140  if ($type == 'global' || $type == 'all') {
141    // Current user tokens.
142    $tokens['global']['user-name']    = t('The name of the currently logged in user.');
143    $tokens['global']['user-id']      = t('The user ID of the currently logged in user.');
144    $tokens['global']['user-mail']    = t('The email address of the currently logged in user.');
145
146    // Site information tokens.
147    $tokens['global']['site-url']     = t("The URL of the site's front page.");
148    $tokens['global']['site-name']    = t('The name of the site.');
149    $tokens['global']['site-slogan']  = t('The slogan of the site.');
150    $tokens['global']['site-mission'] = t("The optional 'mission' of the site.");
151    $tokens['global']['site-mail']    = t('The administrative email address for the site.');
152    $tokens['global'] += token_get_date_token_info(t('The current'), 'site-date-');
153
154    // Current page tokens.
155    $tokens['global']['current-page-title']    = t('The title of the current page.');
156    $tokens['global']['current-page-path']     = t('The URL alias of the current page.');
157    $tokens['global']['current-page-path-raw'] = t('The URL alias of the current page.');
158    $tokens['global']['current-page-url']      = t('The URL of the current page.');
159    $tokens['global']['current-page-number']   = t('The page number of the current page when viewing paged lists.');
160  }
161
162  return $tokens;
163}
164
165/**
166 * General function to include the files that token relies on for the real work.
167 */
168function token_include() {
169  static $run = FALSE;
170
171  if (!$run) {
172    $run = TRUE;
173    $modules_enabled = array_keys(module_list());
174    $modules = array_intersect(_token_core_supported_modules(), $modules_enabled);
175    foreach ($modules as $module) {
176      module_load_include('inc', 'token', "token_$module");
177    }
178  }
179}
180
181/**
182 * Replace all tokens in a given string with appropriate values.
183 *
184 * @param $text
185 *   A string potentially containing replaceable tokens.
186 * @param $type
187 *   (optional) A flag indicating the class of substitution tokens to use. If
188 *   an object is passed in the second param, 'type' should contain the
189 *   object's type. For example, 'node', 'comment', or 'user'. If no type is
190 *   specified, only 'global' site-wide substitution tokens are built.
191 * @param $object
192 *   (optional) An object to use for building substitution values (e.g. a node
193 *   comment, or user object).
194 * @param $leading
195 *   (optional) Character(s) to prepend to the token key before searching for
196 *   matches. Defaults to TOKEN_PREFIX.
197 * @param $trailing
198 *   (optional) Character(s) to append to the token key before searching for
199 *   matches. Defaults to TOKEN_SUFFIX.
200 * @param $options
201 *   (optional) A keyed array of settings and flags to control the token
202 *   generation and replacement process. Supported options are:
203 *   - clear: A boolean flag indicating that tokens should be removed from the
204 *     final text if no replacement value can be generated.
205 * @param $flush
206 *   (optional) A flag indicating whether or not to flush the token cache.
207 *   Useful for processes that need to slog through huge numbers of tokens
208 *   in a single execution cycle. Flushing it will keep them from burning
209 *   through memory. Defaults to FALSE.
210 *
211 * @return
212 *   Text with tokens replaced.
213 */
214function token_replace($text, $type = 'global', $object = NULL, $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX, $options = array(), $flush = FALSE) {
215  return token_replace_multiple($text, array($type => $object), $leading, $trailing, $options, $flush);
216}
217
218/**
219 * Replace all tokens in a given string with appropriate values.
220 *
221 * Contrary to token_replace() this function supports replacing multiple types.
222 *
223 * @param $text
224 *   A string potentially containing replaceable tokens.
225 * @param $types
226 *   (optional) An array of substitution classes and optional objects. The key
227 *   is a flag indicating the class of substitution tokens to use. If an object
228 *   is passed as value, the key should contain the object's type. For example,
229 *   'node', 'comment', or 'user'. The object will be used for building
230 *   substitution values. If no type is specified, only 'global' site-wide
231 *   substitution tokens are built.
232 * @param $leading
233 *   (optional) Character(s) to prepend to the token key before searching for
234 *   matches. Defaults to TOKEN_PREFIX.
235 * @param $trailing
236 *   (optional) Character(s) to append to the token key before searching for
237 *   matches. Defaults to TOKEN_SUFFIX.
238 * @param $options
239 *   (optional) A keyed array of settings and flags to control the token
240 *   generation and replacement process. Supported options are:
241 *   - clear: A boolean flag indicating that tokens should be removed from the
242 *     final text if no replacement value can be generated.
243 * @param $flush
244 *   (optional) A flag indicating whether or not to flush the token cache.
245 *   Useful for processes that need to slog through huge numbers of tokens
246 *   in a single execution cycle. Flushing it will keep them from burning
247 *   through memory. Defaults to FALSE.
248 *
249 * @return
250 *   Text with tokens replaced.
251 */
252function token_replace_multiple($text, $types = array('global' => NULL), $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX, $options = array(), $flush = FALSE) {
253  // Ensure that the $text parameter is a string and not an array which is an
254  // invalid input.
255  if (is_array($text)) {
256    foreach ($text as $key => $value) {
257      $text[$key] = token_replace_multiple($value, $types, $leading, $trailing, $options, $flush);
258    }
259    return $text;
260  }
261
262  // If there are no tokens to replace, just return the text.
263  $text_tokens = token_scan($text, $leading, $trailing);
264  if (empty($text_tokens)) {
265    return $text;
266  }
267
268  $full = new stdClass();
269  $full->tokens = $full->values = array();
270
271  // Allow global token replacement by default.
272  if (empty($types) || !is_array($types)) {
273    $types = array('global' => NULL);
274  }
275
276  foreach ($types as $type => $object) {
277    $temp = token_get_values($type, $object, $flush, $options);
278    $full->tokens = array_merge($full->tokens, $temp->tokens);
279    $full->values = array_merge($full->values, $temp->values);
280  }
281
282  // Support clearing out tokens that would not be replaced.
283  if (!empty($options['clear'])) {
284    foreach ($text_tokens as $token) {
285      if (!in_array($token, $full->tokens)) {
286        $full->tokens[] = $token;
287        $full->values[] = '';
288      }
289    }
290  }
291
292  $tokens = token_prepare_tokens($full->tokens, $leading, $trailing);
293  return str_replace($tokens, $full->values, $text);
294}
295
296/**
297 * Return a list of valid substitution tokens and their values for
298 * the specified type.
299 *
300 * @param $type
301 *   (optional) A flag indicating the class of substitution tokens to use. If an
302 *   object is passed in the second param, 'type' should contain the
303 *   object's type. For example, 'node', 'comment', or 'user'. If no
304 *   type is specified, only 'global' site-wide substitution tokens are
305 *   built.
306 * @param $object
307 *   (optional) An object to use for building substitution values (e.g. a node
308 *   comment, or user object).
309 * @param $flush
310 *   (optional) A flag indicating whether or not to flush the token cache.
311 *   Useful for processes that need to slog through huge numbers of tokens
312 *   in a single execution cycle. Flushing it will keep them from burning
313 *   through memory. Defaults to FALSE.
314 * @param $options
315 *   (optional) A keyed array of settings and flags to control the token
316 *   generation process.
317 *
318 * @return
319 *   An object with two properties:
320 *   - tokens: All the possible tokens names generated.
321 *   - values: The corresponding values for the tokens.
322 *
323 * Note that before performing actual token replacement that the token names
324 * should be run through token_prepare_tokens().
325 */
326function token_get_values($type = 'global', $object = NULL, $flush = FALSE, $options = array()) {
327  static $tokens = array();
328  static $running = FALSE;
329
330  // Simple recursion check. This is to avoid content_view()'s potential
331  // for endless looping when a filter uses tokens, which load the content
332  // view, which calls the filter, which uses tokens, which...
333  if ($running) {
334    // We'll allow things to get two levels deep, but bail out after that
335    // without performing any substitutions.
336    $result = new stdClass();
337    $result->tokens = array();
338    $result->values = array();
339    return $result;
340  }
341  else {
342    $running = TRUE;
343  }
344
345  // Flush the static token cache. Useful for processes that need to slog
346  // through huge numbers of tokens in a single execution cycle. Flushing it
347  // will keep them from burning through memory.
348  if ($flush || !empty($options['reset'])) {
349    $tokens = array();
350  }
351
352  // Allow simple resets of the static values.
353  if ($type === 'reset') {
354    $tokens = array();
355    $running = FALSE;
356    return;
357  }
358
359  // Neutralize options that do not affect token replacement.
360  $serialized_options = $options;
361  unset($serialized_options['clear']);
362
363  // Store the token cache by object ID and serialized options.
364  $cid = _token_get_id($type, $object) . ':' . md5(serialize($serialized_options));
365  if ($type != 'global' && !isset($tokens[$type][$cid])) {
366    token_include();
367    $tokens[$type][$cid] = module_invoke_all('token_values', $type, $object, $options);
368  }
369
370  // Special-case global tokens, as we always want to be able to process
371  // those substitutions.
372  if (!isset($tokens['global'][$cid])) {
373    token_include();
374    $tokens['global'][$cid] = module_invoke_all('token_values', 'global', NULL, $options);
375  }
376
377  $all = $tokens['global'][$cid];
378  if ($type != 'global') {
379    // Avoid using array_merge() if only global tokens were requested.
380    $all = array_merge($all, $tokens[$type][$cid]);
381  }
382
383  // Allow other modules to alter the replacements.
384  $context = array(
385    'type' => $type,
386    'object' => $object,
387    'options' => $options,
388  );
389  drupal_alter('token_values', $all, $context);
390
391  $result = new stdClass();
392  $result->tokens = array_keys($all);
393  $result->values = array_values($all);
394
395  $running = FALSE;
396
397  return $result;
398}
399
400/**
401 * A helper function that retrieves all currently exposed tokens,
402 * and merges them recursively. This is only necessary when building
403 * the token listing -- during actual value replacement, only tokens
404 * in a particular domain are requested and a normal array_marge() is
405 * sufficient.
406 *
407 * @param $types
408 *   A flag indicating the class of substitution tokens to use. If an
409 *   object is passed in the second param, 'types' should contain the
410 *   object's type. For example, 'node', 'comment', or 'user'. 'types'
411 *   may also be an array of types of the form array('node','user'). If no
412 *   type is specified, only 'global' site-wide substitution tokens are
413 *   built.
414 *
415 * @return
416 *   The array of usable tokens and their descriptions, organized by
417 *   token type.
418 */
419function token_get_list($types = 'all') {
420  token_include();
421  $return = array();
422  settype($types, 'array');
423  foreach (module_implements('token_list') as $module) {
424    foreach ($types as $type) {
425      $module_token_list = module_invoke($module, 'token_list', $type);
426      if (isset($module_token_list) && is_array($module_token_list)) {
427        foreach ($module_token_list as $category => $tokens) {
428          foreach ($tokens as $token => $title) {
429            // Automatically append a raw token warning.
430            if (substr($token, -4) === '-raw' && strpos($title, t('raw user input')) === FALSE && strpos($title, t('UNIX timestamp format')) === FALSE) {
431              $title .= ' <em>' . t('Warning: Token value contains raw user input.') . '</em>';
432            }
433            $return[$category][$token] = $title;
434          }
435        }
436      }
437    }
438  }
439  // Sort the tokens by name.
440  foreach (array_keys($return) as $category) {
441    ksort($return[$category]);
442  }
443  return $return;
444}
445
446/**
447 * A helper function to prepare raw tokens for replacement.
448 *
449 * @param $tokens
450 *   The array of tokens names with no delimiting characters.
451 * @param $leading
452 *   String to prepend to the token. Default is TOKEN_PREFIX.
453 * @param $trailing
454 *   String to append to the token. Default is TOKEN_SUFFIX.
455 *
456 * @return
457 *   An array of the formatted tokens.
458 */
459function token_prepare_tokens($tokens = array(), $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX) {
460  foreach ($tokens as $key => $value) {
461    $tokens[$key] = $leading . $value . $trailing;
462  }
463  return $tokens;
464}
465
466/**
467 * A helper function to return an object's ID for use in static caching.
468 */
469function _token_get_id($type = 'global', $object = NULL) {
470  if (!isset($object)) {
471    return "default";
472  }
473  switch ($type) {
474    case 'node':
475      return isset($object->vid) ? $object->vid : (isset($object->nid) ? $object->nid : 0);
476    case 'comment':
477      return isset($object->cid) ? $object->cid : 0;
478    case 'user':
479      return isset($object->uid) ? $object->uid : 0;
480    case 'taxonomy':
481      return isset($object->tid) ? $object->tid : 0;
482    default:
483      return crc32(serialize($object));
484  }
485}
486
487/**
488 * Build a list of common date tokens for use in hook_token_list().
489 *
490 * @param $description
491 */
492function token_get_date_token_info($description, $token_prefix = '') {
493  $time = time();
494  $tokens[$token_prefix . 'small']  = t("!description date in 'small' format. (%date)", array('!description' => $description, '%date' => format_date($time, 'small')));
495  $tokens[$token_prefix . 'yyyy']   = t("!description year (four digit)", array('!description' => $description));
496  $tokens[$token_prefix . 'yy']     = t("!description year (two digit)", array('!description' => $description));
497  $tokens[$token_prefix . 'month']  = t("!description month (full word)", array('!description' => $description));
498  $tokens[$token_prefix . 'mon']    = t("!description month (abbreviated)", array('!description' => $description));
499  $tokens[$token_prefix . 'mm']     = t("!description month (two digits with leading zeros)", array('!description' => $description));
500  $tokens[$token_prefix . 'm']      = t("!description month (one or two digits without leading zeros)", array('!description' => $description));
501  $tokens[$token_prefix . 'ww']     = t("!description week (two digits with leading zeros)", array('!description' => $description));
502  if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
503    $tokens[$token_prefix . 'date'] = t("!description date (numeric representation of the day of the week)", array('!description' => $description));
504  }
505  $tokens[$token_prefix . 'day']    = t("!description day (full word)", array('!description' => $description));
506  $tokens[$token_prefix . 'ddd']    = t("!description day (abbreviation)", array('!description' => $description));
507  $tokens[$token_prefix . 'dd']     = t("!description day (two digits with leading zeros)", array('!description' => $description));
508  $tokens[$token_prefix . 'd']      = t("!description day (one or two digits without leading zeros)", array('!description' => $description));
509  $tokens[$token_prefix . 'raw']    = t("!description in UNIX timestamp format (%date)", array('!description' => $description, '%date' => $time));
510  $tokens[$token_prefix . 'since']  = t("!description in 'time-since' format. (%date)", array('!description' => $description, '%date' => format_interval($time - 360, 2)));
511  return $tokens;
512}
513
514/**
515 * Build a list of common date tokens for use in hook_token_values().
516 */
517function token_get_date_token_values($timestamp = NULL, $token_prefix = '', $langcode = NULL) {
518  static $formats;
519
520  if (!isset($formats)) {
521    $formats = array();
522    $formats['small'] = variable_get('date_format_short', 'm/d/Y - H:i');
523    $formats['yyyy']  = 'Y';
524    $formats['yy']    = 'y';
525    $formats['month'] = 'F';
526    $formats['mon']   = 'M';
527    $formats['mm']    = 'm';
528    $formats['m']     = 'n';
529    $formats['ww']    = 'W';
530    if (version_compare(PHP_VERSION, '5.1.0', '>=')) {
531      $formats['date'] = 'N';
532    }
533    $formats['day']   = 'l';
534    $formats['ddd']   = 'D';
535    $formats['dd']    = 'd';
536    $formats['d']     = 'j';
537  }
538
539  $time = time();
540  if (!isset($timestamp)) {
541    $timestamp = $time;
542  }
543
544  $tokens = array();
545  foreach ($formats as $token => $format) {
546    $tokens[$token_prefix . $token] = token_format_date($timestamp, 'custom', $format, NULL, $langcode);
547  }
548  $tokens[$token_prefix . 'raw'] = $timestamp;
549  $tokens[$token_prefix . 'since'] = format_interval($time - $timestamp, 2, $langcode);
550
551  return $tokens;
552}
553
554/**
555 * A copy of format_date() that supports the 'N' date format character.
556 *
557 * @see format_date()
558 */
559function token_format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) {
560  global $user;
561  static $timezones = array();
562
563  // Statically cache each user's timezone so it doesn't need to be re-fetched
564  // ever call.
565  if (!isset($timezones[$user->uid])) {
566    if (!empty($user->uid) && variable_get('configurable_timezones', 1) && strlen($user->timezone)) {
567      $timezones[$user->uid] = $user->timezone;
568    }
569    else {
570      $timezones[$user->uid] = variable_get('date_default_timezone', 0);
571    }
572  }
573
574  $timestamp += $timezones[$user->uid];
575
576  switch ($type) {
577    case 'custom':
578      // No change to format.
579      break;
580    case 'small':
581      $format = variable_get('date_format_short', 'm/d/Y - H:i');
582      break;
583    case 'large':
584      $format = variable_get('date_format_long', 'l, F j, Y - H:i');
585      break;
586    case 'medium':
587    default:
588      $format = variable_get('date_format_medium', 'D, m/d/Y - H:i');
589  }
590
591  $max = strlen($format);
592  $date = '';
593  for ($i = 0; $i < $max; $i++) {
594    $c = $format[$i];
595    if (strpos('AaDlM', $c) !== FALSE) {
596      $date .= t(gmdate($c, $timestamp), array(), $langcode);
597    }
598    elseif ($c == 'F') {
599      // Special treatment for long month names: May is both an abbreviation
600      // and a full month name in English, but other languages have
601      // different abbreviations.
602      $date .= trim(t('!long-month-name ' . gmdate($c, $timestamp), array('!long-month-name' => ''), $langcode));
603    }
604    elseif (strpos('BdgGhHiIjLmnNsStTUwWYyz', $c) !== FALSE) {
605      // This condition was modified to allow the 'N' date format character.
606      $date .= gmdate($c, $timestamp);
607    }
608    elseif ($c == 'r') {
609      $date .= token_format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone, $langcode);
610    }
611    elseif ($c == 'O') {
612      $date .= sprintf('%s%02d%02d', ($timezone < 0 ? '-' : '+'), abs($timezone / 3600), abs($timezone % 3600) / 60);
613    }
614    elseif ($c == 'Z') {
615      $date .= $timezone;
616    }
617    elseif ($c == '\\') {
618      $date .= $format[++$i];
619    }
620    else {
621      $date .= $c;
622    }
623  }
624
625  return $date;
626}
627
628/**
629 * Validate an tokens in raw text based on possible contexts.
630 *
631 * @param $value
632 *   A string with the raw text containing the raw tokens, or an array of
633 *   tokens from token_scan().
634 * @param $valid_types
635 *   An array of token types to validage against.
636 * @param $leading
637 *   Character(s) to prepend to the token key before searching for
638 *   matches. Defaults to TOKEN_PREFIX.
639 * @param $trailing
640 *   Character(s) to append to the token key before searching for
641 *   matches. Defaults to TOKEN_SUFFIX.
642 *
643 * @return
644 *   An array with the invalid tokens in their original raw forms.
645 */
646function token_get_invalid_tokens_by_context($value, $valid_types = array(), $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX) {
647  if (in_array('all', $valid_types)) {
648    $valid_types = array('all');
649  }
650  else {
651    // Add the token types that are always valid in global context.
652    $valid_types[] = 'global';
653  }
654
655  $invalid_tokens = array();
656  $valid_tokens = array();
657  $value_tokens = is_string($value) ? token_scan($value, $leading, $trailing) : $value;
658
659  foreach (token_get_list($valid_types) as $category => $tokens) {
660    $valid_tokens += $tokens;
661  }
662
663  foreach ($value_tokens as $token) {
664    if (isset($valid_tokens[$token])) {
665      continue;
666    }
667    elseif (preg_match('/^(.*[_-])([^-_])+$/', $token, $matches)) {
668      // Allow tokens that do not have a direct match to tokens listed in
669      // hook_token_info() to be matched against a 'wildcard' token name.
670      if (isset($valid_tokens[$matches[1] . '?'])) {
671        // [token-name-?] wildcards.
672        continue;
673      }
674      elseif (isset($valid_tokens[$matches[1] . '????'])) {
675        // [token-name-????] wildcards.
676        continue;
677      }
678      elseif (is_numeric($matches[2]) && isset($valid_tokens[$matches[1] . 'N'])) {
679        // [token-name-N] wildcards if N is a numeric value.
680        continue;
681      }
682    }
683    $invalid_tokens[] = $token;
684  }
685
686  array_unique($invalid_tokens);
687  $invalid_tokens = token_prepare_tokens($invalid_tokens, $leading, $trailing);
688  return $invalid_tokens;
689}
690
691/**
692 * Build a list of all token-like patterns that appear in the text.
693 *
694 * @param $text
695 *   The text to be scanned for possible tokens.
696 * @param $leading
697 *   Character(s) to prepend to the token key before searching for
698 *   matches. Defaults to TOKEN_PREFIX.
699 * @param $trailing
700 *   Character(s) to append to the token key before searching for
701 *   matches. Defaults to TOKEN_SUFFIX.
702 *
703 * @return
704 *   An array of discovered tokens.
705 */
706function token_scan($text, $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX) {
707  $leadingregex = preg_quote($leading, '/');
708  $trailingregex = preg_quote($trailing, '/');
709
710  $regex = '/' . $leadingregex;
711  $regex .= '([^\s';
712  if (drupal_strlen($leading) == 1) {
713    // Only add the leading string as a non-match if it is a single character.
714    $regex .= $leadingregex;
715  }
716  if (drupal_strlen($trailing) == 1) {
717    // Only add the trailing string as a non-match if it is a single character.
718    $regex .= $trailingregex;
719  }
720  $regex .= ']+)' . $trailingregex . '/x';
721
722  preg_match_all($regex, $text, $matches);
723  return $matches[1];
724}
725
726/**
727 * Validate a form element that should have tokens in it.
728 *
729 * Form elements that want to add this validation should have the #token_types
730 * parameter defined.
731 *
732 * For example:
733 * @code
734 * $form['my_node_text_element'] = array(
735 *   '#type' => 'textfield',
736 *   '#title' => t('Some text to token-ize that has a node context.'),
737 *   '#default_value' => 'The title of this node is [title].',
738 *   '#element_validate' => array('token_element_validate'),
739 *   '#token_types' => array('node'),
740 *   '#min_tokens' => 1,
741 *   '#max_tokens' => 10,
742 * );
743 * @endcode
744 */
745function token_element_validate(&$element, &$form_state) {
746  $value = isset($element['#value']) ? $element['#value'] : $element['#default_value'];
747
748  if (!drupal_strlen($value)) {
749    // Empty value needs no further validation since the element should depend
750    // on using the '#required' FAPI property.
751    return $element;
752  }
753
754  $tokens = token_scan($value);
755  $title = empty($element['#title']) ? $element['#parents'][0] : $element['#title'];
756
757  // Validate if an element must have a minimum number of tokens.
758  if (isset($element['#min_tokens']) && count($tokens) < $element['#min_tokens']) {
759    // @todo Change this error message to include the minimum number.
760    $error = format_plural($element['#min_tokens'], 'The %element-title cannot contain fewer than one token.', 'The %element-title must contain at least @count tokens.', array('%element-title' => $title));
761    form_error($element, $error);
762  }
763
764  // Validate if an element must have a maximum number of tokens.
765  if (isset($element['#max_tokens']) && count($tokens) > $element['#max_tokens']) {
766    // @todo Change this error message to include the maximum number.
767    $error = format_plural($element['#max_tokens'], 'The %element-title must contain as most one token.', 'The %element-title must contain at most @count tokens.', array('%element-title' => $title));
768    form_error($element, $error);
769  }
770
771  // Check if the field defines specific token types.
772  if (!empty($element['#token_types'])) {
773    $invalid_tokens = token_get_invalid_tokens_by_context($tokens, $element['#token_types']);
774    if ($invalid_tokens) {
775      form_error($element, t('The %element-title is using the following invalid tokens: @invalid-tokens.', array('%element-title' => $title, '@invalid-tokens' => implode(', ', $invalid_tokens))));
776    }
777  }
778
779  return $element;
780}
781
782/**
783 * Deprecated. Use token_element_validate() instead.
784 */
785function token_element_validate_token_context(&$element, &$form_state) {
786  return token_element_validate($element, $form_state);
787}
788
789/**
790 * Find tokens that have been declared twice by different modules.
791 */
792function token_find_duplicate_tokens() {
793  token_include();
794  $all_tokens = array();
795
796  foreach (module_implements('token_list') as $module) {
797    $module_token_list = module_invoke($module, 'token_list', 'all');
798    if (!isset($module_token_list) || !is_array($module_token_list)) {
799      // Skip modules that do not return an array as that is a valid return
800      // value.
801      continue;
802    }
803    if (in_array($module, _token_core_supported_modules())) {
804      $module = 'token';
805    }
806    foreach ($module_token_list as $type => $tokens) {
807      foreach (array_keys($tokens) as $token) {
808        $all_tokens[$type . ':' . $token][] = $module;
809      }
810    }
811  }
812
813  foreach ($all_tokens as $token => $modules) {
814    if (count($modules) < 2) {
815      unset($all_tokens[$token]);
816    }
817  }
818
819  return $all_tokens;
820}
821
822/**
823 * Get a translated menu link by its mlid, without access checking.
824 *
825 * This function is a copy of menu_link_load() but with its own cache and a
826 * simpler query to load the link. This also skips normal menu link access
827 * checking by using _token_menu_link_translate().
828 *
829 * @param $mlid
830 *   The mlid of the menu item.
831 *
832 * @return
833 *   A menu link translated for rendering.
834 *
835 * @see menu_link_load()
836 * @see _token_menu_link_translate()
837 */
838function token_menu_link_load($mlid) {
839  static $cache = array();
840
841  if (!is_numeric($mlid)) {
842    return FALSE;
843  }
844
845  if (!isset($cache[$mlid])) {
846    $item = db_fetch_array(db_query("SELECT * FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $mlid));
847    if (!empty($item)) {
848      _token_menu_link_translate($item);
849    }
850    $cache[$mlid] = $item;
851  }
852
853  return $cache[$mlid];
854}
855
856/**
857 * Get a translated book menu link by its mlid, without access checking.
858 *
859 * This function is a copy of book_link_load() but with its own cache and a
860 * simpler query to load the link. This also skips normal menu link access
861 * checking by using _token_menu_link_translate().
862 *
863 * @param $mlid
864 *   The mlid of the book menu item.
865 *
866 * @return
867 *   A book menu link translated for rendering.
868 *
869 * @see book_link_load()
870 * @see _token_menu_link_translate()
871 */
872function token_book_link_load($mlid) {
873  static $cache = array();
874
875  if (!is_numeric($mlid)) {
876    return FALSE;
877  }
878
879  if (!isset($cache[$mlid])) {
880    $item = db_fetch_array(db_query("SELECT * FROM {menu_links} ml INNER JOIN {book} b ON b.mlid = ml.mlid LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = %d", $mlid));
881    if (!empty($item)) {
882      _token_menu_link_translate($item);
883    }
884    $cache[$mlid] = $item;
885  }
886
887  return $cache[$mlid];
888}
889
890function _token_menu_link_translate(&$item) {
891  $map = array();
892
893  if (!is_array($item['options'])) {
894    $item['options'] = unserialize($item['options']);
895  }
896
897  if ($item['external']) {
898    $item['access'] = 1;
899    $item['href'] = $item['link_path'];
900    $item['title'] = $item['link_title'];
901    $item['localized_options'] = $item['options'];
902  }
903  else {
904    $map = explode('/', $item['link_path']);
905    _menu_link_map_translate($map, $item['to_arg_functions']);
906    $item['href'] = implode('/', $map);
907
908    // Note - skip callbacks without real values for their arguments.
909    if (strpos($item['href'], '%') !== FALSE) {
910      $item['access'] = FALSE;
911      return FALSE;
912    }
913
914    $item['access'] = TRUE;
915    _menu_item_localize($item, $map, TRUE);
916  }
917
918  // Allow other customizations - e.g. adding a page-specific query string to the
919  // options array. For performance reasons we only invoke this hook if the link
920  // has the 'alter' flag set in the options array.
921  if (!empty($item['options']['alter'])) {
922    drupal_alter('translated_menu_link', $item, $map);
923  }
924
925  return $map;
926}
927
928/**
929 * Find all ancestors of a given menu link ID.
930 *
931 * @param $mlid
932 *   A menu link ID.
933 *
934 * @return
935 *   An array of menu links from token_menu_link_load() with the root link
936 *   first, and the menu link with ID $mlid last.
937 */
938function token_menu_link_get_parents_all($mlid) {
939  $parents = array();
940
941  while (!empty($mlid)) {
942    $link = token_menu_link_load($mlid);
943    array_unshift($parents, $link);
944    $mlid = $link['plid'];
945  }
946
947  return $parents;
948}
949
950/**
951 * Deprecated. Use the raw return value of token_menu_link_get_parents_all() instead.
952 */
953function _menu_titles($menu_link, $nid) {
954  $titles = array();
955  $parents = token_menu_link_get_parents_all($menu_link['mlid']);
956  foreach ($parents as $mlid => $parent) {
957    $titles[] = $parent['title'];
958  }
959  return $titles;
960}
Nota: Vea TracBrowser para ayuda de uso del navegador del repositorio.