Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Drupal\oe_bootstrap_theme_helper\TwigExtension;

use Drupal\Component\Utility\Html;
use Drupal\Component\Utility\Xss;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Link;
use Drupal\Core\Render\BubbleableMetadata;
Expand Down Expand Up @@ -139,7 +140,7 @@ public function bclCardList(array $items): array {
}
if (isset($item['text'])) {
$bcl_card['text'] = [
'content' => $item['text'],
'content' => $this->normalizeCardTextContent($item['text']),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can't the calling template pass a markup object as content?
This should be the general contract for all components.

'classes' => 'mb-2',
'tag' => 'div',
];
Expand All @@ -164,6 +165,27 @@ public function bclCardList(array $items): array {
return $bcl_cards;
}

/**
* Normalizes card text values so processed HTML keeps rendering as markup.
*
* Formatted text can reach the listing/card templates as an already filtered
* HTML string. Wrap those strings in safe markup so Twig does not escape
* them back into raw HTML source.
*
* @param mixed $content
* The card text value.
*
* @return mixed
* The normalized text value.
*/
private function normalizeCardTextContent(mixed $content): mixed {
if (!is_string($content) || !preg_match('/<[a-zA-Z\\/][^>]*>/', $content)) {
return $content;
}

return Markup::create(Xss::filterAdmin($content));
}

/**
* Get file icon class given its extension.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Drupal\Tests\oe_bootstrap_theme_helper\Kernel;

use Drupal\Component\Render\MarkupInterface;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Render\BubbleableMetadata;
use Drupal\Core\Render\RenderContext;
Expand Down Expand Up @@ -458,6 +459,22 @@ public function testBclCardList(): void {
}
}

/**
* Tests BCL card list preserves formatted text as safe markup.
*/
public function testBclCardListFormattedText(): void {
$extension = $this->container->get('oe_bootstrap_theme_helper.twig_extension');
$result = $extension->bclCardList([
[
'text' => '<p>I add a text with <strong>bolds</strong>, <em>italic</em> and <a href="https://www.google.es">loopy link</a></p>',
],
]);

$content = $result[0]['text']['content'] ?? NULL;
$this->assertInstanceOf(MarkupInterface::class, $content);
$this->assertSame('<p>I add a text with <strong>bolds</strong>, <em>italic</em> and <a href="https://www.google.es">loopy link</a></p>', (string) $content);
}

/**
* Provides data for testBclCardList().
*
Expand Down
52 changes: 52 additions & 0 deletions tests/src/Kernel/fixtures/markup_rendering_patterns/listing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -615,3 +615,55 @@ listing_default_2_col_highlight_with_image_copyright:
'article.listing-item--highlight .card-body.pt-3': 1
equals:
'article.listing-item--highlight .bcl-copyright': '© 2024 Example Organisation'
listing_default_1_col_with_formatted_text:
render:
'#type': pattern
'#id': 'listing'
'#variant': 'default'
'#fields':
columns: '1'
title: 'Listing item block title'
items:
- title:
'#markup': '<a class="standalone" href="/example1">Card title 1</a>'
text: '<p>I add a text with <strong>bolds</strong>, <em>italic</em> and <a href="https://www.google.es">loopy link</a></p>'
image:
src: 'https://picsum.photos/600/400/'
alt: 'alt img'
assertions:
count:
'article.listing-item': 1
'article.listing-item .card-text p': 1
'article.listing-item .card-text strong': 1
'article.listing-item .card-text em': 1
'article.listing-item .card-text a[href="https://www.google.es"]': 1
equals:
'article.listing-item .card-text strong': 'bolds'
'article.listing-item .card-text em': 'italic'
'article.listing-item .card-text a[href="https://www.google.es"]': 'loopy link'
listing_highlight_1_col_with_formatted_text:
render:
'#type': pattern
'#id': 'listing'
'#variant': 'highlight'
'#fields':
columns: '1'
title: 'Listing item block title'
items:
- title:
'#markup': '<a class="standalone" href="/example1">Card title 1</a>'
text: '<p>I add a text with <strong>bolds</strong>, <em>italic</em> and <a href="https://www.google.es">loopy link</a></p>'
image:
src: 'https://picsum.photos/600/400/'
alt: 'alt img'
assertions:
count:
'article.listing-item--highlight': 1
'article.listing-item--highlight .card-text p': 1
'article.listing-item--highlight .card-text strong': 1
'article.listing-item--highlight .card-text em': 1
'article.listing-item--highlight .card-text a[href="https://www.google.es"]': 1
equals:
'article.listing-item--highlight .card-text strong': 'bolds'
'article.listing-item--highlight .card-text em': 'italic'
'article.listing-item--highlight .card-text a[href="https://www.google.es"]': 'loopy link'