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