Bug #87574
closedRedirects to records (linkHandler) don't work
Added by Benjamin Robinson almost 6 years ago. Updated almost 5 years ago.
100%
Description
A redirect (sysext:redirects) to a record (f.e. t3://record?identifier=tx_news&uid=1) results in the error:
Core: Exception handler (WEB): Uncaught TYPO3 Exception: Call to a member function getMountPointInfo() on string | Error thrown in file /html/typo3/typo3_src-9.5.4/typo3/sysext/frontend/Classes/Typolink/PageLinkBuilder.php in line 111. Requested URL: [...]
TSconfig
TCEMAIN.linkHandler{ tx_news{ handler = TYPO3\CMS\Recordlist\LinkHandler\RecordLinkHandler label = News scanAfter = page displayBefore = file configuration { table = tx_news_domain_model_news storagePid = 35 hidePageTree = 0 pageTreeMountPoints = 35 } } }
Setup
config.recordLinks{ tx_news{ typolink{ parameter = 39 additionalParams.data = field:uid additionalParams.wrap = &tx_news_pi1[controller]=News&tx_news_pi1[action]=detail&tx_news_pi1[news]=| useCacheHash = 1 } } }
I have tested this setup with links in regular content like ...
<a href="t3://record?identifier=tx_news&uid=1">Link</a>
... and those are working fine.
Updated by Benjamin Robinson over 5 years ago
- Related to Bug #82798: Linkhandler for records works only when BE user is logged in added
Updated by Benjamin Robinson over 5 years ago
I found out, that the "News"-Tab is only available in the linkwizard of the redirects module, if the TSconfig is included via ext_tables.php
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig('<INCLUDE_TYPOSCRIPT: source="DIR:EXT:user_site/Configuration/TsConfig/Page">');
If it is included in the TSconfig in the page properties of the rootpage ...
<INCLUDE_TYPOSCRIPT: source="DIR:EXT:user_site/Configuration/TsConfig/Page">
... the tab is not even visible.
Summary¶
TSconfig via ext_tables.php:- Error Call to a member function getMountPointInfo() on string
- Issue 82798 occurs (Links to records broken if cache is generated by someone who is not logged into BE). Can someone please reopen this issue?
- Tab not even available in link wizard of redirects module
Updated by Benni Mack over 5 years ago
Hey Ben,
thank you very much for your report!
Could you please try with latest 9.5.x-dev? we've improved this area since 9.5.4, and hopefully this is now solved.
Updated by Benni Mack over 5 years ago
- Status changed from New to Needs Feedback
Updated by Benjamin Robinson over 5 years ago
Hi Benni,
I have just tested it with 9.5.5-dev. It's all the same as with 9.5.4:
If the TSconfig-file is included in the page properties of the rootpage:¶
- The redirect does not work. Error in FE: Call to a member function getMountPointInfo() on string
- It is not possible to create new redirects to records, because the „News“-tab is missing in the link wizard of the redirect module
If the TSconfig-file is included via ext_tables.php¶
- The redirect does not work. Error inn FE: Call to a member function getMountPointInfo() on string
- It is possible to create redirects to to records
- Issue 82798 still occurs (I dont have permissions to reopen the issue. Could you please?)
Updated by Daniel Dorndorf over 5 years ago
I have the same issue...
Also in Backend the redirect module just throws an exception:
PHP Warning: Invalid argument supplied for foreach() in /public/typo3/sysext/frontend/Classes/Controller/TypoScriptFrontendController.php line 4399
It seems in backend the rootLine in the TypoScriptFrontendController is missing, which is needed to determine its page ts.
In Frontend the PageRepository is not properly initalized, even though It would have, you would run into the same error as in the backend, that the controller cannot fetch the page ts.
DatabaseRecordLinkBuilder relies on the method getPagesTSconfig which expects to have a rootline.
I faced this issue also when building a rest api via middleware using the lib.parseFunc_rte, I tried using the way the htmlviewhelper but wrote an own frontend emulating function, maybe it helps fixing this:
$rootPageId = null; /** @var \TYPO3\CMS\Core\Site\Entity\Site $site */ foreach (ObjectUtility::makeInstance(SiteFinder::class)->getAllSites() as $site) { if (GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY') === $site->getBase()->getHost()) { $rootPageId = $site->getRootPageId(); } } if (null === $rootPageId) { throw new RteFrontendSimulationException('Can\'t determine the rootpage uid to use'); } /** @var \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController $typoScriptFrontendController */ $typoScriptFrontendController = ObjectUtility::makeInstance( TypoScriptFrontendController::class, null, $rootPageId, 1 ); /** @var array $rootline */ $rootline = ObjectUtility::makeInstance(RootlineUtility::class, $rootPageId)->get(); $typoScriptFrontendController->rootLine = $rootline; $typoScriptFrontendController->sys_page = ObjectUtility::makeInstance(PageRepository::class); $GLOBALS['TSFE'] = $typoScriptFrontendController; /** @var \TYPO3\CMS\Core\TypoScript\TemplateService $templateService */ $templateService = ObjectUtility::makeInstance(TemplateService::class); $templateService->start($rootline); $GLOBALS['TSFE']->tmpl = $templateService;
Updated by Daniel Dorndorf over 5 years ago
You can build a workaround with that by xclass the databaseRecordLinkBuilder
<?php declare(strict_types=1); namespace Vendor\Extension\XClass; use TYPO3\CMS\Core\Site\SiteFinder; use TYPO3\CMS\Core\TypoScript\TemplateService; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\RootlineUtility; use TYPO3\CMS\Extbase\Object\ObjectManager; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; use TYPO3\CMS\Frontend\Page\PageRepository; use TYPO3\CMS\Frontend\Typolink\DatabaseRecordLinkBuilder as CoreDatabaseRecordLinkBuilder; /** * @package Extension * @subpackage XClass */ class DatabaseRecordLinkBuilder extends CoreDatabaseRecordLinkBuilder { /** * @return TypoScriptFrontendController */ public function getTypoScriptFrontendController(): TypoScriptFrontendController { $rootPageId = null; /** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */ $objectManager = GeneralUtility::makeInstance(ObjectManager::class); /** @var \TYPO3\CMS\Core\Site\Entity\Site $site */ foreach ($objectManager->get(SiteFinder::class)->getAllSites() as $site) { if (GeneralUtility::getIndpEnv('TYPO3_HOST_ONLY') === $site->getBase()->getHost()) { $rootPageId = $site->getRootPageId(); } } if (!$this->typoScriptFrontendController instanceof TypoScriptFrontendController) { /** @var \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController $typoScriptFrontendController */ $this->typoScriptFrontendController = $objectManager->get( TypoScriptFrontendController::class, null, $rootPageId, 1 ); } if (false === is_array($this->typoScriptFrontendController->rootLine)) { $this->typoScriptFrontendController->rootLine = $objectManager->get(RootlineUtility::class, $rootPageId)->get(); } if (!$this->typoScriptFrontendController->sys_page instanceof PageRepository) { $this->typoScriptFrontendController->sys_page = $objectManager->get(PageRepository::class); } $GLOBALS['TSFE'] = $this->typoScriptFrontendController; if (!$GLOBALS['TSFE']->tmpl instanceof TemplateService) { /** @var \TYPO3\CMS\Core\TypoScript\TemplateService $templateService */ $templateService = $objectManager->get(TemplateService::class); $templateService->start($this->typoScriptFrontendController->rootLine); $GLOBALS['TSFE']->tmpl = $templateService; } return $this->typoScriptFrontendController; } }
Updated by Daniel Dorndorf over 5 years ago
Just got further into it and found out another problem that databaseRecordLinkBuilder isn't returning anything at all, but the RedirectService is expecting an array with key url.
public/typo3/sysext/frontend/Classes/Typolink/DatabaseRecordLinkBuilder.php:30 public/typo3/sysext/redirects/Classes/Service/RedirectService.php:248
Updated by Daniel Dorndorf over 5 years ago
Here the xclass workaround for the faulty redirectservice
<?php declare(strict_types=1); namespace Vendor\Extension\XClass; use Psr\Http\Message\UriInterface; use TYPO3\CMS\Core\Http\Uri; use TYPO3\CMS\Core\Site\Entity\SiteInterface; use TYPO3\CMS\Core\TypoScript\TemplateService; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\HttpUtility; use TYPO3\CMS\Core\Utility\RootlineUtility; use TYPO3\CMS\Extbase\Object\ObjectManager; use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; use TYPO3\CMS\Frontend\Page\PageRepository; use TYPO3\CMS\Frontend\Typolink\UnableToLinkException; use TYPO3\CMS\Redirects\Service\RedirectService as CoreRedirectService; /** * @package Extension * @subpackage XClass */ class RedirectService extends CoreRedirectService { /** * @param array $redirectRecord * @param \TYPO3\CMS\Core\Site\Entity\SiteInterface|null $site * @param array $linkDetails * @param array $queryParams * @return \Psr\Http\Message\UriInterface|null */ protected function getUriFromCustomLinkDetails(array $redirectRecord, ?SiteInterface $site, array $linkDetails, array $queryParams): ?UriInterface { if (!isset($linkDetails['type'], $GLOBALS['TYPO3_CONF_VARS']['FE']['typolinkBuilder'][$linkDetails['type']])) { return null; } /** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */ $objectManager = GeneralUtility::makeInstance(ObjectManager::class); $controller = $this->bootFrontendController($site, $queryParams); /** @var \TYPO3\CMS\Frontend\Typolink\AbstractTypolinkBuilder $linkBuilder */ $linkBuilder = $objectManager->get( $GLOBALS['TYPO3_CONF_VARS']['FE']['typolinkBuilder'][$linkDetails['type']], $controller->cObj, $controller ); try { $configuration = [ 'parameter' => (string) $redirectRecord['target'], 'forceAbsoluteUrl' => true, ]; if ($redirectRecord['force_https']) { $configuration['forceAbsoluteUrl.']['scheme'] = 'https'; } if ($redirectRecord['keep_query_parameters']) { $configuration['additionalParams'] = HttpUtility::buildQueryString($queryParams, '&'); } list($url) = $linkBuilder->build($linkDetails, '', '', $configuration); return new Uri($url); } catch (UnableToLinkException $exception) { try { $linkBuilderReflection = new \ReflectionClass($linkBuilder); $contentObjectRendererPropertyReflection = $linkBuilderReflection->getProperty('contentObjectRenderer'); $contentObjectRendererPropertyReflection->setAccessible(true); /** @var \TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer $contentObjectRenderer */ $contentObjectRenderer = $contentObjectRendererPropertyReflection->getValue($linkBuilder); $url = $contentObjectRenderer->lastTypoLinkUrl; if (false === empty($url)) { return new Uri($url); } } catch (\ReflectionException $e) { // nothing } return null; } } /** * @param \TYPO3\CMS\Core\Site\Entity\SiteInterface|null $site * @param array $queryParams * @return \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController */ protected function bootFrontendController(?SiteInterface $site, array $queryParams): TypoScriptFrontendController { $typoScriptFrontendController = parent::bootFrontendController($site, $queryParams); /** @var \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager */ $objectManager = GeneralUtility::makeInstance(ObjectManager::class); if (false === is_array($typoScriptFrontendController->rootLine)) { $typoScriptFrontendController->rootLine = $objectManager->get( RootlineUtility::class, $typoScriptFrontendController->id )->get(); } if (!$typoScriptFrontendController->sys_page instanceof PageRepository) { $typoScriptFrontendController->sys_page = $objectManager->get(PageRepository::class); } $GLOBALS['TSFE'] = $typoScriptFrontendController; if (!$GLOBALS['TSFE']->tmpl instanceof TemplateService) { /** @var \TYPO3\CMS\Core\TypoScript\TemplateService $templateService */ $templateService = $objectManager->get(TemplateService::class); $templateService->start($typoScriptFrontendController->rootLine); $GLOBALS['TSFE']->tmpl = $templateService; } return $typoScriptFrontendController; } }
Updated by Benni Mack over 5 years ago
Hey Daniel,
great input here. Thanks!
could you provide a patch for this change? (Except for booting up objectManager, can you elaborate why you needed to do this?)
Thanks.
Benni.
Updated by Daniel Dorndorf over 5 years ago
Hi Benni,
you are propably right, GeneralUtility::makeInstance should do the job.
For a patch I'm not 100% sure if it does not cause problems anywhere else.
In the DatabaseRecordLinkBuilder it always throws an exception but I dont know why this was necessary.
I'm missing some background information here :-/
In my extension I wrote a trait and xclassed all linkbuilder and the redirectservice so the module and the redirects are working, not sure if this causes problems somewhere else.
Updated by Gerrit Code Review about 5 years ago
- Status changed from Needs Feedback to Under Review
Patch set 1 for branch master of project Packages/TYPO3.CMS has been pushed to the review server.
It is available at https://review.typo3.org/c/Packages/TYPO3.CMS/+/62128
Updated by Gerrit Code Review about 5 years ago
Patch set 2 for branch master of project Packages/TYPO3.CMS has been pushed to the review server.
It is available at https://review.typo3.org/c/Packages/TYPO3.CMS/+/62128
Updated by Gerrit Code Review about 5 years ago
Patch set 1 for branch 9.5 of project Packages/TYPO3.CMS has been pushed to the review server.
It is available at https://review.typo3.org/c/Packages/TYPO3.CMS/+/62137
Updated by Benni Mack about 5 years ago
- Status changed from Under Review to Resolved
- % Done changed from 0 to 100
Applied in changeset d10eafa67d1e04297513986423074ef976526b1a.
Updated by Benni Mack almost 5 years ago
- Status changed from Resolved to Closed
Updated by Georg Ringer over 4 years ago
- Related to Feature #90818: Redirects - show config.recordLinks on target Links added