diff --git a/typo3/sysext/core/Classes/LinkHandling/EmailLinkHandler.php b/typo3/sysext/core/Classes/LinkHandling/EmailLinkHandler.php index f4de78cb1129..adb93c920921 100644 --- a/typo3/sysext/core/Classes/LinkHandling/EmailLinkHandler.php +++ b/typo3/sysext/core/Classes/LinkHandling/EmailLinkHandler.php @@ -41,6 +41,9 @@ public function asString(array $parameters): string */ public function resolveHandlerData(array $data): array { - return ['email' => substr($data['email'], 7)]; + if (stripos($data['email'], 'mailto:') === 0) { + return ['email' => substr($data['email'], 7)]; + } + return ['email' => $data['email']]; } } diff --git a/typo3/sysext/core/Classes/LinkHandling/FileLinkHandler.php b/typo3/sysext/core/Classes/LinkHandling/FileLinkHandler.php index b2799ebb5585..9a48839d73ac 100644 --- a/typo3/sysext/core/Classes/LinkHandling/FileLinkHandler.php +++ b/typo3/sysext/core/Classes/LinkHandling/FileLinkHandler.php @@ -15,6 +15,7 @@ * The TYPO3 project - inspiring people to share! */ use TYPO3\CMS\Core\Resource\Exception\FileDoesNotExistException; +use TYPO3\CMS\Core\Resource\FileInterface; use TYPO3\CMS\Core\Resource\ResourceFactory; use TYPO3\CMS\Core\Utility\GeneralUtility; @@ -70,19 +71,30 @@ public function asString(array $parameters): string */ public function resolveHandlerData(array $data): array { - if (isset($data['uid'])) { - $fileId = $data['uid']; - } else { - $fileId = $data['identifier']; - } try { - $file = $this->getResourceFactory()->getFileObject($fileId); + $file = $this->resolveFile($data); } catch (FileDoesNotExistException $e) { $file = null; } return ['file' => $file]; } + /** + * @param array $data + * @return FileInterface|null + * @throws FileDoesNotExistException + */ + protected function resolveFile(array $data): ?FileInterface + { + if (isset($data['uid'])) { + return $this->getResourceFactory()->getFileObject($data['uid']); + } + if (isset($data['identifier'])) { + return $this->getResourceFactory()->getFileObjectFromCombinedIdentifier($data['identifier']); + } + return null; + } + /** * Initializes the resource factory (only once) * diff --git a/typo3/sysext/core/Tests/Unit/LinkHandling/EmailLinkHandlerTest.php b/typo3/sysext/core/Tests/Unit/LinkHandling/EmailLinkHandlerTest.php index 6a2c3ea39812..8154a8d03809 100644 --- a/typo3/sysext/core/Tests/Unit/LinkHandling/EmailLinkHandlerTest.php +++ b/typo3/sysext/core/Tests/Unit/LinkHandling/EmailLinkHandlerTest.php @@ -28,23 +28,32 @@ class EmailLinkHandlerTest extends UnitTestCase public function resolveParametersForNonFilesDataProvider() { return [ + 'email without protocol' => [ + [ + 'email' => 'one@example.com' + ], + [ + 'email' => 'one@example.com' + ], + 'mailto:one@example.com' + ], 'email with protocol' => [ [ - 'email' => 'mailto:one@love.com' + 'email' => 'mailto:one@example.com' ], [ - 'email' => 'one@love.com' + 'email' => 'one@example.com' ], - 'mailto:one@love.com' + 'mailto:one@example.com' ], 'email with protocol 2' => [ [ - 'email' => 'mailto:info@typo3.org' + 'email' => 'mailto:info@example.org' ], [ - 'email' => 'info@typo3.org' + 'email' => 'info@example.org' ], - 'mailto:info@typo3.org' + 'mailto:info@example.org' ], ]; } diff --git a/typo3/sysext/core/Tests/Unit/LinkHandling/FileLinkHandlerTest.php b/typo3/sysext/core/Tests/Unit/LinkHandling/FileLinkHandlerTest.php index 5147d283149e..b9c778dea25d 100644 --- a/typo3/sysext/core/Tests/Unit/LinkHandling/FileLinkHandlerTest.php +++ b/typo3/sysext/core/Tests/Unit/LinkHandling/FileLinkHandlerTest.php @@ -84,6 +84,7 @@ public function resolveFileReferencesToSplitParameters($input, $expected, $final // fake methods to return proper objects $fileObject = new File(['identifier' => $expected['file'], 'name' => 'foobar.txt'], $storage); $factory->expects($this->any())->method('getFileObject')->with($expected['file'])->willReturn($fileObject); + $factory->expects($this->any())->method('getFileObjectFromCombinedIdentifier')->with($expected['file'])->willReturn($fileObject); $expected['file'] = $fileObject; /** @var FileLinkHandler|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\TestingFramework\Core\AccessibleObjectInterface $subject */ diff --git a/typo3/sysext/frontend/Tests/Functional/SiteHandling/TypoLinkTest.php b/typo3/sysext/frontend/Tests/Functional/SiteHandling/TypoLinkTest.php new file mode 100644 index 000000000000..af82bad7a29e --- /dev/null +++ b/typo3/sysext/frontend/Tests/Functional/SiteHandling/TypoLinkTest.php @@ -0,0 +1,250 @@ + 'fileadmin/logo.png' + ]; + + public static function setUpBeforeClass(): void + { + parent::setUpBeforeClass(); + static::initializeDatabaseSnapshot(); + } + + public static function tearDownAfterClass(): void + { + static::destroyDatabaseSnapshot(); + parent::tearDownAfterClass(); + } + + protected function setUp(): void + { + parent::setUp(); + + // these settings are forwarded to the frontend sub-request as well + $this->internalRequestContext = (new InternalRequestContext()) + ->withGlobalSettings(['TYPO3_CONF_VARS' => static::TYPO3_CONF_VARS]); + + $this->writeSiteConfiguration( + 'acme-com', + $this->buildSiteConfiguration(1000, 'https://acme.com/'), + [ + $this->buildDefaultLanguageConfiguration('EN', 'https://acme.us/'), + $this->buildLanguageConfiguration('FR', 'https://acme.fr/', ['EN']), + $this->buildLanguageConfiguration('FR-CA', 'https://acme.ca/', ['FR', 'EN']), + ] + ); + + $this->withDatabaseSnapshot(function () { + $this->setUpDatabase(); + }); + } + + protected function setUpDatabase() + { + $backendUser = $this->setUpBackendUserFromFixture(1); + Bootstrap::initializeLanguageObject(); + + $scenarioFile = __DIR__ . '/Fixtures/SlugScenario.yaml'; + $factory = DataHandlerFactory::fromYamlFile($scenarioFile); + $writer = DataHandlerWriter::withBackendUser($backendUser); + $writer->invokeFactory($factory); + static::failIfArrayIsNotEmpty( + $writer->getErrors() + ); + + // @todo Provide functionality of assigning TSconfig to Testing Framework + $connection = GeneralUtility::makeInstance(ConnectionPool::class) + ->getConnectionForTable('pages'); + /** @var $connection \TYPO3\CMS\Core\Database\Connection */ + $connection->update( + 'pages', + ['TSconfig' => implode(chr(10), [ + 'TCEMAIN.linkHandler.content {', + ' configuration.table = tt_content', + '}', + ])], + ['uid' => 1000] + ); + + $this->setUpFileStorage(); + $this->setUpFrontendRootPage( + 1000, + [ + 'typo3/sysext/frontend/Tests/Functional/SiteHandling/Fixtures/LinkGenerator.typoscript', + ], + [ + 'title' => 'ACME Root', + 'sitetitle' => $this->siteTitle, + ] + ); + } + + /** + * @todo Provide functionality of creating and indexing fileadmin/ in Testing Framework + */ + private function setUpFileStorage() + { + $storageRepository = new StorageRepository(); + $storageId = $storageRepository->createLocalStorage( + 'fileadmin/ (auto-created)', + 'fileadmin/', + 'relative', + 'Default storage created in TypoLinkTest', + true + ); + $storage = $storageRepository->findByUid($storageId); + (new Indexer($storage))->processChangesInStorages(); + } + + protected function tearDown(): void + { + unset($this->internalRequestContext); + parent::tearDown(); + } + + /** + * @return array + */ + public function linkIsGeneratedDataProvider(): array + { + $instructions = [ + [ + 't3://email?email=mailto:user@example.org&other=other#other', + 'user@example.org', + ], + [ + 't3://email?email=user@example.org&other=other#other', + 'user@example.org', + ], + [ + 't3://file?uid=1&type=1&other=other#other', + 'fileadmin/logo.png', + ], + [ + 't3://file?identifier=1:/logo.png&other=other#other', + 'fileadmin/logo.png', + ], + [ + 't3://file?identifier=fileadmin/logo.png&other=other#other', + 'fileadmin/logo.png', + ], + [ + 't3://folder?identifier=fileadmin&other=other#other', + 'fileadmin/', + ], + [ + 't3://page?uid=1200&type=1¶m-a=a¶m-b=b#fragment', + 'EN: Features', + ], + [ + 't3://record?identifier=content&uid=10001&other=other#fragment', + 'EN: Features', + ], + [ + 't3://url?url=https://typo3.org&other=other#other', + 'https://typo3.org', + ], + ]; + return $this->keysFromTemplate($instructions, '%1$s;'); + } + + /** + * @param string $parameter + * @param string $expectation + * + * @test + * @dataProvider linkIsGeneratedDataProvider + */ + public function linkIsGenerated(string $parameter, string $expectation) + { + $sourcePageId = 1100; + + $response = $this->executeFrontendRequest( + (new InternalRequest('https://acme.us/')) + ->withPageId($sourcePageId) + ->withInstructions([ + (new TypoScriptInstruction(TemplateService::class))->withTypoScript([ + 'config.' => [ + 'recordLinks.' => [ + 'content.' => [ + 'forceLink' => 1, + 'typolink.' => [ + 'parameter' => 1200, + 'section.' => [ + 'data' => 'field:uid', + 'wrap' => 'c|', + ], + ], + ], + ], + ], + ]), + $this->createTypoLinkInstruction([ + 'parameter' => $parameter, + ]) + ]), + $this->internalRequestContext + ); + + static::assertSame($expectation, (string)$response->getBody()); + } + + /** + * @param array $typoLink + * @return ArrayValueInstruction + */ + private function createTypoLinkInstruction(array $typoLink): ArrayValueInstruction + { + return (new ArrayValueInstruction(LinkHandlingController::class)) + ->withArray([ + '10' => 'TEXT', + '10.' => [ + 'typolink.' => $typoLink + ] + ]); + } +}