diff --git a/modules/block/block.module b/modules/block/block.module index e0cb691..90bc6a1 100644 --- a/modules/block/block.module +++ b/modules/block/block.module @@ -242,7 +242,7 @@ function block_block_save($delta = 0, $edit = array()) { function block_block_view($delta = '') { $block = db_query('SELECT body, format FROM {block_custom} WHERE bid = :bid', array(':bid' => $delta))->fetchObject(); $data['subject'] = NULL; - $data['content'] = check_markup($block->body, $block->format, '', TRUE); + $data['content'] = check_markup($block->body, $block->format, array('cache' => TRUE, 'type' => 'block', 'object' => $block)); return $data; } diff --git a/modules/book/book.test b/modules/book/book.test index cc61778..4cbc6f4 100644 --- a/modules/book/book.test +++ b/modules/book/book.test @@ -165,7 +165,7 @@ class BookTestCase extends DrupalWebTestCase { // Check printer friendly version. $this->drupalGet('book/export/html/' . $node->nid); $this->assertText($node->title, t('Printer friendly title found.')); - $this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), t('Printer friendly body found.')); + $this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format'], array('type' => 'node', 'object' => $node)), t('Printer friendly body found.')); $number++; } @@ -234,7 +234,7 @@ class BookTestCase extends DrupalWebTestCase { // Make sure each part of the book is there. foreach ($nodes as $node) { $this->assertText($node->title, t('Node title found in printer friendly version.')); - $this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format']), t('Node body found in printer friendly version.')); + $this->assertRaw(check_markup($node->body[LANGUAGE_NONE][0]['value'], $node->body[LANGUAGE_NONE][0]['format'], array('type' => 'node', 'object' => $node)), t('Node body found in printer friendly version.')); } // Make sure we can't export an unsupported format. diff --git a/modules/comment/comment.module b/modules/comment/comment.module index 60a9ca4..fb2c7f8 100644 --- a/modules/comment/comment.module +++ b/modules/comment/comment.module @@ -2158,7 +2158,7 @@ function comment_submit($comment) { // 1) Filter it into HTML // 2) Strip out all HTML tags // 3) Convert entities back to plain-text. - $comment->subject = truncate_utf8(trim(decode_entities(strip_tags(check_markup($comment->comment_body[LANGUAGE_NONE][0]['value'], $comment->comment_body[LANGUAGE_NONE][0]['format'])))), 29, TRUE); + $comment->subject = truncate_utf8(trim(decode_entities(strip_tags(check_markup($comment->comment_body[LANGUAGE_NONE][0]['value'], $comment->comment_body[LANGUAGE_NONE][0]['format'], array('type' => 'comment', 'object' => $comment))))), 29, TRUE); // Edge cases where the comment body is populated only by HTML tags will // require a default subject. if ($comment->subject == '') { diff --git a/modules/comment/comment.test b/modules/comment/comment.test index 770e01d..88fd2b5 100644 --- a/modules/comment/comment.test +++ b/modules/comment/comment.test @@ -1701,7 +1701,7 @@ class CommentTokenReplaceTestCase extends CommentHelperCase { $tests['[comment:mail]'] = check_plain($this->admin_user->mail); $tests['[comment:homepage]'] = check_url($comment->homepage); $tests['[comment:title]'] = filter_xss($comment->subject); - $tests['[comment:body]'] = _text_sanitize($instance, LANGUAGE_NONE, $comment->comment_body[LANGUAGE_NONE][0], 'value'); + $tests['[comment:body]'] = _text_sanitize($instance, LANGUAGE_NONE, 'comment', $comment, $comment->comment_body[LANGUAGE_NONE][0], 'value'); $tests['[comment:url]'] = url('comment/' . $comment->cid, $url_options + array('fragment' => 'comment-' . $comment->cid)); $tests['[comment:edit-url]'] = url('comment/' . $comment->cid . '/edit', $url_options); $tests['[comment:created:since]'] = format_interval(REQUEST_TIME - $comment->created, 2, $language->language); diff --git a/modules/comment/comment.tokens.inc b/modules/comment/comment.tokens.inc index d62b7e2..2194d7c 100644 --- a/modules/comment/comment.tokens.inc +++ b/modules/comment/comment.tokens.inc @@ -157,7 +157,7 @@ function comment_tokens($type, $tokens, array $data = array(), array $options = case 'body': $item = $comment->comment_body[LANGUAGE_NONE][0]; $instance = field_info_instance('comment', 'body', 'comment_body'); - $replacements[$original] = $sanitize ? _text_sanitize($instance, LANGUAGE_NONE, $item, 'value') : $item['value']; + $replacements[$original] = $sanitize ? _text_sanitize($instance, LANGUAGE_NONE, 'comment', $comment, $item, 'value') : $item['value']; break; // Comment related URLs. diff --git a/modules/field/modules/text/text.module b/modules/field/modules/text/text.module index 89c605c..1fe8f99 100644 --- a/modules/field/modules/text/text.module +++ b/modules/field/modules/text/text.module @@ -156,9 +156,9 @@ function text_field_load($entity_type, $entities, $field, $instances, $langcode, // Only process items with a cacheable format, the rest will be handled // by formatters if needed. if (empty($instances[$id]['settings']['text_processing']) || filter_format_allowcache($item['format'])) { - $items[$id][$delta]['safe_value'] = isset($item['value']) ? _text_sanitize($instances[$id], $langcode, $item, 'value') : ''; + $items[$id][$delta]['safe_value'] = isset($item['value']) ? _text_sanitize($instances[$id], $langcode, $entity_type, $entity, $item, 'value') : ''; if ($field['type'] == 'text_with_summary') { - $items[$id][$delta]['safe_summary'] = isset($item['summary']) ? _text_sanitize($instances[$id], $langcode, $item, 'summary') : ''; + $items[$id][$delta]['safe_summary'] = isset($item['summary']) ? _text_sanitize($instances[$id], $langcode, $entity_type, $entity, $item, 'summary') : ''; } } } @@ -261,7 +261,7 @@ function text_field_formatter_view($entity_type, $entity, $field, $instance, $la case 'text_default': case 'text_trimmed': foreach ($items as $delta => $item) { - $output = _text_sanitize($instance, $langcode, $item, 'value'); + $output = _text_sanitize($instance, $langcode, $entity_type, $entity, $item, 'value'); if ($display['type'] == 'text_trimmed') { $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']); } @@ -272,10 +272,10 @@ function text_field_formatter_view($entity_type, $entity, $field, $instance, $la case 'text_summary_or_trimmed': foreach ($items as $delta => $item) { if (!empty($item['summary'])) { - $output = _text_sanitize($instance, $langcode, $item, 'summary'); + $output = _text_sanitize($instance, $langcode, $entity_type, $entity, $item, 'summary'); } else { - $output = _text_sanitize($instance, $langcode, $item, 'value'); + $output = _text_sanitize($instance, $langcode, $entity_type, $entity, $item, 'value'); $output = text_summary($output, $instance['settings']['text_processing'] ? $item['format'] : NULL, $display['settings']['trim_length']); } $element[$delta] = array('#markup' => $output); @@ -301,7 +301,11 @@ function text_field_formatter_view($entity_type, $entity, $field, $instance, $la * @param $instance * The instance definition. * @param $langcode - * The language associated to $item. + * The language associated to $item. + * @param $entity_type + * The entity type to sanitize, used for contextual data. + * @param $entity + * The entity to sanitize, used for contextual data. * @param $item * The field value to sanitize. * @param $column @@ -310,13 +314,13 @@ function text_field_formatter_view($entity_type, $entity, $field, $instance, $la * @return * The sanitized string. */ -function _text_sanitize($instance, $langcode, $item, $column) { +function _text_sanitize($instance, $langcode, $entity_type, $entity, $item, $column) { // If the value uses a cacheable text format, text_field_load() precomputes // the sanitized string. if (isset($item["safe_$column"])) { return $item["safe_$column"]; } - return $instance['settings']['text_processing'] ? check_markup($item[$column], $item['format'], $langcode) : check_plain($item[$column]); + return $instance['settings']['text_processing'] ? check_markup($item[$column], $item['format'], array('langcode' => $langcode, 'type' => $entity_type, 'object' => $entity)) : check_plain($item[$column]); } /** diff --git a/modules/filter/filter.api.php b/modules/filter/filter.api.php index 6675e4a..13fb27a 100644 --- a/modules/filter/filter.api.php +++ b/modules/filter/filter.api.php @@ -65,6 +65,11 @@ * details. * - process callback: (required) The name the function that performs the * actual filtering. See hook_filter_FILTER_process() for details. + * - 'context callback': The name of a function that extracts contextual data + * from the object of the filtering. For example, a filter that uses the + * name of the author of a node would use this to provide the name of the + * author as context to the filter. This function will provide additional + * contextual data to the prepare and process callbacks. * - cache (default TRUE): Specifies whether the filtered text can be cached. * Note that setting this to FALSE makes the entire text format not * cacheable, which may have an impact on the site's overall performance. @@ -189,8 +194,9 @@ function hook_filter_FILTER_settings($form, &$form_state, $filter, $format, $def * The filter object containing settings for the given format. * @param $format * The text format object assigned to the text to be filtered. - * @param $langcode - * The language code of the text to be filtered. + * @param $context + * The contextual data for the text to be filtered, including the + * language code and any context provided in a 'context callback'. * @param $cache * A Boolean indicating whether the filtered text is going to be cached in * {cache_filter}. @@ -200,7 +206,7 @@ function hook_filter_FILTER_settings($form, &$form_state, $filter, $format, $def * @return * The prepared, escaped text. */ -function hook_filter_FILTER_prepare($text, $filter, $format, $langcode, $cache, $cache_id) { +function hook_filter_FILTER_prepare($text, $filter, $format, $context, $cache, $cache_id) { // Escape and tags. $text = preg_replace('|(.+?)|se', "[codefilter_code]$1[/codefilter_code]", $text); return $text; @@ -222,8 +228,9 @@ function hook_filter_FILTER_prepare($text, $filter, $format, $langcode, $cache, * The filter object containing settings for the given format. * @param $format * The text format object assigned to the text to be filtered. - * @param $langcode - * The language code of the text to be filtered. + * @param $context + * The contextual data for the text to be filtered, including the + * language code and any context provided in a 'context callback'. * @param $cache * A Boolean indicating whether the filtered text is going to be cached in * {cache_filter}. @@ -233,13 +240,52 @@ function hook_filter_FILTER_prepare($text, $filter, $format, $langcode, $cache, * @return * The filtered text. */ -function hook_filter_FILTER_process($text, $filter, $format, $langcode, $cache, $cache_id) { +function hook_filter_FILTER_process($text, $filter, $format, $context, $cache, $cache_id) { $text = preg_replace('|\[codefilter_code\](.+?)\[/codefilter_code\]|se', "
$1
", $text); return $text; } /** + * Context callback for hook_filter_info(). + * + * Note: This is not really a hook. The function name is manually specified via + * 'context callback' in hook_filter_info(), with this recommended callback + * name pattern. It is called from check_markup(). + * + * See hook_filter_info() for a description of the filtering process. This step + * is where the contextual data is generated for the filter. + * + * @param $text + * The text string to be filtered. + * @param $filter + * The filter object containing settings for the given format. + * @param $format + * The text format object assigned to the text to be filtered. + * @param $type + * The type of object being filtered. + * @param $object + * The object variable of the object being filtered. + * + * @return + * An associative array of contextual data from the object that, upon + * changing, will require the filter to be rerun. + */ +function hook_filter_FILTER_context($text, $filter, $format, $type, $object) { + switch ($type) { + case 'node': + case 'comment': + $account = user_load($object->uid); + // Filter will have to recache when the account name changes, either when + // the author of the comment/node changes or when the user changes their + // name. + return array( + 'user_name' => $account->name, + ); + } +} + +/** * Tips callback for hook_filter_info(). * * Note: This is not really a hook. The function name is manually specified via diff --git a/modules/filter/filter.module b/modules/filter/filter.module index 773fa80..32dae4c 100644 --- a/modules/filter/filter.module +++ b/modules/filter/filter.module @@ -704,18 +704,31 @@ function filter_list_format($format_id) { * @param $format_id * The format id of the text to be filtered. If no format is assigned, the * fallback format will be used. - * @param $langcode - * Optional: the language code of the text to be filtered, e.g. 'en' for - * English. This allows filters to be language aware so language specific - * text replacement can be implemented. - * @param $cache - * Boolean whether to cache the filtered output in the {cache_filter} table. - * The caller may set this to FALSE when the output is already cached - * elsewhere to avoid duplicate cache lookups and storage. + * @param $options + * An associative array of options, used to override the defaults. Possible + * values include: + * - cache: Boolean whether to cache the filtered output in the + * {cache_filter} table. The caller may set this to TRUE when the output + * is not being cached elsewhere. Otherwise, this should be left as FALSE + * to avoid duplicate cache lookups and storage. Defaults to FALSE. + * - langcode: the language code of the text to be filtered, e.g. 'en' for + * English. This allows filters to be language aware so language specific + * text replacement can be implemented. Defaults to ''. + * - object: an object from which contextual data can be pulled, such as a + * node object, a user object, or a block. Defaults to NULL for no context. + * - type: a string that identifies the type of object, such as 'node', or + * 'user', or 'block'. Defaults to '' for no context. * * @ingroup sanitization */ -function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE) { +function check_markup($text, $format_id = NULL, $options = array()) { + $options += array( + 'cache' => FALSE, + 'langcode' => '', + 'object' => NULL, + 'type' => '', + ); + if (!isset($format_id)) { $format_id = filter_fallback_format(); } @@ -726,10 +739,28 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE) } // Check for a cached version of this piece of text. - $cache = $cache && !empty($format->cache); + $cache = $options['cache'] && !empty($format->cache); $cache_id = ''; + + // Get a complete list of filters, ordered properly. + $filters = filter_list_format($format->format); + $filter_info = filter_get_filters(); + + $context = array( + 'langcode' => $options['langcode'], + ); + foreach ($filters as $name => $filter) { + if ($filter->status && isset($filter_info[$name]['context callback']) && function_exists($filter_info[$name]['context callback'])) { + $function = $filter_info[$name]['context callback']; + $result = $function($text, $filter, $format, $options['type'], $options['object']); + if (is_array($result)) { + $context += $result; + } + } + } + if ($cache) { - $cache_id = $format->format . ':' . $langcode . ':' . hash('sha256', $text); + $cache_id = $format->format . ':' . hash('sha256', $text) . ':' . hash('sha256', serialize($context)); if ($cached = cache_get($cache_id, 'cache_filter')) { return $cached->data; } @@ -739,15 +770,11 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE) // need to deal with one possibility. $text = str_replace(array("\r\n", "\r"), "\n", $text); - // Get a complete list of filters, ordered properly. - $filters = filter_list_format($format->format); - $filter_info = filter_get_filters(); - // Give filters the chance to escape HTML-like data such as code or formulas. foreach ($filters as $name => $filter) { if ($filter->status && isset($filter_info[$name]['prepare callback']) && function_exists($filter_info[$name]['prepare callback'])) { $function = $filter_info[$name]['prepare callback']; - $text = $function($text, $filter, $format, $langcode, $cache, $cache_id); + $text = $function($text, $filter, $format, $context, $cache, $cache_id); } } @@ -755,12 +782,14 @@ function check_markup($text, $format_id = NULL, $langcode = '', $cache = FALSE) foreach ($filters as $name => $filter) { if ($filter->status && isset($filter_info[$name]['process callback']) && function_exists($filter_info[$name]['process callback'])) { $function = $filter_info[$name]['process callback']; - $text = $function($text, $filter, $format, $langcode, $cache, $cache_id); + $text = $function($text, $filter, $format, $context, $cache, $cache_id); } } // Store in cache with a minimum expiration time of 1 day. if ($cache) { + // Avoid flooding the cache_filter table with outdated entries. + cache_clear_all($format->format . ':' . hash('sha256', $text) . ':', 'cache_filter', TRUE); cache_set($cache_id, $text, 'cache_filter', REQUEST_TIME + (60 * 60 * 24)); } diff --git a/modules/filter/filter.test b/modules/filter/filter.test index a3d1bde..584d4e8 100644 --- a/modules/filter/filter.test +++ b/modules/filter/filter.test @@ -1750,3 +1750,80 @@ class FilterHooksTestCase extends DrupalWebTestCase { } } +/** + * Tests the filter system's contextual data handling. + */ +class FilterContextTestCase extends DrupalWebTestCase { + protected $format_id; + + public static function getInfo() { + return array( + 'name' => 'Filter context', + 'description' => 'Tests the ability of the filtering system to incorporate textual data.', + 'group' => 'Filter', + ); + } + + function setUp() { + parent::setUp('filter_test_2'); + $admin_user = $this->drupalCreateUser(array('administer filters')); + $this->drupalLogin($admin_user); + $format_name = $this->randomName(); + $this->drupalPost('admin/config/content/formats/add', array('name' => $format_name, 'format' => strtolower($format_name), 'filters[filter_test_2_context][status]' => TRUE), t('Save configuration')); + $this->drupalLogout(); + $this->format_id = db_query("SELECT format FROM {filter_format} WHERE name = :name", array(':name' => $format_name))->fetchField(); + filter_formats_reset(); + } + + /** + * Test to make sure that filters are able to use contextual data, and that + * the caching system works properly with contextual data changes. + */ + function testFilterContext() { + $text1 = $this->randomName(); + $text2 = $this->randomName(); + $var1 = (object)array('one' => $this->randomName()); + $var2 = (object)array('one' => $this->randomName()); + + // Text 1 with object 1. + $result1 = check_markup($text1, $this->format_id, array('cache' => TRUE, 'type' => 'test', 'object' => $var1)); + list($success, $text, $var, $rand1) = explode('/', $result1); + $this->assertEqual($success, 'Success', 'Context successfully passed.'); + $this->assertEqual($text, $text1, 'Text matched.'); + $this->assertEqual($var, $var1->one, 'Contextual variable matched.'); + $rand_store[] = $rand1; + + // Text 1 with object 1 again - make sure random variable stays the same. + $result2 = check_markup($text1, $this->format_id, array('cache' => TRUE, 'type' => 'test', 'object' => $var1)); + list($success, $text, $var, $rand2) = explode('/', $result2); + $this->assertEqual($success, 'Success', 'Context successfully passed.'); + $this->assertEqual($text, $text1, 'Text matched.'); + $this->assertEqual($var, $var1->one, 'Contextual variable matched.'); + $this->assertEqual($rand1, $rand2, 'Filter with contextual data was successfully cached.'); + + // Text 1 with object 2 - make sure cache invalidates. + $result3 = check_markup($text1, $this->format_id, array('cache' => TRUE, 'type' => 'test', 'object' => $var2)); + list($success, $text, $var, $rand3) = explode('/', $result3); + $this->assertEqual($success, 'Success', 'Context successfully passed.'); + $this->assertEqual($text, $text1, 'Text matched.'); + $this->assertEqual($var, $var2->one, 'Contextual variable matched.'); + $this->assertNotEqual($rand1, $rand3, 'Filter with contextual data successfully invalidated cache.'); + + // Back to text 1 with object 1 - cache should still be valid. + $result4 = check_markup($text1, $this->format_id, array('cache' => TRUE, 'type' => 'test', 'object' => $var1)); + list($success, $text, $var, $rand4) = explode('/', $result4); + $this->assertEqual($success, 'Success', 'Context successfully passed.'); + $this->assertEqual($text, $text1, 'Text matched.'); + $this->assertEqual($var, $var1->one, 'Contextual variable matched.'); + $this->assertEqual($rand1, $rand4, 'Filter with contextual data did not have cache invalidated.'); + + // Text 2 with object 2 - should be entirely new data. + $result5 = check_markup($text2, $this->format_id, array('cache' => TRUE, 'type' => 'test', 'object' => $var2)); + list($success, $text, $var, $rand5) = explode('/', $result5); + $this->assertEqual($success, 'Success', 'Context successfully passed.'); + $this->assertEqual($text, $text2, 'Text matched.'); + $this->assertEqual($var, $var2->one, 'Contextual variable matched.'); + $this->assertNotEqual($rand1, $rand5, 'No cache mix-up.'); + $this->assertNotEqual($rand3, $rand5, 'No cache mix-up.'); + } +} diff --git a/modules/node/node.api.php b/modules/node/node.api.php index 3e8029c..fa86a1f 100644 --- a/modules/node/node.api.php +++ b/modules/node/node.api.php @@ -709,7 +709,7 @@ function hook_node_update_index($node) { $text = ''; $comments = db_query('SELECT subject, comment, format FROM {comment} WHERE nid = :nid AND status = :status', array(':nid' => $node->nid, ':status' => COMMENT_PUBLISHED)); foreach ($comments as $comment) { - $text .= '

' . check_plain($comment->subject) . '

' . check_markup($comment->comment, $comment->format, '', TRUE); + $text .= '

' . check_plain($comment->subject) . '

' . check_markup($comment->comment, $comment->format, array('cache' => TRUE, 'type' => 'comment', 'object' => $comment)); } return $text; } diff --git a/modules/node/node.test b/modules/node/node.test index 8a871c0..3925740 100644 --- a/modules/node/node.test +++ b/modules/node/node.test @@ -2115,8 +2115,8 @@ class NodeTokenReplaceTestCase extends DrupalWebTestCase { $tests['[node:type]'] = 'article'; $tests['[node:type-name]'] = 'Article'; $tests['[node:title]'] = check_plain($node->title); - $tests['[node:body]'] = _text_sanitize($instance, $node->language, $node->body[$node->language][0], 'value'); - $tests['[node:summary]'] = _text_sanitize($instance, $node->language, $node->body[$node->language][0], 'summary'); + $tests['[node:body]'] = _text_sanitize($instance, $node->language, 'node', $node, $node->body[$node->language][0], 'value'); + $tests['[node:summary]'] = _text_sanitize($instance, $node->language, 'node', $node, $node->body[$node->language][0], 'summary'); $tests['[node:language]'] = check_plain($node->language); $tests['[node:url]'] = url('node/' . $node->nid, $url_options); $tests['[node:edit-url]'] = url('node/' . $node->nid . '/edit', $url_options); diff --git a/modules/node/node.tokens.inc b/modules/node/node.tokens.inc index 80dbda5..a56619f 100644 --- a/modules/node/node.tokens.inc +++ b/modules/node/node.tokens.inc @@ -139,7 +139,7 @@ function node_tokens($type, $tokens, array $data = array(), array $options = arr $item = $node->body[$node->language][0]; $column = ($name == 'body') ? 'value' : 'summary'; $instance = field_info_instance('node', 'body', $node->type); - $replacements[$original] = $sanitize ? _text_sanitize($instance, $node->language, $item, $column) : $item[$column]; + $replacements[$original] = $sanitize ? _text_sanitize($instance, $node->language, 'node', $node, $item, $column) : $item[$column]; } break; diff --git a/modules/profile/profile.module b/modules/profile/profile.module index 2374fe8..6d6318e 100644 --- a/modules/profile/profile.module +++ b/modules/profile/profile.module @@ -286,7 +286,7 @@ function profile_view_field($account, $field) { if (isset($account->{$field->name}) && $value = $account->{$field->name}) { switch ($field->type) { case 'textarea': - return check_markup($value, filter_default_format($account), '', TRUE); + return check_markup($value, filter_default_format($account), array('cache' => TRUE, 'type' => 'user', 'object' => $account)); case 'textfield': case 'selection': return $browse ? l($value, 'profile/' . $field->name . '/' . $value) : check_plain($value); diff --git a/modules/simpletest/tests/filter_test_2.info b/modules/simpletest/tests/filter_test_2.info new file mode 100644 index 0000000..dee13ae --- /dev/null +++ b/modules/simpletest/tests/filter_test_2.info @@ -0,0 +1,8 @@ +; $Id$ +name = Filter test module (2) +description = Tests filter system contextual data handling. +package = Testing +version = VERSION +core = 7.x +files[] = filter_test_2.module +hidden = TRUE diff --git a/modules/simpletest/tests/filter_test_2.module b/modules/simpletest/tests/filter_test_2.module new file mode 100644 index 0000000..f30f70c --- /dev/null +++ b/modules/simpletest/tests/filter_test_2.module @@ -0,0 +1,43 @@ + 'Contextual-data filter', + 'description' => 'Displays based on contextual data.', + 'process callback' => '_filter_test_2_process', + 'context callback' => '_filter_test_2_context', + ); + return $filters; +} + +/** + * Filter process callback. + */ +function _filter_test_2_process($text, $filter, $format, $context, $cache, $cache_id) { + if (isset($context['filter_test_2'])) { + return 'Success/' . $text . '/' . $context['filter_test_2'] . '/' . mt_rand(); + } + else { + return 'Failure/' . $text . '//' . mt_rand(); + } +} + +/** + * Filter context callback. + */ +function _filter_test_2_context($text, $filter, $format, $type, $object) { + if ($type == 'test' && !empty($object->one)) { + return array( + 'filter_test_2' => $object->one, + ); + } +} diff --git a/modules/taxonomy/taxonomy.module b/modules/taxonomy/taxonomy.module index 50d2fd6..a8aa618 100644 --- a/modules/taxonomy/taxonomy.module +++ b/modules/taxonomy/taxonomy.module @@ -697,7 +697,7 @@ function taxonomy_term_view($term, $view_mode = 'full', $langcode = NULL) { // Add term description if the term has one. if (!empty($term->description)) { $build['description'] = array( - '#markup' => check_markup($term->description, $term->format, '', TRUE), + '#markup' => check_markup($term->description, $term->format, array('cache' => TRUE, 'type' => 'taxonomy_term', 'object' => $term)), '#weight' => 0, '#prefix' => '
', '#suffix' => '
', diff --git a/modules/taxonomy/taxonomy.pages.inc b/modules/taxonomy/taxonomy.pages.inc index 3aed290..9016e8d 100644 --- a/modules/taxonomy/taxonomy.pages.inc +++ b/modules/taxonomy/taxonomy.pages.inc @@ -67,7 +67,7 @@ function taxonomy_term_feed($term) { $channel['title'] = variable_get('site_name', 'Drupal') . ' - ' . $term->name; // Only display the description if we have a single term, to avoid clutter and confusion. // HTML will be removed from feed description. - $channel['description'] = check_markup($term->description, $term->format, '', TRUE); + $channel['description'] = check_markup($term->description, $term->format, array('cache' => TRUE, 'type' => 'taxonomy_term', 'object' => $term)); $nids = taxonomy_select_nodes($term->tid, FALSE, variable_get('feed_default_items', 10)); node_feed($nids, $channel); diff --git a/modules/taxonomy/taxonomy.test b/modules/taxonomy/taxonomy.test index 1fd47f5..278573a 100644 --- a/modules/taxonomy/taxonomy.test +++ b/modules/taxonomy/taxonomy.test @@ -1099,7 +1099,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { $tests = array(); $tests['[term:tid]'] = $term1->tid; $tests['[term:name]'] = check_plain($term1->name); - $tests['[term:description]'] = check_markup($term1->description, $term1->format); + $tests['[term:description]'] = check_markup($term1->description, $term1->format, array('type' => 'taxonomy_term', 'object' => $term1)); $tests['[term:url]'] = url('taxonomy/term/' . $term1->tid, array('absolute' => TRUE)); $tests['[term:node-count]'] = 0; $tests['[term:parent:name]'] = '[term:parent:name]'; @@ -1114,7 +1114,7 @@ class TaxonomyTokenReplaceTestCase extends TaxonomyWebTestCase { $tests = array(); $tests['[term:tid]'] = $term2->tid; $tests['[term:name]'] = check_plain($term2->name); - $tests['[term:description]'] = check_markup($term2->description, $term2->format); + $tests['[term:description]'] = check_markup($term2->description, $term2->format, array('type' => 'taxonomy_term', 'object' => $term2)); $tests['[term:url]'] = url('taxonomy/term/' . $term2->tid, array('absolute' => TRUE)); $tests['[term:node-count]'] = 1; $tests['[term:parent:name]'] = check_plain($term1->name); diff --git a/modules/taxonomy/taxonomy.tokens.inc b/modules/taxonomy/taxonomy.tokens.inc index f8ae457..40a7f0c 100644 --- a/modules/taxonomy/taxonomy.tokens.inc +++ b/modules/taxonomy/taxonomy.tokens.inc @@ -106,7 +106,7 @@ function taxonomy_tokens($type, $tokens, array $data = array(), array $options = break; case 'description': - $replacements[$original] = $sanitize ? check_markup($term->description, $term->format, '', TRUE) : $term->description; + $replacements[$original] = $sanitize ? check_markup($term->description, $term->format, array('cache' => TRUE, 'type' => 'taxonomy_term', 'object' => $term)) : $term->description; break; case 'url': diff --git a/modules/user/user.module b/modules/user/user.module index de72baf..02ac31f 100644 --- a/modules/user/user.module +++ b/modules/user/user.module @@ -3341,7 +3341,7 @@ function user_comment_view($comment) { // hypothetical process of loading, viewing, and saving will hijack the // stored data. Consider renaming to $comment->signature_safe or similar // here and elsewhere in Drupal 8. - $comment->signature = check_markup($comment->signature, $comment->signature_format, '', TRUE); + $comment->signature = check_markup($comment->signature, $comment->signature_format, array('cache' => TRUE, 'type' => 'comment', 'object' => $comment)); } else { $comment->signature = ''; diff --git a/modules/user/user.test b/modules/user/user.test index 6ecbfac..a304ef0 100644 --- a/modules/user/user.test +++ b/modules/user/user.test @@ -1754,7 +1754,7 @@ class UserSignatureTestCase extends DrupalWebTestCase { // Assert that the signature did not make it through unfiltered. $this->drupalGet('node/' . $node->nid); $this->assertNoRaw($signature_text, 'Unfiltered signature text not found.'); - $this->assertRaw(check_markup($signature_text, $this->plain_text_format->format), 'Filtered signature text found.'); + $this->assertRaw(check_markup($signature_text, $this->plain_text_format->format, array('type' => 'comment', 'object' => comment_load($comment_id))), 'Filtered signature text found.'); } }