diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 0426b38..c3b9156 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -7,7 +7,6 @@ on: - cron: '30 3 * * *' jobs: - tests: name: All tests runs-on: ubuntu-20.04 @@ -15,6 +14,9 @@ jobs: matrix: php: [ '8.1', '8.2'] TYPO3: [ '11', '12'] + include: + - TYPO3: 12 + PHP: 8.3 steps: - name: Checkout repository uses: actions/checkout@v2 diff --git a/Build/Scripts/runTests.sh b/Build/Scripts/runTests.sh index 5d3c2cd..1343efb 100755 --- a/Build/Scripts/runTests.sh +++ b/Build/Scripts/runTests.sh @@ -5,7 +5,7 @@ # # Function to write a .env file in Build/testing-docker -# This is read by docker-compose and vars defined here are +# This is read by "docker compose" and vars defined here are # used in Build/testing-docker/docker-compose.yml setUpDockerComposeDotEnv() { # Delete possibly existing local .env file if exists @@ -14,7 +14,7 @@ setUpDockerComposeDotEnv() { { echo "COMPOSE_PROJECT_NAME=local" # To prevent access rights of files created by the testing, the docker image later - # runs with the same user that is currently executing the script. docker-compose can't + # runs with the same user that is currently executing the script. "docker compose" can't # use $UID directly itself since it is a shell variable and not an env variable, so # we have to set it explicitly here. echo "HOST_UID=`id -u`" @@ -24,10 +24,10 @@ setUpDockerComposeDotEnv() { # Your local user echo "HOST_USER=${USER}" echo "TEST_FILE=${TEST_FILE}" + echo "TYPO3_VERSION=${TYPO3_VERSION}" echo "PHP_XDEBUG_ON=${PHP_XDEBUG_ON}" echo "PHP_XDEBUG_PORT=${PHP_XDEBUG_PORT}" echo "DOCKER_PHP_IMAGE=${DOCKER_PHP_IMAGE}" - echo "TYPO3=${TYPO3}" echo "PHP_VERSION=${PHP_VERSION}" echo "EXTRA_TEST_OPTIONS=${EXTRA_TEST_OPTIONS}" echo "SCRIPT_VERBOSE=${SCRIPT_VERBOSE}" @@ -44,7 +44,7 @@ Successfully tested with docker version 18.06.1-ce and docker-compose 1.21.2. Usage: $0 [options] [file] -No arguments: Run all unit tests with PHP 7.4 +No arguments: Run all unit tests with PHP 8.0 Options: -s <...> @@ -58,7 +58,7 @@ Options: - phpstan: phpstan analyze - unit (default): PHP unit tests - -t <10|11> + -t <11|12> Only with -s composerUpdate|acceptance|functional TYPO3 core major version the extension is embedded in for testing. @@ -69,9 +69,9 @@ Options: - postgres: use postgres - sqlite: use sqlite - -p <7.2|7.3|7.4|8.0|8.1|8.2> + -p <8.0|8.1|8.|8.3> Specifies the PHP minor version to be used - - 7.4 (default): use PHP 7.4 + - 8.0 (default): use PHP 8.0 -e "" Only with -s acceptance|functional|unit @@ -107,16 +107,16 @@ Options: Show this help. Examples: - # Run unit tests using PHP 7.4 + # Run unit tests using PHP 8.0 ./Build/Scripts/runTests.sh - # Run unit tests using PHP 7.3 - ./Build/Scripts/runTests.sh -p 7.3 + # Run unit tests using PHP 8.1 + ./Build/Scripts/runTests.sh -p 8.1 EOF # Test if docker-compose exists, else exit out with error -if ! type "docker-compose" > /dev/null; then - echo "This script relies on docker and docker-compose. Please install" >&2 +if ! type "docker" > /dev/null; then + echo "This script relies on docker. Please install" >&2 exit 1 fi @@ -139,13 +139,13 @@ fi # Option defaults TEST_SUITE="unit" DBMS="mariadb" -PHP_VERSION="7.4" +PHP_VERSION="8.0" +TYPO3_VERSION="11" PHP_XDEBUG_ON=0 PHP_XDEBUG_PORT=9003 EXTRA_TEST_OPTIONS="" SCRIPT_VERBOSE=0 CGLCHECK_DRY_RUN="" -TYPO3="10" # Option parsing # Reset in case getopts has been used previously in the shell @@ -163,12 +163,15 @@ while getopts ":s:d:p:e:t:xy:nhuv" OPT; do ;; p) PHP_VERSION=${OPTARG} - if ! [[ ${PHP_VERSION} =~ ^(7.2|7.3|7.4|8.0|8.1|8.2)$ ]]; then + if ! [[ ${PHP_VERSION} =~ ^(8.0|8.1|8.2|8.3)$ ]]; then INVALID_OPTIONS+=("${OPTARG}") fi ;; t) - TYPO3=${OPTARG} + TYPO3_VERSION=${OPTARG} + if ! [[ ${TYPO3_VERSION} =~ ^(11|12)$ ]]; then + INVALID_OPTIONS+=("p ${OPTARG}") + fi ;; e) EXTRA_TEST_OPTIONS=${OPTARG} @@ -212,7 +215,7 @@ if [ ${#INVALID_OPTIONS[@]} -ne 0 ]; then exit 1 fi -# Move "7.4" to "php74", the latter is the docker container name +# Move "8.0" to "php80", the latter is the docker container name DOCKER_PHP_IMAGE=$(echo "php${PHP_VERSION}" | sed -e 's/\.//') # Set $1 to first mass argument, this is the optional test file or test directory to execute @@ -230,9 +233,9 @@ fi case ${TEST_SUITE} in acceptance) setUpDockerComposeDotEnv - docker-compose run acceptance_backend_mariadb10 + docker compose run acceptance_backend_mariadb10 SUITE_EXIT_CODE=$? - docker-compose down + #docker compose down ;; cgl) # Active dry-run for cgl needs not "-n" but specific options @@ -240,36 +243,36 @@ case ${TEST_SUITE} in CGLCHECK_DRY_RUN="--dry-run --diff" fi setUpDockerComposeDotEnv - docker-compose run cgl + docker compose run cgl SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; composerUpdate) setUpDockerComposeDotEnv - docker-compose run composer_update + docker compose run composer_update SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; composerValidate) setUpDockerComposeDotEnv - docker-compose run composer_validate + docker compose run composer_validate SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; functional) setUpDockerComposeDotEnv case ${DBMS} in mariadb) - docker-compose run functional_mariadb10 + docker compose run functional_mariadb10 SUITE_EXIT_CODE=$? ;; postgres) - docker-compose run functional_postgres10 + docker compose run functional_postgres10 SUITE_EXIT_CODE=$? ;; sqlite) mkdir -p ${CORE_ROOT}/.Build/Web/typo3temp/var/tests/functional-sqlite-dbs/ - docker-compose run functional_sqlite + docker compose run functional_sqlite SUITE_EXIT_CODE=$? ;; *) @@ -278,25 +281,25 @@ case ${TEST_SUITE} in echo "${HELP}" >&2 exit 1 esac - docker-compose down + docker compose down ;; lint) setUpDockerComposeDotEnv - docker-compose run lint + docker compose run lint SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; phpstan) setUpDockerComposeDotEnv - docker-compose run phpstan + docker compose run phpstan SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; unit) setUpDockerComposeDotEnv - docker-compose run unit + docker compose run unit SUITE_EXIT_CODE=$? - docker-compose down + docker compose down ;; update) # pull typo3/core-testing-*:latest versions of those ones that exist locally diff --git a/Build/php-cs-fixer.php b/Build/php-cs-fixer.php index 4493c7a..b240551 100644 --- a/Build/php-cs-fixer.php +++ b/Build/php-cs-fixer.php @@ -1,5 +1,62 @@ getFinder()->exclude(['var']); +$config->getFinder()->in('./')->exclude(['var', '.Build']); + +$config->setRules([ + '@DoctrineAnnotation' => true, + '@PER-CS' => true, + 'array_syntax' => ['syntax' => 'short'], + 'blank_line_after_opening_tag' => true, + 'braces' => ['allow_single_line_closure' => true], + 'cast_spaces' => ['space' => 'none'], + 'compact_nullable_type_declaration' => true, + 'concat_space' => ['spacing' => 'one'], + 'declare_equal_normalize' => ['space' => 'none'], + 'dir_constant' => true, + 'function_to_constant' => ['functions' => ['get_called_class', 'get_class', 'get_class_this', 'php_sapi_name', 'phpversion', 'pi']], + 'type_declaration_spaces' => true, + 'lowercase_cast' => true, + 'method_argument_space' => ['on_multiline' => 'ensure_fully_multiline'], + 'modernize_strpos' => true, + 'modernize_types_casting' => true, + 'native_function_casing' => true, + 'new_with_parentheses' => true, + 'no_alias_functions' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_empty_phpdoc' => true, + 'no_empty_statement' => true, + 'no_extra_blank_lines' => true, + 'no_leading_import_slash' => true, + 'no_leading_namespace_whitespace' => true, + 'no_null_property_initialization' => true, + 'no_short_bool_cast' => true, + 'no_singleline_whitespace_before_semicolons' => true, + 'no_superfluous_elseif' => true, + 'no_trailing_comma_in_singleline' => true, + 'no_unneeded_control_parentheses' => true, + 'no_unused_imports' => true, + 'no_useless_else' => true, + 'no_useless_nullsafe_operator' => true, + 'no_whitespace_in_blank_line' => true, + 'ordered_imports' => true, + 'php_unit_construct' => ['assertions' => ['assertEquals', 'assertSame', 'assertNotEquals', 'assertNotSame']], + 'php_unit_mock_short_will_return' => true, + 'php_unit_test_case_static_method_calls' => ['call_type' => 'self'], + 'phpdoc_no_access' => true, + 'phpdoc_no_empty_return' => true, + 'phpdoc_no_package' => true, + 'phpdoc_scalar' => true, + 'phpdoc_trim' => true, + 'phpdoc_types' => true, + 'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], + 'return_type_declaration' => ['space_before' => 'none'], + 'single_quote' => true, + 'single_line_comment_style' => ['comment_types' => ['hash']], + 'single_trait_insert_per_statement' => true, + 'trailing_comma_in_multiline' => ['elements' => ['arrays']], + 'whitespace_after_comma_in_array' => ['ensure_single_space' => true], + 'yoda_style' => ['equal' => false, 'identical' => false, 'less_and_greater' => false], +]); + return $config; diff --git a/Build/phpstan-baseline.neon b/Build/phpstan-baseline.neon index d7c7848..2b7792d 100644 --- a/Build/phpstan-baseline.neon +++ b/Build/phpstan-baseline.neon @@ -1,5 +1,25 @@ parameters: ignoreErrors: + - + message: "#^Call to method getDataStructure\\(\\) on an unknown class TYPO3\\\\CMS\\\\Core\\\\Configuration\\\\Event\\\\AfterFlexFormDataStructureParsedEvent\\.$#" + count: 1 + path: ../Classes/Backend/EventListener/FlexFormParsingModifyEventListener.php + + - + message: "#^Call to method getIdentifier\\(\\) on an unknown class TYPO3\\\\CMS\\\\Core\\\\Configuration\\\\Event\\\\AfterFlexFormDataStructureParsedEvent\\.$#" + count: 1 + path: ../Classes/Backend/EventListener/FlexFormParsingModifyEventListener.php + + - + message: "#^Call to method setDataStructure\\(\\) on an unknown class TYPO3\\\\CMS\\\\Core\\\\Configuration\\\\Event\\\\AfterFlexFormDataStructureParsedEvent\\.$#" + count: 1 + path: ../Classes/Backend/EventListener/FlexFormParsingModifyEventListener.php + + - + message: "#^Parameter \\$event of method B13\\\\FormCustomTemplates\\\\Backend\\\\EventListener\\\\FlexFormParsingModifyEventListener\\:\\:modifyDataStructure\\(\\) has invalid type TYPO3\\\\CMS\\\\Core\\\\Configuration\\\\Event\\\\AfterFlexFormDataStructureParsedEvent\\.$#" + count: 1 + path: ../Classes/Backend/EventListener/FlexFormParsingModifyEventListener.php + - message: "#^Parameter \\$event of method B13\\\\FormCustomTemplates\\\\Backend\\\\EventListener\\\\ModifyButtonBarEventListener\\:\\:__invoke\\(\\) has invalid type TYPO3\\\\CMS\\\\Backend\\\\Template\\\\Components\\\\ModifyButtonBarEvent\\.$#" count: 1 diff --git a/Build/testing-docker/docker-compose.yml b/Build/testing-docker/docker-compose.yml index ef6fb62..4e68bfe 100644 --- a/Build/testing-docker/docker-compose.yml +++ b/Build/testing-docker/docker-compose.yml @@ -1,4 +1,3 @@ -version: '2.3' services: chrome: # Image for Mac M1 @@ -47,6 +46,8 @@ services: php -S web:8000 -t ${CORE_ROOT}/.Build/Web fi " + ports: + - "8000:8000" acceptance_backend_mariadb10: image: typo3/core-testing-${DOCKER_PHP_IMAGE}:latest @@ -78,7 +79,7 @@ services: echo Database is up; php -v | grep '^PHP'; mkdir -p Web/typo3temp/var/tests/ - COMMAND=\"vendor/codeception/codeception/codecept run Backend -d -c Web/typo3conf/ext/form_custom_templates/Tests/codeception.yml ${TEST_FILE}\" + COMMAND=\"vendor/codeception/codeception/codecept run Backend -d -c ../Tests/codeception.yml ${TEST_FILE}\" if [ ${PHP_XDEBUG_ON} -eq 0 ]; then XDEBUG_MODE=\"off\" \ $${COMMAND}; @@ -106,25 +107,12 @@ services: set -x fi php -v | grep '^PHP'; - if [ ${PHP_XDEBUG_ON} -eq 0 ]; then - php -dxdebug.mode=off \ - .Build/bin/php-cs-fixer fix \ - -v \ - ${CGLCHECK_DRY_RUN} \ - --config=Build/php-cs-fixer.php \ - --using-cache=no . - else DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'` XDEBUG_MODE=\"debug,develop\" \ XDEBUG_TRIGGER=\"foo\" \ XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \ PHP_CS_FIXER_ALLOW_XDEBUG=1 \ - .Build/bin/php-cs-fixer fix \ - -v \ - ${CGLCHECK_DRY_RUN} \ - --config=Build/php-cs-fixer.php \ - --using-cache=no . - fi + .Build/bin/php-cs-fixer fix -v --config=Build/php-cs-fixer.php --using-cache=no . " composer_update: @@ -142,7 +130,15 @@ services: set -x fi php -v | grep '^PHP'; - COMPOSER_HOME=${CORE_ROOT}/.Build/.composer composer update --no-progress --no-interaction; + if [ ${TYPO3_VERSION} -eq 11 ]; then + composer req --dev --no-update typo3/cms-composer-installers:^3.0 + composer req typo3/cms-core:^11.5 typo3/cms-recordlist:^11.5 --no-update + fi + if [ ${TYPO3_VERSION} -eq 12 ]; then + composer req --dev --no-update "typo3/cms-composer-installers:^5.0" + composer req typo3/cms-core:^12.4 --no-update + fi + composer update --no-progress --no-interaction; " composer_validate: diff --git a/Classes/Backend/EventListener/FlexFormParsingModifyEventListener.php b/Classes/Backend/EventListener/FlexFormParsingModifyEventListener.php new file mode 100644 index 0000000..30722c9 --- /dev/null +++ b/Classes/Backend/EventListener/FlexFormParsingModifyEventListener.php @@ -0,0 +1,78 @@ +getIdentifier(); + + if (($identifier['ext-form-overrideFinishers'] ?? '') !== 'enabled') { + return; + } + + $addToFinishers = ['EmailToSender', 'EmailToReceiver']; + $options = $this->emailTemplateService->getOptions(); + $dataStructure = $event->getDataStructure(); + + if ($options === []) { + return; + } + + // Search for finishers and add items + foreach ($dataStructure['sheets'] as $sheetIdentifier => $sheet) { + foreach ($addToFinishers as $finisherIdentifier) { + $fieldName = 'settings.finishers.' . $finisherIdentifier . '.emailTemplateUid'; + + // Field name not found? Continue. + if (!($dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName] ?? false)) { + continue; + } + + // @deprecated - this code block can be removed when support for TYPO3 v11 is dropped + if (is_array($dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName]['TCEforms']['config']['items'] ?? null)) { + $dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName]['TCEforms']['config']['items'] = + array_merge( + $dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName]['TCEforms']['config']['items'], + $options + ); + continue; + } + + // V12 - Add options to select and fix default value + if (is_array($dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName]['config']['items'] ?? null)) { + if ((int)($dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName]['config']['default'] ?? 0) > 0) { + // Replacing the Page ID with the page Title in the field label + foreach ($options as $option) { + if ((int)($option['value'] ?? -1) !== (int)($dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName]['config']['default'] ?? 0)) { + continue; + } + if (empty($option['label'])) { + continue; + } + $dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName]['label'] = + str_replace( + (string)$dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName]['config']['default'], + $option['label'], + $dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName]['label'] + ); + break; + } + } + + $dataStructure['sheets'][$sheetIdentifier]['ROOT']['el'][$fieldName]['config']['items'] = $options; + } + } + } + + $event->setDataStructure($dataStructure); + } +} diff --git a/Classes/Backend/EventListener/ModifyButtonBarEventListener.php b/Classes/Backend/EventListener/ModifyButtonBarEventListener.php index 31f6d88..2966c02 100644 --- a/Classes/Backend/EventListener/ModifyButtonBarEventListener.php +++ b/Classes/Backend/EventListener/ModifyButtonBarEventListener.php @@ -18,9 +18,7 @@ final class ModifyButtonBarEventListener { - public function __construct(private readonly PageRepository $pageRepository, private readonly Configuration $configuration) - { - } + public function __construct(private readonly PageRepository $pageRepository, private readonly Configuration $configuration) {} public function __invoke(ModifyButtonBarEvent $event): void { diff --git a/Classes/Configuration.php b/Classes/Configuration.php index c58c313..2e2d74e 100644 --- a/Classes/Configuration.php +++ b/Classes/Configuration.php @@ -9,9 +9,8 @@ class Configuration implements SingletonInterface { - const DEFAULT_DOKTYPE = 125; - const DEFAULT_PAGE_TYPE = 101; - + public const DEFAULT_DOKTYPE = 125; + public const DEFAULT_PAGE_TYPE = 101; private array $typoScript = []; public function __construct(protected readonly ConfigurationManagerInterface $configurationManager) diff --git a/Classes/Domain/Finisher/EmailTemplateFinisher.php b/Classes/Domain/Finisher/EmailTemplateFinisher.php index 15d6b5b..aa191fc 100644 --- a/Classes/Domain/Finisher/EmailTemplateFinisher.php +++ b/Classes/Domain/Finisher/EmailTemplateFinisher.php @@ -22,10 +22,10 @@ class EmailTemplateFinisher extends EmailFinisher { - public function __construct(protected readonly EmailTemplateService $emailTemplateService, protected readonly Configuration $configuration) - { - parent::__construct(); - } + public function __construct( + protected readonly EmailTemplateService $emailTemplateService, + protected readonly Configuration $configuration + ) {} protected function executeInternal() { @@ -117,7 +117,7 @@ protected function executeInternal() $parts[] = [ 'format' => 'Html', 'contentType' => 'text/html', - 'content' => $this->emailTemplateService->create((int)$emailTemplateUid, $formRuntime, $this->getStandaloneView($title, $formRuntime, 'html')->render(), 0), + 'content' => $this->emailTemplateService->create((int)$emailTemplateUid, $formRuntime, $this->getStandaloneView($title, $formRuntime, 'html')->render(), 0), ]; } diff --git a/Classes/Hooks/DataStructureEmailOptionsHook.php b/Classes/Hooks/DataStructureEmailOptionsHook.php index f16c12e..d14954f 100644 --- a/Classes/Hooks/DataStructureEmailOptionsHook.php +++ b/Classes/Hooks/DataStructureEmailOptionsHook.php @@ -8,9 +8,7 @@ class DataStructureEmailOptionsHook { - public function __construct(protected readonly EmailTemplateService $emailTemplateService) - { - } + public function __construct(protected readonly EmailTemplateService $emailTemplateService) {} /** * Add emailTemplateUid options diff --git a/Classes/Hooks/PlaintextPreviewHook.php b/Classes/Hooks/PlaintextPreviewHook.php index c724d68..441ebd8 100644 --- a/Classes/Hooks/PlaintextPreviewHook.php +++ b/Classes/Hooks/PlaintextPreviewHook.php @@ -14,9 +14,7 @@ class PlaintextPreviewHook { - public function __construct(private readonly Configuration $configuration) - { - } + public function __construct(private readonly Configuration $configuration) {} /** * @todo: remove when v11 support was dropped diff --git a/Classes/Middleware/EmailPagePreviewGuard.php b/Classes/Middleware/EmailPagePreviewGuard.php index 952561b..9edfb18 100644 --- a/Classes/Middleware/EmailPagePreviewGuard.php +++ b/Classes/Middleware/EmailPagePreviewGuard.php @@ -20,8 +20,7 @@ class EmailPagePreviewGuard implements MiddlewareInterface public function __construct( protected readonly Context $context, protected readonly ExtensionConfiguration $extensionConfiguration - ) { - } + ) {} public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface { diff --git a/Classes/Service/EmailTemplateService.php b/Classes/Service/EmailTemplateService.php index ffa4d5e..a95ce7c 100644 --- a/Classes/Service/EmailTemplateService.php +++ b/Classes/Service/EmailTemplateService.php @@ -9,6 +9,7 @@ use Psr\Http\Message\ServerRequestInterface; use TYPO3\CMS\Core\Database\ConnectionPool; use TYPO3\CMS\Core\Http\Uri; +use TYPO3\CMS\Core\Information\Typo3Version; use TYPO3\CMS\Core\Service\MarkerBasedTemplateService; use TYPO3\CMS\Core\Site\Entity\Site; use TYPO3\CMS\Core\Site\SiteFinder; @@ -32,7 +33,7 @@ public function __construct( public function create(int $uid, FormRuntime $formRuntime, string $resultTable = '', int $type = 101): string { $subResponse = $this->stashEnvironment( - fn (): ResponseInterface => $this->sendSubRequest($uid, $type, $GLOBALS['TYPO3_REQUEST']) + fn(): ResponseInterface => $this->sendSubRequest($uid, $type, $GLOBALS['TYPO3_REQUEST']) ); $templateContent = $this->markerBasedTemplateService->substituteMarker( (string)$subResponse->getBody(), @@ -87,8 +88,14 @@ protected function sendSubRequest(int $pageId, int $type, ServerRequestInterface public function getOptions(): array { - $options = array_reduce($this->getEmailTemplatePages(), static function ($options, $item) { - $options[] = [$item['title'], $item['uid']]; + $typo3Version = (new Typo3Version())->getMajorVersion(); + $options = array_reduce($this->getEmailTemplatePages(), static function ($options, $item) use ($typo3Version) { + if ($typo3Version > 11) { + $options[] = ['label' => $item['title'], 'value' => $item['uid']]; + } else { + $options[] = [$item['title'], $item['uid']]; + } + return $options; }, []); diff --git a/Configuration/Services.yaml b/Configuration/Services.yaml index b65a6c1..6f8a266 100644 --- a/Configuration/Services.yaml +++ b/Configuration/Services.yaml @@ -12,6 +12,12 @@ services: - name: event.listener identifier: 'tx_form_custom_templates/modifyButtonBarEventListener' + B13\FormCustomTemplates\Backend\EventListener\FlexFormParsingModifyEventListener: + tags: + - name: event.listener + identifier: 'tx_form_custom_templates/modify-data-structure' + method: 'modifyDataStructure' + B13\FormCustomTemplates\Hooks\PlaintextPreviewHook: public: true diff --git a/Configuration/TCA/Overrides/pages.php b/Configuration/TCA/Overrides/pages.php index 14fbbf4..f5c6366 100644 --- a/Configuration/TCA/Overrides/pages.php +++ b/Configuration/TCA/Overrides/pages.php @@ -8,7 +8,7 @@ use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; use TYPO3\CMS\Core\Utility\GeneralUtility; -(function ($extensionKey = 'form_custom_templates', $table='pages') { +(function ($extensionKey = 'form_custom_templates', $table = 'pages') { // Add page type $emailDoktype = (string)GeneralUtility::makeInstance(ExtensionConfiguration::class)->get('form_custom_templates', 'doktype'); @@ -55,7 +55,7 @@ 'visibilityEmailTemplate' => [ 'label' => 'LLL:EXT:frontend/Resources/Private/Language/locallang_tca.xlf:pages.palettes.visibility', 'showitem' => 'hidden', - ] + ], ], 'types' => [ $emailDoktype => [ diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript index b31d607..f3afe38 100644 --- a/Configuration/TypoScript/setup.typoscript +++ b/Configuration/TypoScript/setup.typoscript @@ -18,7 +18,7 @@ plugin.tx_form { } } -[traverse(request.getQueryParams(), 'type') == {$plugin.tx_form_custom_templates.typeNum}] +[request && traverse(request.getQueryParams(), 'type') == {$plugin.tx_form_custom_templates.typeNum}] lib.contentElement { format = txt templateRootPaths { diff --git a/Configuration/Yaml/FormSetup.yaml b/Configuration/Yaml/FormSetup.yaml index 0eafbbe..42d6cf6 100644 --- a/Configuration/Yaml/FormSetup.yaml +++ b/Configuration/Yaml/FormSetup.yaml @@ -41,7 +41,7 @@ emailTemplateUid: label: 'form_custom_templates.select.label' config: - type: text + type: select renderType: selectSingle minitems: 1 maxitems: 1 @@ -50,10 +50,8 @@ # items again, because it is a different format! Partey! items: 0: - - 'form_custom_templates.select.default' - # The default value is required for v10, otherwise this will show up as "INVALID VALUE" - # can be set to "" (empty string) once we support v11 and up only. - - "[Empty]" + label: 'form_custom_templates.select.default' + value: '' TYPO3: CMS: Form: @@ -131,4 +129,4 @@ TYPO3: EmailToSender: <<: *email_template_options EmailToReceiver: - <<: *email_template_options \ No newline at end of file + <<: *email_template_options diff --git a/Tests/Acceptance/Backend.suite.yml b/Tests/Acceptance/Backend.suite.yml index 0c032f8..12664e8 100644 --- a/Tests/Acceptance/Backend.suite.yml +++ b/Tests/Acceptance/Backend.suite.yml @@ -1,4 +1,4 @@ -class_name: BackendTester +actor: BackendTester modules: enabled: - WebDriver: diff --git a/Tests/Acceptance/Backend/FormFeaturesCest.php b/Tests/Acceptance/Backend/FormFeaturesCest.php index 6cc0c02..4324e3d 100644 --- a/Tests/Acceptance/Backend/FormFeaturesCest.php +++ b/Tests/Acceptance/Backend/FormFeaturesCest.php @@ -31,6 +31,7 @@ public function _before(BackendTester $I) // Suppress alert popup $I->executeJS('window.onbeforeunload = undefined;'); $I->useExistingSession('admin'); + $I->wait(20); $I->switchToMainFrame(); $I->click('Forms', self::$mainMenu); @@ -45,7 +46,7 @@ public function seeTemplateSelectorInFinisher(BackendTester $I): void $I->click('#t3-form-navigation-component-tree-root-container'); $I->waitForElementVisible($finisher); $I->click($finisher . ' a[data-bs-toggle="collapse"]'); - $I->wait(2); + $I->wait(10); $actual = $I->grabMultiple('//label/*[contains(text(),"Select email template")]/parent::*/following-sibling::div//select//option'); $expected = ['Default', 'Contact template', 'Shopping cart template']; @@ -70,7 +71,6 @@ public function seeTemplateSelectorInFinisher(BackendTester $I): void public function seeChangeIdentifier(BackendTester $I): void { $newIdentifier = 'new-firstname'; - $selectedItem = '//div[@class="ui-sortable-handle t3-form-form-element-selected"]//*[@class="meta-label"]//span[@data-template-property="_identifier"]'; $identifierInput = '//div[@id="t3-form-inspector"]//label//span[contains(text(),"Change Identifier")]/parent::*/following-sibling::div//input'; $I->waitForElement(self::$stage); @@ -82,17 +82,17 @@ public function seeChangeIdentifier(BackendTester $I): void $I->amGoingTo('See invalid identifier message'); $I->fillField($identifierInput, 'invalid}'); $I->waitForText('Not a valid identifier. A valid identifier may contain only a-Z and 0-9 and must not be empty.', 5, self::$inspectorValidators); - $I->assertNotEquals('invalid}', $I->grabTextFrom($selectedItem)); + $I->assertNotEquals('invalid}', $I->grabTextFrom($identifierInput)); $I->amGoingTo('See identifier already in use'); $I->fillField($identifierInput, 'lastname'); $I->waitForText('Reset to \'firstname\' because this identifier is already in use.', 5, self::$inspectorValidators); - $I->assertNotEquals('lastname', $I->grabTextFrom($selectedItem)); + $I->assertNotEquals('lastname', $I->grabTextFrom($identifierInput)); $I->amGoingTo('Changed identifier on stage'); $I->fillField($identifierInput, $newIdentifier); + $I->wait(4); $I->waitForElementNotVisible(self::$inspectorValidators); - $I->waitForText($newIdentifier, 5, $selectedItem); - $I->assertEquals($newIdentifier, $I->grabTextFrom($selectedItem)); + $I->waitForText($newIdentifier); } } diff --git a/Tests/Acceptance/Backend/PluginFormCest.php b/Tests/Acceptance/Backend/PluginFormCest.php index 782394e..faf70e7 100644 --- a/Tests/Acceptance/Backend/PluginFormCest.php +++ b/Tests/Acceptance/Backend/PluginFormCest.php @@ -45,9 +45,10 @@ public function seeOverrideTemplateOptions(BackendTester $I): void $I->scrollTo('//label[contains(text(),"Select email template")]'); $I->amGoingTo('See a list of expected email template pages in select of form plugin override'); - $actual = $I->grabMultiple('//label[contains(text(),"Select email template")]/following-sibling::div//select//option'); - $expected = ['Default [[Empty]]', 'Contact template [2]', 'Shopping cart template [3]']; - $I->assertEquals($expected, $actual); + $actual = $I->grabTextFrom('//label[contains(text(),"Select email template")]/following-sibling::div//select'); + + $I->assertStringContainsString('Contact template', $actual); + $I->assertStringContainsString('Shopping cart template', $actual); } /** diff --git a/Tests/Acceptance/Fixtures/be_groups.xml b/Tests/Acceptance/Fixtures/be_groups.xml new file mode 100644 index 0000000..03b3585 --- /dev/null +++ b/Tests/Acceptance/Fixtures/be_groups.xml @@ -0,0 +1,22 @@ + + + + + 1 + 0 + 1452959228 + editor-group + pages + 1452959228 + + + 2 + 0 + 1452959228 + some test group + pages + 1452959228 + 1 + + diff --git a/Tests/Acceptance/Fixtures/be_users.xml b/Tests/Acceptance/Fixtures/be_users.xml new file mode 100644 index 0000000..ea03d65 --- /dev/null +++ b/Tests/Acceptance/Fixtures/be_users.xml @@ -0,0 +1,44 @@ + + + + + 1 + 0 + 1366642540 + admin + $1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1 + 1 + 0 + 0 + 0 + 0 + 1366642540 + 1 + 0 + NULL + 1371033743 + 0 + Klaus Admin + + + 2 + 0 + 1452944912 + editor + $1$tCrlLajZ$C0sikFQQ3SWaFAZ1Me0Z/1 + 0 + 0 + 0 + 0 + 0 + 1452944912 + 1 + 0 + NULL + 1452944915 + 0 + 1 + 1 + + diff --git a/Tests/Acceptance/Support/Extension/BackendFormCustomTemplatesEnvironment.php b/Tests/Acceptance/Support/Extension/BackendFormCustomTemplatesEnvironment.php index 9633e3e..194b52a 100644 --- a/Tests/Acceptance/Support/Extension/BackendFormCustomTemplatesEnvironment.php +++ b/Tests/Acceptance/Support/Extension/BackendFormCustomTemplatesEnvironment.php @@ -25,42 +25,23 @@ class BackendFormCustomTemplatesEnvironment extends BackendEnvironment 'beuser', 'extbase', 'fluid', - 'filelist', - 'extensionmanager', - 'setup', 'backend', - 'belog', - 'install', - 'impexp', 'frontend', - 'recordlist', - 'redirects', - 'reports', - 'sys_note', - 'scheduler', - 'tstemplate', - 'lowlevel', - 'dashboard', - 'workspaces', - 'info', - 'fluid_styled_content', - 'indexed_search', - 'adminpanel', + 'install', 'form', - 'felogin', - 'seo', - 'recycler', + 'recordlist', ], 'testExtensionsToLoad' => [ 'typo3conf/ext/form_custom_templates', ], + // @todo: Migrate to csvDatabaseFixtures 'xmlDatabaseFixtures' => [ - 'PACKAGE:typo3/testing-framework/Resources/Core/Acceptance/Fixtures/be_users.xml', - 'PACKAGE:../Web/typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/be_sessions.xml', - 'PACKAGE:typo3/testing-framework/Resources/Core/Acceptance/Fixtures/be_groups.xml', - 'PACKAGE:../Web/typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/pages.xml', - 'PACKAGE:../Web/typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/sys_template.xml', - 'PACKAGE:../Web/typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/tt_content.xml', + 'PACKAGE:../Web/typo3temp/var/tests/acceptance/typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/be_users.xml', + 'PACKAGE:../Web/typo3temp/var/tests/acceptance/typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/be_sessions.xml', + 'PACKAGE:../Web/typo3temp/var/tests/acceptance/typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/be_groups.xml', + 'PACKAGE:../Web/typo3temp/var/tests/acceptance/typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/pages.xml', + 'PACKAGE:../Web/typo3temp/var/tests/acceptance/typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/sys_template.xml', + 'PACKAGE:../Web/typo3temp/var/tests/acceptance/typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/tt_content.xml', ], 'configurationToUseInTestInstance' => [ 'MAIL' => [ @@ -68,8 +49,8 @@ class BackendFormCustomTemplatesEnvironment extends BackendEnvironment ], ], 'pathsToLinkInTestInstance' => [ - 'typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/sites' => - 'typo3conf/sites', + 'typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/sites' => + 'typo3conf/sites', ], ]; @@ -78,8 +59,9 @@ public function bootstrapTypo3Environment(SuiteEvent $suiteEvent) $bootstrap = parent::bootstrapTypo3Environment($suiteEvent); $testbase = new Testbase(); $testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/tests/acceptance/fileadmin/form_definitions'); + // Copy form fixture into place - copy(ORIGINAL_ROOT . 'typo3conf/ext/form_custom_templates/Tests/Acceptance/Fixtures/form_definitions/test-form.form.yaml', ORIGINAL_ROOT . 'typo3temp/var/tests/acceptance/fileadmin/form_definitions/test-form.form.yaml'); + copy(ORIGINAL_ROOT . '../../Tests/Acceptance/Fixtures/form_definitions/test-form.form.yaml', ORIGINAL_ROOT . 'typo3temp/var/tests/acceptance/fileadmin/form_definitions/test-form.form.yaml'); return $bootstrap; } diff --git a/composer.json b/composer.json index 6d7727a..a3f431f 100644 --- a/composer.json +++ b/composer.json @@ -18,18 +18,24 @@ "require": { "php": "^8.1", "typo3/cms-form": "^11.5 || ^12.3", - "typo3/cms-core": "^11.5 || ^12.3", "typo3/cms-frontend": "^11.5 || ^12.3", "typo3/cms-install": "^11.5 || ^12.3", - "typo3/cms-filelist": "^11.5 || ^12.3" + "typo3/cms-filelist": "^11.5 || ^12.3", + "typo3/cms-core": "^11.5 || ^12.3" }, "require-dev": { - "codeception/codeception": "^4.1", - "codeception/module-asserts": "^1.2", - "codeception/module-webdriver": "^1.1", - "typo3/testing-framework": "^6.16.2", - "typo3/coding-standards": "^0.4.0", - "phpstan/phpstan": "^1.4.8" + "codeception/codeception": "^5.1", + "codeception/module-asserts": "^3.0.0", + "codeception/module-webdriver": "^4.0.1", + "typo3/testing-framework": "^7.0", + "typo3/coding-standards": "^v0.7.1", + "phpstan/phpstan": "^1.10.62", + "typo3/cms-composer-installers": "^5.0", + "typo3/cms-beuser": "*", + "typo3/cms-extensionmanager": "*", + "typo3/cms-setup": "*", + "typo3/cms-tstemplate": "*", + "typo3/cms-fluid-styled-content": "*" }, "scripts": { "post-autoload-dump": [