Commit 7be9318b authored by Benni Mack's avatar Benni Mack Committed by Stefan Bürk
Browse files

[TASK] Deprecate $cObj->lastTypoLink* properties

This change deprecates the following properties:

ContentObjectRenderer->lastTypoLinkUrl
ContentObjectRenderer->lastTypoLinkTarget
ContentObjectRenderer->lastTypoLinkLD (LD stands for "LinkData")

All this information is available in the newly
introduced LinkResultInterface, that can be built with
the LinkFactory directly, or via ContentObjectRenderer->createLink().

Resolves: #97549
Releases: main
Change-Id: I80b21d54e75714ecc148cece35e064720a57df44
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/74510

Reviewed-by: Oliver Bartsch's avatarOliver Bartsch <bo@cedev.de>
Reviewed-by: Jochen's avatarJochen <rothjochen@gmail.com>
Reviewed-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
Tested-by: core-ci's avatarcore-ci <typo3@b13.com>
Tested-by: Jochen's avatarJochen <rothjochen@gmail.com>
Tested-by: Stefan Bürk's avatarStefan Bürk <stefan@buerk.tech>
parent df0d68bc
.. include:: /Includes.rst.txt
.. _deprecation-97549-1651696509
=====================================================================
Deprecation: #97549 - ContentObjectRenderer->lastTypoLink* properties
=====================================================================
See :issue:`97549`
Description
===========
When generating links via :php:`ContentObjectRenderer->typoLink()`,
it had been possible to retrieve information about the generated link
with the following public properties:
* :php:`ContentObjectRenderer->lastTypoLinkUrl`
* :php:`ContentObjectRenderer->lastTypoLinkTarget`
* :php:`ContentObjectRenderer->lastTypoLinkLD`
Since those information are also available in the :php:`LinkResultInterface`,
which is returned by :php:`ContentObjectRenderer->createLink()` or
can be accessed via :php:`ContentObjectRenderer->lastTypoLinkResult`,
these properties have now been deprecated.
Impact
======
Accessing these properties is still possible, but will stop working in
TYPO3 v13.0. The extension scanner will detect any usage as weak match.
Affected installations
======================
TYPO3 installations using these properties in their extensions in either
PHP or TypoScript code.
Migration
=========
It is recommended to retrieve this information via the :php:`LinkResultInterface`
object returned by calling :php:`ContentObjectRenderer->createLink()` directly,
or if this is not possible via :php:`ContentObjectRenderer->lastTypoLinkResult`.
.. index:: Frontend, PHP-API, TypoScript, FullyScanned, ext:frontend
......@@ -321,6 +321,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
* This will be set by typoLink() to the url of the most recent link created.
*
* @var string
* @deprecated will be removed in TYPO3 v13.0. Use $this->lastTypoLinkResult or call LinkFactory directly
*/
public $lastTypoLinkUrl = '';
......@@ -328,11 +329,13 @@ class ContentObjectRenderer implements LoggerAwareInterface
* DO. link target.
*
* @var string
* @deprecated will be removed in TYPO3 v13.0. Use $this->lastTypoLinkResult or call LinkFactory directly
*/
public $lastTypoLinkTarget = '';
/**
* @var array
* @deprecated will be removed in TYPO3 v13.0. Use $this->lastTypoLinkResult or call LinkFactory directly
*/
public $lastTypoLinkLD = [];
......@@ -4469,7 +4472,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
return $e->getLinkText();
}
// If flag "returnLastTypoLinkUrl" set, then just return the latest URL made
// If flag "returnLast" set, then just return the latest URL / url / target that was built.
// This returns the information without being wrapped in a "HtmlLinkResult" object.
switch ($conf['returnLast'] ?? null) {
case 'url':
......@@ -4521,6 +4524,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
$this->lastTypoLinkResult = $linkResult;
// Now populate all legacy values
// @deprecated can be removed in TYPO3 13.0.
$this->lastTypoLinkTarget = $linkResult->getTarget();
$this->lastTypoLinkUrl = $linkResult->getUrl();
$this->lastTypoLinkLD['target'] = htmlspecialchars($linkResult->getTarget());
......@@ -4647,8 +4651,7 @@ class ContentObjectRenderer implements LoggerAwareInterface
} else {
$conf['additionalParams'] = $urlParameters;
}
$this->createUrl($conf);
return $this->lastTypoLinkUrl;
return $this->createUrl($conf);
}
/**
......
......@@ -34,7 +34,9 @@ use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
use TYPO3\CMS\Frontend\ContentObject\Menu\Exception\NoSuchMenuTypeException;
use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
use TYPO3\CMS\Frontend\Event\FilterMenuItemsEvent;
use TYPO3\CMS\Frontend\Typolink\LinkResultInterface;
use TYPO3\CMS\Frontend\Typolink\PageLinkBuilder;
use TYPO3\CMS\Frontend\Typolink\UnableToLinkException;
/**
* Generating navigation/menus from TypoScript
......@@ -1184,11 +1186,10 @@ abstract class AbstractMenuContentObject
* @param int $key Pointer to a key in the $this->menuArr array where the value for that key represents the menu item we are linking to (page record)
* @param string $altTarget Alternative target
* @param string $typeOverride Alternative type
* @return array Returns an array with A-tag attributes as key/value pairs (HREF, TARGET and data-window-* attrs)
* @return array Returns an array with A-tag attributes as key/value pairs ('href', 'target' and data-window-* attrs)
*/
protected function link($key, $altTarget, $typeOverride)
{
$attrs = [];
$runtimeCache = $this->getRuntimeCache();
$MP_var = $this->getMPvar($key);
$cacheId = 'menu-generated-links-' . md5($key . $altTarget . $typeOverride . $MP_var . ((string)($this->mconf['showAccessRestrictedPages'] ?? '_')) . json_encode($this->menuArr[$key]));
......@@ -1223,22 +1224,22 @@ abstract class AbstractMenuContentObject
$mainTarget = $altTarget ?: (string)$this->parent_cObj->stdWrapValue('target', $this->mconf ?? []);
// Creating link
$addParams = ($this->mconf['addParams'] ?? '') . ($this->I['val']['additionalParams'] ?? '') . $MP_params;
$LD = $this->menuTypoLink($this->menuArr[$key], $mainTarget, $addParams, $typeOverride, $overrideId);
// Overriding URL / Target if set to do so:
if ($this->menuArr[$key]['_OVERRIDE_HREF'] ?? false) {
$LD['totalURL'] = $this->menuArr[$key]['_OVERRIDE_HREF'];
if ($this->menuArr[$key]['_OVERRIDE_TARGET']) {
$LD['target'] = $this->menuArr[$key]['_OVERRIDE_TARGET'];
try {
$linkResult = $this->menuTypoLink($this->menuArr[$key], $mainTarget, $addParams, $typeOverride, $overrideId);
// Overriding URL / Target if set to do so:
if ($this->menuArr[$key]['_OVERRIDE_HREF'] ?? false) {
$linkResult = $linkResult->withAttribute('href', $this->menuArr[$key]['_OVERRIDE_HREF']);
if ($this->menuArr[$key]['_OVERRIDE_TARGET'] ?? false) {
$linkResult = $linkResult->withAttribute('target', $this->menuArr[$key]['_OVERRIDE_TARGET']);
}
}
$attrs = $linkResult->getAttributes();
} catch (UnableToLinkException $e) {
$attrs = [
'href' => '',
'target' => '',
];
}
// Added this check: What it does is to enter the baseUrl (if set, which it should for "realurl" based sites)
// as URL if the calculated value is empty. The problem is that no link is generated with a blank URL
// and blank URLs might appear when the realurl encoding is used and a link to the frontpage is generated.
$attrs['HREF'] = (string)$LD['totalURL'] !== '' ? $LD['totalURL'] : $tsfe->baseUrl;
$attrs['TARGET'] = $LD['target'] ?? '';
$attrs = array_merge($attrs, $LD['ATagParams']);
// @todo: prefer to use lower-case href / target to be consistent
unset($attrs['href'], $attrs['target']);
$runtimeCache->set($cacheId, $attrs);
// End showAccessRestrictedPages
......@@ -1592,9 +1593,9 @@ abstract class AbstractMenuContentObject
* @param string $addParams Parameters to add to URL
* @param int|string $typeOverride "type" value, empty string means "not set"
* @param int|null $overridePageId link to this page instead of the $page[uid] value
* @return array See linkData
* @return LinkResultInterface
*/
protected function menuTypoLink($page, $oTarget, $addParams, $typeOverride, ?int $overridePageId = null)
protected function menuTypoLink(array $page, string $oTarget, $addParams, $typeOverride, ?int $overridePageId = null): LinkResultInterface
{
$conf = [
'parameter' => $overridePageId ?? $page['uid'] ?? 0,
......@@ -1629,11 +1630,7 @@ abstract class AbstractMenuContentObject
if ($page['sectionIndex_uid'] ?? false) {
$conf['section'] = $page['sectionIndex_uid'];
}
$this->parent_cObj->typoLink('|', $conf);
$LD = $this->parent_cObj->lastTypoLinkLD;
$LD['totalURL'] = $this->parent_cObj->lastTypoLinkUrl;
$LD['ATagParams'] = $this->parent_cObj->lastTypoLinkResult ? $this->parent_cObj->lastTypoLinkResult->getAttributes() : [];
return $LD;
return $this->parent_cObj->createLink('|', $conf);
}
/**
......
......@@ -438,7 +438,7 @@ class LanguageMenuProcessor implements DataProcessorInterface
*/
public function replacePlaceholderInRenderedMenuItem(array $menuItem): array
{
$link = $this->jsonEncode($menuItem['linkHREF']['HREF']);
$link = $this->jsonEncode($menuItem['linkHREF']['href']);
$menuItem['parts']['title'] = str_replace(self::LINK_PLACEHOLDER, $link, $menuItem['parts']['title']);
......
......@@ -534,8 +534,8 @@ class MenuProcessor implements DataProcessorInterface
*/
public function replacePlaceholderInRenderedMenuItem($menuItem, $conf)
{
$link = $this->jsonEncode($menuItem['linkHREF']['HREF'] ?? '');
$target = $this->jsonEncode($menuItem['linkHREF']['TARGET'] ?? '');
$link = $this->jsonEncode($menuItem['linkHREF']['href'] ?? '');
$target = $this->jsonEncode($menuItem['linkHREF']['target'] ?? '');
$menuItem['parts']['title'] = str_replace(self::LINK_PLACEHOLDER, $link, $menuItem['parts']['title']);
$menuItem['parts']['title'] = str_replace(self::TARGET_PLACEHOLDER, $target, $menuItem['parts']['title']);
......
......@@ -449,13 +449,13 @@ class AbstractPlugin
* @param bool $cache See pi_linkTP_keepPIvars
* @param bool $clearAnyway See pi_linkTP_keepPIvars
* @param int $altPageId See pi_linkTP_keepPIvars
* @return string The URL ($this->cObj->lastTypoLinkUrl)
* @return string The URL ($this->cObj->lastTypoLinkResult)
* @see pi_linkTP_keepPIvars()
*/
public function pi_linkTP_keepPIvars_url($overrulePIvars = [], $cache = false, $clearAnyway = false, $altPageId = 0)
{
$this->pi_linkTP_keepPIvars('|', $overrulePIvars, $cache, $clearAnyway, $altPageId);
return $this->cObj->lastTypoLinkUrl;
return $this->cObj->lastTypoLinkResult->getUrl();
}
/**
......@@ -484,9 +484,9 @@ class AbstractPlugin
$overrulePIvars = array_merge($overrulePIvars, (array)$mergeArr);
$str = $this->pi_linkTP_keepPIvars($str, $overrulePIvars, $cache, false, $altPageId);
}
// If urlOnly flag, return only URL as it has recently be generated.
// If urlOnly flag, return only URL as it has recently been generated.
if ($urlOnly) {
$str = $this->cObj->lastTypoLinkUrl;
$str = $this->cObj->lastTypoLinkResult->getUrl();
}
}
return $str;
......
......@@ -90,24 +90,11 @@ class DatabaseRecordLinkBuilder extends AbstractTypolinkBuilder
if (!empty($linkDetails['fragment'])) {
$typoScriptConfiguration['section'] = $linkDetails['fragment'];
}
// Build the full link to the record
// Build the full link to the record by calling LinkFactory again ("inception")
$request = $this->contentObjectRenderer->getRequest();
$localContentObjectRenderer = GeneralUtility::makeInstance(ContentObjectRenderer::class);
$localContentObjectRenderer->start($record, $databaseTable, $request);
$localContentObjectRenderer->parameters = $this->contentObjectRenderer->parameters;
$link = (string)$localContentObjectRenderer->typoLink($linkText, $typoScriptConfiguration);
$this->contentObjectRenderer->lastTypoLinkLD = $localContentObjectRenderer->lastTypoLinkLD;
$this->contentObjectRenderer->lastTypoLinkUrl = $localContentObjectRenderer->lastTypoLinkUrl;
$this->contentObjectRenderer->lastTypoLinkTarget = $localContentObjectRenderer->lastTypoLinkTarget;
$this->contentObjectRenderer->lastTypoLinkResult = $localContentObjectRenderer->lastTypoLinkResult;
// nasty workaround so typolink stops putting a link together, there is a link already built
throw new UnableToLinkException(
'',
1491130170,
null,
$link
);
return $localContentObjectRenderer->createLink($linkText, $typoScriptConfiguration);
}
}
......@@ -146,13 +146,11 @@ class PageLinkBuilder extends AbstractTypolinkBuilder
if (!isset($conf['extTarget'])) {
$conf['extTarget'] = (isset($page['target']) && trim($page['target'])) ? $page['target'] : $target;
}
$this->contentObjectRenderer->typoLink($linkText, $conf);
$target = $this->contentObjectRenderer->lastTypoLinkTarget;
$url = $this->contentObjectRenderer->lastTypoLinkUrl;
$linkResultFromExternalUrl = $this->contentObjectRenderer->createLink($linkText, $conf);
$target = $linkResultFromExternalUrl->getTarget();
$url = $linkResultFromExternalUrl->getUrl();
// If the page external URL is resolved into a URL or email, this should be taken into account when compiling the final link result object
if ($this->contentObjectRenderer->lastTypoLinkResult) {
$linkResultType = $this->contentObjectRenderer->lastTypoLinkResult->getType();
}
$linkResultType = $linkResultFromExternalUrl->getType();
if (empty($url)) {
throw new UnableToLinkException('Link to external page "' . $page['uid'] . '" does not have a proper target URL, so "' . $linkText . '" was not linked.', 1551621999, null, $linkText);
}
......
......@@ -615,9 +615,9 @@ class AbstractMenuContentObjectTest extends UnitTestCase
public function menuTypoLinkCreatesExpectedTypoLinkConfiguration(array $expected, array $mconf, array $page, string $oTarget, $addParams = '', $typeOverride = '', $overrideId = null): void
{
$cObject = $this->getMockBuilder(ContentObjectRenderer::class)
->onlyMethods(['typoLink'])
->onlyMethods(['createLink'])
->getMock();
$cObject->expects(self::once())->method('typoLink')->with('|', $expected);
$cObject->expects(self::once())->method('createLink')->with('|', $expected);
$this->subject->_set('parent_cObj', $cObject);
$this->subject->_set('mconf', $mconf);
$this->subject->_call('menuTypoLink', $page, $oTarget, $addParams, $typeOverride, $overrideId);
......
......@@ -189,7 +189,7 @@ class DatabaseRecordLinkBuilderTest extends UnitTestCase
);
$cObj->start(Argument::cetera())->shouldBeCalled();
$cObj->typoLink(Argument::cetera())->shouldBeCalled();
$cObj->createLink(Argument::cetera())->shouldBeCalled();
$frontendControllerProphecy->getPagesTSconfig()->willReturn($pageTsConfig);
......
......@@ -949,4 +949,19 @@ return [
'Breaking-97065-TYPO3FrontendAlwaysRenderedInUTF-8.rst',
],
],
'TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer->lastTypoLinkUrl' => [
'restFiles' => [
'Deprecation-97549-ContentObjectRenderer-lastTypoLinkProperties.rst',
],
],
'TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer->lastTypoLinkTarget' => [
'restFiles' => [
'Deprecation-97549-ContentObjectRenderer-lastTypoLinkProperties.rst',
],
],
'TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer->lastTypoLinkLD' => [
'restFiles' => [
'Deprecation-97549-ContentObjectRenderer-lastTypoLinkProperties.rst',
],
],
];
......@@ -269,7 +269,7 @@ class RedirectService implements LoggerAwareInterface
*/
protected function resolveSite(array $linkDetails, ?SiteInterface $site): ?SiteInterface
{
if (($site === null || $site instanceof NullSite) && ($linkDetails['type'] ?? '') === 'page') {
if (($site === null || $site instanceof NullSite) && ($linkDetails['type'] ?? '') === LinkService::TYPE_PAGE) {
try {
return $this->siteFinder->getSiteByPageId((int)$linkDetails['pageuid']);
} catch (SiteNotFoundException $e) {
......@@ -334,11 +334,6 @@ class RedirectService implements LoggerAwareInterface
$result = $linkBuilder->build($linkDetails, '', '', $configuration);
return new Uri($result->getUrl());
} catch (UnableToLinkException $e) {
// This exception is also thrown by the DatabaseRecordTypolinkBuilder
$url = $controller->cObj->lastTypoLinkUrl;
if (!empty($url)) {
return new Uri($url);
}
return null;
}
}
......
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