Project

General

Profile

Actions

Bug #87574

closed

Redirects to records (linkHandler) don't work

Added by Benjamin Robinson about 5 years ago. Updated over 4 years ago.

Status:
Closed
Priority:
Should have
Assignee:
-
Category:
Link Handling, Site Handling & Routing
Target version:
-
Start date:
2019-01-29
Due date:
% Done:

100%

Estimated time:
TYPO3 Version:
9
PHP Version:
Tags:
Complexity:
Is Regression:
Sprint Focus:

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&amp;uid=1">Link</a>

... and those are working fine.


Related issues 2 (0 open2 closed)

Related to TYPO3 Core - Bug #82798: Linkhandler for records works only when BE user is logged inClosed2017-10-18

Actions
Related to TYPO3 Core - Feature #90818: Redirects - show config.recordLinks on target LinksClosedGeorg Ringer2020-03-23

Actions
Actions #1

Updated by Benjamin Robinson about 5 years ago

  • Related to Bug #82798: Linkhandler for records works only when BE user is logged in added
Actions #2

Updated by Benjamin Robinson about 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?
TSconfig via page properties:
  • Tab not even available in link wizard of redirects module
Actions #3

Updated by Benni Mack about 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.

Actions #4

Updated by Benni Mack about 5 years ago

  • Status changed from New to Needs Feedback
Actions #5

Updated by Benjamin Robinson about 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?)
Actions #6

Updated by Daniel Dorndorf about 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;
Actions #7

Updated by Daniel Dorndorf about 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;
    }
}
Actions #8

Updated by Daniel Dorndorf about 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
Actions #9

Updated by Daniel Dorndorf about 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;
    }
}

Actions #10

Updated by Benni Mack about 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.

Actions #11

Updated by Daniel Dorndorf about 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.

Actions #12

Updated by Gerrit Code Review over 4 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

Actions #13

Updated by Gerrit Code Review over 4 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

Actions #14

Updated by Gerrit Code Review over 4 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

Actions #15

Updated by Benni Mack over 4 years ago

  • Status changed from Under Review to Resolved
  • % Done changed from 0 to 100
Actions #16

Updated by Benni Mack over 4 years ago

  • Status changed from Resolved to Closed
Actions #17

Updated by Georg Ringer about 4 years ago

  • Related to Feature #90818: Redirects - show config.recordLinks on target Links added
Actions

Also available in: Atom PDF