diff --git a/includes/bootstrap.inc b/includes/bootstrap.inc index d37a98e..ed147a8 100644 --- a/includes/bootstrap.inc +++ b/includes/bootstrap.inc @@ -1236,15 +1236,7 @@ function drupal_unpack($obj, $field = 'data') { * A string containing the English string to translate. * @param $args * An associative array of replacements to make after translation. - * Occurrences in $string of any key in $args are replaced with the - * corresponding value, after sanitization. The sanitization function depends - * on the first character of the key: - * - !variable: Inserted as is. Use this for text that has already been - * sanitized. - * - @variable: Escaped to HTML using check_plain(). Use this for anything - * displayed on a page on the site. - * - %variable: Escaped as a placeholder for user-submitted content using - * drupal_placeholder(), which shows up as emphasized text. + * See format_string(). * @param $options * An associative array of additional options, with the following elements: * - 'langcode' (defaults to the current language): The language code to @@ -1290,26 +1282,50 @@ function t($string, array $args = array(), array $options = array()) { return $string; } else { - // Transform arguments before inserting them. - foreach ($args as $key => $value) { - switch ($key[0]) { - case '@': - // Escaped only. - $args[$key] = check_plain($value); - break; - - case '%': - default: - // Escaped and placeholder. - $args[$key] = drupal_placeholder($value); - break; + return format_string($string, $args); + } +} - case '!': - // Pass-through. - } +/** + * Replace placeholders with sanitized values in a string. + * + * @param $string + * A string containing placeholders. + * @param $args + * An associative array of replacements to make. Occurrences in $string of + * any key in $args are replaced with the corresponding value, after + * sanitization. The sanitization function depends on the first character of + * the key: + * - !variable: Inserted as is. Use this for text that has already been + * sanitized. + * - @variable: Escaped to HTML using check_plain(). Use this for anything + * displayed on a page on the site. + * - %variable: Escaped as a placeholder for user-submitted content using + * drupal_placeholder(), which shows up as emphasized text. + * + * @see t() + * @ingroup sanitization + */ +function format_string($string, array $args = array()) { + // Transform arguments before inserting them. + foreach ($args as $key => $value) { + switch ($key[0]) { + case '@': + // Escaped only. + $args[$key] = check_plain($value); + break; + + case '%': + default: + // Escaped and placeholder. + $args[$key] = drupal_placeholder($value); + break; + + case '!': + // Pass-through. } - return strtr($string, $args); } + return strtr($string, $args); } /** diff --git a/misc/drupal.js b/misc/drupal.js index 3cebbd2..7e2cc4d 100644 --- a/misc/drupal.js +++ b/misc/drupal.js @@ -111,6 +111,8 @@ Drupal.detachBehaviors = function (context, settings, trigger) { /** * Encode special characters in a plain-text string for display as HTML. + * + * @ingroup sanitization */ Drupal.checkPlain = function (str) { var character, regex, @@ -126,6 +128,45 @@ Drupal.checkPlain = function (str) { }; /** + * Replace placeholders with sanitized values in a string. + * + * @param str + * A string with placeholders. + * @param args + * An object of replacements pairs to make. Incidences of any key in this + * array are replaced with the corresponding value. Based on the first + * character of the key, the value is escaped and/or themed: + * - !variable: inserted as is + * - @variable: escape plain text to HTML (Drupal.checkPlain) + * - %variable: escape text and theme as a placeholder for user-submitted + * content (checkPlain + Drupal.theme('placeholder')) + * + * @see Drupal.t() + * @ingroup sanitization + */ +Drupal.formatString = function(str, args) { + // Transform arguments before inserting them. + for (var key in args) { + switch (key.charAt(0)) { + // Escaped only. + case '@': + args[key] = Drupal.checkPlain(args[key]); + break; + // Pass-through. + case '!': + break; + // Escaped and placeholder. + case '%': + default: + args[key] = Drupal.theme('placeholder', args[key]); + break; + } + str = str.replace(key, args[key]); + } + return str; +} + +/** * Translate strings to the page language or a given language. * * See the documentation of the server-side t() function for further details. @@ -135,11 +176,7 @@ Drupal.checkPlain = function (str) { * @param args * An object of replacements pairs to make after translation. Incidences * of any key in this array are replaced with the corresponding value. - * Based on the first character of the key, the value is escaped and/or themed: - * - !variable: inserted as is - * - @variable: escape plain text to HTML (Drupal.checkPlain) - * - %variable: escape text and theme as a placeholder for user-submitted - * content (checkPlain + Drupal.theme('placeholder')) + * See Drupal.formatString(). * @return * The translated string. */ @@ -150,24 +187,7 @@ Drupal.t = function (str, args) { } if (args) { - // Transform arguments before inserting them. - for (var key in args) { - switch (key.charAt(0)) { - // Escaped only. - case '@': - args[key] = Drupal.checkPlain(args[key]); - break; - // Pass-through. - case '!': - break; - // Escaped and placeholder. - case '%': - default: - args[key] = Drupal.theme('placeholder', args[key]); - break; - } - str = str.replace(key, args[key]); - } + str = Drupal.formatString(str, args); } return str; }; @@ -193,11 +213,7 @@ Drupal.t = function (str, args) { * @param args * An object of replacements pairs to make after translation. Incidences * of any key in this array are replaced with the corresponding value. - * Based on the first character of the key, the value is escaped and/or themed: - * - !variable: inserted as is - * - @variable: escape plain text to HTML (Drupal.checkPlain) - * - %variable: escape text and theme as a placeholder for user-submitted - * content (checkPlain + Drupal.theme('placeholder')) + * See Drupal.formatString(). * Note that you do not need to include @count in this array. * This replacement is done automatically for the plural case. * @return diff --git a/modules/simpletest/tests/common.test b/modules/simpletest/tests/common.test index 177e457..5f69673 100644 --- a/modules/simpletest/tests/common.test +++ b/modules/simpletest/tests/common.test @@ -345,14 +345,14 @@ class CommonURLUnitTest extends DrupalWebTestCase { } /** - * Tests for the check_plain() and filter_xss() functions. + * Tests for the check_plain(), filter_xss() and format_string() functions. */ class CommonXssUnitTest extends DrupalUnitTestCase { public static function getInfo() { return array( 'name' => 'String filtering tests', - 'description' => 'Confirm that check_plain(), filter_xss(), and check_url() work correctly, including invalid multi-byte sequences.', + 'description' => 'Confirm that check_plain(), filter_xss(), format_string() and check_url() work correctly, including invalid multi-byte sequences.', 'group' => 'System', ); } @@ -386,6 +386,22 @@ class CommonXssUnitTest extends DrupalUnitTestCase { } /** + * Test t() and format_string() replacement functionality. + */ + function testFormatStringAndT() { + foreach (array('format_string', 't') as $function) { + $text = $function('Simple text'); + $this->assertEqual($text, 'Simple text', $function . ' leaves simple text alone.'); + $text = $function('Escaped text: @value', array('@value' => '