diff --git a/Classes/Configuration/Extension.php b/Classes/Configuration/Extension.php index acb7a28..abc3edb 100644 --- a/Classes/Configuration/Extension.php +++ b/Classes/Configuration/Extension.php @@ -25,11 +25,7 @@ namespace TRITUM\RepeatableFormElements\Configuration; use TRITUM\RepeatableFormElements\Hooks\FormHooks; -use TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider; -use TYPO3\CMS\Core\Imaging\IconRegistry; -use TYPO3\CMS\Core\Information\Typo3Version; use TYPO3\CMS\Core\Utility\ExtensionManagementUtility; -use TYPO3\CMS\Core\Utility\GeneralUtility; /** * Extension @@ -58,26 +54,9 @@ public static function addTypoScriptSetup(): void ')); } - public static function registerIcons(): void - { - if ('12.4' <= self::getTypo3Version()) return; - - $iconRegistry = GeneralUtility::makeInstance(IconRegistry::class); - $iconRegistry->registerIcon( - 't3-form-icon-repeatable-container', - SvgIconProvider::class, - ['source' => 'EXT:repeatable_form_elements/Resources/Public/Icons/t3-form-icon-repeatable-container.svg'] - ); - } - public static function registerHooks(): void { $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['afterInitializeCurrentPage'][1511196413] = FormHooks::class; $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/form']['beforeRendering'][1511196413] = FormHooks::class; } - - private static function getTypo3Version() : string - { - return (new Typo3Version())->getVersion(); - } } diff --git a/Classes/Event/CopyVariantEvent.php b/Classes/Event/CopyVariantEvent.php index 3b2ebba..c28db96 100644 --- a/Classes/Event/CopyVariantEvent.php +++ b/Classes/Event/CopyVariantEvent.php @@ -25,16 +25,15 @@ class CopyVariantEvent private bool $variantEnabled = true; public function __construct( - array $options, + array $options, FormElementInterface $originalFormElement, FormElementInterface $newFormElement, - string $newIdentifier - ) - { - $this->options = $options; + string $newIdentifier, + ) { + $this->options = $options; $this->originalFormElement = $originalFormElement; - $this->newFormElement = $newFormElement; - $this->newIdentifier = $newIdentifier; + $this->newFormElement = $newFormElement; + $this->newIdentifier = $newIdentifier; } public function getOptions(): array @@ -71,6 +70,4 @@ public function setVariantEnabled(bool $variantEnabled): void { $this->variantEnabled = $variantEnabled; } - - } diff --git a/Classes/EventListener/AdaptVariantConditionEventListener.php b/Classes/EventListener/AdaptVariantConditionEventListener.php index b09c01e..f211f29 100644 --- a/Classes/EventListener/AdaptVariantConditionEventListener.php +++ b/Classes/EventListener/AdaptVariantConditionEventListener.php @@ -13,12 +13,10 @@ use TRITUM\RepeatableFormElements\Event\CopyVariantEvent; - /** * Replace original identifiers inside variant condition with identifiers of new element * * @param CopyVariantEvent $event - * @return void */ final class AdaptVariantConditionEventListener { @@ -30,19 +28,19 @@ public function __invoke(CopyVariantEvent $event): void // get path strings for identifiers for replacement in condition // e.g. for `traverse(formValues, 'repeatablecontainer-1.0.checkbox-1')` $originalIdentifierAsPath = str_replace('.', '/', $originalIdentifier); - $newIdentifierAsPath = str_replace('.', '/', $event->getNewIdentifier()); + $newIdentifierAsPath = str_replace('.', '/', $event->getNewIdentifier()); // adapt original condition to match identifier of the copied form element $options['condition'] = str_replace( [ $originalIdentifier, - $originalIdentifierAsPath + $originalIdentifierAsPath, ], [ $event->getNewIdentifier(), - $newIdentifierAsPath + $newIdentifierAsPath, ], - $options['condition'] + $options['condition'], ); $event->setOptions($options); diff --git a/Classes/Finisher/SaveToDatabaseFinisher.php b/Classes/Finisher/SaveToDatabaseFinisher.php index 56a194c..efe20fa 100644 --- a/Classes/Finisher/SaveToDatabaseFinisher.php +++ b/Classes/Finisher/SaveToDatabaseFinisher.php @@ -19,12 +19,12 @@ use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; /** - * This finisher is an extension to the default SaveToDatabaseFinisher. + * This finisher is an extension to the default SaveToDatabaseFinisher. * It is intentionally registered as a new identifier to keep compatibility with existing forms. */ class SaveToDatabaseFinisher extends \TYPO3\CMS\Form\Domain\Finishers\SaveToDatabaseFinisher { - protected function process(int $iterationCount) + protected function process(int $iterationCount): void { $this->throwExceptionOnInconsistentConfiguration(); @@ -68,10 +68,14 @@ protected function process(int $iterationCount) * @param array $databaseData prepared data * @param string $table Tablename to save data to * @param int $iterationCount finisher iteration - * @return void */ - protected function processContainer(string $containerPath, array $elementsConfiguration, array $databaseData, string $table, int $iterationCount) - { + protected function processContainer( + string $containerPath, + array $elementsConfiguration, + array $databaseData, + string $table, + int $iterationCount, + ): void { $containerValues = ArrayUtility::getValueByPath($this->getFormValues(), $containerPath, '.'); foreach ($containerValues as $copyId => $containerItem) { $prefix = $containerPath . '.' . $copyId . '.'; @@ -90,8 +94,12 @@ protected function processContainer(string $containerPath, array $elementsConfig * @param string $prefix prefix to get the form element object by a full identifier * @return array the filled database data */ - protected function prepareData(array $elementsConfiguration, array $databaseData, array $values = [], string $prefix = ''): array - { + protected function prepareData( + array $elementsConfiguration, + array $databaseData, + array $values = [], + string $prefix = '', + ): array { if (empty($values)) { $values = $this->getFormValues(); } @@ -131,8 +139,12 @@ protected function prepareData(array $elementsConfiguration, array $databaseData * $databaseData into the table $table * and provide some finisher variables */ - protected function saveToDatabase(array $databaseData, string $table, int $iterationCount, ?int $containerItemKey = null) - { + protected function saveToDatabase( + array $databaseData, + string $table, + int $iterationCount, + ?int $containerItemKey = null, + ): void { if (!empty($databaseData)) { if ($this->parseOption('mode') === 'update') { $whereClause = $this->parseOption('whereClause'); @@ -142,7 +154,7 @@ protected function saveToDatabase(array $databaseData, string $table, int $itera $this->databaseConnection->update( $table, $databaseData, - $whereClause + $whereClause, ); } else { $this->databaseConnection->insert($table, $databaseData); @@ -150,18 +162,19 @@ protected function saveToDatabase(array $databaseData, string $table, int $itera $this->finisherContext->getFinisherVariableProvider()->add( $this->shortFinisherIdentifier, 'insertedUids.' . $iterationCount . (is_int($containerItemKey) ? '.' . $containerItemKey : ''), - $insertedUid + $insertedUid, ); $currentCount = $this->finisherContext->getFinisherVariableProvider()->get( $this->shortFinisherIdentifier, - 'countInserts.', $iterationCount, - 0 + 'countInserts.', + $iterationCount, + 0, ); $this->finisherContext->getFinisherVariableProvider()->addOrUpdate( $this->shortFinisherIdentifier, 'countInserts.' . $iterationCount, - $currentCount++ + $currentCount++, ); } } diff --git a/Classes/FormElements/RepeatableContainer.php b/Classes/FormElements/RepeatableContainer.php index c73baaf..78d5aa9 100644 --- a/Classes/FormElements/RepeatableContainer.php +++ b/Classes/FormElements/RepeatableContainer.php @@ -1,6 +1,6 @@ getPages() as $page) { $this->setRootRepeatableContainerIdentifiers($page, $formRuntime); @@ -76,16 +79,18 @@ public function beforeRendering(FormRuntime $formRuntime, RootRenderableInterfac } /** - * @param RenderableInterface $formElement + * @param RenderableInterface $renderable * @param FormRuntime $formRuntime * @param array $repeatableContainerIdentifiers + * + * @throws DuplicateFormElementException */ protected function setRootRepeatableContainerIdentifiers( RenderableInterface $renderable, FormRuntime $formRuntime, - array $repeatableContainerIdentifiers = [] + array $repeatableContainerIdentifiers = [], ): void { - $isRepeatableContainer = $renderable instanceof RepeatableContainerInterface ? true : false; + $isRepeatableContainer = $renderable instanceof RepeatableContainerInterface; $hasOriginalIdentifier = isset($renderable->getRenderingOptions()['_originalIdentifier']); if ($isRepeatableContainer) { @@ -104,7 +109,7 @@ protected function setRootRepeatableContainerIdentifiers( $originalIdentifier = $renderable->getIdentifier(); $renderable->setRenderingOption('_originalIdentifier', $originalIdentifier); - if($renderable instanceof AbstractFormElement && $renderable->getDefaultValue()) { + if ($renderable instanceof AbstractFormElement && $renderable->getDefaultValue()) { $formRuntime->getFormDefinition()->addElementDefaultValue($newIdentifier, $renderable->getDefaultValue()); } @@ -139,14 +144,15 @@ protected function setRootRepeatableContainerIdentifiers( * returns TRUE if the user went back to any previous step in the form. * * @param FormRuntime $formRuntime - * @param CompositeRenderableInterface $currentPage - * @param CompositeRenderableInterface $lastPage + * @param CompositeRenderableInterface|null $currentPage + * @param CompositeRenderableInterface|null $lastPage + * * @return bool */ protected function userWentBackToPreviousStep( FormRuntime $formRuntime, CompositeRenderableInterface $currentPage = null, - CompositeRenderableInterface $lastPage = null + CompositeRenderableInterface $lastPage = null, ): bool { return $currentPage !== null && $lastPage !== null diff --git a/Classes/Service/CopyService.php b/Classes/Service/CopyService.php index 222af58..af9c325 100644 --- a/Classes/Service/CopyService.php +++ b/Classes/Service/CopyService.php @@ -31,35 +31,12 @@ class CopyService { - /** - * @var FormRuntime - */ - protected $formRuntime; - - /** - * @var FormState - */ - protected $formState; - - /** - * @var FormDefinition - */ - protected $formDefinition; - - /** - * @var array - */ - protected $repeatableContainersByOriginalIdentifier = []; - - /** - * @var array - */ - protected $typeDefinitions = []; - - /** - * @var Features - */ - protected $features; + protected FormRuntime $formRuntime; + protected ?FormState $formState; + protected FormDefinition $formDefinition; + protected array $repeatableContainersByOriginalIdentifier = []; + protected array $typeDefinitions = []; + protected mixed $features; /** * @param FormRuntime $formRuntime @@ -83,7 +60,7 @@ public function createCopiesFromCurrentRequest(): CopyService $this->removeDeletedRepeatableContainersFromFormValuesByRequest($requestArguments); $requestArguments = array_replace_recursive( $this->formState->getFormValues(), - $requestArguments + $requestArguments, ); $this->copyRepeatableContainersFromArguments($requestArguments); @@ -108,7 +85,7 @@ public function createCopiesFromFormState(): CopyService */ public function copyProcessingRule( string $originalFormElement, - string $newElementCopy + string $newElementCopy, ): array { $originalProcessingRule = $this->formRuntime->getFormDefinition()->getProcessingRule($originalFormElement); @@ -129,7 +106,7 @@ public function copyProcessingRule( */ protected function copyRepeatableContainersFromArguments( array $requestArguments, - array $argumentPath = [] + array $argumentPath = [], ): void { foreach ($requestArguments as $argumentKey => $argumentValue) { if (is_array($argumentValue)) { @@ -187,15 +164,16 @@ protected function copyRepeatableContainersFromArguments( } /** - * @param RepeatableContainerInterface $originalContainer + * @param RepeatableContainerInterface $copyFromContainer * @param RepeatableContainerInterface $moveAfterContainer * @param string $newIdentifier + * * @return RepeatableContainerInterface */ protected function copyRepeatableContainer( RepeatableContainerInterface $copyFromContainer, RepeatableContainerInterface $moveAfterContainer, - string $newIdentifier + string $newIdentifier, ): RepeatableContainerInterface { $typeName = $copyFromContainer->getType(); $implementationClassName = $this->typeDefinitions[$typeName]['implementationClassName']; @@ -227,7 +205,7 @@ protected function copyRepeatableContainer( */ protected function copyOptions( FormElementInterface $newElementCopy, - FormElementInterface $originalFormElement + FormElementInterface $originalFormElement, ): void { $newElementCopy->setLabel($originalFormElement->getLabel()); $newElementCopy->setDefaultValue($originalFormElement->getDefaultValue()); @@ -255,7 +233,7 @@ protected function copyOptions( /** * @param FormElementInterface $originalFormElement - * @param CompositeRenderableInterface $parentFormElement + * @param CompositeRenderableInterface $parentFormElementCopy * @param string $identifierOriginal * @param string $identifierReplacement */ @@ -263,12 +241,12 @@ protected function createNestedElements( FormElementInterface $originalFormElement, CompositeRenderableInterface $parentFormElementCopy, string $identifierOriginal, - string $identifierReplacement + string $identifierReplacement, ): void { $newIdentifier = str_replace($identifierOriginal, $identifierReplacement, $originalFormElement->getIdentifier()); $newFormElement = $parentFormElementCopy->createElement( $newIdentifier, - $originalFormElement->getType() + $originalFormElement->getType(), ); $this->copyOptions($newFormElement, $originalFormElement); $this->copyProcessingRule($originalFormElement->getIdentifier(), $newIdentifier); @@ -317,14 +295,14 @@ protected function getRepeatableContainerByOriginalIdentifier(string $originalId } /** - * @param FormElementInterface $originalFormElement + * @param FormElementInterface $formElement * @param int $timestamp * @param string $defaultMessage */ protected function addError( FormElementInterface $formElement, int $timestamp, - string $defaultMessage = '' + string $defaultMessage = '', ): void { $error = GeneralUtility::makeInstance( Error::class, @@ -333,9 +311,9 @@ protected function addError( $timestamp, [], $defaultMessage, - $this->formRuntime + $this->formRuntime, ), - $timestamp + $timestamp, ); $this->formDefinition ->getProcessingRule($formElement->getIdentifier()) @@ -349,7 +327,7 @@ protected function addError( */ protected function removeDeletedRepeatableContainersFromFormValuesByRequest( array $requestArguments, - array $argumentPath = [] + array $argumentPath = [], ): void { foreach ($requestArguments as $argumentKey => $argumentValue) { if (is_array($argumentValue)) { @@ -387,14 +365,15 @@ protected function removeDeletedRepeatableContainersFromFormValuesByRequest( * @param FormElementInterface $originalFormElement * @param FormElementInterface $newFormElement * @param string $newIdentifier - * @return void */ protected function copyVariants( FormElementInterface $originalFormElement, FormElementInterface $newFormElement, - string $newIdentifier): void - { - if (!$this->features->isFeatureEnabled('repeatableFormElements.copyVariants')) return; + string $newIdentifier, + ): void { + if (!$this->features->isFeatureEnabled('repeatableFormElements.copyVariants')) { + return; + } $originalVariants = $originalFormElement->getVariants(); foreach ($originalVariants as $originalIdentifier => $originalVariant) { @@ -405,15 +384,10 @@ protected function copyVariants( // variant properties are protected and class is marked internal, // so we use reflection $reflectionClass = new \ReflectionClass(RenderableVariant::class); - $propOption = $reflectionClass->getProperty('options'); - $propCondition = $reflectionClass->getProperty('condition'); - // @todo: can be ommited when php7.4 is no longer supported. - if (version_compare(phpversion(), '8.1.0', '<')) { - $propOption->setAccessible(true); - $propCondition->setAccessible(true); - } - $options = $propOption->getValue($originalVariant); - $options['condition'] = $propCondition->getValue($originalVariant); + $propOption = $reflectionClass->getProperty('options'); + $propCondition = $reflectionClass->getProperty('condition'); + $options = $propOption->getValue($originalVariant); + $options['condition'] = $propCondition->getValue($originalVariant); $options['identifier'] = $originalIdentifier; $eventDispatcher = GeneralUtility::makeInstance(EventDispatcherInterface::class); @@ -423,7 +397,9 @@ protected function copyVariants( ); // only add this variant, if it did not get disabled. - if (!$event->isVariantEnabled()) continue; + if (!$event->isVariantEnabled()) { + continue; + } $options = $event->getOptions(); $newFormElement->createVariant($options); diff --git a/Configuration/JavaScriptModules.php b/Configuration/JavaScriptModules.php new file mode 100644 index 0000000..07c3a1e --- /dev/null +++ b/Configuration/JavaScriptModules.php @@ -0,0 +1,8 @@ + ['form'], + 'imports' => [ + '@tritum/repeatable-form-elements/' => 'EXT:repeatable_form_elements/Resources/Public/JavaScript/', + ], +]; diff --git a/Configuration/Sets/RepeatableFormElements/config.yaml b/Configuration/Sets/RepeatableFormElements/config.yaml new file mode 100644 index 0000000..3248258 --- /dev/null +++ b/Configuration/Sets/RepeatableFormElements/config.yaml @@ -0,0 +1,4 @@ +name: tritum/repeatable-form-elements +label: "Form: Repeatable form elements" +dependencies: + - typo3/form diff --git a/Configuration/Sets/RepeatableFormElements/setup.typoscript b/Configuration/Sets/RepeatableFormElements/setup.typoscript new file mode 100644 index 0000000..088e6ec --- /dev/null +++ b/Configuration/Sets/RepeatableFormElements/setup.typoscript @@ -0,0 +1 @@ +@import 'EXT:repeatable_form_elements/Configuration/TypoScript/setup.typoscript' diff --git a/Configuration/TCA/Overrides/sys_template.php b/Configuration/TCA/Overrides/sys_template.php index 6838c21..8c2d83b 100644 --- a/Configuration/TCA/Overrides/sys_template.php +++ b/Configuration/TCA/Overrides/sys_template.php @@ -6,6 +6,6 @@ \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile( \TRITUM\RepeatableFormElements\Configuration\Extension::KEY, 'Configuration/TypoScript', - 'Repeatable form configuration' + 'Repeatable form configuration', ); }); diff --git a/Configuration/TypoScript/setup.typoscript b/Configuration/TypoScript/setup.typoscript index 8dc3a13..d4447b7 100644 --- a/Configuration/TypoScript/setup.typoscript +++ b/Configuration/TypoScript/setup.typoscript @@ -8,7 +8,7 @@ plugin.tx_form { page { includeJSFooter { - 1511193639 = EXT:repeatable_form_elements/Resources/Public/JavaScript/Form/Frontend/RepeatableContainer.js + 1511193639 = EXT:repeatable_form_elements/Resources/Public/JavaScript/frontend/repeatable-container.js } includeCSS { diff --git a/Configuration/Yaml/FormSetup.yaml b/Configuration/Yaml/FormSetup.yaml index 3913d1c..e540975 100644 --- a/Configuration/Yaml/FormSetup.yaml +++ b/Configuration/Yaml/FormSetup.yaml @@ -1,32 +1,29 @@ -TYPO3: - CMS: - Form: - prototypes: - standard: - finishersDefinition: - ExtendedSaveToDatabase: - formEngine: - label: 'Extended SaveToDatabase Finisher' - implementationClassName: 'TRITUM\RepeatableFormElements\Finisher\SaveToDatabaseFinisher' +prototypes: + standard: + finishersDefinition: + ExtendedSaveToDatabase: + formEngine: + label: 'Extended SaveToDatabase Finisher' + implementationClassName: 'TRITUM\RepeatableFormElements\Finisher\SaveToDatabaseFinisher' - formElementsDefinition: - Form: - renderingOptions: - translation: - translationFile: - 10: 'EXT:form/Resources/Private/Language/locallang.xlf' - 1595333290: 'EXT:repeatable_form_elements/Resources/Private/Language/locallang.xlf' - translationFiles: - 1595333290: 'EXT:repeatable_form_elements/Resources/Private/Language/locallang.xlf' - partialRootPaths: - 1595333290: 'EXT:repeatable_form_elements/Resources/Private/Frontend/Partials' + formElementsDefinition: + Form: + renderingOptions: + translation: + translationFile: + 10: 'EXT:form/Resources/Private/Language/locallang.xlf' + 1595333290: 'EXT:repeatable_form_elements/Resources/Private/Language/locallang.xlf' + translationFiles: + 1595333290: 'EXT:repeatable_form_elements/Resources/Private/Language/locallang.xlf' + partialRootPaths: + 1595333290: 'EXT:repeatable_form_elements/Resources/Private/Frontend/Partials' - RepeatableContainer: - __inheritances: - 10: 'TYPO3.CMS.Form.prototypes.standard.formElementsDefinition.Fieldset' - implementationClassName: 'TRITUM\RepeatableFormElements\FormElements\RepeatableContainer' - properties: - minimumCopies: 0 - maximumCopies: 10 - showRemoveButton: true - elementClassAttribute: 'repeatable-container' + RepeatableContainer: + __inheritances: + 10: 'TYPO3.CMS.Form.prototypes.standard.formElementsDefinition.Fieldset' + implementationClassName: 'TRITUM\RepeatableFormElements\FormElements\RepeatableContainer' + properties: + minimumCopies: 0 + maximumCopies: 10 + showRemoveButton: true + elementClassAttribute: 'repeatable-container' diff --git a/Configuration/Yaml/FormSetupBackend.yaml b/Configuration/Yaml/FormSetupBackend.yaml index e980952..a227030 100644 --- a/Configuration/Yaml/FormSetupBackend.yaml +++ b/Configuration/Yaml/FormSetupBackend.yaml @@ -1,65 +1,62 @@ -TYPO3: - CMS: - Form: - prototypes: - standard: - formElementsDefinition: - RepeatableContainer: - formEditor: - predefinedDefaults: - properties: - minimumCopies: 0 - maximumCopies: 10 - showRemoveButton: true - copyButtonLabel: 'formEditor.elements.RepeatableContainer.editor.copyButtonLabel.value' - removeButtonLabel: 'formEditor.elements.RepeatableContainer.editor.removeButtonLabel.value' - label: 'formEditor.elements.RepeatableContainer.label' - groupSorting: 150 - iconIdentifier: 't3-form-icon-repeatable-container' - editors: - 200: - label: 'formEditor.elements.RepeatableContainer.editor.label.label' - 400: - identifier: 'copyButtonLabel' - templateName: 'Inspector-TextEditor' - label: 'formEditor.elements.RepeatableContainer.editor.copyButtonLabel.label' - propertyPath: 'properties.copyButtonLabel' - 500: - identifier: 'removeButtonLabel' - templateName: 'Inspector-TextEditor' - label: 'formEditor.elements.RepeatableContainer.editor.removeButtonLabel.label' - propertyPath: 'properties.removeButtonLabel' - 600: - identifier: 'minimumCopies' - templateName: 'Inspector-TextEditor' - label: 'formEditor.elements.RepeatableContainer.editor.minimumCopies.label' - propertyPath: 'properties.minimumCopies' - propertyValidatorsMode: 'OR' - propertyValidators: - 10: 'Integer' - 20: 'FormElementIdentifierWithinCurlyBracesExclusive' - 700: - identifier: 'maximumCopies' - templateName: 'Inspector-TextEditor' - label: 'formEditor.elements.RepeatableContainer.editor.maximumCopies.label' - propertyPath: 'properties.maximumCopies' - propertyValidatorsMode: 'OR' - propertyValidators: - 10: 'Integer' - 20: 'FormElementIdentifierWithinCurlyBracesExclusive' - 800: - identifier: 'showRemoveButton' - templateName: 'Inspector-CheckboxEditor' - label: 'formEditor.elements.RepeatableContainer.editor.showRemoveButton.label' - propertyPath: 'properties.showRemoveButton' - formEditor: - translationFile: - 10: 'EXT:form/Resources/Private/Language/Database.xlf' - 1595333290: 'EXT:repeatable_form_elements/Resources/Private/Language/Database.xlf' - translationFiles: - 1595333290: 'EXT:repeatable_form_elements/Resources/Private/Language/Database.xlf' - dynamicRequireJsModules: - additionalViewModelModules: - 1595333290: 'TYPO3/CMS/RepeatableFormElements/Form/Backend/FormEditor/ViewModel' - formEditorPartials: - FormElement-RepeatableContainer: 'Stage/Fieldset' +prototypes: + standard: + formElementsDefinition: + RepeatableContainer: + formEditor: + predefinedDefaults: + properties: + minimumCopies: 0 + maximumCopies: 10 + showRemoveButton: true + copyButtonLabel: 'formEditor.elements.RepeatableContainer.editor.copyButtonLabel.value' + removeButtonLabel: 'formEditor.elements.RepeatableContainer.editor.removeButtonLabel.value' + label: 'formEditor.elements.RepeatableContainer.label' + groupSorting: 150 + iconIdentifier: 't3-form-icon-repeatable-container' + editors: + 200: + label: 'formEditor.elements.RepeatableContainer.editor.label.label' + 400: + identifier: 'copyButtonLabel' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.RepeatableContainer.editor.copyButtonLabel.label' + propertyPath: 'properties.copyButtonLabel' + 500: + identifier: 'removeButtonLabel' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.RepeatableContainer.editor.removeButtonLabel.label' + propertyPath: 'properties.removeButtonLabel' + 600: + identifier: 'minimumCopies' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.RepeatableContainer.editor.minimumCopies.label' + propertyPath: 'properties.minimumCopies' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'Integer' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + 700: + identifier: 'maximumCopies' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.RepeatableContainer.editor.maximumCopies.label' + propertyPath: 'properties.maximumCopies' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'Integer' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + 800: + identifier: 'showRemoveButton' + templateName: 'Inspector-CheckboxEditor' + label: 'formEditor.elements.RepeatableContainer.editor.showRemoveButton.label' + propertyPath: 'properties.showRemoveButton' + formEditor: + translationFile: + 10: 'EXT:form/Resources/Private/Language/Database.xlf' + 1595333290: 'EXT:repeatable_form_elements/Resources/Private/Language/Database.xlf' + translationFiles: + 1595333290: 'EXT:repeatable_form_elements/Resources/Private/Language/Database.xlf' + dynamicJavaScriptModules: + additionalViewModelModules: + 1595333290: '@tritum/repeatable-form-elements/backend/form-editor/view-model.js' + formEditorPartials: + FormElement-RepeatableContainer: 'Stage/Fieldset' diff --git a/README.md b/README.md index fe735b0..c4f3b78 100644 --- a/README.md +++ b/README.md @@ -12,13 +12,12 @@ TYPO3 form framework. It displays one/ a set of fields which can be duplicated and removed if desired. Any existing validation is copied as well. All form finishers will be aware of the copied field(s). -## Installation +## Preferred installation -Copy the extension folder to `\typo3conf\ext\ `, upload it via the extension -manager or add it to your composer.json (`composer require tritum/repeatable-form-elements`). -Add the static TypoScript configuration to your TypoScript template. - -We have tested with TYPO3 v11.5 and v12.4. +1. Require the extension via composer. +2. Add the site set tritum/form-element-linked-checkbox to the dependencies of + your site packages site set (TYPO3 v13). Or add the static TypoScript + configuration to your TypoScript template (TYPO3 v12 and TYPO3 v13). ## Usage @@ -41,8 +40,8 @@ To deactivate the copying of variants, the feature `repeatableFormElements.copyV The following options can be used to extend the behavior when copying. -| Name | Description | -| ---- |------------------------------------------------------------------| +| Name | Description | +|------------------|------------------------------------------------------------------| | CopyVariantEvent | Extend manipulation of copied variants or disable specific ones. | ## Credits diff --git a/Resources/Public/JavaScript/Form/Backend/FormEditor/ViewModel.js b/Resources/Public/JavaScript/Form/Backend/FormEditor/ViewModel.js deleted file mode 100644 index 0ab51d9..0000000 --- a/Resources/Public/JavaScript/Form/Backend/FormEditor/ViewModel.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Module: TYPO3/CMS/RepeatableFormElements/Form/Backend/FormEditor/ViewModel - */ - -define(['jquery', - 'TYPO3/CMS/Form/Backend/FormEditor/StageComponent' - ], function ($, StageComponent) { - 'use strict'; - - return (function ($, StageComponent) { - - /** - * @private - * - * @var object - */ - var _formEditorApp = null; - - /** - * @private - * - * @return object - */ - function getFormEditorApp() { - return _formEditorApp; - } - - /** - * @private - * - * @return object - */ - function getPublisherSubscriber() { - return getFormEditorApp().getPublisherSubscriber(); - } - - /** - * @private - * - * @return void - */ - function _subscribeEvents() { - - /** - * @private - * - * @param string - * @param array - * args[0] = formElement - * args[1] = template - * @return void - * @subscribe view/stage/abstract/render/template/perform - */ - getPublisherSubscriber().subscribe('view/stage/abstract/render/template/perform', function (topic, args) { - if (args[0].get('type') === 'RepeatableContainer') { - StageComponent.renderSimpleTemplate(args[0], args[1]); - } - }); - } - - /** - * @public - * - * @param object formEditorApp - * @return void - */ - function bootstrap(formEditorApp) { - _formEditorApp = formEditorApp; - _subscribeEvents(); - } - - /** - * Publish the public methods. - * Implements the "Revealing Module Pattern". - */ - return { - bootstrap: bootstrap - }; - - })($, StageComponent); -}); diff --git a/Resources/Public/JavaScript/backend/form-editor/view-model.js b/Resources/Public/JavaScript/backend/form-editor/view-model.js new file mode 100644 index 0000000..6b68d43 --- /dev/null +++ b/Resources/Public/JavaScript/backend/form-editor/view-model.js @@ -0,0 +1,111 @@ +/** + * Module: @vendor/form-element-linked-checkbox/Backend/FormEditor/ViewModel.js + */ + +import $ from 'jquery'; +import * as Helper from '@typo3/form/backend/form-editor/helper.js' +import {renderCheckboxTemplate} from '@typo3/form/backend/form-editor/stage-component.js' + +/** + * @private + * + * @var object + */ +let _formEditorApp = null; + +/** + * @private + * + * @return object + */ +function getFormEditorApp() { + return _formEditorApp; +}; + +/** + * @private + * + * @return object + */ +function getPublisherSubscriber() { + return getFormEditorApp().getPublisherSubscriber(); +}; + +/** + * @private + * + * @return object + */ +function getUtility() { + return getFormEditorApp().getUtility(); +}; + +/** + * @private + * + * @param object + * @return object + */ +function getHelper() { + return Helper; +}; + +/** + * @private + * + * @return object + */ +function getCurrentlySelectedFormElement() { + return getFormEditorApp().getCurrentlySelectedFormElement(); +}; + +/** + * @private + * + * @param mixed test + * @param string message + * @param int messageCode + * @return void + */ +function assert(test, message, messageCode) { + return getFormEditorApp().assert(test, message, messageCode); +}; + +/** + * @private + * + * @return void + * @throws 1491643380 + */ +function _helperSetup() { + assert('function' === $.type(Helper.bootstrap), + 'The view model helper does not implement the method "bootstrap"', + 1491643380 + ); + Helper.bootstrap(getFormEditorApp()); +}; + +/** + * @private + * + * @return void + */ +function _subscribeEvents() { + getPublisherSubscriber().subscribe('view/stage/abstract/render/template/perform', function(topic, args) { + if (args[0].get('type') === 'RepeatableContainer') { + renderCheckboxTemplate(args[0], args[1]); + } + }); +}; + +/** + * @public + * + * @param object formEditorApp + * @return void + */ +export function bootstrap(formEditorApp) { + _formEditorApp = formEditorApp; + _helperSetup(); + _subscribeEvents(); +}; diff --git a/Resources/Public/JavaScript/Form/Frontend/RepeatableContainer.js b/Resources/Public/JavaScript/frontend/repeatable-container.js similarity index 100% rename from Resources/Public/JavaScript/Form/Frontend/RepeatableContainer.js rename to Resources/Public/JavaScript/frontend/repeatable-container.js diff --git a/composer.json b/composer.json index e18a99b..4c80f0f 100644 --- a/composer.json +++ b/composer.json @@ -30,9 +30,9 @@ } ], "require": { - "typo3/cms-core": "^11.5.23 || ^12.4", - "typo3/cms-extbase": "^11.5.23 || ^12.4", - "typo3/cms-form": "^11.5.23 || ^12.4" + "typo3/cms-core": "^12.4 || ^13.4", + "typo3/cms-extbase": "^12.4 || ^13.4", + "typo3/cms-form": "^12.4 || ^13.4" }, "autoload": { "psr-4": { diff --git a/ext_emconf.php b/ext_emconf.php index 56f8014..252f5c5 100644 --- a/ext_emconf.php +++ b/ext_emconf.php @@ -1,4 +1,5 @@ 'Repeatable form elements', 'description' => 'Adds a new form element which allows the editor to create new container elements with any type fields in them. In the frontend, a user can create any number of new containers. This is an extension for TYPO3 CMS.', @@ -6,10 +7,10 @@ 'state' => 'stable', 'author' => 'Ralf Zimmermann, Elias Häußler, Christian Seyfferth', 'author_email' => 'r.zimmermann@dreistrom.land, elias@haeussler.dev, c.seyfferth@dreistrom.land', - 'version' => '4.1.1', + 'version' => '5.0.0', 'constraints' => [ 'depends' => [ - 'typo3' => '11.5.23-12.4.99', + 'typo3' => '12.4.0-13.4.99', ], 'conflicts' => [], 'suggests' => [], diff --git a/ext_localconf.php b/ext_localconf.php index f5393bf..1198e1e 100644 --- a/ext_localconf.php +++ b/ext_localconf.php @@ -6,7 +6,6 @@ call_user_func(function () { Extension::addTypoScriptSetup(); - Extension::registerIcons(); Extension::registerHooks(); $GLOBALS['TYPO3_CONF_VARS']['SYS']['features']['repeatableFormElements.copyVariants'] ??= true;