Skip to content

Commit

Permalink
[BUGFIX] RTE: Support anchors without href
Browse files Browse the repository at this point in the history
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: TYPO3com <noreply@typo3.com>
Tested-by: Susanne Moog <look@susi.dev>
Tested-by: Guido Schmechel <guido.schmechel@brandung.de>
Tested-by: Benni Mack <benni@typo3.org>
Reviewed-by: Susanne Moog <look@susi.dev>
Reviewed-by: Guido Schmechel <guido.schmechel@brandung.de>
Reviewed-by: Benni Mack <benni@typo3.org>
  • Loading branch information
thommyhh authored and bmack committed Nov 9, 2019
1 parent 819db04 commit 4487d5b
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 1 deletion.
5 changes: 5 additions & 0 deletions typo3/sysext/core/Classes/Html/RteHtmlParser.php
Expand Up @@ -338,6 +338,11 @@ protected function TS_links_db($value)
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'] ?? '');

Expand Down
78 changes: 78 additions & 0 deletions typo3/sysext/core/Tests/Unit/Html/RteHtmlParserTest.php
Expand Up @@ -631,4 +631,82 @@ public function paragraphCorrectlyTransformedOnWayToDatabaseAndBackToRte($conten
$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));
}
}
Expand Up @@ -5026,7 +5026,7 @@ public function typoLink($linkText, $conf)
$title = $resolvedLinkParameters['title'];

if (!$linkParameter) {
return $linkText;
return $this->resolveAnchorLink($linkText, $conf);
}

// Detecting kind of link and resolve all necessary parameters
Expand Down Expand Up @@ -7150,4 +7150,25 @@ protected function getTypoScriptFrontendController()
{
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;
}
}

0 comments on commit 4487d5b

Please sign in to comment.