Commit 031a6876 authored by webchick's avatar webchick

#318636 by effulgentsia, sun, Damien Tournoud, Xano, and jrchamp: Make l() themable.

parent 15d972d1
......@@ -2683,6 +2683,7 @@ function drupal_attributes(array $attributes = array()) {
*/
function l($text, $path, array $options = array()) {
global $language_url;
static $use_theme = NULL;
// Merge in defaults.
$options += array(
......@@ -2702,6 +2703,35 @@ function l($text, $path, array $options = array()) {
$options['attributes']['title'] = strip_tags($options['attributes']['title']);
}
// Determine if rendering of the link is to be done with a theme function
// or the inline default. Inline is faster, but if the theme system has been
// loaded and a module or theme implements a preprocess or process function
// or overrides the theme_link() function, then invoke theme(). Preliminary
// benchmarks indicate that invoking theme() can slow down the l() function
// by 20% or more, and that some of the link-heavy Drupal pages spend more
// than 10% of the total page request time in the l() function.
if (!isset($use_theme) && function_exists('theme')) {
// Allow edge cases to prevent theme initialization and force inline link
// rendering.
if (variable_get('theme_link', TRUE)) {
drupal_theme_initialize();
$registry = theme_get_registry();
// We don't want to duplicate functionality that's in theme(), so any
// hint of a module or theme doing anything at all special with the 'link'
// theme hook should simply result in theme() being called. This includes
// the overriding of theme_link() with an alternate function or template,
// the presence of preprocess or process functions, or the presence of
// include files.
$use_theme = !isset($registry['link']['function']) || ($registry['link']['function'] != 'theme_link');
$use_theme = $use_theme || !empty($registry['link']['preprocess functions']) || !empty($registry['link']['process functions']) || !empty($registry['link']['includes']);
}
else {
$use_theme = FALSE;
}
}
if ($use_theme) {
return theme('link', array('text' => $text, 'path' => $path, 'options' => $options));
}
return '<a href="' . check_plain(url($path, $options)) . '"' . drupal_attributes($options['attributes']) . '>' . ($options['html'] ? $text : check_plain($text)) . '</a>';
}
......@@ -5348,6 +5378,9 @@ function drupal_common_theme() {
'status_messages' => array(
'variables' => array('display' => NULL),
),
'link' => array(
'variables' => array('text' => NULL, 'path' => NULL, 'options' => array()),
),
'links' => array(
'variables' => array('links' => NULL, 'attributes' => array('class' => array('links')), 'heading' => array()),
),
......
......@@ -1369,6 +1369,31 @@ function theme_status_messages($variables) {
return $output;
}
/**
* Return a themed link.
*
* All Drupal code that outputs a link should call the l() function. That
* function performs some initial preprocessing, and then, if necessary,
* calls theme('link') for rendering the anchor tag.
*
* To optimize performance for sites that don't need custom theming of links,
* the l() function includes an inline copy of this function, and uses that copy
* if none of the enabled modules or the active theme implement any preprocess
* or process functions or override this theme implementation.
*
* @param $variables
* An associative array containing the keys 'text', 'path', and 'options'.
* See the l() function for information about these variables.
*
* @return
* An HTML string containing a link to the given path.
*
* @see l()
*/
function theme_link($variables) {
return '<a href="' . check_plain(url($variables['path'], $variables['options'])) . '"' . drupal_attributes($variables['options']['attributes']) . '>' . ($variables['options']['html'] ? $variables['text'] : check_plain($variables['text'])) . '</a>';
}
/**
* Return a themed set of links.
*
......
......@@ -70,9 +70,23 @@ class CommonURLUnitTest extends DrupalUnitTestCase {
* Confirm that invalid text given as $path is filtered.
*/
function testLXSS() {
global $conf;
$text = $this->randomName();
$path = "<SCRIPT>alert('XSS')</SCRIPT>";
// Regardless of whether there is a theme override of theme_link() or not,
// unless the 'theme_link' configuration variable is FALSE, l() will
// attempt to initialize the theme system in order to determine if
// the link needs to be themed. However, drupal_theme_initialize() requires
// a database query, which doesn't work in the context of unit tests,
// because simpletest sets up a table prefix, but doesn't generate the
// corresponding prefixed tables. We need to either circumvent theme system
// initialization, or make CommonURLUnitTest inherit from DrupalWebTestCase.
// Since our goal in this unit test is specifically to test the default
// implementation, we choose the former.
$theme_link_saved = isset($conf['theme_link']) ? $conf['theme_link'] : NULL;
$conf['theme_link'] = FALSE;
$link = l($text, $path);
$conf['theme_link'] = $theme_link_saved;
$sanitized_path = check_url(url($path));
$this->assertTrue(strpos($link, $sanitized_path) !== FALSE, t('XSS attack @path was filtered', array('@path' => $path)));
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment