diff --git a/typo3/sysext/backend/Classes/Form/Element/InputSlugElement.php b/typo3/sysext/backend/Classes/Form/Element/InputSlugElement.php index 19670964d267f0a663eb5f1a82afd0b640873d6d..431631f4d921565711cd6f50b0c22cfa9276c942 100644 --- a/typo3/sysext/backend/Classes/Form/Element/InputSlugElement.php +++ b/typo3/sysext/backend/Classes/Form/Element/InputSlugElement.php @@ -18,13 +18,12 @@ namespace TYPO3\CMS\Backend\Form\Element; use TYPO3\CMS\Backend\Controller\FormSlugAjaxController; use TYPO3\CMS\Core\Imaging\Icon; use TYPO3\CMS\Core\Localization\LanguageService; -use TYPO3\CMS\Core\Site\Entity\SiteInterface; use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Core\Utility\MathUtility; use TYPO3\CMS\Core\Utility\StringUtility; /** - * General type=input element with some additional value. + * General type=input element for TCA Type=Slug with some additional value. */ class InputSlugElement extends AbstractFormElement { @@ -80,13 +79,13 @@ class InputSlugElement extends AbstractFormElement $languageField = $GLOBALS['TCA'][$table]['ctrl']['languageField']; $languageId = (int)((is_array($row[$languageField]) ? $row[$languageField][0] : $row[$languageField]) ?? 0); } - $baseUrl = $this->getPrefix($this->data['site'], $languageId); $itemValue = $parameterArray['itemFormElValue']; $config = $parameterArray['fieldConf']['config']; $evalList = GeneralUtility::trimExplode(',', $config['eval'], true); $size = MathUtility::forceIntegerInRange($config['size'] ?? $this->defaultInputWidth, $this->minimumInputWidth, $this->maxInputWidth); $width = (int)$this->formMaxWidth($size); + $baseUrl = $this->data['customData'][$this->data['fieldName']]['slugPrefix'] ?? ''; // Convert UTF-8 characters back (that is important, see Slug class when sanitizing) $itemValue = rawurldecode($itemValue); @@ -220,30 +219,6 @@ class InputSlugElement extends AbstractFormElement return $resultArray; } - /** - * Render the prefix for the input field. - * - * @param SiteInterface $site - * @param int $requestLanguageId - * @return string - */ - protected function getPrefix(SiteInterface $site, int $requestLanguageId = 0): string - { - try { - $language = ($requestLanguageId < 0) ? $site->getDefaultLanguage() : $site->getLanguageById($requestLanguageId); - $base = $language->getBase(); - $baseUrl = (string)$base; - $baseUrl = rtrim($baseUrl, '/'); - if (!empty($baseUrl) && empty($base->getScheme()) && $base->getHost() !== '') { - $baseUrl = 'http:' . $baseUrl; - } - } catch (\InvalidArgumentException $e) { - // No site found - $baseUrl = ''; - } - return $baseUrl; - } - /** * @return LanguageService */ diff --git a/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSlug.php b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSlug.php new file mode 100644 index 0000000000000000000000000000000000000000..a96852c169fbaa0ea79a30e501145a288a76cc17 --- /dev/null +++ b/typo3/sysext/backend/Classes/Form/FormDataProvider/TcaSlug.php @@ -0,0 +1,92 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Backend\Form\FormDataProvider; + +/* + * 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 TYPO3\CMS\Backend\Form\FormDataProviderInterface; +use TYPO3\CMS\Core\Site\Entity\SiteInterface; +use TYPO3\CMS\Core\Utility\GeneralUtility; + +/** + * Handles custom data for TCA Type=Slug + */ +class TcaSlug implements FormDataProviderInterface +{ + /** + * Resolve slug prefix items + * + * @param array $result + * + * @return array + * @throws \UnexpectedValueException + */ + public function addData(array $result): array + { + $table = $result['tableName']; + $site = $result['site']; + $row = $result['databaseRow']; + $languageId = 0; + + if (($result['processedTca']['ctrl']['languageField'] ?? '') !== '') { + $languageField = $result['processedTca']['ctrl']['languageField']; + $languageId = (int)((is_array($row[$languageField]) ? $row[$languageField][0] : $row[$languageField]) ?? 0); + } + + foreach ($result['processedTca']['columns'] as $fieldName => $fieldConfig) { + if (($fieldConfig['config']['type'] ?? '') !== 'slug') { + continue; + } + + $prefix = $fieldConfig['config']['appearance']['prefix'] ?? ''; + + if ($prefix !== '') { + $parameters = ['site' => $site, 'languageId' => $languageId, 'table' => $table, 'row' => $row]; + $prefix = GeneralUtility::callUserFunction($prefix, $parameters, $this); + } elseif ($site instanceof SiteInterface) { + // default behaviour used for pages + $prefix = $this->getPrefixForSite($site, $languageId); + } + + $result['customData'][$fieldName]['slugPrefix'] = $prefix; + $result['processedTca']['columns'][$fieldName]['config']['appearance']['prefix'] = $prefix; + } + + return $result; + } + + /** + * Render the prefix for the input field. + * + * @param SiteInterface $site + * @param int $languageId + * @return string + */ + protected function getPrefixForSite(SiteInterface $site, int $languageId): string + { + try { + $language = ($languageId < 0) ? $site->getDefaultLanguage() : $site->getLanguageById($languageId); + $base = $language->getBase(); + $prefix = rtrim((string)$base, '/'); + if ($prefix !== '' && empty($base->getScheme()) && $base->getHost() !== '') { + $prefix = 'http:' . $prefix; + } + } catch (\InvalidArgumentException $e) { + // No site found + $prefix = ''; + } + + return $prefix; + } +} diff --git a/typo3/sysext/backend/Tests/Unit/Form/Element/InputSlugElementTest.php b/typo3/sysext/backend/Tests/Unit/Form/Element/InputSlugElementTest.php deleted file mode 100644 index dd7dd9528241aae4700b595d3d22db3ce690913f..0000000000000000000000000000000000000000 --- a/typo3/sysext/backend/Tests/Unit/Form/Element/InputSlugElementTest.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -declare(strict_types=1); -namespace TYPO3\CMS\Backend\Tests\Unit\Form\Element; - -/* - * 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 TYPO3\CMS\Backend\Form\Element\InputSlugElement; -use TYPO3\CMS\Core\Site\Entity\Site; -use TYPO3\TestingFramework\Core\Unit\UnitTestCase; - -/** - * Tests for InputSlugElement Form - */ -class InputSlugElementTest extends UnitTestCase -{ - /** - * @test - */ - public function getPrefixReturnsDefaultBaseUrlForAllDefinedLanguagesAndMinusOne(): void - { - $languages = [ - [ - 'languageId' => 0, - 'locale' => 'en_US.UTF-8', - 'base' => '/en/' - ], - [ - 'languageId' => 1, - 'locale' => 'de_DE.UTF-8', - 'base' => '/de/' - ] - ]; - - $site = new Site('www.foo.de', 0, [ - 'languages' => $languages - ]); - - $subject = $this->getAccessibleMock( - InputSlugElement::class, - ['dummy'], - [], - '', - false - ); - - self::assertSame('/en', $subject->_call('getPrefix', $site, -1)); - self::assertSame('/en', $subject->_call('getPrefix', $site, 0)); - self::assertSame('/de', $subject->_call('getPrefix', $site, 1)); - } -} diff --git a/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSlugTest.php b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSlugTest.php new file mode 100644 index 0000000000000000000000000000000000000000..8658bb8dddbbe4a5d0ec31ba13994623edb4adb7 --- /dev/null +++ b/typo3/sysext/backend/Tests/Unit/Form/FormDataProvider/TcaSlugTest.php @@ -0,0 +1,368 @@ +<?php +declare(strict_types=1); + +namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataProvider; + +/* + * 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 TYPO3\CMS\Backend\Form\FormDataProvider\TcaSlug; +use TYPO3\CMS\Core\Site\Entity\Site; +use TYPO3\TestingFramework\Core\Unit\UnitTestCase; + +/** + * Test case + */ +class TcaSlugTest extends UnitTestCase +{ + /** + * @var TcaSlug + */ + protected $subject; + + protected function setUp(): void + { + parent::setUp(); + $this->subject = new TcaSlug(); + } + + /** + * Data provider for getSlugPrefixForDefinedLanguagesAndUserFunc + * + * @return array [$input, $expected] + */ + public function resultArrayDataProvider(): array + { + return [ + 'Language default [0]' => [ + [ + 'tableName' => 'aTable', + 'site' => new Site('www.foo.de', 0, [ + 'languages' => [ + [ + 'languageId' => 0, + 'locale' => 'en_US.UTF-8', + 'base' => '/en/' + ], + [ + 'languageId' => 1, + 'locale' => 'de_DE.UTF-8', + 'base' => '/de/' + ] + ] + ]), + 'databaseRow' => [ + 'sys_language_uid' => [0] + ], + 'processedTca' => [ + 'ctrl' => [ + 'languageField' => 'sys_language_uid' + ], + 'columns' => [ + 'slugField' => [ + 'config' => [ + 'type' => 'slug' + ] + ] + ] + ] + ], + [ + 'tableName' => 'aTable', + 'site' => new Site('www.foo.de', 0, [ + 'languages' => [ + [ + 'languageId' => 0, + 'locale' => 'en_US.UTF-8', + 'base' => '/en/' + ], + [ + 'languageId' => 1, + 'locale' => 'de_DE.UTF-8', + 'base' => '/de/' + ] + ] + ]), + 'databaseRow' => [ + 'sys_language_uid' => [0] + ], + 'processedTca' => [ + 'ctrl' => [ + 'languageField' => 'sys_language_uid' + ], + 'columns' => [ + 'slugField' => [ + 'config' => [ + 'type' => 'slug', + 'appearance' => [ + 'prefix' => '/en' + ] + ] + ] + ] + ], + 'customData' => [ + 'slugField' => [ + 'slugPrefix' => '/en' + ] + ] + ] + ], + 'Language 1' => [ + [ + 'tableName' => 'aTable', + 'site' => new Site('www.foo.de', 0, [ + 'languages' => [ + [ + 'languageId' => 0, + 'locale' => 'en_US.UTF-8', + 'base' => '/en/' + ], + [ + 'languageId' => 1, + 'locale' => 'de_DE.UTF-8', + 'base' => '/de/' + ] + ] + ]), + 'databaseRow' => [ + 'sys_language_uid' => [1] + ], + 'processedTca' => [ + 'ctrl' => [ + 'languageField' => 'sys_language_uid' + ], + 'columns' => [ + 'slugField' => [ + 'config' => [ + 'type' => 'slug' + ] + ] + ] + ] + ], + [ + 'tableName' => 'aTable', + 'site' => new Site('www.foo.de', 0, [ + 'languages' => [ + [ + 'languageId' => 0, + 'locale' => 'en_US.UTF-8', + 'base' => '/en/' + ], + [ + 'languageId' => 1, + 'locale' => 'de_DE.UTF-8', + 'base' => '/de/' + ] + ] + ]), + 'databaseRow' => [ + 'sys_language_uid' => [1] + ], + 'processedTca' => [ + 'ctrl' => [ + 'languageField' => 'sys_language_uid' + ], + 'columns' => [ + 'slugField' => [ + 'config' => [ + 'type' => 'slug', + 'appearance' => [ + 'prefix' => '/de' + ] + ] + ] + ] + ], + 'customData' => [ + 'slugField' => [ + 'slugPrefix' => '/de' + ] + ] + ] + ], + 'Language -1' => [ + [ + 'tableName' => 'aTable', + 'site' => new Site('www.foo.de', 0, [ + 'languages' => [ + [ + 'languageId' => 0, + 'locale' => 'en_US.UTF-8', + 'base' => '/en/' + ], + [ + 'languageId' => 1, + 'locale' => 'de_DE.UTF-8', + 'base' => '/de/' + ] + ] + ]), + 'databaseRow' => [ + 'sys_language_uid' => [-1] + ], + 'processedTca' => [ + 'ctrl' => [ + 'languageField' => 'sys_language_uid' + ], + 'columns' => [ + 'slugField' => [ + 'config' => [ + 'type' => 'slug' + ] + ] + ] + ] + ], + [ + 'tableName' => 'aTable', + 'site' => new Site('www.foo.de', 0, [ + 'languages' => [ + [ + 'languageId' => 0, + 'locale' => 'en_US.UTF-8', + 'base' => '/en/' + ], + [ + 'languageId' => 1, + 'locale' => 'de_DE.UTF-8', + 'base' => '/de/' + ] + ] + ]), + 'databaseRow' => [ + 'sys_language_uid' => [-1] + ], + 'processedTca' => [ + 'ctrl' => [ + 'languageField' => 'sys_language_uid' + ], + 'columns' => [ + 'slugField' => [ + 'config' => [ + 'type' => 'slug', + 'appearance' => [ + 'prefix' => '/en' + ] + ] + ] + ] + ], + 'customData' => [ + 'slugField' => [ + 'slugPrefix' => '/en' + ] + ] + ] + ], + 'UserFunc' => [ + [ + 'tableName' => 'aTable', + 'site' => new Site('www.foo.de', 0, [ + 'languages' => [ + [ + 'languageId' => 0, + 'locale' => 'en_US.UTF-8', + 'base' => '/en/' + ], + [ + 'languageId' => 1, + 'locale' => 'de_DE.UTF-8', + 'base' => '/de/' + ] + ] + ]), + 'databaseRow' => [ + 'sys_language_uid' => [0] + ], + 'processedTca' => [ + 'ctrl' => [ + 'languageField' => 'sys_language_uid' + ], + 'columns' => [ + 'slugField' => [ + 'config' => [ + 'type' => 'slug', + 'appearance' => [ + 'prefix' => function (array $parameters, TcaSlug $reference): string { + return $parameters['site']->getIdentifier() + . '-' + . $parameters['languageId'] + . '-' + . $parameters['table'] + . '-' + . $parameters['row']['sys_language_uid'][0]; + } + ] + ] + ] + ] + ] + ], + [ + 'tableName' => 'aTable', + 'site' => new Site('www.foo.de', 0, [ + 'languages' => [ + [ + 'languageId' => 0, + 'locale' => 'en_US.UTF-8', + 'base' => '/en/' + ], + [ + 'languageId' => 1, + 'locale' => 'de_DE.UTF-8', + 'base' => '/de/' + ] + ] + ]), + 'databaseRow' => [ + 'sys_language_uid' => [0] + ], + 'processedTca' => [ + 'ctrl' => [ + 'languageField' => 'sys_language_uid' + ], + 'columns' => [ + 'slugField' => [ + 'config' => [ + 'type' => 'slug', + 'appearance' => [ + 'prefix' => 'www.foo.de-0-aTable-0' + ] + ] + ] + ] + ], + 'customData' => [ + 'slugField' => [ + 'slugPrefix' => 'www.foo.de-0-aTable-0' + ] + ] + ] + ] + ]; + } + + /** + * @test + * @dataProvider resultArrayDataProvider + * + * @param array $input + * @param array $expected + */ + public function getSlugPrefixForDefinedLanguagesAndUserFunc(array $input, array $expected): void + { + self::assertEquals($expected, $this->subject->addData($input)); + } +} diff --git a/typo3/sysext/core/Configuration/DefaultConfiguration.php b/typo3/sysext/core/Configuration/DefaultConfiguration.php index 27a861f8ecda923735252e87cc6d3ef74394f2a0..7b020508b4de4888832ff0d09d5a04bad32b3a7e 100644 --- a/typo3/sysext/core/Configuration/DefaultConfiguration.php +++ b/typo3/sysext/core/Configuration/DefaultConfiguration.php @@ -567,6 +567,12 @@ return [ \TYPO3\CMS\Backend\Form\FormDataProvider\TcaRadioItems::class ], ], + \TYPO3\CMS\Backend\Form\FormDataProvider\TcaSlug::class => [ + 'depends' => [ + \TYPO3\CMS\Backend\Form\FormDataProvider\DatabaseRecordOverrideValues::class, + \TYPO3\CMS\Backend\Form\FormDataProvider\TcaTypesShowitem::class, + ], + ], \TYPO3\CMS\Backend\Form\FormDataProvider\TcaGroup::class => [ 'depends' => [ \TYPO3\CMS\Backend\Form\FormDataProvider\DatabaseRecordOverrideValues::class, diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-89573-AllowFlexibleBaseUrlForSlugFieldsInFormEngine.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-89573-AllowFlexibleBaseUrlForSlugFieldsInFormEngine.rst new file mode 100644 index 0000000000000000000000000000000000000000..6fa4185f3b956d4f819afe4cc8ee147d053b5319 --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-89573-AllowFlexibleBaseUrlForSlugFieldsInFormEngine.rst @@ -0,0 +1,58 @@ +.. include:: ../../Includes.txt + +======================================================================= +Feature: #89573 - Allow flexible base url for slug fields in FormEngine +======================================================================= + +See :issue:`89573` + +Description +=========== + +It is now possible to add a custom base url for TCA columns of type `slug`. The +base url is displayed in front of the input field in FormEngine. + +To add a custom base url a :php:`userFunc` can be assigned to the new setting +`prefix` which is available under :php:`['columns'][*]['config']['appearance']`. + +.. code-block:: php + + 'config' => [ + 'type' => 'slug', + 'appearance' => [ + 'prefix' => \Vendor\Extension\UserFunctions\FormEngine\SlugPrefix::class . '->getPrefix' + ] + ] + +The :php:`userFunc` receives two parameters. The first parameter is the parameters +array containing the site object, the language id, the current table and the +current row. The second parameter is the reference object :php:`TcaSlug`. The +:php:`userFunc` should return the string which is then used as the base url in +FormEngine. + +.. code-block:: php + + <?php + declare(strict_types = 1); + + namespace Vendor\Extension\UserFunctions\FormEngine + + use TYPO3\CMS\Backend\Form\FormDataProvider\TcaSlug; + + class SlugPrefix + { + public function getPrefix(array $parameters, TcaSlug $reference): string + { + return 'custom base url'; + } + } + + +Impact +====== + +Developers are enabled to provide custom base urls for their slug fields. If you +are already using slug fields in your TCA, nothing changes as the current +behaviour is still used as the default. + +.. index:: Backend, PHP-API, TCA, ext:backend