| [ Index ] |
PHP Cross Reference of Drupal 6 (yi-drupal) |
[Summary view] [Print] [Text view]
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 */ 17 define('TOKEN_PREFIX', '['); 18 19 /** 20 * The default token suffix string. 21 */ 22 define('TOKEN_SUFFIX', ']'); 23 24 /** 25 * Implements hook_help(). 26 */ 27 function 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 */ 40 function _token_core_supported_modules() { 41 return array('node', 'user', 'taxonomy', 'comment', 'menu', 'book'); 42 } 43 44 /** 45 * Implements hook_menu(). 46 */ 47 function 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 */ 78 function 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 */ 94 function 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 */ 137 function 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 */ 168 function 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 */ 214 function 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 */ 252 function token_replace_multiple($text, $types = array('global' => NULL), $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX, $options = array(), $flush = FALSE) { 253 $full = new stdClass(); 254 $full->tokens = $full->values = array(); 255 256 // Allow global token replacement by default. 257 if (empty($types) || !is_array($types)) { 258 $types = array('global' => NULL); 259 } 260 261 foreach ($types as $type => $object) { 262 $temp = token_get_values($type, $object, $flush, $options); 263 $full->tokens = array_merge($full->tokens, $temp->tokens); 264 $full->values = array_merge($full->values, $temp->values); 265 } 266 267 // Support clearing out tokens that would not be replaced. 268 if (!empty($options['clear'])) { 269 foreach (token_scan($text) as $token) { 270 if (!in_array($token, $full->tokens)) { 271 $full->tokens[] = $token; 272 $full->values[] = ''; 273 } 274 } 275 } 276 277 $tokens = token_prepare_tokens($full->tokens, $leading, $trailing); 278 return str_replace($tokens, $full->values, $text); 279 } 280 281 /** 282 * Return a list of valid substitution tokens and their values for 283 * the specified type. 284 * 285 * @param $type 286 * (optional) A flag indicating the class of substitution tokens to use. If an 287 * object is passed in the second param, 'type' should contain the 288 * object's type. For example, 'node', 'comment', or 'user'. If no 289 * type is specified, only 'global' site-wide substitution tokens are 290 * built. 291 * @param $object 292 * (optional) An object to use for building substitution values (e.g. a node 293 * comment, or user object). 294 * @param $flush 295 * (optional) A flag indicating whether or not to flush the token cache. 296 * Useful for processes that need to slog through huge numbers of tokens 297 * in a single execution cycle. Flushing it will keep them from burning 298 * through memory. Defaults to FALSE. 299 * @param $options 300 * (optional) A keyed array of settings and flags to control the token 301 * generation process. 302 * 303 * @return 304 * An object with two properties: 305 * - tokens: All the possible tokens names generated. 306 * - values: The corresponding values for the tokens. 307 * 308 * Note that before performing actual token replacement that the token names 309 * should be run through token_prepare_tokens(). 310 */ 311 function token_get_values($type = 'global', $object = NULL, $flush = FALSE, $options = array()) { 312 static $tokens; 313 static $running; 314 315 // Flush the static token cache. Useful for processes that need to slog through 316 // huge numbers of tokens in a single execution cycle. Flushing it will keep 317 // them from burning through memory. 318 if ($flush || !isset($tokens)) { 319 $tokens = array(); 320 } 321 322 // Since objects in PHP5 are always passed by reference, we ensure we're 323 // working on a copy of the object. 324 if (is_object($object)) { 325 $object = drupal_clone($object); 326 } 327 328 // Simple recursion check. This is to avoid content_view()'s potential 329 // for endless looping when a filter uses tokens, which load the content 330 // view, which calls the filter, which uses tokens, which... 331 if ($running) { 332 // We'll allow things to get two levels deep, but bail out after that 333 // without performing any substitutions. 334 $result = new stdClass(); 335 $result->tokens = array(); 336 $result->values = array(); 337 return $result; 338 } 339 340 $running = TRUE; 341 342 token_include(); 343 344 $id = _token_get_id($type, $object); 345 if ($id && isset($tokens[$type][$id])) { 346 $tmp_tokens = $tokens[$type][$id]; 347 } 348 else { 349 $tmp_tokens = module_invoke_all('token_values', $type, $object, $options); 350 $tokens[$type][$id] = $tmp_tokens; 351 } 352 353 // Special-case global tokens, as we always want to be able to process 354 // those substitutions. 355 if (!isset($tokens['global']['default'])) { 356 $tokens['global']['default'] = module_invoke_all('token_values', 'global'); 357 } 358 359 $all = array_merge($tokens['global']['default'], $tokens[$type][$id]); 360 361 $result = new stdClass(); 362 $result->tokens = array_keys($all); 363 $result->values = array_values($all); 364 365 $running = FALSE; 366 367 return $result; 368 } 369 370 /** 371 * A helper function that retrieves all currently exposed tokens, 372 * and merges them recursively. This is only necessary when building 373 * the token listing -- during actual value replacement, only tokens 374 * in a particular domain are requested and a normal array_marge() is 375 * sufficient. 376 * 377 * @param $types 378 * A flag indicating the class of substitution tokens to use. If an 379 * object is passed in the second param, 'types' should contain the 380 * object's type. For example, 'node', 'comment', or 'user'. 'types' 381 * may also be an array of types of the form array('node','user'). If no 382 * type is specified, only 'global' site-wide substitution tokens are 383 * built. 384 * 385 * @return 386 * The array of usable tokens and their descriptions, organized by 387 * token type. 388 */ 389 function token_get_list($types = 'all') { 390 token_include(); 391 $return = array(); 392 settype($types, 'array'); 393 foreach (module_implements('token_list') as $module) { 394 foreach ($types as $type) { 395 $module_token_list = module_invoke($module, 'token_list', $type); 396 if (isset($module_token_list) && is_array($module_token_list)) { 397 foreach ($module_token_list as $category => $tokens) { 398 foreach ($tokens as $token => $title) { 399 // Automatically append a raw token warning. 400 if (substr($token, -4) === '-raw' && strpos($title, t('raw user input')) === FALSE && strpos($title, t('UNIX timestamp format')) === FALSE) { 401 $title .= ' <em>' . t('Warning: Token value contains raw user input.') . '</em>'; 402 } 403 $return[$category][$token] = $title; 404 } 405 } 406 } 407 } 408 } 409 // Sort the tokens by name. 410 foreach (array_keys($return) as $category) { 411 ksort($return[$category]); 412 } 413 return $return; 414 } 415 416 /** 417 * A helper function to prepare raw tokens for replacement. 418 * 419 * @param $tokens 420 * The array of tokens names with no delimiting characters. 421 * @param $leading 422 * String to prepend to the token. Default is TOKEN_PREFIX. 423 * @param $trailing 424 * String to append to the token. Default is TOKEN_SUFFIX. 425 * 426 * @return 427 * An array of the formatted tokens. 428 */ 429 function token_prepare_tokens($tokens = array(), $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX) { 430 foreach ($tokens as $key => $value) { 431 $tokens[$key] = $leading . $value . $trailing; 432 } 433 return $tokens; 434 } 435 436 /** 437 * A helper function to return an object's ID for use in static caching. 438 */ 439 function _token_get_id($type = 'global', $object = NULL) { 440 if (!isset($object)) { 441 return "default"; 442 } 443 switch ($type) { 444 case 'node': 445 return isset($object->nid) ? $object->nid : 0; 446 case 'comment': 447 return isset($object->cid) ? $object->cid : 0; 448 case 'user': 449 return isset($object->uid) ? $object->uid : 0; 450 case 'taxonomy': 451 return isset($object->tid) ? $object->tid : 0; 452 default: 453 return crc32(serialize($object)); 454 } 455 } 456 457 /** 458 * Build a list of common date tokens for use in hook_token_list(). 459 * 460 * @param $description 461 */ 462 function token_get_date_token_info($description, $token_prefix = '') { 463 $time = time(); 464 $tokens[$token_prefix . 'small'] = t("!description date in 'small' format. (%date)", array('!description' => $description, '%date' => format_date($time, 'small'))); 465 $tokens[$token_prefix . 'yyyy'] = t("!description year (four digit)", array('!description' => $description)); 466 $tokens[$token_prefix . 'yy'] = t("!description year (two digit)", array('!description' => $description)); 467 $tokens[$token_prefix . 'month'] = t("!description month (full word)", array('!description' => $description)); 468 $tokens[$token_prefix . 'mon'] = t("!description month (abbreviated)", array('!description' => $description)); 469 $tokens[$token_prefix . 'mm'] = t("!description month (two digits with leading zeros)", array('!description' => $description)); 470 $tokens[$token_prefix . 'm'] = t("!description month (one or two digits without leading zeros)", array('!description' => $description)); 471 $tokens[$token_prefix . 'ww'] = t("!description week (two digits with leading zeros)", array('!description' => $description)); 472 if (version_compare(PHP_VERSION, '5.1.0', '>=')) { 473 $tokens[$token_prefix . 'date'] = t("!description date (numeric representation of the day of the week)", array('!description' => $description)); 474 } 475 $tokens[$token_prefix . 'day'] = t("!description day (full word)", array('!description' => $description)); 476 $tokens[$token_prefix . 'ddd'] = t("!description day (abbreviation)", array('!description' => $description)); 477 $tokens[$token_prefix . 'dd'] = t("!description day (two digits with leading zeros)", array('!description' => $description)); 478 $tokens[$token_prefix . 'd'] = t("!description day (one or two digits without leading zeros)", array('!description' => $description)); 479 $tokens[$token_prefix . 'raw'] = t("!description in UNIX timestamp format (%date)", array('!description' => $description, '%date' => $time)); 480 $tokens[$token_prefix . 'since'] = t("!description in 'time-since' format. (%date)", array('!description' => $description, '%date' => format_interval($time - 360, 2))); 481 return $tokens; 482 } 483 484 /** 485 * Build a list of common date tokens for use in hook_token_values(). 486 */ 487 function token_get_date_token_values($timestamp = NULL, $token_prefix = '', $langcode = NULL) { 488 static $formats; 489 490 if (!isset($formats)) { 491 $formats = array(); 492 $formats['small'] = variable_get('date_format_short', 'm/d/Y - H:i'); 493 $formats['yyyy'] = 'Y'; 494 $formats['yy'] = 'y'; 495 $formats['month'] = 'F'; 496 $formats['mon'] = 'M'; 497 $formats['mm'] = 'm'; 498 $formats['m'] = 'n'; 499 $formats['ww'] = 'W'; 500 if (version_compare(PHP_VERSION, '5.1.0', '>=')) { 501 $formats['date'] = 'N'; 502 } 503 $formats['day'] = 'l'; 504 $formats['ddd'] = 'D'; 505 $formats['dd'] = 'd'; 506 $formats['d'] = 'j'; 507 } 508 509 $time = time(); 510 if (!isset($timestamp)) { 511 $timestamp = $time; 512 } 513 514 $tokens = array(); 515 foreach ($formats as $token => $format) { 516 $tokens[$token_prefix . $token] = token_format_date($timestamp, 'custom', $format, NULL, $langcode); 517 } 518 $tokens[$token_prefix . 'raw'] = $timestamp; 519 $tokens[$token_prefix . 'since'] = format_interval($time - $timestamp, 2, $langcode); 520 521 return $tokens; 522 } 523 524 /** 525 * A copy of format_date() that supports the 'N' date format character. 526 * 527 * @see format_date() 528 */ 529 function token_format_date($timestamp, $type = 'medium', $format = '', $timezone = NULL, $langcode = NULL) { 530 global $user; 531 static $timezones = array(); 532 533 // Statically cache each user's timezone so it doesn't need to be re-fetched 534 // ever call. 535 if (!isset($timezones[$user->uid])) { 536 if (!empty($user->uid) && variable_get('configurable_timezones', 1) && strlen($user->timezone)) { 537 $timezones[$user->uid] = $user->timezone; 538 } 539 else { 540 $timezones[$user->uid] = variable_get('date_default_timezone', 0); 541 } 542 } 543 544 $timestamp += $timezones[$user->uid]; 545 546 switch ($type) { 547 case 'custom': 548 // No change to format. 549 break; 550 case 'small': 551 $format = variable_get('date_format_short', 'm/d/Y - H:i'); 552 break; 553 case 'large': 554 $format = variable_get('date_format_long', 'l, F j, Y - H:i'); 555 break; 556 case 'medium': 557 default: 558 $format = variable_get('date_format_medium', 'D, m/d/Y - H:i'); 559 } 560 561 $max = strlen($format); 562 $date = ''; 563 for ($i = 0; $i < $max; $i++) { 564 $c = $format[$i]; 565 if (strpos('AaDlM', $c) !== FALSE) { 566 $date .= t(gmdate($c, $timestamp), array(), $langcode); 567 } 568 elseif ($c == 'F') { 569 // Special treatment for long month names: May is both an abbreviation 570 // and a full month name in English, but other languages have 571 // different abbreviations. 572 $date .= trim(t('!long-month-name ' . gmdate($c, $timestamp), array('!long-month-name' => ''), $langcode)); 573 } 574 elseif (strpos('BdgGhHiIjLmnNsStTUwWYyz', $c) !== FALSE) { 575 // This condition was modified to allow the 'N' date format character. 576 $date .= gmdate($c, $timestamp); 577 } 578 elseif ($c == 'r') { 579 $date .= token_format_date($timestamp - $timezone, 'custom', 'D, d M Y H:i:s O', $timezone, $langcode); 580 } 581 elseif ($c == 'O') { 582 $date .= sprintf('%s%02d%02d', ($timezone < 0 ? '-' : '+'), abs($timezone / 3600), abs($timezone % 3600) / 60); 583 } 584 elseif ($c == 'Z') { 585 $date .= $timezone; 586 } 587 elseif ($c == '\\') { 588 $date .= $format[++$i]; 589 } 590 else { 591 $date .= $c; 592 } 593 } 594 595 return $date; 596 } 597 598 /** 599 * Validate an tokens in raw text based on possible contexts. 600 * 601 * @param $value 602 * A string with the raw text containing the raw tokens, or an array of 603 * tokens from token_scan(). 604 * @param $valid_types 605 * An array of token types to validage against. 606 * @param $leading 607 * Character(s) to prepend to the token key before searching for 608 * matches. Defaults to TOKEN_PREFIX. 609 * @param $trailing 610 * Character(s) to append to the token key before searching for 611 * matches. Defaults to TOKEN_SUFFIX. 612 * 613 * @return 614 * An array with the invalid tokens in their original raw forms. 615 */ 616 function token_get_invalid_tokens_by_context($value, $valid_types = array(), $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX) { 617 if (in_array('all', $valid_types)) { 618 $valid_types = array('all'); 619 } 620 else { 621 // Add the token types that are always valid in global context. 622 $valid_types[] = 'global'; 623 } 624 625 $invalid_tokens = array(); 626 $valid_tokens = array(); 627 $value_tokens = is_string($value) ? token_scan($value, $leading, $trailing) : $value; 628 629 foreach (token_get_list($valid_types) as $category => $tokens) { 630 $valid_tokens += $tokens; 631 } 632 633 foreach ($value_tokens as $token) { 634 if (isset($valid_tokens[$token])) { 635 continue; 636 } 637 elseif (preg_match('/^(.*[_-])([^-_])+$/', $token, $matches)) { 638 // Allow tokens that do not have a direct match to tokens listed in 639 // hook_token_info() to be matched against a 'wildcard' token name. 640 if (isset($valid_tokens[$matches[1] . '?'])) { 641 // [token-name-?] wildcards. 642 continue; 643 } 644 elseif (isset($valid_tokens[$matches[1] . '????'])) { 645 // [token-name-????] wildcards. 646 continue; 647 } 648 elseif (is_numeric($matches[2]) && isset($valid_tokens[$matches[1] . 'N'])) { 649 // [token-name-N] wildcards if N is a numeric value. 650 continue; 651 } 652 } 653 $invalid_tokens[] = $token; 654 } 655 656 array_unique($invalid_tokens); 657 $invalid_tokens = token_prepare_tokens($invalid_tokens, $leading, $trailing); 658 return $invalid_tokens; 659 } 660 661 /** 662 * Build a list of all token-like patterns that appear in the text. 663 * 664 * @param $text 665 * The text to be scanned for possible tokens. 666 * @param $leading 667 * Character(s) to prepend to the token key before searching for 668 * matches. Defaults to TOKEN_PREFIX. 669 * @param $trailing 670 * Character(s) to append to the token key before searching for 671 * matches. Defaults to TOKEN_SUFFIX. 672 * 673 * @return 674 * An array of discovered tokens. 675 */ 676 function token_scan($text, $leading = TOKEN_PREFIX, $trailing = TOKEN_SUFFIX) { 677 // Matches tokens with the following pattern: [$token] 678 $leading = preg_quote($leading, '/'); 679 $trailing = preg_quote($trailing, '/'); 680 preg_match_all("/{$leading}([^\s]+?){$trailing}/", $text, $matches); 681 return $matches[1]; 682 } 683 684 /** 685 * Validate a form element that should have tokens in it. 686 * 687 * Form elements that want to add this validation should have the #token_types 688 * parameter defined. 689 * 690 * For example: 691 * @code 692 * $form['my_node_text_element'] = array( 693 * '#type' => 'textfield', 694 * '#title' => t('Some text to token-ize that has a node context.'), 695 * '#default_value' => 'The title of this node is [title].', 696 * '#element_validate' => array('token_element_validate'), 697 * '#token_types' => array('node'), 698 * '#min_tokens' => 1, 699 * '#max_tokens' => 10, 700 * ); 701 * @endcode 702 */ 703 function token_element_validate(&$element, &$form_state) { 704 $value = isset($element['#value']) ? $element['#value'] : $element['#default_value']; 705 706 if (!drupal_strlen($value)) { 707 // Empty value needs no further validation since the element should depend 708 // on using the '#required' FAPI property. 709 return $element; 710 } 711 712 $tokens = token_scan($value); 713 $title = empty($element['#title']) ? $element['#parents'][0] : $element['#title']; 714 715 // Validate if an element must have a minimum number of tokens. 716 if (isset($element['#min_tokens']) && count($tokens) < $element['#min_tokens']) { 717 // @todo Change this error message to include the minimum number. 718 $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)); 719 form_error($element, $error); 720 } 721 722 // Validate if an element must have a maximum number of tokens. 723 if (isset($element['#max_tokens']) && count($tokens) > $element['#max_tokens']) { 724 // @todo Change this error message to include the maximum number. 725 $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)); 726 form_error($element, $error); 727 } 728 729 // Check if the field defines specific token types. 730 if (!empty($element['#token_types'])) { 731 $invalid_tokens = token_get_invalid_tokens_by_context($tokens, $element['#token_types']); 732 if ($invalid_tokens) { 733 form_error($element, t('The %element-title is using the following invalid tokens: @invalid-tokens.', array('%element-title' => $title, '@invalid-tokens' => implode(', ', $invalid_tokens)))); 734 } 735 } 736 737 return $element; 738 } 739 740 /** 741 * Deprecated. Use token_element_validate() instead. 742 */ 743 function token_element_validate_token_context(&$element, &$form_state) { 744 return token_element_validate($element, $form_state); 745 } 746 747 /** 748 * Find tokens that have been declared twice by different modules. 749 */ 750 function token_find_duplicate_tokens() { 751 token_include(); 752 $all_tokens = array(); 753 754 foreach (module_implements('token_list') as $module) { 755 $module_token_list = module_invoke($module, 'token_list', 'all'); 756 if (!isset($module_token_list) || !is_array($module_token_list)) { 757 // Skip modules that do not return an array as that is a valid return 758 // value. 759 continue; 760 } 761 if (in_array($module, _token_core_supported_modules())) { 762 $module = 'token'; 763 } 764 foreach ($module_token_list as $type => $tokens) { 765 foreach (array_keys($tokens) as $token) { 766 $all_tokens[$type . ':' . $token][] = $module; 767 } 768 } 769 } 770 771 foreach ($all_tokens as $token => $modules) { 772 if (count($modules) < 2) { 773 unset($all_tokens[$token]); 774 } 775 } 776 777 return $all_tokens; 778 } 779 780 /** 781 * Get a translated menu link by its mlid, without access checking. 782 * 783 * This function is a copy of menu_link_load() but with its own cache and a 784 * simpler query to load the link. This also skips normal menu link access 785 * checking by using _token_menu_link_translate(). 786 * 787 * @param $mlid 788 * The mlid of the menu item. 789 * 790 * @return 791 * A menu link translated for rendering. 792 * 793 * @see menu_link_load() 794 * @see _token_menu_link_translate() 795 */ 796 function token_menu_link_load($mlid) { 797 static $cache = array(); 798 799 if (!is_numeric($mlid)) { 800 return FALSE; 801 } 802 803 if (!isset($cache[$mlid])) { 804 $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)); 805 if (!empty($item)) { 806 _token_menu_link_translate($item); 807 } 808 $cache[$mlid] = $item; 809 } 810 811 return $cache[$mlid]; 812 } 813 814 /** 815 * Get a translated book menu link by its mlid, without access checking. 816 * 817 * This function is a copy of book_link_load() but with its own cache and a 818 * simpler query to load the link. This also skips normal menu link access 819 * checking by using _token_menu_link_translate(). 820 * 821 * @param $mlid 822 * The mlid of the book menu item. 823 * 824 * @return 825 * A book menu link translated for rendering. 826 * 827 * @see book_link_load() 828 * @see _token_menu_link_translate() 829 */ 830 function token_book_link_load($mlid) { 831 static $cache = array(); 832 833 if (!is_numeric($mlid)) { 834 return FALSE; 835 } 836 837 if (!isset($cache[$mlid])) { 838 $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)); 839 if (!empty($item)) { 840 _token_menu_link_translate($item); 841 } 842 $cache[$mlid] = $item; 843 } 844 845 return $cache[$mlid]; 846 } 847 848 function _token_menu_link_translate(&$item) { 849 $map = array(); 850 851 if (!is_array($item['options'])) { 852 $item['options'] = unserialize($item['options']); 853 } 854 855 if ($item['external']) { 856 $item['access'] = 1; 857 $item['href'] = $item['link_path']; 858 $item['title'] = $item['link_title']; 859 $item['localized_options'] = $item['options']; 860 } 861 else { 862 $map = explode('/', $item['link_path']); 863 _menu_link_map_translate($map, $item['to_arg_functions']); 864 $item['href'] = implode('/', $map); 865 866 // Note - skip callbacks without real values for their arguments. 867 if (strpos($item['href'], '%') !== FALSE) { 868 $item['access'] = FALSE; 869 return FALSE; 870 } 871 872 $item['access'] = TRUE; 873 _menu_item_localize($item, $map, TRUE); 874 } 875 876 // Allow other customizations - e.g. adding a page-specific query string to the 877 // options array. For performance reasons we only invoke this hook if the link 878 // has the 'alter' flag set in the options array. 879 if (!empty($item['options']['alter'])) { 880 drupal_alter('translated_menu_link', $item, $map); 881 } 882 883 return $map; 884 } 885 886 /** 887 * Find all ancestors of a given menu link ID. 888 * 889 * @param $mlid 890 * A menu link ID. 891 * 892 * @return 893 * An array of menu links from token_menu_link_load() with the root link 894 * first, and the menu link with ID $mlid last. 895 */ 896 function token_menu_link_get_parents_all($mlid) { 897 $parents = array(); 898 899 while (!empty($mlid)) { 900 $link = token_menu_link_load($mlid); 901 array_unshift($parents, $link); 902 $mlid = $link['plid']; 903 } 904 905 return $parents; 906 } 907 908 /** 909 * Deprecated. Use the raw return value of token_menu_link_get_parents_all() instead. 910 */ 911 function _menu_titles($menu_link, $nid) { 912 $titles = array(); 913 $parents = token_menu_link_get_parents_all($menu_link['mlid']); 914 foreach ($parents as $mlid => $parent) { 915 $titles[] = $parent['title']; 916 } 917 return $titles; 918 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Mon Jul 9 18:01:44 2012 | Cross-referenced by PHPXref 0.7 |