core/includes/common.inc | 2 +-
core/modules/comment/comment.module | 8 +-
.../lib/Drupal/comment/CommentViewBuilder.php | 4 +-
.../editor/Plugin/Filter/EditorFileReference.php | 12 +-
core/modules/filter/filter.module | 47 ++---
.../lib/Drupal/filter/FilterProcessResult.php | 204 +++++++++++++++++++++
.../Drupal/filter/Plugin/Filter/FilterAutoP.php | 3 +-
.../Drupal/filter/Plugin/Filter/FilterCaption.php | 18 +-
.../lib/Drupal/filter/Plugin/Filter/FilterHtml.php | 3 +-
.../filter/Plugin/Filter/FilterHtmlCorrector.php | 3 +-
.../filter/Plugin/Filter/FilterHtmlEscape.php | 3 +-
.../filter/Plugin/Filter/FilterHtmlImageSecure.php | 3 +-
.../lib/Drupal/filter/Plugin/Filter/FilterNull.php | 3 +-
.../lib/Drupal/filter/Plugin/Filter/FilterUrl.php | 3 +-
.../lib/Drupal/filter/Plugin/FilterInterface.php | 23 +--
.../lib/Drupal/filter/Tests/FilterAPITest.php | 7 +
.../lib/Drupal/filter/Tests/FilterUnitTest.php | 81 ++++----
.../filter_test/Plugin/Filter/FilterTestAssets.php | 14 +-
.../Plugin/Filter/FilterTestCacheTags.php | 12 +-
.../Plugin/Filter/FilterTestPostRenderCache.php | 16 +-
.../Plugin/Filter/FilterTestReplace.php | 3 +-
.../Filter/FilterTestRestrictTagsAndAttributes.php | 3 +-
.../Field/FieldFormatter/TextDefaultFormatter.php | 4 +-
.../Field/FieldFormatter/TextTrimmedFormatter.php | 4 +-
.../text/Plugin/Field/FieldType/TextItemBase.php | 18 +-
.../Drupal/text/Tests/TextWithSummaryItemTest.php | 65 +++++++
26 files changed, 414 insertions(+), 152 deletions(-)
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 65aef09..2a06394 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -3705,7 +3705,7 @@ function drupal_render_cache_generate_placeholder($callback, array $context, $to
foreach ($context as $key => $value) {
$context_attribute .= $key . ':' . $value . ';';
}
- return '';
+ return '';
}
/**
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index be95620..fea8dee 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -13,6 +13,7 @@
use Drupal\comment\CommentInterface;
use Drupal\comment\Entity\Comment;
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
+use Drupal\Component\Utility\String;
use Drupal\Core\Entity\EntityInterface;
use Drupal\entity\Entity\EntityViewDisplay;
use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
@@ -22,6 +23,7 @@
use Drupal\field\FieldInstanceConfigInterface;
use Drupal\field\FieldConfigInterface;
use Drupal\file\FileInterface;
+use Drupal\filter\FilterProcessResult;
use Drupal\user\EntityOwnerInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\HttpFoundation\Request;
@@ -1359,7 +1361,11 @@ function template_preprocess_comment(&$variables) {
}
if (isset($variables['elements']['signature'])) {
- $variables['signature'] = $variables['elements']['signature']['#markup'];
+ $signature = $variables['elements']['signature']['#markup'];
+ if (!$signature instanceof FilterProcessResult) {
+ $signature = String::checkPlain($signature);
+ }
+ $variables['signature'] = (string) $signature;
unset($variables['elements']['signature']);
}
else {
diff --git a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
index 89667a7..581ed84 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
@@ -139,9 +139,7 @@ public function buildComponents(array &$build, array $entities, array $displays,
'#type' => 'processed_text',
'#text' => $account->getSignature(),
'#format' => $account->getSignatureFormat(),
- '#context' => array(
- 'langcode' => $entity->language()->getId(),
- ),
+ '#langcode' => $entity->language()->getId(),
);
// @todo Document; similar to what we saw in https://drupal.org/node/2099131
drupal_render($build[$id]['signature'], TRUE);
diff --git a/core/modules/editor/lib/Drupal/editor/Plugin/Filter/EditorFileReference.php b/core/modules/editor/lib/Drupal/editor/Plugin/Filter/EditorFileReference.php
index bfd5dd4..6306f27 100644
--- a/core/modules/editor/lib/Drupal/editor/Plugin/Filter/EditorFileReference.php
+++ b/core/modules/editor/lib/Drupal/editor/Plugin/Filter/EditorFileReference.php
@@ -9,6 +9,7 @@
use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\NestedArray;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -29,6 +30,8 @@ class EditorFileReference extends FilterBase {
* {@inheritdoc}
*/
public function process($text, $langcode) {
+ $result = new FilterProcessResult($text);
+
if (stristr($text, 'data-editor-file-uuid') !== FALSE) {
$all_file_cache_tags = array();
$dom = Html::load($text);
@@ -40,15 +43,10 @@ public function process($text, $langcode) {
$all_file_cache_tags[] = $file->getCacheTag();
}
}
- return array(
- '#markup' => $text,
- '#cache' => array(
- 'tags' => NestedArray::mergeDeepArray($all_file_cache_tags),
- ),
- );
+ $result->setCacheTags(NestedArray::mergeDeepArray($all_file_cache_tags));
}
- return $text;
+ return $result;
}
}
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index c1e91ae..ef15e48 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -103,9 +103,7 @@ function filter_element_info() {
'#text' => '',
'#format' => NULL,
'#filter_types_to_skip' => array(),
- '#context' => array(
- 'langcode' => 'und',
- ),
+ '#langcode' => 'und',
'#pre_render' => array('filter_pre_render_text'),
);
return $type;
@@ -128,10 +126,9 @@ function filter_element_info() {
* - #format: containing the machine name of the filter format to be used to
* filter the text. Defaults to the fallback format. See
* filter_fallback_format().
- * - #context: containing the following key-value pairs:
- * - 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 'und'.
+ * - #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 'und'.
* - #filter_types_to_skip: an array of filter types to skip, or an empty
* array (default) to skip no filter types. All of the format's filters will
* be applied, except for filters of the types that are marked to be skipped.
@@ -147,12 +144,13 @@ function filter_pre_render_text(array $element) {
$format_id = $element['#format'];
$filter_types_to_skip = $element['#filter_types_to_skip'];
$text = $element['#text'];
- $langcode = $element['#context']['langcode'];
+ $langcode = $element['#langcode'];
if (!isset($format_id)) {
$format_id = filter_fallback_format();
}
// If the requested text format does not exist, the text cannot be filtered.
+ /** @var \Drupal\filter\Entity\FilterFormat $format **/
if (!$format = entity_load('filter_format', $format_id)) {
watchdog('filter', 'Missing text format: %format.', array('%format' => $format_id), WATCHDOG_ALERT);
$element['#markup'] = '';
@@ -169,9 +167,11 @@ function filter_pre_render_text(array $element) {
$text = str_replace(array("\r\n", "\r"), "\n", $text);
// Get a complete list of filters, ordered properly.
+ /** @var \Drupal\filter\FilterBag $filters **/
$filters = $format->filters();
// Give filters the chance to escape HTML-like data such as code or formulas.
+ /** @var \Drupal\filter\Plugin\FilterInterface $filter **/
foreach ($filters as $filter) {
// If necessary, skip filters of a certain type.
if (in_array($filter->getType(), $filter_types_to_skip)) {
@@ -184,7 +184,7 @@ function filter_pre_render_text(array $element) {
// Perform filtering.
$all_cache_tags = array();
- $all_attached_assets = array();
+ $all_assets = array();
$all_post_render_cache_callbacks = array();
foreach ($filters as $filter) {
// If necessary, skip filters of a certain type.
@@ -193,21 +193,10 @@ function filter_pre_render_text(array $element) {
}
if ($filter->status) {
$result = $filter->process($text, $langcode);
- if (is_string($result)) {
- $text = $result;
- }
- else {
- $text = $result['#markup'];
- if (isset($result['#cache']) && isset($result['#cache']['tags'])) {
- $all_cache_tags[] = $result['#cache']['tags'];
- }
- if (isset($result['#attached'])) {
- $all_attached_assets[] = $result['#attached'];
- }
- if (isset($result['#post_render_cache'])) {
- $all_post_render_cache_callbacks[] = $result['#post_render_cache'];
- }
- }
+ $all_assets[] = $result->getAssets();
+ $all_cache_tags[] = $result->getCacheTags();
+ $all_post_render_cache_callbacks[] = $result->getPostRenderCacheCallbacks();
+ $text = $result->getProcessedText();
}
}
@@ -226,14 +215,14 @@ function filter_pre_render_text(array $element) {
// Collect all attached assets.
if (isset($element['#attached'])) {
// Prepend the original attached assets array.
- array_unshift($all_attached_assets, $element['#attached']);
+ array_unshift($all_assets, $element['#attached']);
}
- $element['#attached'] = NestedArray::mergeDeepArray($all_attached_assets);
+ $element['#attached'] = NestedArray::mergeDeepArray($all_assets);
// Collect all #post_render_cahe callbacks.
if (isset($element['#post_render_cache'])) {
// Prepend the original attached #post_render_cache array.
- array_unshift($all_attached_assets, $element['#post_render_cache']);
+ array_unshift($all_assets, $element['#post_render_cache']);
}
$element['#post_render_cache'] = NestedArray::mergeDeepArray($all_post_render_cache_callbacks);
@@ -485,9 +474,7 @@ function check_markup($text, $format_id = NULL, $langcode = 'und', $filter_types
'#text' => $text,
'#format' => $format_id,
'#filter_types_to_skip' => $filter_types_to_skip,
- '#context' => array(
- 'langcode' => $langcode
- ),
+ '#langcode' => $langcode,
);
return drupal_render($build);
}
diff --git a/core/modules/filter/lib/Drupal/filter/FilterProcessResult.php b/core/modules/filter/lib/Drupal/filter/FilterProcessResult.php
new file mode 100644
index 0000000..de40f07
--- /dev/null
+++ b/core/modules/filter/lib/Drupal/filter/FilterProcessResult.php
@@ -0,0 +1,204 @@
+setAssets(array(
+ * 'library' => array(
+ * 'filter/caption',
+ * ),
+ * ));
+ *
+ * // Associate cache tags to be invalidated by.
+ * $result->setCacheTags($node->getCacheTag());
+ *
+ * return $result;
+ * }
+ * @endcode
+ */
+class FilterProcessResult {
+
+ /**
+ * The processed text.
+ *
+ * @see \Drupal\filter\Plugin\FilterInterface::process()
+ *
+ * @var string
+ */
+ protected $processedText;
+
+ /**
+ * An array of associated assets to be attached.
+ *
+ * @see drupal_process_attached()
+ *
+ * @var array
+ */
+ protected $assets;
+
+ /**
+ * The attached cache tags.
+ *
+ * @see drupal_render_collect_cache_tags()
+ *
+ * @var array
+ */
+ protected $cacheTags;
+
+ /**
+ * The associated #post_render_cache callbacks.
+ *
+ * @see _drupal_render_process_post_render_cache()
+ *
+ * @var array
+ */
+ protected $postRenderCacheCallbacks;
+
+ /**
+ * Constructs a FilterProcessResult object.
+ *
+ * @param string $processed_text
+ * The text as processed by a text filter.
+ */
+ public function __construct($processed_text) {
+ $this->processedText = $processed_text;
+
+ $this->assets = array();
+ $this->cacheTags = array();
+ $this->postRenderCacheCallbacks = array();
+ }
+
+ /**
+ * Gets the processed text.
+ *
+ * @return string
+ */
+ public function getProcessedText() {
+ return $this->processedText;
+ }
+
+ /**
+ * Gets the processed text.
+ *
+ * @return string
+ */
+ public function __toString() {
+ return $this->getProcessedText();
+ }
+
+ /**
+ * Sets the processed text.
+ *
+ * @param string $processed_text
+ * The text as processed by a text filter.
+ *
+ * @return $this
+ */
+ public function setProcessedText($processed_text) {
+ $this->processedText = $processed_text;
+ return $this;
+ }
+
+ /**
+ * Gets cache tags associated with the processed text.
+ *
+ * @return array
+ */
+ public function getCacheTags() {
+ return $this->cacheTags;
+ }
+
+ /**
+ * Sets cache tags associated with the processed text.
+ *
+ * @param array $cache_tags
+ * The cache tags to be associated.
+ *
+ * @return $this
+ */
+ public function setCacheTags(array $cache_tags) {
+ $this->cacheTags = $cache_tags;
+ return $this;
+ }
+
+ /**
+ * Gets assets associated with the processed text.
+ *
+ * @return array
+ */
+ public function getAssets() {
+ return $this->assets;
+ }
+
+ /**
+ * Sets assets associated with the processed text.
+ *
+ * @param array $assets
+ * The associated assets to be attached.
+ *
+ * @return $this
+ */
+ public function setAssets(array $assets) {
+ $this->assets = $assets;
+ return $this;
+ }
+
+ /**
+ * Gets #post_render_cache callbacks associated with the processed text.
+ *
+ * @return array
+ */
+ public function getPostRenderCacheCallbacks() {
+ return $this->postRenderCacheCallbacks;
+ }
+
+ /**
+ * Sets #post_render_cache callbacks associated with the processed text.
+ *
+ * @param array $post_render_cache_callbacks
+ * The associated #post_render_cache callbacks to be executed.
+ *
+ * @return $this
+ */
+ public function setPostRenderCacheCallbacks(array $post_render_cache_callbacks) {
+ $this->postRenderCacheCallbacks = $post_render_cache_callbacks;
+ return $this;
+ }
+
+}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterAutoP.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterAutoP.php
index 049785a..0627527 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterAutoP.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterAutoP.php
@@ -7,6 +7,7 @@
namespace Drupal\filter\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -24,7 +25,7 @@ class FilterAutoP extends FilterBase {
* {@inheritdoc}
*/
public function process($text, $langcode) {
- return _filter_autop($text);
+ return new FilterProcessResult(_filter_autop($text));
}
/**
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php
index 94edfc5..1b7c7e1 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterCaption.php
@@ -11,6 +11,7 @@
use Drupal\Component\Utility\String;
use Drupal\Component\Utility\Unicode;
use Drupal\Component\Utility\Xss;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -29,6 +30,8 @@ class FilterCaption extends FilterBase {
* {@inheritdoc}
*/
public function process($text, $langcode) {
+ $result = new FilterProcessResult($text);
+
if (stristr($text, 'data-caption') !== FALSE || stristr($text, 'data-align') !== FALSE) {
$caption_found = FALSE;
$dom = Html::load($text);
@@ -99,21 +102,18 @@ public function process($text, $langcode) {
$node->parentNode->replaceChild($updated_node, $node);
}
- $text = Html::serialize($dom);
+ $result->setProcessedText(Html::serialize($dom));
if ($caption_found) {
- return array(
- '#markup' => $text,
- '#attached' => array(
- 'library' => array(
- 'filter/caption',
- ),
+ $result->setAssets(array(
+ 'library' => array(
+ 'filter/caption',
),
- );
+ ));
}
}
- return $text;
+ return $result;
}
/**
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtml.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtml.php
index ebb02ea..543f147 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtml.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtml.php
@@ -7,6 +7,7 @@
namespace Drupal\filter\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -59,7 +60,7 @@ public function settingsForm(array $form, array &$form_state) {
* {@inheritdoc}
*/
public function process($text, $langcode) {
- return _filter_html($text, $this);
+ return new FilterProcessResult(_filter_html($text, $this));
}
/**
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlCorrector.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlCorrector.php
index a68651a..0be711c 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlCorrector.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlCorrector.php
@@ -8,6 +8,7 @@
namespace Drupal\filter\Plugin\Filter;
use Drupal\Component\Utility\Html;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -26,7 +27,7 @@ class FilterHtmlCorrector extends FilterBase {
* {@inheritdoc}
*/
public function process($text, $langcode) {
- return Html::normalize($text);
+ return new FilterProcessResult(Html::normalize($text));
}
}
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlEscape.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlEscape.php
index fd31c9c..a508298 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlEscape.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlEscape.php
@@ -7,6 +7,7 @@
namespace Drupal\filter\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -25,7 +26,7 @@ class FilterHtmlEscape extends FilterBase {
* {@inheritdoc}
*/
public function process($text, $langcode) {
- return _filter_html_escape($text);
+ return new FilterProcessResult(_filter_html_escape($text));
}
/**
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlImageSecure.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlImageSecure.php
index acfd423..f03c75a 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlImageSecure.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterHtmlImageSecure.php
@@ -7,6 +7,7 @@
namespace Drupal\filter\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -26,7 +27,7 @@ class FilterHtmlImageSecure extends FilterBase {
* {@inheritdoc}
*/
public function process($text, $langcode) {
- return _filter_html_image_secure_process($text);
+ return new FilterProcessResult(_filter_html_image_secure_process($text));
}
/**
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterNull.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterNull.php
index d73aa58..9c30edc 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterNull.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterNull.php
@@ -7,6 +7,7 @@
namespace Drupal\filter\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -48,7 +49,7 @@ public function __construct(array $configuration, $plugin_id, $plugin_definition
* {@inheritdoc}
*/
public function process($text, $langcode) {
- return '';
+ return new FilterProcessResult('');
}
/**
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterUrl.php b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterUrl.php
index 8ca02e0..469ff41 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterUrl.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/Filter/FilterUrl.php
@@ -7,6 +7,7 @@
namespace Drupal\filter\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -42,7 +43,7 @@ public function settingsForm(array $form, array &$form_state) {
* {@inheritdoc}
*/
public function process($text, $langcode) {
- return _filter_url($text, $this);
+ return new FilterProcessResult(_filter_url($text, $this));
}
/**
diff --git a/core/modules/filter/lib/Drupal/filter/Plugin/FilterInterface.php b/core/modules/filter/lib/Drupal/filter/Plugin/FilterInterface.php
index 09865aa..fea8559 100644
--- a/core/modules/filter/lib/Drupal/filter/Plugin/FilterInterface.php
+++ b/core/modules/filter/lib/Drupal/filter/Plugin/FilterInterface.php
@@ -158,29 +158,16 @@ public function prepare($text, $langcode);
/**
* Performs the filter processing.
*
- * The typical use case is to just apply some filtering to the given text, but
- * for more advanced use cases, it may be necessary to also:
- * 1. declare asset libraries to be loaded;
- * 2. declare cache tags that the filtered text depends upon, so when either of
- * those cache tags is invalidated, the filtered text should also be
- * invalidated;
- * 3. apply uncacheable filtering, for example because it differs per user.
- * In case a filter needs one or more of these advanced use cases, it can
- * return a renderable array instead of a string, with the following keys:
- * - #markup: containing the filtered text;
- * - #attached: listing the asset libraries to be attached (to adddress 1.),
- * - #cache: listing the cache tags to be associated (to address 2.),
- * - #post_render_cache: listing the callbacks to be applied (to address 3.).
- *
* @param string $text
* The text string to be filtered.
* @param string $langcode
* The language code of the text to be filtered.
*
- * @return string|array
- * The filtered text, or, if it's necessary a renderable array containing at
- * least the #markup key, but possibly also the #attached, #cache or
- * #post_render_cache keys.
+ * @return \Drupal\filter\FilterProcessResult
+ * The filtered text, wrapped in a FilterProcessResult object, and possibly
+ * with associated assets, cache tags and #post_render_cache callbacks.
+ *
+ * @see \Drupal\filter\FilterProcessResult
*/
public function process($text, $langcode);
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
index ddfc4ac..e0c06d5 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterAPITest.php
@@ -249,6 +249,13 @@ function testProcessedTextElement() {
'weight' => 1,
'status' => TRUE,
),
+ // Run the HTML corrector filter last, because it has the potential to
+ // break the render cache placeholders added by the
+ // filter_test_post_render_cache filter.
+ 'filter_htmlcorrector' => array(
+ 'weight' => 10,
+ 'status' => TRUE,
+ ),
),
))->save();
diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php
index bff4331..6426bf6 100644
--- a/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php
+++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterUnitTest.php
@@ -52,14 +52,7 @@ function testCaptionFilter() {
$filter = $this->filters['filter_caption'];
$test = function($input) use ($filter) {
- $filtered = $filter->process($input, 'und');
- if (is_string($filtered)) {
- return $filtered;
- }
- else {
- drupal_render($filtered);
- return $filtered;
- }
+ return $filter->process($input, 'und');
};
$attached_library = array(
@@ -71,33 +64,33 @@ function testCaptionFilter() {
// No data-caption nor data-align attributes.
$input = '
';
$expected = $input;
- $this->assertIdentical($expected, $test($input));
+ $this->assertIdentical($expected, $test($input)->getProcessedText());
// Only data-caption attribute.
$input = '
';
$expected = '
Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
// Empty data-caption attribute.
$input = '
';
$expected = '
';
- $this->assertIdentical($expected, $test($input));
+ $this->assertIdentical($expected, $test($input)->getProcessedText());
// HTML entities in the caption.
$input = '
';
$expected = '
“Loquacious llama!”';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
// HTML encoded as HTML entities in data-caption attribute.
$input = '
';
$expected = '
Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
// HTML (not encoded as HTML entities) in data-caption attribute, which is
// not allowed by the HTML spec, but may happen when people manually write
@@ -105,95 +98,95 @@ function testCaptionFilter() {
$input = '
';
$expected = '
Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
// Security test: attempt an XSS.
$input = '
';
$expected = '
alert(\'Loquacious llama!\')';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
// Only data-align attribute: all 3 allowed values.
$input = '
';
$expected = '
';
- $this->assertIdentical($expected, $test($input));
+ $this->assertIdentical($expected, $test($input)->getProcessedText());
$input = '
';
$expected = '
';
- $this->assertIdentical($expected, $test($input));
+ $this->assertIdentical($expected, $test($input)->getProcessedText());
$input = '
';
$expected = '
';
- $this->assertIdentical($expected, $test($input));
+ $this->assertIdentical($expected, $test($input)->getProcessedText());
// Only data-align attribute: a disallowed value.
$input = '
';
$expected = '
';
- $this->assertIdentical($expected, $test($input));
+ $this->assertIdentical($expected, $test($input)->getProcessedText());
// Empty data-align attribute.
$input = '
';
$expected = '
';
- $this->assertIdentical($expected, $test($input));
+ $this->assertIdentical($expected, $test($input)->getProcessedText());
// Both data-caption and data-align attributes: all 3 allowed values for the
// data-align attribute.
$input = '
';
$expected = '
Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
$input = '
';
$expected = '
Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
$input = '
';
$expected = '
Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
// Both data-caption and data-align attributes, but a disallowed data-align
// attribute value.
$input = '
';
$expected = '
Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
// Ensure the filter also works with uncommon yet valid attribute quoting.
$input = '
';
$expected = '
Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
// Security test: attempt to inject an additional class.
$input = '
';
$expected = '
Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
// Security test: attempt an XSS.
$input = '
';
$expected = '
Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
// Finally, ensure that this also works on any other tag.
$input = '';
$expected = 'Loquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
$input = 'baz';
$expected = 'bazLoquacious llama!';
$output = $test($input);
- $this->assertIdentical($expected, $output['#markup']);
- $this->assertIdentical($attached_library, $output['#attached']);
+ $this->assertIdentical($expected, $output->getProcessedText());
+ $this->assertIdentical($attached_library, $output->getAssets());
}
/**
@@ -717,7 +710,7 @@ function testUrlFilter() {
*/
function assertFilteredString($filter, $tests) {
foreach ($tests as $source => $tasks) {
- $result = $filter->process($source, $filter, FALSE, '');
+ $result = $filter->process($source, $filter)->getProcessedText();
foreach ($tasks as $value => $is_expected) {
// Not using assertIdentical, since combination with strpos() is hard to grok.
if ($is_expected) {
diff --git a/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestAssets.php b/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestAssets.php
index 7a2c70a..4bd93af 100644
--- a/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestAssets.php
+++ b/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestAssets.php
@@ -7,6 +7,7 @@
namespace Drupal\filter_test\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -25,14 +26,13 @@ class FilterTestAssets extends FilterBase {
* {@inheritdoc}
*/
public function process($text, $langcode) {
- return array(
- '#markup' => $text,
- '#attached' => array(
- 'library' => array(
- 'filter/caption',
- ),
+ $result = new FilterProcessResult($text);
+ $result->setAssets(array(
+ 'library' => array(
+ 'filter/caption',
),
- );
+ ));
+ return $result;
}
}
diff --git a/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestCacheTags.php b/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestCacheTags.php
index 5e2aaa5..d970b83 100644
--- a/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestCacheTags.php
+++ b/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestCacheTags.php
@@ -7,6 +7,7 @@
namespace Drupal\filter_test\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -25,14 +26,9 @@ class FilterTestCacheTags extends FilterBase {
* {@inheritdoc}
*/
public function process($text, $langcode) {
- return array(
- '#markup' => $text,
- '#cache' => array(
- 'tags' => array(
- 'foo' => array('bar', 'baz'),
- )
- ),
- );
+ $result = new FilterProcessResult($text);
+ $result->setCacheTags(array('foo' => array('bar', 'baz')));
+ return $result;
}
}
diff --git a/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestPostRenderCache.php b/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestPostRenderCache.php
index fafeb2e..cee058a 100644
--- a/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestPostRenderCache.php
+++ b/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestPostRenderCache.php
@@ -7,6 +7,7 @@
namespace Drupal\filter_test\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -30,14 +31,13 @@ public function process($text, $langcode) {
'thing' => 'llama',
'token' => drupal_render_cache_generate_token(),
);
- return array(
- '#markup' => $text . '
' . drupal_render_cache_generate_placeholder($callback, $context, $context['token']) . '
',
- '#post_render_cache' => array(
- $callback => array(
- $context,
- ),
- )
- );
+ $result = new FilterProcessResult($text . '' . drupal_render_cache_generate_placeholder($callback, $context, $context['token']) . '
');
+ $result->setPostRenderCacheCallbacks(array(
+ $callback => array(
+ $context,
+ ),
+ ));
+ return $result;
}
/**
diff --git a/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestReplace.php b/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestReplace.php
index 510a322..735c02d 100644
--- a/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestReplace.php
+++ b/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestReplace.php
@@ -7,6 +7,7 @@
namespace Drupal\filter_test\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
/**
@@ -28,7 +29,7 @@ public function process($text, $langcode) {
$text = array();
$text[] = 'Filter: ' . $this->getLabel() . ' (' . $this->getPluginId() . ')';
$text[] = 'Language: ' . $langcode;
- return implode("
\n", $text);
+ return new FilterProcessResult(implode("
\n", $text));
}
}
diff --git a/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestRestrictTagsAndAttributes.php b/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestRestrictTagsAndAttributes.php
index 4066a1a..c11160d 100644
--- a/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestRestrictTagsAndAttributes.php
+++ b/core/modules/filter/tests/filter_test/lib/Drupal/filter_test/Plugin/Filter/FilterTestRestrictTagsAndAttributes.php
@@ -7,6 +7,7 @@
namespace Drupal\filter_test\Plugin\Filter;
+use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
use Drupal\Component\Utility\Xss;
@@ -29,7 +30,7 @@ public function process($text, $langcode) {
$allowed_tags = array_filter($this->settings['restrictions']['allowed'], function($value) {
return is_array($value) || (bool) $value !== FALSE;
});
- return Xss::filter($text, array_keys($allowed_tags));
+ return new FilterProcessResult(Xss::filter($text, array_keys($allowed_tags)));
}
/**
diff --git a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextDefaultFormatter.php b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextDefaultFormatter.php
index 6c48dd8..d570417 100644
--- a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextDefaultFormatter.php
+++ b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextDefaultFormatter.php
@@ -58,9 +58,7 @@ protected function viewElementsWithTextProcessing(FieldItemListInterface $items)
'#type' => 'processed_text',
'#text' => $item->value,
'#format' => $item->format,
- '#context' => array(
- 'langcode' => $item->getLangcode(),
- ),
+ '#langcode' => $item->getLangcode(),
);
// @todo Document; similar to what we saw in https://drupal.org/node/2099131
drupal_render($elements[$delta], TRUE);
diff --git a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php
index fa643a8..b29ec1c 100644
--- a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php
+++ b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldFormatter/TextTrimmedFormatter.php
@@ -94,9 +94,7 @@ protected function viewElementsWithTextProcessing(FieldItemListInterface $items)
'#type' => 'processed_text',
'#text' => NULL,
'#format' => $item->format,
- '#context' => array(
- 'langcode' => $item->getLangcode(),
- ),
+ '#langcode' => $item->getLangcode(),
);
if ($this->getPluginId() == 'text_summary_or_trimmed' && !empty($item->summary)) {
diff --git a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextItemBase.php b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextItemBase.php
index 1715f0d..5a12995 100644
--- a/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextItemBase.php
+++ b/core/modules/text/lib/Drupal/text/Plugin/Field/FieldType/TextItemBase.php
@@ -15,7 +15,7 @@
/**
* Base class for 'text' configurable field types.
*/
-abstract class TextItemBase extends FieldItemBase {
+abstract class TextItemBase extends FieldItemBase implements PrepareCacheInterface {
/**
* {@inheritdoc}
@@ -67,6 +67,22 @@ public function isEmpty() {
/**
* {@inheritdoc}
*/
+ public function getCacheData() {
+ $data = $this->getValue();
+ // Where possible, generate the processed (sanitized) version of each
+ // textual property (e.g., 'value', 'summary') within this field item early
+ // so that it is cached in the field cache.
+ foreach ($this->definition->getPropertyDefinitions() as $property => $definition) {
+ if ($definition->getClass() == '\Drupal\text\TextProcessed') {
+ $data[$property] = $this->get($property)->getValue();
+ }
+ }
+ return $data;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
public function onChange($property_name) {
// Notify the parent of changes.
if (isset($this->parent)) {
diff --git a/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php b/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php
index 68a6179..9b93095 100644
--- a/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php
+++ b/core/modules/text/lib/Drupal/text/Tests/TextWithSummaryItemTest.php
@@ -103,6 +103,71 @@ public function testCrudAndUpdate() {
}
/**
+ * Tests that the processed values are cached.
+ */
+ function testProcessedCache() {
+ // Use an entity type that has caching enabled.
+ $entity_type = 'entity_test_rev';
+
+ $this->createField($entity_type);
+
+ // Create an entity with a summary and a text format.
+ $entity = entity_create($entity_type);
+ $entity->summary_field->value = $value = $this->randomName();
+ $entity->summary_field->summary = $summary = $this->randomName();
+ $entity->summary_field->format = 'plain_text';
+ $entity->name->value = $this->randomName();
+ $entity->save();
+
+ // Check that the processed values are correctly computed.
+ $this->assertEqual($entity->summary_field->processed, $value);
+ $this->assertEqual($entity->summary_field->summary_processed, $summary);
+
+ // Load the entity and check that the field cache contains the expected
+ // data.
+ $entity = entity_load($entity_type, $entity->id());
+ $cache = \Drupal::cache('entity')->get("field:$entity_type:" . $entity->id());
+ $this->assertEqual($cache->data, array(
+ Language::LANGCODE_NOT_SPECIFIED => array(
+ 'summary_field' => array(
+ 0 => array(
+ 'value' => $value,
+ 'summary' => $summary,
+ 'format' => 'plain_text',
+ 'processed' => $value,
+ 'summary_processed' => $summary,
+ ),
+ ),
+ ),
+ ));
+
+ // Inject fake processed values into the cache to make sure that these are
+ // used as-is and not re-calculated when the entity is loaded.
+ $data = array(
+ Language::LANGCODE_NOT_SPECIFIED => array(
+ 'summary_field' => array(
+ 0 => array(
+ 'value' => $value,
+ 'summary' => $summary,
+ 'format' => 'plain_text',
+ 'processed' => 'Cached processed value',
+ 'summary_processed' => 'Cached summary processed value',
+ ),
+ ),
+ ),
+ );
+ \Drupal::cache('entity')->set("field:$entity_type:" . $entity->id(), $data);
+ $entity = entity_load($entity_type, $entity->id(), TRUE);
+ $this->assertEqual($entity->summary_field->processed, 'Cached processed value');
+ $this->assertEqual($entity->summary_field->summary_processed, 'Cached summary processed value');
+
+ // Change the format, this should update the processed properties.
+ $entity->summary_field->format = 'no_filters';
+ $this->assertEqual($entity->summary_field->processed, $value);
+ $this->assertEqual($entity->summary_field->summary_processed, $summary);
+ }
+
+ /**
* Creates a text_with_summary field and field instance.
*
* @param string $entity_type