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
+ ]
+ ]);
+ }
+}