[BUGFIX] Use params set in db in recordLinkHandler 83/57883/2
authorSusanne Moog <susanne.moog@typo3.org>
Mon, 25 Jun 2018 18:39:31 +0000 (20:39 +0200)
committerAndreas Fernandez <a.fernandez@scripting-base.de>
Mon, 13 Aug 2018 10:20:18 +0000 (12:20 +0200)
Resolving of links with a custom record link handler is now done
in the following order:

- TypoScriptConfiguration Parameters
- Parameters set in link field in content element
- If param is empty in link field but set in config that is taken into
account as a fallback
- It is _not_ possible to unset a value (class, title, target, link) in
the link field if it _is_ set to a default value in TypoScript

Caveat: Overwriting attributes only works if they are set via parameter
in configuration _not_ if they are set via ATagParams.

Resolves: #81620
Releases: master, 8.7
Change-Id: Ie723e7d7d36a03bac4ec97211749317d7311dd3e
Reviewed-on: https://review.typo3.org/57883
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
typo3/sysext/frontend/Classes/Typolink/DatabaseRecordLinkBuilder.php
typo3/sysext/frontend/Tests/TYPO3/CMS/Frontend/Tests/Unit/Typolink/DatabaseRecordLinkBuilderTest.php [new file with mode: 0644]

index 85bf051..b397650 100644 (file)
@@ -17,6 +17,7 @@ namespace TYPO3\CMS\Frontend\Typolink;
 
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\Service\TypoLinkCodecService;
 
 /**
  * Builds a TypoLink to a database record
@@ -62,6 +63,14 @@ class DatabaseRecordLinkBuilder extends AbstractTypolinkBuilder
         // Unset the parameter part of the given TypoScript configuration while keeping
         // config that has been set in addition.
         unset($conf['parameter.']);
+
+        $typoLinkCodecService = GeneralUtility::makeInstance(TypoLinkCodecService::class);
+        $parameterFromDb = $typoLinkCodecService->decode($conf['parameter']);
+        unset($parameterFromDb['url']);
+        $parameterFromTypoScript = $typoLinkCodecService->decode($typoScriptConfiguration['parameter']);
+        $parameter = array_replace_recursive($parameterFromTypoScript, array_filter($parameterFromDb));
+        $typoScriptConfiguration['parameter'] = $typoLinkCodecService->encode($parameter);
+
         $typoScriptConfiguration = array_replace_recursive($conf, $typoScriptConfiguration);
 
         if (!empty($linkDetails['fragment'])) {
diff --git a/typo3/sysext/frontend/Tests/TYPO3/CMS/Frontend/Tests/Unit/Typolink/DatabaseRecordLinkBuilderTest.php b/typo3/sysext/frontend/Tests/TYPO3/CMS/Frontend/Tests/Unit/Typolink/DatabaseRecordLinkBuilderTest.php
new file mode 100644 (file)
index 0000000..c318c9d
--- /dev/null
@@ -0,0 +1,197 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Frontend\Tests\Unit\Typolink;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use Prophecy\Argument;
+use TYPO3\CMS\Core\TypoScript\TemplateService;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
+use TYPO3\CMS\Frontend\Page\PageRepository;
+use TYPO3\CMS\Frontend\Typolink\DatabaseRecordLinkBuilder;
+use TYPO3\CMS\Frontend\Typolink\UnableToLinkException;
+use TYPO3\CMS\Recordlist\LinkHandler\RecordLinkHandler;
+use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
+
+/**
+ * Tests for DatabaseRecordLinkBuilderTest
+ */
+class DatabaseRecordLinkBuilderTest extends UnitTestCase
+{
+
+    /**
+     * Dataprovider with different parameter configurations
+     *
+     * @return array
+     */
+    public function attributesSetInRecordLinkOverwriteConfiguredAttributesDataProvider(): array
+    {
+        return [
+            'attributes from db overwrite config' => [
+                '27 tsTarget tsClass tsTitle',
+                't3://record?identifier=tx_news&uid=1 dbTarget dbClass dbTitle',
+                '27 dbTarget dbClass dbTitle',
+            ],
+            'no attributes from db - config is taken' => [
+                '27 tsTarget tsClass tsTitle',
+                't3://record?identifier=tx_news&uid=1',
+                '27 tsTarget tsClass tsTitle',
+            ],
+            'mixed: target from db' => [
+                '27 tsTarget tsClass tsTitle',
+                't3://record?identifier=tx_news&uid=1 dbTarget',
+                '27 dbTarget tsClass tsTitle',
+            ],
+            'mixed: class from db' => [
+                '27 tsTarget tsClass tsTitle',
+                't3://record?identifier=tx_news&uid=1 - dbClass',
+                '27 tsTarget dbClass tsTitle',
+            ],
+            'mixed: title from db' => [
+                '27 tsTarget tsClass tsTitle',
+                't3://record?identifier=tx_news&uid=1 - - dbTitle',
+                '27 tsTarget tsClass dbTitle',
+            ],
+            'mixed: target and title from db' => [
+                '27 tsTarget tsClass tsTitle',
+                't3://record?identifier=tx_news&uid=1 dbTarget - dbTitle',
+                '27 dbTarget tsClass dbTitle',
+            ],
+            'mixed: target and title from db, no class set' => [
+                '27 tsTarget - tsTitle',
+                't3://record?identifier=tx_news&uid=1 dbTarget - dbTitle',
+                '27 dbTarget - dbTitle',
+            ],
+            'mixed: title from db, no config set' => [
+                '27',
+                't3://record?identifier=tx_news&uid=1 - - dbTitle',
+                '27 - - dbTitle',
+            ],
+            'no attributes configured' => [
+                '27',
+                't3://record?identifier=tx_news&uid=1',
+                '27',
+            ],
+
+        ];
+    }
+
+    /**
+     * @test
+     *
+     * Tests showing that values set in the link record directly will overwrite those configured
+     * in the default link handler configuration
+     *
+     * Note that the TypolinkCodecService is not mocked on purpose to get the full unit tested.
+     *
+     * @dataProvider attributesSetInRecordLinkOverwriteConfiguredAttributesDataProvider
+     * @param string $parameterFromTypoScript
+     * @param string $parameterFromDb
+     * @param string $expectedParameter
+     */
+    public function attributesSetInRecordLinkOverwriteConfiguredAttributes(string $parameterFromTypoScript, string $parameterFromDb, string $expectedParameter): void
+    {
+        $confFromDb = [
+            'parameter' => $parameterFromDb,
+        ];
+        $extractedLinkDetails = [
+            'identifier' => 'tx_news',
+            'uid' => '1',
+            'type' => 'record',
+            'typoLinkParameter' => 't3://record?identifier=tx_news&uid=1',
+        ];
+        $typoScriptConfig = [
+            'config.' => [
+                'recordLinks.' => [
+                    'tx_news.' =>
+                        [
+                            'forceLink' => '0',
+                            'typolink.' =>
+                                [
+                                    'parameter' => $parameterFromTypoScript,
+                                    'additionalParams' => '&tx_news_pi1[news]={field:uid}',
+                                    'additionalParams.' =>
+                                        [
+                                            'insertData' => '1',
+                                        ],
+                                ],
+                        ],
+                ],
+            ],
+        ];
+        $pageTsConfig = [
+            'TCEMAIN.' =>
+                [
+                    'linkHandler.' =>
+                        [
+
+                            'tx_news.' =>
+                                [
+                                    'handler' => RecordLinkHandler::class,
+                                    'label' => 'News',
+                                    'configuration.' =>
+                                        [
+                                            'table' => 'tx_news_domain_model_news',
+                                        ],
+                                    'scanAfter' => 'page',
+                                ],
+                        ],
+                ],
+
+        ];
+        $target = '';
+        $linkText = 'Test Link';
+
+        $expectedConfiguration = [
+            'parameter' => $expectedParameter,
+            'additionalParams' => '&tx_news_pi1[news]={field:uid}',
+            'additionalParams.' => ['insertData' => '1'],
+        ];
+
+        // Arrange
+        $tsfe = $this->prophesize(TypoScriptFrontendController::class);
+        $templateService = $this->prophesize(TemplateService::class);
+        $pageRepository = $this->prophesize(PageRepository::class);
+        $cObj = $this->prophesize(ContentObjectRenderer::class);
+
+        $GLOBALS['TSFE'] = $tsfe->reveal();
+        $tsfe->tmpl = $templateService->reveal();
+        $tsfe->tmpl->setup = $typoScriptConfig;
+        $tsfe->sys_page = $pageRepository->reveal();
+        GeneralUtility::addInstance(ContentObjectRenderer::class, $cObj->reveal());
+
+        $pageRepository->checkRecord('tx_news_domain_model_news', 1)->willReturn(
+            [
+                'uid' => '1',
+            ]
+        );
+
+        $cObj->start(Argument::cetera())->shouldBeCalled();
+        $cObj->typoLink(Argument::cetera())->shouldBeCalled();
+
+        $tsfe->getPagesTSconfig()->willReturn($pageTsConfig);
+
+        // Act
+        $databaseRecordLinkBuilder = new DatabaseRecordLinkBuilder($cObj->reveal());
+        try {
+            $databaseRecordLinkBuilder->build($extractedLinkDetails, $linkText, $target, $confFromDb);
+        } catch (UnableToLinkException $exception) {
+            // Assert
+            $cObj->typoLink($linkText, $expectedConfiguration)->shouldHaveBeenCalled();
+        }
+    }
+}