| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293 | <?php/** * @file * Enhances the token API in core: adds a browseable UI, missing tokens, etc. *//** * The maximum depth for token tree recursion. */define('TOKEN_MAX_DEPTH', 9);/** * Implements hook_help(). */function token_help($path, $arg) {  if ($path == 'admin/help#token') {    if (current_path() != 'admin/help/token') {      // Because system_modules() executes hook_help() for each module to 'test'      // if they will return anything, but not actually display it, we want to      // return a TRUE value if this is not actually the help page.      return TRUE;    }    $output = '<dl>';    $output .= '<dt>' . t('List of the currently available tokens on this site') . '</dt>';    $output .= '<dd>' . theme('token_tree', array('token_types' => 'all', 'click_insert' => FALSE, 'show_restricted' => TRUE)) . '</dd>';    $output .= '</dl>';    return $output;  }}/** * Implements hook_system_info_alter(). * * Prevent the token_actions module from being enabled since updates may have * left the old module files still in the directory. */function token_system_info_alter(&$info, $file, $type) {  if ($type == 'module' && $file->name == 'token_actions') {    $info['hidden'] = TRUE;  }}/** * Return an array of the core modules supported by token.module. */function _token_core_supported_modules() {  return array('book', 'field', 'menu', 'profile');}/** * Implements hook_menu(). */function token_menu() {  /*$items['token/autocomplete/all/%menu_tail'] = array(    'page callback' => 'token_autocomplete',    'access callback' => TRUE,    'type' => MENU_CALLBACK,    'file' => 'token.pages.inc',  );*/  $items['token/autocomplete/%token_type'] = array(    'page callback' => 'token_autocomplete_token',    'page arguments' => array(2),    'access callback' => TRUE,    'type' => MENU_CALLBACK,    'file' => 'token.pages.inc',  );  /*$items['token/autocomplete/%token_type/%menu_tail'] = array(    'page callback' => 'token_autocomplete_token',    'page arguments' => array(2, 3),    'access callback' => TRUE,    'type' => MENU_CALLBACK,    'file' => 'token.pages.inc',  );*/  $items['token/tree'] = array(    'page callback' => 'token_page_output_tree',    'access callback' => TRUE,    'type' => MENU_CALLBACK,    'file' => 'token.pages.inc',    'theme callback' => 'ajax_base_page_theme',  );  // Devel token pages.  if (module_exists('devel')) {    $items['node/%node/devel/token'] = array(      'title' => 'Tokens',      'page callback' => 'token_devel_token_object',      'page arguments' => array('node', 1),      'access arguments' => array('access devel information'),      'type' => MENU_LOCAL_TASK,      'file' => 'token.pages.inc',      'weight' => 5,    );    $items['comment/%comment/devel/token'] = array(      'title' => 'Tokens',      'page callback' => 'token_devel_token_object',      'page arguments' => array('comment', 1),      'access arguments' => array('access devel information'),      'type' => MENU_LOCAL_TASK,      'file' => 'token.pages.inc',      'weight' => 5,    );    $items['taxonomy/term/%taxonomy_term/devel/token'] = array(      'title' => 'Tokens',      'page callback' => 'token_devel_token_object',      'page arguments' => array('taxonomy_term', 2),      'access arguments' => array('access devel information'),      'type' => MENU_LOCAL_TASK,      'file' => 'token.pages.inc',      'weight' => 5,    );    $items['user/%user/devel/token'] = array(      'title' => 'Tokens',      'page callback' => 'token_devel_token_object',      'page arguments' => array('user', 1),      'access arguments' => array('access devel information'),      'type' => MENU_LOCAL_TASK,      'file' => 'token.pages.inc',      'weight' => 5,    );  }  // Admin menu callback to clear token caches.  $items['token/flush-cache'] = array(    'page callback' => 'token_flush_cache_callback',    'access arguments' => array('flush caches'),    'type' => MENU_CALLBACK,    'file' => 'token.pages.inc',  );  return $items;}/** * Implements hook_admin_menu_output_alter(). */function token_admin_menu_output_alter(&$content) {  $content['icon']['icon']['flush-cache']['token'] = array(    '#title' => t('Token registry'),    '#href' => 'token/flush-cache',    '#options' => array(      'query' => drupal_get_destination() + array('token' => drupal_get_token('token/flush-cache')),    ),  );}function token_type_load($token_type) {  $info = token_get_info();  return isset($info['types'][$token_type]) ? $info['types'][$token_type] : FALSE;}/** * Implements hook_theme(). */function token_theme() {  $info['tree_table'] = array(    'variables' => array(      'header' => array(),      'rows' => array(),      'attributes' => array(),      'empty' => '',      'caption' => '',    ),    'file' => 'token.pages.inc',  );  $info['token_tree'] = array(    'variables' => array(      'token_types' => array(),      'global_types' => TRUE,      'click_insert' => TRUE,      'show_restricted' => FALSE,      'recursion_limit' => 3,      'dialog' => FALSE,    ),    'file' => 'token.pages.inc',  );  $info['token_tree_link'] = array(    'variables' => array(      'text' => NULL,      'options' => array(),      'dialog' => TRUE,    ) + $info['token_tree']['variables'],    'file' => 'token.pages.inc',  );  return $info;}/** * Implements hook_library(). */function token_library() {  // jQuery treeTable plugin.  $libraries['treeTable'] = array(    'title' => 'jQuery treeTable',    'website' => 'http://plugins.jquery.com/project/treetable',    'version' => '2.3.0',    'js' => array(      drupal_get_path('module', 'token') . '/jquery.treeTable.js' => array(),    ),    'css' => array(      drupal_get_path('module', 'token') . '/jquery.treeTable.css' => array(),    ),  );  $libraries['dialog'] = array(    'title' => 'Token dialog',    'version' => '1.0',    'js' => array(      drupal_get_path('module', 'token') . '/token.js' => array(),    ),    'dependencies' => array(      array('system', 'ui.dialog'),    ),  );  return $libraries;}/** * Implements hook_form_alter(). * * Adds a submit handler to forms which could affect the tokens available on * the site. */function token_form_alter(&$form, $form_state, $form_id) {  switch ($form_id) {    // Profile field forms.    case 'profile_field_form':    case 'profile_field_delete':    // User picture form.    case 'user_admin_settings':    // System date type form.    // @todo Remove when http://drupal.org/node/1173706 is fixed.    case 'system_add_date_format_type_form':    case 'system_delete_date_format_type_form':      $form += array('#submit' => array());      array_unshift($form['#submit'], 'token_clear_cache');      break;  }}/** * Implements hook_block_view_alter(). */function token_block_view_alter(&$data, $block) {  if (!empty($block->title) && $block->title != '<none>') {    // Perform unsanitized token replacement since _block_render_blocks() will    // call check_plain() on $block->title.    $block->title = token_replace($block->title, array(), array('sanitize' => FALSE));  }}/** * Implements hook_form_FORM_ID_alter(). */function token_form_block_add_block_form_alter(&$form, $form_state) {  token_form_block_admin_configure_alter($form, $form_state);}/** * Implements hook_form_FORM_ID_alter(). */function token_form_block_admin_configure_alter(&$form, $form_state) {  $form['settings']['title']['#description'] .= ' ' . t('This field supports tokens.');  // @todo Figure out why this token validation does not seem to be working here.  $form['settings']['title']['#element_validate'][] = 'token_element_validate';  $form['settings']['title'] += array('#token_types' => array());}/** * Implements hook_widget_form_alter(). */function token_field_widget_form_alter(&$element, &$form_state, $context) {  if (!empty($element['#description']) && !empty($context['instance']['description'])) {    $element['#description'] = filter_xss_admin(token_replace($context['instance']['description']));  }}/** * Implements hook_field_info_alter(). */function token_field_info_alter(&$info) {  $defaults = array(    'taxonomy_term_reference' => 'taxonomy_term_reference_plain',    'number_integer' => 'number_unformatted',    'number_decimal' => 'number_unformatted',    'number_float' => 'number_unformatted',    'file' => 'file_url_plain',    'image' => 'file_url_plain',    'text' => 'text_default',    'text_long' => 'text_default',    'text_with_summary' => 'text_default',    'list_integer' => 'list_default',    'list_float' => 'list_default',    'list_text' => 'list_default',    'list_boolean' => 'list_default',  );  foreach ($defaults as $field_type => $default_token_formatter) {    if (isset($info[$field_type])) {      $info[$field_type] += array('default_token_formatter' => $default_token_formatter);    }  }}/** * Implements hook_field_display_alter(). */function token_field_display_alter(&$display, $context) {  if ($context['view_mode'] == 'token') {    $view_mode_settings = field_view_mode_settings($context['instance']['entity_type'], $context['instance']['bundle']);    // If the token view mode fell back to the 'default' view mode, then    // use the default token formatter.    if (empty($view_mode_settings[$context['view_mode']]['custom_settings'])) {      $field_type_info = field_info_field_types($context['field']['type']);      // If the field has specified a specific formatter to be used by default      // with tokens, use that, otherwise use the default formatter.      $formatter = !empty($field_type_info['default_token_formatter']) ? $field_type_info['default_token_formatter'] : $field_type_info['default_formatter'];      // Now that we have a formatter, fill in all the settings.      $display['type'] = $formatter;      $formatter_info = field_info_formatter_types($formatter);      $display['settings'] = isset($formatter_info['settings']) ? $formatter_info['settings'] : array();      $display['settings']['label'] = 'hidden';      $display['module'] = $formatter_info['module'];    }  }}/** * Implements hook_field_create_instance(). */function token_field_create_instance($instance) {  token_clear_cache();}/** * Implements hook_field_update_instance(). */function token_field_update_instance($instance) {  token_clear_cache();}/** * Implements hook_field_delete_instance(). */function token_field_delete_instance($instance) {  token_clear_cache();}/** * Clear token caches and static variables. */function token_clear_cache() {  if (db_table_exists('cache_token')) {    cache_clear_all('*', 'cache_token', TRUE);  }  drupal_static_reset('token_get_info');  drupal_static_reset('token_get_global_token_types');  drupal_static_reset('token_get_entity_mapping');  drupal_static_reset('token_build_tree');  drupal_static_reset('_token_profile_fields');  drupal_static_reset('token_menu_link_load');  drupal_static_reset('token_book_link_load');  drupal_static_reset('token_node_menu_link_load');  drupal_static_reset('_token_field_info');}/** * Return an array of entity type to token type mappings. * * Why do we need this? Because when the token API was moved to core we did not * re-use the entity type as the base name for taxonomy terms and vocabulary * tokens. * * @see token_entity_info_alter() * @see http://drupal.org/node/737726 */function token_get_entity_mapping($value_type = 'token', $value = NULL, $fallback = FALSE) {  $mapping = &drupal_static(__FUNCTION__, array());  if (empty($mapping)) {    foreach (entity_get_info() as $entity_type => $info) {      $mapping[$entity_type] = !empty($info['token type']) ? $info['token type'] : $entity_type;    }    // Allow modules to alter the mapping array.    drupal_alter('token_entity_mapping', $mapping);  }  if (!isset($value)) {    return $value_type == 'token' ? array_flip($mapping) : $mapping;  }  elseif ($value_type == 'token') {    $return = array_search($value, $mapping);    return $return !== FALSE ? $return : ($fallback ? $value : FALSE);  }  elseif ($value_type == 'entity') {    return isset($mapping[$value]) ? $mapping[$value] : ($fallback ? $value : FALSE);  }}/** * Implements hook_entity_info_alter(). * * Because some token types to do not match their entity type names, we have to * map them to the proper type. This is purely for other modules' benefit. * * @see token_get_entity_mapping() * @see http://drupal.org/node/737726 */function token_entity_info_alter(&$info) {  foreach (array_keys($info) as $entity_type) {    // Add a token view mode if it does not already exist. Only work with    // fieldable entities.    if (!empty($info[$entity_type]['fieldable'])) {      if (!isset($info[$entity_type])) {        $info[$entity_type]['view modes'] = array();      }      if (!isset($info[$entity_type]['view modes']['token'])) {        $info[$entity_type]['view modes']['token'] = array(          'label' => t('Tokens'),          'custom settings' => FALSE,        );      }    }    if (!empty($info[$entity_type]['token type'])) {      // If the entity's token type is already defined, great!      continue;    }    // Fill in default token types for entities.    switch ($entity_type) {      case 'taxonomy_term':      case 'taxonomy_vocabulary':        // Stupid taxonomy token types...        $info[$entity_type]['token type'] = str_replace('taxonomy_', '', $entity_type);        break;      default:        // By default the token type is the same as the entity type.        $info[$entity_type]['token type'] = $entity_type;        break;    }  }}/** * Implements hook_module_implements_alter(). * * Adds missing token support for core modules. */function token_module_implements_alter(&$implementations, $hook) {  module_load_include('inc', 'token', 'token.tokens');  if ($hook == 'tokens' || $hook == 'token_info' || $hook == 'token_info_alter' || $hook == 'tokens_alter') {    foreach (_token_core_supported_modules() as $module) {      if (module_exists($module) && function_exists($module . '_' . $hook)) {        $implementations[$module] = FALSE;      }    }    // Move token.module to get included first since it is responsible for    // other modules.    unset($implementations['token']);    $implementations = array_merge(array('token' => 'tokens'), $implementations);  }}/** * Implements hook_flush_caches(). */function token_flush_caches() {  if (db_table_exists('cache_token')) {    return array('cache_token');  }}/** * Retrieve, sort, store token info from the cache. * * @param $token_type *   The optional token type that if specified will return *   $info['types'][$token_type]. * @param $token *   The optional token name if specified will return *   $info['tokens'][$token_type][$token]. * * @return *   An array of all token information from hook_token_info(), or the array *   of a token type or specific token. * * @see hook_token_info() * @see hook_token_info_alter() */function token_get_info($token_type = NULL, $token = NULL) {  global $language;  // Use the advanced drupal_static() pattern, since this is called very often.  static $drupal_static_fast;  if (!isset($drupal_static_fast)) {    $drupal_static_fast['token_info'] = &drupal_static(__FUNCTION__);  }  $token_info = &$drupal_static_fast['token_info'];  if (empty($token_info)) {    $cid = "info:{$language->language}";    if ($cache = cache_get($cid, 'cache_token')) {      $token_info = $cache->data;    }    else {      $token_info = token_info();      foreach (array_keys($token_info['types']) as $type_key) {        if (isset($token_info['types'][$type_key]['type'])) {          $base_type = $token_info['types'][$type_key]['type'];          // If this token type extends another token type, then merge in          // the base token type's tokens.          if (isset($token_info['tokens'][$base_type])) {            $token_info['tokens'] += array($type_key => array());            $token_info['tokens'][$type_key] += $token_info['tokens'][$base_type];          }        }        else {          // Add a 'type' value to each token type so we can properly use          // token_type_load().          $token_info['types'][$type_key]['type'] = $type_key;        }      }      // Pre-sort tokens.      uasort($token_info['types'], 'token_asort_tokens');      foreach (array_keys($token_info['tokens']) as $type) {        uasort($token_info['tokens'][$type], 'token_asort_tokens');      }      // Store info in cache for future use.      cache_set($cid, $token_info, 'cache_token');    }  }  if (isset($token_type) && isset($token)) {    return isset($token_info['tokens'][$token_type][$token]) ? $token_info['tokens'][$token_type][$token] : NULL;  }  elseif (isset($token_type)) {    return isset($token_info['types'][$token_type]) ? $token_info['types'][$token_type] : NULL;  }  else {    return $token_info;  }}/** * Return the module responsible for a token. * * @param string $type *   The token type. * @param string $name *   The token name. * * @return mixed *   The value of $info['tokens'][$type][$name]['module'] from token_get_info(), *   or NULL if the value does not exist. */function _token_module($type, $name) {  $token_info = token_get_info($type, $name);  return isset($token_info['module']) ? $token_info['module'] : NULL;}/** * uasort() callback to sort tokens by the 'name' property. */function token_asort_tokens($token_a, $token_b) {  return strnatcmp($token_a['name'], $token_b['name']);}/** * Get a list of token types that can be used without any context (global). * * @return *   An array of global token types. */function token_get_global_token_types() {  $global_types = &drupal_static(__FUNCTION__, array());  if (empty($global_types)) {    $token_info = token_get_info();    foreach ($token_info['types'] as $type => $type_info) {      // If the token types has not specified that 'needs-data' => TRUE, then      // it is a global token type that will always be replaced in any context.      if (empty($type_info['needs-data'])) {        $global_types[] = $type;      }    }  }  return $global_types;}/** * Validate an tokens in raw text based on possible contexts. * * @param $value *   A string with the raw text containing the raw tokens, or an array of *   tokens from token_scan(). * @param $tokens *   An array of token types that will be used when token replacement is *   performed. * @return *   An array with the invalid tokens in their original raw forms. */function token_get_invalid_tokens_by_context($value, $valid_types = array()) {  if (in_array('all', $valid_types)) {    $info = token_get_info();    $valid_types = array_keys($info['types']);  }  else {    // Add the token types that are always valid in global context.    $valid_types = array_merge($valid_types, token_get_global_token_types());  }  $invalid_tokens = array();  $value_tokens = is_string($value) ? token_scan($value) : $value;  foreach ($value_tokens as $type => $tokens) {    if (!in_array($type, $valid_types)) {      // If the token type is not a valid context, its tokens are invalid.      $invalid_tokens = array_merge($invalid_tokens, array_values($tokens));    }    else {      // Check each individual token for validity.      $invalid_tokens = array_merge($invalid_tokens, token_get_invalid_tokens($type, $tokens));    }  }  array_unique($invalid_tokens);  return $invalid_tokens;}/** * Validate an array of tokens based on their token type. * * @param $type *   The type of tokens to validate (e.g. 'node', etc.) * @param $tokens *   A keyed array of tokens, and their original raw form in the source text. * @return *   An array with the invalid tokens in their original raw forms. */function token_get_invalid_tokens($type, $tokens) {  $token_info = token_get_info();  $invalid_tokens = array();  foreach ($tokens as $token => $full_token) {    if (isset($token_info['tokens'][$type][$token])) {      continue;    }    // Split token up if it has chains.    $parts = explode(':', $token, 2);    if (!isset($token_info['tokens'][$type][$parts[0]])) {      // This is an invalid token (not defined).      $invalid_tokens[] = $full_token;    }    elseif (count($parts) == 2) {      $sub_token_info = $token_info['tokens'][$type][$parts[0]];      if (!empty($sub_token_info['dynamic'])) {        // If this token has been flagged as a dynamic token, skip it.        continue;      }      elseif (empty($sub_token_info['type'])) {        // If the token has chains, but does not support it, it is invalid.        $invalid_tokens[] = $full_token;      }      else {        // Resursively check the chained tokens.        $sub_tokens = token_find_with_prefix(array($token => $full_token), $parts[0]);        $invalid_tokens = array_merge($invalid_tokens, token_get_invalid_tokens($sub_token_info['type'], $sub_tokens));      }    }  }  return $invalid_tokens;}/** * Validate a form element that should have tokens in it. * * Form elements that want to add this validation should have the #token_types * parameter defined. * * For example: * @code * $form['my_node_text_element'] = array( *   '#type' => 'textfield', *   '#title' => t('Some text to token-ize that has a node context.'), *   '#default_value' => 'The title of this node is [node:title].', *   '#element_validate' => array('token_element_validate'), *   '#token_types' => array('node'), *   '#min_tokens' => 1, *   '#max_tokens' => 10, * ); * @endcode */function token_element_validate(&$element, &$form_state) {  $value = isset($element['#value']) ? $element['#value'] : $element['#default_value'];  if (!drupal_strlen($value)) {    // Empty value needs no further validation since the element should depend    // on using the '#required' FAPI property.    return $element;  }  $tokens = token_scan($value);  $title = empty($element['#title']) ? $element['#parents'][0] : $element['#title'];  // @todo Find old Drupal 6 style tokens and add them to invalid tokens.  // Validate if an element must have a minimum number of tokens.  if (isset($element['#min_tokens']) && count($tokens) < $element['#min_tokens']) {    // @todo Change this error message to include the minimum number.    $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));    form_error($element, $error);  }  // Validate if an element must have a maximum number of tokens.  if (isset($element['#max_tokens']) && count($tokens) > $element['#max_tokens']) {    // @todo Change this error message to include the maximum number.    $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));    form_error($element, $error);  }  // Check if the field defines specific token types.  if (isset($element['#token_types'])) {    $invalid_tokens = token_get_invalid_tokens_by_context($tokens, $element['#token_types']);    if ($invalid_tokens) {      form_error($element, t('The %element-title is using the following invalid tokens: @invalid-tokens.', array('%element-title' => $title, '@invalid-tokens' => implode(', ', $invalid_tokens))));    }  }  return $element;}/** * Deprecated. Use token_element_validate() instead. */function token_element_validate_token_context(&$element, &$form_state) {  return token_element_validate($element, $form_state);}/** * Implements hook_form_FORM_ID_alter(). */function token_form_field_ui_field_edit_form_alter(&$form, $form_state) {  if (!isset($form['instance']) || !empty($form['#field']['locked'])) {    return;  }  if (($form['#field']['type'] == 'file' || $form['#field']['type'] == 'image') && isset($form['instance']['settings']['file_directory']) && !module_exists('filefield_paths')) {    // GAH! We can only support global tokens in the upload file directory path.    $form['instance']['settings']['file_directory']['#element_validate'][] = 'token_element_validate';    $form['instance']['settings']['file_directory'] += array('#token_types' => array());    $form['instance']['settings']['file_directory']['#description'] .= ' ' . t('This field supports tokens.');  }  // Note that the description is tokenized via token_field_widget_form_alter().  $form['instance']['description']['#description'] .= '<br />' . t('This field supports tokens.');  $form['instance']['description']['#element_validate'][] = 'token_element_validate';  $form['instance']['description'] += array('#token_types' => array());  $form['instance']['settings']['token_tree'] = array(    '#theme' => 'token_tree',    '#token_types' => array(),    '#dialog' => TRUE,    '#weight' => $form['instance']['description']['#weight'] + 0.5,  );}/** * Implements hook_form_FORM_ID_alter(). * * Alters the configure action form to add token context validation and * adds the token tree for a better token UI and selection. */function token_form_system_actions_configure_alter(&$form, $form_state) {  $action = actions_function_lookup($form['actions_action']['#value']);  switch ($action) {    case 'system_message_action':    case 'system_send_email_action':    case 'system_goto_action':      $form['token_tree'] = array(        '#theme' => 'token_tree',        '#token_types' => 'all',        '#dialog' => TRUE,        '#weight' => 100,      );      // @todo Add token validation to the action fields that can use tokens.      break;  }}/** * Implements hook_form_FORM_ID_alter(). * * Alters the user e-mail fields to add token context validation and * adds the token tree for a better token UI and selection. */function token_form_user_admin_settings_alter(&$form, &$form_state) {  $email_token_help = t('Available variables are: [site:name], [site:url], [user:name], [user:mail], [site:login-url], [site:url-brief], [user:edit-url], [user:one-time-login-url], [user:cancel-url].');  foreach (element_children($form) as $key) {    $element = &$form[$key];    // Remove the crummy default token help text.    if (!empty($element['#description'])) {      $element['#description'] = trim(str_replace($email_token_help, t('The list of available tokens that can be used in e-mails is provided below.'), $element['#description']));    }    switch ($key) {      case 'email_admin_created':      case 'email_pending_approval':      case 'email_no_approval_required':      case 'email_password_reset':      case 'email_cancel_confirm':        // Do nothing, but allow execution to continue.        break;      case 'email_activated':      case 'email_blocked':      case 'email_canceled':        // These fieldsets have their e-mail elements inside a 'settings'        // sub-element, so switch to that element instead.        $element = &$form[$key]['settings'];        break;      default:        continue 2;    }    foreach (element_children($element) as $sub_key) {      if (!isset($element[$sub_key]['#type'])) {        continue;      }      elseif ($element[$sub_key]['#type'] == 'textfield' && substr($sub_key, -8) === '_subject') {        // Add validation to subject textfields.        $element[$sub_key]['#element_validate'][] = 'token_element_validate';        $element[$sub_key] += array('#token_types' => array('user'));      }      elseif ($element[$sub_key]['#type'] == 'textarea' && substr($sub_key, -5) === '_body') {        // Add validation to body textareas.        $element[$sub_key]['#element_validate'][] = 'token_element_validate';        $element[$sub_key] += array('#token_types' => array('user'));      }    }  }  // Add the token tree UI.  $form['email']['token_tree'] = array(    '#theme' => 'token_tree',    '#token_types' => array('user'),    '#show_restricted' => TRUE,    '#dialog' => TRUE,    '#weight' => 90,  );}/** * Build a tree array of tokens used for themeing or information. * * @param $token_type *   The token type. * @param $flat_tree *   A boolean if TRUE will only make a flat array of tokens, otherwise *   child tokens will be inside the 'children' parameter of a token. * @param $show_restricted *   A boolean if TRUE will show restricted tokens. Otherwise they will be *   hidden. Default is FALSE. * @param $recursion_limit *   An integer with the maximum number of token levels to recurse. * @param $parents *   An optional array with the current parents of the tokens. */function token_build_tree($token_type, array $options = array()) {  global $language;  // Static cache of already built token trees.  $trees = &drupal_static(__FUNCTION__, array());  $options += array(    'restricted' => FALSE,    'depth' => 4,    'data' => array(),    'values' => FALSE,    'flat' => FALSE,  );  // Do not allow past the maximum token information depth.  $options['depth'] = min($options['depth'], TOKEN_MAX_DEPTH);  // If $token_type is an entity, make sure we are using the actual token type.  if ($entity_token_type = token_get_entity_mapping('entity', $token_type)) {    $token_type = $entity_token_type;  }  $tree_cid = "tree:{$token_type}:{$language->language}:{$options['depth']}";  // If we do not have this base tree in the static cache, check {cache_token}  // otherwise generate and store it in the cache.  if (!isset($trees[$tree_cid])) {    if ($cache = cache_get($tree_cid, 'cache_token')) {      $trees[$tree_cid] = $cache->data;    }    else {      $options['parents'] = array();      $trees[$tree_cid] = _token_build_tree($token_type, $options);      cache_set($tree_cid, $trees[$tree_cid], 'cache_token');    }  }  $tree = $trees[$tree_cid];  // If the user has requested a flat tree, convert it.  if (!empty($options['flat'])) {    $tree = token_flatten_tree($tree);  }  // Fill in token values.  if (!empty($options['values'])) {    $token_values = array();    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 = token_generate($token_type, $token_values, $options['data']);      foreach ($token_values as $token => $replacement) {        $tree[$token]['value'] = $replacement;      }    }  }  return $tree;}/** * Flatten a token tree. */function token_flatten_tree($tree) {  $result = array();  foreach ($tree as $token => $token_info) {    $result[$token] = $token_info;    if (isset($token_info['children']) && is_array($token_info['children'])) {      $result += token_flatten_tree($token_info['children']);      // unset($result[$token]['children']);    }  }  return $result;}/** * Generate a token tree. */function _token_build_tree($token_type, array $options) {  $options += array(    'parents' => array(),  );  $info = token_get_info();  if ($options['depth'] <= 0 || !isset($info['types'][$token_type]) || !isset($info['tokens'][$token_type])) {    return array();  }  $tree = array();  foreach ($info['tokens'][$token_type] as $token => $token_info) {    // Build the raw token string.    $token_parents = $options['parents'];    if (empty($token_parents)) {      // If the parents array is currently empty, assume the token type is its      // parent.      $token_parents[] = $token_type;    }    elseif (in_array($token, array_slice($token_parents, 1))) {      // Prevent duplicate recursive tokens. For example, this will prevent      // the tree from generating the following tokens or deeper:      // [comment:parent:parent]      // [comment:parent:root:parent]      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;    // Add the token's real name (leave out the base token type).    $tree[$raw_token]['token'] = implode(':', array_slice($token_parents, 1));    // Add the token's parent as its raw token value.    if (!empty($options['parents'])) {      $tree[$raw_token]['parent'] = '[' . implode(':', $options['parents']) . ']';    }    // Fetch the child tokens.    if (!empty($token_info['type'])) {      $child_options = $options;      $child_options['depth']--;      $child_options['parents'] = $token_parents;      $tree[$raw_token]['children'] = _token_build_tree($token_info['type'], $child_options);    }  }  return $tree;}/** * Get a translated menu link by its mlid, without access checking. * * This function is a copy of menu_link_load() but with its own cache and a * simpler query to load the link. This also skips normal menu link access * checking by using _token_menu_link_translate(). * * @param $mlid *   The mlid of the menu item. * * @return *   A menu link translated for rendering. * * @see menu_link_load() * @see _token_menu_link_translate() */function token_menu_link_load($mlid) {  $cache = &drupal_static(__FUNCTION__, array());  if (!is_numeric($mlid)) {    return FALSE;  }  if (!isset($cache[$mlid])) {    $item = db_query("SELECT * FROM {menu_links} ml LEFT JOIN {menu_router} m ON m.path = ml.router_path WHERE ml.mlid = :mlid", array(':mlid' => $mlid))->fetchAssoc();    if (!empty($item)) {      _token_menu_link_translate($item);    }    $cache[$mlid] = $item;  }  return $cache[$mlid];}/** * Get a translated book menu link by its mlid, without access checking. * * This function is a copy of book_link_load() but with its own cache and a * simpler query to load the link. This also skips normal menu link access * checking by using _token_menu_link_translate(). * * @param $mlid *   The mlid of the book menu item. * * @return *   A book menu link translated for rendering. * * @see book_link_load() * @see _token_menu_link_translate() */function token_book_link_load($mlid) {  $cache = &drupal_static(__FUNCTION__, array());  if (!is_numeric($mlid)) {    return FALSE;  }  if (!isset($cache[$mlid])) {    $item = 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 = :mlid", array(':mlid' => $mlid))->fetchAssoc();    if (!empty($item)) {      _token_menu_link_translate($item);    }    $cache[$mlid] = $item;  }  return $cache[$mlid];}function _token_menu_link_translate(&$item) {  $map = array();  if (!is_array($item['options'])) {    $item['options'] = unserialize($item['options']);  }  if ($item['external']) {    $item['access'] = 1;    $item['href'] = $item['link_path'];    $item['title'] = $item['link_title'];    $item['localized_options'] = $item['options'];  }  else {    // Complete the path of the menu link with elements from the current path,    // if it contains dynamic placeholders (%).    $map = explode('/', $item['link_path']);    if (strpos($item['link_path'], '%') !== FALSE) {      // Invoke registered to_arg callbacks.      if (!empty($item['to_arg_functions'])) {        _menu_link_map_translate($map, $item['to_arg_functions']);      }    }    $item['href'] = implode('/', $map);    // Skip links containing untranslated arguments.    if (strpos($item['href'], '%') !== FALSE) {      $item['access'] = FALSE;      return FALSE;    }    $item['access'] = TRUE;    _menu_item_localize($item, $map, TRUE);  }  // Allow other customizations - e.g. adding a page-specific query string to the  // options array. For performance reasons we only invoke this hook if the link  // has the 'alter' flag set in the options array.  if (!empty($item['options']['alter'])) {    drupal_alter('translated_menu_link', $item, $map);  }  return $map;}/** * Prepare a string for use as a valid token name. * * @param $name *   The token name to clean. * @return *   The cleaned token name. */function token_clean_token_name($name) {  static $names = array();  if (!isset($names[$name])) {    $cleaned_name = strtr($name, array(' ' => '-', '_' => '-', '/' => '-', '[' => '-', ']' => ''));    $cleaned_name = preg_replace('/[^\w\-]/i', '', $cleaned_name);    $cleaned_name = trim($cleaned_name, '-');    $names[$name] = $cleaned_name;  }  return $names[$name];}/** * Do not use this function yet. Its API has not been finalized. */function token_render_array(array $array, array $options = array()) {  $rendered = array();  foreach (element_children($array) as $key) {    $value = $array[$key];    $rendered[] = is_array($value) ? render($value) : (string) $value;  }  if (!empty($options['sanitize'])) {    $rendered = array_map('check_plain', $rendered);  }  $join = isset($options['join']) ? $options['join'] : ', ';  return implode($join, $rendered);}/** * Do not use this function yet. Its API has not been finalized. */function token_render_array_value($value, array $options = array()) {  $rendered = is_array($value) ? render($value) : (string) $value;  if (!empty($options['sanitize'])) {    $rendered = check_plain($rendered);  }  return $rendered;}/** * Copy of drupal_render_cache_get() that does not care about request method. */function token_render_cache_get($elements) {  if (!$cid = drupal_render_cid_create($elements)) {    return FALSE;  }  $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'cache';  if (!empty($cid) && $cache = cache_get($cid, $bin)) {    // Add additional libraries, JavaScript, CSS and other data attached    // to this element.    if (isset($cache->data['#attached'])) {      drupal_process_attached($cache->data);    }    // Return the rendered output.    return $cache->data['#markup'];  }  return FALSE;}/** * Coyp of drupal_render_cache_set() that does not care about request method. */function token_render_cache_set(&$markup, $elements) {  // This should only run of drupal_render_cache_set() did not.  if (in_array($_SERVER['REQUEST_METHOD'], array('GET', 'HEAD'))) {    return FALSE;  }  $original_method = $_SERVER['REQUEST_METHOD'];  $_SERVER['REQUEST_METHOD'] = 'GET';  drupal_render_cache_set($markup, $elements);  $_SERVER['REQUEST_METHOD'] = $original_method;}function token_menu_link_load_all_parents($mlid) {  $cache = &drupal_static(__FUNCTION__, array());  if (!is_numeric($mlid)) {    return array();  }  if (!isset($cache[$mlid])) {    $cache[$mlid] = array();    $plid = db_query("SELECT plid FROM {menu_links} WHERE mlid = :mlid", array(':mlid' => $mlid))->fetchField();    while ($plid && $parent = token_menu_link_load($plid)) {      $cache[$mlid] = array($plid => $parent['title']) + $cache[$mlid];      $plid = $parent['plid'];    }  }  return $cache[$mlid];}function token_taxonomy_term_load_all_parents($tid) {  $cache = &drupal_static(__FUNCTION__, array());  if (!is_numeric($tid)) {    return array();  }  if (!isset($cache[$tid])) {    $cache[$tid] = array();    $parents = taxonomy_get_parents_all($tid);    array_shift($parents); // Remove this term from the array.    $parents = array_reverse($parents);    foreach ($parents as $term) {      $cache[$tid][$term->tid] = entity_label('taxonomy_term', $term);    }  }  return $cache[$tid];}/** * Load the preferred menu link associated with a node. * * @param $node *   A node object. * * @return *   A menu link array from token_menu_link_load() or FALSE if a menu link was *   not found. * * @see menu_node_prepare() * @see token_menu_link_load() */function token_node_menu_link_load($node) {  $cache = &drupal_static(__FUNCTIon__, array());  if (!isset($cache[$node->nid])) {    $mlid = FALSE;    // Nodes do not have their menu links loaded via menu_node_load().    if (!isset($node->menu)) {      // We need to clone the node as menu_node_prepare() may cause data loss.      // @see http://drupal.org/node/1317926      $menu_node = clone $node;      menu_node_prepare($menu_node);      $mlid = !empty($menu_node->menu['mlid']) ? $menu_node->menu['mlid'] : FALSE;    }    else {      $mlid = !empty($node->menu['mlid']) ? $node->menu['mlid'] : FALSE;    }    $cache[$node->nid] = $mlid;  }  return $cache[$node->nid] ? token_menu_link_load($cache[$node->nid]) : FALSE;}
 |