Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
project
drupal
Commits
40003c83
Commit
40003c83
authored
Aug 19, 2009
by
Dries
Browse files
- Patch
#113614
by eaton, fago, et al: add centralized token/placeholder subsituation to core.
parent
e998857e
Changes
20
Hide whitespace changes
Inline
Side-by-side
includes/common.inc
View file @
40003c83
...
...
@@ -3549,6 +3549,7 @@ function _drupal_bootstrap_full() {
require_once
DRUPAL_ROOT
.
'/includes/mail.inc'
;
require_once
DRUPAL_ROOT
.
'/includes/actions.inc'
;
require_once
DRUPAL_ROOT
.
'/includes/ajax.inc'
;
require_once
DRUPAL_ROOT
.
'/includes/token.inc'
;
// Set the Drupal custom error handler.
set_error_handler
(
'_drupal_error_handler'
);
set_exception_handler
(
'_drupal_exception_handler'
);
...
...
includes/token.inc
0 → 100644
View file @
40003c83
<?php
// $Id$
/**
* @file
* Drupal placeholder/token replacement system.
*
* Provides a set of extensible API functions for replacing placeholders in text
* with meaningful values.
*
* For example: When configuring automated emails, an administrator enters standard
* text for the email. Variables like the title of a node and the date the email
* was sent can be entered as placeholders like [node:title] and [date:short].
* When a Drupal module prepares to send the email, it can call the token_replace()
* function, passing in the text. The token system will scan the text for placeholder
* tokens, give other modules an opportunity to replace them with meaningful text,
* then return the final product to the original module.
*
* Tokens follow the form: [$type:$name], where $type is a general class of
* tokens like 'node', 'user', or 'comment' and $name is the name of a given
* placeholder. For example, [node:title].
*
* In addition to raw text containing placeholders, modules may pass in an array
* of objects to be used when performing the replacement. The objects should be
* keyed by the token type they correspond to. For example:
*
* @code
* // Load a node and a user, then replace tokens in the text.
* $text = 'On [date:short], [user:name] read [node:title].';
* $node = node_load(1);
* $user = user_load(1);
*
* // [date:...] tokens use the current date automatically.
* $data = array('node' => $node, 'user' => $user);
* return token_replace($text, $data);
* @endcode
*
* Some tokens may be chained in the form of [$type:$pointer:$name], where $type
* is a normal token type, $pointer is a reference to another token type, and
* $name is the name of a given placeholder. For example, [node:author:mail]. In
* that example, 'author' is a pointer to the 'user' account that created the node,
* and 'mail' is a placeholder available for any 'user'.
*
* @see token_replace()
* @see hook_tokens()
* @see hook_token_info()
*/
/**
* Replace all tokens in a given string with appropriate values.
*
* @param $text
* A string potentially containing replacable tokens.
* @param $data
* (optional) An array of keyed objects. For simple replacement scenarios
* 'node', 'user', and others are common keys, with an accompanying node or
* user object being the value. Some token types, like 'site', do not require
* any explicit information from $data and can be replaced even if it is empty.
* @param $options
* (optional) A keyed array of settings and flags to control the token
* replacement process. Supported options are:
* - language: A language object to be used when generating locale-sensitive
* tokens.
* - callback: A callback function that will be used to post-process the array
* of token replacements after they are generated. For example, a module using
* tokens in a text-only email might provide a callback to strip HTML
* entities from token values before they are inserted into the final text.
* - clear: A boolean flag indicating that tokens should be removed from the
* final text if no replacement value can be generated.
* - sanitize: A boolean flag indicating that tokens should be sanitized for
* display to a web browser. Defaults to TRUE. Developers who set this option
* to FALSE assume responsibility for running filter_xss(), check_plain() or
* other appropriate scrubbing functions before displaying data to users.
* @return
* Text with tokens replaced.
*/
function
token_replace
(
$text
,
array
$data
=
array
(),
array
$options
=
array
())
{
$replacements
=
array
();
foreach
(
token_scan
(
$text
)
as
$type
=>
$tokens
)
{
$replacements
+=
token_generate
(
$type
,
$tokens
,
$data
,
$options
);
}
// Optionally alter the list of replacement values.
if
(
!
empty
(
$options
[
'callback'
])
&&
drupal_function_exists
(
$options
[
'callback'
]))
{
$function
=
$options
[
'callback'
];
$function
(
$replacements
,
$data
,
$options
);
}
$tokens
=
array_keys
(
$replacements
);
$values
=
array_values
(
$replacements
);
return
str_replace
(
$tokens
,
$values
,
$text
);
}
/**
* Build a list of all token-like patterns that appear in the text.
*
* @param $text
* The text to be scanned for possible tokens.
* @return
* An associative array of discovered tokens, grouped by type.
*/
function
token_scan
(
$text
)
{
// Matches tokens with the following pattern: [$type:$token]
// $type and $token may not contain white spaces.
preg_match_all
(
'/\[([^\s\]:]*):([^\s\]]*)\]/'
,
$text
,
$matches
);
$types
=
$matches
[
1
];
$tokens
=
$matches
[
2
];
// Iterate through the matches, building an associative array containing
// $tokens grouped by $types, pointing to the version of the token found in
// the source text. For example, $results['node']['title'] = '[node:title]';
$results
=
array
();
for
(
$i
=
0
;
$i
<
count
(
$tokens
);
$i
++
)
{
$results
[
$types
[
$i
]][
$tokens
[
$i
]]
=
$matches
[
0
][
$i
];
}
return
$results
;
}
/**
* Generate replacement values for a list of tokens.
*
* @param $type
* The type of token being replaced. 'node', 'user', and 'date' are common.
* @param $tokens
* An array of tokens to be replaced, keyed by the literal text of the token
* as it appeared in the source text.
* @param $data
* (optional) An array of keyed objects. For simple replacement scenarios
* 'node', 'user', and others are common keys, with an accompanying node or
* user object being the value. Some token types, like 'site', do not require
* any explicit information from $data and can be replaced even if it is empty.
* @param $options
* (optional) A keyed array of settings and flags to control the token
* replacement process. Supported options are:
* - 'language' A language object to be used when generating locale-sensitive
* tokens.
* - 'callback' A callback function that will be used to post-process the array
* of token replacements after they are generated. Can be used when modules
* require special formatting of token text, for example URL encoding or
* truncation to a specific length.
* - 'sanitize' A boolean flag indicating that tokens should be sanitized for
* display to a web browser. Developers who set this option to FALSE assume
* responsibility for running filter_xss(), check_plain() or other
* appropriate scrubbing functions before displaying data to users.
* @return
* An associative array of replacement values, keyed by the original 'raw'
* tokens that were found in the source text. For example:
* $results['[node:title]'] = 'My new node';
*/
function
token_generate
(
$type
,
array
$tokens
,
array
$data
=
array
(),
array
$options
=
array
())
{
$results
=
array
();
$options
+=
array
(
'sanitize'
=>
TRUE
);
foreach
(
module_implements
(
'tokens'
)
as
$module
)
{
$function
=
$module
.
'_tokens'
;
if
(
drupal_function_exists
(
$function
))
{
$result
=
$function
(
$type
,
$tokens
,
$data
,
$options
);
foreach
(
$result
as
$original
=>
$replacement
)
{
$results
[
$original
]
=
$replacement
;
}
}
}
return
$results
;
}
/**
* Given a list of tokens, return those that begin with a specific prefix.
*
* Used to extract a group of 'chained' tokens (such as [node:author:name]) from
* the full list of tokens found in text. For example:
* @code
* $data = array(
* 'author:name' => '[node:author:name]',
* 'title' => '[node:title]',
* 'created' => '[node:author:name]',
* );
* $results = token_find_with_prefix($data, 'author');
* $results == array('name' => '[node:author:name]');
* @endcode
*
* @param $tokens
* A keyed array of tokens, and their original raw form in the source text.
* @param $prefix
* A textual string to be matched at the beginning of the token.
* @param $delimiter
* An optional string containing the character that separates the prefix from
* the rest of the token. Defaults to ':'.
* @return
* An associative array of discovered tokens, with the prefix and delimiter
* stripped from the key.
*/
function
token_find_with_prefix
(
array
$tokens
,
$prefix
,
$delimiter
=
':'
)
{
$results
=
array
();
foreach
(
$tokens
as
$token
=>
$raw
)
{
$parts
=
split
(
$delimiter
,
$token
,
2
);
if
(
count
(
$parts
)
==
2
&&
$parts
[
0
]
==
$prefix
)
{
$results
[
$parts
[
1
]]
=
$raw
;
}
}
return
$results
;
}
/**
* Returns metadata describing supported tokens.
*
* The metadata array contains token type, name, and description data as well as
* an optional pointer indicating that the token chains to another set of tokens.
* For example:
* @code
* $data['types']['node'] = array(
* 'name' => t('Nodes'),
* 'description' => t('Tokens related to node objects.'),
* );
* $data['tokens']['node']['title'] = array(
* 'name' => t('Title'),
* 'description' => t('The title of the current node.'),
* );
* $data['tokens']['node']['author'] = array(
* 'name' => t('Author'),
* 'description' => t('The author of the current node.'),
* 'type' => 'user',
* );
* @endcode
* @return
* An associative array of token information, grouped by token type.
*/
function
token_info
()
{
$data
=
&
drupal_static
(
__FUNCTION__
);
if
(
!
isset
(
$data
))
{
$data
=
module_invoke_all
(
'token_info'
);
drupal_alter
(
'token_info'
,
$data
);
}
return
$data
;
}
modules/comment/comment.info
View file @
40003c83
...
...
@@ -10,3 +10,4 @@ files[] = comment.admin.inc
files
[]
=
comment
.
pages
.
inc
files
[]
=
comment
.
install
files
[]
=
comment
.
test
files
[]
=
comment
.
tokens
.
inc
modules/comment/comment.tokens.inc
0 → 100644
View file @
40003c83
<?php
// $Id$
/**
* @file
* Builds placeholder replacement tokens for comment-related data.
*/
/**
* Implement hook_token_info().
*/
function
comment_token_info
()
{
$type
=
array
(
'name'
=>
t
(
'Comments'
),
'description'
=>
t
(
'Tokens for comments posted on the site.'
),
'needs-data'
=>
'comment'
,
);
// Comment-related tokens for nodes
$node
[
'comment-count'
]
=
array
(
'name'
=>
t
(
"Comment count"
),
'description'
=>
t
(
"The number of comments posted on a node."
),
);
$node
[
'comment-count-new'
]
=
array
(
'name'
=>
t
(
"New comment count"
),
'description'
=>
t
(
"The number of comments posted on a node since the reader last viewed it."
),
);
// Core comment tokens
$comment
[
'cid'
]
=
array
(
'name'
=>
t
(
"Comment ID"
),
'description'
=>
t
(
"The unique ID of the comment."
),
);
$comment
[
'pid'
]
=
array
(
'name'
=>
t
(
"Parent ID"
),
'description'
=>
t
(
"The unique ID of the comment's parent, if comment threading is active."
),
);
$comment
[
'nid'
]
=
array
(
'name'
=>
t
(
"Node ID"
),
'description'
=>
t
(
"The unique ID of the node the comment was posted to."
),
);
$comment
[
'uid'
]
=
array
(
'name'
=>
t
(
"User ID"
),
'description'
=>
t
(
"The unique ID of the user who posted the comment."
),
);
$comment
[
'hostname'
]
=
array
(
'name'
=>
t
(
"IP Address"
),
'description'
=>
t
(
"The IP address of the computer the comment was posted from."
),
);
$comment
[
'name'
]
=
array
(
'name'
=>
t
(
"Name"
),
'description'
=>
t
(
"The name left by the comment author."
),
);
$comment
[
'mail'
]
=
array
(
'name'
=>
t
(
"Email address"
),
'description'
=>
t
(
"The email address left by the comment author."
),
);
$comment
[
'homepage'
]
=
array
(
'name'
=>
t
(
"Home page"
),
'description'
=>
t
(
"The home page URL left by the comment author."
),
);
$comment
[
'title'
]
=
array
(
'name'
=>
t
(
"Title"
),
'description'
=>
t
(
"The title of the comment."
),
);
$comment
[
'body'
]
=
array
(
'name'
=>
t
(
"Content"
),
'description'
=>
t
(
"The formatted content of the comment itself."
),
);
$comment
[
'url'
]
=
array
(
'name'
=>
t
(
"URL"
),
'description'
=>
t
(
"The URL of the comment."
),
);
$comment
[
'edit-url'
]
=
array
(
'name'
=>
t
(
"Edit URL"
),
'description'
=>
t
(
"The URL of the comment's edit page."
),
);
// Chained tokens for comments
$comment
[
'created'
]
=
array
(
'name'
=>
t
(
"Date created"
),
'description'
=>
t
(
"The date the comment was posted."
),
'type'
=>
'date'
,
);
$comment
[
'parent'
]
=
array
(
'name'
=>
t
(
"Parent"
),
'description'
=>
t
(
"The comment's parent, if comment threading is active."
),
'type'
=>
'comment'
,
);
$comment
[
'node'
]
=
array
(
'name'
=>
t
(
"Node"
),
'description'
=>
t
(
"The node the comment was posted to."
),
'type'
=>
'node'
,
);
$comment
[
'author'
]
=
array
(
'name'
=>
t
(
"Author"
),
'description'
=>
t
(
"The author of the comment, if they were logged in."
),
'type'
=>
'user'
,
);
return
array
(
'types'
=>
array
(
'comment'
=>
$type
),
'tokens'
=>
array
(
'node'
=>
$node
,
'comment'
=>
$comment
,
),
);
}
/**
* Implement hook_tokens().
*/
function
comment_tokens
(
$type
,
$tokens
,
array
$data
=
array
(),
array
$options
=
array
())
{
$url_options
=
array
(
'absolute'
=>
TRUE
);
if
(
isset
(
$options
[
'language'
]))
{
$url_options
[
'language'
]
=
$language
;
$language_code
=
$language
->
language
;
}
else
{
$language_code
=
NULL
;
}
$sanitize
=
!
empty
(
$options
[
'sanitize'
]);
$replacements
=
array
();
if
(
$type
==
'comment'
&&
!
empty
(
$data
[
'comment'
]))
{
$comment
=
$data
[
'comment'
];
foreach
(
$tokens
as
$name
=>
$original
)
{
switch
(
$name
)
{
// Simple key values on the comment.
case
'cid'
:
$replacements
[
$original
]
=
$comment
->
cid
;
break
;
case
'nid'
:
$replacements
[
$original
]
=
$comment
->
nid
;
break
;
case
'uid'
:
$replacements
[
$original
]
=
$comment
->
uid
;
break
;
case
'pid'
:
$replacements
[
$original
]
=
$comment
->
pid
;
break
;
// Poster identity information for comments
case
'hostname'
:
$replacements
[
$original
]
=
$sanitize
?
check_plain
(
$comment
->
hostname
)
:
$comment
->
hostname
;
break
;
case
'name'
:
$name
=
(
$comment
->
uid
==
0
)
?
variable_get
(
'anonymous'
,
t
(
'Anonymous'
))
:
$comment
->
name
;
$replacements
[
$original
]
=
$sanitize
?
filter_xss
(
$name
)
:
$name
;
break
;
case
'mail'
:
if
(
$comment
->
uid
!=
0
)
{
$account
=
user_load
(
$comment
->
uid
);
$mail
=
$account
->
mail
;
}
else
{
$mail
=
$comment
->
mail
;
}
$replacements
[
$original
]
=
$sanitize
?
check_plain
(
$mail
)
:
$mail
;
break
;
case
'homepage'
:
$replacements
[
$original
]
=
$sanitize
?
filter_xss_bad_protocol
(
$comment
->
homepage
)
:
$comment
->
homepage
;
break
;
case
'title'
:
$replacements
[
$original
]
=
$sanitize
?
filter_xss
(
$comment
->
subject
)
:
$comment
->
subject
;
break
;
case
'body'
:
$replacements
[
$original
]
=
$sanitize
?
check_markup
(
$comment
->
comment
,
$comment
->
format
)
:
$replacements
[
$original
]
=
$comment
->
comment
;
break
;
// Comment related URLs.
case
'url'
:
$replacements
[
$original
]
=
url
(
'comment/'
.
$comment
->
cid
,
array
(
'absolute'
=>
TRUE
,
'fragment'
=>
'comment-'
.
$comment
->
cid
));
break
;
case
'edit-url'
:
$replacements
[
$original
]
=
url
(
'comment/edit/'
.
$comment
->
cid
,
array
(
'absolute'
=>
TRUE
));
break
;
// Default values for the chained tokens handled below.
case
'author'
:
$replacements
[
$original
]
=
$sanitize
?
filter_xss
(
$comment
->
name
)
:
$comment
->
name
;
break
;
case
'parent'
:
if
(
!
empty
(
$comment
->
pid
))
{
$parent
=
comment_load
(
$comment
->
pid
);
$replacements
[
$original
]
=
$sanitize
?
filter_xss
(
$parent
->
subject
)
:
$parent
->
subject
;
}
break
;
case
'created'
:
$replacements
[
$original
]
=
format_date
(
$comment
->
timestamp
,
'medium'
,
''
,
NULL
,
$language_code
);
break
;
case
'node'
:
$node
=
node_load
(
$comment
->
nid
);
$replacements
[
$original
]
=
$sanitize
?
filter_xss
(
$node
->
title
)
:
$node
->
title
;
break
;
}
}
// Chained token relationships.
if
(
$node_tokens
=
token_find_with_prefix
(
$tokens
,
'node'
))
{
$node
=
node_load
(
$comment
->
nid
);
$replacements
+=
token_generate
(
'node'
,
$node_tokens
,
array
(
'node'
=>
$node
),
$options
);
}
if
(
$date_tokens
=
token_find_with_prefix
(
$tokens
,
'created'
))
{
$replacements
+=
token_generate
(
'date'
,
$date_tokens
,
array
(
'date'
=>
$comment
->
timestamp
),
$options
);
}
if
((
$parent_tokens
=
token_find_with_prefix
(
$tokens
,
'parent'
))
&&
$parent
=
comment_load
(
$comment
->
pid
))
{
$replacements
+=
token_generate
(
'comment'
,
$parent_tokens
,
array
(
'comment'
=>
$parent
),
$options
);
}
if
((
$author_tokens
=
token_find_with_prefix
(
$tokens
,
'author'
))
&&
$account
=
user_load
(
$comment
->
uid
))
{
$replacements
+=
token_generate
(
'user'
,
$author_tokens
,
array
(
'user'
=>
$account
),
$options
);
}
}
elseif
(
$type
==
'node'
&
!
empty
(
$data
[
'node'
]))
{
$node
=
$data
[
'node'
];
foreach
(
$tokens
as
$name
=>
$original
)
{
switch
(
$name
)
{
case
'comment-count'
:
$replacements
[
$original
]
=
$node
->
comment_count
;
break
;
case
'comment-count-new'
:
$replacements
[
$original
]
=
comment_num_new
(
$node
->
nid
);
break
;
}
}
}
return
$replacements
;
}
modules/node/node.info
View file @
40003c83
...
...
@@ -10,4 +10,5 @@ files[] = node.admin.inc
files
[]
=
node
.
pages
.
inc
files
[]
=
node
.
install
files
[]
=
node
.
test
files
[]
=
node
.
tokens
.
inc
required
=
TRUE
modules/node/node.tokens.inc
0 → 100644
View file @
40003c83
<?php
// $Id$
/**
* @file
* Builds placeholder replacement tokens for node-related data.
*/
/**
* Implement hook_token_info().
*/
function
node_token_info
()
{
$type
=
array
(
'name'
=>
t
(
'Nodes'
),
'description'
=>
t
(
'Tokens related to individual nodes.'
),
'needs-data'
=>
'node'
,
);
// Core tokens for nodes.
$node
[
'nid'
]
=
array
(
'name'
=>
t
(
"Node ID"
),
'description'
=>
t
(
"The unique ID of the node."
),
);
$node
[
'vid'
]
=
array
(
'name'
=>
t
(
"Revision ID"
),
'description'
=>
t
(
"The unique ID of the node's latest revision."
),
);
$node
[
'tnid'
]
=
array
(
'name'
=>
t
(
"Translation set ID"
),
'description'
=>
t
(
"The unique ID of the original-language version of this node, if one exists."
),
);
$node
[
'uid'
]
=
array
(
'name'
=>
t
(
"User ID"
),
'description'
=>
t
(
"The unique ID of the user who posted the node."
),
);
$node
[
'type'
]
=
array
(
'name'
=>
t
(
"Content type"
),
'description'
=>
t
(
"The type of the node."
),
);
$node
[
'type-name'
]
=
array
(
'name'
=>
t
(
"Content type name"
),
'description'
=>
t
(
"The human-readable name of the node type."
),
);
$node
[
'title'
]
=
array
(
'name'
=>
t
(
"Title"
),
'description'
=>
t
(
"The title of the node."
),
);
$node
[
'body'
]
=
array
(
'name'
=>
t
(
"Body"
),
'description'
=>
t
(
"The main body text of the node."
),
);
$node
[
'summary'
]
=
array
(
'name'
=>
t
(
"Summary"
),
'description'
=>
t
(
"The summary of the node's main body text."
),
);
$node
[
'language'
]
=
array
(
'name'
=>
t
(
"Language"
),
'description'
=>
t
(
"The language the node is written in."
),
);
$node
[
'url'
]
=
array
(
'name'
=>
t
(
"URL"
),
'description'
=>
t
(
"The URL of the node."
),
);
$node
[
'edit-url'
]
=
array
(
'name'
=>
t
(
"Edit URL"
),
'description'
=>
t
(
"The URL of the node's edit page."
),
);
// Chained tokens for nodes.
$node
[
'created'
]
=
array
(
'name'
=>
t
(
"Date created"
),
'description'
=>
t
(
"The date the node was posted."
),
'type'
=>
'date'
,
);
$node
[
'changed'
]
=
array
(
'name'
=>
t
(
"Date changed"
),
'description'
=>
t
(
"The date the node was most recently updated."
),
'type'
=>
'date'
,
);
$node
[
'author'
]
=
array
(
'name'
=>
t
(
"Author"
),
'description'
=>
t
(
"The author of the node."
),
'type'
=>
'user'
,
);
return
array
(
'types'
=>
array
(
'node'
=>
$type
),
'tokens'
=>
array
(
'node'
=>
$node
),
);
}
/**
* Implement hook_tokens().
*/
function
node_tokens
(
$type
,
$tokens
,
array
$data
=
array
(),
array
$options
=
array
())
{
$url_options
=
array
(
'absolute'
=>
TRUE
);
if
(
isset
(
$options
[
'language'
]))
{
$url_options
[
'language'
]
=
$language
;
$language_code
=
$language
->
language
;
}
else
{
$language_code
=
NULL
;
}
$sanitize
=
!
empty
(
$options
[
'sanitize'
]);
$replacements
=
array
();