Commit 4487d5b1 authored by Thorben Nissen's avatar Thorben Nissen Committed by Benni Mack
Browse files

[BUGFIX] RTE: Support anchors without `href`

Removes enforcing of href attribute when storing RTE text into the
database. Removes adding of absolute scheme on a tags without href
attribute, when loading text from the database. Changes
ContentObjectRenderer::typolink to render a tag without href anyway, if
id or name attribute is present.
Adds unit tests.

Resolves: #87992
Releases: master, 9.5
Change-Id: I4dcd33e6f13dc6a1f364c96b425aa2f241653ae9
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/60324


Tested-by: default avatarTYPO3com <noreply@typo3.com>
Tested-by: Susanne Moog's avatarSusanne Moog <look@susi.dev>
Tested-by: default avatarGuido Schmechel <guido.schmechel@brandung.de>
Tested-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
Reviewed-by: Susanne Moog's avatarSusanne Moog <look@susi.dev>
Reviewed-by: default avatarGuido Schmechel <guido.schmechel@brandung.de>
Reviewed-by: Benni Mack's avatarBenni Mack <benni@typo3.org>
parent 819db04f
......@@ -338,6 +338,11 @@ class RteHtmlParser extends HtmlParser implements LoggerAwareInterface
foreach ($blockSplit as $k => $v) {
if ($k % 2) {
list($tagAttributes) = $this->get_tag_attributes($this->getFirstTag($v), true);
// Anchors would not have an href attribute
if (!isset($tagAttributes['href'])) {
continue;
}
$linkService = GeneralUtility::makeInstance(LinkService::class);
$linkInformation = $linkService->resolve($tagAttributes['href'] ?? '');
......
......@@ -631,4 +631,82 @@ class RteHtmlParserTest extends UnitTestCase
$thisConfig = ['proc.' => $this->procOptions];
self::assertEquals($expectedResult, $subject->RTE_transform($subject->RTE_transform($content, [], 'db', $thisConfig), [], 'rte', $thisConfig));
}
/**
* Data provider for anchorCorrectlyTransformedOnWayToDatabase
*/
public static function anchorCorrectlyTransformedOnWayToDatabaseProvider()
{
return [
[
'<p><a name="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
'<p><a name="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>'
],
[
'<p><a id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
'<p><a id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>'
],
[
'<p><a name="some_anchor" id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
'<p><a name="some_anchor" id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>'
],
[
'<p><a id="some_anchor">Some text inside the anchor</a></p>',
'<p><a id="some_anchor">Some text inside the anchor</a></p>'
]
];
}
/**
* @test
* @dataProvider anchorCorrectlyTransformedOnWayToDatabaseProvider
* @param $content
* @param $expectedResult
*/
public function anchorCorrectlyTransformedOnWayToDatabase($content, $expectedResult)
{
$eventDispatcher = $this->createMock(EventDispatcherInterface::class);
$subject = new RteHtmlParser($eventDispatcher);
$thisConfig = ['proc.' => $this->procOptions];
self::assertEquals($expectedResult, $subject->RTE_transform($content, [], 'db', $thisConfig));
}
/**
* Data provider for anchorCorrectlyTransformedOnWayToDatabaseAndBackToRTE
*/
public static function anchorCorrectlyTransformedOnWayToDatabaseAndBackToRTEProvider()
{
return [
[
'<p><a name="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
'<p><a name="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>'
],
[
'<p><a id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
'<p><a id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>'
],
[
'<p><a name="some_anchor" id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>',
'<p><a name="some_anchor" id="some_anchor"></a></p>' . CRLF . '<h3>Some headline here</h3>'
],
[
'<p><a id="some_anchor">Some text inside the anchor</a></p>',
'<p><a id="some_anchor">Some text inside the anchor</a></p>'
]
];
}
/**
* @test
* @dataProvider anchorCorrectlyTransformedOnWayToDatabaseAndBackToRTEProvider
* @param $content
* @param $expectedResult
*/
public function anchorCorrectlyTransformedOnWayToDatabaseAndBackToRTE($content, $expectedResult)
{
$eventDispatcher = $this->createMock(EventDispatcherInterface::class);
$subject = new RteHtmlParser($eventDispatcher);
$thisConfig = ['proc.' => $this->procOptions];
self::assertEquals($expectedResult, $subject->RTE_transform($subject->RTE_transform($content, [], 'db', $thisConfig), [], 'rte', $thisConfig));
}
}
......@@ -5026,7 +5026,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
$title = $resolvedLinkParameters['title'];
if (!$linkParameter) {
return $linkText;
return $this->resolveAnchorLink($linkText, $conf);
}
// Detecting kind of link and resolve all necessary parameters
......@@ -7150,4 +7150,25 @@ class ContentObjectRenderer implements LoggerAwareInterface
{
return $this->typoScriptFrontendController ?: $GLOBALS['TSFE'];
}
/**
* Support anchors without href value
* Changes ContentObjectRenderer::typolink to render a tag without href,
* if id or name attribute is present.
*
* @param string $linkText
* @param array $conf Typolink configuration decoded as array
* @return string Full a-Tag or just the linktext if id or name are not set.
*/
protected function resolveAnchorLink(string $linkText, array $conf): string
{
$anchorTag = '<a ' . $this->getATagParams($conf) . '>';
$aTagParams = GeneralUtility::get_tag_attributes($anchorTag);
// If it looks like a anchor tag, render it anyway
if (isset($aTagParams['id']) || isset($aTagParams['name'])) {
return $anchorTag . $linkText . '</a>';
}
// Otherwise just return the link text
return $linkText;
}
}
Supports Markdown
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