From 553681a2baaa94ec4b446897d3761e6ed399f373 Mon Sep 17 00:00:00 2001 From: Ralf Zimmermann <ralf.zimmermann@tritum.de> Date: Sat, 19 Nov 2016 13:06:19 +0100 Subject: [PATCH] [FEATURE] EXT:form - integrate new form framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The main purpose of this patch is to integrate a flexible framework for building forms. It replaces the legacy 'form wizard' based on ExtJS and the depending frontend rendering system. The new backend 'form editor' relies on vanilla JS and jQuery. Different JS patterns have been applied to ensure a modern architecture, high flexibility and extensibility. A new backend module lists all existing forms and allows the creation of new ones. The 'mailform' content element is reworked. It lists available forms and enables the backend editor to override certain settings, e.g. 'finisher' settings (formerly known as 'postProcessors'). Till now it was not possible to customize and extend the 'form editor'. To allow the registration of new finishers, validators and pre-defined form elements a lot of architectural changes were needed. After a long conceptional phase the team decided to remove the former code base, backport the 'form' package of the Flow project and improve the given concepts. The result is a new form extension. A lot of code received major improvements and tons of additional features have been integrated. The list of features is long and impressive. The documentation - which is part of a future patch - will explain the ideas, concept and architecture as well as the functionality in detail. This patch marks the beginning of a series of patches. Further work is needed to implement a better UI and more tests. The currently integrated element tree cannot be finished for now. We plan to use the new TYPO3 SVG tree but have to wait for the drag and drop implementation. Furthermore, the old form wizard will be moved to a separate extension for backward compatibility. Resolves: #77910 Releases: master Change-Id: Idde8453bc573da835959fa3e51e30f57792d98b0 Reviewed-on: https://review.typo3.org/50311 Tested-by: TYPO3com <no-reply@typo3.com> Reviewed-by: Bjoern Jacob <bjoern.jacob@tritum.de> Tested-by: Bjoern Jacob <bjoern.jacob@tritum.de> Reviewed-by: Ralf Zimmermann <ralf.zimmermann@tritum.de> Tested-by: Ralf Zimmermann <ralf.zimmermann@tritum.de> Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de> Reviewed-by: Frank Naegler <frank.naegler@typo3.org> Tested-by: Frank Naegler <frank.naegler@typo3.org> Reviewed-by: Alexander Opitz <opitz.alexander@googlemail.com> Tested-by: Alexander Opitz <opitz.alexander@googlemail.com> Reviewed-by: Andreas Häfner <andreas.haefner@tritum.de> Tested-by: Andreas Häfner <andreas.haefner@tritum.de> Reviewed-by: Benni Mack <benni@typo3.org> Tested-by: Benni Mack <benni@typo3.org> Reviewed-by: Christian Kuhn <lolli@schwarzbu.ch> Tested-by: Christian Kuhn <lolli@schwarzbu.ch> --- Build/Resources/Public/Less/form.less | 1477 +++++++----- composer.json | 1 + composer.lock | 102 +- ...7910-EXTform-IntroduceNewFormFramework.rst | 54 + .../ContentObject/FormContentObject.php | 124 - .../Controller/AbstractBackendController.php | 91 + .../Controller/FormEditorController.php | 445 ++++ .../Controller/FormFrontendController.php | 138 ++ .../Controller/FormManagerController.php | 463 ++++ .../Classes/Controller/FrontendController.php | 342 --- .../Classes/Controller/WizardController.php | 92 - .../Classes/Domain/Builder/ElementBuilder.php | 510 ---- .../Classes/Domain/Builder/FormBuilder.php | 579 ----- .../Domain/Builder/ValidationBuilder.php | 254 -- .../Configuration/ConfigurationService.php | 61 + .../Exception/PrototypeNotFoundException.php} | 21 +- .../FilterInterface.php => Exception.php} | 18 +- .../IdentifierNotValidException.php} | 22 +- .../Domain/Exception/RenderingException.php | 27 + .../TypeDefinitionNotFoundException.php | 28 + .../TypeDefinitionNotValidException.php | 28 + .../UnknownCompositRenderableException.php | 28 + .../Domain/Factory/AbstractFormFactory.php | 65 + .../Domain/Factory/ArrayFormFactory.php | 120 + .../Domain/Factory/FormFactoryInterface.php | 46 + .../Domain/Factory/JsonToTypoScript.php | 591 ----- .../Classes/Domain/Filter/AbstractFilter.php | 40 - .../Domain/Filter/AlphabeticFilter.php | 64 - .../Domain/Filter/AlphanumericFilter.php | 64 - .../Classes/Domain/Filter/CurrencyFilter.php | 109 - .../Classes/Domain/Filter/RegExpFilter.php | 61 - .../Classes/Domain/Filter/RemoveXssFilter.php | 43 - .../Classes/Domain/Filter/TitleCaseFilter.php | 33 - .../form/Classes/Domain/Filter/TrimFilter.php | 70 - .../Classes/Domain/Filter/UpperCaseFilter.php | 32 - .../Domain/Finishers/AbstractFinisher.php | 208 ++ .../Domain/Finishers/ClosureFinisher.php | 64 + .../Domain/Finishers/ConfirmationFinisher.php | 63 + .../Finishers/DeleteUploadsFinisher.php | 57 + .../Domain/Finishers/EmailFinisher.php | 187 ++ .../Finishers/Exception/FinisherException.php | 27 + .../Domain/Finishers/FinisherContext.php | 115 + .../Domain/Finishers/FinisherInterface.php | 52 + .../Domain/Finishers/FlashMessageFinisher.php | 97 + .../Domain/Finishers/RedirectFinisher.php | 151 ++ .../Finishers/SaveToDatabaseFinisher.php | 100 + .../Classes/Domain/Model/Configuration.php | 174 -- .../form/Classes/Domain/Model/Content.php | 133 -- .../form/Classes/Domain/Model/Element.php | 485 ---- .../form/Classes/Domain/Model/Exception.php | 27 + .../DuplicateFormElementException.php | 28 + .../FinisherPresetNotFoundException.php | 28 + .../FormDefinitionConsistencyException.php | 28 + .../ValidatorPresetNotFoundException.php | 28 + .../Classes/Domain/Model/FormDefinition.php | 695 ++++++ .../FormElements/AbstractFormElement.php | 168 ++ .../Model/FormElements/AbstractSection.php | 199 ++ .../Model/FormElements/AdvancedPassword.php | 53 + .../FormElements/DatePicker.php} | 20 +- .../Domain/Model/FormElements/FileUpload.php | 109 + .../FormElements/FormElementInterface.php | 147 ++ .../Model/FormElements/GenericFormElement.php | 26 + .../Domain/Model/FormElements/Page.php | 68 + .../Domain/Model/FormElements/Section.php | 166 ++ .../Model/FormElements/UnknownFormElement.php | 147 ++ .../Domain/Model/Json/AbstractJsonElement.php | 178 -- .../Domain/Model/Json/ButtonJsonElement.php | 74 - .../Model/Json/CheckboxGroupJsonElement.php | 120 - .../Domain/Model/Json/CheckboxJsonElement.php | 77 - .../Model/Json/ContainerJsonElement.php | 42 - .../Domain/Model/Json/FieldsetJsonElement.php | 81 - .../Model/Json/FileuploadJsonElement.php | 78 - .../Domain/Model/Json/FormJsonElement.php | 141 -- .../Domain/Model/Json/HeaderJsonElement.php | 88 - .../Domain/Model/Json/HiddenJsonElement.php | 72 - .../Domain/Model/Json/NameJsonElement.php | 98 - .../Domain/Model/Json/PasswordJsonElement.php | 82 - .../Model/Json/RadioGroupJsonElement.php | 120 - .../Domain/Model/Json/RadioJsonElement.php | 77 - .../Domain/Model/Json/ResetJsonElement.php | 74 - .../Domain/Model/Json/SelectJsonElement.php | 120 - .../Domain/Model/Json/SubmitJsonElement.php | 74 - .../Domain/Model/Json/TextareaJsonElement.php | 97 - .../Model/Json/TextblockJsonElement.php | 82 - .../Domain/Model/Json/TextlineJsonElement.php | 84 - .../AbstractCompositeRenderable.php | 210 ++ .../Model/Renderable/AbstractRenderable.php | 423 ++++ .../CompositeRenderableInterface.php | 35 + .../Model/Renderable/RenderableInterface.php | 92 + .../Renderable/RootRenderableInterface.php | 86 + .../Domain/Model/ValidationElement.php | 89 - .../ArrayToValidationElementConverter.php | 124 - .../Renderer/AbstractElementRenderer.php | 73 + .../Domain/Renderer/FluidFormRenderer.php | 54 + .../Domain/Renderer/RendererInterface.php | 62 + .../Renderer/UnknownFormElementRenderer.php | 72 + .../Domain/Repository/ContentRepository.php | 129 - .../Repository/TypoScriptRepository.php | 223 -- .../Exception/PropertyMappingException.php | 29 + .../Classes/Domain/Runtime/FormRuntime.php | 780 ++++++ .../form/Classes/Domain/Runtime/FormState.php | 100 + .../Domain/Validator/AbstractValidator.php | 116 - .../Domain/Validator/AlphabeticValidator.php | 88 - .../Validator/AlphanumericValidator.php | 88 - .../Domain/Validator/BetweenValidator.php | 109 - .../Domain/Validator/DateValidator.php | 154 -- .../Domain/Validator/DigitValidator.php | 47 - .../Domain/Validator/EmailValidator.php | 55 - .../Domain/Validator/EqualsValidator.php | 69 - .../Validator/FileAllowedTypesValidator.php | 81 - .../Validator/FileMaximumSizeValidator.php | 72 - .../Validator/FileMinimumSizeValidator.php | 72 - .../Domain/Validator/FloatValidator.php | 62 - .../Domain/Validator/GreaterThanValidator.php | 68 - .../Domain/Validator/InArrayValidator.php | 124 - .../Domain/Validator/IntegerValidator.php | 62 - .../Classes/Domain/Validator/IpValidator.php | 46 - .../Domain/Validator/LengthValidator.php | 128 - .../Domain/Validator/LessThanValidator.php | 68 - .../Domain/Validator/RegExpValidator.php | 55 - .../Domain/Validator/RequiredValidator.php | 102 - .../Classes/Domain/Validator/UriValidator.php | 46 - .../Validator/ValidationElementValidator.php | 257 -- typo3/sysext/form/Classes/Exception.php | 25 + .../Hooks/DataStructureIdentifierHook.php | 249 ++ .../Hooks/HandleIncomingFormValues.php | 205 -- .../MailformPreviewRenderer.php | 42 - .../Classes/Hooks/SoftReferenceParserHook.php | 60 + .../Configuration/ConfigurationManager.php | 146 ++ .../ConfigurationManagerInterface.php | 29 + .../Classes/Mvc/Configuration/Exception.php | 27 + .../Exception/CycleInheritancesException.php | 28 + .../ExtensionNameRequiredException.php | 28 + .../Exception/NoSuchFileException.php | 24 + .../Exception/ParseErrorException.php | 24 + .../InheritancesResolverService.php | 375 +++ .../Mvc/Configuration/TypoScriptService.php | 99 + .../Classes/Mvc/Configuration/YamlSource.php | 156 ++ .../Mvc/Controller/ControllerContext.php | 85 - .../Classes/Mvc/Persistence/Exception.php | 27 + .../Exception/NoUniqueIdentifierException.php | 25 + ...NoUniquePersistenceIdentifierException.php | 25 + .../Exception/PersistenceManagerException.php | 27 + .../Persistence/FormPersistenceManager.php | 493 ++++ .../FormPersistenceManagerInterface.php | 110 + .../form/Classes/Mvc/ProcessingRule.php | 181 ++ .../UploadedFileReferenceConverter.php | 291 +++ .../Classes/Mvc/Validation/CountValidator.php | 69 + .../Classes/Mvc/Validation/EmptyValidator.php | 55 + .../InvalidValidationOptionsException.php | 25 + .../Mvc/Validation/MimeTypeValidator.php | 90 + .../sysext/form/Classes/Mvc/View/FormView.php | 233 ++ .../PostProcess/AbstractPostProcessor.php | 37 - .../Classes/PostProcess/MailPostProcessor.php | 566 ----- .../Classes/PostProcess/PostProcessor.php | 106 - .../PostProcess/PostProcessorInterface.php | 44 - .../PostProcess/RedirectPostProcessor.php | 84 - .../Classes/Service/TranslationService.php | 508 ++++ .../form/Classes/Utility/ArrayUtility.php | 153 ++ .../form/Classes/Utility/ElementCounter.php | 34 - .../form/Classes/Utility/FormUtility.php | 283 --- .../form/Classes/Utility/SessionUtility.php | 160 -- .../Utility/TypoScriptToJsonConverter.php | 212 -- .../View/Wizard/Element/FormWizardElement.php | 230 -- .../AggregateSelectOptionsViewHelper.php | 116 - .../ViewHelpers/Be/PageRendererViewHelper.php | 56 + .../RenderContentElementPreviewViewHelper.php | 73 + .../ViewHelpers/Form/CheckboxViewHelper.php | 41 + .../ViewHelpers/Form/DatePickerViewHelper.php | 161 ++ .../ViewHelpers/Form/TimePickerViewHelper.php | 152 ++ .../Form/UploadedResourceViewHelper.php | 133 ++ .../Classes/ViewHelpers/FormViewHelper.php | 62 + .../ViewHelpers/PlainMailViewHelper.php | 132 - .../ViewHelpers/PlainTextMailViewHelper.php | 79 + .../RenderAllFormValuesViewHelper.php | 190 ++ .../RenderRenderableViewHelper.php | 66 + .../Classes/ViewHelpers/RenderViewHelper.php | 101 + .../Classes/ViewHelpers/SelectViewHelper.php | 139 -- .../TranslateElementPropertyViewHelper.php | 80 + .../form/Configuration/Backend/AjaxRoutes.php | 13 - .../Configuration/FlexForms/FormFramework.xml | 40 + .../form/Configuration/PageTS/modWizards.ts | 380 +-- .../TCA/Overrides/sys_template.php | 5 - .../TCA/Overrides/tt_content.php | 141 +- .../TypoScript/Elements/Button.ts | 38 - .../TypoScript/Elements/Buttontag.ts | 145 -- .../TypoScript/Elements/Checkbox.ts | 147 -- .../TypoScript/Elements/Checkboxgroup.ts | 67 - .../TypoScript/Elements/Contentelement.ts | 69 - .../TypoScript/Elements/Fieldset.ts | 96 - .../TypoScript/Elements/Fileupload.ts | 148 -- .../Configuration/TypoScript/Elements/Form.ts | 166 -- .../TypoScript/Elements/Header.ts | 65 - .../TypoScript/Elements/Hidden.ts | 140 -- .../TypoScript/Elements/Input.ts | 168 -- .../TypoScript/Elements/Inputtypebutton.ts | 144 -- .../TypoScript/Elements/Label.ts | 22 - .../TypoScript/Elements/Optgroup.ts | 17 - .../TypoScript/Elements/Option.ts | 17 - .../TypoScript/Elements/Password.ts | 153 -- .../TypoScript/Elements/Radio.ts | 148 -- .../TypoScript/Elements/Radiogroup.ts | 67 - .../TypoScript/Elements/Reset.ts | 145 -- .../TypoScript/Elements/Select.ts | 137 -- .../TypoScript/Elements/Submit.ts | 141 -- .../TypoScript/Elements/Textarea.ts | 148 -- .../TypoScript/Elements/Textblock.ts | 65 - .../TypoScript/Elements/Textline.ts | 160 -- .../TypoScript/Filters/Filters.ts | 66 - .../TypoScript/Validators/Validators.ts | 111 - .../form/Configuration/TypoScript/backend.txt | 15 + .../Configuration/TypoScript/constants.txt | 10 - .../form/Configuration/TypoScript/setup.txt | 278 +-- .../UserTSconfig/userTSConfig.txt | 2 - .../form/Configuration/Yaml/BaseSetup.yaml | 340 +++ .../Configuration/Yaml/FormEditorSetup.yaml | 1005 ++++++++ .../Configuration/Yaml/FormEngineSetup.yaml | 132 + typo3/sysext/form/Documentation/.gitignore | 7 - .../Administration/DefaultNewRecord/Index.rst | 38 - .../Documentation/Administration/Index.rst | 23 - .../DefaultsReference/ElementsTab/Index.rst | 131 - .../DefaultsReference/FormTab/Index.rst | 187 -- .../DefaultsReference/Index.rst | 24 - .../DefaultsReference/OptionsTab/Index.rst | 404 ---- .../DefaultsReference/ShowTabs/Index.rst | 75 - .../ElementsReference/Index.rst | 41 - .../Administration/WizardSettings/Index.rst | 25 - .../Filters/Alphabetic/Index.rst | 33 - .../Filters/Alphanumeric/Index.rst | 33 - .../Configuration/Filters/Currency/Index.rst | 71 - .../Configuration/Filters/Digit/Index.rst | 11 - .../Configuration/Filters/Index.rst | 53 - .../Configuration/Filters/Integer/Index.rst | 12 - .../Configuration/Filters/Lowercase/Index.rst | 12 - .../Configuration/Filters/Regexp/Index.rst | 28 - .../Filters/Stripnewlines/Index.rst | 11 - .../Configuration/Filters/Titlecase/Index.rst | 24 - .../Configuration/Filters/Trim/Index.rst | 33 - .../Configuration/Filters/Uppercase/Index.rst | 12 - .../Documentation/Configuration/Index.rst | 28 - .../Configuration/Layout/Index.rst | 69 - .../Layout/LayoutObjectSpecific/Index.rst | 29 - .../Layout/LayoutViewSpecific/Index.rst | 1592 ------------ .../Layout/LayoutWholeForm/Index.rst | 46 - .../Configuration/Objects/Button/Index.rst | 158 -- .../Configuration/Objects/Checkbox/Index.rst | 169 -- .../Configuration/Objects/Fieldset/Index.rst | 96 - .../Objects/Fileupload/Index.rst | 156 -- .../Configuration/Objects/Form/Index.rst | 313 --- .../Configuration/Objects/Header/Index.rst | 30 - .../Configuration/Objects/Hidden/Index.rst | 100 - .../Configuration/Objects/Index.rst | 172 -- .../Objects/ObjectAttributes/Index.rst | 973 -------- .../Configuration/Objects/Optgroup/Index.rst | 110 - .../Configuration/Objects/Option/Index.rst | 115 - .../Configuration/Objects/Password/Index.rst | 198 -- .../Configuration/Objects/Radio/Index.rst | 190 -- .../Configuration/Objects/Reset/Index.rst | 154 -- .../Configuration/Objects/Select/Index.rst | 165 -- .../Configuration/Objects/Submit/Index.rst | 152 -- .../Configuration/Objects/Textarea/Index.rst | 169 -- .../Configuration/Objects/Textblock/Index.rst | 22 - .../Configuration/Objects/Textline/Index.rst | 185 -- .../Configuration/Postprocessors/Index.rst | 61 - .../Postprocessors/Mail/Index.rst | 321 --- .../Postprocessors/Redirect/Index.rst | 26 - .../Configuration/Rules/Alphabetic/Index.rst | 69 - .../Rules/Alphanumeric/Index.rst | 69 - .../Configuration/Rules/Between/Index.rst | 122 - .../Configuration/Rules/Date/Index.rst | 85 - .../Configuration/Rules/Digit/Index.rst | 55 - .../Configuration/Rules/Email/Index.rst | 60 - .../Configuration/Rules/Equals/Index.rst | 80 - .../Configuration/Rules/Float/Index.rst | 69 - .../Configuration/Rules/Greaterthan/Index.rst | 74 - .../Configuration/Rules/Inarray/Index.rst | 99 - .../Configuration/Rules/Index.rst | 78 - .../Configuration/Rules/Integer/Index.rst | 55 - .../Configuration/Rules/Ip/Index.rst | 55 - .../Configuration/Rules/Length/Index.rst | 103 - .../Configuration/Rules/Lessthan/Index.rst | 74 - .../Configuration/Rules/Regexp/Index.rst | 73 - .../Configuration/Rules/Required/Index.rst | 57 - .../Configuration/Rules/Uri/Index.rst | 58 - .../Rules/ValidationAttributes/Index.rst | 148 -- .../Images/FormCreationWizard.png | Bin 43622 -> 0 bytes .../Images/FormCreationWizardElementsTab.png | Bin 70074 -> 0 bytes .../Images/FormCreationWizardFormTab.png | Bin 70667 -> 0 bytes .../Images/FormCreationWizardOptionsTab.png | Bin 61598 -> 0 bytes .../Images/FormCreationWizardShowTabs.png | Bin 30793 -> 0 bytes typo3/sysext/form/Documentation/Includes.txt | 13 - typo3/sysext/form/Documentation/Index.rst | 56 - .../form/Documentation/Introduction/Index.rst | 30 - typo3/sysext/form/Documentation/Settings.cfg | 17 - typo3/sysext/form/Documentation/Targets.rst | 9 - .../Documentation/Tests/Attributes/button.txt | 205 -- .../Tests/Attributes/checkbox.txt | 226 -- .../Tests/Attributes/checkboxgroup.txt | 39 - .../Tests/Attributes/fieldset.txt | 95 - .../Documentation/Tests/Attributes/hidden.txt | 76 - .../Tests/Attributes/optgroup.txt | 172 -- .../Documentation/Tests/Attributes/option.txt | 176 -- .../Tests/Attributes/password.txt | 251 -- .../Documentation/Tests/Attributes/radio.txt | 226 -- .../Tests/Attributes/radiogroup.txt | 39 - .../Documentation/Tests/Attributes/reset.txt | 205 -- .../Documentation/Tests/Attributes/select.txt | 209 -- .../Documentation/Tests/Attributes/submit.txt | 205 -- .../Tests/Attributes/textarea.txt | 239 -- .../Tests/Attributes/textline.txt | 250 -- .../Documentation/Tests/Filter/alphabetic.txt | 56 - .../Tests/Filter/alphanumeric.txt | 56 - .../Documentation/Tests/Filter/currency.txt | 68 - .../form/Documentation/Tests/Filter/digit.txt | 45 - .../Documentation/Tests/Filter/integer.txt | 45 - .../Documentation/Tests/Filter/lowercase.txt | 45 - .../Documentation/Tests/Filter/regexp.txt | 48 - .../Tests/Filter/stripnewlines.txt | 49 - .../Documentation/Tests/Filter/titlecase.txt | 45 - .../form/Documentation/Tests/Filter/trim.txt | 60 - .../Documentation/Tests/Filter/uppercase.txt | 45 - .../Documentation/Tests/Request/checkbox.txt | 70 - .../Documentation/Tests/Request/option.txt | 127 - .../Documentation/Tests/Request/radio.txt | 74 - .../Tests/Validation/alphabetic.txt | 43 - .../Tests/Validation/alphanumeric.txt | 43 - .../Tests/Validation/between.txt | 47 - .../Tests/Validation/combined.txt | 36 - .../Documentation/Tests/Validation/date.txt | 32 - .../Documentation/Tests/Validation/digit.txt | 32 - .../Documentation/Tests/Validation/email.txt | 32 - .../Documentation/Tests/Validation/equals.txt | 39 - .../Documentation/Tests/Validation/float.txt | 32 - .../Tests/Validation/greaterthan.txt | 33 - .../Tests/Validation/inarray.txt | 38 - .../Tests/Validation/integer.txt | 32 - .../Documentation/Tests/Validation/ip.txt | 32 - .../Documentation/Tests/Validation/length.txt | 45 - .../Tests/Validation/lessthan.txt | 33 - .../Documentation/Tests/Validation/regexp.txt | 33 - .../Tests/Validation/required.txt | 31 - .../Documentation/Tests/Validation/uri.txt | 32 - .../Private/Backend/Layouts/FormEditor.html | 54 + .../Private/Backend/Layouts/FormManager.html | 7 + .../FormEditor/Stage/FileUploadTemplate.html | 33 + .../FormEditor/Stage/SelectTemplate.html | 32 + .../FormEditor/Stage/SimpleTemplate.html | 27 + .../Backend/Templates/FormEditor/Index.html | 64 + .../FormEditor/Inspector/CheckboxEditor.html | 10 + .../CollectionElementHeaderEditor.html | 5 + .../FormEditor/Inspector/FinishersEditor.html | 8 + .../Inspector/FormElementHeaderEditor.html | 3 + .../Inspector/PropertyGridEditor.html | 47 + .../Inspector/RemoveElementEditor.html | 5 + .../Inspector/RequiredValidatorEditor.html | 10 + .../Inspector/SingleSelectEditor.html | 6 + .../FormEditor/Inspector/TextEditor.html | 10 + .../FormEditor/Inspector/TextareaEditor.html | 8 + .../Inspector/Typo3WinBrowserEditor.html | 17 + .../Inspector/ValidatorsEditor.html | 8 + .../FormEditor/Modals/InsertElements.html | 30 + .../FormEditor/Modals/InsertPages.html | 26 + .../FormEditor/Modals/ValidationErrors.html | 17 + .../FormEditor/Stage/AdvancedPassword.html | 1 + .../Templates/FormEditor/Stage/Checkbox.html | 1 + .../FormEditor/Stage/ContentElement.html | 30 + .../FormEditor/Stage/DatePicker.html | 1 + .../Templates/FormEditor/Stage/Fieldset.html | 18 + .../FormEditor/Stage/FileUpload.html | 1 + .../Templates/FormEditor/Stage/Hidden.html | 1 + .../FormEditor/Stage/ImageUpload.html | 1 + .../FormEditor/Stage/MultiCheckbox.html | 1 + .../FormEditor/Stage/MultiSelect.html | 1 + .../Templates/FormEditor/Stage/Page.html | 1 + .../Templates/FormEditor/Stage/Password.html | 1 + .../FormEditor/Stage/RadioButton.html | 1 + .../FormEditor/Stage/SingleSelect.html | 1 + .../FormEditor/Stage/StaticText.html | 30 + .../FormEditor/Stage/SummaryPage.html | 1 + .../Templates/FormEditor/Stage/Text.html | 1 + .../Templates/FormEditor/Stage/Textarea.html | 1 + .../FormEditor/Stage/_ElementToolbar.html | 35 + .../FormEditor/Stage/_UnknownElement.html | 1 + .../FormEditor/Yaml/NewForms/BlankForm.yaml | 8 + .../Yaml/NewForms/SimpleContactForm.yaml | 75 + .../Backend/Templates/FormManager/Index.html | 110 + .../Frontend/Layouts/FormElements/Field.html | 22 + .../Partials/FormElements/Field/Required.html | 1 + .../FormElements/Form/Navigation.html | 25 + .../Templates/Finishers/Email/Html.html | 48 + .../Templates/Finishers/Email/Plaintext.html | 3 + .../FormElements/AdvancedPassword.html | 28 + .../Templates/FormElements/Checkbox.html | 15 + .../FormElements/ContentElement.html | 22 + .../Templates/FormElements/DatePicker.html | 47 + .../Templates/FormElements/Fieldset.html | 9 + .../Templates/FormElements/FileUpload.html | 11 + .../Frontend/Templates/FormElements/Form.html | 7 + .../Templates/FormElements/Hidden.html | 1 + .../Templates/FormElements/Honeypot.html | 8 + .../Templates/FormElements/ImageUpload.html | 13 + .../Templates/FormElements/MultiCheckbox.html | 20 + .../Templates/FormElements/MultiSelect.html | 12 + .../Frontend/Templates/FormElements/Page.html | 9 + .../Templates/FormElements/Password.html | 11 + .../Templates/FormElements/RadioButton.html | 22 + .../Templates/FormElements/SingleSelect.html | 12 + .../Templates/FormElements/StaticText.html | 9 + .../Templates/FormElements/SummaryPage.html | 43 + .../Frontend/Templates/FormElements/Text.html | 12 + .../Templates/FormElements/Textarea.html | 13 + .../Private/Frontend/Templates/Render.html | 5 + .../Resources/Private/Language/Database.xlf | 698 +++++- .../Resources/Private/Language/locallang.xlf | 299 +-- .../locallang_formManager_javascript.xlf | 68 + .../Private/Language/locallang_module.xlf | 17 + .../Private/Language/locallang_wizard.xlf | 875 ------- .../Resources/Private/Layouts/Default.html | 1 - .../AdditionalElements/Label.html | 1 - .../AdditionalElements/Legend.html | 3 - .../ContainerElements/Checkboxgroup.html | 25 - .../ContainerElements/Fieldset.html | 25 - .../Confirmation/ContainerElements/Form.html | 53 - .../ContainerElements/Radiogroup.html | 25 - .../Confirmation/FlatElements/Checkbox.html | 49 - .../Confirmation/FlatElements/Hidden.html | 18 - .../FlatElements/Imagebutton.html | 0 .../Confirmation/FlatElements/Input.html | 23 - .../FlatElements/InputTypeButton.html | 0 .../Confirmation/FlatElements/Password.html | 16 - .../Confirmation/FlatElements/Radio.html | 27 - .../Confirmation/FlatElements/Reset.html | 0 .../Confirmation/FlatElements/Select.html | 111 - .../Confirmation/FlatElements/Submit.html | 0 .../Confirmation/FlatElements/Textarea.html | 23 - .../Confirmation/FlatElements/Textblock.html | 0 .../Confirmation/FlatElements/Textfield.html | 23 - .../Confirmation/FlatElements/Upload.html | 8 - .../Mail/Html/AdditionalElements/Label.html | 1 - .../Mail/Html/AdditionalElements/Legend.html | 1 - .../Html/ContainerElements/Checkboxgroup.html | 31 - .../Mail/Html/ContainerElements/Fieldset.html | 31 - .../Mail/Html/ContainerElements/Form.html | 14 - .../Html/ContainerElements/Radiogroup.html | 31 - .../Mail/Html/FlatElements/Button.html | 0 .../Mail/Html/FlatElements/ButtonTag.html | 0 .../Mail/Html/FlatElements/Checkbox.html | 10 - .../Html/FlatElements/ContentElement.html | 0 .../Mail/Html/FlatElements/Header.html | 0 .../Mail/Html/FlatElements/Hidden.html | 0 .../Mail/Html/FlatElements/Imagebutton.html | 0 .../Mail/Html/FlatElements/Input.html | 8 - .../Html/FlatElements/InputTypeButton.html | 0 .../Mail/Html/FlatElements/Password.html | 0 .../Mail/Html/FlatElements/Radio.html | 10 - .../Mail/Html/FlatElements/Reset.html | 0 .../Mail/Html/FlatElements/Select.html | 26 - .../Mail/Html/FlatElements/Submit.html | 0 .../Mail/Html/FlatElements/Textarea.html | 8 - .../Mail/Html/FlatElements/Textblock.html | 0 .../Mail/Html/FlatElements/Textfield.html | 8 - .../Mail/Html/FlatElements/Upload.html | 10 - .../ContainerElements/Checkboxgroup.html | 1 - .../Plain/ContainerElements/Fieldset.html | 1 - .../Mail/Plain/ContainerElements/Form.html | 2 - .../Plain/ContainerElements/Radiogroup.html | 1 - .../Mail/Plain/FlatElements/Button.html | 0 .../Mail/Plain/FlatElements/ButtonTag.html | 0 .../Mail/Plain/FlatElements/Checkbox.html | 1 - .../Plain/FlatElements/ContentElement.html | 0 .../Mail/Plain/FlatElements/Header.html | 0 .../Mail/Plain/FlatElements/Hidden.html | 0 .../Mail/Plain/FlatElements/Imagebutton.html | 0 .../Mail/Plain/FlatElements/Input.html | 1 - .../Plain/FlatElements/InputTypeButton.html | 0 .../Mail/Plain/FlatElements/Password.html | 0 .../Mail/Plain/FlatElements/Radio.html | 2 - .../Mail/Plain/FlatElements/Reset.html | 0 .../Mail/Plain/FlatElements/Select.html | 3 - .../Mail/Plain/FlatElements/Submit.html | 0 .../Mail/Plain/FlatElements/Textarea.html | 1 - .../Mail/Plain/FlatElements/Textblock.html | 0 .../Mail/Plain/FlatElements/Textfield.html | 1 - .../Mail/Plain/FlatElements/Upload.html | 2 - .../ErrorValidationMessage.html | 3 - .../Show/AdditionalElements/Label.html | 5 - .../Show/AdditionalElements/Legend.html | 7 - .../MandatoryValidationMessage.html | 3 - .../Show/ContainerElements/Checkboxgroup.html | 23 - .../Show/ContainerElements/Fieldset.html | 25 - .../Default/Show/ContainerElements/Form.html | 44 - .../Show/ContainerElements/Radiogroup.html | 23 - .../Default/Show/FlatElements/Button.html | 25 - .../Default/Show/FlatElements/ButtonTag.html | 23 - .../Default/Show/FlatElements/Checkbox.html | 50 - .../Show/FlatElements/ContentElement.html | 5 - .../Default/Show/FlatElements/Header.html | 5 - .../Default/Show/FlatElements/Hidden.html | 18 - .../Show/FlatElements/Imagebutton.html | 31 - .../Default/Show/FlatElements/Input.html | 31 - .../Show/FlatElements/InputTypeButton.html | 31 - .../Default/Show/FlatElements/Password.html | 26 - .../Default/Show/FlatElements/Radio.html | 25 - .../Default/Show/FlatElements/Reset.html | 31 - .../Default/Show/FlatElements/Select.html | 36 - .../Default/Show/FlatElements/Submit.html | 20 - .../Default/Show/FlatElements/Textarea.html | 28 - .../Default/Show/FlatElements/Textblock.html | 5 - .../Default/Show/FlatElements/Textfield.html | 31 - .../Default/Show/FlatElements/Upload.html | 25 - .../Templates/Frontend/AfterProcess.html | 5 - .../Templates/Frontend/Confirmation.html | 6 - .../Private/Templates/Frontend/Show.html | 5 - .../PostProcessor/Mail/Default/Html.html | 5 - .../PostProcessor/Mail/Default/Plain.html | 5 - .../Resources/Private/Templates/Wizard.html | 71 - .../sysext/form/Resources/Public/Css/form.css | 1123 +++++---- .../Public/Images/advanced-password.svg | 10 + .../form/Resources/Public/Images/broom.png | Bin 710 -> 0 bytes .../form/Resources/Public/Images/captcha.jpg | Bin 1439 -> 0 bytes .../form/Resources/Public/Images/checkbox.svg | 8 + .../Public/Images/content-element.svg | 10 + .../Resources/Public/Images/cursor-move.png | Bin 482 -> 0 bytes .../Resources/Public/Images/date-picker.svg | 14 + .../Resources/Public/Images/drive-upload.png | Bin 589 -> 0 bytes .../Resources/Public/Images/duplicate.svg | 6 + .../Resources/Public/Images/edit-heading.png | Bin 177 -> 0 bytes .../Public/Images/edit-textblock.png | Bin 169 -> 0 bytes .../form/Resources/Public/Images/fieldset.svg | 10 + .../Resources/Public/Images/file-upload.svg | 9 + .../form/Resources/Public/Images/finisher.svg | 20 + .../Resources/Public/Images/image-upload.svg | 8 + .../Resources/Public/Images/insert-after.svg | 9 + .../Resources/Public/Images/insert-in.svg | 9 + .../form/Resources/Public/Images/mail.png | Bin 430 -> 0 bytes .../Public/Images/module-menu-down.png | Bin 121 -> 0 bytes .../Public/Images/module-menu-right.png | Bin 114 -> 0 bytes .../Public/Images/multi-checkbox.svg | 11 + .../Resources/Public/Images/multi-select.svg | 15 + .../form/Resources/Public/Images/page.svg | 18 + .../form/Resources/Public/Images/password.svg | 10 + .../Resources/Public/Images/radio-button.svg | 10 + .../form/Resources/Public/Images/remove.gif | Bin 206 -> 0 bytes .../Resources/Public/Images/single-select.svg | 10 + .../Resources/Public/Images/static-text.svg | 10 + .../Public/Images/submit-trigger.gif | Bin 1109 -> 0 bytes .../Resources/Public/Images/summary-page.svg | 13 + .../form/Resources/Public/Images/text.svg | 9 + .../form/Resources/Public/Images/textarea.svg | 9 + .../form/Resources/Public/Images/tooltip.png | Bin 465 -> 0 bytes .../Public/Images/ui-button-default.png | Bin 322 -> 0 bytes .../Resources/Public/Images/ui-button.png | Bin 314 -> 0 bytes .../Resources/Public/Images/ui-check-box.png | Bin 392 -> 0 bytes .../Public/Images/ui-check-boxes.png | Bin 374 -> 0 bytes .../Resources/Public/Images/ui-combo-box.png | Bin 326 -> 0 bytes .../Resources/Public/Images/ui-group-box.png | Bin 325 -> 0 bytes .../Resources/Public/Images/ui-labels.png | Bin 367 -> 0 bytes .../Public/Images/ui-radio-button.png | Bin 411 -> 0 bytes .../Public/Images/ui-radio-buttons.png | Bin 447 -> 0 bytes .../Public/Images/ui-scroll-pane-text.png | Bin 344 -> 0 bytes .../Public/Images/ui-text-field-hidden.png | Bin 207 -> 0 bytes .../Public/Images/ui-text-field-password.png | Bin 226 -> 0 bytes .../Resources/Public/Images/ui-text-field.png | Bin 317 -> 0 bytes .../Public/Images/user-silhouette.png | Bin 509 -> 0 bytes .../Resources/Public/Images/validator.svg | 9 + .../Public/JavaScript/Backend/FormEditor.js | 1114 +++++++++ .../JavaScript/Backend/FormEditor/Core.js | 2127 +++++++++++++++++ .../JavaScript/Backend/FormEditor/Helper.js | 310 +++ .../Backend/FormEditor/InspectorComponent.js | 1777 ++++++++++++++ .../JavaScript/Backend/FormEditor/Mediator.js | 1059 ++++++++ .../Backend/FormEditor/ModalsComponent.js | 493 ++++ .../Backend/FormEditor/StageComponent.js | 1119 +++++++++ .../Backend/FormEditor/TreeComponent.js | 648 +++++ .../Backend/FormEditor/ViewModel.js | 1697 +++++++++++++ .../Public/JavaScript/Backend/FormManager.js | 240 ++ .../Backend/FormManager/ViewModel.js | 534 +++++ .../Vendor/jquery.mjs.nestedSortable.js | 907 +++++++ .../Resources/Public/JavaScript/Wizard.js | 276 --- .../Wizard/Elements/Basic/Button.js | 107 - .../Wizard/Elements/Basic/Checkbox.js | 110 - .../Wizard/Elements/Basic/Fieldset.js | 130 - .../Wizard/Elements/Basic/Fileupload.js | 111 - .../JavaScript/Wizard/Elements/Basic/Form.js | 176 -- .../Wizard/Elements/Basic/Hidden.js | 88 - .../Wizard/Elements/Basic/Password.js | 115 - .../JavaScript/Wizard/Elements/Basic/Radio.js | 110 - .../JavaScript/Wizard/Elements/Basic/Reset.js | 137 -- .../Wizard/Elements/Basic/Select.js | 130 - .../Wizard/Elements/Basic/Submit.js | 135 -- .../Wizard/Elements/Basic/Textarea.js | 118 - .../Wizard/Elements/Basic/Textline.js | 117 - .../JavaScript/Wizard/Elements/ButtonGroup.js | 89 - .../JavaScript/Wizard/Elements/Container.js | 569 ----- .../Wizard/Elements/Content/Header.js | 71 - .../Wizard/Elements/Content/Textblock.js | 70 - .../JavaScript/Wizard/Elements/Dummy.js | 69 - .../JavaScript/Wizard/Elements/Elements.js | 301 --- .../Elements/Predefined/CheckboxGroup.js | 154 -- .../Wizard/Elements/Predefined/Email.js | 46 - .../Wizard/Elements/Predefined/Name.js | 156 -- .../Wizard/Elements/Predefined/RadioGroup.js | 154 -- .../JavaScript/Wizard/Helpers/Element.js | 71 - .../JavaScript/Wizard/Helpers/History.js | 139 -- .../Wizard/Ux/Ext.ux.form.FakeFormPanel.js | 35 - .../Wizard/Ux/Ext.ux.form.ValueCheckbox.js | 21 - .../Wizard/Ux/Ext.ux.form.spinnerfield.js | 62 - .../Wizard/Ux/Ext.ux.form.textfieldsubmit.js | 52 - .../Wizard/Ux/Ext.ux.grid.CheckColumn.js | 84 - .../Wizard/Ux/Ext.ux.grid.ItemDeleter.js | 29 - .../Ux/Ext.ux.grid.SingleSelectCheckColumn.js | 15 - .../Wizard/Ux/Ext.ux.isemptyobject.js | 8 - .../JavaScript/Wizard/Ux/Ext.ux.merge.js | 26 - .../JavaScript/Wizard/Ux/Ext.ux.spinner.js | 443 ---- .../Public/JavaScript/Wizard/Viewport.js | 275 --- .../Public/JavaScript/Wizard/Viewport/Left.js | 129 - .../Wizard/Viewport/Left/Elements.js | 96 - .../Wizard/Viewport/Left/Elements/Basic.js | 178 -- .../Viewport/Left/Elements/ButtonGroup.js | 122 - .../Wizard/Viewport/Left/Elements/Content.js | 78 - .../Viewport/Left/Elements/Predefined.js | 98 - .../JavaScript/Wizard/Viewport/Left/Form.js | 194 -- .../Wizard/Viewport/Left/Form/Attributes.js | 26 - .../Wizard/Viewport/Left/Form/Behaviour.js | 128 - .../Viewport/Left/Form/PostProcessor.js | 210 -- .../Left/Form/PostProcessors/Dummy.js | 52 - .../Viewport/Left/Form/PostProcessors/Mail.js | 34 - .../Left/Form/PostProcessors/PostProcessor.js | 297 --- .../Left/Form/PostProcessors/Redirect.js | 33 - .../Wizard/Viewport/Left/Form/Prefix.js | 168 -- .../Wizard/Viewport/Left/Options.js | 170 -- .../Wizard/Viewport/Left/Options/Dummy.js | 65 - .../Viewport/Left/Options/Forms/Attributes.js | 1149 --------- .../Viewport/Left/Options/Forms/Filters.js | 243 -- .../Left/Options/Forms/Filters/Alphabetic.js | 33 - .../Options/Forms/Filters/Alphanumeric.js | 33 - .../Left/Options/Forms/Filters/Currency.js | 34 - .../Left/Options/Forms/Filters/Digit.js | 18 - .../Left/Options/Forms/Filters/Dummy.js | 58 - .../Left/Options/Forms/Filters/Filter.js | 345 --- .../Left/Options/Forms/Filters/Integer.js | 18 - .../Left/Options/Forms/Filters/LowerCase.js | 18 - .../Left/Options/Forms/Filters/RegExp.js | 33 - .../Options/Forms/Filters/StripNewLines.js | 18 - .../Left/Options/Forms/Filters/TitleCase.js | 18 - .../Left/Options/Forms/Filters/Trim.js | 33 - .../Left/Options/Forms/Filters/UpperCase.js | 18 - .../Viewport/Left/Options/Forms/Label.js | 229 -- .../Viewport/Left/Options/Forms/Legend.js | 152 -- .../Viewport/Left/Options/Forms/Options.js | 249 -- .../Viewport/Left/Options/Forms/Validation.js | 275 --- .../Options/Forms/Validation/Alphabetic.js | 36 - .../Options/Forms/Validation/Alphanumeric.js | 36 - .../Left/Options/Forms/Validation/Between.js | 38 - .../Left/Options/Forms/Validation/Date.js | 36 - .../Left/Options/Forms/Validation/Digit.js | 35 - .../Left/Options/Forms/Validation/Dummy.js | 58 - .../Left/Options/Forms/Validation/Email.js | 35 - .../Left/Options/Forms/Validation/Equals.js | 36 - .../Forms/Validation/FileAllowedTypes.js | 36 - .../Forms/Validation/FileMaximumSize.js | 36 - .../Forms/Validation/FileMinimumSize.js | 36 - .../Left/Options/Forms/Validation/Float.js | 35 - .../Options/Forms/Validation/GreaterThan.js | 36 - .../Left/Options/Forms/Validation/InArray.js | 37 - .../Left/Options/Forms/Validation/Integer.js | 35 - .../Left/Options/Forms/Validation/Ip.js | 35 - .../Left/Options/Forms/Validation/Length.js | 37 - .../Left/Options/Forms/Validation/LessThan.js | 36 - .../Left/Options/Forms/Validation/RegExp.js | 36 - .../Left/Options/Forms/Validation/Required.js | 35 - .../Left/Options/Forms/Validation/Rule.js | 436 ---- .../Left/Options/Forms/Validation/Uri.js | 35 - .../Viewport/Left/Options/Forms/Various.js | 290 --- .../Wizard/Viewport/Left/Options/Panel.js | 148 -- .../JavaScript/Wizard/Viewport/Right.js | 144 -- .../AbstractBackendControllerTest.php | 48 + .../Fixtures/BackendUtilityFixture.php | 60 + .../Unit/Controller/Fixtures/BlankForm.yaml} | 0 .../Fixtures/SimpleContactForm.yaml} | 0 .../Controller/FormEditorControllerTest.php | 424 ++++ .../Controller/FormFrontendControllerTest.php | 418 ++++ .../Controller/FormManagerControllerTest.php | 410 ++++ .../ConfigurationServiceTest.php | 71 + .../Unit/Domain/Model/ConfigurationTest.php | 136 -- .../Unit/Filter/AlphabeticFilterTest.php | 56 - .../Unit/Filter/AlphanumericFilterTest.php | 64 - .../Tests/Unit/Filter/CurrencyFilterTest.php | 102 - .../Tests/Unit/Filter/DigitFilterTest.php | 54 - .../Tests/Unit/Filter/IntegerFilterTest.php | 55 - .../Tests/Unit/Filter/LowerCaseFilterTest.php | 56 - .../Tests/Unit/Filter/RegExpFilterTest.php | 65 - .../Unit/Filter/StripNewLinesFilterTest.php | 74 - .../Tests/Unit/Filter/TitleCaseFilterTest.php | 64 - .../form/Tests/Unit/Filter/TrimFilterTest.php | 81 - .../Tests/Unit/Filter/UpperCaseFilterTest.php | 56 - .../PostProcessorWithFormPrefixFixture.php | 47 - .../PostProcessorWithoutFormPrefixFixture.php | 40 - .../PostProcessorWithoutInterfaceFixture.php | 37 - .../Hooks/DataStructureIdentifierHookTest.php | 240 ++ .../Mvc/Configuration/Fixtures/Header.yaml | 6 + .../Mvc/Configuration/Fixtures/Invalid.yaml | 2 + .../InheritancesResolverServiceTest.php | 429 ++++ .../Configuration/TypoScriptServiceTest.php | 69 + .../Unit/Mvc/Configuration/YamlSourceTest.php | 107 + .../Mvc/Persistence/Fixtures/BlankForm.txt} | 0 .../Mvc/Persistence/Fixtures/BlankForm.yaml} | 0 .../FormPersistenceManagerTest.php | 617 +++++ .../Tests/Unit/Mvc/ProcessingRuleTest.php | 120 + .../Mvc/Validation/CountValidatorTest.php | 120 + .../Mvc/Validation/EmptyValidatorTest.php | 123 + .../Validation/Fixtures/TestValidator.php} | 24 +- .../Mvc/Validation/MimeTypeValidatorTest.php | 109 + .../PostProcess/MailPostProcessorTest.php | 100 - .../Unit/PostProcess/PostProcessorTest.php | 149 -- .../Tests/Unit/Utility/ArrayUtilityTest.php | 251 ++ .../Utility/TypoScriptToJsonConverterTest.php | 82 - .../Unit/Validator/AbstractValidatorTest.php | 51 - .../Validator/AlphabeticValidatorTest.php | 138 -- .../Validator/AlphanumericValidatorTest.php | 138 -- .../Unit/Validator/BetweenValidatorTest.php | 146 -- .../Unit/Validator/DateValidatorTest.php | 86 - .../Unit/Validator/DigitValidatorTest.php | 76 - .../Unit/Validator/EmailValidatorTest.php | 79 - .../Unit/Validator/EqualsValidatorTest.php | 80 - .../FileAllowedTypesValidatorTest.php | 114 - .../FileMaximumSizeValidatorTest.php | 76 - .../FileMinimumSizeValidatorTest.php | 76 - .../Unit/Validator/FloatValidatorTest.php | 63 - .../Validator/GreaterThanValidatorTest.php | 78 - .../Unit/Validator/InArrayValidatorTest.php | 800 ------- .../Unit/Validator/IntegerValidatorTest.php | 90 - .../Tests/Unit/Validator/IpValidatorTest.php | 78 - .../Unit/Validator/LengthValidatorTest.php | 127 - .../Unit/Validator/LessThanValidatorTest.php | 79 - .../Unit/Validator/RegExpValidatorTest.php | 76 - .../Unit/Validator/RequiredValidatorTest.php | 87 - .../Tests/Unit/Validator/UriValidatorTest.php | 76 - .../Form/DatePickerViewHelperTest.php | 143 ++ typo3/sysext/form/composer.json | 56 +- typo3/sysext/form/ext_emconf.php | 8 +- typo3/sysext/form/ext_localconf.php | 91 +- typo3/sysext/form/ext_tables.php | 21 + typo3/sysext/form/ext_tables.sql | 6 - typo3/sysext/form/ext_typoscript_setup.txt | 2 + 744 files changed, 32848 insertions(+), 46224 deletions(-) create mode 100644 typo3/sysext/core/Documentation/Changelog/master/Feature-77910-EXTform-IntroduceNewFormFramework.rst delete mode 100644 typo3/sysext/form/Classes/ContentObject/FormContentObject.php create mode 100644 typo3/sysext/form/Classes/Controller/AbstractBackendController.php create mode 100644 typo3/sysext/form/Classes/Controller/FormEditorController.php create mode 100644 typo3/sysext/form/Classes/Controller/FormFrontendController.php create mode 100644 typo3/sysext/form/Classes/Controller/FormManagerController.php delete mode 100644 typo3/sysext/form/Classes/Controller/FrontendController.php delete mode 100644 typo3/sysext/form/Classes/Controller/WizardController.php delete mode 100644 typo3/sysext/form/Classes/Domain/Builder/ElementBuilder.php delete mode 100644 typo3/sysext/form/Classes/Domain/Builder/FormBuilder.php delete mode 100644 typo3/sysext/form/Classes/Domain/Builder/ValidationBuilder.php create mode 100644 typo3/sysext/form/Classes/Domain/Configuration/ConfigurationService.php rename typo3/sysext/form/Classes/Domain/{Filter/StripNewLinesFilter.php => Configuration/Exception/PrototypeNotFoundException.php} (55%) rename typo3/sysext/form/Classes/Domain/{Filter/FilterInterface.php => Exception.php} (66%) rename typo3/sysext/form/Classes/Domain/{Filter/LowerCaseFilter.php => Exception/IdentifierNotValidException.php} (55%) create mode 100644 typo3/sysext/form/Classes/Domain/Exception/RenderingException.php create mode 100644 typo3/sysext/form/Classes/Domain/Exception/TypeDefinitionNotFoundException.php create mode 100644 typo3/sysext/form/Classes/Domain/Exception/TypeDefinitionNotValidException.php create mode 100644 typo3/sysext/form/Classes/Domain/Exception/UnknownCompositRenderableException.php create mode 100644 typo3/sysext/form/Classes/Domain/Factory/AbstractFormFactory.php create mode 100644 typo3/sysext/form/Classes/Domain/Factory/ArrayFormFactory.php create mode 100644 typo3/sysext/form/Classes/Domain/Factory/FormFactoryInterface.php delete mode 100644 typo3/sysext/form/Classes/Domain/Factory/JsonToTypoScript.php delete mode 100644 typo3/sysext/form/Classes/Domain/Filter/AbstractFilter.php delete mode 100644 typo3/sysext/form/Classes/Domain/Filter/AlphabeticFilter.php delete mode 100644 typo3/sysext/form/Classes/Domain/Filter/AlphanumericFilter.php delete mode 100644 typo3/sysext/form/Classes/Domain/Filter/CurrencyFilter.php delete mode 100644 typo3/sysext/form/Classes/Domain/Filter/RegExpFilter.php delete mode 100644 typo3/sysext/form/Classes/Domain/Filter/RemoveXssFilter.php delete mode 100644 typo3/sysext/form/Classes/Domain/Filter/TitleCaseFilter.php delete mode 100644 typo3/sysext/form/Classes/Domain/Filter/TrimFilter.php delete mode 100644 typo3/sysext/form/Classes/Domain/Filter/UpperCaseFilter.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/AbstractFinisher.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/ClosureFinisher.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/ConfirmationFinisher.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/DeleteUploadsFinisher.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/EmailFinisher.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/Exception/FinisherException.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/FinisherContext.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/FinisherInterface.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/FlashMessageFinisher.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/RedirectFinisher.php create mode 100644 typo3/sysext/form/Classes/Domain/Finishers/SaveToDatabaseFinisher.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Configuration.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Content.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Element.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/Exception.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/Exception/DuplicateFormElementException.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/Exception/FinisherPresetNotFoundException.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/Exception/FormDefinitionConsistencyException.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/Exception/ValidatorPresetNotFoundException.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/FormDefinition.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/FormElements/AbstractFormElement.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/FormElements/AbstractSection.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/FormElements/AdvancedPassword.php rename typo3/sysext/form/Classes/Domain/{Filter/IntegerFilter.php => Model/FormElements/DatePicker.php} (61%) create mode 100644 typo3/sysext/form/Classes/Domain/Model/FormElements/FileUpload.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/FormElements/FormElementInterface.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/FormElements/GenericFormElement.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/FormElements/Page.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/FormElements/Section.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/FormElements/UnknownFormElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/AbstractJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/ButtonJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/CheckboxGroupJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/CheckboxJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/ContainerJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/FieldsetJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/FileuploadJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/FormJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/HeaderJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/HiddenJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/NameJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/PasswordJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/RadioGroupJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/RadioJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/ResetJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/SelectJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/SubmitJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/TextareaJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/TextblockJsonElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/Json/TextlineJsonElement.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/Renderable/AbstractCompositeRenderable.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/Renderable/AbstractRenderable.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/Renderable/CompositeRenderableInterface.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/Renderable/RenderableInterface.php create mode 100644 typo3/sysext/form/Classes/Domain/Model/Renderable/RootRenderableInterface.php delete mode 100644 typo3/sysext/form/Classes/Domain/Model/ValidationElement.php delete mode 100644 typo3/sysext/form/Classes/Domain/Property/TypeConverter/ArrayToValidationElementConverter.php create mode 100644 typo3/sysext/form/Classes/Domain/Renderer/AbstractElementRenderer.php create mode 100644 typo3/sysext/form/Classes/Domain/Renderer/FluidFormRenderer.php create mode 100644 typo3/sysext/form/Classes/Domain/Renderer/RendererInterface.php create mode 100644 typo3/sysext/form/Classes/Domain/Renderer/UnknownFormElementRenderer.php delete mode 100644 typo3/sysext/form/Classes/Domain/Repository/ContentRepository.php delete mode 100644 typo3/sysext/form/Classes/Domain/Repository/TypoScriptRepository.php create mode 100644 typo3/sysext/form/Classes/Domain/Runtime/Exception/PropertyMappingException.php create mode 100644 typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php create mode 100644 typo3/sysext/form/Classes/Domain/Runtime/FormState.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/AbstractValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/AlphabeticValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/AlphanumericValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/BetweenValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/DateValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/DigitValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/EmailValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/EqualsValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/FileAllowedTypesValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/FileMaximumSizeValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/FileMinimumSizeValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/FloatValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/GreaterThanValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/InArrayValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/IntegerValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/IpValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/LengthValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/LessThanValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/RegExpValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/RequiredValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/UriValidator.php delete mode 100644 typo3/sysext/form/Classes/Domain/Validator/ValidationElementValidator.php create mode 100644 typo3/sysext/form/Classes/Exception.php create mode 100644 typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php delete mode 100644 typo3/sysext/form/Classes/Hooks/HandleIncomingFormValues.php delete mode 100644 typo3/sysext/form/Classes/Hooks/PageLayoutView/MailformPreviewRenderer.php create mode 100644 typo3/sysext/form/Classes/Hooks/SoftReferenceParserHook.php create mode 100644 typo3/sysext/form/Classes/Mvc/Configuration/ConfigurationManager.php create mode 100644 typo3/sysext/form/Classes/Mvc/Configuration/ConfigurationManagerInterface.php create mode 100644 typo3/sysext/form/Classes/Mvc/Configuration/Exception.php create mode 100644 typo3/sysext/form/Classes/Mvc/Configuration/Exception/CycleInheritancesException.php create mode 100644 typo3/sysext/form/Classes/Mvc/Configuration/Exception/ExtensionNameRequiredException.php create mode 100644 typo3/sysext/form/Classes/Mvc/Configuration/Exception/NoSuchFileException.php create mode 100644 typo3/sysext/form/Classes/Mvc/Configuration/Exception/ParseErrorException.php create mode 100644 typo3/sysext/form/Classes/Mvc/Configuration/InheritancesResolverService.php create mode 100644 typo3/sysext/form/Classes/Mvc/Configuration/TypoScriptService.php create mode 100644 typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php delete mode 100644 typo3/sysext/form/Classes/Mvc/Controller/ControllerContext.php create mode 100644 typo3/sysext/form/Classes/Mvc/Persistence/Exception.php create mode 100644 typo3/sysext/form/Classes/Mvc/Persistence/Exception/NoUniqueIdentifierException.php create mode 100644 typo3/sysext/form/Classes/Mvc/Persistence/Exception/NoUniquePersistenceIdentifierException.php create mode 100644 typo3/sysext/form/Classes/Mvc/Persistence/Exception/PersistenceManagerException.php create mode 100644 typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManager.php create mode 100644 typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManagerInterface.php create mode 100644 typo3/sysext/form/Classes/Mvc/ProcessingRule.php create mode 100644 typo3/sysext/form/Classes/Mvc/Property/TypeConverter/UploadedFileReferenceConverter.php create mode 100644 typo3/sysext/form/Classes/Mvc/Validation/CountValidator.php create mode 100644 typo3/sysext/form/Classes/Mvc/Validation/EmptyValidator.php create mode 100644 typo3/sysext/form/Classes/Mvc/Validation/Exception/InvalidValidationOptionsException.php create mode 100644 typo3/sysext/form/Classes/Mvc/Validation/MimeTypeValidator.php create mode 100644 typo3/sysext/form/Classes/Mvc/View/FormView.php delete mode 100644 typo3/sysext/form/Classes/PostProcess/AbstractPostProcessor.php delete mode 100644 typo3/sysext/form/Classes/PostProcess/MailPostProcessor.php delete mode 100644 typo3/sysext/form/Classes/PostProcess/PostProcessor.php delete mode 100644 typo3/sysext/form/Classes/PostProcess/PostProcessorInterface.php delete mode 100644 typo3/sysext/form/Classes/PostProcess/RedirectPostProcessor.php create mode 100644 typo3/sysext/form/Classes/Service/TranslationService.php create mode 100644 typo3/sysext/form/Classes/Utility/ArrayUtility.php delete mode 100644 typo3/sysext/form/Classes/Utility/ElementCounter.php delete mode 100644 typo3/sysext/form/Classes/Utility/FormUtility.php delete mode 100644 typo3/sysext/form/Classes/Utility/SessionUtility.php delete mode 100644 typo3/sysext/form/Classes/Utility/TypoScriptToJsonConverter.php delete mode 100644 typo3/sysext/form/Classes/View/Wizard/Element/FormWizardElement.php delete mode 100644 typo3/sysext/form/Classes/ViewHelpers/AggregateSelectOptionsViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/Be/PageRendererViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/Be/RenderContentElementPreviewViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/Form/CheckboxViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/Form/DatePickerViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/Form/TimePickerViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/Form/UploadedResourceViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/FormViewHelper.php delete mode 100644 typo3/sysext/form/Classes/ViewHelpers/PlainMailViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/PlainTextMailViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/RenderAllFormValuesViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/RenderRenderableViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/RenderViewHelper.php delete mode 100644 typo3/sysext/form/Classes/ViewHelpers/SelectViewHelper.php create mode 100644 typo3/sysext/form/Classes/ViewHelpers/TranslateElementPropertyViewHelper.php delete mode 100644 typo3/sysext/form/Configuration/Backend/AjaxRoutes.php create mode 100644 typo3/sysext/form/Configuration/FlexForms/FormFramework.xml delete mode 100644 typo3/sysext/form/Configuration/TCA/Overrides/sys_template.php delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Button.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Buttontag.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Checkbox.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Checkboxgroup.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Contentelement.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Fieldset.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Fileupload.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Form.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Header.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Hidden.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Input.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Inputtypebutton.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Label.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Optgroup.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Option.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Password.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Radio.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Radiogroup.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Reset.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Select.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Submit.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Textarea.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Textblock.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Elements/Textline.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Filters/Filters.ts delete mode 100644 typo3/sysext/form/Configuration/TypoScript/Validators/Validators.ts create mode 100644 typo3/sysext/form/Configuration/TypoScript/backend.txt delete mode 100644 typo3/sysext/form/Configuration/TypoScript/constants.txt delete mode 100644 typo3/sysext/form/Configuration/UserTSconfig/userTSConfig.txt create mode 100644 typo3/sysext/form/Configuration/Yaml/BaseSetup.yaml create mode 100644 typo3/sysext/form/Configuration/Yaml/FormEditorSetup.yaml create mode 100644 typo3/sysext/form/Configuration/Yaml/FormEngineSetup.yaml delete mode 100644 typo3/sysext/form/Documentation/.gitignore delete mode 100644 typo3/sysext/form/Documentation/Administration/DefaultNewRecord/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Administration/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/ElementsTab/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/FormTab/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/OptionsTab/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/ShowTabs/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Administration/WizardSettings/ElementsReference/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Administration/WizardSettings/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Alphabetic/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Alphanumeric/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Currency/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Digit/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Integer/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Lowercase/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Regexp/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Stripnewlines/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Titlecase/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Trim/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Filters/Uppercase/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Layout/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Layout/LayoutObjectSpecific/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Layout/LayoutViewSpecific/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Layout/LayoutWholeForm/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Button/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Checkbox/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Fieldset/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Fileupload/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Form/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Header/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Hidden/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/ObjectAttributes/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Optgroup/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Option/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Password/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Radio/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Reset/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Select/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Submit/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Textarea/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Textblock/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Objects/Textline/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Postprocessors/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Postprocessors/Mail/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Postprocessors/Redirect/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Alphabetic/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Alphanumeric/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Between/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Date/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Digit/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Email/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Equals/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Float/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Greaterthan/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Inarray/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Integer/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Ip/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Length/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Lessthan/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Regexp/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Required/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/Uri/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Configuration/Rules/ValidationAttributes/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Images/FormCreationWizard.png delete mode 100644 typo3/sysext/form/Documentation/Images/FormCreationWizardElementsTab.png delete mode 100644 typo3/sysext/form/Documentation/Images/FormCreationWizardFormTab.png delete mode 100644 typo3/sysext/form/Documentation/Images/FormCreationWizardOptionsTab.png delete mode 100644 typo3/sysext/form/Documentation/Images/FormCreationWizardShowTabs.png delete mode 100644 typo3/sysext/form/Documentation/Includes.txt delete mode 100644 typo3/sysext/form/Documentation/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Introduction/Index.rst delete mode 100644 typo3/sysext/form/Documentation/Settings.cfg delete mode 100644 typo3/sysext/form/Documentation/Targets.rst delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/button.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/checkbox.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/checkboxgroup.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/fieldset.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/hidden.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/optgroup.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/option.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/password.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/radio.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/radiogroup.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/reset.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/select.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/submit.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/textarea.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Attributes/textline.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/alphabetic.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/alphanumeric.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/currency.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/digit.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/integer.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/lowercase.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/regexp.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/stripnewlines.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/titlecase.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/trim.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Filter/uppercase.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Request/checkbox.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Request/option.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Request/radio.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/alphabetic.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/alphanumeric.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/between.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/combined.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/date.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/digit.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/email.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/equals.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/float.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/greaterthan.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/inarray.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/integer.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/ip.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/length.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/lessthan.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/regexp.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/required.txt delete mode 100644 typo3/sysext/form/Documentation/Tests/Validation/uri.txt create mode 100644 typo3/sysext/form/Resources/Private/Backend/Layouts/FormEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Layouts/FormManager.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/FileUploadTemplate.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/SelectTemplate.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/SimpleTemplate.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Index.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/CheckboxEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/CollectionElementHeaderEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/FinishersEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/FormElementHeaderEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/PropertyGridEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/RemoveElementEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/RequiredValidatorEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/SingleSelectEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/TextEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/TextareaEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/Typo3WinBrowserEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/ValidatorsEditor.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/InsertElements.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/InsertPages.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/ValidationErrors.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/AdvancedPassword.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Checkbox.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/ContentElement.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/DatePicker.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Fieldset.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/FileUpload.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Hidden.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/ImageUpload.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/MultiCheckbox.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/MultiSelect.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Page.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Password.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/RadioButton.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/SingleSelect.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/StaticText.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/SummaryPage.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Text.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Textarea.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/_ElementToolbar.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/_UnknownElement.html create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/BlankForm.yaml create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/SimpleContactForm.yaml create mode 100644 typo3/sysext/form/Resources/Private/Backend/Templates/FormManager/Index.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Layouts/FormElements/Field.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Partials/FormElements/Field/Required.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Partials/FormElements/Form/Navigation.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/Finishers/Email/Html.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/Finishers/Email/Plaintext.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/AdvancedPassword.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Checkbox.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/ContentElement.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/DatePicker.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Fieldset.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/FileUpload.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Form.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Hidden.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Honeypot.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/ImageUpload.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/MultiCheckbox.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/MultiSelect.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Page.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Password.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/RadioButton.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/SingleSelect.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/StaticText.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/SummaryPage.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Text.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Textarea.html create mode 100644 typo3/sysext/form/Resources/Private/Frontend/Templates/Render.html create mode 100644 typo3/sysext/form/Resources/Private/Language/locallang_formManager_javascript.xlf create mode 100644 typo3/sysext/form/Resources/Private/Language/locallang_module.xlf delete mode 100644 typo3/sysext/form/Resources/Private/Language/locallang_wizard.xlf delete mode 100644 typo3/sysext/form/Resources/Private/Layouts/Default.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/AdditionalElements/Label.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/AdditionalElements/Legend.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Checkboxgroup.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Fieldset.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Form.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Radiogroup.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Checkbox.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Hidden.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Imagebutton.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Input.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/InputTypeButton.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Password.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Radio.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Reset.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Select.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Submit.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textarea.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textblock.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textfield.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Upload.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/AdditionalElements/Label.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/AdditionalElements/Legend.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Checkboxgroup.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Fieldset.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Form.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Radiogroup.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Button.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/ButtonTag.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Checkbox.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/ContentElement.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Header.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Hidden.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Imagebutton.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Input.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/InputTypeButton.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Password.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Radio.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Reset.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Select.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Submit.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textarea.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textblock.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textfield.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Upload.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Checkboxgroup.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Fieldset.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Form.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Radiogroup.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Button.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/ButtonTag.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Checkbox.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/ContentElement.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Header.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Hidden.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Imagebutton.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Input.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/InputTypeButton.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Password.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Radio.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Reset.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Select.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Submit.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textarea.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textblock.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textfield.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Upload.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/ErrorValidationMessage.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/Label.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/Legend.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/MandatoryValidationMessage.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Checkboxgroup.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Fieldset.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Form.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Radiogroup.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Button.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/ButtonTag.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Checkbox.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/ContentElement.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Header.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Hidden.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Imagebutton.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Input.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/InputTypeButton.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Password.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Radio.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Reset.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Select.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Submit.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textarea.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textblock.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textfield.html delete mode 100644 typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Upload.html delete mode 100644 typo3/sysext/form/Resources/Private/Templates/Frontend/AfterProcess.html delete mode 100644 typo3/sysext/form/Resources/Private/Templates/Frontend/Confirmation.html delete mode 100644 typo3/sysext/form/Resources/Private/Templates/Frontend/Show.html delete mode 100644 typo3/sysext/form/Resources/Private/Templates/PostProcessor/Mail/Default/Html.html delete mode 100644 typo3/sysext/form/Resources/Private/Templates/PostProcessor/Mail/Default/Plain.html delete mode 100644 typo3/sysext/form/Resources/Private/Templates/Wizard.html create mode 100644 typo3/sysext/form/Resources/Public/Images/advanced-password.svg delete mode 100644 typo3/sysext/form/Resources/Public/Images/broom.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/captcha.jpg create mode 100644 typo3/sysext/form/Resources/Public/Images/checkbox.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/content-element.svg delete mode 100644 typo3/sysext/form/Resources/Public/Images/cursor-move.png create mode 100644 typo3/sysext/form/Resources/Public/Images/date-picker.svg delete mode 100644 typo3/sysext/form/Resources/Public/Images/drive-upload.png create mode 100644 typo3/sysext/form/Resources/Public/Images/duplicate.svg delete mode 100644 typo3/sysext/form/Resources/Public/Images/edit-heading.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/edit-textblock.png create mode 100644 typo3/sysext/form/Resources/Public/Images/fieldset.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/file-upload.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/finisher.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/image-upload.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/insert-after.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/insert-in.svg delete mode 100644 typo3/sysext/form/Resources/Public/Images/mail.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/module-menu-down.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/module-menu-right.png create mode 100644 typo3/sysext/form/Resources/Public/Images/multi-checkbox.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/multi-select.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/page.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/password.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/radio-button.svg delete mode 100644 typo3/sysext/form/Resources/Public/Images/remove.gif create mode 100644 typo3/sysext/form/Resources/Public/Images/single-select.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/static-text.svg delete mode 100644 typo3/sysext/form/Resources/Public/Images/submit-trigger.gif create mode 100644 typo3/sysext/form/Resources/Public/Images/summary-page.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/text.svg create mode 100644 typo3/sysext/form/Resources/Public/Images/textarea.svg delete mode 100644 typo3/sysext/form/Resources/Public/Images/tooltip.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-button-default.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-button.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-check-box.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-check-boxes.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-combo-box.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-group-box.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-labels.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-radio-button.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-radio-buttons.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-scroll-pane-text.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-text-field-hidden.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-text-field-password.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/ui-text-field.png delete mode 100644 typo3/sysext/form/Resources/Public/Images/user-silhouette.png create mode 100644 typo3/sysext/form/Resources/Public/Images/validator.svg create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Core.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Helper.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/InspectorComponent.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Mediator.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/ModalsComponent.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/StageComponent.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/TreeComponent.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/ViewModel.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormManager.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/FormManager/ViewModel.js create mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Backend/Vendor/jquery.mjs.nestedSortable.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Button.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Checkbox.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Fieldset.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Fileupload.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Form.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Hidden.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Password.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Radio.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Reset.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Select.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Submit.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Textarea.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Textline.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/ButtonGroup.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Container.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Content/Header.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Content/Textblock.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Dummy.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Elements.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/CheckboxGroup.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/Email.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/Name.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/RadioGroup.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Helpers/Element.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Helpers/History.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.FakeFormPanel.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.ValueCheckbox.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.spinnerfield.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.textfieldsubmit.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.CheckColumn.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.ItemDeleter.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.SingleSelectCheckColumn.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.isemptyobject.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.merge.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.spinner.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Basic.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/ButtonGroup.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Content.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Predefined.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Attributes.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Behaviour.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessor.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Dummy.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Mail.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/PostProcessor.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Redirect.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Prefix.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Dummy.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Attributes.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Alphabetic.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Alphanumeric.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Currency.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Digit.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Dummy.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Filter.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Integer.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/LowerCase.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/RegExp.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/StripNewLines.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/TitleCase.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Trim.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/UpperCase.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Label.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Legend.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Options.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Alphabetic.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Alphanumeric.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Between.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Date.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Digit.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Dummy.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Email.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Equals.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileAllowedTypes.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileMaximumSize.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileMinimumSize.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Float.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/GreaterThan.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/InArray.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Integer.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Ip.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Length.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/LessThan.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/RegExp.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Required.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Rule.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Uri.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Various.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Panel.js delete mode 100644 typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Right.js create mode 100644 typo3/sysext/form/Tests/Unit/Controller/AbstractBackendControllerTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Controller/Fixtures/BackendUtilityFixture.php rename typo3/sysext/form/{Resources/Private/Partials/Default/Confirmation/FlatElements/Button.html => Tests/Unit/Controller/Fixtures/BlankForm.yaml} (100%) rename typo3/sysext/form/{Resources/Private/Partials/Default/Confirmation/FlatElements/ButtonTag.html => Tests/Unit/Controller/Fixtures/SimpleContactForm.yaml} (100%) create mode 100644 typo3/sysext/form/Tests/Unit/Controller/FormEditorControllerTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Controller/FormFrontendControllerTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Controller/FormManagerControllerTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Domain/Configuration/ConfigurationServiceTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Domain/Model/ConfigurationTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/AlphabeticFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/AlphanumericFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/CurrencyFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/DigitFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/IntegerFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/LowerCaseFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/RegExpFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/StripNewLinesFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/TitleCaseFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/TrimFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Filter/UpperCaseFilterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithFormPrefixFixture.php delete mode 100644 typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithoutFormPrefixFixture.php delete mode 100644 typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithoutInterfaceFixture.php create mode 100644 typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Header.yaml create mode 100644 typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Invalid.yaml create mode 100644 typo3/sysext/form/Tests/Unit/Mvc/Configuration/InheritancesResolverServiceTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Mvc/Configuration/TypoScriptServiceTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Mvc/Configuration/YamlSourceTest.php rename typo3/sysext/form/{Resources/Private/Partials/Default/Confirmation/FlatElements/ContentElement.html => Tests/Unit/Mvc/Persistence/Fixtures/BlankForm.txt} (100%) rename typo3/sysext/form/{Resources/Private/Partials/Default/Confirmation/FlatElements/Header.html => Tests/Unit/Mvc/Persistence/Fixtures/BlankForm.yaml} (100%) create mode 100644 typo3/sysext/form/Tests/Unit/Mvc/Persistence/FormPersistenceManagerTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Mvc/ProcessingRuleTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Mvc/Validation/CountValidatorTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Mvc/Validation/EmptyValidatorTest.php rename typo3/sysext/form/{Classes/Domain/Filter/DigitFilter.php => Tests/Unit/Mvc/Validation/Fixtures/TestValidator.php} (54%) create mode 100644 typo3/sysext/form/Tests/Unit/Mvc/Validation/MimeTypeValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/PostProcess/MailPostProcessorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/PostProcess/PostProcessorTest.php create mode 100644 typo3/sysext/form/Tests/Unit/Utility/ArrayUtilityTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Utility/TypoScriptToJsonConverterTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/AbstractValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/AlphabeticValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/AlphanumericValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/BetweenValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/DateValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/DigitValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/EmailValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/EqualsValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/FileAllowedTypesValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/FileMaximumSizeValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/FileMinimumSizeValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/FloatValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/GreaterThanValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/InArrayValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/IntegerValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/IpValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/LengthValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/LessThanValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/RegExpValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/RequiredValidatorTest.php delete mode 100644 typo3/sysext/form/Tests/Unit/Validator/UriValidatorTest.php create mode 100644 typo3/sysext/form/Tests/Unit/ViewHelpers/Form/DatePickerViewHelperTest.php create mode 100644 typo3/sysext/form/ext_tables.php delete mode 100644 typo3/sysext/form/ext_tables.sql create mode 100644 typo3/sysext/form/ext_typoscript_setup.txt diff --git a/Build/Resources/Public/Less/form.less b/Build/Resources/Public/Less/form.less index 92969d29cbd3..b7d0033a58d8 100644 --- a/Build/Resources/Public/Less/form.less +++ b/Build/Resources/Public/Less/form.less @@ -1,703 +1,948 @@ @charset "UTF-8"; -.std-em-selector { - font-size: 90%; - font-style: normal; +// +// Variables +// +// @toDo remove these variables after including this file in backend.less +// Bootstrap: variables.less +@panel-default-text: @gray-dark; + +// /Build/Resources/Public/Less/cropper/variables.less +@screen-lg: 1200px; // Large screen / wide desktop + +// /Build/Resources/Public/Less/_variables.less +@gray-dark: rgb(90, 90, 90); +@gray: rgb(115, 115, 115); +@brand-primary: #0078e6; +@brand-success: #79a548; +@brand-info: #6daae0; +@brand-warning: #e8a33d; +@brand-danger: #c83c3c; +@table-bg: #fafafa; +@table-bg-hover: darken(@table-bg, 5%); +@panel-default-heading-bg: #ddd; +@text-color: #000; +@btn-default-bg: #eee; +@btn-default-border: #bbb; + +// /Build/Resources/Public/Less/Component/module.less +@module-docheader-height: 65px; +@module-docheader-border: #c3c3c3; + +// /Build/Resources/Public/Less/TYPO3/_module_web_page.less +@page-ce-header-hover-bg: #d0d0d0; + +// /Build/Resources/Public/Less/TYPO3/_element_tree.less +@navigation-bg: #f5f5f5; + +// Form Variables +@stage-max-width: 600px; +@stage-abstract-element-height: 62px; +@stage-abstract-element-toolbar-height: 35px; +@stage-icon-container-width: 40px; +@stage-validation-list-width: 100px; +@stage-breakpoint: (@screen-lg + 100); +@stage-validation-transition-time-in: 0.2s; +@stage-validation-transition-time-out: 0.3s; + +@stage-background-color: #fafafa; +@stage-element-toolbar-background: #d0d0d0; +@collection-element-background: @page-ce-header-hover-bg; + +// +// Mixins +// + +.fade-out-gradient-effect-bottom (@color, @gradient-start-height, @gradient-height) { + &:before, &:after { + display: block; + content: ''; + position: absolute; + bottom: 0; + right: 0; + left: 0; + } + &:before { + height: @gradient-start-height; + background: @color; + } + &:after { + bottom: @gradient-start-height; + height: @gradient-height; + background: linear-gradient(to bottom, rgba(red(@color), green(@color), blue(@color), 0) 0%, @color 100%); + } } -.std-strong-selector { - display: block; - font-size: 90%; - font-weight: normal; - color: #C00; +.selected-button-style-primary () { + .btn { + background-color: #fff; + border-color: #fff; + &:hover, &.active { + background-color: lighten(@brand-info, 30%); + } + } + .icon { + svg path { + fill: @brand-primary; + } + } } -.std-li-selector{ - float: none; - width: auto; - margin-right: 0; - margin-left: 1em; +.collapsed-icon-animation () { + transform: rotate(0deg); + transition: transform 0.2s; } -#fake-form { - - ol { - padding-left: 0 !important; - list-style: none !important; - } - /* style for each form element */ - li { - overflow: hidden; - padding: 0.5em; - margin-bottom: 0.5em; - - &#element-placeholder{ - padding: 0; - margin: 0; - } +.expanded-icon-animation () { + transform: rotate(90deg); + transition: transform 0.2s; +} - input + label, - textarea + label, - select + label { - .std-li-selector; - } +// +// X-Component +// +.t3-form-x-component { + position: absolute; + top: 0; + height: 100%; + line-height: normal; + background: @navigation-bg; - textarea + label { - vertical-align: top; - } + a { + text-decoration: none; } - - textarea, input[type=text]{ - border: 1px solid #c0c0c0; + ol, + ul { + list-style: none; + padding: 0; } - - input[type=text]{ - // equal width like textareas - width:300px; - padding: 3px; + .ui-sortable-placeholder { + outline-offset: -1px !important; } +} - label { - display:block; - margin-right: 1em; - vertical-align: baseline; +.t3-form-x-component-inner-wrapper { + padding: 1.5em; +} - em{ - .std-em-selector; - } +// +// Structure Tree +// +#t3-form-navigation-component { + overflow: hidden; + left: 0; +} - strong{ - .std-strong-selector; - } - } +#t3-form-structure-panel { + overflow: auto; + padding-top: @module-docheader-height; + height: 100%; - .x-checkbox, .x-radio{ - label{ - display:inline-block; - } + .icon { + z-index: 1; } - - legend { - border-bottom:0; - em{ - .std-em-selector; - } - - strong{ - .std-strong-selector; - } + #t3-form-navigation-component-tree-root-container, .tree li > div { + border: 1px solid transparent; + cursor: pointer; } - - fieldset { - position: relative; - margin: 0; - padding: 0; - - &.submit { - border-style: none; - } - - &.fieldset-horizontal { - border-width: 0; - - &.label-below label { - display: block; - margin-left: 0; - margin-top: 0.2em; - font-size: 90%; - text-align: left; - color: #999999; - } - - label{ - em { - display: inline; - } - } - - ol { - padding: 0; + .tree { + .svg-wrapper { + svg { + overflow: visible; + position: relative; + top: -0.8em; + left: 0.6em; } - - li { - float: left; - margin-right: 1em; - padding: 0; + path { + fill: none; + shape-rendering: crispEdges; + stroke: rgb(221, 221, 221); + stroke-width: 1; } } - - &.fieldset-subgroup { - margin-bottom: -2em; - border-style: none; - - ol { - position: relative; - top: -1.4em; - padding: 0; - } - - li { - padding: 0; + li { + white-space: nowrap; + .icon-actions-pagetree-collapse { + margin-right: 0.3em; + img { + .expanded-icon-animation (); + } } - - legend { - margin-left: 0; - padding: 0; + &.mjs-nestedSortable-collapsed { + > ol { + display: none; + } + .icon-actions-pagetree-collapse img { + .collapsed-icon-animation (); + } } - - input + label { - float: none; - width: auto; - display: inline; - margin: 0 0 0 1em; + small { + padding-left: 0.5em; + font-size: 80%; } } - } - - legend { - font-size: 12px; - font-weight: bold; - color: #000000; - - em { - position: absolute; - } - - strong { - position: absolute; - top: 1.4em; + .t3-form-icon { + margin-right: 0.5em; + margin-left: 0.5em; } - } - - /* labels as block, labels displayed above or below the input fields */ - .labels-block{ - label { - display: block; - float: none; - margin: 0 0 0.5em; - width: auto; + .t3-form-element-has-children > div .t3-form-icon { + margin-left: 0.1em; } - - input + label, - textarea + label{ - margin: 0.5em 0 0; + .sortable-hover { + outline: 1px solid darken(@panel-default-heading-bg, 20%); } - + } + .tree li > div:hover, + .t3-form-form-element-selected, + #t3-form-navigation-component-tree-root-container:hover, + .t3-form-root-element-selected { + background-color: darken(@navigation-bg, 1%); + border-color: darken(@navigation-bg, 10%); + border-radius: 2px; + margin-left: -2em; + padding-left: 2em; + margin-right: -1.3em; + } + .tree li > .t3-form-form-element-selected, + .tree li > .t3-form-form-element-selected:hover, + #t3-form-navigation-component-tree-root-container.t3-form-root-element-selected, + #t3-form-navigation-component-tree-root-container.t3-form-root-element-selected:hover { + background-color: #fff; + border-color: darken(@navigation-bg, 10%); + } + .t3-form-x-component-inner-wrapper { + padding-top: 2.5em; } } -/* labels alignment right */ -#fake-form .labels-alignment-right label, -#fake-form .labels-alignment-right .fieldset-subgroup legend, -#fake-form .labels-alignment-right.fieldset-subgroup legend { - text-align: right; -} - -#fake-form .labels-block fieldset.fieldset-subgroup, -#fake-form fieldset.labels-block.fieldset-subgroup { - margin-bottom: 0; -} - -#fake-form .labels-block .fieldset-subgroup legend, -#fake-form .labels-block.fieldset-subgroup legend { - width: auto; -} - -#fake-form .labels-block .fieldset-subgroup legend em, -#fake-form .labels-block.fieldset-subgroup legend em { - position: relative; -} - -#fake-form .labels-block .fieldset-subgroup legend strong, -#fake-form .labels-block.fieldset-subgroup legend strong { - position: relative; - top: 0; -} - -#fake-form .labels-block .fieldset-subgroup ol, -#fake-form .labels-block.fieldset-subgroup ol { - top: 0; - margin: 0; - padding: 0.5em 0 0; -} - -/* element HIDDEN */ -#fake-form .formwizard-element.hidden-element { - cursor: default; -} - -#fake-form .formwizard-element .hidden-dummy-element { - margin: 0; - padding: 5px; - border:1px dotted #A9A9A9; +// +// Inspector +// +.form-group.t3-form-collection-element-remove-button, +.t3-form-inspector-finishers-editor-removeButton, +.form-group.t3-form-inspector-validators-editor-removeButton { + margin: 0 !important; + font-size: 0; } -/* styles for drag and drop content */ -.x-dd-drag-ghost .formwizard-element { - list-style:none; +#t3-form-inspector-panels-container { + overflow: hidden; + right: 0; + padding-top: @module-docheader-height; } -.x-dd-drop-icon { - top: 7px; +#t3-form-inspector-panels { + overflow: auto; + height: 100%; } -.x-dd-drag-ghost ol { - margin: 5px 0; - padding: 0; - list-style: none; -} +#t3-form-inspector { + padding: 1em 0.5em; -.x-dd-drag-ghost .buttongroup, -.x-dd-drag-ghost label em, -.x-dd-drag-ghost label strong { - display: none; + h2, + h3, + h4 { + margin: 0; + padding: 0.1em 0.2em 0.2em 0.5em; + border-top: 1px solid @module-docheader-border; + clear: both; + font: inherit; + font-weight: bold; + } + h2 { + padding-bottom: 1em; + border: none; + border-bottom: 1px solid @module-docheader-border; + } + > h2:first-child { + border-top: none; + } + h3 { + color: @text-color; + padding-top: 0.3em; + border: none; + } + h4 { + padding: 0.8em 3em 0.8em 2.5em; + font-weight: 500; + background-color: @panel-default-heading-bg; + span[data-template-property="label"] { + vertical-align: top; + } + } + .t3-form-remove-element-button { + position: absolute; + top: 90px; + right: 2.5em; + } + .t3-form-control-group, + .t3-form-add-collection-element { + margin: 1.5em 0.5em; + clear: both; + } + .t3-form-inspector-editor-requiredValidator { + label { + cursor: pointer; + } + } } -.x-dd-drag-ghost label { - margin: 0 10px 0 5px; +// +// Inspector Collection +// +.t3-form-add-collection-element { + padding-bottom: 1em; } -.x-dd-drag-ghost legend { - margin: 0 5px; - font-size: 14px; - font-weight: bold; - color: #000; - border: none; -} +.t3-form-collection-container { + margin-top: -1em; + padding: 0.6em; -.x-grid-panel .remove { - background-image: url("../Images/remove.gif"); - width: 15px; - height: 16px; + .ui-sortable-handle { + cursor: auto; + } + h4 { + cursor: move; + } + .icon-actions-view-table-expand { + position: absolute; + left: 0.5em; + } + a.collapsed { + .icon-actions-view-table-expand svg { + .collapsed-icon-animation (); + } + } + a:not(.collapsed) { + .icon-actions-view-table-expand svg { + .expanded-icon-animation (); + } + } } -.x-dd-drag-proxy, -.x-dd-drop-nodrop { - background-color: #fff; - border-color: #c0c0c0; +.t3-form-collection-element { + position: relative; + margin-bottom: 0.5em; + border: 1px solid @module-docheader-border; + border-top: none; + background: @navigation-bg; + + .t3-form-collection-element-remove-button { + position: absolute; + right: 0.5em; + top: 0.6em; + } } -.tab-content{ - fieldset{ - #formwizard{ - display:inherit; +// +// Inspector Property Grid +// +.property-grid { + .form-control { + min-width: initial; + min-width: auto; + font-size: 0.9em; + } + .table { + th { + font-size: 0.9em; } - - &.form-section{ - float: left; - min-width: 380px; - width: 100%; - padding-bottom: 15px; - - &:last-child{ - margin-bottom: 1em; + > tbody > tr { + cursor: pointer; + background-color: @table-bg; + &:last-child { + cursor: auto; } - - ol{ - &#formwizard-right{ - // overwrite inline-style "auto" - width:100vw !important; - overflow: visible !important; - position: relative; - float:none; - left:5px !important; - padding-top:0; - padding-left:0; - margin-right: 10px; - top:30px !important; - display: table-cell; - height:auto !important; - list-style: none; - border-top-style: none; - - &.hover{ - left:0; - width:auto; - } + > td { + padding: 0.6em 0.3em; + text-align: center; + &:first-child { + width: 35px; + } + &:nth-child(2), &:nth-child(3) { + width: 75px; + } + &:nth-child(4) { + width: 65px; + } + &:nth-child(5) { + width: 35px; } } } + .btn { + background-color: @btn-default-bg; + border-color: @btn-default-border; + } } -} - -/* outer wrapper of whole wizard */ -#form-wizard-element { - z-index: 1; - - #formwizard-left{ - display: table-cell; - float:left !important; - margin-right: -1px; - - .x-tab-panel-body{ - // overwrite inline-styles with important ;( - height:100% !important; - overflow:visible !important; + .sort-row-field { + cursor: move; + } + .ui-sortable-helper { + td { + border: none; } } + .ui-sortable-placeholder { + height: 45px; + border-left: 1px solid @module-docheader-border !important; + border-right: 1px solid @module-docheader-border !important; + outline-offset: -5px !important; + } } -/* inner wrapper of whole wizard */ -#formwizard { - background-color: #F8F8F8; -} - -/* applied when a element is moved */ -#formwizard.hover-move { - cursor: move; +// +// Stage +// +#t3-form-stage-inner-container { + display: inline-block; + width: 90%; + text-align: left; + @media (min-width: @stage-breakpoint) { + width: @stage-max-width; + } } -/* left panel */ -#formwizard-left.x-border-panel { +#t3-form-stage-container { + overflow: auto; position: relative; - left: auto; - top: auto; -} - -/* tabs */ -#formwizard-left .x-tab-panel-header { - background-color: transparent; -} - -/* tabs inner */ -#formwizard-left .x-tab-strip { - margin-bottom: 0; -} - -#formwizard-left .x-tab-strip-top .x-tab-left { - padding-right: 20px; -} - -#formwizard-left .x-tab-strip-top .x-tab-right { - padding: 5px 10px 2px; - background-color: #EDEDED; - border-radius: 0; -} - -#formwizard-left .x-tab-strip-top .x-tab-strip-active .x-tab-right, -#formwizard-left .x-tab-strip-top .x-tab-strip-active.x-tab-strip-over .x-tab-right{ - background-color: transparent; - border-bottom-color: #F8F8F8; -} - -#formwizard-left .x-tab-strip-top .x-tab-strip-over .x-tab-right { - background-color: #E1E1E1; -} - -#formwizard-left li.validation-error .x-tab-left, -#formwizard-left div.validation-error .x-accordion-hd { - margin-right: 14px; - background-image: url("../../../../../t3skin/extjs/images/form/exclamation.gif"); - background-position: right 1px; - background-repeat: no-repeat; -} - -/* content below tabs */ -#formwizard-left .x-tab-panel-body-content { - min-height: 330px; - padding: 10px; - background: transparent; - border: 1px solid #C0C0C0; - border-top-width: 0; -} - -/* info messages (also for drag and drop) */ -#formwizard-left .message-information, -#fake-form .message-information, -.x-dd-drag-ghost .message-information { - margin: 10px 0 15px 16px; - padding: 12px 10px; - background-image: none; - border-radius: 0; - box-shadow: none; -} - -#formwizard-left .message-information p, -#fake-form .message-information p, -.x-dd-drag-ghost .message-information { - margin: 0; -} - -/* intro info messages */ -#formwizard-left #formwizard-left-elements-intro, -#formwizard-left #formwizard-left-options-dummy, -#fake-form .message-information { - margin: 0 0 10px; -} - -#formwizard-left .x-tab-panel-body, -#formwizard-left .x-accordion-hd { - background: transparent none; - border-width: 0; -} - -/* accordion */ -#formwizard-left .x-panel-accordion { - border-bottom: 1px solid #C7C7C7; -} - -#formwizard-left .x-panel-accordion:last-child { - border-bottom: medium none; -} - -/* headline of accordion */ -#formwizard-left .x-accordion-hd { - padding-left: 0; -} - -/* toggle icon of accordion */ -#formwizard-left .x-accordion-hd .x-tool-toggle { - margin: 0; - float: left; - background-image: url("../Images/module-menu-down.png"); - background-position: 0 4px; -} - -#formwizard-left .x-panel-collapsed .x-accordion-hd .x-tool-toggle { - background-image: url("../Images/module-menu-right.png"); - background-position: 1px 3px; -} - -#formwizard-left .x-accordion-hd .x-panel-header-text { - font-weight: bold; -} - -#formwizard-left .x-accordion-hd .x-panel-body { - padding-left: 15px; -} - -/* element inside accordion */ -#formwizard-left .x-form { - margin-left: 15px; - margin-top: 10px; -} - -#formwizard-left .x-form fieldset { - padding: 0; - border: none; -} -#formwizard-left .x-form fieldset legend { - padding: 0; - font-size: 12px; - color: #222222; -} - -#formwizard-left .x-panel-tbar { - margin-top: 10px; - padding-left: 15px; - padding-bottom: 0; - border-width: 0; -} - -#formwizard-left .x-panel-tbar .x-toolbar { - padding: 0; -} - -#formwizard-left .x-table-layout { - margin-bottom: 10px; -} - -/* generic element like textfield */ -#formwizard-left .formwizard-element { - margin-left: 12px; - background-color: transparent; - background-image: none; - border-color: transparent; -} - -#formwizard-left .formwizard-element.x-btn-over { - background-color: #D5D5D5; - background-image: -moz-linear-gradient(center top , #F6F6F6 10%, #D5D5D5 90%); - border-color: #C0C0C0; - border-radius: 0; -} - -#formwizard-left .formwizard-element .x-btn-mc { + height: 100%; text-align: left; -} - -/* form elements in left panel */ -.formwizard-left-elements-basic-button { - background-image: url("../Images/ui-button.png"); -} - -.formwizard-left-elements-basic-checkbox { - background-image: url("../Images/ui-check-box.png"); -} - -.formwizard-left-elements-basic-fieldset { - background-image: url("../Images/ui-group-box.png"); -} - -.formwizard-left-elements-basic-fileupload { - background-image: url("../Images/drive-upload.png"); -} - -.formwizard-left-elements-basic-hidden { - background-image: url("../Images/ui-text-field-hidden.png"); -} - -.formwizard-left-elements-basic-password { - background-image: url("../Images/ui-text-field-password.png"); -} - -.formwizard-left-elements-basic-radio { - background-image: url("../Images/ui-radio-button.png"); -} - -.formwizard-left-elements-basic-reset { - background-image: url("../Images/broom.png"); -} - -.formwizard-left-elements-basic-select { - background-image: url("../Images/ui-combo-box.png"); -} - -.formwizard-left-elements-basic-submit { - background-image: url("../Images/ui-button-default.png"); -} - -.formwizard-left-elements-basic-textarea { - background-image: url("../Images/ui-scroll-pane-text.png"); -} - -.formwizard-left-elements-basic-textline { - background-image: url("../Images/ui-text-field.png"); -} - -.formwizard-left-elements-predefined-checkboxgroup { - background-image: url("../Images/ui-check-boxes.png"); -} - -.formwizard-left-elements-predefined-email { - background-image: url("../Images/mail.png"); -} - -.formwizard-left-elements-predefined-name { - background-image: url("../Images/user-silhouette.png"); -} - -.formwizard-left-elements-predefined-radiogroup { - background-image: url("../Images/ui-radio-buttons.png"); -} - -.formwizard-left-elements-content-header { - background-image: url("../Images/edit-heading.png"); -} - -.formwizard-left-elements-content-textblock { - background-image: url("../Images/edit-textblock.png"); -} - -#formwizard-left .x-form-text { - height: 17px; -} - -#formwizard-left .x-btn-text-icon .x-btn-icon-small-left .x-btn-text { - color: black; -} - -#formwizard-left .x-small-editor .x-form-text { - height: 13px !important; -} - -/* icon in element field for applying entered text */ -#formwizard-left .x-form-field-wrap .x-form-submit-trigger { - background-image: url("../Images/submit-trigger.gif"); -} - -/* right panel */ -#formwizard-right { - min-height: 350px; - padding: 30px 0 10px 2px; - overflow: visible; -} - -#fake-form { - padding: 10px; - background-color: tansparent; - border: 1px solid #C0C0C0; -} - -#fake-form li { - overflow: visible; -} - -/* visible area of element on right panel */ -#fake-form div.overflow-hidden { - overflow: hidden; - - input{ - margin-left: 1px; + @media (min-width: @stage-breakpoint) { + text-align: center; } -} -/* wrap around all elements on right panel */ -#fake-form .formwizard-element { - border: 1px solid transparent; - position: relative; -} + ol, + ul { + list-style: none; + } + .form-section { + border: none; + } + .panel-heading { + button { + outline: none; + } + .paginiation-label { + margin-right: 1em; + } + } + .t3-form-new-element-container { + height: @stage-abstract-element-height; + border: 1px dashed @panel-default-heading-bg; + text-align: center; + padding-top: @stage-abstract-element-height/2; + .btn { + transform: translateY(-50%); + } + } -#fake-form .formwizard-element.hover, -#fake-form .formwizard-element.hidden.hover { - background-color: #F9FCFF; - border: 1px solid #C5DBE6; -} + // Abstract + &.t3-form-stage-viewmode-abstract { + ol, + ul { + padding-left: @stage-icon-container-width; + padding-right: 1em; + } + .t3-form-page-title { + margin: 0 0 0.5em; + } + #t3-form-stage-inner-container { + overflow: hidden; + } + .t3-form-element-composit { + &:not(.t3-form-element-toplevel) { + margin-bottom: 1em; + padding-bottom: 1px; + outline: 1px solid #dddddd; + outline-offset: -1px; + } + .sortable-hover { + outline-color: darken(@panel-default-heading-bg, 40%); + } + .t3-form-form-composit-element-selected { + outline-color: @brand-primary; + } + } + .t3-form-element-composit.sortable-hover > .ui-sortable-handle, + .ui-sortable-handle:hover { + border-color: darken(@panel-default-heading-bg, 40%); + .t3-form-icon-container { + background-color: darken(@panel-default-heading-bg, 40%); + path { + fill: #fff; + } + } + } + .ui-sortable { + fieldset { + position: relative; + min-height: 130px; + padding-top: 5em; + legend { + position: absolute; + top: 1em; + display: inline-block; + width: 95%; + } + } + } + .ui-sortable-handle { + overflow: hidden; + position: relative; + height: @stage-abstract-element-height; + margin-bottom: 1em; + border: 1px solid @panel-default-heading-bg; + background-color: #fff; + + &:hover { + .t3-form-validator-list { + right: 0; + transition: right @stage-validation-transition-time-in; + } + .t3-form-element-info .element-content { + span, div { + color: @gray-dark; + } + } + .t3-form-validator-info .t3-form-icon { + margin-right: (@stage-validation-list-width - 25); + transition: margin @stage-validation-transition-time-in; + } + } + &:has (.ui-sortable-handle:hover) { + border-color: transparent; + } + span { + color: @gray-dark; + } + } + .ui-state-disabled { + cursor: auto; + &:hover { + background: none; + } + } + .ui-sortable-placeholder { + margin-bottom: 1em; + } + .t3-form-icon-container { + float: left; + width: @stage-icon-container-width; + height: 100%; + padding: 1em; + cursor: move; + background-color: @panel-default-heading-bg; + .t3-form-icon { + height: 100%; + } + } + .t3-form-form-element-body { + height: 100%; + } + .t3-form-element-info { + position: relative; + float: left; + width: 55%; + height: 100%; + padding-left: 1em; + .fade-out-gradient-effect-bottom (#fff, 0.8em, 1em); + .element-label-container { + float: left; + position: relative; + width: 45%; + height: 100%; + .element-label { + overflow: hidden; + position: absolute; + top: 50%; + width: 100%; + text-overflow: ellipsis; + transform: translateY(-50%); + } + } + .element-content { + padding-top: 1em; + white-space: nowrap; + font-size: 0.8em; + span, div { + color: @panel-default-heading-bg; + } + } + } + .t3-form-validator-info { + position: relative; + overflow: hidden; + float: right; + height: 100%; + .t3-form-icon { + height: 100%; + z-index: 1; + margin-left: 1em; + transition: margin @stage-validation-transition-time-out; + filter: grayscale(100%); + } + .t3-form-validator-list { + .fade-out-gradient-effect-bottom(@panel-default-heading-bg, 1em, 1em); + position: absolute; + top: 0; + right: -@stage-validation-list-width; + width: @stage-validation-list-width; + height: 100%; + padding: 1em 1em 1em (@stage-validation-list-width - 65); + font-size: 0.8em; + transition: right @stage-validation-transition-time-out; + background-color: @panel-default-heading-bg; + } + .validator-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: @gray-dark; + } + } + #t3-form-stage .t3-form-form-element-selected { + position: relative; + padding-top: @stage-abstract-element-toolbar-height; + height: @stage-abstract-element-height + @stage-abstract-element-toolbar-height; + border: none; + + .t3-form-form-element-body { + border: 1px solid @brand-primary; + } + .t3-form-icon-container { + background-color: @brand-primary; + } + .t3-form-element-info .element-content { + span, div { + color: @gray-dark; + } + } + .t3-form-validator-list { + @validation-list-background: lighten(@brand-info, 30%); + right: 0; + transition: right @stage-validation-transition-time-in; + background-color: @validation-list-background; + &:before { + background-color: @validation-list-background; + } + &:after { + background: linear-gradient(to bottom, rgba(red(@validation-list-background), green(@validation-list-background), blue(@validation-list-background), 0) 0%, @validation-list-background 100%); + } + } + .t3-form-validator-info .t3-form-icon { + margin-right: (@stage-validation-list-width - 25); + filter: none; + } + .btn-toolbar-container { + position: absolute; + top: 0; + right: 0; + width: 100%; + height: @stage-abstract-element-toolbar-height; + border: 1px solid @brand-primary; + background-color: @brand-primary; + padding-right: 0.7em; + padding-top: 0.4em; + &:before, &:after { + position: absolute; + top: 0; + display: block; + width: 1px; + height: 100%; + content: ' '; + background-color: @brand-primary; + } + &:before { + left: -1px; + } + &:after { + right: -1px; + } + .dropdown-menu { + left: auto; + left: initial; + min-width: initial; + right: 0; + padding-left: 0; + padding-right: 0; + background-color: darken(@brand-primary, 10%); + > li a:hover { + background-color: darken(@brand-primary, 5%); + } + } + .caret { + color: @brand-primary; + } + .t3-form-dropdown-buttons { + .icon { + margin-right: 0.5em; + } + } + .btn-toolbar { + float: right; + .selected-button-style-primary; + } + } + .meta-label { + display: inline-block; + top: 1em; + left: 5em; + bottom: auto; + font-size: 0.9em; + color: #fff; + span { + color: #fff; + } + } + } + .panel.t3-form-form-stage-selected { + border-color: @brand-primary; + > .panel-heading { + background-color: @brand-primary; + border-color: @brand-primary; + color: #fff; + .selected-button-style-primary; + } + } + } -#fake-form .formwizard-element.active, -#fake-form .formwizard-element.hidden.active { - background-color: #EAF7FF; - border: 1px solid #C5DBE6; -} + // Preview + &.t3-form-stage-viewmode-preview { + input[type="text"], input[type="date"], input[type="password"], textarea, select { + color: #000; + background-color: lighten(@panel-default-heading-bg, 3%); + } + ::placeholder { + color: @gray; + font-style: italic; + } + input[type="date"] { + display: block; + width: 100%; + height: 32px; + padding: 0.6em; + font-size: 12px; + line-height: 1.5; + background-image: none; + border: 1px solid @module-docheader-border; + border-radius: 2px; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; + } + select[multiple="multiple"] { + height: auto; + min-height: 32px; + } + textarea { + min-height: 100px; + } + legend.t3-form-form-element-selected { + border-color: @module-docheader-border; + } + .form-navigation { + .btn-group { + span, button { + display: inline-block; + margin-right: 1em; + } + } + } + .preview-table-first-col { + width: 30%; + } + .t3-form-element-preview { + position: relative; + display: inline-block; + width: 100%; + } + .t3-form-new-element-container { + display: none; + } + .t3-form-element-toplevel > form > .tooltip { + top: 100px !important; + } + } -#fake-form .formwizard-element.hidden { - min-height: 1em; - background-color: transparent; - border: 1px dotted #C5DBE6; + #t3-form-stage { + margin-bottom: 0; + padding-top: 0.5em; + > ol, > ol > li > ol { + padding-left: 0; + padding-right: 0; + } + .t3-form-element-toplevel { + > .t3-form-form-element-selected { + height: auto; + padding-top: 0; + } + > .t3-form-form-element-selected .btn-toolbar-container { + display: none; + } + } + } } -/* toolbar on each element */ -#fake-form .formwizard-element div.buttongroup { +.meta-label { + z-index: 1; position: absolute; - right: 0; - top: -35px; + bottom: 1em; + left: 5.5em; display: none; - z-index: 1; - padding: 4px 3px 12px; - background-image: url("../Images/tooltip.png"); + color: @brand-primary; + line-height: 1.6; + font-size: 0.8em; + .ui-sortable-handle:hover > & { + display: inline-block; + } } -#fake-form .formwizard-element.hover > div.buttongroup, -#fake-form .formwizard-element.active > div.buttongroup { - display: block; +.ui-sortable-placeholder, +.t3-form-element-composit.ui-sortable-placeholder { + background-color: #fff !important; + border: none !important; + outline: 1px dashed lighten(@brand-success, 30%) !important; + outline-offset: -2px !important; + visibility: visible !important; } -#fake-form .formwizard-element div.buttongroup button { - width: 16px; - height: 16px; - background-color: transparent; - border: 0 solid transparent; +// +// Icons +// +.t3-form-icon { + margin-right: 1em; } -#fake-form .formwizard-element div.buttongroup span { - margin: 0 3px; +// +// Validation Errors +// +.t3-form-validation-child-has-error { + color: @brand-danger; } -#fake-form .formwizard-element div.buttongroup span.x-btn-over { - background-color: transparent; - background-image: none; +.t3-form-validation-errors { + #t3-form-navigation-component &, + #t3-form-stage-container & { + position: relative; + color: @brand-danger; + &:before { + z-index: 1; + position: absolute; + display: inline-block; + width: 15px; + height: 15px; + font-family: FontAwesome; + vertical-align: middle; + border-radius: 50%; + font-size: 1em; + line-height: 1.4; + text-align: center; + background: none; + } + } + #t3-form-navigation-component & { + &:hover:before, + &.t3-form-form-element-selected:before { + left: 2.4em; + } + &:before { + margin-top: 0.2em; + color: #fff; + font-size: 10px; + font-weight: 800; + content: "\f12a"; + background-color: @brand-danger; + } + } + &#t3-form-navigation-component-tree-root:before { + left: -2em !important; + margin-top: 0.1em; + } + #t3-form-stage-container &.ui-sortable-handle { + border-color: @brand-danger; + &:before { + left: 4.5em; + margin-top: 1.9em; + content: "\f071"; + } + .element-label { + padding-left: 1.5em; + } + } + #t3-form-inspector-panels .t3-form-collection-element & { + display: inline-block; + color: #fff; + font-size: 0.8em; + font-weight: 700; + background-color: @brand-danger; + margin-top: 0.5em; + padding: 0.1em 0.5em; + border-radius: 2px; + } + #t3-form-inspector-panels &.t3-form-collection-element { + border-color: @brand-danger; + + h4 { + border-color: @brand-danger; + background-color: @brand-danger; + color: #fff; + path { + fill: #fff; + } + } + .t3-form-collection-element-remove-button { + background: #fff; + border-color: transparent; + path { + fill: @brand-danger; + } + } + } } -#fake-form .formwizard-element button.t3-icon-edit-delete { - background-image: url('../../../../core/Resources/Public/Icons/T3Icons/actions/actions-delete.svg'); +// +// Loading Editor Spinner +// +.form-editor-loading-spinner { + width: 150px; + margin: 5em auto 0; + text-align: center; } -#fake-form .formwizard-element button.t3-icon-document-open { - background-image: url('../../../../core/Resources/Public/Icons/T3Icons/actions/actions-open.svg'); +// +// jQuery nestedSortable +// +.ui-sortable-handle { + cursor: pointer; } -@media only screen and (max-width:615px) { - .tab-content fieldset.form-section ol#formwizard-right{ - left:0 !important; +// +// Module +// +.module[data-module-name="web_FormFormbuilder_FormEditor"] { + overflow: hidden; + .module-body, div[data-identifier="moduleWrapper"] { + height: 100%; + } + .module-body { + padding-bottom: 0.5em; + } + .module-docheader-bar-column-left { + button { + &, &:focus, &:active { + outline: 0; + outline-color: initial; + outline-style: initial; + outline-width: 0px; + } + } + .btn-group { + margin-left: 25px; + } } } + +.t3-form-element-new-page-button { + position: absolute; + left: 0.5em; +} \ No newline at end of file diff --git a/composer.json b/composer.json index 784ca9ab3632..7c587acd4c1b 100644 --- a/composer.json +++ b/composer.json @@ -40,6 +40,7 @@ "swiftmailer/swiftmailer": "~5.4.3", "symfony/console": "^2.7 || ^3.0", "symfony/finder": "^2.7 || ^3.0", + "symfony/yaml": "^2.7 || ^3.0", "doctrine/instantiator": "~1.0.4", "typo3/class-alias-loader": "^1.0", "typo3/cms-composer-installers": "^1.2.8", diff --git a/composer.lock b/composer.lock index 51dff215a913..fe9516c43d57 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "90479fd517e730794243c516ef4ba9ae", - "content-hash": "9ac01b16aca6a55472ecf320b6dacf31", + "hash": "9cc1058ed137d0775741d7c5701e77a1", + "content-hash": "2749660dc0a03d1b473808cf28c9cf10", "packages": [ { "name": "cogpowered/finediff", @@ -1119,6 +1119,55 @@ ], "time": "2016-05-18 14:26:46" }, + { + "name": "symfony/yaml", + "version": "v3.1.6", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "7ff51b06c6c3d5cc6686df69004a42c69df09e27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/7ff51b06c6c3d5cc6686df69004a42c69df09e27", + "reference": "7ff51b06c6c3d5cc6686df69004a42c69df09e27", + "shasum": "" + }, + "require": { + "php": ">=5.5.9" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.1-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony Yaml Component", + "homepage": "https://symfony.com", + "time": "2016-10-24 18:41:13" + }, { "name": "typo3/class-alias-loader", "version": "1.0.0", @@ -3234,55 +3283,6 @@ "homepage": "https://symfony.com", "time": "2016-06-29 05:41:56" }, - { - "name": "symfony/yaml", - "version": "v3.1.6", - "source": { - "type": "git", - "url": "https://github.com/symfony/yaml.git", - "reference": "7ff51b06c6c3d5cc6686df69004a42c69df09e27" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/7ff51b06c6c3d5cc6686df69004a42c69df09e27", - "reference": "7ff51b06c6c3d5cc6686df69004a42c69df09e27", - "shasum": "" - }, - "require": { - "php": ">=5.5.9" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.1-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Yaml\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Yaml Component", - "homepage": "https://symfony.com", - "time": "2016-10-24 18:41:13" - }, { "name": "webmozart/assert", "version": "1.1.0", diff --git a/typo3/sysext/core/Documentation/Changelog/master/Feature-77910-EXTform-IntroduceNewFormFramework.rst b/typo3/sysext/core/Documentation/Changelog/master/Feature-77910-EXTform-IntroduceNewFormFramework.rst new file mode 100644 index 000000000000..db99783b331e --- /dev/null +++ b/typo3/sysext/core/Documentation/Changelog/master/Feature-77910-EXTform-IntroduceNewFormFramework.rst @@ -0,0 +1,54 @@ +.. include:: ../../Includes.txt + +========================================================= +Feature: #77910 - EXT:form - introduce new form framework +========================================================= + +See :issue:`77910` + +Description +=========== + +A flexible framework for building forms is integrated. It replaces the legacy 'form wizard' based on ExtJS and the +depending frontend rendering system. + +The new backend 'form editor' relies on vanilla JS and jQuery. Different JS patterns have been applied to ensure +a modern architecture, high flexibility and extensibility. + +A new backend module lists all existing forms and allows the creation of new ones. The 'mailform' content element +is reworked. It lists available forms and enables the backend editor to override certain settings, e.g. 'finisher' +settings (formerly known as 'postProcessors'). + +Till now it was not possible to customize and extend the 'form editor'. To allow the registration of new +finishers, validators and pre-defined form elements a lot of architectural changes were needed. After a long +conceptional phase the team decided to remove the former code base, backport the 'form' package of the Flow +project and improve the given ideas and concepts. The result is a new form extension. A lot of code received +major improvements and tons of additional features have been integrated. + +The list of features is long and impressive. The documentation will explain the ideas, concept and architecture +as well as the functionality in detail. The following list names some of them: + +* YAML as configuration and description language including inheritances and overrides. +* File based configuration. +* All JavaScript components of the form wizard (and the wizard itself) can be replaced or extended. +* Own PHP renderer for form and/ or form elements possible. +* Create entire forms via API. +* Create conditions for form elements and validators programmatically. +* Create 'prototypes' and use them as boilerplate. +* Create new form elements and use them in the wizard. +* Uploads are handled as FAL objects. +* Ships bunch of built-in finishers, like email, redirect, save to database. +* Create own finishers. Override thos in the content element. +* Create and apply own validators. +* Multi language support. +* Multi step support. +* Multiple forms per page. +* Built-in spam protection (honeypot). + + +Impact +====== + +Happy little wizard. + +.. index:: Frontend, PHP-API, JavaScript, ext:form \ No newline at end of file diff --git a/typo3/sysext/form/Classes/ContentObject/FormContentObject.php b/typo3/sysext/form/Classes/ContentObject/FormContentObject.php deleted file mode 100644 index e2176de9c1a7..000000000000 --- a/typo3/sysext/form/Classes/ContentObject/FormContentObject.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\ContentObject; - -/* - * 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\Core\TypoScript\Parser\TypoScriptParser; -use TYPO3\CMS\Core\Utility\ArrayUtility; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Form\Domain\Model\Configuration; -use TYPO3\CMS\Frontend\ContentObject\AbstractContentObject; -use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; - -/** - * FORM cObject, a wrapper to allow to use 10 = FORM in TypoScript - * which actually executes the Extbase plugin (marked as non-cached) - */ -class FormContentObject extends AbstractContentObject -{ - /** - * Renders the application defined cObject FORM - * - * The Extbase plugin "Form" is initialized. At this time, the - * controller "Frontend" action "show" does the rest. - * - * @param array $conf TS configuration for this cObject - * @return string HTML output - * @throws \InvalidArgumentException - */ - public function render($conf = []) - { - $mergedTypoScript = null; - // If the FORM configuration is retrieved from the database - // all TypoScript interpretation will be disabled for security. - if ($this->cObj->data['CType'] === 'mailform') { - // If the FORM configuration is retrieved from the database - // and a predefined form is selected then the TypoScript - // interpretation is allowed. - $renderPredefinedForm = false; - $predefinedFormIdentifier = null; - if (!empty($this->cObj->data['tx_form_predefinedform'])) { - $predefinedFormIdentifier = $this->cObj->data['tx_form_predefinedform']; - if (isset($this->getTypoScriptFrontendController()->tmpl->setup['plugin.']['tx_form.']['predefinedForms.'][$predefinedFormIdentifier . '.'])) { - $renderPredefinedForm = true; - } else { - throw new \InvalidArgumentException('No FORM configuration for identifier "' . $predefinedFormIdentifier . '" available.', 1466769483); - } - } - - if ($renderPredefinedForm && $predefinedFormIdentifier) { - $mergedTypoScript = $this->getTypoScriptFrontendController()->tmpl->setup['plugin.']['tx_form.']['predefinedForms.'][$predefinedFormIdentifier . '.']; - ArrayUtility::mergeRecursiveWithOverrule($mergedTypoScript, $conf); - } else { - $bodytext = $this->cObj->data['bodytext']; - /** @var $typoScriptParser TypoScriptParser */ - $typoScriptParser = GeneralUtility::makeInstance(TypoScriptParser::class); - $typoScriptParser->parse($bodytext); - $mergedTypoScript = (array)$typoScriptParser->setup; - ArrayUtility::mergeRecursiveWithOverrule($mergedTypoScript, $conf); - // Disables TypoScript interpretation since TypoScript is handled that could contain insecure settings: - $mergedTypoScript[Configuration::DISABLE_CONTENT_ELEMENT_RENDERING] = true; - } - } - - // make sure the extbase plugin is marked as Uncached - $content = $this->prepareNonCacheableUserFunction(is_array($mergedTypoScript) ? $mergedTypoScript : $conf); - - // Only apply stdWrap to TypoScript that was NOT created by the wizard: - if (isset($conf['stdWrap.'])) { - $content = $this->cObj->stdWrap($content, $conf['stdWrap.']); - } - return $content; - } - - /** - * Set up the extbase plugin to be a non-cacheable user function - * - * @param array $typoScript - * @return string the content as placeholder for USER_INT code - */ - protected function prepareNonCacheableUserFunction($typoScript) - { - $configuration = [ - 'userFunc' => 'TYPO3\\CMS\\Extbase\\Core\\Bootstrap->run', - 'pluginName' => 'Form', - 'extensionName' => 'Form', - 'vendorName' => 'TYPO3\\CMS', - 'controller' => 'Frontend', - 'action' => 'show', - 'settings' => ['typoscript' => $typoScript], - 'persistence' => [], - 'view' => [], - ]; - - $this->cObj->setUserObjectType(ContentObjectRenderer::OBJECTTYPE_USER_INT); - $substKey = 'INT_SCRIPT.' . $this->getTypoScriptFrontendController()->uniqueHash(); - $content = '<!--' . $substKey . '-->'; - $this->getTypoScriptFrontendController()->config['INTincScript'][$substKey] = [ - 'conf' => $configuration, - 'cObj' => serialize($this->cObj), - 'type' => 'FUNC' - ]; - $this->cObj->setUserObjectType(false); - return $content; - } - - /** - * @return \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController - */ - protected function getTypoScriptFrontendController() - { - return $GLOBALS['TSFE']; - } -} diff --git a/typo3/sysext/form/Classes/Controller/AbstractBackendController.php b/typo3/sysext/form/Classes/Controller/AbstractBackendController.php new file mode 100644 index 000000000000..0f028e4982f7 --- /dev/null +++ b/typo3/sysext/form/Classes/Controller/AbstractBackendController.php @@ -0,0 +1,91 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Controller; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\PathUtility; +use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; +use TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManagerInterface; + +/** + * The abstract form backend controller + * + * Scope: backend + */ +abstract class AbstractBackendController extends ActionController +{ + + /** + * @var array + */ + protected $formSettings; + + /** + * @var \TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface + */ + protected $formPersistenceManager; + + /** + * @param \TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface $formPersistenceManager + * @return void + * @internal + */ + public function injectFormPersistenceManager(\TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface $formPersistenceManager) + { + $this->formPersistenceManager = $formPersistenceManager; + } + + /** + * @internal + */ + public function initializeObject() + { + $this->formSettings = $this->objectManager->get(ConfigurationManagerInterface::class) + ->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_YAML_SETTINGS, 'form'); + } + + /** + * Convert arrays with EXT: resource paths to web paths + * + * Input: + * [ + * 100 => 'EXT:form/Resources/Public/Css/form.css' + * ] + * + * Output: + * + * [ + * 0 => 'typo3/sysext/form/Resources/Public/Css/form.css' + * ] + * + * @param array $resourcePaths + * @return array + */ + protected function resolveResourcePaths(array $resourcePaths): array + { + $return = []; + foreach ($resourcePaths as $resourcePath) { + $fullResourcePath = GeneralUtility::getFileAbsFileName($resourcePath); + $resourcePath = PathUtility::getAbsoluteWebPath($fullResourcePath); + if (empty($resourcePath)) { + continue; + } + $return[] = $resourcePath; + } + + return $return; + } +} diff --git a/typo3/sysext/form/Classes/Controller/FormEditorController.php b/typo3/sysext/form/Classes/Controller/FormEditorController.php new file mode 100644 index 000000000000..756015cd4019 --- /dev/null +++ b/typo3/sysext/form/Classes/Controller/FormEditorController.php @@ -0,0 +1,445 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Controller; + +/* + * 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\Template\Components\ButtonBar; +use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Backend\View\BackendTemplateView; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Imaging\Icon; +use TYPO3\CMS\Core\Utility\ArrayUtility as CoreArrayUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Fluid\View\StandaloneView; +use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService; +use TYPO3\CMS\Form\Domain\Exception\RenderingException; +use TYPO3\CMS\Form\Domain\Factory\ArrayFormFactory; +use TYPO3\CMS\Form\Mvc\Persistence\Exception\PersistenceManagerException; +use TYPO3\CMS\Form\Service\TranslationService; +use TYPO3\CMS\Form\Utility\ArrayUtility; +use TYPO3\CMS\Lang\LanguageService; + +/** + * The form editor controller + * + * Scope: backend + */ +class FormEditorController extends AbstractBackendController +{ + + /** + * Default View Container + * + * @var BackendTemplateView + */ + protected $defaultViewObjectName = BackendTemplateView::class; + + /** + * @var array + */ + protected $prototypeConfiguration; + + /** + * Displays the form editor + * + * @param string $formPersistenceIdentifier + * @param string $prototypeName + * @return void + * @throws PersistenceManagerException + * @internal + */ + public function indexAction(string $formPersistenceIdentifier, string $prototypeName = null) + { + $this->registerDocheaderButtons(); + $this->view->getModuleTemplate()->setModuleName($this->request->getPluginName() . '_' . $this->request->getControllerName()); + $this->view->getModuleTemplate()->setFlashMessageQueue($this->controllerContext->getFlashMessageQueue()); + + if ( + strpos($formPersistenceIdentifier, 'EXT:') === 0 + && !$this->formSettings['persistenceManager']['allowSaveToExtensionPaths'] + ) { + throw new PersistenceManagerException('Edit a extension formDefinition is not allowed.', 1478265661); + } + + $formDefinition = $this->formPersistenceManager->load($formPersistenceIdentifier); + $formDefinition = ArrayUtility::stripTagsFromValuesRecursive($formDefinition); + if (empty($prototypeName)) { + $prototypeName = isset($formDefinition['prototypeName']) ? $formDefinition['prototypeName'] : 'standard'; + } + $formDefinition['prototypeName'] = $prototypeName; + + $configurationService = $this->objectManager->get(ConfigurationService::class); + $this->prototypeConfiguration = $configurationService->getPrototypeConfiguration($prototypeName); + + $formEditorDefinitions = $this->getFormEditorDefinitions(); + + $formEditorAppInitialData = [ + 'formEditorDefinitions' => $formEditorDefinitions, + 'formDefinition' => $formDefinition, + 'formPersistenceIdentifier' => $formPersistenceIdentifier, + 'prototypeName' => $prototypeName, + 'endpoints' => [ + 'formPageRenderer' => $this->controllerContext->getUriBuilder()->uriFor('renderFormPage'), + 'saveForm' => $this->controllerContext->getUriBuilder()->uriFor('saveForm') + ], + 'additionalViewModelModules' => $this->prototypeConfiguration['formEditor']['dynamicRequireJsModules']['additionalViewModelModules'], + 'maximumUndoSteps' => $this->prototypeConfiguration['formEditor']['maximumUndoSteps'], + ]; + + $this->view->assign('formEditorAppInitialData', json_encode($formEditorAppInitialData)); + $this->view->assign('stylesheets', $this->resolveResourcePaths($this->prototypeConfiguration['formEditor']['stylesheets'])); + $this->view->assign('formEditorTemplates', $this->renderFormEditorTemplates( + $this->prototypeConfiguration['formEditor']['formEditorTemplates'], + $formEditorDefinitions + )); + $this->view->assign('dynamicRequireJsModules', $this->prototypeConfiguration['formEditor']['dynamicRequireJsModules']); + + $popupWindowWidth = 700; + $popupWindowHeight = 750; + $popupWindowSize = ($this->getBackendUser()->getTSConfigVal('options.popupWindowSize')) + ? trim($this->getBackendUser()->getTSConfigVal('options.popupWindowSize')) + : null; + if (!empty($popupWindowSize)) { + list($popupWindowWidth, $popupWindowHeight) = GeneralUtility::intExplode('x', $popupWindowSize); + } + + $addInlineSettings = [ + 'FormEditor' => [ + 'typo3WinBrowserUrl' => BackendUtility::getModuleUrl('wizard_element_browser'), + ], + 'Popup' => [ + 'PopupWindow' => [ + 'width' => $popupWindowWidth, + 'height' => $popupWindowHeight + ], + ] + ]; + + CoreArrayUtility::mergeRecursiveWithOverrule( + $addInlineSettings, + $this->prototypeConfiguration['formEditor']['addInlineSettings'] + ); + $this->view->assign('addInlineSettings', $addInlineSettings); + } + + /** + * Save a formDefinition which was build by the form editor. + * + * @param string $formPersistenceIdentifier + * @param array $formDefinition + * @return string + * @internal + */ + public function saveFormAction(string $formPersistenceIdentifier, array $formDefinition): string + { + $formDefinition = ArrayUtility::stripTagsFromValuesRecursive($formDefinition); + $formDefinition = $this->convertJsonArrayToAssociativeArray($formDefinition); + $this->formPersistenceManager->save($formPersistenceIdentifier, $formDefinition); + return ''; + } + + /** + * Render a page from the formDefinition which was build by the form editor. + * Use the frontend rendering and set the form framework to preview mode. + * + * @param array $formDefinition + * @param int $pageIndex + * @param string $prototypeName + * @return string + * @internal + */ + public function renderFormPageAction(array $formDefinition, int $pageIndex, string $prototypeName = null): string + { + $formDefinition = ArrayUtility::stripTagsFromValuesRecursive($formDefinition); + $formDefinition = $this->convertJsonArrayToAssociativeArray($formDefinition); + if (empty($prototypeName)) { + $prototypeName = isset($formDefinition['prototypeName']) ? $formDefinition['prototypeName'] : 'standard'; + } + + $formFactory = $this->objectManager->get(ArrayFormFactory::class); + $formDefinition = $formFactory->build($formDefinition, $prototypeName); + $formDefinition->setRenderingOption('previewMode', true); + $form = $formDefinition->bind($this->request, $this->response); + $form->overrideCurrentPage($pageIndex); + return $form->render(); + } + + /** + * Prepare the formElements.*.formEditor section from the yaml settings. + * Sort all formElements into groups and add additional data. + * + * @param array $formElementsDefinition + * @return array + */ + protected function getInsertRenderablesPanelConfiguration(array $formElementsDefinition): array + { + $formElementGroups = isset($this->prototypeConfiguration['formEditor']['formElementGroups']) ? $this->prototypeConfiguration['formEditor']['formElementGroups'] : []; + $formElementsByGroup = []; + + foreach ($formElementsDefinition as $formElementName => $formElementConfiguration) { + if (!isset($formElementConfiguration['group'])) { + continue; + } + if (!isset($formElementsByGroup[$formElementConfiguration['group']])) { + $formElementsByGroup[$formElementConfiguration['group']] = []; + } + + $formElementsByGroup[$formElementConfiguration['group']][] = [ + 'key' => $formElementName, + 'cssKey' => preg_replace('/[^a-z0-9]/', '-', strtolower($formElementName)), + 'label' => TranslationService::getInstance()->translate( + $formElementConfiguration['label'], + null, + $this->prototypeConfiguration['formEditor']['translationFile'], + null, + $formElementConfiguration['label'] + ), + 'sorting' => $formElementConfiguration['groupSorting'], + 'iconIdentifier' => $formElementConfiguration['iconIdentifier'], + ]; + } + + $formGroups = []; + foreach ($formElementGroups as $groupName => $groupConfiguration) { + if (!isset($formElementsByGroup[$groupName])) { + continue; + } + + usort($formElementsByGroup[$groupName], function ($a, $b) { + return $a['sorting'] - $b['sorting']; + }); + unset($formElementsByGroup[$groupName]['sorting']); + + $formGroups[] = [ + 'key' => $groupName, + 'elements' => $formElementsByGroup[$groupName], + 'label' => TranslationService::getInstance()->translate( + $groupConfiguration['label'], + null, + $this->prototypeConfiguration['formEditor']['translationFile'], + null, + $groupConfiguration['label'] + ), + ]; + } + + return $formGroups; + } + + /** + * Reduce the Yaml settings by the 'formEditor' keyword. + * + * @return array + */ + protected function getFormEditorDefinitions(): array + { + $formEditorDefinitions = []; + foreach ([$this->prototypeConfiguration, $this->prototypeConfiguration['formEditor']] as $configuration) { + foreach ($configuration as $firstLevelItemKey => $firstLevelItemValue) { + if (substr($firstLevelItemKey, -10) !== 'Definition') { + continue; + } + $reducedKey = substr($firstLevelItemKey, 0, -10); + foreach ($configuration[$firstLevelItemKey] as $formEditorDefinitionKey => $formEditorDefinitionValue) { + if (isset($formEditorDefinitionValue['formEditor'])) { + $formEditorDefinitionValue = array_intersect_key($formEditorDefinitionValue, array_flip(['formEditor'])); + $formEditorDefinitions[$reducedKey][$formEditorDefinitionKey] = $formEditorDefinitionValue['formEditor']; + } else { + $formEditorDefinitions[$reducedKey][$formEditorDefinitionKey] = $formEditorDefinitionValue; + } + } + } + } + $formEditorDefinitions = ArrayUtility::reIndexNumericArrayKeysRecursive($formEditorDefinitions); + $formEditorDefinitions = TranslationService::getInstance()->translateValuesRecursive( + $formEditorDefinitions, + $this->prototypeConfiguration['formEditor']['translationFile'] + ); + return $formEditorDefinitions; + } + + /** + * Registers the Icons into the docheader + * + * @throws \InvalidArgumentException + */ + protected function registerDocheaderButtons() + { + /** @var ButtonBar $buttonBar */ + $buttonBar = $this->view->getModuleTemplate()->getDocHeaderComponent()->getButtonBar(); + $getVars = $this->request->getArguments(); + + if (isset($getVars['action']) && $getVars['action'] === 'index') { + $newPageButton = $buttonBar->makeInputButton() + ->setDataAttributes(['action' => 'formeditor-new-page', 'identifier' => 'headerNewPage']) + ->setTitle($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.new_page_button')) + ->setName('formeditor-new-page') + ->setValue('new-page') + ->setClasses('t3-form-element-new-page-button hidden') + ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-page-new', Icon::SIZE_SMALL)); + + $closeButton = $buttonBar->makeLinkButton() + ->setDataAttributes(['identifier' => 'closeButton']) + ->setHref(BackendUtility::getModuleUrl('web_FormFormbuilder')) + ->setClasses('t3-form-element-close-form-button hidden') + ->setTitle($this->getLanguageService()->sL('LLL:EXT:lang/locallang_core.xlf:rm.closeDoc')) + ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-document-close', Icon::SIZE_SMALL)); + + $saveButton = $buttonBar->makeInputButton() + ->setDataAttributes(['identifier' => 'saveButton']) + ->setTitle($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.save_button')) + ->setName('formeditor-save-form') + ->setValue('save') + ->setClasses('t3-form-element-save-form-button hidden') + ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-document-save', Icon::SIZE_SMALL)) + ->setShowLabelText(true); + + $undoButton = $buttonBar->makeInputButton() + ->setDataAttributes(['identifier' => 'undoButton']) + ->setTitle($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.undo_button')) + ->setName('formeditor-undo-form') + ->setValue('undo') + ->setClasses('t3-form-element-undo-form-button hidden disabled') + ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-view-go-back', Icon::SIZE_SMALL)); + + $redoButton = $buttonBar->makeInputButton() + ->setDataAttributes(['identifier' => 'redoButton']) + ->setTitle($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.redo_button')) + ->setName('formeditor-redo-form') + ->setValue('redo') + ->setClasses('t3-form-element-redo-form-button hidden disabled') + ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-view-go-forward', Icon::SIZE_SMALL)); + + $buttonBar->addButton($newPageButton, ButtonBar::BUTTON_POSITION_LEFT, 1); + $buttonBar->addButton($closeButton, ButtonBar::BUTTON_POSITION_LEFT, 2); + $buttonBar->addButton($saveButton, ButtonBar::BUTTON_POSITION_LEFT, 3); + $buttonBar->addButton($undoButton, ButtonBar::BUTTON_POSITION_LEFT, 4); + $buttonBar->addButton($redoButton, ButtonBar::BUTTON_POSITION_LEFT, 4); + } + } + + /** + * Some data which is build by the form editor needs a transformation before + * it can be used by the framework. + * Multivalue elements like select elements produce data like: + * + * [ + * _label => 'label' + * _value => 'value' + * ] + * + * This method transform this into: + * + * [ + * 'value' => 'label' + * ] + * + * @param array $input + * @return array + */ + protected function convertJsonArrayToAssociativeArray(array $input): array + { + $output = []; + foreach ($input as $key => $value) { + if (is_integer($key) && is_array($value) && isset($value['_label']) && isset($value['_value'])) { + $key = $value['_value']; + $value = $value['_label']; + } + if (is_array($value)) { + $output[$key] = $this->convertJsonArrayToAssociativeArray($value); + } else { + $output[$key] = $value; + } + } + return $output; + } + + /** + * Render the "text/x-formeditor-template" templates. + * + * @param array $formEditorTemplates + * @param array $formEditorDefinitions + * @return array + */ + protected function renderFormEditorTemplates(array $formEditorTemplates, array $formEditorDefinitions): array + { + if ( + !isset($formEditorTemplates['templateRootPaths']) + || !is_array($formEditorTemplates['templateRootPaths']) + ) { + throw new RenderingException( + 'The option templateRootPaths must be set.', + 1480294720 + ); + } + if ( + !isset($formEditorTemplates['layoutRootPaths']) + || !is_array($formEditorTemplates['layoutRootPaths']) + ) { + throw new RenderingException( + 'The option layoutRootPaths must be set.', + 1480294721 + ); + } + if ( + !isset($formEditorTemplates['partialRootPaths']) + || !is_array($formEditorTemplates['partialRootPaths']) + ) { + throw new RenderingException( + 'The option partialRootPaths must be set.', + 1480294722 + ); + } + + $standaloneView = $this->objectManager->get(StandaloneView::class); + $standaloneView->setTemplateRootPaths($formEditorTemplates['templateRootPaths']); + $standaloneView->setLayoutRootPaths($formEditorTemplates['layoutRootPaths']); + $standaloneView->setPartialRootPaths($formEditorTemplates['partialRootPaths']); + $standaloneView->assignMultiple([ + 'insertRenderablesPanelConfiguration' => $this->getInsertRenderablesPanelConfiguration($formEditorDefinitions['formElements']) + ]); + + unset($formEditorTemplates['templateRootPaths']); + unset($formEditorTemplates['layoutRootPaths']); + unset($formEditorTemplates['partialRootPaths']); + + $renderedFormEditorTemplates = []; + foreach ($formEditorTemplates as $formEditorTemplateName => $formEditorTemplateTemplate) { + $renderedFormEditorTemplates[$formEditorTemplateName] = $standaloneView->render($formEditorTemplateTemplate); + } + + return $renderedFormEditorTemplates; + } + + /** + * Returns the current BE user. + * + * @return BackendUserAuthentication + */ + protected function getBackendUser(): BackendUserAuthentication + { + return $GLOBALS['BE_USER']; + } + + /** + * Returns the language service + * + * @return LanguageService + */ + protected function getLanguageService(): LanguageService + { + return $GLOBALS['LANG']; + } +} diff --git a/typo3/sysext/form/Classes/Controller/FormFrontendController.php b/typo3/sysext/form/Classes/Controller/FormFrontendController.php new file mode 100644 index 000000000000..41585604ce37 --- /dev/null +++ b/typo3/sysext/form/Classes/Controller/FormFrontendController.php @@ -0,0 +1,138 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Controller; + +/* + * 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\Core\Utility\ArrayUtility; +use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; +use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService; +use TYPO3\CMS\Form\Mvc\Configuration\TypoScriptService; + +/** + * The frontend controller + * + * Scope: frontend + */ +class FormFrontendController extends ActionController +{ + + /** + * @var \TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface + */ + protected $formPersistenceManager; + + /** + * @param \TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface $formPersistenceManager + * @return void + * @internal + */ + public function injectFormPersistenceManager(\TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface $formPersistenceManager) + { + $this->formPersistenceManager = $formPersistenceManager; + } + + /** + * Take the form which should be rendered from the plugin settings + * and overlay the formDefinition with additional data from + * flexform and typoscript settings. + * This method is used directly to display the first page from the + * formDefinition because its cached. + * + * @return void + * @internal + */ + public function renderAction() + { + $formDefinition = []; + if (!empty($this->settings['persistenceIdentifier'])) { + $formDefinition = $this->formPersistenceManager->load($this->settings['persistenceIdentifier']); + $formDefinition['persistenceIdentifier'] = $this->settings['persistenceIdentifier']; + $formDefinition = $this->overrideByFlexFormSettings($formDefinition); + $formDefinition = $this->overrideByTypoScriptSettings($formDefinition); + } + $this->view->assign('formConfiguration', $formDefinition); + } + + /** + * This method is used to display all pages / finishers except the + * first page because its non cached. + * + * @return void + * @internal + */ + public function performAction() + { + $this->forward('render'); + } + + /** + * Override the formDefinition with additional data from the Flexform + * settings. For now, only finisher settings are overridable. + * + * @param array $formDefinition + * @return array + */ + protected function overrideByFlexFormSettings(array $formDefinition): array + { + if (isset($formDefinition['finishers'])) { + foreach ($formDefinition['finishers'] as &$finisherValue) { + $finisherIdentifier = $finisherValue['identifier']; + if ($this->settings['overrideFinishers'] && isset($this->settings['finishers'][$finisherIdentifier])) { + $prototypeName = isset($formDefinition['prototypeName']) ? $formDefinition['prototypeName'] : 'standard'; + $configurationService = $this->objectManager->get(ConfigurationService::class); + $prototypeConfiguration = $configurationService->getPrototypeConfiguration($prototypeName); + + foreach ($finisherValue['options'] as $optionKey => $optionValue) { + // If a previous overriden finisher property is excluded at some time + // it is still present in the flexform database row. + // To avoid a override from the time the property is excluded, this check is needed + if (!isset($prototypeConfiguration['finishersDefinition'][$finisherIdentifier]['FormEngine']['elements'][$optionKey])) { + continue; + } + if (isset($this->settings['finishers'][$finisherIdentifier][$optionKey])) { + $finisherValue['options'][$optionKey] = $this->settings['finishers'][$finisherIdentifier][$optionKey]; + } + } + } + } + } + return $formDefinition; + } + + /** + * Every formDefinition setting are overridable by typoscript. + * If the typoscript configuration path + * plugin.tx_form.settings.formDefinitionOverrides.<identifier> + * exists, this settings are merged into the formDefinition. + * + * @param array $formDefinition + * @return array + */ + protected function overrideByTypoScriptSettings(array $formDefinition): array + { + if ( + isset($this->settings['formDefinitionOverrides'][$formDefinition['identifier']]) + && !empty($this->settings['formDefinitionOverrides'][$formDefinition['identifier']]) + ) { + ArrayUtility::mergeRecursiveWithOverrule( + $formDefinition, + $this->settings['formDefinitionOverrides'][$formDefinition['identifier']] + ); + $formDefinition = $this->objectManager->get(TypoScriptService::class) + ->resolvePossibleTypoScriptConfiguration($formDefinition); + } + return $formDefinition; + } +} diff --git a/typo3/sysext/form/Classes/Controller/FormManagerController.php b/typo3/sysext/form/Classes/Controller/FormManagerController.php new file mode 100644 index 000000000000..a45ada81cc56 --- /dev/null +++ b/typo3/sysext/form/Classes/Controller/FormManagerController.php @@ -0,0 +1,463 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Controller; + +/* + * 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 Symfony\Component\Yaml\Yaml; +use TYPO3\CMS\Backend\Template\Components\ButtonBar; +use TYPO3\CMS\Backend\Utility\BackendUtility; +use TYPO3\CMS\Backend\View\BackendTemplateView; +use TYPO3\CMS\Core\Authentication\BackendUserAuthentication; +use TYPO3\CMS\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Imaging\Icon; +use TYPO3\CMS\Core\Imaging\IconFactory; +use TYPO3\CMS\Core\Messaging\AbstractMessage; +use TYPO3\CMS\Core\Page\PageRenderer; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Mvc\View\JsonView; +use TYPO3\CMS\Form\Exception as FormException; +use TYPO3\CMS\Form\Service\TranslationService; +use TYPO3\CMS\Form\Utility\ArrayUtility; +use TYPO3\CMS\Lang\LanguageService; + +/** + * The form manager controller + * + * Scope: backend + */ +class FormManagerController extends AbstractBackendController +{ + + /** + * Default View Container + * + * @var BackendTemplateView + */ + protected $defaultViewObjectName = BackendTemplateView::class; + + /** + * Initialize the references action. + * This action use the Fluid JsonView::class as view. + * + * @return void + * @internal + */ + public function initializeReferencesAction() + { + $this->defaultViewObjectName = JsonView::class; + } + + /** + * Displays the Form Manager + * + * @return void + * @internal + */ + public function indexAction() + { + $this->registerDocheaderButtons(); + $this->view->getModuleTemplate()->setModuleName($this->request->getPluginName() . '_' . $this->request->getControllerName()); + $this->view->getModuleTemplate()->setFlashMessageQueue($this->controllerContext->getFlashMessageQueue()); + + $this->view->assign('forms', $this->getAvailableFormDefinitions()); + $this->view->assign('stylesheets', $this->resolveResourcePaths($this->formSettings['formManager']['stylesheets'])); + $this->view->assign('dynamicRequireJsModules', $this->formSettings['formManager']['dynamicRequireJsModules']); + $this->view->assign('formManagerAppInitialData', $this->getFormManagerAppInitialData()); + if (!empty($this->formSettings['formManager']['javaScriptTranslationFile'])) { + $this->getPageRenderer()->addInlineLanguageLabelFile($this->formSettings['formManager']['javaScriptTranslationFile']); + } + } + + /** + * Creates a new Form and redirects to the Form Editor + * + * @param string $formName + * @param string $templatePath + * @param string $prototypeName + * @param string $savePath + * @return string + * @throws FormException + * @internal + */ + public function createAction(string $formName, string $templatePath, string $prototypeName, string $savePath): string + { + if (!$this->isValidTemplatePath($prototypeName, $templatePath)) { + throw new FormException(sprintf('The template path "%s" is not allowed', $templatePath), 1329233410); + } + if (empty($formName)) { + throw new FormException(sprintf('No form name', $templatePath), 1472312204); + } + + $templatePath = GeneralUtility::getFileAbsFileName($templatePath); + $form = Yaml::parse(file_get_contents($templatePath)); + $form['label'] = $formName; + $form['identifier'] = $this->formPersistenceManager->getUniqueIdentifier($this->convertFormNameToIdentifier($formName)); + $form['prototypeName'] = $prototypeName; + + $formPersistenceIdentifier = $this->formPersistenceManager->getUniquePersistenceIdentifier($form['identifier'], $savePath); + $this->formPersistenceManager->save($formPersistenceIdentifier, $form); + + return $this->controllerContext->getUriBuilder()->uriFor('index', ['formPersistenceIdentifier' => $formPersistenceIdentifier], 'FormEditor'); + } + + /** + * Duplicates a given formDefinition and redirects to the Form Editor + * + * @param string $formName + * @param string $formPersistenceIdentifier persistence identifier of the form to duplicate + * @param string $savePath + * @return string + * @internal + */ + public function duplicateAction(string $formName, string $formPersistenceIdentifier, string $savePath): string + { + $formToDuplicate = $this->formPersistenceManager->load($formPersistenceIdentifier); + $formToDuplicate['label'] = $formName; + $formToDuplicate['identifier'] = $this->formPersistenceManager->getUniqueIdentifier($this->convertFormNameToIdentifier($formName)); + + $formPersistenceIdentifier = $this->formPersistenceManager->getUniquePersistenceIdentifier($formToDuplicate['identifier'], $savePath); + $this->formPersistenceManager->save($formPersistenceIdentifier, $formToDuplicate); + + return $this->controllerContext->getUriBuilder()->uriFor('index', ['formPersistenceIdentifier' => $formPersistenceIdentifier], 'FormEditor'); + } + + /** + * Show references to this persistence identifier + * + * @param string $formPersistenceIdentifier persistence identifier of the form to duplicate + * @return void + * @internal + */ + public function referencesAction(string $formPersistenceIdentifier) + { + $this->view->assign('references', $this->getProcessedReferencesRows($formPersistenceIdentifier)); + $this->view->assign('formPersistenceIdentifier', $formPersistenceIdentifier); + // referencesAction uses the extbase JsonView::class. + // That's why we have to set the view variables in this way. + $this->view->setVariablesToRender([ + 'references', + 'formPersistenceIdentifier' + ]); + } + + /** + * Delete a formDefinition identified by the $formPersistenceIdentifier. + * Only formDefinitions within storage folders are deletable. + * + * @param string $formPersistenceIdentifier persistence identifier to delete + * @return void + * @internal + */ + public function deleteAction(string $formPersistenceIdentifier) + { + if ( + empty($this->getReferences($formPersistenceIdentifier)) + && strpos($formPersistenceIdentifier, 'EXT:') === false + ) { + $this->formPersistenceManager->delete($formPersistenceIdentifier); + } else { + $this->addFlashMessage( + TranslationService::getInstance()->translate( + $this->formSettings['formManager']['controller']['deleteAction']['errorMessage'], + [$formPersistenceIdentifier], + $this->formSettings['formManager']['translationFile'], + null, + $this->formSettings['formManager']['controller']['deleteAction']['errorMessage'] + ), + TranslationService::getInstance()->translate( + $this->formSettings['formManager']['controller']['deleteAction']['errorTitle'], + null, + $this->formSettings['formManager']['translationFile'], + null, + $this->formSettings['formManager']['controller']['deleteAction']['errorTitle'] + ), + AbstractMessage::ERROR, + true + ); + } + $this->redirect('index'); + } + + /** + * Return a list of all accessible file mountpoints. + * + * Only registered mountpoints from + * TYPO3.CMS.Form.persistenceManager.allowedFileMounts + * are listet. This is list will be reduced by the configured + * mountpoints for the current backend user. + * + * @return array + */ + protected function getAccessibleFormStorageFolders(): array + { + $preparedAccessibleFormStorageFolders = []; + foreach ($this->formPersistenceManager->getAccessibleFormStorageFolders() as $identifier => $folder) { + $preparedAccessibleFormStorageFolders[] = [ + 'label' => $folder->getName(), + 'value' => $identifier + ]; + } + return $preparedAccessibleFormStorageFolders; + } + + /** + * Returns the json encoded data which is used by the form editor + * JavaScript app. + * + * @return string + */ + protected function getFormManagerAppInitialData(): string + { + $formManagerAppInitialData = [ + 'selectablePrototypesConfiguration' => $this->formSettings['formManager']['selectablePrototypesConfiguration'], + 'accessibleFormStorageFolders' => $this->getAccessibleFormStorageFolders(), + 'endpoints' => [ + 'create' => $this->controllerContext->getUriBuilder()->uriFor('create'), + 'duplicate' => $this->controllerContext->getUriBuilder()->uriFor('duplicate'), + 'delete' => $this->controllerContext->getUriBuilder()->uriFor('delete'), + 'references' => $this->controllerContext->getUriBuilder()->uriFor('references') + ], + ]; + + $formManagerAppInitialData = ArrayUtility::reIndexNumericArrayKeysRecursive($formManagerAppInitialData); + $formManagerAppInitialData = TranslationService::getInstance()->translateValuesRecursive( + $formManagerAppInitialData, + $this->formSettings['formManager']['translationFile'] + ); + return json_encode($formManagerAppInitialData); + } + + /** + * List all formDefinitions which can be loaded through t form persistence + * manager. Enrich this data by a reference counter. + * @return array + */ + protected function getAvailableFormDefinitions(): array + { + $availableFormDefinitions = []; + foreach ($this->formPersistenceManager->listForms() as $formDefinition) { + $referenceCount = count($this->getReferences($formDefinition['persistenceIdentifier'])); + $formDefinition['referenceCount'] = $referenceCount; + $availableFormDefinitions[] = $formDefinition; + } + return $availableFormDefinitions; + } + + /** + * Returns an array with informations about the references for a + * formDefinition identified by $persistenceIdentifier. + * + * @param string $persistenceIdentifier + * @return array + * @throws \InvalidArgumentException + */ + protected function getProcessedReferencesRows(string $persistenceIdentifier): array + { + if (empty($persistenceIdentifier)) { + throw new \InvalidArgumentException('$persistenceIdentifier must not be empty.', 1477071939); + } + + $references = []; + $iconFactory = GeneralUtility::makeInstance(IconFactory::class); + + $referenceRows = $this->getReferences($persistenceIdentifier); + foreach ($referenceRows as &$referenceRow) { + $record = $this->getBackendUtility()::getRecord($referenceRow['tablename'], $referenceRow['recuid']); + if (!$record) { + continue; + } + $pageRecord = $this->getBackendUtility()::getRecord('pages', $record['pid']); + $urlParameters = [ + 'edit' => [ + $referenceRow['tablename'] => [ + $referenceRow['recuid'] => 'edit' + ] + ], + 'returnUrl' => $this->getBackendUtility()::getModuleUrl('web_FormFormbuilder') + ]; + + $references[] = [ + 'recordPageTitle' => is_array($pageRecord) ? $this->getBackendUtility()::getRecordTitle('pages', $pageRecord) : '', + 'recordTitle' => $this->getBackendUtility()::getRecordTitle($referenceRow['tablename'], $record, true), + 'recordIcon' => $iconFactory->getIconForRecord($referenceRow['tablename'], $record, Icon::SIZE_SMALL)->render(), + 'recordUid' => $referenceRow['recuid'], + 'recordEditUrl' => $this->getBackendUtility()::getModuleUrl('record_edit', $urlParameters), + ]; + } + return $references; + } + + /** + * Returns an array with all sys_refindex database rows which be + * connected to a formDefinition identified by $persistenceIdentifier + * + * @param string $persistenceIdentifier + * @return array + * @throws \InvalidArgumentException + */ + protected function getReferences(string $persistenceIdentifier): array + { + if (empty($persistenceIdentifier)) { + throw new \InvalidArgumentException('$persistenceIdentifier must not be empty.', 1472238493); + } + + $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_refindex'); + $referenceRows = $queryBuilder + ->select('*') + ->from('sys_refindex') + ->where( + $queryBuilder->expr()->eq('deleted', 0), + $queryBuilder->expr()->eq('softref_key', $queryBuilder->createNamedParameter('formPersistenceIdentifier', \PDO::PARAM_STR)), + $queryBuilder->expr()->eq('ref_string', $queryBuilder->createNamedParameter($persistenceIdentifier, \PDO::PARAM_STR)), + $queryBuilder->expr()->eq('tablename', $queryBuilder->createNamedParameter('tt_content', \PDO::PARAM_STR)) + ) + ->execute() + ->fetchAll(); + return $referenceRows; + } + + /** + * Check if a given $templatePath for a given $prototypeName is valid + * and accessible. + * + * Valid template paths has to be configured within + * TYPO3.CMS.Form.formManager.selectablePrototypesConfiguration.[('identifier': $prototypeName)].newFormTemplates.[('templatePath': $templatePath)] + * + * @param string $prototypeName + * @param string $templatePath + * @return bool + */ + protected function isValidTemplatePath(string $prototypeName, string $templatePath): bool + { + $isValid = false; + foreach ($this->formSettings['formManager']['selectablePrototypesConfiguration'] as $prototypesConfiguration) { + if ($prototypesConfiguration['identifier'] !== $prototypeName) { + continue; + } + foreach ($prototypesConfiguration['newFormTemplates'] as $templatesConfiguration) { + if ($templatesConfiguration['templatePath'] !== $templatePath) { + continue; + } + $isValid = true; + break; + } + } + + $templatePath = GeneralUtility::getFileAbsFileName($templatePath); + if (!is_file($templatePath)) { + $isValid = false; + } + + return $isValid; + } + + /** + * Registers the Icons into the docheader + * + * @throws \InvalidArgumentException + */ + protected function registerDocheaderButtons() + { + /** @var ButtonBar $buttonBar */ + $buttonBar = $this->view->getModuleTemplate()->getDocHeaderComponent()->getButtonBar(); + $currentRequest = $this->request; + $moduleName = $currentRequest->getPluginName(); + $getVars = $this->request->getArguments(); + + $mayMakeShortcut = $this->getBackendUser()->mayMakeShortcut(); + if ($mayMakeShortcut) { + $extensionName = $currentRequest->getControllerExtensionName(); + if (count($getVars) === 0) { + $modulePrefix = strtolower('tx_' . $extensionName . '_' . $moduleName); + $getVars = ['id', 'M', $modulePrefix]; + } + + $shortcutButton = $buttonBar->makeShortcutButton() + ->setModuleName($moduleName) + ->setDisplayName($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:module.shortcut_name')) + ->setGetVariables($getVars); + $buttonBar->addButton($shortcutButton); + } + + if (isset($getVars['action']) && $getVars['action'] !== 'index') { + $backButton = $buttonBar->makeLinkButton() + ->setTitle($this->getLanguageService()->sL('LLL:EXT:lang/locallang_common.xlf:back')) + ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-view-go-up', Icon::SIZE_SMALL)) + ->setHref($this->getBackendUtility()::getModuleUrl($moduleName)); + $buttonBar->addButton($backButton); + } else { + $addFormButton = $buttonBar->makeLinkButton() + ->setDataAttributes(['identifier' => 'newForm']) + ->setHref('#') + ->setTitle($this->getLanguageService()->sL('LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.create_new_form')) + ->setIcon($this->view->getModuleTemplate()->getIconFactory()->getIcon('actions-document-new', Icon::SIZE_SMALL)); + $buttonBar->addButton($addFormButton, ButtonBar::BUTTON_POSITION_LEFT); + } + } + + /** + * Returns a form identifier which is the lower cased form name. + * + * @param string $formName + * @return string + */ + protected function convertFormNameToIdentifier(string $formName): string + { + $formIdentifier = preg_replace('/[^a-zA-Z0-9-_]/', '', $formName); + $formIdentifier = lcfirst($formIdentifier); + return $formIdentifier; + } + + /** + * Returns the BackendUtility + * This wrapper is needed for unit tests. + * + * @return string + */ + protected function getBackendUtility(): string + { + return BackendUtility::class; + } + + /** + * Returns the current BE user. + * + * @return BackendUserAuthentication + */ + protected function getBackendUser(): BackendUserAuthentication + { + return $GLOBALS['BE_USER']; + } + + /** + * Returns the Language Service + * + * @return LanguageService + */ + protected function getLanguageService(): LanguageService + { + return $GLOBALS['LANG']; + } + + /** + * Returns the page renderer + * + * @return PageRenderer + */ + protected function getPageRenderer(): PageRenderer + { + return GeneralUtility::makeInstance(PageRenderer::class); + } +} diff --git a/typo3/sysext/form/Classes/Controller/FrontendController.php b/typo3/sysext/form/Classes/Controller/FrontendController.php deleted file mode 100644 index 43fd2035ea93..000000000000 --- a/typo3/sysext/form/Classes/Controller/FrontendController.php +++ /dev/null @@ -1,342 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Controller; - -/* - * 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\Extbase\Mvc\Controller\ActionController; -use TYPO3\CMS\Extbase\Utility\LocalizationUtility; -use TYPO3\CMS\Form\Domain\Builder\FormBuilder; -use TYPO3\CMS\Form\Domain\Builder\ValidationBuilder; -use TYPO3\CMS\Form\Domain\Model\Configuration; -use TYPO3\CMS\Form\Domain\Model\ValidationElement; -use TYPO3\CMS\Form\Mvc\Controller\ControllerContext; -use TYPO3\CMS\Form\Utility\FormUtility; - -/** - * The form wizard controller - */ -class FrontendController extends ActionController -{ - /** - * @var FormBuilder - */ - protected $formBuilder; - - /** - * @var ValidationBuilder - */ - protected $validationBuilder; - - /** - * @var \TYPO3\CMS\Form\Utility\SessionUtility - */ - protected $sessionUtility; - - /** - * @var FormUtility - */ - protected $formUtility; - - /** - * The TypoScript array - * - * @var array - */ - protected $typoscript = []; - - /** - * TRUE if the validation of the form should be skipped - * - * @var bool - */ - protected $skipValidation = false; - - /** - * @var ControllerContext - */ - protected $controllerContext; - - /** - * @var Configuration - */ - protected $configuration; - - /** - * @param \TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility - * @return void - */ - public function injectSessionUtility(\TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility) - { - $this->sessionUtility = $sessionUtility; - } - - /** - * initialize action - * - * @return void - */ - protected function initializeAction() - { - $this->configuration = Configuration::create()->setTypoScript($this->settings['typoscript']); - $this->formUtility = FormUtility::create($this->configuration); - $this->validationBuilder = ValidationBuilder::create($this->configuration); - $this->validationBuilder->setFormUtility($this->formUtility); - $this->formBuilder = FormBuilder::create($this->configuration); - $this->formBuilder->setValidationBuilder($this->validationBuilder); - $this->formBuilder->setFormUtility($this->formUtility); - $this->typoscript = $this->settings['typoscript']; - - // uploaded file storage - $this->sessionUtility->initSession($this->configuration->getPrefix()); - // move the incoming "formPrefix" data to the $model argument - // now we can validate the $model argument - if ($this->request->hasArgument($this->configuration->getPrefix())) { - $this->skipValidation = false; - $argument = $this->request->getArgument($this->configuration->getPrefix()); - $this->request->setArgument('model', $argument); - } else { - // If there are more forms at a page we have to skip - // the validation of not submitted forms - $this->skipValidation = true; - $this->request->setArgument('model', []); - } - } - - /** - * initialize show action - * - * @return void - */ - protected function initializeShowAction() - { - $validationResults = $this->request->getOriginalRequestMappingResults()->forProperty('model'); - $this->validationBuilder->buildRules(); - if ($validationResults->hasErrors()) { - $this->formBuilder->setValidationErrors($validationResults); - } - } - - /** - * initialize the confirmation action - * - * @return void - */ - protected function initializeConfirmationAction() - { - $this->prepareValidations(); - } - - /** - * initialize the process action - * - * @return void - */ - protected function initializeProcessAction() - { - $this->prepareValidations(); - } - - /** - * Builds the controller context by extending - * the Extbase context with custom additions. - * - * @return ControllerContext - */ - protected function buildControllerContext() - { - $controllerContext = ControllerContext::extend(parent::buildControllerContext()) - ->setConfiguration($this->configuration); - $this->formBuilder->setControllerContext($controllerContext); - return $controllerContext; - } - - /** - * Handles show action, presenting the actual form. - * - * @param \TYPO3\CMS\Form\Domain\Model\ValidationElement $incomingData - * @ignorevalidation $incomingData - * @return void - */ - public function showAction(ValidationElement $incomingData = null) - { - if ($incomingData !== null) { - $this->controllerContext->setValidationElement($incomingData); - } - $form = $this->formBuilder->buildModel(); - $this->view->assign('model', $form); - } - - /** - * Handles confirmation action, presenting the user submitted - * data again for final confirmation. - * - * @param \TYPO3\CMS\Form\Domain\Model\ValidationElement $model - * @return void - */ - public function confirmationAction(ValidationElement $model) - { - $this->skipForeignFormProcessing(); - - if (count($model->getIncomingFields()) === 0) { - $this->sessionUtility->destroySession(); - $this->forward('show'); - } - $this->controllerContext->setValidationElement($model); - $form = $this->formBuilder->buildModel(); - // store uploaded files - $this->sessionUtility->storeSession(); - $this->view->assign('model', $form); - - $message = $this->formUtility->renderItem( - $this->typoscript['confirmation.']['message.'], - $this->typoscript['confirmation.']['message'], - LocalizationUtility::translate('tx_form_view_confirmation.message', 'form') - ); - $this->view->assign('message', $message); - } - - /** - * action dispatchConfirmationButtonClick - * - * @param \TYPO3\CMS\Form\Domain\Model\ValidationElement $model - * @return void - */ - public function dispatchConfirmationButtonClickAction(ValidationElement $model) - { - $this->skipForeignFormProcessing(); - - if ($this->request->hasArgument('confirmation-true')) { - $this->forward('process', null, null, [$this->configuration->getPrefix() => $this->request->getArgument('model')]); - } else { - $this->sessionUtility->destroySession(); - $this->forward('show', null, null, ['incomingData' => $this->request->getArgument('model')]); - } - } - - /** - * Handles process action, actually processing the user - * submitted data and forwarding it to post-processors - * (e.g. sending out mail messages). - * - * @param \TYPO3\CMS\Form\Domain\Model\ValidationElement $model - * @return void - */ - public function processAction(ValidationElement $model) - { - $this->skipForeignFormProcessing(); - - $this->controllerContext->setValidationElement($model); - $form = $this->formBuilder->buildModel(); - $postProcessorTypoScript = []; - if (isset($this->typoscript['postProcessor.'])) { - $postProcessorTypoScript = $this->typoscript['postProcessor.']; - } - - /** @var $postProcessor \TYPO3\CMS\Form\PostProcess\PostProcessor */ - $postProcessor = $this->objectManager->get( - \TYPO3\CMS\Form\PostProcess\PostProcessor::class, - $form, $postProcessorTypoScript - ); - $postProcessor->setControllerContext($this->controllerContext); - - // @todo What is happening here? - $content = $postProcessor->process(); - $this->sessionUtility->destroySession(); - $this->forward('afterProcess', null, null, ['postProcessorContent' => $content]); - } - - /** - * action after process - * - * @param string $postProcessorContent - * @return void - */ - public function afterProcessAction($postProcessorContent) - { - $this->view->assign('postProcessorContent', $postProcessorContent); - } - - /** - * Skip the processing of foreign forms. - * If there is more than one form on a page - * we have to be sure that only the submitted form will be - * processed. On data submission, the extbase action "confirmation" or - * "process" is called. The detection which form is submitted - * is done by the form prefix. All forms which do not have any - * submitted data are skipped and forwarded to the show action. - * - * @return void - */ - protected function skipForeignFormProcessing() - { - if ( - !$this->request->hasArgument($this->configuration->getPrefix()) - && !$this->sessionUtility->getSessionData() - ) { - $this->forward('show'); - } - } - - /** - * If the current form should be validated - * then set the dynamic validation - * - * @return void - */ - protected function prepareValidations() - { - if ($this->skipValidation || !$this->arguments->hasArgument('model')) { - return; - } - - $this->validationBuilder->buildRules($this->request->getArgument('model')); - $this->setDynamicValidation($this->validationBuilder->getRules()); - $this->skipValidation = false; - } - - /** - * Sets the dynamic validation rules. - * - * @param array $toValidate - * @throws \TYPO3\CMS\Extbase\Mvc\Exception\NoSuchArgumentException - * @throws \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException - */ - protected function setDynamicValidation(array $toValidate = []) - { - // build custom validation chain - /** @var \TYPO3\CMS\Extbase\Validation\ValidatorResolver $validatorResolver */ - $validatorResolver = $this->objectManager->get(\TYPO3\CMS\Extbase\Validation\ValidatorResolver::class); - - /** @var \TYPO3\CMS\Form\Domain\Validator\ValidationElementValidator $modelValidator */ - $modelValidator = $validatorResolver->createValidator(\TYPO3\CMS\Form\Domain\Validator\ValidationElementValidator::class); - foreach ($toValidate as $propertyName => $validations) { - foreach ($validations as $validation) { - if (empty($validation['validator'])) { - throw new \TYPO3\CMS\Extbase\Validation\Exception\NoSuchValidatorException('Invalid validate configuration for ' . $propertyName . ': Could not resolve class name for validator "' . $validation['validatorName'] . '".', 1441893777); - } - $modelValidator->addPropertyValidator($propertyName, $validation['validator']); - } - } - - if ($modelValidator->countPropertyValidators()) { - /** @var \TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator $baseConjunctionValidator */ - $baseConjunctionValidator = $this->arguments->getArgument('model')->getValidator(); - if ($baseConjunctionValidator === null) { - $baseConjunctionValidator = $validatorResolver->createValidator(\TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator::class); - $this->arguments->getArgument('model')->setValidator($baseConjunctionValidator); - } - $baseConjunctionValidator->addValidator($modelValidator); - } - } -} diff --git a/typo3/sysext/form/Classes/Controller/WizardController.php b/typo3/sysext/form/Classes/Controller/WizardController.php deleted file mode 100644 index e7185dc80f9a..000000000000 --- a/typo3/sysext/form/Classes/Controller/WizardController.php +++ /dev/null @@ -1,92 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Controller; - -/* - * 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 Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Form\Domain\Repository\ContentRepository; - -/** - * The form wizard controller - */ -class WizardController -{ - /** - * The constructor to load the LL file - */ - public function __construct() - { - $this->getLanguageService()->includeLLFile('EXT:form/Resources/Private/Language/locallang_wizard.xlf'); - } - - /** - * The save action called via AJAX - * - * The action which should be taken when the form in the wizard is saved - * - * @param ServerRequestInterface $request - * @param ResponseInterface $response - * @return ResponseInterface returns a 500 error or a valid JSON response - */ - public function saveAction(ServerRequestInterface $request, ResponseInterface $response) - { - $repository = $this->getRepository(); - $typoscript = ''; - $jsonArray = []; - - // Check if the referenced record is available - if ($repository->hasRecord()) { - // Save the data - $typoscript = $repository->save(); - } - - if (!$typoscript) { - $response = $response->withStatus(500); - $message = $this->getLanguageService()->getLL('action_save_message_failed', false); - } else { - $message = $this->getLanguageService()->getLL('action_save_message_saved', false); - $jsonArray['fakeTs'] = $typoscript; - } - - $jsonArray['message'] = $message; - $response->getBody()->write(json_encode($jsonArray)); - return $response - ->withHeader('Expires', 'Mon, 26 Jul 1997 05:00:00 GMT') - ->withHeader('Last-Modified', gmdate('D, d M Y H:i:s') . ' GMT') - ->withHeader('Cache-Control', 'no-cache, must-revalidate') - ->withHeader('Pragma', 'no-cache'); - } - - /** - * Gets the repository object. - * - * @return ContentRepository - */ - protected function getRepository() - { - return GeneralUtility::makeInstance(ContentRepository::class); - } - - /** - * Returns an instance of LanguageService - * - * @return \TYPO3\CMS\Lang\LanguageService - */ - protected function getLanguageService() - { - return $GLOBALS['LANG']; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Builder/ElementBuilder.php b/typo3/sysext/form/Classes/Domain/Builder/ElementBuilder.php deleted file mode 100644 index c2b0023ff4fa..000000000000 --- a/typo3/sysext/form/Classes/Domain/Builder/ElementBuilder.php +++ /dev/null @@ -1,510 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Builder; - -/* - * 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\Core\Utility\MathUtility; -use TYPO3\CMS\Form\Domain\Model\Element; - -/** - * Builder for Element domain models. - */ -class ElementBuilder -{ - /** - * @param FormBuilder $formBuilder - * @param Element $element - * @param array $userDefinedTypoScript - * @return ElementBuilder - */ - public static function create(FormBuilder $formBuilder, Element $element, array $userDefinedTypoScript) - { - /** @var ElementBuilder $elementBuilder */ - $elementBuilder = \TYPO3\CMS\Form\Utility\FormUtility::getObjectManager()->get(self::class); - $elementBuilder->setFormBuilder($formBuilder); - $elementBuilder->setElement($element); - $elementBuilder->setUserConfiguredElementTyposcript($userDefinedTypoScript); - return $elementBuilder; - } - - /** - * @var \TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository - */ - protected $typoScriptRepository; - - /** - * @var \TYPO3\CMS\Extbase\Service\TypoScriptService - */ - protected $typoScriptService; - - /** - * @var array - */ - protected $userConfiguredElementTyposcript = []; - - /** - * @var array - */ - protected $htmlAttributes = []; - - /** - * @var array - */ - protected $additionalArguments = []; - - /** - * @var array - */ - protected $wildcardPrefixes = []; - - /** - * @var FormBuilder - */ - protected $formBuilder; - - /** - * @var Element - */ - protected $element; - - /** - * @param \TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository - * @return void - */ - public function injectTypoScriptRepository(\TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository) - { - $this->typoScriptRepository = $typoScriptRepository; - } - - /** - * @param \TYPO3\CMS\Extbase\Service\TypoScriptService $typoScriptService - * @return void - */ - public function injectTypoScriptService(\TYPO3\CMS\Extbase\Service\TypoScriptService $typoScriptService) - { - $this->typoScriptService = $typoScriptService; - } - - /** - * @param FormBuilder $formBuilder - */ - public function setFormBuilder(FormBuilder $formBuilder) - { - $this->formBuilder = $formBuilder; - } - - /** - * @param Element $element - */ - public function setElement(Element $element) - { - $this->element = $element; - } - - /** - * Set the fluid partial path to the element - * - * @return void - */ - public function setPartialPaths() - { - $this->setElementPartialPath(); - } - - /** - * Set the fluid partial path to the element - * - * @return void - */ - protected function setElementPartialPath() - { - if (!isset($this->userConfiguredElementTyposcript['partialPath'])) { - $partialPath = $this->typoScriptRepository->getDefaultFluidTemplate($this->element->getElementType()); - } else { - $partialPath = $this->userConfiguredElementTyposcript['partialPath']; - unset($this->userConfiguredElementTyposcript['partialPath']); - } - $this->element->setPartialPath($partialPath); - } - - /** - * Set the fluid partial path to the element - * - * @return void - */ - public function setVisibility() - { - $visibility = false; - if ($this->formBuilder->getControllerAction() === 'show') { - if (!isset($this->userConfiguredElementTyposcript['visibleInShowAction'])) { - $visibility = (bool)$this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'visibleInShowAction'); - } else { - $visibility = (bool)$this->userConfiguredElementTyposcript['visibleInShowAction']; - } - } elseif ($this->formBuilder->getControllerAction() === 'confirmation') { - if (!isset($this->userConfiguredElementTyposcript['visibleInConfirmationAction'])) { - $visibility = (bool)$this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'visibleInConfirmationAction'); - } else { - $visibility = (bool)$this->userConfiguredElementTyposcript['visibleInConfirmationAction']; - } - } elseif ($this->formBuilder->getControllerAction() === 'process') { - if (!isset($this->userConfiguredElementTyposcript['visibleInMail'])) { - $visibility = (bool)$this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'visibleInMail'); - } else { - $visibility = (bool)$this->userConfiguredElementTyposcript['visibleInMail']; - } - } - $this->element->setShowElement($visibility); - } - - /** - * Find all prefix-* attributes and return the - * found prefixs. Than delete them from the htmlAttributes array - * - * @return void - */ - public function setHtmlAttributeWildcards() - { - foreach ($this->htmlAttributes as $attributeName => $attributeValue) { - if (strpos($attributeName, '-*') > 0) { - $prefix = substr($attributeName, 0, -1); - $this->wildcardPrefixes[] = $prefix; - unset($this->htmlAttributes[$attributeName]); - } - } - } - - /** - * Overlay user defined html attribute values - * To determine whats a html attribute, the htmlAttributes - * array is used. If a html attribute value is found in userConfiguredElementTyposcript - * this value is set to htmlAttributes and removed from userConfiguredElementTyposcript. - * - * @return void - */ - public function overlayUserdefinedHtmlAttributeValues() - { - foreach ($this->htmlAttributes as $attributeName => $attributeValue) { - $attributeNameWithoutDot = rtrim($attributeName, '.'); - $attributeNameToSet = $attributeNameWithoutDot; - - if ($this->arrayKeyExists($attributeNameWithoutDot, $this->userConfiguredElementTyposcript)) { - $attributeValue = $this->formBuilder->getFormUtility()->renderItem( - $this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.'], - $this->userConfiguredElementTyposcript[$attributeNameWithoutDot] - ); - $this->htmlAttributes[$attributeNameToSet] = $attributeValue; - } - - unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.']); - unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot]); - } - - // the prefix-* magic - $ignoreKeys = []; - foreach ($this->userConfiguredElementTyposcript as $attributeName => $attributeValue) { - // ignore child elements - if ( - MathUtility::canBeInterpretedAsInteger($attributeName) - || isset($ignoreKeys[$attributeName]) - ) { - $ignoreKeys[$attributeName . '.'] = true; - continue; - } - - foreach ($this->wildcardPrefixes as $wildcardPrefix) { - if (strpos($attributeName, $wildcardPrefix) !== 0) { - continue; - } - $attributeNameWithoutDot = rtrim($attributeName, '.'); - $attributeValue = $this->formBuilder->getFormUtility()->renderItem( - $this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.'], - $this->userConfiguredElementTyposcript[$attributeNameWithoutDot] - ); - $this->htmlAttributes[$attributeNameWithoutDot] = $attributeValue; - $ignoreKeys[$attributeNameWithoutDot . '.'] = true; - unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.']); - unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot]); - break; - } - } - } - - /** - * If fixedHtmlAttributeValues are defined for this element - * then overwrite the html attribute value - * - * @return void - */ - public function overlayFixedHtmlAttributeValues() - { - $fixedHtmlAttributeValues = $this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'fixedHtmlAttributeValues.'); - if (is_array($fixedHtmlAttributeValues)) { - foreach ($fixedHtmlAttributeValues as $attributeName => $attributeValue) { - $this->htmlAttributes[$attributeName] = $attributeValue; - } - } - } - - /** - * Move htmlAttributes to additionalArguments that must be passed - * as a view helper argument - * - * @return void - */ - public function moveHtmlAttributesToAdditionalArguments() - { - $htmlAttributesUsedByTheViewHelperDirectly = $this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'htmlAttributesUsedByTheViewHelperDirectly.'); - if (is_array($htmlAttributesUsedByTheViewHelperDirectly)) { - foreach ($htmlAttributesUsedByTheViewHelperDirectly as $attributeName) { - if (array_key_exists($attributeName, $this->htmlAttributes)) { - $this->additionalArguments[$attributeName] = $this->htmlAttributes[$attributeName]; - unset($this->htmlAttributes[$attributeName]); - } - } - } - } - - /** - * Set the viewhelper default arguments in the additionalArguments array - * - * @return void - */ - public function setViewHelperDefaulArgumentsToAdditionalArguments() - { - $viewHelperDefaultArguments = $this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'viewHelperDefaultArguments.'); - if (is_array($viewHelperDefaultArguments)) { - foreach ($viewHelperDefaultArguments as $viewHelperDefaulArgumentName => $viewHelperDefaulArgumentValue) { - $viewHelperDefaulArgumentNameWithoutDot = rtrim($viewHelperDefaulArgumentName, '.'); - $this->additionalArguments[$viewHelperDefaulArgumentNameWithoutDot] = $viewHelperDefaulArgumentValue; - } - } - unset($this->userConfiguredElementTyposcript['viewHelperDefaultArguments']); - } - - /** - * Move all userdefined properties to the additionalArguments - * array. Ignore the child elements - * - * @return void - */ - public function moveAllOtherUserdefinedPropertiesToAdditionalArguments() - { - $viewHelperDefaultArguments = $this->typoScriptRepository->getModelConfigurationByScope($this->element->getElementType(), 'viewHelperDefaultArguments.'); - $ignoreKeys = []; - - foreach ($this->userConfiguredElementTyposcript as $attributeName => $attributeValue) { - // ignore child elements - if ( - MathUtility::canBeInterpretedAsInteger($attributeName) - || isset($ignoreKeys[$attributeName]) - || $attributeName == 'postProcessor.' - || $attributeName == 'rules.' - || $attributeName == 'filters.' - || $attributeName == 'layout' - ) { - $ignoreKeys[$attributeName . '.'] = true; - continue; - } - $attributeNameWithoutDot = rtrim($attributeName, '.'); - $attributeNameToSet = $attributeNameWithoutDot; - - if ( - isset($viewHelperDefaultArguments[$attributeName]) - || isset($viewHelperDefaultArguments[$attributeNameWithoutDot]) - ) { - $this->formBuilder->getFormUtility()->renderArrayItems($attributeValue); - $attributeValue = $this->typoScriptService->convertTypoScriptArrayToPlainArray($attributeValue); - } else { - $attributeValue = $this->formBuilder->getFormUtility()->renderItem( - $this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.'], - $this->userConfiguredElementTyposcript[$attributeNameWithoutDot] - ); - } - $this->additionalArguments[$attributeNameToSet] = $attributeValue; - $ignoreKeys[$attributeNameToSet . '.'] = true; - $ignoreKeys[$attributeNameToSet] = true; - - unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot . '.']); - unset($this->userConfiguredElementTyposcript[$attributeNameWithoutDot]); - } - // remove "stdWrap." from "additionalArguments" on - // the FORM Element - if ( - !$this->formBuilder->getConfiguration()->getContentElementRendering() - && $this->element->getElementType() == 'FORM' - ) { - unset($this->additionalArguments['stdWrap']); - unset($this->additionalArguments['stdWrap.']); - } - } - - /** - * Set the name and id attribute - * - * @return array - */ - public function setNameAndId() - { - if ( - $this->element->getParentElement() - && (int)$this->typoScriptRepository->getModelConfigurationByScope($this->element->getParentElement()->getElementType(), 'childrenInheritName') == 1 - ) { - $this->htmlAttributes['name'] = $this->element->getParentElement()->getName(); - $this->additionalArguments['multiple'] = '1'; - $name = $this->sanitizeNameAttribute($this->userConfiguredElementTyposcript['name']); - $this->element->setName($name); - } else { - $this->htmlAttributes['name'] = $this->sanitizeNameAttribute($this->htmlAttributes['name']); - $this->element->setName($this->htmlAttributes['name']); - } - $this->htmlAttributes['id'] = $this->sanitizeIdAttribute($this->htmlAttributes['id']); - $this->element->setId($this->htmlAttributes['id']); - } - - /** - * If the name is not defined it is automatically generated - * using the following syntax: id-{element_counter} - * The name attribute will be transformed if it contains some - * non allowed characters: - * - spaces are changed into hyphens - * - remove all characters except a-z A-Z 0-9 _ - - * - * @param string $name - * @return string - */ - public function sanitizeNameAttribute($name) - { - $name = $this->formBuilder->getFormUtility()->sanitizeNameAttribute($name); - if (empty($name)) { - $name = 'id-' . $this->element->getElementCounter(); - } - return $name; - } - - /** - * If the id is not defined it is automatically generated - * using the following syntax: field-{element_counter} - * The id attribute will be transformed if it contains some - * non allowed characters: - * - spaces are changed into hyphens - * - if the id start with a integer then transform it to field-{integer} - * - remove all characters expect a-z A-Z 0-9 _ - : . - * - * @param string $id - * @return string - */ - protected function sanitizeIdAttribute($id) - { - $id = $this->formBuilder->getFormUtility()->sanitizeIdAttribute($id); - if (empty($id)) { - $id = 'field-' . $this->element->getElementCounter(); - } - return $id; - } - - /** - * Check if a needle exists in a array. - * - * @param string $needle - * @param array $haystack - * @return bool TRUE if found - */ - protected function arrayKeyExists($needle, array $haystack = []) - { - return - isset($haystack[$needle]) || isset($haystack[$needle . '.']) - ; - } - - /** - * Get the current html attributes - * - * @return array - */ - public function getHtmlAttributes() - { - return $this->htmlAttributes; - } - - /** - * Set the current html attributes - * - * @param array $htmlAttributes - */ - public function setHtmlAttributes(array $htmlAttributes) - { - $this->htmlAttributes = $htmlAttributes; - } - - /** - * Get the current additional arguments - * - * @return array - */ - public function getAdditionalArguments() - { - return $this->additionalArguments; - } - - /** - * Set the current additional arguments - * - * @param array $additionalArguments - */ - public function setAdditionalArguments(array $additionalArguments) - { - $this->additionalArguments = $additionalArguments; - } - - /** - * Get the current wildcard prefixes - * - * @return array - */ - public function getWildcardPrefixes() - { - return $this->wildcardPrefixes; - } - - /** - * Set the current wildcard prefixes - * - * @param array $wildcardPrefixes - */ - public function setWildcardPrefixes(array $wildcardPrefixes) - { - $this->wildcardPrefixes = $wildcardPrefixes; - } - - /** - * Get the current Element - * - * @return array - */ - public function getUserConfiguredElementTyposcript() - { - return $this->userConfiguredElementTyposcript; - } - - /** - * Set the current Element - * - * @param array $userConfiguredElementTyposcript - */ - public function setUserConfiguredElementTyposcript(array $userConfiguredElementTyposcript) - { - $this->userConfiguredElementTyposcript = $userConfiguredElementTyposcript; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Builder/FormBuilder.php b/typo3/sysext/form/Classes/Domain/Builder/FormBuilder.php deleted file mode 100644 index 9f4ddac0ba68..000000000000 --- a/typo3/sysext/form/Classes/Domain/Builder/FormBuilder.php +++ /dev/null @@ -1,579 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Builder; - -/* - * 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\Core\Utility\ArrayUtility; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Form\Domain\Model\Configuration; -use TYPO3\CMS\Form\Domain\Model\Element; -use TYPO3\CMS\Form\Domain\Model\ValidationElement; -use TYPO3\CMS\Form\Mvc\Controller\ControllerContext; -use TYPO3\CMS\Form\Utility\FormUtility; - -/** - * TypoScript factory for form - * - * Takes the incoming TypoScript and adds all the necessary form objects - * according to the configuration. - */ -class FormBuilder -{ - /** - * @param Configuration $configuration - * @return FormBuilder - */ - public static function create(Configuration $configuration) - { - /** @var FormBuilder $formBuilder */ - $formBuilder = \TYPO3\CMS\Form\Utility\FormUtility::getObjectManager()->get(self::class); - $formBuilder->setConfiguration($configuration); - return $formBuilder; - } - - /** - * @var FormUtility - */ - protected $formUtility; - - /** - * @var \TYPO3\CMS\Extbase\Service\TypoScriptService - */ - protected $typoScriptService; - - /** - * @var ValidationBuilder - */ - protected $validationBuilder; - - /** - * @var \TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository - */ - protected $typoScriptRepository; - - /** - * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher - */ - protected $signalSlotDispatcher; - - /** - * @var \TYPO3\CMS\Form\Utility\SessionUtility - */ - protected $sessionUtility; - - /** - * @var \TYPO3\CMS\Extbase\Object\ObjectManager - */ - protected $objectManager; - - /** - * @var \TYPO3\CMS\Form\Utility\ElementCounter - */ - protected $elementCounter; - - /** - * @var NULL|\TYPO3\CMS\Extbase\Error\Result - */ - protected $validationErrors = null; - - /** - * @var Configuration; - */ - protected $configuration; - - /** - * @var ControllerContext - */ - protected $controllerContext; - - /** - * @param \TYPO3\CMS\Extbase\Service\TypoScriptService $typoScriptService - * @return void - */ - public function injectTypoScriptService(\TYPO3\CMS\Extbase\Service\TypoScriptService $typoScriptService) - { - $this->typoScriptService = $typoScriptService; - } - - /** - * @param \TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository - * @return void - */ - public function injectTypoScriptRepository(\TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository) - { - $this->typoScriptRepository = $typoScriptRepository; - } - - /** - * @param \TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher - * @return void - */ - public function injectSignalSlotDispatcher(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher $signalSlotDispatcher) - { - $this->signalSlotDispatcher = $signalSlotDispatcher; - } - - /** - * @param \TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility - * @return void - */ - public function injectSessionUtility(\TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility) - { - $this->sessionUtility = $sessionUtility; - } - - /** - * @param \TYPO3\CMS\Form\Utility\ElementCounter $elementCounter - * @return void - */ - public function injectElementCounter(\TYPO3\CMS\Form\Utility\ElementCounter $elementCounter) - { - $this->elementCounter = $elementCounter; - } - - /** - * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager - * @return void - */ - public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) - { - $this->objectManager = $objectManager; - } - - /** - * @return Configuration - */ - public function getConfiguration() - { - return $this->configuration; - } - - /** - * @param Configuration $configuration - */ - public function setConfiguration(Configuration $configuration) - { - $this->configuration = $configuration; - } - - /** - * @return ControllerContext - */ - public function getControllerContext() - { - return $this->controllerContext; - } - - /** - * @param ControllerContext $controllerContext - */ - public function setControllerContext(ControllerContext $controllerContext) - { - $this->controllerContext = $controllerContext; - } - - /** - * @return FormUtility - */ - public function getFormUtility() - { - return $this->formUtility; - } - - /** - * @param FormUtility $formUtility - */ - public function setFormUtility(FormUtility $formUtility) - { - $this->formUtility = $formUtility; - } - - /** - * @return ValidationBuilder - */ - public function getValidationBuilder() - { - return $this->validationBuilder; - } - - /** - * @param ValidationBuilder $validationBuilder - */ - public function setValidationBuilder(ValidationBuilder $validationBuilder) - { - $this->validationBuilder = $validationBuilder; - } - - /** - * Build model from TypoScript - * Needed if more than one form exist at a page - * - * @return NULL|\TYPO3\CMS\Form\Domain\Model\Element The form object containing the child elements - */ - public function buildModel() - { - $userConfiguredFormTypoScript = $this->configuration->getTypoScript(); - $form = $this->createElementObject(); - $this->reviveElement($form, $userConfiguredFormTypoScript, 'FORM'); - $form->setThemeName($this->configuration->getThemeName()); - return $form; - } - - /** - * Create a element - * - * @return \TYPO3\CMS\Form\Domain\Model\Element - */ - protected function createElementObject() - { - $element = GeneralUtility::makeInstance(Element::class); - return $element; - } - - /** - * Revive the domain model of the accordant element. - * - * @param Element $element - * @param array $userConfiguredElementTypoScript The configuration array - * @param string $elementType The element type (e.g BUTTON) - * @return void - */ - protected function reviveElement(Element $element, array $userConfiguredElementTypoScript, $elementType = '') - { - // @todo Check $userConfiguredElementTypoScript - - $element->setElementType($elementType); - $element->setElementCounter($this->elementCounter->getElementId()); - - $elementBuilder = ElementBuilder::create($this, $element, $userConfiguredElementTypoScript); - $elementBuilder->setPartialPaths(); - $elementBuilder->setVisibility(); - - if ($element->getElementType() == 'CONTENTELEMENT') { - $attributeValue = ''; - if ($this->configuration->getContentElementRendering()) { - $attributeValue = $this->formUtility->renderItem( - $userConfiguredElementTypoScript['cObj.'], - $userConfiguredElementTypoScript['cObj'] - ); - } - $element->setAdditionalArguments([ - 'content' => $attributeValue, - ]); - } else { - $this->setAttributes($elementBuilder, $element, $userConfiguredElementTypoScript); - $userConfiguredElementTypoScript = $elementBuilder->getUserConfiguredElementTyposcript(); - $this->setValidationMessages($element); - - $this->signalSlotDispatcher->dispatch( - __CLASS__, - 'txFormAfterElementCreation', - [$element, $this] - ); - // create all child elements - $this->setChildElementsByIntegerKey($element, $userConfiguredElementTypoScript); - } - } - - /** - * Rendering of a "numerical array" of Form objects from TypoScript - * Creates new object for each element found - * - * @param Element $element - * @param array $userConfiguredElementTypoScript The configuration array - * @return void - * @throws \InvalidArgumentException - */ - protected function setChildElementsByIntegerKey(Element $element, array $userConfiguredElementTypoScript) - { - if (is_array($userConfiguredElementTypoScript)) { - $keys = ArrayUtility::filterAndSortByNumericKeys($userConfiguredElementTypoScript); - foreach ($keys as $key) { - if ( - (int)$key - && strpos($key, '.') === false - ) { - $elementType = $userConfiguredElementTypoScript[$key]; - if (isset($userConfiguredElementTypoScript[$key . '.'])) { - $concreteChildElementTypoScript = $userConfiguredElementTypoScript[$key . '.']; - } else { - $concreteChildElementTypoScript = []; - } - $this->distinguishElementType($element, $concreteChildElementTypoScript, $elementType); - } - } - } else { - throw new \InvalidArgumentException('Container element with id=' . $element->getElementCounter() . ' has no configuration which means no children.', 1333754854); - } - } - - /** - * Create and add element by type. - * If its not a registered form element - * try to render it as contentelement with the internal elementType - * CONTENTELEMENT - * - * @param Element $element - * @param array $userConfiguredElementTypoScript The configuration array - * @param string $elementType The element type (e.g BUTTON) - * @return void - */ - protected function distinguishElementType(Element $element, array $userConfiguredElementTypoScript, $elementType = '') - { - if (in_array($elementType, $this->typoScriptRepository->getRegisteredElementTypes())) { - $this->addChildElement($element, $userConfiguredElementTypoScript, $elementType); - } elseif ($this->configuration->getContentElementRendering()) { - $contentObject = [ - 'cObj' => $elementType, - 'cObj.' => $userConfiguredElementTypoScript - ]; - $this->addChildElement($element, $contentObject, 'CONTENTELEMENT'); - } - } - - /** - * Add child object to this element - * - * @param Element $element - * @param array $userConfiguredElementTypoScript The configuration array - * @param string $elementType The element type (e.g BUTTON) - * @return void - */ - protected function addChildElement(Element $element, array $userConfiguredElementTypoScript, $elementType = '') - { - $childElement = $this->createElementObject(); - $childElement->setParentElement($element); - $element->addChildElement($childElement); - $this->reviveElement($childElement, $userConfiguredElementTypoScript, $elementType); - } - - /** - * Set the htmlAttributes and the additionalAttributes - * Remap htmlAttributes to additionalAttributes if needed - * - * @param ElementBuilder $elementBuilder - * @param Element $element - * @return void - */ - protected function setAttributes(ElementBuilder $elementBuilder, Element $element) - { - $htmlAttributes = $this->typoScriptRepository->getModelDefinedHtmlAttributes($element->getElementType()); - $elementBuilder->setHtmlAttributes($htmlAttributes); - $elementBuilder->setHtmlAttributeWildcards(); - $elementBuilder->overlayUserdefinedHtmlAttributeValues(); - $elementBuilder->setNameAndId(); - $elementBuilder->overlayFixedHtmlAttributeValues(); - // remove all NULL values - $htmlAttributes = array_filter($elementBuilder->getHtmlAttributes()); - - $elementBuilder->setHtmlAttributes($htmlAttributes); - $elementBuilder->moveHtmlAttributesToAdditionalArguments(); - $elementBuilder->setViewHelperDefaulArgumentsToAdditionalArguments(); - $elementBuilder->moveAllOtherUserdefinedPropertiesToAdditionalArguments(); - $htmlAttributes = $elementBuilder->getHtmlAttributes(); - $userConfiguredElementTypoScript = $elementBuilder->getUserConfiguredElementTyposcript(); - $additionalArguments = $elementBuilder->getAdditionalArguments(); - $element->setHtmlAttributes($htmlAttributes); - $additionalArguments = $this->typoScriptService->convertTypoScriptArrayToPlainArray($additionalArguments); - $additionalArguments['prefix'] = $this->configuration->getPrefix(); - $element->setAdditionalArguments($additionalArguments); - $this->handleIncomingValues($element, $userConfiguredElementTypoScript); - - if ( - $element->getElementType() === 'FORM' - && $this->getControllerAction() === 'show' - ) { - if (empty($element->getHtmlAttribute('action'))) { - if ( - $element->getAdditionalArgument('confirmation') - && (int)$element->getAdditionalArgument('confirmation') === 1 - ) { - $element->setAdditionalArgument('action', 'confirmation'); - } else { - $element->setAdditionalArgument('action', 'process'); - } - } else { - $element->setAdditionalArgument('pageUid', $element->getHtmlAttribute('action')); - $element->setAdditionalArgument('action', null); - } - } - - // needed if confirmation page is enabled - if ( - $this->sessionUtility->getSessionData($element->getName()) - && $element->getAdditionalArgument('uploadedFiles') === null - ) { - $element->setAdditionalArgument('uploadedFiles', $this->sessionUtility->getSessionData($element->getName())); - } - } - - /** - * Handles the incoming form data - * - * @param Element $element - * @param array $userConfiguredElementTypoScript - * @return array - */ - protected function handleIncomingValues(Element $element, array $userConfiguredElementTypoScript) - { - if (!$this->getIncomingData()) { - return; - } - $elementName = $element->getName(); - if ($element->getHtmlAttribute('value') !== null) { - $modelValue = $element->getHtmlAttribute('value'); - } else { - $modelValue = $element->getAdditionalArgument('value'); - } - - if ($this->getIncomingData()->getIncomingField($elementName) !== null) { - /* filter values and set it back to incoming fields */ - $filters = isset($userConfiguredElementTypoScript['filters.']) ? $userConfiguredElementTypoScript['filters.'] : []; - if (!empty($filters)) { - $keys = ArrayUtility::filterAndSortByNumericKeys($filters); - foreach ($keys as $key) { - $class = $userConfiguredElementTypoScript['filters.'][$key]; - if ( - (int)$key - && strpos($key, '.') === false - ) { - $filterArguments = $userConfiguredElementTypoScript['filters.'][$key . '.']; - $filterClassName = $this->typoScriptRepository->getRegisteredClassName((string)$class, 'registeredFilters'); - if ($filterClassName !== null) { - // toDo: handel array values - if (is_string($this->getIncomingData()->getIncomingField($elementName))) { - if (is_null($filterArguments)) { - $filter = $this->objectManager->get($filterClassName); - } else { - $filter = $this->objectManager->get($filterClassName, $filterArguments); - } - if ($filter) { - $value = $filter->filter($this->getIncomingData()->getIncomingField($elementName)); - $this->getIncomingData()->setIncomingField($elementName, $value); - } else { - throw new \RuntimeException('Class "' . $filterClassName . '" could not be loaded.', 1476122052); - } - } - } else { - throw new \RuntimeException('Class "' . $filterClassName . '" not registered via TypoScript.', 1476048485); - } - } - } - } - - if ($element->getHtmlAttribute('value') !== null) { - $element->setHtmlAttribute('value', $this->getIncomingData()->getIncomingField($elementName)); - } else { - $element->setAdditionalArgument('value', $this->getIncomingData()->getIncomingField($elementName)); - } - } - $this->signalSlotDispatcher->dispatch( - __CLASS__, - 'txFormHandleIncomingValues', - [ - $element, - $this->getIncomingData(), - $modelValue, - $this - ] - ); - } - - /** - * Set the rendered mandatory message - * and the validation error message if available - * - * @param Element $element - * @return void - */ - protected function setValidationMessages(Element $element) - { - $elementName = $element->getName(); - $mandatoryMessages = $this->validationBuilder->getMandatoryValidationMessagesByElementName($elementName); - $element->setMandatoryValidationMessages($mandatoryMessages); - if ( - $this->getValidationErrors() - && $this->getValidationErrors()->forProperty($elementName)->hasErrors() - ) { - /** @var \TYPO3\CMS\Extbase\Error\Error[] $errors */ - $errors = $this->getValidationErrors()->forProperty($elementName)->getErrors(); - $errorMessages = []; - foreach ($errors as $error) { - $errorMessages[] = $error->getMessage(); - } - $element->setValidationErrorMessages($errorMessages); - } - } - - /** - * Return the form prefix - * - * @return string - */ - public function getFormPrefix() - { - return $this->configuration->getPrefix(); - } - - /** - * TRUE if the content element rendering should be disabled. - * - * @return bool - */ - public function getDisableContentElementRendering() - { - return !$this->configuration->getContentElementRendering(); - } - - /** - * TRUE if the content element rendering should be disabled. - * - * @return string - */ - public function getControllerAction() - { - return $this->controllerContext->getRequest()->getControllerActionName(); - } - - /** - * Get the incoming flat form data - * - * @return ValidationElement - */ - public function getIncomingData() - { - return $this->controllerContext->getValidationElement(); - } - - /** - * Set the validation errors - * - * @param \TYPO3\CMS\Extbase\Error\Result $validationErrors - * @return void - */ - public function setValidationErrors(\TYPO3\CMS\Extbase\Error\Result $validationErrors) - { - $this->validationErrors = $validationErrors; - } - - /** - * Get the validation errors - * - * @return NULL|\TYPO3\CMS\Extbase\Error\Result - */ - public function getValidationErrors() - { - return $this->validationErrors; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Builder/ValidationBuilder.php b/typo3/sysext/form/Classes/Domain/Builder/ValidationBuilder.php deleted file mode 100644 index f56bccc9365b..000000000000 --- a/typo3/sysext/form/Classes/Domain/Builder/ValidationBuilder.php +++ /dev/null @@ -1,254 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Builder; - -/* - * 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\Core\Utility\ArrayUtility; -use TYPO3\CMS\Form\Domain\Model\Configuration; -use TYPO3\CMS\Form\Domain\Validator\AbstractValidator; -use TYPO3\CMS\Form\Utility\FormUtility; - -/** - * Parse and hole all the validation rules - */ -class ValidationBuilder -{ - /** - * @param Configuration $configuration - * @return ValidationBuilder - */ - public static function create(Configuration $configuration) - { - /** @var ValidationBuilder $validationBuilder */ - $validationBuilder = \TYPO3\CMS\Form\Utility\FormUtility::getObjectManager()->get(self::class); - $validationBuilder->setConfiguration($configuration); - return $validationBuilder; - } - - /** - * @var array|array[] - */ - protected $rules = []; - - /** - * @var string - */ - protected $formPrefix = ''; - - /** - * @var \TYPO3\CMS\Extbase\Object\ObjectManager - */ - protected $objectManager; - - /** - * @var \TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository - */ - protected $typoScriptRepository; - - /** - * @var FormUtility - */ - protected $formUtility; - - /** - * @var Configuration - */ - protected $configuration; - - /** - * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager - * @return void - */ - public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) - { - $this->objectManager = $objectManager; - } - - /** - * @param \TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository - * @return void - */ - public function injectTypoScriptRepository(\TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository) - { - $this->typoScriptRepository = $typoScriptRepository; - } - - /** - * @param Configuration $configuration - */ - public function setConfiguration(Configuration $configuration) - { - $this->configuration = $configuration; - } - - /** - * @param FormUtility $formUtility - */ - public function setFormUtility(FormUtility $formUtility) - { - $this->formUtility = $formUtility; - } - - /** - * Build validation rules from typoscript. - * The old breakOnError property are no longer supported - * - * @param array $rawArgument - * @return void - */ - public function buildRules(array $rawArgument = []) - { - $userConfiguredFormTyposcript = $this->configuration->getTypoScript(); - $rulesTyposcript = isset($userConfiguredFormTyposcript['rules.']) ? $userConfiguredFormTyposcript['rules.'] : null; - $this->rules[$this->configuration->getPrefix()] = []; - if (is_array($rulesTyposcript)) { - $keys = ArrayUtility::filterAndSortByNumericKeys($rulesTyposcript); - foreach ($keys as $key) { - $ruleName = $rulesTyposcript[$key]; - $validatorClassName = $this->typoScriptRepository->getRegisteredClassName($ruleName, 'registeredValidators'); - if ($validatorClassName === null) { - throw new \RuntimeException( - 'Class "' . $validatorClassName . '" not registered via typoscript.', - 1476048511 - ); - } - if ( - (int)$key - && strpos($key, '.') === false - ) { - $ruleArguments = $rulesTyposcript[$key . '.']; - $fieldName = $this->formUtility->sanitizeNameAttribute($ruleArguments['element']); - // remove unsupported validator options - $validatorOptions = $ruleArguments; - $validatorOptions['errorMessage'] = [$ruleArguments['error.'], $ruleArguments['error']]; - $keysToRemove = array_flip([ - 'breakOnError', - 'message', - 'message.', - 'error', - 'error.', - 'showMessage', - ]); - $validatorOptions = array_diff_key($validatorOptions, $keysToRemove); - - // Instantiate the validator to check if all required options are assigned - // and to use the validator message rendering function to pre-render the mandatory message - /** @var AbstractValidator $validator */ - $validator = $this->objectManager->get($validatorClassName, $validatorOptions); - - if ($validator instanceof AbstractValidator) { - $validator->setRawArgument($rawArgument); - $validator->setFormUtility($this->formUtility); - - if ((int)$ruleArguments['showMessage'] === 1) { - $mandatoryMessage = $validator->renderMessage($ruleArguments['message.'], $ruleArguments['message']); - } else { - $mandatoryMessage = null; - } - - $this->rules[$this->configuration->getPrefix()][$fieldName][] = [ - 'validator' => $validator, - 'validatorName' => $validatorClassName, - 'validatorOptions' => $validatorOptions, - 'mandatoryMessage' => $mandatoryMessage - ]; - } else { - throw new \RuntimeException( - 'Class "' . $validatorClassName . '" could not be loaded.', - 1476048550 - ); - } - } - } - } - } - - /** - * Set all validation rules - * - * @param array $rules - * @return void - */ - public function setRules(array $rules) - { - $this->rules = $rules[$this->configuration->getPrefix()]; - } - - /** - * Get all validation rules - * - * @return array - */ - public function getRules() - { - return $this->rules[$this->configuration->getPrefix()]; - } - - /** - * Set a validation rule - * - * @param string $key - * @param array $rule - * @return void - */ - public function setRulesByElementName($key = '', array $rule = []) - { - $this->rules[$this->configuration->getPrefix()][$key] = $rule; - } - - /** - * Get a validation rule by key - * - * @param string $key - * @return NULL|array - */ - public function getRulesByElementName($key = '') - { - if (isset($this->rules[$this->configuration->getPrefix()][$key])) { - return $this->rules[$this->configuration->getPrefix()][$key]; - } - return null; - } - - /** - * Remove a validation rule by key - * - * @param string $key - * @return void - */ - public function removeRule($key = '') - { - unset($this->rules[$this->configuration->getPrefix()][$key]); - } - - /** - * Get all mandatory validation messages for a element - * - * @param string $key - * @return array - */ - public function getMandatoryValidationMessagesByElementName($key = '') - { - $mandatoryMessages = []; - if ($this->getRulesByElementName($key)) { - $rules = $this->getRulesByElementName($key); - foreach ($rules as $rule) { - if ($rule['mandatoryMessage']) { - $mandatoryMessages[] = $rule['mandatoryMessage']; - } - } - } - return $mandatoryMessages; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Configuration/ConfigurationService.php b/typo3/sysext/form/Classes/Domain/Configuration/ConfigurationService.php new file mode 100644 index 000000000000..555e7956593b --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Configuration/ConfigurationService.php @@ -0,0 +1,61 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Configuration; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Domain\Configuration\Exception\PrototypeNotFoundException; +use TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManagerInterface; + +/** + * Helper for configuration settings + * + * Scope: frontend / backend + */ +class ConfigurationService +{ + + /** + * @var array + */ + protected $formSettings; + + /** + * @internal + */ + public function initializeObject() + { + $this->formSettings = GeneralUtility::makeInstance(ObjectManager::class) + ->get(ConfigurationManagerInterface::class) + ->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_YAML_SETTINGS, 'form'); + } + + /** + * Get the prototype configuration + * + * @param string $prototypeName name of the prototype to get the configuration for + * @return array the prototype configuration + * @throws PrototypeNotFoundException if prototype with the name $prototypeName was not found + * @api + */ + public function getPrototypeConfiguration(string $prototypeName): array + { + if (!isset($this->formSettings['prototypes'][$prototypeName])) { + throw new PrototypeNotFoundException(sprintf('The Prototype "%s" was not found.', $prototypeName), 1475924277); + } + return $this->formSettings['prototypes'][$prototypeName]; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Filter/StripNewLinesFilter.php b/typo3/sysext/form/Classes/Domain/Configuration/Exception/PrototypeNotFoundException.php similarity index 55% rename from typo3/sysext/form/Classes/Domain/Filter/StripNewLinesFilter.php rename to typo3/sysext/form/Classes/Domain/Configuration/Exception/PrototypeNotFoundException.php index 66eca1dd192f..90c311f53c22 100644 --- a/typo3/sysext/form/Classes/Domain/Filter/StripNewLinesFilter.php +++ b/typo3/sysext/form/Classes/Domain/Configuration/Exception/PrototypeNotFoundException.php @@ -1,5 +1,6 @@ <?php -namespace TYPO3\CMS\Form\Domain\Filter; +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Configuration\Exception; /* * This file is part of the TYPO3 CMS project. @@ -14,19 +15,13 @@ namespace TYPO3\CMS\Form\Domain\Filter; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Form\Domain\Exception; + /** - * Strip new lines filter + * This exception is thrown if a form prototype for a given name was not found. + * + * @api */ -class StripNewLinesFilter extends AbstractFilter implements FilterInterface +class PrototypeNotFoundException extends Exception { - /** - * Strip newlines - * - * @param string $value - * @return string - */ - public function filter($value) - { - return str_replace([CRLF, LF, CR], ' ', (string)$value); - } } diff --git a/typo3/sysext/form/Classes/Domain/Filter/FilterInterface.php b/typo3/sysext/form/Classes/Domain/Exception.php similarity index 66% rename from typo3/sysext/form/Classes/Domain/Filter/FilterInterface.php rename to typo3/sysext/form/Classes/Domain/Exception.php index 06222269ed79..0fd7c4652cdb 100644 --- a/typo3/sysext/form/Classes/Domain/Filter/FilterInterface.php +++ b/typo3/sysext/form/Classes/Domain/Exception.php @@ -1,5 +1,6 @@ <?php -namespace TYPO3\CMS\Form\Domain\Filter; +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain; /* * This file is part of the TYPO3 CMS project. @@ -14,16 +15,13 @@ namespace TYPO3\CMS\Form\Domain\Filter; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Form\Exception as FormException; + /** - * Interface for filters + * A generic Form domain Exception + * + * @api */ -interface FilterInterface +class Exception extends FormException { - /** - * Return filtered value - * - * @param mixed $value - * @return mixed - */ - public function filter($value); } diff --git a/typo3/sysext/form/Classes/Domain/Filter/LowerCaseFilter.php b/typo3/sysext/form/Classes/Domain/Exception/IdentifierNotValidException.php similarity index 55% rename from typo3/sysext/form/Classes/Domain/Filter/LowerCaseFilter.php rename to typo3/sysext/form/Classes/Domain/Exception/IdentifierNotValidException.php index 55c16d470042..8b5d257996f5 100644 --- a/typo3/sysext/form/Classes/Domain/Filter/LowerCaseFilter.php +++ b/typo3/sysext/form/Classes/Domain/Exception/IdentifierNotValidException.php @@ -1,5 +1,6 @@ <?php -namespace TYPO3\CMS\Form\Domain\Filter; +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Exception; /* * This file is part of the TYPO3 CMS project. @@ -14,19 +15,14 @@ namespace TYPO3\CMS\Form\Domain\Filter; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Form\Domain\Exception; + /** - * Lowercase filter + * This exception is thrown if the "identifier" for a Form, a Page or a Form Element + * is invalid (i.e. empty or not a string) + * + * @api */ -class LowerCaseFilter extends AbstractFilter implements FilterInterface +class IdentifierNotValidException extends Exception { - /** - * Convert alphabetic characters to lowercase - * - * @param string $value - * @return string - */ - public function filter($value) - { - return $this->convertCase($value, 'toLower'); - } } diff --git a/typo3/sysext/form/Classes/Domain/Exception/RenderingException.php b/typo3/sysext/form/Classes/Domain/Exception/RenderingException.php new file mode 100644 index 000000000000..1430f8a06089 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Exception/RenderingException.php @@ -0,0 +1,27 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Exception; + +/* + * 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\Form\Domain\Exception; + +/** + * This exception is thrown if a rendering error occurs + * + * @api + */ +class RenderingException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Exception/TypeDefinitionNotFoundException.php b/typo3/sysext/form/Classes/Domain/Exception/TypeDefinitionNotFoundException.php new file mode 100644 index 000000000000..3600a2adf994 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Exception/TypeDefinitionNotFoundException.php @@ -0,0 +1,28 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Exception; + +/* + * 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\Form\Domain\Exception; + +/** + * This exception is thrown if a Type Definition for a form element was not found, + * or if the implementationClassName was not set. + * + * @api + */ +class TypeDefinitionNotFoundException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Exception/TypeDefinitionNotValidException.php b/typo3/sysext/form/Classes/Domain/Exception/TypeDefinitionNotValidException.php new file mode 100644 index 000000000000..70b58a4f4d67 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Exception/TypeDefinitionNotValidException.php @@ -0,0 +1,28 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Exception; + +/* + * 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\Form\Domain\Exception; + +/** + * This exception is thrown if a Type Definition for a form element was not valid, + * i.e. it has properties which are not supported. + * + * @api + */ +class TypeDefinitionNotValidException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Exception/UnknownCompositRenderableException.php b/typo3/sysext/form/Classes/Domain/Exception/UnknownCompositRenderableException.php new file mode 100644 index 000000000000..9ac5c2071658 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Exception/UnknownCompositRenderableException.php @@ -0,0 +1,28 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Exception; + +/* + * 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\Form\Domain\Exception; + +/** + * This exception is thrown if the ArrayFormFactory want to create child + * elements within a unknown composit renderable. + * + * @api + */ +class UnknownCompositRenderableException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Factory/AbstractFormFactory.php b/typo3/sysext/form/Classes/Domain/Factory/AbstractFormFactory.php new file mode 100644 index 000000000000..60c9e26a517f --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Factory/AbstractFormFactory.php @@ -0,0 +1,65 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Factory; + +/* + * 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\Form\Domain\Model\FormDefinition; + +/** + * Base class for custom *Form Factories*. A Form Factory is responsible for building + * a {@link TYPO3\CMS\Form\Domain\Model\FormDefinition}. + * + * {@inheritDoc} + * + * Example + * ======= + * + * Generally, you should use this class as follows: + * + * <pre> + * class MyFooBarFactory extends AbstractFormFactory { + * public function build(array $configuration, $prototypeName) { + * $configurationService = GeneralUtility::makeInstance(ObjectManager::class)->get(ConfigurationService::class); + * $prototypeConfiguration = $configurationService->getPrototypeConfiguration($prototypeName); + * $formDefinition = GeneralUtility::makeInstance(ObjectManager::class)->get(FormDefinition::class, 'nameOfMyForm', $prototypeConfiguration); + * + * // now, you should call methods on $formDefinition to add pages and form elements + * + * return $formDefinition; + * } + * } + * </pre> + * + * Scope: frontend / backend + * **This class is meant to be sub classed by developers.** + * @api + */ +abstract class AbstractFormFactory implements FormFactoryInterface +{ + /** + * Helper to be called by every AbstractFormFactory after everything has been built to trigger the "onBuildingFinished" + * template method on all form elements. + * + * @param FormDefinition $form + * @return void + * @api + */ + protected function triggerFormBuildingFinished(FormDefinition $form) + { + foreach ($form->getRenderablesRecursively() as $renderable) { + $renderable->onBuildingFinished(); + } + } +} diff --git a/typo3/sysext/form/Classes/Domain/Factory/ArrayFormFactory.php b/typo3/sysext/form/Classes/Domain/Factory/ArrayFormFactory.php new file mode 100644 index 000000000000..4b08561b4b48 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Factory/ArrayFormFactory.php @@ -0,0 +1,120 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Factory; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService; +use TYPO3\CMS\Form\Domain\Exception\IdentifierNotValidException; +use TYPO3\CMS\Form\Domain\Exception\UnknownCompositRenderableException; +use TYPO3\CMS\Form\Domain\Model\FormDefinition; +use TYPO3\CMS\Form\Domain\Model\FormElements\AbstractSection; +use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface; + +/** + * A factory that creates a FormDefinition from an array + * + * Scope: frontend / backend + */ +class ArrayFormFactory extends AbstractFormFactory +{ + + /** + * Build a form definition, depending on some configuration. + * + * @param array $configuration + * @param string $prototypeName + * @return FormDefinition + * @internal + */ + public function build(array $configuration, string $prototypeName = null): FormDefinition + { + if (empty($prototypeName)) { + $prototypeName = isset($configuration['prototypeName']) ? $configuration['prototypeName'] : 'standard'; + } + $persistenceIdentifier = (isset($configuration['persistenceIdentifier'])) ? $configuration['persistenceIdentifier'] : null; + + $prototypeConfiguration = GeneralUtility::makeInstance(ObjectManager::class) + ->get(ConfigurationService::class) + ->getPrototypeConfiguration($prototypeName); + + $form = GeneralUtility::makeInstance(ObjectManager::class)->get( + FormDefinition::class, + $configuration['identifier'], + $prototypeConfiguration, + 'Form', + $persistenceIdentifier + ); + if (isset($configuration['renderables'])) { + foreach ($configuration['renderables'] as $pageConfiguration) { + $this->addNestedRenderable($pageConfiguration, $form); + } + } + + unset($configuration['persistenceIdentifier']); + unset($configuration['prototypeName']); + unset($configuration['renderables']); + unset($configuration['type']); + unset($configuration['identifier']); + unset($configuration['label']); + $form->setOptions($configuration); + + $this->triggerFormBuildingFinished($form); + + return $form; + } + + /** + * Add form elements to the $parentRenderable + * + * @param array $nestedRenderableConfiguration + * @param CompositeRenderableInterface CompositeRenderableInterface $parentRenderable + * @return mixed + * @throws IdentifierNotValidException + * @throws UnknownCompositRenderableException + */ + protected function addNestedRenderable(array $nestedRenderableConfiguration, CompositeRenderableInterface $parentRenderable) + { + if (!isset($nestedRenderableConfiguration['identifier'])) { + throw new IdentifierNotValidException('Identifier not set.', 1329289436); + } + if ($parentRenderable instanceof FormDefinition) { + $renderable = $parentRenderable->createPage($nestedRenderableConfiguration['identifier'], $nestedRenderableConfiguration['type']); + } elseif ($parentRenderable instanceof AbstractSection) { + $renderable = $parentRenderable->createElement($nestedRenderableConfiguration['identifier'], $nestedRenderableConfiguration['type']); + } else { + throw new UnknownCompositRenderableException('Unknown composit renderable "' . get_class($parentRenderable) . '"', 1479593622); + } + + if (isset($nestedRenderableConfiguration['renderables']) && is_array($nestedRenderableConfiguration['renderables'])) { + $childRenderables = $nestedRenderableConfiguration['renderables']; + } else { + $childRenderables = []; + } + + unset($nestedRenderableConfiguration['type']); + unset($nestedRenderableConfiguration['identifier']); + unset($nestedRenderableConfiguration['renderables']); + + $renderable->setOptions($nestedRenderableConfiguration); + + foreach ($childRenderables as $elementConfiguration) { + $this->addNestedRenderable($elementConfiguration, $renderable); + } + + return $renderable; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Factory/FormFactoryInterface.php b/typo3/sysext/form/Classes/Domain/Factory/FormFactoryInterface.php new file mode 100644 index 000000000000..1ccb06cfcba5 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Factory/FormFactoryInterface.php @@ -0,0 +1,46 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Factory; + +/* + * 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\Form\Domain\Model\FormDefinition; + +/** + * A Form Factory is responsible for building a {@link TYPO3\CMS\Form\Domain\Model\FormDefinition}. + * **Instead of implementing this interface, subclassing {@link AbstractFormFactory} is more appropriate + * in most cases**. + * + * A Form Factory can be called anytime a FormDefinition should be built; in most cases + * it is done through an invocation of a Form Rendering ViewHelper. + * + * Scope: frontend / backend + * @api + */ +interface FormFactoryInterface +{ + + /** + * Build a form definition, depending on some configuration. + * + * The configuration array is factory-specific; for example a YAML or JSON factory + * could retrieve the path to the YAML / JSON file via the configuration array. + * + * @param array $configuration factory-specific configuration array + * @param string $prototypeName The name of the "PrototypeName" to use; it is factory-specific to implement this. + * @return FormDefinition a newly built form definition + * @api + */ + public function build(array $configuration, string $prototypeName = null): FormDefinition; +} diff --git a/typo3/sysext/form/Classes/Domain/Factory/JsonToTypoScript.php b/typo3/sysext/form/Classes/Domain/Factory/JsonToTypoScript.php deleted file mode 100644 index b10f8a575b51..000000000000 --- a/typo3/sysext/form/Classes/Domain/Factory/JsonToTypoScript.php +++ /dev/null @@ -1,591 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Factory; - -/* - * 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! - */ - -/** - * Json to Typoscript converter - * - * Takes the incoming Json and converts it to Typoscipt - */ -class JsonToTypoScript -{ - /** - * Internal counter for the elements - * - * @var int - */ - protected $elementId = 0; - - /** - * Storage for the validation rules - * In TypoScript they are set in the form, in the wizard on the elements - * - * @var array - */ - protected $validationRules = []; - - /** - * Internal counter for the validation rules - * - * @var int - */ - protected $validationRulesCounter = 1; - - /** - * Convert JSON to TypoScript - * - * First a TypoScript array is constructed, - * which will be converted to a formatted string - * - * @param string $json Json containing all configuration for the form - * @return string The typoscript for the form - */ - public function convert($json) - { - $elements = json_decode((string)$json, true); - $typoscriptArray = []; - $typoscript = null; - $this->convertToTyposcriptArray($elements, $typoscriptArray); - if ($typoscriptArray['10.'] && is_array($typoscriptArray['10.']) && !empty($typoscriptArray['10.'])) { - $typoscript = $this->typoscriptArrayToString($typoscriptArray['10.']); - } - return $typoscript; - } - - /** - * Converts the JSON array for each element to a TypoScript array - * and adds this Typoscript array to the parent - * - * @param array $elements The JSON array - * @param array $parent The parent element - * @param bool $childrenWithParentName Indicates if the children use the parent name - * @return void - */ - protected function convertToTyposcriptArray(array $elements, array &$parent, $childrenWithParentName = false) - { - if (is_array($elements)) { - $elementCounter = 10; - foreach ($elements as $element) { - if ($element['xtype']) { - $this->elementId++; - switch ($element['xtype']) { - case 'typo3-form-wizard-elements-basic-button': - - case 'typo3-form-wizard-elements-basic-checkbox': - - case 'typo3-form-wizard-elements-basic-fileupload': - - case 'typo3-form-wizard-elements-basic-hidden': - - case 'typo3-form-wizard-elements-basic-password': - - case 'typo3-form-wizard-elements-basic-radio': - - case 'typo3-form-wizard-elements-basic-reset': - - case 'typo3-form-wizard-elements-basic-select': - - case 'typo3-form-wizard-elements-basic-submit': - - case 'typo3-form-wizard-elements-basic-textarea': - - case 'typo3-form-wizard-elements-basic-textline': - - case 'typo3-form-wizard-elements-predefined-email': - - case 'typo3-form-wizard-elements-content-header': - - case 'typo3-form-wizard-elements-content-textblock': - $this->getDefaultElementSetup($element, $parent, $elementCounter, $childrenWithParentName); - break; - case 'typo3-form-wizard-elements-basic-fieldset': - - case 'typo3-form-wizard-elements-predefined-name': - $this->getDefaultElementSetup($element, $parent, $elementCounter); - $this->getContainer($element, $parent, $elementCounter); - break; - case 'typo3-form-wizard-elements-predefined-checkboxgroup': - - case 'typo3-form-wizard-elements-predefined-radiogroup': - $this->getDefaultElementSetup($element, $parent, $elementCounter); - $this->getContainer($element, $parent, $elementCounter, true); - break; - case 'typo3-form-wizard-elements-basic-form': - $this->getDefaultElementSetup($element, $parent, $elementCounter); - $this->getContainer($element, $parent, $elementCounter); - $this->getForm($element, $parent, $elementCounter); - break; - default: - - } - } - $elementCounter = $elementCounter + 10; - } - } - } - - /** - * Called for elements are a container for other elements like FORM and FIELDSET - * - * @param array $element The JSON array for this element - * @param array $parent The parent element - * @param bool $childrenWithParentName Indicates if the children use the parent name - * @param int $elementCounter The element counter - * @return void - */ - protected function getContainer(array $element, array &$parent, $elementCounter, $childrenWithParentName = false) - { - if ($element['elementContainer'] && $element['elementContainer']['items']) { - $this->convertToTyposcriptArray($element['elementContainer']['items'], $parent[$elementCounter . '.'], $childrenWithParentName); - } - } - - /** - * Only called for the type FORM - * - * Adds the validation rules to the form. In the wizard they are added to - * each element. In this script the validation rules are stored in a - * separate array to add them to the form at a later point. - * - * @param array $element The JSON array for this element - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @return void - */ - protected function getForm(array $element, array &$parent, $elementCounter) - { - // @todo Put at the top of the form - if (!empty($this->validationRules)) { - $parent[$elementCounter . '.']['rules'] = $this->validationRules; - } - } - - /** - * Called for each element - * - * Adds the content object type to the parent array and starts adding the - * configuration for the element - * - * @param array $element The JSON array for this element - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @param bool $childrenWithParentName Indicates if the children use the parent name - * @return void - */ - protected function getDefaultElementSetup(array $element, array &$parent, $elementCounter, $childrenWithParentName = false) - { - $contentObjectType = $this->getContentObjectType($element); - if (is_null($contentObjectType) === false) { - $parent[$elementCounter] = $contentObjectType; - $parent[$elementCounter . '.'] = []; - if ($element['configuration']) { - $this->setConfiguration($element, $parent, $elementCounter, $childrenWithParentName); - } - } - } - - /** - * Returns the content object type which is related to the ExtJS xtype - * - * @param array $element The JSON array for this element - * @return string The Content Object Type - */ - protected function getContentObjectType(array $element) - { - $contentObjectType = null; - $shortXType = str_replace('typo3-form-wizard-elements-', '', $element['xtype']); - list($category, $type) = explode('-', $shortXType); - switch ($category) { - case 'basic': - $contentObjectType = strtoupper($type); - break; - case 'predefined': - switch ($type) { - case 'checkboxgroup': - - case 'radiogroup': - $contentObjectType = strtoupper($type); - break; - case 'email': - $contentObjectType = 'TEXTLINE'; - break; - case 'name': - $contentObjectType = 'FIELDSET'; - } - break; - case 'content': - switch ($type) { - case 'header': - - case 'textblock': - $contentObjectType = strtoupper($type); - } - default: - - } - return $contentObjectType; - } - - /** - * Iterates over the various configuration settings and calls the - * appropriate function for each setting - * - * @param array $element The JSON array for this element - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @param bool $childrenWithParentName Indicates if the children use the parent name - * @return void - */ - protected function setConfiguration(array $element, array &$parent, $elementCounter, $childrenWithParentName = false) - { - foreach ($element['configuration'] as $key => $value) { - switch ($key) { - case 'attributes': - $this->setAttributes($value, $parent, $elementCounter, $childrenWithParentName); - break; - case 'confirmation': - $this->setConfirmation($value, $parent, $elementCounter); - break; - case 'filters': - $this->setFilters($value, $parent, $elementCounter); - break; - case 'label': - $this->setLabel($value, $parent, $elementCounter); - break; - case 'layout': - $this->setLayout($element, $value, $parent, $elementCounter); - break; - case 'legend': - $this->setLegend($value, $parent, $elementCounter); - break; - case 'options': - $this->setOptions($element, $value, $parent, $elementCounter); - break; - case 'postProcessor': - $this->setPostProcessor($value, $parent, $elementCounter); - break; - case 'prefix': - $this->setPrefix($value, $parent, $elementCounter); - break; - case 'validation': - $this->setValidationRules($element, $value); - break; - case 'various': - $this->setVarious($element, $value, $parent, $elementCounter); - break; - default: - - } - } - } - - /** - * Set the attributes for the element - * - * @param array $attributes The JSON array for the attributes of this element - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @param bool $childrenWithParentName Indicates if the children use the parent name - * @return void - */ - protected function setAttributes(array $attributes, array &$parent, $elementCounter, $childrenWithParentName = false) - { - foreach ($attributes as $key => $value) { - if ($key === 'name' && $value === '' && !$childrenWithParentName) { - $value = $this->elementId; - } - if ($value != '') { - $parent[$elementCounter . '.'][$key] = $value; - } - } - } - - /** - * Set the confirmation for the element FORM - * - * The confirmation indicates a confirmation screen has to be displayed - * - * @param bool $confirmation TRUE when confirmation screen - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @return void - */ - protected function setConfirmation($confirmation, array &$parent, $elementCounter) - { - $parent[$elementCounter . '.']['confirmation'] = $confirmation; - } - - /** - * Set the filters for the element - * - * @param array $filters The JSON array for the filters of this element - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @return void - */ - protected function setFilters(array $filters, array &$parent, $elementCounter) - { - if (!empty($filters)) { - $parent[$elementCounter . '.']['filters'] = []; - $filterCounter = 1; - foreach ($filters as $name => $filterConfiguration) { - $parent[$elementCounter . '.']['filters'][$filterCounter] = $name; - $parent[$elementCounter . '.']['filters'][$filterCounter . '.'] = $filterConfiguration; - $filterCounter++; - } - } - } - - /** - * Set the label for the element - * - * @param array $label The JSON array for the label of this element - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @return void - */ - protected function setLabel(array $label, array &$parent, $elementCounter) - { - if ($label['value'] != '') { - $parent[$elementCounter . '.']['label.']['value'] = $label['value']; - } - } - - /** - * Changes the layout of some elements when this has been set in the wizard - * - * The wizard only uses 'back' or 'front' to set the layout. The TypoScript - * of the form uses a XML notation for the position of the label to the - * field. - * - * @param array $element The JSON array for this element - * @param string $value The layout setting, back or front - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @return void - */ - protected function setLayout(array $element, $value, array &$parent, $elementCounter) - { - switch ($element['xtype']) { - case 'typo3-form-wizard-elements-basic-button': - - case 'typo3-form-wizard-elements-basic-fileupload': - - case 'typo3-form-wizard-elements-basic-password': - - case 'typo3-form-wizard-elements-basic-reset': - - case 'typo3-form-wizard-elements-basic-submit': - - case 'typo3-form-wizard-elements-basic-textline': - if ($value === 'back') { - $parent[$elementCounter . '.']['layout'] = '<input />' . LF . '<label />'; - } - break; - case 'typo3-form-wizard-elements-basic-checkbox': - - case 'typo3-form-wizard-elements-basic-radio': - if ($value === 'front') { - $parent[$elementCounter . '.']['layout'] = '<label />' . LF . '<input />'; - } - break; - case 'typo3-form-wizard-elements-basic-select': - if ($value === 'back') { - $parent[$elementCounter . '.']['layout'] = '<select>' . LF . '<elements />' . LF . '</select>' . LF . '<label />'; - } - break; - case 'typo3-form-wizard-elements-basic-textarea': - if ($value === 'back') { - $parent[$elementCounter . '.']['layout'] = '<textarea />' . LF . '<label />'; - } - break; - default: - - } - } - - /** - * Set the legend for the element - * - * @param array $legend The JSON array for the legend of this element - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @return void - */ - protected function setLegend(array $legend, array &$parent, $elementCounter) - { - if ($legend['value'] != '') { - $parent[$elementCounter . '.']['legend.']['value'] = $legend['value']; - } - } - - /** - * Set the options for a SELECT - * - * Although other elements like CHECKBOXGROUP and RADIOGROUP are using the - * option setting as well, they act like containers and are handled - * differently - * - * @param array $element The JSON array for this element - * @param array $options The JSON array for the options of this element - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @return void - */ - protected function setOptions(array $element, array $options, array &$parent, $elementCounter) - { - if (is_array($options) && $element['xtype'] === 'typo3-form-wizard-elements-basic-select') { - $optionCounter = 10; - foreach ($options as $option) { - $parent[$elementCounter . '.'][$optionCounter] = 'OPTION'; - $parent[$elementCounter . '.'][$optionCounter . '.']['text'] = $option['text']; - if (isset($option['attributes'])) { - $parent[$elementCounter . '.'][$optionCounter . '.'] += $option['attributes']; - } - $optionCounter = $optionCounter + 10; - } - } - } - - /** - * Set the post processors for the element - * - * @param array $postProcessors The JSON array for the post processors of this element - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @return void - */ - protected function setPostProcessor(array $postProcessors, array &$parent, $elementCounter) - { - if (!empty($postProcessors)) { - $parent[$elementCounter . '.']['postProcessor'] = []; - $postProcessorCounter = 1; - foreach ($postProcessors as $name => $postProcessorConfiguration) { - $parent[$elementCounter . '.']['postProcessor'][$postProcessorCounter] = $name; - $parent[$elementCounter . '.']['postProcessor'][$postProcessorCounter . '.'] = $postProcessorConfiguration; - $postProcessorCounter++; - } - } - } - - /** - * Set the prefix for the element FORM - * - * The prefix will be used in the names of all elements in the form - * - * @param string $prefix The prefix for all element names - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @return void - */ - protected function setPrefix($prefix, array &$parent, $elementCounter) - { - $parent[$elementCounter . '.']['prefix'] = $prefix; - } - - /** - * Stores the validation rules, set to the elements, in a temporary array - * - * In the wizard the validation rules are added to the element, - * in TypoScript they are added to the form. - * - * @param array $element The JSON array for this element - * @param array $validationRules The temporary storage array for the rules - * @return void - */ - protected function setValidationRules(array $element, array $validationRules) - { - foreach ($validationRules as $name => $ruleConfiguration) { - if (isset($element['configuration']['attributes']['name']) && $element['configuration']['attributes']['name'] != '') { - $ruleConfiguration['element'] = $element['configuration']['attributes']['name']; - } elseif (isset($element['configuration']['various']['name']) && $element['configuration']['various']['name'] != '') { - $ruleConfiguration['element'] = $element['configuration']['various']['name']; - } else { - $ruleConfiguration['element'] = $this->elementId; - } - $this->validationRules[$this->validationRulesCounter] = $name; - $this->validationRules[$this->validationRulesCounter . '.'] = $ruleConfiguration; - $this->validationRulesCounter++; - } - } - - /** - * Set the various configuration of an element - * - * @param array $element The JSON array for this element - * @param array $various The JSON array for the various options of this element - * @param array $parent The parent element - * @param int $elementCounter The element counter - * @return void - */ - protected function setVarious(array $element, array $various, array &$parent, $elementCounter) - { - foreach ($various as $key => $value) { - switch ($key) { - case 'headingSize': - - case 'content': - - case 'text': - - case 'name': - $parent[$elementCounter . '.'][$key] = (string)$value; - break; - } - } - } - - /** - * Converts a TypoScript array to a formatted string - * - * Takes care of indentation, curly brackets and parentheses - * - * @param array $typoscriptArray The TypoScript array - * @param string $addKey Key which has underlying configuration - * @param int $tabCount The amount of tabs for indentation - * @return string The formatted TypoScript string - */ - protected function typoscriptArrayToString(array $typoscriptArray, $addKey = '', $tabCount = -1) - { - $typoscript = ''; - if ($addKey != '') { - $typoscript .= str_repeat(TAB, $tabCount) . str_replace('.', '', $addKey) . ' {' . LF; - } - $tabCount++; - foreach ($typoscriptArray as $key => $value) { - if (!is_array($value)) { - if (strstr($value, LF)) { - $typoscript .= str_repeat(TAB, $tabCount) . $key . ' (' . LF; - if ($key !== 'text') { - $value = str_replace(LF, LF . str_repeat(TAB, ($tabCount + 1)), $value); - $typoscript .= str_repeat(TAB, ($tabCount + 1)) . $value . LF; - } else { - $typoscript .= $value . LF; - } - $typoscript .= str_repeat(TAB, $tabCount) . ')' . LF; - } else { - $typoscript .= str_repeat(TAB, $tabCount) . $key . ' = ' . $value . LF; - } - } else { - $typoscript .= $this->typoscriptArrayToString($value, $key, $tabCount); - } - } - if ($addKey != '') { - $tabCount--; - $typoscript .= str_repeat(TAB, $tabCount) . '}' . LF; - } - return $typoscript; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Filter/AbstractFilter.php b/typo3/sysext/form/Classes/Domain/Filter/AbstractFilter.php deleted file mode 100644 index 073158e6818b..000000000000 --- a/typo3/sysext/form/Classes/Domain/Filter/AbstractFilter.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Filter; - -/* - * 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\Core\Charset\CharsetConverter; -use TYPO3\CMS\Core\Utility\GeneralUtility; - -/** - * Abstract class for filters. - */ -abstract class AbstractFilter -{ - protected function convertCase($value, $case) - { - return $this->getCharsetConverter()->conv_case( - 'utf-8', - $value, - $case - ); - } - - /** - * @return CharsetConverter - */ - protected function getCharsetConverter() - { - return GeneralUtility::makeInstance(CharsetConverter::class); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Filter/AlphabeticFilter.php b/typo3/sysext/form/Classes/Domain/Filter/AlphabeticFilter.php deleted file mode 100644 index f9f529a33467..000000000000 --- a/typo3/sysext/form/Classes/Domain/Filter/AlphabeticFilter.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Filter; - -/* - * 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! - */ - -/** - * Alphabetic filter - */ -class AlphabeticFilter extends AbstractFilter implements FilterInterface -{ - /** - * Allow whitespace - * - * @var bool - */ - protected $allowWhiteSpace; - - /** - * Constructor - * - * @param array $arguments Filter configuration - */ - public function __construct($arguments = []) - { - $this->setAllowWhiteSpace($arguments['allowWhiteSpace']); - } - - /** - * Allow white space in the submitted value - * - * @param bool $allowWhiteSpace True if allowed - * @return void - */ - public function setAllowWhiteSpace($allowWhiteSpace = true) - { - $this->allowWhiteSpace = (bool)$allowWhiteSpace; - } - - /** - * Return filtered value - * Remove all but alphabetic characters - * Allow whitespace by choice - * - * @param string $value - * @return string - */ - public function filter($value) - { - $whiteSpace = $this->allowWhiteSpace ? '\\s' : ''; - $pattern = '/[^\pL' . $whiteSpace . ']/u'; - return preg_replace($pattern, '', (string)$value); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Filter/AlphanumericFilter.php b/typo3/sysext/form/Classes/Domain/Filter/AlphanumericFilter.php deleted file mode 100644 index 0c8c2670c127..000000000000 --- a/typo3/sysext/form/Classes/Domain/Filter/AlphanumericFilter.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Filter; - -/* - * 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! - */ - -/** - * Alphanumeric filter - */ -class AlphanumericFilter extends AbstractFilter implements FilterInterface -{ - /** - * Allow whitespace - * - * @var bool - */ - protected $allowWhiteSpace; - - /** - * Constructor - * - * @param array $arguments Filter configuration - */ - public function __construct($arguments = []) - { - $this->setAllowWhiteSpace($arguments['allowWhiteSpace']); - } - - /** - * Allow white space in the submitted value - * - * @param bool $allowWhiteSpace True if allowed - * @return void - */ - public function setAllowWhiteSpace($allowWhiteSpace = true) - { - $this->allowWhiteSpace = (bool)$allowWhiteSpace; - } - - /** - * Return filtered value - * Remove all but alphabetic and numeric characters - * Allow whitespace by choice - * - * @param string $value - * @return string - */ - public function filter($value) - { - $whiteSpace = $this->allowWhiteSpace ? '\\s' : ''; - $pattern = '/[^\pL\d' . $whiteSpace . ']/u'; - return preg_replace($pattern, '', (string)$value); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Filter/CurrencyFilter.php b/typo3/sysext/form/Classes/Domain/Filter/CurrencyFilter.php deleted file mode 100644 index 99ae3c229df0..000000000000 --- a/typo3/sysext/form/Classes/Domain/Filter/CurrencyFilter.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Filter; - -/* - * 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! - */ - -/** - * Currency filter - */ -class CurrencyFilter extends AbstractFilter implements FilterInterface -{ - /** - * Separator between group of thousands - * Mostly dot, comma or whitespace - * - * @var string - */ - protected $decimalsPoint; - - /** - * Separator between group of thousands - * Mostly dot, comma or whitespace - * - * @var string - */ - protected $thousandSeparator; - - /** - * Constructor - * - * @param array $arguments Filter configuration - */ - public function __construct($arguments = []) - { - $this->setDecimalsPoint($arguments['decimalPoint']); - $this->setThousandSeparator($arguments['thousandSeparator']); - } - - /** - * Set the decimal point character - * - * @param string $decimalsPoint Character used for decimal point - * @return void - */ - public function setDecimalsPoint($decimalsPoint = '.') - { - if (empty($decimalsPoint)) { - $this->decimalsPoint = '.'; - } else { - $this->decimalsPoint = (string)$decimalsPoint; - } - } - - /** - * Set the thousand separator character - * - * @param string $thousandSeparator Character used for thousand separator - * @return void - */ - public function setThousandSeparator($thousandSeparator = ',') - { - if (empty($thousandSeparator)) { - $this->thousandSeparator = ','; - } elseif ($thousandSeparator === 'space') { - $this->thousandSeparator = ' '; - } elseif ($thousandSeparator === 'none') { - $this->thousandSeparator = ''; - } else { - $this->thousandSeparator = (string)$thousandSeparator; - } - } - - /** - * Change to float with 2 decimals - * Change the dot to comma if requested - * - * @param string $value - * @return string - */ - public function filter($value) - { - $value = str_replace( - [ - $this->thousandSeparator, - $this->decimalsPoint, - ], - [ - '', - '.' - ], - (string)$value - ); - - // replace all non numeric characters, decimalPoint and negativ sign - $value = preg_replace('/[^0-9.-]/', '', $value); - $value = (double)$value; - return number_format($value, 2, $this->decimalsPoint, $this->thousandSeparator); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Filter/RegExpFilter.php b/typo3/sysext/form/Classes/Domain/Filter/RegExpFilter.php deleted file mode 100644 index aace6b266089..000000000000 --- a/typo3/sysext/form/Classes/Domain/Filter/RegExpFilter.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Filter; - -/* - * 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! - */ - -/** - * Regular expression filter - */ -class RegExpFilter extends AbstractFilter implements FilterInterface -{ - /** - * Regular expression for filter - * - * @var bool - */ - protected $regularExpression; - - /** - * Constructor - * - * @param array $arguments Filter configuration - */ - public function __construct(array $arguments = []) - { - $this->setRegularExpression($arguments['expression']); - } - - /** - * Set the regular expression - * - * @param string $expression The regular expression - * @return void - */ - public function setRegularExpression($expression) - { - $this->regularExpression = (string)$expression; - } - - /** - * Return filtered value - * Remove all characters found in regular expression - * - * @param string $value - * @return string - */ - public function filter($value) - { - return preg_replace($this->regularExpression, '', (string)$value); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Filter/RemoveXssFilter.php b/typo3/sysext/form/Classes/Domain/Filter/RemoveXssFilter.php deleted file mode 100644 index 0a97a2bc04fc..000000000000 --- a/typo3/sysext/form/Classes/Domain/Filter/RemoveXssFilter.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Filter; - -/* - * 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\Core\Utility\GeneralUtility; - -/** - * Remove Cross Site Scripting filter - * - * @deprecated since TYPO3 v8, will be removed in TYPO3 v9 - */ -class RemoveXssFilter extends AbstractFilter implements FilterInterface -{ - /** - * Return filtered value - * Removes potential XSS code from the input string. - * - * Using an external class by Travis Puderbaugh <kallahar@quickwired.com> - * - * @param string $value Unfiltered value - * @return string The filtered value - * @deprecated since TYPO3 v8, will be removed in TYPO3 v9 - */ - public function filter($value) - { - $value = stripslashes($value); - $value = html_entity_decode($value, ENT_QUOTES); - $filteredValue = GeneralUtility::removeXSS($value); - return $filteredValue; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Filter/TitleCaseFilter.php b/typo3/sysext/form/Classes/Domain/Filter/TitleCaseFilter.php deleted file mode 100644 index 89ae0c4a2876..000000000000 --- a/typo3/sysext/form/Classes/Domain/Filter/TitleCaseFilter.php +++ /dev/null @@ -1,33 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Filter; - -/* - * 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! - */ - -/** - * Title filter - */ -class TitleCaseFilter extends AbstractFilter implements FilterInterface -{ - /** - * Convert alphabetic characters to title case - * - * @param string $value - * @return string - */ - public function filter($value) - { - $lower = $this->convertCase($value, 'toLower'); - return ucwords($lower); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Filter/TrimFilter.php b/typo3/sysext/form/Classes/Domain/Filter/TrimFilter.php deleted file mode 100644 index cf36b9d3437c..000000000000 --- a/typo3/sysext/form/Classes/Domain/Filter/TrimFilter.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Filter; - -/* - * 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! - */ - -/** - * Trim filter - */ -class TrimFilter extends AbstractFilter implements FilterInterface -{ - /** - * Characters used by trim filter - * - * @var string - */ - protected $characterList; - - /** - * Constructor - * - * @param array $arguments Filter configuration - */ - public function __construct(array $arguments = []) - { - $this->setCharacterList($arguments['characterList']); - } - - /** - * Set the characters that need to be stripped from the - * beginning or the end of the input, - * in addition to the default trim characters - * - * @param string $characterList - * @return void - */ - public function setCharacterList($characterList) - { - $this->characterList = $characterList; - } - - /** - * Return filtered value - * Strip characters from the beginning and the end - * - * @param string $value - * @return string - */ - public function filter($value) - { - if ( - $this->characterList === null - || $this->characterList === '' - ) { - return trim((string)$value); - } else { - return trim((string)$value, $this->characterList); - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Filter/UpperCaseFilter.php b/typo3/sysext/form/Classes/Domain/Filter/UpperCaseFilter.php deleted file mode 100644 index d24b96758d66..000000000000 --- a/typo3/sysext/form/Classes/Domain/Filter/UpperCaseFilter.php +++ /dev/null @@ -1,32 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Filter; - -/* - * 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! - */ - -/** - * Uppercase filter - */ -class UpperCaseFilter extends AbstractFilter implements FilterInterface -{ - /** - * Convert alphabetic characters to uppercase - * - * @param string $value - * @return string - */ - public function filter($value) - { - return $this->convertCase($value, 'toUpper'); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/AbstractFinisher.php b/typo3/sysext/form/Classes/Domain/Finishers/AbstractFinisher.php new file mode 100644 index 000000000000..f88c5187e362 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/AbstractFinisher.php @@ -0,0 +1,208 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers; + +/* + * 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\Extbase\Reflection\ObjectAccess; +use TYPO3\CMS\Extbase\Utility\ArrayUtility; +use TYPO3\CMS\Form\Service\TranslationService; +use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; + +/** + * Finisher base class. + * + * Scope: frontend + * **This class is meant to be sub classed by developers** + */ +abstract class AbstractFinisher implements FinisherInterface +{ + + /** + * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface + */ + protected $objectManager; + + /** + * @var string + */ + protected $finisherIdentifier = ''; + + /** + * The options which have been set from the outside. Instead of directly + * accessing them, you should rather use parseOption(). + * + * @var array + */ + protected $options = []; + + /** + * These are the default options of the finisher. + * Override them in your concrete implementation. + * Default options should not be changed from "outside" + * + * @var array + */ + protected $defaultOptions = []; + + /** + * @var \TYPO3\CMS\Form\Domain\Finishers\FinisherContext + */ + protected $finisherContext; + + /** + * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager + * @internal + */ + public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * @param array $options configuration options in the format ['option1' => 'value1', 'option2' => 'value2', ...] + * @return void + * @api + */ + public function setOptions(array $options) + { + $this->options = $options; + } + + /** + * Sets a single finisher option (@see setOptions()) + * + * @param string $optionName name of the option to be set + * @param mixed $optionValue value of the option + * @return void + * @api + */ + public function setOption(string $optionName, $optionValue) + { + $this->options[$optionName] = $optionValue; + } + + /** + * Executes the finisher + * + * @param FinisherContext $finisherContext The Finisher context that contains the current Form Runtime and Response + * @return void + * @api + */ + final public function execute(FinisherContext $finisherContext) + { + $this->finisherIdentifier = (new \ReflectionClass($this))->getShortName(); + $this->finisherContext = $finisherContext; + $this->executeInternal(); + } + + /** + * This method is called in the concrete finisher whenever self::execute() is called. + * + * Override and fill with your own implementation! + * + * @return void + * @api + */ + abstract protected function executeInternal(); + + /** + * Read the option called $optionName from $this->options, and parse {...} + * as object accessors. + * + * Then translate the value. + * + * If $optionName was not found, the corresponding default option is returned (from $this->defaultOptions) + * + * @param string $optionName + * @return string|array|null + * @api + */ + protected function parseOption(string $optionName) + { + if ($optionName === 'translation') { + return null; + } + + $optionValue = ArrayUtility::getValueByPath($this->options, $optionName); + $defaultValue = ArrayUtility::getValueByPath($this->defaultOptions, $optionName); + + if ($optionValue === null && $defaultValue !== null) { + $optionValue = $defaultValue; + } + + if ($optionValue === null) { + return null; + } + + if (is_array($optionValue)) { + return $optionValue; + } + + $formRuntime = $this->finisherContext->getFormRuntime(); + $optionToCompare = $optionValue; + + // You can encapsulate a option value with {}. + // This enables you to access every getable property from the + // TYPO3\CMS\Form\Domain\Runtime. + // + // For example: {formState.formValues.<elemenIdentifier>} + // This is equal to "$formRuntime->getFormState()->getFormValues()[<elemenIdentifier>]" + $optionValue = preg_replace_callback('/{([^}]+)}/', function ($match) use ($formRuntime) { + return ObjectAccess::getPropertyPath($formRuntime, $match[1]); + }, $optionValue); + + if ($optionToCompare === $optionValue) { + + // This is just a shortcut for a {formState.formValues.<elementIdentifier>} notation. + // If one of the finisher option values is equal + // to a identifier from the form definition then + // the value of the submitted form element is used + // insteed. + // Lets say you have a textfield in your form with the + // identifier "Text1". If you put "Text1" + // in the email finisher option "subject" then the submited value + // from the "Text1" element is used as the email subject. + $formValues = $this->finisherContext->getFormValues(); + if (!is_bool($optionValue) && array_key_exists($optionValue, $formValues)) { + $optionValue = $formRuntime[$optionValue]; + } + } + + if (isset($this->options['translation']['translationFile'])) { + $optionValue = TranslationService::getInstance()->translateFinisherOption( + $formRuntime, + $this->finisherIdentifier, + $optionName, + $optionValue, + $this->options['translation'] + ); + } + + if (empty($optionValue)) { + if ($defaultValue !== null) { + $optionValue = $defaultValue; + } + } + return $optionValue; + } + + /** + * @return TypoScriptFrontendController + */ + protected function getTypoScriptFrontendController() + { + return $GLOBALS['TSFE']; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/ClosureFinisher.php b/typo3/sysext/form/Classes/Domain/Finishers/ClosureFinisher.php new file mode 100644 index 000000000000..b4d20cf73f66 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/ClosureFinisher.php @@ -0,0 +1,64 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers; + +/* + * 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\Form\Domain\Finishers\Exception\FinisherException; + +/** + * A simple finisher that invokes a closure when executed + * + * Usage: + * //... + * $closureFinisher = $this->objectManager->get(ClosureFinisher::class); + * $closureFinisher->setOption('closure', function($finisherContext) { + * $formRuntime = $finisherContext->getFormRuntime(); + * // ... + * }); + * $formDefinition->addFinisher($closureFinisher); + * // ... + * + * Scope: frontend + */ +class ClosureFinisher extends AbstractFinisher +{ + + /** + * @var array + */ + protected $defaultOptions = [ + 'closure' => null + ]; + + /** + * Executes this finisher + * @see AbstractFinisher::execute() + * + * @return void + * @throws FinisherException + */ + protected function executeInternal() + { + /** @var $closure \Closure */ + $closure = $this->parseOption('closure'); + if ($closure === null) { + return; + } + if (!$closure instanceof \Closure) { + throw new FinisherException(sprintf('The option "closure" must be of type Closure, "%s" given.', gettype($closure)), 1332155239); + } + $closure($this->finisherContext); + } +} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/ConfirmationFinisher.php b/typo3/sysext/form/Classes/Domain/Finishers/ConfirmationFinisher.php new file mode 100644 index 000000000000..722dfbb17115 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/ConfirmationFinisher.php @@ -0,0 +1,63 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers; + +/* + * 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\Form\Domain\Finishers\Exception\FinisherException; + +/** + * A simple finisher that outputs a given text + * + * Options: + * + * - message: A hard-coded message to be rendered + * + * Usage: + * //... + * $confirmationFinisher = $this->objectManager->get(ConfirmationFinisher::class); + * $confirmationFinisher->setOptions( + * [ + * 'message' => 'foo', + * ] + * ); + * $formDefinition->addFinisher($confirmationFinisher); + * // ... + * + * Scope: frontend + */ +class ConfirmationFinisher extends AbstractFinisher +{ + + /** + * @var array + */ + protected $defaultOptions = [ + 'message' => 'The form has been submitted.', + ]; + + /** + * Executes this finisher + * @see AbstractFinisher::execute() + * + * @return void + * @throws FinisherException + */ + protected function executeInternal() + { + $formRuntime = $this->finisherContext->getFormRuntime(); + $message = $this->parseOption('message'); + $formRuntime->getResponse()->setContent($message); + } +} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/DeleteUploadsFinisher.php b/typo3/sysext/form/Classes/Domain/Finishers/DeleteUploadsFinisher.php new file mode 100644 index 000000000000..43dcd1363e94 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/DeleteUploadsFinisher.php @@ -0,0 +1,57 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers; + +/* + * 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\Extbase\Domain\Model\FileReference; +use TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload; + +/** + * This finisher remove the submited files. + * Use this e.g after the email finisher if you dont want + * to keep the files online. + * + * Scope: frontend + */ +class DeleteUploadsFinisher extends AbstractFinisher +{ + + /** + * Executes this finisher + * @see AbstractFinisher::execute() + * + * @return void + */ + protected function executeInternal() + { + $formRuntime = $this->finisherContext->getFormRuntime(); + + $elements = $formRuntime->getFormDefinition()->getRenderablesRecursively(); + foreach ($elements as $element) { + if (!$element instanceof FileUpload) { + continue; + } + $file = $formRuntime[$element->getIdentifier()]; + if (!$file) { + continue; + } + + if ($file instanceof FileReference) { + $file = $file->getOriginalResource(); + } + $file->getStorage()->deleteFile($file); + } + } +} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/EmailFinisher.php b/typo3/sysext/form/Classes/Domain/Finishers/EmailFinisher.php new file mode 100644 index 000000000000..c2070b49535f --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/EmailFinisher.php @@ -0,0 +1,187 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers; + +/* + * 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\Core\Mail\MailMessage; +use TYPO3\CMS\Extbase\Domain\Model\FileReference; +use TYPO3\CMS\Fluid\View\StandaloneView; +use TYPO3\CMS\Form\Domain\Finishers\Exception\FinisherException; +use TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload; +use TYPO3\CMS\Form\Service\TranslationService; + +/** + * This finisher sends an email to one recipient + * + * Options: + * + * - templatePathAndFilename (mandatory): Template path and filename for the mail body + * - layoutRootPath: root path for the layouts + * - partialRootPath: root path for the partials + * - variables: associative array of variables which are available inside the Fluid template + * + * The following options control the mail sending. In all of them, placeholders in the form + * of {...} are replaced with the corresponding form value; i.e. {email} as recipientAddress + * makes the recipient address configurable. + * + * - subject (mandatory): Subject of the email + * - recipientAddress (mandatory): Email address of the recipient + * - recipientName: Human-readable name of the recipient + * - senderAddress (mandatory): Email address of the sender + * - senderName: Human-readable name of the sender + * - replyToAddress: Email address of to be used as reply-to email (use multiple addresses with an array) + * - carbonCopyAddress: Email address of the copy recipient (use multiple addresses with an array) + * - blindCarbonCopyAddress: Email address of the blind copy recipient (use multiple addresses with an array) + * - format: format of the email (one of the FORMAT_* constants). By default mails are sent as HTML + * + * Scope: frontend + */ +class EmailFinisher extends AbstractFinisher +{ + const FORMAT_PLAINTEXT = 'plaintext'; + const FORMAT_HTML = 'html'; + + /** + * @var array + */ + protected $defaultOptions = [ + 'recipientName' => '', + 'senderName' => '', + 'format' => self::FORMAT_HTML, + 'attachUploads' => true + ]; + + /** + * Executes this finisher + * @see AbstractFinisher::execute() + * + * @return void + * @throws FinisherException + */ + protected function executeInternal() + { + $formRuntime = $this->finisherContext->getFormRuntime(); + $standaloneView = $this->initializeStandaloneView(); + $standaloneView->assign('form', $formRuntime); + + $translationService = TranslationService::getInstance(); + if (isset($this->options['translation']['language']) && !empty($this->options['translation']['language'])) { + $languageBackup = $translationService->getLanguage(); + $translationService->setLanguage($this->options['translation']['language']); + } + $message = $standaloneView->render(); + if (!empty($languageBackup)) { + $translationService->setLanguage($languageBackup); + } + + $subject = $this->parseOption('subject'); + $recipientAddress = $this->parseOption('recipientAddress'); + $recipientName = $this->parseOption('recipientName'); + $senderAddress = $this->parseOption('senderAddress'); + $senderName = $this->parseOption('senderName'); + $replyToAddress = $this->parseOption('replyToAddress'); + $carbonCopyAddress = $this->parseOption('carbonCopyAddress'); + $blindCarbonCopyAddress = $this->parseOption('blindCarbonCopyAddress'); + $format = $this->parseOption('format'); + $attachUploads = $this->parseOption('attachUploads'); + + if (empty($subject)) { + throw new FinisherException('The option "subject" must be set for the EmailFinisher.', 1327060320); + } + if (empty($recipientAddress)) { + throw new FinisherException('The option "recipientAddress" must be set for the EmailFinisher.', 1327060200); + } + if (empty($senderAddress)) { + throw new FinisherException('The option "senderAddress" must be set for the EmailFinisher.', 1327060210); + } + + $mail = $this->objectManager->get(MailMessage::class); + + $mail->setFrom([$senderAddress => $senderName]) + ->setTo([$recipientAddress => $recipientName]) + ->setSubject($subject); + + if (!empty($replyToAddress)) { + $mail->setReplyTo($replyToAddress); + } + + if (!empty($carbonCopyAddress)) { + $mail->setCc($carbonCopyAddress); + } + + if (!empty($blindCarbonCopyAddress)) { + $mail->setBcc($blindCarbonCopyAddress); + } + + if ($format === self::FORMAT_PLAINTEXT) { + $mail->setBody($message, 'text/plain'); + } else { + $mail->setBody($message, 'text/html'); + } + + $elements = $formRuntime->getFormDefinition()->getRenderablesRecursively(); + + if ($attachUploads) { + foreach ($elements as $element) { + if (!$element instanceof FileUpload) { + continue; + } + $file = $formRuntime[$element->getIdentifier()]; + if ($file) { + if ($file instanceof FileReference) { + $file = $file->getOriginalResource(); + } + + $mail->attach(\Swift_Attachment::newInstance($file->getContents(), $file->getName(), $file->getMimeType())); + } + } + } + + $mail->send(); + } + + /** + * @return StandaloneView + * @throws FinisherException + */ + protected function initializeStandaloneView(): StandaloneView + { + if (!isset($this->options['templatePathAndFilename'])) { + throw new FinisherException('The option "templatePathAndFilename" must be set for the EmailFinisher.', 1327058829); + } + + $format = ucfirst($this->parseOption('format')); + + $this->options['templatePathAndFilename'] = strtr($this->options['templatePathAndFilename'], [ + '{@format}' => $format + ]); + + $standaloneView = $this->objectManager->get(StandaloneView::class); + $standaloneView->setTemplatePathAndFilename($this->options['templatePathAndFilename']); + + if (isset($this->options['partialRootPaths']) && is_array($this->options['partialRootPaths'])) { + $standaloneView->setPartialRootPaths($this->options['partialRootPaths']); + } + + if (isset($this->options['layoutRootPaths']) && is_array($this->options['layoutRootPaths'])) { + $standaloneView->setLayoutRootPaths($this->options['layoutRootPaths']); + } + + if (isset($this->options['variables'])) { + $standaloneView->assignMultiple($this->options['variables']); + } + return $standaloneView; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/Exception/FinisherException.php b/typo3/sysext/form/Classes/Domain/Finishers/Exception/FinisherException.php new file mode 100644 index 000000000000..7464f2181088 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/Exception/FinisherException.php @@ -0,0 +1,27 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers\Exception; + +/* + * 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\Form\Domain\Exception; + +/** + * This exception is thrown in Form Finishers + * + * @api + */ +class FinisherException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/FinisherContext.php b/typo3/sysext/form/Classes/Domain/Finishers/FinisherContext.php new file mode 100644 index 000000000000..80a40fb0e961 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/FinisherContext.php @@ -0,0 +1,115 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers; + +/* + * 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\Extbase\Mvc\Controller\ControllerContext; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; + +/** + * The context that is passed to each finisher when executed. + * It acts like an EventObject that is able to stop propagation. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + * @internal + */ +class FinisherContext +{ + + /** + * If TRUE further finishers won't be invoked + * + * @var bool + */ + protected $cancelled = false; + + /** + * A reference to the Form Runtime that the finisher belongs to + * + * @var \TYPO3\CMS\Form\Domain\Runtime\FormRuntime + */ + protected $formRuntime; + + /** + * The assigned controller context which might be needed by the finisher. + * + * @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext + */ + protected $controllerContext; + + /** + * @param FormRuntime $formRuntime + * @internal + */ + public function __construct(FormRuntime $formRuntime, ControllerContext $controllerContext) + { + $this->formRuntime = $formRuntime; + $this->controllerContext = $controllerContext; + } + + /** + * Cancels the finisher invocation after the current finisher + * + * @return void + * @api + */ + public function cancel() + { + $this->cancelled = true; + } + + /** + * TRUE if no futher finishers should be invoked. Defaults to FALSE + * + * @return bool + * @internal + */ + public function isCancelled(): bool + { + return $this->cancelled; + } + + /** + * The Form Runtime that is associated with the current finisher + * + * @return FormRuntime + * @api + */ + public function getFormRuntime(): FormRuntime + { + return $this->formRuntime; + } + + /** + * The values of the submitted form (after validation and property mapping) + * + * @return array + * @api + */ + public function getFormValues(): array + { + return $this->formRuntime->getFormState()->getFormValues(); + } + + /** + * @return ControllerContext + * @api + */ + public function getControllerContext(): ControllerContext + { + return $this->controllerContext; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/FinisherInterface.php b/typo3/sysext/form/Classes/Domain/Finishers/FinisherInterface.php new file mode 100644 index 000000000000..c5c9683973a0 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/FinisherInterface.php @@ -0,0 +1,52 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers; + +/* + * 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! + */ + +/** + * Finisher that can be attached to a form in order to be invoked + * as soon as the complete form is submitted + * + * Scope: frontend + */ +interface FinisherInterface +{ + + /** + * Executes the finisher + * + * @param FinisherContext $finisherContext The Finisher context that contains the current Form Runtime and Response + * @return void + * @api + */ + public function execute(FinisherContext $finisherContext); + + /** + * @param array $options configuration options in the format ['option1' => 'value1', 'option2' => 'value2', ...] + * @return void + * @api + */ + public function setOptions(array $options); + + /** + * Sets a single finisher option (@see setOptions()) + * + * @param string $optionName name of the option to be set + * @param mixed $optionValue value of the option + * @return void + * @api + */ + public function setOption(string $optionName, $optionValue); +} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/FlashMessageFinisher.php b/typo3/sysext/form/Classes/Domain/Finishers/FlashMessageFinisher.php new file mode 100644 index 000000000000..5856a0988fa0 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/FlashMessageFinisher.php @@ -0,0 +1,97 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers; + +/* + * 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\Core\Messaging\AbstractMessage; +use TYPO3\CMS\Core\Messaging\FlashMessage; +use TYPO3\CMS\Extbase\Error\Error; +use TYPO3\CMS\Extbase\Error\Message; +use TYPO3\CMS\Extbase\Error\Notice; +use TYPO3\CMS\Extbase\Error\Warning; +use TYPO3\CMS\Form\Domain\Finishers\Exception\FinisherException; + +/** + * A simple finisher that adds a message to the FlashMessageContainer + * + * Usage: + * //... + * $flashMessageFinisher = $this->objectManager->get(FlashMessageFinisher::class); + * $flashMessageFinisher->setOptions( + * [ + * 'messageBody' => 'Some message body', + * 'messageTitle' => 'Some message title', + * 'messageArguments' => ['foo' => 'bar'], + * 'severity' => \TYPO3\CMS\Core\Messaging\AbstractMessage::ERROR + * ] + * ); + * $formDefinition->addFinisher($flashMessageFinisher); + * // ... + * + * Scope: frontend + */ +class FlashMessageFinisher extends AbstractFinisher +{ + + /** + * @var array + */ + protected $defaultOptions = [ + 'messageBody' => null, + 'messageTitle' => '', + 'messageArguments' => [], + 'messageCode' => null, + 'severity' => AbstractMessage::OK, + ]; + + /** + * Executes this finisher + * @see AbstractFinisher::execute() + * + * @return void + * @throws FinisherException + */ + protected function executeInternal() + { + $messageBody = $this->parseOption('messageBody'); + if (!is_string($messageBody)) { + throw new FinisherException(sprintf('The message body must be of type string, "%s" given.', gettype($messageBody)), 1335980069); + } + $messageTitle = $this->parseOption('messageTitle'); + $messageArguments = $this->parseOption('messageArguments'); + $messageCode = $this->parseOption('messageCode'); + $severity = $this->parseOption('severity'); + switch ($severity) { + case AbstractMessage::NOTICE: + $message = $this->objectManager->get(Notice::class, $messageBody, $messageCode, $messageArguments, $messageTitle); + break; + case AbstractMessage::WARNING: + $message = $this->objectManager->get(Warning::class, $messageBody, $messageCode, $messageArguments, $messageTitle); + break; + case AbstractMessage::ERROR: + $message = $this->objectManager->get(Error::class, $messageBody, $messageCode, $messageArguments, $messageTitle); + break; + default: + $message = $this->objectManager->get(Message::class, $messageBody, $messageCode, $messageArguments, $messageTitle); + break; + } + + $flashMessage = $this->objectManager->get(FlashMessage::class, + $message->render(), $message->getTitle(), $severity, true + ); + + $this->finisherContext->getControllerContext()->getFlashMessageQueue()->addMessage($flashMessage); + } +} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/RedirectFinisher.php b/typo3/sysext/form/Classes/Domain/Finishers/RedirectFinisher.php new file mode 100644 index 000000000000..0d20268f11b5 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/RedirectFinisher.php @@ -0,0 +1,151 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Mvc\Exception\StopActionException; +use TYPO3\CMS\Extbase\Mvc\Exception\UnsupportedRequestTypeException; +use TYPO3\CMS\Extbase\Mvc\Web\Request; +use TYPO3\CMS\Extbase\Mvc\Web\Response; +use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; + +/** + * This finisher redirects to another Controller. + * + * Scope: frontend + */ +class RedirectFinisher extends AbstractFinisher +{ + + /** + * @var array + */ + protected $defaultOptions = [ + 'pageUid' => 1, + 'additionalParameters' => '', + 'delay' => 0, + 'statusCode' => 303, + ]; + + /** + * @var \TYPO3\CMS\Extbase\Mvc\Web\Request + */ + protected $request; + + /** + * @var \TYPO3\CMS\Extbase\Mvc\Web\Response + */ + protected $response; + + /** + * @var \TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder + */ + protected $uriBuilder; + + /** + * Executes this finisher + * @see AbstractFinisher::execute() + * + * @return void + */ + protected function executeInternal() + { + $formRuntime = $this->finisherContext->getFormRuntime(); + $this->request = $formRuntime->getRequest(); + $this->response = $formRuntime->getResponse(); + $this->uriBuilder = $this->objectManager->get(UriBuilder::class); + $this->uriBuilder->setRequest($this->request); + + $pageUid = (int)str_replace('pages_', '', $this->parseOption('pageUid')); + $additionalParameters = $this->parseOption('additionalParameters'); + $additionalParameters = '&' . ltrim($additionalParameters, '&'); + $delay = (int)$this->parseOption('delay'); + $statusCode = (int)$this->parseOption('statusCode'); + + $this->finisherContext->cancel(); + $this->redirect($pageUid, $additionalParameters, $delay, $statusCode); + } + + /** + * Redirects the request to another page. + * + * Redirect will be sent to the client which then performs another request to the new URI. + * + * NOTE: This method only supports web requests and will thrown an exception + * if used with other request types. + * + * @param int $pageUid Target page uid. If NULL, the current page uid is used + * @param string $additionalParameters + * @param int $delay (optional) The delay in seconds. Default is no delay. + * @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other + * @return void + * @throws UnsupportedRequestTypeException If the request is not a web request + * @see forward() + */ + protected function redirect(int $pageUid = 1, string $additionalParameters = '', int $delay = 0, int $statusCode = 303) + { + if (!$this->request instanceof Request) { + throw new UnsupportedRequestTypeException('redirect() only supports web requests.', 1471776457); + } + + $typolinkConfiguration = [ + 'parameter' => $pageUid, + 'additionalParams' => $additionalParameters, + ]; + $redirectUri = $this->getTypoScriptFrontendController()->cObj->typoLink_URL($typolinkConfiguration); + $this->redirectToUri($redirectUri, $delay, $statusCode); + } + + /** + * Redirects the web request to another uri. + * + * NOTE: This method only supports web requests and will thrown an exception if used with other request types. + * + * @param string $uri A string representation of a URI + * @param int $delay (optional) The delay in seconds. Default is no delay. + * @param int $statusCode (optional) The HTTP status code for the redirect. Default is "303 See Other + * @throws UnsupportedRequestTypeException If the request is not a web request + * @throws StopActionException + */ + protected function redirectToUri(string $uri, int $delay = 0, int $statusCode = 303) + { + if (!$this->request instanceof Request) { + throw new UnsupportedRequestTypeException('redirect() only supports web requests.', 1471776458); + } + + $uri = $this->addBaseUriIfNecessary($uri); + $escapedUri = htmlentities($uri, ENT_QUOTES, 'utf-8'); + + $this->response->setContent('<html><head><meta http-equiv="refresh" content="' . (int)$delay . ';url=' . $escapedUri . '"/></head></html>'); + if ($this->response instanceof \TYPO3\CMS\Extbase\Mvc\Web\Response) { + $this->response->setStatus($statusCode); + $this->response->setHeader('Location', (string)$uri); + } + echo $this->response->shutdown(); + throw new StopActionException('redirectToUri', 1477070964); + } + + /** + * Adds the base uri if not already in place. + * + * @param string $uri The URI + * @return string + */ + protected function addBaseUriIfNecessary(string $uri): string + { + return GeneralUtility::locationHeaderUrl((string)$uri); + } +} diff --git a/typo3/sysext/form/Classes/Domain/Finishers/SaveToDatabaseFinisher.php b/typo3/sysext/form/Classes/Domain/Finishers/SaveToDatabaseFinisher.php new file mode 100644 index 000000000000..bb7eb27a7f16 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Finishers/SaveToDatabaseFinisher.php @@ -0,0 +1,100 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Finishers; + +/* + * 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\Core\Database\ConnectionPool; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Domain\Model\FileReference; +use TYPO3\CMS\Form\Domain\Finishers\Exception\FinisherException; +use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; + +/** + * This finisher saves the data from a submitted form into + * a database table. + * + * Scope: frontend + */ +class SaveToDatabaseFinisher extends AbstractFinisher +{ + + /** + * @var array + */ + protected $defaultOptions = [ + 'table' => null, + 'elements' => [], + ]; + + /** + * Executes this finisher + * @see AbstractFinisher::execute() + * + * @return void + * @throws FinisherException + */ + protected function executeInternal() + { + $table = $this->parseOption('table'); + $elementsConfiguration = $this->parseOption('elements'); + + $databaseConnection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable($table); + $schemaManager = $databaseConnection->getSchemaManager(); + + if ($schemaManager->tablesExist([$table]) === false) { + throw new FinisherException('The table "' . $table . '" does not exist.', 1476362091); + } + + $databaseColumns = $schemaManager->listTableColumns($table); + foreach ($elementsConfiguration as $elementIdentifier => $elementConfiguration) { + if (!array_key_exists($elementConfiguration['mapOnDatabaseColumn'], $databaseColumns)) { + throw new FinisherException('The column "' . $elementConfiguration['mapOnDatabaseColumn'] . '" does not exist in table "' . $table . '".', 1476362572); + } + } + + $formRuntime = $this->finisherContext->getFormRuntime(); + + $insertData = []; + foreach ($this->finisherContext->getFormValues() as $elementIdentifier => $elementValue) { + $element = $formRuntime->getFormDefinition()->getElementByIdentifier($elementIdentifier); + if ( + !$element instanceof FormElementInterface + || !isset($elementsConfiguration[$elementIdentifier]) + || !isset($elementsConfiguration[$elementIdentifier]['mapOnDatabaseColumn']) + ) { + continue; + } + + if ($elementValue instanceof FileReference) { + if (isset($elementsConfiguration[$elementIdentifier]['saveFileIdentifierInsteadOfUid'])) { + $saveFileIdentifierInsteadOfUid = (bool)$elementsConfiguration[$elementIdentifier]['saveFileIdentifierInsteadOfUid']; + } else { + $saveFileIdentifierInsteadOfUid = false; + } + + if ($saveFileIdentifierInsteadOfUid) { + $elementValue = $elementValue->getOriginalResource()->getCombinedIdentifier(); + } else { + $elementValue = $elementValue->getOriginalResource()->getProperty('uid_local'); + } + } + $insertData[$elementsConfiguration[$elementIdentifier]['mapOnDatabaseColumn']] = $elementValue; + } + + if (!empty($insertData)) { + $databaseConnection->insert($table, $insertData); + } + } +} diff --git a/typo3/sysext/form/Classes/Domain/Model/Configuration.php b/typo3/sysext/form/Classes/Domain/Model/Configuration.php deleted file mode 100644 index f923ab8e35fc..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Configuration.php +++ /dev/null @@ -1,174 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model; - -/* - * 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! - */ - -/** - * The Configuration model is a high-level API - * for the underlying TypoScript configuration. - */ -class Configuration -{ - /** - * @var string - */ - const DISABLE_CONTENT_ELEMENT_RENDERING = 'disableContentElement'; - - /** - * @var string - */ - const DEFAULT_THEME_NAME = 'Default'; - - /** - * @return Configuration - */ - public static function create() - { - return \TYPO3\CMS\Form\Utility\FormUtility::getObjectManager()->get(self::class); - } - - /** - * @var array - */ - protected $typoScript = []; - - /** - * @var bool - */ - protected $contentElementRendering = false; - - /** - * @var string - */ - protected $prefix = 'form'; - - /** - * @var string - */ - protected $themeName = ''; - - /** - * @var \TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository - */ - protected $typoScriptRepository; - - /** - * @param \TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository - * @return void - */ - public function injectTypoScriptRepository(\TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository $typoScriptRepository) - { - $this->typoScriptRepository = $typoScriptRepository; - } - - /** - * @return array - */ - public function getTypoScript() - { - return $this->typoScript; - } - - /** - * @param array $typoScript - * @return Configuration - */ - public function setTypoScript(array $typoScript) - { - $this->typoScript = $typoScript; - $this->update(); - return $this; - } - - public function getContentElementRendering() - { - return $this->contentElementRendering; - } - - /** - * @param $contentElementRendering - * @return Configuration - */ - public function setContentElementRendering($contentElementRendering) - { - $this->contentElementRendering = (bool)$contentElementRendering; - return $this; - } - - /** - * @return string - */ - public function getPrefix() - { - return $this->prefix; - } - - /** - * @param string $prefix - * @return Configuration - */ - public function setPrefix($prefix) - { - $this->prefix = (string)$prefix; - return $this; - } - - /** - * @return string - */ - public function getThemeName() - { - return $this->themeName; - } - - /** - * @param string $themeName - * @return Configuration - */ - public function setThemeName($themeName = '') - { - if ($themeName === '') { - $themeName = static::DEFAULT_THEME_NAME; - } - $this->themeName = $themeName; - return $this; - } - - /** - * Updates the local properties - called after - * new TypoScript has been assigned in this object. - */ - protected function update() - { - // Determine content rendering mode. If activated, cObject and stdWrap can be - // used to execute various processes that must not be allowed on TypoScript - // that has been created by non-privileged backend users (= insecure TypoScript) - $this->setContentElementRendering( - empty($this->typoScript[static::DISABLE_CONTENT_ELEMENT_RENDERING]) - ); - // Determine the HTML form element prefix to distinguish - // different form components on the same page in the frontend - if (!empty($this->typoScript['prefix'])) { - $this->setPrefix($this->typoScript['prefix']); - } - // Set the theme name - if (!empty($this->typoScript['themeName'])) { - $this->setThemeName($this->typoScript['themeName']); - } elseif (!empty($this->typoScriptRepository->getModelConfigurationByScope('FORM', 'themeName'))) { - $this->setThemeName($this->typoScriptRepository->getModelConfigurationByScope('FORM', 'themeName')); - } else { - $this->setThemeName(static::DEFAULT_THEME_NAME); - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Content.php b/typo3/sysext/form/Classes/Domain/Model/Content.php deleted file mode 100644 index c171aa4664f3..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Content.php +++ /dev/null @@ -1,133 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model; - -/* - * 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! - */ - -/** - * Content domain model - */ -class Content -{ - /** - * The uid - * - * @var int - */ - protected $uid = 0; - - /** - * The page id - * - * @var int - */ - protected $pageId = 0; - - /** - * The configuration Typoscript - * - * @var array - */ - protected $typoscript = []; - - /** - * The plain bodytext - * - * @var string - */ - protected $bodytext = ''; - - /** - * Sets the uid - * - * @param int $uid The uid - * @return void - */ - public function setUid($uid) - { - $this->uid = (int)$uid; - } - - /** - * Returns the uid - * - * @return int The uid - */ - public function getUid() - { - return $this->uid; - } - - /** - * Sets the page id - * - * @param int $pageId The page id - * @return void - */ - public function setPageId($pageId) - { - $this->pageId = (int)$pageId; - } - - /** - * Returns the page id - * - * @return int The page id - */ - public function getPageId() - { - return $this->pageId; - } - - /** - * Sets the Typoscript configuration - * - * @param array $typoscript The Typoscript configuration - * @return void - */ - public function setTyposcript(array $typoscript) - { - $this->typoscript = (array)$typoscript; - } - - /** - * Returns the Typoscript configuration - * - * @return array The Typoscript configuration - */ - public function getTyposcript() - { - return $this->typoscript; - } - - /** - * Sets the bodytext - * - * @param string $bodytext The bodytext - * @return void - */ - public function setBodytext($bodytext = '') - { - $this->bodytext = $bodytext; - } - - /** - * Returns the bodytext - * - * @return string The bodytext - */ - public function getBodytext() - { - return $this->bodytext; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Element.php b/typo3/sysext/form/Classes/Domain/Model/Element.php deleted file mode 100644 index 6f2941ed7430..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Element.php +++ /dev/null @@ -1,485 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model; - -/* - * 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\Extbase\DomainObject\AbstractEntity; -use TYPO3\CMS\Extbase\Persistence\ObjectStorage; - -/** - * The Element Domain Model represents the high-level - * view on the user submitted data using a nested hierarchy. - */ -class Element extends AbstractEntity -{ - /** - * This array holds all the additional arguments to use it in the template - * - * @var array - */ - protected $additionalArguments; - - /** - * child elements - * - * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Form\Domain\Model\Element> - */ - protected $childElements; - - /** - * A global counter over all elements - * - * @var int - */ - protected $elementCounter; - - /** - * The element type (e.g BUTTON) - * - * @var string - */ - protected $elementType; - - /** - * The validation error messages - * - * @var array - */ - protected $validationErrorMessages; - - /** - * This array holds all the element html attributes with their values - * - * @var array - */ - protected $htmlAttributes; - - /** - * The id attribute - * - * @var string - */ - protected $id; - - /** - * The mandatory validation messages - * - * @var array - */ - protected $mandatoryValidationMessages; - - /** - * The name attribute - * - * @var string - */ - protected $name; - - /** - * parent element - * - * @var \TYPO3\CMS\Form\Domain\Model\Element - */ - protected $parentElement; - - /** - * The fluid partial for the element - * - * @var string - */ - protected $partialPath; - - /** - * TRUE if the element should be displayed - * - * @var bool - */ - protected $showElement; - - /** - * The theme name - * - * @var string - */ - protected $themeName; - - /** - * Creates an instance. - */ - public function __construct() - { - $this->initStorageObjects(); - } - - /** - * Initializes all ObjectStorage properties. - * - * @return void - */ - protected function initStorageObjects() - { - $this->childElements = new ObjectStorage(); - } - - /** - * Return a array with all the additional arguments to use it in the template - * - * @return array - */ - public function getAdditionalArguments() - { - return $this->additionalArguments; - } - - /** - * Sets a array with all the additional arguments to use it in the template - * - * @param array $additionalArguments - * @return void - */ - public function setAdditionalArguments($additionalArguments = []) - { - $this->additionalArguments = $additionalArguments; - } - - /** - * Get a single attribute value - * - * @param string $key - * @return array - */ - public function getAdditionalArgument($key = '') - { - return $this->additionalArguments[$key]; - } - - /** - * Set a single attribute and value - * - * @param string $key - * @param mixed $value - * @return array - */ - public function setAdditionalArgument($key = '', $value = null) - { - $this->additionalArguments[$key] = $value; - } - - /** - * Adds a child element - * - * @param \TYPO3\CMS\Form\Domain\Model\Element $element - * @return void - */ - public function addChildElement(Element $element) - { - $this->childElements->attach($element); - } - - /** - * Returns the child elements - * - * @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Form\Domain\Model\Element> $element - */ - public function getChildElements() - { - return $this->childElements; - } - - /** - * Sets the child elements - * - * @param \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\TYPO3\CMS\Form\Domain\Model\Element> $childElements - * @return void - */ - public function setChildElements(ObjectStorage $childElements) - { - $this->childElements = $childElements; - } - - /** - * Returns the element counter - * - * @return int - */ - public function getElementCounter() - { - return $this->elementCounter; - } - - /** - * Sets the element counter - * - * @param int $elementCounter - * @return void - */ - public function setElementCounter($elementCounter = 0) - { - $this->elementCounter = $elementCounter; - } - - /** - * Returns the element type - * - * @return string - */ - public function getElementType() - { - return $this->elementType; - } - - /** - * Returns the element type in lower case - * - * @return string - */ - public function getElementTypeLowerCase() - { - return strtolower($this->elementType); - } - - /** - * Sets the parent element - * - * @param string $elementType - * @return void - */ - public function setElementType($elementType) - { - $this->elementType = (string)$elementType; - } - - /** - * Returns the validation error messages - * - * @return array - */ - public function getValidationErrorMessages() - { - return $this->validationErrorMessages; - } - - /** - * Sets the validation error messages - * - * @param array $validationErrorMessages - * @return void - */ - public function setValidationErrorMessages(array $validationErrorMessages) - { - $this->validationErrorMessages = $validationErrorMessages; - } - - /** - * Returns the element html attributes and values - * - * @return array - */ - public function getHtmlAttributes() - { - return $this->htmlAttributes; - } - - /** - * Sets the element html attributes and values - * - * @param array $htmlAttributes - * @return void - */ - public function setHtmlAttributes($htmlAttributes = []) - { - $this->htmlAttributes = $htmlAttributes; - } - - /** - * Remove a single html attribute - * - * @param string $key - * @return void - */ - public function removeHtmlAttribute($key = '') - { - unset($this->htmlAttributes[$key]); - } - - /** - * Get a single html attribute value - * - * @param string $key - * @return array - */ - public function getHtmlAttribute($key = '') - { - return $this->htmlAttributes[$key]; - } - - /** - * Set a single html attribute and value - * - * @param string $key - * @param mixed $value - * @return array - */ - public function setHtmlAttribute($key = '', $value = null) - { - $this->htmlAttributes[$key] = $value; - } - - /** - * Returns the id attribute - * - * @return string - */ - public function getId() - { - return $this->id; - } - - /** - * Sets the id attribute - * - * @param string $id - * @return void - */ - public function setId($id) - { - $this->id = (string)$id; - } - - /** - * Returns the mandatory validation messages - * - * @return array - */ - public function getMandatoryValidationMessages() - { - return $this->mandatoryValidationMessages; - } - - /** - * Sets the mandatory validation messages - * - * @param array $mandatoryValidationMessages - * @return void - */ - public function setMandatoryValidationMessages(array $mandatoryValidationMessages) - { - $this->mandatoryValidationMessages = $mandatoryValidationMessages; - } - - /** - * Returns the name attribute - * - * @return string - */ - public function getName() - { - return $this->name; - } - - /** - * Sets the name attribute - * - * @param string $name - * @return void - */ - public function setName($name) - { - $this->name = (string)$name; - } - - /** - * Returns the parent element - * - * @return Element - */ - public function getParentElement() - { - return $this->parentElement; - } - - /** - * Sets the parent element - * - * @param \TYPO3\CMS\Form\Domain\Model\Element - * @return void - */ - public function setParentElement(Element $parentElement) - { - $this->parentElement = $parentElement; - } - - /** - * Returns the fluid partial path for the element - * - * @return string - */ - public function getPartialPath() - { - return $this->partialPath; - } - - /** - * Sets the fluid partial path for the element - * - * @param string $partialPath - * @return void - */ - public function setPartialPath($partialPath) - { - $this->partialPath = (string)$partialPath; - } - - /** - * Returns TRUE if the element should be displayed - * - * @return bool - */ - public function getShowElement() - { - return $this->showElement; - } - - /** - * TRUE if the element should be displayed - * - * @param bool $showElement - * @return void - */ - public function setShowElement($showElement = false) - { - $this->showElement = $showElement; - } - - /** - * Set the theme name - * - * @param string - * @return $themeName - */ - public function setThemeName($themeName = 'Default') - { - $this->themeName = $themeName; - } - - /** - * Returns the theme name - * - * @return string - */ - public function getThemeName() - { - return $this->themeName; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Exception.php b/typo3/sysext/form/Classes/Domain/Model/Exception.php new file mode 100644 index 000000000000..5704857b7478 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/Exception.php @@ -0,0 +1,27 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model; + +/* + * 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\Form\Domain\Exception as DomainException; + +/** + * A generic Form model Exception + * + * @api + */ +class Exception extends DomainException +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Model/Exception/DuplicateFormElementException.php b/typo3/sysext/form/Classes/Domain/Model/Exception/DuplicateFormElementException.php new file mode 100644 index 000000000000..83f718abf26e --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/Exception/DuplicateFormElementException.php @@ -0,0 +1,28 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\Exception; + +/* + * 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\Form\Domain\Model\Exception; + +/** + * This exception is thrown if two Form Elements with the same Identifier are added + * to a form. + * + * @api + */ +class DuplicateFormElementException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Model/Exception/FinisherPresetNotFoundException.php b/typo3/sysext/form/Classes/Domain/Model/Exception/FinisherPresetNotFoundException.php new file mode 100644 index 000000000000..5200d0e0cb81 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/Exception/FinisherPresetNotFoundException.php @@ -0,0 +1,28 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\Exception; + +/* + * 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\Form\Domain\Model\Exception; + +/** + * This exception is thrown if a Finisher Preset was not found, + * or if the implementationClassName was not set. + * + * @api + */ +class FinisherPresetNotFoundException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Model/Exception/FormDefinitionConsistencyException.php b/typo3/sysext/form/Classes/Domain/Model/Exception/FormDefinitionConsistencyException.php new file mode 100644 index 000000000000..d04d41cd272b --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/Exception/FormDefinitionConsistencyException.php @@ -0,0 +1,28 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\Exception; + +/* + * 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\Form\Domain\Model\Exception; + +/** + * This exception is thrown if the form definition would get an inconsistent state, like + * adding a page to two different forms + * + * @api + */ +class FormDefinitionConsistencyException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Model/Exception/ValidatorPresetNotFoundException.php b/typo3/sysext/form/Classes/Domain/Model/Exception/ValidatorPresetNotFoundException.php new file mode 100644 index 000000000000..cfacf8e2e383 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/Exception/ValidatorPresetNotFoundException.php @@ -0,0 +1,28 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\Exception; + +/* + * 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\Form\Domain\Model\Exception; + +/** + * This exception is thrown if a Validator Preset was not found, + * or if the implementationClassName was not set. + * + * @api + */ +class ValidatorPresetNotFoundException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Model/FormDefinition.php b/typo3/sysext/form/Classes/Domain/Model/FormDefinition.php new file mode 100644 index 000000000000..d619d70c47e6 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/FormDefinition.php @@ -0,0 +1,695 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model; + +/* + * 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\Core\Utility\ArrayUtility as CoreArrayUtility; +use TYPO3\CMS\Extbase\Mvc\Web\Request; +use TYPO3\CMS\Extbase\Mvc\Web\Response; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Reflection\ObjectAccess; +use TYPO3\CMS\Extbase\Utility\ArrayUtility; +use TYPO3\CMS\Form\Domain\Exception\IdentifierNotValidException; +use TYPO3\CMS\Form\Domain\Exception\TypeDefinitionNotFoundException; +use TYPO3\CMS\Form\Domain\Finishers\FinisherInterface; +use TYPO3\CMS\Form\Domain\Model\Exception\DuplicateFormElementException; +use TYPO3\CMS\Form\Domain\Model\Exception\FinisherPresetNotFoundException; +use TYPO3\CMS\Form\Domain\Model\Exception\FormDefinitionConsistencyException; +use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; +use TYPO3\CMS\Form\Domain\Model\FormElements\Page; +use TYPO3\CMS\Form\Domain\Model\Renderable\AbstractCompositeRenderable; +use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; +use TYPO3\CMS\Form\Exception as FormException; +use TYPO3\CMS\Form\Mvc\ProcessingRule; +use TYPO3\CMS\Form\Utility\ArrayUtility as FormArrayUtility; + +/** + * This class encapsulates a complete *Form Definition*, with all of its pages, + * form elements, validation rules which apply and finishers which should be + * executed when the form is completely filled in. + * + * It is *not modified* when the form executes. + * + * The Anatomy Of A Form + * ===================== + * + * A FormDefinition consists of multiple *Page* ({@link Page}) objects. When a + * form is displayed to the user, only one *Page* is visible at any given time, + * and there is a navigation to go back and forth between the pages. + * + * A *Page* consists of multiple *FormElements* ({@link FormElementInterface}, {@link AbstractFormElement}), + * which represent the input fields, textareas, checkboxes shown inside the page. + * + * *FormDefinition*, *Page* and *FormElement* have *identifier* properties, which + * must be unique for each given type (i.e. it is allowed that the FormDefinition and + * a FormElement have the *same* identifier, but two FormElements are not allowed to + * have the same identifier. + * + * Simple Example + * -------------- + * + * Generally, you can create a FormDefinition manually by just calling the API + * methods on it, or you use a *Form Definition Factory* to build the form from + * another representation format such as YAML. + * + * /---code php + * $formDefinition = $this->objectManager->get(FormDefinition::class, 'myForm'); + * + * $page1 = $this->objectManager->get(Page::class, 'page1'); + * $formDefinition->addPage($page); + * + * $element1 = $this->objectManager->get(GenericFormElement::class, 'title', 'Textfield'); # the second argument is the type of the form element + * $page1->addElement($element1); + * \--- + * + * Creating a Form, Using Abstract Form Element Types + * ===================================================== + * + * While you can use the {@link FormDefinition::addPage} or {@link Page::addElement} + * methods and create the Page and FormElement objects manually, it is often better + * to use the corresponding create* methods ({@link FormDefinition::createPage} + * and {@link Page::createElement}), as you pass them an abstract *Form Element Type* + * such as *Text* or *Page*, and the system **automatically + * resolves the implementation class name and sets default values**. + * + * So the simple example from above should be rewritten as follows: + * + * /---code php + * $prototypeConfiguration = []; // We'll talk about this later + * + * $formDefinition = $this->objectManager->get(FormDefinition::class, 'myForm', $prototypeConfiguration); + * $page1 = $formDefinition->createPage('page1'); + * $element1 = $page1->addElement('title', 'Textfield'); + * \--- + * + * Now, you might wonder how the system knows that the element *Textfield* + * is implemented using a GenericFormElement: **This is configured in the $prototypeConfiguration**. + * + * To make the example from above actually work, we need to add some sensible + * values to *$prototypeConfiguration*: + * + * <pre> + * $prototypeConfiguration = [ + * 'formElementsDefinition' => [ + * 'Page' => [ + * 'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\Page' + * ], + * 'Textfield' => [ + * 'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement' + * ] + * ] + * ] + * </pre> + * + * For each abstract *Form Element Type* we add some configuration; in the above + * case only the *implementation class name*. Still, it is possible to set defaults + * for *all* configuration options of such an element, as the following example + * shows: + * + * <pre> + * $prototypeConfiguration = [ + * 'formElementsDefinition' => [ + * 'Page' => [ + * 'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\Page', + * 'label' => 'this is the label of the page if nothing is specified' + * ], + * 'Textfield' => [ + * 'implementationClassName' => 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement', + * 'label' = >'Default Label', + * 'defaultValue' => 'Default form element value', + * 'properties' => [ + * 'placeholder' => 'Text which is shown if element is empty' + * ] + * ] + * ] + * ] + * </pre> + * + * Using Preconfigured $prototypeConfiguration + * --------------------------------- + * + * Often, it is not really useful to manually create the $prototypeConfiguration array. + * + * Most of it comes pre-configured inside the extensions's yaml settings, + * and the {@link \TYPO3\CMS\Form\Domain\Configuration\ConfigurationService} contains helper methods + * which return the ready-to-use *$prototypeConfiguration*. + * + * Property Mapping and Validation Rules + * ===================================== + * + * Besides Pages and FormElements, the FormDefinition can contain information + * about the *format of the data* which is inputted into the form. This generally means: + * + * - expected Data Types + * - Property Mapping Configuration to be used + * - Validation Rules which should apply + * + * Background Info + * --------------- + * You might wonder why Data Types and Validation Rules are *not attached + * to each FormElement itself*. + * + * If the form should create a *hierarchical output structure* such as a multi- + * dimensional array or a PHP object, your expected data structure might look as follows: + * <pre> + * - person + * -- firstName + * -- lastName + * -- address + * --- street + * --- city + * </pre> + * + * Now, let's imagine you want to edit *person.address.street* and *person.address.city*, + * but want to validate that the *combination* of *street* and *city* is valid + * according to some address database. + * + * In this case, the form elements would be configured to fill *street* and *city*, + * but the *validator* needs to be attached to the *compound object* *address*, + * as both parts need to be validated together. + * + * Connecting FormElements to the output data structure + * ==================================================== + * + * The *identifier* of the *FormElement* is most important, as it determines + * where in the output structure the value which is entered by the user is placed, + * and thus also determines which validation rules need to apply. + * + * Using the above example, if you want to create a FormElement for the *street*, + * you should use the identifier *person.address.street*. + * + * Rendering a FormDefinition + * ========================== + * + * In order to trigger *rendering* on a FormDefinition, + * the current {@link \TYPO3\CMS\Extbase\Mvc\Web\Request} needs to be bound to the FormDefinition, + * resulting in a {@link \TYPO3\CMS\Form\Domain\Runtime\FormRuntime} object which contains the *Runtime State* of the form + * (such as the currently inserted values). + * + * /---code php + * # $currentRequest and $currentResponse need to be available, f.e. inside a controller you would + * # use $this->request and $this->response; inside a ViewHelper you would use $this->controllerContext->getRequest() + * # and $this->controllerContext->getResponse() + * $form = $formDefinition->bind($currentRequest, $currentResponse); + * + * # now, you can use the $form object to get information about the currently + * # entered values into the form, etc. + * \--- + * + * Refer to the {@link \TYPO3\CMS\Form\Domain\Runtime\FormRuntime} API doc for further information. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + */ +class FormDefinition extends AbstractCompositeRenderable +{ + + /** + * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface + */ + protected $objectManager; + + /** + * The finishers for this form + * + * @var \TYPO3\CMS\Form\Domain\Finishers\FinisherInterface[] + */ + protected $finishers = []; + + /** + * Property Mapping Rules, indexed by element identifier + * + * @var \TYPO3\CMS\Form\Mvc\ProcessingRule[] + */ + protected $processingRules = []; + + /** + * Contains all elements of the form, indexed by identifier. + * Is used as internal cache as we need this really often. + * + * @var \TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface[] + */ + protected $elementsByIdentifier = []; + + /** + * Form element default values in the format ['elementIdentifier' => 'default value'] + * + * @var array + */ + protected $elementDefaultValues = []; + + /** + * @var array + */ + protected $typeDefinitions; + + /** + * @var array + */ + protected $validatorsDefinition; + + /** + * @var array + */ + protected $finishersDefinition; + + /** + * The persistence identifier of the form + * + * @var string + */ + protected $persistenceIdentifier = null; + + /** + * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager + * @internal + */ + public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * Constructor. Creates a new FormDefinition with the given identifier. + * + * @param string $identifier The Form Definition's identifier, must be a non-empty string. + * @param array $prototypeConfiguration overrides form defaults of this definition + * @param string $type element type of this form + * @param string $persistenceIdentifier the persistence identifier of the form + * @throws IdentifierNotValidException if the identifier was not valid + * @api + */ + public function __construct( + string $identifier, + array $prototypeConfiguration = [], + string $type = 'Form', + string $persistenceIdentifier = null + ) { + $this->typeDefinitions = isset($prototypeConfiguration['formElementsDefinition']) ? $prototypeConfiguration['formElementsDefinition'] : []; + $this->validatorsDefinition = isset($prototypeConfiguration['validatorsDefinition']) ? $prototypeConfiguration['validatorsDefinition'] : []; + $this->finishersDefinition = isset($prototypeConfiguration['finishersDefinition']) ? $prototypeConfiguration['finishersDefinition'] : []; + + if (!is_string($identifier) || strlen($identifier) === 0) { + throw new IdentifierNotValidException('The given identifier was not a string or the string was empty.', 1477082503); + } + + $this->identifier = $identifier; + $this->type = $type; + $this->persistenceIdentifier = $persistenceIdentifier; + + if ($prototypeConfiguration !== []) { + $this->initializeFromFormDefaults(); + } + } + + /** + * Initialize the form defaults of the current type + * + * @return void + * @throws TypeDefinitionNotFoundException + * @internal + */ + protected function initializeFromFormDefaults() + { + if (!isset($this->typeDefinitions[$this->type])) { + throw new TypeDefinitionNotFoundException(sprintf('Type "%s" not found. Probably some configuration is missing.', $this->type), 1474905835); + } + $typeDefinition = $this->typeDefinitions[$this->type]; + $this->setOptions($typeDefinition); + } + + /** + * Set multiple properties of this object at once. + * Every property which has a corresponding set* method can be set using + * the passed $options array. + * + * @param array $options + * @return void + * @internal + */ + public function setOptions(array $options) + { + if (isset($options['rendererClassName'])) { + $this->setRendererClassName($options['rendererClassName']); + } + if (isset($options['renderingOptions'])) { + foreach ($options['renderingOptions'] as $key => $value) { + if (is_array($value)) { + $currentValue = isset($this->getRenderingOptions()[$key]) ? $this->getRenderingOptions()[$key] : []; + CoreArrayUtility::mergeRecursiveWithOverrule($currentValue, $value); + $this->setRenderingOption($key, $currentValue); + } else { + $this->setRenderingOption($key, $value); + } + } + } + if (isset($options['finishers'])) { + foreach ($options['finishers'] as $finisherConfiguration) { + $this->createFinisher($finisherConfiguration['identifier'], isset($finisherConfiguration['options']) ? $finisherConfiguration['options'] : []); + } + } + + FormArrayUtility::assertAllArrayKeysAreValid($options, ['rendererClassName', 'renderingOptions', 'finishers', 'formEditor']); + } + + /** + * Create a page with the given $identifier and attach this page to the form. + * + * - Create Page object based on the given $typeName + * - set defaults inside the Page object + * - attach Page object to this form + * - return the newly created Page object + * + * @param string $identifier Identifier of the new page + * @param string $typeName Type of the new page + * @return Page the newly created page + * @throws TypeDefinitionNotFoundException + * @api + */ + public function createPage(string $identifier, string $typeName = 'Page'): Page + { + if (!isset($this->typeDefinitions[$typeName])) { + throw new TypeDefinitionNotFoundException(sprintf('Type "%s" not found. Probably some configuration is missing.', $typeName), 1474905953); + } + + $typeDefinition = $this->typeDefinitions[$typeName]; + + if (!isset($typeDefinition['implementationClassName'])) { + throw new TypeDefinitionNotFoundException(sprintf('The "implementationClassName" was not set in type definition "%s".', $typeName), 1477083126); + } + $implementationClassName = $typeDefinition['implementationClassName']; + $page = $this->objectManager->get($implementationClassName, $identifier, $typeName); + + if (isset($typeDefinition['label'])) { + $page->setLabel($typeDefinition['label']); + } + + if (isset($typeDefinition['rendererClassName'])) { + $page->setRendererClassName($typeDefinition['rendererClassName']); + } + + if (isset($typeDefinition['renderingOptions'])) { + foreach ($typeDefinition['renderingOptions'] as $key => $value) { + $page->setRenderingOption($key, $value); + } + } + + FormArrayUtility::assertAllArrayKeysAreValid($typeDefinition, ['implementationClassName', 'label', 'rendererClassName', 'renderingOptions', 'formEditor']); + + $this->addPage($page); + return $page; + } + + /** + * Add a new page at the end of the form. + * + * Instead of this method, you should often use {@link createPage} instead. + * + * @param Page $page + * @return void + * @throws FormDefinitionConsistencyException if Page is already added to a FormDefinition + * @see createPage + * @api + */ + public function addPage(Page $page) + { + $this->addRenderable($page); + } + + /** + * Get the Form's pages + * + * @return array<Page> The Form's pages in the correct order + * @api + */ + public function getPages(): array + { + return $this->renderables; + } + + /** + * Check whether a page with the given $index exists + * + * @param int $index + * @return bool TRUE if a page with the given $index exists, otherwise FALSE + * @api + */ + public function hasPageWithIndex(int $index): bool + { + return isset($this->renderables[$index]); + } + + /** + * Get the page with the passed index. The first page has index zero. + * + * If page at $index does not exist, an exception is thrown. @see hasPageWithIndex() + * + * @param int $index + * @return Page the page, or NULL if none found. + * @throws FormException if the specified index does not exist + * @api + */ + public function getPageByIndex(int $index) + { + if (!$this->hasPageWithIndex($index)) { + throw new FormException(sprintf('There is no page with an index of %d', $index), 1329233627); + } + return $this->renderables[$index]; + } + + /** + * Adds the specified finisher to this form + * + * @param FinisherInterface $finisher + * @return void + * @api + */ + public function addFinisher(FinisherInterface $finisher) + { + $this->finishers[] = $finisher; + } + + /** + * @param string $finisherIdentifier identifier of the finisher as registered in the current form (for example: "Redirect") + * @param array $options options for this finisher in the format ['option1' => 'value1', 'option2' => 'value2', ...] + * @return FinisherInterface + * @throws FinisherPresetNotFoundException + * @api + */ + public function createFinisher(string $finisherIdentifier, array $options = []): FinisherInterface + { + if (isset($this->finishersDefinition[$finisherIdentifier]) && is_array($this->finishersDefinition[$finisherIdentifier]) && isset($this->finishersDefinition[$finisherIdentifier]['implementationClassName'])) { + $implementationClassName = $this->finishersDefinition[$finisherIdentifier]['implementationClassName']; + $defaultOptions = isset($this->finishersDefinition[$finisherIdentifier]['options']) ? $this->finishersDefinition[$finisherIdentifier]['options'] : []; + CoreArrayUtility::mergeRecursiveWithOverrule($defaultOptions, $options); + + $finisher = $this->objectManager->get($implementationClassName); + $finisher->setOptions($defaultOptions); + $this->addFinisher($finisher); + return $finisher; + } else { + throw new FinisherPresetNotFoundException('The finisher preset identified by "' . $finisherIdentifier . '" could not be found, or the implementationClassName was not specified.', 1328709784); + } + } + + /** + * Gets all finishers of this form + * + * @return \TYPO3\CMS\Form\Domain\Finishers\FinisherInterface[] + * @api + */ + public function getFinishers(): array + { + return $this->finishers; + } + + /** + * Add an element to the ElementsByIdentifier Cache. + * + * @param RenderableInterface $renderable + * @return void + * @throws DuplicateFormElementException + * @internal + */ + public function registerRenderable(RenderableInterface $renderable) + { + if ($renderable instanceof FormElementInterface) { + if (isset($this->elementsByIdentifier[$renderable->getIdentifier()])) { + throw new DuplicateFormElementException(sprintf('A form element with identifier "%s" is already part of the form.', $renderable->getIdentifier()), 1325663761); + } + $this->elementsByIdentifier[$renderable->getIdentifier()] = $renderable; + } + } + + /** + * Remove an element from the ElementsByIdentifier cache + * + * @param RenderableInterface $renderable + * @return void + * @internal + */ + public function unregisterRenderable(RenderableInterface $renderable) + { + if ($renderable instanceof FormElementInterface) { + unset($this->elementsByIdentifier[$renderable->getIdentifier()]); + } + } + + /** + * Get a Form Element by its identifier + * + * If identifier does not exist, returns NULL. + * + * @param string $elementIdentifier + * @return FormElementInterface The element with the given $elementIdentifier or NULL if none found + * @api + */ + public function getElementByIdentifier(string $elementIdentifier) + { + return isset($this->elementsByIdentifier[$elementIdentifier]) ? $this->elementsByIdentifier[$elementIdentifier] : null; + } + + /** + * Sets the default value of a form element + * + * @param string $elementIdentifier identifier of the form element. This supports property paths! + * @param mixed $defaultValue + * @return void + * @internal + */ + public function addElementDefaultValue(string $elementIdentifier, $defaultValue) + { + $this->elementDefaultValues = ArrayUtility::setValueByPath($this->elementDefaultValues, $elementIdentifier, $defaultValue); + } + + /** + * returns the default value of the specified form element + * or NULL if no default value was set + * + * @param string $elementIdentifier identifier of the form element. This supports property paths! + * @return mixed The elements default value + * @internal + */ + public function getElementDefaultValueByIdentifier(string $elementIdentifier) + { + return ObjectAccess::getPropertyPath($this->elementDefaultValues, $elementIdentifier); + } + + /** + * Move $pageToMove before $referencePage + * + * @param Page $pageToMove + * @param Page $referencePage + * @return void + * @api + */ + public function movePageBefore(Page $pageToMove, Page $referencePage) + { + $this->moveRenderableBefore($pageToMove, $referencePage); + } + + /** + * Move $pageToMove after $referencePage + * + * @param Page $pageToMove + * @param Page $referencePage + * @return void + * @api + */ + public function movePageAfter(Page $pageToMove, Page $referencePage) + { + $this->moveRenderableAfter($pageToMove, $referencePage); + } + + /** + * Remove $pageToRemove from form + * + * @param Page $pageToRemove + * @return void + * @api + */ + public function removePage(Page $pageToRemove) + { + $this->removeRenderable($pageToRemove); + } + + /** + * Bind the current request & response to this form instance, effectively creating + * a new "instance" of the Form. + * + * @param Request $request + * @param Response $response + * @return FormRuntime + * @api + */ + public function bind(Request $request, Response $response): FormRuntime + { + return $this->objectManager->get(FormRuntime::class, $this, $request, $response); + } + + /** + * @param string $propertyPath + * @return ProcessingRule + * @api + */ + public function getProcessingRule(string $propertyPath): ProcessingRule + { + if (!isset($this->processingRules[$propertyPath])) { + $this->processingRules[$propertyPath] = $this->objectManager->get(ProcessingRule::class); + } + return $this->processingRules[$propertyPath]; + } + + /** + * Get all mapping rules + * + * @return \TYPO3\CMS\Form\Mvc\ProcessingRule[] + * @internal + */ + public function getProcessingRules(): array + { + return $this->processingRules; + } + + /** + * @return array + * @internal + */ + public function getTypeDefinitions(): array + { + return $this->typeDefinitions; + } + + /** + * @return array + * @internal + */ + public function getValidatorsDefinition(): array + { + return $this->validatorsDefinition; + } + + /** + * Get the persistence identifier of the form + * + * @return string + * @internal + */ + public function getPersistenceIdentifier(): string + { + return $this->persistenceIdentifier; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Model/FormElements/AbstractFormElement.php b/typo3/sysext/form/Classes/Domain/Model/FormElements/AbstractFormElement.php new file mode 100644 index 000000000000..f977a975ada5 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/FormElements/AbstractFormElement.php @@ -0,0 +1,168 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\FormElements; + +/* + * 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\Extbase\Validation\Validator\NotEmptyValidator; +use TYPO3\CMS\Form\Domain\Exception\IdentifierNotValidException; +use TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; + +/** + * A base form element, which is the starting point for creating custom (PHP-based) + * Form Elements. + * + * A *FormElement* is a part of a *Page*, which in turn is part of a FormDefinition. + * See {@link FormDefinition} for an in-depth explanation. + * + * Subclassing this class is a good starting-point for implementing custom PHP-based + * Form Elements. + * + * Most of the functionality and API is implemented in {@link \TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable}, so + * make sure to check out this class as well. + * + * Still, it is quite rare that you need to subclass this class; often + * you can just use the {@link \TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement} and replace some templates. + * + * Scope: frontend + * **This class is meant to be sub classed by developers.** + */ +abstract class AbstractFormElement extends AbstractRenderable implements FormElementInterface +{ + + /** + * @var array + */ + protected $properties = []; + + /** + * Constructor. Needs this FormElement's identifier and the FormElement type + * + * @param string $identifier The FormElement's identifier + * @param string $type The Form Element Type + * @throws IdentifierNotValidException + * @api + */ + public function __construct(string $identifier, string $type) + { + if (!is_string($identifier) || strlen($identifier) === 0) { + throw new IdentifierNotValidException('The given identifier was not a string or the string was empty.', 1477082502); + } + $this->identifier = $identifier; + $this->type = $type; + } + + /** + * Override this method in your custom FormElements if needed + * + * @return void + * @api + */ + public function initializeFormElement() + { + } + + /** + * Get the global unique identifier of the element + * + * @return string + * @api + */ + public function getUniqueIdentifier(): string + { + $formDefinition = $this->getRootForm(); + $uniqueIdentifier = sprintf('%s-%s', $formDefinition->getIdentifier(), $this->identifier); + $uniqueIdentifier = preg_replace('/[^a-zA-Z0-9-_]/', '_', $uniqueIdentifier); + return lcfirst($uniqueIdentifier); + } + + /** + * Get the default value of the element + * + * @return mixed + * @api + */ + public function getDefaultValue() + { + $formDefinition = $this->getRootForm(); + return $formDefinition->getElementDefaultValueByIdentifier($this->identifier); + } + + /** + * Set the default value of the element + * + * @param mixed $defaultValue + * @return void + * @api + */ + public function setDefaultValue($defaultValue) + { + $formDefinition = $this->getRootForm(); + $formDefinition->addElementDefaultValue($this->identifier, $defaultValue); + } + + /** + * Check if the element is required + * + * @return bool + * @api + */ + public function isRequired(): bool + { + foreach ($this->getValidators() as $validator) { + if ($validator instanceof NotEmptyValidator) { + return true; + } + } + return false; + } + + /** + * Set a property of the element + * + * @param string $key + * @param mixed $value + * @return void + * @api + */ + public function setProperty(string $key, $value) + { + $this->properties[$key] = $value; + } + + /** + * Get all properties + * + * @return array + * @api + */ + public function getProperties(): array + { + return $this->properties; + } + + /** + * Override this method in your custom FormElements if needed + * + * @param FormRuntime $formRuntime + * @param mixed $elementValue + * @param array $requestArguments submitted raw request values + * @return void + * @api + */ + public function onSubmit(FormRuntime $formRuntime, &$elementValue, array $requestArguments = []) + { + } +} diff --git a/typo3/sysext/form/Classes/Domain/Model/FormElements/AbstractSection.php b/typo3/sysext/form/Classes/Domain/Model/FormElements/AbstractSection.php new file mode 100644 index 000000000000..2754c6d1f715 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/FormElements/AbstractSection.php @@ -0,0 +1,199 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\FormElements; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Domain\Exception\IdentifierNotValidException; +use TYPO3\CMS\Form\Domain\Exception\TypeDefinitionNotFoundException; +use TYPO3\CMS\Form\Domain\Exception\TypeDefinitionNotValidException; +use TYPO3\CMS\Form\Domain\Model\Exception\FormDefinitionConsistencyException; +use TYPO3\CMS\Form\Domain\Model\Renderable\AbstractCompositeRenderable; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; + +/** + * A base class for "section-like" form parts like "Page" or "Section" (which + * is rendered as "Fieldset") + * + * This class contains multiple FormElements ({@link FormElementInterface}). + * + * Please see {@link FormDefinition} for an in-depth explanation. + * + * **This class is NOT meant to be sub classed by developers.** + * Scope: frontend + */ +abstract class AbstractSection extends AbstractCompositeRenderable +{ + + /** + * Constructor. Needs the identifier and type of this element + * + * @param string $identifier The Section identifier + * @param string $type The Section type + * @throws IdentifierNotValidException if the identifier was no non-empty string + * @api + */ + public function __construct(string $identifier, string $type) + { + if (!is_string($identifier) || strlen($identifier) === 0) { + throw new IdentifierNotValidException('The given identifier was not a string or the string was empty.', 1477082501); + } + + $this->identifier = $identifier; + $this->type = $type; + } + + /** + * Get the child Form Elements + * + * @return FormElementInterface[] The Page's elements + * @api + */ + public function getElements(): array + { + return $this->renderables; + } + + /** + * Get the child Form Elements + * + * @return FormElementInterface[] The Page's elements + * @api + */ + public function getElementsRecursively(): array + { + return $this->getRenderablesRecursively(); + } + + /** + * Add a new form element at the end of the section + * + * @param FormElementInterface $formElement The form element to add + * @return void + * @throws FormDefinitionConsistencyException if FormElement is already added to a section + * @api + */ + public function addElement(FormElementInterface $formElement) + { + $this->addRenderable($formElement); + } + + /** + * Create a form element with the given $identifier and attach it to this section/page. + * + * - Create Form Element object based on the given $typeName + * - set defaults inside the Form Element (based on the parent form's field defaults) + * - attach Form Element to this Section/Page + * - return the newly created Form Element object + * + * + * @param string $identifier Identifier of the new form element + * @param string $typeName type of the new form element + * @return FormElementInterface the newly created form element + * @throws TypeDefinitionNotFoundException + * @throws TypeDefinitionNotValidException + * @api + */ + public function createElement(string $identifier, string $typeName): FormElementInterface + { + $formDefinition = $this->getRootForm(); + + $typeDefinitions = $formDefinition->getTypeDefinitions(); + if (isset($typeDefinitions[$typeName])) { + $typeDefinition = $typeDefinitions[$typeName]; + } else { + $element = GeneralUtility::makeInstance(ObjectManager::class) + ->get(UnknownFormElement::class, $identifier, $typeName); + $this->addElement($element); + return $element; + } + + if (!isset($typeDefinition['implementationClassName'])) { + throw new TypeDefinitionNotFoundException(sprintf('The "implementationClassName" was not set in type definition "%s".', $typeName), 1325689855); + } + + $implementationClassName = $typeDefinition['implementationClassName']; + $element = GeneralUtility::makeInstance(ObjectManager::class) + ->get($implementationClassName, $identifier, $typeName); + if (!$element instanceof FormElementInterface) { + throw new TypeDefinitionNotValidException(sprintf('The "implementationClassName" for element "%s" ("%s") does not implement the FormElementInterface.', $identifier, $implementationClassName), 1327318156); + } + unset($typeDefinition['implementationClassName']); + + $this->addElement($element); + $element->setOptions($typeDefinition); + + $element->initializeFormElement(); + return $element; + } + + /** + * Move FormElement $element before $referenceElement. + * + * Both $element and $referenceElement must be direct descendants of this Section/Page. + * + * @param FormElementInterface $elementToMove + * @param FormElementInterface $referenceElement + * @return void + * @api + */ + public function moveElementBefore(FormElementInterface $elementToMove, FormElementInterface $referenceElement) + { + $this->moveRenderableBefore($elementToMove, $referenceElement); + } + + /** + * Move FormElement $element after $referenceElement + * + * Both $element and $referenceElement must be direct descendants of this Section/Page. + * + * @param FormElementInterface $elementToMove + * @param FormElementInterface $referenceElement + * @return void + * @api + */ + public function moveElementAfter(FormElementInterface $elementToMove, FormElementInterface $referenceElement) + { + $this->moveRenderableAfter($elementToMove, $referenceElement); + } + + /** + * Remove $elementToRemove from this Section/Page + * + * @param FormElementInterface $elementToRemove + * @return void + * @api + */ + public function removeElement(FormElementInterface $elementToRemove) + { + $this->removeRenderable($elementToRemove); + } + + /** + * This callback is invoked by the FormRuntime whenever values are mapped and validated + * (after a form page was submitted) + * @see FormRuntime::mapAndValidate() + * + * @param FormRuntime $formRuntime + * @param mixed $elementValue submitted value of the element *before post processing* + * @param array $requestArguments submitted raw request values + * @return void + * @api + */ + public function onSubmit(FormRuntime $formRuntime, &$elementValue, array $requestArguments = []) + { + } +} diff --git a/typo3/sysext/form/Classes/Domain/Model/FormElements/AdvancedPassword.php b/typo3/sysext/form/Classes/Domain/Model/FormElements/AdvancedPassword.php new file mode 100644 index 000000000000..206abb9fd80e --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/FormElements/AdvancedPassword.php @@ -0,0 +1,53 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\FormElements; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Error\Error; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; + +/** + * A password with confirmation form element + * + * Scope: frontend + */ +class AdvancedPassword extends AbstractFormElement +{ + + /** + * This callback is invoked by the FormRuntime whenever values are mapped and validated + * (after a form page was submitted) + * + * @param FormRuntime $formRuntime + * @param mixed $elementValue submitted value of the element *before post processing* + * @param array $requestArguments submitted raw request values + * @return void + * @see FormRuntime::mapAndValidate() + * @internal + */ + public function onSubmit(FormRuntime $formRuntime, &$elementValue, array $requestArguments = []) + { + if ($elementValue['password'] !== $elementValue['confirmation']) { + $processingRule = $this->getRootForm()->getProcessingRule($this->getIdentifier()); + $processingRule->getProcessingMessages()->addError( + GeneralUtility::makeInstance(ObjectManager::class) + ->get(Error::class, 'Password doesn\'t match confirmation', 1334768052) + ); + } + $elementValue = $elementValue['password']; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Filter/IntegerFilter.php b/typo3/sysext/form/Classes/Domain/Model/FormElements/DatePicker.php similarity index 61% rename from typo3/sysext/form/Classes/Domain/Filter/IntegerFilter.php rename to typo3/sysext/form/Classes/Domain/Model/FormElements/DatePicker.php index 24bc8fe80103..5aea0b31c056 100644 --- a/typo3/sysext/form/Classes/Domain/Filter/IntegerFilter.php +++ b/typo3/sysext/form/Classes/Domain/Model/FormElements/DatePicker.php @@ -1,5 +1,6 @@ <?php -namespace TYPO3\CMS\Form\Domain\Filter; +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\FormElements; /* * This file is part of the TYPO3 CMS project. @@ -15,18 +16,19 @@ namespace TYPO3\CMS\Form\Domain\Filter; */ /** - * Integer filter + * A date picker form element + * + * Scope: frontend */ -class IntegerFilter extends AbstractFilter implements FilterInterface +class DatePicker extends AbstractFormElement { + /** - * Cast to integer - * - * @param string $value - * @return string + * @return void + * @internal */ - public function filter($value) + public function initializeFormElement() { - return (int)((string)$value); + $this->setDataType('DateTime'); } } diff --git a/typo3/sysext/form/Classes/Domain/Model/FormElements/FileUpload.php b/typo3/sysext/form/Classes/Domain/Model/FormElements/FileUpload.php new file mode 100644 index 000000000000..0bfe7132aa2c --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/FormElements/FileUpload.php @@ -0,0 +1,109 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\FormElements; + +/* + * 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\Core\Resource\ResourceFactory; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\PathUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter; +use TYPO3\CMS\Form\Mvc\Validation\MimeTypeValidator; + +/** + * A generic file upload form element + * + * Scope: frontend + */ +class FileUpload extends AbstractFormElement +{ + + /** + * @return void + * @internal + */ + public function initializeFormElement() + { + $this->setDataType('TYPO3\CMS\Extbase\Domain\Model\FileReference'); + } + + /** + * Set the property mapping configuration for the file upload element. + * * Add the UploadedFileReferenceConverter to convert an uploaded file to an + * FileReference. + * * Add the MimeTypeValidator to the UploadedFileReferenceConverter to + * delete non valid filetypes directly. + * * Setup the storage: + * If the property "saveToFileMount" exist for this element it will be used. + * If this file mount or the property "saveToFileMount" does not exist + * the folder in which the form definition lies (persistence identifier) will be used. + * If the form is generated programmatically and therefore no + * persistence identifier exist the default storage "1:/user_upload/" will be used. + * + * @return void + * @internal + * @todo: could we find a not so ugly solution for that? + */ + public function onBuildingFinished() + { + /** @var \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $propertyMappingConfiguration */ + $propertyMappingConfiguration = $this->getRootForm()->getProcessingRule($this->getIdentifier())->getPropertyMappingConfiguration(); + + $mimeTypeValidator = GeneralUtility::makeInstance(ObjectManager::class) + ->get(MimeTypeValidator::class, ['allowedMimeTypes' => $this->properties['allowedMimeTypes']]); + $uploadConfiguration = [ + UploadedFileReferenceConverter::CONFIGURATION_FILE_VALIDATORS => [$mimeTypeValidator], + UploadedFileReferenceConverter::CONFIGURATION_UPLOAD_CONFLICT_MODE => 'rename', + ]; + + $saveToFileMountIdentifier = (isset($this->properties['saveToFileMount'])) ? $this->properties['saveToFileMount'] : null; + if ($this->checkSaveFileMountAccess($saveToFileMountIdentifier)) { + $uploadConfiguration[UploadedFileReferenceConverter::CONFIGURATION_UPLOAD_FOLDER] = $saveToFileMountIdentifier; + } else { + $persistenceIdentifier = $this->getRootForm()->getPersistenceIdentifier(); + if (!empty($persistenceIdentifier)) { + $pathinfo = PathUtility::pathinfo($persistenceIdentifier); + $saveToFileMountIdentifier = $pathinfo['dirname']; + if ($this->checkSaveFileMountAccess($saveToFileMountIdentifier)) { + $uploadConfiguration[UploadedFileReferenceConverter::CONFIGURATION_UPLOAD_FOLDER] = $saveToFileMountIdentifier; + } + } + } + + $propertyMappingConfiguration->setTypeConverterOptions(UploadedFileReferenceConverter::class, $uploadConfiguration); + } + + /** + * @param string $saveToFileMountIdentifier + * @return bool + * @internal + */ + protected function checkSaveFileMountAccess(string $saveToFileMountIdentifier): bool + { + if (empty($saveToFileMountIdentifier)) { + return false; + } + + $resourceFactory = GeneralUtility::makeInstance(ObjectManager::class) + ->get(ResourceFactory::class); + + try { + $resourceFactory->getFolderObjectFromCombinedIdentifier($saveToFileMountIdentifier); + return true; + } catch (\InvalidArgumentException $e) { + return false; + } + } +} diff --git a/typo3/sysext/form/Classes/Domain/Model/FormElements/FormElementInterface.php b/typo3/sysext/form/Classes/Domain/Model/FormElements/FormElementInterface.php new file mode 100644 index 000000000000..15413677133d --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/FormElements/FormElementInterface.php @@ -0,0 +1,147 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\FormElements; + +/* + * 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\Extbase\Validation\Validator\ValidatorInterface; +use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; + +/** + * A base form element interface, which can be the starting point for creating + * custom (PHP-based) Form Elements. + * + * A *FormElement* is a part of a *Page*, which in turn is part of a FormDefinition. + * See {@link FormDefinition} for an in-depth explanation. + * + * **Often, you should rather subclass {@link AbstractFormElement} instead of + * implementing this interface.** + * + * Scope: frontend + */ +interface FormElementInterface extends RenderableInterface +{ + + /** + * Will be called as soon as the element is (tried to be) added to a form + * @see registerInFormIfPossible() + * + * @return void + * @internal + */ + public function initializeFormElement(); + + /** + * Returns a unique identifier of this element. + * While element identifiers are only unique within one form, + * this includes the identifier of the form itself, making it "globally" unique + * + * @return string the "globally" unique identifier of this element + * @api + */ + public function getUniqueIdentifier(): string; + + /** + * Get the default value with which the Form Element should be initialized + * during display. + * + * @return mixed the default value for this Form Element + * @api + */ + public function getDefaultValue(); + + /** + * Set the default value with which the Form Element should be initialized + * during display. + * + * @param mixed $defaultValue the default value for this Form Element + * @api + */ + public function setDefaultValue($defaultValue); + + /** + * Set an element-specific configuration property. + * + * @param string $key + * @param mixed $value + * @return void + * @api + */ + public function setProperty(string $key, $value); + + /** + * Get all element-specific configuration properties + * + * @return array + * @api + */ + public function getProperties(): array; + + /** + * Set a rendering option + * + * @param string $key + * @param mixed $value + * @api + */ + public function setRenderingOption(string $key, $value); + + /** + * Returns the child validators of the ConjunctionValidator that is registered for this element + * + * @return \SplObjectStorage<ValidatorInterface> + * @internal + */ + public function getValidators(): \SplObjectStorage; + + /** + * Registers a validator for this element + * + * @param ValidatorInterface $validator + * @return void + * @api + */ + public function addValidator(ValidatorInterface $validator); + + /** + * Set the target data type for this element + * + * @param string $dataType the target data type + * @return void + * @api + */ + public function setDataType(string $dataType); + + /** + * Whether or not this element is required + * + * @return bool + * @api + */ + public function isRequired(): bool; + + /** + * This callback is invoked by the FormRuntime whenever values are mapped and validated + * (after a form page was submitted) + * + * @param FormRuntime $formRuntime + * @param mixed $elementValue submitted value of the element *before post processing* + * @param array $requestArguments submitted raw request values + * @return void + * @see FormRuntime::mapAndValidate() + * @api + */ + public function onSubmit(FormRuntime $formRuntime, &$elementValue, array $requestArguments = []); +} diff --git a/typo3/sysext/form/Classes/Domain/Model/FormElements/GenericFormElement.php b/typo3/sysext/form/Classes/Domain/Model/FormElements/GenericFormElement.php new file mode 100644 index 000000000000..05119f810076 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/FormElements/GenericFormElement.php @@ -0,0 +1,26 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\FormElements; + +/* + * 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! + */ + +/** + * A generic form element + * + * Scope: frontend + * @api + */ +class GenericFormElement extends AbstractFormElement +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Model/FormElements/Page.php b/typo3/sysext/form/Classes/Domain/Model/FormElements/Page.php new file mode 100644 index 000000000000..568c23c3c71f --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/FormElements/Page.php @@ -0,0 +1,68 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\FormElements; + +/* + * 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\Form\Domain\Model\FormDefinition; +use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface; +use TYPO3\CMS\Form\Exception as FormException; + +/** + * A Page, being part of a bigger FormDefinition. It contains numerous FormElements + * as children. + * + * A FormDefinition consists of multiple Pages, where only one page is visible + * at any given time. + * + * Most of the API of this object is implemented in {@link AbstractSection}, + * so make sure to review this class as well. + * + * Please see {@link FormDefinition} for an in-depth explanation. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + */ +class Page extends AbstractSection +{ + + /** + * Constructor. Needs this Page's identifier + * + * @param string $identifier The Page's identifier + * @param string $type The Page's type + * @throws \TYPO3\CMS\Form\Domain\Exception\IdentifierNotValidException if the identifier was no non-empty string + * @api + */ + public function __construct(string $identifier, string $type = 'Page') + { + parent::__construct($identifier, $type); + } + + /** + * Set the parent renderable + * + * @param CompositeRenderableInterface $parentRenderable + * @return void + * @throws FormException + * @api + */ + public function setParentRenderable(CompositeRenderableInterface $parentRenderable) + { + if (!($parentRenderable instanceof FormDefinition)) { + throw new FormException(sprintf('The specified parentRenderable must be a FormDefinition, got "%s"', is_object($parentRenderable) ? get_class($parentRenderable) : gettype($parentRenderable)), 1329233747); + } + parent::setParentRenderable($parentRenderable); + } +} diff --git a/typo3/sysext/form/Classes/Domain/Model/FormElements/Section.php b/typo3/sysext/form/Classes/Domain/Model/FormElements/Section.php new file mode 100644 index 000000000000..61ee7864f719 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/FormElements/Section.php @@ -0,0 +1,166 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\FormElements; + +/* + * 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\Extbase\Validation\Validator\NotEmptyValidator; +use TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface; + +/** + * A Section, being part of a bigger Page + * + * This class contains multiple FormElements ({@link FormElementInterface}). + * + * Please see {@link FormDefinition} for an in-depth explanation. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + */ +class Section extends AbstractSection implements FormElementInterface +{ + + /** + * @var array + */ + protected $properties = []; + + /** + * Will be called as soon as the element is (tried to be) added to a form + * @see registerInFormIfPossible() + * + * @return void + * @internal + */ + public function initializeFormElement() + { + } + + /** + * Returns a unique identifier of this element. + * While element identifiers are only unique within one form, + * this includes the identifier of the form itself, making it "globally" unique + * + * @return string the "globally" unique identifier of this element + * @api + */ + public function getUniqueIdentifier(): string + { + $formDefinition = $this->getRootForm(); + return sprintf('%s-%s', $formDefinition->getIdentifier(), $this->identifier); + } + + /** + * Get the default value with which the Form Element should be initialized + * during display. + * Note: This is currently not used for section elements + * + * @return mixed the default value for this Form Element + * @api + */ + public function getDefaultValue() + { + return null; + } + + /** + * Set the default value with which the Form Element should be initialized + * during display. + * Note: This is currently ignored for section elements + * + * @param mixed $defaultValue the default value for this Form Element + * @api + */ + public function setDefaultValue($defaultValue) + { + } + + /** + * Get all element-specific configuration properties + * + * @return array + * @api + */ + public function getProperties(): array + { + return $this->properties; + } + + /** + * Set an element-specific configuration property. + * + * @param string $key + * @param mixed $value + * @return void + * @api + */ + public function setProperty(string $key, $value) + { + $this->properties[$key] = $value; + } + + /** + * Set the rendering option $key to $value. + * + * @param string $key + * @param mixed $value + * @return mixed + * @api + */ + public function setRenderingOption(string $key, $value) + { + $this->renderingOptions[$key] = $value; + } + + /** + * Get all validators on the element + * + * @return \SplObjectStorage + * @internal + */ + public function getValidators(): \SplObjectStorage + { + $formDefinition = $this->getRootForm(); + return $formDefinition->getProcessingRule($this->getIdentifier())->getValidators(); + } + + /** + * Add a validator to the element + * + * @param ValidatorInterface $validator + * @return void + * @api + */ + public function addValidator(ValidatorInterface $validator) + { + $formDefinition = $this->getRootForm(); + $formDefinition->getProcessingRule($this->getIdentifier())->addValidator($validator); + } + + /** + * Whether or not this element is required + * + * @return bool + * @api + */ + public function isRequired(): bool + { + foreach ($this->getValidators() as $validator) { + if ($validator instanceof NotEmptyValidator) { + return true; + } + } + return false; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Model/FormElements/UnknownFormElement.php b/typo3/sysext/form/Classes/Domain/Model/FormElements/UnknownFormElement.php new file mode 100644 index 000000000000..08ab78e29be0 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/FormElements/UnknownFormElement.php @@ -0,0 +1,147 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\FormElements; + +/* + * 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\Form\Domain\Exception\IdentifierNotValidException; +use TYPO3\CMS\Form\Domain\Model\Renderable\AbstractRenderable; +use TYPO3\CMS\Form\Domain\Renderer\UnknownFormElementRenderer; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; + +/** + * A Form Element that has no definition. + * + * Scope: frontend + */ +class UnknownFormElement extends AbstractRenderable implements FormElementInterface +{ + + /** + * Constructor. Needs this FormElement's identifier and the FormElement type + * + * @param string $identifier The FormElement's identifier + * @param string $type The Form Element Type + * @throws IdentifierNotValidException + * @api + */ + public function __construct(string $identifier, string $type) + { + if (!is_string($identifier) || strlen($identifier) === 0) { + throw new IdentifierNotValidException('The given identifier was not a string or the string was empty.', 1382364370); + } + $this->identifier = $identifier; + $this->type = $type; + } + + /** + * Returns a unique identifier of this element. + * While element identifiers are only unique within one form, + * this includes the identifier of the form itself, making it "globally" unique + * + * @return string the "globally" unique identifier of this element + * @api + */ + public function getUniqueIdentifier(): string + { + $formDefinition = $this->getRootForm(); + $uniqueIdentifier = sprintf('%s-%s', $formDefinition->getIdentifier(), $this->identifier); + $uniqueIdentifier = preg_replace('/[^a-zA-Z0-9-_]/', '_', $uniqueIdentifier); + return lcfirst($uniqueIdentifier); + } + + /** + * Unknown Form Elements are rendered with the UnknownFormElementRenderer + * + * @return string the renderer class name + * @internal + */ + public function getRendererClassName(): string + { + return UnknownFormElementRenderer::class; + } + + /** + * Not used in this implementation + * + * @return void + * @internal + */ + public function initializeFormElement() + { + } + + /** + * @return mixed the default value for this Form Element + * @internal + */ + public function getDefaultValue() + { + return null; + } + + /** + * Not used in this implementation + * + * @param mixed $defaultValue the default value for this Form Element + * @internal + */ + public function setDefaultValue($defaultValue) + { + } + + /** + * Not used in this implementation + * + * @param string $key + * @param mixed $value + * @return void + * @internal + */ + public function setProperty(string $key, $value) + { + } + + /** + * @return array + * @internal + */ + public function getProperties(): array + { + return []; + } + + /** + * @return bool + * @internal + */ + public function isRequired(): bool + { + return false; + } + + /** + * Not used in this implementation + * + * @param FormRuntime $formRuntime + * @param mixed $elementValue submitted value of the element *before post processing* + * @param array $requestArguments submitted raw request values + * @return void + * @see FormRuntime::mapAndValidate() + * @internal + */ + public function onSubmit(FormRuntime $formRuntime, &$elementValue, array $requestArguments = []) + { + } +} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/AbstractJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/AbstractJsonElement.php deleted file mode 100644 index f61431d417c4..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/AbstractJsonElement.php +++ /dev/null @@ -1,178 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON element abstract - */ -class AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = ''; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = []; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = []; - - /** - * Child elements allowed withing this element - * - * Some elements like select handle their own child elements - * - * @var bool - */ - protected $childElementsAllowed = true; - - /** - * Set all the parameters for this object - * - * @param array $parameters Configuration array - * @return void - */ - public function setParameters(array $parameters) - { - foreach ($this->configuration as $key => $value) { - switch ($key) { - case 'attributes': - $this->setAttributes($parameters); - break; - case 'filters': - $this->setFilters($parameters); - break; - case 'label': - $this->setLabel($parameters); - break; - case 'layout': - $this->setLayout($parameters); - break; - case 'validation': - $this->setValidation($parameters); - break; - } - } - } - - /** - * Check if child elements are allowed within this element - * - * @return bool TRUE if allowed - */ - public function childElementsAllowed() - { - return $this->childElementsAllowed; - } - - /** - * Set the attributes according to the allowed attributes of this element - * - * @param array $parameters Configuration array - * @return void - */ - protected function setAttributes(array $parameters) - { - foreach ($this->allowedAttributes as $allowedAttribute) { - if (isset($parameters[$allowedAttribute])) { - $this->configuration['attributes'][$allowedAttribute] = $parameters[$allowedAttribute]; - } elseif (!isset($this->configuration['attributes'][$allowedAttribute])) { - $this->configuration['attributes'][$allowedAttribute] = ''; - } - } - } - - /** - * Set the filters of the element - * - * @param array $parameters Configuration array - * @return void - */ - protected function setFilters(array $parameters) - { - if (isset($parameters['filters.']) && is_array($parameters['filters.'])) { - $filters = $parameters['filters.']; - foreach ($filters as $key => $filterName) { - if ((int)$key && strpos($key, '.') === false) { - $filterConfiguration = []; - if (isset($filters[$key . '.'])) { - $filterConfiguration = $filters[$key . '.']; - } - $this->configuration['filters'][$filterName] = $filterConfiguration; - } - } - } else { - $this->configuration['filters'] = new \stdClass(); - } - } - - /** - * Set the label of the element - * - * @param array $parameters Configuration array - * @return void - */ - protected function setLabel(array $parameters) - { - if (isset($parameters['label']) && !isset($parameters['label.'])) { - $this->configuration['label']['value'] = $parameters['label']; - } elseif (!isset($parameters['label']) && isset($parameters['label.'])) { - $this->configuration['label']['value'] = $parameters['label.']['value']; - } - } - - /** - * Set the layout of the element - * - * @param array $parameters Configuration array - * @return void - */ - protected function setLayout(array $parameters) - { - if (isset($parameters['layout'])) { - if ($this->configuration['layout'] === 'front') { - $this->configuration['layout'] = 'back'; - } else { - $this->configuration['layout'] = 'front'; - } - } - } - - /** - * Set the validation rules for the element - * - * @param array $parameters Configuration array - * @return void - */ - protected function setValidation(array $parameters) - { - if (isset($parameters['validation']) && is_array($parameters['validation'])) { - $this->configuration['validation'] = $parameters['validation']; - } else { - $this->configuration['validation'] = new \stdClass(); - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/ButtonJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/ButtonJsonElement.php deleted file mode 100644 index 8d1520a680e5..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/ButtonJsonElement.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON button - */ -class ButtonJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-button'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [ - 'type' => 'button' - ], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'front', - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'autofocus', - 'disabled', - 'name', - 'type', - 'value' - ]; -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/CheckboxGroupJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/CheckboxGroupJsonElement.php deleted file mode 100644 index f9795d072d9d..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/CheckboxGroupJsonElement.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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\Core\Utility\ArrayUtility; - -/** - * JSON checkboxgroup - */ -class CheckboxGroupJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\FieldsetJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-predefined-checkboxgroup'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [], - 'legend' => [ - 'value' => '' - ], - 'options' => [], - 'various' => [ - 'name' => '' - ], - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'class', - 'dir', - 'id', - 'lang', - 'style' - ]; - - /** - * Set all the parameters for this object - * - * @param array $parameters Configuration array - * @return void - * @see \TYPO3\CMS\Form\Domain\Model\Json\FieldsetJsonElement::setParameters() - */ - public function setParameters(array $parameters) - { - parent::setParameters($parameters); - $this->setOptions($parameters); - $this->setVarious($parameters); - } - - /** - * Set the options for this object - * - * @param array $parameters Configuration array - * @return void - */ - protected function setOptions(array $parameters) - { - if (is_array($parameters)) { - $keys = ArrayUtility::filterAndSortByNumericKeys($parameters); - foreach ($keys as $key) { - $class = $parameters[$key]; - if ((int)$key && strpos($key, '.') === false) { - if (isset($parameters[$key . '.']) && $class === 'CHECKBOX') { - $childElementArguments = $parameters[$key . '.']; - if (isset($childElementArguments['checked'])) { - $childElementArguments['attributes']['selected'] = 'selected'; - unset($childElementArguments['checked']); - } - if (isset($childElementArguments['value'])) { - $childElementArguments['attributes']['value'] = $childElementArguments['value']; - unset($childElementArguments['value']); - } - if (isset($childElementArguments['label.'])) { - $childElementArguments['text'] = $childElementArguments['label.']['value']; - unset($childElementArguments['label.']); - } - $this->configuration['options'][] = $childElementArguments; - } - } - } - } - } - - /** - * Set the various properties for this object - * - * @param array $parameters Configuration array - * @return void - */ - protected function setVarious(array $parameters) - { - if (isset($parameters['name'])) { - $this->configuration['various']['name'] = $parameters['name']; - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/CheckboxJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/CheckboxJsonElement.php deleted file mode 100644 index 68b38864902c..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/CheckboxJsonElement.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON checkbox - */ -class CheckboxJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-checkbox'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [ - 'type' => 'checkbox' - ], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'back', - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'autofocus', - 'checked', - 'disabled', - 'name', - 'readonly', - 'required', - 'type', - 'value' - ]; -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/ContainerJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/ContainerJsonElement.php deleted file mode 100644 index 32f477f7842f..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/ContainerJsonElement.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON container abstract - */ -class ContainerJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The items within this container - * - * @var array - */ - public $elementContainer = [ - 'hasDragAndDrop' => true, - 'items' => [] - ]; - - /** - * Add an element to this container - * - * @param \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement $element The element to add - * @return void - */ - public function addElement(\TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement $element) - { - $this->elementContainer['items'][] = $element; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/FieldsetJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/FieldsetJsonElement.php deleted file mode 100644 index 60283e036aef..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/FieldsetJsonElement.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON fieldset - */ -class FieldsetJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\ContainerJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-fieldset'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [], - 'legend' => [ - 'value' => '' - ] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'class', - 'dir', - 'id', - 'lang', - 'style' - ]; - - /** - * Set all the parameters for this object - * - * @param array $parameters Configuration array - * @return void - * @see \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement::setParameters() - */ - public function setParameters(array $parameters) - { - parent::setParameters($parameters); - $this->setLegend($parameters); - } - - /** - * Set the legend for the element - * - * @param array $parameters Configuration array - * @return void - */ - protected function setLegend(array $parameters) - { - if (isset($parameters['legend']) && !isset($parameters['legend.'])) { - $this->configuration['legend']['value'] = $parameters['legend']; - } elseif (!isset($parameters['legend']) && isset($parameters['legend.'])) { - $this->configuration['legend']['value'] = $parameters['legend.']['value']; - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/FileuploadJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/FileuploadJsonElement.php deleted file mode 100644 index 50cc8ae2c99a..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/FileuploadJsonElement.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON Fileupload - */ -class FileuploadJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-fileupload'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [ - 'type' => 'file' - ], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'front', - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'accept', - 'autofocus', - 'disabled', - 'multiple', - 'name', - 'readonly', - 'required', - 'type', - 'value' - ]; -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/FormJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/FormJsonElement.php deleted file mode 100644 index 312ce8bc827b..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/FormJsonElement.php +++ /dev/null @@ -1,141 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON form - */ -class FormJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\ContainerJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-form'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [], - 'prefix' => 'tx_form', - 'confirmation' => true, - 'postProcessor' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'accept', - 'accept-charset', - 'action', - 'autocomplete', - 'enctype', - 'method', - 'novalidate' - ]; - - /** - * Set all the parameters for this object - * - * @param array $parameters Configuration array - * @return void - * @see \TYPO3\CMS\Form\Domain\Model\Json\ContainerJsonElement::setParameters() - */ - public function setParameters(array $parameters) - { - parent::setParameters($parameters); - $this->setPrefix($parameters); - $this->setConfirmation($parameters); - $this->setPostProcessors($parameters); - } - - /** - * Set the confirmation message boolean - * - * @param array $parameters Configuration array - * @return void - */ - protected function setConfirmation(array $parameters) - { - if (isset($parameters['confirmation'])) { - $this->configuration['confirmation'] = $parameters['confirmation']; - } - } - - /** - * Set the post processors and their configuration - * - * @param array $parameters Configuration array - * @return void - */ - protected function setPostProcessors(array $parameters) - { - if (isset($parameters['postProcessor.']) && is_array($parameters['postProcessor.'])) { - $postProcessors = $parameters['postProcessor.']; - foreach ($postProcessors as $key => $postProcessorName) { - if ((int)$key && strpos($key, '.') === false) { - $postProcessorConfiguration = []; - if (isset($postProcessors[$key . '.'])) { - $postProcessorConfiguration = $postProcessors[$key . '.']; - } - $this->configuration['postProcessor'][$postProcessorName] = $postProcessorConfiguration; - } - } - } else { - $this->configuration['postProcessor'] = [ - 'mail' => [ - 'recipientEmail' => '', - 'senderEmail' => '' - ] - ]; - } - } - - /** - * Set the prefix - * - * @param array $parameters Configuration array - * @return void - */ - protected function setPrefix(array $parameters) - { - if (isset($parameters['prefix'])) { - $this->configuration['prefix'] = $parameters['prefix']; - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/HeaderJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/HeaderJsonElement.php deleted file mode 100644 index 2461799969f8..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/HeaderJsonElement.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON header - */ -class HeaderJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-content-header'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [], - 'various' => [ - 'headingSize' => 'h1', - 'content' => '' - ] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'class', - 'dir', - 'id', - 'lang', - 'style', - 'title' - ]; - - /** - * Set all the parameters for this object - * - * @param array $parameters Configuration array - * @return void - * @see \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement::setParameters() - */ - public function setParameters(array $parameters) - { - parent::setParameters($parameters); - $this->setVarious($parameters); - } - - /** - * Set the various properties for the element - * - * For this element this is the headingsize and the value - * - * @param array $parameters Configuration array - * @return void - */ - protected function setVarious(array $parameters) - { - if (isset($parameters['headingSize'])) { - if (preg_match('#^h[1-5]$#', $parameters['headingSize'])) { - $this->configuration['various']['headingSize'] = $parameters['headingSize']; - } - } - if (isset($parameters['content'])) { - $this->configuration['various']['content'] = $parameters['content']; - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/HiddenJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/HiddenJsonElement.php deleted file mode 100644 index 6c7f61cd554c..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/HiddenJsonElement.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON hidden - */ -class HiddenJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-hidden'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [ - 'type' => 'hidden' - ], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'front', - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'name', - 'type', - 'value' - ]; -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/NameJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/NameJsonElement.php deleted file mode 100644 index 6f4ba0e573f3..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/NameJsonElement.php +++ /dev/null @@ -1,98 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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\Core\Utility\ArrayUtility; - -/** - * JSON name - */ -class NameJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\FieldsetJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-predefined-name'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [], - 'legend' => [ - 'value' => '' - ], - 'various' => [ - 'prefix' => false, - 'suffix' => false, - 'middleName' => false - ] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'class', - 'dir', - 'id', - 'lang', - 'style' - ]; - - /** - * Set all the parameters for this object - * - * @param array $parameters Configuration array - * @return void - * @see \TYPO3\CMS\Form\Domain\Model\Json\FieldsetJsonElement::setParameters() - */ - public function setParameters(array $parameters) - { - parent::setParameters($parameters); - $this->setVarious($parameters); - } - - /** - * Set the various properties for the element - * - * For this element this is the prefix, suffix and middleName if they will - * be shown in the form - * - * @param array $parameters Configuration array - * @return void - */ - protected function setVarious(array $parameters) - { - if (is_array($parameters)) { - $keys = ArrayUtility::filterAndSortByNumericKeys($parameters); - foreach ($keys as $key) { - if ((int)$key && strpos($key, '.') === false) { - if (isset($parameters[$key . '.'])) { - $childElementArguments = $parameters[$key . '.']; - if (in_array($childElementArguments['name'], ['prefix', 'suffix', 'middleName'])) { - $this->configuration['various'][$childElementArguments['name']] = true; - } - } - } - } - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/PasswordJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/PasswordJsonElement.php deleted file mode 100644 index 402d60995e35..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/PasswordJsonElement.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON password - */ -class PasswordJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-password'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [ - 'type' => 'password' - ], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'front', - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'autocomplete', - 'autofocus', - 'disabled', - 'maxlength', - 'minlength', - 'name', - 'pattern', - 'placeholder', - 'readonly', - 'required', - 'size', - 'type', - 'value' - ]; -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/RadioGroupJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/RadioGroupJsonElement.php deleted file mode 100644 index 27b6bff0f9ee..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/RadioGroupJsonElement.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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\Core\Utility\ArrayUtility; - -/** - * JSON radiogroup - */ -class RadioGroupJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\FieldsetJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-predefined-radiogroup'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [], - 'legend' => [ - 'value' => '' - ], - 'options' => [], - 'various' => [ - 'name' => '' - ], - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'class', - 'dir', - 'id', - 'lang', - 'style' - ]; - - /** - * Set all the parameters for this object - * - * @param array $parameters Configuration array - * @return void - * @see \TYPO3\CMS\Form\Domain\Model\Json\FieldsetJsonElement::setParameters() - */ - public function setParameters(array $parameters) - { - parent::setParameters($parameters); - $this->setOptions($parameters); - $this->setVarious($parameters); - } - - /** - * Set the options for this object - * - * @param array $parameters Configuration array - * @return void - */ - protected function setOptions(array $parameters) - { - if (is_array($parameters)) { - $keys = ArrayUtility::filterAndSortByNumericKeys($parameters); - foreach ($keys as $key) { - $class = $parameters[$key]; - if ((int)$key && strpos($key, '.') === false) { - if (isset($parameters[$key . '.']) && $class === 'RADIO') { - $childElementArguments = $parameters[$key . '.']; - if (isset($childElementArguments['checked'])) { - $childElementArguments['attributes']['selected'] = 'selected'; - unset($childElementArguments['checked']); - } - if (isset($childElementArguments['value'])) { - $childElementArguments['attributes']['value'] = $childElementArguments['value']; - unset($childElementArguments['value']); - } - if (isset($childElementArguments['label.'])) { - $childElementArguments['text'] = $childElementArguments['label.']['value']; - unset($childElementArguments['label.']); - } - $this->configuration['options'][] = $childElementArguments; - } - } - } - } - } - - /** - * Set the various properties for this object - * - * @param array $parameters Configuration array - * @return void - */ - protected function setVarious(array $parameters) - { - if (isset($parameters['name'])) { - $this->configuration['various']['name'] = $parameters['name']; - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/RadioJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/RadioJsonElement.php deleted file mode 100644 index dec274e5d472..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/RadioJsonElement.php +++ /dev/null @@ -1,77 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON radio - */ -class RadioJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-radio'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [ - 'type' => 'radio' - ], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'back', - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'autofocus', - 'checked', - 'disabled', - 'name', - 'readonly', - 'required', - 'type', - 'value' - ]; -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/ResetJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/ResetJsonElement.php deleted file mode 100644 index 389f455b4fdd..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/ResetJsonElement.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON reset - */ -class ResetJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-reset'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [ - 'type' => 'reset' - ], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'front', - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'autofocus', - 'disabled', - 'name', - 'type', - 'value' - ]; -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/SelectJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/SelectJsonElement.php deleted file mode 100644 index 54b62f1f7c39..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/SelectJsonElement.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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\Core\Utility\ArrayUtility; - -/** - * JSON select - */ -class SelectJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-select'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'front', - 'options' => [], - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'autofocus', - 'disabled', - 'multiple', - 'name', - 'required', - 'size' - ]; - - protected $childElementsAllowed = false; - - /** - * Set all the parameters for this object - * - * @param array $parameters Configuration array - * @return void - * @see \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement::setParameters() - */ - public function setParameters(array $parameters) - { - parent::setParameters($parameters); - $this->setOptions($parameters); - } - - /** - * Set the options for this object - * - * @param array $parameters Configuration array - * @return void - */ - protected function setOptions(array $parameters) - { - if (is_array($parameters)) { - $keys = ArrayUtility::filterAndSortByNumericKeys($parameters); - foreach ($keys as $key) { - $class = $parameters[$key]; - if ((int)$key && strpos($key, '.') === false) { - if (isset($parameters[$key . '.']) && $class === 'OPTION') { - $childElementArguments = $parameters[$key . '.']; - if (isset($childElementArguments['selected'])) { - $childElementArguments['attributes']['selected'] = $childElementArguments['selected']; - unset($childElementArguments['selected']); - } - if (isset($childElementArguments['value'])) { - $childElementArguments['attributes']['value'] = $childElementArguments['value']; - unset($childElementArguments['value']); - } - $this->configuration['options'][] = $childElementArguments; - } - } - } - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/SubmitJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/SubmitJsonElement.php deleted file mode 100644 index 514e40599284..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/SubmitJsonElement.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON submit - */ -class SubmitJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-submit'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [ - 'type' => 'submit' - ], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'front', - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'autofocus', - 'disabled', - 'name', - 'type', - 'value' - ]; -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/TextareaJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/TextareaJsonElement.php deleted file mode 100644 index 4a0a8d30657b..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/TextareaJsonElement.php +++ /dev/null @@ -1,97 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON textarea - */ -class TextareaJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-textarea'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [ - 'cols' => 40, - 'rows' => 5 - ], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'front', - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'autofocus', - 'cols', - 'disabled', - 'inputmode', - 'maxlength', - 'minlength', - 'name', - 'placeholder', - 'readonly', - 'required', - 'rows', - 'selectionDirection', - 'selectionEnd', - 'selectionStart', - 'text', - 'wrap' - ]; - - /** - * Set the attributes according to the allowed attributes of this element - * - * @param array $parameters Configuration array - * @return void - */ - protected function setAttributes(array $parameters) - { - parent::setAttributes($parameters); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/TextblockJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/TextblockJsonElement.php deleted file mode 100644 index 970a5d055bd0..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/TextblockJsonElement.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON textblock - */ -class TextblockJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-content-textblock'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [], - 'various' => [ - 'text' => '' - ] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'class', - 'dir', - 'id', - 'lang', - 'style', - 'title' - ]; - - /** - * Set all the parameters for this object - * - * @param array $parameters Configuration array - * @return void - * @see \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement::setParameters() - */ - public function setParameters(array $parameters) - { - parent::setParameters($parameters); - $this->setVarious($parameters); - } - - /** - * Set the various properties for the element - * - * For this element this is the headingsize and the value - * - * @param array $parameters Configuration array - * @return void - */ - protected function setVarious(array $parameters) - { - if (isset($parameters['text'])) { - $this->configuration['various']['text'] = $parameters['text']; - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Json/TextlineJsonElement.php b/typo3/sysext/form/Classes/Domain/Model/Json/TextlineJsonElement.php deleted file mode 100644 index f4ea76428c2e..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/Json/TextlineJsonElement.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model\Json; - -/* - * 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! - */ - -/** - * JSON textline - */ -class TextlineJsonElement extends \TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement -{ - /** - * The ExtJS xtype of the element - * - * @var string - */ - public $xtype = 'typo3-form-wizard-elements-basic-textline'; - - /** - * The configuration array for the xtype - * - * @var array - */ - public $configuration = [ - 'attributes' => [ - 'type' => 'text' - ], - 'filters' => [], - 'label' => [ - 'value' => '' - ], - 'layout' => 'front', - 'validation' => [] - ]; - - /** - * Allowed attributes for this object - * - * @var array - */ - protected $allowedAttributes = [ - 'accesskey', - 'class', - 'contenteditable', - 'contextmenu', - 'dir', - 'draggable', - 'dropzone', - 'hidden', - 'id', - 'lang', - 'spellcheck', - 'style', - 'tabindex', - 'title', - 'translate', - /* element specific attributes */ - 'autocomplete', - 'autofocus', - 'disabled', - 'inputmode', - 'list', - 'maxlength', - 'minlength', - 'name', - 'pattern', - 'placeholder', - 'readonly', - 'required', - 'size', - 'type', - 'value' - ]; -} diff --git a/typo3/sysext/form/Classes/Domain/Model/Renderable/AbstractCompositeRenderable.php b/typo3/sysext/form/Classes/Domain/Model/Renderable/AbstractCompositeRenderable.php new file mode 100644 index 000000000000..adda0931a113 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/Renderable/AbstractCompositeRenderable.php @@ -0,0 +1,210 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\Renderable; + +/* + * 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\Form\Domain\Model\Exception\FormDefinitionConsistencyException; + +/** + * Convenience base class which implements common functionality for most + * classes which implement CompositeRenderableInterface, i.e. have **child renderable elements**. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + */ +abstract class AbstractCompositeRenderable extends AbstractRenderable implements CompositeRenderableInterface +{ + + /** + * array of child renderables + * + * @var \TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface[] + */ + protected $renderables = []; + + /** + * Add a renderable to the list of child renderables. + * + * This function will be wrapped by the subclasses, f.e. with an "addPage" + * or "addElement" method with the correct type hint. + * + * @param RenderableInterface $renderable + * @return void + * @throws FormDefinitionConsistencyException + * @internal + */ + protected function addRenderable(RenderableInterface $renderable) + { + if ($renderable->getParentRenderable() !== null) { + throw new FormDefinitionConsistencyException(sprintf('The renderable with identifier "%s" is already added to another element (element identifier: "%s").', $renderable->getIdentifier(), $renderable->getParentRenderable()->getIdentifier()), 1325665144); + } + $renderable->setIndex(count($this->renderables)); + $renderable->setParentRenderable($this); + $this->renderables[] = $renderable; + } + + /** + * Move $renderableToMove before $referenceRenderable + * + * This function will be wrapped by the subclasses, f.e. with an "movePageBefore" + * or "moveElementBefore" method with the correct type hint. + * + * @param RenderableInterface $renderableToMove + * @param RenderableInterface $referenceRenderable + * @return void + * @throws FormDefinitionConsistencyException + * @internal + */ + protected function moveRenderableBefore(RenderableInterface $renderableToMove, RenderableInterface $referenceRenderable) + { + if ($renderableToMove->getParentRenderable() !== $referenceRenderable->getParentRenderable() || $renderableToMove->getParentRenderable() !== $this) { + throw new FormDefinitionConsistencyException('Moved renderables need to be part of the same parent element.', 1326089744); + } + + $reorderedRenderables = []; + $i = 0; + foreach ($this->renderables as $renderable) { + if ($renderable === $renderableToMove) { + continue; + } + + if ($renderable === $referenceRenderable) { + $reorderedRenderables[] = $renderableToMove; + $renderableToMove->setIndex($i); + $i++; + } + $reorderedRenderables[] = $renderable; + $renderable->setIndex($i); + $i++; + } + $this->renderables = $reorderedRenderables; + } + + /** + * Move $renderableToMove after $referenceRenderable + * + * This function will be wrapped by the subclasses, f.e. with an "movePageAfter" + * or "moveElementAfter" method with the correct type hint. + * + * @param RenderableInterface $renderableToMove + * @param RenderableInterface $referenceRenderable + * @return void + * @throws FormDefinitionConsistencyException + * @internal + */ + protected function moveRenderableAfter(RenderableInterface $renderableToMove, RenderableInterface $referenceRenderable) + { + if ($renderableToMove->getParentRenderable() !== $referenceRenderable->getParentRenderable() || $renderableToMove->getParentRenderable() !== $this) { + throw new FormDefinitionConsistencyException('Moved renderables need to be part of the same parent element.', 1477083145); + } + + $reorderedRenderables = []; + $i = 0; + foreach ($this->renderables as $renderable) { + if ($renderable === $renderableToMove) { + continue; + } + + $reorderedRenderables[] = $renderable; + $renderable->setIndex($i); + $i++; + + if ($renderable === $referenceRenderable) { + $reorderedRenderables[] = $renderableToMove; + $renderableToMove->setIndex($i); + $i++; + } + } + $this->renderables = $reorderedRenderables; + } + + /** + * Returns all RenderableInterface instances of this composite renderable recursively + * + * @return RenderableInterface[] + * @internal + */ + public function getRenderablesRecursively(): array + { + $renderables = []; + foreach ($this->renderables as $renderable) { + $renderables[] = $renderable; + if ($renderable instanceof CompositeRenderableInterface) { + $renderables = array_merge($renderables, $renderable->getRenderablesRecursively()); + } + } + return $renderables; + } + + /** + * Remove a renderable from this renderable. + * + * This function will be wrapped by the subclasses, f.e. with an "removePage" + * or "removeElement" method with the correct type hint. + * + * @param RenderableInterface $renderableToRemove + * @return void + * @throws FormDefinitionConsistencyException + * @internal + */ + protected function removeRenderable(RenderableInterface $renderableToRemove) + { + if ($renderableToRemove->getParentRenderable() !== $this) { + throw new FormDefinitionConsistencyException('The renderable to be removed must be part of the calling parent renderable.', 1326090127); + } + + $updatedRenderables = []; + foreach ($this->renderables as $renderable) { + if ($renderable === $renderableToRemove) { + continue; + } + + $updatedRenderables[] = $renderable; + } + $this->renderables = $updatedRenderables; + + $renderableToRemove->onRemoveFromParentRenderable(); + } + + /** + * Register this element at the parent form, if there is a connection to the parent form. + * + * @return void + * @internal + */ + public function registerInFormIfPossible() + { + parent::registerInFormIfPossible(); + foreach ($this->renderables as $renderable) { + $renderable->registerInFormIfPossible(); + } + } + + /** + * This function is called after a renderable has been removed from its parent + * renderable. + * This just passes the event down to all child renderables of this composite renderable. + * + * @return void + * @internal + */ + public function onRemoveFromParentRenderable() + { + foreach ($this->renderables as $renderable) { + $renderable->onRemoveFromParentRenderable(); + } + parent::onRemoveFromParentRenderable(); + } +} diff --git a/typo3/sysext/form/Classes/Domain/Model/Renderable/AbstractRenderable.php b/typo3/sysext/form/Classes/Domain/Model/Renderable/AbstractRenderable.php new file mode 100644 index 000000000000..9bfbb596e141 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/Renderable/AbstractRenderable.php @@ -0,0 +1,423 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\Renderable; + +/* + * 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\Core\Utility\ArrayUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface; +use TYPO3\CMS\Form\Domain\Model\Exception\FormDefinitionConsistencyException; +use TYPO3\CMS\Form\Domain\Model\Exception\ValidatorPresetNotFoundException; +use TYPO3\CMS\Form\Domain\Model\FormDefinition; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; +use TYPO3\CMS\Form\Utility\ArrayUtility as FormArrayUtility; + +/** + * Convenience base class which implements common functionality for most + * classes which implement RenderableInterface. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + * @internal + */ +abstract class AbstractRenderable implements RenderableInterface +{ + + /** + * Abstract "type" of this Renderable. Is used during the rendering process + * to determine the template file or the View PHP class being used to render + * the particular element. + * + * @var string + */ + protected $type; + + /** + * The identifier of this renderable + * + * @var string + */ + protected $identifier; + + /** + * The parent renderable + * + * @var CompositeRenderableInterface + */ + protected $parentRenderable; + + /** + * The label of this renderable + * + * @var string + */ + protected $label = ''; + + /** + * associative array of rendering options + * + * @var array + */ + protected $renderingOptions = []; + + /** + * Renderer class name to be used for this renderable. + * + * Is only set if a specific renderer should be used for this renderable, + * if it is NULL the caller needs to determine the renderer or take care + * of the rendering itself. + * + * @var string + */ + protected $rendererClassName = null; + + /** + * The position of this renderable inside the parent renderable. + * + * @var int + */ + protected $index = 0; + + /** + * Get the type of the renderable + * + * @return string + * @api + */ + public function getType(): string + { + return $this->type; + } + + /** + * Get the identifier of the element + * + * @return string + * @api + */ + public function getIdentifier(): string + { + return $this->identifier; + } + + /** + * Set multiple properties of this object at once. + * Every property which has a corresponding set* method can be set using + * the passed $options array. + * + * @param array $options + * @return void + * @api + */ + public function setOptions(array $options) + { + if (isset($options['label'])) { + $this->setLabel($options['label']); + } + + if (isset($options['defaultValue'])) { + $this->setDefaultValue($options['defaultValue']); + } + + if (isset($options['properties'])) { + foreach ($options['properties'] as $key => $value) { + $this->setProperty($key, $value); + } + } + + if (isset($options['rendererClassName'])) { + $this->setRendererClassName($options['rendererClassName']); + } + + if (isset($options['renderingOptions'])) { + foreach ($options['renderingOptions'] as $key => $value) { + if (is_array($value)) { + $currentValue = isset($this->getRenderingOptions()[$key]) ? $this->getRenderingOptions()[$key] : []; + ArrayUtility::mergeRecursiveWithOverrule($currentValue, $value); + $this->setRenderingOption($key, $currentValue); + } else { + $this->setRenderingOption($key, $value); + } + } + } + + if (isset($options['validators'])) { + foreach ($options['validators'] as $validatorConfiguration) { + $this->createValidator($validatorConfiguration['identifier'], isset($validatorConfiguration['options']) ? $validatorConfiguration['options'] : []); + } + } + + FormArrayUtility::assertAllArrayKeysAreValid($options, ['label', 'defaultValue', 'properties', 'rendererClassName', 'renderingOptions', 'validators', 'formEditor']); + } + + /** + * Create a validator for the element + * + * @param string $validatorIdentifier + * @param array $options + * @return mixed + * @throws ValidatorPresetNotFoundException + * @api + */ + public function createValidator(string $validatorIdentifier, array $options = []) + { + $validatorsDefinition = $this->getRootForm()->getValidatorsDefinition(); + if (isset($validatorsDefinition[$validatorIdentifier]) && is_array($validatorsDefinition[$validatorIdentifier]) && isset($validatorsDefinition[$validatorIdentifier]['implementationClassName'])) { + $implementationClassName = $validatorsDefinition[$validatorIdentifier]['implementationClassName']; + $defaultOptions = isset($validatorsDefinition[$validatorIdentifier]['options']) ? $validatorsDefinition[$validatorIdentifier]['options'] : []; + + ArrayUtility::mergeRecursiveWithOverrule($defaultOptions, $options); + + $validator = GeneralUtility::makeInstance(ObjectManager::class) + ->get($implementationClassName, $defaultOptions); + $this->addValidator($validator); + return $validator; + } else { + throw new ValidatorPresetNotFoundException('The validator preset identified by "' . $validatorIdentifier . '" could not be found, or the implementationClassName was not specified.', 1328710202); + } + } + + /** + * Add a validator to the element + * + * @param ValidatorInterface $validator + * @return void + * @api + */ + public function addValidator(ValidatorInterface $validator) + { + $formDefinition = $this->getRootForm(); + $formDefinition->getProcessingRule($this->getIdentifier())->addValidator($validator); + } + + /** + * Get all validators on the element + * + * @return \SplObjectStorage + * @internal + */ + public function getValidators(): \SplObjectStorage + { + $formDefinition = $this->getRootForm(); + return $formDefinition->getProcessingRule($this->getIdentifier())->getValidators(); + } + + /** + * Set the datatype + * + * @param string $dataType + * @return void + * @api + */ + public function setDataType(string $dataType) + { + $formDefinition = $this->getRootForm(); + $formDefinition->getProcessingRule($this->getIdentifier())->setDataType($dataType); + } + + /** + * Set the renderer class name + * + * @param string $rendererClassName + * @return void + * @api + */ + public function setRendererClassName(string $rendererClassName) + { + $this->rendererClassName = $rendererClassName; + } + + /** + * Get the classname of the renderer + * + * @return null|string + * @api + */ + public function getRendererClassName() + { + return $this->rendererClassName; + } + + /** + * Get all rendering options + * + * @return array + * @api + */ + public function getRenderingOptions(): array + { + return $this->renderingOptions; + } + + /** + * Set the rendering option $key to $value. + * + * @param string $key + * @param mixed $value + * @return mixed + * @api + */ + public function setRenderingOption(string $key, $value) + { + $this->renderingOptions[$key] = $value; + } + + /** + * Get the parent renderable + * + * @return null|CompositeRenderableInterface + * @return void + * @api + */ + public function getParentRenderable() + { + return $this->parentRenderable; + } + + /** + * Set the parent renderable + * + * @param CompositeRenderableInterface $parentRenderable + * @return void + * @api + */ + public function setParentRenderable(CompositeRenderableInterface $parentRenderable) + { + $this->parentRenderable = $parentRenderable; + $this->registerInFormIfPossible(); + } + + /** + * Get the root form this element belongs to + * + * @return FormDefinition + * @throws FormDefinitionConsistencyException + * @api + */ + public function getRootForm(): FormDefinition + { + $rootRenderable = $this->parentRenderable; + while ($rootRenderable !== null && !($rootRenderable instanceof FormDefinition)) { + $rootRenderable = $rootRenderable->getParentRenderable(); + } + if ($rootRenderable === null) { + throw new FormDefinitionConsistencyException(sprintf('The form element "%s" is not attached to a parent form.', $this->identifier), 1326803398); + } + + return $rootRenderable; + } + + /** + * Register this element at the parent form, if there is a connection to the parent form. + * + * @return void + * @internal + */ + public function registerInFormIfPossible() + { + try { + $rootForm = $this->getRootForm(); + $rootForm->registerRenderable($this); + } catch (FormDefinitionConsistencyException $exception) { + } + } + + /** + * Triggered when the renderable is removed from it's parent + * + * @return void + * @internal + */ + public function onRemoveFromParentRenderable() + { + try { + $rootForm = $this->getRootForm(); + $rootForm->unregisterRenderable($this); + } catch (FormDefinitionConsistencyException $exception) { + } + $this->parentRenderable = null; + } + + /** + * Get the index of the renderable + * + * @return int + * @internal + */ + public function getIndex(): int + { + return $this->index; + } + + /** + * Set the index of the renderable + * + * @param int $index + * @return void + * @internal + */ + public function setIndex(int $index) + { + $this->index = $index; + } + + /** + * Get the label of the renderable + * + * @return string + * @api + */ + public function getLabel(): string + { + return $this->label; + } + + /** + * Set the label which shall be displayed next to the form element + * + * @param string $label + * @return void + * @api + */ + public function setLabel(string $label) + { + $this->label = $label; + } + + /** + * Override this method in your custom Renderable if needed + * + * @param FormRuntime $formRuntime + * @return void + * @api + */ + public function beforeRendering(FormRuntime $formRuntime) + { + } + + /** + * This is a callback that is invoked by the Form Factory after the whole form has been built. + * It can be used to add new form elements as children for complex form elements. + * + * Override this method in your custom Renderable if needed. + * + * @return void + * @api + */ + public function onBuildingFinished() + { + } +} diff --git a/typo3/sysext/form/Classes/Domain/Model/Renderable/CompositeRenderableInterface.php b/typo3/sysext/form/Classes/Domain/Model/Renderable/CompositeRenderableInterface.php new file mode 100644 index 000000000000..4e15db0f0344 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/Renderable/CompositeRenderableInterface.php @@ -0,0 +1,35 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\Renderable; + +/* + * 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! + */ + +/** + * Interface which all Form Parts must adhere to **when they have sub elements**. + * This includes especially "FormDefinition" and "Page". + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + */ +interface CompositeRenderableInterface extends RenderableInterface +{ + + /** + * Returns all RenderableInterface instances of this composite renderable recursively + * + * @return \TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface[] + * @internal + */ + public function getRenderablesRecursively(): array; +} diff --git a/typo3/sysext/form/Classes/Domain/Model/Renderable/RenderableInterface.php b/typo3/sysext/form/Classes/Domain/Model/Renderable/RenderableInterface.php new file mode 100644 index 000000000000..f473dfad8fa0 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/Renderable/RenderableInterface.php @@ -0,0 +1,92 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\Renderable; + +/* + * 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! + */ + +/** + * Base interface which all Form Parts except the FormDefinition must adhere + * to (i.e. all elements which are NOT the root of a Form). + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + */ +interface RenderableInterface extends RootRenderableInterface +{ + + /** + * Return the parent renderable + * + * @return null|CompositeRenderableInterface the parent renderable + * @internal + */ + public function getParentRenderable(); + + /** + * Set the new parent renderable. You should not call this directly; + * it is automatically called by addRenderable. + * + * This method should also register itself at the parent form, if possible. + * + * @param CompositeRenderableInterface $renderable + * @return void + * @internal + */ + public function setParentRenderable(CompositeRenderableInterface $renderable); + + /** + * Set the index of this renderable inside the parent renderable + * + * @param int $index + * @return void + * @internal + */ + public function setIndex(int $index); + + /** + * Get the index inside the parent renderable + * + * @return int + * @api + */ + public function getIndex(): int; + + /** + * This function is called after a renderable has been removed from its parent + * renderable. The function should make sure to clean up the internal state, + * like reseting $this->parentRenderable or deregistering the renderable + * at the form. + * + * @return void + * @internal + */ + public function onRemoveFromParentRenderable(); + + /** + * This is a callback that is invoked by the Form Factory after the whole form has been built. + * It can be used to add new form elements as children for complex form elements. + * + * @return void + * @api + */ + public function onBuildingFinished(); + + /** + * Register this element at the parent form, if there is a connection to the parent form. + * + * @return void + * @internal + */ + public function registerInFormIfPossible(); +} diff --git a/typo3/sysext/form/Classes/Domain/Model/Renderable/RootRenderableInterface.php b/typo3/sysext/form/Classes/Domain/Model/Renderable/RootRenderableInterface.php new file mode 100644 index 000000000000..e0ece97b3597 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Model/Renderable/RootRenderableInterface.php @@ -0,0 +1,86 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Model\Renderable; + +/* + * 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\Form\Domain\Runtime\FormRuntime; + +/** + * Base interface which all parts of a form must adhere to. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + */ +interface RootRenderableInterface +{ + + /** + * Abstract "type" of this Renderable. Is used during the rendering process + * to determine the template file or the View PHP class being used to render + * the particular element. + * + * @return string + * @api + */ + public function getType(): string; + + /** + * The identifier of this renderable + * + * @return string + * @api + */ + public function getIdentifier(): string; + + /** + * Get the label which shall be displayed next to the form element + * + * @return string + * @api + */ + public function getLabel(): string; + + /** + * This is a callback that is invoked by the Renderer before the corresponding element is rendered. + * Use this to access previously submitted values and/or modify the $formRuntime before an element + * is outputted to the browser. + * + * @param FormRuntime $formRuntime + * @return void + * @api + */ + public function beforeRendering(FormRuntime $formRuntime); + + /** + * Get the renderer class name to be used to display this renderable; + * must implement RendererInterface + * + * Is only set if a specific renderer should be used for this renderable, + * if it is NULL the caller needs to determine the renderer or take care + * of the renderer itself. + * + * @return null|string the renderer class name + * @api + */ + public function getRendererClassName(); + + /** + * Get all rendering options + * + * @return array associative array of rendering options + * @api + */ + public function getRenderingOptions(): array; +} diff --git a/typo3/sysext/form/Classes/Domain/Model/ValidationElement.php b/typo3/sysext/form/Classes/Domain/Model/ValidationElement.php deleted file mode 100644 index 15812fc2c8c1..000000000000 --- a/typo3/sysext/form/Classes/Domain/Model/ValidationElement.php +++ /dev/null @@ -1,89 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Model; - -/* - * 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\Extbase\DomainObject\AbstractEntity; - -/** - * The ValidationElement Domain Model represents the low-level - * view on the user submitted data in a flat hierarchy. - */ -class ValidationElement extends AbstractEntity -{ - /** - * This array holds all the field from the request - * - * @var array - */ - protected $incomingFields; - - /** - * Return a array with all the fields from the request - * - * @return array - */ - public function getIncomingFields() - { - return $this->incomingFields; - } - - /** - * Sets a array with all the fields from the request - * - * @param array $incomingFields - * @return void - */ - public function setIncomingFields($incomingFields = []) - { - $this->incomingFields = $incomingFields; - } - - /** - * Get a single fields from the request - * - * @param string $key - * @return mixed - */ - public function getIncomingField($key = '') - { - return $this->incomingFields[$key]; - } - - /** - * Set a single fields from the request - * - * @param string $key - * @param mixed $value - * @return array - */ - public function setIncomingField($key = '', $value = null) - { - $this->incomingFields[$key] = $value; - } - - /** - * Determines whether a field is part of the incoming fields. - * - * @param string $key The key of the field to be looked up - * @return bool - */ - public function hasIncomingField($key) - { - return - isset($this->incomingFields[$key]) - || array_key_exists($key, $this->incomingFields) - ; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Property/TypeConverter/ArrayToValidationElementConverter.php b/typo3/sysext/form/Classes/Domain/Property/TypeConverter/ArrayToValidationElementConverter.php deleted file mode 100644 index f5e0b1824b9c..000000000000 --- a/typo3/sysext/form/Classes/Domain/Property/TypeConverter/ArrayToValidationElementConverter.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Property\TypeConverter; - -/* - * 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\Core\Utility\GeneralUtility; -use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface; -use TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter; -use TYPO3\CMS\Form\Domain\Model\ValidationElement; - -/** - * The form wizard controller - */ -class ArrayToValidationElementConverter extends AbstractTypeConverter -{ - /** - * @var array<string> - */ - protected $sourceTypes = ['array']; - - /** - * @var string - */ - protected $targetType = 'TYPO3\\CMS\\Form\\Domain\\Model\\ValidationElement'; - - /** - * @var int - */ - protected $priority = 1; - - /** - * We can only convert empty strings to array or array to array. - * - * @param mixed $source - * @param string $targetType - * @return bool - */ - public function canConvertFrom($source, $targetType) - { - return is_array($source); - } - - /** - * Convert the incoming array to a ValidationElement - * - * @param array $source - * @param string $targetType - * @param array $convertedChildProperties - * @param PropertyMappingConfigurationInterface $configuration - * @return ValidationElement - * @api - */ - public function convertFrom($source, $targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface $configuration = null) - { - /** @var ValidationElement $validationElement */ - $validationElement = GeneralUtility::makeInstance(ValidationElement::class); - if (is_array($source)) { - /** - * Find uploaded files. - * - * Extbase has already mapped the $_FILES data into the request - * @see TYPO3\CMS\Extbase\Mvc\Web\Request::build() - * If a $_FILES array is found in the request data ($source), - * set the file mime type with - * \TYPO3\CMS\Core\Type\File\FileInfo - * and write the data back into $source. - */ - foreach ($source as $propertyName => $value) { - if (is_array($value)) { - $uploadedFiles = []; - if ( - isset($value['name']) - && isset($value['type']) - && isset($value['tmp_name']) - && isset($value['size']) - ) { - // if single file upload - cast to array - $uploadedFiles[] = $value; - } elseif ( - isset($value[0]['name']) - && isset($value[0]['type']) - && isset($value[0]['tmp_name']) - && isset($value[0]['size']) - ) { - // multi file upload - $uploadedFiles = $value; - } - - if (!empty($uploadedFiles)) { - foreach ($uploadedFiles as $key => &$file) { - if ( - $file['name'] === '' - && $file['type'] === '' - && $file['tmp_name'] === '' - && $file['size'] === 0 - ) { - unset($uploadedFiles[$key]); - continue; - } - $fileInfo = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Type\File\FileInfo::class, $file['tmp_name']); - $file['type'] = $fileInfo->getMimeType(); - $file['name'] = htmlspecialchars($file['name']); - } - $source[$propertyName] = $uploadedFiles; - } - } - } - $validationElement->setIncomingFields($source); - } - - return $validationElement; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Renderer/AbstractElementRenderer.php b/typo3/sysext/form/Classes/Domain/Renderer/AbstractElementRenderer.php new file mode 100644 index 000000000000..ba766239fc43 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Renderer/AbstractElementRenderer.php @@ -0,0 +1,73 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Renderer; + +/* + * 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\Form\Domain\Runtime\FormRuntime; + +/** + * Abstract renderer which can be used as base class for custom renderers. + * + * Scope: frontend + * **This class is meant to be sub classed by developers**. + * @api + */ +abstract class AbstractElementRenderer implements RendererInterface +{ + + /** + * The assigned controller context which might be needed by the renderer. + * + * @var \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext + * @api + */ + protected $controllerContext; + + /** + * @var \TYPO3\CMS\Form\Domain\Runtime\FormRuntime + * @api + */ + protected $formRuntime; + + /** + * Set the controller context which should be used + * + * @param \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext + * @api + */ + public function setControllerContext(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $controllerContext) + { + $this->controllerContext = $controllerContext; + } + + /** + * @param FormRuntime $formRuntime + * @return void + * @api + */ + public function setFormRuntime(FormRuntime $formRuntime) + { + $this->formRuntime = $formRuntime; + } + + /** + * @return FormRuntime + * @api + */ + public function getFormRuntime(): FormRuntime + { + return $this->formRuntime; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Renderer/FluidFormRenderer.php b/typo3/sysext/form/Classes/Domain/Renderer/FluidFormRenderer.php new file mode 100644 index 000000000000..0f2311d66966 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Renderer/FluidFormRenderer.php @@ -0,0 +1,54 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Renderer; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; +use TYPO3\CMS\Form\Mvc\View\FormView; + +/** + * A renderer which render all renderables within the $formRuntime. + * All the work is done within FormView::class. + * This is just a proxy class to make the rendering process more clear. + * See the documentation within FormView::class for additional information. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + * @internal + */ +class FluidFormRenderer extends AbstractElementRenderer implements RendererInterface +{ + + /** + * Initialize the FormView::class and render the this->formRuntime. + * This method is expected to invoke the beforeRendering() callback + * on each $renderable. This is done within FormView::class. + * + * @param RootRenderableInterface $renderable + * @return string the rendered $formRuntime + * @internal + */ + public function render(RootRenderableInterface $renderable): string + { + $formView = GeneralUtility::makeInstance(ObjectManager::class) + ->get(FormView::class); + + $formView->setFormRuntime($this->formRuntime); + $formView->setControllerContext($this->controllerContext); + return $formView->renderRenderable($renderable); + } +} diff --git a/typo3/sysext/form/Classes/Domain/Renderer/RendererInterface.php b/typo3/sysext/form/Classes/Domain/Renderer/RendererInterface.php new file mode 100644 index 000000000000..e67416ee4297 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Renderer/RendererInterface.php @@ -0,0 +1,62 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Renderer; + +/* + * 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\Extbase\Mvc\Controller\ControllerContext; +use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; + +/** + * Base interface for Renderers. A Renderer is used to render a renderable. + * + * Scope: frontend + * **This interface is meant to be implemented by developers, although often you + * will subclass AbstractElementRenderer** ({@link AbstractElementRenderer}). + */ +interface RendererInterface +{ + + /** + * Set the controller context which should be used + * + * @param ControllerContext $controllerContext + * @api + */ + public function setControllerContext(ControllerContext $controllerContext); + + /** + * Note: This method is expected to invoke the beforeRendering() callback + * on each $renderable + * + * @param RootRenderableInterface $renderable + * @return string the rendered $formRuntime + * @api + */ + public function render(RootRenderableInterface $renderable): string; + + /** + * @param FormRuntime $formRuntime + * @return void + * @api + */ + public function setFormRuntime(FormRuntime $formRuntime); + + /** + * @return FormRuntime + * @api + */ + public function getFormRuntime(): FormRuntime; +} diff --git a/typo3/sysext/form/Classes/Domain/Renderer/UnknownFormElementRenderer.php b/typo3/sysext/form/Classes/Domain/Renderer/UnknownFormElementRenderer.php new file mode 100644 index 000000000000..4dfefee55a4d --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Renderer/UnknownFormElementRenderer.php @@ -0,0 +1,72 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Renderer; + +/* + * 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\Form\Domain\Exception\TypeDefinitionNotFoundException; +use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; + +/** + * Renderer for unknown Form Elements + * This is used to render Form Elements without definition depending on the context: + * In "preview mode" (e.g. inside the FormEditor) a div with an error message is rendered + * If previewMode is FALSE this will return an empty string if the rendering Option "skipUnknownElements" is TRUE for + * the form, or throw an Exception otherwise. + * + * Scope: frontend + */ +class UnknownFormElementRenderer extends AbstractElementRenderer +{ + + /** + * This renders the given $renderable depending on the context: + * In preview Mode this returns an error message. Otherwise this throws an exception or returns an empty string + * depending on the "skipUnknownElements" rendering option + * + * @param RootRenderableInterface $renderable + * @return string the rendered $renderable + * @throws TypeDefinitionNotFoundException + * @internal + */ + public function render(RootRenderableInterface $renderable): string + { + $renderingOptions = $this->formRuntime->getRenderingOptions(); + $previewMode = isset($renderingOptions['previewMode']) && $renderingOptions['previewMode'] === true; + if ($previewMode) { + return sprintf('<div class="t3-form-unknown-element" data-element-identifier-path="%s"><em>Unknown Form Element "%s"</em></div>', htmlspecialchars($this->getRenderablePath($renderable)), htmlspecialchars($renderable->getType())); + } + $skipUnknownElements = isset($renderingOptions['skipUnknownElements']) && $renderingOptions['skipUnknownElements'] === true; + if (!$skipUnknownElements) { + throw new TypeDefinitionNotFoundException(sprintf('Type "%s" not found. Probably some configuration is missing.', $renderable->getType()), 1382364019); + } + return ''; + } + + /** + * Returns the path of a $renderable in the format <formIdentifier>/<sectionIdentifier>/<sectionIdentifier>/.../<elementIdentifier> + * + * @param RootRenderableInterface $renderable + * @return string + * @internal + */ + protected function getRenderablePath(RootRenderableInterface $renderable): string + { + $path = $renderable->getIdentifier(); + while ($renderable = $renderable->getParentRenderable()) { + $path = $renderable->getIdentifier() . '/' . $path; + } + return $path; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Repository/ContentRepository.php b/typo3/sysext/form/Classes/Domain/Repository/ContentRepository.php deleted file mode 100644 index ee488067f529..000000000000 --- a/typo3/sysext/form/Classes/Domain/Repository/ContentRepository.php +++ /dev/null @@ -1,129 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Repository; - -/* - * 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\Utility\BackendUtility; -use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Form\Domain\Factory\JsonToTypoScript; -use TYPO3\CMS\Form\Domain\Model\Content; -use TYPO3\CMS\Form\Utility\TypoScriptToJsonConverter; - -/** - * Repository for \TYPO3\CMS\Form\Domain\Model\Content - */ -class ContentRepository -{ - /** - * Get the referenced record from the database - * - * Using the GET or POST variable 'P' - * - * @param null|int $recordId - * @param null|string $table - * @return bool|Content if found, FALSE if not - */ - public function getRecord($recordId = null, $table = null) - { - $record = false; - $getPostVariables = GeneralUtility::_GP('P'); - if (!$table) { - $table = 'tt_content'; - } - - if (!$recordId) { - $recordId = (int)$getPostVariables['uid']; - } - - if ((int)$recordId === 0) { - /** @var $typoScriptParser TypoScriptParser */ - $typoScriptParser = GeneralUtility::makeInstance(TypoScriptParser::class); - $typoScriptParser->parse(''); - /** @var $record Content */ - $record = GeneralUtility::makeInstance(Content::class); - $record->setUid(0); - $record->setPageId(0); - $record->setTyposcript($typoScriptParser->setup); - $record->setBodytext(''); - - return $record; - } - - $row = BackendUtility::getRecord($table, (int)$recordId); - if (is_array($row)) { - // strip off the leading "[Translate to XY]" text after localizing the original record - $languageField = $GLOBALS['TCA']['tt_content']['ctrl']['languageField']; - $transOrigPointerField = $GLOBALS['TCA']['tt_content']['ctrl']['transOrigPointerField']; - if ($row[$languageField] > 0 && $row[$transOrigPointerField] > 0) { - $bodytext = preg_replace('/^\[.*?\] /', '', $row['bodytext'], 1); - } else { - $bodytext = $row['bodytext']; - } - - /** @var $typoScriptParser TypoScriptParser */ - $typoScriptParser = GeneralUtility::makeInstance(TypoScriptParser::class); - $typoScriptParser->parse($bodytext); - /** @var $record Content */ - $record = GeneralUtility::makeInstance(Content::class); - $record->setUid($row['uid']); - $record->setPageId($row['pid']); - $record->setTyposcript($typoScriptParser->setup); - $record->setBodytext($bodytext); - } - return $record; - } - - /** - * Check if the referenced record exists - * - * @return bool TRUE if record exists, FALSE if not - */ - public function hasRecord() - { - return $this->getRecord() !== false; - } - - /** - * Convert the incoming data of the FORM wizard - * - * @return string $typoscript after conversion - */ - public function save() - { - $json = GeneralUtility::_GP('configuration'); - /** @var $converter JsonToTypoScript */ - $converter = GeneralUtility::makeInstance(JsonToTypoScript::class); - $typoscript = $converter->convert($json); - return $typoscript; - } - - /** - * Read and convert the content record to JSON - * - * @return string The JSON object if record exists, FALSE if not - */ - public function getRecordAsJson() - { - $json = false; - $record = $this->getRecord(); - if ($record) { - $typoscript = $record->getTyposcript(); - /** @var $converter TypoScriptToJsonConverter */ - $converter = GeneralUtility::makeInstance(TypoScriptToJsonConverter::class); - $json = $converter->convert($typoscript); - } - return $json; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Repository/TypoScriptRepository.php b/typo3/sysext/form/Classes/Domain/Repository/TypoScriptRepository.php deleted file mode 100644 index 88964381e5f5..000000000000 --- a/typo3/sysext/form/Classes/Domain/Repository/TypoScriptRepository.php +++ /dev/null @@ -1,223 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Repository; - -/* - * 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\Core\SingletonInterface; -use TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser; -use TYPO3\CMS\Core\Utility\GeneralUtility; - -/** - * Provide the TypoScript data-source - */ -class TypoScriptRepository implements SingletonInterface -{ - /** - * @var array - */ - protected $modelDefinitionTypoScript = []; - - /** - * @var array - */ - protected $registeredElementTypes = []; - - /** - * @var \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser - */ - protected $typoScriptParser; - - /** - * The constructor - */ - public function __construct() - { - $this->typoScriptParser = GeneralUtility::makeInstance(TypoScriptParser::class); - $this->modelDefinitionTypoScript = $this->resolveTypoScriptReferences( - $GLOBALS['TSFE']->tmpl->setup['plugin.']['tx_form.'] - ); - $this->setRegisteredElementTypes(); - } - - /** - * Get all registered form elements - * - * @return array - */ - public function getRegisteredElementTypes() - { - return $this->registeredElementTypes; - } - - /** - * Set all registered form elements - * - * @param array $registeredElementTypes - * @return void - * @throws \InvalidArgumentException - */ - public function setRegisteredElementTypes(array $registeredElementTypes = []) - { - if (!empty($registeredElementTypes)) { - $this->registeredElementTypes = $registeredElementTypes; - } else { - if (!isset($this->modelDefinitionTypoScript['settings.']['registeredElements.'])) { - throw new \InvalidArgumentException('There are no registeredElements available.', 1441791615); - } - $registeredElements = $this->modelDefinitionTypoScript['settings.']['registeredElements.']; - foreach ($registeredElements as $registeredElementKey => $value) { - $registeredElementKey = rtrim($registeredElementKey, '.'); - $this->registeredElementTypes[] = $registeredElementKey; - } - } - } - - /** - * Get the html attributes defined by the model - * with their default values - * - * @param string $elementType - * @return array - */ - public function getModelDefinedHtmlAttributes($elementType = '') - { - if ($elementType == '') { - return []; - } - $htmlAttributes = $this->getModelConfigurationByScope($elementType, 'htmlAttributes.'); - if (is_array($htmlAttributes)) { - $htmlAttributes = array_fill_keys($htmlAttributes, null); - } else { - $htmlAttributes = []; - } - $defaultHtmlAttributeValues = $this->getModelConfigurationByScope($elementType, 'defaultHtmlAttributeValues.'); - if (is_array($defaultHtmlAttributeValues)) { - foreach ($defaultHtmlAttributeValues as $defaultHtmlAttributeKey => $defaultHtmlAttributeValue) { - $htmlAttributes[$defaultHtmlAttributeKey] = $defaultHtmlAttributeValue; - } - } elseif (!is_array($htmlAttributes)) { - $htmlAttributes = []; - } - return $htmlAttributes; - } - - /** - * Get the default fluid template for a element. - * - * @param string $elementType - * @param string $partialType - * @return string - */ - public function getDefaultFluidTemplate($elementType, $partialType = 'partialPath') - { - $partialPath = $this->getModelConfigurationByScope($elementType, $partialType); - if ($partialPath) { - return $partialPath; - } - return ''; - } - - /** - * Get the model definition from TypoScript for a specific scope. - * - * @param string $elementType - * @param string $scope - * @return mixed - */ - public function getModelConfigurationByScope($elementType, $scope) - { - if (isset($this->modelDefinitionTypoScript['settings.']['registeredElements.'][$elementType . '.'][$scope])) { - return $this->modelDefinitionTypoScript['settings.']['registeredElements.'][$elementType . '.'][$scope]; - } - return null; - } - - /** - * Get a registered class name by a - * specific scope (validator or filter) - * - * @param string $name - * @param string $scope (registeredValidators, registeredFilters) - * @return mixed - */ - public function getRegisteredClassName($name, $scope) - { - $name = strtolower($name); - if (isset($this->modelDefinitionTypoScript['settings.'][$scope . '.'][$name . '.']['className'])) { - return $this->modelDefinitionTypoScript['settings.'][$scope . '.'][$name . '.']['className']; - } - return null; - } - - /** - * Render a TypoScript and resolve all references (eg. " < plugin.tx_form...") recursively - * - * @param array $typoScript - * @return array - * @todo Extract to core then... - */ - protected function resolveTypoScriptReferences(array $typoScript) - { - $ignoreKeys = []; - foreach ($typoScript as $key => $value) { - if (isset($ignoreKeys[$key])) { - continue; - } - // i am a reference - if ($value[0] === '<') { - if (isset($typoScript[$key . '.'])) { - $oldTypoScript = $typoScript[$key . '.']; - } else { - $oldTypoScript = []; - } - // detect search level - $referencePath = trim(substr($value, 1)); - $dotPosition = strpos($referencePath, '.'); - if ($dotPosition === 0) { - // same position x =< .y - list($flatValue, $arrayValue) = $this->typoScriptParser->getVal(substr($referencePath, 1), $typoScript); - } else { - list($flatValue, $arrayValue) = $this->typoScriptParser->getVal($referencePath, $GLOBALS['TSFE']->tmpl->setup); - } - if (is_array($arrayValue)) { - $typoScript[$key . '.'] = array_replace_recursive($arrayValue, $oldTypoScript); - } - if ($flatValue[0] === '<') { - $temporaryTypoScript = [ - 'temp' => $flatValue, - 'temp.' => $typoScript[$key . '.'], - ]; - $temporaryTypoScript = $this->resolveTypoScriptReferences($temporaryTypoScript); - $arrayValue = array_replace_recursive($temporaryTypoScript['temp.'], $arrayValue, $oldTypoScript); - } - if (is_array($arrayValue)) { - $typoScript[$key . '.'] = array_replace_recursive($arrayValue, $oldTypoScript); - } elseif (isset($flatValue)) { - $typoScript[$key] = $flatValue; - } else { - $typoScript[$key . '.'] = $oldTypoScript; - } - } - // if array, then look deeper - if (isset($typoScript[$key . '.'])) { - $ignoreKeys[$key . '.'] = true; - $typoScript[$key . '.'] = $this->resolveTypoScriptReferences($typoScript[$key . '.']); - } elseif (is_array($typoScript[$key])) { - // if array, then look deeper - $typoScript[$key] = $this->resolveTypoScriptReferences($typoScript[$key]); - } - } - return $typoScript; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Runtime/Exception/PropertyMappingException.php b/typo3/sysext/form/Classes/Domain/Runtime/Exception/PropertyMappingException.php new file mode 100644 index 000000000000..60a22d53867c --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Runtime/Exception/PropertyMappingException.php @@ -0,0 +1,29 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Runtime\Exception; + +/* + * 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\Form\Domain\Exception; + +/** + * This Exception is thrown in the FormRuntime if the PropertyMapper throws + * a \TYPO3\CMS\Extbase\Property\Exception. It adds some more Information to + * better understand why the PropertyMapper failed to map the properties + * + * @api + */ +class PropertyMappingException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php b/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php new file mode 100644 index 000000000000..395f20c0a58f --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Runtime/FormRuntime.php @@ -0,0 +1,780 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Runtime; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Error\Result; +use TYPO3\CMS\Extbase\Mvc\Controller\Arguments; +use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext; +use TYPO3\CMS\Extbase\Mvc\Web\Request; +use TYPO3\CMS\Extbase\Mvc\Web\Response; +use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Property\Exception as PropertyException; +use TYPO3\CMS\Extbase\Reflection\PropertyReflection; +use TYPO3\CMS\Extbase\Utility\ArrayUtility; +use TYPO3\CMS\Form\Domain\Exception\RenderingException; +use TYPO3\CMS\Form\Domain\Finishers\FinisherContext; +use TYPO3\CMS\Form\Domain\Model\FormDefinition; +use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; +use TYPO3\CMS\Form\Domain\Model\FormElements\Page; +use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; +use TYPO3\CMS\Form\Domain\Renderer\RendererInterface; +use TYPO3\CMS\Form\Domain\Runtime\Exception\PropertyMappingException; +use TYPO3\CMS\Form\Mvc\Validation\EmptyValidator; +use TYPO3\CMS\Form\Utility\ArrayUtility as FormArrayUtility; +use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; + +/** + * This class implements the *runtime logic* of a form, i.e. deciding which + * page is shown currently, what the current values of the form are, trigger + * validation and property mapping. + * + * You generally receive an instance of this class by calling {@link \TYPO3\CMS\Form\Domain\Model\FormDefinition::bind}. + * + * Rendering a Form + * ================ + * + * That's easy, just call render() on the FormRuntime: + * + * /---code php + * $form = $formDefinition->bind($request, $response); + * $renderedForm = $form->render(); + * \--- + * + * Accessing Form Values + * ===================== + * + * In order to get the values the user has entered into the form, you can access + * this object like an array: If a form field with the identifier *firstName* + * exists, you can do **$form['firstName']** to retrieve its current value. + * + * You can also set values in the same way. + * + * Rendering Internals + * =================== + * + * The FormRuntime asks the FormDefinition about the configured Renderer + * which should be used ({@link \TYPO3\CMS\Form\Domain\Model\FormDefinition::getRendererClassName}), + * and then trigger render() on this element. + * + * This makes it possible to declaratively define how a form should be rendered. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + * @api + */ +class FormRuntime implements RootRenderableInterface, \ArrayAccess +{ + const HONEYPOT_NAME_SESSION_IDENTIFIER = 'tx_form_honeypot_name_'; + + /** + * @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface + */ + protected $objectManager; + + /** + * @var \TYPO3\CMS\Form\Domain\Model\FormDefinition + */ + protected $formDefinition; + + /** + * @var \TYPO3\CMS\Extbase\Mvc\Web\Request + */ + protected $request; + + /** + * @var \TYPO3\CMS\Extbase\Mvc\Web\Response + */ + protected $response; + + /** + * @var \TYPO3\CMS\Form\Domain\Runtime\FormState + */ + protected $formState; + + /** + * The current page is the page which will be displayed to the user + * during rendering. + * + * If $currentPage is NULL, the *last* page has been submitted and + * finishing actions need to take place. You should use $this->isAfterLastPage() + * instead of explicitely checking for NULL. + * + * @var \TYPO3\CMS\Form\Domain\Model\FormElements\Page + */ + protected $currentPage = null; + + /** + * Reference to the page which has been shown on the last request (i.e. + * we have to handle the submitted data from lastDisplayedPage) + * + * @var \TYPO3\CMS\Form\Domain\Model\FormElements\Page + */ + protected $lastDisplayedPage = null; + + /** + * @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService + */ + protected $hashService; + + /** + * @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService + * @return void + * @internal + */ + public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService) + { + $this->hashService = $hashService; + } + + /** + * @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager + * @internal + */ + public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager) + { + $this->objectManager = $objectManager; + } + + /** + * @param FormDefinition $formDefinition + * @param Request $request + * @param Response $response + * @api + */ + public function __construct(FormDefinition $formDefinition, Request $request, Response $response) + { + $this->formDefinition = $formDefinition; + $arguments = $request->getArguments(); + $this->request = clone $request; + $formIdentifier = $this->formDefinition->getIdentifier(); + if (isset($arguments[$formIdentifier])) { + $this->request->setArguments($arguments[$formIdentifier]); + } + + $this->response = $response; + } + + /** + * @return void + * @internal + */ + public function initializeObject() + { + $this->initializeFormStateFromRequest(); + $this->initializeCurrentPageFromRequest(); + $this->initializeHoneypotFromRequest(); + + if (!$this->isFirstRequest() && $this->getRequest()->getMethod() === 'POST') { + $this->processSubmittedFormValues(); + } + + $this->renderHoneypot(); + } + + /** + * @return void + */ + protected function initializeFormStateFromRequest() + { + $serializedFormStateWithHmac = $this->request->getInternalArgument('__state'); + if ($serializedFormStateWithHmac === null) { + $this->formState = GeneralUtility::makeInstance(FormState::class); + } else { + $serializedFormState = $this->hashService->validateAndStripHmac($serializedFormStateWithHmac); + $this->formState = unserialize(base64_decode($serializedFormState)); + } + } + + /** + * @return void + */ + protected function initializeCurrentPageFromRequest() + { + if (!$this->formState->isFormSubmitted()) { + $this->currentPage = $this->formDefinition->getPageByIndex(0); + return; + } + $this->lastDisplayedPage = $this->formDefinition->getPageByIndex($this->formState->getLastDisplayedPageIndex()); + + // We know now that lastDisplayedPage is filled + $currentPageIndex = (int)$this->request->getInternalArgument('__currentPage'); + if ($currentPageIndex > $this->lastDisplayedPage->getIndex() + 1) { + // We only allow jumps to following pages + $currentPageIndex = $this->lastDisplayedPage->getIndex() + 1; + } + + // We now know that the user did not try to skip a page + if ($currentPageIndex === count($this->formDefinition->getPages())) { + // Last Page + $this->currentPage = null; + } else { + $this->currentPage = $this->formDefinition->getPageByIndex($currentPageIndex); + } + } + + /** + * @return void + */ + protected function initializeHoneypotFromRequest() + { + $renderingOptions = $this->formDefinition->getRenderingOptions(); + if (!isset($renderingOptions['honeypot']['enable']) || $renderingOptions['honeypot']['enable'] === false || TYPO3_MODE === 'BE') { + return; + } + + FormArrayUtility::assertAllArrayKeysAreValid($renderingOptions['honeypot'], ['enable', 'formElementToUse']); + + if (!$this->isFirstRequest()) { + $elementsCount = count($this->lastDisplayedPage->getElements()); + if ($elementsCount === 0) { + return; + } + + $honeypotNameFromSession = $this->getHoneypotNameFromSession($this->lastDisplayedPage); + if ($honeypotNameFromSession) { + $honeypotElement = $this->lastDisplayedPage->createElement($honeypotNameFromSession, $renderingOptions['honeypot']['formElementToUse']); + $validator = $this->objectManager->get(EmptyValidator::class); + $honeypotElement->addValidator($validator); + } + } + } + + /** + * @return void + */ + protected function renderHoneypot() + { + $renderingOptions = $this->formDefinition->getRenderingOptions(); + if (!isset($renderingOptions['honeypot']['enable']) || $renderingOptions['honeypot']['enable'] === false || TYPO3_MODE === 'BE') { + return; + } + + FormArrayUtility::assertAllArrayKeysAreValid($renderingOptions['honeypot'], ['enable', 'formElementToUse']); + + if (!$this->isAfterLastPage()) { + $elementsCount = count($this->currentPage->getElements()); + if ($elementsCount === 0) { + return; + } + + if (!$this->isFirstRequest()) { + $honeypotNameFromSession = $this->getHoneypotNameFromSession($this->lastDisplayedPage); + if ($honeypotNameFromSession) { + $honeypotElement = $this->formDefinition->getElementByIdentifier($honeypotNameFromSession); + if ($honeypotElement instanceof FormElementInterface) { + $this->lastDisplayedPage->removeElement($honeypotElement); + } + } + } + + $elementsCount = count($this->currentPage->getElements()); + $randomElementNumber = mt_rand(0, ($elementsCount - 1)); + $honeypotName = substr(str_shuffle('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'), 0, mt_rand(5, 26)); + + $referenceElement = $this->currentPage->getElements()[$randomElementNumber]; + $honeypotElement = $this->currentPage->createElement($honeypotName, $renderingOptions['honeypot']['formElementToUse']); + $validator = $this->objectManager->get(EmptyValidator::class); + + $honeypotElement->addValidator($validator); + if (mt_rand(0, 1) === 1) { + $this->currentPage->moveElementAfter($honeypotElement, $referenceElement); + } else { + $this->currentPage->moveElementBefore($honeypotElement, $referenceElement); + } + $this->setHoneypotNameInSession($this->currentPage, $honeypotName); + } + } + + /** + * @param Page $page + * return null|string + */ + protected function getHoneypotNameFromSession(Page $page) + { + if ($this->getTypoScriptFrontendController()->loginUser) { + $honeypotNameFromSession = $this->getTypoScriptFrontendController()->fe_user->getKey( + 'user', + self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->getIdentifier() . $page->getIdentifier() + ); + } else { + $honeypotNameFromSession = $this->getTypoScriptFrontendController()->fe_user->getKey( + 'ses', + self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->getIdentifier() . $page->getIdentifier() + ); + } + return $honeypotNameFromSession; + } + + /** + * @param Page $page + * @param string $honeypotName + * @return void + */ + protected function setHoneypotNameInSession(Page $page, string $honeypotName) + { + if ($this->getTypoScriptFrontendController()->loginUser) { + $this->getTypoScriptFrontendController()->fe_user->setKey( + 'user', + self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->getIdentifier() . $page->getIdentifier(), + $honeypotName + ); + } else { + $this->getTypoScriptFrontendController()->fe_user->setKey( + 'ses', + self::HONEYPOT_NAME_SESSION_IDENTIFIER . $this->getIdentifier() . $page->getIdentifier(), + $honeypotName + ); + } + } + + /** + * Returns TRUE if the last page of the form has been submitted, otherwise FALSE + * + * @return bool + */ + protected function isAfterLastPage(): bool + { + return $this->currentPage === null; + } + + /** + * Returns TRUE if no previous page is stored in the FormState, otherwise FALSE + * + * @return bool + */ + protected function isFirstRequest(): bool + { + return $this->lastDisplayedPage === null; + } + + /** + * @return void + */ + protected function processSubmittedFormValues() + { + $result = $this->mapAndValidatePage($this->lastDisplayedPage); + if ($result->hasErrors() && !$this->userWentBackToPreviousStep()) { + $this->currentPage = $this->lastDisplayedPage; + $this->request->setOriginalRequestMappingResults($result); + } + } + + /** + * returns TRUE if the user went back to any previous step in the form. + * + * @return bool + */ + protected function userWentBackToPreviousStep(): bool + { + return !$this->isAfterLastPage() && !$this->isFirstRequest() && $this->currentPage->getIndex() < $this->lastDisplayedPage->getIndex(); + } + + /** + * @param Page $page + * @return Result + * @throws PropertyMappingException + */ + protected function mapAndValidatePage(Page $page): Result + { + $result = $this->objectManager->get(Result::class); + $requestArguments = $this->request->getArguments(); + + $propertyPathsForWhichPropertyMappingShouldHappen = []; + $registerPropertyPaths = function ($propertyPath) use (&$propertyPathsForWhichPropertyMappingShouldHappen) { + $propertyPathParts = explode('.', $propertyPath); + $accumulatedPropertyPathParts = []; + foreach ($propertyPathParts as $propertyPathPart) { + $accumulatedPropertyPathParts[] = $propertyPathPart; + $temporaryPropertyPath = implode('.', $accumulatedPropertyPathParts); + $propertyPathsForWhichPropertyMappingShouldHappen[$temporaryPropertyPath] = $temporaryPropertyPath; + } + }; + foreach ($page->getElementsRecursively() as $element) { + $value = ArrayUtility::getValueByPath($requestArguments, $element->getIdentifier()); + $element->onSubmit($this, $value, $requestArguments); + + $this->formState->setFormValue($element->getIdentifier(), $value); + $registerPropertyPaths($element->getIdentifier()); + } + + // The more parts the path has, the more early it is processed + usort($propertyPathsForWhichPropertyMappingShouldHappen, function ($a, $b) { + return substr_count($b, '.') - substr_count($a, '.'); + }); + + $processingRules = $this->formDefinition->getProcessingRules(); + + foreach ($propertyPathsForWhichPropertyMappingShouldHappen as $propertyPath) { + if (isset($processingRules[$propertyPath])) { + $processingRule = $processingRules[$propertyPath]; + $value = $this->formState->getFormValue($propertyPath); + try { + $value = $processingRule->process($value); + } catch (PropertyException $exception) { + throw new PropertyMappingException( + 'Failed to process FormValue at "' . $propertyPath . '" from "' . gettype($value) . '" to "' . $processingRule->getDataType() . '"', + 1480024933, + $exception + ); + } + $result->forProperty($propertyPath)->merge($processingRule->getProcessingMessages()); + $this->formState->setFormValue($propertyPath, $value); + } + } + + return $result; + } + + /** + * Override the current page taken from the request, rendering the page with index $pageIndex instead. + * + * This is typically not needed in production code, but it is very helpful when displaying + * some kind of "preview" of the form. + * + * @param int $pageIndex + * @return void + * @api + */ + public function overrideCurrentPage(int $pageIndex) + { + $this->currentPage = $this->formDefinition->getPageByIndex($pageIndex); + } + + /** + * Render this form. + * + * @return null|string rendered form + * @throws RenderingException + * @api + */ + public function render() + { + if ($this->isAfterLastPage()) { + $this->invokeFinishers(); + return $this->response->getContent(); + } + + $this->formState->setLastDisplayedPageIndex($this->currentPage->getIndex()); + + if ($this->formDefinition->getRendererClassName() === null) { + throw new RenderingException(sprintf('The form definition "%s" does not have a rendererClassName set.', $this->formDefinition->getIdentifier()), 1326095912); + } + $rendererClassName = $this->formDefinition->getRendererClassName(); + $renderer = $this->objectManager->get($rendererClassName); + if (!($renderer instanceof RendererInterface)) { + throw new RenderingException(sprintf('The renderer "%s" des not implement RendererInterface', $rendererClassName), 1326096024); + } + + $controllerContext = $this->getControllerContext(); + + $renderer->setControllerContext($controllerContext); + $renderer->setFormRuntime($this); + return $renderer->render($this); + } + + /** + * Executes all finishers of this form + * + * @return void + */ + protected function invokeFinishers() + { + $finisherContext = $this->objectManager->get(FinisherContext::class, + $this, + $this->getControllerContext() + ); + foreach ($this->formDefinition->getFinishers() as $finisher) { + $finisher->execute($finisherContext); + if ($finisherContext->isCancelled()) { + break; + } + } + } + + /** + * @return string The identifier of underlying form + * @api + */ + public function getIdentifier(): string + { + return $this->formDefinition->getIdentifier(); + } + + /** + * Get the request this object is bound to. + * + * This is mostly relevant inside Finishers, where you f.e. want to redirect + * the user to another page. + * + * @return Request the request this object is bound to + * @api + */ + public function getRequest(): Request + { + return $this->request; + } + + /** + * Get the response this object is bound to. + * + * This is mostly relevant inside Finishers, where you f.e. want to set response + * headers or output content. + * + * @return Response the response this object is bound to + * @api + */ + public function getResponse(): Response + { + return $this->response; + } + + /** + * Returns the currently selected page + * + * @return Page + * @api + */ + public function getCurrentPage(): Page + { + return $this->currentPage; + } + + /** + * Returns the previous page of the currently selected one or NULL if there is no previous page + * + * @return null|Page + * @api + */ + public function getPreviousPage() + { + $previousPageIndex = $this->currentPage->getIndex() - 1; + if ($this->formDefinition->hasPageWithIndex($previousPageIndex)) { + return $this->formDefinition->getPageByIndex($previousPageIndex); + } + return null; + } + + /** + * Returns the next page of the currently selected one or NULL if there is no next page + * + * @return null|Page + * @api + */ + public function getNextPage() + { + $nextPageIndex = $this->currentPage->getIndex() + 1; + if ($this->formDefinition->hasPageWithIndex($nextPageIndex)) { + return $this->formDefinition->getPageByIndex($nextPageIndex); + } + return null; + } + + /** + * @return ControllerContext + */ + protected function getControllerContext(): ControllerContext + { + $uriBuilder = $this->objectManager->get(UriBuilder::class); + $uriBuilder->setRequest($this->request); + $controllerContext = $this->objectManager->get(ControllerContext::class); + $controllerContext->setRequest($this->request); + $controllerContext->setResponse($this->response); + $controllerContext->setArguments($this->objectManager->get(Arguments::class, [])); + $controllerContext->setUriBuilder($uriBuilder); + return $controllerContext; + } + + /** + * Abstract "type" of this Renderable. Is used during the rendering process + * to determine the template file or the View PHP class being used to render + * the particular element. + * + * @return string + * @api + */ + public function getType(): string + { + return $this->formDefinition->getType(); + } + + /** + * @param string $identifier + * @return bool + * @internal + */ + public function offsetExists($identifier) + { + if ($this->getElementValue($identifier) !== null) { + return true; + } + + if (is_callable([$this, 'get' . ucfirst($identifier)])) { + return true; + } + if (is_callable([$this, 'has' . ucfirst($identifier)])) { + return true; + } + if (is_callable([$this, 'is' . ucfirst($identifier)])) { + return true; + } + if (property_exists($this, $identifier)) { + $propertyReflection = new PropertyReflection($this, $identifier); + return $propertyReflection->isPublic(); + } + + return false; + } + + /** + * @param string $identifier + * @return mixed + * @internal + */ + public function offsetGet($identifier) + { + if ($this->getElementValue($identifier) !== null) { + return $this->getElementValue($identifier); + } + $getterMethodName = 'get' . ucfirst($identifier); + if (is_callable([$this, $getterMethodName])) { + return $this->{$getterMethodName}(); + } + return null; + } + + /** + * @param string $identifier + * @param mixed $value + * @return void + * @internal + */ + public function offsetSet($identifier, $value) + { + $this->formState->setFormValue($identifier, $value); + } + + /** + * @param string $identifier + * @return void + * @internal + */ + public function offsetUnset($identifier) + { + $this->formState->setFormValue($identifier, null); + } + + /** + * Returns the value of the specified element + * + * @param string $identifier + * @return mixed + * @api + */ + public function getElementValue(string $identifier) + { + $formValue = $this->formState->getFormValue($identifier); + if ($formValue !== null) { + return $formValue; + } + return $this->formDefinition->getElementDefaultValueByIdentifier($identifier); + } + + /** + * @return array<Page> The Form's pages in the correct order + * @api + */ + public function getPages(): array + { + return $this->formDefinition->getPages(); + } + + /** + * @return FormState + * @internal + */ + public function getFormState(): FormState + { + return $this->formState; + } + + /** + * Get all rendering options + * + * @return array associative array of rendering options + * @api + */ + public function getRenderingOptions(): array + { + return $this->formDefinition->getRenderingOptions(); + } + + /** + * Get the renderer class name to be used to display this renderable; + * must implement RendererInterface + * + * @return string the renderer class name + * @api + */ + public function getRendererClassName(): string + { + return $this->formDefinition->getRendererClassName(); + } + + /** + * Get the label which shall be displayed next to the form element + * + * @return string + * @api + */ + public function getLabel(): string + { + return $this->formDefinition->getLabel(); + } + + /** + * Get the underlying form definition from the runtime + * + * @return FormDefinition + * @api + */ + public function getFormDefinition(): FormDefinition + { + return $this->formDefinition; + } + + /** + * This is a callback that is invoked by the Renderer before the corresponding element is rendered. + * Use this to access previously submitted values and/or modify the $formRuntime before an element + * is outputted to the browser. + * + * @param FormRuntime $formRuntime + * @return void + * @api + */ + public function beforeRendering(FormRuntime $formRuntime) + { + } + + /** + * @return TypoScriptFrontendController + */ + protected function getTypoScriptFrontendController() + { + return $GLOBALS['TSFE']; + } +} diff --git a/typo3/sysext/form/Classes/Domain/Runtime/FormState.php b/typo3/sysext/form/Classes/Domain/Runtime/FormState.php new file mode 100644 index 000000000000..ff6b43733fa0 --- /dev/null +++ b/typo3/sysext/form/Classes/Domain/Runtime/FormState.php @@ -0,0 +1,100 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Domain\Runtime; + +/* + * 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\Extbase\Utility\ArrayUtility; + +/** + * The current state of the form which is attached to the {@link FormRuntime} + * and saved in a session or the client. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + * @internal + */ +class FormState +{ + + /** + * Constant which means that we are currently not on any page; i.e. the form + * has never rendered before. + */ + const NOPAGE = -1; + + /** + * The last displayed page index + * + * @var int + */ + protected $lastDisplayedPageIndex = self::NOPAGE; + + /** + * @var array + */ + protected $formValues = []; + + /** + * @return bool FALSE if the form has never been submitted before, TRUE otherwise + */ + public function isFormSubmitted(): bool + { + return $this->lastDisplayedPageIndex !== self::NOPAGE; + } + + /** + * @return int + */ + public function getLastDisplayedPageIndex(): int + { + return $this->lastDisplayedPageIndex; + } + + /** + * @param int $lastDisplayedPageIndex + * @return void + */ + public function setLastDisplayedPageIndex(int $lastDisplayedPageIndex) + { + $this->lastDisplayedPageIndex = $lastDisplayedPageIndex; + } + + /** + * @return array + */ + public function getFormValues(): array + { + return $this->formValues; + } + + /** + * @param string $propertyPath + * @param mixed $value + * @return void + */ + public function setFormValue(string $propertyPath, $value) + { + $this->formValues = ArrayUtility::setValueByPath($this->formValues, $propertyPath, $value); + } + + /** + * @param string $propertyPath + * @return mixed + */ + public function getFormValue(string $propertyPath) + { + return ArrayUtility::getValueByPath($this->formValues, $propertyPath); + } +} diff --git a/typo3/sysext/form/Classes/Domain/Validator/AbstractValidator.php b/typo3/sysext/form/Classes/Domain/Validator/AbstractValidator.php deleted file mode 100644 index adfb477a54ee..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/AbstractValidator.php +++ /dev/null @@ -1,116 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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\Extbase\Utility\LocalizationUtility; -use TYPO3\CMS\Form\Utility\FormUtility; - -abstract class AbstractValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator -{ - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate'; - - /** - * @var FormUtility - */ - protected $formUtility; - - /** - * @var mixed - */ - protected $rawArgument; - - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - ]; - - /** - * This validator always needs to be executed even if the given value is empty. - * See AbstractValidator::validate() - * - * @var bool - */ - protected $acceptsEmptyValues = false; - - /** - * @param mixed $rawArgument - */ - public function setRawArgument($rawArgument) - { - $this->rawArgument = $rawArgument; - } - - /** - * @param FormUtility $formUtility - */ - public function setFormUtility(FormUtility $formUtility) - { - $this->formUtility = $formUtility; - } - - /** - * Substitute makers in the message text - * In some cases this method will be override by rule class - * - * @param string $message Message text with markers - * @return string Message text with substituted markers - */ - public function substituteMarkers($message) - { - return $message; - } - - /** - * Get the local language label(s) for the message - * In some cases this method will be override by rule class - * - * @param string $type The type - * @return string The local language message label - */ - public function getLocalLanguageLabel($type = '') - { - $label = static::LOCALISATION_OBJECT_NAME . '.' . $type; - $message = LocalizationUtility::translate($label, 'form'); - return $message; - } - - /** - * Set the message, like 'required' for the validation rule - * and substitutes markers for values, like %maximum - * - * - * @param mixed $message Message as string or TS - * @param NULL|string $type Name of the cObj - * @param string $messageType message or error - * @return string - */ - public function renderMessage($message = null, $type = null, $messageType = 'message') - { - $message = $this->formUtility->renderItem( - $message, - $type, - $this->getLocalLanguageLabel($messageType) - ); - return $this->substituteMarkers($message); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/AlphabeticValidator.php b/typo3/sysext/form/Classes/Domain/Validator/AlphabeticValidator.php deleted file mode 100644 index 57085725ec6d..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/AlphabeticValidator.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class AlphabeticValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'allowWhiteSpace' => ['', 'Whitespaces are allowed', 'boolean', false], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_alphabetic'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if ( - !isset($this->options['allowWhiteSpace']) - || $this->options['allowWhiteSpace'] === '' - || (int)$this->options['allowWhiteSpace'] === 0 - ) { - $this->options['allowWhiteSpace'] = false; - } else { - $this->options['allowWhiteSpace'] = true; - } - - $whiteSpace = $this->options['allowWhiteSpace'] ? '\\s' : ''; - $pattern = '/[^\pL' . $whiteSpace . ']/u'; - $compareValue = preg_replace($pattern, '', (string)$value); - - if ($compareValue !== $value) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442004245 - ); - } - } - - /** - * Get the local language label(s) for the message - * Overrides the abstract - * - * @param string $type The type - * @return string The local language message label - * @see \TYPO3\CMS\Form\Validation\AbstractValidator::_getLocalLanguageLabel() - */ - public function getLocalLanguageLabel($type = '') - { - $label = static::LOCALISATION_OBJECT_NAME . '.message'; - $messages[] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label, 'form'); - if ($this->options['allowWhiteSpace']) { - $messages[] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 2, 'form'); - } - $message = implode(', ', $messages); - return $message; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/AlphanumericValidator.php b/typo3/sysext/form/Classes/Domain/Validator/AlphanumericValidator.php deleted file mode 100644 index 1bd18c8fc97e..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/AlphanumericValidator.php +++ /dev/null @@ -1,88 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class AlphanumericValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'allowWhiteSpace' => ['', 'Whitespaces are allowed', 'boolean', false], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_alphanumeric'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if ( - !isset($this->options['allowWhiteSpace']) - || $this->options['allowWhiteSpace'] === '' - || (int)$this->options['allowWhiteSpace'] === 0 - ) { - $this->options['allowWhiteSpace'] = false; - } else { - $this->options['allowWhiteSpace'] = true; - } - - $whiteSpace = $this->options['allowWhiteSpace'] ? '\\s' : ''; - $pattern = '/[^\pL\d)' . $whiteSpace . ']/u'; - $compareValue = preg_replace($pattern, '', (string)$value); - - if ($compareValue !== $value) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442004457 - ); - } - } - - /** - * Get the local language label(s) for the message - * Overrides the abstract - * - * @param string $type The type - * @return string The local language message label - * @see \TYPO3\CMS\Form\Validation\AbstractValidator::_getLocalLanguageLabel() - */ - public function getLocalLanguageLabel($type = '') - { - $label = static::LOCALISATION_OBJECT_NAME . '.message'; - $messages[] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label, 'form'); - if ($this->options['allowWhiteSpace']) { - $messages[] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 2, 'form'); - } - $message = implode(', ', $messages); - return $message; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/BetweenValidator.php b/typo3/sysext/form/Classes/Domain/Validator/BetweenValidator.php deleted file mode 100644 index b528845ebeb1..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/BetweenValidator.php +++ /dev/null @@ -1,109 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class BetweenValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'minimum' => ['', 'The minimum value', 'integer', true], - 'maximum' => ['', 'The maximum value', 'integer', true], - 'inclusive' => ['', 'Minimum and maximum value are inclusive in comparison', 'integer', false], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_between'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if ( - !isset($this->options['inclusive']) - || $this->options['inclusive'] === '' - || (int)$this->options['inclusive'] === 0 - ) { - if ($value <= $this->options['minimum'] || $value >= $this->options['maximum']) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442003544 - ); - } - } else { - if ($value < $this->options['minimum'] || $value > $this->options['maximum']) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442003545 - ); - } - } - } - - /** - * Get the local language label(s) for the message - * Overrides the abstract - * - * @param string $type The type - * @return string The local language message label - * @see \TYPO3\CMS\Form\Validation\AbstractValidator::_getLocalLanguageLabel() - */ - public function getLocalLanguageLabel($type = '') - { - $label = static::LOCALISATION_OBJECT_NAME . '.' . $type; - $messages[] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label, 'form'); - if ($this->inclusive) { - $messages[] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 2, 'form'); - } - $message = implode(', ', $messages); - return $message; - } - - /** - * Substitute makers in the message text - * Overrides the abstract - * - * @param string $message Message text with markers - * @return string Message text with substituted markers - */ - public function substituteMarkers($message) - { - return str_replace( - ['%minimum', '%maximum'], - [$this->options['minimum'], $this->options['maximum']], - $message - ); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/DateValidator.php b/typo3/sysext/form/Classes/Domain/Validator/DateValidator.php deleted file mode 100644 index fca3f0d76539..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/DateValidator.php +++ /dev/null @@ -1,154 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class DateValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'format' => ['', 'The maximum value', 'string', true], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_date'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if ( - $this->options['format'] === null - || $this->options['format'] === '' - ) { - $this->options['format'] = '%e-%m-%Y'; - } - - if (function_exists('strptime')) { - $parsedDate = strptime($value, $this->options['format']); - $parsedDateYear = $parsedDate['tm_year'] + 1900; - $parsedDateMonth = $parsedDate['tm_mon'] + 1; - $parsedDateDay = $parsedDate['tm_mday']; - if (!checkdate($parsedDateMonth, $parsedDateDay, $parsedDateYear)) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442001386 - ); - return; - } - } else { - // %a => D : An abbreviated textual representation of the day (conversion works only for english) - // %A => l : A full textual representation of the day (conversion works only for english) - // %d => d : Day of the month, 2 digits with leading zeros - // %e => j : Day of the month, 2 digits without leading zeros - // %j => z : Day of the year, 3 digits with leading zeros - // %b => M : Abbreviated month name, based on the locale (conversion works only for english) - // %B => F : Full month name, based on the locale (conversion works only for english) - // %h => M : Abbreviated month name, based on the locale (an alias of %b) (conversion works only for english) - // %m => m : Two digit representation of the month - // %y => y : Two digit representation of the year - // %Y => Y : Four digit representation for the year - $dateTimeFormat = str_replace( - ['%a', '%A', '%d', '%e', '%j', '%b', '%B', '%h', '%m', '%y', '%Y'], - ['D', 'l', 'd', 'j', 'z', 'M', 'F', 'M', 'm', 'y', 'Y'], - $this->options['format'] - ); - $dateTimeObject = date_create_from_format($dateTimeFormat, $value); - if ($dateTimeObject === false) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442001386 - ); - return; - } - - if ($value !== $dateTimeObject->format($dateTimeFormat)) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442001386 - ); - } - } - } - - /** - * Substitute makers in the message text - * Overrides the abstract - * - * @param string $message Message text with markers - * @return string Message text with substituted markers - */ - public function substituteMarkers($message) - { - $humanReadableDateFormat = $this->humanReadableDateFormat($this->options['format']); - $message = str_replace('%format', $humanReadableDateFormat, $message); - return $message; - } - - /** - * Converts strftime date format to human readable format - * according to local language. - * - * Example for default language: %e-%m-%Y becomes d-mm-yyyy - * - * @param string $format strftime format - * @return string Human readable format - */ - protected function humanReadableDateFormat($format) - { - $label = self::LOCALISATION_OBJECT_NAME . '.strftime.'; - $pairs = [ - '%A' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'A', 'form'), - '%a' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'a', 'form'), - '%d' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'd', 'form'), - '%e' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'e', 'form'), - '%B' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'B', 'form'), - '%b' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'b', 'form'), - '%m' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'm', 'form'), - '%Y' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'Y', 'form'), - '%y' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'y', 'form'), - '%H' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'H', 'form'), - '%I' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'I', 'form'), - '%M' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'M', 'form'), - '%S' => \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 'S', 'form') - ]; - $humanReadableFormat = str_replace(array_keys($pairs), array_values($pairs), $format); - return $humanReadableFormat; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/DigitValidator.php b/typo3/sysext/form/Classes/Domain/Validator/DigitValidator.php deleted file mode 100644 index 3654bcae4a10..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/DigitValidator.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class DigitValidator extends AbstractValidator -{ - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_digit'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - $compareValue = preg_replace('/[^0-9]/', '', (string)$value); - if ($compareValue !== $value) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442000838 - ); - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/EmailValidator.php b/typo3/sysext/form/Classes/Domain/Validator/EmailValidator.php deleted file mode 100644 index edb05fcb1818..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/EmailValidator.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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\Core\Utility\GeneralUtility; - -/** - * Class EmailValidator - */ -class EmailValidator extends AbstractValidator -{ - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_email'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if (empty($value) || !is_string($value)) { - return; - } - - if (!GeneralUtility::validEmail($value)) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442000235 - ); - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/EqualsValidator.php b/typo3/sysext/form/Classes/Domain/Validator/EqualsValidator.php deleted file mode 100644 index 4f5ea32b9c87..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/EqualsValidator.php +++ /dev/null @@ -1,69 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class EqualsValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'field' => ['', 'The field to be compared', 'string', true], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_equals'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - $comparisonValue = $this->rawArgument[$this->options['field']]; - if ($value !== $comparisonValue) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442005826 - ); - } - } - - /** - * Substitute makers in the message text - * Overrides the abstract - * - * @param string $message Message text with markers - * @return string Message text with substituted markers - */ - public function substituteMarkers($message) - { - $message = str_replace('%field', $this->options['field'], $message); - return $message; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/FileAllowedTypesValidator.php b/typo3/sysext/form/Classes/Domain/Validator/FileAllowedTypesValidator.php deleted file mode 100644 index b7b58f245f7d..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/FileAllowedTypesValidator.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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\Core\Utility\GeneralUtility; - -class FileAllowedTypesValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'types' => ['', 'The allowed file types', 'string', true], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_fileallowedtypes'; - - /** - * Check if the file mime type is allowed. - * - * The mime type is set in the propertymapper - * - * @see TYPO3\CMS\Form\Domain\Property\TypeConverter::convertFrom - * - * @param array $value - * @return void - */ - public function isValid($value) - { - $allowedTypes = strtolower($this->options['types']); - $allowedMimeTypes = GeneralUtility::trimExplode(',', $allowedTypes, true); - $fileMimeType = !empty($value['type']) ? strtolower($value['type']) : ''; - - if (!in_array($fileMimeType, $allowedMimeTypes, true)) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442006702 - ); - } - } - - /** - * Substitute makers in the message text - * Overrides the abstract - * - * @param string $message Message text with markers - * @return string Message text with substituted markers - */ - public function substituteMarkers($message) - { - $allowedTypes = strtolower($this->options['types']); - $allowedMimeTypes = GeneralUtility::trimExplode(',', $allowedTypes); - $allowedTypesStringForDisplay = implode(', ', $allowedMimeTypes); - $message = str_replace('%allowedTypes', $allowedTypesStringForDisplay, $message); - - return $message; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/FileMaximumSizeValidator.php b/typo3/sysext/form/Classes/Domain/Validator/FileMaximumSizeValidator.php deleted file mode 100644 index 38d1ba9fb71b..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/FileMaximumSizeValidator.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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\Core\Utility\GeneralUtility; - -class FileMaximumSizeValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'maximum' => ['', 'The maximum file size', 'integer', true], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_filemaximumsize'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - $fileValue = $this->rawArgument[$this->options['element']]; - $value = $fileValue['size']; - if ($value > (int)$this->options['maximum']) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442006702 - ); - } - } - - /** - * Substitute makers in the message text - * Overrides the abstract - * - * @param string $message Message text with markers - * @return string Message text with substituted markers - */ - public function substituteMarkers($message) - { - $message = str_replace('%maximum', GeneralUtility::formatSize($this->options['maximum']), $message); - return $message; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/FileMinimumSizeValidator.php b/typo3/sysext/form/Classes/Domain/Validator/FileMinimumSizeValidator.php deleted file mode 100644 index 5aac7591a05e..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/FileMinimumSizeValidator.php +++ /dev/null @@ -1,72 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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\Core\Utility\GeneralUtility; - -class FileMinimumSizeValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'minimum' => ['', 'The minimum file size', 'integer', true], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_fileminimumsize'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - $fileValue = $this->rawArgument[$this->options['element']]; - $value = $fileValue['size']; - if ($value < (int)$this->options['minimum']) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442006702 - ); - } - } - - /** - * Substitute makers in the message text - * Overrides the abstract - * - * @param string $message Message text with markers - * @return string Message text with substituted markers - */ - public function substituteMarkers($message) - { - $message = str_replace('%minimum', GeneralUtility::formatSize($this->options['minimum']), $message); - return $message; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/FloatValidator.php b/typo3/sysext/form/Classes/Domain/Validator/FloatValidator.php deleted file mode 100644 index 4322d718c497..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/FloatValidator.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class FloatValidator extends AbstractValidator -{ - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_float'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - $locale = localeconv(); - $valueFiltered = str_replace( - [ - $locale['thousands_sep'], - $locale['mon_thousands_sep'], - $locale['decimal_point'], - $locale['mon_decimal_point'] - ], - [ - '', - '', - '.', - '.' - ], - $value - ); - if ($value != strval((float)$valueFiltered)) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442002070 - ); - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/GreaterThanValidator.php b/typo3/sysext/form/Classes/Domain/Validator/GreaterThanValidator.php deleted file mode 100644 index bd35699a9bd7..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/GreaterThanValidator.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class GreaterThanValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'minimum' => ['', 'The minimum value', 'integer', true], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_greaterthan'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if ((int)$value <= (int)$this->options['minimum']) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442002213 - ); - } - } - - /** - * Substitute makers in the message text - * Overrides the abstract - * - * @param string $message Message text with markers - * @return string Message text with substituted markers - */ - public function substituteMarkers($message) - { - $message = str_replace('%minimum', $this->options['minimum'], $message); - return $message; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/InArrayValidator.php b/typo3/sysext/form/Classes/Domain/Validator/InArrayValidator.php deleted file mode 100644 index ef3c9d3cf90f..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/InArrayValidator.php +++ /dev/null @@ -1,124 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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\Core\Charset\CharsetConverter; -use TYPO3\CMS\Core\Utility\GeneralUtility; - -class InArrayValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'array' => ['', 'The array values from the wizard configuration (array = test1,test2)', 'string', false], - 'array.' => ['', 'The array values from the documented configuration', 'array', false], - 'strict' => ['', 'Compare types', 'boolean', false], - 'ignorecase' => ['', 'Ignore cases', 'boolean', false] - ]; - - /** - * @var CharsetConverter - */ - protected $charsetConverter; - - /** - * Constructor - * - * Creates charsetConverter object if option ignorecase is set - * - * @param array $options - * @throws \TYPO3\CMS\Extbase\Validation\Exception\InvalidValidationOptionsException - */ - public function __construct(array $options) - { - parent::__construct($options); - if (!empty($this->options['ignorecase'])) { - $this->charsetConverter = GeneralUtility::makeInstance(CharsetConverter::class); - } - } - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_inarray'; - - /** - * Check if $value is valid. If it is not valid, add an error to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if (empty($value)) { - return; - } - - /** - * A single select results in a string, - * a multiselect in an array. - * In both cases, the operations will be processed on an array. - */ - if (is_string($value)) { - $value = [$value]; - } - - /** - * The form wizard generates the following configuration: - * array = test1,test2 - * In this case the string has to be exploded. - * The following configuration was documented: - * array { - * 1 = TYPO3 4.5 LTS - * 2 = TYPO3 6.2 LTS - * 3 = TYPO3 7 LTS - * } - * In this case there is already an array but the "options" key differs. - */ - $allowedOptionsArray = []; - if (!empty($this->options['array']) && is_string($this->options['array'])) { - $allowedOptionsArray = GeneralUtility::trimExplode(',', $this->options['array'], true); - } elseif (!empty($this->options['array.']) && is_array($this->options['array.'])) { - $allowedOptionsArray = $this->options['array.']; - } - - if (!empty($this->options['ignorecase'])) { - foreach ($value as &$incomingArrayValue) { - $incomingArrayValue = $this->charsetConverter->conv_case('utf-8', $incomingArrayValue, 'toLower'); - } - foreach ($allowedOptionsArray as &$option) { - $option = $this->charsetConverter->conv_case('utf-8', $option, 'toLower'); - } - } - - foreach ($value as $incomingArrayValue) { - if (!in_array($incomingArrayValue, $allowedOptionsArray, !empty($this->options['strict']))) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442002594 - ); - } - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/IntegerValidator.php b/typo3/sysext/form/Classes/Domain/Validator/IntegerValidator.php deleted file mode 100644 index 87106fa9c146..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/IntegerValidator.php +++ /dev/null @@ -1,62 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class IntegerValidator extends AbstractValidator -{ - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_integer'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - $locale = localeconv(); - $valueFiltered = str_replace( - [ - $locale['thousands_sep'], - $locale['mon_thousands_sep'], - $locale['decimal_point'], - $locale['mon_decimal_point'] - ], - [ - '', - '', - '.', - '.' - ], - $value - ); - if (strval((int)$valueFiltered) != $valueFiltered) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1442000119 - ); - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/IpValidator.php b/typo3/sysext/form/Classes/Domain/Validator/IpValidator.php deleted file mode 100644 index 9cdebec40b85..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/IpValidator.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class IpValidator extends AbstractValidator -{ - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_ip'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if (!preg_match('/\\b(([01]?\\d?\\d|2[0-4]\\d|25[0-5])\\.){3}([01]?\\d?\\d|2[0-4]\\d|25[0-5])\\b/', $value)) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1441999896 - ); - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/LengthValidator.php b/typo3/sysext/form/Classes/Domain/Validator/LengthValidator.php deleted file mode 100644 index 5639aa07d56c..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/LengthValidator.php +++ /dev/null @@ -1,128 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class LengthValidator extends AbstractValidator -{ - /** - * TYPO3 charset encoding object - * - * @var \TYPO3\CMS\Core\Charset\CharsetConverter - */ - protected $charsetConverter = null; - - /** - * @param \TYPO3\CMS\Core\Charset\CharsetConverter $charsetConverter - * @return void - */ - public function injectCharsetConverter(\TYPO3\CMS\Core\Charset\CharsetConverter $charsetConverter) - { - $this->charsetConverter = $charsetConverter; - } - - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'minimum' => ['', 'The minimum value', 'integer', true], - 'maximum' => ['', 'The maximum value', 'integer', false], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_length'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - $length = $this->charsetConverter->strlen('utf-8', $value); - if ($length < (int)$this->options['minimum']) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1441999425 - ); - return; - } - if ( - !isset($this->options['maximum']) - || $this->options['maximum'] === '' - ) { - $this->options['maximum'] = null; - } - if ( - $this->options['maximum'] !== null - && $length > (int)$this->options['maximum'] - ) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1441999425 - ); - } - } - - /** - * Get the local language label(s) for the message - * Overrides the abstract - * - * @param string $type The type - * @return string The local language message label - * @see \TYPO3\CMS\Form\Validation\AbstractValidator::_getLocalLanguageLabel() - */ - public function getLocalLanguageLabel($type = '') - { - $label = static::LOCALISATION_OBJECT_NAME . '.' . $type; - $messages[] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label, 'form'); - if ($this->options['maximum'] !== null) { - $messages[] = \TYPO3\CMS\Extbase\Utility\LocalizationUtility::translate($label . 2, 'form'); - } - $message = implode(', ', $messages); - return $message; - } - - /** - * Substitute makers in the message text - * Overrides the abstract - * - * @param string $message Message text with markers - * @return string Message text with substituted markers - */ - public function substituteMarkers($message) - { - return str_replace( - ['%minimum', '%maximum'], - [$this->options['minimum'], $this->options['maximum']], - $message - ); - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/LessThanValidator.php b/typo3/sysext/form/Classes/Domain/Validator/LessThanValidator.php deleted file mode 100644 index 5c5466cabf3b..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/LessThanValidator.php +++ /dev/null @@ -1,68 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class LessThanValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'maximum' => ['', 'The maximum value', 'integer', true], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_lessthan'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if ($value >= $this->options['maximum'] || !is_numeric($value)) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1441997981 - ); - } - } - - /** - * Substitute makers in the message text - * Overrides the abstract - * - * @param string $message Message text with markers - * @return string Message text with substituted markers - */ - public function substituteMarkers($message) - { - $message = str_replace('%maximum', $this->options['maximum'], $message); - return $message; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/RegExpValidator.php b/typo3/sysext/form/Classes/Domain/Validator/RegExpValidator.php deleted file mode 100644 index 09857bd17ab5..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/RegExpValidator.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class RegExpValidator extends AbstractValidator -{ - /** - * @var array - */ - protected $supportedOptions = [ - 'element' => ['', 'The name of the element', 'string', true], - 'errorMessage' => ['', 'The error message', 'array', true], - 'expression' => ['', 'The regular expression', 'string', true], - ]; - - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_regexp'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if (!preg_match($this->options['expression'], $value)) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1441997233 - ); - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/RequiredValidator.php b/typo3/sysext/form/Classes/Domain/Validator/RequiredValidator.php deleted file mode 100644 index e1c80eface79..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/RequiredValidator.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class RequiredValidator extends AbstractValidator -{ - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_required'; - - /** - * @var bool - */ - protected $allFieldsAreEmpty = true; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if (is_array($value)) { - array_walk_recursive($value, function ($value, $key, $validator) { - if (!empty($value) || $value === '0' || $value === 0) { - $validator->setAllFieldsAreEmpty(false); - } - }, - $this - ); - if ($this->getAllFieldsAreEmpty()) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1441980673 - ); - } - } else { - if ( - empty($value) - && $value !== 0 - && $value !== '0' - ) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 144198067 - ); - } - } - } - - /** - * A helper method for the array_walk_recursive callback in the - * function isValid(). - * If the callback detect a empty value, the - * property allFieldsAreEmpty is set to TRUE. - * - * @param bool $allFieldsAreEmpty - * @return void - */ - protected function setAllFieldsAreEmpty($allFieldsAreEmpty = true) - { - $this->allFieldsAreEmpty = $allFieldsAreEmpty; - } - - /** - * A helper method for the array_walk_recursive callback in the - * function isValid(). - * If the callback detect a empty value, the - * property allFieldsAreEmpty is set to TRUE. - * - * @return bool - */ - protected function getAllFieldsAreEmpty() - { - return $this->allFieldsAreEmpty; - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/UriValidator.php b/typo3/sysext/form/Classes/Domain/Validator/UriValidator.php deleted file mode 100644 index 3d2505c0adcb..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/UriValidator.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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! - */ - -class UriValidator extends AbstractValidator -{ - /** - * Constant for localisation - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_system_validate_uri'; - - /** - * Check if $value is valid. If it is not valid, needs to add an error - * to result. - * - * @param mixed $value - * @return void - */ - public function isValid($value) - { - if (!preg_match('/^(?#Protocol)(?:(?:ht|f)tp(?:s?)\\:\\/\\/|~\\/|\\/)?(?#Username:Password)(?:\\w+:\\w+@)?(?#Subdomains)(?:(?:[-\\w]+\\.)+(?#TopLevel Domains)(?:com|org|net|gov|mil|biz|info|mobi|name|aero|jobs|museum|travel|[a-z]{2}))(?#Port)(?::[\\d]{1,5})?(?#Directories)(?:(?:(?:\\/(?:[-\\w~!$+|.,=]|%[a-f\\d]{2})+)+|\\/)+|\\?|#)?(?#Query)(?:(?:\\?(?:[-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?(?:[-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)(?:&(?:[-\\w~!$+|.,*:]|%[a-f\\d{2}])+=?(?:[-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)*)*(?#Anchor)(?:#(?:[-\\w~!$+|.,*:=]|%[a-f\\d]{2})*)?$/', $value)) { - $this->addError( - $this->renderMessage( - $this->options['errorMessage'][0], - $this->options['errorMessage'][1], - 'error' - ), - 1441997233 - ); - } - } -} diff --git a/typo3/sysext/form/Classes/Domain/Validator/ValidationElementValidator.php b/typo3/sysext/form/Classes/Domain/Validator/ValidationElementValidator.php deleted file mode 100644 index 81d1ca20b7b6..000000000000 --- a/typo3/sysext/form/Classes/Domain/Validator/ValidationElementValidator.php +++ /dev/null @@ -1,257 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Domain\Validator; - -/* - * 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\Extbase\Validation\Validator\ValidatorInterface; - -/** - * A generic object validator which allows for specifying property validators - */ -class ValidationElementValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator implements \TYPO3\CMS\Extbase\Validation\Validator\ObjectValidatorInterface -{ - /** - * @var \SplObjectStorage[] - */ - protected $propertyValidators = []; - - /** - * @var \TYPO3\CMS\Form\Utility\SessionUtility - */ - protected $sessionUtility; - - /** - * @param \TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility - */ - public function injectSessionUtility(\TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility) - { - $this->sessionUtility = $sessionUtility; - } - - /** - * Checks if the given value is valid according to the validator, and returns - * the Error Messages object which occurred. - * - * @param mixed $value The value that should be validated - * @return \TYPO3\CMS\Extbase\Error\Result - * @api - */ - public function validate($value) - { - $this->result = new \TYPO3\CMS\Extbase\Error\Result(); - if ($this->acceptsEmptyValues === false || $this->isEmpty($value) === false) { - if (!is_object($value)) { - $this->addError('Object expected, %1$s given.', 1241099149, [gettype($value)]); - } elseif ($this->isValidatedAlready($value) === false) { - $this->isValid($value); - } - } - - return $this->result; - } - - /** - * Load the property value to be used for validation. - * - * In case the object is a doctrine proxy, we need to load the real instance first. - * - * @param \TYPO3\CMS\Form\Domain\Model\ValidationElement $validationElement - * @param string $propertyName - * @return mixed - */ - protected function getPropertyValue(\TYPO3\CMS\Form\Domain\Model\ValidationElement $validationElement, $propertyName) - { - /** - * If a confirmation page is set and a fileupload was done before - * there is no incoming data if the process action is called. - * The data is only in the session at this time. - * This results in a negative validation (if a validation is set). - * Therefore, look first in the session. - */ - if ($this->sessionUtility->getSessionData($propertyName)) { - $propertyValue = $this->sessionUtility->getSessionData($propertyName); - } else { - $propertyValue = $validationElement->getIncomingField($propertyName); - } - return $propertyValue; - } - - /** - * Checks if the specified property of the given object is valid, and adds - * found errors to the $messages object. - * - * @param mixed $value The value to be validated - * @param \Traversable $validators The validators to be called on the value - * @param string $propertyName Name of ther property to check - * @return void - */ - protected function checkProperty($value, $validators, $propertyName) - { - /** @var \TYPO3\CMS\Extbase\Error\Result $result */ - $result = null; - foreach ($validators as $validator) { - if ($validator instanceof ObjectValidatorInterface) { - $validator->setValidatedInstancesContainer($this->validatedInstancesContainer); - } - - /** - * File upload validation. - * - * If a $_FILES array is found in the request data, - * iterate over all requested files and validate each - * single file. - */ - if ( - isset($value[0]['name']) - && isset($value[0]['type']) - && isset($value[0]['tmp_name']) - && isset($value[0]['size']) - ) { - foreach ($value as $file) { - $currentResult = $validator->validate($file); - if ($currentResult->hasMessages()) { - if ($result == null) { - $result = $currentResult; - } else { - $result->merge($currentResult); - } - } - } - } else { - $currentResult = $validator->validate($value); - if ($currentResult->hasMessages()) { - if ($result == null) { - $result = $currentResult; - } else { - $result->merge($currentResult); - } - } - } - } - if ($result != null) { - $this->result->forProperty($propertyName)->merge($result); - } - } - - /** - * Checks if the given value is valid according to the property validators. - * - * @param mixed $object The value that should be validated - * @return void - * @api - */ - protected function isValid($object) - { - foreach ($this->propertyValidators as $propertyName => $validators) { - $propertyValue = $this->getPropertyValue($object, $propertyName); - $this->checkProperty($propertyValue, $validators, $propertyName); - } - } - - /** - * Checks the given object can be validated by the validator implementation - * - * @param mixed $object The object to be checked - * @return bool TRUE if the given value can be validated - * @api - */ - public function canValidate($object) - { - if ( - is_object($object) - && $object instanceof \TYPO3\CMS\Form\Domain\Model\ValidationElement - ) { - return true; - } - return false; - } - - /** - * Adds the given validator for validation of the specified property. - * - * @param string $propertyName Name of the property to validate - * @param ValidatorInterface $validator The property validator - * @return void - * @api - */ - public function addPropertyValidator($propertyName, ValidatorInterface $validator) - { - if (!isset($this->propertyValidators[$propertyName])) { - $this->propertyValidators[$propertyName] = new \SplObjectStorage(); - } - $this->propertyValidators[$propertyName]->attach($validator); - } - - /** - * @param object $object - * @return bool - */ - protected function isValidatedAlready($object) - { - if ($this->validatedInstancesContainer === null) { - $this->validatedInstancesContainer = new \SplObjectStorage(); - } - if ($this->validatedInstancesContainer->contains($object)) { - return true; - } else { - $this->validatedInstancesContainer->attach($object); - - return false; - } - } - - /** - * Returns all property validators - or only validators of the specified property - * - * @param string $propertyName Name of the property to return validators for - * @return array An array of validators - */ - public function getPropertyValidators($propertyName = null) - { - if ($propertyName !== null) { - return (isset($this->propertyValidators[$propertyName])) ? $this->propertyValidators[$propertyName] : []; - } else { - return $this->propertyValidators; - } - } - - /** - * @return int - */ - public function countPropertyValidators() - { - $count = 0; - foreach ($this->propertyValidators as $propertyValidators) { - $count += $propertyValidators->count(); - } - return $count; - } - - /** - * @var \SplObjectStorage - */ - protected $validatedInstancesContainer; - - /** - * Allows to set a container to keep track of validated instances. - * - * @param \SplObjectStorage $validatedInstancesContainer A container to keep track of validated instances - * @return void - * @api - */ - public function setValidatedInstancesContainer(\SplObjectStorage $validatedInstancesContainer) - { - $this->validatedInstancesContainer = $validatedInstancesContainer; - } -} diff --git a/typo3/sysext/form/Classes/Exception.php b/typo3/sysext/form/Classes/Exception.php new file mode 100644 index 000000000000..bd686473e717 --- /dev/null +++ b/typo3/sysext/form/Classes/Exception.php @@ -0,0 +1,25 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form; + +/* + * 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! + */ + +/** + * A generic Form Exception + * + * @api + */ +class Exception extends \Exception +{ +} diff --git a/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php b/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php new file mode 100644 index 000000000000..eefae9dbf731 --- /dev/null +++ b/typo3/sysext/form/Classes/Hooks/DataStructureIdentifierHook.php @@ -0,0 +1,249 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Hooks; + +/* + * 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\Core\Utility\ArrayUtility as CoreArrayUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Utility\ArrayUtility as ExtbaseArrayUtility; +use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService; +use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface; +use TYPO3\CMS\Form\Service\TranslationService; + +/** + * Hooks into flex form handling of backend for tt_content form elements: + * + * * Adds existing forms to flex form drop down list + * * Adds finisher settings if "override finishers" is active + * + * Scope: backend + * @internal + */ +class DataStructureIdentifierHook +{ + + /** + * The data structure depends on a current form selection (persistenceIdentifier) + * and if the field "overrideFinishers" is active. Add both to the identifier to + * hand these information over to parseDataStructureByIdentifierPostProcess() hook. + * + * @param array $fieldTca Incoming field TCA + * @param string $tableName Handled table + * @param string $fieldName Handled field + * @param array $row Current data row + * @param array $identifier Already calculated identifier + * @return array Modified identifier + */ + public function getDataStructureIdentifierPostProcess( + array $fieldTca, + string $tableName, + string $fieldName, + array $row, + array $identifier + ): array { + if ($tableName === 'tt_content' && $fieldName === 'pi_flexform' && $row['CType'] === 'form_formframework') { + $currentFlexData = []; + if (!is_array($row['pi_flexform']) && !empty($row['pi_flexform'])) { + $currentFlexData = GeneralUtility::xml2array($row['pi_flexform']); + } + + // Add selected form value + $identifier['ext-form-persistenceIdentifier'] = ''; + if (!empty($currentFlexData['data']['sDEF']['lDEF']['settings.persistenceIdentifier']['vDEF'])) { + $identifier['ext-form-persistenceIdentifier'] = $currentFlexData['data']['sDEF']['lDEF']['settings.persistenceIdentifier']['vDEF']; + } + + // Add bool - finisher override active or not + $identifier['ext-form-overrideFinishers'] = false; + if ( + isset($currentFlexData['data']['sDEF']['lDEF']['settings.overrideFinishers']['vDEF']) + && (int)$currentFlexData['data']['sDEF']['lDEF']['settings.overrideFinishers']['vDEF'] === 1 + ) { + $identifier['ext-form-overrideFinishers'] = true; + } + } + return $identifier; + } + + /** + * Returns a modified flexform data array. + * + * This adds the list of existing form definitions to the form selection drop down + * and adds sheets to override finisher settings if requested. + * + * @param array $dataStructure + * @param array $identifier + * @return array + */ + public function parseDataStructureByIdentifierPostProcess(array $dataStructure, array $identifier): array + { + if (isset($identifier['ext-form-persistenceIdentifier'])) { + // Add list of existing forms to drop down if we find our key in the identifier + $formPersistenceManager = GeneralUtility::makeInstance(ObjectManager::class)->get(FormPersistenceManagerInterface::class); + foreach ($formPersistenceManager->listForms() as $form) { + $dataStructure['sheets']['sDEF']['ROOT']['el']['settings.persistenceIdentifier']['TCEforms']['config']['items'][] = [ + $form['name'] . ' (' . $form['persistenceIdentifier'] . ')', + $form['persistenceIdentifier'], + ]; + } + + // If a specific form is selected and if finisher override is active, add finisher sheets + if (!empty($identifier['ext-form-persistenceIdentifier']) + && isset($identifier['ext-form-overrideFinishers']) + && $identifier['ext-form-overrideFinishers'] === true + ) { + $persistenceIdentifier = $identifier['ext-form-persistenceIdentifier']; + $formDefinition = $formPersistenceManager->load($persistenceIdentifier); + $newSheets = $this->getAdditionalFinisherSheets($persistenceIdentifier, $formDefinition); + CoreArrayUtility::mergeRecursiveWithOverrule( + $dataStructure, + $newSheets + ); + } + } + return $dataStructure; + } + + /** + * Returns additional flexform sheets with finisher fields + * + * @param string $persistenceIdentifier Current persistence identifier + * @param array $formDefinition The form definition + * @return array + */ + protected function getAdditionalFinisherSheets(string $persistenceIdentifier, array $formDefinition): array + { + if (!isset($formDefinition['finishers']) || empty($formDefinition['finishers'])) { + return []; + } + + $prototypeName = isset($formDefinition['prototypeName']) ? $formDefinition['prototypeName'] : 'standard'; + $prototypeConfiguration = GeneralUtility::makeInstance(ObjectManager::class) + ->get(ConfigurationService::class) + ->getPrototypeConfiguration($prototypeName); + + if (!isset($prototypeConfiguration['finishersDefinition']) || empty($prototypeConfiguration['finishersDefinition'])) { + return []; + } + + $formIdentifier = $formDefinition['identifier']; + $finishersDefinition = $prototypeConfiguration['finishersDefinition']; + + $sheets = ['sheets' => []]; + foreach ($formDefinition['finishers'] as $finisherValue) { + $finisherIdentifier = $finisherValue['identifier']; + if (!isset($finishersDefinition[$finisherIdentifier]['FormEngine']['elements'])) { + continue; + } + $sheetIdentifier = md5( + implode('', [ + $persistenceIdentifier, + $prototypeName, + $formIdentifier, + $finisherIdentifier + ]) + ); + + $translationFile = $finishersDefinition[$finisherIdentifier]['FormEngine']['translationFile']; + $finishersDefinition[$finisherIdentifier]['FormEngine'] = TranslationService::getInstance()->translateValuesRecursive( + $finishersDefinition[$finisherIdentifier]['FormEngine'], + $translationFile + ); + $finisherLabel = $finishersDefinition[$finisherIdentifier]['FormEngine']['label']; + $sheet = $this->initializeNewSheetArray($sheetIdentifier, $finisherLabel); + + $sheetElements = []; + foreach ($finisherValue['options'] as $optionKey => $optionValue) { + if (is_array($optionValue)) { + $optionKey = $optionKey . '.' . $this->extractDottedPathToLastElement($finisherValue['options'][$optionKey]); + $elementConfiguration = ExtbaseArrayUtility::getValueByPath($finishersDefinition[$finisherIdentifier]['FormEngine']['elements'], $optionKey); + $optionValue = ExtbaseArrayUtility::getValueByPath($finisherValue['options'], $optionKey); + } else { + $elementConfiguration = $finishersDefinition[$finisherIdentifier]['FormEngine']['elements'][$optionKey]; + } + + if (empty($elementConfiguration)) { + continue; + } + + if (empty($optionValue)) { + $elementConfiguration['label'] .= ' (default: "[Empty]")'; + } else { + $elementConfiguration['label'] .= ' (default: "' . $optionValue . '")'; + } + $elementConfiguration['config']['default'] = $optionValue; + $sheetElements['settings.finishers.' . $finisherIdentifier . '.' . $optionKey] = $elementConfiguration; + } + + ksort($sheetElements); + + $sheet[$sheetIdentifier]['ROOT']['el'] = $sheetElements; + CoreArrayUtility::mergeRecursiveWithOverrule($sheets['sheets'], $sheet); + } + if (empty($sheets['sheets'])) { + return []; + } + + return $sheets; + } + + /** + * Boilerplate XML array of a new sheet + * + * @param string $sheetIdentifier + * @param string $finisherName + * @throws \InvalidArgumentException + * @return array + */ + protected function initializeNewSheetArray(string $sheetIdentifier, string $finisherName): array + { + if (empty($sheetIdentifier)) { + throw new \InvalidArgumentException('$sheetIdentifier must not be empty.', 1472060918); + } + if (empty($finisherName)) { + throw new \InvalidArgumentException('$finisherName must not be empty.', 1472060919); + } + + return [ + $sheetIdentifier => [ + 'ROOT' => [ + 'TCEforms' => [ + 'sheetTitle' => $finisherName, + ], + 'type' => 'array', + 'el' => [], + ], + ], + ]; + } + + /** + * Recursive helper to implode a nested array to a dotted path notation + * + * @param array $array + * @return string + */ + protected function extractDottedPathToLastElement(array $array): string + { + $dottedPath = key($array); + foreach ($array as $key => $value) { + if (is_array($value)) { + $dottedPath = $dottedPath . '.' . $this->extractDottedPathToLastElement($value); + } + } + return $dottedPath; + } +} diff --git a/typo3/sysext/form/Classes/Hooks/HandleIncomingFormValues.php b/typo3/sysext/form/Classes/Hooks/HandleIncomingFormValues.php deleted file mode 100644 index aeeeec1477a9..000000000000 --- a/typo3/sysext/form/Classes/Hooks/HandleIncomingFormValues.php +++ /dev/null @@ -1,205 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Hooks; - -/* - * 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\Core\SingletonInterface; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Form\Domain\Builder\FormBuilder; -use TYPO3\CMS\Form\Domain\Model\Element; -use TYPO3\CMS\Form\Domain\Model\ValidationElement; - -/** - * Handle the incoming form data - */ -class HandleIncomingFormValues implements SingletonInterface -{ - /** - * @var \TYPO3\CMS\Form\Utility\SessionUtility - */ - protected $sessionUtility; - - /** - * @param \TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility - * @return void - */ - public function injectSessionUtility(\TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility) - { - $this->sessionUtility = $sessionUtility; - } - - /** - * Handle the incoming form data - * - * @param Element $element The element - * @param ValidationElement $validationElement - * @param mixed $modelValue - * @param FormBuilder $formBuilder - * @return void - */ - public function handleIncomingFormValues(Element $element, ValidationElement $validationElement, $modelValue, FormBuilder $formBuilder) - { - $elementName = $element->getName(); - - if ($element->getElementType() === 'CHECKBOX') { - $groupedElement = false; - if ($element->getParentElement()->getElementType() === 'CHECKBOXGROUP') { - $incomingName = $element->getParentElement()->getName(); - $groupedElement = true; - } else { - $incomingName = $elementName; - } - $incomingData = $formBuilder->getIncomingData()->getIncomingField($incomingName); - $checked = false; - if (is_array($incomingData)) { - if ( - isset($incomingData[$elementName]) - && $incomingData[$elementName] !== '' - ) { - $this->setAttribute($element, 'checked', 'checked'); - $checked = true; - } else { - $this->setAttribute($element, 'checked', null); - } - } else { - if ( - (!empty($modelValue) && $incomingData === $modelValue) - || $incomingData === $incomingName . '-' . $element->getElementCounter() - ) { - $this->setAttribute($element, 'checked', 'checked'); - $checked = true; - } else { - $this->setAttribute($element, 'checked', null); - } - } - if ( - $groupedElement - && $checked - ) { - $element->getParentElement()->setAdditionalArgument('atLeastOneCheckedChildElement', true); - } - } elseif ($element->getElementType() === 'RADIO') { - $groupedElement = false; - if ($element->getParentElement()->getElementType() === 'RADIOGROUP') { - $incomingName = $element->getParentElement()->getName(); - $groupedElement = true; - } else { - $incomingName = $elementName; - } - $checked = false; - $incomingData = $formBuilder->getIncomingData()->getIncomingField($incomingName); - if ( - (!empty($modelValue) && $incomingData === $modelValue) - || $incomingData === $incomingName . '-' . $element->getElementCounter() - ) { - $this->setAttribute($element, 'checked', 'checked'); - $checked = true; - } else { - $this->setAttribute($element, 'checked', null); - } - if ( - $groupedElement - && $checked - ) { - $element->getParentElement()->setAdditionalArgument('atLeastOneCheckedChildElement', true); - } - } elseif ($element->getElementType() === 'OPTION') { - $modelValue = (string)($element->getAdditionalArgument('value') ?: $element->getElementCounter()); - if ($element->getParentElement()->getElementType() === 'OPTGROUP') { - $parentName = $element->getParentElement()->getParentElement()->getName(); - } else { - $parentName = $element->getParentElement()->getName(); - } - $incomingData = $formBuilder->getIncomingData()->getIncomingField($parentName); - - /* Multiselect */ - if (is_array($incomingData)) { - if (in_array($modelValue, $incomingData, true)) { - $element->setAdditionalArgument('selected', 'selected'); - } else { - $element->setAdditionalArgument('selected', null); - } - } else { - if ($modelValue === $incomingData) { - $element->setAdditionalArgument('selected', 'selected'); - } else { - $element->setAdditionalArgument('selected', null); - } - } - } elseif ($element->getElementType() === 'TEXTAREA') { - $incomingData = $formBuilder->getIncomingData()->getIncomingField($elementName); - $element->setAdditionalArgument('text', $incomingData); - } elseif ($element->getElementType() === 'FILEUPLOAD') { - if ( - $formBuilder->getValidationErrors() == null - || ( - $formBuilder->getValidationErrors() - && $formBuilder->getValidationErrors()->forProperty($elementName)->hasErrors() !== true - ) - ) { - $uploadedFiles = $formBuilder->getIncomingData()->getIncomingField($elementName); - if (is_array($uploadedFiles)) { - foreach ($uploadedFiles as $key => &$file) { - $tempFilename = $this->saveUploadedFile($file['tmp_name']); - if (!$tempFilename) { - unset($uploadedFiles[$key]); - continue; - } - $file['tempFilename'] = $tempFilename; - } - $element->setAdditionalArgument('uploadedFiles', $uploadedFiles); - $this->setAttribute($element, 'value', ''); - $this->sessionUtility->setSessionData($elementName, $uploadedFiles); - } - } - } - } - - /** - * Save a uploaded file - * - * @param string $uploadedFile - * @return NULL|string - */ - public function saveUploadedFile($uploadedFile) - { - if (is_uploaded_file($uploadedFile)) { - $tempFilename = GeneralUtility::upload_to_tempfile($uploadedFile); - if (TYPO3_OS === 'WIN') { - $tempFilename = GeneralUtility::fixWindowsFilePath($tempFilename); - } - if ($tempFilename !== '') { - return $tempFilename; - } - } - return null; - } - - /** - * Set the value Attribute to the right place - * - * @param Element $element The element - * @param string $key - * @param string $value - * @return void - */ - public function setAttribute(Element $element, $key, $value = '') - { - if ($element->getHtmlAttribute($key) !== null) { - $element->setHtmlAttribute($key, $value); - } else { - $element->setAdditionalArgument($key, $value); - } - } -} diff --git a/typo3/sysext/form/Classes/Hooks/PageLayoutView/MailformPreviewRenderer.php b/typo3/sysext/form/Classes/Hooks/PageLayoutView/MailformPreviewRenderer.php deleted file mode 100644 index 5a5b3f8818fd..000000000000 --- a/typo3/sysext/form/Classes/Hooks/PageLayoutView/MailformPreviewRenderer.php +++ /dev/null @@ -1,42 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Hooks\PageLayoutView; - -/* - * 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! - */ - -/** - * Contains a preview rendering for the page module of - * CType="mailform" - */ -class MailformPreviewRenderer implements \TYPO3\CMS\Backend\View\PageLayoutViewDrawItemHookInterface -{ - /** - * Preprocesses the preview rendering of a content element of type "mailform" - * - * @param \TYPO3\CMS\Backend\View\PageLayoutView $parentObject Calling parent object - * @param bool $drawItem Whether to draw the item using the default functionality - * @param string $headerContent Header content - * @param string $itemContent Item content - * @param array $row Record row of tt_content - * - * @return void - */ - public function preProcess(\TYPO3\CMS\Backend\View\PageLayoutView &$parentObject, &$drawItem, &$headerContent, &$itemContent, array &$row) - { - if ($row['CType'] === 'mailform') { - $contentType = $parentObject->CType_labels[$row['CType']]; - $itemContent = $parentObject->linkEditContent('<strong>' . htmlspecialchars($contentType) . '</strong>', $row) . '<br />'; - $drawItem = false; - } - } -} diff --git a/typo3/sysext/form/Classes/Hooks/SoftReferenceParserHook.php b/typo3/sysext/form/Classes/Hooks/SoftReferenceParserHook.php new file mode 100644 index 000000000000..17d22b1c89dd --- /dev/null +++ b/typo3/sysext/form/Classes/Hooks/SoftReferenceParserHook.php @@ -0,0 +1,60 @@ +<?php +namespace TYPO3\CMS\Form\Hooks; + +/* + * 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\Core\Database\SoftReferenceIndex; + +/** + * Register new referenced formDefinitions within a plugin as a soft reference. + * + * This is used in BE to track how often a specific form is used in a content + * element. The number is shown in the form module "Manage forms". + * + * Scope: backend + * @internal + */ +class SoftReferenceParserHook extends SoftReferenceIndex +{ + /** + * Main function through which all processing happens + * + * @param string $table Database table name + * @param string $field Field name for which processing occurs + * @param int $uid UID of the record + * @param string $content The content/value of the field + * @param string $spKey The softlink parser key. This is only interesting if more than one parser is grouped in the same class. That is the case with this parser. + * @param array $spParams Parameters of the softlink parser. Basically this is the content inside optional []-brackets after the softref keys. Parameters are exploded by "; + * @param string $structurePath If running from inside a FlexForm structure, this is the path of the tag. + * @return array Result array on positive matches, see description above. Otherwise FALSE + */ + public function findRef($table, $field, $uid, $content, $spKey, $spParams, $structurePath = '') + { + $this->tokenID_basePrefix = $table . ':' . $uid . ':' . $field . ':' . $structurePath . ':' . $spKey; + $tokenId = $this->makeTokenID($content); + return [ + 'content' => '{softref:' . $tokenId . '}', + 'elements' => [ + $tokenId => [ + 'matchString' => $content, + 'subst' => [ + 'type' => 'string', + 'tokenID' => $tokenId, + 'tokenValue' => $content + ], + ] + ] + ]; + } +} diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/ConfigurationManager.php b/typo3/sysext/form/Classes/Mvc/Configuration/ConfigurationManager.php new file mode 100644 index 000000000000..3bb82c2b40eb --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Configuration/ConfigurationManager.php @@ -0,0 +1,146 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Configuration; + +/* + * 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\Core\Utility\ArrayUtility as CoreArrayUtility; +use TYPO3\CMS\Extbase\Configuration\ConfigurationManager as ExtbaseConfigurationManager; +use TYPO3\CMS\Form\Mvc\Configuration\Exception\ExtensionNameRequiredException; +use TYPO3\CMS\Form\Utility\ArrayUtility; + +/** + * Extend the ExtbaseConfigurationManager to read YAML configurations. + * + * Scope: frontend / backend + * @internal + */ +class ConfigurationManager extends ExtbaseConfigurationManager implements ConfigurationManagerInterface +{ + /** + * @var \TYPO3\CMS\Form\Mvc\Configuration\YamlSource + */ + protected $yamlSource; + + /** + * 1st level configuration cache + * + * @var array + */ + protected $configurationCache = []; + + /** + * @param \TYPO3\CMS\Form\Mvc\Configuration\YamlSource $yamlSource + * @internal + */ + public function injectYamlSource(\TYPO3\CMS\Form\Mvc\Configuration\YamlSource $yamlSource) + { + $this->yamlSource = $yamlSource; + } + + /** + * @param string $configurationType The kind of configuration to fetch - must be one of the CONFIGURATION_TYPE_* constants + * @param string $extensionName if specified, the configuration for the given extension will be returned. + * @param string $pluginName if specified, the configuration for the given plugin will be returned. + * @return array The configuration + * @internal + */ + public function getConfiguration($configurationType, $extensionName = null, $pluginName = null) + { + switch ($configurationType) { + case self::CONFIGURATION_TYPE_YAML_SETTINGS: + return $this->getConfigurationFromYamlFile($extensionName); + default: + return parent::getConfiguration($configurationType, $extensionName, $pluginName); + } + } + + /** + * Load and parse yaml files which are configured within the TypoScript + * path plugin.tx_extensionkey.settings.yamlConfigurations + * + * The following steps will be done: + * + * * Convert each singe yaml file into an array + * * merge this arrays together + * * resolve all declared inheritances + * * remove all keys if their values are NULL + * * return all configuration paths within TYPO3.CMS + * * sort by array keys, if all keys within the current nesting level are numerical keys + * * resolve possible TypoScript settings in FE mode + * + * @param string $extensionName + * @return array + * @throws ExtensionNameRequiredException + */ + protected function getConfigurationFromYamlFile(string $extensionName): array + { + if (empty($extensionName)) { + throw new ExtensionNameRequiredException( + 'Please specify an extension key to load a YAML configuration', + 1471473377 + ); + } + $ucFirstExtensioName = ucfirst($extensionName); + + // 1st level cache + $configurationCacheKey = strtolower(self::CONFIGURATION_TYPE_YAML_SETTINGS . '|' . $extensionName); + if (isset($this->configurationCache[$configurationCacheKey])) { + return $this->configurationCache[$configurationCacheKey]; + } + + $typoscriptSettings = parent::getConfiguration( + ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS, + $extensionName + ); + $yamlSettingsFilePaths = isset($typoscriptSettings['yamlConfigurations']) + ? $typoscriptSettings['yamlConfigurations'] + : []; + $yamlSettings = InheritancesResolverService::create($this->yamlSource->load($yamlSettingsFilePaths)) + ->getResolvedConfiguration(); + + $yamlSettings = ArrayUtility::removeNullValuesRecursive($yamlSettings); + $yamlSettings = is_array($yamlSettings['TYPO3']['CMS'][$ucFirstExtensioName]) + ? $yamlSettings['TYPO3']['CMS'][$ucFirstExtensioName] + : []; + $yamlSettings = ArrayUtility::sortNumericArrayKeysRecursive($yamlSettings); + $yamlSettings = $this->overrideConfigurationByTypoScript($yamlSettings, $extensionName); + + // 1st level cache + $this->configurationCache[$configurationCacheKey] = $yamlSettings; + return $yamlSettings; + } + + /** + * @param array $yamlSettings + * @param string $extensionName + * @return array + */ + protected function overrideConfigurationByTypoScript(array $yamlSettings, string $extensionName): array + { + $typoScript = parent::getConfiguration(self::CONFIGURATION_TYPE_SETTINGS, $extensionName); + if (is_array($typoScript['yamlSettingsOverrides']) && !empty($typoScript['yamlSettingsOverrides'])) { + CoreArrayUtility::mergeRecursiveWithOverrule( + $yamlSettings, + $typoScript['yamlSettingsOverrides'] + ); + + if ($this->environmentService->isEnvironmentInFrontendMode()) { + $yamlSettings = $this->objectManager->get(TypoScriptService::class) + ->resolvePossibleTypoScriptConfiguration($yamlSettings); + } + } + return $yamlSettings; + } +} diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/ConfigurationManagerInterface.php b/typo3/sysext/form/Classes/Mvc/Configuration/ConfigurationManagerInterface.php new file mode 100644 index 000000000000..0fd7c3b71278 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Configuration/ConfigurationManagerInterface.php @@ -0,0 +1,29 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Configuration; + +/* + * 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\Extbase\Configuration\ConfigurationManagerInterface as ExtbaseConfigurationManagerInterface; + +/** + * Class ConfigurationManagerInterface + * + * Scope: frontend / backend + * @internal + */ +interface ConfigurationManagerInterface extends ExtbaseConfigurationManagerInterface +{ + const CONFIGURATION_TYPE_YAML_SETTINGS = 'YamlSettings'; +} diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/Exception.php b/typo3/sysext/form/Classes/Mvc/Configuration/Exception.php new file mode 100644 index 000000000000..5aa153b8a138 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Configuration/Exception.php @@ -0,0 +1,27 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Configuration; + +/* + * 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\Form\Exception as FormException; + +/** + * A generic Form configuration Exception + * + * @internal + */ +class Exception extends FormException +{ +} diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/Exception/CycleInheritancesException.php b/typo3/sysext/form/Classes/Mvc/Configuration/Exception/CycleInheritancesException.php new file mode 100644 index 000000000000..d2d832aaf953 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Configuration/Exception/CycleInheritancesException.php @@ -0,0 +1,28 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Configuration\Exception; + +/* + * 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\Form\Mvc\Configuration\Exception; + +/** + * This exception is thrown if the InheritancesResolverService wants to + * solve declared inheritances which point cyclically to themselves. + * + * @internal + */ +class CycleInheritancesException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/Exception/ExtensionNameRequiredException.php b/typo3/sysext/form/Classes/Mvc/Configuration/Exception/ExtensionNameRequiredException.php new file mode 100644 index 000000000000..e125e6720948 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Configuration/Exception/ExtensionNameRequiredException.php @@ -0,0 +1,28 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Configuration\Exception; + +/* + * 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\Form\Mvc\Configuration\Exception; + +/** + * This exception is thrown if the configuration manager wants to load + * a YAML file from an empty extension key + * + * @internal + */ +class ExtensionNameRequiredException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/Exception/NoSuchFileException.php b/typo3/sysext/form/Classes/Mvc/Configuration/Exception/NoSuchFileException.php new file mode 100644 index 000000000000..c616ce3354c5 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Configuration/Exception/NoSuchFileException.php @@ -0,0 +1,24 @@ +<?php +namespace TYPO3\CMS\Form\Mvc\Configuration\Exception; + +/* + * 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\Form\Mvc\Configuration\Exception; + +/** + * A No Such File exception + */ +class NoSuchFileException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/Exception/ParseErrorException.php b/typo3/sysext/form/Classes/Mvc/Configuration/Exception/ParseErrorException.php new file mode 100644 index 000000000000..47682656c118 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Configuration/Exception/ParseErrorException.php @@ -0,0 +1,24 @@ +<?php +namespace TYPO3\CMS\Form\Mvc\Configuration\Exception; + +/* + * 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\Form\Mvc\Configuration\Exception; + +/** + * A Parse Error exception + */ +class ParseErrorException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/InheritancesResolverService.php b/typo3/sysext/form/Classes/Mvc/Configuration/InheritancesResolverService.php new file mode 100644 index 000000000000..26f13ec540f3 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Configuration/InheritancesResolverService.php @@ -0,0 +1,375 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Configuration; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Utility\ArrayUtility; +use TYPO3\CMS\Form\Mvc\Configuration\Exception\CycleInheritancesException; + +/** + * Resolve declared inheritances within an configuration array + * + * Scope: frontend / backend + * @internal + */ +class InheritancesResolverService +{ + + /** + * The operator which is used to declare inheritances + */ + const INHERITANCE_OPERATOR = '__inheritances'; + + /** + * The reference configuration is used to get untouched values which + * can be merged into the touched configuration. + * + * @var array + */ + protected $referenceConfiguration = []; + + /** + * This stack is needed to find cyclically inheritances which are on + * the same nesting level but which do not follow each other directly. + * + * @var array + */ + protected $inheritanceStack = []; + + /** + * Needed to park a configuration path for cyclically inheritances + * detection while inheritances for this path is ongoing. + * + * @var string + */ + protected $inheritancePathToCkeck = ''; + + /** + * Returns an instance of this service. Additionally the configuration + * which should be resolved can be passed. + * + * @param array $configuration + * @return InheritancesResolverService + * @internal + */ + public static function create(array $configuration = []): InheritancesResolverService + { + /** @var InheritancesResolverService $inheritancesResolverService */ + $inheritancesResolverService = GeneralUtility::makeInstance(ObjectManager::class) + ->get(self::class); + $inheritancesResolverService->setReferenceConfiguration($configuration); + return $inheritancesResolverService; + } + + /** + * Reset the state of this service. + * Mainly introduced for unit tests. + * + * @return InheritancesResolverService + * @internal + */ + public function reset() + { + $this->referenceConfiguration = []; + $this->inheritanceStack = []; + $this->inheritancePathToCkeck = ''; + return $this; + } + + /** + * Set the reference configuration which is used to get untouched + * values which can be merged into the touched configuration. + * + * @param array + * @return InheritancesResolverService + */ + public function setReferenceConfiguration(array $referenceConfiguration) + { + $this->referenceConfiguration = $referenceConfiguration; + return $this; + } + + /** + * Resolve all inheritances within a configuration. + * After that the configuration array is cleaned from the + * inheritance operator. + * + * @return array + * @internal + */ + public function getResolvedConfiguration(): array + { + $configuration = $this->resolve($this->referenceConfiguration); + $configuration = $this->removeInheritanceOperatorRecursive($configuration); + return $configuration; + } + + /** + * Resolve all inheritances within a configuration. + * + * @toDo: More description + * @param array $configuration + * @param array $pathStack + * @param bool $setInheritancePathToCkeck + * @return array + */ + protected function resolve( + array $configuration, + array $pathStack = [], + bool $setInheritancePathToCkeck = true + ): array { + foreach ($configuration as $key => $values) { + $pathStack[] = $key; + $path = implode('.', $pathStack); + + $this->throwExceptionIfCycleInheritances($path, $path); + if ($setInheritancePathToCkeck) { + $this->inheritancePathToCkeck = $path; + } + + if (is_array($configuration[$key])) { + if (isset($configuration[$key][self::INHERITANCE_OPERATOR])) { + $inheritances = ArrayUtility::getValueByPath( + $this->referenceConfiguration, + $path . '.' . self::INHERITANCE_OPERATOR + ); + + if (is_array($inheritances)) { + $inheritedConfigurations = $this->resolveInheritancesRecursive($inheritances); + + $configuration[$key] = $this->mergeRecursiveWithOverrule( + $inheritedConfigurations, + $configuration[$key] + ); + } + + unset($configuration[$key][self::INHERITANCE_OPERATOR]); + } + + if (!empty($configuration[$key])) { + $configuration[$key] = $this->resolve( + $configuration[$key], + $pathStack + ); + } + } + array_pop($pathStack); + } + + return $configuration; + } + + /** + * Additional helper for the resolve method. + * + * @toDo: More description + * @param array $inheritances + * @return array + * @throws CycleInheritancesException + */ + protected function resolveInheritancesRecursive(array $inheritances): array + { + ksort($inheritances); + $inheritedConfigurations = []; + foreach ($inheritances as $inheritancePath) { + $this->throwExceptionIfCycleInheritances($inheritancePath, $inheritancePath); + $inheritedConfiguration = ArrayUtility::getValueByPath( + $this->referenceConfiguration, + $inheritancePath + ); + + if ( + isset($inheritedConfiguration[self::INHERITANCE_OPERATOR]) + && count($inheritedConfiguration) === 1 + ) { + if ($this->inheritancePathToCkeck === $inheritancePath) { + throw new CycleInheritancesException( + $this->inheritancePathToCkeck . ' has cycle inheritances', + 1474900796 + ); + } + + $inheritedConfiguration = $this->resolveInheritancesRecursive( + $inheritedConfiguration[self::INHERITANCE_OPERATOR] + ); + } else { + $pathStack = explode('.', $inheritancePath); + $key = array_pop($pathStack); + $newConfiguration = [ + $key => $inheritedConfiguration + ]; + $inheritedConfiguration = $this->resolve( + $newConfiguration, + $pathStack, + false + ); + $inheritedConfiguration = $inheritedConfiguration[$key]; + } + + $inheritedConfigurations = $this->mergeRecursiveWithOverrule( + $inheritedConfigurations, + $inheritedConfiguration + ); + } + + return $inheritedConfigurations; + } + + /** + * Throw an exception if a cycle is detected. + * + * @toDo: More description + * @param string $path + * @param string $pathToCheck + * @return void + * @throws CycleInheritancesException + */ + protected function throwExceptionIfCycleInheritances(string $path, string $pathToCheck) + { + $configuration = ArrayUtility::getValueByPath( + $this->referenceConfiguration, + $path + ); + + if (isset($configuration[self::INHERITANCE_OPERATOR])) { + $inheritances = ArrayUtility::getValueByPath( + $this->referenceConfiguration, + $path . '.' . self::INHERITANCE_OPERATOR + ); + if (is_array($inheritances)) { + foreach ($inheritances as $inheritancePath) { + $configuration = ArrayUtility::getValueByPath( + $this->referenceConfiguration, + $inheritancePath + ); + if (isset($configuration[self::INHERITANCE_OPERATOR])) { + $_inheritances = ArrayUtility::getValueByPath( + $this->referenceConfiguration, + $inheritancePath . '.' . self::INHERITANCE_OPERATOR + ); + foreach ($_inheritances as $_inheritancePath) { + if (strpos($pathToCheck, $_inheritancePath) === 0) { + throw new CycleInheritancesException( + $pathToCheck . ' has cycle inheritances', + 1474900797 + ); + } + } + } + + if ( + is_array($this->inheritanceStack[$pathToCheck]) + && in_array($inheritancePath, $this->inheritanceStack[$pathToCheck]) + ) { + $this->inheritanceStack[$pathToCheck][] = $inheritancePath; + throw new CycleInheritancesException( + $pathToCheck . ' has cycle inheritances', + 1474900799 + ); + } + $this->inheritanceStack[$pathToCheck][] = $inheritancePath; + $this->throwExceptionIfCycleInheritances($inheritancePath, $pathToCheck); + } + $this->inheritanceStack[$pathToCheck] = null; + } + } + } + + /** + * Recursively remove self::INHERITANCE_OPERATOR keys + * + * @param array $array + * @return array the modified array + */ + protected function removeInheritanceOperatorRecursive(array $array): array + { + $result = $array; + foreach ($result as $key => $value) { + if ($key === self::INHERITANCE_OPERATOR) { + unset($result[$key]); + continue; + } + + if (is_array($value)) { + $result[$key] = $this->removeInheritanceOperatorRecursive($value); + } + } + return $result; + } + + /** + * Merges two arrays recursively and "binary safe" (integer keys are overridden as well), + * overruling similar values in the first array ($firstArray) with the + * values of the second array ($secondArray) + * In case of identical keys, ie. keeping the values of the second. + * This is basicly the Extbase arrayMergeRecursiveOverrule method. + * This method act different to the core mergeRecursiveWithOverrule method. + * This method has the possibility to overrule a array value within the + * $firstArray with a string value within the $secondArray. + * The core method does not support such a overrule. + * The reason for this code duplication is that the extbase method will be + * deprecated in the future. + * + * @param array $firstArray First array + * @param array $secondArray Second array, overruling the first array + * @param bool $dontAddNewKeys If set, keys that are NOT found in $firstArray (first array) + * will not be set. Thus only existing value can/will be + * overruled from second array. + * @param bool $emptyValuesOverride If set (which is the default), values from $secondArray + * will overrule if they are empty (according to PHP's empty() function) + * @return array Resulting array where $secondArray values has overruled $firstArray values + * @internal + */ + protected function mergeRecursiveWithOverrule( + array $firstArray, + array $secondArray, + bool $dontAddNewKeys = false, + bool $emptyValuesOverride = true + ): array { + foreach ($secondArray as $key => $value) { + if ( + array_key_exists($key, $firstArray) + && is_array($firstArray[$key]) + ) { + if (is_array($secondArray[$key])) { + $firstArray[$key] = $this->mergeRecursiveWithOverrule( + $firstArray[$key], + $secondArray[$key], + $dontAddNewKeys, + $emptyValuesOverride + ); + } else { + $firstArray[$key] = $secondArray[$key]; + } + } else { + if ($dontAddNewKeys) { + if (array_key_exists($key, $firstArray)) { + if ($emptyValuesOverride || !empty($value)) { + $firstArray[$key] = $value; + } + } + } else { + if ($emptyValuesOverride || !empty($value)) { + $firstArray[$key] = $value; + } + } + } + } + reset($firstArray); + return $firstArray; + } +} diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/TypoScriptService.php b/typo3/sysext/form/Classes/Mvc/Configuration/TypoScriptService.php new file mode 100644 index 000000000000..2f48eb5ffd98 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Configuration/TypoScriptService.php @@ -0,0 +1,99 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Configuration; + +/* + * 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\Frontend\Controller\TypoScriptFrontendController; + +/** + * Utilities to manage and convert TypoScript + * + * Scope: frontend + */ +class TypoScriptService +{ + + /** + * @var \TYPO3\CMS\Extbase\Service\TypoScriptService + */ + protected $extbaseTypoScriptService; + + /** + * @param \TYPO3\CMS\Extbase\Service\TypoScriptService $typoScriptService + * @internal + */ + public function injectTypoScriptService(\TYPO3\CMS\Extbase\Service\TypoScriptService $typoScriptService) + { + $this->extbaseTypoScriptService = $typoScriptService; + } + + /** + * Parse an configuration with ContentObjectRenderer::cObjGetSingle() + * and return the result. + * + * @param array $configuration + * @return array + * @internal + */ + public function resolvePossibleTypoScriptConfiguration(array $configuration = []): array + { + $configuration = $this->extbaseTypoScriptService->convertPlainArrayToTypoScriptArray($configuration); + $configuration = $this->resolveTypoScriptConfiguration($configuration); + $configuration = $this->extbaseTypoScriptService->convertTypoScriptArrayToPlainArray($configuration); + return $configuration; + } + + /** + * Parse an configuration with ContentObjectRenderer::cObjGetSingle() + * if there is an array key without and with a dot at the end. + * This sample would be identified as a TypoScript parsable configuration + * part: + * + * [ + * 'example' => 'TEXT' + * 'example.' => [ + * 'value' => 'some value' + * ] + * ] + * + * @param array $configuration + * @return array + */ + protected function resolveTypoScriptConfiguration(array $configuration = []): array + { + foreach ($configuration as $key => $value) { + $keyWithoutDot = rtrim((string)$key, '.'); + if (isset($configuration[$keyWithoutDot]) && isset($configuration[$keyWithoutDot . '.'])) { + $value = $this->getTypoScriptFrontendController()->cObj->cObjGetSingle( + $configuration[$keyWithoutDot], + $configuration[$keyWithoutDot . '.'] + ); + $configuration[$keyWithoutDot] = $value; + } elseif (!isset($configuration[$keyWithoutDot]) && isset($configuration[$keyWithoutDot . '.'])) { + $configuration[$keyWithoutDot] = $this->resolveTypoScriptConfiguration($value); + } + unset($configuration[$keyWithoutDot . '.']); + } + return $configuration; + } + + /** + * @return TypoScriptFrontendController + */ + protected function getTypoScriptFrontendController() + { + return $GLOBALS['TSFE']; + } +} diff --git a/typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php b/typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php new file mode 100644 index 000000000000..d628a18bd9ab --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Configuration/YamlSource.php @@ -0,0 +1,156 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Configuration; + +/* + * 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 Symfony\Component\Yaml\Exception\ParseException; +use Symfony\Component\Yaml\Yaml; +use TYPO3\CMS\Core\Resource\File; +use TYPO3\CMS\Core\Utility\ArrayUtility; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoSuchFileException; +use TYPO3\CMS\Form\Mvc\Configuration\Exception\ParseErrorException; +use TYPO3\CMS\Form\Utility\ArrayUtility as FormArrayUtility; + +/** + * Configuration source based on YAML files + * + * Scope: frontend / backend + * @internal + */ +class YamlSource +{ + /** + * Will be set if the PHP YAML Extension is installed. + * Having this installed massively improves YAML parsing performance. + * + * @var bool + * @see http://pecl.php.net/package/yaml + */ + protected $usePhpYamlExtension = false; + + /** + * Use PHP YAML Extension if installed. + * @internal + */ + public function __construct() + { + if (extension_loaded('yaml')) { + $this->usePhpYamlExtension = true; + } + } + + /** + * Loads the specified configuration files and returns its merged content + * as an array. + * + * @param array $filesToLoad + * @return array + * @throws ParseErrorException + * @throws NoSuchFileException + * @internal + */ + public function load(array $filesToLoad): array + { + $configuration = []; + foreach ($filesToLoad as $fileToLoad) { + if ($fileToLoad instanceof File) { + $fileIdentifier = $fileToLoad->getIdentifier(); + $rawYamlContent = $fileToLoad->getContents(); + } else { + $fileIdentifier = $fileToLoad; + $fileToLoad = GeneralUtility::getFileAbsFileName($fileToLoad); + if (is_file($fileToLoad)) { + $rawYamlContent = file_get_contents($fileToLoad); + } else { + throw new NoSuchFileException( + 'The file "' . $fileToLoad . '" does not exist.', + 1471473378 + ); + } + } + + try { + if ($this->usePhpYamlExtension) { + $loadedConfiguration = @yaml_parse($rawYamlContent); + if ($loadedConfiguration === false) { + throw new ParseErrorException( + 'A parse error occurred while parsing file "' . $fileIdentifier . '".', + 1391894094 + ); + } + } else { + $loadedConfiguration = Yaml::parse($rawYamlContent); + } + + if (is_array($loadedConfiguration)) { + ArrayUtility::mergeRecursiveWithOverrule($configuration, $loadedConfiguration); + } + } catch (ParseException $exception) { + throw new ParseErrorException( + 'A parse error occurred while parsing file "' . $fileIdentifier . '". Error message: ' . $exception->getMessage(), + 1480195405 + ); + } + } + + $configuration = FormArrayUtility::convertBooleanStringsToBooleanRecursive($configuration); + return $configuration; + } + + /** + * Save the specified configuration array to the given file in YAML format. + * + * @param File|string $fileToSave The file to write to. + * @param array $configuration The configuration to save + * @return void + * @internal + */ + public function save($fileToSave, array $configuration) + { + $header = $this->getHeaderFromFile($fileToSave); + $yaml = Yaml::dump($configuration, 99, 2); + if ($fileToSave instanceof File) { + $fileToSave->setContents($header . LF . $yaml); + } else { + @file_put_contents($fileToSave, $header . LF . $yaml); + } + } + + /** + * Read the header part from the given file. That means, every line + * until the first non comment line is found. + * + * @param File|string $file + * @return string The header of the given YAML file + */ + protected function getHeaderFromFile($file): string + { + $header = ''; + if ($file instanceof File) { + $fileLines = explode(LF, $file->getContents()); + } else { + $fileLines = file($file); + } + foreach ($fileLines as $line) { + if (preg_match('/^#/', $line)) { + $header .= $line; + } else { + break; + } + } + return $header; + } +} diff --git a/typo3/sysext/form/Classes/Mvc/Controller/ControllerContext.php b/typo3/sysext/form/Classes/Mvc/Controller/ControllerContext.php deleted file mode 100644 index bd002485e9df..000000000000 --- a/typo3/sysext/form/Classes/Mvc/Controller/ControllerContext.php +++ /dev/null @@ -1,85 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Mvc\Controller; - -/* - * 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\Extbase\Reflection\ObjectAccess; -use TYPO3\CMS\Form\Domain\Model\Configuration; -use TYPO3\CMS\Form\Domain\Model\ValidationElement; - -/** - * Extension to the default Extbase Controller Context. - */ -class ControllerContext extends \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext -{ - /** - * Extends a given default ControllerContext. - * - * @param \TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext - * @return ControllerContext - */ - public static function extend(\TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext $source) - { - $controllerContext = \TYPO3\CMS\Form\Utility\FormUtility::getObjectManager()->get(self::class); - $propertyNames = ObjectAccess::getGettableProperties($source); - foreach ($propertyNames as $propertyName => $propertyValue) { - ObjectAccess::setProperty($controllerContext, $propertyName, $propertyValue); - } - return $controllerContext; - } - - /** - * @var Configuration - */ - protected $configuration; - - /** - * @var ValidationElement - */ - protected $validationElement; - - /** - * @return Configuration - */ - public function getConfiguration() - { - return $this->configuration; - } - - /** - * @param Configuration $configuration - * @return ControllerContext - */ - public function setConfiguration(Configuration $configuration) - { - $this->configuration = $configuration; - return $this; - } - - /** - * @return ValidationElement - */ - public function getValidationElement() - { - return $this->validationElement; - } - - /** - * @param ValidationElement $validationElement - */ - public function setValidationElement(ValidationElement $validationElement) - { - $this->validationElement = $validationElement; - } -} diff --git a/typo3/sysext/form/Classes/Mvc/Persistence/Exception.php b/typo3/sysext/form/Classes/Mvc/Persistence/Exception.php new file mode 100644 index 000000000000..2efc4a6d8c75 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Persistence/Exception.php @@ -0,0 +1,27 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Persistence; + +/* + * 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\Form\Exception as FormException; + +/** + * A generic Form persistence Exception + * + * @internal + */ +class Exception extends FormException +{ +} diff --git a/typo3/sysext/form/Classes/Mvc/Persistence/Exception/NoUniqueIdentifierException.php b/typo3/sysext/form/Classes/Mvc/Persistence/Exception/NoUniqueIdentifierException.php new file mode 100644 index 000000000000..fb267c3a0fef --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Persistence/Exception/NoUniqueIdentifierException.php @@ -0,0 +1,25 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Persistence\Exception; + +/* + * 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\Form\Mvc\Persistence\Exception; + +/** + * @internal + */ +class NoUniqueIdentifierException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Mvc/Persistence/Exception/NoUniquePersistenceIdentifierException.php b/typo3/sysext/form/Classes/Mvc/Persistence/Exception/NoUniquePersistenceIdentifierException.php new file mode 100644 index 000000000000..de1f7cd00bea --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Persistence/Exception/NoUniquePersistenceIdentifierException.php @@ -0,0 +1,25 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Persistence\Exception; + +/* + * 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\Form\Mvc\Persistence\Exception; + +/** + * @internal + */ +class NoUniquePersistenceIdentifierException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Mvc/Persistence/Exception/PersistenceManagerException.php b/typo3/sysext/form/Classes/Mvc/Persistence/Exception/PersistenceManagerException.php new file mode 100644 index 000000000000..f8cf7baeae04 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Persistence/Exception/PersistenceManagerException.php @@ -0,0 +1,27 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Persistence\Exception; + +/* + * 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\Form\Mvc\Persistence\Exception; + +/** + * Generic Persistence Manager Exception, to be thrown f.e. if a given form is not loadable + * + * @internal + */ +class PersistenceManagerException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManager.php b/typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManager.php new file mode 100644 index 000000000000..fd19c9d33d84 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManager.php @@ -0,0 +1,493 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Persistence; + +/* + * 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\Core\Resource\Exception\InsufficientFolderAccessPermissionsException; +use TYPO3\CMS\Core\Resource\File; +use TYPO3\CMS\Core\Resource\Filter\FileExtensionFilter; +use TYPO3\CMS\Core\Resource\Folder; +use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Core\Utility\PathUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Mvc\Configuration\ConfigurationManagerInterface; +use TYPO3\CMS\Form\Mvc\Configuration\YamlSource; +use TYPO3\CMS\Form\Mvc\Persistence\Exception\NoUniqueIdentifierException; +use TYPO3\CMS\Form\Mvc\Persistence\Exception\NoUniquePersistenceIdentifierException; +use TYPO3\CMS\Form\Mvc\Persistence\Exception\PersistenceManagerException; + +/** + * Concrete implementation of the FormPersistenceManagerInterface + * + * Scope: frontend / backend + */ +class FormPersistenceManager implements FormPersistenceManagerInterface +{ + + /** + * @var \TYPO3\CMS\Form\Mvc\Configuration\YamlSource + */ + protected $yamlSource; + + /** + * @var \TYPO3\CMS\Core\Resource\StorageRepository + */ + protected $storageRepository; + + /** + * @var array + */ + protected $formSettings; + + /** + * @param \TYPO3\CMS\Form\Mvc\Configuration\YamlSource $yamlSource + * @internal + */ + public function injectYamlSource(\TYPO3\CMS\Form\Mvc\Configuration\YamlSource $yamlSource) + { + $this->yamlSource = $yamlSource; + } + + /** + * @param \TYPO3\CMS\Core\Resource\StorageRepository $storageRepository + * @internal + */ + public function injectStorageRepository(\TYPO3\CMS\Core\Resource\StorageRepository $storageRepository) + { + $this->storageRepository = $storageRepository; + } + + /** + * @internal + */ + public function initializeObject() + { + $this->formSettings = GeneralUtility::makeInstance(ObjectManager::class) + ->get(ConfigurationManagerInterface::class) + ->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_YAML_SETTINGS, 'form'); + } + + /** + * Load the array formDefinition identified by $persistenceIdentifier, and return it. + * Only files with the extension .yaml are loaded. + * At this place there is no check if the file location is allowed. + * + * @param string $persistenceIdentifier + * @return array + * @throws PersistenceManagerException + * @internal + */ + public function load(string $persistenceIdentifier): array + { + if (pathinfo($persistenceIdentifier, PATHINFO_EXTENSION) !== 'yaml') { + throw new PersistenceManagerException(sprintf('The file "%s" could not be loaded.', $persistenceIdentifier), 1477679819); + } + + if (strpos($persistenceIdentifier, 'EXT:') === 0) { + $file = $persistenceIdentifier; + } else { + $file = $this->getFileByIdentifier($persistenceIdentifier); + } + return $this->yamlSource->load([$file]); + } + + /** + * Save the array form representation identified by $persistenceIdentifier. + * Only files with the extension .yaml are saved. + * If the formDefinition is located within a EXT: resource, save is only + * allowed if the configuration path + * TYPO3.CMS.Form.persistenceManager.allowSaveToExtensionPaths + * is set to true. + * + * @param string $persistenceIdentifier + * @param array $formDefinition + * @return void + * @throws PersistenceManagerException + * @internal + */ + public function save(string $persistenceIdentifier, array $formDefinition) + { + if (pathinfo($persistenceIdentifier, PATHINFO_EXTENSION) !== 'yaml') { + throw new PersistenceManagerException(sprintf('The file "%s" could not be saved.', $persistenceIdentifier), 1477679820); + } + + if (strpos($persistenceIdentifier, 'EXT:') === 0) { + if (!$this->formSettings['persistenceManager']['allowSaveToExtensionPaths']) { + throw new PersistenceManagerException('Save to extension paths is not allowed.', 1477680881); + } + $fileToSave = GeneralUtility::getFileAbsFileName($persistenceIdentifier); + } else { + $fileToSave = $this->getOrCreateFile($persistenceIdentifier); + } + + $this->yamlSource->save($fileToSave, $formDefinition); + } + + /** + * Delete the form representation identified by $persistenceIdentifier. + * Only files with the extension .yaml are removed. + * formDefinitions within an EXT: resource are not removable. + * + * @param string $persistenceIdentifier + * @return void + * @throws PersistenceManagerException + * @internal + */ + public function delete(string $persistenceIdentifier) + { + if (pathinfo($persistenceIdentifier, PATHINFO_EXTENSION) !== 'yaml') { + throw new PersistenceManagerException(sprintf('The file "%s" could not be removed.', $persistenceIdentifier), 1472239534); + } + if (!$this->exists($persistenceIdentifier)) { + throw new PersistenceManagerException(sprintf('The file "%s" could not be removed.', $persistenceIdentifier), 1472239535); + } + if (strpos($persistenceIdentifier, 'EXT:') === 0) { + throw new PersistenceManagerException(sprintf('The file "%s" could not be removed.', $persistenceIdentifier), 1472239536); + } + + list($storageUid, $fileIdentifier) = explode(':', $persistenceIdentifier, 2); + $storage = $this->getStorageByUid((int)$storageUid); + $file = $storage->getFile($fileIdentifier); + if (!$storage->checkFileActionPermission('delete', $file)) { + throw new PersistenceManagerException(sprintf('No delete access to file "%s".', $persistenceIdentifier), 1472239516); + } + $storage->deleteFile($file); + } + + /** + * Check whether a form with the specified $persistenceIdentifier exists + * + * @param string $persistenceIdentifier + * @return bool TRUE if a form with the given $persistenceIdentifier can be loaded, otherwise FALSE + * @internal + */ + public function exists(string $persistenceIdentifier): bool + { + $exists = false; + if (pathinfo($persistenceIdentifier, PATHINFO_EXTENSION) === 'yaml') { + if (strpos($persistenceIdentifier, 'EXT:') === 0) { + $exists = file_exists(GeneralUtility::getFileAbsFileName($persistenceIdentifier)); + } else { + list($storageUid, $fileIdentifier) = explode(':', $persistenceIdentifier, 2); + $storage = $this->getStorageByUid((int)$storageUid); + $exists = $storage->hasFile($fileIdentifier); + } + } + return $exists; + } + + /** + * List all form definitions which can be loaded through this form persistence + * manager. + * + * Returns an associative array with each item containing the keys 'name' (the human-readable name of the form) + * and 'persistenceIdentifier' (the unique identifier for the Form Persistence Manager e.g. the path to the saved form definition). + * + * @return array in the format [['name' => 'Form 01', 'persistenceIdentifier' => 'path1'], [ .... ]] + * @internal + */ + public function listForms(): array + { + $fileExtensionFilter = GeneralUtility::makeInstance(FileExtensionFilter::class); + $fileExtensionFilter->setAllowedFileExtensions(['yaml']); + + $identifiers = []; + $forms = []; + /** @var \TYPO3\CMS\Core\Resource\Folder $folder */ + foreach ($this->getAccessibleFormStorageFolders() as $folder) { + $storage = $folder->getStorage(); + $storage->addFileAndFolderNameFilter([$fileExtensionFilter, 'filterFileList']); + + $files = $folder->getFiles(0, 0, Folder::FILTER_MODE_USE_OWN_AND_STORAGE_FILTERS, true); + foreach ($files as $file) { + $persistenceIdentifier = $storage->getUid() . ':' . $file->getIdentifier(); + + $form = $this->load($persistenceIdentifier); + $forms[] = [ + 'identifier' => $form['identifier'], + 'name' => isset($form['label']) ? $form['label'] : $form['identifier'], + 'persistenceIdentifier' => $persistenceIdentifier, + 'readOnly' => false, + 'location' => 'storage', + 'duplicateIdentifier' => false, + ]; + $identifiers[$form['identifier']]++; + } + $storage->resetFileAndFolderNameFiltersToDefault(); + } + + foreach ($this->getAccessibleExtensionFolders() as $relativePath => $fullPath) { + $relativePath = rtrim($relativePath, '/') . '/'; + foreach (new \DirectoryIterator($fullPath) as $fileInfo) { + if ($fileInfo->getExtension() !== 'yaml') { + continue; + } + $form = $this->load($relativePath . $fileInfo->getFilename()); + $forms[] = [ + 'identifier' => $form['identifier'], + 'name' => isset($form['label']) ? $form['label'] : $form['identifier'], + 'persistenceIdentifier' => $relativePath . $fileInfo->getFilename(), + 'readOnly' => $this->formSettings['persistenceManager']['allowSaveToExtensionPaths'] ? false: true, + 'location' => 'extension', + 'duplicateIdentifier' => false, + ]; + $identifiers[$form['identifier']]++; + } + } + + foreach ($identifiers as $identifier => $count) { + if ($count > 1) { + foreach ($forms as &$formDefinition) { + if ($formDefinition['identifier'] === $identifier) { + $formDefinition['duplicateIdentifier'] = true; + } + } + } + } + + return $forms; + } + + /** + * Return a list of all accessible file mountpoints for the + * current backend user. + * + * Only registered mountpoints from + * TYPO3.CMS.Form.persistenceManager.allowedFileMounts + * are listet. + * + * @return Folder[] + * @internal + */ + public function getAccessibleFormStorageFolders(): array + { + $storageFolders = []; + if ( + !isset($this->formSettings['persistenceManager']['allowedFileMounts']) + || !is_array($this->formSettings['persistenceManager']['allowedFileMounts']) + || empty($this->formSettings['persistenceManager']['allowedFileMounts']) + ) { + return $storageFolders; + } + + foreach ($this->formSettings['persistenceManager']['allowedFileMounts'] as $allowedFileMount) { + list($storageUid, $fileMountIdentifier) = explode(':', $allowedFileMount, 2); + $fileMountIdentifier = rtrim($fileMountIdentifier, '/') . '/'; + + try { + $storage = $this->getStorageByUid((int)$storageUid); + } catch (PersistenceManagerException $e) { + continue; + } + + try { + $folder = $storage->getFolder($fileMountIdentifier); + } catch (InsufficientFolderAccessPermissionsException $e) { + continue; + } + $storageFolders[$allowedFileMount] = $folder; + } + return $storageFolders; + } + + /** + * Return a list of all accessible extension folders + * + * Only registered mountpoints from + * TYPO3.CMS.Form.persistenceManager.allowedExtensionPaths + * are listet. + * + * @return array + * @internal + */ + public function getAccessibleExtensionFolders(): array + { + $extensionFolders = []; + if ( + !isset($this->formSettings['persistenceManager']['allowedExtensionPaths']) + || !is_array($this->formSettings['persistenceManager']['allowedExtensionPaths']) + || empty($this->formSettings['persistenceManager']['allowedExtensionPaths']) + ) { + return $extensionFolders; + } + + foreach ($this->formSettings['persistenceManager']['allowedExtensionPaths'] as $allowedExtensionPath) { + if (strpos($allowedExtensionPath, 'EXT:') !== 0) { + continue; + } + + $allowedExtensionFullPath = GeneralUtility::getFileAbsFileName($allowedExtensionPath); + if (!file_exists($allowedExtensionFullPath)) { + continue; + } + $extensionFolders[$allowedExtensionPath] = $allowedExtensionFullPath; + } + return $extensionFolders; + } + + /** + * This takes a form identifier and returns a unique persistence identifier for it. + * By default this is just similar to the identifier. But if a form with the same persistence identifier already + * exists a suffix is appended until the persistence identifier is unique. + * + * @param string $formIdentifier lowerCamelCased form identifier + * @param string $savePath + * @return string unique form persistence identifier + * @throws NoUniquePersistenceIdentifierException + * @internal + */ + public function getUniquePersistenceIdentifier(string $formIdentifier, string $savePath): string + { + $savePath = rtrim($savePath, '/') . '/'; + $formPersistenceIdentifier = $savePath . $formIdentifier . '.yaml'; + if (!$this->exists($formPersistenceIdentifier)) { + return $formPersistenceIdentifier; + } + for ($attempts = 1; $attempts < 100; $attempts++) { + $formPersistenceIdentifier = $savePath . sprintf('%s_%d', $formIdentifier, $attempts) . '.yaml'; + if (!$this->exists($formPersistenceIdentifier)) { + return $formPersistenceIdentifier; + } + } + $formPersistenceIdentifier = $savePath . sprintf('%s_%d', $formIdentifier, time()) . '.yaml'; + if (!$this->exists($formPersistenceIdentifier)) { + return $formPersistenceIdentifier; + } + + throw new NoUniquePersistenceIdentifierException( + sprintf('Could not find a unique persistence identifier for form identifier "%s" after %d attempts', $formIdentifier, $attempts), + 1476010403 + ); + } + + /** + * This takes a form identifier and returns a unique identifier for it. + * If a formDefinition with the same identifier already exists a suffix is + * appended until the identifier is unique. + * + * @param string $identifier + * @return string unique form identifier + * @throws NoUniqueIdentifierException + * @internal + */ + public function getUniqueIdentifier(string $identifier): string + { + $originalIdentifier = $identifier; + if ($this->checkForDuplicateIdentifier($identifier)) { + for ($attempts = 1; $attempts < 100; $attempts++) { + $identifier = sprintf('%s_%d', $originalIdentifier, $attempts); + if (!$this->checkForDuplicateIdentifier($identifier)) { + return $identifier; + } + } + $identifier = $originalIdentifier . '_' . time(); + if ($this->checkForDuplicateIdentifier($identifier)) { + throw new NoUniqueIdentifierException( + sprintf('Could not find a unique identifier for form identifier "%s" after %d attempts', $identifier, $attempts), + 1477688567 + ); + } + } + return $identifier; + } + + /** + * Check if a identifier is already used by a formDefintion. + * + * @param string $identifier + * @return bool + * @internal + */ + public function checkForDuplicateIdentifier(string $identifier): bool + { + $identifierUsed = false; + foreach ($this->listForms() as $formDefinition) { + if ($formDefinition['identifier'] === $identifier) { + $identifierUsed = true; + break; + } + } + return $identifierUsed; + } + + /** + * Returns a File object for a given $persistenceIdentifier + * + * @param string $persistenceIdentifier + * @return File + * @throws PersistenceManagerException + */ + protected function getFileByIdentifier(string $persistenceIdentifier): File + { + list($storageUid, $fileIdentifier) = explode(':', $persistenceIdentifier, 2); + $storage = $this->getStorageByUid((int)$storageUid); + $file = $storage->getFile($fileIdentifier); + if (!$storage->checkFileActionPermission('read', $file)) { + throw new PersistenceManagerException(sprintf('No read access to file "%s".', $persistenceIdentifier), 1471630578); + } + return $file; + } + + /** + * Returns a File object for a given $persistenceIdentifier. + * If no file for this identifier exists a new object will be + * created. + * + * @param string $persistenceIdentifier + * @return File + * @throws PersistenceManagerException + */ + protected function getOrCreateFile(string $persistenceIdentifier): File + { + list($storageUid, $fileIdentifier) = explode(':', $persistenceIdentifier, 2); + $storage = $this->getStorageByUid((int)$storageUid); + $pathinfo = PathUtility::pathinfo($fileIdentifier); + + if (!$storage->hasFolder($pathinfo['dirname'])) { + throw new PersistenceManagerException(sprintf('Could not create folder "%s".', $pathinfo['dirname']), 1471630579); + } + $folder = $storage->getFolder($pathinfo['dirname']); + if (!$storage->checkFolderActionPermission('write', $folder)) { + throw new PersistenceManagerException(sprintf('No write access to folder "%s".', $pathinfo['dirname']), 1471630580); + } + + if (!$storage->hasFile($fileIdentifier)) { + $file = $folder->createFile($pathinfo['basename']); + } else { + $file = $storage->getFile($fileIdentifier); + } + return $file; + } + + /** + * Returns a ResourceStorage for a given uid + * + * @param int $storageUid + * @return ResourceStorage + * @throws PersistenceManagerException + */ + protected function getStorageByUid(int $storageUid): ResourceStorage + { + $storage = $this->storageRepository->findByUid($storageUid); + if ( + !$storage instanceof ResourceStorage + || !$storage->isBrowsable() + ) { + throw new PersistenceManagerException(sprintf('Could not access storage with uid "%d".', $storageUid), 1471630581); + } + return $storage; + } +} diff --git a/typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManagerInterface.php b/typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManagerInterface.php new file mode 100644 index 000000000000..f1cd660ddb62 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Persistence/FormPersistenceManagerInterface.php @@ -0,0 +1,110 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Persistence; + +/* + * 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\Core\Resource\Folder; + +/** + * The form persistence manager interface + * + * Scope: frontend / backend + */ +interface FormPersistenceManagerInterface +{ + + /** + * Load the array form representation identified by $persistenceIdentifier, and return it + * + * @param string $persistenceIdentifier + * @return array + * @api + */ + public function load(string $persistenceIdentifier): array; + + /** + * Save the array form representation identified by $persistenceIdentifier + * + * @param string $persistenceIdentifier + * @param array $formDefinition + * @api + */ + public function save(string $persistenceIdentifier, array $formDefinition); + + /** + * Check whether a form with the specified $persistenceIdentifier exists + * + * @param string $persistenceIdentifier + * @return bool TRUE if a form with the given $persistenceIdentifier can be loaded, otherwise FALSE + * @api + */ + public function exists(string $persistenceIdentifier): bool; + + /** + * Delete the form representation identified by $persistenceIdentifier + * + * @param string $persistenceIdentifier + * @return void + * @api + */ + public function delete(string $persistenceIdentifier); + + /** + * List all form definitions which can be loaded through this form persistence + * manager. + * + * Returns an associative array with each item containing the keys 'name' (the human-readable name of the form) + * and 'persistenceIdentifier' (the unique identifier for the Form Persistence Manager e.g. the path to the saved form definition). + * + * @return array in the format [['name' => 'Form 01', 'persistenceIdentifier' => 'path1'], [ .... ]] + * @api + */ + public function listForms(): array; + + /** + * Return a list of all accessible file mount points + * + * @return Folder[] + * @api + */ + public function getAccessibleFormStorageFolders(): array; + + /** + * Return a list of all accessible extension folders + * + * @return array + * @api + */ + public function getAccessibleExtensionFolders(): array; + + /** + * This takes a form identifier and returns a unique persistence identifier for it. + * + * @param string $formIdentifier + * @param string $savePath + * @return string + * @api + */ + public function getUniquePersistenceIdentifier(string $formIdentifier, string $savePath): string; + + /** + * Check if a identifier is already used by a formDefintion. + * + * @param string $identifier + * @return bool + * @api + */ + public function checkForDuplicateIdentifier(string $identifier): bool; +} diff --git a/typo3/sysext/form/Classes/Mvc/ProcessingRule.php b/typo3/sysext/form/Classes/Mvc/ProcessingRule.php new file mode 100644 index 000000000000..6b8949a420b5 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/ProcessingRule.php @@ -0,0 +1,181 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Error\Result; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Property\PropertyMapper; +use TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration; +use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator; +use TYPO3\CMS\Extbase\Validation\Validator\ValidatorInterface; + +/** + * A processing Rule contains information for property mapping and validation. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + * @internal + */ +class ProcessingRule +{ + + /** + * The target data type the data should be converted to + * + * @var string + */ + protected $dataType; + + /** + * @var \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration + */ + protected $propertyMappingConfiguration; + + /** + * @var \TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator + */ + protected $validator; + + /** + * @var \TYPO3\CMS\Extbase\Error\Result + */ + protected $processingMessages; + + /** + * @var \TYPO3\CMS\Extbase\Property\PropertyMapper + */ + protected $propertyMapper; + + /** + * @param \TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $propertyMappingConfiguration + * @return void + * @internal + */ + public function injectPropertyMappingConfiguration(\TYPO3\CMS\Extbase\Property\PropertyMappingConfiguration $propertyMappingConfiguration) + { + $this->propertyMappingConfiguration = $propertyMappingConfiguration; + } + + /** + * @param \TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator $validator + * @return void + * @internal + */ + public function injectConjunctionValidator(\TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator $validator) + { + $this->validator = $validator; + } + + /** + * @param \TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper + * @return void + * @internal + */ + public function injectPropertyMapper(\TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper) + { + $this->propertyMapper = $propertyMapper; + } + + /** + * Constructs this processing rule + * @internal + */ + public function __construct() + { + $this->processingMessages = GeneralUtility::makeInstance(ObjectManager::class) + ->get(Result::class); + } + + /** + * @return PropertyMappingConfiguration + * @internal + */ + public function getPropertyMappingConfiguration(): PropertyMappingConfiguration + { + return $this->propertyMappingConfiguration; + } + + /** + * @return string + * @internal + */ + public function getDataType(): string + { + return $this->dataType; + } + + /** + * @param string $dataType + * @internal + */ + public function setDataType(string $dataType) + { + $this->dataType = $dataType; + } + + /** + * Returns the child validators of the ConjunctionValidator that is bound to this processing rule + * + * @return \SplObjectStorage<ValidatorInterface> + * @internal + */ + public function getValidators(): \SplObjectStorage + { + return $this->validator->getValidators(); + } + + /** + * @param ValidatorInterface $validator + * @return void + * @internal + */ + public function addValidator(ValidatorInterface $validator) + { + $this->validator->addValidator($validator); + } + + /** + * @param mixed $value + * @return mixed + * @internal + */ + public function process($value) + { + if ($this->dataType !== null) { + $value = $this->propertyMapper->convert($value, $this->dataType, $this->propertyMappingConfiguration); + $messages = $this->propertyMapper->getMessages(); + } else { + $messages = GeneralUtility::makeInstance(ObjectManager::class) + ->get(Result::class); + } + + $validationResult = $this->validator->validate($value); + $messages->merge($validationResult); + + $this->processingMessages->merge($messages); + return $value; + } + + /** + * @return Result + * @internal + */ + public function getProcessingMessages(): Result + { + return $this->processingMessages; + } +} diff --git a/typo3/sysext/form/Classes/Mvc/Property/TypeConverter/UploadedFileReferenceConverter.php b/typo3/sysext/form/Classes/Mvc/Property/TypeConverter/UploadedFileReferenceConverter.php new file mode 100644 index 000000000000..7ced599d5103 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Property/TypeConverter/UploadedFileReferenceConverter.php @@ -0,0 +1,291 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Property\TypeConverter; + +/* + * 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\Core\Resource\File as File; +use TYPO3\CMS\Core\Resource\FileReference as CoreFileReference; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Domain\Model\AbstractFileFolder; +use TYPO3\CMS\Extbase\Domain\Model\FileReference as ExtbaseFileReference; +use TYPO3\CMS\Extbase\Error\Error; +use TYPO3\CMS\Extbase\Property\Exception\TypeConverterException; +use TYPO3\CMS\Extbase\Property\PropertyMappingConfigurationInterface; +use TYPO3\CMS\Extbase\Property\TypeConverter\AbstractTypeConverter; +use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator; + +/** + * Class UploadedFileReferenceConverter + * + * Scope: frontend + * @internal + */ +class UploadedFileReferenceConverter extends AbstractTypeConverter +{ + + /** + * Folder where the file upload should go to (including storage). + */ + const CONFIGURATION_UPLOAD_FOLDER = 1; + + /** + * How to handle a upload when the name of the uploaded file conflicts. + */ + const CONFIGURATION_UPLOAD_CONFLICT_MODE = 2; + + /** + * Validator for file types + */ + const CONFIGURATION_FILE_VALIDATORS = 4; + + /** + * @var string + */ + protected $defaultUploadFolder = '1:/user_upload/'; + + /** + * One of 'cancel', 'replace', 'rename' + * + * @var string + */ + protected $defaultConflictMode = 'rename'; + + /** + * @var array + */ + protected $sourceTypes = ['array']; + + /** + * @var string + */ + protected $targetType = ExtbaseFileReference::class; + + /** + * Take precedence over the available FileReferenceConverter + * + * @var int + */ + protected $priority = 2; + + /** + * @var \TYPO3\CMS\Core\Resource\FileInterface[] + */ + protected $convertedResources = []; + + /** + * @var \TYPO3\CMS\Core\Resource\ResourceFactory + */ + protected $resourceFactory; + + /** + * @var \TYPO3\CMS\Extbase\Security\Cryptography\HashService + */ + protected $hashService; + + /** + * @var \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface + */ + protected $persistenceManager; + + /** + * @param \TYPO3\CMS\Core\Resource\ResourceFactory $resourceFactory + * @return void + * @internal + */ + public function injectResourceFactory(\TYPO3\CMS\Core\Resource\ResourceFactory $resourceFactory) + { + $this->resourceFactory = $resourceFactory; + } + + /** + * @param \TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService + * @return void + * @internal + */ + public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService) + { + $this->hashService = $hashService; + } + + /** + * @param \TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager + * @return void + * @internal + */ + public function injectPersistenceManager(\TYPO3\CMS\Extbase\Persistence\PersistenceManagerInterface $persistenceManager) + { + $this->persistenceManager = $persistenceManager; + } + + /** + * Actually convert from $source to $targetType, taking into account the fully + * built $convertedChildProperties and $configuration. + * + * @param string|int $source + * @param string $targetType + * @param array $convertedChildProperties + * @param PropertyMappingConfigurationInterface $configuration + * @return AbstractFileFolder + * @internal + */ + public function convertFrom($source, $targetType, array $convertedChildProperties = [], PropertyMappingConfigurationInterface $configuration = null) + { + if (!isset($source['error']) || $source['error'] === \UPLOAD_ERR_NO_FILE) { + if (isset($source['submittedFile']['resourcePointer'])) { + try { + $resourcePointer = $this->hashService->validateAndStripHmac($source['submittedFile']['resourcePointer']); + if (strpos($resourcePointer, 'file:') === 0) { + $fileUid = substr($resourcePointer, 5); + return $this->createFileReferenceFromFalFileObject($this->resourceFactory->getFileObject($fileUid)); + } else { + return $this->createFileReferenceFromFalFileReferenceObject($this->resourceFactory->getFileReferenceObject($resourcePointer), $resourcePointer); + } + } catch (\InvalidArgumentException $e) { + // Nothing to do. No file is uploaded and resource pointer is invalid. Discard! + } + } + return null; + } + + if ($source['error'] !== \UPLOAD_ERR_OK) { + return $this->objectManager->get(Error::class, $this->getUploadErrorMessage($source['error']), 1471715915); + } + + if (isset($this->convertedResources[$source['tmp_name']])) { + return $this->convertedResources[$source['tmp_name']]; + } + + try { + $resource = $this->importUploadedResource($source, $configuration); + } catch (\Exception $e) { + return $this->objectManager->get(Error::class, $e->getMessage(), $e->getCode()); + } + + $this->convertedResources[$source['tmp_name']] = $resource; + return $resource; + } + + /** + * Import a resource and respect configuration given for properties + * + * @param array $uploadInfo + * @param PropertyMappingConfigurationInterface $configuration + * @return ExtbaseFileReference + * @throws TypeConverterException + */ + protected function importUploadedResource( + array $uploadInfo, + PropertyMappingConfigurationInterface $configuration + ): ExtbaseFileReference { + if (!GeneralUtility::verifyFilenameAgainstDenyPattern($uploadInfo['name'])) { + throw new TypeConverterException('Uploading files with PHP file extensions is not allowed!', 1471710357); + } + + $uploadFolderId = $configuration->getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_FOLDER) ?: $this->defaultUploadFolder; + $conflictMode = $configuration->getConfigurationValue(self::class, self::CONFIGURATION_UPLOAD_CONFLICT_MODE) ?: $this->defaultConflictMode; + + $uploadFolder = $this->resourceFactory->retrieveFileOrFolderObject($uploadFolderId); + $uploadedFile = $uploadFolder->addUploadedFile($uploadInfo, $conflictMode); + + $validators = $configuration->getConfigurationValue(self::class, self::CONFIGURATION_FILE_VALIDATORS); + if (is_array($validators)) { + foreach ($validators as $validator) { + if ($validator instanceof AbstractValidator) { + $validationResult = $validator->validate($uploadedFile); + if ($validationResult->hasErrors()) { + $uploadedFile->getStorage()->deleteFile($uploadedFile); + throw new TypeConverterException($validationResult->getErrors()[0]->getMessage(), 1471708999); + } + } + } + } + + $resourcePointer = isset($uploadInfo['submittedFile']['resourcePointer']) && strpos($uploadInfo['submittedFile']['resourcePointer'], 'file:') === false + ? $this->hashService->validateAndStripHmac($uploadInfo['submittedFile']['resourcePointer']) + : null; + + $fileReferenceModel = $this->createFileReferenceFromFalFileObject($uploadedFile, $resourcePointer); + + return $fileReferenceModel; + } + + /** + * @param File $file + * @param int $resourcePointer + * @return ExtbaseFileReference + */ + protected function createFileReferenceFromFalFileObject( + File $file, + int $resourcePointer = null + ): ExtbaseFileReference { + $fileReference = $this->resourceFactory->createFileReferenceObject( + [ + 'uid_local' => $file->getUid(), + 'uid_foreign' => uniqid('NEW_'), + 'uid' => uniqid('NEW_'), + 'crop' => null, + ] + ); + return $this->createFileReferenceFromFalFileReferenceObject($fileReference, $resourcePointer); + } + + /** + * @param CoreFileReference $falFileReference + * @param int $resourcePointer + * @return ExtbaseFileReference + */ + protected function createFileReferenceFromFalFileReferenceObject( + CoreFileReference $falFileReference, + int $resourcePointer = null + ): ExtbaseFileReference { + if ($resourcePointer === null) { + $fileReference = $this->objectManager->get(ExtbaseFileReference::class); + } else { + $fileReference = $this->persistenceManager->getObjectByIdentifier($resourcePointer, ExtbaseFileReference::class, false); + } + + $fileReference->setOriginalResource($falFileReference); + return $fileReference; + } + + /** + * Returns a human-readable message for the given PHP file upload error + * constant. + * + * @param int $errorCode + * @return string + */ + protected function getUploadErrorMessage(int $errorCode): string + { + switch ($errorCode) { + case \UPLOAD_ERR_INI_SIZE: + return 'The uploaded file exceeds the upload_max_filesize directive in php.ini'; + case \UPLOAD_ERR_FORM_SIZE: + return 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'; + case \UPLOAD_ERR_PARTIAL: + return 'The uploaded file was only partially uploaded'; + case \UPLOAD_ERR_NO_FILE: + return 'No file was uploaded'; + case \UPLOAD_ERR_NO_TMP_DIR: + return 'Missing a temporary folder'; + case \UPLOAD_ERR_CANT_WRITE: + return 'Failed to write file to disk'; + case \UPLOAD_ERR_EXTENSION: + return 'File upload stopped by extension'; + default: + return 'Unknown upload error'; + } + } +} diff --git a/typo3/sysext/form/Classes/Mvc/Validation/CountValidator.php b/typo3/sysext/form/Classes/Mvc/Validation/CountValidator.php new file mode 100644 index 000000000000..1d21963e671a --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Validation/CountValidator.php @@ -0,0 +1,69 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Validation; + +/* + * 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\Extbase\Validation\Validator\AbstractValidator; + +/** + * Validator for countable types + * + * Scope: frontend + * @internal + */ +class CountValidator extends AbstractValidator +{ + /** + * @var array + */ + protected $supportedOptions = [ + 'minimum' => [0, 'The minimum count to accept', 'integer'], + 'maximum' => [PHP_INT_MAX, 'The maximum count to accept', 'integer'] + ]; + + /** + * The given value is valid if it is an array or \Countable that contains the specified amount of elements. + * + * @param mixed $value + * @return void + * @api + */ + public function isValid($value) + { + if (!is_array($value) && !($value instanceof \Countable)) { + $this->addError( + $this->translateErrorMessage( + 'validation.error.1475002976', + 'form' + ), + 1475002976 + ); + return; + } + + $minimum = (int)$this->options['minimum']; + $maximum = (int)$this->options['maximum']; + if (count($value) < $minimum || count($value) > $maximum) { + $this->addError( + $this->translateErrorMessage( + 'validation.error.1475002994', + 'form', + [$minimum, $maximum] + ), + 1475002994 + ); + } + } +} diff --git a/typo3/sysext/form/Classes/Mvc/Validation/EmptyValidator.php b/typo3/sysext/form/Classes/Mvc/Validation/EmptyValidator.php new file mode 100644 index 000000000000..5e1204a5520e --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Validation/EmptyValidator.php @@ -0,0 +1,55 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Validation; + +/* + * 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\Extbase\Validation\Validator\AbstractValidator; + +/** + * Validator for empty values. + * + * Scope: frontend + * @api + */ +class EmptyValidator extends AbstractValidator +{ + /** + * This validator always needs to be executed even if the given value is empty. + * See AbstractValidator::validate() + * + * @var bool + */ + protected $acceptsEmptyValues = true; + + /** + * Checks if the given property ($propertyValue) is empty (NULL, empty string, empty array or empty object). + * + * @param mixed $value The value that should be validated + * @return void + * @api + */ + public function isValid($value) + { + if (!empty($value)) { + $this->addError( + $this->translateErrorMessage( + 'validation.error.1476396435', + 'form' + ), + 1476396435 + ); + } + } +} diff --git a/typo3/sysext/form/Classes/Mvc/Validation/Exception/InvalidValidationOptionsException.php b/typo3/sysext/form/Classes/Mvc/Validation/Exception/InvalidValidationOptionsException.php new file mode 100644 index 000000000000..5386627f7f80 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Validation/Exception/InvalidValidationOptionsException.php @@ -0,0 +1,25 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Validation\Exception; + +/* + * 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\Form\Exception; + +/** + * @api + */ +class InvalidValidationOptionsException extends Exception +{ +} diff --git a/typo3/sysext/form/Classes/Mvc/Validation/MimeTypeValidator.php b/typo3/sysext/form/Classes/Mvc/Validation/MimeTypeValidator.php new file mode 100644 index 000000000000..4cf7b93a030e --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/Validation/MimeTypeValidator.php @@ -0,0 +1,90 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\Validation; + +/* + * 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\Core\Resource\File; +use TYPO3\CMS\Extbase\Domain\Model\FileReference; +use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator; +use TYPO3\CMS\Form\Mvc\Validation\Exception\InvalidValidationOptionsException; + +/** + * Validator for mime types + * + * Scope: frontend + * @api + */ +class MimeTypeValidator extends AbstractValidator +{ + /** + * @var array + */ + protected $supportedOptions = [ + 'allowedMimeTypes' => [null, 'Allowed mime types (using */* IANA media types)', 'array', true] + ]; + + /** + * The given $value is valid if it is an FileReference of the + * configured type (one of the image/* IANA media subtypes) + * + * Note: a value of NULL or empty string ('') is considered valid + * + * @param FileReference|File $resource The resource that should be validated + * @return void + * @api + */ + public function isValid($resource) + { + $this->validateOptions(); + + if ($resource instanceof FileReference) { + $resource = $resource->getOriginalResource(); + } elseif (!$resource instanceof File) { + $this->addError( + $this->translateErrorMessage( + 'validation.error.1471708997', + 'form' + ), + 1471708997 + ); + return; + } + + $allowedMimeTypes = $this->options['allowedMimeTypes']; + if (!in_array($resource->getMimeType(), $allowedMimeTypes, true)) { + $this->addError( + $this->translateErrorMessage( + 'validation.error.1471708998', + 'form', + [$resource->getMimeType()] + ), + 1471708998 + ); + } + } + + /** + * Checks if this validator is correctly configured + * + * @return void + * @throws InvalidValidationOptionsException if the configured validation options are incorrect + */ + protected function validateOptions() + { + if (!is_array($this->options['allowedMimeTypes']) || $this->options['allowedMimeTypes'] === []) { + throw new InvalidValidationOptionsException('The option "allowedMimeTypes" must be an array with at least one item.', 1471713296); + } + } +} diff --git a/typo3/sysext/form/Classes/Mvc/View/FormView.php b/typo3/sysext/form/Classes/Mvc/View/FormView.php new file mode 100644 index 000000000000..8cac863e5763 --- /dev/null +++ b/typo3/sysext/form/Classes/Mvc/View/FormView.php @@ -0,0 +1,233 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Mvc\View; + +/* + * 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\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Fluid\View\AbstractTemplateView; +use TYPO3\CMS\Form\Domain\Exception\RenderingException; +use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface; +use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; +use TYPO3\CMS\Form\Domain\Renderer\RendererInterface; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; + +/** + * A fluid TemplateView implementation which used to render *Renderables*. + * + * The FormView is especially capable of rendering nested renderables + * as well, i.e a form with a page, with all FormElements. + * + * Options + * ======= + * + * The FormView uses some rendering options which are of particular + * importance, as they determine how the form field is resolved to a path + * in the file system. + * + * All rendering options are retrieved from the renderable which shall be rendered, + * using the {@link \TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface::getRenderingOptions()} + * method. + * + * templateRootPaths + * ----------------- + * + * Used to define several paths for templates, which will be tried in reversed + * order (the paths are searched from bottom to top). The first folder where + * the desired layout is found, is used. If the array keys are numeric, + * they are first sorted and then tried in reversed order. + * + * layoutRootPaths + * --------------- + * + * Used to define several paths for layouts, which will be tried in reversed + * order (the paths are searched from bottom to top). The first folder where + * the desired layout is found, is used. If the array keys are numeric, + * they are first sorted and then tried in reversed order. + * + * partialRootPaths + * ---------------- + * + * Used to define several paths for partials, which will be tried in reversed + * order. The first folder where the desired partial is found, is used. + * The keys of the array define the order. + * + * renderableNameInTemplate + * ------------------------ + * + * This is a mostly-internal setting which controls the name under which the current + * renderable is made available inside the template. For example, it controls that + * inside the template of a "Page", the Page object is available using the variable + * *page*. + * + * Rendering Child Renderables + * =========================== + * + * If a renderable wants to render child renderables, inside its template, + * it can do that using the <code><formvh:renderRenderable></code> ViewHelper. + * + * A template example from Page shall demonstrate this: + * + * <pre> + * {namespace formvh=TYPO3\CMS\Form\ViewHelpers} + * <f:for each="{page.elements}" as="element"> + * <formvh:renderRenderable renderable="{element}" /> + * </f:for> + * </pre> + * + * Rendering PHP Based Child Renderables + * ===================================== + * + * If a child renderable has a *rendererClassName* set (i.e. {@link \TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface::getRendererClassName()} + * returns a non-NULL string), this renderer is automatically instanciated + * and the rendering for this element is delegated to this Renderer. + * + * Scope: frontend + * **This class is NOT meant to be sub classed by developers.** + * @internal + */ +class FormView extends AbstractTemplateView +{ + + /** + * @var \TYPO3\CMS\Form\Domain\Runtime\FormRuntime + */ + protected $formRuntime; + + /** + * @param FormRuntime $formRuntime + * @return void + * @internal + */ + public function setFormRuntime(FormRuntime $formRuntime) + { + $this->formRuntime = $formRuntime; + } + + /** + * @return FormRuntime + * @internal + */ + public function getFormRuntime(): FormRuntime + { + return $this->formRuntime; + } + + /** + * Render the $renderable and return the content. + * + * @param RootRenderable $renderable + * @return string + * @throws RenderingException + * @internal + */ + public function renderRenderable(RootRenderableInterface $renderable): string + { + // Invoke the beforeRendering callback on the renderable + $renderable->beforeRendering($this->formRuntime); + + if ( + $renderable->getRendererClassName() !== null + && $renderable->getRendererClassName() !== $this->formRuntime->getRendererClassName() + ) { + // If a child renderable has a *rendererClassName* set + // then render it with this foreign renderer. + $rendererClassName = $renderable->getRendererClassName(); + $renderer = GeneralUtility::makeInstance(ObjectManager::class)->get($rendererClassName); + if (!($renderer instanceof RendererInterface)) { + throw new RenderingException( + sprintf('The renderer class "%s" for "%s" does not implement RendererInterface.', $rendererClassName, $renderable->getType()), + 1480286138 + ); + } + $renderer->setControllerContext($this->baseRenderingContext->getControllerContext()); + $renderer->setFormRuntime($this->formRuntime); + return $renderer->render($renderable); + } + + $renderingOptions = $renderable->getRenderingOptions(); + + if (!isset($renderingOptions['templateRootPaths'])) { + throw new RenderingException( + sprintf('The option templateRootPaths must be set for renderable "%s"', $renderable->getType()), + 1480293084 + ); + } + if (!isset($renderingOptions['layoutRootPaths'])) { + throw new RenderingException( + sprintf('The option layoutRootPaths must be set for renderable "%s"', $renderable->getType()), + 1480293085 + ); + } + if (!isset($renderingOptions['partialRootPaths'])) { + throw new RenderingException( + sprintf('The option partialRootPaths must be set for renderable "%s"', $renderable->getType()), + 1480293086 + ); + } + if (!isset($renderingOptions['renderableNameInTemplate'])) { + throw new RenderingException( + sprintf('The option renderableNameInTemplate must be set for renderable "%s"', $renderable->getType()), + 1480293087 + ); + } + + $renderingContext = $this->getCurrentRenderingContext(); + // Configure the fluid TemplateView with the rendering options + // from the renderable + $renderingContext->getTemplatePaths()->setTemplateRootPaths($renderingOptions['templateRootPaths']); + $renderingContext->getTemplatePaths()->setLayoutRootPaths($renderingOptions['layoutRootPaths']); + $renderingContext->getTemplatePaths()->setPartialRootPaths($renderingOptions['partialRootPaths']); + + // Add the renderable object to the template variables and use the + // configured variable name + $renderingContext->getVariableProvider()->add($renderingOptions['renderableNameInTemplate'], $renderable); + + // Render the renderable. + if (isset($renderingOptions['templatePathAndFilename'])) { + $renderingContext->getTemplatePaths()->setTemplatePathAndFilename($renderingOptions['templatePathAndFilename']); + $output = $this->render(); + } else { + // Use the *type* of the renderable as template name + $output = $this->render($renderable->getType()); + } + + return $this->renderPreviewMode($output, $renderable); + } + + /** + * Wrap every renderable with a span with a identifier path data attribute. + * + * @param string $output + * @param RootRenderableInterface $renderable + * @return string + * @internal + */ + protected function renderPreviewMode(string $output, RootRenderableInterface $renderable): string + { + $renderingOptions = $this->formRuntime->getRenderingOptions(); + $previewMode = isset($renderingOptions['previewMode']) && $renderingOptions['previewMode'] === true; + if ($previewMode) { + $path = $renderable->getIdentifier(); + if ($renderable instanceof RenderableInterface) { + while ($renderable = $renderable->getParentRenderable()) { + $path = $renderable->getIdentifier() . '/' . $path; + } + } + $output = sprintf('<span data-element-identifier-path="%s">%s</span>', $path, $output); + } + return $output; + } +} diff --git a/typo3/sysext/form/Classes/PostProcess/AbstractPostProcessor.php b/typo3/sysext/form/Classes/PostProcess/AbstractPostProcessor.php deleted file mode 100644 index 1a0f0366b91d..000000000000 --- a/typo3/sysext/form/Classes/PostProcess/AbstractPostProcessor.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\PostProcess; - -/* - * 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! - */ - -/** - * The mail post processor - */ -abstract class AbstractPostProcessor -{ - /** - * @var \TYPO3\CMS\Form\Mvc\Controller\ControllerContext - */ - protected $controllerContext; - - /** - * Set the current controller context - * - * @param \TYPO3\CMS\Form\Mvc\Controller\ControllerContext $controllerContext - * @return void - */ - public function setControllerContext(\TYPO3\CMS\Form\Mvc\Controller\ControllerContext $controllerContext) - { - $this->controllerContext = $controllerContext; - } -} diff --git a/typo3/sysext/form/Classes/PostProcess/MailPostProcessor.php b/typo3/sysext/form/Classes/PostProcess/MailPostProcessor.php deleted file mode 100644 index 1cf9517a0019..000000000000 --- a/typo3/sysext/form/Classes/PostProcess/MailPostProcessor.php +++ /dev/null @@ -1,566 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\PostProcess; - -/* - * 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\Core\Mail\Rfc822AddressesParser; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\MailUtility; -use TYPO3\CMS\Core\Utility\MathUtility; -use TYPO3\CMS\Extbase\Utility\LocalizationUtility; -use TYPO3\CMS\Form\Utility\FormUtility; - -/** - * The mail post processor - */ -class MailPostProcessor extends AbstractPostProcessor implements PostProcessorInterface -{ - /** - * Constant for localization - * - * @var string - */ - const LOCALISATION_OBJECT_NAME = 'tx_form_view_mail'; - - /** - * @var \TYPO3\CMS\Extbase\Object\ObjectManager - */ - protected $objectManager; - - /** - * @var \TYPO3\CMS\Form\Utility\SessionUtility - */ - protected $sessionUtility; - - /** - * @var FormUtility - */ - protected $formUtility; - - /** - * @var \TYPO3\CMS\Form\Domain\Model\Element - */ - protected $form; - - /** - * @var array - */ - protected $typoScript; - - /** - * @var \TYPO3\CMS\Core\Mail\MailMessage - */ - protected $mailMessage; - - /** - * @var string - */ - protected $htmlMailTemplatePath = 'Html'; - - /** - * @var string - */ - protected $plaintextMailTemplatePath = 'Plain'; - - /** - * @var array - */ - protected $dirtyHeaders = []; - - /** - * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager - * @return void - */ - public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) - { - $this->objectManager = $objectManager; - } - - /** - * @param \TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility - * @return void - */ - public function injectSessionUtility(\TYPO3\CMS\Form\Utility\SessionUtility $sessionUtility) - { - $this->sessionUtility = $sessionUtility; - } - - /** - * Constructor - * - * @param \TYPO3\CMS\Form\Domain\Model\Element $form Form domain model - * @param array $typoScript Post processor TypoScript settings - */ - public function __construct(\TYPO3\CMS\Form\Domain\Model\Element $form, array $typoScript) - { - $this->form = $form; - $this->typoScript = $typoScript; - $this->mailMessage = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class); - $this->setTemplatePaths(); - } - - /** - * The main method called by the post processor - * - * Configures the mail message - * - * @return string HTML message from this processor - */ - public function process() - { - $this->formUtility = FormUtility::create($this->controllerContext->getConfiguration()); - $this->setSubject(); - $this->setFrom(); - $this->setTo(); - $this->setCc(); - $this->setReplyTo(); - $this->setPriority(); - $this->setOrganization(); - $this->setHtmlContent(); - $this->setPlainContent(); - $this->addAttachmentsFromSession(); - $this->send(); - return $this->render(); - } - - /** - * Sets the subject of the mail message - * - * If not configured, it will use a default setting - * - * @return void - */ - protected function setSubject() - { - if (isset($this->typoScript['subject'])) { - $subject = $this->formUtility->renderItem( - $this->typoScript['subject.'], - $this->typoScript['subject'] - ); - } elseif ($this->getTypoScriptValueFromIncomingData('subjectField') !== null) { - $subject = $this->getTypoScriptValueFromIncomingData('subjectField'); - } else { - $subject = 'Formmail on ' . GeneralUtility::getIndpEnv('HTTP_HOST'); - } - - $subject = $this->sanitizeHeaderString($subject); - $this->mailMessage->setSubject($subject); - } - - /** - * Sets the sender of the mail message - * - * Mostly the sender is a combination of the name and the email address - * - * @return void - */ - protected function setFrom() - { - if (isset($this->typoScript['senderEmail'])) { - $fromEmail = $this->formUtility->renderItem( - $this->typoScript['senderEmail.'], - $this->typoScript['senderEmail'] - ); - } elseif ($this->getTypoScriptValueFromIncomingData('senderEmailField') !== null) { - $fromEmail = $this->getTypoScriptValueFromIncomingData('senderEmailField'); - } else { - $fromEmail = $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromAddress']; - } - if (!GeneralUtility::validEmail($fromEmail)) { - $fromEmail = MailUtility::getSystemFromAddress(); - } - if (isset($this->typoScript['senderName'])) { - $fromName = $this->formUtility->renderItem( - $this->typoScript['senderName.'], - $this->typoScript['senderName'] - ); - } elseif ($this->getTypoScriptValueFromIncomingData('senderNameField') !== null) { - $fromName = $this->getTypoScriptValueFromIncomingData('senderNameField'); - } else { - $fromName = $GLOBALS['TYPO3_CONF_VARS']['MAIL']['defaultMailFromName']; - } - $fromName = $this->sanitizeHeaderString($fromName); - if (!empty($fromName)) { - $from = [$fromEmail => $fromName]; - } else { - $from = $fromEmail; - } - $this->mailMessage->setFrom($from); - } - - /** - * Filter input-string for valid email addresses - * - * @param string $emails If this is a string, it will be checked for one or more valid email addresses. - * @return array List of valid email addresses - */ - protected function filterValidEmails($emails) - { - if (!is_string($emails)) { - // No valid addresses - empty list - return []; - } - - /** @var $addressParser Rfc822AddressesParser */ - $addressParser = GeneralUtility::makeInstance(Rfc822AddressesParser::class, $emails); - $addresses = $addressParser->parseAddressList(); - - $validEmails = []; - foreach ($addresses as $address) { - $fullAddress = $address->mailbox . '@' . $address->host; - if (GeneralUtility::validEmail($fullAddress)) { - if ($address->personal) { - $validEmails[$fullAddress] = $address->personal; - } else { - $validEmails[] = $fullAddress; - } - } - } - return $validEmails; - } - - /** - * Adds the receiver of the mail message when configured - * - * Checks the address if it is a valid email address - * - * @return void - */ - protected function setTo() - { - $emails = $this->formUtility->renderItem( - $this->typoScript['recipientEmail.'], - $this->typoScript['recipientEmail'] - ); - $validEmails = $this->filterValidEmails($emails); - if (!empty($validEmails)) { - $this->mailMessage->setTo($validEmails); - } - } - - /** - * Adds the carbon copy receiver of the mail message when configured - * - * Checks the address if it is a valid email address - * - * @return void - */ - protected function setCc() - { - $emails = $this->formUtility->renderItem( - $this->typoScript['ccEmail.'], - $this->typoScript['ccEmail'] - ); - $validEmails = $this->filterValidEmails($emails); - if (!empty($validEmails)) { - $this->mailMessage->setCc($validEmails); - } - } - - /** - * Adds the reply to header of the mail message when configured - * - * Checks the address if it is a valid email address - * - * @return void - */ - protected function setReplyTo() - { - if (isset($this->typoScript['replyToEmail'])) { - $emails = $this->formUtility->renderItem( - $this->typoScript['replyToEmail.'], - $this->typoScript['replyToEmail'] - ); - } elseif ($this->getTypoScriptValueFromIncomingData('replyToEmailField') !== null) { - $emails = $this->getTypoScriptValueFromIncomingData('replyToEmailField'); - } - $validEmails = $this->filterValidEmails($emails); - if (!empty($validEmails)) { - $this->mailMessage->setReplyTo($validEmails); - } - } - - /** - * Set the priority of the mail message - * - * When not in settings, the value will be 3. If the priority is configured, - * but too big, it will be set to 5, which means very low. - * - * @return void - */ - protected function setPriority() - { - $priority = 3; - if (isset($this->typoScript['priority'])) { - $priorityFromTs = $this->formUtility->renderItem( - $this->typoScript['priority.'], - $this->typoScript['priority'] - ); - } - if (!empty($priorityFromTs)) { - $priority = MathUtility::forceIntegerInRange($priorityFromTs, 1, 5); - } - $this->mailMessage->setPriority($priority); - } - - /** - * Add a text header to the mail header of the type Organization - * - * Sanitizes the header string when necessary - * - * @return void - */ - protected function setOrganization() - { - if (isset($this->typoScript['organization'])) { - $organization = $this->formUtility->renderItem( - $this->typoScript['organization.'], - $this->typoScript['organization'] - ); - } - if (!empty($organization)) { - $organization = $this->sanitizeHeaderString($organization); - $this->mailMessage->getHeaders()->addTextHeader('Organization', $organization); - } - } - - /** - * Set the default character set used - * - * Respect formMailCharset if it was set, otherwise use metaCharset for mail - * if different than utf-8 - * - * @return void - */ - protected function setCharacterSet() - { - $characterSet = null; - if ($GLOBALS['TSFE']->config['config']['formMailCharset']) { - $characterSet = $GLOBALS['TSFE']->csConvObj->parse_charset($GLOBALS['TSFE']->config['config']['formMailCharset']); - } elseif ($GLOBALS['TSFE']->metaCharset !== 'utf-8') { - $characterSet = $GLOBALS['TSFE']->metaCharset; - } - if ($characterSet) { - $this->mailMessage->setCharset($characterSet); - } - } - - /** - * Add the HTML content - * - * Add a MimePart of the type text/html to the message. - * - * @return void - */ - protected function setHtmlContent() - { - $htmlContent = $this->getView($this->htmlMailTemplatePath)->render(); - $this->mailMessage->setBody($htmlContent, 'text/html'); - } - - /** - * Add the plain content - * - * Add a MimePart of the type text/plain to the message. - * - * @return void - */ - protected function setPlainContent() - { - $plainContent = $this->getView($this->plaintextMailTemplatePath, 'Plain')->render(); - $this->mailMessage->addPart($plainContent, 'text/plain'); - } - - /** - * Sends the mail. - * Sending the mail requires the recipient and message to be set. - * - * @return void - */ - protected function send() - { - if ($this->mailMessage->getTo() && $this->mailMessage->getBody()) { - $this->mailMessage->send(); - } - } - - /** - * Render the message after trying to send the mail - * - * @return string HTML message from the mail view - */ - protected function render() - { - if ($this->mailMessage->isSent()) { - $output = $this->renderMessage('success'); - } else { - $output = $this->renderMessage('error'); - } - return $output; - } - - /** - * Checks string for suspicious characters - * - * @param string $string String to check - * @return string Valid or empty string - */ - protected function sanitizeHeaderString($string) - { - $pattern = '/[\\r\\n\\f\\e]/'; - if (preg_match($pattern, $string) > 0) { - $this->dirtyHeaders[] = $string; - $string = ''; - } - return $string; - } - - /** - * Loop through all elements of the session and attach the file - * if its a uploaded file - * - * @return void - */ - protected function addAttachmentsFromSession() - { - $sessionData = $this->sessionUtility->getSessionData(); - if (is_array($sessionData)) { - foreach ($sessionData as $fieldName => $values) { - if (is_array($values)) { - foreach ($values as $file) { - if (isset($file['tempFilename'])) { - if ( - is_file($file['tempFilename']) - && GeneralUtility::isAllowedAbsPath($file['tempFilename']) - ) { - $this->mailMessage->attach(\Swift_Attachment::fromPath($file['tempFilename'])->setFilename($file['name'])); - } - } - } - } - } - } - } - - /** - * Set the html and plaintext templates - * - * @return void - */ - protected function setTemplatePaths() - { - if ( - isset($this->typoScript['htmlMailTemplatePath']) - && $this->typoScript['htmlMailTemplatePath'] !== '' - ) { - $this->htmlMailTemplatePath = $this->typoScript['htmlMailTemplatePath']; - } - - if ( - isset($this->typoScript['plaintextMailTemplatePath']) - && $this->typoScript['plaintextMailTemplatePath'] !== '' - ) { - $this->plaintextMailTemplatePath = $this->typoScript['plaintextMailTemplatePath']; - } - } - - /** - * Make fluid view instance - * - * @param string $templateName - * @param string $scope - * @return \TYPO3\CMS\Fluid\View\StandaloneView - */ - protected function getView($templateName, $scope = 'Html') - { - $configurationManager = $this->objectManager->get(\TYPO3\CMS\Extbase\Configuration\ConfigurationManager::class); - $typoScript = $configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK); - /** @var \TYPO3\CMS\Fluid\View\StandaloneView $view */ - $view = $this->objectManager->get(\TYPO3\CMS\Fluid\View\StandaloneView::class); - - /* Extend all template root paths to $templateRootPaths/PostProcessor/Mail/$themeName */ - foreach ($typoScript['view']['templateRootPaths'] as &$path) { - if (substr($path, -1) !== '/') { - $path .= '/'; - } - $path .= 'PostProcessor/Mail/' . $this->controllerContext->getConfiguration()->getThemeName(); - } - /* Extend all partial root paths to $partialRootPaths/$themeName/PostProcessor/Mail/$scope/ */ - foreach ($typoScript['view']['partialRootPaths'] as &$path) { - if (substr($path, -1) !== '/') { - $path .= '/'; - } - $path .= $this->controllerContext->getConfiguration()->getThemeName() . '/PostProcessor/Mail/' . $scope . '/'; - } - $view->setLayoutRootPaths($typoScript['view']['layoutRootPaths']); - $view->setTemplateRootPaths($typoScript['view']['templateRootPaths']); - $view->setPartialRootPaths($typoScript['view']['partialRootPaths']); - $view->setTemplate($templateName); - $view->assignMultiple([ - 'model' => $this->form - ]); - return $view; - } - - /** - * Render the processor message - * - * @param string $messageType - * @return string - */ - protected function renderMessage($messageType) - { - return $this->formUtility->renderItem( - $this->typoScript['messages.'][$messageType . '.'], - $this->typoScript['messages.'][$messageType], - $this->getLocalLanguageLabel($messageType) - ); - } - - /** - * Get the local language label(s) for the message - * In some cases this method will be override by rule class - * - * @param string $type The type - * @return string The local language message label - */ - protected function getLocalLanguageLabel($type = '') - { - $label = static::LOCALISATION_OBJECT_NAME . '.' . $type; - $message = LocalizationUtility::translate($label, 'form'); - return $message; - } - - /** - * Determines user submitted data from a field - * that has been defined as TypoScript property. - * - * @param string $propertyName - * @return NULL|mixed - */ - protected function getTypoScriptValueFromIncomingData($propertyName) - { - if (empty($this->typoScript[$propertyName])) { - return null; - } - - $propertyValue = $this->typoScript[$propertyName]; - $incomingData = $this->controllerContext->getValidationElement(); - if (!$incomingData->hasIncomingField($propertyValue)) { - return null; - } - - return $incomingData->getIncomingField($propertyValue); - } -} diff --git a/typo3/sysext/form/Classes/PostProcess/PostProcessor.php b/typo3/sysext/form/Classes/PostProcess/PostProcessor.php deleted file mode 100644 index 1fb2f5227d21..000000000000 --- a/typo3/sysext/form/Classes/PostProcess/PostProcessor.php +++ /dev/null @@ -1,106 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\PostProcess; - -/* - * 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\Core\Utility\ArrayUtility; - -/** - * The post processor - */ -class PostProcessor extends AbstractPostProcessor -{ - /** - * @var \TYPO3\CMS\Extbase\Object\ObjectManager - */ - protected $objectManager; - - /** - * @var array - */ - protected $postProcessorTypoScript; - - /** - * @var \TYPO3\CMS\Form\Domain\Model\Element $form - */ - protected $form; - - /** - * @param \TYPO3\CMS\Extbase\Object\ObjectManager $objectManager - * @return void - */ - public function injectObjectManager(\TYPO3\CMS\Extbase\Object\ObjectManager $objectManager) - { - $this->objectManager = $objectManager; - } - - /** - * Constructor - * - * @param \TYPO3\CMS\Form\Domain\Model\Element $form - * @param array $postProcessorTypoScript Post processor TypoScript settings - */ - public function __construct(\TYPO3\CMS\Form\Domain\Model\Element $form, array $postProcessorTypoScript) - { - $this->form = $form; - $this->postProcessorTypoScript = $postProcessorTypoScript; - } - - /** - * The main method called by the controller - * - * Iterates over the configured post processors and calls them with their - * own settings - * - * @return string HTML messages from the called processors - */ - public function process() - { - $html = ''; - - if (is_array($this->postProcessorTypoScript)) { - $keys = ArrayUtility::filterAndSortByNumericKeys($this->postProcessorTypoScript); - - foreach ($keys as $key) { - if (!(int)$key || strpos($key, '.') !== false) { - continue; - } - $className = false; - $processorName = $this->postProcessorTypoScript[$key]; - $processorArguments = []; - if (isset($this->postProcessorTypoScript[$key . '.'])) { - $processorArguments = $this->postProcessorTypoScript[$key . '.']; - } - - if (class_exists($processorName, true)) { - $className = $processorName; - } else { - $classNameExpanded = 'TYPO3\\CMS\\Form\\PostProcess\\' . ucfirst(strtolower($processorName)) . 'PostProcessor'; - if (class_exists($classNameExpanded, true)) { - $className = $classNameExpanded; - } - } - if ($className !== false) { - $processor = $this->objectManager->get($className, $this->form, $processorArguments); - if ($processor instanceof PostProcessorInterface) { - $processor->setControllerContext($this->controllerContext); - $html .= $processor->process(); - } - } - } - } - - return $html; - } -} diff --git a/typo3/sysext/form/Classes/PostProcess/PostProcessorInterface.php b/typo3/sysext/form/Classes/PostProcess/PostProcessorInterface.php deleted file mode 100644 index bbbbc7d89865..000000000000 --- a/typo3/sysext/form/Classes/PostProcess/PostProcessorInterface.php +++ /dev/null @@ -1,44 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\PostProcess; - -/* - * 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! - */ - -/** - * Interface for post-processors - */ -interface PostProcessorInterface -{ - /** - * Constructor - * - * @param \TYPO3\CMS\Form\Domain\Model\Element $form Form domain model - * @param array $typoScript Post processor TypoScript settings - */ - public function __construct(\TYPO3\CMS\Form\Domain\Model\Element $form, array $typoScript); - - /** - * Set the current controller context - * - * @param \TYPO3\CMS\Form\Mvc\Controller\ControllerContext $controllerContext - * @return void - */ - public function setControllerContext(\TYPO3\CMS\Form\Mvc\Controller\ControllerContext $controllerContext); - - /** - * The main method called by the post processor - * - * @return string The post processing HTML - */ - public function process(); -} diff --git a/typo3/sysext/form/Classes/PostProcess/RedirectPostProcessor.php b/typo3/sysext/form/Classes/PostProcess/RedirectPostProcessor.php deleted file mode 100644 index 58f7083997b1..000000000000 --- a/typo3/sysext/form/Classes/PostProcess/RedirectPostProcessor.php +++ /dev/null @@ -1,84 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\PostProcess; - -/* - * 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! - */ - -/** - * The redirect post-processor - */ -class RedirectPostProcessor extends AbstractPostProcessor implements PostProcessorInterface -{ - /** - * @var \TYPO3\CMS\Form\Domain\Model\Element - */ - protected $form; - - /** - * @var array - */ - protected $typoScript; - - /** - * @var string - */ - protected $destination; - - /** - * Constructor - * - * @param \TYPO3\CMS\Form\Domain\Model\Element $form Form domain model - * @param array $typoScript Post processor TypoScript settings - */ - public function __construct(\TYPO3\CMS\Form\Domain\Model\Element $form, array $typoScript) - { - $this->form = $form; - $this->typoScript = $typoScript; - } - - /** - * The main method called by the post processor - * - * @return string HTML message from this processor - */ - public function process() - { - $this->setDestination(); - $this->render(); - } - - /** - * Sets the redirect destination - * - * @return void - */ - protected function setDestination() - { - $this->destination = ''; - if ($this->typoScript['destination']) { - $urlConf = ['parameter' => $this->typoScript['destination']]; - $this->destination = $GLOBALS['TSFE']->cObj->typoLink_URL($urlConf); - } - } - - /** - * Redirect to a destination - * - * @return void - */ - protected function render() - { - \TYPO3\CMS\Core\Utility\HttpUtility::redirect($this->destination); - return; - } -} diff --git a/typo3/sysext/form/Classes/Service/TranslationService.php b/typo3/sysext/form/Classes/Service/TranslationService.php new file mode 100644 index 000000000000..b975b03d38b3 --- /dev/null +++ b/typo3/sysext/form/Classes/Service/TranslationService.php @@ -0,0 +1,508 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Service; + +/* + * 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\Core\Localization\Locales; +use TYPO3\CMS\Core\Localization\LocalizationFactory; +use TYPO3\CMS\Core\SingletonInterface; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Utility\ArrayUtility; +use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; +use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; +use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; +use TYPO3\CMS\Lang\LanguageService; + +/** + * Advanced translations + * This class is subjected to change. + * **Do NOT subclass** + * + * Scope: frontend / backend + * @internal + */ +class TranslationService implements SingletonInterface +{ + + /** + * Local Language content + * + * @var array + */ + protected $LOCAL_LANG = []; + + /** + * Contains those LL keys, which have been set to (empty) in TypoScript. + * This is necessary, as we cannot distinguish between a nonexisting + * translation and a label that has been cleared by TS. + * In both cases ['key'][0]['target'] is "". + * + * @var array + */ + protected $LOCAL_LANG_UNSET = []; + + /** + * Key of the language to use + * + * @var string + */ + protected $languageKey = null; + + /** + * Pointer to alternative fall-back language to use + * + * @var array + */ + protected $alternativeLanguageKeys = []; + + /** + * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface + */ + protected $configurationManager = null; + + /** + * Return TranslationService as singleton + * + * @return TranslationService + * @internal + */ + public static function getInstance() + { + return GeneralUtility::makeInstance(ObjectManager::class)->get(self::class); + } + + /** + * Returns the localized label of the LOCAL_LANG key, $key. + * + * @param mixed $key The key from the LOCAL_LANG array for which to return the value. + * @param array $arguments the arguments of the extension, being passed over to vsprintf + * @param string $locallangPathAndFilename + * @param string $language + * @param mixed $defaultValue + * @return mixed The value from LOCAL_LANG or $defaultValue if no translation was found. + * @internal + */ + public function translate( + $key, + array $arguments = null, + string $locallangPathAndFilename = null, + string $language = null, + $defaultValue = '' + ) { + $value = null; + $key = (string)$key; + + if ($locallangPathAndFilename) { + $key = $locallangPathAndFilename . ':' . $key; + } + + $keyParts = explode(':', $key); + if (GeneralUtility::isFirstPartOfStr($key, 'LLL:')) { + $locallangPathAndFilename = $keyParts[1] . ':' . $keyParts[2]; + $key = $keyParts[3]; + } elseif (GeneralUtility::isFirstPartOfStr($key, 'EXT:')) { + $locallangPathAndFilename = $keyParts[0] . ':' . $keyParts[1]; + $key = $keyParts[2]; + } else { + if (count($keyParts) === 2) { + $locallangPathAndFilename = $keyParts[0]; + $key = $keyParts[1]; + } + } + + if ($language) { + $this->languageKey = $language; + } + + $this->initializeLocalization($locallangPathAndFilename); + + // The "from" charset of csConv() is only set for strings from TypoScript via _LOCAL_LANG + if (!empty($this->LOCAL_LANG[$this->languageKey][$key][0]['target']) + || isset($this->LOCAL_LANG_UNSET[$this->languageKey][$key]) + ) { + // Local language translation for key exists + $value = $this->LOCAL_LANG[$this->languageKey][$key][0]['target']; + } elseif (!empty($this->alternativeLanguageKeys)) { + $languages = array_reverse($this->alternativeLanguageKeys); + foreach ($languages as $language) { + if (!empty($this->LOCAL_LANG[$language][$key][0]['target']) + || isset($this->LOCAL_LANG_UNSET[$language][$key]) + ) { + // Alternative language translation for key exists + $value = $this->LOCAL_LANG[$language][$key][0]['target']; + break; + } + } + } + + if ($value === null && (!empty($this->LOCAL_LANG['default'][$key][0]['target']) + || isset($this->LOCAL_LANG_UNSET['default'][$key])) + ) { + // Default language translation for key exists + // No charset conversion because default is English and thereby ASCII + $value = $this->LOCAL_LANG['default'][$key][0]['target']; + } + + if (is_array($arguments) && $value !== null) { + $value = vsprintf($value, $arguments); + } else { + if (empty($value)) { + $value = $defaultValue; + } + } + + return $value; + } + + /** + * Recursively translate values. + * + * @param array $array + * @param string $translationFile + * @return array the modified array + * @internal + */ + public function translateValuesRecursive(array $array, string $translationFile = null): array + { + $result = $array; + foreach ($result as $key => $value) { + if (is_array($value)) { + $result[$key] = $this->translateValuesRecursive($value, $translationFile); + } else { + $result[$key] = $this->translate($value, null, $translationFile, null, $value); + } + } + return $result; + } + + /** + * @param FormRuntime $formRuntime + * @param string $finisherIdentifier + * @param string $optionKey + * @param string $optionValue + * @param array $renderingOptions + * @return string + * @throws \InvalidArgumentException + * @api + */ + public function translateFinisherOption( + FormRuntime $formRuntime, + string $finisherIdentifier, + string $optionKey, + string $optionValue, + array $renderingOptions = [] + ): string { + if (empty($finisherIdentifier)) { + throw new \InvalidArgumentException('The argument "finisherIdentifier" is empty', 1476216059); + } + if (empty($optionKey)) { + throw new \InvalidArgumentException('The argument "optionKey" is empty', 1476216060); + } + + $finisherIdentifier = preg_replace('/Finisher$/', '', $finisherIdentifier); + $translationFile = $renderingOptions['translationFile']; + if (isset($renderingOptions['translatePropertyValueIfEmpty'])) { + $translatePropertyValueIfEmpty = (bool)$renderingOptions['translatePropertyValueIfEmpty']; + } else { + $translatePropertyValueIfEmpty = true; + } + + if (empty($optionValue) && !$translatePropertyValueIfEmpty) { + return $optionValue; + } + + $language = null; + if (isset($renderingOptions['language'])) { + $language = $renderingOptions['language']; + } + + $translationKeyChain = [ + sprintf('%s:%s.finisher.%s.%s', $translationFile, $formRuntime->getIdentifier(), $finisherIdentifier, $optionKey), + sprintf('%s:finisher.%s.%s', $translationFile, $finisherIdentifier, $optionKey) + ]; + $translatedValue = $this->processTranslationChain($translationKeyChain, $language); + $translatedValue = (empty($translatedValue)) ? $optionValue : $translatedValue; + + return $translatedValue; + } + + /** + * @param RootRenderableInterface $element + * @param string $property + * @param FormRuntime $formRuntime + * @return string|array + * @throws \InvalidArgumentException + * @internal + */ + public function translateFormElementValue( + RootRenderableInterface $element, + string $property, + FormRuntime $formRuntime + ) { + if (empty($property)) { + throw new \InvalidArgumentException('The argument "property" is empty', 1476216007); + } + + $propertyType = 'properties'; + $renderingOptions = $element->getRenderingOptions(); + + if ($property === 'label') { + $defaultValue = $element->getLabel(); + } else { + if ($element instanceof FormElementInterface) { + $defaultValue = ArrayUtility::getValueByPath($element->getProperties(), $property); + } else { + $propertyType = 'renderingOptions'; + $defaultValue = ArrayUtility::getValueByPath($renderingOptions, $property); + } + } + + if (isset($renderingOptions['translation']['translatePropertyValueIfEmpty'])) { + $translatePropertyValueIfEmpty = $renderingOptions['translation']['translatePropertyValueIfEmpty']; + } else { + $translatePropertyValueIfEmpty = true; + } + + if (empty($defaultValue) && !$translatePropertyValueIfEmpty) { + return $defaultValue; + } + + $defaultValue = empty($defaultValue) ? '' : $defaultValue; + $translationFile = $renderingOptions['translation']['translationFile']; + + $language = null; + if (isset($renderingOptions['translation']['language'])) { + $language = $renderingOptions['translation']['language']; + } + $translationKeyChain = []; + if ($property === 'options' && is_array($defaultValue)) { + foreach ($defaultValue as $optionValue => &$optionLabel) { + $translationKeyChain = [ + sprintf('%s:%s.element.%s.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $property, $optionValue), + sprintf('%s:element.%s.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $property, $optionValue) + ]; + $translatedValue = $this->processTranslationChain($translationKeyChain, $language); + $optionLabel = (empty($translatedValue)) ? $optionLabel : $translatedValue; + } + $translatedValue = $defaultValue; + } else { + $translationKeyChain = [ + sprintf('%s:%s.element.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $property), + sprintf('%s:element.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $property), + sprintf('%s:element.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $property), + ]; + $translatedValue = $this->processTranslationChain($translationKeyChain, $language); + $translatedValue = (empty($translatedValue)) ? $defaultValue : $translatedValue; + } + + return $translatedValue; + } + + /** + * @param string $languageKey + * @internal + */ + public function setLanguage(string $languageKey) + { + $this->languageKey = $languageKey; + } + + /** + * @return string + * @internal + */ + public function getLanguage(): string + { + return $this->languageKey; + } + + /** + * @param array $translationKeyChain + * @param string $language + * @return string|null + */ + protected function processTranslationChain(array $translationKeyChain, string $language = null) + { + $translatedValue = null; + foreach ($translationKeyChain as $translationKey) { + $translatedValue = $this->translate($translationKey, null, null, $language); + if (!empty($translatedValue)) { + break; + } + } + return $translatedValue; + } + + /** + * @param string $locallangPathAndFilename + * @return void + */ + protected function initializeLocalization(string $locallangPathAndFilename) + { + if (empty($this->languageKey)) { + $this->setLanguageKeys(); + } + + if (!empty($locallangPathAndFilename)) { + /** @var $languageFactory LocalizationFactory */ + $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class); + + $this->LOCAL_LANG = $languageFactory->getParsedData($locallangPathAndFilename, $this->languageKey, 'utf-8'); + + foreach ($this->alternativeLanguageKeys as $language) { + $tempLL = $languageFactory->getParsedData($locallangPathAndFilename, $language, 'utf-8'); + if ($this->languageKey !== 'default' && isset($tempLL[$language])) { + $this->LOCAL_LANG[$language] = $tempLL[$language]; + } + } + } + $this->loadTypoScriptLabels(); + } + + /** + * Sets the currently active language/language_alt keys. + * Default values are "default" for language key and "" for language_alt key. + * + * @return void + */ + protected function setLanguageKeys() + { + $this->languageKey = 'default'; + + $this->alternativeLanguageKeys = []; + if (TYPO3_MODE === 'FE') { + if (isset($this->getTypoScriptFrontendController()->config['config']['language'])) { + $this->languageKey = $this->getTypoScriptFrontendController()->config['config']['language']; + if (isset($this->getTypoScriptFrontendController()->config['config']['language_alt'])) { + $this->alternativeLanguageKeys[] = $this->getTypoScriptFrontendController()->config['config']['language_alt']; + } else { + /** @var $locales \TYPO3\CMS\Core\Localization\Locales */ + $locales = GeneralUtility::makeInstance(Locales::class); + if (in_array($this->languageKey, $locales->getLocales(), true)) { + foreach ($locales->getLocaleDependencies($this->languageKey) as $language) { + $this->alternativeLanguageKeys[] = $language; + } + } + } + } + } elseif (!empty($GLOBALS['BE_USER']->uc['lang'])) { + $this->languageKey = $GLOBALS['BE_USER']->uc['lang']; + } elseif (!empty($this->getLanguageService()->lang)) { + $this->languageKey = $this->getLanguageService()->lang; + } + } + + /** + * Overwrites labels that are set via TypoScript. + * TS locallang labels have to be configured like: + * plugin.tx_form._LOCAL_LANG.languageKey.key = value + * + * @return void + */ + protected function loadTypoScriptLabels() + { + $frameworkConfiguration = $this->getConfigurationManager() + ->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, 'form'); + + if (!is_array($frameworkConfiguration['_LOCAL_LANG'])) { + return; + } + $this->LOCAL_LANG_UNSET = []; + foreach ($frameworkConfiguration['_LOCAL_LANG'] as $languageKey => $labels) { + if (!(is_array($labels) && isset($this->LOCAL_LANG[$languageKey]))) { + continue; + } + foreach ($labels as $labelKey => $labelValue) { + if (is_string($labelValue)) { + $this->LOCAL_LANG[$languageKey][$labelKey][0]['target'] = $labelValue; + if ($labelValue === '') { + $this->LOCAL_LANG_UNSET[$languageKey][$labelKey] = ''; + } + } elseif (is_array($labelValue)) { + $labelValue = $this->flattenTypoScriptLabelArray($labelValue, $labelKey); + foreach ($labelValue as $key => $value) { + $this->LOCAL_LANG[$languageKey][$key][0]['target'] = $value; + if ($value === '') { + $this->LOCAL_LANG_UNSET[$languageKey][$key] = ''; + } + } + } + } + } + } + + /** + * Flatten TypoScript label array; converting a hierarchical array into a flat + * array with the keys separated by dots. + * + * Example Input: array('k1' => array('subkey1' => 'val1')) + * Example Output: array('k1.subkey1' => 'val1') + * + * @param array $labelValues Hierarchical array of labels + * @param string $parentKey the name of the parent key in the recursion; is only needed for recursion. + * @return array flattened array of labels. + */ + protected function flattenTypoScriptLabelArray(array $labelValues, string $parentKey = ''): array + { + $result = []; + foreach ($labelValues as $key => $labelValue) { + if (!empty($parentKey)) { + $key = $parentKey . '.' . $key; + } + if (is_array($labelValue)) { + $labelValue = $this->flattenTypoScriptLabelArray($labelValue, $key); + $result = array_merge($result, $labelValue); + } else { + $result[$key] = $labelValue; + } + } + return $result; + } + + /** + * Returns instance of the configuration manager + * + * @return ConfigurationManagerInterface + */ + protected function getConfigurationManager(): ConfigurationManagerInterface + { + if ($this->configurationManager !== null) { + return $this->configurationManager; + } + + $this->configurationManager = GeneralUtility::makeInstance(ObjectManager::class) + ->get(ConfigurationManagerInterface::class); + return $this->configurationManager; + } + + /** + * @return LanguageService + */ + protected function getLanguageService(): LanguageService + { + return $GLOBALS['LANG']; + } + + /** + * @return TypoScriptFrontendController + */ + protected function getTypoScriptFrontendController(): TypoScriptFrontendController + { + return $GLOBALS['TSFE']; + } +} diff --git a/typo3/sysext/form/Classes/Utility/ArrayUtility.php b/typo3/sysext/form/Classes/Utility/ArrayUtility.php new file mode 100644 index 000000000000..8790146e8c1a --- /dev/null +++ b/typo3/sysext/form/Classes/Utility/ArrayUtility.php @@ -0,0 +1,153 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Utility; + +/* + * 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\Form\Domain\Exception\TypeDefinitionNotValidException; + +/** + * Collection of static array utility functions + * + * Scope: frontend / backend + * @internal + */ +class ArrayUtility +{ + + /** + * Validates the given $arrayToTest by checking if an element is not in $allowedArrayKeys. + * + * @param array $arrayToTest + * @param array $allowedArrayKeys + * @return void + * @throws TypeDefinitionNotValidException if an element in $arrayToTest is not in $allowedArrayKeys + * @internal + */ + public static function assertAllArrayKeysAreValid(array $arrayToTest, array $allowedArrayKeys) + { + $notAllowedArrayKeys = array_keys(array_diff_key($arrayToTest, array_flip($allowedArrayKeys))); + if (count($notAllowedArrayKeys) !== 0) { + throw new TypeDefinitionNotValidException(sprintf('The options "%s" were not allowed (allowed were: "%s")', implode(', ', $notAllowedArrayKeys), implode(', ', $allowedArrayKeys)), 1325697085); + } + } + + /** + * Sort keys from the current nesting level if all keys within the + * current nesting level are integers. + * + * @param array $array + * @return array + * @internal + */ + public static function sortNumericArrayKeysRecursive(array $array): array + { + if (count(array_filter(array_keys($array), 'is_string')) === 0) { + ksort($array); + } + foreach ($array as $key => $value) { + if (is_array($value) && !empty($value)) { + $array[$key] = self::sortNumericArrayKeysRecursive($value); + } + } + return $array; + } + + /** + * Reindex keys from the current nesting level if all keys within + * the current nesting level are integers. + * + * @param array $array + * @return array + * @internal + */ + public static function reIndexNumericArrayKeysRecursive(array $array): array + { + if (count(array_filter(array_keys($array), 'is_string')) === 0) { + $array = array_values($array); + } + foreach ($array as $key => $value) { + if (is_array($value) && !empty($value)) { + $array[$key] = self::reIndexNumericArrayKeysRecursive($value); + } + } + return $array; + } + + /** + * Recursively remove keys if their value are NULL. + * + * @param array $array + * @return array the modified array + * @internal + */ + public static function removeNullValuesRecursive(array $array): array + { + $result = $array; + foreach ($result as $key => $value) { + if (is_array($value)) { + $result[$key] = self::removeNullValuesRecursive($value); + } elseif ($value === null) { + unset($result[$key]); + } + } + return $result; + } + + /** + * Recursively translate values. + * + * @param array $array + * @return array the modified array + * @internal + */ + public static function stripTagsFromValuesRecursive(array $array): array + { + $result = $array; + foreach ($result as $key => $value) { + if (is_array($value)) { + $result[$key] = self::stripTagsFromValuesRecursive($value); + } else { + if (!is_bool($value)) { + $result[$key] = strip_tags($value); + } + } + } + return $result; + } + + /** + * Recursively convert 'true' and 'false' strings to boolen values. + * + * @param array $array + * @return array the modified array + * @internal + */ + public static function convertBooleanStringsToBooleanRecursive(array $array): array + { + $result = $array; + foreach ($result as $key => $value) { + if (is_array($value)) { + $result[$key] = self::convertBooleanStringsToBooleanRecursive($value); + } else { + if ($value === 'true') { + $result[$key] = true; + } elseif ($value === 'false') { + $result[$key] = false; + } + } + } + return $result; + } +} diff --git a/typo3/sysext/form/Classes/Utility/ElementCounter.php b/typo3/sysext/form/Classes/Utility/ElementCounter.php deleted file mode 100644 index 002dabfbb1a2..000000000000 --- a/typo3/sysext/form/Classes/Utility/ElementCounter.php +++ /dev/null @@ -1,34 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Utility; - -/* - * 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! - */ - -class ElementCounter implements \TYPO3\CMS\Core\SingletonInterface -{ - /** - * @var int - */ - protected $elementCounter = 0; - - /** - * Raise the element counter by one - * - * @return int - */ - public function getElementId() - { - $this->elementCounter++; - return $this->elementCounter; - } -} diff --git a/typo3/sysext/form/Classes/Utility/FormUtility.php b/typo3/sysext/form/Classes/Utility/FormUtility.php deleted file mode 100644 index a0c49999706b..000000000000 --- a/typo3/sysext/form/Classes/Utility/FormUtility.php +++ /dev/null @@ -1,283 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Utility; - -/* - * 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\Core\Utility\GeneralUtility; -use TYPO3\CMS\Form\Domain\Model\Configuration; - -/** - * A utility for the form - */ -class FormUtility -{ - /** - * @param Configuration $configuration - * @return FormBuilder - */ - public static function create(Configuration $configuration) - { - /** @var FormBuilder $formBuilder */ - $formUtility = self::getObjectManager()->get(self::class); - $formUtility->setConfiguration($configuration); - return $formUtility; - } - - /** - * @var Configuration - */ - protected $configuration; - - /** - * @param Configuration $configuration - */ - public function setConfiguration(Configuration $configuration) - { - $this->configuration = $configuration; - } - - /** - * Render TypoScript values - * There are different variants. It is possible that the TypoScript - * which we want to render looks like this: - * - * array( - * 'message' => 'TEXT', - * 'message.' => array( - * 'value' => 'blah' - * ) - * ) - * - * or in "short syntax" (the wizard writes some syntax partly) - * - * array( - * 'message.' => array( - * 'value' => 'blah' - * ) - * ) - * - * or it is simply a string. - * - * Furthermore we have 2 modes: - * - contentelement rendering is allowed - * - or contentelement rendering is not allowed - * - * This method will take care of all scenarios and provide some - * fallbacks. - * Call this method always in the following way: - * - * renderItem( - * $typoscript['itemToRender.'], - * $typoscript['itemToRender'], - * $optionalDefaultMessage - * ) - * - * You dont have to handle if is $typoscript['itemToRender.'] is - * set or not. This function determines this. - * This allows us to get the value of a TypoScript construct - * without knowing about "short syntax", only a string, a cObject, - * if cObject rendering is allowed and so on. - * - * If contentelement rendering is allowed: - * If $type and $configuration are set - * render as an cObject. - * - * If $type is set but $configuration is empty - * only return the value of $type. - * - * If $type is empty and $configuration is an array ("short syntax") - * render the $configuration as content type TEXT. - * - * If $type is empty and $configuration is a string - * render the value of $configuration like - * 10 = TEXT 10.value = $configuration. - * - * If $type is empty and $configuration is empty - * return the $defaultMessage. - * - * If contentelement rendering is not allowed: - * If $type is set but $configuration is empty - * only return the value of $type. - * - * If $type is set and $configuration['value'] isset - * return the value of $configuration['value']. - * - * If $type is set and $configuration['value'] is not set - * return the value of $defaultMessage. - * - * If $type is empty and $configuration['value'] isset - * return the value of $configuration['value']. - * - * If $type is empty and $configuration['value'] is not set - * return the value of $defaultMessage. - * - * @param mixed $configuration a string or a TypoScript array - * @param NULL|string $type cObject type or simply a string value - * @param string $defaultMessage - * @return string - */ - public function renderItem($configuration, $type = null, $defaultMessage = '') - { - if ($this->configuration->getContentElementRendering()) { - $renderedMessage = null; - if ($type !== null) { - if (is_array($configuration)) { - /* Direct cObject rendering */ - $value = $configuration; - } else { - /* got only a string, no rendering required */ - $renderedMessage = $type; - } - } else { - if ($configuration !== null) { - /* Render the "short syntax" - * The wizard write things like label.value - * The previous version of EXT:form interpreted this - * as a TEXT content object, so we do the same - * */ - $type = 'TEXT'; - if (is_array($configuration)) { - $value = $configuration; - } else { - $value['value'] = $configuration; - } - } else { - /* return the default message - * If $type === NULL and $configuration === NULL - * return the default message (if set). - * */ - $renderedMessage = $defaultMessage; - } - } - if ($renderedMessage === null) { - $renderedMessage = $GLOBALS['TSFE']->cObj->cObjGetSingle( - $type, - $value - ); - } - } else { - if ($type !== null) { - if ($configuration !== null) { - /* the wizard write things like label.value = some text - * so we need the handle that, even content object rendering - * is not allowed. - * */ - if (isset($configuration['value'])) { - $renderedMessage = $configuration['value']; - } else { - $renderedMessage = $defaultMessage; - } - } else { - // string, no rendering required - $renderedMessage = $type; - } - } else { - $renderedMessage = $defaultMessage; - if ( - is_array($configuration) - && isset($configuration['value']) - ) { - $renderedMessage = $configuration['value']; - } - } - } - return $renderedMessage; - } - - /** - * Render array values recursively as cObjects using the - * method renderItem. - * - * @param array $arrayToRender - * @return array - */ - public function renderArrayItems(array &$arrayToRender = []) - { - foreach ($arrayToRender as $attributeName => &$attributeValue) { - $attributeNameWithoutDot = rtrim($attributeName, '.'); - if ( - isset($arrayToRender[$attributeNameWithoutDot]) - && isset($arrayToRender[$attributeNameWithoutDot . '.']) - ) { - $attributeValue = $this->renderItem( - $arrayToRender[$attributeNameWithoutDot . '.'], - $arrayToRender[$attributeNameWithoutDot] - ); - unset($arrayToRender[$attributeNameWithoutDot . '.']); - } elseif ( - !isset($arrayToRender[$attributeNameWithoutDot]) - && isset($arrayToRender[$attributeNameWithoutDot . '.']) - ) { - $this->renderArrayItems($attributeValue); - } - } - } - - /** - * If the name is not defined it is automatically generated - * using the following syntax: id-{element_counter} - * The name attribute will be transformed if it contains some - * non allowed characters: - * - spaces are changed into hyphens - * - remove all characters except a-z A-Z 0-9 _ - - * - * @param string $name - * @return string - */ - public function sanitizeNameAttribute($name) - { - if (!empty($name)) { - // Change spaces into hyphens - $name = preg_replace('/\\s/', '-', $name); - // Remove non-word characters - $name = preg_replace('/[^a-zA-Z0-9_\\-]+/', '', $name); - } - return $name; - } - - /** - * If the id is not defined it is automatically generated - * using the following syntax: field-{element_counter} - * The id attribute will be transformed if it contains some - * non allowed characters: - * - spaces are changed into hyphens - * - if the id start with a integer then transform it to field-{integer} - * - remove all characters expect a-z A-Z 0-9 _ - : . - * - * @param string $id - * @return string - */ - public function sanitizeIdAttribute($id) - { - if (!empty($id)) { - // Change spaces into hyphens - $attribute = preg_replace('/\\s/', '-', $id); - // Change first non-letter to field- - if (preg_match('/^([^a-zA-Z]{1})/', $attribute)) { - $id = 'field-' . $attribute; - } - // Remove non-word characters - $id = preg_replace('/([^a-zA-Z0-9_:\\-\\.]*)/', '', $id); - } - return $id; - } - - /** - * @return \TYPO3\CMS\Extbase\Object\ObjectManager - */ - public static function getObjectManager() - { - return GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class); - } -} diff --git a/typo3/sysext/form/Classes/Utility/SessionUtility.php b/typo3/sysext/form/Classes/Utility/SessionUtility.php deleted file mode 100644 index 00f9c5ae734a..000000000000 --- a/typo3/sysext/form/Classes/Utility/SessionUtility.php +++ /dev/null @@ -1,160 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Utility; - -/* - * 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\Core\SingletonInterface; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController; - -/** - * A session utility - */ -class SessionUtility implements SingletonInterface -{ - /** - * Session data - * - * @var array - */ - protected $sessionData = []; - - /** - * Prefix for the session - * - * @var string - */ - protected $formPrefix = ''; - - /** - * @var TypoScriptFrontendController - */ - protected $frontendController; - - /** - * Constructor - */ - public function __construct() - { - $this->frontendController = $GLOBALS['TSFE']; - } - - /** - * Store the form input in a session - * - * @param string $formPrefix - * @return void - */ - public function initSession($formPrefix = '') - { - $this->setFormPrefix($formPrefix); - if ($this->frontendController->loginUser) { - $this->sessionData = $this->frontendController->fe_user->getKey('user', $this->formPrefix); - } else { - $this->sessionData = $this->frontendController->fe_user->getKey('ses', $this->formPrefix); - } - } - - /** - * Store the form input in a session - * - * @return void - */ - public function storeSession() - { - if ($this->frontendController->loginUser) { - $this->frontendController->fe_user->setKey('user', $this->formPrefix, $this->getSessionData()); - } else { - $this->frontendController->fe_user->setKey('ses', $this->formPrefix, $this->getSessionData()); - } - $this->frontendController->storeSessionData(); - } - - /** - * Destroy the session data for the form - * - * @return void - */ - public function destroySession() - { - $this->removeFiles(); - if ($this->frontendController->loginUser) { - $this->frontendController->fe_user->setKey('user', $this->formPrefix, null); - } else { - $this->frontendController->fe_user->setKey('ses', $this->formPrefix, null); - } - $this->frontendController->storeSessionData(); - } - - /** - * Set the session Data by $key - * - * @param string $key - * @param string $value - * @return void - */ - public function setSessionData($key, $value) - { - $this->sessionData[$key] = $value; - } - - /** - * Retrieve a member of the $sessionData variable - * - * If no $key is passed, returns the entire $sessionData array - * - * @param string $key Parameter to search for - * @param mixed $default Default value to use if key not found - * @return mixed Returns NULL if key does not exist - */ - public function getSessionData($key = null, $default = null) - { - if ($key === null) { - return $this->sessionData; - } - return isset($this->sessionData[$key]) ? $this->sessionData[$key] : $default; - } - - /** - * Set the form prefix - * - * @param string $formPrefix - * @return array - */ - public function setFormPrefix($formPrefix) - { - $this->formPrefix = $formPrefix; - } - - /** - * Remove uploaded files from the typo3temp - * - * @return void - */ - protected function removeFiles() - { - $sessionData = $this->getSessionData(); - if (is_array($sessionData)) { - foreach ($sessionData as $fieldName => $values) { - if (is_array($values)) { - foreach ($values as $file) { - if (isset($file['tempFilename'])) { - GeneralUtility::unlink_tempfile($file['tempFilename']); - } - } - } - } - } - } -} diff --git a/typo3/sysext/form/Classes/Utility/TypoScriptToJsonConverter.php b/typo3/sysext/form/Classes/Utility/TypoScriptToJsonConverter.php deleted file mode 100644 index 6ef9881b0205..000000000000 --- a/typo3/sysext/form/Classes/Utility/TypoScriptToJsonConverter.php +++ /dev/null @@ -1,212 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Utility; - -/* - * 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\Core\Utility\ArrayUtility; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Form\Domain\Model\Json\AbstractJsonElement; - -/** - * Typoscript to JSON converter - * - * Takes the incoming TypoScript and converts it to JSON. - */ -class TypoScriptToJsonConverter -{ - protected $registeredElementNames = [ - 'BUTTON', - 'CHECKBOX', - 'CHECKBOXGROUP', - 'FIELDSET', - 'FILEUPLOAD', - 'HEADER', - 'HIDDEN', - 'OPTGROUP', - 'OPTION', - 'PASSWORD', - 'RADIO', - 'RADIOGROUP', - 'RESET', - 'SELECT', - 'SUBMIT', - 'TEXTAREA', - 'TEXTBLOCK', - 'TEXTLINE' - ]; - - /** - * @var array - */ - protected $nameMapping = [ - 'checkboxgroup' => 'CheckboxGroup', - 'radiogroup' => 'RadioGroup', - ]; - - /** - * @var array - */ - protected $validationRules; - - /** - * Convert TypoScript string to JSON - * - * @param array $typoscript TypoScript string containing all configuration for the form - * @return TYPO3\CMS\Form\Domain\Model\Json\FormJsonElement|false The JSON for the form - */ - public function convert(array $typoscript) - { - $this->setValidationRules($typoscript); - $jsonObject = $this->createElement('form', $typoscript); - return $jsonObject; - } - - /** - * Create element by loading class - * and instantiating the object - * - * @param string $class Type of element - * @param array $arguments Configuration array - * @return AbstractJsonElement - * @throws \RuntimeException - */ - public function createElement($class, array $arguments = []) - { - $class = strtolower((string)$class); - if (!empty($this->nameMapping[$class])) { - $class = $this->nameMapping[$class]; - } - $className = 'TYPO3\\CMS\\Form\\Domain\\Model\Json\\' . ucfirst($class) . 'JsonElement'; - $this->addValidationRules($arguments); - - if (!class_exists($className)) { - throw new \RuntimeException('Class "' . $className . '" does not exist', 1440779351); - } - - /** @var $object AbstractJsonElement */ - $object = GeneralUtility::makeInstance($className); - $object->setParameters($arguments); - if ($object->childElementsAllowed()) { - $this->getChildElementsByIntegerKey($object, $arguments); - } - return $object; - } - - /** - * Rendering of a "numerical array" of Form objects from TypoScript - * Creates new object for each element found - * - * @param AbstractJsonElement $parentElement Parent model object - * @param array $typoscript Configuration array - * @return void - */ - protected function getChildElementsByIntegerKey(AbstractJsonElement $parentElement, array $typoscript) - { - if (is_array($typoscript)) { - $keys = ArrayUtility::filterAndSortByNumericKeys($typoscript); - foreach ($keys as $key) { - $class = $typoscript[$key]; - if ((int)$key && strpos($key, '.') === false) { - if (isset($typoscript[$key . '.'])) { - $elementArguments = $typoscript[$key . '.']; - } else { - $elementArguments = []; - } - $this->setElementType($parentElement, $class, $elementArguments); - } - } - } - } - - /** - * Set the element type of the object - * - * Checks if the typoscript object is part of the FORM or has a predefined - * class for name or header object - * - * @param AbstractJsonElement $parentElement The parent object - * @param string $class A predefined class - * @param array $arguments Configuration array - * @return void - */ - private function setElementType(AbstractJsonElement $parentElement, $class, array $arguments) - { - if (in_array($class, $this->registeredElementNames)) { - if (strstr($arguments['class'], 'predefined-name')) { - $class = 'NAME'; - } - $this->addElement($parentElement, $class, $arguments); - } - } - - /** - * Add child object to this element - * - * @param AbstractJsonElement $parentElement The parent object - * @param string $class Type of element - * @param array $arguments Configuration array - * @return void - */ - public function addElement(AbstractJsonElement $parentElement, $class, array $arguments) - { - try { - $element = $this->createElement($class, $arguments); - $parentElement->addElement($element); - } catch (\RuntimeException $exception) { - // Catch missing classes or element types - // There are elements that can be used the - // TypoScript-like declaration, which don't - // have a counterpart in the ExtJS wizard. - } - } - - /** - * Set the validation rules - * - * @param array $typoscript Configuration array - * @return void - */ - protected function setValidationRules(array $typoscript) - { - if (isset($typoscript['rules.']) && is_array($typoscript['rules.'])) { - $this->validationRules = $typoscript['rules.']; - } - } - - /** - * Add validation rules to an element if available - * - * In TypoScript the validation rules belong to the form and are connected - * to the elements by name. However, in the wizard, they are added to the - * element for usability - * - * @param array $arguments The element arguments - * @return void - */ - protected function addValidationRules(array &$arguments) - { - if (!empty($this->validationRules) && isset($arguments['name'])) { - foreach ($this->validationRules as $key => $ruleName) { - if ((int)$key && strpos($key, '.') === false) { - if (isset($this->validationRules[$key . '.'])) { - $ruleConfiguration = $this->validationRules[$key . '.']; - if (isset($ruleConfiguration['element']) && $ruleConfiguration['element'] === $arguments['name']) { - $arguments['validation'][$ruleName] = $ruleConfiguration; - } - } - } - } - } - } -} diff --git a/typo3/sysext/form/Classes/View/Wizard/Element/FormWizardElement.php b/typo3/sysext/form/Classes/View/Wizard/Element/FormWizardElement.php deleted file mode 100644 index c219d9b7173f..000000000000 --- a/typo3/sysext/form/Classes/View/Wizard/Element/FormWizardElement.php +++ /dev/null @@ -1,230 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\View\Wizard\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\AbstractFormElement; -use TYPO3\CMS\Core\Utility\GeneralUtility; -use TYPO3\CMS\Core\Utility\StringUtility; -use TYPO3\CMS\Extbase\Service\TypoScriptService; -use TYPO3\CMS\Form\Domain\Repository\ContentRepository; -use TYPO3\CMS\Form\Utility\TypoScriptToJsonConverter; - -/** - * form Wizard widget - */ -class FormWizardElement extends AbstractFormElement -{ - /** - * @var array - */ - protected $resultArray; - - /** - * Store initialized resultArray - */ - protected function initializeResultArray() - { - $this->resultArray = parent::initializeResultArray(); - } - - /** - * @return int - */ - protected function getCurrentPageId() - { - // $this->data used to be globalOptions - return (int)$this->data['inlineFirstPid']; - } - - /** - * @return int - */ - protected function getCurrentUid() - { - return (int)$this->data['databaseRow']['uid']; - } - - /** - * @return array - */ - protected function getPlainPageWizardModTsConfigSettingsProperties() - { - $settings = $this->data['pageTsConfig']['mod.']['wizards.']['form.']; - return $this->getTypoScriptService()->convertTypoScriptArrayToPlainArray($settings); - } - - /** - * Gets the repository object. - * - * @return ContentRepository - */ - protected function getRepository() - { - return GeneralUtility::makeInstance(ContentRepository::class); - } - - /** - * Read and convert the content record to JSON - * - * @see \TYPO3\CMS\Form\Domain\Repository\ContentRepository::getRecordAsJson - * @return TYPO3\CMS\Form\Domain\Model\Json\FormJsonElement|false The JSON object if record exists, FALSE if not - */ - protected function getRecordAsJson() - { - $json = false; - $record = $this->getRepository()->getRecord($this->getCurrentUid(), 'tt_content'); - if ($record) { - $typoscript = $record->getTyposcript(); - /** @var $converter TypoScriptToJsonConverter */ - $converter = GeneralUtility::makeInstance(TypoScriptToJsonConverter::class); - $json = $converter->convert($typoscript); - } - return $json; - } - - /** - * @return string - */ - protected function getAjaxUrl() - { - /** - * @see TYPO3.CMS/src/typo3/sysext/backend/Classes/Form/Element/AbstractFormElement.php:267 for wizard type=popup - */ - $parameterArray = $this->data['parameterArray']; - $table = $this->data['tableName']; - $fieldName = $this->data['fieldName']; - $row = $this->data['databaseRow']; - $itemName = $parameterArray['itemFormElName']; - // Resolving script filename and setting URL. - - $params = []; - $params['fieldConfig'] = $parameterArray['fieldConf']; - $params['table'] = $table; - $params['uid'] = $row['uid']; - $params['pid'] = $row['pid']; - $params['field'] = $fieldName; - - $params['formName'] = 'editform'; - $params['itemName'] = $itemName; - $params['hmac'] = GeneralUtility::hmac($params['formName'] . $params['itemName'], 'wizard_js'); - - return GeneralUtility::implodeArrayForUrl('', ['P' => $params]); - } - - /** - * locallang files for return array - * - * @return array strings - */ - protected function getLocalization() - { - $wizardLabels = 'EXT:form/Resources/Private/Language/locallang_wizard.xlf'; - $controllerLabels = 'EXT:form/Resources/Private/Language/locallang.xlf'; - return [$controllerLabels, $wizardLabels]; - } - - /** - * @param array $settings - * @return string - */ - protected function resultAddWizardSettingsJson(array $settings) - { - $recordJson = $this->getRecordAsJson(); - $settings['Configuration'] = $recordJson; - $settings['ajaxUrl'] = $this->getAjaxUrl(); - $settingsCommand = 'TYPO3.Form.Wizard.Settings = ' . json_encode($settings) . ';'; - // enhance global variable for requireJs shim "exports: 'TYPO3.Form.Wizard.Settings'" to work - $this->resultArray['additionalJavaScriptPost'][] = ';TYPO3.Form = TYPO3.Form || {Wizard:{}};'; - $this->resultArray['additionalJavaScriptPost'][] = - ';define("TYPO3/CMS/Form/Wizard/Settings", function() {' - . "\n" . ' TYPO3.Form.Wizard.Settings = ' . json_encode($settings) . ';' - . "\n" . ' return TYPO3.Form.Wizard.Settings;' - . "\n" . '});'; - return $settingsCommand; - } - - /** - * Load the wizards css - */ - protected function resultAddWizardCss() - { - $this->resultArray['stylesheetFiles'][] = 'EXT:form/Resources/Public/Css/form.css'; - } - - /** - * @return array - */ - public function render() - { - $this->initializeResultArray(); - - /** @var \TYPO3\CMS\Core\Authentication\BackendUserAuthentication $beUser */ - $beUser = $GLOBALS['BE_USER']; - /** @var string $showWizardByDefault */ - $showWizardByDefault = $beUser->getTSConfigVal('setup.default.tx_form.showWizardByDefault'); - if ((int)$showWizardByDefault == 0) { - $content = ''; - $record = $this->getRepository()->getRecord($this->getCurrentUid(), 'tt_content'); - if ($record) { - $content = $record->getBodytext(); - } - - $id = StringUtility::getUniqueId('formengine-textarea-'); - $this->resultArray['html'] = '<textarea id="formengine-textarea-' . $id . '"' - . ' class="form-control t3js-formengine-textarea formengine-textarea" wrap="off"' - . ' onchange="TBE_EDITOR.fieldChanged(\'tt_content\',\'' . $this->getCurrentUid() . '\',\'bodytext\',\'data[tt_content][' . $this->getCurrentUid() . '][bodytext]\');"' - . ' rows="15" style="" name="data[tt_content][' . $this->getCurrentUid() . '][bodytext]">' . $content . '</textarea>'; - return $this->resultArray; - } - - $this->resultAddWizardCss(); - $this->resultArray['additionalInlineLanguageLabelFiles'] += $this->getLocalization(); - $settings = $this->getPlainPageWizardModTsConfigSettingsProperties(); - $settingsCommand = $this->resultAddWizardSettingsJson($settings); - $this->resultArray['requireJsModules'][] = [ - 'TYPO3/CMS/Form/Wizard' => "function(){\n" - //. "\t" . 'console.log(this, arguments);' . "\n" - . "\t" . $settingsCommand . "\n" - . '}' - ]; - $attributes = []; - $attributes['id'] = StringUtility::getUniqueId('formengine-form-wizard-'); - /** - * @see TYPO3.CMS/src/typo3/sysext/backend/Classes/Form/Element/AbstractFormElement.php:267 for wizard type=popup - */ - $parameterArray = $this->data['parameterArray']; - $attributes['name'] = $parameterArray['itemFormElName']; - - $attributeString = ''; - foreach ($attributes as $attributeName => $attributeValue) { - $attributeString .= ' ' . $attributeName . '="' . htmlspecialchars($attributeValue) . '"'; - } - - $input = '<input ' . $attributeString . ' type="hidden" />' . "\n"; - $content = $input . '<div id="form-wizard-element"></div>'; - $this->resultArray['html'] = '<div id="form-wizard-element-container" rel="' . $attributes['id'] . '">' - . "\n" . $content - . "\n" . '</div>'; - return $this->resultArray; - } - - /** - * @return TypoScriptService - */ - protected function getTypoScriptService() - { - return GeneralUtility::makeInstance(TypoScriptService::class); - } -} diff --git a/typo3/sysext/form/Classes/ViewHelpers/AggregateSelectOptionsViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/AggregateSelectOptionsViewHelper.php deleted file mode 100644 index 92b78094d7f9..000000000000 --- a/typo3/sysext/form/Classes/ViewHelpers/AggregateSelectOptionsViewHelper.php +++ /dev/null @@ -1,116 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\ViewHelpers; - -/* - * 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\Fluid\Core\ViewHelper\AbstractViewHelper; -use TYPO3\CMS\Form\Domain\Model\Element; - -/** - * Aggregator for the select options - */ -class AggregateSelectOptionsViewHelper extends AbstractViewHelper -{ - /** - * @var array - */ - protected $options = []; - - /** - * @var array - */ - protected $selectedValues = []; - - /** - * Initializes the arguments - */ - public function initializeArguments() - { - parent::initializeArguments(); - $this->registerArgument('model', Element::class, '', true); - $this->registerArgument('returnSelectedValues', 'bool', '', false, false); - } - - /** - * @return array - */ - public function render() - { - /** @var Element $model */ - $model = $this->arguments['model']; - /** @var Element $element */ - foreach ($model->getChildElements() as $element) { - $this->createElement($element); - } - - return $this->arguments['returnSelectedValues'] ? $this->selectedValues : $this->options; - } - - /** - * @param Element $model - * @param array $optGroupData - * @return void - */ - protected function createElement(Element $model, array $optGroupData = []) - { - $this->checkElementForOptgroup($model, $optGroupData); - } - - /** - * @param Element $model - * @param array $optGroupData - * @return void - */ - protected function checkElementForOptgroup(Element $model, array $optGroupData = []) - { - if ($model->getElementType() === 'OPTGROUP') { - $optGroupData = [ - 'label' => $model->getAdditionalArgument('label'), - 'disabled' => $model->getAdditionalArgument('disabled'), - ]; - $this->getChildElements($model, $optGroupData); - } else { - $optionData = [ - 'value' => $model->getAdditionalArgument('value') ?: $model->getElementCounter(), - 'label' => $model->getAdditionalArgument('text'), - 'selected' => $model->getAdditionalArgument('selected'), - ]; - - if (!empty($optionData['selected'])) { - $this->selectedValues[] = $optionData['value']; - } - - if (count($optGroupData)) { - $optGroupLabel = $optGroupData['label']; - $this->options[$optGroupLabel]['disabled'] = $optGroupData['disabled']; - $this->options[$optGroupLabel]['options'][] = $optionData; - } else { - $this->options[] = $optionData; - } - } - } - - /** - * @param Element $model - * @param array $optGroupData - * @return void - */ - protected function getChildElements(Element $model, array $optGroupData = []) - { - /** @var Element $element */ - foreach ($model->getChildElements() as $element) { - $this->createElement($element, $optGroupData); - } - } -} diff --git a/typo3/sysext/form/Classes/ViewHelpers/Be/PageRendererViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/Be/PageRendererViewHelper.php new file mode 100644 index 000000000000..0c32752b168e --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/Be/PageRendererViewHelper.php @@ -0,0 +1,56 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers\Be; + +/* + * 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\Fluid\ViewHelpers\Be\PageRendererViewHelper as FluidPageRendererViewHelper; + +/** + * Extends the FluidPageRendererViewHelper + * Add the additional argument 'addInlineSettings' to add settings to + * the TYPO3 javascript inline setting + * + * Scope: backend + * @internal + */ +class PageRendererViewHelper extends FluidPageRendererViewHelper +{ + + /** + * Initialize arguments. + * + * @return void + * @internal + */ + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerArgument('addInlineSettings', 'array', 'Adds Javascript Inline Setting'); + } + + /** + * @return void + * @internal + */ + public function render() + { + $addInlineSettings = $this->arguments['addInlineSettings']; + if (is_array($addInlineSettings) && count($addInlineSettings) > 0) { + $this->pageRenderer->addInlineSettingArray(null, $addInlineSettings); + } + + parent::render(); + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/Be/RenderContentElementPreviewViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/Be/RenderContentElementPreviewViewHelper.php new file mode 100644 index 000000000000..a03f342b8679 --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/Be/RenderContentElementPreviewViewHelper.php @@ -0,0 +1,73 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers\Be; + +/* + * 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\Utility\BackendUtility; +use TYPO3\CMS\Backend\View\PageLayoutView; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper; +use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; +use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; + +/** + * Used by the form editor. + * Render a content element preview like the page module + * + * Scope: backend + * @internal + */ +class RenderContentElementPreviewViewHelper extends AbstractViewHelper +{ + use CompileWithRenderStatic; + + /** + * As this ViewHelper renders HTML, the output must not be escaped. + * + * @var bool + */ + protected $escapeOutput = false; + + /** + * Initialize arguments. + * + * @return void + * @internal + */ + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerArgument('contentElementUid', 'int', 'The uid of a content element'); + } + + /** + * @param array $arguments + * @param callable|\Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * @return string + * @internal + */ + public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) + { + $content = ''; + $contentElementUid = $arguments['contentElementUid']; + $contentRecord = BackendUtility::getRecord('tt_content', $contentElementUid); + if (!empty($contentRecord)) { + $pageLayoutView = GeneralUtility::makeInstance(PageLayoutView::class); + $content = $pageLayoutView->tt_content_drawItem($contentRecord); + } + return $content; + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/Form/CheckboxViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/Form/CheckboxViewHelper.php new file mode 100644 index 000000000000..400fcbacc5c9 --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/Form/CheckboxViewHelper.php @@ -0,0 +1,41 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers\Form; + +/* + * 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\Fluid\ViewHelpers\Form\CheckboxViewHelper as FluidCheckboxViewHelper; + +/** + * Fix a bug within the fluid checkbox viewhelper. + * It is not possible to pass a bool value for the 'multiple' option + * + * Scope: frontend + * @api + */ +class CheckboxViewHelper extends FluidCheckboxViewHelper +{ + + /** + * Renders the checkbox. + * + * @return string + * @api + */ + public function render() + { + $this->arguments['multiple'] = (bool)$this->arguments['multiple']; + return parent::render(); + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/Form/DatePickerViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/Form/DatePickerViewHelper.php new file mode 100644 index 000000000000..b586e4b26d83 --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/Form/DatePickerViewHelper.php @@ -0,0 +1,161 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers\Form; + +/* + * 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\Extbase\Property\PropertyMapper; +use TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper; + +/** + * Display a jQuery date picker. + * + * Note: Requires jQuery UI to be included on the page. + * + * Scope: frontend + * @api + */ +class DatePickerViewHelper extends AbstractFormFieldViewHelper +{ + + /** + * @var string + */ + protected $tagName = 'input'; + + /** + * @var \TYPO3\CMS\Extbase\Property\PropertyMapper + */ + protected $propertyMapper; + + /** + * @param \TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper + * @return void + * @internal + */ + public function injectPropertyMapper(\TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper) + { + $this->propertyMapper = $propertyMapper; + } + + /** + * Initialize the arguments. + * + * @return void + * @api + */ + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerTagAttribute('size', 'int', 'The size of the input field'); + $this->registerTagAttribute('placeholder', 'string', 'Specifies a short hint that describes the expected value of an input element'); + $this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this view helper', false, 'f3-form-error'); + $this->registerArgument('initialDate', 'string', 'Initial date (@see http://www.php.net/manual/en/datetime.formats.php for supported formats)'); + $this->registerArgument('enableDatePicker', 'bool', 'Enable the Datepicker', false, true); + $this->registerArgument('dateFormat', 'string', 'The date format', false, 'Y-m-d'); + $this->registerUniversalTagAttributes(); + } + + /** + * Renders the text field, hidden field and required javascript + * + * @return string + * @api + */ + public function render() + { + $enableDatePicker = $this->arguments['enableDatePicker']; + $dateFormat = $this->arguments['dateFormat']; + + $name = $this->getName(); + $this->registerFieldNameForFormTokenGeneration($name); + + $this->tag->addAttribute('type', 'date'); + $this->tag->addAttribute('name', $name . '[date]'); + if ($enableDatePicker) { + $this->tag->addAttribute('readonly', true); + } + $date = $this->getSelectedDate(); + if ($date !== null) { + $this->tag->addAttribute('value', $date->format($dateFormat)); + } + + if ($this->hasArgument('id')) { + $id = $this->arguments['id']; + } else { + $id = 'field' . md5(uniqid()); + $this->tag->addAttribute('id', $id); + } + $this->setErrorClassAttribute(); + $content = ''; + $content .= $this->tag->render(); + $content .= '<input type="hidden" name="' . $name . '[dateFormat]" value="' . htmlspecialchars($dateFormat) . '" />'; + + if ($enableDatePicker) { + $datePickerDateFormat = $this->convertDateFormatToDatePickerFormat($dateFormat); + $this->templateVariableContainer->add('datePickerDateFormat', $datePickerDateFormat); + $content .= $this->renderChildren(); + $this->templateVariableContainer->remove('datePickerDateFormat'); + } + return $content; + } + + /** + * @return null|\DateTime + */ + protected function getSelectedDate() + { + $fluidFormRenderer = $this->viewHelperVariableContainer->getView(); + $formRuntime = $fluidFormRenderer->getFormRuntime(); + $formState = $formRuntime->getFormState(); + + $date = $formRuntime[$this->arguments['property']]; + if ($date instanceof \DateTime) { + return $date; + } + if ($date !== null) { + $date = $this->propertyMapper->convert($date, 'DateTime'); + if (!$date instanceof \DateTime) { + return null; + } + return $date; + } + if ($this->hasArgument('initialDate')) { + return new \DateTime($this->arguments['initialDate']); + } + } + + /** + * @param string $dateFormat + * @return string + */ + protected function convertDateFormatToDatePickerFormat(string $dateFormat): string + { + $replacements = [ + 'd' => 'dd', + 'D' => 'D', + 'j' => 'o', + 'l' => 'DD', + + 'F' => 'MM', + 'm' => 'mm', + 'M' => 'M', + 'n' => 'm', + + 'Y' => 'yy', + 'y' => 'y' + ]; + return strtr($dateFormat, $replacements); + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/Form/TimePickerViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/Form/TimePickerViewHelper.php new file mode 100644 index 000000000000..9b3d0fc21cda --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/Form/TimePickerViewHelper.php @@ -0,0 +1,152 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers\Form; + +/* + * 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\Extbase\Property\PropertyMapper; +use TYPO3\CMS\Fluid\ViewHelpers\Form\AbstractFormFieldViewHelper; + +/** + * Displays two select-boxes for hour and minute selection. + * + * Scope: frontend + * @api + */ +class TimePickerViewHelper extends AbstractFormFieldViewHelper +{ + + /** + * @var string + */ + protected $tagName = 'select'; + + /** + * @var \TYPO3\CMS\Extbase\Property\PropertyMapper + */ + protected $propertyMapper; + + /** + * @param \TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper + * @return void + * @internal + */ + public function injectPropertyMapper(\TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper) + { + $this->propertyMapper = $propertyMapper; + } + + /** + * Initialize the arguments. + * + * @return void + * @internal + */ + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerTagAttribute('size', 'int', 'The size of the select field'); + $this->registerTagAttribute('placeholder', 'string', 'Specifies a short hint that describes the expected value of an input element'); + $this->registerTagAttribute('disabled', 'string', 'Specifies that the select element should be disabled when the page loads'); + $this->registerArgument('errorClass', 'string', 'CSS class to set if there are errors for this view helper', false, 'f3-form-error'); + $this->registerArgument('initialDate', 'string', 'Initial time (@see http://www.php.net/manual/en/datetime.formats.php for supported formats)'); + $this->registerUniversalTagAttributes(); + } + + /** + * Renders the select fields for hour & minute + * + * @return string + * @api + */ + public function render() + { + $name = $this->getName(); + $this->registerFieldNameForFormTokenGeneration($name); + + $this->tag->addAttribute('name', $name . '[hour]'); + $date = $this->getSelectedDate(); + $this->setErrorClassAttribute(); + + $content = ''; + $content .= $this->buildHourSelector($date); + $content .= $this->buildMinuteSelector($date); + return $content; + } + + /** + * @return \DateTime + */ + protected function getSelectedDate(): \DateTime + { + $fluidFormRenderer = $this->viewHelperVariableContainer->getView(); + $formRuntime = $fluidFormRenderer->getFormRuntime(); + + $date = $formRuntime[$this->arguments['property']]; + if ($date instanceof \DateTime) { + return $date; + } + if ($date !== null) { + $date = $this->propertyMapper->convert($date, 'DateTime'); + if (!$date instanceof \DateTime) { + return null; + } + return $date; + } + if ($this->hasArgument('initialDate')) { + return new \DateTime($this->arguments['initialDate']); + } + } + + /** + * @param \DateTime $date + * @return string + */ + protected function buildHourSelector(\DateTime $date = null): string + { + $value = $date !== null ? $date->format('H') : null; + $hourSelector = clone $this->tag; + $hourSelector->addAttribute('name', sprintf('%s[hour]', $this->getName())); + $options = ''; + foreach (range(0, 23) as $hour) { + $hour = str_pad($hour, 2, '0', STR_PAD_LEFT); + $selected = $hour === $value ? ' selected="selected"' : ''; + $options .= '<option value="' . $hour . '"' . $selected . '>' . $hour . '</option>'; + } + $hourSelector->setContent($options); + return $hourSelector->render(); + } + + /** + * @param \DateTime $date + * @return string + */ + protected function buildMinuteSelector(\DateTime $date = null): string + { + $value = $date !== null ? $date->format('i') : null; + $minuteSelector = clone $this->tag; + if ($this->hasArgument('id')) { + $minuteSelector->addAttribute('id', $this->arguments['id'] . '-minute'); + } + $minuteSelector->addAttribute('name', sprintf('%s[minute]', $this->getName())); + $options = ''; + foreach (range(0, 59) as $minute) { + $minute = str_pad($minute, 2, '0', STR_PAD_LEFT); + $selected = $minute === $value ? ' selected="selected"' : ''; + $options .= '<option value="' . $minute . '"' . $selected . '>' . $minute . '</option>'; + } + $minuteSelector->setContent($options); + return $minuteSelector->render(); + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/Form/UploadedResourceViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/Form/UploadedResourceViewHelper.php new file mode 100644 index 000000000000..988c3114d131 --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/Form/UploadedResourceViewHelper.php @@ -0,0 +1,133 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers\Form; + +/* + * 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\Extbase\Domain\Model\FileReference; +use TYPO3\CMS\Extbase\Property\PropertyMapper; +use TYPO3\CMS\Extbase\Security\Cryptography\HashService; +use TYPO3\CMS\Fluid\ViewHelpers\Form\UploadViewHelper; + +/** + * This ViewHelper makes the specified Image object available for its + * childNodes. + * In case the form is redisplayed because of validation errors, a previously + * uploaded image will be correctly used. + * + * Scope: frontend + * @api + */ +class UploadedResourceViewHelper extends UploadViewHelper +{ + + /** + * @var HashService + */ + protected $hashService; + + /** + * @var \TYPO3\CMS\Extbase\Property\PropertyMapper + */ + protected $propertyMapper; + + /** + * @param HashService $hashService + * @return void + * @internal + */ + public function injectHashService(\TYPO3\CMS\Extbase\Security\Cryptography\HashService $hashService) + { + $this->hashService = $hashService; + } + + /** + * @param \TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper + * @return void + * @internal + */ + public function injectPropertyMapper(\TYPO3\CMS\Extbase\Property\PropertyMapper $propertyMapper) + { + $this->propertyMapper = $propertyMapper; + } + + /** + * Initialize the arguments. + * + * @return void + * @internal + */ + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerArgument('as', 'string', ''); + $this->registerArgument('accept', 'array', 'Values for the accept attribute', false, []); + } + + /** + * @return string + * @api + */ + public function render() + { + $output = ''; + + $as = $this->arguments['as']; + $accept = $this->arguments['accept']; + $resource = $this->getUploadedResource(); + + if (!empty($accept)) { + $this->tag->addAttribute('accept', implode(',', $accept)); + } + + if ($resource !== null) { + $resourcePointerIdAttribute = ''; + if ($this->hasArgument('id')) { + $resourcePointerIdAttribute = ' id="' . htmlspecialchars($this->arguments['id']) . '-file-reference"'; + } + $resourcePointerValue = $resource->getUid(); + if ($resourcePointerValue === null) { + // Newly created file reference which is not persisted yet. + // Use the file UID instead, but prefix it with "file:" to communicate this to the type converter + $resourcePointerValue = 'file:' . $resource->getOriginalResource()->getOriginalFile()->getUid(); + } + $output .= '<input type="hidden" name="' . $this->getName() . '[submittedFile][resourcePointer]" value="' . htmlspecialchars($this->hashService->appendHmac((string)$resourcePointerValue)) . '"' . $resourcePointerIdAttribute . ' />'; + + $this->templateVariableContainer->add($as, $resource); + $output .= $this->renderChildren(); + $this->templateVariableContainer->remove($as); + } + + $output .= parent::render(); + return $output; + } + + /** + * Return a previously uploaded resource. + * Return NULL if errors occurred during property mapping for this property. + * + * @return null|FileReference + */ + protected function getUploadedResource() + { + if ($this->getMappingResultsForProperty()->hasErrors()) { + return null; + } + $resource = $this->getValueAttribute(); + if ($resource instanceof FileReference) { + return $resource; + } + return $this->propertyMapper->convert($resource, FileReference::class); + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/FormViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/FormViewHelper.php new file mode 100644 index 000000000000..d6e2e18709be --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/FormViewHelper.php @@ -0,0 +1,62 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers; + +/* + * 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\Extbase\Mvc\Web\Request; +use TYPO3\CMS\Extbase\Security\Cryptography\HashService; +use TYPO3\CMS\Fluid\ViewHelpers\FormViewHelper as FluidFormViewHelper; +use TYPO3Fluid\Fluid\Core\ViewHelper\TagBuilder; + +/** + * Custom form ViewHelper that renders the form state instead of referrer fields + * + * Scope: frontend + * @api + */ +class FormViewHelper extends FluidFormViewHelper +{ + + /** + * Renders hidden form fields for referrer information about + * the current request. + * + * @return string Hidden fields with referrer information + */ + protected function renderHiddenReferrerFields() + { + $tagBuilder = $this->objectManager->get(TagBuilder::class, 'input'); + $tagBuilder->addAttribute('type', 'hidden'); + $stateName = $this->prefixFieldName($this->arguments['object']->getFormDefinition()->getIdentifier()) . '[__state]'; + $tagBuilder->addAttribute('name', $stateName); + + $serializedFormState = base64_encode(serialize($this->arguments['object']->getFormState())); + $tagBuilder->addAttribute('value', $this->hashService->appendHmac($serializedFormState)); + return $tagBuilder->render(); + } + + /** + * We do NOT return NULL as in this case, the Form ViewHelpers do not enter $objectAccessorMode. + * However, we return the form identifier. + * + * @return string + */ + protected function getFormObjectName() + { + $fluidFormRenderer = $this->viewHelperVariableContainer->getView(); + $formRuntime = $fluidFormRenderer->getFormRuntime(); + return $formRuntime->getFormDefinition()->getIdentifier(); + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/PlainMailViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/PlainMailViewHelper.php deleted file mode 100644 index 50263817e0fb..000000000000 --- a/typo3/sysext/form/Classes/ViewHelpers/PlainMailViewHelper.php +++ /dev/null @@ -1,132 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\ViewHelpers; - -/* - * 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\Fluid\Core\ViewHelper\AbstractViewHelper; -use TYPO3\CMS\Form\Domain\Model\Element; - -/** - * A viewhelper for the plain mail view - */ -class PlainMailViewHelper extends AbstractViewHelper -{ - /** - * Initializes the arguments - */ - public function initializeArguments() - { - parent::initializeArguments(); - $this->registerArgument('labelContent', 'mixed', ''); - $this->registerArgument('content', 'mixed', ''); - $this->registerArgument('newLineAfterLabel', 'bool', '', false, false); - $this->registerArgument('indent', 'int', '', false, 0); - } - - /** - * Render the plain mail view - * - * @return string - */ - public function render() - { - $templateVariableContainer = $this->renderingContext->getViewHelperVariableContainer(); - if (!$templateVariableContainer->exists(__CLASS__, 'spaces')) { - $templateVariableContainer->add(__CLASS__, 'spaces', 0); - } - - $spaces = $templateVariableContainer->get(__CLASS__, 'spaces'); - $output = ''; - if ($this->arguments['labelContent']) { - if ($this->arguments['labelContent'] instanceof Element) { - $output = $this->getLabel($this->arguments['labelContent']); - } else { - $output = $this->arguments['labelContent']; - } - if ($this->arguments['newLineAfterLabel']) { - if ($output !== '') { - $output = str_repeat(chr(32), $spaces) . $output . LF; - } - $this->setIndent($this->arguments['indent']); - } - } - - if ($this->arguments['content']) { - if (!$this->arguments['newLineAfterLabel']) { - $this->setIndent($this->arguments['indent']); - } - if ($this->arguments['labelContent'] && !$this->arguments['newLineAfterLabel']) { - $output = $output . ': ' . $this->getValue($this->arguments['content']); - } elseif ($this->arguments['labelContent'] && $this->arguments['newLineAfterLabel']) { - $output = $output - . str_repeat(chr(32), ($spaces + 4)) - . str_replace(LF, LF . str_repeat(chr(32), ($spaces + 4)), $this->getValue($this->arguments['content'])); - } else { - $output = $this->getValue($this->arguments['content']); - } - } - - if ($this->arguments['labelContent'] || $this->arguments['content']) { - if ($output !== '' && !$this->arguments['newLineAfterLabel']) { - $output = str_repeat(chr(32), $spaces) . $output; - } - } else { - $this->setIndent($this->arguments['indent']); - } - - return $output; - } - - /** - * Get the label - * @param Element $model - * @return string - */ - protected function getLabel(Element $model) - { - $label = ''; - if ($model->getAdditionalArgument('legend')) { - $label = $model->getAdditionalArgument('legend'); - } elseif ($model->getAdditionalArgument('label')) { - $label = $model->getAdditionalArgument('label'); - } - return $label; - } - - /** - * Get the label - * - * @param string $content - * @return string - */ - protected function getValue($content) - { - return $content instanceof Element ? $content->getAdditionalArgument('value') : $content; - } - - /** - * Set the current indent level - * - * @param int $indent - * @return void - */ - public function setIndent($indent = 0) - { - $templateVariableContainer = $this->renderingContext->getViewHelperVariableContainer(); - $spaces = $templateVariableContainer->get(__CLASS__, 'spaces'); - $spaces += (int)$indent; - $templateVariableContainer->addOrUpdate(__CLASS__, 'indent', $indent); - $templateVariableContainer->addOrUpdate(__CLASS__, 'spaces', $spaces); - } -} diff --git a/typo3/sysext/form/Classes/ViewHelpers/PlainTextMailViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/PlainTextMailViewHelper.php new file mode 100644 index 000000000000..0b8e0f4efd17 --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/PlainTextMailViewHelper.php @@ -0,0 +1,79 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers; + +/* + * 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\Form\Domain\Runtime\FormRuntime; +use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; +use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; + +/** + * A viewhelper for the plain mail view + * + * Scope: frontend + * @api + */ +class PlainTextMailViewHelper extends \TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper +{ + use CompileWithRenderStatic; + + /** + * Initialize the arguments. + * + * @return void + * @internal + */ + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerArgument('formValue', 'array', 'The values from a form element', true); + $this->registerArgument('formRuntime', FormRuntime::class, 'A FormRuntime instance', true); + } + + /** + * @param array $arguments + * @param callable|\Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * @return string + * @api + */ + public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) + { + $formValue = $arguments['formValue']; + $formRuntime = $arguments['formRuntime']; + + $label = $formValue['element']->getLabel(); + $label = TranslateElementPropertyViewHelper::renderStatic( + ['element' => $formValue['element'], 'property' => 'label', 'formRuntime' => $formRuntime], + $renderChildrenClosure, + $renderingContext + ); + $processedValue = (!empty($formValue['processedValue'])) ? $formValue['processedValue'] : '-'; + $isMultiValue = $formValue['isMultiValue']; + + $label .= ': '; + if ($isMultiValue) { + $output = $label . array_shift($processedValue) . LF; + $indent = str_repeat(chr(32), (strlen($label))); + foreach ($processedValue as $multiValue) { + $output .= $indent . $multiValue; + } + } else { + $output = $label . $processedValue; + } + + return $output . LF . LF; + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/RenderAllFormValuesViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/RenderAllFormValuesViewHelper.php new file mode 100644 index 000000000000..31fe05006ea7 --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/RenderAllFormValuesViewHelper.php @@ -0,0 +1,190 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers; + +/* + * 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\Core\Resource\File; +use TYPO3\CMS\Extbase\Domain\Model\FileReference; +use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper; +use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface; +use TYPO3\CMS\Form\Domain\Model\Renderable\CompositeRenderableInterface; +use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; +use TYPO3\CMS\Form\Domain\Renderer\RendererInterface; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; + +/** + * Renders the values of a form + * + * Scope: frontend + * @api + */ +class RenderAllFormValuesViewHelper extends AbstractViewHelper +{ + + /** + * @var bool + */ + protected $escapeOutput = false; + + /** + * Initialize the arguments. + * + * @return void + * @internal + */ + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerArgument('renderable', RootRenderableInterface::class, 'A RootRenderableInterface instance', true); + $this->registerArgument('as', 'string', 'The name within the template', false, 'formValue'); + $this->registerArgument('formRuntime', FormRuntime::class, 'A FormRuntime instance', false, null); + } + + /** + * @return string the rendered form values + * @api + */ + public function render() + { + $renderable = $this->arguments['renderable']; + $as = $this->arguments['as']; + $formRuntime = $this->arguments['formRuntime']; + + if ($renderable instanceof CompositeRenderableInterface) { + $elements = $renderable->getRenderablesRecursively(); + } else { + $elements = [$renderable]; + } + + if ($formRuntime === null) { + /** @var RendererInterface $fluidFormRenderer */ + $fluidFormRenderer = $this->viewHelperVariableContainer->getView(); + $formRuntime = $fluidFormRenderer->getFormRuntime(); + } + + $output = ''; + foreach ($elements as $element) { + if (!$element instanceof FormElementInterface || $element->getType() === 'Honeypot') { + continue; + } + $value = $formRuntime[$element->getIdentifier()]; + + $formValue = [ + 'element' => $element, + 'value' => $value, + 'processedValue' => $this->processElementValue($element, $value, $formRuntime), + 'isMultiValue' => is_array($value) || $value instanceof \Iterator + ]; + $this->templateVariableContainer->add($as, $formValue); + $output .= $this->renderChildren(); + $this->templateVariableContainer->remove($as); + } + return $output; + } + + /** + * Converts the given value to a simple type (string or array) considering the underlying FormElement definition + * + * @param FormElementInterface $element + * @param mixed $value + * @param FormRuntime $formRuntime + * @return mixed + */ + protected function processElementValue(FormElementInterface $element, $value, FormRuntime $formRuntime) + { + $properties = $element->getProperties(); + if (isset($properties['options']) && is_array($properties['options'])) { + $properties['options'] = TranslateElementPropertyViewHelper::renderStatic( + ['element' => $element, 'property' => 'options', 'formRuntime' => $formRuntime], + $this->buildRenderChildrenClosure(), + $this->renderingContext + ); + if (is_array($value)) { + return $this->mapValuesToOptions($value, $properties['options']); + } else { + return $this->mapValueToOption($value, $properties['options']); + } + } + if (is_object($value)) { + return $this->processObject($element, $value); + } + return $value; + } + + /** + * Replaces the given values (=keys) with the corresponding elements in $options + * @see mapValueToOption() + * + * @param array $value + * @param array $options + * @return array + */ + protected function mapValuesToOptions(array $value, array $options): array + { + $result = []; + foreach ($value as $key) { + $result[] = $this->mapValueToOption($key, $options); + } + return $result; + } + + /** + * Replaces the given value (=key) with the corresponding element in $options + * If the key does not exist in $options, it is returned without modification + * + * @param mixed $value + * @param array $options + * @return mixed + */ + protected function mapValueToOption($value, array $options) + { + return isset($options[$value]) ? $options[$value] : $value; + } + + /** + * Converts the given $object to a string representation considering the $element FormElement definition + * + * @param FormElementInterface $element + * @param object $object + * @return string + */ + protected function processObject(FormElementInterface $element, $object): string + { + $properties = $element->getProperties(); + if ($object instanceof \DateTime) { + if (isset($properties['dateFormat'])) { + $dateFormat = $properties['dateFormat']; + if (isset($properties['displayTimeSelector']) && $properties['displayTimeSelector'] === true) { + $dateFormat .= ' H:i'; + } + } else { + $dateFormat = \DateTime::W3C; + } + return $object->format($dateFormat); + } + + if ($object instanceof File || $object instanceof FileReference) { + if ($object instanceof FileReference) { + $object = $object->getOriginalResource(); + } + return $object->getName(); + } + + if (method_exists($object, '__toString')) { + return (string)$object; + } + return 'Object [' . get_class($object) . ']'; + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/RenderRenderableViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/RenderRenderableViewHelper.php new file mode 100644 index 000000000000..b454752c4489 --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/RenderRenderableViewHelper.php @@ -0,0 +1,66 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers; + +/* + * 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\Fluid\Core\ViewHelper\AbstractViewHelper; +use TYPO3\CMS\Form\Domain\Model\Renderable\RenderableInterface; +use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; +use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; + +/** + * Render a renderable. + * + * Set the renderable into the \TYPO3\CMS\Form\Mvc\View\FormView + * and return the rendered content. + * + * Scope: frontend + * @api + */ +class RenderRenderableViewHelper extends AbstractViewHelper +{ + use CompileWithRenderStatic; + + /** + * @var bool + */ + protected $escapeOutput = false; + + /** + * Initialize the arguments. + * + * @return void + * @internal + */ + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerArgument('renderable', RenderableInterface::class, 'A RenderableInterface instance', true); + } + + /** + * @param array $arguments + * @param callable|\Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * @return string + * @public + */ + public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) + { + /** @var \TYPO3\CMS\Form\Mvc\View\FormView $view */ + $view = $renderingContext->getViewHelperVariableContainer()->getView(); + return $view->renderRenderable($arguments['renderable']); + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/RenderViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/RenderViewHelper.php new file mode 100644 index 000000000000..7846237ad319 --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/RenderViewHelper.php @@ -0,0 +1,101 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers; + +/* + * 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\Core\Utility\ArrayUtility; +use TYPO3\CMS\Extbase\Mvc\Web\Response; +use TYPO3\CMS\Fluid\Core\ViewHelper\AbstractViewHelper; +use TYPO3\CMS\Form\Domain\Factory\ArrayFormFactory; +use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface; +use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; +use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; + +/** + * Main Entry Point to render a Form into a Fluid Template + * + * Usage + * ===== + * + * <pre> + * {namespace formvh=TYPO3\CMS\Form\ViewHelpers} + * <formvh:render factoryClass="NameOfYourCustomFactoryClass" /> + * </pre> + * + * The factory class must implement {@link TYPO3\CMS\Form\Domain\Factory\FormFactoryInterface}. + * + * Scope: frontend + * @api + */ +class RenderViewHelper extends AbstractViewHelper +{ + use CompileWithRenderStatic; + + /** + * @var bool + */ + protected $escapeOutput = false; + + /** + * Initialize the arguments. + * + * @return void + * @internal + */ + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerArgument('persistenceIdentifier', 'string', 'The persistence identifier for the form.', false, null); + $this->registerArgument('factoryClass', 'string', 'The fully qualified class name of the factory', false, ArrayFormFactory::class); + $this->registerArgument('prototypeName', 'string', 'Name of the prototype to use', false, null); + $this->registerArgument('overrideConfiguration', 'array', 'factory specific configuration', false, []); + } + + /** + * @param array $arguments + * @param callable|\Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * @return string + * @public + */ + public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) + { + $persistenceIdentifier = $arguments['persistenceIdentifier']; + $factoryClass = $arguments['factoryClass']; + $prototypeName = $arguments['prototypeName']; + $overrideConfiguration = $arguments['overrideConfiguration']; + + if (!empty($persistenceIdentifier)) { + $formPersistenceManager = $renderingContext->getObjectManager()->get(FormPersistenceManagerInterface::class); + $formConfiguration = $formPersistenceManager->load($persistenceIdentifier); + ArrayUtility::mergeRecursiveWithOverrule( + $formConfiguration, + $overrideConfiguration + ); + $overrideConfiguration = $formConfiguration; + $overrideConfiguration['persistenceIdentifier'] = $persistenceIdentifier; + } + + if (empty($prototypeName)) { + $prototypeName = isset($overrideConfiguration['prototypeName']) ? $overrideConfiguration['prototypeName'] : 'standard'; + } + + $factory = $renderingContext->getObjectManager()->get($factoryClass); + $formDefinition = $factory->build($overrideConfiguration, $prototypeName); + $response = $renderingContext->getObjectManager()->get(Response::class, $renderingContext->getControllerContext()->getResponse()); + $form = $formDefinition->bind($renderingContext->getControllerContext()->getRequest(), $response); + return $form->render(); + } +} diff --git a/typo3/sysext/form/Classes/ViewHelpers/SelectViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/SelectViewHelper.php deleted file mode 100644 index e1455c3177f4..000000000000 --- a/typo3/sysext/form/Classes/ViewHelpers/SelectViewHelper.php +++ /dev/null @@ -1,139 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\ViewHelpers; - -/* - * 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! - */ - -/** - * The form wizard controller - */ -class SelectViewHelper extends \TYPO3\CMS\Fluid\ViewHelpers\Form\SelectViewHelper -{ - /** - * Render the option tags. - * - * @return array an associative array of options, key will be the value of the option tag - */ - protected function getOptions() - { - if (!is_array($this->arguments['options']) && !$this->arguments['options'] instanceof \Traversable) { - return []; - } - $options = []; - $optionsArgument = $this->arguments['options']; - foreach ($optionsArgument as $key => $value) { - if (is_string($key)) { - $options[$key]['disabled'] = $value['disabled']; - $options[$key]['isOptgroup'] = true; - $optGroupOptions = $value['options']; - foreach ($optGroupOptions as $optionKey => $optionValue) { - $option = $this->getOption($optionKey, $optionValue); - $options[$key]['options'][key($option)] = current($option); - } - } else { - $option = $this->getOption($key, $value); - $options[key($option)] = current($option); - } - } - - if ($this->arguments['sortByOptionLabel']) { - asort($options, SORT_LOCALE_STRING); - } - return $options; - } - - /** - * Build a option array. - * - * @param string $key - * @param string $value - * @return array an associative array of an option, key will be the value of the option tag - */ - protected function getOption($key, $value) - { - $option = []; - if (is_object($value) || is_array($value)) { - if ($this->hasArgument('optionValueField')) { - $key = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getPropertyPath($value, $this->arguments['optionValueField']); - if (is_object($key)) { - if (method_exists($key, '__toString')) { - $key = (string)$key; - } else { - throw new \TYPO3\CMS\Fluid\Core\ViewHelper\Exception('Identifying value for object of class "' . get_class($value) . '" was an object.', 1460975593); - } - } - // @todo use $this->persistenceManager->isNewObject() once it is implemented - } elseif ($this->persistenceManager->getIdentifierByObject($value) !== null) { - $key = $this->persistenceManager->getIdentifierByObject($value); - } elseif (method_exists($value, '__toString')) { - $key = (string)$value; - } else { - throw new \TYPO3\CMS\Fluid\Core\ViewHelper\Exception('No identifying value for object of class "' . get_class($value) . '" found.', 1460975538); - } - if ($this->hasArgument('optionLabelField')) { - $value = \TYPO3\CMS\Extbase\Reflection\ObjectAccess::getPropertyPath($value, $this->arguments['optionLabelField']); - if (is_object($value)) { - if (method_exists($value, '__toString')) { - $value = (string)$value; - } else { - throw new \TYPO3\CMS\Fluid\Core\ViewHelper\Exception('Label value for object of class "' . get_class($value) . '" was an object without a __toString() method.', 1460975633); - } - } - } elseif (method_exists($value, '__toString')) { - $value = (string)$value; - // @todo use $this->persistenceManager->isNewObject() once it is implemented - } elseif ($this->persistenceManager->getIdentifierByObject($value) !== null) { - $value = $this->persistenceManager->getIdentifierByObject($value); - } - } - $option[$key] = $value; - return $option; - } - - /** - * Render the option tags. - * - * @param array $options the options for the form. - * @return string rendered tags. - */ - protected function renderOptionTags($options) - { - $output = ''; - if ($this->hasArgument('prependOptionLabel')) { - $value = $this->hasArgument('prependOptionValue') ? $this->arguments['prependOptionValue'] : ''; - $label = $this->arguments['prependOptionLabel']; - $output .= $this->renderOptionTag($value, $label, false) . LF; - } - foreach ($options as $value => $label) { - if ( - isset($label['isOptgroup']) - && $label['isOptgroup'] === true - ) { - $output .= '<optgroup label="' . htmlspecialchars($value) . '"'; - if ($label['disabled'] !== null) { - $output .= ' disabled="disabled"'; - } - $output .= '>' . LF; - foreach ($label['options'] as $optionValue => $optionLabel) { - $isSelected = $this->isSelected($optionValue); - $output .= $this->renderOptionTag($optionValue, $optionLabel, $isSelected) . LF; - } - $output .= ' </optgroup>' . LF; - } else { - $isSelected = $this->isSelected($value); - $output .= $this->renderOptionTag($value, $label, $isSelected) . LF; - } - } - return $output; - } -} diff --git a/typo3/sysext/form/Classes/ViewHelpers/TranslateElementPropertyViewHelper.php b/typo3/sysext/form/Classes/ViewHelpers/TranslateElementPropertyViewHelper.php new file mode 100644 index 000000000000..5abb18b4c59b --- /dev/null +++ b/typo3/sysext/form/Classes/ViewHelpers/TranslateElementPropertyViewHelper.php @@ -0,0 +1,80 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\ViewHelpers; + +/* + * 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\Fluid\Core\ViewHelper\AbstractViewHelper; +use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface; +use TYPO3\CMS\Form\Domain\Renderer\RendererInterface; +use TYPO3\CMS\Form\Domain\Runtime\FormRuntime; +use TYPO3\CMS\Form\Service\TranslationService; +use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface; +use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic; + +/** + * Translate form element properites. + * + * Scope: frontend / backend + * @api + */ +class TranslateElementPropertyViewHelper extends AbstractViewHelper +{ + use CompileWithRenderStatic; + + /** + * Initialize arguments. + * + * @return void + * @internal + */ + public function initializeArguments() + { + parent::initializeArguments(); + $this->registerArgument('element', RootRenderableInterface::class, 'Form Element to translate', true); + $this->registerArgument('property', 'string', 'Property to translate', false); + $this->registerArgument('renderingOptionProperty', 'string', 'Property to translate', false); + $this->registerArgument('formRuntime', FormRuntime::class, 'The form runtime', false); + } + + /** + * Return array element by key. + * + * @param array $arguments + * @param \Closure $renderChildrenClosure + * @param RenderingContextInterface $renderingContext + * @return string + * @api + */ + public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext) + { + $element = $arguments['element']; + $formRuntime = $arguments['formRuntime']; + + $property = null; + if (!empty($arguments['property'])) { + $property = $arguments['property']; + } elseif (!empty($arguments['renderingOptionProperty'])) { + $property = $arguments['renderingOptionProperty']; + } + + if ($formRuntime === null) { + /** @var RendererInterface $fluidFormRenderer */ + $fluidFormRenderer = $renderingContext->getViewHelperVariableContainer()->getView(); + $formRuntime = $fluidFormRenderer->getFormRuntime(); + } + + return TranslationService::getInstance()->translateFormElementValue($element, $property, $formRuntime); + } +} diff --git a/typo3/sysext/form/Configuration/Backend/AjaxRoutes.php b/typo3/sysext/form/Configuration/Backend/AjaxRoutes.php deleted file mode 100644 index 660792b6c3ab..000000000000 --- a/typo3/sysext/form/Configuration/Backend/AjaxRoutes.php +++ /dev/null @@ -1,13 +0,0 @@ -<?php -use TYPO3\CMS\Form\Controller\WizardController; - -/** - * Definitions for AJAX routes provided by EXT:form - */ -return [ - // Save the current form wizard - 'formwizard_save' => [ - 'path' => '/wizard/form/save', - 'target' => WizardController::class . '::saveAction' - ], -]; diff --git a/typo3/sysext/form/Configuration/FlexForms/FormFramework.xml b/typo3/sysext/form/Configuration/FlexForms/FormFramework.xml new file mode 100644 index 000000000000..b8b9dab0418b --- /dev/null +++ b/typo3/sysext/form/Configuration/FlexForms/FormFramework.xml @@ -0,0 +1,40 @@ +<T3DataStructure> + <sheets> + <sDEF> + <ROOT> + <TCEforms> + <sheetTitle>LLL:EXT:form/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.formframework.sheet_general</sheetTitle> + </TCEforms> + <type>array</type> + <el> + <settings.persistenceIdentifier> + <TCEforms> + <label>LLL:EXT:form/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.formframework.persistenceIdentifier</label> + <onChange>reload</onChange> + <config> + <type>select</type> + <items> + <numIndex index="0" type="array"> + <numIndex index="0">LLL:EXT:form/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.formframework.selectPersistenceIdentifier</numIndex> + <numIndex index="1"></numIndex> + </numIndex> + </items> + <softref>formPersistenceIdentifier</softref> + </config> + </TCEforms> + </settings.persistenceIdentifier> + <settings.overrideFinishers> + <TCEforms> + <label>LLL:EXT:form/Resources/Private/Language/Database.xlf:tt_content.pi_flexform.formframework.overrideFinishers</label> + <displayCond>FIELD:settings.persistenceIdentifier:REQ:TRUE</displayCond> + <onChange>reload</onChange> + <config> + <type>check</type> + </config> + </TCEforms> + </settings.overrideFinishers> + </el> + </ROOT> + </sDEF> + </sheets> +</T3DataStructure> diff --git a/typo3/sysext/form/Configuration/PageTS/modWizards.ts b/typo3/sysext/form/Configuration/PageTS/modWizards.ts index aa514e38263e..86a93700c9b2 100644 --- a/typo3/sysext/form/Configuration/PageTS/modWizards.ts +++ b/typo3/sysext/form/Configuration/PageTS/modWizards.ts @@ -1,367 +1,17 @@ mod.wizards { - newContentElement.wizardItems { - forms { - show :=addToList(mailform) - elements { - mailform { - iconIdentifier = content-elements-mailform - title = LLL:EXT:backend/Resources/Private/Language/locallang_db_new_content_el.xlf:forms_mail_title - description = LLL:EXT:backend/Resources/Private/Language/locallang_db_new_content_el.xlf:forms_mail_description - tt_content_defValues { - CType = mailform - bodytext ( -enctype = multipart/form-data -method = post -prefix = tx_form - ) - } - } - } - } - } - - form { - defaults { - showTabs = elements, options, form - tabs { - elements { - showAccordions = basic, predefined, content - accordions { - basic { - showButtons = textline, textarea, checkbox, radio, select, fileupload, hidden, password, fieldset, submit, reset, button - } - predefined { - showButtons = name, email, checkboxgroup, radiogroup - } - content { - showButtons = header, textblock - } - } - } - - options { - showAccordions = legend, label, attributes, options, validation, filters, various - accordions { - label { - showProperties = label - } - attributes { - showProperties = accept, accept-charset, accesskey, action, alt, autocomplete, autofocus, checked, class, cols, contenteditable, contextmenu, dir, draggable, dropzone, disabled, enctype, hidden, height, id, inputmode, label, lang, list, max, maxlength, method, min, minlength, multiple, name, novalidate, pattern, placeholder, readonly, required, rows, selected, selectionDirection, selectionEnd, selectionStart, size, spellcheck, src, step, style, tabindex, text, title, translate, type, value, width, wrap - } - validation { - showRules = alphabetic, alphanumeric, between, date, digit, email, equals, fileallowedtypes, filemaximumsize, fileminimumsize, float, greaterthan, inarray, integer, ip, length, lessthan, regexp, required, uri - - rules { - alphabetic { - showProperties = message, error, showMessage, allowWhiteSpace - } - - alphanumeric { - showProperties = message, error, showMessage, allowWhiteSpace - } - - between { - showProperties = message, error, showMessage, minimum, maximum, inclusive - } - - date { - showProperties = message, error, showMessage, format - } - - digit { - showProperties = message, error, showMessage - } - - email { - showProperties = message, error, showMessage - } - - equals { - showProperties = message, error, showMessage, field - } - - fileallowedtypes { - showProperties = message, error, showMessage, types - } - - filemaximumsize { - showProperties = message, error, showMessage, maximum - } - - fileminimumsize { - showProperties = message, error, showMessage, minimum - } - - float { - showProperties = message, error, showMessage - } - - greaterthan { - showProperties = message, error, showMessage, minimum - } - - inarray { - showProperties = message, error, showMessage, array, strict - } - - integer { - showProperties = message, error, showMessage - } - - ip { - showProperties = message, error, showMessage - } - - length { - showProperties = message, error, showMessage, minimum, maximum - } - - lessthan { - showProperties = message, error, showMessage, maximum - } - - regexp { - showProperties = message, error, showMessage, expression - } - - required { - showProperties = message, error, showMessage - } - - uri { - showProperties = message, error, showMessage - } - } - } - filtering { - showFilters = alphabetic, alphanumeric, currency, digit, integer, lowercase, regexp, stripnewlines, titlecase, trim, uppercase - - filters { - alphabetic { - showProperties = allowWhiteSpace - } - - alphanumeric { - showProperties = allowWhiteSpace - } - - currency { - showProperties = decimalPoint, thousandSeparator - } - - digit { - showProperties = - } - - integer { - showProperties = - } - - lowercase { - showProperties = - } - - regexp { - showProperties = expression - } - - stripnewlines { - showProperties = - } - - titlecase { - showProperties = - } - - trim { - showProperties = characterList - } - - uppercase { - showProperties = - } - } - } - } - } - - form { - showAccordions = behaviour, prefix, attributes, postProcessor - accordions { - postProcessor { - showPostProcessors = mail, redirect - postProcessors { - mail { - showProperties = recipientEmail, senderEmail, subject - } - redirect { - showProperties = destination - } - } - } - } - } - } - } - - elements { - form { - accordions { - attributes { - showProperties = accept, action, dir, enctype, lang, method, novalidate, class, id, style, title - } - } - } - - button { - showAccordions = label, attributes - accordions { - attributes { - showProperties = name, value, class, id - } - } - } - - checkbox { - showAccordions = label, attributes, validation - accordions { - attributes { - showProperties = name, value, class, id, checked, required - } - validation { - showRules = required - } - } - } - - fieldset { - showAccordions = legend, attributes - accordions { - attributes { - showProperties = class, id - } - } - } - - fileupload { - showAccordions = label, attributes, validation - accordions { - attributes { - showProperties = name, class, id, required - } - validation { - showRules = required, fileallowedtypes, filemaximumsize, fileminimumsize - } - } - } - - hidden { - showAccordions = attributes - accordions { - attributes { - showProperties = name, value - } - } - } - - password { - showAccordions = label, attributes, validation - accordions { - attributes { - showProperties = name, placeholder, class, id, autocomplete, required - } - validation { - showRules = required, equals - } - } - } - - radio < .checkbox - - reset < .button - reset { - accordions { - attributes { - showProperties := removeFromList(name) - } - } - } - - select { - showAccordions = label, attributes, options, validation - accordions { - attributes { - showProperties = name, size, class, id, multiple, required - } - validation { - showRules = required - } - } - } - - submit < .button - submit { - accordions { - attributes { - showProperties := removeFromList(name) - } - } - } - - textarea { - showAccordions = label, attributes, validation, filters - accordions { - attributes { - showProperties = name, placeholder, cols, rows, class, id, required, text - } - filtering { - showFilters = alphabetic, alphanumeric, lowercase, regexp, stripnewlines, titlecase, trim, uppercase - } - validation { - showRules = alphabetic, alphanumeric, length, regexp, required - } - } - } - - textline { - showAccordions = label, attributes, validation, filters - accordions { - attributes { - showProperties = name, placeholder, type, class, id, autocomplete, required - } - validation { - showRules = alphabetic, alphanumeric, between, date, digit, email, equals, float, greaterthan, inarray, integer, ip, length, lessthan, regexp, required, uri - } - filtering { - showFilters = alphabetic, alphanumeric, currency, digit, integer, lowercase, regexp, titlecase, trim, uppercase - } - } - } - - name { - showAccordions = legend, various - } - - email < .textline - - checkboxgroup { - showAccordions = legend, options, various, validation - accordions { - validation { - showRules = required - } - } - } - - radiogroup < .checkboxgroup - - header { - showAccordions = various - } - - textblock { - showAccordions = various - } - } - } + newContentElement.wizardItems { + forms { + show :=addToList(formframework) + elements { + formframework { + iconIdentifier = content-elements-mailform + title = LLL:EXT:backend/Resources/Private/Language/locallang_db_new_content_el.xlf:forms_mail_title + description = LLL:EXT:backend/Resources/Private/Language/locallang_db_new_content_el.xlf:forms_mail_description + tt_content_defValues { + CType = form_formframework + } + } + } + } + } } \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TCA/Overrides/sys_template.php b/typo3/sysext/form/Configuration/TCA/Overrides/sys_template.php deleted file mode 100644 index 6d53f0320ee2..000000000000 --- a/typo3/sysext/form/Configuration/TCA/Overrides/sys_template.php +++ /dev/null @@ -1,5 +0,0 @@ -<?php -defined('TYPO3_MODE') or die(); - -// Register static TypoScript resource -\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addStaticFile('form', 'Configuration/TypoScript/', 'Default TS'); diff --git a/typo3/sysext/form/Configuration/TCA/Overrides/tt_content.php b/typo3/sysext/form/Configuration/TCA/Overrides/tt_content.php index 00b0d95497b3..99ec0cd67be5 100644 --- a/typo3/sysext/form/Configuration/TCA/Overrides/tt_content.php +++ b/typo3/sysext/form/Configuration/TCA/Overrides/tt_content.php @@ -1,121 +1,30 @@ <?php defined('TYPO3_MODE') or die(); -// add an CType element "mailform" -$GLOBALS['TCA']['tt_content']['ctrl']['typeicon_classes']['mailform'] = 'mimetypes-x-content-form'; - -// check if there is already a forms tab and add the item after that, otherwise -// add the tab item as well -$additionalCTypeItem = [ - 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:CType.I.8', - 'mailform', - 'content-elements-mailform' -]; - -$existingCTypeItems = $GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items']; -$groupFound = false; -$groupPosition = false; -foreach ($existingCTypeItems as $position => $item) { - if ($item[0] === 'LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:CType.div.forms') { - $groupFound = true; - $groupPosition = $position; - break; - } -} - -if ($groupFound && $groupPosition) { - // add the new CType item below CType - array_splice($GLOBALS['TCA']['tt_content']['columns']['CType']['config']['items'], $groupPosition+1, 0, [0 => $additionalCTypeItem]); -} else { - // nothing found, add two items (group + new CType) at the bottom of the list - \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem('tt_content', 'CType', - ['LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:CType.div.forms', '--div--'] +call_user_func(function () { + // Add the FlexForm + \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPiFlexFormValue( + '*', + 'FILE:EXT:form/Configuration/FlexForms/FormFramework.xml', + 'form_formframework' ); - \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTcaSelectItem('tt_content', 'CType', $additionalCTypeItem); -} - -// predefined forms -\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addTCAcolumns( - 'tt_content', - [ - 'tx_form_predefinedform' => [ - 'label' => 'LLL:EXT:form/Resources/Private/Language/Database.xlf:tx_form_predefinedform', - 'exclude' => true, - 'config' => [ - 'type' => 'select', - 'renderType' => 'selectSingle', - 'items' => [ - [ - 'LLL:EXT:form/Resources/Private/Language/Database.xlf:tx_form_predefinedform.selectPredefinedForm', - '' - ], - ], - ], - ], - ] -); -$GLOBALS['TCA']['tt_content']['ctrl']['requestUpdate'] .= ',tx_form_predefinedform'; - -// Hide bodytext if a predefined form is selected -$GLOBALS['TCA']['tt_content']['columns']['bodytext']['displayCond']['AND'] = [ - 'OR' => [ - 'FIELD:CType:!=:mailform', - 'AND' => [ - 'FIELD:CType:=:mailform', - 'FIELD:tx_form_predefinedform:REQ:false', - ], - ], -]; -$GLOBALS['TCA']['tt_content']['columns']['bodytext']['config']['wizards']['forms'] = [ - 'notNewRecords' => true, - 'enableByTypeConfig' => 1, - 'type' => 'script', - 'title' => 'Form wizard', - 'icon' => 'EXT:backend/Resources/Public/Images/FormFieldWizard/wizard_forms.gif', - 'module' => [ - 'name' => 'wizard_form' - ], - 'params' => [ - 'xmlOutput' => 0 - ] -]; - -// Add palettes if they are not available -if (!isset($GLOBALS['TCA']['tt_content']['palettes']['visibility'])) { - $GLOBALS['TCA']['tt_content']['palettes']['visibility'] = [ - 'showitem' => ' - hidden;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:hidden_formlabel, - sectionIndex;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:sectionIndex_formlabel, - linkToTop;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:linkToTop_formlabel - ', - ]; -} - -if (!isset($GLOBALS['TCA']['tt_content']['palettes']['frames'])) { - $GLOBALS['TCA']['tt_content']['palettes']['frames'] = [ - 'showitem' => ' - layout;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:layout_formlabel - ', - ]; -} - -$GLOBALS['TCA']['tt_content']['types']['mailform']['showitem'] = ' - --palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.general;general, - --palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.header;header,rowDescription, - --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:CType.I.8, - bodytext;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:bodytext.ALT.mailform, - tx_form_predefinedform;LLL:EXT:form/Resources/Private/Language/Database.xlf:tx_form_predefinedform, - --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance, - --palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.frames;frames, - --div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.access, - --palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.visibility;visibility, - --palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.access;access, -'; -if (!is_array($GLOBALS['TCA']['tt_content']['types']['mailform']['columnsOverrides'])) { - $GLOBALS['TCA']['tt_content']['types']['mailform']['columnsOverrides'] = []; -} -if (!is_array($GLOBALS['TCA']['tt_content']['types']['mailform']['columnsOverrides']['bodytext'])) { - $GLOBALS['TCA']['tt_content']['types']['mailform']['columnsOverrides']['bodytext'] = []; -} -$GLOBALS['TCA']['tt_content']['types']['mailform']['columnsOverrides']['bodytext']['config']['renderType'] = 'formwizard'; + $GLOBALS['TCA']['tt_content']['types']['form_formframework']['showitem'] = + '--palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.general;general,' + . '--palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.header;header,rowDescription,' + . '--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.plugin,pi_flexform,' + . '--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.access,' + . '--palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.visibility;visibility,' + . '--palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.access;access,' + . '--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.appearance,' + . '--palette--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:palette.frames;frames,' + . '--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.behaviour,' + . '--div--;LLL:EXT:frontend/Resources/Private/Language/locallang_ttc.xlf:tabs.extended'; + + // Register the plugin + \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerPlugin( + 'TYPO3.CMS.Form', + 'Formframework', + 'Form' + ); +}); diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Button.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Button.ts deleted file mode 100644 index 7cec6af661dc..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Button.ts +++ /dev/null @@ -1,38 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - BUTTON { - 10 { - displayName = Default - partialPath = FlatElements/Button - } - } - } - } - - settings { - registeredElements { - # BUTTON - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.button - # - # A historical element which generates a <input type="button" /> tag. - # To be compatible it is a copy of the new element INPUTTYPEBUTTON - # If you want to use a <button> tag you have to use - # BUTTON =< .BUTTONTAG or use the BUTTONTAG directly - BUTTON =< .INPUTTYPEBUTTON - BUTTON { - partialPath =< plugin.tx_form.view.elementPartials.INPUTTYPEBUTTON.10.partialPath - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Buttontag.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Buttontag.ts deleted file mode 100644 index a4ce6f5cf219..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Buttontag.ts +++ /dev/null @@ -1,145 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - BUTTONTAG { - 10 { - displayName = Default - partialPath = FlatElements/ButtonTag - } - } - } - } - - settings { - registeredElements { - # BUTTONTAG - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.button - # - # Generates an element <button type="..." /> - BUTTONTAG { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = autofocus - 210 = disabled - 220 = name - 230 = type - 240 = value - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - # ButtonViewHelper - 120 = autofocus - 130 = type - } - - # defaultHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set if there is no entry in the user configured element. - defaultHtmlAttributeValues { - type = button - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.BUTTONTAG.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 0 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 0 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Checkbox.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Checkbox.ts deleted file mode 100644 index 5f2ecfb83ab9..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Checkbox.ts +++ /dev/null @@ -1,147 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - CHECKBOX { - 10 { - displayName = Default - partialPath = FlatElements/Checkbox - } - } - } - } - - settings { - registeredElements { - # CHECKBOX - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.checkbox - # - # @ToDo: add more details - CHECKBOX { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = type - 210 = autofocus - 220 = checked - 230 = disabled - 240 = name - 250 = readonly - 260 = required - 270 = value - } - - # fixedHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set as attributes. - fixedHtmlAttributeValues { - type = checkbox - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - # CheckboxViewHelper - 120 = checked - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.CHECKBOX.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 1 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 1 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Checkboxgroup.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Checkboxgroup.ts deleted file mode 100644 index fc079b2e885a..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Checkboxgroup.ts +++ /dev/null @@ -1,67 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - CHECKBOXGROUP { - 10 { - displayName = Default - partialPath = ContainerElements/Checkboxgroup - } - } - } - } - - settings { - registeredElements { - # CHECKBOXGROUP - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: none - # - # This defines a container element. - # @ToDo: add more details - CHECKBOXGROUP =< .FIELDSET - CHECKBOXGROUP { - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.CHECKBOXGROUP.10.partialPath - - # childrenInheritName - # Used by: frontend - # Overwritable by user: FALSE - # - # If set to 1 all child elements inherit the name of the parent element. - # @ToDo: add more details - childrenInheritName = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 1 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 1 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Contentelement.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Contentelement.ts deleted file mode 100644 index 871984870c94..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Contentelement.ts +++ /dev/null @@ -1,69 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - # useless for the wizard but needed for the frontend - CONTENTELEMENT { - 10 { - displayName = Default - partialPath = FlatElements/ContentElement - } - } - } - } - - settings { - registeredElements { - # CONTENTELEMENT - # Used by: frontend - # Used ViewHelper: none - # - # This defines an internal element which holds some basic configuration - # like visibility settings and the used partial path. - # - # @ToDo: add more details - CONTENTELEMENT { - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.CONTENTELEMENT.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 0 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 0 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Fieldset.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Fieldset.ts deleted file mode 100644 index 2f273285b174..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Fieldset.ts +++ /dev/null @@ -1,96 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - FIELDSET { - 10 { - displayName = Default - partialPath = ContainerElements/Fieldset - } - } - } - } - - settings { - registeredElements { - # FIELDSET - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: none - # - # This defines a container element. - # @ToDo: add more details - FIELDSET { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - # element specific attributes - 200 = disabled - 210 = name - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.FIELDSET.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 0 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 0 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Fileupload.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Fileupload.ts deleted file mode 100644 index 065586cb4b18..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Fileupload.ts +++ /dev/null @@ -1,148 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - FILEUPLOAD { - 10 { - displayName = Default - partialPath = FlatElements/Upload - } - } - } - } - - settings { - registeredElements { - # FILEUPLOAD - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.upload - # - # @ToDo: add more details - FILEUPLOAD { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = type - 210 = accept - 220 = autofocus - 230 = disabled - 240 = multiple - 250 = name - 260 = readonly - 270 = required - 280 = value - } - - # fixedHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set as attributes. - fixedHtmlAttributeValues { - type = file - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - # UploadViewHelper - 120 = multiple - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.FILEUPLOAD.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 1 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 1 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Form.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Form.ts deleted file mode 100644 index 1ededb98bdf6..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Form.ts +++ /dev/null @@ -1,166 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - FORM { - 10 { - displayName = Default - partialPath = ContainerElements/Form - } - } - } - } - - settings { - registeredElements { - # FORM - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # Used ViewHelper: f:form - # - # @ToDo: add more details - FORM { - # themeName - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # Sets the theme name used for templating. - # Right now there is one default theme. One can define an own theme and use this one instead. - # - # This setting can be overwritten in the FORM object. - # @ToDo: add more details - themeName = Default - - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = action - 210 = accept - 220 = accept-charset - 230 = autocomplete - 240 = enctype - 250 = method - 260 = name - 270 = novalidate - 280 = target - } - - # defaultHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set if there is no entry in the user configured element. - defaultHtmlAttributeValues { - enctype = multipart/form-data - method = post - } - - # fixedHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set as attributes. - fixedHtmlAttributeValues = - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - # FormViewHelper - 90 = enctype - 100 = method - 110 = name - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.FORM.10.partialPath - - # viewHelperDefaultArguments - # Used by: frontend - # Overwritable by user: FALSE - # - # This helper array is used to cast some values needed by the ViewHelpers. - # E.g the f:form ViewHelper needs an array for the - # argument "additionalParams". If additionalParams is not set - # in the userdefined TypoScript this results in a NULL value in the - # templating variable "{model.additionalArguments.additionalParams}" - # and this throws an error. Most of the ViewHelper arguments - # are strings and/ or can handle such NULL values but there are some - # ViewHelpers which need some type casting. - viewHelperDefaultArguments { - arguments { - } - - additionalParams { - } - - argumentsToBeExcludedFromQueryString { - } - } - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Header.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Header.ts deleted file mode 100644 index fc7ea71872eb..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Header.ts +++ /dev/null @@ -1,65 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - HEADER { - 10 { - displayName = Default - partialPath = FlatElements/Header - } - } - } - } - - settings { - registeredElements { - # HEADER - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:format.raw - # - # @ToDo: add more details - HEADER { - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.HEADER.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 0 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 0 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Hidden.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Hidden.ts deleted file mode 100644 index c33c1edad4a8..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Hidden.ts +++ /dev/null @@ -1,140 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - HIDDEN { - 10 { - displayName = Default - partialPath = FlatElements/Hidden - } - } - } - } - - settings { - registeredElements { - # HIDDEN - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.hidden - # - # @ToDo: add more details - HIDDEN { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = type - 210 = name - 220 = value - } - - # fixedHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set as attributes. - fixedHtmlAttributeValues { - type = hidden - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.HIDDEN.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 0 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 0 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Input.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Input.ts deleted file mode 100644 index 03017f165caf..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Input.ts +++ /dev/null @@ -1,168 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - INPUT { - 10 { - displayName = Default - partialPath = FlatElements/Input - } - } - } - } - - settings { - registeredElements { - # INPUT - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.textfield - # - # @ToDo: add more details - INPUT { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = type - 210 = accept - 220 = autocomplete - 230 = autofocus - 240 = checked - 250 = disabled - 260 = list - 270 = inputmode - 280 = max - 290 = maxlength - 300 = min - 310 = minlength - 320 = multiple - 330 = name - 340 = pattern - 350 = placeholder - 360 = readonly - 370 = required - 380 = size - 390 = src - 400 = step - 410 = value - 420 = width - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - # TextfieldViewHelper - 120 = autofocus - 130 = maxlength - 140 = size - 150 = placeholder - 160 = pattern - 170 = required - 180 = type - } - - # defaultHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set if there is no entry in the user configured element. - defaultHtmlAttributeValues { - type = text - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.INPUT.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 1 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 1 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Inputtypebutton.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Inputtypebutton.ts deleted file mode 100644 index 5c674eb5f6e9..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Inputtypebutton.ts +++ /dev/null @@ -1,144 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - INPUTTYPEBUTTON { - 10 { - displayName = Default - partialPath = FlatElements/InputTypeButton - } - } - } - } - - settings { - registeredElements { - # INPUTTYPEBUTTON - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.textfield - # - # Generates an element <input type="button" /> - INPUTTYPEBUTTON { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = type - 210 = autofocus - 220 = disabled - 230 = name - 240 = value - } - - # fixedHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set as attributes. - fixedHtmlAttributeValues { - type = button - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - # TextfieldViewHelper - 120 = type - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.INPUTTYPEBUTTON.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 0 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 0 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Label.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Label.ts deleted file mode 100644 index 5a3afe02284b..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Label.ts +++ /dev/null @@ -1,22 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - # special definitions for internal usage - LABEL { - 10 { - displayName = Default - partialPath = AdditionalElements/Label - } - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Optgroup.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Optgroup.ts deleted file mode 100644 index a30bf70512bd..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Optgroup.ts +++ /dev/null @@ -1,17 +0,0 @@ -plugin.tx_form { - settings { - registeredElements { - # OPTGROUP - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: form:select (extends f:form.select) - # - # Element is needed to define select options via TypoScript but - # the rendering is based on the select ViewHelper. - # Therefore no special settings are needed. - # - # @ToDo: add more details - OPTGROUP { - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Option.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Option.ts deleted file mode 100644 index cc84a4b2078e..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Option.ts +++ /dev/null @@ -1,17 +0,0 @@ -plugin.tx_form { - settings { - registeredElements { - # OPTION - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: form:select (extends f:form.select) - # - # Element is needed to define select options via TypoScript but - # the rendering is based on the select ViewHelper. - # Therefore no special settings are needed. - # - # @ToDo: add more details - OPTION { - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Password.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Password.ts deleted file mode 100644 index c364c2a22b96..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Password.ts +++ /dev/null @@ -1,153 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - PASSWORD { - 10 { - displayName = Default - partialPath = FlatElements/Password - } - } - } - } - - settings { - registeredElements { - # PASSWORD - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.password - # - # @ToDo: add more details - PASSWORD { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = type - 210 = autocomplete - 220 = autofocus - 230 = disabled - 240 = maxlength - 250 = minlength - 260 = name - 270 = pattern - 280 = placeholder - 290 = readonly - 300 = required - 310 = size - 320 = value - } - - # fixedHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set as attributes. - fixedHtmlAttributeValues { - type = password - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - # PasswordViewHelper - 120 = maxlength - 130 = size - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.PASSWORD.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 0 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 0 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Radio.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Radio.ts deleted file mode 100644 index 9cf918ef280a..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Radio.ts +++ /dev/null @@ -1,148 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - RADIO { - 10 { - displayName = Default - partialPath = FlatElements/Radio - } - } - } - } - - settings { - registeredElements { - # RADIO - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.radio - # - # @ToDo: add more details - RADIO { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = type - 210 = autofocus - 220 = checked - 230 = disabled - 240 = name - 250 = readonly - 260 = required - 270 = value - } - - # fixedHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set as attributes. - fixedHtmlAttributeValues { - type = radio - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - 120 = multiple - # RadioViewHelper - 130 = checked - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.RADIO.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 1 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 1 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Radiogroup.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Radiogroup.ts deleted file mode 100644 index 9edbc46be524..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Radiogroup.ts +++ /dev/null @@ -1,67 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - RADIOGROUP { - 10 { - displayName = Default - partialPath = ContainerElements/Radiogroup - } - } - } - } - - settings { - registeredElements { - # RADIOGROUP - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: none - # - # This defines a container element. - # @ToDo: add more details - RADIOGROUP =< .FIELDSET - RADIOGROUP { - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.RADIOGROUP.10.partialPath - - # childrenInheritName - # Used by: frontend - # Overwritable by user: FALSE - # - # If set to 1 all child elements inherit the name of the parent element. - # @ToDo: add more details - childrenInheritName = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 1 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 1 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Reset.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Reset.ts deleted file mode 100644 index 7a1ce431b00d..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Reset.ts +++ /dev/null @@ -1,145 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - RESET { - 10 { - displayName = Default - partialPath = FlatElements/Reset - } - } - } - } - - settings { - registeredElements { - # RESET - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.textfield - # - # @ToDo: add more details - RESET { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = type - 210 = autofocus - 220 = disabled - 230 = name - 240 = value - } - - # fixedHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set as attributes. - fixedHtmlAttributeValues { - type = reset - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - # TextfieldViewHelper - 120 = autofocus - 130 = type - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.RESET.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 0 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 0 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Select.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Select.ts deleted file mode 100644 index be8760d80453..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Select.ts +++ /dev/null @@ -1,137 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - SELECT { - 10 { - displayName = Default - partialPath = FlatElements/Select - } - } - } - } - - settings { - registeredElements { - # SELECT - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: form:select (extends f:form.select) - # - # @ToDo: add more details - SELECT { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = autofocus - 210 = disabled - 220 = multiple - 230 = name - 240 = required - 250 = size - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - # SelectViewHelper - 120 = multiple - 130 = size - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.SELECT.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 1 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 1 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Submit.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Submit.ts deleted file mode 100644 index 1965b63f140b..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Submit.ts +++ /dev/null @@ -1,141 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - SUBMIT { - 10 { - displayName = Default - partialPath = FlatElements/Submit - } - } - } - } - - settings { - registeredElements { - # SUBMIT - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.submit - # - # @ToDo: add more details - SUBMIT { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = type - 210 = autofocus - 220 = disabled - 230 = name - 240 = value - } - - # fixedHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set as attributes. - fixedHtmlAttributeValues { - type = submit - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = dir - 20 = id - 30 = lang - 40 = style - 50 = title - 60 = accesskey - 70 = tabindex - 80 = onclick - 90 = name - 100 = value - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.SUBMIT.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 0 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 0 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Textarea.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Textarea.ts deleted file mode 100644 index c383e4dfa463..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Textarea.ts +++ /dev/null @@ -1,148 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - TEXTAREA { - 10 { - displayName = Default - partialPath = FlatElements/Textarea - } - } - } - } - - settings { - registeredElements { - # TEXTAREA - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.textarea - # - # @ToDo: add more details - TEXTAREA { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = autofocus - 210 = cols - 220 = disabled - 230 = inputmode - 240 = maxlength - 250 = minlength - 260 = name - 270 = placeholder - 280 = readonly - 290 = required - 300 = rows - 310 = selectionDirection - 320 = selectionEnd - 330 = selectionStart - 340 = wrap - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - # TextareaViewHelper - 120 = autofocus - 130 = rows - 140 = cols - 150 = placeholder - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.TEXTAREA.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 1 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 1 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Textblock.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Textblock.ts deleted file mode 100644 index 805f39674b49..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Textblock.ts +++ /dev/null @@ -1,65 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - TEXTBLOCK { - 10 { - displayName = Default - partialPath = FlatElements/Textblock - } - } - } - } - - settings { - registeredElements { - # TEXTBLOCK - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:format.raw - # - # @ToDo: add more details - TEXTBLOCK { - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.TEXTBLOCK.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 0 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 0 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Elements/Textline.ts b/typo3/sysext/form/Configuration/TypoScript/Elements/Textline.ts deleted file mode 100644 index 0951d0eeebce..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Elements/Textline.ts +++ /dev/null @@ -1,160 +0,0 @@ -plugin.tx_form { - # elementPartials - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines the template selection array for the form wizard. - # Each defined item is shown as option within the wizard. - # - # If there is no partialPath property in the userdefined TypoScript - # then elementPartials.ELEMENTNAME.10.partialPath is the default. - view { - elementPartials { - TEXTLINE { - 10 { - displayName = Default - partialPath = FlatElements/Textfield - } - } - } - } - - settings { - registeredElements { - # TEXTLINE - # Used by: frontend, wizard (not implemented right now) - # Used ViewHelper: f:form.textfield - # - # @ToDo: add more details - TEXTLINE { - # htmlAttributes - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # Defines allowed HTML attributes for a specific element. - # Based on selfhtml documentation version 8.1.2 (see http://wiki.selfhtml.org/wiki/Referenz:HTML/). - # This is needed to detect and map these strings within the user configured element definition as HTML attributes. - # As soon as prefix-* is defined every attribute is registered automatically as HTML attribute. - htmlAttributes { - # generic attributes - 10 = id - 20 = class - 30 = accesskey - 40 = contenteditable - 50 = contextmenu - 60 = dir - 70 = draggable - 80 = dropzone - 90 = hidden - 100 = lang - 110 = spellcheck - 120 = style - 130 = tabindex - 140 = title - 150 = data-* - 160 = translate - # element specific attributes - 200 = type - 210 = autocomplete - 220 = autofocus - 230 = disabled - 240 = inputmode - 250 = list - 260 = maxlength - 270 = minlength - 280 = name - 290 = pattern - 300 = placeholder - 310 = readonly - 320 = required - 330 = size - 340 = value - } - - # htmlAttributesUsedByTheViewHelperDirectly - # Used by: frontend - # Overwritable by user: FALSE - # - # Each HTML attribute defined at ".htmlAttributes" is available as array within the model. - # This array will be added to the resulting HTML tag. - # For this purpose the Fluid argument "additionalAttributes" of the ViewHelper is used. - # - # Some HTML attributes have to be assigned directly as an argument to the ViewHelper. - # The htmlAttributesUsedByTheViewHelperDirectly map is used to remove the specified - # HTML attribute from the "htmlAttributes" array and sets it for the model's "additionalArguments" array. - # - # There are two attributes which special behavior: - # * disabled - # * readonly - # These attributes can be assigned to the most ViewHelpers but whenever a "disabled" attribute appears - # the browser will disable this element no matter of the value. - # See: https://forge.typo3.org/issues/42474 - # Therefore it is held in the htmlAttributes array and the code removes this attribute if its value is set to 0. - htmlAttributesUsedByTheViewHelperDirectly { - # generic attributes - 10 = class - 20 = dir - 30 = id - 40 = lang - 50 = style - 60 = title - 70 = accesskey - 80 = tabindex - 90 = onclick - 100 = name - 110 = value - # TextfieldViewHelper - 120 = autofocus - 130 = maxlength - 140 = size - 150 = placeholder - 160 = pattern - 170 = required - 180 = type - } - - # defaultHtmlAttributeValues - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The following values are automatically set if there is no entry in the user configured element. - defaultHtmlAttributeValues { - type = text - } - - # partialPath - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: TRUE - # - # The defined partial is used to render the element. - # The partial paths to the element are build based on the following rule: - # {$plugin.tx_form.view.partialRootPath}/{$themeName}/@actionName/{$partialPath}. - partialPath =< plugin.tx_form.view.elementPartials.TEXTLINE.10.partialPath - - # visibleInShowAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the form. - # @ToDo: add more details - visibleInShowAction = 1 - - # visibleInConfirmationAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the confirmation page. - # @ToDo: add more details - visibleInConfirmationAction = 1 - - # visibleInProcessAction - # Used by: frontend - # Overwritable by user: TRUE - # - # If set to 1 this element is displayed in the mail. - # @ToDo: add more details - visibleInMail = 1 - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Filters/Filters.ts b/typo3/sysext/form/Configuration/TypoScript/Filters/Filters.ts deleted file mode 100644 index 6d6c36c44b25..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Filters/Filters.ts +++ /dev/null @@ -1,66 +0,0 @@ -plugin.tx_form { - settings { - # registeredFilters - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The array holds all available filters. - # "displayName" is planned for the wizard (not implemented right now). - registeredFilters { - alphabetic { - displayName = Alphabetic - className = TYPO3\CMS\Form\Domain\Filter\AlphabeticFilter - } - - alphanumeric { - displayName = Alphanumeric - className = TYPO3\CMS\Form\Domain\Filter\AlphanumericFilter - } - - currency { - displayName = Currency - className = TYPO3\CMS\Form\Domain\Filter\CurrencyFilter - } - - digit { - displayName = Digit - className = TYPO3\CMS\Form\Domain\Filter\DigitFilter - } - - integer { - displayName = Integer - className = TYPO3\CMS\Form\Domain\Filter\IntegerFilter - } - - lowercase { - displayName = Lowercase - className = TYPO3\CMS\Form\Domain\Filter\LowerCaseFilter - } - - regexp { - displayName = Regular Expression - className = TYPO3\CMS\Form\Domain\Filter\RegExpFilter - } - - stripnewlines { - displayName = Strip New Lines - className = TYPO3\CMS\Form\Domain\Filter\StripNewLinesFilter - } - - titlecase { - displayName = Titlecase - className = TYPO3\CMS\Form\Domain\Filter\TitleCaseFilter - } - - trim { - displayName = Trim - className = TYPO3\CMS\Form\Domain\Filter\TrimFilter - } - - uppercase { - displayName = Uppercase - className = TYPO3\CMS\Form\Domain\Filter\UpperCaseFilter - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/Validators/Validators.ts b/typo3/sysext/form/Configuration/TypoScript/Validators/Validators.ts deleted file mode 100644 index d8534f1558b9..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/Validators/Validators.ts +++ /dev/null @@ -1,111 +0,0 @@ -plugin.tx_form { - settings { - # registeredValidators - # Used by: frontend, wizard (not implemented right now) - # Overwritable by user: FALSE - # - # The array holds all available validators. - # "displayName" is planned for the form wizard (not implemented right now). - registeredValidators { - alphabetic { - displayName = Alphabetic - className = TYPO3\CMS\Form\Domain\Validator\AlphabeticValidator - } - - alphanumeric { - displayName = Alphanumeric - className = TYPO3\CMS\Form\Domain\Validator\AlphanumericValidator - } - - between { - displayName = Between - className = TYPO3\CMS\Form\Domain\Validator\BetweenValidator - } - - date { - displayName = Date - className = TYPO3\CMS\Form\Domain\Validator\DateValidator - } - - digit { - displayName = Digit - className = TYPO3\CMS\Form\Domain\Validator\DigitValidator - } - - email { - displayName = Email address - className = TYPO3\CMS\Form\Domain\Validator\EmailValidator - } - - equals { - displayName = Equals - className = TYPO3\CMS\Form\Domain\Validator\EqualsValidator - } - - fileallowedtypes { - displayName = Allowed mimetypes for file - className = TYPO3\CMS\Form\Domain\Validator\FileAllowedTypesValidator - } - - filemaximumsize { - displayName = Maximum size for file (bytes) - className = TYPO3\CMS\Form\Domain\Validator\FileMaximumSizeValidator - } - - fileminimumsize { - displayName = Minimum size for file (bytes) - className = TYPO3\CMS\Form\Domain\Validator\FileMinimumSizeValidator - } - - float { - displayName = Float - className = TYPO3\CMS\Form\Domain\Validator\FloatValidator - } - - greaterthan { - displayName = Greater than - className = TYPO3\CMS\Form\Domain\Validator\GreaterThanValidator - } - - inarray { - displayName = In array - className = TYPO3\CMS\Form\Domain\Validator\InArrayValidator - } - - integer { - displayName = Integer - className = TYPO3\CMS\Form\Domain\Validator\IntegerValidator - } - - ip { - displayName = Ip address - className = TYPO3\CMS\Form\Domain\Validator\IpValidator - } - - length { - displayName = Length - className = TYPO3\CMS\Form\Domain\Validator\LengthValidator - } - - lessthan { - displayName = Less than - className = TYPO3\CMS\Form\Domain\Validator\LessThanValidator - } - - regexp { - displayName = Regular Expression - className = TYPO3\CMS\Form\Domain\Validator\RegExpValidator - } - - required { - displayName = Required - className = TYPO3\CMS\Form\Domain\Validator\RequiredValidator - } - - uri { - displayName = Uniform Resource Identifier - className = TYPO3\CMS\Form\Domain\Validator\UriValidator - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/backend.txt b/typo3/sysext/form/Configuration/TypoScript/backend.txt new file mode 100644 index 000000000000..20604aa8aec7 --- /dev/null +++ b/typo3/sysext/form/Configuration/TypoScript/backend.txt @@ -0,0 +1,15 @@ +module.tx_form { + settings { + yamlConfigurations { + 10 = EXT:form/Configuration/Yaml/BaseSetup.yaml + 20 = EXT:form/Configuration/Yaml/FormEditorSetup.yaml + 30 = EXT:form/Configuration/Yaml/FormEngineSetup.yaml + } + } + + view { + templateRootPaths.10 = EXT:form/Resources/Private/Backend/Templates/ + partialRootPaths.10 = EXT:form/Resources/Private/Backend/Partials/ + layoutRootPaths.10 = EXT:form/Resources/Private/Backend/Layouts/ + } +} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/constants.txt b/typo3/sysext/form/Configuration/TypoScript/constants.txt deleted file mode 100644 index 55c53277bd23..000000000000 --- a/typo3/sysext/form/Configuration/TypoScript/constants.txt +++ /dev/null @@ -1,10 +0,0 @@ -plugin.tx_form { - view { - # cat=plugin.tx_form/file; type=string; label=Path to template root (FE) - templateRootPath = EXT:form/Resources/Private/Templates/ - # cat=plugin.tx_form/file; type=string; label=Path to template partials (FE) - partialRootPath = EXT:form/Resources/Private/Partials/ - # cat=plugin.tx_form/file; type=string; label=Path to template layouts (FE) - layoutRootPath = EXT:form/Resources/Private/Layouts/ - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/TypoScript/setup.txt b/typo3/sysext/form/Configuration/TypoScript/setup.txt index acb530668a11..172a79d81ffb 100644 --- a/typo3/sysext/form/Configuration/TypoScript/setup.txt +++ b/typo3/sysext/form/Configuration/TypoScript/setup.txt @@ -1,256 +1,26 @@ - # Replace rendering of old mailform -tt_content.mailform > -tt_content.mailform = COA -tt_content.mailform.10 = < lib.stdheader -tt_content.mailform.20 = FORM -tt_content.mailform.20 { - stdWrap.wrap = <div class="csc-mailform">|</div> - stdWrap { - editIcons = tt_content: bodytext - editIcons.iconTitle.data = LLL:EXT:css_styled_content/pi1/locallang.xlf:eIcon.form - prefixComment = 2 | Mail form inserted: - } -} - - # Include model definition for all available form elements -<INCLUDE_TYPOSCRIPT: source="DIR: EXT:form/Configuration/TypoScript/Elements" extensions="ts"> - # Include definition of filters -<INCLUDE_TYPOSCRIPT: source="DIR: EXT:form/Configuration/TypoScript/Filters" extensions="ts"> - # Include definition of filters -<INCLUDE_TYPOSCRIPT: source="DIR: EXT:form/Configuration/TypoScript/Validators" extensions="ts"> - plugin.tx_form { - features { - skipDefaultArguments = 1 - } - - view { - templateRootPaths { - 10 = {$plugin.tx_form.view.templateRootPath} - } - - partialRootPaths { - 10 = {$plugin.tx_form.view.partialRootPath} - } - - layoutRootPaths { - 10 = {$plugin.tx_form.view.layoutRootPath} - } - } - - _CSS_DEFAULT_STYLE ( - div.csc-mailform ol { - list-style-type: none; - } - - div.csc-mailform ol, - div.csc-mailform ol li { - margin: 0; - padding: 0; - } - - div.csc-mailform ol li { - overflow: hidden; - } - - div.csc-mailform fieldset { - margin: 0; - padding: 0; - position: relative; - } - - div.csc-mailform legend { - margin-left: 1em; - color: #000000; - font-weight: bold; - } - - div.csc-mailform fieldset ol { - padding: 1em 1em 0 1em; - } - - div.csc-mailform fieldset li { - padding: 0.5em; - margin-bottom: 0.5em; - list-style: none; - } - - div.csc-mailform fieldset.submit { - border-style: none; - } - - /** - * Normal label - * Left aligned, in front of input - */ - div.csc-mailform li label { - float: left; - width: 13em; - margin-right: 1em; - vertical-align: baseline; - } - - div.csc-mailform li input + label, - div.csc-mailform li textarea + label, - div.csc-mailform li select + label { - float: none; - width: auto; - margin-right: 0; - margin-left: 1em; - } - - div.csc-mailform li textarea + label { - vertical-align: top; - } - - label em, - legend em { - display: block; - color: #060; - font-size: 85%; - font-style: normal; - text-transform: uppercase; - } - - legend em { - position: absolute; - } - - label strong, - legend strong { - display: block; - color: #C00; - font-size: 85%; - font-weight: normal; - text-transform: uppercase; - } - - legend strong { - position: absolute; - top: 1.4em; - } - - /** - * Labels alignment right - */ - .labels-alignment-right label, - .labels-alignment-right .fieldset-subgroup legend, - .labels-alignment-right.fieldset-subgroup legend { - text-align: right; - } - - /** - * Horizontal fieldset - */ - fieldset.fieldset-horizontal { - border-width: 0; - } - - fieldset.fieldset-horizontal ol { - padding: 0; - } - - fieldset.fieldset-horizontal li { - float: left; - padding: 0; - margin-right: 1em; - } - - fieldset.fieldset-horizontal.label-below label { - display: block; - margin-left: 0; - margin-top: 0.2em; - font-size: 90%; - color: #999999; - text-align: left; - } - - fieldset.fieldset-horizontal label em { - display: inline; - } - - /** - * Subgroup fieldset - */ - fieldset.fieldset-subgroup { - margin-bottom: -2em; - border-style: none; - } - - fieldset.fieldset-subgroup legend { - margin-left: 0; - padding: 0; - font-weight: normal; - width: 13em; - } - - fieldset.fieldset-subgroup ol { - position: relative; - top: -1.4em; - margin: 0 0 0 14em; - padding: 0; - } - - fieldset.fieldset-subgroup li { - padding: 0; - } - - fieldset.fieldset-subgroup input + label { - float: none; - width: auto; - display: inline; - margin: 0 0 0 1em; - } - - /** - * Labels as block - * Labels displayed above or below the input fields - */ - .labels-block label { - display: block; - float: none; - margin: 0 0 0.5em; - width: auto; - } - - .labels-block input + label, - .labels-block textarea + label { - margin: 0.5em 0 0; - } - - .labels-block fieldset.fieldset-subgroup, - fieldset.labels-block.fieldset-subgroup { - margin-bottom: 0; - } - - .labels-block .fieldset-subgroup legend, - .labels-block.fieldset-subgroup legend { - width: auto; - } - - .labels-block .fieldset-subgroup legend em, - .labels-block.fieldset-subgroup legend em { - position: relative; - } - - .labels-block .fieldset-subgroup legend strong, - .labels-block.fieldset-subgroup legend strong { - position: relative; - top: 0; - } - - .labels-block .fieldset-subgroup ol, - .labels-block.fieldset-subgroup ol { - top: 0; - margin: 0; - padding: 0.5em 0 0; - } - - /** - * hide hidden elements - */ - .csc-form-element-hidden { - display: none; - } - ) + view { + templateRootPaths.5 = EXT:form/Resources/Private/Frontend/Templates/ + partialRootPaths.5 = EXT:form/Resources/Private/Frontend/Partials/ + layoutRootPaths.5 = EXT:form/Resources/Private/Frontend/Layouts/ + } + + mvc { + callDefaultActionIfActionCantBeResolved = 1 + } + + settings { + yamlConfigurations { + 10 = EXT:form/Configuration/Yaml/BaseSetup.yaml + 20 = EXT:form/Configuration/Yaml/FormEngineSetup.yaml + } + } } + +# Rendering of content elements +lib.tx_form.contentElementRendering = RECORDS +lib.tx_form.contentElementRendering { + tables = tt_content + source.current = 1 + dontCheckPid = 1 +} \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/UserTSconfig/userTSConfig.txt b/typo3/sysext/form/Configuration/UserTSconfig/userTSConfig.txt deleted file mode 100644 index bb99a382ec23..000000000000 --- a/typo3/sysext/form/Configuration/UserTSconfig/userTSConfig.txt +++ /dev/null @@ -1,2 +0,0 @@ -# Enable the FORM wizard by default for all users -setup.default.tx_form.showWizardByDefault = 1 diff --git a/typo3/sysext/form/Configuration/Yaml/BaseSetup.yaml b/typo3/sysext/form/Configuration/Yaml/BaseSetup.yaml new file mode 100644 index 000000000000..dacc84b9cffb --- /dev/null +++ b/typo3/sysext/form/Configuration/Yaml/BaseSetup.yaml @@ -0,0 +1,340 @@ +TYPO3: + CMS: + Form: + persistenceManager: + allowedFileMounts: + 10: 1:/user_upload/ + allowSaveToExtensionPaths: false + #allowedExtensionPaths: + #10: EXT:example/Resources/Private/Forms/ + + prototypes: + standard: + + ########### DEFAULT FORM ELEMENT DEFINITIONS ########### + formElementsDefinition: + + ### BASE ELEMENTS ### + Form: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin' + rendererClassName: 'TYPO3\CMS\Form\Domain\Renderer\FluidFormRenderer' + renderingOptions: + renderableNameInTemplate: 'form' + honeypot: + enable: true + formElementToUse: 'Honeypot' + + ### FORM ELEMENTS: CONTAINER ### + Page: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin' + implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\Page' + renderingOptions: + renderableNameInTemplate: 'page' + + SummaryPage: + __inheritances: + 10: 'TYPO3.CMS.Form.prototypes.standard.formElementsDefinition.Page' + + Fieldset: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin' + implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\Section' + renderingOptions: + renderableNameInTemplate: 'section' + + ### FORM ELEMENTS: INPUT ### + Text: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin' + + Password: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin' + + AdvancedPassword: + __inheritances: + 10: 'TYPO3.CMS.Form.prototypes.standard.formElementsDefinition.Password' + implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\AdvancedPassword' + properties: + elementClassAttribute: 'input-medium' + confirmationLabel: '' + # Optional description (hint) for the first password input element + #passwordDescription: '' + confirmationClassAttribute: 'input-medium' + + Textarea: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin' + properties: + elementClassAttribute: 'xxlarge' + + Honeypot: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.TextMixin' + properties: + renderAsHiddenField: false + styleAttribute: 'position:absolute; margin:0 0 0 -999em;' + + ### FORM ELEMENTS: SELECT ### + Checkbox: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin' + properties: + elementClassAttribute: 'add-on' + value: 1 + + MultiCheckbox: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.MultiSelectionMixin' + + MultiSelect: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.MultiSelectionMixin' + properties: + elementClassAttribute: 'xlarge' + + RadioButton: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.SingleSelectionMixin' + properties: + elementClassAttribute: 'xlarge' + + SingleSelect: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.SingleSelectionMixin' + + ### FORM ELEMENTS: CUSTOM ### + DatePicker: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin' + implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\DatePicker' + properties: + elementClassAttribute: 'small' + timeSelectorClassAttribute: 'mini' + dateFormat: 'Y-m-d' + enableDatePicker: true + displayTimeSelector: false + + StaticText: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.ReadOnlyFormElementMixin' + properties: + text: '' + + Hidden: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin' + + ContentElement: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.ReadOnlyFormElementMixin' + properties: + contentElementUid: '' + + ### FORM ELEMENTS: UPLOADS ### + FileUpload: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.FileUploadMixin' + properties: + allowedMimeTypes: ['application/doc', 'application/docx', 'application/odt', 'application/pdf'] + + ImageUpload: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.FileUploadMixin' + properties: + allowedMimeTypes: ['image/jpeg', 'image/png', 'image/bmp'] + elementClassAttribute: 'lightbox' + imageLinkMaxWidth: 500 + imageMaxWidth: 500 + imageMaxHeight: 500 + + ### FINISHERS ### + + finishersDefinition: + Closure: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.finishersTranslationSettingsMixin' + implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\ClosureFinisher' + options: + #closure: + + Confirmation: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.finishersTranslationSettingsMixin' + implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\ConfirmationFinisher' + #options: + #message: '' + + EmailToSender: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.finishersEmailMixin' + + EmailToReceiver: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.finishersEmailMixin' + + DeleteUploads: + implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\DeleteUploadsFinisher' + + FlashMessage: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.finishersTranslationSettingsMixin' + implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\FlashMessageFinisher' + #options: + #messageBody: '' + #messageTitle: '' + #messageArguments: {} + #messageCode: 0 + #severity: 0 + + Redirect: + implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\RedirectFinisher' + #options: + #pageUid: 1 + #additionalParameters: '' + #delay: 0 + #statusCode: 303 + + SaveToDatabase: + implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\SaveToDatabaseFinisher' + #options: + #table: '' + #elements: + # <elementIdentifier>: + # mapOnDatabaseColumn: sender_name + # saveFileIdentifierInsteadOfUid: 'false' + + ### VALIDATORS ### + validatorsDefinition: + NotEmpty: + implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\NotEmptyValidator' + DateTime: + implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\DateTimeValidator' + Alphanumeric: + implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\AlphanumericValidator' + Text: + implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\TextValidator' + StringLength: + implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\StringLengthValidator' + #options: + #minimum: 0 + #maximum: 0 + EmailAddress: + implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\EmailAddressValidator' + Integer: + implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\IntegerValidator' + Float: + implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\FloatValidator' + NumberRange: + implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\NumberRangeValidator' + #options: + #minimum: 0 + #maximum: 0 + RegularExpression: + implementationClassName: 'TYPO3\CMS\Extbase\Validation\Validator\RegularExpressionValidator' + #options: + #regularExpression: '/^.*$/' + Count: + implementationClassName: 'TYPO3\CMS\Form\Mvc\Validation\CountValidator' + #options: + #minimum: 0 + #maximum: 0 + + ########### MIXINS ########### + mixins: + translationSettingsMixin: + translation: + translationFile: 'EXT:form/Resources/Private/Language/locallang.xlf' + translatePropertyValueIfEmpty: true + + finishersTranslationSettingsMixin: + options: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.translationSettingsMixin' + + ########### FORM ELEMENT MIXINS ########### + formElementMixins: + BaseFormElementMixin: + renderingOptions: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.translationSettingsMixin' + + templateRootPaths: + 10: 'EXT:form/Resources/Private/Frontend/Templates/FormElements/' + partialRootPaths: + 10: 'EXT:form/Resources/Private/Frontend/Partials/FormElements/' + layoutRootPaths: + 10: 'EXT:form/Resources/Private/Frontend/Layouts/FormElements/' + + # It is possible to set a full path to a template e.g. for custom elements + # In this case 'templateRootPaths' will be ignored + # templatePathAndFilename: 'EXT:form/Resources/Private/Frontend/Templates/FormElements/RadioButton.html' + + # set this to TRUE if you want to avoid exceptions for FormElements without definitions + skipUnknownElements: true + + ReadOnlyFormElementMixin: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin' + implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement' + renderingOptions: + renderableNameInTemplate: 'element' + + FormElementMixin: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseFormElementMixin' + implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\GenericFormElement' + properties: + containerClassAttribute: 'input' + elementClassAttribute: '' + elementErrorClassAttribute: 'error' + renderingOptions: + renderableNameInTemplate: 'element' + + TextMixin: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin' + + SelectionMixin: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin' + + SingleSelectionMixin: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.SelectionMixin' + + MultiSelectionMixin: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.SelectionMixin' + + FileUploadMixin: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.FormElementMixin' + implementationClassName: 'TYPO3\CMS\Form\Domain\Model\FormElements\FileUpload' + properties: + saveToFileMount: '1:/user_upload/' + + finishersEmailMixin: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.finishersTranslationSettingsMixin' + implementationClassName: 'TYPO3\CMS\Form\Domain\Finishers\EmailFinisher' + options: + #subject: '' + #recipientAddress: '' + #recipientName: '' + #senderAddress: + #senderName: '' + #replyToAddress: '' + #carbonCopyAddress: '' + #blindCarbonCopyAddress: '' + #format: 'html' + #attachUploads: true + #translation: + # language: 'default' + # {@format} depends the 'format' value + templatePathAndFilename: 'EXT:form/Resources/Private/Frontend/Templates/Finishers/Email/{@format}.html' + #partialRootPaths: [] + #layoutRootPaths: [] + #variables: {} diff --git a/typo3/sysext/form/Configuration/Yaml/FormEditorSetup.yaml b/typo3/sysext/form/Configuration/Yaml/FormEditorSetup.yaml new file mode 100644 index 000000000000..98edc7625355 --- /dev/null +++ b/typo3/sysext/form/Configuration/Yaml/FormEditorSetup.yaml @@ -0,0 +1,1005 @@ +TYPO3: + CMS: + Form: + ########### FORM MANAGER CONFIGURATION ########### + formManager: + dynamicRequireJsModules: + app: 'TYPO3/CMS/Form/Backend/FormManager' + viewModel: 'TYPO3/CMS/Form/Backend/FormManager/ViewModel' + stylesheets: + 100: 'EXT:form/Resources/Public/Css/form.css' + translationFile: 'EXT:form/Resources/Private/Language/Database.xlf' + javaScriptTranslationFile: 'EXT:form/Resources/Private/Language/locallang_formManager_javascript.xlf' + selectablePrototypesConfiguration: + 100: + identifier: 'standard' + label: 'formManager.selectablePrototypesConfiguration.standard.label' + newFormTemplates: + 100: + templatePath: 'EXT:form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/BlankForm.yaml' + label: 'formManager.selectablePrototypesConfiguration.standard.newFormTemplates.blankForm.label' + 200: + templatePath: 'EXT:form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/SimpleContactForm.yaml' + label: 'formManager.selectablePrototypesConfiguration.standard.newFormTemplates.simpleContactForm.label' + controller: + deleteAction: + errorTitle: 'formManagerController.deleteAction.error.title' + errorMessage: 'formManagerController.deleteAction.error.body' + + ########### FORMEDITOR CONFIGURATION ########### + prototypes: + standard: + formEditor: + translationFile: 'EXT:form/Resources/Private/Language/Database.xlf' + dynamicRequireJsModules: + app: 'TYPO3/CMS/Form/Backend/FormEditor' + mediator: 'TYPO3/CMS/Form/Backend/FormEditor/Mediator' + viewModel: 'TYPO3/CMS/Form/Backend/FormEditor/ViewModel' + additionalViewModelModules: + + addInlineSettings: [] + maximumUndoSteps: 10 + + stylesheets: + # @toDo: move form.less to the TYPO3 Build folder + 200: 'EXT:form/Resources/Public/Css/form.css' + + formEditorTemplates: + templateRootPaths: + 10: 'EXT:form/Resources/Private/Backend/Templates/FormEditor/' + partialRootPaths: + 10: 'EXT:form/Resources/Private/Backend/Partials/FormEditor/' + layoutRootPaths: + 10: 'EXT:form/Resources/Private/Backend/Layouts/FormEditor/' + + # Element options editors + FormElement-_ElementToolbar: 'Stage/_ElementToolbar' + FormElement-_UnknownElement: 'Stage/_UnknownElement' + FormElement-Page: 'Stage/Page' + FormElement-SummaryPage: 'Stage/SummaryPage' + FormElement-Fieldset: 'Stage/Fieldset' + FormElement-Text: 'Stage/Text' + FormElement-Password: 'Stage/Password' + FormElement-AdvancedPassword: 'Stage/AdvancedPassword' + FormElement-Textarea: 'Stage/Textarea' + FormElement-Checkbox: 'Stage/Checkbox' + FormElement-MultiCheckbox: 'Stage/MultiCheckbox' + FormElement-MultiSelect: 'Stage/MultiSelect' + FormElement-RadioButton: 'Stage/RadioButton' + FormElement-SingleSelect: 'Stage/SingleSelect' + FormElement-DatePicker: 'Stage/DatePicker' + FormElement-StaticText: 'Stage/StaticText' + FormElement-Hidden: 'Stage/Hidden' + FormElement-ContentElement: 'Stage/ContentElement' + FormElement-FileUpload: 'Stage/FileUpload' + FormElement-ImageUpload: 'Stage/ImageUpload' + + Modal-InsertElements: 'Modals/InsertElements' + Modal-InsertPages: 'Modals/InsertPages' + Modal-ValidationErrors: 'Modals/ValidationErrors' + + Inspector-FormElementHeaderEditor: 'Inspector/FormElementHeaderEditor' + Inspector-CollectionElementHeaderEditor: 'Inspector/CollectionElementHeaderEditor' + Inspector-TextEditor: 'Inspector/TextEditor' + Inspector-PropertyGridEditor: 'Inspector/PropertyGridEditor' + Inspector-SingleSelectEditor: 'Inspector/SingleSelectEditor' + Inspector-TextareaEditor: 'Inspector/TextareaEditor' + Inspector-RemoveElementEditor: 'Inspector/RemoveElementEditor' + Inspector-FinishersEditor: 'Inspector/FinishersEditor' + Inspector-ValidatorsEditor: 'Inspector/ValidatorsEditor' + Inspector-RequiredValidatorEditor: 'Inspector/RequiredValidatorEditor' + Inspector-CheckboxEditor: 'Inspector/CheckboxEditor' + Inspector-Typo3WinBrowserEditor: 'Inspector/Typo3WinBrowserEditor' + + formElementPropertyValidatorsDefinition: + NotEmpty: + errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.NotEmpty.label' + Integer: + errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.Integer.label' + NaiveEmail: + errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.NaiveEmail.label' + NaiveEmailOrEmpty: + errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.NaiveEmail.label' + FormElementIdentifierWithinCurlyBracesInclusive: + errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.FormElementIdentifierWithinCurlyBraces.label' + FormElementIdentifierWithinCurlyBracesExclusive: + errorMessage: 'formEditor.formElementPropertyValidatorsDefinition.FormElementIdentifierWithinCurlyBraces.label' + + formElementGroups: + input: + label: 'formEditor.formElementGroups.input.label' + select: + label: 'formEditor.formElementGroups.select.label' + custom: + label: 'formEditor.formElementGroups.custom.label' + container: + label: 'formEditor.formElementGroups.container.label' + page: + label: 'formEditor.formElementGroups.page.label' + + ########### DEFAULT FORM ELEMENT DEFINITIONS ########### + formElementsDefinition: + Form: + formEditor: + _isCompositeFormElement: false + _isTopLevelFormElement: true + + saveSuccessFlashMessageTitle: 'formEditor.elements.Form.saveSuccessFlashMessageTitle' + saveSuccessFlashMessageMessage: 'formEditor.elements.Form.saveSuccessFlashMessageMessage' + + modalValidationErrorsDialogTitle: 'formEditor.modals.validationErrors.dialogTitle' + modalValidationErrorsConfirmButton: 'formEditor.modals.validationErrors.confirmButton' + + modalInsertElementsDialogTitle: 'formEditor.modals.insertElements.dialogTitle' + modalInsertPagesDialogTitle: 'formEditor.modals.newPages.dialogTitle' + + modalCloseDialogMessage: 'formEditor.modals.close.dialogMessage' + modalCloseDialogTitle: 'formEditor.modals.close.dialogTitle' + modalCloseConfirmButton: 'formEditor.modals.close.confirmButton' + modalCloseCancleButton: 'formEditor.modals.close.cancleButton' + + modalRemoveElementDialogTitle: 'formEditor.modals.removeElement.dialogTitle' + modalRemoveElementDialogMessage: 'formEditor.modals.removeElement.dialogMessage' + modalRemoveElementConfirmButton: 'formEditor.modals.removeElement.confirmButton' + modalRemoveElementCancleButton: 'formEditor.modals.removeElement.cancleButton' + modalRemoveElementLastAvailablePageFlashMessageTitle: 'formEditor.modals.removeElement.lastAvailablePageFlashMessageTitle' + modalRemoveElementLastAvailablePageFlashMessageMessage: 'formEditor.modals.removeElement.lastAvailablePageFlashMessageMessage' + + paginationTitle: 'formEditor.pagination.title' + + iconIdentifier: 'content-elements-mailform' + predefinedDefaults: + editors: + 900: + identifier: 'finishers' + templateName: 'Inspector-FinishersEditor' + label: 'formEditor.elements.Form.editor.finishers.label' + selectOptions: + 10: + value: '' + label: 'formEditor.elements.Form.editor.finishers.EmptyValue.label' + 20: + value: 'EmailToSender' + label: 'formEditor.elements.Form.editor.finishers.EmailToSender.label' + 30: + value: 'EmailToReceiver' + label: 'formEditor.elements.Form.editor.finishers.EmailToReceiver.label' + 40: + value: 'Redirect' + label: 'formEditor.elements.Form.editor.finishers.Redirect.label' + 50: + value: 'DeleteUploads' + label: 'formEditor.elements.Form.editor.finishers.DeleteUploads.label' + + propertyCollections: + finishers: + 10: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.formEmailFinisherMixin' + identifier: 'EmailToSender' + + 20: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.formEmailFinisherMixin' + identifier: 'EmailToReceiver' + editors: + 100: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.header.label' + 200: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.subject.label' + 300: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientAddress.label' + 400: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientName.label' + 500: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.senderAddress.label' + 600: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.senderName.label' + 700: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.replyToAddress.label' + 800: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.carbonCopyAddress.label' + 900: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.blindCarbonCopyAddress.label' + 1000: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.format.label' + 1100: + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.attachUploads.label' + 1200: + identifier: 'language' + templateName: 'Inspector-SingleSelectEditor' + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.language.label' + propertyPath: 'options.translation.language' + selectOptions: + 10: + value: 'default' + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.language.1' + + 30: + identifier: 'Redirect' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.Form.finisher.Redirect.editor.header.label' + 200: + identifier: 'pageUid' + templateName: 'Inspector-Typo3WinBrowserEditor' + label: 'formEditor.elements.Form.finisher.Redirect.editor.pageUid.label' + buttonLabel: 'formEditor.elements.Form.finisher.Redirect.editor.pageUid.buttonLabel' + browsableType: pages + propertyPath: 'options.pageUid' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'Integer' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + + 300: + identifier: 'additionalParameters' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.Form.finisher.Redirect.editor.additionalParameters.label' + propertyPath: 'options.additionalParameters' + + 40: + identifier: 'DeleteUploads' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.Form.finisher.DeleteUploads.editor.header.label' + + 50: + identifier: 'Confirmation' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.Form.finisher.Confirmation.editor.header.label' + + 60: + identifier: 'Closure' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.Form.finisher.Closure.editor.header.label' + + 70: + identifier: 'FlashMessage' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.Form.finisher.FlashMessage.editor.header.label' + + 80: + identifier: 'SaveToDatabase' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.Form.finisher.SaveToDatabase.editor.header.label' + + ### FORM ELEMENTS: CONTAINER ### + Fieldset: + formEditor: + label: 'formEditor.elements.Fieldset.label' + group: container + groupSorting: 100 + _isCompositeFormElement: true + iconIdentifier: 't3-form-icon-fieldset' + editors: + 200: + label: 'formEditor.elements.Fieldset.editor.label.label' + 800: null + + ### FORM ELEMENTS: PAGE TYPES ### + Page: + formEditor: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.RemovableFormElementMixin' + predefinedDefaults: + label: 'formEditor.elements.Page.label' + group: page + groupSorting: 100 + _isTopLevelFormElement: true + _isCompositeFormElement: true + iconIdentifier: 't3-form-icon-page' + editors: + 200: + label: 'formEditor.elements.Page.editor.label.label' + + SummaryPage: + formEditor: + predefinedDefaults: + label: 'formEditor.elements.SummaryPage.label' + group: page + groupSorting: 200 + _isTopLevelFormElement: true + _isCompositeFormElement: false + iconIdentifier: 't3-form-icon-summary-page' + editors: + 200: + label: 'formEditor.elements.SummaryPage.editor.label.label' + + ### FORM ELEMENTS: INPUT ### + + Text: + formEditor: + label: 'formEditor.elements.Text.label' + group: input + groupSorting: 100 + iconIdentifier: 't3-form-icon-text' + + Password: + formEditor: + label: 'formEditor.elements.Password.label' + group: input + groupSorting: 300 + iconIdentifier: 't3-form-icon-password' + + AdvancedPassword: + formEditor: + label: 'formEditor.elements.AdvancedPassword.label' + group: input + groupSorting: 400 + predefinedDefaults: + properties: + confirmationLabel: 'formEditor.element.AdvancedPassword.editor.confirmationLabel.predefinedDefaults' + defaultValue: null + iconIdentifier: 't3-form-icon-advanced-password' + editors: + 300: + identifier: 'confirmationLabel' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.AdvancedPassword.editor.confirmationLabel.label' + propertyPath: 'properties.confirmationLabel' + 500: null + + Textarea: + formEditor: + label: 'formEditor.elements.Textarea.label' + group: input + groupSorting: 200 + iconIdentifier: 't3-form-icon-textarea' + + ### FORM ELEMENTS: SELECT ### + Checkbox: + formEditor: + label: 'formEditor.elements.Checkbox.label' + group: select + groupSorting: 100 + iconIdentifier: 't3-form-icon-checkbox' + + MultiCheckbox: + formEditor: + label: 'formEditor.elements.MultiCheckbox.label' + group: select + groupSorting: 500 + iconIdentifier: 't3-form-icon-multi-checkbox' + + MultiSelect: + formEditor: + label: 'formEditor.elements.MultiSelect.label' + group: select + groupSorting: 400 + iconIdentifier: 't3-form-icon-multi-select' + + RadioButton: + formEditor: + label: 'formEditor.elements.RadioButton.label' + group: select + groupSorting: 300 + iconIdentifier: 't3-form-icon-radio-button' + + SingleSelect: + formEditor: + label: 'formEditor.elements.SingleSelect.label' + group: select + groupSorting: 200 + iconIdentifier: 't3-form-icon-single-select' + + ### FORM ELEMENTS: CUSTOM ### + DatePicker: + formEditor: + label: 'formEditor.elements.DatePicker.label' + group: custom + groupSorting: 100 + iconIdentifier: 't3-form-icon-date-picker' + editors: + 900: + identifier: 'validators' + templateName: 'Inspector-ValidatorsEditor' + label: 'formEditor.elements.DatePicker.editor.validators.label' + selectOptions: + 10: + value: '' + label: 'formEditor.elements.DatePicker.editor.validators.EmptyValue.label' + 20: + value: 'DateTime' + label: 'formEditor.elements.DatePicker.editor.validators.DateTime.label' + + propertyCollections: + validators: + 10: + identifier: 'DateTime' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.DatePicker.validators.DateTime.editor.header.label' + + StaticText: + formEditor: + label: 'formEditor.elements.StaticText.label' + group: custom + groupSorting: 400 + predefinedDefaults: + properties: + text: '' + iconIdentifier: 't3-form-icon-static-text' + editors: + 300: + identifier: 'staticText' + templateName: 'Inspector-TextareaEditor' + label: 'formEditor.elements.StaticText.editor.staticText.label' + propertyPath: 'properties.text' + + ContentElement: + formEditor: + label: 'formEditor.elements.ContentElement.label' + group: custom + groupSorting: 500 + predefinedDefaults: + properties: + contentElementUid: '' + iconIdentifier: 't3-form-icon-content-element' + editors: + 200: null + 300: + identifier: 'staticText' + templateName: 'Inspector-Typo3WinBrowserEditor' + label: 'formEditor.elements.StaticText.editor.contentElement.label' + buttonLabel: 'formEditor.elements.StaticText.editor.contentElement.buttonLabel' + browsableType: tt_content + propertyPath: 'properties.contentElementUid' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'Integer' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + + ### FORM ELEMENTS: UPLOADS ### + FileUpload: + formEditor: + label: 'formEditor.elements.FileUpload.label' + group: custom + groupSorting: 200 + predefinedDefaults: + properties: + allowedMimeTypes: ['application/doc', 'application/docx', 'application/odt', 'application/pdf'] + iconIdentifier: 't3-form-icon-file-upload' + editors: + 300: + identifier: 'allowedMimeTypes' + templateName: 'Inspector-SingleSelectEditor' + label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.label' + propertyPath: 'properties.allowedMimeTypes' + selectOptions: + 10: + value: ['application/doc', 'application/docx', 'application/odt', 'application/pdf'] + label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.1' + 20: + value: ['application/xls'] + label: 'formEditor.elements.FileUpload.editor.allowedMimeTypes.2' + + ImageUpload: + formEditor: + label: 'formEditor.elements.ImageUpload.label' + group: custom + groupSorting: 300 + predefinedDefaults: + properties: + allowedMimeTypes: ['image/jpeg', 'image/png', 'image/bmp'] + iconIdentifier: 't3-form-icon-image-upload' + + ### FINISHERS ### + finishersDefinition: + EmailToSender: + formEditor: + iconIdentifier: 't3-form-icon-finisher' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.header.label' + predefinedDefaults: + options: + subject: '' + recipientAddress: '' + recipientName: '' + senderAddress: '' + senderName: '' + replyToAddress: '' + carbonCopyAddress: '' + blindCarbonCopyAddress: '' + format: 'html' + attachUploads: true + + EmailToReceiver: + formEditor: + iconIdentifier: 't3-form-icon-finisher' + label: 'formEditor.elements.Form.finisher.EmailToReceiver.editor.header.label' + predefinedDefaults: + options: + subject: '' + recipientAddress: '' + recipientName: '' + senderAddress: '' + senderName: '' + replyToAddress: '' + carbonCopyAddress: '' + blindCarbonCopyAddress: '' + format: 'html' + attachUploads: true + translation: + language: '' + + Redirect: + formEditor: + iconIdentifier: 't3-form-icon-finisher' + label: 'formEditor.elements.Form.finisher.Redirect.editor.header.label' + predefinedDefaults: + options: + pageUid: '' + additionalParameters: '' + + Closure: + formEditor: + iconIdentifier: 't3-form-icon-finisher' + label: 'formEditor.elements.Form.finisher.Closure.editor.header.label' + predefinedDefaults: + options: + closure: '' + + Confirmation: + formEditor: + iconIdentifier: 't3-form-icon-finisher' + label: 'formEditor.elements.Form.finisher.Confirmation.editor.header.label' + predefinedDefaults: + options: + message: '' + + FlashMessage: + formEditor: + iconIdentifier: 't3-form-icon-finisher' + label: 'formEditor.elements.Form.finisher.FlashMessage.editor.header.label' + predefinedDefaults: + options: + messageBody: '' + messageTitle: '' + messageArguments: '' + messageCode: 0 + severity: 0 + + SaveToDatabase: + formEditor: + iconIdentifier: 't3-form-icon-finisher' + label: 'formEditor.elements.Form.finisher.SaveToDatabase.editor.header.label' + predefinedDefaults: + options: + table: '' + elements: + + DeleteUploads: + formEditor: + iconIdentifier: 't3-form-icon-finisher' + label: 'formEditor.elements.Form.finisher.DeleteUploads.editor.header.label' + + ### VALIDATORS ### + validatorsDefinition: + NotEmpty: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label : 'formEditor.elements.FormElement.editor.requiredValidator.label' + DateTime: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label: 'formEditor.elements.DatePicker.validators.DateTime.editor.header.label' + Alphanumeric: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label: 'formEditor.elements.TextMixin.editor.validators.Alphanumeric.label' + Text: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label: 'formEditor.elements.TextMixin.editor.validators.Text.label' + StringLength: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label: 'formEditor.elements.TextMixin.editor.validators.StringLength.label' + predefinedDefaults: + options: + minimum: '' + maximum: '' + EmailAddress: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label: 'formEditor.elements.TextMixin.editor.validators.EmailAddress.label' + Integer: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label: 'formEditor.elements.TextMixin.editor.validators.Integer.label' + Float: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label: 'formEditor.elements.TextMixin.editor.validators.Float.label' + NumberRange: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label: 'formEditor.elements.TextMixin.editor.validators.NumberRange.label' + predefinedDefaults: + options: + minimum: '' + maximum: '' + RegularExpression: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label: 'formEditor.elements.TextMixin.editor.validators.RegularExpression.label' + predefinedDefaults: + options: + regularExpression: '' + Count: + formEditor: + iconIdentifier: 't3-form-icon-validator' + label: 'formEditor.elements.MultiSelectionMixin.validators.Count.editor.header.label' + predefinedDefaults: + options: + minimum: '' + maximum: '' + + ########### MIXINS ########### + mixins: + ########### FORM ELEMENT MIXINS ########### + formElementMixins: + BaseFormElementMixin: + formEditor: + predefinedDefaults: + editors: + 100: + identifier: 'header' + templateName: 'Inspector-FormElementHeaderEditor' + 200: + identifier: 'label' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.BaseFormElementMixin.editor.label.label' + propertyPath: 'label' + + RemoveButtonMixin: + 9999: + identifier: 'removeButton' + templateName: 'Inspector-RemoveElementEditor' + + RemovableFormElementMixin: + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.RemoveButtonMixin' + + BaseCollectionEditorsMixin: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.RemoveButtonMixin' + 100: + identifier: 'header' + templateName: 'Inspector-CollectionElementHeaderEditor' + label: '' + + MinimumMaximumEditorsMixin: + 200: + identifier: 'minimum' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.MinimumMaximumEditorsMixin.editor.minimum.label' + propertyPath: 'options.minimum' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'Integer' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + 300: + identifier: 'maximum' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.MinimumMaximumEditorsMixin.editor.maximum.label' + propertyPath: 'options.maximum' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'Integer' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + + ReadOnlyFormElementMixin: + formEditor: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.RemovableFormElementMixin' + editors: + 200: + label: 'formEditor.elements.ReadOnlyFormElement.editor.label.label' + + FormElementMixin: + formEditor: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.RemovableFormElementMixin' + editors: + 200: + label: 'formEditor.elements.FormElement.editor.label.label' + 800: + identifier: 'requiredValidator' + templateName: 'Inspector-RequiredValidatorEditor' + label: 'formEditor.elements.FormElement.editor.requiredValidator.label' + validatorIdentifier: 'NotEmpty' + + TextMixin: + formEditor: + predefinedDefaults: + properties: + placeholder: '' + defaultValue: '' + editors: + 400: + identifier: 'placeholder' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.TextMixin.editor.placeholder.label' + propertyPath: 'properties.placeholder' + 500: + identifier: 'defaultValue' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.TextMixin.editor.defaultValue.label' + propertyPath: 'defaultValue' + 900: + identifier: 'validators' + templateName: 'Inspector-ValidatorsEditor' + label: 'formEditor.elements.TextMixin.editor.validators.label' + selectOptions: + 10: + value: '' + label: 'formEditor.elements.TextMixin.editor.validators.EmptyValue.label' + 20: + value: 'Alphanumeric' + label: 'formEditor.elements.TextMixin.editor.validators.Alphanumeric.label' + 30: + value: 'Text' + label: 'formEditor.elements.TextMixin.editor.validators.Text.label' + 40: + value: 'StringLength' + label: 'formEditor.elements.TextMixin.editor.validators.StringLength.label' + 50: + value: 'EmailAddress' + label: 'formEditor.elements.TextMixin.editor.validators.EmailAddress.label' + 60: + value: 'Integer' + label: 'formEditor.elements.TextMixin.editor.validators.Integer.label' + 70: + value: 'Float' + label: 'formEditor.elements.TextMixin.editor.validators.Float.label' + 80: + value: 'NumberRange' + label: 'formEditor.elements.TextMixin.editor.validators.NumberRange.label' + 90: + value: 'RegularExpression' + label: 'formEditor.elements.TextMixin.editor.validators.RegularExpression.label' + + propertyCollections: + validators: + 10: + identifier: 'Alphanumeric' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.TextMixin.validators.Alphanumeric.editor.header.label' + 20: + identifier: 'Text' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.TextMixin.validators.Text.editor.header.label' + 30: + identifier: 'StringLength' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 20: 'TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin' + 100: + label: 'formEditor.elements.TextMixin.validators.StringLength.editor.header.label' + 40: + identifier: 'EmailAddress' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.TextMixin.validators.EmailAddress.editor.header.label' + 50: + identifier: 'Integer' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.TextMixin.validators.Integer.editor.header.label' + 60: + identifier: 'Float' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.TextMixin.validators.Float.editor.header.label' + 70: + identifier: 'NumberRange' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 20: 'TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin' + 100: + label: 'formEditor.elements.TextMixin.validators.NumberRange.editor.header.label' + 80: + identifier: 'RegularExpression' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.TextMixin.validators.RegularExpression.editor.header.label' + 200: + identifier: 'regex' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.TextMixin.validators.RegularExpression.editor.regex.label' + fieldExplanationText: 'formEditor.elements.TextMixin.validators.RegularExpression.editor.regex.fieldExplanationText' + propertyPath: 'options.regularExpression' + propertyValidators: + 10: 'NotEmpty' + + SelectionMixin: + formEditor: + predefinedDefaults: + properties: + options: [] + editors: + 300: + identifier: 'options' + templateName: 'Inspector-PropertyGridEditor' + label: 'formEditor.elements.SelectionMixin.editor.options.label' + propertyPath: 'properties.options' + isSortable: true + enableAddRow: true + enableDeleteRow: true + removeLastAvailableRowFlashMessageTitle: 'formEditor.elements.SelectionMixin.editor.options.removeLastAvailableRowFlashMessageTitle' + removeLastAvailableRowFlashMessageMessage: 'formEditor.elements.SelectionMixin.editor.options.removeLastAvailableRowFlashMessageMessage' + + SingleSelectionMixin: + formEditor: + editors: + 300: + shouldShowPreselectedValueColumn: 'single' + multiSelection: false + + MultiSelectionMixin: + formEditor: + editors: + 300: + shouldShowPreselectedValueColumn: 'multiple' + multiSelection: true + 900: + identifier: 'validators' + templateName: 'Inspector-ValidatorsEditor' + label: 'formEditor.elements.MultiSelectionMixin.editor.validators.label' + selectOptions: + 10: + value: '' + label: 'formEditor.elements.MultiSelectionMixin.editor.validators.EmptyValue.label' + 20: + value: 'Count' + label: 'formEditor.elements.MultiSelectionMixin.editor.validators.Count.label' + + propertyCollections: + validators: + 10: + identifier: 'Count' + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 20: 'TYPO3.CMS.Form.mixins.formElementMixins.MinimumMaximumEditorsMixin' + 100: + label: 'formEditor.elements.MultiSelectionMixin.validators.Count.editor.header.label' + + FileUploadMixin: + formEditor: + predefinedDefaults: + properties: + saveToFileMount: '1:/user_upload/' + editors: + 400: + identifier: 'saveToFileMount' + templateName: 'Inspector-SingleSelectEditor' + label: 'formEditor.elements.FileUploadMixin.editor.saveToFileMount.label' + propertyPath: 'properties.saveToFileMount' + selectOptions: + 10: + value: '1:/user_upload/' + label: '1:/user_upload/' + + formEmailFinisherMixin: + editors: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.formElementMixins.BaseCollectionEditorsMixin' + 100: + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.header.label' + 200: + identifier: 'subject' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.subject.label' + propertyPath: 'options.subject' + propertyValidators: + 10: 'NotEmpty' + 20: 'FormElementIdentifierWithinCurlyBracesInclusive' + 300: + identifier: 'recipientAddress' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.recipientAddress.label' + propertyPath: 'options.recipientAddress' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'NaiveEmail' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + 400: + identifier: 'recipientName' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.recipientName.label' + propertyPath: 'options.recipientName' + 500: + identifier: 'senderAddress' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.senderAddress.label' + propertyPath: 'options.senderAddress' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'NaiveEmail' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + 600: + identifier: 'senderName' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.senderName.label' + propertyPath: 'options.senderName' + 700: + identifier: 'replyToAddress' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.replyToAddress.label' + propertyPath: 'options.replyToAddress' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'NaiveEmailOrEmpty' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + 800: + identifier: 'carbonCopyAddress' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.carbonCopyAddress.label' + propertyPath: 'options.carbonCopyAddress' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'NaiveEmailOrEmpty' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + 900: + identifier: 'blindCarbonCopyAddress' + templateName: 'Inspector-TextEditor' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.blindCarbonCopyAddress.label' + propertyPath: 'options.blindCarbonCopyAddress' + propertyValidatorsMode: 'OR' + propertyValidators: + 10: 'NaiveEmailOrEmpty' + 20: 'FormElementIdentifierWithinCurlyBracesExclusive' + 1000: + identifier: 'format' + templateName: 'Inspector-SingleSelectEditor' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.format.label' + propertyPath: 'options.format' + selectOptions: + 10: + value: 'plaintext' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.format.1' + 20: + value: 'html' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.format.2' + 1100: + identifier: 'attachUploads' + templateName: 'Inspector-CheckboxEditor' + label: 'formEditor.elements.Form.finisher.EmailToSender.editor.attachUploads.label' + propertyPath: 'options.attachUploads' \ No newline at end of file diff --git a/typo3/sysext/form/Configuration/Yaml/FormEngineSetup.yaml b/typo3/sysext/form/Configuration/Yaml/FormEngineSetup.yaml new file mode 100644 index 000000000000..e67943029be4 --- /dev/null +++ b/typo3/sysext/form/Configuration/Yaml/FormEngineSetup.yaml @@ -0,0 +1,132 @@ +TYPO3: + CMS: + Form: + prototypes: + standard: + ########### TCE Forms CONFIGURATION ########### + + ### FINISHERS ### + finishersDefinition: + EmailToSender: + FormEngine: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.FormEngineEmailMixin' + + EmailToReceiver: + FormEngine: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.FormEngineEmailMixin' + label: 'tt_content.finishersDefinition.EmailToReceiver.label' + elements: + subject: + label: 'tt_content.finishersDefinition.EmailToReceiver.subject.label' + recipientAddress: + label: 'tt_content.finishersDefinition.EmailToReceiver.recipientAddress.label' + recipientName: + label: 'tt_content.finishersDefinition.EmailToReceiver.recipientName.label' + senderAddress: + label: 'tt_content.finishersDefinition.EmailToReceiver.senderAddress.label' + senderName: + label: 'tt_content.finishersDefinition.EmailToReceiver.senderName.label' + replyToAddress: + label: 'tt_content.finishersDefinition.EmailToReceiver.replyToAddress.label' + carbonCopyAddress: + label: 'tt_content.finishersDefinition.EmailToReceiver.carbonCopyAddress.label' + blindCarbonCopyAddress: + label: 'tt_content.finishersDefinition.EmailToReceiver.blindCarbonCopyAddress.label' + format: + label: 'tt_content.finishersDefinition.EmailToReceiver.format.label' + translation: + language: + label: 'tt_content.finishersDefinition.EmailToReceiver.language.label' + config: + type: select + renderType: 'selectSingle' + minitems: 1 + maxitems: 1 + size: 1 + items: + 10: + 0: 'tt_content.finishersDefinition.EmailToReceiver.language.1' + 1: 'default' + + Redirect: + FormEngine: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.FormEngineTranslationSettingsMixin' + label: 'tt_content.finishersDefinition.Redirect.label' + elements: + pageUid: + label: 'tt_content.finishersDefinition.Redirect.pageUid.label' + config: + type: 'group' + internal_type: 'db' + allowed: 'pages' + size: 1 + minitems: 1 + maxitems: 1 + show_thumbs: 0 + additionalParameters: + label: 'tt_content.finishersDefinition.Redirect.additionalParameters.label' + config: + type: 'input' + + ########### MIXINS ########### + mixins: + FormEngineTranslationSettingsMixin: + translationFile: 'EXT:form/Resources/Private/Language/Database.xlf' + + FormEngineEmailMixin: + __inheritances: + 10: 'TYPO3.CMS.Form.mixins.FormEngineTranslationSettingsMixin' + label: 'tt_content.finishersDefinition.EmailToSender.label' + elements: + subject: + label: 'tt_content.finishersDefinition.EmailToSender.subject.label' + config: + type: 'input' + recipientAddress: + label: 'tt_content.finishersDefinition.EmailToSender.recipientAddress.label' + config: + type: 'input' + eval: 'required' + recipientName: + label: 'tt_content.finishersDefinition.EmailToSender.recipientName.label' + config: + type: 'input' + senderAddress: + label: 'tt_content.finishersDefinition.EmailToSender.senderAddress.label' + config: + type: 'input' + eval: 'required' + senderName: + label: 'tt_content.finishersDefinition.EmailToSender.senderName.label' + config: + type: 'input' + replyToAddress: + label: 'tt_content.finishersDefinition.EmailToSender.replyToAddress.label' + config: + type: 'input' + carbonCopyAddress: + label: 'tt_content.finishersDefinition.EmailToSender.carbonCopyAddress.label' + config: + type: 'input' + blindCarbonCopyAddress: + label: 'tt_content.finishersDefinition.EmailToSender.blindCarbonCopyAddress.label' + config: + type: 'input' + format: + label: 'tt_content.finishersDefinition.EmailToSender.format.label' + config: + type: select + renderType: 'selectSingle' + minitems: 1 + maxitems: 1 + size: 1 + items: + 10: + 0: 'tt_content.finishersDefinition.EmailToSender.format.1' + 1: 'html' + 20: + 0: 'tt_content.finishersDefinition.EmailToSender.format.2' + 1: 'plaintext' \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/.gitignore b/typo3/sysext/form/Documentation/.gitignore deleted file mode 100644 index 6cd159f2dad9..000000000000 --- a/typo3/sysext/form/Documentation/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -# this is file .gitignore - -# ignore everything in this directory -_make/* - -# but do not ignore this file -!_not_versioned/.gitignore diff --git a/typo3/sysext/form/Documentation/Administration/DefaultNewRecord/Index.rst b/typo3/sysext/form/Documentation/Administration/DefaultNewRecord/Index.rst deleted file mode 100644 index 2d3fafad982d..000000000000 --- a/typo3/sysext/form/Documentation/Administration/DefaultNewRecord/Index.rst +++ /dev/null @@ -1,38 +0,0 @@ -.. include:: ../../Includes.txt - - -.. _default-new-record: - -================== -Default new record -================== - -When an editor creates a new FORM record, the bodytext will be filled by -default with some simple form settings which are displayed below. The -integrator can change this setting to specific needs, but this string -**cannot be big due to some core limitations**. It is impossible to add -the configuration for a complete form, although it might be a simple one. -This restriction is caused by the fact that the whole string is put in -a URI as parameter. - -Furthermore this only works when the editor is using the web module, i.e. -the functionality is not supported by the list module. - -.. code-block:: typoscript - - mod.wizards { - newContentElement.wizardItems { - forms.elements { - mailform { - tt_content_defValues { - bodytext ( - enctype = multipart/form-data - method = post - prefix = tx_form - ) - } - } - } - } - } - diff --git a/typo3/sysext/form/Documentation/Administration/Index.rst b/typo3/sysext/form/Documentation/Administration/Index.rst deleted file mode 100644 index e04e18353448..000000000000 --- a/typo3/sysext/form/Documentation/Administration/Index.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. include:: ../Includes.txt - - -.. _administration: - -============== -Administration -============== - -Upon installation, the extension will set default properties in the page -TSconfig in the variable :ts:`mod.wizards`. - -These properties may be modified for any particular page, BE user or BE -user group and are described below. - -.. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: - - DefaultNewRecord/Index - WizardSettings/Index - diff --git a/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/ElementsTab/Index.rst b/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/ElementsTab/Index.rst deleted file mode 100644 index ad2f016ce0b2..000000000000 --- a/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/ElementsTab/Index.rst +++ /dev/null @@ -1,131 +0,0 @@ -.. include:: ../../../../Includes.txt - - -.. _wizard-settings-defaults-elements-tab: - -============== -Tab "Elements" -============== - -The elements tab contains an accordion with buttons, grouped by their -type. These buttons identify a form element, like a text field, password -field or submit button. When dragging a button to the form on the right -and dropping it at a certain point in the form, the element will be added -to the form at that point. An editor can also double click a button. When -doing so, the element will be added at the end of the form. - -.. figure:: ../../../../Images/FormCreationWizardElementsTab.png - :alt: The form wizard with the tab "Elements". - -.. contents:: - :local: - :depth: 1 - - -.. _wizard-settings-defaults-elements-showaccordions: - -showAccordions -============== - -(:ts:`mod.wizards.form.defaults.tabs.elements.showAccordions`) - -:aspect:`Property:` - showAccordions - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of the accordions that will be shown in the - wizard. Each of the three accordions contain a single showButton - property which defines which form elements will be shown in a given - accordion. - -:aspect:`Default:` - basic, predefined, content - - -.. _wizard-settings-defaults-elements-accordions-showbuttons: - -showButtons -=========== - -(:ts:`mod.wizards.form.defaults.tabs.elements.accordions.[NameOfAccordion].showButtons`) - -:aspect:`Property:` - showButtons - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of the buttons that will be shown in the accordion. - Please note, in the shown path has [NameOfAccordion] to be replaced with - the name of the specific accordion. - -:aspect:`Default:` - **"basic" elements** - - - textline (Text Field) - - textarea (Textarea) - - checkbox (Checkbox) - - radio (Radio Button) - - select (Drop Down) - - fileupload (Upload Field) - - hidden (Hidden Field) - - password (Password Field) - - fieldset (Fieldset) - - submit (Submit Button) - - reset (Reset Button) - - button (Button) - - | - - **"predefined" elements** - - - name (Full Name) - - email (Email) - - checkboxgroup (Checkbox Group) - - radiogroup (Radio Button Group) - - | - - **"content" elements** - - - header (Header) - - textblock (Text Block) - - -.. _wizard-settings-defaults-elements-tab-configuration: - -Default configuration -===================== - -The default configuration of the elements tab is as follows. - -.. code-block:: typoscript - - mod.wizards { - form { - defaults { - showTabs = elements, options, form - tabs { - elements { - showAccordions = basic, predefined, content - accordions { - basic { - showButtons = textline, textarea, checkbox, radio, select, fileupload, hidden, password, fieldset, submit, reset, button - } - predefined { - showButtons = name, email, checkboxgroup, radiogroup - } - content { - showButtons = header, textblock - } - } - } - } - } - } - } - diff --git a/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/FormTab/Index.rst b/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/FormTab/Index.rst deleted file mode 100644 index d6f2066ad166..000000000000 --- a/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/FormTab/Index.rst +++ /dev/null @@ -1,187 +0,0 @@ -.. include:: ../../../../Includes.txt - - -.. _wizard-settings-defaults-form-tab: - -========== -Tab "Form" -========== - -The form tab shows the configuration of the outer form, like the attributes -of the form or the prefix as well as the post-processor configuration. - -.. figure:: ../../../../Images/FormCreationWizardFormTab.png - :alt: The form wizard with the tab "Form". - -.. contents:: - :local: - :depth: 1 - - -.. _wizard-settings-defaults-form-showaccordions: - -showAccordions -============== - -(:ts:`mod.wizards.form.defaults.tabs.form.showAccordions`) - -:aspect:`Property:` - showAccordions - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of the accordions that are allowed to be shown in - the wizard. This does not mean they are all shown by default, but - depends on the chosen element type. - - Some accordions have further properties, which are described below. - -:aspect:`Default:` - The following accordions are available in the form tab: - - * behaviour - * prefix - * :ref:`attributes <wizard-settings-defaults-form-attributes>` - * :ref:`postProcessor <wizard-settings-defaults-form-postprocessor>` - - -.. _wizard-settings-defaults-form-attributes: - -Attributes accordion -==================== - - -.. _wizard-settings-defaults-form-attributes-showproperties: - -showProperties --------------- - -.. attention:: - - The configuration of the attributes accordion is not working as - expected and has to be fixed in a coming version of TYPO3. There is - a workaround which is shown below. - -(:ts:`mod.wizards.form.defaults.tabs.form.accordions.attributes.showProperties`) - -:aspect:`Property:` - showProperties - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of the form attributes that are allowed to be shown - in the accordion. - -:aspect:`Default:` - accept, action, dir, enctype, lang, method, novalidate, class, id, style, title - -Since the above shown configuration is not working, the following workaround can -be applied. To configure the attribute accordion of the form element, address the -object directly via :ts:`mod.wizards.form.elements.form.accordions.attributes.showProperties`. - - -.. _wizard-settings-defaults-form-postprocessor: - -Post-processors accordion -========================= - - -.. _wizard-settings-defaults-form-postprocessor-showpostprocessors: - -showPostProcessors ------------------- - -(:ts:`mod.wizards.form.defaults.tabs.form.accordions.postProcessor.showPostProcessors`) - -:aspect:`Property:` - showPostProcessors - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of the post-processors that are allowed to be shown - in the wizard. - - For each post-processors a list of properties to be shown can be defined. - -:aspect:`Default:` - mail, redirect - - -.. _wizard-settings-defaults-options-postprocessor-postprocessors: - -postProcessors.[post-processor].showProperties ----------------------------------------------- - -(:ts:`mod.wizards.form.defaults.tabs.form.accordions.postProcessor.postProcessors.[post-processor].showProperties`) - -:aspect:`Property:` - postProcessors.[post-processor].showProperties - -:aspect:`Data type:` - string - -:aspect:`Description:` - Configuration for the post-processors individually. - - The syntax is :ts:`postProcessors.[name of the post-processor].showProperties`. - -:aspect:`Default:` - The following element properties are available: - - .. t3-field-list-table:: - :header-rows: 1 - - - :Field: - Element: - :Description: - Properties: - - :Field: - mail - :Description: - recipientEmail, senderEmail, subject - - :Field: - redirect - :Description: - destination - - -.. _wizard-settings-defaults-form-tab-configuration: - -Default configuration -===================== - -The default configuration of the form tab looks as follows: - -.. code-block:: typoscript - - mod.wizards { - form { - defaults { - tabs { - form { - showAccordions = behaviour, prefix, attributes, postProcessor - accordions { - postProcessor { - showPostProcessors = mail, redirect - postProcessors { - mail { - showProperties = recipientEmail, senderEmail, subject - } - redirect { - showProperties = destination - } - } - } - } - } - } - } - } - } - diff --git a/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/Index.rst b/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/Index.rst deleted file mode 100644 index 44a5e56f60ba..000000000000 --- a/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/Index.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _wizard-settings-defaults: - -================== -Defaults reference -================== - -This chapter describes the settings for the visible tabs, the accordions -available inside these tabs and the default configuration for all element -types. - - -.. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: - - ShowTabs/Index - ElementsTab/Index - OptionsTab/Index - FormTab/Index - diff --git a/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/OptionsTab/Index.rst b/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/OptionsTab/Index.rst deleted file mode 100644 index 8b0b89f3a1ab..000000000000 --- a/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/OptionsTab/Index.rst +++ /dev/null @@ -1,404 +0,0 @@ -.. include:: ../../../../Includes.txt - - -.. _wizard-settings-defaults-options-tab: - -============= -Tab "Options" -============= - -The options tab will show the configuration of a particular element in -the form. When no element has been selected, it will show a message that -the editor has to select an element in the form. - -The content of this tab depends on the type of element the editor has chosen -in the form. - -.. figure:: ../../../../Images/FormCreationWizardOptionsTab.png - :alt: The form wizard with the tab "Options". - -.. contents:: - :local: - :depth: 1 - - -.. _wizard-settings-defaults-options-showaccordions: - -showAccordions -============== - -:aspect:`Property:` - showAccordions - -:aspect:`TypoScript Path:` - :ts:`mod.wizards.form.defaults.tabs.options.showAccordions` - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of the accordions that are allowed to be shown - in the wizard. This does not mean they are all shown by default, - but depends on the chosen element type. - - Some tabs have further configuration which is described below. - -:aspect:`Default:` - The following accordions are available: - - - *legend*: Legend Properties - - *label*: :ref:`Label Properties <wizard-settings-defaults-options-label>` - - *attributes*: :ref:`Attributes Properties <wizard-settings-defaults-options-attributes>` - - *options*: Field Options - - *validation*: :ref:`Validation <wizard-settings-defaults-validation-label>` - - *filters*: :ref:`Filters <wizard-settings-defaults-filters-label>` - - *various*: Various Properties - -:aspect:`Example:` - .. code-block:: typoscript - - mod.wizards { - form { - defaults { - tabs { - options { - showAccordions = legend, label, attributes, options, validation, filters, various - } - } - } - - } - } - - -.. _wizard-settings-defaults-options-label: - -Label accordion -=============== - - -.. _wizard-settings-defaults-options-label-showproperties: - -showProperties --------------- - -:aspect:`Property:` - showProperties - -:aspect:`TypoScript Path:` - :ts:`mod.wizards.form.defaults.tabs.options.accordions.label.showProperties` - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of the label options that are allowed to be shown - in the accordion. The appearance of an option depends on the chosen - element type. If an element type does not support an option, it will not - be shown. - -:aspect:`Default:` - label - - -.. _wizard-settings-defaults-options-attributes: - -Attributes accordion -==================== - - -.. _wizard-settings-defaults-options-attributes-showproperties: - -showProperties --------------- - -:aspect:`Property:` - showProperties - -:aspect:`TypoScript Path:` - :ts:`mod.wizards.form.defaults.tabs.options.accordions.attributes.showProperties` - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of attributes that are allowed to be shown in the - accordion. The appearance of an attribute depends on the chosen element - type. If an element type does not support an attribute, it will not be - shown. - - -:aspect:`Default:` - accept, accept-charset, accesskey, action, alt, autocomplete, autofocus, - checked, class, cols, contenteditable, contextmenu, dir, draggable, - dropzone, disabled, enctype, hidden, height, id, inputmode, label, lang, - list, max, maxlength, method, min, minlength, multiple, name, - novalidate, pattern, placeholder, readonly, required, rows, selected, - selectionDirection, selectionEnd, selectionStart, size, spellcheck, src, - step, style, tabindex, text, title, translate, type, value, width, wrap - - -.. _wizard-settings-defaults-validation-label: - -Validation accordion -==================== - - -.. _wizard-settings-defaults-options-validation-showrules: - -showRules ---------- - -:aspect:`Property:` - showRules - -:aspect:`TypoScript Path:` - :ts:`mod.wizards.form.defaults.tabs.options.accordions.validation.showRules` - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of rules that are allowed to be shown in the - wizard. - -:aspect:`Default:` - alphabetic, alphanumeric, between, date, digit, email, equals, - fileallowedtypes, filemaximumsize, fileminimumsize, float, greaterthan, - inarray, integer, ip, length, lessthan, regexp, required, uri - - -.. _wizard-settings-defaults-options-validation-rules: - -rules.[rule].showProperties ---------------------------- - -:aspect:`Property:` - rules.[rule].showProperties - -:aspect:`TypoScript Path:` - :ts:`mod.wizards.form.defaults.tabs.options.accordions.validation.rules.[rule].showProperties` - -:aspect:`Data type:` - [array of objects] - -:aspect:`Description:` - For each rule one can define which properties should appear. - The syntax is :ts:`rules.[name of the rule].showProperties`. - -:aspect:`Default:` - The following element properties are available: - - =================== ======================================================== - Element Properties - =================== ======================================================== - alphabetic message, error, showMessage, allowWhiteSpace - alphanumeric message, error, showMessage, allowWhiteSpace - between message, error, showMessage, minimum, maximum, inclusive - date message, error, showMessage, format - digit message, error, showMessage - email message, error, showMessage - equals message, error, showMessage, field - fileallowedtypes message, error, showMessage, types - filemaximumsize message, error, showMessage, maximum - fileminimumsize message, error, showMessage, minimum - float message, error, showMessage - greaterthan message, error, showMessage, minimum - inarray message, error, showMessage, array, strict - integer message, error, showMessage - ip message, error, showMessage - length message, error, showMessage, minimum, maximum - lessthan message, error, showMessage, maximum - regexp message, error, showMessage, expression - required message, error, showMessage - uri message, error, showMessage - =================== ======================================================== - - -.. _wizard-settings-defaults-filters-label: - -Filters accordion -================= - - -.. _wizard-settings-defaults-options-filtering-showfilters: - -showFilters ------------ - -:aspect:`Property:` - showFilters - -:aspect:`TypoScript Path:` - :ts:`mod.wizards.form.defaults.tabs.options.accordions.filtering.showFilters` - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of the filters that are allowed to be shown in - the wizard. - - For each filter a list of properties to be shown can be defined. - -:aspect:`Default:` - alphabetic, alphanumeric, currency, digit, integer, lowercase, regexp, - stripnewlines, titlecase, trim, uppercase - - -.. _wizard-settings-defaults-options-filtering-filters: - -filters.[filter].showProperties -------------------------------- - -:aspect:`Property:` - filters.[filter].showProperties - -:aspect:`TypoScript Path:` - :ts:`mod.wizards.form.defaults.tabs.options.accordions.filtering.filters.[filter].showProperties` - -:aspect:`Data type:` - string - -:aspect:`Description:` - Configuration for the filters individually. Not all filters have a - configuration. Only the filters which are mentioned below can be - configured. - - The syntax is :ts:`filters.[name of the filter].showProperties`. - -:aspect:`Default:` - The following element properties are available: - - =================== =============================== - Element Properties - =================== =============================== - alphabetic allowWhiteSpace - alphanumeric allowWhiteSpace - currency decimalPoint, thousandSeparator - regexp expression - trim characterList - =================== =============================== - - -.. _wizard-settings-defaults-options-tab-configuration: - -Default configuration -===================== - -The default configuration of the options tab looks like this: - -.. code-block:: typoscript - - options { - showAccordions = legend, label, attributes, options, validation, filters, various - accordions { - label { - showProperties = label - } - attributes { - showProperties = accept, accept-charset, accesskey, action, alt, autocomplete, autofocus, checked, class, cols, contenteditable, contextmenu, dir, draggable, dropzone, disabled, enctype, hidden, height, id, inputmode, label, lang, list, max, maxlength, method, min, minlength, multiple, name, novalidate, pattern, placeholder, readonly, required, rows, selected, selectionDirection, selectionEnd, selectionStart, size, spellcheck, src, step, style, tabindex, text, title, translate, type, value, width, wrap - } - validation { - showRules = alphabetic, alphanumeric, between, date, digit, email, equals, fileallowedtypes, filemaximumsize, fileminimumsize, float, greaterthan, inarray, integer, ip, length, lessthan, regexp, required, uri - rules { - alphabetic { - showProperties = message, error, showMessage, allowWhiteSpace - } - alphanumeric { - showProperties = message, error, showMessage, allowWhiteSpace - } - between { - showProperties = message, error, showMessage, minimum, maximum, inclusive - } - date { - showProperties = message, error, showMessage, format - } - digit { - showProperties = message, error, showMessage - } - email { - showProperties = message, error, showMessage - } - equals { - showProperties = message, error, showMessage, field - } - fileallowedtypes { - showProperties = message, error, showMessage, types - } - filemaximumsize { - showProperties = message, error, showMessage, maximum - } - fileminimumsize { - showProperties = message, error, showMessage, minimum - } - float { - showProperties = message, error, showMessage - } - greaterthan { - showProperties = message, error, showMessage, minimum - } - inarray { - showProperties = message, error, showMessage, array, strict - } - integer { - showProperties = message, error, showMessage - } - ip { - showProperties = message, error, showMessage - } - length { - showProperties = message, error, showMessage, minimum, maximum - } - lessthan { - showProperties = message, error, showMessage, maximum - } - regexp { - showProperties = message, error, showMessage, expression - } - required { - showProperties = message, error, showMessage - } - uri { - showProperties = message, error, showMessage - } - } - } - filtering { - showFilters = alphabetic, alphanumeric, currency, digit, integer, lowercase, regexp, stripnewlines, titlecase, trim, uppercase - filters { - alphabetic { - showProperties = allowWhiteSpace - } - alphanumeric { - showProperties = allowWhiteSpace - } - currency { - showProperties = decimalPoint, thousandSeparator - } - digit { - showProperties = - } - integer { - showProperties = - } - lowercase { - showProperties = - } - regexp { - showProperties = expression - } - titlecase { - showProperties = - } - trim { - showProperties = characterList - } - uppercase { - showProperties = - } - } - } - } - } - diff --git a/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/ShowTabs/Index.rst b/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/ShowTabs/Index.rst deleted file mode 100644 index 667d2e35641f..000000000000 --- a/typo3/sysext/form/Documentation/Administration/WizardSettings/DefaultsReference/ShowTabs/Index.rst +++ /dev/null @@ -1,75 +0,0 @@ -.. include:: ../../../../Includes.txt - - -.. _wizard-settings-defaults-showtabs: - -======== -showTabs -======== - -(:ts:`mod.wizards.form.defaults.showTabs`) - -The configuration "showTabs" defines the outermost tabs on the left -side of the form wizard. - -.. figure:: ../../../../Images/FormCreationWizardShowTabs.png - :alt: The form wizard with the main tabs. - -:aspect:`Property:` - showTabs - -:aspect:`Data type:` - string - -:aspect:`Description:` - Comma-separated list of the tabs that will be shown in the wizard. - -:aspect:`Default:` - elements, options, form - - -.. _wizard-settings-defaults-tabs: - -tabs -==== - -(:ts:`mod.wizards.form.defaults.tabs`) - -Each of the 3 tabs can be further customized. - -:aspect:`Property:` - tabs - -:aspect:`Data type:` - [array of objects] - - ->tabs.[tabName] - -:aspect:`Description:` - Configuration for each tab. - - -Example -======= - -.. code-block:: typoscript - - mod.wizards { - form { - defaults { - showTabs = elements, options, form - tabs { - elements { - ... - } - options { - ... - } - form { - ... - } - } - } - } - } - diff --git a/typo3/sysext/form/Documentation/Administration/WizardSettings/ElementsReference/Index.rst b/typo3/sysext/form/Documentation/Administration/WizardSettings/ElementsReference/Index.rst deleted file mode 100644 index ba46630a4e4a..000000000000 --- a/typo3/sysext/form/Documentation/Administration/WizardSettings/ElementsReference/Index.rst +++ /dev/null @@ -1,41 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _wizard-settings-elements: - -================== -Elements reference -================== - -Overrule the default settings of the :ref:`option <wizard-settings-defaults-options-tab>` -tab for specific element types. - -In the left "settings" part there is a tab called "Options". The contents -of this tab will adapt itself to the selected element type in the form. -If no elements configuration exists, the default settings will be used. - - -.. _overriding-element-settings: - -Overriding element settings -=========================== - -It is possible to override the default option tab settings for each -element individually. This is done by using the same configuration as -in :ts:`mod.wizards.form.defaults.tabs.options`, but using this -configuration in :ts:`mod.wizards.form.elements.[elementName]`. - -The example below will hide all the accordions within the option tab for -a text field (TEXTLINE element), except the filters: - -.. code-block:: typoscript - - mod.wizards.form.elements { - textline { - showAccordions = filters - } - } - -By using this setting you can show or hide accordions, attributes, -validation rules or filters, for each and every individual element. - diff --git a/typo3/sysext/form/Documentation/Administration/WizardSettings/Index.rst b/typo3/sysext/form/Documentation/Administration/WizardSettings/Index.rst deleted file mode 100644 index 403d263bbeea..000000000000 --- a/typo3/sysext/form/Documentation/Administration/WizardSettings/Index.rst +++ /dev/null @@ -1,25 +0,0 @@ -.. include:: ../../Includes.txt - - -.. _wizard-settings: - -=============== -Wizard settings -=============== - -The wizard basically consists of two parts on the screen, the left -"settings" part and the right "form" part. With TSconfig settings it -is possible to configure the contents of the left "settings" part. The -integrator can remove tabs, accordions or a specific setting for a single -type of form element, or for all element types at once. - -The basic configuration has two settings: **defaults** and **elements**. - -.. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: - - DefaultsReference/Index - ElementsReference/Index - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Alphabetic/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Alphabetic/Index.rst deleted file mode 100644 index fe954f8816d8..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Alphabetic/Index.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-alphabetic: - -========== -alphabetic -========== - -Removes all characters which are not in the range a-z or A-Z. With the -setting allowWhiteSpace, spaces are allowed as well. - - -.. _reference-filters-alphabetic-allowwhitespace: - -allowWhiteSpace -=============== - -:aspect:`Property:` - allowWhiteSpace - -:aspect:`Data type:` - boolean - -:aspect:`Description:` - If allowWhiteSpace = 1, whitespace is allowed in front of, after or - between the characters. - -:aspect:`Default:` - 0 - -[tsref:(cObject).FORM->filters.alphabetic] - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Alphanumeric/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Alphanumeric/Index.rst deleted file mode 100644 index 437e71a2a8f2..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Alphanumeric/Index.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-alphanumeric: - -============ -alphanumeric -============ - -Removes all characters which are not in the range a-z, A-Z or 0-9. With the -setting allowWhiteSpace, spaces are allowed as well. - - -.. _reference-filters-alphanumeric-allowwhitespace: - -allowWhiteSpace -=============== - -:aspect:`Property:` - allowWhiteSpace - -:aspect:`Data type:` - boolean - -:aspect:`Description:` - If allowWhiteSpace = 1, whitespace is allowed in front of, after or - between the characters. - -:aspect:`Default:` - 0 - -[tsref:(cObject).FORM->filters.alphanumeric] - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Currency/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Currency/Index.rst deleted file mode 100644 index 6e85c765e3c0..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Currency/Index.rst +++ /dev/null @@ -1,71 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-currency: - -======== -currency -======== - -Changes a number to a formatted version with two decimals. The decimals -point and thousands separator are configurable. - -**Example** - -- Submitted data: 100000.99 - -- Filtered: 100 000,99 - -.. code-block:: typoscript - - filters { - 1 = currency - 1 { - decimalPoint = , - thousandSeparator = space - } - } - - -.. _reference-filters-currency-decimalpoint: - -decimalPoint -============ - -:aspect:`Property:` - decimalPoint - -:aspect:`Data type:` - string - -:aspect:`Description:` - Value for the decimal point, mostly a dot '.' or a comma ',' - -:aspect:`Default:` - . - - -.. _reference-filters-currency-thousandseparator: - -thousandSeparator -================= - -:aspect:`Property:` - thousandSeparator - -:aspect:`Data type:` - string - -:aspect:`Description:` - Value for the thousand separator. - - Special values: - - - **space** : Adds a space as thousand separator - - **none** : No thousand separator - -:aspect:`Default:` - , - -[tsref:(cObject).FORM->filters.currency] - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Digit/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Digit/Index.rst deleted file mode 100644 index 8c3602ee48d5..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Digit/Index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-digit: - -===== -digit -===== - -Removes all characters which are not in the range 0-9. - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Index.rst deleted file mode 100644 index 733579287f8e..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Index.rst +++ /dev/null @@ -1,53 +0,0 @@ -.. include:: ../../Includes.txt - - -.. _reference-filters: - -Filters -======= - -Add filters to the FORM objects. - -It is possible to have multiple filters for one FORM object, but the filters -have to be added one by one. - -The submitted data for this particular object will be filtered by the -assigned filters in the given order. The filtered data will be shown to the -visitor when there are errors in the form or on a confirmation page. -Otherwise the filtered data will be send by mail to the receiver. - -.. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: - - Alphabetic/Index.rst - Alphanumeric/Index.rst - Currency/Index.rst - Digit/Index.rst - Integer/Index.rst - Lowercase/Index.rst - Regexp/Index.rst - Stripnewlines/Index.rst - Titlecase/Index.rst - Trim/Index.rst - Uppercase/Index.rst - -**Example** - -The example shown below applies two filters to a FORM object. - -- Submitted data: john doe3 - -- Filtered: John Doe - -.. code-block:: typoscript - - filters { - 1 = alphabetic - 1 { - allowWhiteSpace = 1 - } - 2 = titlecase - } - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Integer/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Integer/Index.rst deleted file mode 100644 index a05461bab644..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Integer/Index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-integer: - -======= -integer -======= - -Integers can be specified in decimal (10-based), optionally preceded by a -sign (- or +). - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Lowercase/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Lowercase/Index.rst deleted file mode 100644 index ab78c2374bd9..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Lowercase/Index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-lowercase: - -========= -lowercase -========= - -Returns the incoming value with all alphabetic characters converted to -lowercase. Alphabetic is determined by the Unicode character properties. - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Regexp/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Regexp/Index.rst deleted file mode 100644 index 2e2bf1169408..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Regexp/Index.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-regexp: - -====== -regexp -====== - -Removes matches in the submitted data found by the defined pattern. - - -.. _reference-filters-regexp-expression: - -expression -========== - -:aspect:`Property:` - expression - -:aspect:`Data type:` - boolean - -:aspect:`Description:` - The pattern holding the characters which need to be deleted. - -[tsref:(cObject).FORM->filters.regexp] - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Stripnewlines/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Stripnewlines/Index.rst deleted file mode 100644 index 943570d5c315..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Stripnewlines/Index.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-stripnewlines: - -============= -stripnewlines -============= - -Convenient for textareas. It removes new lines from the submitted value. - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Titlecase/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Titlecase/Index.rst deleted file mode 100644 index 9f30a8b438d2..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Titlecase/Index.rst +++ /dev/null @@ -1,24 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-titlecase: - -========= -titlecase -========= - -Returns the incoming value with all alphabetic characters converted to title -case. Alphabetic is determined by the Unicode character properties. - -**Example** - -- Submitted data: kasper skårhøj - -- Filtered: Kasper Skårhøj - -.. code-block:: typoscript - - filters { - 1 = titlecase - } - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Trim/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Trim/Index.rst deleted file mode 100644 index 6b457f484bcf..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Trim/Index.rst +++ /dev/null @@ -1,33 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-trim: - -==== -trim -==== - -Strips characters from the beginning and the end of the submitted value -according to the list of characters. If no character list is set, it will -only trim an ordinary space, a tab, a new line, a carriage return, the -NUL-byte and a vertical tab. - - -.. _reference-filters-trim-characterlist: - -characterList -============= - -:aspect:`Property:` - characterList - -:aspect:`Data type:` - string - -:aspect:`Description:` - List of characters to be trimmed. - - See the PHP-manual (trim) for the options of the charlist. - -[tsref:(cObject).FORM->filters.regexp] - diff --git a/typo3/sysext/form/Documentation/Configuration/Filters/Uppercase/Index.rst b/typo3/sysext/form/Documentation/Configuration/Filters/Uppercase/Index.rst deleted file mode 100644 index b4e7276194d9..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Filters/Uppercase/Index.rst +++ /dev/null @@ -1,12 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-filters-uppercase: - -========= -uppercase -========= - -Returns the incoming value with all alphabetic characters converted to -uppercase. Alphabetic is determined by the Unicode character properties. - diff --git a/typo3/sysext/form/Documentation/Configuration/Index.rst b/typo3/sysext/form/Documentation/Configuration/Index.rst deleted file mode 100644 index baeea5914b7a..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Index.rst +++ /dev/null @@ -1,28 +0,0 @@ -.. include:: ../Includes.txt - - -.. _configuration: - -============= -Configuration -============= - -.. only:: html - - .. tip:: - - Whenever there is a reference to anything named an "object" in this - section it is a reference to a "FORM object" and not the "cObjects" - unless it is clearly stated. - -.. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: - - Objects/Index - Layout/Index - Rules/Index - Filters/Index - Postprocessors/Index - diff --git a/typo3/sysext/form/Documentation/Configuration/Layout/Index.rst b/typo3/sysext/form/Documentation/Configuration/Layout/Index.rst deleted file mode 100644 index 92e5b74750b2..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Layout/Index.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. include:: ../../Includes.txt - - -.. _reference-layout: - -====== -Layout -====== - -.. attention:: - - The form wizard (available in the TYPO3 backend) does not support the - complex layout mechanism described in this chapter. As soon as the - integrator has applied custom layout settings, the form wizard should - not be used anymore. When opening the customized form inside the form - wizard and hitting the "Save" button, all custom layout settings will be - lost. - -Using layout allows the integrator to change the default visual appearance -of the FORM objects. - -The FORM consists of FORM objects, which have their own layout each. The -layout of these objects can be changed for the whole form, for a specific -view or just for a particular object. - -By default, the overall markup is based on ordered lists with list elements -in it, to have a proper layout framework which is also accessible for people -with disabilities. - -Some objects are considered being container objects, as they have child -objects. These objects are FORM, FIELDSET, CHECKBOXGROUP and RADIOGROUP. To -have a proper markup for these objects, nested ordered lists are used. - -**Example** - -.. code-block:: html - - <form> - <ol> - <li> - <fieldset> - <ol> - <li> - <input /> - </li> - </ol> - </fieldset> - </li> - <li> - <input /> - </li> - </ol> - </form> - -It could be stated that SELECT and OPTGROUP elements are container objects -as well, and actually this is correct. They also contain child objects. But -these objects are not allowed to use the above mentioned markup. - -There are 3 ways to modify the layout: - -.. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: - - LayoutWholeForm/Index - LayoutViewSpecific/Index - LayoutObjectSpecific/Index - diff --git a/typo3/sysext/form/Documentation/Configuration/Layout/LayoutObjectSpecific/Index.rst b/typo3/sysext/form/Documentation/Configuration/Layout/LayoutObjectSpecific/Index.rst deleted file mode 100644 index 84e51101aedb..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Layout/LayoutObjectSpecific/Index.rst +++ /dev/null @@ -1,29 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _change-layout-individual-form: - -=============================================== -Change the layout for an individual FORM object -=============================================== - -It is also possible to override the layout setting of a particular object -within the form, like a checkbox. The layout function within an object only -accepts the markup, like the following one. - -.. code-block:: typoscript - - tt_content.mailform.20 { - 10 = CHECKBOX - 10 { - label = I want to receive the monthly newsletter by email. - layout ( - <input /> - <label /> - ) - } - } - -The example shows how to switch the input field and the label, just for this -particular checkbox. - diff --git a/typo3/sysext/form/Documentation/Configuration/Layout/LayoutViewSpecific/Index.rst b/typo3/sysext/form/Documentation/Configuration/Layout/LayoutViewSpecific/Index.rst deleted file mode 100644 index 30b70de679d6..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Layout/LayoutViewSpecific/Index.rst +++ /dev/null @@ -1,1592 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _change-layout-specific-view: - -===================================== -Change the layout for a specific view -===================================== - -.. contents:: - :local: - :depth: 1 - - -.. _change-layout-specific-view-available-views: - -Available views -=============== - -There are 3 views available: - -form: - This view displays the form with its form fields which can be filled by - the user and submitted. - -confirmation: - If activated, this view shows a confirmation page which has to be - confirmed by the user. - -postProcessor: - The mail postProcessor has its own view for rendering the mail which is - sent to the receiver. - -It is not recommended to change the layout of a FORM object for all views. -For example when customizing the TEXTLINE object the integrator will get -strange results based on the following example: - -.. code-block:: typoscript - - tt_content.mailform.20 { - layout { - textline ( - <div class="form-group"> - <div class="col-sm-3 control-label"> - <label /> - </div> - <div class="col-sm-5"> - <input class="form-control" /> - </div> - </div> - ) - } - } - -The setup shown above changes the appearance of all TEXTLINE objects for all -views. That is, the user will get a confirmation page and a mail with -broken/ senseless input fields instead of the user data. - -In order to only change the TEXTLINE object specific to all of the 3 views, -the following code could be applied. - -.. code-block:: typoscript - - tt_content.mailform.20 { - # customize form view - form { - layout { - textline ( - <div class="form-group"> - <div class="col-sm-3 control-label"> - <label /> - </div> - <div class="col-sm-5"> - <input class="form-control" /> - </div> - </div> - ) - } - } - - # customize confirmation view - confirmation { - layout { - textline ( - <div class="form-group"> - <div class="col-sm-3"> - <strong><label /></strong> - </div> - <div class="col-sm-5"> - <inputvalue /> - </div> - </div> - ) - } - } - - # customize postProcessor/ mail - postProcessor { - layout { - textline ( - <td colspan="2"> - <div class="textline"><inputvalue /></div> - </td> - ) - } - } - } - - -.. _change-layout-specific-view-properties: - -Properties and defaults -======================= - -If the integrator does not define any :ts:`.layout` setting the default -layout defined in the PHP classes will be used. - -The following list shows all available elements within all the different -views including their corresponding default layouts. - -.. contents:: - :local: - :depth: 1 - - -.. _reference-layout-form: - -form -^^^^ - -:aspect:`Property:` - form - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - -:aspect:`Description:` - Layout of the FORM object/ outer wrap. - - The <containerwrap /> tag will be substituted by the outer container - wrap and includes all child elements. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <form> - <containerWrap /> - </form> - - -.. _reference-layout-confirmation: - -confirmation -^^^^^^^^^^^^ - -:aspect:`Property:` - confirmation - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - confirmation - -:aspect:`Description:` - Layout of the outer wrap. - - The <containerwrap /> tag will be substituted by the outer container - wrap and includes all child elements. - -:aspect:`Default:` - Default layout **confirmation view**: - - .. code-block:: html - - <containerWrap /> - - -.. _reference-layout-html: - -html -^^^^ - -:aspect:`Property:` - html - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - postProcessor - -:aspect:`Description:` - Layout of the outer wrap. - - The <containerwrap /> tag will be substituted by the outer container - wrap and includes all child elements. - -:aspect:`Default:` - Default layout **postProcessor view**: - - .. code-block:: html - - <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - </head> - <body> - <table cellspacing="0"> - <containerWrap /> - </table> - </body> - </html> - - -.. _reference-layout-containerwrap: - -containerWrap -^^^^^^^^^^^^^ - -:aspect:`Property:` - containerWrap - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Inner wrap for container objects. - - The <elements /> tag will be substituted with all the child elements, - including their element wraps. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <ol> - <elements /> - </ol> - - Default layout **confirmation view**: - - .. code-block:: html - - <ol> - <elements /> - </ol> - - Default layout **postProcessor view**: - - .. code-block:: html - - <tbody> - <elements /> - </tbody> - - -.. _reference-layout-elementwrap: - -elementWrap -^^^^^^^^^^^ - -:aspect:`Property:` - elementWrap - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Outer wrap for regular objects. - - The <element /> tag will be substituted with the child element. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <li> - <element /> - </li> - - Default layout **confirmation view**: - - .. code-block:: html - - <li> - <element /> - </li> - - Default layout **postProcessor view**: - - .. code-block:: html - - <tr> - <element /> - </tr> - - -.. _reference-layout-label: - -label -^^^^^ - -:aspect:`Property:` - label - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the labels. - - The <labelvalue /> tag will be substituted with the label text. - - If available, the <mandatory /> tag will be substituted with the - validation rule message, styled by its own layout. - - If available, the <error /> tag will be substituted with the error - message from the validation rule when the submitted value is not valid. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label> - <labelvalue /> - <mandatory /> - <error /> - </label> - - Default layout **confirmation view**: - - .. code-block:: html - - <label> - <labelvalue /> - </label> - - Default layout **postProcessor view**: - - .. code-block:: html - - <em> - <labelvalue /> - </em> - - -.. _reference-layout-mandatory: - -mandatory -^^^^^^^^^ - -:aspect:`Property:` - mandatory - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - -:aspect:`Description:` - Layout for the validation rule message to describe the rule. - - The <mandatoryvalue /> tag will be substituted with the validation rule - message. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <em> - <mandatoryvalue /> - </em> - - -.. _reference-layout-error: - -error -^^^^^ - -:aspect:`Property:` - error - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - -:aspect:`Description:` - Layout for the validation rule error message when the submitted data - does not validate. - - The <errorvalue /> tag will be substituted with the validation rule - error message. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <strong> - <errorvalue /> - </strong> - - -.. _reference-layout-legend: - -legend -^^^^^^ - -:aspect:`Property:` - legend - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the legend. - - The <legendvalue /> tag will be substituted with the legend text. - - If available, the <mandatory /> tag will be substituted with the - validation rule message, styled by its own layout. - - If available, the <error /> tag will be substituted with the error - message from the validation rule when the submitted value is not valid. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <legend> - <legendvalue /> - <mandatory /> - <error /> - </legend> - - Default layout **confirmation view**: - - .. code-block:: html - - <legend> - <legendvalue /> - </legend> - - - Default layout **postProcessor view**: - - .. code-block:: html - - <thead> - <tr> - <th colspan="2" align="left"> - <legendvalue /> - </th> - </tr> - </thead> - - -.. _reference-layout-button: - -button -^^^^^^ - -:aspect:`Property:` - button - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - -:aspect:`Description:` - Layout for the BUTTON object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label /> - <input /> - - -.. _reference-layout-checkbox: - -checkbox -^^^^^^^^ - -:aspect:`Property:` - checkbox - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the CHECKBOX object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label /> - <input /> - - Default layout **confirmation view**: - - .. code-block:: html - - <label /> - <inputvalue /> - - Default layout **postProcessor view**: - - .. code-block:: html - - <td style="width: 200px;"> - <label /> - </td> - <td> - <inputvalue /> - </td> - - -.. _reference-layout-checkboxgroup: - -checkboxgroup -^^^^^^^^^^^^^ - -:aspect:`Property:` - checkboxgroup - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the CHECKBOXGROUP object. - - The <containerwrap /> tag will be substituted by the outer container - wrap and includes all child elements. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - - Default layout **confirmation view**: - - .. code-block:: html - - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - - Default layout **postProcessor view**: - - .. code-block:: html - - <td colspan="2"> - <table cellspacing="0" style="padding-left: 20px; margin-bottom: 20px;"> - <legend /> - <containerWrap /> - </table> - </td> - - -.. _reference-layout-fieldset: - -fieldset -^^^^^^^^ - -:aspect:`Property:` - fieldset - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the FIELDSET object. - - The <containerwrap /> tag will be substituted by the outer container - wrap and includes all child elements. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - - Default layout **confirmation view**: - - .. code-block:: html - - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - - Default layout **postProcessor view**: - - .. code-block:: html - - <td colspan="2"> - <table cellspacing="0" style="padding-left: 20px; margin-bottom: 20px;"> - <legend /> - <containerWrap /> - </table> - </td> - - -.. _reference-layout-fileupload: - -fileupload -^^^^^^^^^^ - -:aspect:`Property:` - fileupload - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the FILEUPLOAD object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label /> - <input /> - - Default layout **confirmation view**: - - .. code-block:: html - - <label /> - <inputvalue /> - - Default layout **postProcessor view**: - - .. code-block:: html - - <td style="width: 200px;"> - <label /> - </td> - <td> - <inputvalue /> - </td> - - -.. _reference-layout-hidden: - -hidden -^^^^^^ - -:aspect:`Property:` - hidden - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - postProcessor - -:aspect:`Description:` - Layout for the HIDDEN object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <input /> - - Default layout **postProcessor view**: - - .. code-block:: html - - <td style="width: 200px;"> - <em> - <label /> - </em> - </td> - <td> - <inputvalue /> - </td> - - -.. _reference-layout-optgroup: - -optgroup -^^^^^^^^ - -:aspect:`Property:` - optgroup - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the OPTGROUP object. - - The <elements /> tag will be substituted with all the child elements, - which actually can only be OPTION objects. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <optgroup> - <elements /> - </optgroup> - - Default layout **confirmation view**: - - .. code-block:: html - - <elements /> - - Default layout **postProcessor view**: - - .. code-block:: html - - <elements /> - - -.. _reference-layout-option: - -option -^^^^^^ - -:aspect:`Property:` - option - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the OPTION object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <option /> - - Default layout **confirmation view**: - - .. code-block:: html - - <inputvalue /> - - Default layout **postProcessor view**: - - .. code-block:: html - - <div> - <inputvalue /> - </div> - - -.. _reference-layout-password: - -password -^^^^^^^^ - -:aspect:`Property:` - password - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - -:aspect:`Description:` - Layout for the PASSWORD object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label /> - <input /> - - -.. _reference-layout-radio: - -radio -^^^^^ - -:aspect:`Property:` - radio - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the RADIO object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label /> - <input /> - - Default layout **confirmation view**: - - .. code-block:: html - - <label /> - <inputvalue /> - - Default layout **postProcessor view**: - - .. code-block:: html - - <td style="width: 200px;"> - <label /> - </td> - <td> - <inputvalue /> - </td> - - -.. _reference-layout-radiogroup: - -radiogroup -^^^^^^^^^^ - -:aspect:`Property:` - radiogroup - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the RADIOGROUP object. - - The <containerwrap /> tag will be substituted by the outer container - wrap and includes all child elements. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - - - Default layout **confirmation view**: - - .. code-block:: html - - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - - Default layout **postProcessor view**: - - .. code-block:: html - - <td colspan="2"> - <table cellspacing="0" style="padding-left: 20px; margin-bottom: 20px;"> - <legend /> - <containerWrap /> - </table> - </td> - - -.. _reference-layout-reset: - -reset -^^^^^ - -:aspect:`Property:` - reset - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - -:aspect:`Description:` - Layout for the RESET object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label /> - <input /> - - -.. _reference-layout-select: - -select -^^^^^^ - -:aspect:`Property:` - select - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the SELECT object. - - The <elements /> tag will be substituted with all the child elements, - which only can be OPTGROUP or OPTION objects. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label /> - <select> - <elements /> - </select> - - Default layout **confirmation view**: - - .. code-block:: html - - <label /> - <ol> - <elements /> - </ol> - - Default layout **postProcessor view**: - - .. code-block:: html - - <td style="width: 200px;"> - <label /> - </td> - <td> - <elements /> - </td> - - -.. _reference-layout-submit: - -submit -^^^^^^ - -:aspect:`Property:` - submit - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - -:aspect:`Description:` - Layout for the SUBMIT object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label /> - <input /> - - -.. _reference-layout-textarea: - -textarea -^^^^^^^^ - -:aspect:`Property:` - textarea - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the TEXTAREA object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label /> - <textarea /> - - - Default layout **confirmation view**: - - .. code-block:: html - - <label /> - <inputvalue /> - - Default layout **postProcessor view**: - - .. code-block:: html - - <td style="width: 200px;" valign="top"> - <label /> - </td> - <td> - <inputvalue /> - </td> - - -.. _reference-layout-textblock: - -textblock -^^^^^^^^^ - -:aspect:`Property:` - textblock - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - -:aspect:`Description:` - Layout for the TEXTBLOCK object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <textblock /> - - -.. _reference-layout-textline: - -textline -^^^^^^^^ - -:aspect:`Property:` - textline - -:aspect:`Data type:` - string - -:aspect:`Available in views:` - - form - - confirmation - - postProcessor - -:aspect:`Description:` - Layout for the TEXTLINE object. - -:aspect:`Default:` - Default layout **form view**: - - .. code-block:: html - - <label /> - <input /> - - Default layout **confirmation view**: - - .. code-block:: html - - <label /> - <inputvalue /> - - Default layout **postProcessor view**: - - .. code-block:: html - - <td style="width: 200px;"> - <label /> - </td> - <td> - <inputvalue /> - </td> - - -.. _change-layout-specific-view-example: - -Example showing all .layout properties and defaults -=================================================== - -The code snippets below shows all available settings across all views -including their default layout. - -.. code-block:: typoscript - - tt_content.mailform.20 { - # ### - # form view - # #### - - form { - layout { - form ( - <form> - <containerWrap /> - </form> - ) - - containerWrap ( - <ol> - <elements /> - </ol> - ) - - elementWrap ( - <li> - <element /> - </li> - ) - - label ( - <label> - <labelvalue /> - <mandatory /> - <error /> - </label> - ) - - mandatory ( - <em> - <mandatoryvalue /> - </em> - ) - - error ( - <strong> - <errorvalue /> - </strong> - ) - - legend ( - <legend> - <legendvalue /> - <mandatory /> - <error /> - </legend> - ) - - button ( - <label /> - <input /> - ) - - checkbox ( - <label /> - <input /> - ) - - checkboxgroup ( - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - ) - fieldset ( - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - ) - - fileupload ( - <label /> - <input /> - ) - - hidden ( - <input /> - ) - - optgroup ( - <optgroup> - <elements /> - </optgroup> - ) - - option ( - <option /> - ) - - password ( - <label /> - <input /> - ) - - radio ( - <label /> - <input /> - ) - - radiogroup ( - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - ) - - reset ( - <label /> - <input /> - ) - - select ( - <label /> - <select> - <elements /> - </select> - ) - - submit ( - <label /> - <input /> - ) - - textarea ( - <label /> - <textarea /> - ) - - textblock ( - <textblock /> - ) - - textline ( - <label /> - <input /> - ) - } - } - - # ### - # confirmation view - # ### - - confirmation { - layout { - confirmation ( - <containerWrap /> - ) - - containerWrap ( - <ol> - <elements /> - </ol> - ) - - elementWrap ( - <li> - <element /> - </li> - ) - - label ( - <label> - <labelvalue /> - </label> - ) - - legend ( - <legend> - <legendvalue /> - </legend> - ) - - checkbox ( - <label /> - <inputvalue /> - ) - - checkboxgroup ( - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - ) - - fieldset ( - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - ) - - fileupload ( - <label /> - <inputvalue /> - ) - - optgroup ( - <elements /> - ) - - option ( - <inputvalue /> - ) - - radio ( - <label /> - <inputvalue /> - ) - - radiogroup ( - <fieldset> - <legend /> - <containerWrap /> - </fieldset> - ) - - select ( - <label /> - <ol> - <elements /> - </ol> - ) - - textarea ( - <label /> - <inputvalue /> - ) - - textline ( - <label /> - <inputvalue /> - ) - } - } - - # ### - # postProcesso view - # ### - - postProcessor { - layout { - html ( - <html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - </head> - <body> - <table cellspacing="0"> - <containerWrap /> - </table> - </body> - </html> - ) - - containerWrap ( - <tbody> - <elements /> - </tbody> - ) - - elementWrap ( - <tr> - <element /> - </tr> - ) - - label ( - <em> - <labelvalue /> - </em> - ) - - legend ( - <thead> - <tr> - <th colspan="2" align="left"> - <legendvalue /> - </th> - </tr> - </thead> - ) - - checkbox ( - <td style="width: 200px;"> - <label /> - </td> - <td> - <inputvalue /> - </td> - ) - - checkboxgroup ( - <td colspan="2"> - <table cellspacing="0" style="padding-left: 20px; margin-bottom: 20px;"> - <legend /> - <containerWrap /> - </table> - </td> - ) - - fieldset ( - <td colspan="2"> - <table cellspacing="0" style="padding-left: 20px; margin-bottom: 20px;"> - <legend /> - <containerWrap /> - </table> - </td> - ) - - fileupload ( - <td style="width: 200px;"> - <label /> - </td> - <td> - <inputvalue /> - </td> - ) - - hidden ( - <td style="width: 200px;"> - <em> - <label /> - </em> - </td> - <td> - <inputvalue /> - </td> - ) - - optgroup ( - <elements /> - ) - - option ( - <div> - <inputvalue /> - </div> - ) - - radio ( - <td style="width: 200px;"> - <label /> - </td> - <td> - <inputvalue /> - </td> - ) - - radiogroup ( - <td colspan="2"> - <table cellspacing="0" style="padding-left: 20px; margin-bottom: 20px;"> - <legend /> - <containerWrap /> - </table> - </td> - ) - - select ( - <td style="width: 200px;"> - <label /> - </td> - <td> - <elements /> - </td> - ) - - textarea ( - <td style="width: 200px;" valign="top"> - <label /> - </td> - <td> - <inputvalue /> - </td> - ) - - textline ( - <td style="width: 200px;"> - <label /> - </td> - <td> - <inputvalue /> - </td> - ) - } - } - } - diff --git a/typo3/sysext/form/Documentation/Configuration/Layout/LayoutWholeForm/Index.rst b/typo3/sysext/form/Documentation/Configuration/Layout/LayoutWholeForm/Index.rst deleted file mode 100644 index 5ee35260eed0..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Layout/LayoutWholeForm/Index.rst +++ /dev/null @@ -1,46 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _change-layout-of-whole-form: - -=================================== -Change the layout of the whole form -=================================== - -.. attention:: - - It is not recommended to change the layout globally for the whole form. - Unfortunately, using view specific layout settings did not work for a - long time and is now widely used by integrators. - - There are several reasons for not to use global layout settings: - - - Some objects cannot be changed globally. - - Changing some objects will cause problems which lead to failures in - the processing. The code will die with PHP errors. - - Quite often it does not make sense to do these changes globally. - - Instead change the layout for a :ref:`specific view <change-layout-specific-view>`! - -Apart from the above mentioned problems one could change the layout globally -using the following TypoScript setup. Using :ts:`tt_content.mailform.20` -registers the chances for all forms of the below the page tree. If one wants -to change the layout only for a specific form, a TypoScript library could be -build as shown :ref:`here <reference-form-example>`. - -.. code-block:: typoscript - - tt_content.mailform.20 { - layout { - # changing the layout of the form object globally - form ( - <form class="form-class"> - <containerWrap /> - </form> - ) - } - } - -As one can see, an (X)HTML kind of markup is used. Actually it is XML, with -some extra tags like the :ts:`containerWrap`. - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Button/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Button/Index.rst deleted file mode 100644 index 06eec34f7828..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Button/Index.rst +++ /dev/null @@ -1,158 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-button: - -====== -BUTTON -====== - -Creates a push button. User agents should use the value of the value -attribute as the button's label. - -Push buttons have no default behavior. Each push button may have -client-side scripts associated with the element's event attributes. -When an event occurs (e.g., the user presses the button, releases it, -etc.), the associated script is triggered. - - -.. _reference-button-accesskey: - -accesskey -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accesskey`. - - -.. _reference-button-alt: - -alt -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-alt`. - - -.. _reference-button-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-button-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-button-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-button-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-button-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-button-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-button-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-button` - specific information. - - -.. _reference-button-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-button-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-button-tabindex: - -tabindex -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-tabindex`. - - -.. _reference-button-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - - -.. _reference-button-type: - -type -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-type`. - -:aspect:`Default:` - button - - -.. _reference-button-value: - -value -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-value`. - -[tsref:(cObject).FORM.FormObject.BUTTON] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Checkbox/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Checkbox/Index.rst deleted file mode 100644 index 3de4a636112d..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Checkbox/Index.rst +++ /dev/null @@ -1,169 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-checkbox: - -======== -CHECKBOX -======== - -Creates a checkbox. - -Checkboxes are on/off switches that may be toggled by the user. A switch is -"on" when the control element's checked attribute is set. When a form is -submitted, only "on" checkbox controls can become successful. - -Several checkboxes in a form may share the same control name. Thus, for -example, checkboxes allow users to select several values for the same -property. A CHECKBOX object only displays one checkbox in the form. - - -.. _reference-checkbox-accesskey: - -accesskey -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accesskey`. - - -.. _reference-checkbox-alt: - -alt -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-alt`. - - -.. _reference-checkbox-checked: - -checked -======= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-checked`. - - -.. _reference-checkbox-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-checkbox-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-checkbox-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-checkbox-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-checkbox-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-checkbox-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-checkbox-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-checkbox` - specific information. - - -.. _reference-checkbox-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-checkbox-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-checkbox-tabindex: - -tabindex -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-tabindex`. - - -.. _reference-checkbox-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - - -.. _reference-checkbox-type: - -type -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-type`. - -:aspect:`Default:` - checkbox - - -.. _reference-checkbox-value: - -value -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-value`. - -[tsref:(cObject).FORM.FormObject.CHECKBOX] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Fieldset/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Fieldset/Index.rst deleted file mode 100644 index 7884cca96e62..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Fieldset/Index.rst +++ /dev/null @@ -1,96 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-fieldset: - -======== -FIELDSET -======== - -The FIELDSET element allows authors to group thematically related controls -and labels. Grouping controls makes it easier for users to understand the -purpose while simultaneously facilitating tabbing navigation for visual user -agents and speech navigation for speech-oriented user agents. The proper use -of this element makes documents more accessible. - - -.. _reference-fieldset-1-2-3-4: - -1, 2, 3, 4 ... -============== - -:aspect:`Property:` - 1, 2, 3, 4 ... - -:aspect:`Data type:` - [array of FORM objects] - -:aspect:`Description:` - FORM objects that are part of the FIELDSET. - - -.. _reference-fieldset-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-fieldset-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-fieldset-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-fieldset-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-fieldset-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-fieldset` - specific information. - - -.. _reference-fieldset-legend: - -legend -====== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-legend`. - - -.. _reference-fieldset-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - -[tsref:(cObject).FORM.FormObject.FIELDSET] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Fileupload/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Fileupload/Index.rst deleted file mode 100644 index d7490ed27dde..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Fileupload/Index.rst +++ /dev/null @@ -1,156 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-fileupload: - -========== -FILEUPLOAD -========== - -Creates a file select control. User agents may use the value of the value -attribute as the initial file name. - -This control type allows the user to select files so that their contents may -be submitted with a form. - - -.. _reference-fileupload-accesskey: - -accesskey -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accesskey`. - - -.. _reference-fileupload-alt: - -alt -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-alt`. - - -.. _reference-fileupload-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-fileupload-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-fileupload-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-fileupload-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-fileupload-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-fileupload-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-fileupload-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-fileupload` - specific information. - - -.. _reference-fileupload-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-fileupload-size: - -size -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-size`. - - -.. _reference-fileupload-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-fileupload-tabindex: - -tabindex -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-tabindex`. - - -.. _reference-fileupload-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - - -.. _reference-fileupload-type: - -type -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-type`. - -:aspect:`Default:` - file - -[tsref:(cObject).FORM.FormObject.FILEUPLOAD] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Form/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Form/Index.rst deleted file mode 100644 index 928d05c51688..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Form/Index.rst +++ /dev/null @@ -1,313 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-form: - -==== -FORM -==== - -A form will always start with the FORM object. TYPO3 recognizes this object -and sends all TypoScript data to the FORM extension. - - -.. _reference-form-accept: - -accept -====== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accept`. - - -.. _reference-form-accept-charset: - -accept-charset -============== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accept-charset`. - - -.. _reference-form-action: - -action -====== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-action`. - - -.. _reference-form-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-form-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-form-enctype: - -enctype -======= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-enctype`. - - -.. _reference-form-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-form-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-form-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-form` - specific information. - - -.. _reference-form-method: - -method -====== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-method`. - - -.. _reference-form-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-form-postprocessor: - -postProcessor -============= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-postProcessor`. - - -.. _reference-form-prefix: - -prefix -====== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-prefix`. - - -.. _reference-form-rules: - -rules -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-rules`. - - -.. _reference-form-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-form-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - - -[tsref:(cObject).FORM] - -.. _reference-form-example: - -Example -======= - -This example shows a simple payment form. At the beginning the layout of the -radio buttons is changed for the form view. The label and the input field -are switched. - -The example builds a form as TypoScript library which can be assigned to a -marker or used inside a fluid template. The defined layout settings are only -valid within this TS library. - -.. code-block:: typoscript - - lib.form = FORM - lib.form { - method = post - - postProcessor { - # ... - } - - form { - layout { - radio ( - <input /> - <label /> - ) - } - } - - 10 = FIELDSET - 10 { - legend = Name - - 10 = SELECT - 10 { - label = Title - - 10 = OPTION - 10 { - data = Mr. - selected = 1 - } - - 20 = OPTION - 20 { - data = Mrs. - } - - 30 = OPTION - 30 { - data = Ms. - } - - 40 = OPTION - 40 { - data = Dr. - } - - 50 = OPTION - 50 { - data = Viscount - } - } - - 20 = TEXTLINE - 20 { - label = First name - } - - 30 = TEXTLINE - 30 { - label = Last name - } - } - - 20 = FIELDSET - 20 { - legend = Address - - 10 = TEXTLINE - 10 { - label = Street - } - - 20 = TEXTLINE - 20 { - label = City - } - - 30 = TEXTLINE - 30 { - label = State - } - - 40 = TEXTLINE - 40 { - label = ZIP code - } - } - - 30 = FIELDSET - 30 { - legend = Payment details - - 10 = FIELDSET - 10 { - legend = Credit card - - 10 = RADIO - 10 { - label = American Express - name = creditcard - } - - 20 = RADIO - 20 { - label = Mastercard - name = creditcard - } - - 30 = RADIO - 30 { - label = Visa - name = creditcard - } - - 40 = RADIO - 40 { - label = Blockbuster Card - name = creditcard - } - } - - 20 = TEXTLINE - 20 { - label = Card number - } - - 30 = TEXTLINE - 30 { - label = Expiry date - } - } - - 40 = SUBMIT - 40 { - value = Submit my details - } - } - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Header/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Header/Index.rst deleted file mode 100644 index 3b8940f963e8..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Header/Index.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-header: - -====== -HEADER -====== - -Creates a textual headline wrapped with a headline tag (e.g. h1). This -element can be used for a visual separation or transportation of content. It -is neither displayed on the confirmation page nor in the email. - -.. _reference-header-content: - -content -======= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-content`. - - -.. _reference-header-headingSize: - -headingSize -=========== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-headingSize`. - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Hidden/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Hidden/Index.rst deleted file mode 100644 index 727c6d6fb6dc..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Hidden/Index.rst +++ /dev/null @@ -1,100 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-hidden: - -====== -HIDDEN -====== - -Creates a hidden control. - -Authors may create controls that are not rendered (visually) but whose -values are submitted with a form. This control type can generally be used to -store information between client/ server exchanges that would otherwise -be lost due to the stateless nature of HTTP (see [RFC2616]). - - -.. _reference-hidden-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-hidden-filters: - -filters -======= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-filters`. - - -.. _reference-hidden-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-hidden-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-hidden-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-hidden` - specific information. - - -.. _reference-hidden-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-hidden-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-hidden-type: - -type -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-type`. - - -.. _reference-hidden-value: - -value -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-value`. - -[tsref:(cObject).FORM.FormObject.HIDDEN] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Index.rst deleted file mode 100644 index 1ef14fbab5ef..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Index.rst +++ /dev/null @@ -1,172 +0,0 @@ -.. include:: ../../Includes.txt - - -.. _form-objects: - -============ -FORM objects -============ - -The editor is not bound to FORM objects shown below. Whenever FORM will be -put in TypoScript, the contents of this property will be sent to the -FORM plugin. However, one can use regular TYPO3 content objects (cObjects) -as well. This means the integrator has the possibility to add COA, TEXT or -even HMENU in the FORM TypoScript. - -Due to technical limitations it is **not** possible to nest form objects -inside content objects. The following nesting will not work: -:ts:`FORM` > :ts:`COA` > :ts:`TEXTLINE`. - -Furthermore, using cObjects is only allowed when **not** using the form -content element/ wizard in the backend. This is due to security reasons. -The functionality is only available when embedding a form directly in the -TypoScript setup. - -.. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: - - ObjectAttributes/Index - Button/Index - Checkbox/Index - Fieldset/Index - Fileupload/Index - Form/Index - Header/Index - Hidden/Index - Optgroup/Index - Option/Index - Password/Index - Radio/Index - Reset/Index - Select/Index - Submit/Index - Textarea/Index - Textblock/Index - Textline/Index - -============== ================================================= ================================================= ================================================= ================================================= ====================================================== =================================================== -Element BUTTON CHECKBOX FIELDSET FILEUPLOAD FORM HEADER -============== ================================================= ================================================= ================================================= ================================================= ====================================================== =================================================== -accept :ref:`X <reference-objects-attributes-accept>` -accept-charset :ref:`X <reference-objects-attributes-accept-charset>` -accesskey :ref:`X <reference-objects-attributes-accesskey>` :ref:`X <reference-objects-attributes-accesskey>` :ref:`X <reference-objects-attributes-accesskey>` -action :ref:`X <reference-objects-attributes-action>` -alt :ref:`X <reference-objects-attributes-alt>` :ref:`X <reference-objects-attributes-alt>` :ref:`X <reference-objects-attributes-alt>` -checked :ref:`X <reference-objects-attributes-checked>` -class :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` -content :ref:`X <reference-objects-attributes-content>` -cols -data -dir :ref:`X <reference-objects-attributes-dir>` :ref:`X <reference-objects-attributes-dir>` :ref:`X <reference-objects-attributes-dir>` :ref:`X <reference-objects-attributes-dir>` :ref:`X <reference-objects-attributes-dir>` -disabled :ref:`X <reference-objects-attributes-disabled>` :ref:`X <reference-objects-attributes-disabled>` :ref:`X <reference-objects-attributes-disabled>` -enctyp :ref:`X <reference-objects-attributes-enctype>` -filters -headingSize :ref:`X <reference-objects-attributes-headingSize>` -id :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` -label :ref:`X <reference-objects-attributes-label>` :ref:`X <reference-objects-attributes-label>` :ref:`X <reference-objects-attributes-label>` -lang :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` -layout :ref:`X <reference-layout>` :ref:`X <reference-layout>` :ref:`X <reference-layout>` :ref:`X <reference-layout>` :ref:`X <reference-layout>` -legend :ref:`X <reference-objects-attributes-legend>` -maxlength -method :ref:`X <reference-objects-attributes-method>` -multiple -name :ref:`X <reference-objects-attributes-name>` :ref:`X <reference-objects-attributes-name>` :ref:`X <reference-objects-attributes-name>` :ref:`X <reference-objects-attributes-name>` -postProcessor :ref:`X <reference-objects-attributes-postProcessor>` -prefix :ref:`X <reference-objects-attributes-prefix>` -readonly -rows -rules :ref:`X <reference-objects-attributes-rules>` -selected -size :ref:`X <reference-objects-attributes-size>` -src -style :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` -tabindex :ref:`X <reference-objects-attributes-tabindex>` :ref:`X <reference-objects-attributes-tabindex>` :ref:`X <reference-objects-attributes-tabindex>` -title :ref:`X <reference-objects-attributes-title>` :ref:`X <reference-objects-attributes-title>` :ref:`X <reference-objects-attributes-title>` :ref:`X <reference-objects-attributes-title>` -type :ref:`X <reference-objects-attributes-type>` :ref:`X <reference-objects-attributes-type>` :ref:`X <reference-objects-attributes-type>` -value :ref:`X <reference-objects-attributes-value>` :ref:`X <reference-objects-attributes-value>` -============== ================================================= ================================================= ================================================= ================================================= ====================================================== =================================================== - -============== ================================================= ================================================= ================================================= ================================================= ================================================= ================================================= -Element HIDDEN OPTGROUP OPTION PASSWORD RADIO RESET -============== ================================================= ================================================= ================================================= ================================================= ================================================= ================================================= -accept -accept-charset -accesskey :ref:`X <reference-objects-attributes-accesskey>` :ref:`X <reference-objects-attributes-accesskey>` :ref:`X <reference-objects-attributes-accesskey>` -action -alt :ref:`X <reference-objects-attributes-alt>` :ref:`X <reference-objects-attributes-alt>` :ref:`X <reference-objects-attributes-alt>` -checked :ref:`X <reference-objects-attributes-checked>` -class :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` -content -cols -data :ref:`X <reference-objects-attributes-data>` -dir :ref:`X <reference-objects-attributes-dir>` :ref:`X <reference-objects-attributes-dir>` :ref:`X <reference-objects-attributes-dir>` -disabled :ref:`X <reference-objects-attributes-disabled>` :ref:`X <reference-objects-attributes-disabled>` :ref:`X <reference-objects-attributes-disabled>` :ref:`X <reference-objects-attributes-disabled>` :ref:`X <reference-objects-attributes-disabled>` -enctype -filters :ref:`X <reference-objects-attributes-filters>` :ref:`X <reference-objects-attributes-filters>` -headingSize -id :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` -label :ref:`X <reference-objects-attributes-label>` :ref:`X <reference-objects-attributes-label>` :ref:`X <reference-objects-attributes-label>` :ref:`X <reference-objects-attributes-label>` :ref:`X <reference-objects-attributes-label>` -lang :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` -layout :ref:`X <reference-layout>` :ref:`X <reference-layout>` :ref:`X <reference-layout>` :ref:`X <reference-layout>` :ref:`X <reference-layout>` :ref:`X <reference-layout>` -legend -maxlength :ref:`X <reference-objects-attributes-maxlength>` -method -multiple -name :ref:`X <reference-objects-attributes-name>` :ref:`X <reference-objects-attributes-name>` :ref:`X <reference-objects-attributes-name>` :ref:`X <reference-objects-attributes-name>` -postProcessor -prefix -readonly :ref:`X <reference-objects-attributes-readonly>` -rows -rules -selected :ref:`X <reference-objects-attributes-selected>` -size :ref:`X <reference-objects-attributes-size>` -style :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` -tabindex :ref:`X <reference-objects-attributes-tabindex>` :ref:`X <reference-objects-attributes-tabindex>` :ref:`X <reference-objects-attributes-tabindex>` -title :ref:`X <reference-objects-attributes-title>` :ref:`X <reference-objects-attributes-title>` :ref:`X <reference-objects-attributes-title>` :ref:`X <reference-objects-attributes-title>` :ref:`X <reference-objects-attributes-title>` -type :ref:`X <reference-objects-attributes-type>` :ref:`X <reference-objects-attributes-type>` :ref:`X <reference-objects-attributes-type>` :ref:`X <reference-objects-attributes-type>` -value :ref:`X <reference-objects-attributes-value>` :ref:`X <reference-objects-attributes-value>` :ref:`X <reference-objects-attributes-value>` :ref:`X <reference-objects-attributes-value>` :ref:`X <reference-objects-attributes-value>` -============== ================================================= ================================================= ================================================= ================================================= ================================================= ================================================= - -============== ================================================= ================================================= ================================================= ================================================= ================================================= -Element SELECT SUBMIT TEXTAREA TEXTBLOCK TEXTLINE -============== ================================================= ================================================= ================================================= ================================================= ================================================= -accept -accept-charset -accesskey :ref:`X <reference-objects-attributes-accesskey>` :ref:`X <reference-objects-attributes-accesskey>` :ref:`X <reference-objects-attributes-accesskey>` -action -alt :ref:`X <reference-objects-attributes-alt>` :ref:`X <reference-objects-attributes-alt>` -checked -class :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` :ref:`X <reference-objects-attributes-class>` -content :ref:`X <reference-objects-attributes-content>` -cols :ref:`X <reference-objects-attributes-cols>` -data -dir :ref:`X <reference-objects-attributes-dir>` :ref:`X <reference-objects-attributes-dir>` :ref:`X <reference-objects-attributes-dir>` -disabled :ref:`X <reference-objects-attributes-disabled>` :ref:`X <reference-objects-attributes-disabled>` :ref:`X <reference-objects-attributes-disabled>` :ref:`X <reference-objects-attributes-disabled>` -enctype -filters -headingSize -id :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` :ref:`X <reference-objects-attributes-id>` -label :ref:`X <reference-objects-attributes-label>` :ref:`X <reference-objects-attributes-label>` :ref:`X <reference-objects-attributes-label>` :ref:`X <reference-objects-attributes-label>` -lang :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` :ref:`X <reference-objects-attributes-lang>` -layout :ref:`X <reference-layout>` :ref:`X <reference-layout>` -legend -maxlength :ref:`X <reference-objects-attributes-maxlength>` -method -multiple :ref:`X <reference-objects-attributes-multiple>` -name :ref:`X <reference-objects-attributes-name>` :ref:`X <reference-objects-attributes-name>` :ref:`X <reference-objects-attributes-name>` :ref:`X <reference-objects-attributes-name>` -postProcessor -prefix -readonly :ref:`X <reference-objects-attributes-readonly>` :ref:`X <reference-objects-attributes-readonly>` -rows :ref:`X <reference-objects-attributes-rows>` -rules -selected -size :ref:`X <reference-objects-attributes-size>` :ref:`X <reference-objects-attributes-size>` -style :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` :ref:`X <reference-objects-attributes-style>` -tabindex :ref:`X <reference-objects-attributes-tabindex>` :ref:`X <reference-objects-attributes-tabindex>` :ref:`X <reference-objects-attributes-tabindex>` :ref:`X <reference-objects-attributes-tabindex>` -title :ref:`X <reference-objects-attributes-title>` :ref:`X <reference-objects-attributes-title>` :ref:`X <reference-objects-attributes-title>` :ref:`X <reference-objects-attributes-title>` -type :ref:`X <reference-objects-attributes-type>` -value :ref:`X <reference-objects-attributes-value>` :ref:`X <reference-objects-attributes-value>` -============== ================================================= ================================================= ================================================= ================================================= ================================================= - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/ObjectAttributes/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/ObjectAttributes/Index.rst deleted file mode 100644 index a95d2f1d9d52..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/ObjectAttributes/Index.rst +++ /dev/null @@ -1,973 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-object-attributes: - -================= -Object Attributes -================= - -.. contents:: - :local: - :depth: 1 - - -.. _reference-objects-attributes-accept: - -accept -====== - -:aspect:`Property:` - accept - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute specifies a comma-separated list of content types that a - server processing this form will handle correctly. - - User agents may use this information to filter out non-conforming files - when prompting a user to select files to be sent to the server (cf. the - INPUT element when type="file"). - - RFC2045: For a complete list, see http://www.iana.org/assignments/media-types/ - - -.. _reference-objects-attributes-accept-charset: - -accept-charset -============== - -:aspect:`Property:` - accept-charset - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute specifies the list of character encodings for input data - that is accepted by the server processing this form. - - The value is a space- and/or comma-delimited list of charset values. - - The client must interpret this list as an exclusive-or list. I.e., the - server is able to accept any single character encoding per entity - received. - - The default value for this attribute is the reserved string "UNKNOWN". - User agents may interpret this value as the character encoding that was - used to transmit the document containing this FORM element. - - RFC2045: For a complete list, see http://www.iana.org/assignments/character-sets/ - - -.. _reference-objects-attributes-accesskey: - -accesskey -========= - -:aspect:`Property:` - accesskey - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute assigns an access key to an element. - - An access key is a single character from the document character set. - - **Note**: Authors should consider the input method of the expected - reader when specifying an accesskey. - - Pressing an access key assigned to an element gives focus to the - element. - - The action that occurs when an element receives focus depends on the - element. For example, when a user activates a link defined by the - element, the user agent generally follows the link. When a user - activates a radio button, the user agent changes the value of the radio - button. When the user activates a text field, it allows input, etc. - - -.. _reference-objects-attributes-action: - -action -====== - -:aspect:`Property:` - action - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute specifies a form processing agent. - - In normal circumstances the action attribute will be filled - automatically, because the form must call the same URI where the form - resides. - - Besides specifying a page uid it is also possible to set an anchor. See - the examples below. - - .. code-block:: typoscript - - action = #anchor - action = 4#anchor - - -.. _reference-objects-attributes-alt: - -alt -=== - -:aspect:`Property:` - alt - -:aspect:`Data type:` - string - -:aspect:`Description:` - For user agents that cannot display images, forms, or applets, this - attribute specifies alternative text. The language of this text is - specified by the lang attribute. - - -.. _reference-objects-attributes-checked: - -checked -======= - -:aspect:`Property:` - checked - -:aspect:`Data type:` - boolean/ checked - -:aspect:`Description:` - When the type attribute has the value "radio" or "checkbox", this - boolean attribute specifies that the button is activated. - - User agents must ignore this attribute for other control types. - - **Examples:** - - .. code-block:: typoscript - - checked = 1 - checked = 0 - checked = checked - - -.. _reference-objects-attributes-class: - -class -===== - -:aspect:`Property:` - class - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute assigns a class name or set of class names to an element. - - Any number of elements may be assigned the same class name or names. - - Multiple class names must be separated by white space characters. - - -.. _reference-objects-attributes-cols: - -cols -==== - -:aspect:`Property:` - cols - -:aspect:`Data type:` - integer - -:aspect:`Description:` - This attribute specifies the visible width. - - Users should be able to enter longer lines than this, so user agents - should provide some means to scroll through the contents of the control - when the contents extend beyond the visible area. User agents may wrap - visible text lines to keep long lines visible without the need for - scrolling. - -:aspect:`Default:` - 40 - - -.. _reference-objects-attributes-content: - -content -======= - -:aspect:`Property:` - content - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute contains the content of a FORM object. - - -.. _reference-objects-attributes-data: - -data -==== - -:aspect:`Property:` - data - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute contains the content of a FORM object. - - -.. _reference-objects-attributes-dir: - -dir -=== - -:aspect:`Property:` - dir - -:aspect:`Data type:` - ltr/ rtl - -:aspect:`Description:` - This attribute specifies the base direction of directionally neutral - text (i.e., text that does not have inherent directionality as defined - in [UNICODE]) in an element's content and attribute values. - - It also specifies the directionality of tables. Possible values: - - - LTR: Left-to-right text or table. - - - RTL: Right-to-left text or table. - - In addition to specifying the language of a document with the lang - attribute, authors may need to specify the base directionality - (left-to-right or right-to-left) of portions of a document's text, of a - table structure, etc. This is done with the dir attribute. - - -.. _reference-objects-attributes-disabled: - -disabled -======== - -:aspect:`Property:` - disabled - -:aspect:`Data type:` - boolean/ disabled - -:aspect:`Description:` - When set for a form control, this boolean attribute disables the control - for user input. - - When set, the disabled attribute has the following effects on an - element: - - - Disabled controls do not receive focus. - - - Disabled controls are skipped in tabbing navigation. - - - Disabled controls cannot be successful. - - This attribute is inherited but local declarations override the - inherited value. - - How disabled elements are rendered depends on the user agent. For - example, some user agents "gray out" disabled menu items, button labels, - etc. - - **Examples:** - - .. code-block:: typoscript - - disabled = 1 - disabled = 0 - disabled = disabled - - -.. _reference-objects-attributes-enctype: - -enctype -======= - -:aspect:`Property:` - enctype - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute specifies the content type used to submit the form to the - server (when the value of method is "post"). The default value for this - attribute is "application/x-www-form-urlencoded". - - The value "multipart/form-data" should be used in combination with the - INPUT element, type="file". - -:aspect:`Default:` - application/x-www-form-urlencoded - - -.. _reference-objects-attributes-filters: - -filters -======= - -:aspect:`Property:` - filters - -:aspect:`Data type:` - [array of numbers] - - ->filters - -:aspect:`Description:` - Add filters to the FORM object. - - This accepts multiple filters for one FORM object, but you have to add - these filters one by one. The submitted data for this particular object - will be filtered by the assigned filters in the given order. - - The filtered data will be shown to the visitor when there are errors in - the form or on a confirmation page. Otherwise the filtered data will be - send by mail to the receiver. - - **Example:** - - .. code-block:: typoscript - - filters { - 1 = alphabetic - 1 ( - allowWhiteSpace = 1 - ) - 2 = titlecase - } - - **Submitted data:** john doe3 - - **Filtered:** John Doe - -:aspect:`Default:` - .. code-block:: typoscript - - filters { - 0 = trim - } - - -.. _reference-objects-attributes-headingSize: - -headingSize -=========== - -:aspect:`Property:` - headingSize - -:aspect:`Data type:` - h1, h2, h3, h4, h5 - -:aspect:`Description:` - This attributes allows to wrap the content of a FORM object with a - headline tag. - -:aspect:`Default:` - h1 - - -.. _reference-objects-attributes-id: - -id -== - -:aspect:`Property:` - id - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute assigns an id to an element. - - This id must be unique in a document. - - If an id has been assigned to the object and a value has been entered - for the label, the "for" attribute will inherit the id. - - **Example for FORM object BUTTON:** - - .. code-block:: html - - <label for="click">Push this button</label> - <input type="button" id="click" value="Click me" /> - - -.. _reference-objects-attributes-label: - -label -===== - -:aspect:`Property:` - label - -:aspect:`Data type:` - string/ cObject - -:aspect:`Description:` - The value of the label of a FORM object. - - By default the value of the label is a TEXT cObject, but you can use - other cObjects as well. When no cObject type is used it assumes you want - to use TEXT. In this case you can assign the value directly to the label - property or indirectly to the value property of the label. - - For more information about cObjects, take a look in the document TSREF. - - **Example:** - - .. code-block:: typoscript - - label = TEXT - label { - value = First name - } - - **Example:** - - .. code-block:: typoscript - - label = First name - - **Example:** - - .. code-block:: typoscript - - label.value = First name - - -.. _reference-objects-attributes-lang: - -lang -==== - -:aspect:`Property:` - lang - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute specifies the base language of an element's attribute - values and text content. The default value of this attribute is unknown. - - Briefly, language codes consist of a primary code and a possibly empty - series of subcodes: - - - language-code = primary-code ( "-" subcode )\* - - Here are some sample language codes: - - - *en*: English - - - *en-US*: the U.S. version of English - - - *en-cockney*: the Cockney version of English - - - *i-navajo*: the Navajo language spoken by some Native Americans - - - *x-klingon*: The primary tag "x" indicates an experimental language tag - - -.. _reference-objects-attributes-layout: - -layout -====== - -:aspect:`Property:` - layout - -:aspect:`Data type:` - string - -:aspect:`Description:` - See general information for :ref:`reference-layout`. - - -.. _reference-objects-attributes-legend: - -legend -====== - -:aspect:`Property:` - legend - -:aspect:`Data type:` - string/ cObject - -:aspect:`Description:` - The value of the legend of a FORM object. - - By default the value of the legend is a TEXT cObject, but you can use - other cObjects as well. When no cObject type is used it assumes you want - to use TEXT. In this case you can assign the value directly to the - legend property or indirectly to the value property of the legend. - - For more information about cObjects, take a look in the document TSREF. - - **Example:** - - .. code-block:: typoscript - - legend = TEXT - legend { - value = Personal information - } - - - **Example:** - - .. code-block:: typoscript - - legend = Personal information - - - **Example:** - - .. code-block:: typoscript - - legend.value = Personal information - - -.. _reference-objects-attributes-maxlength: - -maxlength -========= - -:aspect:`Property:` - maxlength - -:aspect:`Data type:` - integer - -:aspect:`Description:` - This attribute specifies the maximum number of characters the user may - enter. This number may exceed the specified size, in which case the user - agent should offer a scrolling mechanism. The default value for this - attribute is an unlimited number. - - -.. _reference-objects-attributes-method: - -method -====== - -:aspect:`Property:` - method - -:aspect:`Data type:` - post/ get - -:aspect:`Description:` - Specifies which HTTP method will be used to submit form data. - - Only form data submitted with the entered or default method will be - processed. - -:aspect:`Default:` - get - - -.. _reference-objects-attributes-multiple: - -multiple -======== - -:aspect:`Property:` - multiple - -:aspect:`Data type:` - boolean/ multiple - -:aspect:`Description:` - If set, this boolean attribute allows multiple selections. - - If not set, the SELECT element only permits single selections. - - **Examples:** - - .. code-block:: typoscript - - multiple = 1 - multiple = 0 - multiple = multiple - - -.. _reference-objects-attributes-name: - -name -==== - -:aspect:`Property:` - name - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute names the element so that submitted data can be - identified by processing the form server side. - - If no name has been given, it will get assigned an internal counter - together with the prefix, like: - - .. code-block:: html - - <input type="button" name="tx_form[21]" value="click" /> - <input type="checkbox" name="tx_form[22]" value="click" /> - - -.. _reference-objects-attributes-postProcessor: - -postProcessor -============= - -:aspect:`Property:` - postProcessor - -:aspect:`Data type:` - [array of numbers] - -:aspect:`Description:` - Add postprocessors to the FORM. - - This accepts multiple postprocessors for one FORM object, but they have - to be added one by one. - - **Example** : - - .. code-block:: typoscript - - postProcessor { - 1 = mail - 1 { - recipientEmail = bar@foo.org - senderEmail = foo@bar.com - } - } - - -.. _reference-objects-attributes-prefix: - -prefix -====== - -:aspect:`Property:` - prefix - -:aspect:`Data type:` - string - -:aspect:`Description:` - The prefix of the values in the name attributes of the FORM objects. - - <input name=" **prefix** [first\_name]" value="" /> - -:aspect:`Default:` - tx\_form - - -.. _reference-objects-attributes-readonly: - -readonly -======== - -:aspect:`Property:` - readonly - -:aspect:`Data type:` - boolean/ readonly - -:aspect:`Description:` - When set for a form control, this boolean attribute prohibits changes to - the control. - - The readonly attribute specifies whether the control may be modified by - the user. - - When set, the readonly attribute has the following effects on an - element: - - - Read-only elements receive focus but cannot be modified by the user. - - - Read-only elements are included in tabbing navigation. - - - Read-only elements may be successful. - - How read-only elements are rendered depends on the user agent. - - **Examples:** - - .. code-block:: html - - readonly = 1 - readonly = 0 - readonly = disabled - - **Note**: The only way to modify dynamically the value of the readonly - attribute is through a script. - - -.. _reference-objects-attributes-rows: - -rows -==== - -:aspect:`Property:` - rows - -:aspect:`Data type:` - integer - -:aspect:`Description:` - This attribute specifies the number of visible text lines. - - Users should be able to enter more lines than this, so user agents - should provide some means to scroll through the contents of the control - when the contents extend beyond the visible area. - -:aspect:`Default:` - 5 - - -.. _reference-objects-attributes-rules: - -rules -===== - -:aspect:`Property:` - rules - -:aspect:`Data type:` - [array of numbers] - -:aspect:`Description:` - Add validation rules to the FORM. - - This accepts multiple validation rules for one FORM object, but the - rules have to be added one by one. It is also possible to add validation - rules for different FORM objects. - - **Example:** - - .. code-block:: typoscript - - rules { - 1 = required - 1 { - element = first_name - } - 2 = required - 2 { - element = last_name - showMessage = 0 - error = TEXT - error { - value = Please enter your last name - } - } - } - - Validation rules are a powerful tool to add validation to the form. - Please take a look at the rules section in this manual. - - -.. _reference-objects-attributes-selected: - -selected -======== - -:aspect:`Property:` - selected - -:aspect:`Data type:` - boolean/ selected - -:aspect:`Description:` - When set, this boolean attribute specifies that a option is pre- - selected. - - **Examples:** - - .. code-block:: typoscript - - selected = 1 - selected = 0 - selected = selected - - -.. _reference-objects-attributes-size: - -size -==== - -:aspect:`Property:` - size - -:aspect:`Data type:` - integer - -:aspect:`Description:` - This attribute tells the user agent the initial width of the control. - The size has to be entered as integer without any measuring unit. - - -.. _reference-objects-attributes-src: - -src -=== - -:aspect:`Property:` - src - -:aspect:`Data type:` - imgResource - -:aspect:`Description:` - This attribute specifies the location of the image to be used to - decorate the graphical submit button. GIFBUILDER objects are not - allowed. - - -.. _reference-objects-attributes-style: - -style -===== - -:aspect:`Property:` - style - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute specifies CSS style information for the current element. - - -.. _reference-objects-attributes-tabindex: - -tabindex -======== - -:aspect:`Property:` - tabindex - -:aspect:`Data type:` - integer - -:aspect:`Description:` - This attribute specifies the position of the current element in the - tabbing order for the current document. This value must be a number - between 0 and 32767. User agents should ignore leading zeros. - - The tabbing order defines the order in which elements will receive focus - when navigated by the user via the keyboard. The tabbing order may - include elements nested within other elements. - - Elements that may receive focus should be navigated by user agents - according to the following rules: - - #. Those elements that support the tabindex attribute and assign a - positive value to it are navigated first. Navigation proceeds from - the element with the lowest tabindex value to the element with the - highest value. Values neither need to be sequential nor must begin - with any particular value. Elements that have identical tabindex - values should be navigated in the order they appear in the character - stream. - - #. Those elements that do not support the tabindex attribute or support - it and assign it a value of "0" are navigated next. These elements - are navigated in the order they appear in the character stream. - - #. Elements that are disabled do not participate in the tabbing order. - - The actual key sequence that causes tabbing navigation or element - activation depends on the configuration of the user agent (e.g., the - "tab" key is used for navigation and the "enter" key is used to activate - a selected element), - - User agents may also define key sequences to navigate the tabbing order - in reverse. When the end (or beginning) of the tabbing order is reached, - user agents may circle back to the beginning (or end). - - -.. _reference-objects-attributes-title: - -title -===== - -:aspect:`Property:` - title - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute offers advisory information about the element for which - it is set. Unlike the TITLE element, which provides information about an - entire document and may only appear once, the title attribute may - annotate any number of elements. Please consult an element's definition - to verify that it supports this attribute. - - Values of the title attribute may be rendered by user agents in a - variety of ways. For instance, visual browsers frequently display the - title as a "tool tip" (a short message that appears when the pointing - device pauses over an object). Audio user agents may speak the title - information in a similar context. - - -.. _reference-objects-attributes-type: - -type -==== - -:aspect:`Property:` - type - -:aspect:`Data type:` - string - -:aspect:`Description:` - Defines the type of form input control to create. - - -.. _reference-objects-attributes-value: - -value -===== - -:aspect:`Property:` - value - -:aspect:`Data type:` - string - -:aspect:`Description:` - This attribute assigns the initial value to the object. - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Optgroup/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Optgroup/Index.rst deleted file mode 100644 index 5a9e74266d96..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Optgroup/Index.rst +++ /dev/null @@ -1,110 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-optgroup: - -======== -OPTGROUP -======== - -The OPTGROUP element allows authors to group choices logically. This is -particularly helpful when the user must choose from a long list of options; -groups of related choices are easier to grasp and remember than a single -long list of options. All OPTGROUP elements must be specified directly -within a SELECT element (i.e., groups may not be nested). - -An OPTGROUP object can only exist between a SELECT object. - - -.. _reference-optgroup-1-2-3-4: - -1, 2, 3, 4 ... -============== - -:aspect:`Property:` - 1, 2, 3, 4 ... - -:aspect:`Data type:` - [array of FORM objects] - -:aspect:`Description:` - OPTION objects, part of the OPTGROUP - - -.. _reference-optgroup-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-optgroup-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-optgroup-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-optgroup-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - -:aspect:`Default:` - optgroup - - -.. _reference-optgroup-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-optgroup-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-optgroup` - specific information. - - -.. _reference-optgroup-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-optgroup-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - -[tsref:(cObject).FORM.FormObject.OPTGROUP] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Option/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Option/Index.rst deleted file mode 100644 index 6db85eedc0f2..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Option/Index.rst +++ /dev/null @@ -1,115 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-option: - -====== -OPTION -====== - -Defines an element inside a select/ drop-down list. - -An OPTION object can only exist "between" a SELECT or OPTGROUP object. - - -.. _reference-option-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-option-data: - -data -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-data`. - - -.. _reference-option-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-option-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-option-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-option-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-option-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-option` - specific information. - - -.. _reference-option-selected: - -selected -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-selected`. - - -.. _reference-option-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-option-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - - -.. _reference-option-value: - -value -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-value`. - -[tsref:(cObject).FORM.FormObject.OPTION] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Password/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Password/Index.rst deleted file mode 100644 index be61de88fc21..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Password/Index.rst +++ /dev/null @@ -1,198 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-password: - -======== -PASSWORD -======== - -Creates a single-line text input control, but the input text is rendered in -such a way as to hide the characters (e.g., a series of asterisks). This -control type is often used for sensitive input such as passwords. Note that -the current value is the text entered by the user, not the text rendered by -the user agent. - -**Note** . Form designers should note that this mechanism affords only light -security protection. Although the password is masked by user agents from -casual observers, it is transmitted to the server in clear text, and may be -read by anyone with low-level access to the network. - - -.. _reference-password-accesskey: - -accesskey -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accesskey`. - - -.. _reference-password-alt: - -alt -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-alt`. - - -.. _reference-password-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-password-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-password-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-password-filters: - -filters -======= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-filters`. - - -.. _reference-password-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-password-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-password-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - tag - - -.. _reference-password-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-password` - specific information. - - -.. _reference-password-maxlength: - -maxlength -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-maxlength`. - - -.. _reference-password-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-password-readonly: - -readonly -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-readonly`. - - -.. _reference-password-size: - -size -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-size`. - - -.. _reference-password-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-password-tabindex: - -tabindex -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-tabindex`. - - -.. _reference-password-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - - -.. _reference-password-type: - -type -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-type`. - -:aspect:`Default:` - password - - -.. _reference-password-value: - -value -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-value`. - -[tsref:(cObject).FORM.FormObject.PASSWORD] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Radio/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Radio/Index.rst deleted file mode 100644 index ce3df5edd327..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Radio/Index.rst +++ /dev/null @@ -1,190 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-radio: - -===== -RADIO -===== - -Creates a radio button. - -Radio buttons are on/ off switches that may be toggled by the user. A switch -is "on" when the control element's checked attribute is set. When a form is -submitted, only "on" radio button controls can become successful. - -Several radio buttons in a form may share the same control name. Thus, for -example, radio buttons allow users to select several values for the same -property. - -Radio buttons are like checkboxes except that when several share the same -control name, they are mutually exclusive: when one is switched "on", all -others with the same name are switched "off". - -Radio buttons are normally grouped in a FIELDSET object. - -**Note from W3C for user agent behaviour**: If no radio button in a set -sharing the same control name is initially "on", user agent behavior for -choosing which control is initially "on" is undefined. - -**Note**: Since existing implementations handle this case differently, the -current specification differs from RFC 1866 ([RFC1866] section 8.1.2.4), -which states: - -At all times, exactly one of the radio buttons in a set is checked. If -none of the elements of a set of radio buttons specifies \`checked', -then the user agent must check the first radio button of the set initially. - -Since user agent behavior differs, authors should ensure that in each set of -radio buttons that one is initially "on". - - -.. _reference-radio-accesskey: - -accesskey -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accesskey`. - - -.. _reference-radio-alt: - -alt -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-alt`. - - -.. _reference-radio-checked: - -checked -======= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-checked`. - - -.. _reference-radio-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-radio-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-radio-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-radio-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-radio-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-radio-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-radio-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-radio` - specific information. - - -.. _reference-radio-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-radio-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-radio-tabindex: - -tabindex -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-tabindex`. - - -.. _reference-radio-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - - -.. _reference-radio-type: - -type -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-type`. - -:aspect:`Default:` - radio - - -.. _reference-radio-value: - -value -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-value`. - -[tsref:(cObject).FORM.FormObject.RADIO] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Reset/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Reset/Index.rst deleted file mode 100644 index 6854cdd9772d..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Reset/Index.rst +++ /dev/null @@ -1,154 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-reset: - -===== -RESET -===== - -Creates a reset button. - -When activated, a reset button resets all controls to their initial values. - - -.. _reference-reset-accesskey: - -accesskey -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accesskey`. - - -.. _reference-reset-alt: - -alt -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-alt`. - - -.. _reference-reset-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-reset-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-reset-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-reset-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-reset-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-reset-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-reset-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-reset` - specific information. - - -.. _reference-reset-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-reset-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-reset-tabindex: - -tabindex -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-tabindex`. - - -.. _reference-reset-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - - -.. _reference-reset-type: - -type -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-type`. - -:aspect:`Default:` - reset - - -.. _reference-reset-value: - -value -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-value`. - -[tsref:(cObject).FORM.FormObject.RESET] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Select/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Select/Index.rst deleted file mode 100644 index 314b2119397d..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Select/Index.rst +++ /dev/null @@ -1,165 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-select: - -====== -SELECT -====== - -The SELECT object creates a menu. Each choice offered by the menu is -represented by an OPTION object. A SELECT object must contain at least one -OPTION object - -**Pre-selected options** - -Zero or more choices may be pre-selected for the user. User agents should -determine which choices are pre-selected as follows: - -- If no OPTION object has the selected attribute set, user agent behavior - for choosing which option is initially selected is undefined. - **Note**: Since existing implementations handle this case differently, the - current specification differs from RFC 1866 ([RFC1866] section 8.1.3), - which states: The initial state has the first option selected, unless a - SELECTED attribute is present on any of the <OPTION> elements. Since user - agent behavior differs, one should ensure that each menu includes a - default pre-selected OPTION. - -- If one OPTION object has the selected attribute set, it should be pre- - selected. - -- If the SELECT object has the multiple attribute set and more than one - OPTION object has the selected attribute set, they should all be pre- - selected. - -- It is considered an error if more than one OPTION object has the selected - attribute set and the SELECT object does not have the multiple attribute - set. User agents may vary in how they handle this error, but should not - pre-select more than one choice. - - -.. _reference-select-1-2-3-4: - -1, 2, 3, 4 ... -============== - -:aspect:`Property:` - 1, 2, 3, 4 ... - -:aspect:`Data type:` - [array of FORM objects] - -:aspect:`Description:` - OPTION and/ or OPTGROUP objects, part of the SELECT. - - -.. _reference-select-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-select-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-select-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-select-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-select-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-select-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-select` - specific information. - - -.. _reference-select-multiple: - -multiple -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-multiple`. - - -.. _reference-select-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-select-size: - -size -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-size`. - - -.. _reference-select-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-select-tabindex: - -tabindex -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-tabindex`. - - -.. _reference-select-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - -[tsref:(cObject).FORM.FormObject.SELECT] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Submit/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Submit/Index.rst deleted file mode 100644 index e7dc89d511cd..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Submit/Index.rst +++ /dev/null @@ -1,152 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-submit: - -====== -SUBMIT -====== - -Creates a submit button. - -When activated, a submit button submits a form. A form may contain more than -one submit button. - - -.. _reference-submit-accesskey: - -accesskey -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accesskey`. - - -.. _reference-submit-alt: - -alt -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-alt`. - - -.. _reference-submit-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-submit-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-submit-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-submit-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-submit-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-submit-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-submit-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-submit` - specific information. - - -.. _reference-submit-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-submit-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-submit-tabindex: - -tabindex -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-tabindex`. - - -.. _reference-submit-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - - -.. _reference-submit-type: - -type -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-type`. - - -.. _reference-submit-value: - -value -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-value`. - -[tsref:(cObject).FORM.FormObject.SUBMIT] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Textarea/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Textarea/Index.rst deleted file mode 100644 index 2c731ec92e4d..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Textarea/Index.rst +++ /dev/null @@ -1,169 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-textarea: - -======== -TEXTAREA -======== - -The TEXTAREA object creates a multi-line text input control. User agents -should use the contents of this object as the initial value of the control -and should render this text initially. - - -.. _reference-textarea-accesskey: - -accesskey -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accesskey`. - - -.. _reference-textarea-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-textarea-cols: - -cols -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-cols`. - - -.. _reference-textarea-data: - -data -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-data`. - - -.. _reference-textarea-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-textarea-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-textarea-filters: - -filters -======= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-filters`. - - -.. _reference-textarea-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-textarea-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-textarea-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-textarea-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-textarea` - specific information. - - -.. _reference-textarea-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-textarea-readonly: - -readonly -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-readonly`. - - -.. _reference-textarea-rows: - -rows -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-rows`. - - -.. _reference-textarea-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-textarea-tabindex: - -tabindex -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-tabindex`. - - -.. _reference-textarea-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - -[tsref:(cObject).FORM.FormObject.TEXTAREA] - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Textblock/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Textblock/Index.rst deleted file mode 100644 index e3e219c4459f..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Textblock/Index.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-textblock: - -========= -TEXTBLOCK -========= - -Creates a block of text. This element can be used for a visual separation or -transportation of content. It is neither displayed on the confirmation page -nor in the email. - - -.. _reference-textblock-content: - -content -======= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-content`. - diff --git a/typo3/sysext/form/Documentation/Configuration/Objects/Textline/Index.rst b/typo3/sysext/form/Documentation/Configuration/Objects/Textline/Index.rst deleted file mode 100644 index 905f9a850437..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Objects/Textline/Index.rst +++ /dev/null @@ -1,185 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-textline: - -======== -TEXTLINE -======== - -Creates a single-line text input control. - - -.. _reference-textline-accesskey: - -accesskey -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-accesskey`. - - -.. _reference-textline-alt: - -alt -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-alt`. - - -.. _reference-textline-class: - -class -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-class`. - - -.. _reference-textline-dir: - -dir -=== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-dir`. - - -.. _reference-textline-disabled: - -disabled -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-disabled`. - - -.. _reference-textline-filters: - -filters -======= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-filters`. - - -.. _reference-textline-id: - -id -== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-id`. - - -.. _reference-textline-label: - -label -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-label`. - - -.. _reference-textline-lang: - -lang -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-lang`. - - -.. _reference-textline-layout: - -layout -====== - -:aspect:`Description:` - See general information for :ref:`reference-layout` and the :ref:`reference-layout-textline` - specific information. - - -.. _reference-textline-maxlength: - -maxlength -========= - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-maxlength`. - - -.. _reference-textline-name: - -name -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-name`. - - -.. _reference-textline-readonly: - -readonly -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-readonly`. - - -.. _reference-textline-size: - -size -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-size`. - - -.. _reference-textline-style: - -style -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-style`. - - -.. _reference-textline-tabindex: - -tabindex -======== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-tabindex` - - -.. _reference-textline-title: - -title -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-title`. - - -.. _reference-textline-type: - -type -==== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-type`. - - -.. _reference-textline-value: - -value -===== - -:aspect:`Description:` - See general information for :ref:`reference-objects-attributes-value`. - -[tsref:(cObject).FORM.FormObject.TEXTLINE] - diff --git a/typo3/sysext/form/Documentation/Configuration/Postprocessors/Index.rst b/typo3/sysext/form/Documentation/Configuration/Postprocessors/Index.rst deleted file mode 100644 index 10ecace040e3..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Postprocessors/Index.rst +++ /dev/null @@ -1,61 +0,0 @@ -.. include:: ../../Includes.txt - - -.. _reference-postprocessors: - -============== -postProcessors -============== - -Add postProcessors to the FORM. - -postProcessors define how TYPO3 processes submitted forms after the form is -rendered according to filters and rules. - -Multiple postProcessors are accepted for one FORM object, but you have to -add these postProcessors one by one. - -Currently there are two postProcessors: - -.. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: - - Mail/Index.rst - Redirect/Index.rst - -The processing will be done in the order of the postProcessors. - -Custom postProcessors -===================== - -It is also possible to configure a custom class as a postProcessor. Just use -the class name as the postProcessor name. -The postProcessor class should implement `TYPO3\CMS\Form\PostProcess\PostProcessorInterface` - -The custom postProcessor is not available within the form wizard. Currently, -there is no possibility to extend the wizard. - -**Example:** - -.. code-block:: typoscript - - postProcessor { - 1 = mail - 1 { - recipientEmail = bar@foo.org - senderEmail = foo@bar.com - subject = Baz - } - - 2 = redirect - 2 { - destination = 5 - } - - 3 = Vendor\ExtensionName\Folder\ClassName - 3 { - } - } - diff --git a/typo3/sysext/form/Documentation/Configuration/Postprocessors/Mail/Index.rst b/typo3/sysext/form/Documentation/Configuration/Postprocessors/Mail/Index.rst deleted file mode 100644 index 2ecf92029cfd..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Postprocessors/Mail/Index.rst +++ /dev/null @@ -1,321 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-postprocessors-mail: - -==== -mail -==== - -The mail postProcessor sends submitted data by mail. - -.. _reference-postProcessor-mail-mail: - -Mail -==== - -Configuration options for the mail to deliver. - - -.. _reference-postprocessors-mail-ccemail: - -ccEmail -------- - -:aspect:`Property:` - ccEmail - -:aspect:`Data type:` - string - -:aspect:`Description:` - Email address the submitted data is sent to as a carbon copy. - - -.. _reference-postprocessors-mail-organization: - -organization ------------- - -:aspect:`Property:` - organization - -:aspect:`Data type:` - string - -:aspect:`Description:` - Organization mail header. - - -.. _reference-postprocessors-mail-priority: - -priority --------- - -:aspect:`Property:` - priority - -:aspect:`Data type:` - integer - -:aspect:`Description:` - Priority of the email. Integer value between 1 and 5. If the priority is - configured, but too high, it will be set to 5, which means very low - priority. - -:aspect:`Default:` - 3 - - -.. _reference-postprocessors-mail-recipientemail: - -recipientEmail --------------- - -:aspect:`Property:` - recipientEmail - -:aspect:`Data type:` - string - -:aspect:`Description:` - Email address the submitted data is sent to. - - -.. _reference-postprocessors-mail-senderemail: - -senderEmail ------------ - -:aspect:`Property:` - senderEmail - -:aspect:`Data type:` - string - -:aspect:`Description:` - Email address which is shown as sender of the email (from header). - -:aspect:`Default:` - TYPO3\_CONF\_VARS['MAIL']['defaultMailFromAddress'] - - -.. _reference-postprocessors-mail-senderemailfield: - -senderEmailField ----------------- - -:aspect:`Property:` - senderEmailField - -:aspect:`Data type:` - string - -:aspect:`Description:` - Name of the form field which holds the sender's email address (from - header). - - Normally, you can find the (filtered) name in the HTML output between - the square brackets like tx\_form[name] where name is the name of the - object. - - Only used if senderEmail is not set. - - -.. _reference-postprocessors-mail-sendername: - -senderName ----------- - -:aspect:`Property:` - senderName - -:aspect:`Data type:` - string - -:aspect:`Description:` - Name which is shown as sender of the email (from header). - -:aspect:`Default:` - TYPO3\_CONF\_VARS['MAIL']['defaultMailFromName'] - - -.. _reference-postprocessors-mail-sendernamefield: - -senderNameField ---------------- - -:aspect:`Property:` - senderNameField - -:aspect:`Data type:` - string - -:aspect:`Description:` - Name of the form field which holds the sender's name (from header). - - Normally you can find the (filtered) name in the HTML output between the - square brackets like tx\_form[name] where name is the name of the - object. - - Only used if senderName is not set. - - -.. _reference-form-subject: - -subject -------- - -:aspect:`Property:` - subject - -:aspect:`Data type:` - string - -:aspect:`Description:` - Subject of the email sent by the form. - -:aspect:`Default:` - Formmail on 'Your\_HOST' - - -.. _reference-postprocessors-mail-subjectfield: - -subjectField ------------- - -:aspect:`Property:` - subjectField - -:aspect:`Data type:` - string - -:aspect:`Description:` - Name of the form field which holds the subject. - - Normally you can find the (filtered) name in the HTML output between the - square brackets like tx\_form[name] where name is the name of the - object. - - Only used if subject is not set. - -[tsref:(cObject).FORM->postProcessor.mail] - - -.. _reference-postprocessors-mail-htmlMailTemplatePath: - -htmlMailTemplatePath --------------------- - -:aspect:`Property:` - htmlMailTemplatePath - -:aspect:`Data type:` - string - -:aspect:`Description:` - Name of the template to use for HTML-Content. - - Default is `Html`. Useful to use multiple Mail Postprocessors with different templates. - - -.. _reference-postprocessors-mail-plaintextMailTemplatePath: - -plaintextMailTemplatePath -------------------------- - -:aspect:`Property:` - plaintextMailTemplatePath - -:aspect:`Data type:` - string - -:aspect:`Description:` - Name of the template to use for Plaintext-Content. - - Default is `Plain`. Useful to use multiple Mail Postprocessors with different templates. - -.. _reference-postProcessor-mail-messages: - -Messages -======== - -.. _reference-postprocessors-mail-messages-error: - -messages.error --------------- - -:aspect:`Property:` - messages.error - -:aspect:`Data type:` - string/ cObject - -:aspect:`Description:` - Overriding the default text of the error message, describing the error. - - When no cObject type is set, the message is a simple string. The value - can directly be assigned to the messages.error property. If one needs - the functionality of cObjects, just define the message appropriately. - Any cObject is allowed. - - For more information about cObjects, take a look in the document TSREF. - - **Example:** - - .. code-block:: typoscript - - messages.error = TEXT - messages.error { - data = LLL:EXT:theme/Resources/Private/Language/Form/locallang.xlf:messagesError - } - - **Example:** - - .. code-block:: typoscript - - messages.error = Error while submitting form - -:aspect:`Description:` - *Local language:*"There was an error when sending the form by mail" - - -.. _reference-postprocessors-mail-messages-success: - -messages.success ----------------- - -:aspect:`Property:` - messages.success - -:aspect:`Data type:` - string/ cObject - -:aspect:`Description:` - Overriding the default text of the confirmation message. - - When no cObject type is set, the message is a simple string. The value - can directly be assigned to the messages.success property. If one needs - the functionality of cObjects, just define the message appropriately. - Any cObject is allowed. - - For more information about cObjects, take a look in the document TSREF. - - **Example:** - - .. code-block:: typoscript - - messages.success = TEXT - messages.success { - data = LLL:EXT:theme/Resources/Private/Language/Form/locallang.xlf:messagesSuccess - } - - **Example:** - - .. code-block:: typoscript - - messages.success = Thanks for submitting - -:aspect:`Default:` - *Local language:*"The form has been sent successfully by mail" - diff --git a/typo3/sysext/form/Documentation/Configuration/Postprocessors/Redirect/Index.rst b/typo3/sysext/form/Documentation/Configuration/Postprocessors/Redirect/Index.rst deleted file mode 100644 index 6fabe9d06b5d..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Postprocessors/Redirect/Index.rst +++ /dev/null @@ -1,26 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-postprocessors-redirect: - -======== -redirect -======== - -The redirect postProcessor redirects the user to a defined destination. - -.. _reference-postprocessors-redirect-destination: - -destination -=========== - -:aspect:`Property:` - destination - -:aspect:`Data type:` - string - -:aspect:`Description:` - One can supply a uid of a page (e.g. 5) or a url (e.g. www.typo3.org). - The user will be redirected to this destination. - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Alphabetic/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Alphabetic/Index.rst deleted file mode 100644 index 2f05287cfff0..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Alphabetic/Index.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-alphabetic: - -========== -alphabetic -========== - -Checks if the submitted value only has the characters a-z or A-Z. - - -.. _reference-rules-alphabetic-allowwhitespace: - -allowWhiteSpace -=============== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-allowwhitespace`. - - -.. _reference-rules-alphabetic-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-alphabetic-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"The value contains not only alphabetic characters" - - -.. _reference-rules-alphabetic-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - - For this specific rule the default message consists of two parts, the - second one will only be added when **allowWhiteSpace** is set. This - functionality is not possible when defining an own message as shown - below. - -:aspect:`Default:` - *local language:*"Use alphabetic characters(, whitespace allowed)" - - -.. _reference-rules-alphabetic-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.alphabetic] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Alphanumeric/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Alphanumeric/Index.rst deleted file mode 100644 index e3b815117023..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Alphanumeric/Index.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-alphanumeric: - -============ -alphanumeric -============ - -Checks if the submitted value only has the characters a-z, A-Z or 0-9. - - -.. _reference-rules-alphanumeric-allowwhitespace: - -allowWhiteSpace -=============== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-allowwhitespace`. - - -.. _reference-rules-alphanumeric-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-alphanumeric-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"The value contains not only alphanumeric characters" - - -.. _reference-rules-alphanumeric-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - - For this specific rule the default message consists of two parts, the - second one will only be added when **allowWhiteSpace** is set. This - functionality is not possible when defining an own message as shown - below. - -:aspect:`Default:` - *local language:*"Use alphanumeric characters(, whitespace allowed)" - - -.. _reference-rules-alphanumeric-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.alphanumeric] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Between/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Between/Index.rst deleted file mode 100644 index 4a848cf4c17a..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Between/Index.rst +++ /dev/null @@ -1,122 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-between: - -======= -between -======= - -Checks if the submitted value is between the given minimum and maximum -value. By default, minimum and maximum are excluded, but can be included in -the validation. - - -.. _reference-rules-between-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-between-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - - For this specific rule the default error message consists of two parts, - the second one will only be added when **inclusive** is set. This - functionality is not possible when defining an own message as shown - below. - The markers %minimum and %maximum will be replaced with the values set - by TypoScript. - -:aspect:`Default:` - *local language:*"The value is not between %minimum and %maximum(, - inclusively)" - - -.. _reference-rules-between-inclusive: - -inclusive -========= - -:aspect:`Property:` - inclusive - -:aspect:`Data type:` - boolean - -:aspect:`Description:` - If inclusive = 1, the minimum and maximum value are included in the - comparison. - -:aspect:`Default:` - 0 - - -.. _reference-rules-between-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - - For this specific rule the default message consists of two parts, the - second one will only be added when **inclusive** is set. This - functionality is not possible when defining an own message as shown - below. - The markers %minimum and %maximum will be replaced with the values set - by TypoScript. - -:aspect:`Default:` - *local language:*"The value must be between %minimum and %maximum(, - inclusively)" - - -.. _reference-rules-between-maximum: - -maximum -======= - -:aspect:`Property:` - maximum - -:aspect:`Data type:` - integer - -:aspect:`Description:` - The maximum value of the comparison. - - -.. _reference-rules-between-minimum: - -minimum -======= - -:aspect:`Property:` - minimum - -:aspect:`Data type:` - integer - -:aspect:`Description:` - The minimum value of the comparison. - - -.. _reference-rules-between-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.between] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Date/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Date/Index.rst deleted file mode 100644 index 4781cc3a4287..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Date/Index.rst +++ /dev/null @@ -1,85 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-date: - -==== -date -==== - -Checks if the submitted value is a valid date, and the format is equal to -the one set in TypoScript. - -The format configuration is like the PHP strftime() conversion specifiers. -The message shown to the visitor supports the format as well, but will be -shown to the visitor in a human readable way. -%e-%m-%Y becomes d-mm-yyyy in English. - - -.. _reference-rules-date-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-date-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"The value does not appear to be a valid date" - - -.. _reference-rules-date-format: - -format -====== - -:aspect:`Property:` - format - -:aspect:`Data type:` - strftime-conf - -:aspect:`Description:` - The format of the submitted data. - - See the PHP-manual (strftime) for the codes, or datatype "strftime-conf" - in the TYPO3 document TSref. - -:aspect:`Default:` - %e-%m-%Y - - -.. _reference-rules-date-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - - The %format marker will be replaced with a human readable format. - %e-%m-%Y becomes d-mm-yyyy in English. - -:aspect:`Default:` - *local language:*"(%format)" - - -.. _reference-rules-date-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.date] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Digit/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Digit/Index.rst deleted file mode 100644 index 3aedee4d27f9..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Digit/Index.rst +++ /dev/null @@ -1,55 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-digit: - -===== -digit -===== - -Checks if the submitted value only has the characters 0-9. - - -.. _reference-rules-digit-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-digit-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"The value contains not only digit characters" - - -.. _reference-rules-digit-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - -:aspect:`Default:` - *local language:*"Use digit characters" - - -.. _reference-rules-digit-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.digit] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Email/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Email/Index.rst deleted file mode 100644 index fdd51d558344..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Email/Index.rst +++ /dev/null @@ -1,60 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-email: - -===== -email -===== - -Checks if the submitted value is a valid email address. - -Validates an RFC 2822 email address, except does not allow most punctuation -and non-ascii alphanumeric characters. Also does not take length -requirements into account. Allows domain name and IP addresses, and ensures -that the IP address entered is valid. - - -.. _reference-rules-email-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-email-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"This is not a valid email address" - - -.. _reference-rules-email-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - -:aspect:`Default:` - *local language:*"(john.doe@domain.com)" - - -.. _reference-rules-email-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.email] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Equals/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Equals/Index.rst deleted file mode 100644 index ec82581fefa2..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Equals/Index.rst +++ /dev/null @@ -1,80 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-equals: - -====== -equals -====== - -Compares the submitted data of two FORM objects. If they are not equal, the -rule does not validate. - -The rule and error messages will be put in the label of the object the rule -is attached with by the property "element". - - -.. _reference-rules-equals-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-equals-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - - The %field marker will be replaces with the property "field". - -:aspect:`Default:` - *local language:*"The value does not equal the value in field '%field'" - - -.. _reference-rules-equals-field: - -field -===== - -:aspect:`Property:` - field - -:aspect:`Data type:` - string - -:aspect:`Description:` - The name of the object to compare with. - - See explanation of "element" property. - - -.. _reference-rules-equals-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - - The %field marker will be replaces with the property "field". - -:aspect:`Default:` - *local language:*"This field must be equal to '%field'" - - -.. _reference-rules-equals-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.equals] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Float/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Float/Index.rst deleted file mode 100644 index 97f02d75c6dc..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Float/Index.rst +++ /dev/null @@ -1,69 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-float: - -===== -float -===== - -Checks if the submitted value is a floating point number (aka floats, -doubles or real numbers). - -Float depends on your config.locale\_all setting. For German -(config.locale\_all = de\_DE) one will get the following values (partly) -with the PHP function localeconv(): - -- 'decimal\_point' => string '.' Decimal point character -- 'thousands\_sep' => string '' Thousands separator -- 'mon\_decimal\_point' => string ',' Monetary decimal point character -- 'mon\_thousands\_sep' => string '.' Monetary thousands separator - -First both thousands separators are deleted from the float, then the decimal -points are replaced by a dot to get a proper float which PHP can handle -properly. - - -.. _reference-rules-float-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-float-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"The value does not appear to be a float" - - -.. _reference-rules-float-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - -:aspect:`Default:` - *local language:*"Enter a float" - - -.. _reference-rules-float-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.float] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Greaterthan/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Greaterthan/Index.rst deleted file mode 100644 index e2d7574e3e97..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Greaterthan/Index.rst +++ /dev/null @@ -1,74 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-greaterthan: - -=========== -greaterthan -=========== - -Checks if the submitted value is greater than the integer set in TypoScript. - - -.. _reference-rules-greaterthan-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-greaterthan-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - - The marker %minimum will be replaced with the value set in TypoScript. - -:aspect:`Default:` - *local language:*"The value does not appear to be greater than %minimum" - - -.. _reference-rules-greaterthan-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - - The marker %minimum will be replaced with the value set by TypoScript. - -:aspect:`Default:` - *local language:*"The value must be greater than %minimum" - - -.. _reference-rules-greaterthan-minimum: - -minimum -======= - -:aspect:`Property:` - minimum - -:aspect:`Data type:` - integer - -:aspect:`Description:` - The submitted value must be greater than the minimum value. - - -.. _reference-rules-greaterthan-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.greaterthan] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Inarray/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Inarray/Index.rst deleted file mode 100644 index 8aa5b07f4756..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Inarray/Index.rst +++ /dev/null @@ -1,99 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-inarray: - -======= -inarray -======= - -Compares the submitted value with the values in the array set in TypoScript. - - -.. _reference-rules-inarray-array: - -array -===== - -:aspect:`Property:` - array - -:aspect:`Data type:` - [array of numbers] - -:aspect:`Description:` - The array containing the values which will be compared with the incoming - data. - - **Example:** - - .. code-block:: html - - array { - 1 = TYPO3 4.5 LTS - 2 = TYPO3 6.2 LTS - 3 = TYPO3 7 LTS - } - - -.. _reference-rules-inarray-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-inarray-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"The value does not appear to be valid" - - -.. _reference-rules-inarray-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - -:aspect:`Default:` - *local language:*"Only a few values are possible" - - -.. _reference-rules-inarray-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - - -.. _reference-rules-inarray-strict: - -strict -====== - -:aspect:`Property:` - strict - -:aspect:`Data type:` - boolean - -:aspect:`Description:` - The types of the needle in the haystack are also checked if strict = 1. - -:aspect:`Default:` - 0 - -[tsref:(cObject).FORM->rules.inarray] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Index.rst deleted file mode 100644 index 37a49aee48f6..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Index.rst +++ /dev/null @@ -1,78 +0,0 @@ -.. include:: ../../Includes.txt - - -.. _reference-rules: - -================ -Validation Rules -================ - -Validation rules are a powerful tool to add validation to the form. The -rules function will always be used at the beginning of the form and belongs -to the FORM object. - -It is possible to have multiple validation rules for one FORM object, but -the rules have to be added one by one. - -**Example** - -.. code-block:: typoscript - - rules { - 1 = required - 1 ( - element = first_name - ) - 2 = required - 2 { - element = last_name - showMessage = 0 - error = TEXT - error { - value = Please enter your last name - } - } - 3 = required - 3 { - element = email_address - } - 4 = email - 4 { - element = email_address - } - } - - -When a rule is defined, it will automatically add a message to the object -the rule is connected with. This message will be shown in the local language -and will tell the user the input needs to be according to this rule. The -message can be hidden or overruled with a user defined string/ cObject. - -The validation will be done by the order of the rules. The validation can be -stopped when a certain rule is not valid. By default all validation rules -will be processed. - -.. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: - - ValidationAttributes/Index.rst - Alphabetic/Index.rst - Alphanumeric/Index.rst - Between/Index.rst - Date/Index.rst - Digit/Index.rst - Email/Index.rst - Equals/Index.rst - Float/Index.rst - Greaterthan/Index.rst - Inarray/Index.rst - Integer/Index.rst - Ip/Index.rst - Length/Index.rst - Lessthan/Index.rst - Regexp/Index.rst - Required/Index.rst - Uri/Index.rst - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Integer/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Integer/Index.rst deleted file mode 100644 index 5d94c1e241b7..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Integer/Index.rst +++ /dev/null @@ -1,55 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-integer: - -======= -integer -======= - -Checks if the submitted value is an integer. - - -.. _reference-rules-integer-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-integer-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"The value does not appear to be an integer" - - -.. _reference-rules-integer-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - -:aspect:`Default:` - *local language:*"Use a integer" - - -.. _reference-rules-integer-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.integer] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Ip/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Ip/Index.rst deleted file mode 100644 index 0d29fac9eef2..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Ip/Index.rst +++ /dev/null @@ -1,55 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-ip: - -== -ip -== - -Checks if the submitted value is an IP address. - - -.. _reference-rules-ip-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-ip-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"The value does not appear to be a valid IP address" - - -.. _reference-rules-ip-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - -:aspect:`Default:` - *local language:*"(123.123.123.123)" - - -.. _reference-rules-ip-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.ip] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Length/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Length/Index.rst deleted file mode 100644 index 264e4b9864d9..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Length/Index.rst +++ /dev/null @@ -1,103 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-length: - -====== -length -====== - -Checks if the submitted value is of a certain length. A minimum length can -be used or a minimum and a maximum length. - - -.. _reference-rules-length-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-length-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - - For this specific rule the default error message consists of two parts, - the second one will only be added when **maximum** is set. This - functionality is not possible when defining an own message as shown - below. - The markers %minimum and %maximum will be replaced with the values set - by TypoScript. - -:aspect:`Default:` - *local language:*"The value is less than %minimum characters long - (, or longer than %maximum)" - - -.. _reference-rules-length-maximum: - -maximum -======= - -:aspect:`Property:` - maximum - -:aspect:`Data type:` - integer - -:aspect:`Description:` - The maximum length of the submitted value. Maximum can only be used in - combination with minimum. - - -.. _reference-rules-length-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - - For this specific rule the default message consists of two parts, the - second one will only be added when **maximum** is set. This - functionality is not possible when defining an own message as shown - below. - The markers %minimum and %maximum will be replaced with the values set - by TypoScript. - -:aspect:`Default:` - *local language:*"The length of the value must have a minimum of - %minimum characters(, and a maximum of %maximum)" - - -.. _reference-rules-length-minimum: - -minimum -======= - -:aspect:`Property:` - minimum - -:aspect:`Data type:` - integer - -:aspect:`Description:` - The minimum length of the submitted value. - - -.. _reference-rules-length-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.length] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Lessthan/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Lessthan/Index.rst deleted file mode 100644 index 4c2a479e71fa..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Lessthan/Index.rst +++ /dev/null @@ -1,74 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-lessthan: - -======== -lessthan -======== - -Checks if the submitted value is less than the integer set in TypoScript. - - -.. _reference-rules-lessthan-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-lessthan-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - - The marker %maximum will be replaced with the value set in TypoScript. - -:aspect:`Default:` - *local language:*"The value does not appear to be less than %maximum" - - -.. _reference-rules-lessthan-maximum: - -maximum -======= - -:aspect:`Property:` - maximum - -:aspect:`Data type:` - integer - -:aspect:`Description:` - The submitted value must be less than the maximum value. - - -.. _reference-rules-lessthan-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - - The marker %maximum will be replaced with the value set in TypoScript. - -:aspect:`Default:` - *local language:*"The value must be less than %maximum" - - -.. _reference-rules-lessthan-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.lessthan] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Regexp/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Regexp/Index.rst deleted file mode 100644 index cc0a8cbca7fd..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Regexp/Index.rst +++ /dev/null @@ -1,73 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-regexp: - -====== -regexp -====== - -Checks if the submitted value matches your own regular expression, using PHP -function preg\_match(). - - -.. _reference-rules-regexp-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-regexp-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"The value does not match against pattern" - - -.. _reference-rules-regexp-expression: - -expression -========== - -:aspect:`Property:` - expression - -:aspect:`Data type:` - string - -:aspect:`Description:` - The submitted value needs to match the expression, given in your - pattern. - - -.. _reference-rules-regexp-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - -:aspect:`Default:` - *local language:*"Use the right pattern" - - - -.. _reference-rules-regexp-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.regexp] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Required/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Required/Index.rst deleted file mode 100644 index 103e3935b348..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Required/Index.rst +++ /dev/null @@ -1,57 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-required: - -======== -required -======== - -Checks if the submitted value exists and is not empty. - -0 or "0" are allowed and the rule will return true. - - -.. _reference-rules-required-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-required-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"This field is required" - - -.. _reference-rules-required-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - -:aspect:`Default:` - *local language:*"Required" - - -.. _reference-rules-required-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.required] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/Uri/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/Uri/Index.rst deleted file mode 100644 index 43d0cc37b1e6..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/Uri/Index.rst +++ /dev/null @@ -1,58 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-rules-uri: - -=== -uri -=== - -This validation rule checks on a URI, which can include all of the -following: - -- scheme://usern:passw@domain:port/path/file.ext?querystring#fragment - - -.. _reference-rules-uri-element: - -element -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-element`. - - -.. _reference-rules-uri-error: - -error -===== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-error`. - -:aspect:`Default:` - *local language:*"The value does not appear to be a hostname" - - -.. _reference-rules-uri-message: - -message -======= - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-message`. - -:aspect:`Default:` - *local language:*"The value must be a hostname" - - -.. _reference-rules-uri-showmessage: - -showMessage -=========== - -:aspect:`Description:` - See general information for :ref:`reference-validation-attributes-showmessage`. - -[tsref:(cObject).FORM->rules.uri] - diff --git a/typo3/sysext/form/Documentation/Configuration/Rules/ValidationAttributes/Index.rst b/typo3/sysext/form/Documentation/Configuration/Rules/ValidationAttributes/Index.rst deleted file mode 100644 index 93e4347018c8..000000000000 --- a/typo3/sysext/form/Documentation/Configuration/Rules/ValidationAttributes/Index.rst +++ /dev/null @@ -1,148 +0,0 @@ -.. include:: ../../../Includes.txt - - -.. _reference-validation-attributes: - -===================== -Validation Attributes -===================== - -.. contents:: - :local: - :depth: 1 - - -.. _reference-validation-attributes-allowwhitespace: - -allowWhiteSpace -=============== - -:aspect:`Property:` - allowWhiteSpace - -:aspect:`Data type:` - boolean - -:aspect:`Description:` - If allowWhiteSpace = 1, whitespace is allowed in front of, after or - between the characters. - -:aspect:`Default:` - 0 - - -.. _reference-validation-attributes-element: - -element -======= - -:aspect:`Property:` - element - -:aspect:`Data type:` - string - -:aspect:`Description:` - Name of the object. Normally the "filtered" name can be found in the - HTML output between the square brackets like tx\_form[name] where "name" - is the name of the object. - - -.. _reference-validation-attributes-error: - -error -===== - -:aspect:`Property:` - error - -:aspect:`Data type:` - string/ cObject - -:aspect:`Description:` - Overriding the default text of the error message, describing the error. - - When no cObject type is set, the message is a simple string. The value - can directly be assigned to the message property. If one needs the - functionality of cObjects, just define the message appropriately. Any - cObject is allowed. - - For more information about cObjects, take a look in the document TSREF. - - **Example:** - - .. code-block:: typoscript - - error = TEXT - error { - data = LLL:EXT:theme/Resources/Private/Language/Form/locallang.xlf:alphabeticError - } - - **Example:** - - .. code-block:: typoscript - - error = The value contains not only alphabetic characters - -:aspect:`Default:` - Depends on the rule. Check over there. - - -.. _reference-validation-attributes-message: - -message -======= - -:aspect:`Property:` - message - -:aspect:`Data type:` - string/ cObject - -:aspect:`Description:` - Overriding the default text of the message, describing the rule. - - When no cObject type is set, the message is a simple string. The value - can directly be assigned to the message property. If one needs the - functionality of cObjects, just define the message appropriately. Any - cObject is allowed. - - For more information about cObjects, take a look in the document TSREF. - - **Example:** - - .. code-block:: typoscript - - message = TEXT - message { - data = LLL:EXT:theme/Resources/Private/Language/Form/locallang.xlf:betweenMessage - } - - **Example:** - - .. code-block:: typoscript - - message = The value must be between %minimum and %maximum - -:aspect:`Default:` - Depends on the rule. Check over there. - - -.. _reference-validation-attributes-showmessage: - -showMessage -=========== - -:aspect:`Property:` - showMessage - -:aspect:`Data type:` - boolean - -:aspect:`Description:` - If showMessage = 0, a message describing the rule will not be added to - the label of the object. - -:aspect:`Default:` - 1 - diff --git a/typo3/sysext/form/Documentation/Images/FormCreationWizard.png b/typo3/sysext/form/Documentation/Images/FormCreationWizard.png deleted file mode 100644 index 2256bbc51c0a4019f45db0757cae217d64fbd88c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43622 zcmdqJbySq?+b(PoQi9ST2q@AB()rL0(jz$nN{e)Zf*_560*Zu$APn6t(h^DyAt5P9 zBc0zl`g`B??*0DuUf<e(?eAkPpNFM0_uTh&opGGUaa<F6PhEi!j~egXxpRa{in3bg z&Ry&{ckV(L?gjXp+yOC-bLZY7m1L!KJjPZg+>J<wX9PBGv5^yFc{_|;CA8sdE6_A+ zzG@h7tJoZejAq1JpF`6;^45r%lIml#NKQfOjH@fj$WTJrB8DyR)F+eO$=&4Gi4WZp zejeeZPK=J;we0Qg^?W2Gx=cz&_V5NH{G^vTniVl)ep433w#EFPk?;=U8VUSZf08YV zijHn{n*WlgU)YauL8I<>y=MQpvci!{dF`6>&)#GTZj;6OnI*i)dniQlc(wDgF$r8C zy{yDWuWafwA#>nPwe!y~a?ZEAr2BK7ao;N)^mBETBzVNc>U2J86%-U$bBOsKpB}BR z*YBB(b%jPoMn**V9IaOFm@xd07b@4Y?zhmSCQl(kjgiX2&u)iFiJ1t+V`P!K^@;s^ z7R;SrA63z_D5~HjKx1Bkw;M}Fg|f#F<cEie30S*d?00gguCDVntAw6*o6lR($b z*juR*zBUEVYh0Hfj}$TJ7Z|-ARET39sQ-JxB^p%Y+3C^!(nr%x+e-r^q~)Aqk&$Ws zMY|gt#IBz%^2m9G;h6fL>}<_<H6E>r8kg|@{Wxi5ZNl^?m5(Pfv$r<hgsprpw;d+u zH2Bf9IoqDVW&AeX>(Mqc5=Z2b?cY@o(#fQ5sg?@F4sEx-jP^f0usk{3Y5x3qW2CDK z{|d)kv(BYuxC;ii87WxzY_+8LetE_FMRY<>e!vnpoa`DF6Y=oy<QrBN7*%h@-cr%3 zm*~W{EshmpZE_-;Z$CR2pm5e(uO~ej`=PZmLA`nyy|L*cVD+NuZAeInVv|KKk8o9` zR+jS2n3adeemlKCN4l{=06#B7NqN=1NTaZ=qrLUQhLm{xD|mQUKN>6!7f!)~Xb<AD z;-iJ+{wb1=C8whJNZs>XSHx*fF?U!2?_1dF^umJMGMaOC^z^ob-GlEvtrS?5zg=46 z8oAa^ZsoewiqK)N{*5-)L{)y~^f5_didwhT8pm0AF-ktGdtm|PxT#xIMD!979Bg6s z-_o8P-%?HR&)~^wGO*Be#G<-2yWW!|`1dX_k8e<Zb<_YA?ZTMu6hj_R{*d`m_@Q<8 z?P19Y!A~l}4uhM*&zyJVCGJ`{7XG+v*8h~6F1SkO+Tp}{#isVxM$-hzgXR6t*3J!$ z+ud2xDrY1YsmacUu^j%jN2FM`th(Xb`zL!-m3E{3(wc2ehZF8o{DMN#>I8K0rmJc@ zl(r<JWE7r5e9~d<2$j*X8?|+XelZ5vwlc@9+=ql`qgm=Q+=XGTlRIqM<1)fTb7N6{ zGU><ui^hG#XWu_pw9~BOZ8)8ZzSQtoZ5+wIVKO!DQ11OL^Fzmg|0>npo*N%>t;6cQ z_j=#lRp9CS{@r`7Bj%T3D(qp$TFw>pmQEyrH7#{qPZ*;ob5g#V-z{#}ro)k9a|jH9 z%;tN<&62aZeX7Bk%d7n!wy(nn`4DHE62HDK4|AIwuMWPiZ#O<FeABnRj-(~4Fdsdv zA1*YF_IY&2r_O5XX_(SQ1(EAEW&EGmZNC1n4*rpO>12<8y*m3;f8;Dx_n|gRNqLY? zvYqCi{A84C8hiPQl2B<lB@*XKUT0Y6BU@*|8b#xx-L0*#h=}c-9knG+1%i*1c#M&G zZQ9m#<;*fts!^T^@|8DJ8=D(8CXF6-MSP&se9`7IVQASG5-lb^b4tg#HF~DeH7wvh zaS!QPX!$J9ctPD}&PEXrEtBqja(154CP`KO3p!rA{0pHH+=<1JmPp&!X9Bj;!XP{Q z>0QT<_~;oo>Q0m0p_Q(1vrD)s%Cq@BCtGUD>v(2HR4Rog<2=)5#U_OHvGPw_jKA~P z&zPp>Q{C%3Sg)cz7;T#*BXoR_cj4mf$yqhQ$#6;&!vn1K+I@~QZ`qEi0{b=Ln=5hi z{?S;WXUmoK!(IEw%U#hn2K1wcE2E{Dn@Weh3b8Xi#IbFbY{WPB>7iM28O+h#a{T_0 z#VAi+adUO|b8~Z+^99G<r%3xXt!mdm*YWaHw8O~3Z-?QywF><sZT~Zk`dBl^<JO+$ zc2t8!&bR*T?kvt;t)kN-!*Ke-f+|(N(8G0Z?%-qJW{CtmLhmE?$HVR1+*hFzvMydl zwXT15SsTC4bFy66csl)pUf}U~&WDr=*H)1%6b&jr`fpK?I`Ku#t4><TJluWlN73d{ zr=Y4Dk&rMP!=w;K!JSg`nT!&ZAN2RN49TVGQoFTxLi!|+Hv;#m5}?+WTlHPg^#1Yb znc`4}e5A7RG_^p$X_Lt4Z<?m9{7d=I|Mkw3!19Cy3a8)PjmKM%Sxl-&hlgfnR#q%2 zhx_}31;#X;hR400Y<R;(B->Ze6%qgOwo<d<0^{c9W>NRG2t`i%Es|^3nAY%O+4Tzs zC%QDg%R^_l|Np8p)LY0U;i93evKy3Ht&LX?RMp+qNEvVNH9VHjxcQ&G>Mbg^+<NG+ zxVShbCdQ%hq+*gC6X>t>^m@W}4Xc_giu`td4Grb#Zwf|+cfaMg9W8mdsV~99%)HQ> zEXr+Cr)WOC+d4X0jVLy=o(jXfx2E(z8YQMjk&tAO{^=ehG1!!-P)wZgeL|{XUYL14 zJ3}G*moW~e%`w*g-1a})^Y<ElhIviL8qdEP_R~L06QX$WZv&L2M*eN>{pbFdm;Il6 z{{L?-@ky)5Z*!(q-_*DK@kGtF+v*{#oQ4%fWmcL?{aHH&70n`k#~u?kuJhVrVq#;J z4)mm?IwclL^|qFJ(EkFk@ZHuYual7lW#3YMMJZr6@{&PjyMSW!c~=6r|5oSi2M->! zM_tdm_g)&hx!>VZb|j79%(KMv3hyOP_nB_qlx@I=V6cV6?(OaM(mz{gn*R1)MjwS> zC@g&)!t~F;2Q@+au~)Y>=<uD@-|#*>-rsB+sIRN5up8BC@bUDeSp}fbAN+W@Ald3u zXWZ>*8bS4`FcOmcuSbz5zlQRD{P^Mdd!h68SO>%RXG%>Xmc8$Eb9Fl33E!Q;O~-b^ zR(GQ2vr2QvQWJ9hrSpR5Iz-Lfwb8)Xo1;kI{f+6bl4nLM(tx@qp?wyAJvum;w&V2P zUGdUSZ4%*!;A1i8r9o+=h>cd*8(Lca%u-1ZaF{&KublkcV)-@YVb5DKYi{wpDv!+> zaqnFM$~$RlNdjJi2}4xwd5|EG?gkZh_hE(l?pD{<E*rP|H(eg-h+(4OG#Jd*NVOjL zFyEDM<=VBYSMN-;wzewc*E`O(CGwb$kB>k4nqohMJqkUJ!qjJacJ{8v26CCRb^1OL zy05RV-fL&9+Bw^dLi(L6{5UyX4O;Y|Et*H#mukL-@aJV7XrjK>*F23YWYIxvLGj%B zdLGMRw98_@-LuZeJJFJ-i?9zyM&s^NzI&72>q~HJByX0Q0&xI$c%7Z>4LHBY!Y5Z1 z@W~|kvOZSMKJX8<xX*qOpxvKeQ`?D*ip}Kw{m*ps^z0|=sAxzEPXWCG?D&!_x>sTU zebV#ib!W^SLGcNKH-r;LmTKEgDJgx>NR84qtIv~{wUGM|=&Gu!x;|N7=`h9R>L+4W z6kl+X7H%_9Q$Fc>oPa4Uu$mC9<3$f-YK+w^iz>KhJe+<nsL^UDzh(<+d>mR&3t> zYw)A_z9ogSfHE&4$&NT_LU?v|Rw0@$5?SxQK6yoA|6D$_??wVA%G>D?-7ZB9-g{g` z{*q_MrGp1lwO%{FqOOa6!vP>ZpXAU`?Y?dT(D9|Tk+E?rK}g`y?&`H!W|ai4iF7&q zgOPI_d%LUOv(=MpoaVz4V+9>1qa_Y=toqXXCH(>Z4mSg;7IvC@LGSlZ=W90I&$7pI z5piEUcUP!>d9IBMi*emc`zKE?KUU2^XeQ$`1Xh#C%zBErx0$*5-G^Uv;GdviQeSaV zQdJdmULeSQCMY5*Drk#FNeAJj$1XYol0F<Z_2;+xb}sovP*D1HSnS}o7{M5xt|kK) z6`Qx}0n{lHzK1Yn<kTgWs=nbp^Zza?{GKN(D-A*F@#E9^KI)M*D?*-29T)mcnMG;I z?L+u{7QZo&atW9<2NRN+%Vmn^!kSpw>loT;uLvLf{z>t>rM2}v331=cS=j!GkJ`66 z_~4FG{SH~3+)BQyoCLIpjT9CMslp;lFq1=t_dVK;Z&c47dTVe)f&#()TxW$$@bUPk z7Mr2m_pw|4T=%Udl`1?V>wONKAPHe}D7^W#cH(cE(cYm${=Od)8y|h~^5x5zNVAdf z+E(n2PJmqhxv&r)FQ5?kPRQ{>YXhYH&hjv(%19)IaU11Yn-sUtFhz^w^n|-MwYaU0 zz2-F3Ga>5F&XJ_!zF#8SjLc_glnuQ)IXM{?%c-Mv+CN%q*@tUIyyS7Zy`)`?^H$8P zL$GY*3%Lj>BV#*K1QE_2@HJI(s=@cDcD=!NE1B^E)IxC)5wE?qiA2VAf^V)ntKTW+ z6@OP+4`4G8h`;e*Grkm?@CL?9$ZF``VJS*R+&UcklQC(>CiPzLnfvpEN(zl@E6iHM z$kF#X4iilMq}q??6HLQuiE*K^r3g8uizVNv!ok6L@%(ur>gFM(;{oTWzhhXJ(?x88 zL_r4=D2&}bAD9t&)*|Px&p|czJ6aWt>x%X7yu5U&lhU|6B=B?ho4YYDIIeu8s24?F zzw)j6?K>QMA@NWj#=~J~%Uz9Tw^lV1>2EXiyL1PliOrFjAMR8###bgBVo^<{V8x32 ze|w_FNt}wWgDs^{V}1p>{#M+(f^;UoI5o%GBn~z>;`dj`yY!tWRU<HUq3)Nq^!-y7 z98@@dUxG;SV%2;hH9d~j;KBm6EFDT$pm4<8kppVOuXMTa7Fkx)bl&|}@5Sd+MVlL= zvlenLqxX$6B&4LIwzi7D2(5)Ww_32Xns{x^T^1moyj72-Q2O-gIlFrY)B27rpMu*Q zuN>Xfo7<c6PZe=xlc0{j(hEZ{Kt6$+#;Uw~t4;?e>^?p|Y`VG6W$r|kuC1-XZXK<Z ze*HEdP0gPR0W9=k_9+cDbr=!y^YhD$ewZKxgtB;F<rT~H(o(2r!~P6@X8c&WEhe86 ztrXXk$qqANB8Aa`Xu|Moig=8k+~q~l1djV=(@D--bR;{pLQjy>pM%bRr=F=o!IF?L z;fbP($${cS@4MXx<-YIM@&0V|VyDww2SX%|aiibK&s6^uySdMKka9_q{u1Q&54;lz z&}JdOPEU^ds27Qb_^yk2Y@}(VN-U0i>oLC$goFFp=?UBemr)h5gVbJVxqLe-*EVMG zosE{{GOppP`GqNU^*032(1VGeQVTw|d;i(S%e^mnF_Gsko&rB{={F_zhA7rm&5?Qe z2+C*jRZs|R!o_hSgN-{zM}<e((VdUEU*juJkl7Lr4@xJgNNJ;(cb`EEGFy)f3E5vr za)4#I{iw9fFa3k3Hr`O%TI98fPoMtrk;fxr%S>$Aay;kFI+hQ`SuTu}RSyMF{bwJk z6|tO3u7k7PApnr+jUa3(HxBI&-4O0izSP?eb9ol&jn-{xH$l<y^!A>;bg`YC*5Zjl z)Wr)1SMSccWI>{j#5vyhyeXT9<dhhuqoYf_))TZC!5LzIxK_JvW}GSNVUUx#eKqQL z&P_VgS~o&`ahFBnM+KT0*hEVs<RktYY(zIIp2%DUX2hBp6+`oF_)DRFXkO{o)9^{6 z(F@7MUijMs%=ohFL8k=9<ug-33vGH~>tgRk70Sqxi*dIXvpJ2d1Umxww=RiDYs$nm zv$T%fb79kJkTn&u?!T#HeXuo;I}n^{LNA>0sORmXS}P%8X4;L&dlYAQ3XS2Xbjk5S zs}Fb)Oj<XD7QUuVjW668b%Dkr(@M>6^PC{8n}M16=FPOTBuKO4tF@Gbp&t<CHoAo- z^)0d_?>S7v1acN9vNjL5x^GvIs*}wTGNP|1T}}GYd`0pVGvkvRs1BNzaY|eM@9c8; z%6*i!wy2Lv>3Un?CH*!Az4O-N8ae_F%G1PUE=yWVxol{B+ZXrptS;@Cqtq%gzX&Ki z+>rW|t?S2?=@e1kx$ruG)$QWDhn+Y%KaPp>Jf}ZnmDRcA#l4CuZj&NVpgGtEawHVN z?OX7(C+WpuxJ;RgxO77&hmMxl<=YH0!2~2hA$gZa_MU`XQ!ua-efRo-T&#Et35l@4 zK$F27(&a0#F|G4dGFo46Hk{*<e4?PBkkKXVdsr59WT04|FvIS(Afn^=B;mFsYoozw zY-;u6ejz548kdjS+3Jgvb3ZSd6{WpG%0|dHmbfmXA?zq)or<ca%X9>b4}mVq=8F~% zdrWq)05#COJxb2~;dFXc=Z83K0+z{#V#Y=!`tM$bT*Dr`WGxCD7qe|rVqyq+l7Ri& zphea(V!|Mak%0kiRX*Y8Z5Ek#JfkeR5P72}tOghch95n6GtN-2r&4jCDmBM$Qs+~Y z%f=6>vfq6f3azBGn|FT&mRl*kyVG?=jqSUUh$ye{ujZ|OHG#q)2fA=IAPw)@mDG9u zHpt0VT39pZ)YbAS(0C`r&>+FmbEAQRfj<7x``CV!tx_z<kY*l2OIcy(1)V9Fd5F04 z^-+<c=i(F)*6A}geDTn(25O<WH2O<QNcchR3OlQ$?sm^%E_|K4<U5388@J5Dto%&o zS7??Bzu6!Toy+1ksZ6cD6bRF}0-GdrC#%Ay!Nfj`Bz0e;vZjZw;%-=Z^@(g*Y@NG? zcadIcy;fNoa6FhWVy;A0XW9OJUK!nt^mJlD%bvGE=ub+#y`!1jVu(|#S<-C)=75BJ zc2`D|Mctp6r7|fHd8*{vIXk9AIL#%T<H&5YNE)`d^Lm|<`2`B-nSP;Lq1$pc&fc#! zg%g96-}-cVWZZ?}?Xn<Bu6pObc*fo!1VV2<JiHL4TGCGzXY&bzZ$oM;b$7c<Qt= z-t)8%bPc_F>q|T*yYKhwCyOw_v$?YM-%@f6GC5WuO=dS?yO;K~N|uvr<K|J1oOLJ^ z4igiT`z01lq;r7_JxPE%O&qNSMn7De_z658e4fqH)eqZ^-UqM!imREJn6iSx^hCUO z?Mj%gX<bL@E>!4TvhBj;SpQ8#_Cqx_EqYY>1SYq}LdkTF1-r)`&$W~Lj5Djhqokwb zN-fwIfb0^iChNY=GsssM)NQj>nlZh)+r6sU7LJ)n`;`rVY8gMTXuhx7pdunNRpW~9 zdmB&cGT<78ul8Fhdf0cb=knn;&1se4p>c?4t@<-VJEiB6!zf=d7KlyMx)(w_YYiiV z<}eIUg7vhV?UCYcQaHBjMDCXl*O-JLzDQ_k#|up*b@&+=&MQ3xsGMkenlH<<^>d~* zysb;i)BORyOmVA{+~FnL*WbI8o5rYZZDPM5^AG&eOFReEBZQkS9(-eUZlb0y#+2{# z1{x8cLc_wwiXH^ch+c+3=b+9egq(ieW6M>lEVF>3K&cgkKZ6mZ<E<)=Eq?obr4oHq zD<?-cCnDna^0)UF={!6<C}X>77YY#mJ45=ZzPtCs(85{zDDS<s>sPPFLhv|aeR;u@ zD&~1LI_5n-1=sOXc4~Szq*=xY)!j!q2c8uCm<Mc*CnO|neHSL`Ip6)}8hgk=R;}4b z`j2Ma<NpL$40m|xqv9q^sOG}T7mh-|EU1YdXr!sM)5o=+UlMiRZ#g(R3SVAiHq`pf zj%G_r&<CP{mlSH$A-@5BV_jl84;rt(l3?ESiK=mtdh3<wEXRm)9v-gWfS_<$7MnMA zp)IBt+7IQD)i7?0Lxkc^#joq8JB6~^h&Wnad49(t1CrFs%S$g$uV-J7V^BfFe1<na zXuUf$sWPM%tIGj5qojO5qRrqTQ?@w5&|FQ8mkLF84TG}2`dHt5I|IYm*0Wyb=!+PB zXK8sLTVtNSzy%lmD@nf{$r+_`g<e93)-z&{9z$N_Cla9(h3v=pXbU-y!>6Qi?wGd` z$ST$G2nmhMeU(>Kc;H!i)7VFg3e~!btF<X<wFC3QcL3S(rBN<4ypDIDEe{uB5D!c* z#PwrPJ~eq$a;xdy*;VE?Ea{vsf7<veZb%%10Xdo5?tQV>`EUBt*ukx>H=Z@5f8O#J z$`nWBjhpD<q0tEKxQIX!^<VN)wAEI9j7yVD=#jsPQv2gIyS}=gKN)O8T<sLS|8d`Y znHPh;o|-jr!N;XfuP8#-b6Xo{<uvyFZP0;Yc6inZbsT0c+p8$G$urGE^^WH^XZE{+ z56UDPXd2tlm|B>gE{3)%A4wfLyADsW`OmTKi1ejbYZ0{&vJmItp^MSQ9GeqD);<hd zwEh18x5YF6>Dfg^I>3h75o^RJI8GN!8jkhP$?b)*3cb?L(^G>Aq$+!BUNYHA)wd|# zOW7sW_i#I=p$HTX6%J_;%a@0_Buw%v6SWlAkD)^G1XhJ|m>87X3_&S*w<1VE$QDq3 z_#uufH<FrP*+@lRAzYWmsOrgVTV#wu#B#K1aD$$WcniNBcUPkg7yl!`c2HMut<?vy zP1l<}%<8^<!j-1;KxcB_-Ea`cwuMy{Wl)~0L-qPQdsY**=w*SNYHuQCRPhI9)&Tx^ zCm7~oh-_<bS9vo@{U_R4SN!(blk3e{b2>{Nc9Ax2TWks4IB|>Bxn+mNC$WGssxI*f zset-486uSV;}xZbCeKpH`vy-=3v}FV4VWs?qoY*6!)aV5tDSK;>%8|Ks|A$1XgoeB zoD6>PBBM8K$`2!$^@oijBQM?(p>(nH;8<G+?(SL6&7FV98E<%r(mFf-h<|+J#$z6a zM%<fz+n17wm7DEmCNBUgY=5`-iMX%Gg(rJ|^Nv`KRu<mjI<wl_w5iv__iA2XGrA%W zl!wAy3T``1)WEomS2~#LDAjac9((_wIXJ9qZe}LVgTq%zGvYIcP7bl&-O(vAios(h zgzvzcV@|8UH>L;j+Q^~IS`?BgU6ucAJ7JPtKrsDs1%EG3<|8rJU!>~K?gZu(jZn3$ zJAUy$ci(sXRl^N-JXyXsqV5GAx;?vEjW?ETlhey8r~GKX_kC_u;4{^ZYkBlq!pIEg zlS}K#5(@J4SST|D;aGPaOS$rjbWm0mYoZv@jaN*k*jQLp7B5&yStMM9cz+}^_3{l? z3in19lN3j#Pi#mDIWI(bDEYPA8?7VfWuN2)W`|bfTQ7ojg-*yvPEL-h>`2LZi6-96 zLCQH)PBEL%OyF;d6QYAN_qSkP41WGRt?;V$w>Q(@?$@|j%Q%tT;pKH&87W@8o5^an z@{!L>%Q23_g>y0Mn+)*nI1U83xG%lJpFh7u+t3#t!mZyadUcz4kq$HKEP3lMO5Q15 zeA=U=S=82FY5!dSK<##u8D4JK<IDWeSjeYLNrzsZW^twFEF^_@R@f*f5g8II;fkm7 z%~AZML)h9;wAOdG=zfI~eYX4Ep+DedPCS>f&Dya1O%Lwa_Q8+ZSw~R}-_nS3Liw(2 z-J;fed%mlc`5)Q1>|)YZHP!W@Jc>!P{k_PcK|Agp{Li`5OOTy%<C=HZ6o2#cKYAAk zWOjsW%g1CNR!kxBqt%*gi==@DnBYf^y>TE%=oodo5%2Mu(9>+^h3=*(whEHmuul`Y zx_OtcF~fK&9~SuQJ1zcLgRf|Bnrv<81>jnq=W|P%<=B~YnO<slyt>me)1v&K_2lGa zQ}go--(_@L_$SM2mPbN3*G#KOF)VbM87l&RQPRxJY(r2Xik7vWB`9O{_s_3{O7&5l zrVr!r1O)@rbKt{jYHHX_!lI)oeC63}a$<G9LM^SWuFg7aoJ8knm8fOkIC)tzvo}I; zc07g^&}!&V4<(VSEAXF)P<xB%`p({$VdQsit{QLgNMq+$CX6&`>Ab42&Ak!e3RD@% zlTv1$(|eSHLBU6%{1QZ+bnJ7bRD~rY^3(!VLG>ze3%aFR)UjjjTfc}&G^uaVqdJ=u zo3%K{RbuS!msAQ3UfR0`%JeI<A~(d0!Q1fg@MGz>+iEB%VTMPOL>9Nkux;0F1L`z* z0I%r(p(sXfs`RRXDwws@@++NyU1BE<Yi#6z0K!=kl<9uF!1Cs>@bDylTQu;rS%!qT zFniM7LH@Ih&KDl+S3fyiE(Au5%cRZ&<{fSE0{zb?&p~HpCqPqSZ4E%N&fhc^ak@FK z0R+BQ<p0Nbdn6;Gy}i8=$X{TOy@q?(QY8K9u3wLI9js-Hx9EK*>@ZmeB-3y$BP^(F z<~G{UVZI}#d35wX&xCA|-`1RxnOXWISw(zDM+X$?N1d^9>tTp3NLX+PP;eO`*29RQ zqQ+bXV*JZ%_5`!^P$Y2iS?;>|ivVEP8;%Ml%M~Y~iE8HRu-4m3VLs-Ar-uj2hdYSx zEPpi(!!~5kU-1NFkVk)o5U@62;5J+u1Qx145(`Ew`74)Xp2z-|7~lU?2Fq4<E)*I9 zt)zt9`B#7Ur(gXD2@Kz=>+&$YkjEVXfr=;7pXvFn`+*aJ+MeC~1;LMiRbjky=gIt+ z*Zr>`2`}N`WH)<V2F)>Nw~XrU>GAKc)uom{H4^fX?jwIz=W=A=PF7dKZMBr~TKz&V z*JU6++gn;zCMvms)bDvKp!VXm(hH<<<zr)0(<v7Og*jYIztDtmqGTTEHVIBcbb+o) z!rSjc0`OoY*3w-_&Vu8i9FuyrWprDigy)vU&|7{{w>6iq$)ca88kK-(zk0{VL)&3} z_Uyy~fk6Cz)N>)2z|qmM_>OaJg?;qdR@du1{h!_MJm<&CA;M*k53()a@Mg$Ip$xLZ zxS9^9n_tYfM+0on12wS9ah6)ZE@a^l9<A18$!X(9)5gZe%a<=%wLc&aAv>aJg?XR+ z_{YIvy)~SI9w;Mt1(SRPao#)BAl7pJ_sXA6&dq^ElBbm9#4lm^TU@j%vj{xg@dqEs zUCXa-zef5BHB$HYJ!V655;ucAelPTB9PP3in7p#~Tn_P!ofxn8+O>nCXJquwtQrg+ ztj2pff|@0|y!uK$Zo8{fXRsOWYm@mi^ZZsnK2@^o>8O6(?iJNDz?fC~6XKf=L7$8J zph|Vu=9S~AilQQ-$|^=BidOL^tfzLi20abwD_|gR=G*AzA{l4Ko-4)q7*xoLz;Xp` z%^&TqC4*cDJHk*{XPSZi!N1n>E2s;Cmb)v7w>A2O5gF8qLvrEdtKTcv!3A>R!i7h@ z?_eBLy0i?05h1;Vm}!GgG`(co08rqEM@Q{#ZM%zo89;oSw?%k@jGCG{)e?GbGxn|5 z2Sx<bL62!wk;2}ZhnQ{eyUnI13{ic)(k<V>xQJ0*ec*#eApj=#*|xT8B(EX`WkCar zG^BeYIGWG2z%x1CvF-fx>(^v<{jYT5MN1?-nvrDe`fTxrK(CVKdmFs%s8XgML{+<t zmRPWdN<V^-5fG|E^ripgHu^_|8<F<ZOu4?sHNO~>*_oo8f&5q5oG6|0Yiny29Wb=@ zwqsM%(3H2sVfP!U3<U~StxNOV26dE6rGE8ZiIyE{QtP^T!K38d#s+nWVu5@^rnK9l zrDoNP8nccyk;SKr8PN@gzt~aC%&;ZH0|VD|lSN!}ladSpZ^vW?1_am)7eorn6_IPm zF%Z%SK;t)8ZjaAoM3~n2cmk-Exp;v<>DiSfVktd4>W{bfAl&8_`<|T~MbqAu=P7Rg znaX6!Ch+`tibSgm1Ygn7{ES3}hHA2(=FINr;?A&@MB~&fN|bHb?tgGux*T^K2h<dC zWupTL_qBIscn}W4Y>|bC=u{ydt8U5`d>%P!+O5a)Ut}&^SUcHC;Je#jQH_jbiC^h{ z0-OWXiPXj?(`ys8L3rdwWl!EW9X1)n1Zuv3%y|C%IY<>a8Zt664&zmQFsXu$K^jZc z<keH-YNd5kPDV@bbiVQIy13IEbf7uTgQKMZQTJ8pBD(9esKk-hVRv}2Fv4eq7UNrO zAgX)LB^ip)$5n)E5+SpY_9dZ3w`ug1)NUD8SZ&T~Nah<?uJumTj+nnVJo?#{puv*` z(j^-4Cj*k>o2Wwxt1Hx#MeM;q!(^6)G;Zy(-5syK))pBz<tpX4q@!#A58v4vQ6<0( zM8(hEonL5-d7ytateg=CxJOPhebm5h1jKF>5uS+SRwiE-gdRQx#}ik_n98{|hekq4 z_X3SVlPz|%*-Un_2q*4kO*`B;&O+QCn}ryM$K7}QRuI;!s%z)a$b!(TQ*}HAT48Kd z6cm-mUEHOQdPF>a{Nt>Rk-M9$eDq&VX{4qIS;wBeWY}3A>WaP91ykvVo_7bDam+nQ z{7Itz4UoXTUVCnCZf$m;W+;payeVtJ)$h6+o_39vRHsJgqYNlvty}ZXmOZ?g-bLi* zsJ@W%=g)`nplfiJp&B7zh<c?I=d{pmtw_Bf;Xf^12wB}IPgpi*X<RE{GeZ2){Y#m3 zvac_D{qLyFmbdGT4s{IQezaj8iMcYW8p%ojEn-*9YlU)VzaK`CF1gmhLT~CM+%Dex zS&&^XPxkvVFw}LHZ?%o?hsw!Z)5Zof<ukt%K2yxTlwj#qNAHx7vHKmY`<t7av+L!2 zE0%e2g@)hB)zx=vuJc#9?Vit>xRcU^i0k3*QXG!wpiV#U&55|Ct?rJ03@hyotG!&K zRz|^*(F`o2TB^v<%eAI4<Y~c;Lu^~S7XD?lu(0rs>Y+i(Q=$EV$$GQF7kS}bLE#jA zULBs$d0N|~f3q<|wHo!1dBvgmxoV#5{pcK;hXQr>I6WsPh=Bozi0;+Y6PaS8ue=Wj z9R~OWG&d4?zM9tE(_vj$8n9r0BW_snvNb&Vx@1)PnD4P8NPel}-X9VJ+`hTVVK!4< zXpo!xgS!0Dyj*J<HU2e>>p@W*(}+v|<M(9(ia(vYgZh8lv%%x=*X3Xh9)`aS8{8BB zI|!hIwLIm2X1wlY5K~+0U2N96T_CrWo{<3**iHc!#5>8GU66=bKLEwx=BSncw-%yE z;eWh|^Y?=^4gT*iUW|AZ`GK6czc~vkDK!mEEf7i|*hAgf1G%NZxYn&^Xc`)Es{g6y zA@`rhSP9u}qFx8-e&zV!_a3NuWMsNo%CEtpcnKRDfc%GaKL!Q{=+cNf4@Xb}H4P3g zjf2}W?K4ayr1*a=qv;mb^eA8xC9J-s0W8ya!<(fT!w7_^QRU<0#zy}XF;6W6VYyFg zmGXVI)flj2(a1-HhBCc8TKW^F7|=!$)lLsv^T1FOPR<D$i@uJ|paQ)w9|%UEWGN26 z|JnV<8N3k}afn>jCw<4O96@(W5wIW2RElfyKH6ToMoK#O^XF}2VIq9|W&)O;@83m5 zL=Iu_GWFdd3wv#9Onyr>@eYHK5Ta&-gZve@se~#mzs+Dgr{TSZ7cX$WNTqP;7rav( z(kdsDxu;b_&LX9Aap?EX&X9NbADpObMT-J9u@59X&}s6GYta)E#;5%tcLOi!0jvg) z@nl>^l+h$Xm<NZhPNGDjO9Oyt`?!_D)NZWoBhWbgugyu6=4E&>>is4E)v<E$I+ein z;4%uuY=PxDS7HyrJ`08Zu_ATDRXVz;3tjP?wV=>hT3R~GNZpZ99{&8-3iRqc+>!sm zJVlGL=cCsvQ;qa*481NFlYMmg@`@FTpa4Y>=cQ$6#Tk6E(|`>`g@u($KVC+IU~CSy zj18{AU*J^*L$ACLELnJDWb1cL#VA_gZY<m4ank&t$)rR~oc9On79f~XxJ(<wKrh%z zkd~1l5zkU&ONpWr_xhG5)zQvMh=vT&vStyJYcik~_65O&GzlbNKya@(?qAJ%kUo#( zG%fg2{>}RuW+^qj6dQTaV<yFIvlu4<52CB|6voKSk(WkA3uJ$+$Gl2MwE=cugYZ8) z1;vlt9Sh3AKOJqaT3TeSU7TPOz{%ouxNTjhQ}tvT9I=3rVEtDC2E&SXByiI!e=m86 z|ESps=2?vO!FIR+=H-lcuntN1Xq+o_02nf&qcs#2J3y^I++8&UuMKJJT}$=u=ulYk zX3v0Q0sk!?G#e~8&mR{t)l=OybKmVnP1$~#7eoxPpl6gm?1~S>CWI<n?jspVinR>% z*EDyDH$Z7<R4^#tIe{@A>_4K8Gc90h0p;>3-x-J(fPdjOSm<}|7a`Bn?fn{B-k5HN zfNm>+YVN%AOScY7SFVEy$Xz?Ob-d4irk7VdqletwxsHHg!BTgNnYjQgz80OaUivRE z`^3xWO^D_;Z6F{bf?2beVTpVY6z8qIy+WOjv6DN{Mi|oxzL@;W|E2ce|J)Epjk65f zP(O_M)DT5;hx#48lHdQbir<oXfjj{x>{312Ee%#@{|JnHU<N-C_R$U@y9qbpovl#> z2J82e?2iZhz}?KM_Gn}9R{1$Pp}EIQ_L$u-aVJ1mgftoaeeB(UrZI9JgvuWuT!#sa zb&S}ym;XG*C_c8WVGEM;@3VL}A??E{QMdlga2DK@D0`B?^1s%?6|BH=PB7Q}xyhJ% zWKo420s88n>9B-imlOqc9z0lsP%6Qfury?{#557G1Dr%KAWQ$#WAME%)%Sn+Q{!Xm zi&~K<kT?)-a<a0D+D>2yY4kr!OibJ+K!aLsTwzBgtpO#p|AC_uD;|1;7>qc^B^I5) z@`Az4$Jh4-0aXAFv!&nhz9Uq?_6RENS~vSZb1d8032bjB1DSPxMQqIT#WWX9AmMVO z5V>91*}utby)UDIczGk@`s)>|w#{G;34kIf9>9xp8<cfSOiXw@k}In4g@kOYjf^zM zY_SvNE;Y*B%nXd--@_@mVWjl-_GV&kYinb~0mIrMI^Ph?RAlUWvJZFHr=nRx+pmkb zbfk!j;_>XYxr2)d#_q&waM3h0G$ac1@sZkckxxL)1+MwJq#rHMB80N%)|~ZF?(3a` zt=ubUum|Ad;(ijd|6W0+{Y2u9-N+rWeBC^9UOqI*kc7v^k2I;k3l}fSNJ}$oq;M=4 z0b;Dkb7{qllGm73M=I=-V3Y(jPs3+L46eJYV`bLPK<<KAo_@bAntpv|CKeP+ftK2D z5mw|a+rV%cd}QsXLxETlpca~p!w-~$a4uVg2c(^*Kgt}ON?=+^UP&=uwZ}LpcYC=4 zUEfOw5x*{Z)DyPv0pK4DRzBe5!>v_Rf$6MOwxtJLUGt!iLBj-nK=CD%AbFQI^DYOU z-wT9)Wc3S`GhsWxc|g+xdf1ZVI*Aw;7g1-ovZ0)BQ3K>Bc+23*>>3A%J*YPO2L}w~ z86Tld&CbnXdKwIPGd`c@?!CNvOZkRw2Fab@Fc98bh@iTw9M2K@X0rf<<g|w`e~7xR zEY8k4LrJ~44mN;kV_*WLoXRKVX85p|_xJaKZ1VE(C<A}3A>3OJ5Fw7>!zg|T^)#9j ztRGwgW%J_g%C?q&44_UoDriug5yia#lfgq38DUt2gf<twdCnT~r78V6gtef}pkhkp z>ESYWmqSa*%Q}+kJEbsGL3R9IVIT64MF8kUy$QBof&8(X&|E1v5Kjj#S~KcR1Ahj+ zd1WXs;nn*+AaER}8Z<NH8J^JKEJO5QVPi9<C9rB^bKV7oMo>_YuUsq$vZWqOXAoK7 z1eD)O5q4&N+dHNf^NR;<mf9pwYDims-)@su*a<oNr4r>Mll0xW|A8U2keK??38ejW z7(ZR%{Zi~wC2pWzT7(ecSLdQN1Lg&(yK$Cbtb%rXUEFIM0yGGpVr}r_wMV7>dVL#V z<)q<%+oss+aAB%rQJcBvKMG9ab)MRq0i>utQdn~Y<SV?-@dMA}aiQf%FO69;Tf0q% zhJz2_c@)z4Gysp9_V8XBxDD%m!mvW{r|1y1a0Qmf#SK7CNrK@9q;BQH+)xFGk&0va zzyl;F#sz2g5S00R_x8(#a4evha@lpEo2m|Dh2%3Sju)JOH$hxdlag9weNZ$nY9rTZ zCHqT8DPz?Vpt0~sN2(l|wv2#%B6$Ks`HRXr$n4HGCY~AeXBxs1B<JKJczD`Fwt$C- zjEX7%VPW9_d@C<yMO}YcWaX!K8cSV;J{*2>d<-t<XFkGaEx5A3pr-sdAHciCqo>T% zHU?(usVP(VMF;^NJE(8;G$cpo0{9IebYt{v@YLE6wKX+inmf=T|2%po=XXMM{JJ^k z<F#i-`wKlZb2sVf>7PsUZ+2|`xhu2}>;^6<Jp6zg$D!{7l^ebUbqBh<E7%QDWx85k z^vgiBG5%l!bHExZ0h+NL#j)J3{cyo`=?x6EcHzReI(9wOC$`((`oy!y$USv1E<nLa z3@M7@c)V6IYHe9x8}PTvSJ?3&;+PxjtlTS60-=p{ikRI0@4W;x9rM5D=>OCH@jo;x z=@%HwrHQ`2e!c$q2k@w}L0Py02cLe;&u6*2ZevU@MMZbSpaA5_tO@j2FdtlQ1K~c` z-4&W?|Cc$=XnrCz_}{y5=zY@m-d>F%l!}d~Cnwba0JIWt9L9f@8+yaiA9pq_#?8I- z$D{v$+pqrw<D)e-HNmp?i4Cgf8lZq-A3xtwef!Xxn1Go(XD5i5YV>dX=h=nM-d&I= zfg**f(XK2tI)2>m)T#|$V{Ag|{i7r3QX*JAsgJ^jEQ^>$TQOLO=#yKZ>#9Nd1%(JJ z@I}v$x?Djgg1>MFx&$^hc08M|4X|A{y*OpD{X@4))c*4m^$T<^``ZrX@;mlteh$J< zO6oBx@d3yM*bI&q*(uh%i*UjwqD6evy2Z+JbaX^7>c+vrfdZ33AkGyxM@P@?#Xjgb z;H}&N$0TY4P+OkP$8=_dN&+}AtBTxK4O(oiNjh+V^hx5gzWN9p{;iYs-l>xQQ3sn> z*{)r`&beSWlsg1w9y$r1oD$x23wL+-!8y=OU)jJh3C22=-26`IqG|P1iO6f`j@1C5 zCJ8#+yLS%&W&cBeCf+|10qxU#99yu}wTF-}iMXs-IaOG2kEW+|)GDTfOC15AGCi~7 z0}O6pq(}wh$Op9~01?AbTt0pJWLWu_!Lg>gnvjZj4*p#wp2N}ARhJ&MK3*NTZ3G=* zlbk0zI{HeygxktUM~5P5lzwp<G!y`rb-?t0%!|<30$LI}k+l<xVztw}DtW+LFpiUP z8n}P~hP)RM(jRBigkgMPw3~wR2Rc5u<+;TLJXrt&mUI&Zlz*#rcQ|``Zg!O8(lUT^ zdpL5(!Lb52*)fpw9sJ(UmbTK42Q(xJuZxs{2O9dE{B}!V=(XF-%*<iDBrRa5fB%T3 z1I&Vffr0h)^(5h&u^#x+Zct5<-@Sv)9B=gZpZlQ{$NcbXiZ2|YQ*4I+EW>2^i;d~E z-Gc~zlh4wT_?@u0(RAWl&@P!<cjdpiySb@nPegVQs0HxvdcJ!O6sodBpOf+**?@3V zsz`l;Y#E*f9+#n2Jw>b<7UcBwG!Zi#rig_Wkj7NP43Ma;xev_|Mv<I_uJFf?9|IU_ zy$bj&QXv|{RKg!EU@mK2mxHZD0TPwzv`H6%%T^%X%n0|30v0fzc_AUVs#3ivqV8Pn zmT?Vbgy>&!JQ!4H-GL5t&25LF303vn?hY9Wd{E+#x)X`6i|~ObnyM0Dg7ybhsaFYK zG=<v?Om59q=G2)bhkd!}-8xo9tRyXcVP*#c;pO@ZLYny9<WQC&6g6soi3(6;YSgu1 z*fkt1ESMAf#b64R@H-YVIRQf7(W4WbO)f4j|NQd_X68FdFJ8Q;tQ3?`21ij?C9Sv@ zDSbC&V7^hc6U07(Uv+PLyQLOlyvk*%|J%2F?-nZnr0C|F)YnWJ27=Vp2`Nmyju((( z2j_xZe+^1KC%kgy3IPGf{gO;E1oZWVWcSH8LXM0gk}h`Ogcn$VwPrN4f=L_9?U=Ke zfH)FZG#OW$sOvfKD9llgvBb2rKHxFuC<98f{~a_JRk?>7{jd`dgHfkd;9>8tWZ;nj zF-etN3v5)H8mbttGGs^a75XTyDT7+d7(6!;l>6FvXq<_UUjiUQP}`$Q{4=IKLS_ki zHAxaS*?JAKD$`heTHvfyh&f5G>t<;43m52%LQlnf4_#m+hR)kM-O<yN4*&;dPZNN+ z5=&kV#Bj8h5ZWYbeRR1GquDJRjE<dN`*{Gmq4|5rqu78Q$j5L}RwsB!pMxzzI<b-V z_S=BCAVl#92`4J-Y3PJRMdA9s-HAK^@2sz&Z!`>BtrgsWX(cKo<cXuBlH9|`lXao5 zp3Jmdu4BJ>^O8!;6j@6Z@%9YhLmA%<lkF)0TtKFSZ!D=olw30mVI9FAby2YlV0_<A z1Wn`hLKEO(06q&H1_cGRwzYYIDRtn*Qk#R6Sz9|{d-@g0V`388>~fFsl7}jil8u-{ zu_0`1(Z4NdFKtiWfRj+vfHUu2I8DKiK~jKqZ^{oqDUj?S0y9v7ZMX!$VQ}U(@4y-) z_+JZvNMJ!rcYoN!W?)PoL>8g11@gxrvztU<+~`DP&H;yEE+C^EP+nygc@_92%dK!? zppZ;qBvka*BmCFMF;euP89sx==C=B~DrD0IwSXNMxd7M9&CUj10F<!&^CvJE!aGeO zTwIS~n>=PQEkMBj@YfLgAn~n@qi|VSw=f_Kw=Gw{uO>t22?_1POQ3FeA8s$fQCd*N z5i`)MNWytAHfYeZ;E)*?ToC9p@FKMw<DS4LY;J5=4d(PczkKaw{?6a!fCiK)d73E< zvcWkRJNxM|nJj$HkN^!;w6FF)jM4psgKB@+G#q3&+ygH)W;}H|_vfAe{@46~9boo$ zOvti;1{<;N*?CPerZz8<YdM_%V0gvdeaPqbP0WX-1~=6J>+4n6a|U2b*EiZ$n}1ke z-b!$1IVa_$BG|ZeYGR05lmX`3m{S;=^A`@k;I6hISMR_@9u`$#V=te9;f?wZ@*Emd zUsF<2`e(s@Zn~bkq%kREN8C>vSe`#*WJ>jZf)aH-_++hs0FC}}qe%qJnf;kc(0@2I z(_6yG*x|6?W5q+?*}uJB!9uE4dse!i5*5d3dg~+Y<{%u21DiS2Q^;&|MW0rg2m#tc z7XX%?v+u*_ZstX9l)JUHya>ZUPV8miHi5reTpGyU3WX92gb<rf&dbh9qXwVRuV2@p z=Xik4We|Ebl@=XpUMq%){|cHV8|Gyg$O)m)=;jW>UgDFoy!>T0phd`RT>>T(!@TMS zUg=sMgebw$gfSH=zll7M|9NmQbQB|rhm&=lkUZoIsk{%B!9z7WGei6ZQ#Q%sO!wsM z0ayf08hCX9MW~l=5ajxMZ?AnD@)+yVB?1}&RmWE9Lc0G7DBzUfYq2MwNzeyv!%hO% zwbgq55eqokO(x21Db%li3qWK5`<cd!nU9p)reLUn$_?Hc&|+1+GCEaSF&a6Pmzi=M zcFmvA>>dO|)dNq~9uQrL^xSX~j8#O$pnDWR3xoiiU%EFq)!I4_HkG#WSoNE-olrz_ zI^i&s)wof*9y7j@+fYdNgQAM^x6ABIWi()JD4Lph4W39lK16eNs-M?f2gzm?%(u`t z1q5$@20WC(<NV~whTxRX?;lMUFZI9-Y1m-KDW{|v5*;1gi3CGqKo$%t?r_iz%9<_k zpq<D_LAEdcp#CF~KL8yP^G%G82NO_jL4&@f5|0#sngVOsn<8Edx_Volc}WUX<R)b@ z4#Yi8%efyg3dqaLo3VP3sfYnR1D^=Y#^aJxn8yY0vjg^)>$K{J!v2;PPYXti!F=i% z;eQuItJuq6Gk}SA5j+Z9TwD-B(nfa$Ex~z}ZV#?vSllWIpTdSiy!RWWUnR7uP$UTO zbu7=#Sv-i?(U3ghb6LE%;*VR>F=Z350}KWnjgPKWeJ2Db&^7DIkWB%wT_E8y!CiV# zyrr=o0p~-5NZ9)u@WS@XfzUwBcuceg?{i=YdqAeZZJ0LtiT6K&^AR;}t2QO47~Df7 zP@oGQI?`K)l;)8MT9r1k3&4UI{K@yorm+Vj>uX<-_JmM!AHV`LDMY;!_ZBcqulBr0 zXwd|O#?I0JPRt&_4j5WxRb_iYBny9w<cwsfApUc(uio3eRoA|&$qObln3;74-M}Qv za*3PaL_t;-iT8OC2<iF2L^4wo7iVYN?-lu}si`3IIwrSjWg<CK;9NhUF146Pk>g38 zZk_<R3}y}Z?MCitmIN()7S{Ns7uiAk@AB$HAU`xQbNEC4$OAAqFeVr`c<SQJOhB12 zoUY3c;FloCk%wJ*IR-TDkZCbAwgX_kU<iF+^c$>P7T*BugjT8yaQfJOtLkMdM5ydI zd`mljq0%uwLPdQy%$4nb_2~ejN&vrwZtK05#&R4WkQJE|&a45}1tQuUI41}U_<n<5 z{aIaL(E$#LP5gDkq^0nG0VXp5*udjukZ-_WcRjjw<YDFrz=e=~N_u*3Zu%@>Jb;HK zSt^C|j2=5j>y6yTH7-NBx^g`{55X7%eTQ1Y#{;~cnf%I{ML-7f#51-bAmoIg>%fZV zSOQ6vDeM@)Fm$$BIVEsE52zv*Wj$0>T{RH=aW%S3yf2!@&^85y5#JsYpoyCO-1V_- zZHv~CoUMy+tdo|C3y?#i2RzWTi#U3@I+ySNhGGd5wuIx%B@=^Mw|u~G+oYWyV4#i< z{CCg;$DzfB@!n*OYuE6wpn!lI1W3Q@D5U-uiWg<n)YLVAYoVA5|0g5?WFnW0nIjH( zF5)${bAX$mUpb2f5x8%XQC%CL1MQcQ@pEgdT&^UWxe+^zKC`j&c^Ot*aX~k0{}*w+ zdmYtD3s7Q|;@?Ekv!*c~nDc%AUCH_b5B^QwU;H~Y|M#WC@Bk+~|0cEm)r9|BZ@`$m zx7(5SwtxF6w(W}tjkWB7<@`WF{P$K`zKJ~XKT}jyCB4$pvRxP&7Z<mW-_bMkjL-Pr zUMMdTh<5)vCB~U@lh-6MBHFQ)v6QFD?=cK_rsV&;81`a_aIxf<FE>O&qecyshwjIM z|B5P_$M{(H&vRK*q5vw~+7U09<S*xhR*fQozsM%|0%2oFgvK3E9NOI61Z8iqmg`I# z(hmeD3tKVy4uO<EcbBb;p_AK*Q|CZr0TTwyy|I%+2j4-p5EB;%B_!A`GJB@*!hcc{ zb0)VuVG4($Y=0Y(z^2OvAPJCStM;b;zaAGRc_+mo7%P2Fk3E5&5y4qbOr$*S!5F<n z0x)MAFmw~Ap#WybMmaDjC{`v{zQPZtOpq|Gc@gfQ`AD7}L~oZtor6TxIC6@~1)?$> zbBI^V!uHZKbokn;st`qJk^kk^VhPbuIY7*bsm}hm3#W*Dc1JBe?g8wTtAJ5Yh3MEv zZ=LPlTfWB#xpg+HHu8WS_-+?h&>yR+gxuZtU}AWCOiwTT6y&pX8s5n-TT8HbGs+0S z*pPG|-Cwf?YSF#|3Hk0I&ljVj0vHM{EHZ-x$)P)g8}cK<OjWfT5E1ypsi>(%!2cSk z`R9{~QHFf^AKf`wUi<AYT?#ChwzK2-{0;hB%K+f!AGnANc|!gP_?nJF5Nr29>4opB z0lwoG;0H|my1W#ZN6L@m@(#u_$X4z9IGPvG=|IE+1zO=2G=w6dCVpLPv#hil4+^M* zgF~6FtZWOwfhnLrfvbSF`*u7eiW((nn_19H<$R{(e}zu$QxoA5C-gL!D?@CBejpR> z3YX5LWv=rmQ;-)R46B5ae=VcUE-89=R23Gk!;;HYv^l|C3YvQ^v?_jVTk|dC^va44 zpoeX7Ar@VznWKAct*z7bi>p9|KRON~vsF>K8JmE_Z^GrF;55xtjDbf4!Wi?72{04^ z+OFLhBUEv5a*~EK2C|oLMuV%8I(Ci!2##GR`A`IwSMNP4JGq^V5o3Nd1%`aKniKQf zx?utQB|{|{n@-2vaadq%LpzHxoVO&W%M4K*TCe*}L<e%geT!v!zy0^%dZERWwt)hG zx#2LRBQzygc#y(L&*Klk0tHTr@bX^O=&OY339sE&roVg|_Vr}1@eGJo@_8LqMlv$6 ze+R;B4Bs52wy_V^>8@l^K|0%!(WD63t<ZgT(;zs;J39m%=#9(Mx9YH7IBoK~70YjR zuzZP`3}*$rCUy4A&kM*=kD-^_M-M#6>{DL8yzxlB=5seo1$-KiYM6t>=6uQ7**^h0 z&E*fLNnMl^jMnj58R4m`2h0JJTUQLzi`Q;6#Wp#2^~k@M0m(i+J&ik>^VEzfJrtut z(gr4cqeyR#H|U&m0m-QD%hJ^6Uwe#kuAi)b0&=Q4%e?Ej5S)P%P@a^O)JbTmc$his z0O$hTX<XM|fhP3guKe|A%^Jl@BxjaQixtm>OY+Q`-JM7^5ZV52_~~VyW!MOzx^!*@ z5M-^MUF2iL@^W!8UYsTQ2&fwvEw#)`OJT`%EZfVlneu|BIrrYTN7H9m)b)+~J7~9i zu!N+~^4)Z;>?Z(NDBFV(H5#;!w-bWcqqOcwm-oprlprHtzWZ}f-D4Pfp3mNz@ud$y z46r|v$8Fa7mJyy;-Jt}UnPwT?qnCbT6t88S-xGP1gCc=p<#)+!#o#k9eCF}GrPKD3 zk5{+vshQRJL+P?B7rp@3B<s_H%`a>W;u7$8zMY@hvYqksM$f?b)wTT87kf1PNJHeB zKC0N3Dpp<YWniCKSVrV@Dw0!NkM`BJi2M?xZ$NpcPCn&%Mvy2~;9wBcIx&lmmr$&x zi#_gvs8X*#+;;~-quruSyTztoa17q@@FgJ&2Ew`C<Rc(1VDwFBBoQYj`UozAJEPVN z8EM@oM|%o#a?>H9xNuHi*k0dsp3K&o4BQ>T7g<x%W#q&@i<mv+#9`7@H#3{Te9yt$ z+)a^Luwv2(KE5Huc$q4qzYaIUj5ymPqJ{w*P>E8XSKCyE@>d?T$B)(3NYx0%XSd8| z8RuBhSU9&-+b=7D#yuw^@$CY0c2{7zM`4gIIx9$bO%74uc%68u>TL1CZ3m%?EvBp{ zf_5XsOPrBSBG;oJHeMi;({=2j`*cyFR;dHad7SlENC)JKxIlM+SR(tKvs4BurY0aT zc#4Nb@sIv#kV=!Pi;9YpzY-}U_I4SZah=#qp2A=YX*}7b;U(w2RniN>%n%Nf-11VZ zMJ-q<zX~BtRkc2-zJW^k<szR}ROeD#)7WRW?~3?Ic9C#+igNfp41MGN+d)Mk*i9dA zsGowspJ!iG(FXMkO@wAu8ww`4iYmSvNy%dd2rcIjSVA}p{Y<<ZW=s!j7<n<&h>cX# z?d!n-ikFj`44etjZ90EO(Dy3M+7_%X8&D<x+27dO;&pE|*mOWcjTZz>idsgBAUCl5 zI8yRt`&w>f)Om#;&_Ep9&p8Kmb&+_Iw5!sJK}tmxsHokMmU-|<Zu!Ad&G><eZ&6#Y zsMj`&h>Be;hOQ+EI~SSUH0R=3B%epUerHR!B8FUGlzN6wOA`kUX;Ar`DOp-Qu#mlJ zy<%=<L;GYy$LKXh=m9jg7CWmPi1w_9EFt``Cq*_z@oVP;CNx&!g6Fi#88WbJ#pqy+ z2Jh}S1OeKHC4j%z1<tir!r00v7p4jfC$O)HkWmyqgMFJnWWs+7ZwtbrwpbT8HlD%s zSL{&l`TG&{8gS)ayA9P%5DpC58JQHT<NR49Td_|MfIS(Q`EIbdPeMeDTV$TJQU=Gh zpEqF0fA}&20F6VNUE%!%pslrT8Xuosr*DR-*Tc)pj4?o6S~>viITE?>?J4nH@TCwf zKSe}Ds5zfTQRTm9rhGpKlN7swh7Ud8#4dT)>LXr+Iacv>(ZL33be8COrhvqE1^Zj+ zn?y{e4{59)#0lEJYDi=+ytqraCb@K_mTG1Yib|hLhY2tU2uDYimp})mZ32S~z1a-} z+*jDkE<*B4pL3gPfCyyKJ}I|hXvVT#Ndwxdbt#P$RqO>;L4dhU+O!-+U%@XxVA(yB zD-hL)8z7`D^@J8q0szL>D=-SR2>taygorOVlSC7UNI19$2g>1VGvt8Vhl<SuhpUzp zQxWe93k&s@V|(YCsBs2>C#kBbN%!3-rj_v72ZI(qIY$UG68%ZmCm2H>2QI)=x7j3b zs0Iov_k+eM@J&IkZp46oTL><sh(`*)fi0o=!#3T?!?v{+h}+8(xP1`|BecOGGf$Xw zp!H3Ipns=zW1StHV-IH&F{dmNfcp%lWewPY{#)GJ5d*d3ov<_JlomK8p1%Y$s^X;{ zpe2Z?`EaG2LDmzhUKz-~!kb0;WCqy;CnBFcdxklinfpHfTQC?w?9nKyfE@_4C(19} z=tNvt)L%&+%w5~80@Bz+*CcH-$mU{T`4C3Ci@8|(>q-k!;Qwjw&BLkQ+qdD>j0~kx zhA5dbBy*-TsLX^|2raA(%TO{W+9gs{riC&zE0$WxB2wCvOr>bALTNB1B!rd*&-rEV z`@Vng^BnJSzrW*tkLP*cWB=3MX<6&LzTeO1y3Xso&g=aAc$CG(M!U}{C7(}NiF|W+ znV_-A1!2pbDOk|)dk#SegHc3x6R2%`eHo~wuXZL%k|p%Q7WFGy)?E{C)|UK+YyNTv zA0HJJg$7F1<O$PGRCW`?!_V`7=_D_pdALdKRZsdV|5QEUD2uD*#GXVoAw$pNFUWxv z6%`Ms7QonQrsI-vY|ooQCoL7h8Uo}Y(4FypFG&`Ckw{ANVC+d;b)4^TiBRdt;#rgE zNr3JMIdEXB>N=>qA)4bewlCuQbYu)MI^yfc3*?K=u2<+O3y8)?qOck!-wl5q`hv_S zE6TWacLGHkSOBB<VT}sxzF0!QD25L&Oo#fB(1L+ahv7iA!S4i0FshN1Hi-VUTjZdh zQQb0H>iU2Vy*SRWY`h-X{Vg9qe@JlfHg~sB?T~>LAynV19*}8skd%>DPJgVxe#12B z@g8t|)8kcvdyHgi*<1n8Xq7Bs5D{>boFuF6ZtDhHCd6^C(F2>$yW((+>=BLp4w#@w zNaVsH2W$hosYzfIQ_Ywh!1AjSMHTz1*xSap6Zhd_3}S-rs}+Dt&YRDiex3kVAiuWa z%?0oQFlgO_iiL@}<1TM4ty1*O*CgE}--5u+?+}U{!>$J|(wn>PgpXbwoZFA7d$e+# z4L|WNW~flH5V`J>=gwAtDo@yPkQ4f#VbsH_BnY1cwBk2V)TvBCz=OYkc>n(SwVju+ zoAzuzwVY9Re`b}Pv>X9-glEud!5shkwGn*y9f>PJ_9ohQ1=y)RyzG#fC;tja6#hhK z3gPSG6*wPVibn#+cmQt`Pn;;SE_|5%4nYIG!^L-pp#u;G6m_1K-YgiG;Vv5MmkP^* zVC74ErQ0N~De*ST=nr&AwvInR5wF*C^=wMYlKRcw4#~%pXKJ;lcc!x?K@c+^Mjww+ zvSux1Jld2*1iJ0<S(wg4*nDbi$lR`Iagci1SE+t!U#o|YC-Yx_aq4^A((8{5*ODtE zIjX9v)=|$Lci&iN$H-VQzwrGs+OYjY+VG~cHy-TC-oKjM?>x_B$u!5j$2!AKZyOW* z9_x;<L{)Lnb3%>C)%qbM3@dpvlid}_+DyNoWa#tH63*xNNj4Yz!`?_}P6d}2KI;Ai zru;<v`_*Kc2EWv{KdW#I2_&nF82&h(@XdSs@a^NE*S6-f_lX&q{7E~<A2*WT|J?C) zg1`-zd!OFFUko8CoTG+WkC03I1IyY7)nmzY-}^<%XCcYY4@2SK1^a{Jx*v=uK0mwS zMwG>s2-t7e6b6q*Lr9j^q0n<7gOHI;ngVq=6`$!~$w*#I=>If$5HI$CS}gu}EwLJs z5~r#6@5F00i59@|0m3b`b$WuRNSS!?rT^87rvl&g-j1;Ut@-l)YWWEJ&1%G|B^ncb zuK(9UZswyw?f*LVd)o~aa-I|ORK$-v(WlqNjsGm}eW%hCxa{)dp_ln77Xv{YsYG=> zyJ=VW&26xTKR3iUL}&5hR@M2mNsu6SYxekwF|NeO=Vpu-*AxBl8sY<IFxIg>*(Y8U zB<pz)<4sf#UiMd9y$?9?@Y*x0A+4=)A3l%V9%26}fR_!whSXTurn6K1$~&eGe%&|p z>!?A0cd37Ok%j`D{q4Nw)%0=2?@~GuK@T3RblN&B6e@6fyZKpu5zV4x`(+yTj3@V& zTL$dDs}nwOO(JB-_f^-W4B6+N7C&WX7uWSkL};bhYq;M3k>aTwCbYaX(7Sg0Sz=Q3 z_Q)4ip5HQtcNM&Ru=v|ViY&b`H@NHBZ+Bjin*zHZn`i&gn8zA^*i+c{@m_fRaWN}s z2>_Blcij69%8g%K&nxgz^My?Z0yfVEV0#E^n|;H%zrX(+e<8(8oUt<(ZJ!(D;mWBC zNL4|goq^s|N?ibSg|+;5XY1INt@F}d!D!hrIk(?jcl-szS<CC&nZk2y%Pj3u`^wE> z>m&ELPAzudZkdt1)<#CffZu<4)zpd?y$3E2&U0m$7#Ud^EHxbNfc)yg?@{kPEtGfP zy87v$M$IX%Z<7TYT+jGKqdfx4mom0_=T3}^imONY?s+gqv!~zact=-wd1m9AoiaU< zzXlwJ)~z&6mDl%tK2o*q<Oz<l^M(Muq~pgKzpLQ6SUs{qq(O*<$s%Uxk;eCmG5Y@~ zh`YlGyr*i!q!U22O@yIdGnpNF&(8nVha}mXdVEdiRH*E(;SWW_byMOK9Ok-_{;t-A zMV?JjgZ15f1(kZE-vfGoZ4g~9+FpCNuZVTd>eSgiek1pyv-IE8^<KK6A7Pm-`T-RK zAE)-5a%EkgHM8Sa^{)s%gNLRD60hR44}RmzP(L7ks@(1ASbNBn^Ol0sXD)o_|9aE8 zl5(MHTUK_qW%jo}!Y+2S(~sAxXooSk2X4FdtsPJSkP2H{L?0uQ^L*gv`V4BdVNjDq zvmWX<SZIK$L24%~@Z561e7ogbyLJscjS_?h(35B65m59<AMkkf!CL}IB7IY=(BAhd zZ>BmYQmm!jP8N<^H9QW9@9=)Hpl}tt?U>2hEnLP%F-Di^R_covJNa!JIJm7q^hAD1 z$=y4wN$C?-Z!WsU8*;t;IjQ|J;C@S{u~b%FX69(*kLR?&9{t{mCbKNP-me~RM%E6v zgE<({cy}e;1u1+dX5*o*Mb|ioP8V9a@*cH8NI>8;GB?zf3JNMqD||~qClncGW;{we z`C6t0nIAoDyAUZUsmo}@#K*@YoJ(Lif)yG_ZR~=B5q$xgLuNTAP#|zleMks?FPEjp z4v!1|)NQ(^V_0cdd61d!7JTUAQz9~_pLmiL@=iRFIJoC+x_d02HCx$~ca~DA(mJge z7UidIiEnC>%t84zcPBoV9%!f;E3p4j`S^5Qa`~93)H<g>26kDF9<wWdEk+7@I$Z6z z_ht;^hb`T|0D|JxKGH0VA|aN4`r5@PIeoeb922HnWn4`fm9T{X^pV}q<fMd+jE<%p zgLtI1BK&}l55}>aG5j{r*XMoB!}ta!GcD|NQR>24@1}2SQBe-^tP<)zC|;ZQt<(+4 zT2DE`F!vXY8$&rKEk-p1dj+q8U^IoK8Kbw$uvmaRLx26?Sf9x63j0;<=%O56epwfj zUmY;~Ld!TK>(<k{x=c9N6ciNjni?B(3kX2Z9meTSr<!5cuaT7W7^x{Rx3o-8-%1B* zg1PaA5jkO3!apUDP|vPdA*YZup7s-B{t$7B-~R4!v+b=(@T@RNYAB7V2ti9X^?dpt z_0AJRZ!Ik>qDG%+fw-ygZjtHxe)+Og>mrIXm+xwZLHo~Jt3z(9s&%in;PBI=qn}79 z^hdue%?sajx13&d=d~UqO^sb&=C^$L^3l94_us#~v0F%Eqet@#;rBajM#x+wtE5v^ zj*jM%yS6;};d{&MJA_b}xHvOF&mly@`}Q&Ce#W)G4eLw<(jbhq2q%VP%^qMMQAk0O zakwkJm{EMTs0!c2ILdfly$JiJ>rj8c0##8UX|R5)$K61u9KLup5bo7tz+<uvkR~X# zC(GK_AH)zsQ5)`c?>%j5kE$mvj>XYn2d+7NLqjorVos4Rh)tL}Bzta<k=Y(%H?DBH zVH>)e!QaRITCeG4j$|3}G~AYDWoKl_DJK>sV@|i?`{%oy%)__xEVj2gVAylkocUej zr>cG!vX)T%%u5!rWlu#J1Z^66`X-_=v+~=N?oaoRa#Q7ezfzi*8zaBaPM&^Jdq?5- zLqje#Bdl``_SaUd+&JFpcaGXJLO$8>q<V6@<Jmxs@dfdwZK0o%v$OBXhI_t=;5&Qp z{#9Q``opFZ4(mgoX2oo)-m#$f)*{k*$;SdThd~U$hzklf<CdYz=DByTNZc4WQK=q} zWa}nixv}A4V-u6)%V_VlLr6fV3^s^<9ee58PzClBB8Yl_`Tn=J+;;$iC)&%MJ5vJ^ z@Duonv^cpzClHdt%7uy(L@4{RC^MhpgmN|3bTi7>)QKK=44j0YME%wmKVxgBg!}VC z3O%kF=Ih;xT|iik4>9Qp^iUb=dm{;Tqdnc-(h8aIF9eI4_BJDxQ3<?{HW`ly+cG<0 z`03e!I~9G&LG*xRgb|_*%yAWq30}?o)Js11!pEPYbktta;>UUw7k^$6p)Ws6<4n@m zo@-*#&7C5BwY5sOexIl@%L&sMGl{csh6+Rabej3Z%6Fdo%fiXJ`riHKG<!dX+@RsA za0kk{lEF23Pt3HZ42EyJKh4=FJ?EFiO`$pS`Btv?I?iZ`ydaiT92Gw6!7S}CvVto+ ze-{l(9>Gnhq-$Pkp?pMDa1$3VcoRKlbR|=N7i5D^No&@ad#flJZGna@DSXDWLP1-W zGVR3O$frlTQHLHma^!MhTHNKUS4C=SdH5(wrK7jCSve|mmQ@q?1pFt8;A>z){ZDIL z*zI@-4-BCcwL~RmA&LY?)z~AvyyALfyPPRz)F+R?h5dNs^XJaRIFre-2EV7|<uXpc z8cvc(83(>FHE&vWGBr;*cAJ&7q%fa$!v1TE?CrOPiXVmh(dHTl@jShGE#G80Hf8)i z8pBe(58;1cJIvyAbunPn_QxN)LOvlW!0WsgLpNQ!o_<f+dP>5e1D}e>hGOn<U)@v% zJq;5_E@hgIWi^iXFG<2$iP{~m3m1%~w*&VZiBo>QsOu79TXz*bM_aS?HLOga5?=Fm zwM9(ug;k7Mr15PpDN48g0JmM*5kWl}S)J&~9b^hgOzWCgw%6%DA^k2XrC3$#M1 z$B<yKK9e7RZey9&AT;ZiGBxkll!FHY5j^6{aINh4rZWULN@IOb4-W<cKPG5Y_u^&> zvWWqJrk>PnYt4Kj5`1sR$R-aDVfQ&uR6#jY%9nf@$~IW%`Gb}3-j<}r8y?5fa~8ab z$__QT4Lu`T{<!U3a8f>|W2Z#5KBKLV$K9mM$j*tBHxf^&Q(_zl3`7u-wvl%(FOC#2 zN>Z@oLNMi`kq#ev9!xe(M5-{V-$f@Rm>VMrP$0-g+o@^LW^<RzT6c$!Jv}nVfbP_V zvz*Af-JLd@;WAqW0R;fVix)2t%`lU0N*CR@tn2zYn~-`FN+I`kGG_>SO^vLhKP4I5 z_@r1dHO6pbF<<}JuU{dZsB2FE???M-_DZ_yEOS1|>o~Kf(krS)r0FPA29H2Qk}<3t z!*X6T^UGYc9}6jdFS%${BBb-Ht3;+GDTQN8zwG9s6?*cKo+(#}98Q!Zp4>6!{5_Xm z@bn+e^=*l;-{ah&KS|S{NBU-fZ>wqYi23<#?w3F^npSHVvtDt{{92Mkj0UNFGkFek zXN<<kQM`jL@#+)I?NN)^B;N%Tz6aYEvug$L>Om}zS8`S5`*uVB3K?l69HqKT0tp*F zN6UN?eyxP~{Yx+S`WXxcO8C7V9xLd}!K<Ko3uc6MrvwZmp7}LMZ@^!@n9O9Z!*_bI zh4^$fS3fdODTJv8PA@IVqAPgLG3Oul+)Di0u_bJiP4?HU-XmBdLn@;?g!KbN5RNzi zzDUj;>r47{auNhtM##cjJ+LItY6V0gb%guE8`k5p-Z<-}i<(p)LNbJM9Y9GKz=3Ap zNOSPR2gwCCttSSIAoT!Ch8q0so&D}q>DVLbgZ1pRC-{!kHX&IX)QDwC+1bvh^HB*9 z#McNJTmm$T^*&UY<fnqq0=LSna!MZztoiFp9TR3_K4)ZPBmzhistw2%WcGUp*67H} z?hs$6(w4tVVQmjGEvPV~NA(%#yMv3YKbvT;5DX`Rw=jo!h}pjyOq^KP*fPjF?Ib&? zJ3QV4{Ym>NAmZ;JJM9h^PuUqFrKV=XpN}w*{<wZm_$PQBN}W4XOLq@N&ZQm$;WLHF zX;5%$(k#hy4$EUnHN1?o!0PyM^WxLeg_CEXs<G*JD7d=s#x1&Ncu*mQLri#lco_fE z<se})8F)U0g2OWc1H{u$)=~!XT>>hGV_*10M3k47mNE?a;{!i3Ah|3OjPJyP==Pjl zlcOU{^}NzaHS-^mYqYf0UCbtZiNq(Lj0t{e!6~1;j6Ke=f2{MpV(+g2c>#1iH7I`f z^$a{2xoxcE3(Z9cFRFS>Y~JuVLNaj0Mqf`4N}ccZ3OD7JJA-dSZ&b5Bzzkq^0Lo{< zqIrD%c@{Ncp5`HhQU}uZlZvTv@B=WcZ;!_|OIyVLw!Eq>#E4w#?{L`lT#CpVM&WWc z=>Bttd1mLq`jqLjV*<T{Ei9hGLZUl3>5qDo#wN09c!S9&%y0Y^<uSu;lT(9RtnTwd zPDv?IjZHd_bv(6&+|FO+*v_=8f%>q{53+L1<H)6J;M|5HsI$R@MSuHp0b{=%xnh#l zU1Wzmi<RB`t}_h9Z|oK=-0iNUkaVnQr^6`1Pc+=Uk<q(WA{Cn<)ovpUOpL>gd$Cq( zd(Z{X#%{CXrX)tMX86U$1Q)!VN|_qQ^NC@dcu8&;JYcC8^@Chbo^5MlpRca3@5~>h zy8SGfbLe-yDo4(fEp;D(p5#m$4VD?^|HOPw+EyQ!SYWAsep{(r@Pvq;6%w`Xs%6Jm zBPSD3JjMCi*1WxU&|dr05`0@8d|S7B%#9Emfffczmn%&ZwI(?Z-7rsK{t7rbTW=fQ zOOoL?sH|L{Wd4gbyn=@{6HaEXc=*by*k}hJmolaI){C4L66&bvi8rBOcVWxy_&&gy zh*>4oD%CpBs9ZX|QPly%NhztR9wYmi>vwXcA0fiS({uaDrs}4f8Y*uh-FM%LVU}EH zZhudNDgMX2v`2VQYVc!(f6B@Y`wmYnJ7+0FIRr|TDD<34VDjaowzm<AtUSf=-t16> z<O%FK0((QwjsRj}W(H~Z6qsaW(X`6X<HL_78s?l{smsQ~L+up4U)$Z;Dweqpl0yt% zb~~g+XEra&%*=$q%lZV-zqGzwi->i*s3;4;+wmsxmA3AnC^-`)UTW%6WBqQe6aunA zV$j;&-j1y<3`;npOk-3zP`W|adD*tPxtXr$AUP<x5JCeTXr8*$KjPPwE|g(-XRWxx zJ<YT0`TMi7vS4stR$dN3;F42{7913Ou#<AAa&%F22tzFdl_IF*FR))fh{0{7C+I=f zhN6dUpsnq|a}$$?5DK+HW@~MoTmhi>+lng3*|03IHZg86c#7^9OqS(KrPp@tvikK% zkR8L?i2dvvhjM~0rWa-vb^kuVc=98Beem&EF^|zCyX=^ngsVoy_J-kb`@@G1h?VTy zo|bEUz3V}!6Z00UR&5&D=~j-19>GNmS6)4|?n7rMHM%=3F+Tn}`Zef`NHg-xGMf={ zkyS9!67H$G=K-m^q{2JtV4}TO)Wx=V=2MB3)9nf_6?Y<U8(S*4)>m4GSIQ1ic;^uJ zJ$L%PyHXBy*6>9~PhnLYfE93{Q@J*3WK3>@46a#&L9!Mk1|V=_!|~5=octdGh~F^J z+AxgO*9ILRyrZav%MEEY6eV-TN0FGNs=hZ;kr~GlY{9C6;KbN7e9SUi=MQ{Sbjx)X zv*&sI(q=2)`<e_DTi}fd6pF2_tu+(>ym$Aj9IwjiqkDH5fg3XOAvk>}tr?-+yth}? zdb*N_?Oj-Sc%qngvj^)`D7`IXKiL(GoxHry??>igbWkJ8E>~pQnTLyui<dW*C%rxE zR+OgD)TV@DmR>bdqxy9p*(#kTCYt~RNcEx@ZZbn6MwUzb`0)cOR+!}R8Gpi%ZQbIO zBy2+TG?))_yMob4B1Tfd@uQS1sF<x;vw<P~1}YP0XJ;f?IsH)RWw7b$8os_@(j^7B zxs8pC&IENp$dACBoSdA+)jzu)+h1s{br(b*Jq_IbuaWByS6#Tvq^tAt@xht--s_Gc z>dBznb+y<~+YJqgfd!xw^Rw#Q{QM|Piob?J4=1ILB(|$E>X4<SrT%XB!LMC5n=hZn zTHc3M4ftw8!2wTw*D2X4DYt$8knaOmYdZ1q`PLI4AG?8SXAR3fAwbxn=g#DlU0q$+ z;&)NJ3PwW$p(5T23NJM^6~W)wiRg!*Pv-6IZES2T_yKWk-+=?8<Kr0qKU>VQs1gA% zn|`PLUCff&eVM_&zQiS`RXRSFbrD^}2K*u5+xWIn3g$q9vDPYoP15k(p{cYM5@gSh zVF5$^buDWG{`uF{(|`~En7f+6%26e48bY2_=B5?q9qw(uUl_->u`=IWEkYuR8j|Nc z6Q}Tz_L1-~2eAc73pV|~{iFVuGgv_{ecVsKIOm8DFeXq^?uPvz@Ms-}&Wv}x``q14 zXl)8*XOnccko(h#9IsTIGcV_Puj|S+j3>Y$1qfS?Ly3j2AOl51iIp>t^pT5F=sJyb zm0%cm5a(hQcZfO-BhVt+_~<VrJzHGmSjk1ZyPfPLMR}s_coa_#U)m~ziw0~xC_?dj zj(V4iHix?(%`n=3{f*+_e!Hr+O;r&O4$pFInoSCNm{dK9SSzd(^pW6q#Ww8O|2XIr zvxFAI+J??*-ya1pN1J*Pve9Y9@g0gE)vc|qVU@U4RMZJOLNAu^Si|#d$1(?P#0a&9 znF$9;qolch7O6*UHG@hBs4dLQQe){7ad5!}X9BXQ>FHA$#^@%<RjS%9qCn{tViP_T z7`Vp81B%M*$OnKC5Y=R${t#NwkmP~gg&F0rSmvpOgdgvY3e=}7=eCQ8qvN<_NegOM zFdZu-j_i`9=)>l!zfgj-r)qIk1cD_}mtf1t?R+FR3<!W|5YY``yxvCc{cYxJ3bKU^ z{BidWqP&H?9vX8Z_CPdB%#jjSl-F$@e~3tB!XDr4q)^kh0IAa&ugilK)~{cWsi{o+ z980iPSmo!}w;m!-;X$F(gd;58!usGX8gZR>6O`w9V?e^o>m%YSwG5)3e=>Uq_X7qa z`x1WZ#6ST?#y~6(k@Y?k!31|T2ggi6rX|+txoo=RIf<O)p~A4Sww7YRm%}~qSZvo2 zI&3m#8RymgVLlI<QtEzMH<s6T94d65(<54@@BjMFz<_Vr^Q-2fnH@0DS-xn|syRqK zdskS&ETcMB%BQ=s-p(W8eT#OgM#E-fe}v#Jgn7tfvcxDG;aZ`61QYkQlPxVRR5SGd zK+j>?r|gyNNXLywo?Cm~_4ekC(H4?)+$Jc8z$)~;!*|63ag5q1{FXW_LdzUSov|Zf z#ZIuBm@sgujB<|pf^&rqqP{@lv#+VOR2@Bbgc*x1uhe#D|D{JvdT8y4%OvJ>;QU8M z$zqZ!cR?||5#s~A0s`P#fg7RKPVH_k3gq1%5|X1zJs~z1$yI)#zP>)BvDryUtEbXJ z-xRfFWr<yP>Q4BE2b;#EYkjGqY3{Aq86SN86RM=TSnzmDg@uKeEU|$sO86NlC>W=q zyCv5-I{odc!18xIpJdG=t-SS}NoRpDx7dOMHOgWo=sYRR`w+?_7keZFKP2TAZQQsK z!E(gLs(iNyfz=YVIta=H0>4zUmV?*Nmlv6f(cWa17*&<yDusrUL!V=+Eq%_B9|h@Y ze;yM6&3%$Zn$fP2P>el*0<^Ab3kwen93jSkCS!`$D=3)yICV6`y~4CisXui=eLV<Z z#egblM&ipojbYt_EyS@hSj4V?)Y{kT_cOo-LA<0D#Tfqfx`ehLhpcriLHglId4q)g zyLBCO5gHtQsZR9jN*Kt`KOUY)ObiYU#epK1dCxG>%zlH^G<O0sYE)y4<$z?8IY_(z zZ1_gA%uLt;)o@@T?I@80_it?rMQw#Xg1Fk=1a&ynrN2&tmX7iUR;u0(o!x<QuMpkH z<X7KhJuVmyLTyD&R(<uYBqlg0=qvDo)Sur&6?fBxdw-s6rAqSIv+@hpbv_n3-Q;&y zXu`HC0v2|Tl_jL}YeFR0oQfL9C_5x*Gwoa1nVD-FvU6gBk?zudM?eB|oGOtua?}T^ z!f?f)6*KWQxpNcpWv-4{vTc4jWMM#b0{r}_ofzR{jN#Q)9-@|gy&<;Xia$vCv}3Uv za=+2;C|zJ={YJOabUCWhnMRD}Ll2G;gn@H4#B3qnLFQfS>gxK}WrmJoj#B#1;GnC| zaiWdb<l(8;=N$oO5De4>Y-q7Pmj_%bp`QGcVM53h*YL5$ZBDFqENfO-x6WdIg1CO$ zhb=d5P45s#nEPOdL}AD?05J{|nX6rMa3lq0AM%S)ZH@#~fYFEY0@!X8_yf<zlZ&Q( z7=5!JAXZ^JLHIX{z0DE1Bgi(9Yq(`3RBz<)5kW#vxYLS$v=JlFR#3QU%hgFyw~lDK z$XvH0Hx#p?{AO+1F&)+4q7#6c651;{gWtLyn}O)+?maWyOD4S7v8hKc+1C6kg`_IX zxg~#Hsi8ZL4H3iwK9S<+si@Io7@1wgpnzdHiAEw#g#ljl9#}rieSE?(Hv58u$HvF` zxw(M|<6BZ(?dTI-f-La!-$t7Zs1#(!E+auPF~v1$oexkOc)Um3!UK`^n&2IbRHk1_ zRMfif$3=@D!)%3ljvfaU4NN6QZgi{{rgqmyWaa#*)bQ?~F+tjZGsXZQ`}+E7<|I7s zEpa~C6dN;!Gfs;C`qIO`f8|}jET&E=EJ=wCwB#QN+wt~ZK8oKeiavZd3O#2{r~TzK zL0gIe&Cu1cj#np|Hhxb4jZ<Yv?*H@O>~6Z4lSPflxLf~7*HQpIvvWXl`9l;J!}={| z4E4#M_<(QILHXEV+;0|v3^={HC_styPinIn7U@Up%ln>I$%lfQ_dkfxNOEQbD!RUm z4HaAqL}F;(Tpr{1w&ec|m2$mj@Ej5k7_ChQeU5362%2q{*J%&~8%SWc#_^Vi=b(1{ z^GAMFZ6EfMKGN$bzg?P3de8yXQ4E@lB*u$35XHwl_#GAP|NNuLqM-+;9W7@yJ<5Ai z;Apv7<Ik5)N>bjiY!()J{^t1=v<E(o<SRreoZ<Oqk(ksN*8N39q?}KBKwr$B1e>6N z8%}cCPVS$f|L_Z$?y<pkc5FO+=)B4>F@&b3P+N$f#Poe9Uf>+dVibaJi&dU8dp1fb zXlp3sE#%%<BK_k_mo%=cZtL^zGyB&cU8vHLmX?OvO<TJaW1#x_KhbWezXu)DiTL={ zMIak=ci^k<X%LfTv@|z^ILCcwxjo{sv}{7Di4Ij&+uVO#LTAhvuozO)r=g+TGiTln z3^ZO@B{ZD{i0`ItL532V5DC;PtJbFjdQAr?;ue5hz)45m&@plMSN!WQxI!-vYZjy} z;7;gv+z?BVa?5I_KUQob*3V)Rsj1-?9$GIMqXm(84@fz$U%!q$0<1R<#KTQnf=@ay zK-}S-twH3@%)dTDd@=h}{GT^y{5Uhj?5``@5yNtyhM9b%!}^kpl_@bSw#|bBtAX;F zy11SFADAT4Gj1%O;QeG0>Em3`rR5tGNu;17U_zb6gh(VEf?Hk3zmP=A#^NF`kDEt2 zY=M=-I9WZLv`H50fc=Ovi*$a;zx{T)R}U?SdJ)0%&2?LL!1AxFA#8MPa@6?q!*pf1 z`)_1Bdg(9xA(0y$%(&e|>zTSb@&$1aJBRdYWmTKXVs^>aD$Lr%awJ@Vh=^r&(2%Sv z19EH;`ycN!LzPARG~h@KefnfVEP&How8yDaObcV<Om**OAXl7H_pZB}dgtd;-65}g zOZc}`O;#$2bVWyAx4uFw@qxt@=M}%OYccOc3=3lP>e%Ryj2KqMc?huypzL<wzyUb$ zJVW!hy9T5&xE3^lH8HCW98*2G+$C&te{bXd6|OvP-_pAW2C~`(*Ug(ndSKDInT){^ z=#!OPDsW;g4e0|=<>|r5e{2v3uVvqDIo!11JU>A;rUsut910M*rirqBdkyeAF;$Oq zTFOjS9dkOsKwQbo6ZNP4jL}}&$)tPH57VPvkun?HxlwN&)-O8BgeB)|Fg=8)OW8d* zFqCCagjKcmy`?BU62jRJ=*pmKl`{oaf{F{9?>3a2i0vkHbwE<gt3w{1)DDiTy*!w9 zbpE_M5wpVA#}LyeU3Vu?Nhw!+sR9I7Ra0}`y?b(EXbSfP(pX>24cuP<3JJH9zA|PE z_!yo@yHFYz7Z+n)E0dX<>Z_%8!SZC;k|l7xDF$j$2=@uQ!waNbgSleN|6t3tg2lkB zKeQgWZw^4qQFo9iph|5>#@IY2A<;O`6ri`)_h1y_JFK-<Zj9U@C+GF>_+oJQWO$#W zBZ)y&bag#5(#*)QSFT<)sk8t^g6&>&J8h7#o<?<~dLA@Ca&9xLr-i=kEbr*ouc>!a zv!8s|kDaD3c3w@&$nbhy4;V-k9<Zev#p*%ja|H=WHN4s{*mZNZS`2G$5EfBtGQFhZ z{-L`Ns|Gshd`Hz#zia~$$U^Gb)s8-d!9i~1NPN%*sUG5R1QZvzH{ED(<;vtmU2&5> zf?+t+_qJ%omk|b*ApVT5o7&a;#di{eq)>bnora#Tc*85OZ>?E#OM@_!`3l?zPKl%P z1-fIV4FeWu@mxXb@K)p0MuMnmSYiAMxn;#(ba1zZ+nSi1@VyAgY26EeaHbs-@|rmz zV8$O&R8x+`vMT(}lo?-2S9a}KzXxh`AQ%ucuW`_sDcLEl?(yI<WtZJ98~VJvP2;^e zbb6^v?5w}O;UxMj*f*uVlc-P7dCFAphUN|SG$RshG$V0ZV1b_0#*O!J*jmx<z;Lwk zirR}_zkO@4diC!Zerw(BfAk9NiB{g_!ht|gD`{~a=tm2D|JnjO<3Z6CD^>_kGmg}< zge3FYDuUF^NxP2qdmF}cJuR9NY^>=YdI4FtN>=vdy8mo8RpxLwYkTv%2i-zKN`~*v zT<6tl$E&w64+3KuP$<}vDDa-Wd|8U+W&MgMk$H)>T!Yb81qD}=J`*axsAeN`cdMH} z95s$g|GrR!9eWoZU3%ctso_38`3WkmHV(v@OpSSd8wakZXDEyuNMspSs;g7{{Qe}j z+W#WA5WMbETPWE%IqUS#W@Y(SFww3+OW>MK-l@UdTg2jd*a8w4mR`zp5}I&q;mf*# z^yO)p83GM?kx}xs;QF~~w1)peRuOwG)jSTl6ac^Z2wJT496C=pDqU$GY=O%`0Ld>4 zo$_`+Q8ZSXl^-*P$kkPee({-osq1mDWbFH*KI{{W1pS1F0!_?mu?PodEU7fcRB!aJ z9h#nziLBvwD5Zqd8K7Qu&}<pB#9@Dp%nB%#>qDrqNL`@FJ0P`2Z0qjR$SvWlN5(zk zLXcpur?8!|NX<7sZrgB`o2KyI@+=YHvzdKNdSvSH)Yp+Hif}ZTq$!Awcm4ee=LoAn zH~@((TNaE*jDYhNhGGD(CTPfWXh#CKp1ljc7X(R}$1O*I^d{!`@Nn6__eXs7RaJ|i z<G>_~CdYPB<`e)n^{#bkvK4SA#$bP|EAGd^ko#Y$#b~G-lK;Al65V4{%Y_bXo$ibD z5{VBzMIT89<rI_6A#$F?bb9q$KzTBg0f}V$uYXGhZOIe1ZsVZK9}ztH@BcUC+W$!E z4w-}`1XJflpuMoK^xM185&(n21nG{Hg~wynzjC(kNquqPg|PXWE7`GocHM_-fGt3j zM?A{O+qZ8!IstVQ5Pe=9iUg%lYUt0QaC`fgz+aHt6z1wI9F=0sI)FX7Np@f@!`wnC z@5&WqM<OT#2|@F8^3*Blk5hMCxqP{H5L_sPC3@(AeopHcgiGbH`#(~My0CXe8(fj( zN)fGyT(FBlRzY<N|H2_Y&bM!u^YVzMBsY!PZK!!3zy?>PMM2GM${B)NP($ikVj3A6 z5vud;&6;c1UPpLDC4;p7+iOCrZe{fhn*nKMHAfUDI4uP6nZ^z6L@mj|_?MQ)8iLMU zqg*&}wt}_3tN@xkbj8x`Z-bt7G#4mG$CwmUwXv#=`S8gTKUjW%$0EiiQ6|ipGY1>g zqokgR?L(K_xrJ!$%SuUY(A5>a@l4K(jO?8b$W#;yf?&DVR?R><1YeoE(9>4zP#9X` z;o9gL_CN&L_OQjP9ps^KHwcJ9a=~H%M_YD*9K3SayvFopeKa*3awy)E_9iiQtgDCO z$5>w}26ed$E+gd_xHkuIAh;Fq;z1)*zA2vF60;7t4;%C}lipZJzV!jr3)(g7%*BL+ zWK4Nw6Oi?J1voRYxxkIA;K>-RTrJ8?RBY!eh{h6xIOe9NDW%dyLu=QrMcR51arW6f zz_;^DLYA<L+JJNK!J`ej7<AEmV0{@FKYC=|LLE4XL%f9aj(v+ZI^`cwI$bDzF5#*F z)gRXgOC-=$<yaQ5i7~Y{mFtydTrpf&yjrxZhM3rw@nm2z9E9|v>^Uf0<4^Fwf^NmK zpl`GN%<Zxv_jYwP<{G$K1CW^tnY7)&c_8I>5j%a*-I{hbsc3!+SWc**CMFDUSr<al zPZL_UOxkq&Y4JmNdoUB-vS>dR2aAiEn471)19&gdt7li~pu-&!4JH@qz7Bwcid6rk z`_Pfss~9`IW<EZMY#^&2T~Q2l=>jbox<_fDtB`5W9K+mE2G08o+`N#IxW(NDp@_tY z1J17(hR)T#LE)leQV!jdP%@w((cnomUx}T_6yL#5(^uQ=?&sBi{ybqfEINM?TTcBi zfP59^4Bbszbs%N}HE=F+el04_h>6dIW9k<c{^Lck^4sq65X~^hiV3KS5YP#8KVmWL zo2eKEGtlBA6o~jfXw;v3y$d6$IHCg0|I3idI9e_#fACj$BuF4+<iNl{tX03kPoEsI zEerFwo!|X`H8%dwRg&BQlRoBkuYqK{H)@f}c4DU=QTy-LOYgm=d%hTCX)OTN@<*Z5 z%YE$)4GrsM6clKnvoI^3sX?<ty|}R4v2)j~F!?6}DAtzp`&CU6c6os99qzc{fb%KI z$;1@3kWkOSfZ{^JI`7%DXE@OKX{K5pBw;<LtrZx2?x~Lj*$>npu_5VPFeg`bTzvHK z`0>0PB^X@YfkQRWB>K5HaGL{lKqJU105R<AvC+}n-_HD4`USvuSI)sr64OiCYY7e* z)kvPIugwrv%g2Gm?Mz_nW3jRDxemlEmvt#3UWymJi*T_x#<L8?lfX(HscIBAG6g*R z{Pej^oe5)Lpkdp!A5a#3HkgRYYG~}LDo3#B=@0Ktvro;}Z1lkVe9_M8{2S1DL9T$J zMP5zqHa4TGacdBeY>_s{x<s<&GiYs$&qFy~9F;apiD*A%KT~BK!`U{M^MH(n;_B2d zyTYpT{BAzU)e2AAK<xn$n$8ui!8pk}DJjTV^fBHoEBn}uY^nfPh=yA1V;rMWfxdK+ zW~Ulkrayf6(eeX#ml`?{2iwELX=J29xnU5*fN{}XVuxlB6XyPJA^>)gAHmzERj?;_ z%`Zl4j1;t`lWB)E5)*sOK@4zM7)>}x(VpZQOkqUvHLzeRajtXM4b%ig`vJHf?ZW03 zRsHMx9Q;)nYHDiTA?Qm>UCqwUPE?+E>rM_tIS|a%=XXT(D;awVmw<JF-!%A1Vl&pr z)>3l_!qqut<ewK&b+w6n5@tkj^AR_Q+O2ATWEb6NjD2lqCnoEf86*3yx1)%|!BTRe z+TDU|JVPXAJ0Eh*imGzx!-QVq{7KQ9Cb5IB%WUpPLKi_L4!w)f*aKF9^fAWV)xFTl zf}6NCDF2|PI+2i|=vaI$`L@ZMy;Q34m`=Aa+qy>ZV-GZqN;l%v*oPfwBIMV+BXX%J zEzwgyOIuR9Kob?d-Vpf>{`u~pSXYXQDtuox{OR-OoE>B&Fl{+GqqF21LQd#LxXcQ& z;rLU0M;x{{-Ajx-F2AUFzkG=~^g^NY2&)flB-~1vH6S_n%zWJEUb5)xyP?YHSEJ=r z-}`y3Kn0|8h?e${<duFO>z8dOq~1^^w$W}H8#iUBtWi=bgjNhlla4zB$a4FRI80ot z^Nuft=^wyq@>9+a+eK9(>!%|_)$?B?k|d|<|7Q%!U#RXs9BL@(e}$UNNd){)>_Xa| zLG(qBTdq7DNpMC)K$rp%g90(?6BBX<oXSC1QHEVGHWPj855pqWoZ9(>=nkY3;?J0n zlFW9)uY>JcLzGzN(>1AyO)g8=hxw*aAl^&M|Ao;ng*EPb8>eNe)qx9XC)1DMytzQ| z7BzXL-5mgUwIk@6z9~DfWBdT*T<A&SOgXGe{fJ^@7YJa!q30w73D4b3ATCq1_%Zi) zajam@hS}4=!ZDTwuQzytei?lG_5*YFF&0O-p9n&6aYRF)E?F8<A0BS*aA=zXox+b7 zl@P2xwnkt80u&7NM!61=nhsAd+!{(DbP8O23)Lj{MkTUrhY+V;zkWTkgbhu8^4*B> z0d1T=gCwXB3fJ*^XUNHSfzCj?i+)d3)x!v%<RPOv1!dGyIPUeQ&)>Ib8tjU35{T*1 zOP4O0v$bh#MYv6*2ndD)Yx2SpZxFY;s{tJ@$UXL@X%gxs0H#w15`+Cc8Thi2DoXPL zhTqGKWq~JwEC+V7ixw>s7r*g-;hYV;Gw6zQeHFA&adD497+`IRuUx6`^=y@}%zmOT z2#2yt;CiC=-*s=KUI87fdL;(cZ1?WhAQAxd*TeYM+*Rbk=0EXY)2*kT<c171p`K)@ zbYip_fff&`Q2i?cG#H=|umq4N-WzYDp-o$p?xj%h^TSDHAdm@2Wb6Fk3qnk@tXO{@ zrjU-6I)~THAn^3)@WqE${-0YXeTrkT56*qiL)->^D{bru66p}Jm`S8%#Lq~Fc@Q{A zq)qb(UV(I;Sb-#x)hz4>66qSThDan^$Q|$>dEy)YTfcUm1i7p&@#1qe!o<733*Wz% z3;+Gky#2p_RZaqgE*7()#0Evf8c(p%kL>8!M%YH;p<~Yi>7&T-K(NGym>i0pqFpHz z4H)KV&BgT|%nKxon%Huh;r9qu0<*K&U<XS$-D(VE-1XoOu#BJ;8o@fj;n252$f5zy zTU$BdCA0C{3-%N;>DLb7)KQ31`XMZWdeIASfGRUQmIKV4sFnXYv-5R{|2-wV{(*&q z3ggO#sTu0_$CqjW@B-n8$7xIIFm3%4r-94cG2lRzo0N15s}NF5(1CF1Qi4<+=Q^_i zmLyBDp$<hxHn+5Z+_`?)&(ZNL=NsY7^YEeE5(I(__@;oOOjTp^iKwsdxEAvZX$E31 zYe;SLWYZ5U_3Dp(quzRPpccJVB1Q?6%{TyH;sy@)yuDBP?)qk&E9yeKh&a@~!yN`w z*j0q38aAI=?wbWzQ?Wd22s?}lq!q9&@@dx-QpMB4-~pO`!ra7!(1m~PCg__G#4>3u zNVufS)?QI?_qcG`4+RP6N%xKSQK{GTrllpGjbTXzfJnPSBTl%%RZUJ#ny5JKcJg;B z4XbH8^L^wDKEKYW)x1neY2Dp{&!7E^8e*ArPBElOii*ByfkL~kx};16vt3B_<}fBl zaqAfEPAtehjDu@yJ#!BcWHMwHfEl3c<kL{Fy22R~V*o>MD+!hsT~rJ}7j5hQZN~91 zMsZ?d0uxDRD6ugpEH6G9hRQn3hpcenW|lXhUQ$1_xfDrVp}^Y0xuKd`Ry>9{Ce1bv zX$|v_<;W>;z-dbV<WqP)DN1avdG2pqT^PFx=r{%p%o#$o*;(t)a16h#sN!(c_UaFN zx<dTgQa<hiMJ<W9Ajdqp`j7bQpJv+GQN7%{8(`%dZnOF~|B41;A0NI{^!duFnowxv z0x#NYYYlw(;I@K;bB0l7zSQ%>#8OYExoYVqqTC(p)pj>0HuwfTCG>*TIoKOu)d*v_ zkdSdS93aGXg<Tm`TTqu{xWL6t2e%b!aO+%rXXb|cpdFePvFE*s*m(06h5*a;2*=~s zI70b-Q6Ldc9=!J!W?NXcMSzD-W<r!3I-~L0c&PlvAc{>uXd4?F>2S1Wl}@WlF*x4< z)i!P=8&tEOgtjn$ps5-jDbGcOq<3F@e<~{zn=9nE|6XkR_k+j(O(aAh#kx?YHP+WR zM^x^)1m(!;w?<HZyo`{<waMFPGayC<r#ObH(&wHllTf8hLbU|nf#MuQ!o&5p{Qam( zdn%`X4xLoKe{C%4_w8_S`~r498gf|H%!^(K(7dn0wrTn;!0Og(#j}?nu38DBsNP0A zv>n^Xm#m1Q;K9ztY&l!v;qSwrJj;l?CARE;@iIIMTnx{`g$w!lHFJNvM_5xr5CMfV z;H3Bd2T0xEI1xRxF-C{c9<y_rg#h56{2Q3@s2G$>y_aqzZq0`LEj%;_;_#W542Ly~ zIp2oeF(rvP0}ZcddjNZMcnRYl_(1*aHJIu{n1#aD%q-y(#jhfMrp445J#*p9tjP`4 z2H)Nt#X`D=NZA~Yb|OeJFEAiDlRan!8~L9f^RfU?^1A-}oj&<***5b{k1b|{q#-C# zRFi1WB%~{(A0addh@L2fJ_8=3S&v%jb?}S|8M5RDZ{9#bbSt?N#6ZtNY7VMFClE*I zWqf}Besy98CJ`J45w@U8>O>GSt`Efl^RRik7V`$!pZWkL5*EKMPLT~i&tX7?0+7d@ zxeKDD9f=}i@X2&|h5d=Q`-MIb)Br_+qlGK>zJimJaXU7V$-vph9Xw%>Wuar9;@gi% z19RUq2QdIEGto2dOMboPWV>p|ylNSQUoy@sOv$qlcdOVjG8+I5`ZeouNAXuUNiKXo z4ykbuo1dCVmU2gHTCzY@(b&R*&X<YVv))`{cko|dUo{B=ig2vlxl_hoSMH}QM+2)2 z0ukeOl;-C7JLT=<3HBaJ4)B#ldax51c^*kYZ3;>czB|@f!$?C8Zab6rLjVh_rmqtd z=@g<*jq?ZWZ{|=gL1Nqd0W&>vm<E^4jU<%T@QpKJo^3P{w9%AuC9FPCaW>yAvg!fK zTP2XHqyE+eDl(7QB6$bGkOW2*#eVH}<F%0<$R>Qt(*biG%9a{((?&hlOtq_SboYz= zS^lW^HX2Pe2R(i`dU$vM;%)8-o;z<I;d?s2L3h^4GzPSzDRG1-?1RgFGYtGWq$xSs zHNTBqKfM`tQDRudmo8uUn)Q%A9GK$jvKvR~yvTWR;o(~%G^a~TP-S3#Ly*8&5>J*# zgom%KTX(=lQ+~05=Tk8UYltnW84!i}SC)GockKM2mH7u)G2-A$tkpi#>MbXohQEGQ zuZUj1J7;y{j(VF^C-ON1QO;XCe~RBQ2No))hkFtpD=4Iv%2|(F{!}u{n{sLn+}h^& zi*~0ls02-IVp~;l(Yhx_r3MspZfRN?r!2w{zyvt!RGUhfg`ipq5(E;yAIKk=2icD0 z9rF&7SjccJtL+*MmJkIm64p<^i0+;BX@cP~tbs7FDOOfzhP&UXZXGSG9d*yRaA61a z0;hKNvEW~&X-;IP$c<3Uq=u3e6dEnby231$WNCf~ffEmR%2&`BPAng)pl4HbP1~Xa zkOZKQ7~OXzy*zY?=+6?Ya=vZd2VUu`#FQ`&>RSB^89;9=f#va$;LI^Thc0OKmdpzr zJCFA$zo--dc3yqVt5G&KUyc~%@Ba~C`bG^WV9@fSb4#BV<`dHvq<43C`fN56gl(|b zH`_DVRqz~t1u-)3+UvWQFm`Y9#dus@6&kiUGVBt-hU$Anuln@Bo4g11?@wA91zLmJ z=HTFfvRk9kQ2Wq2KW799AP;EJ6xXeLs>i^$?iw8YiEe>O@_jT#p@dd>YDJBeaT*>O zNp^s1|3#3R#65>#2`!y=m?e!SS+{cpaLEcoG=_POPPYhc85ACMLyGm$iP0yYi_I*z z_lHi6VIy32Lo8iO`S}Tg88F2%x$b^O#(9y^^0h?w6w*G#*xIcy2vWnwM7of#G=lOc zXJB9ea*G9X=e_`awtflsU3`Z_U!U(t&IgVqj-0}6d{SKw5=@mMyky0SSNN!e1m2WN zs{4Kg&?(7=IH@KA`bFqzQ(fiMB^;2$f5Zj<RzfXW9?Qx!2!=%Lnkbt?C~QbO9_6ZQ zbw7v}Xci)>0wS4Q<)TgZJ`P`!WbC{){W$RFQu*mcia(E?AVSP3jICXmUMK(V%dEAD zqt<rfIlyU-M&!n5AY|}<#3>qMhDe{bR|Cm7KEjoqk(PFaNQ`B7?x>?EB`PmV!17I0 z`MBv~#qvK`v;0SJ!M|N0{nt;E+eD`6@4`2KaJ5@uy2x6(lvqRm?E>uG)Pk*iS2nln zJ@P>oFmu~t<sWZlv4f9Cga_}+XKZ9>O(M;yC6Q(~a1mfCzeUdyFI-}5xY;(WZIHd0 zo3x(y!Gk|4`{KR!tpB{UD^nX6*H*y~<RY;GJCp10KmNW4f3JhTx53|`;O|KIcO?8f z68;?t|Bi%zN5a1&;op()|6h@Cckb})#*+nO7_cCbjJH`B-q^C|$jS3jnfU9{2wQTH z-8O3*V@nIDDBILt+$ND`$&)gig~GG&?~=s7Z?)dG+13)}W?0+85dOg6v4!Jb1mXqw zxr2wlE+DN7pQp)Jcq$LCUQE1thlQo>HXGu__=B{Xv|9T2UC>#Pez|Qi_MgMS{~hGv zmUQ<D_NN(pNa}E_DB`bPREh^R(1_~o=@o>ZttWoAkK#$)Wo?GPN_qta?bA_K_6=~S z_^#U<5a@Sc!@8hA@1S76bskjZpzwVGYRcaJ9@MaP`@HrU1O@~LQT3JmDBk`uK>;+X zzrLypb-e~v%S~NN(?dgTuf|>tb!}~T_q`h0d#Nf~d(|{38&oKol7D@wt#^<wmH1p+ z2P;dpRojUVT;(+Lm#yYDt2V4tUAJDzgBs#I{VN`nAgUoH`~ZGKT~$MU1MxP*&upmf a0sbDukF``csIBKYq(5p%+=+iYg#Q6<X!pGU diff --git a/typo3/sysext/form/Documentation/Images/FormCreationWizardElementsTab.png b/typo3/sysext/form/Documentation/Images/FormCreationWizardElementsTab.png deleted file mode 100644 index c3478cb504f6f91f51781d6396b02f43a5f6688f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70074 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%0A6^_YV`7ezn_l?t|1LK-loLrJjrW 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 diff --git a/typo3/sysext/form/Documentation/Images/FormCreationWizardFormTab.png b/typo3/sysext/form/Documentation/Images/FormCreationWizardFormTab.png deleted file mode 100644 index 02bcb573374073be78f922b97e6d0aabd476c59c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 70667 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%0D5+Tp$E(H+?aIcX5rf6oE_)_jiY7yV}} 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 diff --git a/typo3/sysext/form/Documentation/Images/FormCreationWizardOptionsTab.png b/typo3/sysext/form/Documentation/Images/FormCreationWizardOptionsTab.png deleted file mode 100644 index 22a3affa1a8dfd3003d3cfc365841af60b0babb2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61598 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�)j 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 diff --git a/typo3/sysext/form/Documentation/Images/FormCreationWizardShowTabs.png b/typo3/sysext/form/Documentation/Images/FormCreationWizardShowTabs.png deleted file mode 100644 index 0195c2f99f88429222a51c882eeb28bee25d01b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30793 zcmeFZbyU=E*FK7hfP{2|2%-qmQUZd~As{hyNC-HDfTR*C-604fA)&|&Au)uMAdLzL zLw86DQVK|%J?iuR-nGuV&iVay);jB)=P#f64m0=XzW2TNwXf^i6MkD$nT(i;7!MDR zOjSie2M_O58y?;X6QYywikH3v3m)FRWmScndfp?e>0Z7xh@6n*=jS;S@#yf`MQ+}_ zv83}_<^IW&PdVxcmx8F`%#&jJZ{y?V(#uj%P|_%#$6sopK=tAi@g<J^PJM9At9}^% zmp;Dg?42pQIhua`!ME+LPjjDcp@xPGN!WuZ<cQdt@$>DmfBqGe5~Qy|gnKp2=)Yc4 z{D@+^{?PsB>TpmH{xj|0zkk#7=(TruYgKRj{61<>Z0`4C>Pg<!jDlAbZVO-1FRIhc zx0+Nso7H%JMn}k9P5<-BYGcb0^~}{Fh)fAr4uc}oZ&>01@A=N8Y{f_(aU~@s0zyJU zeEgIsX`kK0Uz3wQ3#?lk&24SU%E}EJt<0$G_Lt)K@7#Hnoc9d3Vuf%MvtF+YgYS?A z+^M#D{87@3Ka#S4e0hsodjiLX*-bZxxpHLN*Y$M8>2z&M_!CaWnVP2x=${PrIy4iE zw76%~;jF)+QjUzBzVhGR<a!fjTJ46?%ZF!Mh-pvPtX?RTygyOvwKi5lE8h}9n=D|F zaqWR^a&DXN{<txX+DQ4mGQZ!Rdl>V&hnERw<Ga(PD&g^;=Y|%2dMUpC;mN7^`1oDZ z0z)C1CHOy1wA_WMxM$e+9zaz(jf%O>DEKmnJF^tDV&Y$_;=b+jWtH3$Y|V6gv~2oC zrf0avO7BK0H;)8HE73)MGpLEb;JY$VXj0)gT;XKW?%tcNI6eP7moWCmfOd*?TZ|%J zrR@vc7jRQ4JDoU3${uf#q&6G0TXC=Njq~{XU6-j3Pln6vq!Pa@jG#6e1Fg~z^v!C$ zHx?J|)4kyUSt%(k#odfjek{>m(XahE;;@<5l`N2REknZ9YM{Vqz)Ays2^SzTnnNeu zi|o2HX37}RA+35vrbbm11!vgY4|>j^;msW7)NE5Kupp1O6f9-S=dM5e&?<I+f>+q( z_s{PkWXyC|^hy1{x!bN9W7^p@m0e5`2eJIec3<WcrL@|vSJ-?y)L(itPS7tfW#`Iz zu3EqFfzil-mZbi8fl=As)&jaIXNF0{9?wtKb2SCU_Z4*pjx;;=4WE6{5}$GT+bTgO zM#kY>Rq8Au+X5y{q7u5~x6`k~EO>oHQ#23T<>lprbx7WOewTC}lbDT{vc*bVdyJ6J z37(S6*H&upj+H%HbhpjrM)6$GeRC_|XfMy>-SL8Ub8@sngP65_-^mlZZnOPg0!wI9 z>zQp_Su-%mbaKxN$@*k2Z|;sy^>C;sUbY-J{d<XemHX)2`AOvHM<G_MT8MoYhs*Nh z_4(#>8ShNi`?+XSIwkQS5@juGHtCd<3yrJZR{2JyZ2bEATD_H|Q3y>pC=qNJ`Keu( zBCxHm1nDR8wB^XIJ(S*fZQ0zERqW@7xua)ZN|R-|UB*>MkG03u360j)$W|VfeUEs4 z`smPXZ@%@3K%3@-=e~Js(~ca0d-2ba0JG(vW@xdKBVO+A<;zD##-g|@vF+N9daXn9 zXsp8NWz}GpzTSZT;eg5gVPm>1g#t!>^JR~RW|$uySX%R%;He+)k=z4xEeOBawg;~f zCg;*q`!JK_jFW}NwchbwlRO#6nDaZEv0E!OKV!&-CrZNRd+%F_$=r6b&YbZ-9`PQU zjeD_U(YlVV;qPRUT=r|<jxLmMHE!0wpjh%?;Y-IeF0>d#+ye+$P5NM-ytRtuaMiaQ zxd>`p3SgVnFVsUy<YGidXgMu2JX?&n#H1Gr_VY6RkwfpYTE?&3;O!pJ$%*1sO&*?S zatLgDkdHX-${FgS=TDQ|8J(_^BFA?(kv=*MD3lPhIvl$zC16@b;n!_-PuSYv7l-PK zqVZ~B#T%`Sp~AXW<B&^BTMY&^c`QfA?MDJ{8?$eRV`6led5IC4_x>c$4LO!lq<Vso zaYB)#Nim}shl*#*$iOqcv@+zzVwirR@wNTOl%WQX+cEvSJcEWcHmAeBad=f{x77P8 zf6A|xJQkmwB##)E%KiMs%Z~Id;{4)TL!^Stkt{-{;r-{$<Zm8Tg8|yD+&QSzh6x4l zy-Y)oP3WyIAM)fHu3W*udT3RI3-GFa_exQIegAGl&I-!oBm)g6Ysr9*VcG+IK4;Cc zN1q2py{<$Av*A`T`mi!OryH+CGistZZy+?ib36;VksHkB)m_Xcwb|MhSwp|^doU?S z*1Q*v&$+qj=#VW~x(3hB+M#%fQeg6vVZcNK$<3qrgXN!f7i7|Yty9G*i~EoIum>C- zzaQ;Y>6I^tWtDnU+<zA9uxTi|uFr|f9S=Ag5GEOQ2QB+Z@m2SpS2sH`Bbo|p334vG z9S<R0ZaUsb6aRp6GGq5DMoeBm&b-?te(+;7IG1n*Vb4hQwbVO-T2>PCelkumk1u!y z+a0jaEPD@oJn?48MmxT`GqUe^qEO;+(r)W6uPMelY`Md?-oKKAiLG{czhC2^o1KTJ zbDul590`FGv^YzNrV|J#@yE7yCS9q#KUoh~^up41wgWb1#vJV_E8~Q<D!l&1=edAI z(L<73qZLlBp3RjX{*=x$QL@Fx;omNZk`gEQ?nQpXG|jX`9`DY^s*So{cB3svR!-I2 ze?mF6$|oX1qfu+s60u(YyXfLQ?t==a(Vl{~Xy!KY=eT%oK9gV(H`iq`)iYXopMD*= zG=gy$fA29peEs3}V;yoPk@o6#vn01}6U_Kk<|juNPK^gC7rfVaDQ3@~8vkmE;eap{ zKbo9x9l~GIW$N+8CCe~DM-i!6@f0fDLta+(zsX}>E3rC1)ffm*5O@}Y#1b<b6dDH? z@2m_K-_AN&Zc^Ji7XI%`#Ap9oQA{{^+W$u5WqW%&)SZucNms5rPuBBgoJfE=xNvTC zp((Ks#j4)mMHlR)qzGRYM-#qIOjS5yy{o3}ZKnq(PNW*YTXen`bv>k*=D)v5$IWCX zoSMrGkO1I@XM{B#D+5Va44O?CE<zYIHa4C(ae}`iw#F6F+xCBt29|?@f*^5B7kA9= z0ZQRDqzFi2hV%5>S;i8QpG%1vtNgLG)dd9_MXb#>SY)<^%I>+vg-d*4RF&_u*OfAO z_SjDTML;UVf&XVL6#QQ=>3pATnC!cE9`|tU&6TP<vd3Gp8JeeXA4bGpPxiOS2T_#u z{JD#AL_#G0|4X<U{eQO;L*>i8Im)pNLQM}JZOpVZ?0tJVXb~{3A9Z}N)ve6tx1D)> zumDm1@YlFkQc2zM(Qk`@Lk~bhX)23>gyb^6SIdg)_tmOP9EVHU+1cTS?EL!m%UyNk z)=#LXA3uI1XO-lv=tmpeZ;fKy+nnEW|C$hc)$WV$?sO<6C8a%8FbO@~XKO%uyKx8M z;W05v4hKI+?^|12Z-@#CzEcRN!Z-}Bjn{bHxbZkIlBfEb-@d!O{URWmAJ?=pq%C|m z6rWwvkWRlWa5CBkz@a@_TU*;oz1nS_N55dG*rFlX!hg6gzw8vjsF>5p5Nk=@b1ton zJ40!*0Wu?YzP`0?avQY1?jvfdM86ilz9u24g!`GJoU%b7w>tAoKRV|lo|TnV%(?3k z#juD>k$XDu9M{cKkW^GuA!k`7J(l%Ci3R7Uuj@yP1y0jGbH9C?f``NGy?ZR1tWuL& zGXEh<Oj*F;jzOMgI^eOW2^tm_--BJ(v~{03y*8=Fl&gRcjmm7pb&KiEz1f9?HpZ$S z7?s*=g!8ZV>DK|^lkwYEP*UpTt9QoM3p>3Qw8wJ3D6z(70!sQZ{gE|7yAKC!J!hK3 zKfSn`qtSKc=}R%E)T?%yhx<E^c9!po8())FWtQ*!wKhIl@ArFXXlTAGSsdBl-WQzz z4bIwWtg0k4b8>aKY^6W{nWP8OvDRxnqoE-Hc5~ddxvEOS`$M{vS8FWW`asw8S=RPP z1xy6to;t<mbv|3)AeE>)D#*Y0SfTsWp^-Fm`RoqKRTc@CSCD(LD1`hkpB=10$^24Z z-o3B68}28(Ng9ri9t9GRnZwB?YL!^E%>CTm^%kL3smP<=4fvFH?E%2;cOqDZ-7bNK z)zM0Fc%<iib;q6WiCxtmn?KI7K04U@{cHV~ExH10=jS@2X4!*M6@GS0zc8FZoa|iF zL+?^fRlJ|UL(_R@s4so6xNeqlOit{%I#i-!zCKaMrIDH_eY{E?95CBT=4FxYws=EG zNT|@H@@O_z7UEhbS0z3!E)K%7$h?ke>#lHOI8}LOky(voFX|a}=zcKcZLc4ELX+OH z7ao$=cz#DA(9eG*$1<R=V{$t+(g2B{KYtz~^PN^8bp&AvEYW#H&4<^dV)dDp45ec9 z)+ZhW3ho2>!c{o-9rv#)oz0bn#!6ocjIM?mfBW<Tx8CmWqm@#gt5wiJ%=Ig;mm{@l zlKIW#wDAcEBN<yRJjm@Ul)R=YhNdIj^mo{%sn?O={))QT%0)vy6NfOV^@eW@JJyO} z7I$usV{eOPQ-0C#YvPglL{7NYEEBpOvi8y8Z>=;5OI0Ri9Ohf@=bk@*`G|gR#G%;l zM<bzxs4R3~`#UR?=d3lLE?zMxiW1y3_$<FjuC*B;Nkm6U={naQ4+nRUdPyTy=e1fA zpUJ>`682eWHgA^Z<m4!}b0|i%Bb;9d-+lP|d*$7!N59q%e|#k82o$K>)`9b1Jvv^5 zqN~F7vs8-lvbq3V_J=#m!Njy`NjyoUKNfp)?l&C%gbEl&5m4zo9x=w=2yv@C(ER>1 zCG67G%f3PPpt8bss}Oe@DZkVBRG8<MHXI05r8&HjJh<3&Xrbv8x6kem%Le~Le$%S+ z%S3928dP#^ia{!d5K|BgHPA}3(0qR7`F#WqT>p8Xy%@A%yxQ6Lp4I+`?~xkunrqGV zvPZiYW|B2DRiXJ?!J_R{-zStqD=9JscjkhsaL6gbv#)sKhheQX_e66w8*cDb2_hs| zn;5^WHyp>D%5u$q+xJ>M&zR?Nk^5p#*2hrtoEx`URc>`8Tx{XF6uvl^QhvKo<=RN# zYRhIG%QZJhrSsoDzbw`4-O<dD4o{WCGOubpZEjZJJY%Gk@Iw@iW|24SCL<o-19*n) z-#MCS{X!HE-Ef4U@T=>03f^&gAZd0Z1?u1G%5<tE1}3eFzI`ftd_cV?w+XGbPLb)g zc`XKt`rm6(cb{uFE$&(+@hLGUzT1>Fwt6hLmwEk>zT-{oAPND;Zd$4vCP!Du#Cl>6 zQDGCWi(rcvS|6+4f+wrs9Di+GW~+gqeZ|GGK14$Ibjvc{ZH}IO(tDN+an<EYj4@_o zL*Y(tx=Dg_!rtCOrlO#4i_H@rrQzl7y99ZB>6dJ3ExTr@#>q0{@kqz*7u=kfQRhD2 zgG^<0N7={fPUV+XEZRVOHrwZpnw%ZCenASaq={lyRO4>0?Uk;glYaPhwrgY6myfp= zx^obBZVP*}ckkkbl@RfL2VD3(H=j_HB)BKm!d<BOU^<j7J2HHO;x3}W->;kvp;p_B z45R#HEb2K~<$B@E$=(HoEH~jzEP}1%l`gIMacW%SwCYt`k%y~q)BJifF5OBIgwoS{ zt5DmP#b~7`>y*{=hU}XHMy2@Q5qUPvRx^;3icKol3M(f{X-PloLd^Eq&CzXNVPSfa zB%js(yD^YZ$mmO^tc7%~oS^rQsrU=ZR>A|d!=?z56hA4iwSod}L`T)~jRDAk)Nh0x zhXniA0S1b}KY+AwHKm-+xc+FlKR-|>N%W~(iuiqVIQ2P0UY<(eeq3E$!|+WKugq;f z$Qu@P#O`j)wh8T8dCQGkDpYV5UQlMfQTpASvbi1V<B5Lm5V4hfvlln9O2Y!5a@B%O zmx{*T))tZ#^_1CZg(VB$P*G_gDzQ#_P`&8q_xq<}XN#`04f1X>uKD-HJG)H7cS83l zMabHQn{i3wd9I9<`{M0wqj{yft(136sXFoOpGDH~=^RNZR&WGa)OZ$-#R#fwNv+pf zqkToLv}7-e{tzoSj2DO(G;v}!Qj(-Je1V7k?vTo=f#p_UM}NI!Qre!6q+NQ(r9~lc zZ~wM5iDP|ny;E0Xx|E?_FaY|-+rwAlH-Y0ALVn!5Jan4>?cSAsd@9@9pII@;+1c<G z7b)e)?^twlX(?~FlJudB!+}*?;UycPv3GpN)~`x`YICd5+Irr_TAy1TEY8%a5h8l! zpA^_oidmrvK3ML_ee>cj{mH)I%sWwXdDNAsrh}T-v{{(&;D}>+uaKPxB_bzp+e3X} zSNPcKbDF?>Tc(=z5ue$68ZHL6$X|wJLQjp*N`<fPDkv&ug_hr+JW4V!zj;Ba_@gw+ z8p~g+&!?~9u{uN<%`G27svLU`mtenmJ%Cbr{gG#y8pq+{MBf{Yk85rM@#jP=w#A8v zJr$nDi>1AvzD0RTcYk}yVW8liDl&i3Y>}NKa7f1P3rkRQfwaQ_Z#p{A$_mcg9D`As zf%MH5aIo=fG9b2HD9>tfxW-FhZU=I1C$mxF)|Dpf_O2IK7h+hYdOl9O0KxTyc&ILj zHzwHeER)Dv=nNLU7oR|g$?XLN2ZulEH+4I&tbI<8Mp%Z|yp|z2%0Gq%XvD78O9b?; zau((G+N8GxcYrM#`8+-ua2)-!0@{qzBy{e}{bwv~PBtsu)eYF+<~|_!)?4fIfRl71 zlHfwa@-HG*zd>1#{&jj=o!2Ml12vz705o~q8qI7?tZ<O>L$E%_@$Sper&Er^=}fj! zaqS~GJXhm&f9c}a<sO1KQrE88$0ttpXv}FXTD+U~-P_DU&jh}$=U08D^J)<4!$<qT z%xy&tg|MO^B@W4c$nu*D-LYqG;Xk&y!*YX;f>{ag*0G@XEd$hMJZ+FloSr@5d*;pH z0gl0H<n!1eR(X6eV-t-12LPxJL+{x4i9%$yEfAwhY@vA^J`w^{XKp-bktho|_9s>N z!LTiQ%~r;D*9CG?im1>n+RL}`RH-g#K~d|mY6&U~C^YUvd96>315egZAL4~C|M0f+ zRohP=UdS4oP1O1*3NDpqmkPF86?}Ac6^@eb#2=D7E^p2nZ2?x}!fbe>XN-Sn^jlFH zdL?F&YDnl??eP~DGj3BX6d}-W{-smp{X3ir)+)*TtioZrR$+958j9E)uIy#XZd@^< zB;OL%>_Qt9^K<M0-l5>r92fd_;m2%SEI^hsj7|dujFK3C?J$P4l>UOURiiWpJoRM$ zX#OYyPrwLKUwd4W+!jR~Y2s5V02Vjj2ue_X-Icm?4$ZFg&Kf&od86}tr_^U^Dk_(6 zp_GhVHH$$rlgX)puf7p0Zmpo^mC}ud_j^(@ry>`p8Bzp|JL-Bog{be~8P`1&eIrz& zEloJ}S$witB5dNWj~H@FLGGBN9Owe|dOb6hs4NCzfWy&8Z$4cj(3q1GdHDHcuhANB z%<EM#E4-HSXpX1XYnE@saLgK*qo2udLGBcsiirCfsz+-l=tal85Ru9(Wb$g5Mb@9? zdoD!E<+w@z-((kmOgvg&vLhrTm~mJc6u#T}D$9zdFZ^bng+w|}`YEHxdiyQGQ?H+f zaWm#2IyY&AUL|#QEuh+kPO2DCix*~VYRjZ#HzTgND!(elIB?{hh&qCNj9FXS6eCF6 zoW|F3Oe`dNmX@Mvb4AWV|2ebRK)x<v6RP}ry0&^BDoIP1vOP2L0w-q`;GRyoXO+dD zUy73pjq>291-*KOE73_7BgO_!PFM<h!;?BP&~I?wqRHVkneUw!jA<{4e9LQc=ADx1 z`DUZaZ^ib;i%SIEr_w8sHMnp&o2O9Zx|Zjt$io63aouT2gngFGB)1m2Dv5=7a*2d? zltq<`W$0PfW+kD)S8w8|TrCyH8XeKHOx(oET%s$yk64rmZ>0S=pDSG~=q(%KIMf+H zA9jy~RoV1oDW*8DAcUcDdI51onXv!Fn~wH&mHCU$ZOV|_F58IJa}*Sh>8$H?^v}Pc zoA+a{2)Xz9`%Lyzw=qg|kWkGPLVE{=U%(HP&8J`9(^d^*=AQF(RSqs?9(q4zSr}Mh zV{lIWnrj66Cd7E@sh6^G)b{7}zMSZ6qD17^>Mz`egEthNQ3!0@^P6prR!`y)g+lq7 zp_(?`e|ki3nCVdQWoY7uy16rttF-Vw3SH8x{;9q2=E_R@`ecK^xnryERh|Oo$a7{L z(3+T(+do-*lO~;8Xk5vGr4;Rc0(E2st()p(!buxi@u)seuJsL*Pm~EpqU4(UVjPO0 zrOY)^wCEbc2ou{_M4!Hsm$cB(muSupRR{-76G@GB?~fHl3Kz4N5hI)fEIGmLN9g-O zo{>%As-M?H$LHF5PuxbB610hYZW7szJEdg=6@wLhmW&Kwf3e%65K=~)6-qm4i5I%Y zF*SuF`&`&gLG`Z;5sfPcdt1<0LKM)v4=oLs>OXRqz7>Ehc~m$uKy$GhVY4nOP_v(W z%f`R14JAsBbSM{2k&3*a@1%X8j7@M?J{wX!cP)Hyou2<zQX!*W*$t7<N9orL+hmeN zty~C7M_)R&1m;-d>uScWl_6J7+}z@FEr5h;^a9T$nqqb<UCK)^XR_9Z`r`<6sp8IK zZ-;fG3^9dPiQTTrS}f|8`h`YK1f$ip#6yK9;%SXYbE`@R>_E+Bz<N(bC*JL7eG)-W zyyuQMX0;RxEvkc_1ch0LeL2YeMwY^Y_t_AmS>hdtrMQq{Y*0A-aLfsNQT_R+_zR&! zhE*=p&-qMv)~z&ER185tX%45JXAU{tsenAv`n<Wiya{L_(QYf7C6N%r8BNI$J7$9& zIK3!`ZVkJ%u0badA#bh|g;-hmGV=;yl3>2p19Hx%ix4`@5-xW(YuQ_z8(a`5AeBEO zH~rBSnjz#ym`%S;HB)Eo0DGnhdSJ}#r-b@f4YQO>GDo}C^HFyOQ9F5jTm>WVS(!?f zLC@LHH1Zh(1%{hmrg4-auZmIV@?|0}V^&G`w|e;rw^07tDVhDXC!U>p)m1q12zPZ7 z5s{~#b~s`sF)_8DQiO7}GG!RfLV@PWc;*3U0Qy62>hL=%O3=SFr|&Y{<yG~PVebOD z3sLkr_(c|li%yY`#0CArEmDd-@(r7J$ZB_;tNB0_5InFGvcz`2{;*%=VTS3;|9UMB zWzcy%{V)j;c^qhsti(=;v~ffVm|zU$WBWPsfdp)=?t?1O013y@bp+)^n*)nv9zd+b za-GFIz*jo;Qur?MC883=oj5|}Oamvu&#lZAg{vw-X6ce1o%>vfxssBSE$CWg1L|EQ zu(}r8e@@l)6ZzvJ46?YC$qIk6M!!&Z#ciHi(g!G;=F6{M;ABP)p_wH|wE{K+&Q8mv zudgL8c)hjxu1NqARNeha855EoGq>|xF4Gv~;h52_0=&kwCxJ{a`aesbQn=%LI&4F4 zi6v|h#b<sGYxTmh%=Yt-o6@$*4SC*J4D!A=K^RFf;!40Fv>vWvj~08_=bzsBWrT4k zMl7_)Tf+`_UP-KY{K(Qf^Qzt1?XG=&Iupf42P~a|3B~~Kq+CQP(sey}ncwWQLWGn# z5FW;PnyjSau*@rF??qbAu5h8H4#i}A2Ao$aPWhaQUTzIodC{%0f{Lbh+cd#^QwkkG zILtJUM>vgEkexl-T5K-})K~d$$AGKdXgY>!liprffBQ&yOM=FH9WR~oKBh49lOe|2 z?^LJf=TuQZD-~zYuWe9tr#<(ZwAvl}G+JOhA)U*@@(E6Ef{B_sXP+?D<IOE&OdFhu zjkM6j7eo}B>^ZS~UVeDcjSxN^+UG$#MAmaJ^ajHGLq&#o$PoV(hK%TniA99IeAJyz zl<0A$H#`>zCrd!FSN8_%^Q9UCfs^!IYs^0j8NiY2Yx?$!Ys<5cG;aqXVmiCM_EgjZ z9?H5(05ou%39+seXNt6QLTcYmQ4Q06%sdKw0y#+WilonG?q-NXdT*x+E@fkOkAJsB z(!1&;$_Drwu1<dpP3kuBVDro@SU~9wyIFm<vktG>?aR|D-X4KUQWLvPRUc+s<~rL7 zr8z8m^Bd1Rf-w^!J?E>Yjb~Z~@^ee_<`XJ4c6G}YlxQ3E?03`PU2(ZhLwI<nxh!w! z-E%gdn0c1`4!N_EEqE>8=hP*_8&~xYSBe|NoW~e`a<Dz0`L_r2=<&JlpU#{)1L&K2 zMUBi@l!;F11lGE`K<Mk4;Q5czJ@&}KqUfa)Iphy_n}SJ_4u>~a4~n6qmO!iDy476k zw)IUz#ArplF`_=GV7hA4DRXEZ8Zl2cyG!SuGSkqMdvDAjv8eDRVyT%xLy{}+<1#Zd zeUlSzz5CW>MB2$OxePM6tNmRo1z82*rjH-zNwhPieV!7$s`Y6(c*4@M|EiQWzg6__ zUWODJ*WXu^t~^W2Swtc3IONj!^z>%kfOC_6gNqN!@mh%<#U8(8KhfD}9i6&M=Z6c7 zQ!_W&n`6&iZ}Oz9_VuhMeT<r{dnBHALH#+e!(fr$)@N~#m_;XjK6<aFpg3-uo3;zk zLZAZ1BmCn1%8BPW9<8Kxwj_7bmnkYK&0I!RKFQ@oSBPdXI4&@JXOr>0EUI{vY36hG zKDs-#5(z-#7GjacP3&fSjn}$Wa(}*V*EtcJc8X8DZkuuk4D9pAWO04vs$t`xik!ap zC0QVIkzOgHX)f26#HS&`*E+;odj^P{`OiEm%<u82rh1zE9B!R`tZ5=jj06n}I4nfQ zGw+vJiW-T7TI9S#^!#zohS8W7kMjju_V31++5kD}_tVkh6Cn4e<=p2TBn`Z}Lv>N@ zm0y~a7r&Soo$JxgpoIkS&(E2%4f753d8l{(;o|T4C8_2N$F4^|r>CTRV)<pw@cge9 zyLjb9a>)kX8`}s{Lft$~+A~U$iYHJLtVmPO`I_R*xencTcWySzy3RCj&Ucw4WEezj zwl#D6Hwp4M;~AL0cj>?#o|um|(Pe5Z!tlS&ua?<#>|Vr8ViTYmiMNJoyoz^7>`h|| z{iio^cflthYTl?uaRVCN{(Z#z4qGf;i-TzkI$t3nk!%OZ-xMAjyk5L!6R{+%iS09J zAY|TzEQiQPu0nMtEW7k2oq|g^crBzvF^Fb8?*pOGqGeIbnB$ea%}Xl1x;^nisg`ki zbpqqF`c4jaE5_eg`QlKFqBf3zw>qFN$v=m@_n))W6t@y<^h`8|9C6pR;~b|t!JcAd zssC0y(Rp&t^$0N?noC@wChflwHr~Sug_Gp#|BeedAc-Jfd)gQdW$(yeMr0+C+^|jr zxvjehw`+q7@W1cqQFc)LJPVmZmGnS<yFpztR1@EIhpX0gmVoN9CjUSM$Mp-!G0nri z$47@iEhcfdRSSIMOKa_Lc(J&rf_H?<P>-FntbcLUF2+<K(la*B>QdjrMBSsynSkxO z>}KwKk>_{$S#0zeua|>lQPy*-XemtMI*uRk7D<?I=ytk&@?!`Ybigl2d7rOzB<BJP zb2e*FWe~zYr<2E^s6a&Vu(#8BjkmRc2@yg`h%J2B6ykk71?lWUn0k29x~NO{^=$Ku z>Mg-Qz`!N1fe&3&+qB^Kw05aR<QRIcja|F*0q+IpiZjuf8`nFPd)e_~!utI0j`+5q zv}b{(Rd^A3V5J#sGdl=GbauLRXEM9CkqW65#i_)oX8qypJZQnQ{1v@F6+mwhoEiRz z<23gNR1z~JyvP#IbM*9~U#gH;o@)A378Kqx_s5~sE>kNkoi*8Lf(U|rV|sep4F!n@ z3xs70!i1B`ZVI%U5V|h2#1nXDpQOy&1f2>F3Yxkx<KCsBZTLw8S)rnd78?@e<u656 zpY~%p9UMY@Upr)549&hjRE`X#`1NKgaO2T!&Y3m-3tp~_gmo)GD95yNA74l9J>>DB zPu;vOLC}cr^ZTdM0R~e%j(3)Qg5`<YO@W=Qtv+5Bbse^fBWy6Ca2N|Qd)SOxLdTG4 zl2rFhjQ{lIQvbul!&4_uN}F*WCvm1HP&$3cGMTBRyCyv3)Xg#2>Yyefm`HBrxj!9c z9!5PI9`DJ&-q6pOJw9$nCgykzanIJ&R-Y>alFlVg&LQGExtDp1GWw;Tnh@T&hXtO^ z7wG8kKNaqmF<Fre8hS+BtnGM)-hD=WzuqaBV#dTpnqB5ejyS0VS>dG@KAMG07X-e1 z)m!id=hgS#oY3V7k{M?SlJ^mi2F7cB`kdcwFZCt!8YOtWj-xr#Mm<AaHHNw;YTon! ziM6_xc=4V_mSLfQGeIe5U9bn}U%-fLyEA3d!$%OC{E~Ys*svH!Q5-ybX5$PX&2lDz z>1&wf!{5KOUW)PY@GJuj?q;1K?UO3z6hV0nh~Es&sR#I)#XyaZf=|dGUq?|!?YTrF z$lIDg=ZhNxH7>OKutV~moGA%zHDh&;=(+qsW)JbGYWDiKFtz47dbk=32^Vl;^{z-c z6S>?Un-OTMynkhS3b{}J!%Nvt*cnQ`nKPz&TA4+LCE3f0^Wov);4peAWZeoXT}zG6 z4_&yWpj|93u^g(GUbh(E)lCzb%(?O-fh)bk06(XPn4X`PI8G~%<BvG`^s0We8K4d< z#=&6M{mz{`S#rT&GOkB<|I@dwr{@G@A*4?HRdbu~rIjoLApvUoa!;1LrKRO50@8Ls z&KH-svu?=Xi}Uk4gMS6~iBZ<yx88Rz;hMpOBi4K+|KC&P^;RqcDOLi_HsC80XevPN zgZ6KFVWAS_hm)s?rv{+`$8k_f(M%u`Jep535SgVle*|jL{QG#DK>hFd|ASsc(J#Zp ziu%5ki!;9kc}tDxAO2o%5V!AnrTXj=gi3UDbnxl@-@m&M#5l<kMDiN<rKjW-8A6(9 zPs!Z8Di9*tVps*t-s3+zd-m)(V3B~r9XEUYN8jc?<%E_AIP!({FpDDbF%glG4!~^{ znO5h@%U8(B%R3oiye(HD0pi4*l>xq002@KyH!*3TJPrcU46(7Xy`<|)MoL<sU&!=H z%44}7rSeV+p9ZI`FtM>^f{FvBE1Ay(RMPT+d;cV&7Uq{(?<Dr~b@LK+!lFU1l_;;O zqbdRW6mdy@>5qR3olhHgGo(t_?LqTL-vh~IpsRnj!6NCt2qjA8_-|KH10qRP4=QP6 z_7yZ6{6?j@r7qjlK9Ct5^9@U^!l^D++@JK{m{FXg34WaR2F{0vhnTtL=`MV*wE*J+ zh$g>xR=lR0PP^_086RB=pOfYO6G0o-$_tGPB0!)MQy?o9^;|VnVMrwLxAm!5W@_NK zsF!~9a~0+1H2$3HwAc1x?**l(+lP{tP58|M9!3goKc*Vt*Z~2^iizomut<CV0J;J4 z&pe%tCG>{C&pzoKmAw4;catdM#Q8TW%jyDkoRwi?JKsm?r!6aovZVtafBCsK{=0J0 zU$<qJ2OUEFYjqf~NqT2A$l-5KmiUi5b0~_r>fn1!fXi#_?pT$pgy?c#UM%k?X`fAA zv-jojtiqo_(YilYB?KHsa~W6%$(Y4%rAcTTN&Go~1!}tt1HOS!++}uy+P-k>g)nyj z=2788knDjE@Slwqe;OFbr}~uWE-4YwQ0d(+VB0TnXruPp*aF5t-v?QBZK&iFJ7t4# z?nTw)>3mShhh77>UtGJ{33wcY;?v`L#yj8zfPgRtg{b|aCc#u2Y<p~2Sc;evlV@Vd z8Os(z&KeMXLGkC2A!iUg@3+5w+uVk*w*uTP4^w3WjzHXHeomg}6^@v!@gi4aJ$Ei< zmy%D|4b)fd93}QZ3;(@~jxq?8o+mZ8*0~6Jff#BA8k#XlUfxwW=xjmPp9!EqUzXsJ z3t%MwYhMVvs-GSgC+IfU4svc-F$hATU1G?76(1q&E+};4iOdkq_}{6896}VV2+p!d zShte4bEKJ=eiW-JGOo(+<ZWsF@Zkdx+R)CZ6DZweXNWl&smN4n`x)tqG!w<Igv!{N z89~R#!@n2VmjprV1n-<lJhch}Aza@$YfjQUy8ZBS=^D;VF0FG1dMG_^$LfIN`l=lY zHL&>Ja;0-W*xj)GoO-h@srQcoh+@HIb3DRj7_tkFuMrm3aeU&?G&F}i5xS;N;~<wf z809pKBFgs_^wiVS1I0T&idD)}<%yU*R_;bij;65P7hW-9ON0}A=?iic>fW&}L;!U) zNP}83d$}N{LQ>Jll&Rar5&B__O?41+K)5e)>Da36kvi&ayyb*uJXDv6@|LtRIa`(6 z9~W%1^qUF8jFc3j`yq82DT3?}^wnyOMF?CUaOo0(W>hy$@_TpZ$=9T46HHY-GYd<U z3c%Y7V#|g*5JodIGoWA{Lu!JrziEjOu<WgU?Bw%i9n|TsrukN5C`um?Nt0wdFhT>> zWtSbz_u`~X!prTAFi}P?RQ|di!$^egVxK|<(M%QL77@|&S<-7ml~9HdD${hWp; zQ>pUrVWAC+q!$2ZYxZrQ@SEs?{@a5xMp}I+QLn#_#2{~4{&&c(vdY%}x7xT~{+ip| zyP=9@@GDeJ!Ja^s7=Ly1)JB!$D!3jz7YdA7Mv4#@UIO2~2<_$V+qXe0#+eWtYhm9& zw;(m%`AY@HLj;N&VNCOG{;i1{*G#RkO>>F&MJw-O|0@1;x5TBTO>6Uev*$bc8}#`M zxMr&~Qbj1c^hAEmWB&NuC}u7fl`j+1^0c>nt|&0By=e<l{154kZ3p#I!ySQ!gOe`} zuB(28hQ7932O*J){*MLRJlLVYSOx6OJK%uhKX=Wp3l%)U-Le6To0%W_VubnD6!o_R zs_oSxr&AGVeS|Tl%Eh?{Wo>0;T<IJwkwnfM9vKNDCBRx{0D?fXIKwVMn@-1P%o)+U zxk~8`A7=sL?Q~P{4)wjPKPJy0?~`@~`(Omke@0;eY{v+kI0%r9?jM_)g06I3DFClr zv2I?>n1Qr(kue4duyV#@7@T83Z$yWM5vj`+d=_-Zes!21WdsQT2zWXsCZx7>xn}z> zDCcLw0lkXFP_eO10v-WwgC{xA#E}FIcK{S$oG$}F1gPArVZ~vc7xnox2|)e6c7H<J z_XdaSZ59$E=fNVJ9eIwr@vjGz-8}fJKH2@i%gL9uPs;q--LNKdb##O)au)y@_&6vj zzrj=HC`CX0G~e+o4=e)jK7#!Xw$;3y#WQ{stdb$#=hI#K;^HXUT|rliHWB?weihW4 zt1|EjRBkM^v==SR_`#jQz`zie2wwb5DX+5~ty#zWE5#5s(>K5xUAaiS@+DpBrKm%Q zyPSOdQA$b*>{~=8?EB_(HD9F!ATBu1S{@=@|AoK7bQIzkS}edB$=!K@c}=41PLNa) z1dv*7VcQ3a%&ycEH?~asAMGuGY7B(6h2#CNxg8+M0z-J+racZ!C&U^e&e-Rimg!(* z@}yA(w-ZCtjj^^U#vIegf`{2`Por6`ZGxDxI@u5ap5sJyR!cv)$zaq0%9Crt@6<4> zi2dzUk$V(F7xkU|h;HZWT+A(lQ{D8Kr~9|k<mrR=%YT7%u4X!ue!7^G@n^xX19)6P z#UM)ZUR|f|IQBp_;&g(NGiG1Xu9calTam5)gkC_!<;|@Zi5bYW=H_My8<PZ0F2}oL zoTa`8f582iD-jMTQOwAhKB(mQ>*DLa`hNvI{|7Ys|L&KvEwRAGJOl&+>F9d8SfvX9 zXUJZ2y?tkTF8~UIv4Mc;V0!6voW&8`_Ll&(F$&uNy3%#-f42-Y@Wo4)9!g@?I3y+C zLvTZDzU3~rT?K?3KrFX!pO%*Xpobs|vah+S&%ix8Pe+E1iH_cJmmF_@t)4vbaD`7R zuSo_BLgZ)9{&eTTeT`h0*v*?aJB!Pa0{`rnGoO1<|1KP^JNO$Le+-uY?3vIhy!o3z zaQ@5x;jQYOCCJ^4_HJ3WYgR$>KUM$r8sA(?5Ej*L7k!6I#~=NK`ICiDFUs_znc!js zEVa--W9M?lEQ6DDdwnth$SpT@L&G6J{m<AfPA(KMb)1v(KLE&@cPn+<s<i)UcOwT5 z5QeF{yhdcoJh1Iw$n;w6$<k?w@b||xKoivt2GC!8|23%<lExW`FeorAf!x^<NAHkU zwhZO7aR1xOP;$0`VuND2z0Gr~)yW_K^NBAIkq-W^)sv9@@3MA^aR{l(dt;xBxzx7f zD;H<3($4bG*F1}cUk(G(KeoC7Cwm|V*ZOL_z$@r+Qru~5_d{c#U3dCRY0IYJtp$1k za~6+1w<iaCgk&s+r3z<a4Lxq%`V>Za0pPnXXdA$a0x3q@veUtp>AT&l3=lRvDk=?} zfY1+?9Ku`+(3;i|J;;HAYY)DC0%zi{Wvs)8$0s<tJYBz_1`AA*ujyX$4U_P%y`L!q zcrrRNQeIU*-*gXfs?@_(!^m%+YuuM{cz7jRCB;Kg8jzf9jc2}^d2_yn6i3wf@>);5 z-$w$nwTU{kq%*jNSU%VEDcXpuR#1i4m#zF>Ul1^K0qjB568USjcQ%?sQO_ize(%jo z;XlFJJtK^+2W0&gT4quv8@>RLm-pOSqoNpvZzyMHXSYT%i}Q+zz=QP^RH`R_P^vN> zdaJ#&f(=Yv#OFWQUrCqp`~-TBoZLy9*SD6w4%PcWTVqmjD??=IXQ2sbCMpDpl7Lk} zlEQUHnKFLh(1`<6jG+er5NH*WfieP%B%uYaX#%q*8{6?xUM3yCsW;SNz+`J*Uv~l4 z-*^`9E@_GFslNx%^j?kUuC(js9Ho3{{q9U;lk=5Cfn!SH1Rm^=>#W*yUZb*y9xKuB zY*r$?zhedq)Xz0JRC+2ftSpE0XuS~QQ~&nqyX+wif?PmZxZe2P4Ue&>r%5Cey{;<S zr02Tb?fQ}-HR#Mi*^$ew8$!mKDrHb4tC<m$+Hmky9mSW#qwlV)^d~2}n%ZJ}bX6E~ z#lYwxH-5A`OYX=f<8GhtHNhxuDYrP)hy&m=nL-aP^_RK5RwL@GvFrAl@Mmtc{gQEL zyfzliBo>rDN>(6Q6nD>8v%&+!5UlZgcka^gXc({IzWoX>LDKe93Um>edwm2<tV~SC zS#qa?X!QB&BUDiOg(Kk5P)<&iu*Bwdrwy0i>$CIQH%xFOM-oNSA}*+J{vNLh7;9*N zR^e1AskYO>_A{;X$*73iM;GLt`@%M@4#e<8mA*%CS*McS*kU{Xt(TYMGbf{}&5`e3 zlgzO%Jb}ITOzJNv#(E~WM^4ajoyROwUQiA>%XqSq!elNrt}=plWjwEYhgsa_&f5w; zbV%qh87Y()>UzgvHZCo50AfkW78x7!$#lO=FltZZL!fuTl}F(5pfA9p06l`O4>5^S z<m|YEjr{mgJGVzcJ>fFj0aVpfgcCJhT1E*CY}ZOHXO#?hrn2M_%(6%<a%!p(6>;OV zQF1K?F-n$G;OrxO$)WlEiK-5iXe1Heu%xOIl~J+t`1;XS=Edo0gJLuJ$U|OEPURRD z6d!a`5mJ9|K<unAI;7|(a?<bDxDpvt7~<Vmlsb)o%)QRUchet}x(#oei6o`c($d}) z7c_Nh@5y4rIGg2LwE!E)If_9h;b+PQOoTgNJFKxA8ykW0^5lp6=&^(a%wYY&OyuJz z#-xkY_qM*ZQ*%jrEWQFykywRe?@Z)UAA&Z^Xp_fUj7rI9oJi1WNbrT!XM(A-j<nh{ zeR%__ZkOYsK%|QY)Pu#h_}q0zCtT83A%-R4?H#tq$LA;oZ?BnE>%4Yf`f3JVxz(UE z7@$LRYknr)>l3zZ@5#4TmX(#ouL(Go(KFF4YewlGerR$F2?_Z|a(3ePNAd^IcnG^@ zUmS+M9IbTb=Te_Crf6hVpVjqXH{r*R-W;XV1O&zj&nvWkZ+6YxuS2|Xzn3`I<}{** zHm2=_>@+k0$<J>~V<q}-S7J#K5lxjx2Rh2A%+t{b<3@S4^TDx}Y-0=LQ?w*Mm96yp zrAVT-ueTD$6`I~1@BLl}V8&jtM?(KbHT^yO8^t&N-qXY9@6{r!I`Y9jXxLw#>XwBO z2YPPPDxWgjbp)D@g2DybFh%v}j^h<%IyxKUUQ%AmiDbg4*GA8CxdaypL+}cXYCIdW z6&cOFr$M@w^4<MWC1lOp;f$pl?Z(?EK;%%-hlb{<C87Mj`gxzMnhNk-qlvtJQrK47 zXPHKgJi*}i;V*5Y%jVctxKhRj3KP;k1?uK^Kc!-<yHLk`*+WADd0LZocCniqOsHA9 z_RLA|^i<yLEcuYJfFC!Ka!0+^hBW3#min|ZuWjX>BBVg{UDA-OxF4`SaUDyS80c{I zPwo|9pRYCPy_Qkwbp<u>b<q(w14P8$nuX~5Pi?l%LdaVDE8b&3t^M!TahR$2-+gPO zANmMJORuM^h$o0T3;=2INRsv^bS057G5-r`tk-VEY@#a=znih9f7UK1-2{T!KL`<& z^7P-={kQqp|1}fxUn8^s&%e}yE|k1<$+kI+5;AmnZ0sZ)C~)h*JMMiXm(_cFn|=7} zk8asx-^~uLj)1?9HX~m(geqAtv2KILr~(8xaN>g;)enq+!0{1G-e__<Lk|LkW(sc5 z=Sw=J$>z{^;OXP0fHV#})%$m{CCtp!5et#J0`L%|ccs{Khs)?<bI9(}($buqoRI!v z-rTxd*`d#oA<2)s+e8~?ruVRe#TGD|a|qKE`UQsCFdvI8?(OY`Zg{B5)f&2{LSqb& zCx+PyXM9dSghqkzEXz>cBQHp^A>?csV8eig$1sVit&Rd4*wfR)Z}z_3p%tJG(c>Y2 zA&m6&Ub{c00abDs&wJdZl>dv><}cd0z5+}0o!M761MBy`F*maTGwFT44?J;HYJATM zjeTb9`6&Wb2oi99V=V*mKPz@6f!<j@^eh%O2gDQ*C_7R`(VKbDTLEM?t@Rdyhqe2X zwo6J5jj{)L=YnIKbiu4h?2!QqJ4`hIH~+xrv*GSrw~I<HE`r;K;G6-MumDg`;HID` zyosh;dh$l=h46eZz4<%NLSq0}3PG^ZY2x=YK$R_|Jx?@c&;O4Nk6XYqFy4V)Y&ucR zGRgP&@E3*i9(5Y6n&iXnMQ~7#Y~Eo-*F!?I3M9la_wX&kDU|4=wVHKk)k5mxP_Qaq z<8mTf7T7U1w%Bn>RdD%0K9eYtum0hF_=hx#oBOach?;%_Hi|Cjr8e^bo;FIoj*TdW zC|DaV<C+`NM#n@%B$Tkbg%%hWJ(uy><je+6H%;2d4fcSNS!~$^vsp#(V0vl);-6dJ zaZ|Vu?fD&p^gWsiBnO{>4uI)~*lxN<`kx*CPN6GM)61T+0X7_Ls9<r-@+MHx)1x6} zHld=Xo{yM=@oc}J!*)>K8npmxx;LRLeCCrtZ@?KHLC^r&<%Z*=?-tq9=_+760d0*! zK*0zYWj9<3N248ZEvEu5gI$Ma97fyA0csaF97%GVAthDtcEpLXnpBBe+qy+)u@n-L z!M~k^G$AfigW)zb7IX!A8DkjD06e*~I(+r~`5VVejAxB-a>@q%%sEO*ob4Z)OzYq9 z)+-1U&ScLn9|F5V#S;J*33&05Qe<=Nw(mv@{q9(nd?*l~cRLbJw{^m5flhr#l0f{? z9D}5N%IHf#^jB(_+iONo7TO1u*>wYq5CzU3LJlU=Gc?cs2~nSO8>(o32zls8AQJDX zKJ0ggu?9IgIbaf(m)!pd0dWg7ng3~OYl;7LPVA6=aWjZP6juKlZKD6(mt0>XxNtq6 z)Str*s!9P>eUwc8Uq7V~J6Ev`AlX~8OXddfM?btkB*@SVTTtClWEWP!eWOL&DSeIT z9zp_cL@8jT49ys>J#fbBcxtzk;$pCQFbnaBwH$fo?<UlrEF4*Rg;4*SYBy-@vF3z@ z#&T%SlXoX?!JI~38SYc11$t0*XN@sI`cCw{)bkd5?AWWks?Hj9xXJY~vj2QU>Sb2r zxRjuBq`FR9h-#kdGlGB?y9nGM^o;(0K3JdQwGmOd;1aNdoeiZ1#Lq%>@_>TUpaRYm zRyY2iFDk6%EljxA@r)CAe^1c7fIPjC&UGsVFZ1WtmijMJ?tzRiLh#86R)3$TW6KfS ziDljWz1={0iIt5FtjJ4HmfGu-YW-cdhrI~NnC8~kGiA>BK1SqeJk|fsW21_ac^7ns zea@#9NHIw65HmRb1N13rqdRL{!v&I(l0XAB94+R=!XW?mrCSbLW5yUaOu4)L58$E> zu0uJ12Eb-&tx;Ls_UKB@@u>~WQn7}l*BWW&7qHKVaON5^FMV$&>Kwh{V|^Kx`()5) z2ktrnDZ@LLX}RjxKq&*s21Z{kH)GMWFhW@aag8f4jA!$j+0jah3JMCqQZt{?e0GV9 zIM4E92u=ec^@X5^u;$o=-+M6Dd5)0v-KB`;<l#|szUknIMJ44uxFZtCLzT&{v5zBz zv@&ptzv5$Zi|b_1{I3fRJhU+M01r%7zdgYT)!H9TByt7!utjp*eOC9PO{+B_+~OAK z1Tf29lYR!T6*_ziM+^$~ft>=`@3AU9w+_ByXt&EeOvl-DCU!`^>qOPDDHq}>3Lx)) zxueoMUQxMb8JlJ+9cpIEm>u;ue7Jv6c=A)q&v4DOcgx@kglQl_sH`vt_XP3r9s58z zl0I7x>1uts1!(LrEn^QyM_xT^-~=Eeu?Sfyz{Puanu>G-W^q3N`2>`eInaLKpZgA@ z|6m#hK{eFyJe7$Q2nY5sGc!a<*y4$Q4jhaNnF0J~>ufp_$Yl>)Ao2bYyC7#1jN&w^ zm#F3{L{0&nfO(V^_<RzE+yH9j{`_7!86qzKN?RW~`DVtGX*KXoI2GS4A`aRM9vGsA zR0<jbh!%C4>5}bczP{2{-r%p%-V9?8I6An@(r#pB#X-y{8J1($^_=YPV*M7&mcG*! z3mBx5LuCI1MY06Cb}%Xb@vgbLUcQ{T07iZR5;7xC-8J5dLSx>8-MMF4KdO;GyP_>* zlDVvF8=+rY>2h?jNDWVDkZOhBVCWjyE1;Dl^2yk^jt;?#K6jldfrg684F>3L1`+^Y zz4SWS{3#2FeihIX^t=J{5k2(Bz)0cxW8lyY4H^6a3xprV{73=vK?KsD|KUE$0Sh6+ z0)r`#eYWO09&9gaSEc?g3TLWZ2KnGdBrsEjrcF=kfJhBwn2NcK2GY@L7REt07=3z8 zPdt?a!A}tew@z9>?S@JLD`^ZTJh{-Bga)72Wybc0KxI*W1S|m~RiT0Po1ItQCc>FE z-5%GYxm36hjb6p(&P=2LhXrgr?ch4-Ynr7l(@k(oVHS+0@U)RLc=f+r8B3IkbVAe7 zqk10#6=0Vp5&r#<<UpT&5oKl>h?w;~55NNI*N1!LGGV6&j*gCCM$y&6Jmbn70to>s zQ-9mE9LH?6Z-pltW4^pcwx6)^0;Pw-l+zj%Eg*l3Vfrue4y8OOqEl^ZK>J2~ouq7I z5_dKQ&~Xx7un!bNCe+WMGt_sU!-;x0Br#MX?KgmiiPR;Ue;5nY4NS<8aS;GGNj?d` z?b1lirTKw7%YHzg?2I9hV+xFJ;R%g)Dh%6t`BQ*!o`ze^;x2iSS>#)D7{&FX5AIt* zWhFrM4ip;woa}~(3d7%nCf<0Hm%FzdS(3f)N(Exdul32ce3Jb^IAldUzq$}aj%5!1 zZ3~H<4=?0fIql(ktCaQF&p&8vIeaBobv<1P^6j|~>V?J&#-cKn_xhBIYyc+pysJP` z25w%p>Y41d8byIS4eT2Ga0&oLiRG>VB=|Kxz0l-<{mPovFMGJH3uz?&t^>N&?sfXd zE}+c?5TsvG+B9KEAUiLQf-G5K-+OWtcdoFNUnE7g?mA&PsK|{mo5nc7Y3-t`csPt1 zreR+dhsUKpBt!E9dtt-0lK@J!FA)*Zhb09_<&d484&v~!;p`**Xde<Csk@ytm(vdr z+>I3l`(V}Emoj!V!QfMgfqy<A;JByfwm2dju!g!<&~0GZGozVAjmz!hI~=f^ixPBS zM83Y!SgBWbN-0OCc}Tc|6Se?)Svvjb#8=r_{a*`P7QlRAQR}U}n%(bUxbyOUeZJ`l zD$od`e(&v7{|+GOCf_Vl@M%e1!7XYx1F-<A3NJ73`SVj{-7<mW=cK(ULbTu9VywYw zo39#j6fLn<%8t!;pW*=@ir=3I8e*S)hy3&kRC9OTq^dc_^SftCiAMQ>@dBnWg7yly zl5jtAlVbJc<mo#R?TH{{s=a~RgW72u(D#FzS5;L7-GyO7k;_LOc(my^*)ARLk%zuM zd59|l2B$c`L7pT}phzxSmpvs5eLm{~xs}D@PCto#|K0O_GjbU3`iytTqFXMs0EwV9 zIn?%}b}|iEnVF?PVD&cl_4S1T<C5zD3_S}+61GVx(pmY4(X9)a{`>jB!+^kS;kQG( zDGQUgfa0wl!2mRXqo+@wj_F;DktyDv>v#s-Ncyr5R*nwFBMDEndN%7qVn`!>#ShI2 z`8ZB3azp-NJP!{9T0982Q0vK<vo<AT+r(mjXPlV%tnak9zJSOn{XxGFq(XI+ts2=d zWl1%2>S-1kGzpYy22ce)e7GT^tw$ykrmP={X;|y(dT=_^0Dg3!nW?x8Ig{XZWq&Lu zUv#>LHff!Uo<t6x!ePT;sNx+mh{k~m#EQbnlOG+h{6T+yOT+3Pn8`3fS4dMk%s=W1 zHjmsHXnNNoFBfD!d)2>`(_;yElh?2T;2qd}@$fd%G51F`u~7nwoYxYUig9>^zhpVF z5fI5q_JQZQqU=^B7^>Iirzi=mk^x{yY?BDo;#G!deMGfvlAHg*c4LJ90Z~*n4-e-$ zs5hQn%r}tFX_1%xdXYUb)j$U~>;LN}`lqBlmdY3-TCr`+OB2tk8;04`?lj$0hZC%# zHO36yL_hCAdE8#YyhE0ZXM1a$VavV+|BaKp99bNw&zFT@zPXIqELPl*Cg#B*vjo!r zAg=HDo%O0OaELA;8ek^eO{j`vebqmaU_+q-uOvC-Pv~NCkWy7erRvcyXJUTh7dNw| zYj)?8Eu_;~Z~%^XhAfZ=x=~>Dc@2uv^2$_+HaK4Oc3R%tNy2riXA%cdmbCzxRowod z+Juv@Apc5};^3$X@nfI^HU$3wYHmD0)6F0M{M<t>9uA+9|9|ZdblxCjy?@*R)}I8* zcBzdjR0oktXrs|^PoN_jDYJ{Ut@ykJJVe!R3iIH-15j7NNLUE>N=D}B8n=km1}plf zV{8oAi6-l><L3G#0Cm}B3k%EfR7x>v32)U#mj_W4w{R6oLT@J`g8NyryPb(f6^=&G zZz4|EV>>@FNx#pTF7y)0oMgOeOUfca5deOSL>O|n;TM|WhTmj@yD$#q0d#-ROTnLQ zMKNB@HYzeI`vx9OhF3i+pL6?|ABxD_%8+IZVEtY=pxuiydrEyF@5OFi?x!>Csk^xd zT%mykkRq{R^uWdyn`TKh$+U9a8M~H|7B)CD;(%pf1X%40<FQa1p`=&=Qw*RF)aoxl zJpr|U_v+HNV{GeNfT)JP4B$-yXUjb-e9AqI*4V99FsA?t3D?&MqlaI80Nct05G7Mn zQ{ZQ*fvk%A0MK$ua4bT~eXvOBL1F=z!z$xz7$ucDnly;Q{Vc`?qdfM>DZ+R2FzU}J z=4c31IE9mfLUWbtY(Wm<5;ssruz_$)5fKq6_;Y)E`+S|(de(LR46ci?TtH!Uz<Q9- z@xplTxlZCy5}M0*eo>%s%5&rs!#6IVlLLr=TDuHeH#<8^Ldz4x_#VLAjKMXRt{-7M z&~p5svXKUNbIWN_Ztd)#+91ceE^q<d5v+*NzIxaxtN;LXZ(-&ExXl2?MeSoW@no)) zrGZ)kt;MVB4BYZF=~V+LAxWGc9GXZT5YnM{@A=aEv&Rm%bPKTHdN3s|qO!ST7=%t1 z>M@oTu7%Kn33V_}i;ac85mG!5Lc^8!El9(GUt$okrx;=POq3~qxPv4$J-cJy8;+k^ zhlm+Z<>of8t!incq#AYFG5SB+`|@xo+xPF0=g}frLbmLwRQ8cIN!EnM%-G2?_I=-y zvP2<5i71M=$Jmz#r3Xb>vW_)WiY#Nvl8AZF>HB^De2@1ye#h^>_o-u+d+yu4T=#XI z*Li-<&-uA6J`qCQ^PBLCd+6yQ!xjp27%-ziSAa8&GF%SDio1n)Ql{(T%6bZ?$NA%+ zpA-RSNSfceB@l6b8%QCaV_>4Sf7BdI)1`oBvR$i#K>#8{w0iUeZPl<16_|@mY_7$8 zv8%8Iz^wIX!gl5un#5ymny<&9&jrAQRbx2(aTn-@B&GZSzX2J?8SguE<AyH~tw72E z`WLNAhE)tO2>v~XUqo>6@#Hh_2Y>brczEFEZUCj+{(}IN6aoGPxv7Q461}>)1GoPA z`>rwT2~9AS#f*3k>$DkgZoOZF`}0{A;nF0zAE+t8SsZW0vjS0}O-WwyshgM}c-x;( z*hy~IR=MuT=%<P$&MUL{{3&5$l34SHi2CYb8%Xv?sneI(*IwbSa-HXtXu0a104P>v zbl#^S7-}#K@&FxExD*{}Cl&Jji*9i6CQL0Ko$ulaJ&WsCaC(5Hed*Z;sJ2HJJJ3_q zWnUD0D(Z`TG1h*RkNwZ}l{SH=%QU!%Ke61T`fo`>LRF1#CV-P&8m}NdhSYOX4#(ff zJZ{K*ESl@}kKfL&K=`scv<ByC(@5K+QyoCpkaC4y&1*aaA~Mi#2cTL)SFI)aS)Els zLYl!^^ol=cVJ!ec3;4Yk-^ED*w0X*=@kIisNa?uL_a5PhG;-L|%i|@ZpDS0!HQRCa zJAmKJ4VB(4=cf+>+D|S!q<7nI;$n_GWYfMycU3v?;+KptcS>Hpor6`v;H>t&V*A4* zwdttfc!hR=>cBI;t=Fqk62L9Yc6LM5Zcoq$QZPhB;1|<a9ZWoOf3vLgU6M4*3BYA* z5qc1Gl=_n+XUwBhq=bd{9H#<m5Uu+3f%-Sw``&-i%mbJX7$!GUY3de98_op1HIljP zvE{3`v~<eHm10huZP3Ug$z5$AeaQ7TvVoA(h$HR0gTn8|bS3IyORb?L>M%(xlCRr} zdD=LrSC|+Xc~yd?Vuc>9*}upE$}*grbkCg>;O*e~4`yx_*Ns2Hm6})U`2~Gt7j7=R zhbUddV$;T@l<apPo=;pVQ5-xjDq2j_FX2<6-{us@I)mM9-@?b3IP$F)Zr;A9{lzy& z){|UwTgU10bJ8D>2&O;u%u+*_xDoIHT)AGho;;9$5vk#F#~PSc|8?QvNn^1SS70Y_ zHtxFSL`cobm)kM5DM8*CU_M&jnq-a^iHh}uDE1g^<nJ8KK1MQjKJ}tPa!fDkO%1N2 z{OI%Z0AXlEpD2V_@YLNL87*@Q#p?71IA`J<k5bYA$O^7p2=7FuK7W={0|ta{-3nCB zHx>HBkOU~i?C$!F8#gk?n9SE9FhmE{Hsa4nOaL85-P7cK)8_-XBe)t&j1Dfs<#$*C zX}SUshwgLR`e@wo7O3Z3JLR)xjwUI+`G_atH}F5Ki1@87u)3_B-#d{QJG*3{GVkBF z4~h$Yz)B|`<N>Y~DeV{NARS6fZg(4QXXoK&^T=<4xaUl_C11=c1mrC;Uzud@rAhaV zwil5;tjK#_2;V1A>AEmcctzx(2B;w1)K>%92nZIrK8hJUilYEd`3l&3z|fR32Xf#N zgbqzl;!oQ?s+a%*7fo~t4BaLW0Fd!?<2cK{#HJNUqZsPw442tz0QLklPeK*I)K$D% zPQ}_=iAxfgT_6Pj^3w%~63luh=t3R{ZD{7w!h4Va`8G((>WmRri4CjUgNB<pXAZ zxG9vgS61J7f5rxSKuAj-gtmk|z!=db8rX_v;l^CCu+4n`bLMkT%<`HN_!QY*wg?e9 z?+`${?C#Ot_cZeI^qFGw5I-R;1g4^=vOL^!{>7vCr5Y8B8n<k=1ffikToNm$q!ynq z1tCJ_r*zsVF!d*G!RC3K%Vr;Xijbav8qU@Z*D5JMZEoP8b%1wBzJ|SE>OAH3GR+<b z%eOv-xe3G+$R$?WaOb(`q!+8)N4^8Yx|Y^zDgA)WC34G(2@79X^t_g}kKtk`H`lT5 z995t}V<E9oRwkuih$#0{Vtb`8Of__v=}jO4TwDqOb-*;A7gzQYQsF>i^8liRo?QAT zn0^jiCx<Y^txLePj&2^)OK{whc6wV_{J4+s#fOR0ym~3s9q&=zTiAnuz*Y49aLAYd zIyX`0N26dLGDDk7=EX1zaTzRu0z(^+dl`wX_Y--~+6l5zaj8U}N8ft&urF~cM$PvI z?#db8)NoFUuF=x*adgYR;7lYf%XrlEUG1%QH8c8F`MEwmAiE$TWe{)R!E~>E^VJUE zL&D(xPQ*)^RkuM^fF+zTp!GQBQo-nqCX)%0&WGEU-lt2yg(+!vo)mg5?Mw~~2B^Bs zwgrX}K{s^S6=FB(eZU4B=${o9{d%4#_Hm9(q;<}a)q-GC<auh#Ben~CvHyz>w}i%v z*I&kiCIh~``}O%H8-zz!ICmh41=Rz}9;0$!FJTfyg4&!cR-E~Vf;-<I>#Xf5HkxsL z_)UW-AEn{Zm&IFV!X)|q&8c_G6&y2K_IGZuU3zb+kSQNzPsFO`NZqSgktX6Bw@qGz zA~2H!B7<>kG44UEk~HG|LmeyrBq@z}=i)ks1dy7B9`O(3r;m=u;3<4cUr$?Kum&>F z-E&)SkEY(4>+!5`B}f3()lyj{Q$Oo){ucz3k2_ooj^UYtXd3hdP9%8MUgP!a;Cp;q z88QyZ#mz4m5NjA|$7ri|eNel1#;yRz+ZGmkM8O%Z@QRVAZ_k%<*zbp-qmM4ZPSf1; z_2Rh0hmkk;f&X<YI+~W80Bt0;dX|G>Fk>D!cw}1T+zYq?VBx!fGeg?W2kZ!;<G$u5 z<iX0y%AoIlo`={Pa!|RMRt}M%c{6qK6*x^$irbz6+H2`8(T>qI!YQiZz&ZzczkMe| zBOuYk%%@NWpeQ6}HkKx-aNm7Znv>qX0jwHKfTG>&I0hyj)5mHjTAs+k&SEeau#_<& zkn{qb7r{P1D01Yh0{$<M%xXAD3TuEg`?HBb#ic9m3jtsqfV4`lJZ*)k-=R^)v9pky z^qc&B!ge9ft|{;jt&^XMbwwRP{74}Ph$ga05#UupE<$Jpl8k9ZxIYJr|0MLPg4Kke zpTQz-+)<-5Sp`p;<UcKsOw`vU38-0<`y38}wMNA1?b{9!K0-e>tyN0FDmJG{+C+fy zZr?=jrMoryEZ`#$8XpQsp_NnKCItlHkG&g<J@QcU>nmlcqE9Rk!{U>u0A7*#CMX4$ z^OULz8N1l}1*qxM$booA8egd063E`V<U*{fCVDSK^%J?7<kHJs1%=|OA{@^qW0>wE zr>mf9<eUBpB@c@ct_r8_l6D0Kx~_*YD#tfp;O<=aO_#9R0=j^PpQL%!2_^j`K1HbR zh&vRO`$rA<4X$e%Roehjti5~kM9`T4s0ujSP-RNuvjNf)iy)Y6qfWpEuR{(lLmlQp zAVNIkXSL?A1!T=b)2;)N<`a=(h=Z^rZ?AB|w+%4dEs;q#P%9$He?VT{^;nA(hJ$M_ z!+u2k+LR7ewgaI7oOu9DkXYNR6A&wEhDxab0ER$*jVmfmWB@AZi<xP;j_m08!@SP~ zwAEce#HJAdR>)1Xvih7BMX9-H5kIinn*s0YKgqpo>0Gvh+E#|yN*p7Kp=&3mgGDXG zOgT&|#nUOn!HldNNWXiW?!6|2QnR<KF(J^ISm$a@U+I~5OfSudVbDF`TTnZzR=RJO zL@E>pPkCTu-I^8k4`nEglrO$-7Y+HeY)4#)>#lg>KfQ0KYF5r*g{%~lIdUb5ieU-R z(|^dA-HA*;0k?#w)NE%IYhFFx*Iy*qU^RTTsQrRQMSvK)h!swDs*rL1sL-UNw(e&U zxH}d(+(?F~oHo<Q9YA`dr3F>C>Kw5UO#kh&m3Got%0=j|LPuxX$$k(*bT~C#Sa@Q7 zUq{+W`?GB6xDLv<8#I6u;;3>>Q7!?gz$asO3R#}JE@BJR(``CriFilLXx6qj-j#kc z1_LGQY&;j;t~p1iv6@qn;yqV*AV=JEzTmWLmoqNFiRcae;%16}bM$_N_~v91!ic-t zg<On4kEKU2sz+~i#b$_d7ms>|^(=G@4|)3G?8Yw!xNFta1KWA_*Nk~(hr)mux^ns+ zUB6DC!+$%vMPi$d2IkOGO<zI30?vkT0A2YsLC?&1i~DSbSvUa3^Dt$BR|436XcU@g zlch(LmuVSA{@ZK|P;O%4bV0hWCeegt$XiVfT8vl%Vjqd%s&~TVI`afBUSkvt(~U&? z)`Xvc$oHI;Yg1mM<3`y4fiVDpFrW#1(^ng0%`02bvUB@*{%5geT?Lru?{<D5Q)oo@ zJq|E<^<TbEHxZRbnjJ_w_YvFIJ}J{MB+@Y8&X6h#LY%pQbUD~RL9Gd*=Hk+s`Pz<K z<1-J$$H-b}Xl{w*s{Oj+;Bk|xuVuF%<8D>V;Nd5RuFa3PyX<3OfAG!2c=KHSEBj-6 z?y9kG+265!=g-!TspY$gH#Hx*+$f;r9o#tcC~WacZFX4qwO6~FzN%sG{c=_x&8W=y z8O%(Sl3z|cb-U7AZ?MI`rj^G)nG%GiV1_-0%RWJfR6Fm(zM+yfhw~GlPhzROrArpE zWyb`xuOu&68Yo&`UKCP-ahJAw?>b@-D8?X=n$s{naVIQ8DrzCg2{CAyJvQ=jzf5xH ztg6{qIKy+pUB9IyC+ABe_D#ms0Zgv0u9(|Q!45lj3%E~2S=y!^eUkQkdAwi_z?Mx* zqnim7hrMrK3KuDbIfNsFWhF#n|HA@*=R-ee)q!~Y{Ra=+9J#r;%AWIQM-N_K;lA%g z7PqmnDgEvklhciKw6}Mbz7ow+kTd><O=z1MJ&6v6IYx<$OIKdc^yp!#ZQoQO;#HGm z1_Q~FfZzlL26}$@t9NDkF%sZ4S~5~;JKZSSP!){Na^JcXcnpA!*h8P`nN>c@<`;yQ zeNwzoWs|da;AJvaHKFg5S#@hom~T(6p`xYgh{iw)mx^}wb!0<@pyHI{VBc3+CgL_R z7R~Jtq+%SPWGJcFvQivsim9Gi%((f(bQ$Ym6(#2hl0B42?Ln$@0R_|nY;%sJ3EGeO zwW*qj;+*atG$1EUqI|-S_Oxe0#T`!<Fq?<60W@h42wRY5T4`>+kPhy6)d7oleI=9O zbkZZIdJCX9f`&x_4aA6d;Xv1rH$@K;%B#EEOGIf%XJK#6VF$2whJ2aL0O!ca$vvB% zb?@x%K0A3UN6Pl5c&}pVL_a6+n!4mOmT**v|G{Nk2LNbx)w&v|b<O#u$<9J$cc4F% z!$C;%?%*K4I_a{T(yB{_uul7>J5JTpw-sC0oJfT+TPlE-tS>{7^v><ua=+yEx7wAD zj+(a(mobL%>-q5==M`F{--1zAf8-u=tGDo_2}PD)?$CH@RDgtXEK%!;Vqa%s{VnH! zfNHa0zX3%Fa*oJIFGHeMxAe%^7||~rhU@fLZBBoh2yXJ9hUPJle8*sze8a(7<mNNi zwu02JYTe_~GuM>`85X1;XIGb~M@2=67q}(!u>5+vC2;vi7<t!Z*kSL9uP6D+OehA% zA&(jLlFKP|Q{qe3DcEdP85vyIu66ac#|`kxUCx8bWcjma7stk-<WZ}pL`lidKjy@# zQ~d|OL$sY`WAs*cm+7m+-2(sHbD~R2OD2}*f)*dGd+>Lcid$si-ysMBH{qLMAc7Ym z-#{+MR2vaGy#i~itB*pl9z<Y6NzQc_DxWfqK3bFj8~S;bS1?It+AFfK)ZD@XXZpZ; zbzSC10lE7eQ`naj?VFb7zf3xRQahF*14qPzPOA%qq&zW9OibC?+2$o7HetEaS2v=} zOhevM8K>T9#m&&ITLyQxa2;~_&MTOc8a1>38<$HUllf=<JV)E{+&b;-DNObh&$}Ft zWxiDL{{H@_vN}V-enTH(juvfAi0OA%-+VUrXf4aSbLty4ZHUUlDvDE<C-K~??zz4j z^ot>mnzX%C9Kh5^o%6Q1{*ov%e_gKYDDy78nPQM){@w-LD#8Ds76yHj?<(nweit+f z-j>0Tf?BN7Rb!`2&$Jd##$<9Ig=!(Kas1YG>W)d!dreJEh(GfTSy*1pA3XT9_!5~` zd8(-yvnUzxsfbAQn_Sq#MR2)?$X@gPE0AJtZ~ydjFR<729}5a%&{7E%5;%IDv#_2H z^_RdiSCo(ljvgf(iDeRWfTi<)BqMau30Njn914SuOiqE#SkEI2EHW50N^p-38FfJm zVL)9(74e*Z3rmsv1ab8HxQlpDEOh@1JKHCO3-hEuKj^E^*vW>9)Ypb1@1Y&pj&@`? z57fnjaO8coBgYBBk?CkhmZlvU&I2VJ3zzW+?J{1|E(0zK6~_ga!2(MtR*nDh&K$gu zT}XD&3M(iy)np||_NRRXG;yM*XPInbbBrxMk{CU9RWSQ^d!>dHZ2>y}0YZBxr|0Y4 zrl;{x4QOm^Y-HqZvi`wC$HU9Z%fo|IT6(u_3T9aDl5y@j24=~Iv3F}+oWD>1nI^)| z!y|tBG^i!S)_Fsr2LvdckmO>QpBe|~TMY!7Aw;;JmKF}Wt~9x$Tu;4Q`nrf#+V5*k z<t#FXn#tw&%1arS%+1ZMudU;PbhTe>19c6mdC}y_(a}(#lWc8m!K`0s>gid+3AJBy zMbC3E9pLSe2L}rnj<dy|G%hH%c@+Va3`d=p0J}orLZ0ioE#zi_;tbVp6)$@szH$az zJm_?)>C{wBZSD1+Umrl=iorq7p*J=HG?Ix1JC#J@CEh-R*~*s}yH1=rlh`%|XbD-O z`=P8h4iJu(^1H54@<9v3(&H}Qq##4M0Zw}0Ggqq2$+Jfs)^l=lXsm(3K}5ETdQOn0 z*Z9URN#cxz1b?FR9i88|Ud#$p4bk3b$UhFwG&eUVB^~OvgSn8yx}&3mazu7ups}%W zZf@@8%{}>)++3j@oLOC6T?oKocy8HeP@BFDK`ezr!G%UF*3HnC0iZcm+;DcZr{_u4 zwVkbi^jARK0I0FRARFlhZrD!{s9#-OB`J}r(_T@qB-B=?Y<Dqeg#!3v1~CAy*9#vI zCn6UN5S8hUVZ$sS!p{=_1y_ds2{KpJM*tSOyFSu;^|8!u);1vFm=bWtFKBwdSp?AH zq=mAlW5ll&?VtyHn(B-LhR^IleS~5agrAQOo6!hxwg$86vtZKeX|v81>*_+L>gsCO z63jZV7t$-QgA6z1?Jf%Es#Vw4o;ue$wF15_R^8~#J7|rrF0}DF^qw`yCY#pz0rTM; zZ#9M{A&;Gipg2IRr?)|4uoDRQY`tOV$uCByg8z0`U9Ax>pbv?>=jNd_h+O*pKW zGB<IasLZoq)9U0mK0e-LV)FdtO=V2=p_dJpwEYHz`&YbqP|3%df7ayfSpo7%R%Rx< zVQRY1>jszr1_lN!|MFH>$B$rz49iWMq}9}3)z>S{yz=5U$dLL3EQ|I=Ama?&gML_5 zRn?h^?l4HEr>6(J>PLv;SPFv6GMm_XE<>!b#^DH!Tj9(_qexdTid5m;E36&0)3NEk z2vx03CO`Da;Nb-Zom{dIQJ@)=OVJy=f<US@tM$w_q|<&OyIQjAddupJ$@$Kok~k=9 z88JVbq60$JrFC^?x;}tNivSWHfvLvTHAt)Tm+bxUXuVkJ<6KVBsP&r#3<qy~d7O?g z*r1Aw48JOJIF=X~7@)U-lx8}$1Qe#s%<3S}g8S;Sm;L*J0|z>>FpW}LTU%+(TTn0! zl%0yhUjR|U{Z(4p9a>6bH&|I&(M=L_K=#{{J~S*0l0Xk1JP4w~C0Zu_1XvncrdZys z!S&N&LzPdSWrp03yH2L<wHd$NA^wzuS>Rmp_Wiy-%r-}0<t1Qpc3t5wcxy?sF8AL? zVm)?bIq!_9HPLiLm*BPls%8$t3cemeU~BZqa3>GM=}nlIJlbhLRSM&(IrIbX^4arz zMdp{Fe$naiB&zN)HhXoF*&kHFKLS~DD>*Am8FCvy^D_Lwki_u(^i*ZtN8%WBE@ZiY z`cS;hv|hJ019Y+^37QtQwojaJCCyx#312ftNAA07jzT%?2V>3px(bDQ%LXSZ66Zn( zT?dT47EAvgxMyJ65(EqWge!m{&!w)<h>AS@$$(6yx7`&JnPBK2M4=ep*<CR5jQ@W> z9*QyfKaYo^j4OL#$BwqzO3Ak}WbKjqaxwSGd6wvl+CI1a1%-0MQKF*f&LeW+^z~S* zOESjjxZKG;(S7+k>2f-82YeJ+?K@h}CbVVz`g*M9^gVCI(!GXnSJ^MQId0Ub^l#4O zqr;ZQ>EX&Y7B?i}(Nxq`EE<J+_V3%jZSZeD_;(om4^6?rBA_`0t<toMHcIJH;R#4= zZV%5EAhaF}VqufSTj+K-6qf<Q6L{IAFT1O&Yj$?F1nB?~tMoNcO%N|~lPIRUs!-&* zIVxUYA!niybZiHbk(v6JFIOKQOCddbaR0uRwzgYw4d)<A>Jb(l&NTKQUoCDHBW*PW zd0KgSc}GVmN>mL4SQ43mP#bSpT2fL{T%3@YNFHuB$I<^?c7-fu>OrW#Gt1c{khYd0 zq7ASQO$>Xnc=DI`?9k-SEaZHE?BHerMFdtjtCW{7UVNxAOPxRWj#+Hii|*s4pJNB< z2kwRXT_X7Z>EA(nF3DgpU^Gx;6R?-M@lJAATCGmy+ywvb;>>j#F=^eubjV_|FJ>J; ze$Q8xUKf9$dng;5b|+9a2Dlc~WkbW-XC)bnKeo}JKk*p^o}d9e_WmjlnwDIh+*uCX zn(QVajX*NaMnff>va$uoF4Vkn4vL6~H0-=@WVO;Y(Wq6O_rQ`-;U{rSQGJ}qnEYK} z=>QD>G;DUGa*2A5n!5X~RokExfwGa^4<9VQjsrS@3n(2TmhtoQR^ne8*CYHR6;)ND z#dMoKSoA?X-1_viBW@jhDe+S3i}1N0RF$)nlRq9lH^JFl-|QHVI82p1G`{P?IA7y2 zUX4d>Vh|pNe5C$fJteOZlm{W*IJR2*_!_nLjs4?a&9*oi!IG1c6OYHkLq^O~BVJ7g znn<2GL)pmM*77F~0H(2BGvH*wJoaZZ4vl(jh--OLb(x$#fNME?_%Qg^YLIvUFA_wH z1`cfoTbtl&GiVAvXd4b|xC{Xt&Z?_+%xlr2r!iw#roKTU_*xA(l^&!~176g*$4IRD zNF!cVQBhG|p7wN(xf<7by;|Tc;B9L~sP?cswXlt1yLzPl<`QH<)}Iaz4Gj$r7UP!K z-N>i~c3Qhqh@QA>Aov6olE_>M$*p+c98e5k>#>t#tM$kh)RHkWGIm3lJV1O0jf`8L zn{&<n3r>o+_u8rQaqG55S5$-??5bjiM{p<N)o6WlD7dv#76|E7(fp^*PW{C7KG@5t z>an`?p;ucYHXw9pD!l`JqP^kg&!2!Y0TFbtDNt;IeF#N&^?9Ky*AvPR(HbB&A3M9e zJ@u^z=H>6xTZ}sR`ue)M&Oy|H3t;9~$;irr^3Z1`x(yEN-@jK}BP+jIN9cx6!YwOo zsI_a*41xDolaIqo#p_z*&yYy-wnr^-CbPdlCI?y<U*zQEG-h=bT0`&${*T|sE#@2? zTe6tgh_2d!C#bmfeGTZE&sXwsaBu(z-NVD<+O?O5U>p)I&4v0d9hdBAo0+AJpnE)# zl3%@K!<k4VH;nDLyhZH`?UCAvqA+f0TNlalZnCf+6%Fl>|V-mRqw3?g(_hYMXd zk#i4+czJm#`KlHjWR5{{KSw`2wFfs$!EoR)zc(2^N<C9L|7mT_Kg^{YZ(6%Po1dS5 zf1u{qp1)Zz<g$))Z|Cvk^)l6w#pT+a@9g(@P_fBKtZnbt^$nly?NitVAKL>cE<1!L z7ONR}_DDZU>dU|W(Es=T(C(jYiMaTP&LexlO-AWz8eA;7aP_9I+xxe$$R3I{^~Y;s zjCBnSP^jQh*?~$FicTDrVZ|Bp2-XYJ)@xui)v<=~DA?dp#=t|q0qcw#%gpi2hxP}o ztnEcfg#0PbL`r%NTeH%();2K2Y8ulvhBuTDO6XV<t_;5I+Fj7~r2h%u!Vc^gws#Kj zbklVZR@yIp8Wx=iwhjbe9fFIKvp@VMMf=U$)`@__=)<D0v%kN$lBA@&m%XjKgrk?Q zho6FkzpsmbfQN(wLDE0O+e=o`#nXWhEaC0!t>)_$;7?GM^ssgD6!Z6TBY3LHNE4*c zgtK;XXXPExvW{p+w49=%y}ctme}Eu;)=?I1s~~MFFZ|C@u`d4Z1ln=2W=4jxf?Bi# z3R?VULF2Nqpn`;qgwz=aLZH>}KXI`2CtS1*@q@pRlR?WV&~`)n&6r^C<>^5C`K+9b z9QwdDB?e7vxc(kmOVIrR-yncNvBOIP<%M!bIiLhl+VE+M@`L}hNARknd{Hj2><-`I zzQD_b_T>k>8sK#Vei?&LI@+55{$qmgcJLhypEux>w(j8Xb&-*gC@Pgo`-OT7g(AU< xudqVszn}1dLhS(>I(*Qf9H}VUo{C_Bwx{0TKdQs~|Lxx&PZmsR>;CCr{|CXS5rhB$ diff --git a/typo3/sysext/form/Documentation/Includes.txt b/typo3/sysext/form/Documentation/Includes.txt deleted file mode 100644 index efbb82abf298..000000000000 --- a/typo3/sysext/form/Documentation/Includes.txt +++ /dev/null @@ -1,13 +0,0 @@ -.. This is 'Includes.txt'. It is included at the very top of each and - every ReST source file in THIS documentation project (= manual). - -.. role:: aspect(emphasis) -.. role:: html(code) -.. role:: js(code) -.. role:: php(code) -.. role:: typoscript(code) -.. role:: ts(typoscript) - :class: typoscript - -.. highlight:: php -.. default-role:: code \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Index.rst b/typo3/sysext/form/Documentation/Index.rst deleted file mode 100644 index 7edcdb088cd3..000000000000 --- a/typo3/sysext/form/Documentation/Index.rst +++ /dev/null @@ -1,56 +0,0 @@ -.. include:: Includes.txt - - -.. _start: - -==== -Form -==== - -:Extension key: - form - -:Version: - 8 - -:Language: - en - -:Description: - Form Library, Plugin and Wizard - -:Keywords: - form - -:Copyright: - 2000-2016 - -:Author: - TYPO3 CMS Core Development Team - -:License: - Open Content License available from `www.opencontent.org/opl.shtml - <http://www.opencontent.org/opl.shtml>`_ - -:Rendered: - |today| - -The content of this document is related to TYPO3, - -a GNU/GPL CMS/Framework available from `www.typo3.org -<http://www.typo3.org/>`_ - - - - -**Table of Contents** - -.. toctree:: - :maxdepth: 5 - :titlesonly: - :glob: - - Introduction/Index - Administration/Index - Configuration/Index - Targets diff --git a/typo3/sysext/form/Documentation/Introduction/Index.rst b/typo3/sysext/form/Documentation/Introduction/Index.rst deleted file mode 100644 index fc1a6b618250..000000000000 --- a/typo3/sysext/form/Documentation/Introduction/Index.rst +++ /dev/null @@ -1,30 +0,0 @@ -.. include:: ../Includes.txt - - -.. _introduction: - -============ -Introduction -============ - - -.. _what-does-it-do: - -What does it do? -================ - -This extension allows editors to easily build forms using a drag and drop -interface. A WYSIWYG view simplifies the process of building a form, and -relatively non-technical editors can easily add chained validation rules -on a field-by-field basis (e.g., email and/ or alphanumeric validation). -Furthermore, filters can be added to each field to manipulate the entered -form data. - -Experienced integrators do not have to use the wizard. Instead, the form -configuration can be created just using a TypoScript like syntax. - -.. figure:: ../Images/FormCreationWizard.png - :alt: The form wizard with some predefined form elements. - -The screenshot shows the form wizard with some predefined form elements. - diff --git a/typo3/sysext/form/Documentation/Settings.cfg b/typo3/sysext/form/Documentation/Settings.cfg deleted file mode 100644 index bcc46bc7d961..000000000000 --- a/typo3/sysext/form/Documentation/Settings.cfg +++ /dev/null @@ -1,17 +0,0 @@ -[general] - -project = Form Library, Plugin and Wizard -version = 8 -release = 8 -t3author = Patrick Broens, Ralf Zimmermann -copyright = 1997-2016 - -description = This is the documentation of TYPO3's system - extension 'form'. This extension brings along the - content element 'form' and a wizard. - - -[html_theme_options] - -project_issues = https://forge.typo3.org/projects/typo3cms-core/issues -project_repository = https://git.typo3.org/Packages/TYPO3.CMS.git diff --git a/typo3/sysext/form/Documentation/Targets.rst b/typo3/sysext/form/Documentation/Targets.rst deleted file mode 100644 index 8b5ef39fa6a1..000000000000 --- a/typo3/sysext/form/Documentation/Targets.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. include:: Includes.txt - -.. _index-labels-for-crossreferencing: - -================================== -Index: Labels for Crossreferencing -================================== - -.. ref-targets-list:: diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/button.txt b/typo3/sysext/form/Documentation/Tests/Attributes/button.txt deleted file mode 100644 index 2087507751b2..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/button.txt +++ /dev/null @@ -1,205 +0,0 @@ -form.attributes.button = FORM -form.attributes.button { - method = post - - # Label test - 10 = FIELDSET - 10 { - legend = Label test - 10 = BUTTON - 10 { - label = label - } - 20 = BUTTON - 20 { - label.value = label.value - } - 30 = BUTTON - 30 { - label = TEXT - label { - value = TEXT cObj - } - } - } - - # Layout test - 20 = FIELDSET - 20 { - legend = Layout test - 10 = BUTTON - 10 { - label = label in front of input (default) - } - 20 = BUTTON - 20 { - layout ( - <input /> - <label /> - ) - label = label after input - } - 30 = BUTTON - 30 { - layout = <input /> - value = No label - } - } - - # Accesskey - 30 = FIELDSET - 30 { - legend = Accesskey test - 10 = BUTTON - 10 { - label = This button has an accesskey - accesskey = a - } - } - - # Alt - 40 = FIELDSET - 40 { - legend = Alt test - 10 = BUTTON - 10 { - label = This button has an alt attribute - alt = This is the alt attribute content - } - } - - # Class - 50 = FIELDSET - 50 { - legend = Class test - 10 = BUTTON - 10 { - label = This button has a class attribute - class = classAtribute - } - 20 = BUTTON - 20 { - label = Multiple classes - class = class1 class2 - } - } - - # Dir - 60 = FIELDSET - 60 { - legend = Dir test - 10 = BUTTON - 10 { - label = Dir ltr - dir = ltr - } - 20 = BUTTON - 20 { - label = Dir rtl - dir = rtl - } - 30 = BUTTON - 30 { - label = Wrong input in dir - dir = abc - } - } - - # Disabled - 70 = FIELDSET - 70 { - legend = Disabled test - 10 = BUTTON - 10 { - label = disabled=1 - disabled = 1 - } - 20 = BUTTON - 20 { - label = disabled=0 - disabled = 0 - } - 30 = BUTTON - 30 { - label = disabled=disabled - disabled = disabled - } - } - - # Id - 80 = FIELDSET - 80 { - legend = Id test - 10 = BUTTON - 10 { - label = This button has an id attribute and the label a for attribute - id = buttonId - } - } - - # Lang - 90 = FIELDSET - 90 { - legend = Lang test - 10 = BUTTON - 10 { - label = This button has a lang attribute - lang = en-US - } - } - - # Name - 100 = FIELDSET - 100 { - legend = Name test - 10 = BUTTON - 10 { - label = This button has a name attribute - name = buttonName - } - } - - # Style - 110 = FIELDSET - 110 { - legend = style test - 10 = BUTTON - 10 { - label = This button has a style attribute - style = border: 1px solid #000000 - } - } - - # Tabindex - 120 = FIELDSET - 120 { - legend = Tabindex test - 10 = BUTTON - 10 { - label = This button has a tabindex attribute - tabindex = 1 - } - } - - # Title - 130 = FIELDSET - 130 { - legend = Title test - 10 = BUTTON - 10 { - label = This button has a title attribute - title = This is the title text - } - } - - # Value - 140 = FIELDSET - 140 { - legend = Value test - 10 = BUTTON - 10 { - label = This button has a value attribute - value = Don't you dare to push this button - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/checkbox.txt b/typo3/sysext/form/Documentation/Tests/Attributes/checkbox.txt deleted file mode 100644 index e169068b6068..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/checkbox.txt +++ /dev/null @@ -1,226 +0,0 @@ -form.attributes.checkbox = FORM -form.attributes.checkbox { - method = post - - # Label test - 10 = FIELDSET - 10 { - legend = Label test - 10 = CHECKBOX - 10 { - label = label - } - 20 = CHECKBOX - 20 { - label.value = label.value - } - 30 = CHECKBOX - 30 { - label = TEXT - label { - value = TEXT cObj - } - } - } - - # Layout test - 20 = FIELDSET - 20 { - legend = Layout test - 10 = CHECKBOX - 10 { - label = label in front of input (default) - } - 20 = CHECKBOX - 20 { - layout ( - <input /> - <label /> - ) - label = label after input - } - 30 = CHECKBOX - 30 { - layout = <input /> - value = No label - } - } - - # Accesskey - 30 = FIELDSET - 30 { - legend = Accesskey test - 10 = CHECKBOX - 10 { - label = This checkbox has an accesskey - accesskey = a - } - } - - # Alt - 40 = FIELDSET - 40 { - legend = Alt test - 10 = CHECKBOX - 10 { - label = This checkbox has an alt attribute - alt = This is the alt attribute content - } - } - - # Checked - 50 = FIELDSET - 50 { - legend = Checked test - 10 = CHECKBOX - 10 { - label = checked=1 - checked = 1 - } - 20 = CHECKBOX - 20 { - label = checked=0 - checked = 0 - } - 30 = CHECKBOX - 30 { - label = checked=checked - checked = checked - } - } - - # Class - 60 = FIELDSET - 60 { - legend = Class test - 10 = CHECKBOX - 10 { - label = This checkbox has a class attribute - class = classAtribute - } - 20 = CHECKBOX - 20 { - label = Multiple classes - class = class1 class2 - } - } - - # Dir - 70 = FIELDSET - 70 { - legend = Dir test - 10 = CHECKBOX - 10 { - label = Dir ltr - dir = ltr - } - 20 = CHECKBOX - 20 { - label = Dir rtl - dir = rtl - } - 30 = CHECKBOX - 30 { - label = Wrong input in dir - dir = abc - } - } - - # Disabled - 80 = FIELDSET - 80 { - legend = Disabled test - 10 = CHECKBOX - 10 { - label = disabled=1 - disabled = 1 - } - 20 = CHECKBOX - 20 { - label = disabled=0 - disabled = 0 - } - 30 = CHECKBOX - 30 { - label = disabled=disabled - disabled = disabled - } - } - - # Id - 90 = FIELDSET - 90 { - legend = Id test - 10 = CHECKBOX - 10 { - label = This checkbox has an id attribute and the label a for attribute - id = checkboxId - } - } - - # Lang - 100 = FIELDSET - 100 { - legend = Lang test - 10 = CHECKBOX - 10 { - label = This checkbox has a lang attribute - lang = en-US - } - } - - # Name - 110 = FIELDSET - 110 { - legend = Name test - 10 = CHECKBOX - 10 { - label = This checkbox has a name attribute - name = checkboxName - } - } - - # Style - 120 = FIELDSET - 120 { - legend = style test - 10 = CHECKBOX - 10 { - label = This checkbox has a style attribute - style = width: 4em; height: 4em; - } - } - - # Tabindex - 130 = FIELDSET - 130 { - legend = Tabindex test - 10 = CHECKBOX - 10 { - label = This checkbox has a tabindex attribute - tabindex = 1 - } - } - - # Title - 140 = FIELDSET - 140 { - legend = Title test - 10 = CHECKBOX - 10 { - label = This checkbox has a title attribute - title = This is the title text - } - } - - # Value - 150 = FIELDSET - 150 { - legend = Value test - 10 = CHECKBOX - 10 { - label = This checkbox has a value attribute - value = checkboxValue - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/checkboxgroup.txt b/typo3/sysext/form/Documentation/Tests/Attributes/checkboxgroup.txt deleted file mode 100644 index 16544eb0554d..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/checkboxgroup.txt +++ /dev/null @@ -1,39 +0,0 @@ -form.attributes.checkboxgroup = FORM -form.attributes.checkboxgroup { - method = post - - rules { - 1 = required - 1 { - element = checkboxgroup - } - } - - 10 = CHECKBOXGROUP - 10 { - legend = Checkbox Group test - name = checkboxgroup - - 10 = CHECKBOX - 10 { - label = Option 1 - } - 20 = CHECKBOX - 20 { - label = Option 2 - } - 30 = CHECKBOX - 30 { - label = Option 3 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/fieldset.txt b/typo3/sysext/form/Documentation/Tests/Attributes/fieldset.txt deleted file mode 100644 index 35b2cc8cbd56..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/fieldset.txt +++ /dev/null @@ -1,95 +0,0 @@ -form.attributes.fieldset = FORM -form.attributes.fieldset { - method = post - - # Legend - 10 = FIELDSET - 10 { - legend = Legend test - 10 = FIELDSET - 10 { - legend = legend - } - 20 = FIELDSET - 20 { - legend.value = legend.value - } - 30 = FIELDSET - 30 { - legend = TEXT - legend { - value = TEXT - } - } - } - - # Layout - 20 = FIELDSET - 20 { - legend = Layout test - 10 = FIELDSET - 10 { - legend = This should be at the bottom in the HTML source - layout ( - <fieldset> - <containerWrap /> - <legend /> - </fieldset> - ) - 10 = BUTTON - } - } - - # Class - 30 = FIELDSET - 30 { - legend = Class test - 10 = FIELDSET - 10 { - legend = One class - class = fieldsetClass - } - 20 = FIELDSET - 20 { - legend = Multiple classes - class = fieldsetClass1 fieldsetClass2 - } - } - - # Dir - 40 = FIELDSET - 40 { - legend = Dir test - 10 = FIELDSET - 10 { - legend = ltr - dir = ltr - } - 20 = FIELDSET - 20 { - legend = rtl - dir = rtl - } - } - - # Id - 50 = FIELDSET - 50 { - legend = Id test - id = fieldsetId - } - - # Lang - 60 = FIELDSET - 60 { - legend = Lang test - lang = en-US - } - - # Style - 70 = FIELDSET - 70 { - legend = Style test - style = background-color: red; - } -} diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/hidden.txt b/typo3/sysext/form/Documentation/Tests/Attributes/hidden.txt deleted file mode 100644 index 3e00780af352..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/hidden.txt +++ /dev/null @@ -1,76 +0,0 @@ -form.attributes.hidden = FORM -form.attributes.hidden { - method = post - - # Class - 10 = FIELDSET - 10 { - legend = Class test - 10 = FIELDSET - 10 { - legend = Single class - 10 = HIDDEN - 10 { - class = hiddenClass - } - } - 20 = FIELDSET - 20 { - legend = Multiple classes - 10 = HIDDEN - 10 { - class = hiddenClass1 hiddenClass2 - } - } - } - - # Id - 20 = FIELDSET - 20 { - legend = Id test - 10 = HIDDEN - 10 { - id = hiddenId - } - } - - # Lang - 30 = FIELDSET - 30 { - legend = Lang test - 10 = HIDDEN - 10 { - lang = en-US - } - } - - # Name - 40 = FIELDSET - 40 { - legend = Name test - 10 = HIDDEN - 10 { - name = hiddenName - } - } - - # Style - 50 = FIELDSET - 50 { - legend = Style test - 10 = HIDDEN - 10 { - style = background-color: red; - } - } - - # Value - 60 = FIELDSET - 60 { - legend = Value test - 10 = HIDDEN - 10 { - value = hiddenValue - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/optgroup.txt b/typo3/sysext/form/Documentation/Tests/Attributes/optgroup.txt deleted file mode 100644 index fe79cc9482f9..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/optgroup.txt +++ /dev/null @@ -1,172 +0,0 @@ -form.attributes.optgroup = FORM -form.attributes.optgroup { - method = post - - # Basics - 10 = FIELDSET - 10 { - legend = Basics, already shows label - 10 = SELECT - 10 { - 1 = OPTGROUP - 1 { - label = Option Group 1 - 1 = OPTION - 1 { - data = optgroup 1 option 1 - } - 2 = OPTION - 2 { - data = optgroup 1 option 2 - } - } - 2 = OPTGROUP - 2 { - label = Option Group 2 - 1 = OPTION - 1 { - data = optgroup 2 option 1 - } - 2 = OPTION - 2 { - data = optgroup 2 option 2 - } - } - } - } - - # Class - 20 = FIELDSET - 20 { - legend = Class - 10 = SELECT - 10 { - 1 = OPTGROUP - 1 { - label = Single Class - class = singleClass - 1 = OPTION - 1 { - data = optgroup 1 option 1 - } - } - 2 = OPTGROUP - 2 { - label = Multiple classes - class = class1 class2 - 1 = OPTION - 1 { - data = optgroup 2 option 1 - } - } - } - } - - # Disabled - 30 = FIELDSET - 30 { - legend = Disabled - 10 = SELECT - 10 { - 1 = OPTGROUP - 1 { - label = disabled=1 - disabled = 1 - 1 = OPTION - 1 { - data = optgroup 1 option 1 - } - } - 2 = OPTGROUP - 2 { - label = disabled=0 - disabled = 0 - 1 = OPTION - 1 { - data = optgroup 2 option 1 - } - } - 3 = OPTGROUP - 3 { - label = disabled=disabled - disabled = disabled - 1 = OPTION - 1 { - data = optgroup 3 option 1 - } - } - } - } - - # Id - 40 = FIELDSET - 40 { - legend = Id - 10 = SELECT - 10 { - 1 = OPTGROUP - 1 { - label = Option group 1 - id = optgroup1 - 1 = OPTION - 1 { - data = optgroup 1 option 1 - } - } - } - } - - # Lang - 50 = FIELDSET - 50 { - legend = Lang - 10 = SELECT - 10 { - 1 = OPTGROUP - 1 { - label = has Lang - lang = en-US - 1 = OPTION - 1 { - data = optgroup 1 option 1 - } - } - } - } - - # Style - 60 = FIELDSET - 60 { - legend = Style - 10 = SELECT - 10 { - 1 = OPTGROUP - 1 { - label = has Style - style = background-color: red; - 1 = OPTION - 1 { - data = optgroup 1 option 1 - } - } - } - } - - # Title - 70 = FIELDSET - 70 { - legend = Title - 10 = SELECT - 10 { - 1 = OPTGROUP - 1 { - label = has Title - title = This is the OPTGROUP title - 1 = OPTION - 1 { - data = optgroup 1 option 1 - } - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/option.txt b/typo3/sysext/form/Documentation/Tests/Attributes/option.txt deleted file mode 100644 index d79cf77ddd97..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/option.txt +++ /dev/null @@ -1,176 +0,0 @@ -form.attributes.option = FORM -form.attributes.option { - method = post - - # Data - 10 = FIELDSET - 10 { - legend = Data - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = optionData - } - } - } - - # Class - 20 = FIELDSET - 20 { - legend = Class - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = Single class - class = singleClass - } - 2 = OPTION - 2 { - data = Multiple classes - class = class1 class2 - } - } - } - - # Disabled - 30 = FIELDSET - 30 { - legend = Disabled - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = disabled=1 - disabled = 1 - } - 2 = OPTION - 2 { - data = disabled=0 - disabled = 0 - } - 3 = OPTION - 3 { - data = disabled=disabled - disabled = disabled - } - } - } - - # Id - 40 = FIELDSET - 40 { - legend = Id - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = optionData - id = optionId - } - } - } - - # Label - 50 = FIELDSET - 50 { - legend = Label - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = This is a long data text - label = short - } - } - } - - # Lang - 60 = FIELDSET - 60 { - legend = Lang - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = optionData - lang = en-US - } - } - } - - # Selected - 70 = FIELDSET - 70 { - legend = Selected - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = selected=0 - selected = 0 - } - 2 = OPTION - 2 { - data = selected=1 - selected = 1 - } - } - 20 = SELECT - 20 { - 1 = OPTION - 1 { - data = selected=0 - selected = 0 - } - 2 = OPTION - 2 { - data = selected=selected - selected = selected - } - } - } - - # Style - 80 = FIELDSET - 80 { - legend = Style - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = optionData - style = height: 4em; - } - } - } - - # Title - 90 = FIELDSET - 90 { - legend = Title - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = optionData - title = This is the option title - } - } - } - - # Value - 100 = FIELDSET - 100 { - legend = Value - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = optionData - value = This is the option value - } - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/password.txt b/typo3/sysext/form/Documentation/Tests/Attributes/password.txt deleted file mode 100644 index 7bc66fab1002..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/password.txt +++ /dev/null @@ -1,251 +0,0 @@ -form.attributes.password = FORM -form.attributes.password { - method = post - - # Label test - 10 = FIELDSET - 10 { - legend = Label test - 10 = PASSWORD - 10 { - label = label - } - 20 = PASSWORD - 20 { - label.value = label.value - } - 30 = PASSWORD - 30 { - label = TEXT - label { - value = TEXT cObj - } - } - } - - # Layout test - 20 = FIELDSET - 20 { - legend = Layout test - 10 = PASSWORD - 10 { - label = label in front of input (default) - } - 20 = PASSWORD - 20 { - layout ( - <input /> - <label /> - ) - label = label after input - } - 30 = PASSWORD - 30 { - layout = <input /> - value = No label - } - } - - # Accesskey - 30 = FIELDSET - 30 { - legend = Accesskey test - 10 = PASSWORD - 10 { - label = This field has an accesskey - accesskey = a - } - } - - # Alt - 40 = FIELDSET - 40 { - legend = Alt test - 10 = PASSWORD - 10 { - label = This field has an alt attribute - alt = This is the alt attribute content - } - } - - # Class - 50 = FIELDSET - 50 { - legend = Class test - 10 = PASSWORD - 10 { - label = This field has a class attribute - class = classAtribute - } - 20 = PASSWORD - 20 { - label = Multiple classes - class = class1 class2 - } - } - - # Dir - 60 = FIELDSET - 60 { - legend = Dir test - 10 = PASSWORD - 10 { - label = Dir ltr - dir = ltr - } - 20 = PASSWORD - 20 { - label = Dir rtl - dir = rtl - } - 30 = PASSWORD - 30 { - label = Wrong input in dir - dir = abc - } - } - - # Disabled - 70 = FIELDSET - 70 { - legend = Disabled test - 10 = PASSWORD - 10 { - label = disabled=1 - disabled = 1 - } - 20 = PASSWORD - 20 { - label = disabled=0 - disabled = 0 - } - 30 = PASSWORD - 30 { - label = disabled=disabled - disabled = disabled - } - } - - # Id - 80 = FIELDSET - 80 { - legend = Id test - 10 = PASSWORD - 10 { - label = This field has an id attribute and the label a for attribute - id = passwordId - } - } - - # Lang - 90 = FIELDSET - 90 { - legend = Lang test - 10 = PASSWORD - 10 { - label = This field has a lang attribute - lang = en-US - } - } - - # Maxlength - 95 = FIELDSET - 95 { - legend = Maxlength test - 10 = PASSWORD - 10 { - label = This field has a maxlength attribute - maxlength = 10 - } - } - - # Name - 100 = FIELDSET - 100 { - legend = Name test - 10 = PASSWORD - 10 { - label = This field has a name attribute - name = passwordName - } - } - - # Readonly - 103 = FIELDSET - 103 { - legend = Readonly test - 10 = PASSWORD - 10 { - label = readonly=1 - readonly = 1 - value = thevalue - } - 20 = PASSWORD - 20 { - label = readonly=0 - readonly = 0 - value = thevalue - } - 30 = PASSWORD - 30 { - label = readonly=readonly - readonly = readonly - value = thevalue - } - } - - # Size - 106 = FIELDSET - 106 { - legend = Size test - 10 = PASSWORD - 10 { - label = This field has a size attribute - size = 10 - } - } - - # Style - 110 = FIELDSET - 110 { - legend = Style test - 10 = PASSWORD - 10 { - label = This field has a style attribute - style = border: 1px solid #000000 - } - } - - # Tabindex - 120 = FIELDSET - 120 { - legend = Tabindex test - 10 = PASSWORD - 10 { - label = This field has a tabindex attribute - tabindex = 1 - } - } - - # Title - 130 = FIELDSET - 130 { - legend = Title test - 10 = PASSWORD - 10 { - label = This field has a title attribute - title = This is the title text - } - } - - # Value - 140 = FIELDSET - 140 { - legend = Value test - 10 = PASSWORD - 10 { - label = This field has a value attribute - value = The value attribute - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/radio.txt b/typo3/sysext/form/Documentation/Tests/Attributes/radio.txt deleted file mode 100644 index f461cfca212a..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/radio.txt +++ /dev/null @@ -1,226 +0,0 @@ -form.attributes.radio = FORM -form.attributes.radio { - method = post - - # Label test - 10 = FIELDSET - 10 { - legend = Label test - 10 = RADIO - 10 { - label = label - } - 20 = RADIO - 20 { - label.value = label.value - } - 30 = RADIO - 30 { - label = TEXT - label { - value = TEXT cObj - } - } - } - - # Layout test - 20 = FIELDSET - 20 { - legend = Layout test - 10 = RADIO - 10 { - label = label in front of input (default) - } - 20 = RADIO - 20 { - layout ( - <input /> - <label /> - ) - label = label after input - } - 30 = RADIO - 30 { - layout = <input /> - value = No label - } - } - - # Accesskey - 30 = FIELDSET - 30 { - legend = Accesskey test - 10 = RADIO - 10 { - label = This radio button has an accesskey - accesskey = a - } - } - - # Alt - 40 = FIELDSET - 40 { - legend = Alt test - 10 = RADIO - 10 { - label = This radio button has an alt attribute - alt = This is the alt attribute content - } - } - - # Checked - 45 = FIELDSET - 45 { - legend = Checked test - 10 = RADIO - 10 { - label = checked=1 - checked = 1 - } - 20 = RADIO - 20 { - label = checked=0 - checked = 0 - } - 30 = RADIO - 30 { - label = checked=checked - checked = checked - } - } - - # Class - 50 = FIELDSET - 50 { - legend = Class test - 10 = RADIO - 10 { - label = This radio button has a class attribute - class = classAtribute - } - 20 = RADIO - 20 { - label = Multiple classes - class = class1 class2 - } - } - - # Dir - 60 = FIELDSET - 60 { - legend = Dir test - 10 = RADIO - 10 { - label = Dir ltr - dir = ltr - } - 20 = RADIO - 20 { - label = Dir rtl - dir = rtl - } - 30 = RADIO - 30 { - label = Wrong input in dir - dir = abc - } - } - - # Disabled - 70 = FIELDSET - 70 { - legend = Disabled test - 10 = RADIO - 10 { - label = disabled=1 - disabled = 1 - } - 20 = RADIO - 20 { - label = disabled=0 - disabled = 0 - } - 30 = RADIO - 30 { - label = disabled=disabled - disabled = disabled - } - } - - # Id - 80 = FIELDSET - 80 { - legend = Id test - 10 = RADIO - 10 { - label = This radio button has an id attribute and the label a for attribute - id = radioId - } - } - - # Lang - 90 = FIELDSET - 90 { - legend = Lang test - 10 = RADIO - 10 { - label = This radio button has a lang attribute - lang = en-US - } - } - - # Name - 100 = FIELDSET - 100 { - legend = Name test - 10 = RADIO - 10 { - label = This radio button has a name attribute - name = radioName - } - } - - # Style - 110 = FIELDSET - 110 { - legend = style test - 10 = RADIO - 10 { - label = This radio button has a style attribute - style = border: 1px solid #000000 - } - } - - # Tabindex - 120 = FIELDSET - 120 { - legend = Tabindex test - 10 = RADIO - 10 { - label = This radio button has a tabindex attribute - tabindex = 1 - } - } - - # Title - 130 = FIELDSET - 130 { - legend = Title test - 10 = RADIO - 10 { - label = This radio button has a title attribute - title = This is the title text - } - } - - # Value - 140 = FIELDSET - 140 { - legend = Value test - 10 = RADIO - 10 { - label = This radio button has a value attribute - value = buttonValue - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/radiogroup.txt b/typo3/sysext/form/Documentation/Tests/Attributes/radiogroup.txt deleted file mode 100644 index eb918cf324a0..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/radiogroup.txt +++ /dev/null @@ -1,39 +0,0 @@ -form.attributes.radiogroup = FORM -form.attributes.radiogroup { - method = post - - rules { - 1 = required - 1 { - element = radiogroup - } - } - - 10 = RADIOGROUP - 10 { - legend = Radio Group test - name = radiogroup - - 10 = RADIO - 10 { - label = Option 1 - } - 20 = RADIO - 20 { - label = Option 2 - } - 30 = RADIO - 30 { - label = Option 3 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/reset.txt b/typo3/sysext/form/Documentation/Tests/Attributes/reset.txt deleted file mode 100644 index 29c0fa25e148..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/reset.txt +++ /dev/null @@ -1,205 +0,0 @@ -form.attributes.reset = FORM -form.attributes.reset { - method = post - - # Label test - 10 = FIELDSET - 10 { - legend = Label test - 10 = RESET - 10 { - label = label - } - 20 = RESET - 20 { - label.value = label.value - } - 30 = RESET - 30 { - label = TEXT - label { - value = TEXT cObj - } - } - } - - # Layout test - 20 = FIELDSET - 20 { - legend = Layout test - 10 = RESET - 10 { - label = label in front of input (default) - } - 20 = RESET - 20 { - layout ( - <input /> - <label /> - ) - label = label after input - } - 30 = RESET - 30 { - layout = <input /> - value = No label - } - } - - # Accesskey - 30 = FIELDSET - 30 { - legend = Accesskey test - 10 = RESET - 10 { - label = This reset button has an accesskey - accesskey = a - } - } - - # Alt - 40 = FIELDSET - 40 { - legend = Alt test - 10 = RESET - 10 { - label = This reset button has an alt attribute - alt = This is the alt attribute content - } - } - - # Class - 50 = FIELDSET - 50 { - legend = Class test - 10 = RESET - 10 { - label = This reset button has a class attribute - class = classAtribute - } - 20 = RESET - 20 { - label = Multiple classes - class = class1 class2 - } - } - - # Dir - 60 = FIELDSET - 60 { - legend = Dir test - 10 = RESET - 10 { - label = Dir ltr - dir = ltr - } - 20 = RESET - 20 { - label = Dir rtl - dir = rtl - } - 30 = RESET - 30 { - label = Wrong input in dir - dir = abc - } - } - - # Disabled - 70 = FIELDSET - 70 { - legend = Disabled test - 10 = RESET - 10 { - label = disabled=1 - disabled = 1 - } - 20 = RESET - 20 { - label = disabled=0 - disabled = 0 - } - 30 = RESET - 30 { - label = disabled=disabled - disabled = disabled - } - } - - # Id - 80 = FIELDSET - 80 { - legend = Id test - 10 = RESET - 10 { - label = This reset button has an id attribute and the label a for attribute - id = resetId - } - } - - # Lang - 90 = FIELDSET - 90 { - legend = Lang test - 10 = RESET - 10 { - label = This reset button has a lang attribute - lang = en-US - } - } - - # Name - 100 = FIELDSET - 100 { - legend = Name test - 10 = RESET - 10 { - label = This reset button has a name attribute - name = resetName - } - } - - # Style - 110 = FIELDSET - 110 { - legend = style test - 10 = RESET - 10 { - label = This reset button has a style attribute - style = border: 1px solid #000000 - } - } - - # Tabindex - 120 = FIELDSET - 120 { - legend = Tabindex test - 10 = RESET - 10 { - label = This reset button has a tabindex attribute - tabindex = 1 - } - } - - # Title - 130 = FIELDSET - 130 { - legend = Title test - 10 = RESET - 10 { - label = This reset button has a title attribute - title = This is the title text - } - } - - # Value - 140 = FIELDSET - 140 { - legend = Value test - 10 = RESET - 10 { - label = This reset button has a value attribute - value = Don't you dare to push this reset button - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/select.txt b/typo3/sysext/form/Documentation/Tests/Attributes/select.txt deleted file mode 100644 index 53da9a7d76a2..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/select.txt +++ /dev/null @@ -1,209 +0,0 @@ -form.tempOption = OPTION -form.tempOption { - data = optionData -} - -form.attributes.select = FORM -form.attributes.select { - method = post - - # Label test - 10 = FIELDSET - 10 { - legend = Label test - 10 = SELECT - 10 { - label = label - 1 < form.tempOption - } - 20 = SELECT - 20 { - label.value = label.value - 1 < form.tempOption - } - 30 = SELECT - 30 { - label = TEXT - label { - value = TEXT cObj - } - 1 < form.tempOption - } - } - - # Layout test - 20 = FIELDSET - 20 { - legend = Layout test - 10 = SELECT - 10 { - label = label in front of input (default) - 1 < form.tempOption - } - 20 = SELECT - 20 { - layout ( - <select> - <elements /> - </select> - <label /> - ) - label = label after input - 1 < form.tempOption - } - 30 = SELECT - 30 { - 1 < form.tempOption - } - } - - # Class - 30 = FIELDSET - 30 { - legend = Class test - 10 = SELECT - 10 { - label = This select has a class attribute - class = classAtribute - 1 < form.tempOption - } - 20 = SELECT - 20 { - label = Multiple classes - class = class1 class2 - 1 < form.tempOption - } - } - - # Disabled - 40 = FIELDSET - 40 { - legend = Disabled test - 10 = SELECT - 10 { - label = disabled=1 - disabled = 1 - 1 < form.tempOption - } - 20 = SELECT - 20 { - label = disabled=0 - disabled = 0 - 1 < form.tempOption - } - 30 = SELECT - 30 { - label = disabled=disabled - disabled = disabled - 1 < form.tempOption - } - } - - # Id - 50 = FIELDSET - 50 { - legend = Id test - 10 = SELECT - 10 { - label = This select has an id attribute and the label a for attribute - id = textlineId - 1 < form.tempOption - } - } - - # Lang - 60 = FIELDSET - 60 { - legend = Lang test - 10 = SELECT - 10 { - label = This select has a lang attribute - lang = en-US - 1 < form.tempOption - } - } - - # Multiple - 70 = FIELDSET - 70 { - legend = Multiple test - 10 = SELECT - 10 { - label = multiple=1 - multiple = 1 - 1 < form.tempOption - } - 20 = SELECT - 20 { - label = multiple=0 - multiple = 0 - 1 < form.tempOption - } - 30 = SELECT - 30 { - label = multiple=multiple - multiple = multiple - 1 < form.tempOption - } - } - - # Name - 110 = FIELDSET - 110 { - legend = Name test - 10 = SELECT - 10 { - label = This select has a name attribute - name = selectName - 1 < form.tempOption - } - } - - # Size - 120 = FIELDSET - 120 { - legend = Size test - 10 = SELECT - 10 { - label = This select has a size attribute - size = 10 - 1 < form.tempOption - } - } - - # Style - 130 = FIELDSET - 130 { - legend = style test - 10 = SELECT - 10 { - label = This select has a style attribute - style = border: 1px solid #000000 - 1 < form.tempOption - } - } - - # Tabindex - 140 = FIELDSET - 140 { - legend = Tabindex test - 10 = SELECT - 10 { - label = This select has a tabindex attribute - tabindex = 1 - 1 < form.tempOption - } - } - - # Title - 150 = FIELDSET - 150 { - legend = Title test - 10 = SELECT - 10 { - label = This select has a title attribute - title = This is the title text - 1 < form.tempOption - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/submit.txt b/typo3/sysext/form/Documentation/Tests/Attributes/submit.txt deleted file mode 100644 index 6abb1e2399b5..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/submit.txt +++ /dev/null @@ -1,205 +0,0 @@ -form.attributes.submit = FORM -form.attributes.submit { - method = post - - # Label test - 10 = FIELDSET - 10 { - legend = Label test - 10 = SUBMIT - 10 { - label = label - } - 20 = SUBMIT - 20 { - label.value = label.value - } - 30 = SUBMIT - 30 { - label = TEXT - label { - value = TEXT cObj - } - } - } - - # Layout test - 20 = FIELDSET - 20 { - legend = Layout test - 10 = SUBMIT - 10 { - label = label in front of input (default) - } - 20 = SUBMIT - 20 { - layout ( - <input /> - <label /> - ) - label = label after input - } - 30 = SUBMIT - 30 { - layout = <input /> - value = No label - } - } - - # Accesskey - 30 = FIELDSET - 30 { - legend = Accesskey test - 10 = SUBMIT - 10 { - label = This submit button has an accesskey - accesskey = a - } - } - - # Alt - 40 = FIELDSET - 40 { - legend = Alt test - 10 = SUBMIT - 10 { - label = This submit button has an alt attribute - alt = This is the alt attribute content - } - } - - # Class - 50 = FIELDSET - 50 { - legend = Class test - 10 = SUBMIT - 10 { - label = This submit button has a class attribute - class = classAtribute - } - 20 = SUBMIT - 20 { - label = Multiple classes - class = class1 class2 - } - } - - # Dir - 60 = FIELDSET - 60 { - legend = Dir test - 10 = SUBMIT - 10 { - label = Dir ltr - dir = ltr - } - 20 = SUBMIT - 20 { - label = Dir rtl - dir = rtl - } - 30 = SUBMIT - 30 { - label = Wrong input in dir - dir = abc - } - } - - # Disabled - 70 = FIELDSET - 70 { - legend = Disabled test - 10 = SUBMIT - 10 { - label = disabled=1 - disabled = 1 - } - 20 = SUBMIT - 20 { - label = disabled=0 - disabled = 0 - } - 30 = SUBMIT - 30 { - label = disabled=disabled - disabled = disabled - } - } - - # Id - 80 = FIELDSET - 80 { - legend = Id test - 10 = SUBMIT - 10 { - label = This submit button has an id attribute and the label a for attribute - id = submitId - } - } - - # Lang - 90 = FIELDSET - 90 { - legend = Lang test - 10 = SUBMIT - 10 { - label = This submit button has a lang attribute - lang = en-US - } - } - - # Name - 100 = FIELDSET - 100 { - legend = Name test - 10 = SUBMIT - 10 { - label = This submit button has a name attribute - name = submitName - } - } - - # Style - 110 = FIELDSET - 110 { - legend = style test - 10 = SUBMIT - 10 { - label = This submit button has a style attribute - style = border: 1px solid #000000 - } - } - - # Tabindex - 120 = FIELDSET - 120 { - legend = Tabindex test - 10 = SUBMIT - 10 { - label = This submit button has a tabindex attribute - tabindex = 1 - } - } - - # Title - 130 = FIELDSET - 130 { - legend = Title test - 10 = SUBMIT - 10 { - label = This submit button has a title attribute - title = This is the title text - } - } - - # Value - 140 = FIELDSET - 140 { - legend = Value test - 10 = SUBMIT - 10 { - label = This submit button has a value attribute - value = Don't you dare to push this reset button - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/textarea.txt b/typo3/sysext/form/Documentation/Tests/Attributes/textarea.txt deleted file mode 100644 index 92cd34c6388d..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/textarea.txt +++ /dev/null @@ -1,239 +0,0 @@ -form.attributes.textarea = FORM -form.attributes.textarea { - method = post - - # Label test - 10 = FIELDSET - 10 { - legend = Label test - 10 = TEXTAREA - 10 { - label = label - } - 20 = TEXTAREA - 20 { - label.value = label.value - } - 30 = TEXTAREA - 30 { - label = TEXT - label { - value = TEXT cObj - } - } - } - - # Layout test - 20 = FIELDSET - 20 { - legend = Layout test - 10 = TEXTAREA - 10 { - label = label in front of input (default) - } - 20 = TEXTAREA - 20 { - layout ( - <textarea /> - <label /> - ) - label = label after input - } - 30 = TEXTAREA - 30 { - value = No label - } - } - - # Data test - 30 = FIELDSET - 30 { - legend = Data test - 10 = TEXTAREA - 10 { - label = There should be data in the textarea - data = There is data in the textarea - } - } - - # Accesskey - 40 = FIELDSET - 40 { - legend = Accesskey test - 10 = TEXTAREA - 10 { - label = This textarea has an accesskey - accesskey = a - } - } - - # Class - 50 = FIELDSET - 50 { - legend = Class test - 10 = TEXTAREA - 10 { - label = This textarea has a class attribute - class = classAtribute - } - 20 = TEXTAREA - 20 { - label = Multiple classes - class = class1 class2 - } - } - - # Cols - 60 = FIELDSET - 60 { - legend = Cols test - 10 = TEXTAREA - 10 { - label = This textarea has a cols - cols = 100 - } - } - - # Dir - 70 = FIELDSET - 70 { - legend = Dir test - 10 = TEXTAREA - 10 { - label = Dir ltr - dir = ltr - } - 20 = TEXTAREA - 20 { - label = Dir rtl - dir = rtl - } - 30 = TEXTAREA - 30 { - label = Wrong input in dir - dir = abc - } - } - - # Disabled - 80 = FIELDSET - 80 { - legend = Disabled test - 10 = TEXTAREA - 10 { - label = disabled=1 - disabled = 1 - } - 20 = TEXTAREA - 20 { - label = disabled=0 - disabled = 0 - } - 30 = TEXTAREA - 30 { - label = disabled=disabled - disabled = disabled - } - } - - # Id - 90 = FIELDSET - 90 { - legend = Id test - 10 = TEXTAREA - 10 { - label = This textarea has an id attribute and the label a for attribute - id = textareaId - } - } - - # Lang - 100 = FIELDSET - 100 { - legend = Lang test - 10 = TEXTAREA - 10 { - label = This textarea has a lang attribute - lang = en-US - } - } - - # Name - 110 = FIELDSET - 110 { - legend = Name test - 10 = TEXTAREA - 10 { - label = This textarea has a name attribute - name = textareaName - } - } - - # Readonly - 120 = FIELDSET - 120 { - legend = Readonly test - 10 = TEXTAREA - 10 { - label = readonly=1 - readonly = 1 - data = This should be readonly - } - 20 = TEXTAREA - 20 { - label = readonly=0 - readonly = 0 - data = This should NOT be readonly - } - 30 = TEXTAREA - 30 { - label = readonly=readonly - readonly = readonly - data = This should be readonly - } - } - - # Rows - 130 = FIELDSET - 130 { - legend = Rows test - 10 = TEXTAREA - 10 { - label = This textarea has rows - rows = 100 - } - } - - # Style - 140 = FIELDSET - 140 { - legend = style test - 10 = TEXTAREA - 10 { - label = This textarea has a style attribute - style = border: 1px solid #000000 - } - } - - # Tabindex - 150 = FIELDSET - 150 { - legend = Tabindex test - 10 = TEXTAREA - 10 { - label = This textarea has a tabindex attribute - tabindex = 1 - } - } - - # Title - 160 = FIELDSET - 160 { - legend = Title test - 10 = TEXTAREA - 10 { - label = This textarea has a title attribute - title = This is the title text - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Attributes/textline.txt b/typo3/sysext/form/Documentation/Tests/Attributes/textline.txt deleted file mode 100644 index 4d7251be836a..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Attributes/textline.txt +++ /dev/null @@ -1,250 +0,0 @@ -form.attributes.textline = FORM -form.attributes.textline { - method = post - - # Label test - 10 = FIELDSET - 10 { - legend = Label test - 10 = TEXTLINE - 10 { - label = label - } - 20 = TEXTLINE - 20 { - label.value = label.value - } - 30 = TEXTLINE - 30 { - label = TEXT - label { - value = TEXT cObj - } - } - } - - # Layout test - 20 = FIELDSET - 20 { - legend = Layout test - 10 = TEXTLINE - 10 { - label = label in front of input (default) - } - 20 = TEXTLINE - 20 { - layout ( - <input /> - <label /> - ) - label = label after input - } - 30 = TEXTLINE - 30 { - value = No label - } - } - - # Accesskey - 30 = FIELDSET - 30 { - legend = Accesskey test - 10 = TEXTLINE - 10 { - label = This textline has an accesskey - accesskey = a - } - } - - # Alt - 40 = FIELDSET - 40 { - legend = Alt test - 10 = TEXTLINE - 10 { - label = This submit button has an alt attribute - alt = This is the alt attribute content - } - } - - # Class - 50 = FIELDSET - 50 { - legend = Class test - 10 = TEXTLINE - 10 { - label = This textline has a class attribute - class = classAtribute - } - 20 = TEXTLINE - 20 { - label = Multiple classes - class = class1 class2 - } - } - - # Dir - 60 = FIELDSET - 60 { - legend = Dir test - 10 = TEXTLINE - 10 { - label = Dir ltr - dir = ltr - } - 20 = TEXTLINE - 20 { - label = Dir rtl - dir = rtl - } - 30 = TEXTLINE - 30 { - label = Wrong input in dir - dir = abc - } - } - - # Disabled - 70 = FIELDSET - 70 { - legend = Disabled test - 10 = TEXTLINE - 10 { - label = disabled=1 - disabled = 1 - } - 20 = TEXTLINE - 20 { - label = disabled=0 - disabled = 0 - } - 30 = TEXTLINE - 30 { - label = disabled=disabled - disabled = disabled - } - } - - # Id - 80 = FIELDSET - 80 { - legend = Id test - 10 = TEXTLINE - 10 { - label = This textline has an id attribute and the label a for attribute - id = textlineId - } - } - - # Lang - 90 = FIELDSET - 90 { - legend = Lang test - 10 = TEXTLINE - 10 { - label = This textline has a lang attribute - lang = en-US - } - } - - # Maxlength - 100 = FIELDSET - 100 { - legend = Maxlength test - 10 = TEXTLINE - 10 { - label = This textline has a maxlength attribute - maxlength = 10 - } - } - - # Name - 110 = FIELDSET - 110 { - legend = Name test - 10 = TEXTLINE - 10 { - label = This textline has a name attribute - name = textlineName - } - } - - # Readonly - 120 = FIELDSET - 120 { - legend = Readonly test - 10 = TEXTLINE - 10 { - label = readonly=1 - readonly = 1 - data = This should be readonly - } - 20 = TEXTLINE - 20 { - label = readonly=0 - readonly = 0 - data = This should NOT be readonly - } - 30 = TEXTLINE - 30 { - label = readonly=readonly - readonly = readonly - data = This should be readonly - } - } - - # Size - 130 = FIELDSET - 130 { - legend = Size test - 10 = TEXTLINE - 10 { - label = This textline has a size attribute - size = 10 - } - } - - # Style - 140 = FIELDSET - 140 { - legend = style test - 10 = TEXTLINE - 10 { - label = This textline has a style attribute - style = border: 1px solid #000000 - } - } - - # Tabindex - 150 = FIELDSET - 150 { - legend = Tabindex test - 10 = TEXTLINE - 10 { - label = This textline has a tabindex attribute - tabindex = 1 - } - } - - # Title - 160 = FIELDSET - 160 { - legend = Title test - 10 = TEXTLINE - 10 { - label = This textline has a title attribute - title = This is the title text - } - } - - # Value - 170 = FIELDSET - 170 { - legend = Value test - 10 = TEXTLINE - 10 { - label = This textline has a value attribute - value = The value attribute - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/alphabetic.txt b/typo3/sysext/form/Documentation/Tests/Filter/alphabetic.txt deleted file mode 100644 index 9df926f7080c..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/alphabetic.txt +++ /dev/null @@ -1,56 +0,0 @@ -form.filter.alphabetic = FORM -form.filter.alphabetic { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Alphabetic - 10 = FIELDSET - 10 { - legend = Alphabetic test - 10 = TEXTLINE - 10 { - label = no settings - value = John Doe 3 - filters { - 1 = alphabetic - } - } - 20 = TEXTLINE - 20 { - label = allowWhiteSpace=1 - value = John Doe 3 - filters { - 1 = alphabetic - 1 { - allowWhiteSpace = 1 - } - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/alphanumeric.txt b/typo3/sysext/form/Documentation/Tests/Filter/alphanumeric.txt deleted file mode 100644 index f6c965c70429..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/alphanumeric.txt +++ /dev/null @@ -1,56 +0,0 @@ -form.filter.alphanumeric = FORM -form.filter.alphanumeric { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Alphanumeric - 10 = FIELDSET - 10 { - legend = Alphanumeric test - 10 = TEXTLINE - 10 { - label = no settings - value = John Doe 3 #$ - filters { - 1 = alphanumeric - } - } - 20 = TEXTLINE - 20 { - label = allowWhiteSpace=1 - value = John Doe 3 #$ - filters { - 1 = alphanumeric - 1 { - allowWhiteSpace = 1 - } - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/currency.txt b/typo3/sysext/form/Documentation/Tests/Filter/currency.txt deleted file mode 100644 index dc5aeb2e331b..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/currency.txt +++ /dev/null @@ -1,68 +0,0 @@ -form.filter.currency = FORM -form.filter.currency { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Currency - 10 = FIELDSET - 10 { - legend = Currency test - 10 = TEXTLINE - 10 { - label = no settings - value = John Doe 3 #$ - filters { - 1 = currency - } - } - 20 = TEXTLINE - 20 { - label = decimalPoint=, - value = John Doe 3 #$ - filters { - 1 = currency - 1 { - decimalPoint = , - } - } - } - 30 = TEXTLINE - 30 { - label = decimalPoint=, / thousandSeparator=space - value = John Doe 3 #$ - filters { - 1 = currency - 1 { - decimalPoint = , - thousandSeparator = space - } - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/digit.txt b/typo3/sysext/form/Documentation/Tests/Filter/digit.txt deleted file mode 100644 index d6fc155be035..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/digit.txt +++ /dev/null @@ -1,45 +0,0 @@ -form.filter.digit = FORM -form.filter.digit { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Digit - 10 = FIELDSET - 10 { - legend = Digit test - 10 = TEXTLINE - 10 { - label = no settings - value = John Doe 3 #$ - filters { - 1 = digit - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/integer.txt b/typo3/sysext/form/Documentation/Tests/Filter/integer.txt deleted file mode 100644 index e6cc33b24039..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/integer.txt +++ /dev/null @@ -1,45 +0,0 @@ -form.filter.integer = FORM -form.filter.integer { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Integer - 10 = FIELDSET - 10 { - legend = Integer test - 10 = TEXTLINE - 10 { - label = no settings - value = John Doe 3 #$ - filters { - 1 = integer - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/lowercase.txt b/typo3/sysext/form/Documentation/Tests/Filter/lowercase.txt deleted file mode 100644 index 536013a499f1..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/lowercase.txt +++ /dev/null @@ -1,45 +0,0 @@ -form.filter.lowercase = FORM -form.filter.lowercase { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Lower case - 10 = FIELDSET - 10 { - legend = Lower case test - 10 = TEXTLINE - 10 { - label = no settings - value = John Doe 3 #$ - filters { - 1 = lowercase - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/regexp.txt b/typo3/sysext/form/Documentation/Tests/Filter/regexp.txt deleted file mode 100644 index 04cf247fe6a4..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/regexp.txt +++ /dev/null @@ -1,48 +0,0 @@ -form.filter.regexp = FORM -form.filter.regexp { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Regular expression - 10 = FIELDSET - 10 { - legend = Regular expression test - 10 = TEXTLINE - 10 { - label = using regexp /[^a-zA-Z]/u - value = John Doe 3 #$ - filters { - 1 = regexp - 1 { - expression = /[^a-zA-Z]/u - } - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/stripnewlines.txt b/typo3/sysext/form/Documentation/Tests/Filter/stripnewlines.txt deleted file mode 100644 index ebc546270a3a..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/stripnewlines.txt +++ /dev/null @@ -1,49 +0,0 @@ -form.filter.stripnewlines = FORM -form.filter.stripnewlines { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Strip newlines - 10 = FIELDSET - 10 { - legend = Strip newlines test - 10 = TEXTAREA - 10 { - label = No settings - data ( -line 1 -line 2 -line 3 - ) - filters { - 1 = stripnewlines - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/titlecase.txt b/typo3/sysext/form/Documentation/Tests/Filter/titlecase.txt deleted file mode 100644 index cfa4e5840dc5..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/titlecase.txt +++ /dev/null @@ -1,45 +0,0 @@ -form.filter.titlecase = FORM -form.filter.titlecase { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Title case - 10 = FIELDSET - 10 { - legend = Title case test - 10 = TEXTLINE - 10 { - label = No settings - value = kasper skårhøj - filters { - 1 = titlecase - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/trim.txt b/typo3/sysext/form/Documentation/Tests/Filter/trim.txt deleted file mode 100644 index 60d87d762f89..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/trim.txt +++ /dev/null @@ -1,60 +0,0 @@ -form.filter.trim = FORM -form.filter.trim { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Trim - 10 = FIELDSET - 10 { - legend = Trim test - 10 = TEXTAREA - 10 { - label = No settings - data ( - line 1 - line 2 - line 3 - ) - filters { - 1 = trim - } - } - 20 = TEXTLINE - 20 { - label = characterList=a,b,f,g - value = abcdefg - filters { - 1 = trim - 1 { - characterList = a,b,f,g - } - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Filter/uppercase.txt b/typo3/sysext/form/Documentation/Tests/Filter/uppercase.txt deleted file mode 100644 index b064a86e4223..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Filter/uppercase.txt +++ /dev/null @@ -1,45 +0,0 @@ -form.filter.uppercase = FORM -form.filter.uppercase { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Upper case - 10 = FIELDSET - 10 { - legend = Upper case test - 10 = TEXTLINE - 10 { - label = no settings - value = John Doe 3 #$ - filters { - 1 = uppercase - } - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Request/checkbox.txt b/typo3/sysext/form/Documentation/Tests/Request/checkbox.txt deleted file mode 100644 index e938f9cbd577..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Request/checkbox.txt +++ /dev/null @@ -1,70 +0,0 @@ -form.request.checkbox = FORM -form.request.checkbox { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Checked test - 10 = FIELDSET - 10 { - legend = Checked test - 10 = CHECKBOX - 10 { - label = Uncheck me - checked = checked - } - 20 = CHECKBOX - 20 { - label = Check me - } - } - - # Multiple test - 20 = FIELDSET - 20 { - legend = Multiple test - 10 = CHECKBOX - 10 { - label = Check 1 - name = checkmultiple - value = check1 - } - 20 = CHECKBOX - 20 { - label = Check 2 - name = checkmultiple - value = check2 - } - 30 = CHECKBOX - 30 { - label = Check 3 - name = checkmultiple - value = check3 - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Request/option.txt b/typo3/sysext/form/Documentation/Tests/Request/option.txt deleted file mode 100644 index c02c6a440656..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Request/option.txt +++ /dev/null @@ -1,127 +0,0 @@ -form.request.option = FORM -form.request.option { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Selected test - 10 = FIELDSET - 10 { - legend = Selected test - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = Select me - value = option2 - } - 2 = OPTION - 2 { - data = Deselect me - selected = selected - value = option1 - } - } - } - - # Regular test - 20 = FIELDSET - 20 { - legend = Regular test - 10 = SELECT - 10 { - 1 = OPTION - 1 { - data = Option 1 - value = option1 - } - 2 = OPTION - 2 { - data = Option 2 - value = option2 - } - 3 = OPTION - 3 { - data = Option 3 - value = option3 - } - } - } - - # Multiple test - 30 = FIELDSET - 30 { - legend = Regular test - 10 = SELECT - 10 { - multiple = multiple - 1 = OPTION - 1 { - data = Option 1 - value = option1 - } - 2 = OPTION - 2 { - data = Option 2 - value = option2 - } - 3 = OPTION - 3 { - data = Option 3 - value = option3 - } - } - } - - # Multiple selected test - 40 = FIELDSET - 40 { - legend = Regular test - 10 = SELECT - 10 { - multiple = multiple - 1 = OPTION - 1 { - data = Uncheck me 1 - value = option1 - selected = selected - } - 2 = OPTION - 2 { - data = Check me - value = option2 - } - 3 = OPTION - 3 { - data = Uncheck me 2 - value = option3 - selected = selected - } - } - } - - # A textline which must be empty to test the above - 50 = FIELDSET - 50 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 60 = FIELDSET - 60 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Request/radio.txt b/typo3/sysext/form/Documentation/Tests/Request/radio.txt deleted file mode 100644 index 0cb778fdd47c..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Request/radio.txt +++ /dev/null @@ -1,74 +0,0 @@ -form.request.radio = FORM -form.request.radio { - method = post - - rules { - 1 = required - 1 { - element = textlineField - } - } - - # Checked test - 10 = FIELDSET - 10 { - legend = Checked test - 10 = RADIO - 10 { - label = Uncheck me - checked = checked - name = checkmultiple - value = check1 - } - 20 = RADIO - 20 { - label = Check me - name = checkmultiple - value = check2 - } - } - - # Radio group - 20 = FIELDSET - 20 { - legend = Radio group - 10 = RADIO - 10 { - label = Check 1 - name = checkmultiple2 - value = check1 - } - 20 = RADIO - 20 { - label = Check 2 - name = checkmultiple2 - value = check2 - } - 30 = RADIO - 30 { - label = Check 3 - name = checkmultiple2 - value = check3 - } - } - - # A textline which must be empty to test the above - 30 = FIELDSET - 30 { - legend = Leave empty to get the form back after submitting - 10 = TEXTLINE - 10 { - name = textlineField - label = Leave me empty - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/alphabetic.txt b/typo3/sysext/form/Documentation/Tests/Validation/alphabetic.txt deleted file mode 100644 index bfdb5ec6289b..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/alphabetic.txt +++ /dev/null @@ -1,43 +0,0 @@ -form.validation.alphabetic = FORM -form.validation.alphabetic { - method = post - - rules { - 1 = alphabetic - 1 { - element = alphabetic1 - } - 2 = alphabetic - 2 { - element = alphabetic2 - allowWhiteSpace = 1 - } - } - - # Alphabetic - 10 = FIELDSET - 10 { - legend = Alphabetic test - 10 = TEXTLINE - 10 { - label = no settings - value = 12345 - name = alphabetic1 - } - 20 = TEXTLINE - 20 { - label = allowWhiteSpace=1 - value = 12345 - name = alphabetic2 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/alphanumeric.txt b/typo3/sysext/form/Documentation/Tests/Validation/alphanumeric.txt deleted file mode 100644 index d25e82610fe8..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/alphanumeric.txt +++ /dev/null @@ -1,43 +0,0 @@ -form.validation.alphanumeric = FORM -form.validation.alphanumeric { - method = post - - rules { - 1 = alphanumeric - 1 { - element = alphanumeric1 - } - 2 = alphanumeric - 2 { - element = alphanumeric2 - allowWhiteSpace = 1 - } - } - - # Alpahnumeric - 10 = FIELDSET - 10 { - legend = Alphanumeric test - 10 = TEXTLINE - 10 { - label = no settings - value = !@#$%^ - name = alphanumeric1 - } - 20 = TEXTLINE - 20 { - label = allowWhiteSpace=1 - value = !@#$%^ - name = alphanumeric2 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/between.txt b/typo3/sysext/form/Documentation/Tests/Validation/between.txt deleted file mode 100644 index 3a324084d914..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/between.txt +++ /dev/null @@ -1,47 +0,0 @@ -form.validation.between = FORM -form.validation.between { - method = post - - rules { - 1 = between - 1 { - element = between1 - minimum = 5 - maximum = 10 - } - 2 = between - 2 { - element = between2 - minimum = 5 - maximum = 10 - inclusive = 1 - } - } - - # Between - 10 = FIELDSET - 10 { - legend = Between test - 10 = TEXTLINE - 10 { - label = Between 5 and 10 - value = abcdef - name = between1 - } - 20 = TEXTLINE - 20 { - label = Between 5 and 10, inclusive - value = abcdef - name = between2 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/combined.txt b/typo3/sysext/form/Documentation/Tests/Validation/combined.txt deleted file mode 100644 index 54095e04e203..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/combined.txt +++ /dev/null @@ -1,36 +0,0 @@ -form.validation.combined = FORM -form.validation.combined { - method = post - - rules { - 1 = required - 1 { - element = email1 - } - 2 = email - 2 { - element = email1 - } - } - - # Email - 10 = FIELDSET - 10 { - legend = Email test - 10 = TEXTLINE - 10 { - label = Email address - value = abc!@# - name = email1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/date.txt b/typo3/sysext/form/Documentation/Tests/Validation/date.txt deleted file mode 100644 index e8612654ced2..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/date.txt +++ /dev/null @@ -1,32 +0,0 @@ -form.validation.date = FORM -form.validation.date { - method = post - - rules { - 1 = date - 1 { - element = date1 - } - } - - # Date - 10 = FIELDSET - 10 { - legend = Date test - 10 = TEXTLINE - 10 { - label = Date %e-%m-%Y - value = 34-13-01 - name = date1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/digit.txt b/typo3/sysext/form/Documentation/Tests/Validation/digit.txt deleted file mode 100644 index 45dd766f1774..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/digit.txt +++ /dev/null @@ -1,32 +0,0 @@ -form.validation.digit = FORM -form.validation.digit { - method = post - - rules { - 1 = digit - 1 { - element = digit1 - } - } - - # Digit - 10 = FIELDSET - 10 { - legend = Digit test - 10 = TEXTLINE - 10 { - label = Digit - value = abc!@# - name = digit1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/email.txt b/typo3/sysext/form/Documentation/Tests/Validation/email.txt deleted file mode 100644 index bd6f487fd303..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/email.txt +++ /dev/null @@ -1,32 +0,0 @@ -form.validation.email = FORM -form.validation.email { - method = post - - rules { - 1 = email - 1 { - element = email1 - } - } - - # Email - 10 = FIELDSET - 10 { - legend = Email test - 10 = TEXTLINE - 10 { - label = Email address - value = abc!@# - name = email1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/equals.txt b/typo3/sysext/form/Documentation/Tests/Validation/equals.txt deleted file mode 100644 index 12e0968ca942..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/equals.txt +++ /dev/null @@ -1,39 +0,0 @@ -form.validation.equals = FORM -form.validation.equals { - method = post - - rules { - 1 = equals - 1 { - element = equals1 - field = equals2 - } - } - - # Equals - 10 = FIELDSET - 10 { - legend = Equals test - 10 = TEXTLINE - 10 { - label = First field - value = abcde - name = equals1 - } - 20 = TEXTLINE - 20 { - label = Second field - value = abcdef - name = equals2 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/float.txt b/typo3/sysext/form/Documentation/Tests/Validation/float.txt deleted file mode 100644 index e82c3f6b168a..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/float.txt +++ /dev/null @@ -1,32 +0,0 @@ -form.validation.float = FORM -form.validation.float { - method = post - - rules { - 1 = float - 1 { - element = float1 - } - } - - # Float - 10 = FIELDSET - 10 { - legend = Float test - 10 = TEXTLINE - 10 { - label = Float - value = abcde - name = float1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/greaterthan.txt b/typo3/sysext/form/Documentation/Tests/Validation/greaterthan.txt deleted file mode 100644 index 01385a0dc060..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/greaterthan.txt +++ /dev/null @@ -1,33 +0,0 @@ -form.validation.greaterthan = FORM -form.validation.greaterthan { - method = post - - rules { - 1 = greaterthan - 1 { - element = greaterthan1 - minimum = 5 - } - } - - # Greater than - 10 = FIELDSET - 10 { - legend = Greater than test - 10 = TEXTLINE - 10 { - label = Greater than - value = abcde - name = greaterthan1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/inarray.txt b/typo3/sysext/form/Documentation/Tests/Validation/inarray.txt deleted file mode 100644 index b21add2530de..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/inarray.txt +++ /dev/null @@ -1,38 +0,0 @@ -form.validation.inarray = FORM -form.validation.inarray { - method = post - - rules { - 1 = inarray - 1 { - element = inarray1 - array { - 1 = TYPO3 - 2 = FLOW3 - 3 = CMS - 4 = OPEN SOURCE - } - } - } - - # In Array - 10 = FIELDSET - 10 { - legend = In array test - 10 = TEXTLINE - 10 { - label = Type TYPO3, FLOW3, CMS or OPEN SOURCE here - value = abcde - name = inarray1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/integer.txt b/typo3/sysext/form/Documentation/Tests/Validation/integer.txt deleted file mode 100644 index edb52352c3a1..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/integer.txt +++ /dev/null @@ -1,32 +0,0 @@ -form.validation.integer = FORM -form.validation.integer { - method = post - - rules { - 1 = integer - 1 { - element = integer1 - } - } - - # Integer - 10 = FIELDSET - 10 { - legend = Integer test - 10 = TEXTLINE - 10 { - label = Type an integer - value = abcde - name = integer1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/ip.txt b/typo3/sysext/form/Documentation/Tests/Validation/ip.txt deleted file mode 100644 index d8005a3fad4a..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/ip.txt +++ /dev/null @@ -1,32 +0,0 @@ -form.validation.ip = FORM -form.validation.ip { - method = post - - rules { - 1 = ip - 1 { - element = ip1 - } - } - - # IP - 10 = FIELDSET - 10 { - legend = IP test - 10 = TEXTLINE - 10 { - label = Type an IP - value = abcde - name = ip1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/length.txt b/typo3/sysext/form/Documentation/Tests/Validation/length.txt deleted file mode 100644 index fd9cdcdb5e52..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/length.txt +++ /dev/null @@ -1,45 +0,0 @@ -form.validation.length = FORM -form.validation.length { - method = post - - rules { - 1 = length - 1 { - element = length1 - minimum = 6 - } - 2 = length - 2 { - element = length2 - minimum = 2 - maximum = 4 - } - } - - # Length - 10 = FIELDSET - 10 { - legend = Length test - 10 = TEXTLINE - 10 { - label = Only minimum - value = abcde - name = length1 - } - 20 = TEXTLINE - 20 { - label = Minimum and maximum - value = abcde - name = length2 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/lessthan.txt b/typo3/sysext/form/Documentation/Tests/Validation/lessthan.txt deleted file mode 100644 index e2a80edea896..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/lessthan.txt +++ /dev/null @@ -1,33 +0,0 @@ -form.validation.lessthan = FORM -form.validation.lessthan { - method = post - - rules { - 1 = lessthan - 1 { - element = lessthan1 - maximum = 10 - } - } - - # Less than - 10 = FIELDSET - 10 { - legend = Less than test - 10 = TEXTLINE - 10 { - label = Less than - value = abcde - name = lessthan1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/regexp.txt b/typo3/sysext/form/Documentation/Tests/Validation/regexp.txt deleted file mode 100644 index 21cf11f9d8e5..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/regexp.txt +++ /dev/null @@ -1,33 +0,0 @@ -form.validation.regexp = FORM -form.validation.regexp { - method = post - - rules { - 1 = regexp - 1 { - element = regexp1 - expression = /\b(([01]?\d?\d|2[0-4]\d|25[0-5])\.){3}([01]?\d?\d|2[0-4]\d|25[0-5])\b/ - } - } - - # Regexp - 10 = FIELDSET - 10 { - legend = Regexp test - 10 = TEXTLINE - 10 { - label = IP number - value = abcde - name = regexp1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/required.txt b/typo3/sysext/form/Documentation/Tests/Validation/required.txt deleted file mode 100644 index 8c01e733a989..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/required.txt +++ /dev/null @@ -1,31 +0,0 @@ -form.validation.required = FORM -form.validation.required { - method = post - - rules { - 1 = required - 1 { - element = required1 - } - } - - # Required - 10 = FIELDSET - 10 { - legend = Required test - 10 = TEXTLINE - 10 { - label = Leave empty to trigger error - name = required1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Documentation/Tests/Validation/uri.txt b/typo3/sysext/form/Documentation/Tests/Validation/uri.txt deleted file mode 100644 index 386c975451e0..000000000000 --- a/typo3/sysext/form/Documentation/Tests/Validation/uri.txt +++ /dev/null @@ -1,32 +0,0 @@ -form.validation.uri = FORM -form.validation.uri { - method = post - - rules { - 1 = uri - 1 { - element = uri1 - } - } - - # URI - 10 = FIELDSET - 10 { - legend = URI test - 10 = TEXTLINE - 10 { - label = URI - value = abcde - name = uri1 - } - } - - # Submit - 40 = FIELDSET - 40 { - 10 = SUBMIT - 10 { - value = Submit - } - } -} \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Layouts/FormEditor.html b/typo3/sysext/form/Resources/Private/Backend/Layouts/FormEditor.html new file mode 100644 index 000000000000..9fcdc2812e8e --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Layouts/FormEditor.html @@ -0,0 +1,54 @@ +{namespace core = TYPO3\CMS\Core\ViewHelpers} +{namespace formvh = TYPO3\CMS\Form\ViewHelpers} + +<formvh:be.pageRenderer + loadExtJsTheme="false" + loadJQuery="true" + includeCssFiles="{stylesheets}" + addInlineSettings="{addInlineSettings}" + includeJsFiles="{0: 'EXT:backend/Resources/Public/JavaScript/jsfunc.tbe_editor.js'}" +/> + +<div data-identifier="moduleLoadingIndicator" class="form-editor-loading-spinner"> + <core:icon identifier="spinner-circle-dark" size="default" /> + <div class="form-editor-loading-spinner-label"><f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.loading" /></div> +</div> + +<div data-identifier="moduleWrapper" class="hidden"> + <f:for each="{formEditorTemplates}" key="formEditorTemplateName" as="formEditorTemplate"> + <script type="text/x-formeditor-template" data-template-name="{formEditorTemplateName}"> + <f:format.raw>{formEditorTemplate}</f:format.raw> + </script> + </f:for> + + <section id="t3-form-navigation-component" class="t3-form-x-component" data-identifier="structureSection"> + <div id="t3-form-structure-panel"> + <f:render section="ElementSidebarTree"/> + </div> + </section> + + <section id="t3-form-stage-container" data-identifier="stageContainer"> + <div id="t3-form-stage-inner-container"> + <header id="t3-form-stage-header"> + <f:render section="Header"/> + </header> + <div id="t3-form-stage-content"> + <f:render section="Stage"/> + </div> + </div> + </section> + + <section id="t3-form-inspector-panels-container" class="t3-form-x-component" data-identifier="inspectorSection"> + <f:render section="Inspector"/> + </section> +</div> + +<script type="text/javascript"> + require(['{dynamicRequireJsModules.app}', '{dynamicRequireJsModules.mediator}', '{dynamicRequireJsModules.viewModel}'], function (formEditorApp, mediator, viewModel) { + window.TYPO3.FORMEDITOR_APP = formEditorApp.getInstance( + <f:format.htmlentitiesDecode>{formEditorAppInitialData}</f:format.htmlentitiesDecode>, + mediator, + viewModel + ).run(); + }); +</script> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Layouts/FormManager.html b/typo3/sysext/form/Resources/Private/Backend/Layouts/FormManager.html new file mode 100644 index 000000000000..3d0054fb9b68 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Layouts/FormManager.html @@ -0,0 +1,7 @@ +<f:be.pageRenderer + loadExtJsTheme="false" + includeCssFiles="{stylesheets}" +/> + +<f:flashMessages class="flashmessages" /> +<f:render section="MainContent" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/FileUploadTemplate.html b/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/FileUploadTemplate.html new file mode 100644 index 000000000000..7ded50d8f187 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/FileUploadTemplate.html @@ -0,0 +1,33 @@ +<div class="meta-label"> + <span data-template-property="_type"></span>: <span data-template-property="_identifier"></span> +</div> + +<div class="t3-form-form-element-body"> + <div class="t3-form-icon-container"> + <span data-identifier="elementIcon"></span> + </div> + + <div class="t3-form-element-info"> + <div class="element-label-container"> + <div class="element-label"> + <span data-template-property="label"></span> + <span data-template-property="_required"></span> + </div> + </div> + <div class="element-content"> + <div data-template-property="properties.saveToFileMount" /> + <div data-identifier="multiValueContainer" data-template-property="properties.allowedMimeTypes"> + <div data-template-property="_value" /> + </div> + </div> + </div> + + <div class="t3-form-validator-info" data-identifier="validators"> + <span data-identifier="validatorIcon"></span> + <div class="t3-form-validator-list"> + <div data-identifier="validatorsContainer"> + <div class="validator-label" data-template-property="_label"></div> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/SelectTemplate.html b/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/SelectTemplate.html new file mode 100644 index 000000000000..878e58060910 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/SelectTemplate.html @@ -0,0 +1,32 @@ +<div class="meta-label"> + <span data-template-property="_type"></span>: <span data-template-property="_identifier"></span> +</div> + +<div class="t3-form-form-element-body"> + <div class="t3-form-icon-container"> + <span data-identifier="elementIcon"></span> + </div> + + <div class="t3-form-element-info"> + <div class="element-label-container"> + <div class="element-label"> + <span data-template-property="label"></span> + <span data-template-property="_required"></span> + </div> + </div> + <div class="element-content"> + <div data-identifier="multiValueContainer" data-template-property="properties.options"> + <div data-template-property="_label"></div> + </div> + </div> + </div> + + <div class="t3-form-validator-info" data-identifier="validators"> + <span data-identifier="validatorIcon"></span> + <div class="t3-form-validator-list"> + <div data-identifier="validatorsContainer"> + <div class="validator-label" data-template-property="_label"></div> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/SimpleTemplate.html b/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/SimpleTemplate.html new file mode 100644 index 000000000000..c2a539144515 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Partials/FormEditor/Stage/SimpleTemplate.html @@ -0,0 +1,27 @@ +<div class="meta-label"> + <span data-template-property="_type"></span>: <span data-template-property="_identifier"></span> +</div> + +<div class="t3-form-form-element-body"> + <div class="t3-form-icon-container"> + <span data-identifier="elementIcon"></span> + </div> + + <div class="t3-form-element-info"> + <div class="element-label-container"> + <div class="element-label"> + <span data-template-property="label"></span> + <span data-template-property="_required"></span> + </div> + </div> + </div> + + <div class="t3-form-validator-info" data-identifier="validators"> + <span data-identifier="validatorIcon"></span> + <div class="t3-form-validator-list"> + <div data-identifier="validatorsContainer"> + <div class="validator-label" data-template-property="_label"></div> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Index.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Index.html new file mode 100644 index 000000000000..2dec63446d1a --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Index.html @@ -0,0 +1,64 @@ +{namespace core = TYPO3\CMS\Core\ViewHelpers} + +<f:layout name="FormEditor" /> + +<f:section name="ElementSidebarTree"> + <div class="t3-form-x-component-inner-wrapper"> + <div id="t3-form-navigation-component-tree-root-container" data-identifier="treeRootContainer"> + <core:icon identifier="content-elements-mailform" /> + <span id="t3-form-navigation-component-tree-root" data-identifier="treeRootElement"></span> + </div> + <div class="tree" data-identifier="structure-element"></div> + <div class="form-group col-sm-12"> + <div class="btn-group btn-group-sm" role="group"> + <a class="btn btn-default t3-form-element-new-page-button" href="#" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.new_page_button')}" data-identifier="treeNewPageBottom"><core:icon identifier="actions-page-new" /> {f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.new_page_button')}</a> + </div> + </div> + </div> +</f:section> + +<f:section name="Header"> + <h1><span id="t3-form-form-definition-label" data-identifier="formDefinitionLabel"></span></h1> +</f:section> + +<f:section name="Stage"> + <div class="panel panel-default" data-identifier="stagePanel"> + <div class="panel-heading" data-identifier="panelHeading"> + <span data-identifier="stageHeaderToolbar"> + <div class="btn-group"> + <button class="btn btn-default" title="" data-identifier="buttonViewModePreview"><core:icon identifier="actions-document-view" alternativeMarkupIdentifier="inline" /></button> + <button class="btn btn-default" title="" data-identifier="buttonViewModeAbstract"><core:icon identifier="actions-document-open" alternativeMarkupIdentifier="inline" /></button> + </div> + <div class="pull-right"> + <span class="paginiation-label" data-identifier="paginationTitle"></span> + <div class="btn-group"> + <button class="btn btn-default" title="" data-identifier="buttonPaginationPrevious"><core:icon identifier="actions-view-paging-previous" alternativeMarkupIdentifier="inline" /></button> + <button class="btn btn-default" title="" data-identifier="buttonPaginationNext"><core:icon identifier="actions-view-paging-next" alternativeMarkupIdentifier="inline" /></button> + </div> + </div> + </span> + </div> + <div class="form-section" data-identifier="stageSection"> + <div class="row"> + <div id="t3-form-stage" class="form-group col-sm-12" data-identifier="stageArea"></div> + </div> + <div class="row" data-identifier="stageNewElementRow"> + <div class="form-group col-sm-12"> + <div class="t3-form-new-element-container"> + <div class="btn-group btn-group-sm" role="group"> + <a class="btn btn-default" href="#" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.stage.toolbar.new_element')}" data-identifier="stageNewElementBottom"><core:icon identifier="actions-document-new" /> {f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.stage.toolbar.new_element')}</a> + </div> + </div> + </div> + </div> + </div> + </div> +</f:section> + +<f:section name="Inspector"> + <div id="t3-form-inspector-panels"> + <div class="t3-form-x-component-inner-wrapper"> + <div id="t3-form-inspector" data-identifier="inspector"></div> + </div> + </div> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/CheckboxEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/CheckboxEditor.html new file mode 100644 index 000000000000..40f29ea016b1 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/CheckboxEditor.html @@ -0,0 +1,10 @@ +<div class="form-editor"> + <div class="t3-form-control-group form-group"> + <label> + <span data-template-property="label" /> + <div class="t3-form-controls" data-identifier="inspectorEditorControlsWrapper"> + <input type="checkbox" /> + </div> + </label> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/CollectionElementHeaderEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/CollectionElementHeaderEditor.html new file mode 100644 index 000000000000..5c2c4649af05 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/CollectionElementHeaderEditor.html @@ -0,0 +1,5 @@ +<div class="t3-form-validator-editor"> + <h4 data-template-property="header-label"> + <span data-template-property="label" /> + </h4> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/FinishersEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/FinishersEditor.html new file mode 100644 index 000000000000..1c0b4cb724df --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/FinishersEditor.html @@ -0,0 +1,8 @@ +<div class="form-editor"> + <h3><span data-template-property="label" /></h3> + <div id="t3-form-add-finisher" class="t3-form-add-collection-element"> + <select data-template-property="selectOptions" class="form-control" /> + </div> +</div> +<div id="t3-form-inspector-finishers" class="t3-form-collection-container" data-identifier="inspectorFinishers"> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/FormElementHeaderEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/FormElementHeaderEditor.html new file mode 100644 index 000000000000..fd3b22694747 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/FormElementHeaderEditor.html @@ -0,0 +1,3 @@ +<div class="form-editor"> + <h2 class="t3-form-inspector-formelement-header-editor" data-template-property="header-label" data-identifier="inspectorFormElementHeaderEditor"></h2> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/PropertyGridEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/PropertyGridEditor.html new file mode 100644 index 000000000000..287b32d90f99 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/PropertyGridEditor.html @@ -0,0 +1,47 @@ +{namespace core = TYPO3\CMS\Core\ViewHelpers} + +<div class="form-editor"> + <div class="t3-form-control-group form-group property-grid"> + <label><span data-template-property="label" /></label> + <div data-editor="new-property-grid" data-template-property="newPropertyPath"> + + <table class="table table-hover" data-identifier="propertyGridContainer"> + <thead> + <tr> + <th></th> + <th>Label</th> + <th>Value</th> + <th>Selected</th> + <th></th> + </tr> + </thead> + <tbody> + <tr data-identifier="rowItem"> + <td><span class="sort-row-field" data-identifier="sortRow"><core:icon identifier="actions-move-move" /></span></td> + <td><input type="text" class="form-control" value="" data-identifier="label" /></td> + <td><input type="text" class="form-control" value="" data-identifier="value" /></td> + <td><input type="checkbox" data-identifier="selectValue" /></td> + <td> + <div class="btn-group btn-group-sm" role="group"> + <button class="btn btn-default" title="Remove this row" data-identifier="deleteRow"><core:icon identifier="actions-delete" /></button> + </div> + </td> + </tr> + <tr data-identifier="addRowItem"> + <td> + <div class="btn-group btn-group-sm" role="group"> + <button class="btn btn-default" title="Add a new row" data-identifier="addRow"><core:icon identifier="actions-add" /></button> + </div> + </td> + <td></td> + <td></td> + <td></td> + <td></td> + </tr> + </tbody> + </table> + + </div> + <div data-editor="property-grid" data-template-property="propertyPath" class="t3-form-grid" /> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/RemoveElementEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/RemoveElementEditor.html new file mode 100644 index 000000000000..afa20c43f220 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/RemoveElementEditor.html @@ -0,0 +1,5 @@ +{namespace core = TYPO3\CMS\Core\ViewHelpers} + +<div class="t3-form-control-group form-group btn-group-sm"> + <button class="btn btn-default" title="Remove this Element"><core:icon identifier="actions-delete" alternativeMarkupIdentifier="inline"/></button> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/RequiredValidatorEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/RequiredValidatorEditor.html new file mode 100644 index 000000000000..40f29ea016b1 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/RequiredValidatorEditor.html @@ -0,0 +1,10 @@ +<div class="form-editor"> + <div class="t3-form-control-group form-group"> + <label> + <span data-template-property="label" /> + <div class="t3-form-controls" data-identifier="inspectorEditorControlsWrapper"> + <input type="checkbox" /> + </div> + </label> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/SingleSelectEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/SingleSelectEditor.html new file mode 100644 index 000000000000..f1f9653c5692 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/SingleSelectEditor.html @@ -0,0 +1,6 @@ +<div class="t3-form-control-group form-group"> + <label><span data-template-property="label" /></label> + <div class="t3-form-controls" data-identifier="inspectorEditorControlsWrapper"> + <select data-template-property="selectOptions" class="form-control" /> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/TextEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/TextEditor.html new file mode 100644 index 000000000000..e74c2b2f4845 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/TextEditor.html @@ -0,0 +1,10 @@ +<div class="form-editor"> + <div class="t3-form-control-group form-group"> + <label><span data-template-property="label" /></label> + <div class="t3-form-controls" data-identifier="inspectorEditorControlsWrapper"> + <input type="text" value="" data-template-property="propertyPath" class="form-control"> + <span data-template-property="fieldExplanationText" /> + <span data-template-property="validationErrors" /> + </div> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/TextareaEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/TextareaEditor.html new file mode 100644 index 000000000000..31e261e24a78 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/TextareaEditor.html @@ -0,0 +1,8 @@ +<div class="form-editor"> + <div class="t3-form-control-group form-group"> + <label><span data-template-property="label" /></label> + <div class="t3-form-controls" data-identifier="inspectorEditorControlsWrapper"> + <textarea data-template-property="propertyPath" class="form-control" /> + </div> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/Typo3WinBrowserEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/Typo3WinBrowserEditor.html new file mode 100644 index 000000000000..66dd258bed10 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/Typo3WinBrowserEditor.html @@ -0,0 +1,17 @@ +<div class="form-editor"> + <div class="t3-form-control-group form-group"> + <label><span data-template-property="label" /></label> + <div class="t3-form-controls" data-identifier="inspectorEditorControlsWrapper"> + <input type="text" value="" data-template-property="propertyPath" class="form-control" data-insert-target=""> + <div class="help-block"> + <a href="#" data-template-property="onclick" class="btn btn-default"> + <span class="t3js-icon icon icon-size-small icon-state-default icon-mimetypes-x-content-text" data-identifier="mimetypes-x-content-text"> + <span class="icon-markup" data-template-property="image" /> + </span> <span data-template-property="buttonLabel" /> + </a> + </div> + <span data-template-property="fieldExplanationText" /> + <span data-template-property="validationErrors" /> + </div> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/ValidatorsEditor.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/ValidatorsEditor.html new file mode 100644 index 000000000000..6d61b5b0bf23 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Inspector/ValidatorsEditor.html @@ -0,0 +1,8 @@ +<div class="form-editor"> + <h3><span data-template-property="label" /></h3> + <div id="t3-form-add-validator" class="t3-form-add-collection-element"> + <select data-template-property="selectOptions" class="form-control" /> + </div> +</div> +<div id="t3-form-inspector-validators" class="t3-form-collection-container" data-identifier="inspectorValidators"> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/InsertElements.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/InsertElements.html new file mode 100644 index 000000000000..1d3c65f09421 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/InsertElements.html @@ -0,0 +1,30 @@ +{namespace core = TYPO3\CMS\Core\ViewHelpers} + +<div id="t3-form-insert-elements-panel"> + <div class="t3-form-x-component-inner-wrapper"> + <f:for each="{insertRenderablesPanelConfiguration}" as="insertRenderablePanelConfiguration"> + <f:if condition="{insertRenderablePanelConfiguration.key} != 'page'"> + <div class="row"> + <div class="col-sm-12"> + <h4 class="t3-form-group-{insertRenderablePanelConfiguration.key}"> + <span>{insertRenderablePanelConfiguration.label}</span> + </h4> + </div> + <f:for each="{insertRenderablePanelConfiguration.elements}" as="element"> + <div class="col-sm-4 btn-group"> + <a class="t3-form-group-{insertRenderablePanelConfiguration.key} t3-form-type-{element.cssKey} btn btn-default btn-block" title="{element.key}" data-element-type="{element.key}"> + <span class="pull-left"> + <core:icon identifier="{element.iconIdentifier}" alternativeMarkupIdentifier="inline" /> + <span>{element.label}</span> + </span> + <span class="clearfix"></span> + </a> + <hr /> + </div> + </f:for> + </div> + <hr /> + </f:if> + </f:for> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/InsertPages.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/InsertPages.html new file mode 100644 index 000000000000..2f4e0b90322a --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/InsertPages.html @@ -0,0 +1,26 @@ +<div id="t3-form-insert-pages-panel"> + <div class="t3-form-x-component-inner-wrapper"> + <f:for each="{insertRenderablesPanelConfiguration}" as="insertRenderablePanelConfiguration"> + <f:if condition="{insertRenderablePanelConfiguration.key} == 'page'"> + <div class="row"> + <div class="col-sm-12"> + <h3 class="t3-form-group-{insertRenderablePanelConfiguration.key}"> + <span>{insertRenderablePanelConfiguration.label}</span> + </h3> + </div> + <f:for each="{insertRenderablePanelConfiguration.elements}" as="element"> + <div class="col-sm-4 btn-group"> + <a class="t3-form-group-{insertRenderablePanelConfiguration.key} t3-form-type-{element.cssKey} btn btn-default btn-block" title="{element.key}" data-element-type="{element.key}"> + <span class="pull-left"> + <core:icon identifier="{element.iconIdentifier}" alternativeMarkupIdentifier="inline" /> + <span>{element.label}</span> + </span> + <span class="clearfix"></span> + </a> + <hr /> + </div> + </f:for> + </f:if> + </f:for> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/ValidationErrors.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/ValidationErrors.html new file mode 100644 index 000000000000..24867aa45fe3 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Modals/ValidationErrors.html @@ -0,0 +1,17 @@ +<div id="t3-form-validation-errors-panel"> + <div class="t3-form-x-component-inner-wrapper"> + <div class="row"> + <div class="col-sm-12"> + <p><f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.modals.validationErrors.dialogMessage" /></p> + </div> + + <div class="col-sm-12"> + <dl class="t3-overview-list" data-identifier="rowsContainer"> + <dt data-identifier="rowItem"> + <i class="fa fa-exclamation-triangle text-danger" aria-hidden="true"></i> <a data-identifier="rowLink"></a> + </dt> + </dl> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/AdvancedPassword.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/AdvancedPassword.html new file mode 100644 index 000000000000..ececec82bde6 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/AdvancedPassword.html @@ -0,0 +1 @@ +<f:render partial="Stage/SimpleTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Checkbox.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Checkbox.html new file mode 100644 index 000000000000..ececec82bde6 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Checkbox.html @@ -0,0 +1 @@ +<f:render partial="Stage/SimpleTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/ContentElement.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/ContentElement.html new file mode 100644 index 000000000000..ac4e0dd62f97 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/ContentElement.html @@ -0,0 +1,30 @@ +<div class="meta-label"> + <span data-template-property="_type"></span>: <span data-template-property="_identifier"></span> +</div> + +<div class="t3-form-form-element-body"> + <div class="t3-form-icon-container"> + <span data-identifier="elementIcon"></span> + </div> + + <div class="t3-form-element-info"> + <div class="element-label-container"> + <div class="element-label"> + <span data-template-property="label"></span> + <span data-template-property="_required"></span> + </div> + </div> + <div class="element-content"> + <div data-template-property="properties.contentElementUid" /> + </div> + </div> + + <div class="t3-form-validator-info" data-identifier="validators"> + <span data-identifier="validatorIcon"></span> + <div class="t3-form-validator-list"> + <div data-identifier="validatorsContainer"> + <div class="validator-label" data-template-property="_label"></div> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/DatePicker.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/DatePicker.html new file mode 100644 index 000000000000..ececec82bde6 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/DatePicker.html @@ -0,0 +1 @@ +<f:render partial="Stage/SimpleTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Fieldset.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Fieldset.html new file mode 100644 index 000000000000..b4e6dcfeb680 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Fieldset.html @@ -0,0 +1,18 @@ +<div class="meta-label"> + <span data-template-property="_type"></span>: <span data-template-property="_identifier"></span> +</div> + +<div class="t3-form-form-element-body"> + <div class="t3-form-icon-container"> + <span data-identifier="elementIcon"></span> + </div> + + <div class="t3-form-element-info"> + <div class="element-label-container"> + <div class="element-label"> + <span data-template-property="label"></span> + <span data-template-property="_required"></span> + </div> + </div> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/FileUpload.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/FileUpload.html new file mode 100644 index 000000000000..e476819a1f04 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/FileUpload.html @@ -0,0 +1 @@ +<f:render partial="Stage/FileUploadTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Hidden.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Hidden.html new file mode 100644 index 000000000000..ececec82bde6 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Hidden.html @@ -0,0 +1 @@ +<f:render partial="Stage/SimpleTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/ImageUpload.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/ImageUpload.html new file mode 100644 index 000000000000..e476819a1f04 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/ImageUpload.html @@ -0,0 +1 @@ +<f:render partial="Stage/FileUploadTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/MultiCheckbox.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/MultiCheckbox.html new file mode 100644 index 000000000000..fbdd4da477ba --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/MultiCheckbox.html @@ -0,0 +1 @@ +<f:render partial="Stage/SelectTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/MultiSelect.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/MultiSelect.html new file mode 100644 index 000000000000..fbdd4da477ba --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/MultiSelect.html @@ -0,0 +1 @@ +<f:render partial="Stage/SelectTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Page.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Page.html new file mode 100644 index 000000000000..aa5e8817b998 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Page.html @@ -0,0 +1 @@ +<h2 class="t3-form-page-title" data-template-property="label"></h2> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Password.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Password.html new file mode 100644 index 000000000000..ececec82bde6 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Password.html @@ -0,0 +1 @@ +<f:render partial="Stage/SimpleTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/RadioButton.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/RadioButton.html new file mode 100644 index 000000000000..fbdd4da477ba --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/RadioButton.html @@ -0,0 +1 @@ +<f:render partial="Stage/SelectTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/SingleSelect.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/SingleSelect.html new file mode 100644 index 000000000000..fbdd4da477ba --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/SingleSelect.html @@ -0,0 +1 @@ +<f:render partial="Stage/SelectTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/StaticText.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/StaticText.html new file mode 100644 index 000000000000..b9520b171c26 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/StaticText.html @@ -0,0 +1,30 @@ +<div class="meta-label"> + <span data-template-property="_type"></span>: <span data-template-property="_identifier"></span> +</div> + +<div class="t3-form-form-element-body"> + <div class="t3-form-icon-container"> + <span data-identifier="elementIcon"></span> + </div> + + <div class="t3-form-element-info"> + <div class="element-label-container"> + <div class="element-label"> + <span data-template-property="label"></span> + <span data-template-property="_required"></span> + </div> + </div> + <div class="element-content"> + <div data-template-property="properties.text" /> + </div> + </div> + + <div class="t3-form-validator-info" data-identifier="validators"> + <span data-identifier="validatorIcon"></span> + <div class="t3-form-validator-list"> + <div data-identifier="validatorsContainer"> + <div class="validator-label" data-template-property="_label"></div> + </div> + </div> + </div> +</div> diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/SummaryPage.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/SummaryPage.html new file mode 100644 index 000000000000..ccd6afd08730 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/SummaryPage.html @@ -0,0 +1 @@ +<h2 data-template-property="label"></h2> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Text.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Text.html new file mode 100644 index 000000000000..ececec82bde6 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Text.html @@ -0,0 +1 @@ +<f:render partial="Stage/SimpleTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Textarea.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Textarea.html new file mode 100644 index 000000000000..ececec82bde6 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/Textarea.html @@ -0,0 +1 @@ +<f:render partial="Stage/SimpleTemplate" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/_ElementToolbar.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/_ElementToolbar.html new file mode 100644 index 000000000000..6b6f071ceb11 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/_ElementToolbar.html @@ -0,0 +1,35 @@ +{namespace core = TYPO3\CMS\Core\ViewHelpers} + +<div class="btn-toolbar-container" data-identifier="elementToolbar"> + <div class="btn-toolbar" data-identifier="elementToolbarButtons"> + <div class="btn-group btn-group-sm" role="group"> + <a class="btn btn-default" href="#" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.stage.toolbar.new_element')}" data-identifier="stageElementToolbarNewElement"><core:icon identifier="actions-document-new" alternativeMarkupIdentifier="inline" /></a> + </div> + <div class="btn-group btn-group-sm" role="group"> + <div class="btn-group t3-form-dropdown-buttons" data-identifier="stageElementToolbarNewElementSplitButton"> + <button type="button" class="btn btn-sm btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.stage.toolbar.new_element')}"> + <core:icon identifier="actions-document-new" alternativeMarkupIdentifier="inline" /> + <span class="caret"></span> + <span class="sr-only">Toggle Dropdown</span> + </button> + <ul class="dropdown-menu"> + <li data-no-sorting> + <a href="#" data-identifier="stageElementToolbarNewElementSplitButtonInside"> + <core:icon identifier="t3-form-icon-insert-in" alternativeMarkupIdentifier="inline" /> + <f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.stage.toolbar.new_element.inside" /> + </a> + </li> + <li data-no-sorting> + <a href="#" data-identifier="stageElementToolbarNewElementSplitButtonAfter"> + <core:icon identifier="t3-form-icon-insert-after" alternativeMarkupIdentifier="inline" /> + <f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.stage.toolbar.new_element.after" /> + </a> + </li> + </ul> + </div> + </div> + <div class="btn-group btn-group-sm" role="group"> + <a class="btn btn-default" href="#" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.stage.toolbar.remove')}" data-identifier="stageElementToolbarRemoveElement"><core:icon identifier="actions-edit-delete" alternativeMarkupIdentifier="inline" /></a> + </div> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/_UnknownElement.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/_UnknownElement.html new file mode 100644 index 000000000000..31d9a2e7d15f --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Stage/_UnknownElement.html @@ -0,0 +1 @@ +<span><f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.elements.UnknownElement" /></span> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/BlankForm.yaml b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/BlankForm.yaml new file mode 100644 index 000000000000..5a1b08a10dcc --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/BlankForm.yaml @@ -0,0 +1,8 @@ +type: 'Form' +identifier: 'blankForm' +label: '[Blank Form]' +renderables: + - + type: 'Page' + identifier: 'page-1' + label: 'Page' \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/SimpleContactForm.yaml b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/SimpleContactForm.yaml new file mode 100644 index 000000000000..4e217ea03bac --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormEditor/Yaml/NewForms/SimpleContactForm.yaml @@ -0,0 +1,75 @@ +identifier: ext-form-simple-contact-form-example +label: 'Simple Contact Form' +type: Form + +finishers: + - + identifier: EmailToReceiver + options: + subject: 'Your message: {subject}' + recipientAddress: 'your.company@example.com' + recipientName: 'Your Company name' + senderAddress: '{email}' + senderName: '{name}' + replyToAddress: '' + carbonCopyAddress: '' + blindCarbonCopyAddress: '' + format: 'html' + attachUploads: 'true' + translation: + language: '' + +renderables: + - + identifier: page-1 + label: 'Contact Form' + type: Page + + renderables: + - + identifier: name + label: 'Name' + type: Text + properties: + placeholder: 'Name' + defaultValue: '' + validators: + - + identifier: NotEmpty + - + identifier: subject + label: 'Subject' + type: Text + properties: + placeholder: 'Subject' + defaultValue: '' + validators: + - + identifier: NotEmpty + - + identifier: email + label: 'Email' + type: Text + properties: + placeholder: 'Email address' + defaultValue: '' + validators: + - + identifier: NotEmpty + - + identifier: EmailAddress + - + identifier: message + label: 'Message' + type: Textarea + properties: + placeholder: '' + defaultValue: '' + validators: + - + identifier: NotEmpty + + - + identifier: summarypage + label: 'Summary page' + type: SummaryPage diff --git a/typo3/sysext/form/Resources/Private/Backend/Templates/FormManager/Index.html b/typo3/sysext/form/Resources/Private/Backend/Templates/FormManager/Index.html new file mode 100644 index 000000000000..cacc99f8dcde --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Backend/Templates/FormManager/Index.html @@ -0,0 +1,110 @@ +{namespace core = TYPO3\CMS\Core\ViewHelpers} + +<f:layout name="FormManager" /> + +<f:section name="MainContent"> + <script type="text/javascript"> + require(['{dynamicRequireJsModules.app}', '{dynamicRequireJsModules.viewModel}'], function (formManagerApp, viewModel) { + var FORMMANAGER_APP = formManagerApp.getInstance( + <f:format.htmlentitiesDecode>{formManagerAppInitialData}</f:format.htmlentitiesDecode>, + viewModel + ).run(); + }); + </script> + + <h1><f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.headline" /></h1> + + <f:if condition="{forms}"> + <f:then> + <div class="panel panel-space panel-default recordlist"> + <div class="panel-heading"> + <span><f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.edit_existing_forms" /></span> + </div> + <div class="collapse in"> + <div class="table-fit"> + <table id="forms" class="table table-striped table-hover"> + <thead> + <tr> + <th nowrap="nowrap" class="col-icon"><a class="btn btn-default" data-identifier="newForm" href="#"><core:icon identifier="actions-add" /></a></th> + <th nowrap="nowrap" class="col-title col-responsive"><f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.form_name" /></th> + <th nowrap="nowrap" class="col-control"></th> + <th nowrap="nowrap"><f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.location" /></th> + <th nowrap="nowrap"><f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.references" /></th> + </tr> + </thead> + <tbody> + <f:for each="{forms}" as="form"> + <tr> + <td nowrap="nowrap" class="col-icon"> + <f:if condition="{form.duplicateIdentifier}"> + <f:then> + <span title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.duplicate_identifier')} {form.identifier}" data-toggle="tooltip" data-placement="top"> + <core:icon identifier="overlay-missing" /> + </span> + </f:then> + <f:else> + <span title="id={form.identifier}" data-toggle="tooltip" data-placement="right"> + <core:icon identifier="content-elements-mailform" /> + </span> + </f:else> + </f:if> + </td> + <td nowrap="nowrap" class="col-title col-responsive"> + <f:if condition="{form.readOnly}"> + <f:then> + <div>{form.name}</div> + </f:then> + <f:else> + <f:link.action controller="FormEditor" action="index" arguments="{formPersistenceIdentifier: form.persistenceIdentifier}" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.edit_form')}" data="{toggle: 'tooltip', placement: 'right'}">{form.name}</f:link.action> + </f:else> + </f:if> + + </td> + <td nowrap="nowrap" class="col-control"> + <div class="btn-group" role="group"> + <f:if condition="{form.readOnly}"> + <f:then> + <button class="btn btn-default form-record-readonly" disabled="disabled" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.edit_form_not_allowed')}"><core:icon identifier="actions-open" /></button> + </f:then> + <f:else> + <f:link.action controller="FormEditor" action="index" arguments="{formPersistenceIdentifier: form.persistenceIdentifier}" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.edit_form')}" class="btn btn-default form-record-open"><core:icon identifier="actions-open" /></f:link.action> + </f:else> + </f:if> + <a href="#" data-identifier="duplicateForm" data-form-persistence-identifier="{form.persistenceIdentifier}" data-form-name="{form.name}" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.duplicate_this_form')}" class="btn btn-default form-record-duplicate"><core:icon identifier="t3-form-icon-duplicate" /></a> + <f:if condition="{form.location} === 'storage'"> + <f:then> + <a href="#" data-identifier="removeForm" data-form-persistence-identifier="{form.persistenceIdentifier}" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.delete_form')}" class="btn btn-default form-record-delete"><core:icon identifier="actions-edit-delete" /></a> + </f:then> + <f:else> + <button class="btn btn-default form-record-delete" disabled="disabled" title="{f:translate(key: 'LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.delete_form_not_allowed')}"><core:icon identifier="actions-edit-delete" /></button> + </f:else> + </f:if> + </div> + </td> + <td nowrap="nowrap">{form.persistenceIdentifier}</td> + <td nowrap="nowrap"> + <f:if condition="{form.referenceCount}"> + <f:then> + <a href="#" data-identifier="showReferences" data-form-persistence-identifier="{form.persistenceIdentifier}" data-form-name="{form.name}">{form.referenceCount}</a> + </f:then> + <f:else> + {form.referenceCount} + </f:else> + </f:if> + </td> + </tr> + </f:for> + </tbody> + </table> + </div> + </div> + </div> + </f:then> + <f:else> + <f:be.infobox title="{f:translate(key: 'formManager.no_forms', extensionName:'form')}" state="-1"> + <p><f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.no_forms"/></p> + <a class="btn btn-primary" data-identifier="newForm" href="#"><f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formManager.create_new_form"/></a> + </f:be.infobox> + </f:else> + </f:if> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Layouts/FormElements/Field.html b/typo3/sysext/form/Resources/Private/Frontend/Layouts/FormElements/Field.html new file mode 100644 index 000000000000..0002b904387d --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Layouts/FormElements/Field.html @@ -0,0 +1,22 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<div class="form-group"> + <label for="{element.uniqueIdentifier}">{formvh:translateElementProperty(element: element, property: 'label')}<f:if condition="{element.required}"><f:render partial="Field/Required" /></f:if></label> + <div class="{element.properties.containerClassAttribute}"> + <f:render section="field" /> + <f:format.raw> + <f:form.validationResults for="{element.identifier}"> + <f:if condition="{validationResults.flattenedErrors}"> + <span class="form-control-feedback error help-inline"> + <f:for each="{validationResults.errors}" as="error"> + {error -> f:translate(key: '{element.renderingOptions.translation.translationFile}:validation.error.{error.code}', arguments: error.arguments)} + <br /> + </f:for> + </span> + </f:if> + </f:form.validationResults> + </f:format.raw> + <f:if condition="{element.properties.elementDescription}"> + <span class="help-block">{formvh:translateElementProperty(element: element, property: 'elementDescription')}</span> + </f:if> + </div> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Partials/FormElements/Field/Required.html b/typo3/sysext/form/Resources/Private/Frontend/Partials/FormElements/Field/Required.html new file mode 100644 index 000000000000..b8618a87e9c6 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Partials/FormElements/Field/Required.html @@ -0,0 +1 @@ +<span class="required">*</span> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Partials/FormElements/Form/Navigation.html b/typo3/sysext/form/Resources/Private/Frontend/Partials/FormElements/Form/Navigation.html new file mode 100644 index 000000000000..154f80c5eed1 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Partials/FormElements/Form/Navigation.html @@ -0,0 +1,25 @@ +<nav class="form-navigation"> + <div class="btn-toolbar" role="toolbar"> + <div class="btn-group" role="group"> + <f:if condition="{form.previousPage}"> + <span class="previous"> + <f:form.button property="__currentPage" value="{form.previousPage.index}" class="btn btn-cancel">{formvh:translateElementProperty(element: form.currentPage, renderingOptionProperty: 'previousButtonLabel')}</f:form.button> + </span> + </f:if> + <f:if condition="{form.nextPage}"> + <f:then> + <span class="next"> + <f:form.button property="__currentPage" value="{form.nextPage.index}" class="btn btn-primary">{formvh:translateElementProperty(element: form.currentPage, renderingOptionProperty: 'nextButtonLabel')}</f:form.button> + </span> + </f:then> + <f:else> + <span class="next submit"> + <f:form.button property="__currentPage" value="{form.pages -> f:count()}" class="btn btn-primary"> + {formvh:translateElementProperty(element: form, renderingOptionProperty: 'submitButtonLabel')} + </f:form.button> + </span> + </f:else> + </f:if> + </div> + </div> +</nav> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/Finishers/Email/Html.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/Finishers/Email/Html.html new file mode 100644 index 000000000000..dce25de4ddad --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/Finishers/Email/Html.html @@ -0,0 +1,48 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> + +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} + +<html> +<head> + <title></title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + <meta name="format-detection" content="telephone=no"> +</head> + +<body> + <table width="600" cellpadding="0" cellspacing="0" border="0"> + <formvh:renderAllFormValues renderable="{form.formDefinition}" formRuntime="{form}"> + <tr> + <td width="600" valign="top" align="left">{formvh:translateElementProperty(element: formValue.element, property: 'label', formRuntime: form)}</td> + <td width="600" valign="top" align="left"> + <f:if condition="{formValue.value}"> + <f:then> + <f:if condition="{formValue.isMultiValue}"> + <f:then> + <table cellspacing="0" border="0"> + <f:for each="{formValue.processedValue}" as="value"> + <tr> + <td>{value}</td> + </tr> + </f:for> + </table> + </f:then> + <f:else> + <table cellspacing="0" border="0"> + <tr> + <td>{formValue.processedValue}</td> + </tr> + </table> + </f:else> + </f:if> + </f:then> + <f:else> + - + </f:else> + </f:if> + </td> + </tr> + </formvh:renderAllFormValues> + </table> +</body> +</html> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/Finishers/Email/Plaintext.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/Finishers/Email/Plaintext.html new file mode 100644 index 000000000000..05cde5a0bf84 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/Finishers/Email/Plaintext.html @@ -0,0 +1,3 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} + +<formvh:renderAllFormValues renderable="{form.formDefinition}" formRuntime="{form}"><formvh:plainTextMail formValue="{formValue}" formRuntime="{form}" /></formvh:renderAllFormValues> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/AdvancedPassword.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/AdvancedPassword.html new file mode 100644 index 000000000000..05a9f1da31cb --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/AdvancedPassword.html @@ -0,0 +1,28 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <div class="form-group"> + <f:form.password + property="{element.identifier}.password" + id="{element.uniqueIdentifier}" + class="{element.properties.elementClassAttribute} form-control" + errorClass="{element.properties.elementErrorClassAttribute}" + additionalAttributes="{f:if(condition: '{element.required}', then: '{required: \'required\', placeholder: \'{formvh:translateElementProperty(element: element, property: \"placeholder\")}\'}', else: '{placeholder: \'{formvh:translateElementProperty(element: element, property: \"placeholder\")}\'}')}" + /> + <f:if condition="{formvh:translateElementProperty(element: element, property: 'passwordDescription')}"> + <span class="help-block">{formvh:translateElementProperty(element: element, property: 'passwordDescription')}</span> + </f:if> + </div> + <div class="form-group"> + <f:if condition="{formvh:translateElementProperty(element: element, property: 'confirmationLabel')}"> + <label for="{element.uniqueIdentifier}-confirmation">{formvh:translateElementProperty(element: element, property: 'confirmationLabel')}<f:if condition="{element.required}"><f:render partial="Field/Required" /></f:if></label> + </f:if> + <f:form.password + property="{element.identifier}.confirmation" + id="{element.uniqueIdentifier}-confirmation" + class="{element.properties.confirmationClassAttribute} form-control" + errorClass="{element.properties.elementErrorClassAttribute}" + additionalAttributes="{f:if(condition: '{element.required}', then: '{required: \'required\', placeholder: \'{formvh:translateElementProperty(element: element, property: \"placeholder\")}\'}', else: '{placeholder: \'{formvh:translateElementProperty(element: element, property: \"placeholder\")}\'}')}" + /> + </div> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Checkbox.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Checkbox.html new file mode 100644 index 000000000000..61e96d2271ed --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Checkbox.html @@ -0,0 +1,15 @@ +<f:layout name="Field" /> +<f:section name="field"> + <div class="form-check"> + <label class="{element.properties.elementClassAttribute} form-check-label"> + <f:form.checkbox + property="{element.identifier}" + id="{element.uniqueIdentifier}" + class="{element.properties.elementClassAttribute}" + value="{element.properties.value}" + errorClass="{element.properties.elementErrorClassAttribute}" + additionalAttributes="{f:if(condition: '{element.required}', then: '{required: \'required\'}')}" + /> + </label> + </div> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/ContentElement.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/ContentElement.html new file mode 100644 index 000000000000..9d1b2d4bb23b --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/ContentElement.html @@ -0,0 +1,22 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:if condition="{element.rootForm.renderingOptions.previewMode}"> + <f:then> + <div class="clearfix"> + <f:if condition="{element.properties.contentElementUid}"> + <f:then> + <formvh:be.renderContentElementPreview contentElementUid="{element.properties.contentElementUid}" /> + </f:then> + <f:else> + <f:translate key="LLL:EXT:form/Resources/Private/Language/Database.xlf:formEditor.elements.ContentElement.selectContentElement" /> + </f:else> + </f:if> + </div> + </f:then> + <f:else> + <f:if condition="{element.properties.contentElementUid}"> + <div class="clearfix{f:if(condition: element.properties.elementClassAttribute, then: ' {element.properties.elementClassAttribute}')}"> + <f:cObject typoscriptObjectPath="lib.tx_form.contentElementRendering">{element.properties.contentElementUid}</f:cObject> + </div> + </f:if> + </f:else> +</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/DatePicker.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/DatePicker.html new file mode 100644 index 000000000000..b1a4e4d93c73 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/DatePicker.html @@ -0,0 +1,47 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <formvh:form.datePicker + id="{element.uniqueIdentifier}" + property="{element.identifier}" + placeholder="{formvh:translateElementProperty(element: element, property: 'placeholder')}" + dateFormat="{element.properties.dateFormat}" + initialDate="{element.properties.initialDate}" + enableDatePicker="{element.properties.enableDatePicker}" + class="{element.properties.elementClassAttribute}" + errorClass="{element.properties.elementErrorClassAttribute}" + additionalAttributes="{f:if(condition: '{element.required}', then: '{required: \'required\'}')}" + > + <f:if condition="{element.rootForm.renderingOptions.previewMode}"> + <f:else> + <f:if condition="{element.properties.enableDatePicker}"> + <script type="text/javascript"> + if ("undefined" !== typeof $) { + $(function() { + $("#<f:format.raw>{element.uniqueIdentifier}</f:format.raw>").datepicker({ + dateFormat: "<f:format.raw>{datePickerDateFormat}</f:format.raw>" + }).on("keydown", function(e) { + // By using "backspace" or "delete", you can clear the datepicker again. + if(e.keyCode == 8 || e.keyCode == 46) { + e.preventDefault(); + $.datepicker._clearDate(this); + } + }); + }); + } + </script> + </f:if> + </f:else> + </f:if> + </formvh:form.datePicker> + + <f:if condition="{element.properties.displayTimeSelector}"> + <formvh:form.timePicker + id="{element.uniqueIdentifier}-time" + property="{element.identifier}" + initialDate="{element.properties.initialDate}" + class="{element.properties.timeSelectorClassAttribute} form-control" + errorClass="{element.properties.elementErrorClassAttribute}" + /> + </f:if> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Fieldset.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Fieldset.html new file mode 100644 index 000000000000..89c64c46685e --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Fieldset.html @@ -0,0 +1,9 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<fieldset id="{section.uniqueIdentifier}" class="form-group{f:if(condition: section.properties.elementClassAttribute, then: ' {section.properties.elementClassAttribute}')}"> + <f:if condition="{section.label}"> + <legend>{formvh:translateElementProperty(element: section, property: 'label')}</legend> + </f:if> + <f:for each="{section.elements}" as="element"> + <formvh:renderRenderable renderable="{element}" /> + </f:for> +</fieldset> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/FileUpload.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/FileUpload.html new file mode 100644 index 000000000000..d2774bec0370 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/FileUpload.html @@ -0,0 +1,11 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <formvh:form.uploadedResource property="{element.identifier}" as="resource" additionalAttributes="{f:if(condition: '{element.required}', then: '{required: \'required\'}')}" accept="{element.properties.allowedMimeTypes}"> + <f:if condition="{resource}"> + <div id="{element.uniqueIdentifier}-preview"> + {resource.originalResource.originalFile.name} + </div> + </f:if> + </formvh:form.uploadedResource> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Form.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Form.html new file mode 100644 index 000000000000..e4c950a565be --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Form.html @@ -0,0 +1,7 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<formvh:form object="{form}" action="perform" method="post" id="{form.identifier}" section="{form.identifier}" enctype="multipart/form-data"> + <formvh:renderRenderable renderable="{form.currentPage}" /> + <div class="actions"> + <f:render partial="Form/Navigation" arguments="{form: form}" /> + </div> +</formvh:form> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Hidden.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Hidden.html new file mode 100644 index 000000000000..0d4b716824f7 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Hidden.html @@ -0,0 +1 @@ +<f:form.hidden property="{element.identifier}" id="{element.uniqueIdentifier}" value="{element.properties.value}" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Honeypot.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Honeypot.html new file mode 100644 index 000000000000..6f26f34f07d3 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Honeypot.html @@ -0,0 +1,8 @@ +<f:if condition="{element.properties.renderAsHiddenField}"> + <f:then> + <f:form.hidden property="{element.identifier}" id="{element.uniqueIdentifier}" additionalAttributes="{autocomplete: 'off'}" /> + </f:then> + <f:else> + <f:form.textfield property="{element.identifier}" id="{element.uniqueIdentifier}" class="{element.properties.elementClassAttribute}" additionalAttributes="{autocomplete: 'off'}" style="{element.properties.styleAttribute}" /> + </f:else> +</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/ImageUpload.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/ImageUpload.html new file mode 100644 index 000000000000..3c6798a59d55 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/ImageUpload.html @@ -0,0 +1,13 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <formvh:form.uploadedResource property="{element.identifier}" as="image" additionalAttributes="{f:if(condition: '{element.required}', then: '{required: \'required\'}')}" accept="{element.properties.allowedMimeTypes}"> + <f:if condition="{image}"> + <div id="{element.uniqueIdentifier}-preview"> + <a href="{f:uri.image(image: image, maxWidth: element.properties.imageLinkMaxWidth)}" class="{element.properties.elementClassAttribute}"> + <f:image image="{image}" maxWidth="{element.properties.imageMaxWidth}" maxHeight="{element.properties.imageMaxHeight}" alt="{formvh:translateElementProperty(element: element, property: 'altText')}" /> + </a> + </div> + </f:if> + </formvh:form.uploadedResource> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/MultiCheckbox.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/MultiCheckbox.html new file mode 100644 index 000000000000..642dcdf1c6bc --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/MultiCheckbox.html @@ -0,0 +1,20 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <div id="{element.uniqueIdentifier}" class="inputs-list"> + <f:for each="{element.properties.options}" as="label" key="value"> + <div class="form-check"> + <label class="form-check-label"> + <formvh:form.checkbox + property="{element.identifier}" + multiple="1" + class="{element.properties.elementClassAttribute}" + value="{value}" + errorClass="{element.properties.elementErrorClassAttribute}" + /> + <span>{formvh:translateElementProperty(element: element, property: 'options.{value}')}</span> + </label> + </div> + </f:for> + </div> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/MultiSelect.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/MultiSelect.html new file mode 100644 index 000000000000..c8459427292d --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/MultiSelect.html @@ -0,0 +1,12 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <f:form.select + property="{element.identifier}" + id="{element.uniqueIdentifier}" + class="{element.properties.elementClassAttribute} form-control" + options="{formvh:translateElementProperty(element: element, property: 'options')}" + multiple="multiple" + errorClass="{element.properties.elementErrorClassAttribute}" + /> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Page.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Page.html new file mode 100644 index 000000000000..cf7462f4bec9 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Page.html @@ -0,0 +1,9 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<fieldset class="form-group"> + <f:if condition="{page.label}"> + <legend>{formvh:translateElementProperty(element: page, property: 'label')}</legend> + </f:if> + <f:for each="{page.elements}" as="element"> + <formvh:renderRenderable renderable="{element}" /> + </f:for> +</fieldset> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Password.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Password.html new file mode 100644 index 000000000000..89f7780705b9 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Password.html @@ -0,0 +1,11 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <f:form.password + property="{element.identifier}" + id="{element.uniqueIdentifier}" + class="{element.properties.elementClassAttribute} form-control" + additionalAttributes="{f:if(condition: '{element.required}', then: '{required: \'required\', placeholder: \'{formvh:translateElementProperty(element: element, property: \"placeholder\")}\'}', else: '{placeholder: \'{formvh:translateElementProperty(element: element, property: \"placeholder\")}\'}')}" + errorClass="{element.properties.elementErrorClassAttribute}" + /> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/RadioButton.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/RadioButton.html new file mode 100644 index 000000000000..7434e45b9420 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/RadioButton.html @@ -0,0 +1,22 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <div id="{element.uniqueIdentifier}" class="inputs-list"> + <div class="form-group"> + <f:for each="{element.properties.options}" as="label" key="value"> + <div class="form-check"> + <label class="form-check-label"> + <f:form.radio + property="{element.identifier}" + class="{element.properties.elementClassAttribute} form-check-input" + value="{value}" + errorClass="{element.properties.elementErrorClassAttribute}" + additionalAttributes="{f:if(condition: '{element.required}', then: '{required: \'required\'}')}" + /> + <span>{formvh:translateElementProperty(element: element, property: 'options.{value}')}</span> + </label> + </div> + </f:for> + </div> + </div> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/SingleSelect.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/SingleSelect.html new file mode 100644 index 000000000000..bb125dc15aed --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/SingleSelect.html @@ -0,0 +1,12 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <f:form.select + property="{element.identifier}" + id="{element.uniqueIdentifier}" + options="{formvh:translateElementProperty(element: element, property: 'options')}" + class="{element.properties.elementClassAttribute} form-control" + errorClass="{element.properties.elementErrorClassAttribute}" + additionalAttributes="{f:if(condition: '{element.required}', then: '{required: \'required\'}')}" + /> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/StaticText.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/StaticText.html new file mode 100644 index 000000000000..9fe0a2a9ef55 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/StaticText.html @@ -0,0 +1,9 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<div class="clearfix"> + <f:if condition="{element.label}"> + <h2>{formvh:translateElementProperty(element: element, property: 'label')}</h2> + </f:if> + <f:if condition="{element.properties.text}"> + <p{f:if(condition: element.properties.elementClassAttribute, then: ' class="{element.properties.elementClassAttribute}"')}>{formvh:translateElementProperty(element: element, property: 'text') -> f:format.nl2br()}</p> + </f:if> +</div> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/SummaryPage.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/SummaryPage.html new file mode 100644 index 000000000000..3afa995f48a0 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/SummaryPage.html @@ -0,0 +1,43 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<fieldset class="form-group"> + <f:if condition="{page.label}"> + <legend>{formvh:translateElementProperty(element: page, property: 'label')}</legend> + </f:if> + <div class="table-responsive"> + <table class="table"> + <formvh:renderAllFormValues renderable="{page.rootForm}"> + <tr> + <td class="summary-table-first-col">{formvh:translateElementProperty(element: formValue.element, property: 'label')}</td> + <td> + <f:if condition="{formValue.value}"> + <f:then> + <f:if condition="{0: formValue.element.type} == {0: 'ImageUpload'}"> + <f:then> + <f:image image="{formValue.value}" maxWidth="{formValue.element.properties.imageMaxWidth}" maxHeight="{formValue.element.properties.imageMaxHeight}" alt="{formvh:translateElementProperty(element: formValue.element, property: 'altText')}" /> + </f:then> + <f:else> + <f:if condition="{formValue.isMultiValue}"> + <f:then> + <ul> + <f:for each="{formValue.processedValue}" as="value"> + <li>{value}</li> + </f:for> + </ul> + </f:then> + <f:else> + {formValue.processedValue} + </f:else> + </f:if> + </f:else> + </f:if> + </f:then> + <f:else> + - + </f:else> + </f:if> + </td> + </tr> + </formvh:renderAllFormValues> + </table> + </div> +</fieldset> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Text.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Text.html new file mode 100644 index 000000000000..0eb10c2c1b8b --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Text.html @@ -0,0 +1,12 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <f:form.textfield + property="{element.identifier}" + id="{element.uniqueIdentifier}" + class="{element.properties.elementClassAttribute} form-control" + placeholder="{formvh:translateElementProperty(element: element, property: 'placeholder')}" + errorClass="{element.properties.elementErrorClassAttribute}" + required="{element.required}" + /> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Textarea.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Textarea.html new file mode 100644 index 000000000000..c0baddec26b0 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/FormElements/Textarea.html @@ -0,0 +1,13 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:layout name="Field" /> +<f:section name="field"> + <f:form.textarea + property="{element.identifier}" + id="{element.uniqueIdentifier}" + class="{element.properties.elementClassAttribute} form-control" + rows="{element.properties.rows}" + cols="{element.properties.cols}" + errorClass="{element.properties.elementErrorClassAttribute}" + additionalAttributes="{f:if(condition: '{element.required}', then: '{required: \'required\', placeholder: \'{formvh:translateElementProperty(element: element, property: \"placeholder\")}\'}', else: '{placeholder: \'{formvh:translateElementProperty(element: element, property: \"placeholder\")}\'}')}" + /> +</f:section> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Frontend/Templates/Render.html b/typo3/sysext/form/Resources/Private/Frontend/Templates/Render.html new file mode 100644 index 000000000000..a42a55d1682a --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Frontend/Templates/Render.html @@ -0,0 +1,5 @@ +{namespace formvh=TYPO3\CMS\Form\ViewHelpers} +<f:flashMessages class="flashmessages" /> +<f:if condition="{formConfiguration}"> + <formvh:render overrideConfiguration="{formConfiguration}"/> +</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Language/Database.xlf b/typo3/sysext/form/Resources/Private/Language/Database.xlf index 069dedb78431..3f8d42d81499 100644 --- a/typo3/sysext/form/Resources/Private/Language/Database.xlf +++ b/typo3/sysext/form/Resources/Private/Language/Database.xlf @@ -1,14 +1,690 @@ <?xml version="1.0" encoding="UTF-8"?> <xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff"> - <file t3:id="1450450398" source-language="en" datatype="plaintext" original="messages" date="2015-12-18T15:53:55Z" product-name="form"> - <header/> - <body> - <trans-unit id="tx_form_predefinedform"> - <source>Predefined form</source> - </trans-unit> - <trans-unit id="tx_form_predefinedform.selectPredefinedForm"> - <source>Choose a predefined form</source> - </trans-unit> - </body> - </file> + <file t3:id="1471786343" source-language="en" datatype="plaintext" original="messages" date="2016-08-21T20:15:32Z" product-name="form"> + <header/> + <body> + <trans-unit id="module.shortcut_name" xml:space="preserve"> + <source>Form manager</source> + </trans-unit> + + <trans-unit id="tt_content.pi_flexform.formframework.persistenceIdentifier" xml:space="preserve"> + <source>Form definition</source> + </trans-unit> + <trans-unit id="tt_content.pi_flexform.formframework.sheet_general" xml:space="preserve"> + <source>General</source> + </trans-unit> + <trans-unit id="tt_content.pi_flexform.formframework.selectPersistenceIdentifier" xml:space="preserve"> + <source>Please select a form definition</source> + </trans-unit> + <trans-unit id="tt_content.pi_flexform.formframework.overrideFinishers" xml:space="preserve"> + <source>Override finisher settings</source> + </trans-unit> + + <trans-unit id="tt_content.finishersDefinition.EmailToSender.label" xml:space="preserve"> + <source>Email to sender</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.subject.label" xml:space="preserve"> + <source>Subject of the email</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.recipientAddress.label" xml:space="preserve"> + <source>Email address of the recipient</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.recipientName.label" xml:space="preserve"> + <source>Human-readable name of the recipient</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.senderAddress.label" xml:space="preserve"> + <source>Email address of the sender</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.senderName.label" xml:space="preserve"> + <source>Human-readable name of the sender</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.replyToAddress.label" xml:space="preserve"> + <source>Email address of to be used as reply-to email</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.carbonCopyAddress.label" xml:space="preserve"> + <source>Email address of the copy recipient</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.blindCarbonCopyAddress.label" xml:space="preserve"> + <source>Email address of the blind copy recipient</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.format.label" xml:space="preserve"> + <source>The format of the email</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.format.1" xml:space="preserve"> + <source>HTML</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToSender.format.2" xml:space="preserve"> + <source>Plain text</source> + </trans-unit> + + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.label" xml:space="preserve"> + <source>Email to receiver</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.subject.label" xml:space="preserve"> + <source>Subject of the email</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.recipientAddress.label" xml:space="preserve"> + <source>Email address of the recipient</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.recipientName.label" xml:space="preserve"> + <source>Human-readable name of the recipient</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.senderAddress.label" xml:space="preserve"> + <source>Email address of the sender</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.senderName.label" xml:space="preserve"> + <source>Human-readable name of the sender</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.replyToAddress.label" xml:space="preserve"> + <source>Email address of to be used as reply-to email</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.carbonCopyAddress.label" xml:space="preserve"> + <source>Email address of the copy recipient</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.blindCarbonCopyAddress.label" xml:space="preserve"> + <source>Email address of the blind copy recipient</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.format.label" xml:space="preserve"> + <source>Format of the email</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.format.1" xml:space="preserve"> + <source>HTML</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.format.2" xml:space="preserve"> + <source>Plain text</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.language.label" xml:space="preserve"> + <source>Translation language</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.EmailToReceiver.language.1" xml:space="preserve"> + <source>Default</source> + </trans-unit> + + <trans-unit id="tt_content.finishersDefinition.Redirect.label" xml:space="preserve"> + <source>Redirect</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.Redirect.pageUid.label" xml:space="preserve"> + <source>Page</source> + </trans-unit> + <trans-unit id="tt_content.finishersDefinition.Redirect.additionalParameters.label" xml:space="preserve"> + <source>Additional link parameter</source> + </trans-unit> + + <trans-unit id="formManagerController.deleteAction.error.title" xml:space="preserve"> + <source>Delete error</source> + </trans-unit> + <trans-unit id="formManagerController.deleteAction.error.body" xml:space="preserve"> + <source>The form "%s" could not be deleted.</source> + </trans-unit> + + <trans-unit id="formManager.headline" xml:space="preserve"> + <source>Manage forms</source> + </trans-unit> + <trans-unit id="formManager.create_new_form" xml:space="preserve"> + <source>Create a new form</source> + </trans-unit> + <trans-unit id="formManager.edit_existing_forms" xml:space="preserve"> + <source>Edit existing forms</source> + </trans-unit> + <trans-unit id="formManager.form_name" xml:space="preserve"> + <source>Form name</source> + </trans-unit> + <trans-unit id="formManager.location" xml:space="preserve"> + <source>Location</source> + </trans-unit> + <trans-unit id="formManager.references" xml:space="preserve"> + <source>References</source> + </trans-unit> + <trans-unit id="formManager.options" xml:space="preserve"> + <source>Options</source> + </trans-unit> + <trans-unit id="formManager.show_references" xml:space="preserve"> + <source>Show references for this form</source> + </trans-unit> + <trans-unit id="formManager.edit_form" xml:space="preserve"> + <source>Edit this form</source> + </trans-unit> + <trans-unit id="formManager.duplicate_this_form" xml:space="preserve"> + <source>Duplicate this form</source> + </trans-unit> + <trans-unit id="formManager.delete_form" xml:space="preserve"> + <source>Remove this form</source> + </trans-unit> + <trans-unit id="formManager.no_forms" xml:space="preserve"> + <source>No form available.</source> + </trans-unit> + <trans-unit id="formManager.edit_form_not_allowed" xml:space="preserve"> + <source>Edit this form is not allowed.</source> + </trans-unit> + <trans-unit id="formManager.delete_form_not_allowed" xml:space="preserve"> + <source>Delete this form is not allowed.</source> + </trans-unit> + <trans-unit id="formManager.duplicate_identifier" xml:space="preserve"> + <source>Duplicate identifier!</source> + </trans-unit> + + <trans-unit id="formManager.selectablePrototypesConfiguration.standard.label" xml:space="preserve"> + <source>Standard</source> + </trans-unit> + <trans-unit id="formManager.selectablePrototypesConfiguration.standard.newFormTemplates.blankForm.label" xml:space="preserve"> + <source>Blank form</source> + </trans-unit> + <trans-unit id="formManager.selectablePrototypesConfiguration.standard.newFormTemplates.simpleContactForm.label" xml:space="preserve"> + <source>Simple contact form (ext:form example)</source> + </trans-unit> + + <trans-unit id="formEditor.header" xml:space="preserve"> + <source>Form Editor</source> + </trans-unit> + <trans-unit id="formEditor.loading" xml:space="preserve"> + <source>Loading...</source> + </trans-unit> + <trans-unit id="formEditor.spinner" xml:space="preserve"> + <source>Loading...</source> + </trans-unit> + <trans-unit id="formEditor.save_button" xml:space="preserve"> + <source>Save</source> + </trans-unit> + <trans-unit id="formEditor.undo_button" xml:space="preserve"> + <source>Undo</source> + </trans-unit> + <trans-unit id="formEditor.redo_button" xml:space="preserve"> + <source>Redo</source> + </trans-unit> + <trans-unit id="formEditor.new_page_button" xml:space="preserve"> + <source>Create new page</source> + </trans-unit> + <trans-unit id="formEditor.structure" xml:space="preserve"> + <source>Structure</source> + </trans-unit> + <trans-unit id="formEditor.insert_elements" xml:space="preserve"> + <source>Insert elements</source> + </trans-unit> + <trans-unit id="formEditor.insert_pages" xml:space="preserve"> + <source>Insert pages</source> + </trans-unit> + + <trans-unit id="formEditor.formElementPropertyValidatorsDefinition.NotEmpty.label" xml:space="preserve"> + <source>Required</source> + </trans-unit> + <trans-unit id="formEditor.formElementPropertyValidatorsDefinition.Integer.label" xml:space="preserve"> + <source>Not a number</source> + </trans-unit> + <trans-unit id="formEditor.formElementPropertyValidatorsDefinition.NaiveEmail.label" xml:space="preserve"> + <source>Invalid email address</source> + </trans-unit> + <trans-unit id="formEditor.formElementPropertyValidatorsDefinition.FormElementIdentifierWithinCurlyBraces.label" xml:space="preserve"> + <source>Invalid form element</source> + </trans-unit> + + <trans-unit id="formEditor.formElementGroups.input.label" xml:space="preserve"> + <source>Basic elements</source> + </trans-unit> + <trans-unit id="formEditor.formElementGroups.select.label" xml:space="preserve"> + <source>Select elements</source> + </trans-unit> + <trans-unit id="formEditor.formElementGroups.custom.label" xml:space="preserve"> + <source>Advanced elements</source> + </trans-unit> + <trans-unit id="formEditor.formElementGroups.container.label" xml:space="preserve"> + <source>Container elements</source> + </trans-unit> + <trans-unit id="formEditor.formElementGroups.page.label" xml:space="preserve"> + <source>Page types</source> + </trans-unit> + + <trans-unit id="formEditor.elements.BaseFormElementMixin.editor.label.label" xml:space="preserve"> + <source>Form name</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Form.saveSuccessFlashMessageTitle" xml:space="preserve"> + <source>Save</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.saveSuccessFlashMessageMessage" xml:space="preserve"> + <source>The form has been successfully saved.</source> + </trans-unit> + + <trans-unit id="formEditor.modals.validationErrors.dialogTitle" xml:space="preserve"> + <source>Alert</source> + </trans-unit> + <trans-unit id="formEditor.modals.validationErrors.dialogMessage" xml:space="preserve"> + <source>Some elements are not configured properly. Please check the following elements:</source> + </trans-unit> + <trans-unit id="formEditor.modals.validationErrors.confirmButton" xml:space="preserve"> + <source>OK</source> + </trans-unit> + + <trans-unit id="formEditor.modals.insertElements.dialogTitle" xml:space="preserve"> + <source>New element</source> + </trans-unit> + <trans-unit id="formEditor.modals.newPages.dialogTitle" xml:space="preserve"> + <source>New page</source> + </trans-unit> + + <trans-unit id="formEditor.modals.removeElement.dialogTitle" xml:space="preserve"> + <source>Remove element?</source> + </trans-unit> + <trans-unit id="formEditor.modals.removeElement.dialogMessage" xml:space="preserve"> + <source>Are you sure that you want to remove this element?</source> + </trans-unit> + <trans-unit id="formEditor.modals.removeElement.confirmButton" xml:space="preserve"> + <source>Remove</source> + </trans-unit> + <trans-unit id="formEditor.modals.removeElement.cancleButton" xml:space="preserve"> + <source>Cancel</source> + </trans-unit> + <trans-unit id="formEditor.modals.removeElement.lastAvailablePageFlashMessageTitle" xml:space="preserve"> + <source>Error</source> + </trans-unit> + <trans-unit id="formEditor.modals.removeElement.lastAvailablePageFlashMessageMessage" xml:space="preserve"> + <source>There must be at least one page within your form.</source> + </trans-unit> + + <trans-unit id="formEditor.modals.close.dialogMessage" xml:space="preserve"> + <source>You have currently unsaved changes. Are you sure that you want to discard all changes?</source> + </trans-unit> + <trans-unit id="formEditor.modals.close.dialogTitle" xml:space="preserve"> + <source>Do you want to quit without saving?</source> + </trans-unit> + <trans-unit id="formEditor.modals.close.confirmButton" xml:space="preserve"> + <source>Yes, discard my changes</source> + </trans-unit> + <trans-unit id="formEditor.modals.close.cancleButton" xml:space="preserve"> + <source>No, I will continue editing</source> + </trans-unit> + + <trans-unit id="formEditor.pagination.title" xml:space="preserve"> + <source>Page {0} of {1}</source> + </trans-unit> + + <trans-unit id="formEditor.elements.MinimumMaximumEditorsMixin.editor.minimum.label" xml:space="preserve"> + <source>Minimum</source> + </trans-unit> + <trans-unit id="formEditor.elements.MinimumMaximumEditorsMixin.editor.maximum.label" xml:space="preserve"> + <source>Maximum</source> + </trans-unit> + + <trans-unit id="formEditor.elements.TextMixin.editor.placeholder.label" xml:space="preserve"> + <source>Placeholder</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.defaultValue.label" xml:space="preserve"> + <source>Default value</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.validators.label" xml:space="preserve"> + <source>Validators</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.validators.EmptyValue.label" xml:space="preserve"> + <source>Add validator</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.validators.Alphanumeric.label" xml:space="preserve"> + <source>Alphanumeric</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.validators.Text.label" xml:space="preserve"> + <source>Non-XML text</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.validators.StringLength.label" xml:space="preserve"> + <source>String length</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.validators.EmailAddress.label" xml:space="preserve"> + <source>Email</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.validators.Integer.label" xml:space="preserve"> + <source>Integer number</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.validators.Float.label" xml:space="preserve"> + <source>Floating-point number</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.validators.NumberRange.label" xml:space="preserve"> + <source>Number range</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.editor.validators.RegularExpression.label" xml:space="preserve"> + <source>Regular expression</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.validators.Alphanumeric.editor.header.label" xml:space="preserve"> + <source>Alphanumeric</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.validators.Text.editor.header.label" xml:space="preserve"> + <source>Non-XML text</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.validators.StringLength.editor.header.label" xml:space="preserve"> + <source>String length</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.validators.EmailAddress.editor.header.label" xml:space="preserve"> + <source>Email</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.validators.Integer.editor.header.label" xml:space="preserve"> + <source>Integer number</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.validators.Float.editor.header.label" xml:space="preserve"> + <source>Floating-point number</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.validators.NumberRange.editor.header.label" xml:space="preserve"> + <source>Number range</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.validators.RegularExpression.editor.header.label" xml:space="preserve"> + <source>Regular expression</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.validators.RegularExpression.editor.regex.label" xml:space="preserve"> + <source>Regular expression</source> + </trans-unit> + <trans-unit id="formEditor.elements.TextMixin.validators.RegularExpression.editor.regex.fieldExplanationText" xml:space="preserve"> + <source>Enter a valid PHP PCRE regular expression here.</source> + </trans-unit> + + <trans-unit id="formEditor.elements.SelectionMixin.editor.options.label" xml:space="preserve"> + <source>Choices</source> + </trans-unit> + <trans-unit id="formEditor.elements.SelectionMixin.editor.options.removeLastAvailableRowFlashMessageTitle" xml:space="preserve"> + <source>Error</source> + </trans-unit> + <trans-unit id="formEditor.elements.SelectionMixin.editor.options.removeLastAvailableRowFlashMessageMessage" xml:space="preserve"> + <source>There must be at least one row within your options.</source> + </trans-unit> + + <trans-unit id="formEditor.elements.MultiSelectionMixin.editor.validators.label" xml:space="preserve"> + <source>Validators</source> + </trans-unit> + <trans-unit id="formEditor.elements.MultiSelectionMixin.editor.validators.EmptyValue.label" xml:space="preserve"> + <source>Add validator</source> + </trans-unit> + <trans-unit id="formEditor.elements.MultiSelectionMixin.editor.validators.Count.label" xml:space="preserve"> + <source>Number of submitted values</source> + </trans-unit> + <trans-unit id="formEditor.elements.MultiSelectionMixin.validators.Count.editor.header.label" xml:space="preserve"> + <source>Number of submitted values</source> + </trans-unit> + + <trans-unit id="formEditor.elements.FileUploadMixin.editor.saveToFileMount.label" xml:space="preserve"> + <source>Uploads save path</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Form.editor.submitButtonLabel.label" xml:space="preserve"> + <source>Submit label</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.editor.finishers.label" xml:space="preserve"> + <source>Finishers</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.editor.finishers.EmptyValue.label" xml:space="preserve"> + <source>Add finisher</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.editor.finishers.EmailToSender.label" xml:space="preserve"> + <source>Email to sender</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.editor.finishers.EmailToReceiver.label" xml:space="preserve"> + <source>Email to receiver</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.editor.finishers.Redirect.label" xml:space="preserve"> + <source>Redirect to a page</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.editor.finishers.DeleteUploads.label" xml:space="preserve"> + <source>Delete uploads</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.header.label" xml:space="preserve"> + <source>Send email (sender)</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.subject.label" xml:space="preserve"> + <source>Subject</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.recipientAddress.label" xml:space="preserve"> + <source>Recipient address</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.recipientName.label" xml:space="preserve"> + <source>Recipient name</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.senderAddress.label" xml:space="preserve"> + <source>Sender address</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.senderName.label" xml:space="preserve"> + <source>Sender name</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.replyToAddress.label" xml:space="preserve"> + <source>Reply-to address</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.carbonCopyAddress.label" xml:space="preserve"> + <source>CC address</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.blindCarbonCopyAddress.label" xml:space="preserve"> + <source>BCC address</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.format.label" xml:space="preserve"> + <source>Format</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.format.1" xml:space="preserve"> + <source>Plain text</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.format.2" xml:space="preserve"> + <source>HTML</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToSender.editor.attachUploads.label" xml:space="preserve"> + <source>Attach uploads</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.header.label" xml:space="preserve"> + <source>Send email (receiver)</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.subject.label" xml:space="preserve"> + <source>Subject</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientAddress.label" xml:space="preserve"> + <source>Recipient address</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.recipientName.label" xml:space="preserve"> + <source>Recipient name</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.senderAddress.label" xml:space="preserve"> + <source>Sender address</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.senderName.label" xml:space="preserve"> + <source>Sender name</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.replyToAddress.label" xml:space="preserve"> + <source>Reply-to address</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.carbonCopyAddress.label" xml:space="preserve"> + <source>CC address</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.blindCarbonCopyAddress.label" xml:space="preserve"> + <source>BCC address</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.format.label" xml:space="preserve"> + <source>Format</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.format.1" xml:space="preserve"> + <source>Plain text</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.format.2" xml:space="preserve"> + <source>HTML</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.attachUploads.label" xml:space="preserve"> + <source>Attach uploads</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.language.label" xml:space="preserve"> + <source>Translation language</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.EmailToReceiver.editor.language.1" xml:space="preserve"> + <source>EN</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Form.finisher.Redirect.editor.header.label" xml:space="preserve"> + <source>Redirect to a page</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.Redirect.editor.pageUid.label" xml:space="preserve"> + <source>Page</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.Redirect.editor.pageUid.buttonLabel" xml:space="preserve"> + <source>Pages</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.Redirect.editor.additionalParameters.label" xml:space="preserve"> + <source>Additional parameters</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Form.finisher.DeleteUploads.editor.header.label" xml:space="preserve"> + <source>Delete uploads</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.Confirmation.editor.header.label" xml:space="preserve"> + <source>Confirmation message</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.Closure.editor.header.label" xml:space="preserve"> + <source>Execute a closure</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.FlashMessage.editor.header.label" xml:space="preserve"> + <source>Flash message</source> + </trans-unit> + <trans-unit id="formEditor.elements.Form.finisher.SaveToDatabase.editor.header.label" xml:space="preserve"> + <source>Save the mail to the Database</source> + </trans-unit> + + <trans-unit id="formEditor.elements.ReadOnlyFormElement.editor.label.label" xml:space="preserve"> + <source>Element name</source> + </trans-unit> + + <trans-unit id="formEditor.elements.FormElement.editor.label.label" xml:space="preserve"> + <source>Element name</source> + </trans-unit> + <trans-unit id="formEditor.elements.FormElement.editor.requiredValidator.label" xml:space="preserve"> + <source>Required field</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Page.label" xml:space="preserve"> + <source>Page</source> + </trans-unit> + <trans-unit id="formEditor.elements.Page.editor.label.label" xml:space="preserve"> + <source>Page name</source> + </trans-unit> + <trans-unit id="formEditor.elements.Page.editor.previousButtonLabel.label" xml:space="preserve"> + <source>Previous label</source> + </trans-unit> + <trans-unit id="formEditor.elements.Page.editor.nextButtonLabel.label" xml:space="preserve"> + <source>Next label</source> + </trans-unit> + + <trans-unit id="formEditor.elements.SummaryPage.label" xml:space="preserve"> + <source>Summary page</source> + </trans-unit> + <trans-unit id="formEditor.elements.SummaryPage.editor.label.label" xml:space="preserve"> + <source>Page name</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Fieldset.label" xml:space="preserve"> + <source>Fieldset</source> + </trans-unit> + <trans-unit id="formEditor.elements.Fieldset.editor.label.label" xml:space="preserve"> + <source>Fieldset name</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Text.label" xml:space="preserve"> + <source>Text</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Password.label" xml:space="preserve"> + <source>Password</source> + </trans-unit> + <trans-unit id="formEditor.elements.AdvancedPassword.label" xml:space="preserve"> + <source>Advanced password</source> + </trans-unit> + + <trans-unit id="formEditor.elements.AdvancedPassword.editor.confirmationLabel.label" xml:space="preserve"> + <source>Confirmation label</source> + </trans-unit> + <trans-unit id="formEditor.element.AdvancedPassword.editor.confirmationLabel.predefinedDefaults" xml:space="preserve"> + <source>Confirmation</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Textarea.label" xml:space="preserve"> + <source>Textarea</source> + </trans-unit> + + <trans-unit id="formEditor.elements.Checkbox.label" xml:space="preserve"> + <source>Checkbox</source> + </trans-unit> + + <trans-unit id="formEditor.elements.MultiCheckbox.label" xml:space="preserve"> + <source>Multi checkbox</source> + </trans-unit> + + <trans-unit id="formEditor.elements.MultiSelect.label" xml:space="preserve"> + <source>Multi select</source> + </trans-unit> + + <trans-unit id="formEditor.elements.RadioButton.label" xml:space="preserve"> + <source>Radio button</source> + </trans-unit> + + <trans-unit id="formEditor.elements.SingleSelect.label" xml:space="preserve"> + <source>Single select</source> + </trans-unit> + + <trans-unit id="formEditor.elements.DatePicker.label" xml:space="preserve"> + <source>Date picker</source> + </trans-unit> + <trans-unit id="formEditor.elements.DatePicker.editor.validators.label" xml:space="preserve"> + <source>Validators</source> + </trans-unit> + <trans-unit id="formEditor.elements.DatePicker.editor.validators.EmptyValue.label" xml:space="preserve"> + <source>Add validator</source> + </trans-unit> + <trans-unit id="formEditor.elements.DatePicker.editor.validators.DateTime.label" xml:space="preserve"> + <source>Date/time</source> + </trans-unit> + <trans-unit id="formEditor.elements.DatePicker.validators.DateTime.editor.header.label" xml:space="preserve"> + <source>Date/time</source> + </trans-unit> + + <trans-unit id="formEditor.elements.StaticText.label" xml:space="preserve"> + <source>Static text</source> + </trans-unit> + <trans-unit id="formEditor.elements.StaticText.editor.staticText.label" xml:space="preserve"> + <source>Text</source> + </trans-unit> + + <trans-unit id="formEditor.elements.ContentElement.label" xml:space="preserve"> + <source>Content element</source> + </trans-unit> + <trans-unit id="formEditor.elements.ContentElement.selectContentElement" xml:space="preserve"> + <source>No content element selected</source> + </trans-unit> + + <trans-unit id="formEditor.elements.StaticText.editor.contentElement.label" xml:space="preserve"> + <source>Content element uid</source> + </trans-unit> + <trans-unit id="formEditor.elements.StaticText.editor.contentElement.buttonLabel" xml:space="preserve"> + <source>tt_content</source> + </trans-unit> + + <trans-unit id="formEditor.elements.FileUpload.label" xml:space="preserve"> + <source>File upload</source> + </trans-unit> + <trans-unit id="formEditor.elements.FileUpload.editor.allowedMimeTypes.label" xml:space="preserve"> + <source>Allowed file mime types</source> + </trans-unit> + <trans-unit id="formEditor.elements.FileUpload.editor.allowedMimeTypes.1" xml:space="preserve"> + <source>Documents (doc, docx, odt, pdf)</source> + </trans-unit> + <trans-unit id="formEditor.elements.FileUpload.editor.allowedMimeTypes.2" xml:space="preserve"> + <source>Spreadsheet documents (xls)</source> + </trans-unit> + + <trans-unit id="formEditor.elements.ImageUpload.label" xml:space="preserve"> + <source>Image upload</source> + </trans-unit> + + <trans-unit id="formEditor.elements.UnknownElement" xml:space="preserve"> + <source>Unknown Element</source> + </trans-unit> + + <trans-unit id="formEditor.stage.toolbar.new_element" xml:space="preserve"> + <source>Create new element</source> + </trans-unit> + <trans-unit id="formEditor.stage.toolbar.remove" xml:space="preserve"> + <source>Remove</source> + </trans-unit> + <trans-unit id="formEditor.stage.toolbar.new_element.inside" xml:space="preserve"> + <source>Inside</source> + </trans-unit> + <trans-unit id="formEditor.stage.toolbar.new_element.after" xml:space="preserve"> + <source>After</source> + </trans-unit> + </body> + </file> </xliff> diff --git a/typo3/sysext/form/Resources/Private/Language/locallang.xlf b/typo3/sysext/form/Resources/Private/Language/locallang.xlf index cb38da6f0ce7..f7fe93314983 100644 --- a/typo3/sysext/form/Resources/Private/Language/locallang.xlf +++ b/typo3/sysext/form/Resources/Private/Language/locallang.xlf @@ -1,209 +1,96 @@ <?xml version="1.0" encoding="UTF-8"?> <xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff"> - <file t3:id="1415814824" source-language="en" datatype="plaintext" original="messages" date="2011-10-17T20:22:32Z" product-name="form"> - <header/> - <body> - <trans-unit id="tx_form_domain_model_element_button.value"> - <source>Push this button</source> - </trans-unit> - <trans-unit id="tx_form_domain_model_element_submit.value"> - <source>Submit form</source> - </trans-unit> - <trans-unit id="tx_form_domain_model_element_reset.value"> - <source>Clear form</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_alphabetic.message"> - <source>Use alphabetic characters</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_alphabetic.message2"> - <source>whitespace allowed</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_alphabetic.error"> - <source>The value contains not only alphabetic characters</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_alphanumeric.message"> - <source>Use alphanumeric characters</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_alphanumeric.message2"> - <source>whitespace allowed</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_alphanumeric.error"> - <source>The value contains not only alphanumeric characters</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_between.message"> - <source>The value must be between %minimum and %maximum</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_between.message2"> - <source>inclusively</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_between.error"> - <source>The value is not between %minimum and %maximum</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_between.error2"> - <source>inclusively</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.message"> - <source>(%format)</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.A"> - <source>dddd</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.a"> - <source>ddd</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.d"> - <source>dd</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.e"> - <source>d</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.B"> - <source>mmmm</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.b"> - <source>mmm</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.m"> - <source>mm</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.Y"> - <source>yyyy</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.y"> - <source>yy</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.H"> - <source>HH</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.I"> - <source>hh</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.M"> - <source>mm</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.strftime.S"> - <source>ss</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_date.error"> - <source>The value does not appear to be a valid date</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_digit.message"> - <source>Use digit characters</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_digit.error"> - <source>The value contains not only digit characters</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_email.message"> - <source>(john.doe@domain.com)</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_email.error"> - <source>This is not a valid email address</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_equals.message"> - <source>This field must be equal to '%field'</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_equals.error"> - <source>The value does not equal the value in field '%field'</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_fileallowedtypes.message"> - <source>(%allowedTypes)</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_fileallowedtypes.error"> - <source>The file type is not allowed</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_filemaximumsize.message"> - <source>The filesize must be smaller than %maximum</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_filemaximumsize.error"> - <source>The filesize is too big</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_fileminimumsize.message"> - <source>The filesize must be bigger than %minimum</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_fileminimumsize.error"> - <source>The filesize is too small</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_float.message"> - <source>Enter a float</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_float.error"> - <source>The value does not appear to be a float</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_greaterthan.message"> - <source>The value must be greater than %minimum</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_greaterthan.error"> - <source>The value does not appear to be greater than %minimum</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_inarray.message"> - <source>Only a few values are possible</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_inarray.error"> - <source>The value does not appear to be valid</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_integer.message"> - <source>Use an integer</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_integer.error"> - <source>The value does not appear to be an integer</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_ip.message"> - <source>(123.123.123.123)</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_ip.error"> - <source>The value does not appear to be a valid IP address</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_length.message"> - <source>The length of the value must have a minimum of %minimum characters</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_length.message2"> - <source>and a maximum of %maximum</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_length.error"> - <source>The value is less than %minimum characters long</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_length.error2"> - <source>or longer than %maximum</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_lessthan.message"> - <source>The value must be less than %maximum</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_lessthan.error"> - <source>The value does not appear to be less than %maximum</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_regexp.message"> - <source>Use the right pattern</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_regexp.error"> - <source>The value does not match against pattern</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_required.message"> - <source>Required</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_required.error"> - <source>This field is required</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_uri.message"> - <source>The value must be a URI</source> - </trans-unit> - <trans-unit id="tx_form_system_validate_uri.error"> - <source>The value does not appear to be a URI</source> - </trans-unit> - <trans-unit id="tx_form_view_confirmation.message"> - <source>Please check your input and confirm by using the appropriate button</source> - </trans-unit> - <trans-unit id="tx_form_view_confirmation.confirm"> - <source>Confirm</source> - </trans-unit> - <trans-unit id="tx_form_view_confirmation.donotconfirm"> - <source>Go back to the form</source> - </trans-unit> - <trans-unit id="tx_form_view_mail.success"> - <source>The form has been sent successfully by mail</source> - </trans-unit> - <trans-unit id="tx_form_view_mail.error"> - <source>There was an error when sending the form by mail</source> - </trans-unit> - </body> - </file> + <file t3:id="1475977066" source-language="en" datatype="plaintext" original="messages" date="2016-10-09T03:38:32Z" product-name="form"> + <header/> + <body> + <trans-unit id="element.Form.renderingOptions.submitButtonLabel" xml:space="preserve"> + <source>Submit</source> + </trans-unit> + <trans-unit id="element.Page.renderingOptions.previousButtonLabel" xml:space="preserve"> + <source>previous Page</source> + </trans-unit> + <trans-unit id="element.Page.renderingOptions.nextButtonLabel" xml:space="preserve"> + <source>next Page</source> + </trans-unit> + <trans-unit id="element.SummaryPage.renderingOptions.previousButtonLabel" xml:space="preserve"> + <source>previous Page</source> + </trans-unit> + <trans-unit id="element.SummaryPage.renderingOptions.nextButtonLabel" xml:space="preserve"> + <source>next Page</source> + </trans-unit> + <trans-unit id="element.ImageUpload.properties.SummaryPage.altText" xml:space="preserve"> + <source>uploaded image</source> + </trans-unit> + + <trans-unit id="validation.error.1221560910" xml:space="preserve"> + <source>This field is mandatory</source> + </trans-unit> + <trans-unit id="validation.error.1221560718" xml:space="preserve"> + <source>This field is mandatory</source> + </trans-unit> + <trans-unit id="validation.error.1347992400" xml:space="preserve"> + <source>This field is mandatory</source> + </trans-unit> + <trans-unit id="validation.error.1347992453" xml:space="preserve"> + <source>This field is mandatory</source> + </trans-unit> + <trans-unit id="validation.error.1238087674" xml:space="preserve"> + <source>Please enter a valid Date</source> + </trans-unit> + <trans-unit id="validation.error.1221551320" xml:space="preserve"> + <source>Please enter letters or digits</source> + </trans-unit> + <trans-unit id="validation.error.1221565786" xml:space="preserve"> + <source>Please enter a valid text (e.g. without XML tags)</source> + </trans-unit> + <trans-unit id="validation.error.1238110957" xml:space="preserve"> + <source>Please enter a valid text</source> + </trans-unit> + <trans-unit id="validation.error.1269883975" xml:space="preserve"> + <source>Please enter a valid text</source> + </trans-unit> + <trans-unit id="validation.error.1428504122" xml:space="preserve"> + <source>Please enter a text between %s and %s characters</source> + </trans-unit> + <trans-unit id="validation.error.1238108068" xml:space="preserve"> + <source>Please enter a text which is longer than %s characters</source> + </trans-unit> + <trans-unit id="validation.error.1238108069" xml:space="preserve"> + <source>Please enter a text which is not longer than %s characters</source> + </trans-unit> + <trans-unit id="validation.error.1221559976" xml:space="preserve"> + <source>Please enter a valid email address</source> + </trans-unit> + <trans-unit id="validation.error.1221560494" xml:space="preserve"> + <source>Please enter a valid number</source> + </trans-unit> + <trans-unit id="validation.error.1221560288" xml:space="preserve"> + <source>Please enter a valid floating point number</source> + </trans-unit> + <trans-unit id="validation.error.1221563685" xml:space="preserve"> + <source>Please enter a valid number</source> + </trans-unit> + <trans-unit id="validation.error.1221561046" xml:space="preserve"> + <source>Please enter a valid number between %s and %s</source> + </trans-unit> + <trans-unit id="validation.error.1221565130" xml:space="preserve"> + <source>Please enter a valid value</source> + </trans-unit> + <trans-unit id="validation.error.1475002976" xml:space="preserve"> + <source>The given subject is not countable.</source> + </trans-unit> + <trans-unit id="validation.error.1475002994" xml:space="preserve"> + <source>Please enter a valid number between %s and %s</source> + </trans-unit> + <trans-unit id="validation.error.1471708997" xml:space="preserve"> + <source>The given value was not an instance of \TYPO3\CMS\Extbase\Domain\Model\FileReference or \TYPO3\CMS\Core\Resource\File.</source> + </trans-unit> + <trans-unit id="validation.error.1471708998" xml:space="preserve"> + <source>The media type "%s" is not allowed for this file.</source> + </trans-unit> + <trans-unit id="validation.error.1476396435" xml:space="preserve"> + <source>Do not fill this field</source> + </trans-unit> + </body> + </file> </xliff> diff --git a/typo3/sysext/form/Resources/Private/Language/locallang_formManager_javascript.xlf b/typo3/sysext/form/Resources/Private/Language/locallang_formManager_javascript.xlf new file mode 100644 index 000000000000..1468bc84c8c6 --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Language/locallang_formManager_javascript.xlf @@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff"> + <file t3:id="1477653834" source-language="en" datatype="plaintext" original="messages" date="2016-10-09T03:38:32Z" product-name="form"> + <header/> + <body> + <trans-unit id="formManager.form_name" xml:space="preserve"> + <source>Form name</source> + </trans-unit> + <trans-unit id="formManager.new_form_name" xml:space="preserve"> + <source>New form name</source> + </trans-unit> + <trans-unit id="formManager.form_save_path" xml:space="preserve"> + <source>Form storage</source> + </trans-unit> + <trans-unit id="formManager.form_prototype" xml:space="preserve"> + <source>Form prototype</source> + </trans-unit> + <trans-unit id="formManager.form_template" xml:space="preserve"> + <source>Start template</source> + </trans-unit> + <trans-unit id="formManager.cancel" xml:space="preserve"> + <source>Cancel</source> + </trans-unit> + <trans-unit id="formManager.remove_form" xml:space="preserve"> + <source>Remove</source> + </trans-unit> + <trans-unit id="formManager.remove_form_title" xml:space="preserve"> + <source>Remove form</source> + </trans-unit> + <trans-unit id="formManager.remove_form_message" xml:space="preserve"> + <source>Are you sure that you want to remove this form?</source> + </trans-unit> + <trans-unit id="formManager.newFormWizard.step1.title" xml:space="preserve"> + <source>Create new form</source> + </trans-unit> + <trans-unit id="formManager.newFormWizard.step1.advanced" xml:space="preserve"> + <source>Advanced settings</source> + </trans-unit> + <trans-unit id="formManager.newFormWizard.step2.title" xml:space="preserve"> + <source>Advanced settings</source> + </trans-unit> + <trans-unit id="formManager.newFormWizard.step3.title" xml:space="preserve"> + <source>Ready</source> + </trans-unit> + <trans-unit id="formManager.newFormWizard.step3.message" xml:space="preserve"> + <source>Now you are ready to create your new form.</source> + </trans-unit> + <trans-unit id="formManager.duplicateFormWizard.step1.title" xml:space="preserve"> + <source>Duplicate form "{0}"</source> + </trans-unit> + <trans-unit id="formManager.no_references" xml:space="preserve"> + <source>There are no references yet</source> + </trans-unit> + <trans-unit id="formManager.page" xml:space="preserve"> + <source>Page</source> + </trans-unit> + <trans-unit id="formManager.record" xml:space="preserve"> + <source>Record</source> + </trans-unit> + <trans-unit id="formManager.references.title" xml:space="preserve"> + <source>References</source> + </trans-unit> + <trans-unit id="formManager.references.headline" xml:space="preserve"> + <source>References for "{0}"</source> + </trans-unit> + </body> + </file> +</xliff> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Language/locallang_module.xlf b/typo3/sysext/form/Resources/Private/Language/locallang_module.xlf new file mode 100644 index 000000000000..fd1de2ed0b3c --- /dev/null +++ b/typo3/sysext/form/Resources/Private/Language/locallang_module.xlf @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff"> + <file t3:id="1465049996" source-language="en" datatype="plaintext" original="messages" date="2016-06-04T20:22:33Z" product-name="form"> + <header/> + <body> + <trans-unit id="mlang_labels_tablabel"> + <source>Build custom forms</source> + </trans-unit> + <trans-unit id="mlang_labels_tabdescr"> + <source>Build custom forms. TODO: Add more description.</source> + </trans-unit> + <trans-unit id="mlang_tabs_tab"> + <source>Forms</source> + </trans-unit> + </body> + </file> +</xliff> diff --git a/typo3/sysext/form/Resources/Private/Language/locallang_wizard.xlf b/typo3/sysext/form/Resources/Private/Language/locallang_wizard.xlf deleted file mode 100644 index 47ec19b828be..000000000000 --- a/typo3/sysext/form/Resources/Private/Language/locallang_wizard.xlf +++ /dev/null @@ -1,875 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<xliff version="1.0" xmlns:t3="http://typo3.org/schemas/xliff"> - <file t3:id="1415814825" source-language="en" datatype="plaintext" original="messages" date="2011-10-17T20:22:33Z" product-name="form"> - <header/> - <body> - <trans-unit id="title"> - <source>Form Wizard</source> - </trans-unit> - <trans-unit id="yes"> - <source>Yes</source> - </trans-unit> - <trans-unit id="no"> - <source>No</source> - </trans-unit> - <trans-unit id="refresh"> - <source>Refresh without saving</source> - </trans-unit> - <trans-unit id="errorTitle"> - <source>Wizard error</source> - </trans-unit> - <trans-unit id="errorMessage"> - <source>No reference to record</source> - </trans-unit> - <trans-unit id="save"> - <source>Save form</source> - </trans-unit> - <trans-unit id="saveAndClose"> - <source>Save and close form</source> - </trans-unit> - <trans-unit id="history_undo"> - <source>Undo</source> - </trans-unit> - <trans-unit id="history_redo"> - <source>Redo</source> - </trans-unit> - <trans-unit id="action_save"> - <source>Saving form</source> - </trans-unit> - <trans-unit id="action_save_error"> - <source>Server-side failure with status code</source> - </trans-unit> - <trans-unit id="action_save_message_failed"> - <source>Failed to save the form</source> - </trans-unit> - <trans-unit id="action_save_message_saved"> - <source>Changes saved successfully</source> - </trans-unit> - <trans-unit id="button_remove"> - <source>Remove</source> - </trans-unit> - <trans-unit id="left_elements"> - <source>Elements</source> - </trans-unit> - <trans-unit id="left_elements_intro_title"> - <source>Adding elements to the form</source> - </trans-unit> - <trans-unit id="left_elements_intro_description"> - <source>Drag or double-click elements to add</source> - </trans-unit> - <trans-unit id="left_elements_basic"> - <source>Basic form elements</source> - </trans-unit> - <trans-unit id="basic_button"> - <source>Button</source> - </trans-unit> - <trans-unit id="basic_checkbox"> - <source>Checkbox</source> - </trans-unit> - <trans-unit id="basic_fieldset"> - <source>Fieldset</source> - </trans-unit> - <trans-unit id="basic_fileupload"> - <source>Upload Field</source> - </trans-unit> - <trans-unit id="basic_hidden"> - <source>Hidden Field</source> - </trans-unit> - <trans-unit id="basic_input"> - <source>Input Field</source> - </trans-unit> - <trans-unit id="basic_password"> - <source>Password Field</source> - </trans-unit> - <trans-unit id="basic_radio"> - <source>Radio Button</source> - </trans-unit> - <trans-unit id="basic_reset"> - <source>Reset Button</source> - </trans-unit> - <trans-unit id="basic_select"> - <source>Drop Down</source> - </trans-unit> - <trans-unit id="basic_submit"> - <source>Submit Button</source> - </trans-unit> - <trans-unit id="basic_textarea"> - <source>Text Area</source> - </trans-unit> - <trans-unit id="basic_textline"> - <source>Text Field</source> - </trans-unit> - <trans-unit id="left_elements_content"> - <source>Content elements</source> - </trans-unit> - <trans-unit id="content_header"> - <source>Header</source> - </trans-unit> - <trans-unit id="content_textblock"> - <source>Text block</source> - </trans-unit> - <trans-unit id="left_elements_predefined"> - <source>Predefined form elements</source> - </trans-unit> - <trans-unit id="predefined_email"> - <source>Email</source> - </trans-unit> - <trans-unit id="predefined_radiogroup"> - <source>Radio Button Group</source> - </trans-unit> - <trans-unit id="predefined_checkboxgroup"> - <source>Checkbox Group</source> - </trans-unit> - <trans-unit id="predefined_name"> - <source>Full Name</source> - </trans-unit> - <trans-unit id="left_options"> - <source>Options</source> - </trans-unit> - <trans-unit id="options_error"> - <source>Error</source> - </trans-unit> - <trans-unit id="options_error_message"> - <source>There is an error in the settings of the current element. Please check!</source> - </trans-unit> - <trans-unit id="options_dummy_title"> - <source>Please select an element on the right</source> - </trans-unit> - <trans-unit id="options_dummy_description"> - <source>When an element has been selected, this area will show the options for the selected element</source> - </trans-unit> - <trans-unit id="options_attributes"> - <source>Attributes Properties</source> - </trans-unit> - <trans-unit id="attributes_accept"> - <source>Accept</source> - </trans-unit> - <trans-unit id="attributes_acceptcharset"> - <source>Acceptcharset</source> - </trans-unit> - <trans-unit id="attributes_accesskey"> - <source>Accesskey</source> - </trans-unit> - <trans-unit id="attributes_action"> - <source>Action</source> - </trans-unit> - <trans-unit id="attributes_alt"> - <source>Alt</source> - </trans-unit> - <trans-unit id="attributes_autocomplete"> - <source>Autocomplete</source> - </trans-unit> - <trans-unit id="attributes_autocomplete_none"> - <source>Not set</source> - </trans-unit> - <trans-unit id="attributes_autocomplete_off"> - <source>Off</source> - </trans-unit> - <trans-unit id="attributes_autocomplete_on"> - <source>On</source> - </trans-unit> - <trans-unit id="attributes_autofocus"> - <source>Autofocus</source> - </trans-unit> - <trans-unit id="attributes_checked"> - <source>Checked</source> - </trans-unit> - <trans-unit id="attributes_class"> - <source>Class</source> - </trans-unit> - <trans-unit id="attributes_cols"> - <source>Cols</source> - </trans-unit> - <trans-unit id="attributes_contenteditable"> - <source>Content editable</source> - </trans-unit> - <trans-unit id="attributes_contenteditable_none"> - <source>Not set</source> - </trans-unit> - <trans-unit id="attributes_contenteditable_true"> - <source>True</source> - </trans-unit> - <trans-unit id="attributes_contenteditable_false"> - <source>False</source> - </trans-unit> - <trans-unit id="attributes_contextmenu"> - <source>Contextmenu</source> - </trans-unit> - <trans-unit id="attributes_dir"> - <source>Dir</source> - </trans-unit> - <trans-unit id="attributes_dir_ltr"> - <source>Left to right</source> - </trans-unit> - <trans-unit id="attributes_dir_rtl"> - <source>Right to left</source> - </trans-unit> - <trans-unit id="attributes_disabled"> - <source>Disabled</source> - </trans-unit> - <trans-unit id="attributes_draggable"> - <source>Draggable</source> - </trans-unit> - <trans-unit id="attributes_draggable_none"> - <source>Not set</source> - </trans-unit> - <trans-unit id="attributes_draggable_true"> - <source>True</source> - </trans-unit> - <trans-unit id="attributes_draggable_false"> - <source>False</source> - </trans-unit> - <trans-unit id="attributes_draggable_auto"> - <source>Auto</source> - </trans-unit> - <trans-unit id="attributes_dropzone"> - <source>Dropzone</source> - </trans-unit> - <trans-unit id="attributes_enctype"> - <source>Enctype</source> - </trans-unit> - <trans-unit id="attributes_enctype_1"> - <source>Encoded</source> - </trans-unit> - <trans-unit id="attributes_enctype_2"> - <source>No encoding - File upload</source> - </trans-unit> - <trans-unit id="attributes_enctype_3"> - <source>No encoding - Spaces converted</source> - </trans-unit> - <trans-unit id="attributes_hidden"> - <source>Hidden</source> - </trans-unit> - <trans-unit id="attributes_height"> - <source>Height</source> - </trans-unit> - <trans-unit id="attributes_id"> - <source>Id</source> - </trans-unit> - <trans-unit id="attributes_inputmode"> - <source>Inputmode</source> - </trans-unit> - <trans-unit id="attributes_inputmode_none"> - <source>Not set</source> - </trans-unit> - <trans-unit id="attributes_inputmode_verbatim"> - <source>Verbatim</source> - </trans-unit> - <trans-unit id="attributes_inputmode_latin"> - <source>Latin</source> - </trans-unit> - <trans-unit id="attributes_inputmode_latin-name"> - <source>Latin-name</source> - </trans-unit> - <trans-unit id="attributes_inputmode_latin-prose"> - <source>Latin-prose</source> - </trans-unit> - <trans-unit id="attributes_inputmode_full-width-latin"> - <source>Full-width-latin</source> - </trans-unit> - <trans-unit id="attributes_inputmode_kana"> - <source>Kana</source> - </trans-unit> - <trans-unit id="attributes_inputmode_kana-name"> - <source>Kana-name</source> - </trans-unit> - <trans-unit id="attributes_inputmode_katakana"> - <source>Katakana</source> - </trans-unit> - <trans-unit id="attributes_inputmode_numeric"> - <source>Numeric</source> - </trans-unit> - <trans-unit id="attributes_inputmode_tel"> - <source>Tel</source> - </trans-unit> - <trans-unit id="attributes_inputmode_email"> - <source>Email</source> - </trans-unit> - <trans-unit id="attributes_inputmode_url"> - <source>Url</source> - </trans-unit> - <trans-unit id="attributes_label"> - <source>Label</source> - </trans-unit> - <trans-unit id="attributes_lang"> - <source>Lang</source> - </trans-unit> - <trans-unit id="attributes_list"> - <source>List</source> - </trans-unit> - <trans-unit id="attributes_max"> - <source>Max</source> - </trans-unit> - <trans-unit id="attributes_maxlength"> - <source>Maxlength</source> - </trans-unit> - <trans-unit id="attributes_method"> - <source>Method</source> - </trans-unit> - <trans-unit id="attributes_min"> - <source>Min</source> - </trans-unit> - <trans-unit id="attributes_minlength"> - <source>Minlength</source> - </trans-unit> - <trans-unit id="attributes_method_get"> - <source>Get</source> - </trans-unit> - <trans-unit id="attributes_pattern"> - <source>Pattern</source> - </trans-unit> - <trans-unit id="attributes_method_post"> - <source>Post</source> - </trans-unit> - <trans-unit id="attributes_multiple"> - <source>Multiple</source> - </trans-unit> - <trans-unit id="attributes_name"> - <source>Name</source> - </trans-unit> - <trans-unit id="attributes_novalidate"> - <source>Novalidate</source> - </trans-unit> - <trans-unit id="attributes_placeholder"> - <source>Placeholder</source> - </trans-unit> - <trans-unit id="attributes_readonly"> - <source>Readonly</source> - </trans-unit> - <trans-unit id="attributes_required"> - <source>Required</source> - </trans-unit> - <trans-unit id="attributes_rows"> - <source>Rows</source> - </trans-unit> - <trans-unit id="attributes_selected"> - <source>Selected</source> - </trans-unit> - <trans-unit id="attributes_selectionDirection"> - <source>Selection direction</source> - </trans-unit> - <trans-unit id="attributes_selectionDirection_none"> - <source>Not set</source> - </trans-unit> - <trans-unit id="attributes_selectionDirection_forward"> - <source>Forward</source> - </trans-unit> - <trans-unit id="attributes_selectionDirection_backward"> - <source>Backward</source> - </trans-unit> - <trans-unit id="attributes_selectionEnd"> - <source>Selection end</source> - </trans-unit> - <trans-unit id="attributes_selectionStart"> - <source>Selection start</source> - </trans-unit> - <trans-unit id="attributes_size"> - <source>Size</source> - </trans-unit> - <trans-unit id="attributes_spellcheck"> - <source>Spellcheck</source> - </trans-unit> - <trans-unit id="attributes_spellcheck_none"> - <source>Not set</source> - </trans-unit> - <trans-unit id="attributes_spellcheck_true"> - <source>True</source> - </trans-unit> - <trans-unit id="attributes_spellcheck_false"> - <source>False</source> - </trans-unit> - <trans-unit id="attributes_src"> - <source>Src</source> - </trans-unit> - <trans-unit id="attributes_step"> - <source>Step</source> - </trans-unit> - <trans-unit id="attributes_style"> - <source>Style</source> - </trans-unit> - <trans-unit id="attributes_tabindex"> - <source>Tabindex</source> - </trans-unit> - <trans-unit id="attributes_text"> - <source>Text</source> - </trans-unit> - <trans-unit id="attributes_title"> - <source>Title</source> - </trans-unit> - <trans-unit id="attributes_translate"> - <source>Translate</source> - </trans-unit> - <trans-unit id="attributes_translate_none"> - <source>Not set</source> - </trans-unit> - <trans-unit id="attributes_translate_yes"> - <source>Yes</source> - </trans-unit> - <trans-unit id="attributes_translate_no"> - <source>No</source> - </trans-unit> - <trans-unit id="attributes_type"> - <source>Type</source> - </trans-unit> - <trans-unit id="attributes_type_button"> - <source>button</source> - </trans-unit> - <trans-unit id="attributes_type_checkbox"> - <source>checkbox</source> - </trans-unit> - <trans-unit id="attributes_type_color"> - <source>color</source> - </trans-unit> - <trans-unit id="attributes_type_date"> - <source>date</source> - </trans-unit> - <trans-unit id="attributes_type_datetime"> - <source>datetime</source> - </trans-unit> - <trans-unit id="attributes_type_datetime-local"> - <source>datetime-local</source> - </trans-unit> - <trans-unit id="attributes_type_email"> - <source>email</source> - </trans-unit> - <trans-unit id="attributes_type_file"> - <source>file</source> - </trans-unit> - <trans-unit id="attributes_type_hidden"> - <source>hidden</source> - </trans-unit> - <trans-unit id="attributes_type_image"> - <source>image</source> - </trans-unit> - <trans-unit id="attributes_type_month"> - <source>month</source> - </trans-unit> - <trans-unit id="attributes_type_number"> - <source>number</source> - </trans-unit> - <trans-unit id="attributes_type_password"> - <source>password</source> - </trans-unit> - <trans-unit id="attributes_type_radio"> - <source>radio</source> - </trans-unit> - <trans-unit id="attributes_type_range"> - <source>range</source> - </trans-unit> - <trans-unit id="attributes_type_reset"> - <source>reset</source> - </trans-unit> - <trans-unit id="attributes_type_search"> - <source>search</source> - </trans-unit> - <trans-unit id="attributes_type_submit"> - <source>submit</source> - </trans-unit> - <trans-unit id="attributes_type_tel"> - <source>tel</source> - </trans-unit> - <trans-unit id="attributes_type_text"> - <source>text</source> - </trans-unit> - <trans-unit id="attributes_type_time"> - <source>time</source> - </trans-unit> - <trans-unit id="attributes_type_url"> - <source>url</source> - </trans-unit> - <trans-unit id="attributes_type_week"> - <source>week</source> - </trans-unit> - <trans-unit id="attributes_value"> - <source>Value</source> - </trans-unit> - <trans-unit id="attributes_width"> - <source>Width</source> - </trans-unit> - <trans-unit id="attributes_wrap"> - <source>Wrap</source> - </trans-unit> - <trans-unit id="attributes_wrap_none"> - <source>Not set</source> - </trans-unit> - <trans-unit id="attributes_wrap_soft"> - <source>Soft</source> - </trans-unit> - <trans-unit id="attributes_wrap_hard"> - <source>Hard</source> - </trans-unit> - <trans-unit id="options_label"> - <source>Label Properties</source> - </trans-unit> - <trans-unit id="label_label"> - <source>Label</source> - </trans-unit> - <trans-unit id="label_layout"> - <source>Label position</source> - </trans-unit> - <trans-unit id="label_layout_front"> - <source>In front</source> - </trans-unit> - <trans-unit id="label_layout_back"> - <source>After</source> - </trans-unit> - <trans-unit id="options_legend"> - <source>Legend Properties</source> - </trans-unit> - <trans-unit id="legend_legend"> - <source>Legend</source> - </trans-unit> - <trans-unit id="options_filters"> - <source>Filters</source> - </trans-unit> - <trans-unit id="filters_emptytext"> - <source>Add a filter</source> - </trans-unit> - <trans-unit id="filters_alert_title"> - <source>Adding a filter</source> - </trans-unit> - <trans-unit id="filters_alert_description"> - <source>The selected filter is already present</source> - </trans-unit> - <trans-unit id="filters_dummy_title"> - <source>No filter selected</source> - </trans-unit> - <trans-unit id="filters_dummy_description"> - <source>To add a filter, select one from the dropdown</source> - </trans-unit> - <trans-unit id="filters_alphabetic"> - <source>Alphabetic</source> - </trans-unit> - <trans-unit id="filters_alphanumeric"> - <source>Alphanumeric</source> - </trans-unit> - <trans-unit id="filters_currency"> - <source>Currency</source> - </trans-unit> - <trans-unit id="filters_digit"> - <source>Digit</source> - </trans-unit> - <trans-unit id="filters_integer"> - <source>Integer</source> - </trans-unit> - <trans-unit id="filters_lowercase"> - <source>Lowercase</source> - </trans-unit> - <trans-unit id="filters_regexp"> - <source>Regular Expression</source> - </trans-unit> - <trans-unit id="filters_stripnewlines"> - <source>Strip New Lines</source> - </trans-unit> - <trans-unit id="filters_titlecase"> - <source>Titlecase</source> - </trans-unit> - <trans-unit id="filters_trim"> - <source>Trim</source> - </trans-unit> - <trans-unit id="filters_uppercase"> - <source>Uppercase</source> - </trans-unit> - <trans-unit id="filters_properties_allowwhitespace"> - <source>Allow whitespace</source> - </trans-unit> - <trans-unit id="filters_properties_decimalpoint"> - <source>Decimal point</source> - </trans-unit> - <trans-unit id="filters_properties_thousandseparator"> - <source>Thousand separator</source> - </trans-unit> - <trans-unit id="filters_properties_expression"> - <source>Expression</source> - </trans-unit> - <trans-unit id="filters_properties_characterlist"> - <source>Character list</source> - </trans-unit> - <trans-unit id="filters_properties_none"> - <source>This filter has no configuration settings</source> - </trans-unit> - <trans-unit id="options_validation"> - <source>Validation</source> - </trans-unit> - <trans-unit id="validation_emptytext"> - <source>Add a validation rule</source> - </trans-unit> - <trans-unit id="validation_alert_title"> - <source>Adding a validation rule</source> - </trans-unit> - <trans-unit id="validation_alert_description"> - <source>The selected validation rule is already present</source> - </trans-unit> - <trans-unit id="validation_dummy_title"> - <source>No validation rule selected</source> - </trans-unit> - <trans-unit id="validation_dummy_description"> - <source>To add a validation rule, select one from the dropdown</source> - </trans-unit> - <trans-unit id="validation_alphabetic"> - <source>Alphabetic</source> - </trans-unit> - <trans-unit id="validation_alphanumeric"> - <source>Alphanumeric</source> - </trans-unit> - <trans-unit id="validation_between"> - <source>Between</source> - </trans-unit> - <trans-unit id="validation_date"> - <source>Date</source> - </trans-unit> - <trans-unit id="validation_digit"> - <source>Digit</source> - </trans-unit> - <trans-unit id="validation_email"> - <source>Email address</source> - </trans-unit> - <trans-unit id="validation_equals"> - <source>Equals</source> - </trans-unit> - <trans-unit id="validation_fileallowedtypes"> - <source>Allowed mimetypes for file</source> - </trans-unit> - <trans-unit id="validation_filemaximumsize"> - <source>Maximum size for file (bytes)</source> - </trans-unit> - <trans-unit id="validation_fileminimumsize"> - <source>Minimum size for file (bytes)</source> - </trans-unit> - <trans-unit id="validation_float"> - <source>Float</source> - </trans-unit> - <trans-unit id="validation_greaterthan"> - <source>Greater than</source> - </trans-unit> - <trans-unit id="validation_inarray"> - <source>In array</source> - </trans-unit> - <trans-unit id="validation_integer"> - <source>Integer</source> - </trans-unit> - <trans-unit id="validation_ip"> - <source>Ip address</source> - </trans-unit> - <trans-unit id="validation_length"> - <source>Length</source> - </trans-unit> - <trans-unit id="validation_lessthan"> - <source>Less than</source> - </trans-unit> - <trans-unit id="validation_regexp"> - <source>Regular Expression</source> - </trans-unit> - <trans-unit id="validation_required"> - <source>Required</source> - </trans-unit> - <trans-unit id="validation_uri"> - <source>Uniform Resource Identifier</source> - </trans-unit> - <trans-unit id="validation_properties_message"> - <source>Message</source> - </trans-unit> - <trans-unit id="validation_properties_error"> - <source>Error</source> - </trans-unit> - <trans-unit id="validation_properties_showmessage"> - <source>Show message in label</source> - </trans-unit> - <trans-unit id="validation_properties_allowwhitespace"> - <source>Allow whitespace</source> - </trans-unit> - <trans-unit id="validation_properties_minimum"> - <source>Minimum</source> - </trans-unit> - <trans-unit id="validation_properties_maximum"> - <source>Maximum</source> - </trans-unit> - <trans-unit id="validation_properties_inclusive"> - <source>Inclusive</source> - </trans-unit> - <trans-unit id="validation_properties_format"> - <source>Format</source> - </trans-unit> - <trans-unit id="validation_properties_field"> - <source>Field</source> - </trans-unit> - <trans-unit id="validation_properties_array"> - <source>Array (Comma separated)</source> - </trans-unit> - <trans-unit id="validation_properties_expression"> - <source>Expression</source> - </trans-unit> - <trans-unit id="validation_properties_types"> - <source>Mime types</source> - </trans-unit> - <trans-unit id="options_fieldoptions"> - <source>Field options</source> - </trans-unit> - <trans-unit id="fieldoptions_emptytext"> - <source>No options to display</source> - </trans-unit> - <trans-unit id="fieldoptions_text"> - <source>Text</source> - </trans-unit> - <trans-unit id="fieldoptions_value"> - <source>Value</source> - </trans-unit> - <trans-unit id="fieldoptions_delete"> - <source>Delete</source> - </trans-unit> - <trans-unit id="fieldoptions_selected"> - <source>Selected</source> - </trans-unit> - <trans-unit id="fieldoptions_button_add"> - <source>Add option</source> - </trans-unit> - <trans-unit id="fieldoptions_new"> - <source>New option</source> - </trans-unit> - <trans-unit id="options_various"> - <source>Various Properties</source> - </trans-unit> - <trans-unit id="various_properties_name"> - <source>Name</source> - </trans-unit> - <trans-unit id="various_properties_content"> - <source>Content</source> - </trans-unit> - <trans-unit id="various_properties_headingsize"> - <source>Heading size</source> - </trans-unit> - <trans-unit id="various_properties_prefix"> - <source>Show prefix</source> - </trans-unit> - <trans-unit id="various_properties_suffix"> - <source>Show suffix</source> - </trans-unit> - <trans-unit id="various_properties_text"> - <source>Text</source> - </trans-unit> - <trans-unit id="various_properties_middlename"> - <source>Show middle name</source> - </trans-unit> - <trans-unit id="left_form"> - <source>Form</source> - </trans-unit> - <trans-unit id="form_prefix"> - <source>Prefix</source> - </trans-unit> - <trans-unit id="form_behaviour"> - <source>Behaviour</source> - </trans-unit> - <trans-unit id="behaviour_confirmation_page"> - <source>Confirmation page</source> - </trans-unit> - <trans-unit id="form_postprocessor"> - <source>Post Processors</source> - </trans-unit> - <trans-unit id="postprocessor_emptytext"> - <source>Add a Post Processor</source> - </trans-unit> - <trans-unit id="postprocessor_alert_title"> - <source>Adding a Post Processor</source> - </trans-unit> - <trans-unit id="postprocessor_alert_description"> - <source>The selected post processor is already present</source> - </trans-unit> - <trans-unit id="postprocessor_mail"> - <source>Send email</source> - </trans-unit> - <trans-unit id="postprocessor_redirect"> - <source>Redirect</source> - </trans-unit> - <trans-unit id="postprocessor_dummy_title"> - <source>No post processor selected</source> - </trans-unit> - <trans-unit id="postprocessor_dummy_description"> - <source>To add a post processor, select one from the dropdown</source> - </trans-unit> - <trans-unit id="postprocessor_properties_recipientemail"> - <source>Email address of the recipient</source> - </trans-unit> - <trans-unit id="postprocessor_properties_senderemail"> - <source>Email address of the sender</source> - </trans-unit> - <trans-unit id="postprocessor_properties_subject"> - <source>Subject</source> - </trans-unit> - <trans-unit id="postprocessor_properties_destination"> - <source>Destination to redirect to</source> - </trans-unit> - <trans-unit id="prefix_prefix"> - <source>Prefix</source> - </trans-unit> - <trans-unit id="elements_dummy_title"> - <source>You haven't added any elements yet!</source> - </trans-unit> - <trans-unit id="elements_dummy_description"> - <source>To get started, drag an element from the left panel onto this area</source> - </trans-unit> - <trans-unit id="elements_button_delete"> - <source>Delete this element</source> - </trans-unit> - <trans-unit id="elements_button_edit"> - <source>Edit this element</source> - </trans-unit> - <trans-unit id="elements_confirm_delete_title"> - <source>Delete element</source> - </trans-unit> - <trans-unit id="elements_confirm_delete_description"> - <source>Are you sure you want to delete the selected element?</source> - </trans-unit> - <trans-unit id="elements_label"> - <source>Edit this label</source> - </trans-unit> - <trans-unit id="elements_label_email"> - <source>Email</source> - </trans-unit> - <trans-unit id="elements_label_prefix"> - <source>Prefix</source> - </trans-unit> - <trans-unit id="elements_label_firstname"> - <source>First name</source> - </trans-unit> - <trans-unit id="elements_label_middlename"> - <source>Middle name</source> - </trans-unit> - <trans-unit id="elements_label_lastname"> - <source>Last name</source> - </trans-unit> - <trans-unit id="elements_label_suffix"> - <source>Suffix</source> - </trans-unit> - <trans-unit id="elements_legend"> - <source>Edit this legend</source> - </trans-unit> - <trans-unit id="elements_legend_name"> - <source>Full name</source> - </trans-unit> - <trans-unit id="elements_header_content"> - <source>Edit this header</source> - </trans-unit> - <trans-unit id="elements_textblock_content"> - <source>Edit this textblock</source> - </trans-unit> - <trans-unit id="elements_option_1"> - <source>Option 1</source> - </trans-unit> - <trans-unit id="elements_option_2"> - <source>Option 2</source> - </trans-unit> - <trans-unit id="elements_option_3"> - <source>Option 3</source> - </trans-unit> - <trans-unit id="elements_value_1"> - <source>Value 1</source> - </trans-unit> - <trans-unit id="elements_value_2"> - <source>Value 2</source> - </trans-unit> - <trans-unit id="elements_value_3"> - <source>Value 3</source> - </trans-unit> - </body> - </file> -</xliff> diff --git a/typo3/sysext/form/Resources/Private/Layouts/Default.html b/typo3/sysext/form/Resources/Private/Layouts/Default.html deleted file mode 100644 index 54d9aa0bb6bc..000000000000 --- a/typo3/sysext/form/Resources/Private/Layouts/Default.html +++ /dev/null @@ -1 +0,0 @@ -<f:render section="main" /> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/AdditionalElements/Label.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/AdditionalElements/Label.html deleted file mode 100644 index e39cf1f7bad6..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/AdditionalElements/Label.html +++ /dev/null @@ -1 +0,0 @@ -<label>{model.additionalArguments.label}</label> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/AdditionalElements/Legend.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/AdditionalElements/Legend.html deleted file mode 100644 index 5a70ce3ee2ee..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/AdditionalElements/Legend.html +++ /dev/null @@ -1,3 +0,0 @@ -<f:if condition="{model.additionalArguments.legend}"> - <legend>{model.additionalArguments.legend}</legend> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Checkboxgroup.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Checkboxgroup.html deleted file mode 100644 index 1018baa6079a..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Checkboxgroup.html +++ /dev/null @@ -1,25 +0,0 @@ -<f:if condition="{model.additionalArguments.atLeastOneCheckedChildElement}"> - <f:if condition="{model.showElement}"> - <f:then> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <fieldset - <f:for each="{model.htmlAttributes}" as="htmAttributeValue" key="htmAttributeKey"> - {htmAttributeKey}="{htmAttributeValue}" - </f:for> - > - {f:render(partial: '{themeName}/Confirmation/AdditionalElements/Legend', arguments: {model: model, themeName: themeName})} - <ol> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Confirmation/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </ol> - </fieldset> - </li> - </f:then> - <f:else> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Confirmation/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </f:else> - </f:if> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Fieldset.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Fieldset.html deleted file mode 100644 index 945aa07a5596..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Fieldset.html +++ /dev/null @@ -1,25 +0,0 @@ -<f:if condition="{model.childElements.0}"> - <f:if condition="{model.showElement}"> - <f:then> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <fieldset - <f:for each="{model.htmlAttributes}" as="htmAttributeValue" key="htmAttributeKey"> - {htmAttributeKey}="{htmAttributeValue}" - </f:for> - > - {f:render(partial: '{themeName}/Confirmation/AdditionalElements/Legend', arguments: {model: model, themeName: themeName})} - <ol> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Confirmation/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </ol> - </fieldset> - </li> - </f:then> - <f:else> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Confirmation/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </f:else> - </f:if> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Form.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Form.html deleted file mode 100644 index b3fb2131f44d..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Form.html +++ /dev/null @@ -1,53 +0,0 @@ -<f:form - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - enctype="{model.additionalArguments.enctype}" - method="{model.additionalArguments.method}" - name="{model.additionalArguments.name}" - onreset="{model.additionalArguments.onreset}" - onsubmit="{model.additionalArguments.onsubmit}" - - absolute="{model.additionalArguments.absolute}" - action="dispatchConfirmationButtonClick" - actionUri="{model.additionalArguments.actionUri}" - addQueryString="{model.additionalArguments.addQueryString}" - additionalAttributes="{model.htmlAttributes}" - additionalParams="{model.additionalArguments.additionalParams}" - arguments="{model.additionalArguments.arguments}" - argumentsToBeExcludedFromQueryString="{model.additionalArguments.argumentsToBeExcludedFromQueryString}" - controller="Frontend" - extensionName="Form" - - format="{f:if(condition:'{model.additionalArguments.format}',then:'{model.additionalArguments.format}')}" - hiddenFieldClassName="{model.additionalArguments.hiddenFieldClassName}" - - object="{model}" - - pageType="{model.additionalArguments.pageType}" - pageUid="{model.additionalArguments.pageUid}" - pluginName="Form" - section="{model.additionalArguments.section}"> - - <ol> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Confirmation/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </ol> - - <ol> - <li class="csc-form-confirmation-false"> - <f:form.submit name="confirmation-false" value="<f:translate key='tx_form_view_confirmation.donotconfirm'/>"><f:translate key='tx_form_view_confirmation.donotconfirm'/></f:form.submit> - </li> - <li class="csc-form-confirmation-true"> - <f:form.submit name="confirmation-true" value="<f:translate key='tx_form_view_confirmation.confirm'/>"><f:translate key='tx_form_view_confirmation.confirm'/></f:form.submit> - </li> - </ol> -</f:form> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Radiogroup.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Radiogroup.html deleted file mode 100644 index 1018baa6079a..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/ContainerElements/Radiogroup.html +++ /dev/null @@ -1,25 +0,0 @@ -<f:if condition="{model.additionalArguments.atLeastOneCheckedChildElement}"> - <f:if condition="{model.showElement}"> - <f:then> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <fieldset - <f:for each="{model.htmlAttributes}" as="htmAttributeValue" key="htmAttributeKey"> - {htmAttributeKey}="{htmAttributeValue}" - </f:for> - > - {f:render(partial: '{themeName}/Confirmation/AdditionalElements/Legend', arguments: {model: model, themeName: themeName})} - <ol> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Confirmation/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </ol> - </fieldset> - </li> - </f:then> - <f:else> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Confirmation/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </f:else> - </f:if> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Checkbox.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Checkbox.html deleted file mode 100644 index 734eb4403aa3..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Checkbox.html +++ /dev/null @@ -1,49 +0,0 @@ -<f:if condition="{model.showElement}"> - <f:if condition="{model.additionalArguments.checked} == 'checked'"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Confirmation/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - <f:if condition="{model.additionalArguments.value}">{model.additionalArguments.value}</f:if> - </li> - </f:if> -</f:if> - -<f:if condition="{model.additionalArguments.checked} == 'checked'"> - <f:if condition="{model.additionalArguments.multiple}"> - <f:then> - <f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}][{model.name}]" - value="<f:if condition='{model.additionalArguments.value}'><f:then>{model.additionalArguments.value}</f:then><f:else>{model.additionalArguments.name}-{model.elementCounter}</f:else></f:if>" - - additionalAttributes="{model.htmlAttributes}" - /> - </f:then> - <f:else> - <f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="<f:if condition='{model.additionalArguments.value}'><f:then>{model.additionalArguments.value}</f:then><f:else>{model.additionalArguments.name}-{model.elementCounter}</f:else></f:if>" - - additionalAttributes="{model.htmlAttributes}" - /> - </f:else> - </f:if> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Hidden.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Hidden.html deleted file mode 100644 index 6f3c765a6eb9..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Hidden.html +++ /dev/null @@ -1,18 +0,0 @@ -<li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - additionalAttributes="{model.htmlAttributes}" - /> -</li> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Imagebutton.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Imagebutton.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Input.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Input.html deleted file mode 100644 index e9c809fd2428..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Input.html +++ /dev/null @@ -1,23 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Confirmation/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - {model.additionalArguments.value} - </li> -</f:if> - -<f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - additionalAttributes="{model.htmlAttributes}" -/> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/InputTypeButton.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/InputTypeButton.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Password.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Password.html deleted file mode 100644 index f7e3d8cdb3b5..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Password.html +++ /dev/null @@ -1,16 +0,0 @@ -<f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - additionalAttributes="{model.htmlAttributes}" -/> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Radio.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Radio.html deleted file mode 100644 index 0667ba983c66..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Radio.html +++ /dev/null @@ -1,27 +0,0 @@ -<f:if condition="{model.showElement}"> - <f:if condition="{model.additionalArguments.checked} == 'checked'"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Confirmation/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - <f:if condition="{model.additionalArguments.value}">{model.additionalArguments.value}</f:if> - </li> - </f:if> -</f:if> - -<f:if condition="{model.additionalArguments.checked} == 'checked'"> - <f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="<f:if condition='{model.additionalArguments.value}'><f:then>{model.additionalArguments.value}</f:then><f:else>{model.additionalArguments.name}-{model.elementCounter}</f:else></f:if>" - - additionalAttributes="{model.htmlAttributes}" - /> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Reset.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Reset.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Select.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Select.html deleted file mode 100644 index 2baa6bdbca2a..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Select.html +++ /dev/null @@ -1,111 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers} - -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Confirmation/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - <ol> -</f:if> -<f:for each="{form:aggregateSelectOptions(model:model)}" as="option"> - <f:if condition="{option.options}"> - <f:then> - <f:for each="{option.options}" as="optgroupOption"> - <f:if condition="{optgroupOption.selected}"> - <f:if condition="{model.showElement}"> - <li class="csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {optgroupOption.label} - </li> - </f:if> - <f:if condition="{model.additionalArguments.multiple}"> - <f:then> - <f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}][]" - value="{optgroupOption.value}" - - additionalAttributes="{model.htmlAttributes}" - /> - </f:then> - <f:else> - <f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{optgroupOption.value}" - - additionalAttributes="{model.htmlAttributes}" - /> - </f:else> - </f:if> - </f:if> - </f:for> - </f:then> - <f:else> - <f:if condition="{option.selected}"> - <f:if condition="{model.showElement}"> - <li class="csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {option.label} - </li> - </f:if> - <f:if condition="{model.additionalArguments.multiple}"> - <f:then> - <f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}][]" - value="{option.value}" - - additionalAttributes="{model.htmlAttributes}" - /> - </f:then> - <f:else> - <f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{option.value}" - - additionalAttributes="{model.htmlAttributes}" - /> - </f:else> - </f:if> - </f:if> - </f:else> - </f:if> -</f:for> -<f:if condition="{model.showElement}"> - </ol> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Submit.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Submit.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textarea.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textarea.html deleted file mode 100644 index eaedfd0856f9..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textarea.html +++ /dev/null @@ -1,23 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Confirmation/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - {model.additionalArguments.text} - </li> -</f:if> - -<f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.text}" - - additionalAttributes="{model.htmlAttributes}" -/> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textblock.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textblock.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textfield.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textfield.html deleted file mode 100644 index 4145a534afe1..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Textfield.html +++ /dev/null @@ -1,23 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Confirmation/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - {model.additionalArguments.value} - </li> -</f:if> - -<f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - additionalAttributes="{model.htmlAttributes}" -/> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Upload.html b/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Upload.html deleted file mode 100644 index fae3e351b939..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Upload.html +++ /dev/null @@ -1,8 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Confirmation/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - <f:for each="{model.additionalArguments.uploadedFiles}" as="uploadedFile"> - {uploadedFile.name}<br /> - </f:for> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/AdditionalElements/Label.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/AdditionalElements/Label.html deleted file mode 100644 index 9dc9dfbeddbf..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/AdditionalElements/Label.html +++ /dev/null @@ -1 +0,0 @@ -<em>{model.additionalArguments.label}</em> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/AdditionalElements/Legend.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/AdditionalElements/Legend.html deleted file mode 100644 index e6420befc51c..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/AdditionalElements/Legend.html +++ /dev/null @@ -1 +0,0 @@ -{model.additionalArguments.legend} \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Checkboxgroup.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Checkboxgroup.html deleted file mode 100644 index 4fa40c7046a2..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Checkboxgroup.html +++ /dev/null @@ -1,31 +0,0 @@ -<f:if condition="{model.additionalArguments.atLeastOneCheckedChildElement}"> - <f:if condition="{model.showElement}"> - <f:then> - <tr class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <td colspan="2"> - <table cellspacing="0" style="padding-left: 20px; margin-bottom: 20px;"> - <f:if condition="{model.additionalArguments.legend}"> - <thead> - <tr> - <th colspan="2" align="left"> - {f:render(partial: 'AdditionalElements/Legend', arguments: {model: model})} - </th> - </tr> - </thead> - </f:if> - <tbody> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{element.partialPath}" arguments="{model: element}" /> - </f:for> - </tbody> - </table> - </td> - </tr> - </f:then> - <f:else> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{element.partialPath}" arguments="{model: element}" /> - </f:for> - </f:else> - </f:if> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Fieldset.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Fieldset.html deleted file mode 100644 index 8b4a9e74a669..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Fieldset.html +++ /dev/null @@ -1,31 +0,0 @@ -<f:if condition="{model.childElements.0}"> - <f:if condition="{model.showElement}"> - <f:then> - <tr class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <td colspan="2"> - <table cellspacing="0" style="padding-left: 20px; margin-bottom: 20px;"> - <f:if condition="{model.additionalArguments.legend}"> - <thead> - <tr> - <th colspan="2" align="left"> - {f:render(partial: 'AdditionalElements/Legend', arguments: {model: model})} - </th> - </tr> - </thead> - </f:if> - <tbody> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{element.partialPath}" arguments="{model: element}" /> - </f:for> - </tbody> - </table> - </td> - </tr> - </f:then> - <f:else> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{element.partialPath}" arguments="{model: element}" /> - </f:for> - </f:else> - </f:if> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Form.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Form.html deleted file mode 100644 index dc4d724a604f..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Form.html +++ /dev/null @@ -1,14 +0,0 @@ -<html> - <head> - <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> - </head> - <body> - <table cellspacing="0"> - <tbody> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{element.partialPath}" arguments="{model: element}" /> - </f:for> - </tbody> - </table> - </body> -</html> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Radiogroup.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Radiogroup.html deleted file mode 100644 index 4fa40c7046a2..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/ContainerElements/Radiogroup.html +++ /dev/null @@ -1,31 +0,0 @@ -<f:if condition="{model.additionalArguments.atLeastOneCheckedChildElement}"> - <f:if condition="{model.showElement}"> - <f:then> - <tr class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <td colspan="2"> - <table cellspacing="0" style="padding-left: 20px; margin-bottom: 20px;"> - <f:if condition="{model.additionalArguments.legend}"> - <thead> - <tr> - <th colspan="2" align="left"> - {f:render(partial: 'AdditionalElements/Legend', arguments: {model: model})} - </th> - </tr> - </thead> - </f:if> - <tbody> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{element.partialPath}" arguments="{model: element}" /> - </f:for> - </tbody> - </table> - </td> - </tr> - </f:then> - <f:else> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{element.partialPath}" arguments="{model: element}" /> - </f:for> - </f:else> - </f:if> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Button.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Button.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/ButtonTag.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/ButtonTag.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Checkbox.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Checkbox.html deleted file mode 100644 index a5d44397423e..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Checkbox.html +++ /dev/null @@ -1,10 +0,0 @@ -<f:if condition="{model.showElement}"> -<f:if condition="{model.additionalArguments.checked} == 'checked'"> -<tr class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <td style="width: 200px;"> - {f:render(partial: 'AdditionalElements/Label', arguments: {model: model})} - </td> - <td>{model.additionalArguments.value}</td> -</tr> -</f:if> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/ContentElement.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/ContentElement.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Header.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Header.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Hidden.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Hidden.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Imagebutton.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Imagebutton.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Input.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Input.html deleted file mode 100644 index 3a539949329e..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Input.html +++ /dev/null @@ -1,8 +0,0 @@ -<f:if condition="{model.showElement}"> -<tr class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <td style="width: 200px;"> - {f:render(partial: 'AdditionalElements/Label', arguments: {model: model})} - </td> - <td>{model.additionalArguments.value}</td> -</tr> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/InputTypeButton.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/InputTypeButton.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Password.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Password.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Radio.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Radio.html deleted file mode 100644 index 73e16d87c7b1..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Radio.html +++ /dev/null @@ -1,10 +0,0 @@ -<f:if condition="{model.showElement}"> -<f:if condition="{model.additionalArguments.checked} == 'checked'"> -<tr class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <td style="width: 200px;"> - {f:render(partial: 'AdditionalElements/Label', arguments: {model: model})} - </td> - <td>{model.additionalArguments.value}</td> -</tr> -</f:if> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Reset.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Reset.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Select.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Select.html deleted file mode 100644 index e071ed69d0c4..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Select.html +++ /dev/null @@ -1,26 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers} -<f:if condition="{model.showElement}"> -<tr class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <td style="width: 200px;"> - {f:render(partial: 'AdditionalElements/Label', arguments: {model: model})} - </td> - <td> -<f:for each="{form:aggregateSelectOptions(model:model)}" as="option"> -<f:if condition="{option.options}"> -<f:then> -<f:for each="{option.options}" as="optgroupOption"> -<f:if condition="{optgroupOption.selected}"> - <div class="csc-form-element csc-form-element-{model.elementTypeLowerCase}">{optgroupOption.label}</div> -</f:if> -</f:for> -</f:then> -<f:else> -<f:if condition="{option.selected}"> - <div class="csc-form-element csc-form-element-{model.elementTypeLowerCase}">{option.label}</div> -</f:if> -</f:else> -</f:if> -</f:for> - </td> -</tr> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Submit.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Submit.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textarea.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textarea.html deleted file mode 100644 index 367fe2a72231..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textarea.html +++ /dev/null @@ -1,8 +0,0 @@ -<f:if condition="{model.showElement}"> -<tr class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <td style="width: 200px;" valign="top"> - {f:render(partial: 'AdditionalElements/Label', arguments: {model: model})} - </td> - <td>{model.additionalArguments.text}</td> -</tr> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textblock.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textblock.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textfield.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textfield.html deleted file mode 100644 index 3a539949329e..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Textfield.html +++ /dev/null @@ -1,8 +0,0 @@ -<f:if condition="{model.showElement}"> -<tr class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <td style="width: 200px;"> - {f:render(partial: 'AdditionalElements/Label', arguments: {model: model})} - </td> - <td>{model.additionalArguments.value}</td> -</tr> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Upload.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Upload.html deleted file mode 100644 index 9459c26689f6..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Html/FlatElements/Upload.html +++ /dev/null @@ -1,10 +0,0 @@ -<f:if condition="{model.showElement}"> -<tr class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <td style="width: 200px;"> - {f:render(partial: 'AdditionalElements/Label', arguments: {model: model})} - </td> - <td> -<f:for each="{model.additionalArguments.uploadedFiles}" as="uploadedFile">{uploadedFile.name}<br /></f:for> - </td> -</tr> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Checkboxgroup.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Checkboxgroup.html deleted file mode 100644 index 2a2a5e64fb17..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Checkboxgroup.html +++ /dev/null @@ -1 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers}<f:if condition="{model.additionalArguments.atLeastOneCheckedChildElement}"><f:if condition="{model.showElement}"><f:then><form:plainMail labelContent="{model}" newLineAfterLabel="1" indent="4" /><f:for each="{model.childElements}" as="element"><f:render partial="{element.partialPath}" arguments="{model: element}" /></f:for><form:plainMail indent="-4"/></f:then><f:else><f:for each="{model.childElements}" as="element"><f:render partial="{element.partialPath}" arguments="{model: element}" /></f:for></f:else></f:if></f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Fieldset.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Fieldset.html deleted file mode 100644 index 7bfa2832b075..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Fieldset.html +++ /dev/null @@ -1 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers}<f:if condition="{model.childElements.0}"><f:if condition="{model.showElement}"><f:then><form:plainMail labelContent="{model}" newLineAfterLabel="1" indent="4" /><f:for each="{model.childElements}" as="element"><f:render partial="{element.partialPath}" arguments="{model: element}" /></f:for><form:plainMail indent="-4"/></f:then><f:else><f:for each="{model.childElements}" as="element"><f:render partial="{element.partialPath}" arguments="{model: element}" /></f:for></f:else></f:if></f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Form.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Form.html deleted file mode 100644 index 4a71b60e7da3..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Form.html +++ /dev/null @@ -1,2 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers} -<form:plainMail content="{model}" /><f:for each="{model.childElements}" as="element"><f:render partial="{element.partialPath}" arguments="{model: element}" /></f:for> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Radiogroup.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Radiogroup.html deleted file mode 100644 index 2a2a5e64fb17..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/ContainerElements/Radiogroup.html +++ /dev/null @@ -1 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers}<f:if condition="{model.additionalArguments.atLeastOneCheckedChildElement}"><f:if condition="{model.showElement}"><f:then><form:plainMail labelContent="{model}" newLineAfterLabel="1" indent="4" /><f:for each="{model.childElements}" as="element"><f:render partial="{element.partialPath}" arguments="{model: element}" /></f:for><form:plainMail indent="-4"/></f:then><f:else><f:for each="{model.childElements}" as="element"><f:render partial="{element.partialPath}" arguments="{model: element}" /></f:for></f:else></f:if></f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Button.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Button.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/ButtonTag.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/ButtonTag.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Checkbox.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Checkbox.html deleted file mode 100644 index 29119c77277c..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Checkbox.html +++ /dev/null @@ -1 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers}<f:if condition="{model.showElement}"><f:if condition="{model.additionalArguments.checked} == 'checked'"><form:plainMail labelContent="{model}" content="{model.additionalArguments.value}" /></f:if></f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/ContentElement.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/ContentElement.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Header.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Header.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Hidden.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Hidden.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Imagebutton.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Imagebutton.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Input.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Input.html deleted file mode 100644 index 76676300102b..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Input.html +++ /dev/null @@ -1 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers}<f:if condition="{model.showElement}"><form:plainMail labelContent="{model}" content="{model}" /></f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/InputTypeButton.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/InputTypeButton.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Password.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Password.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Radio.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Radio.html deleted file mode 100644 index 73e09419f922..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Radio.html +++ /dev/null @@ -1,2 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers}<f:if condition="{model.showElement}"><f:if condition="{model.additionalArguments.checked} == 'checked'"><form:plainMail labelContent="{model}" content="{model.additionalArguments.value}" /> -</f:if></f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Reset.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Reset.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Select.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Select.html deleted file mode 100644 index 122c8666ff20..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Select.html +++ /dev/null @@ -1,3 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers}<f:if condition="{model.showElement}"><form:plainMail labelContent="{model}" newLineAfterLabel="1" indent="4" /><f:for each="{form:aggregateSelectOptions(model:model)}" as="option"><f:if condition="{option.options}"><f:then><f:for each="{option.options}" as="optgroupOption"><f:if condition="{optgroupOption.selected}"><form:plainMail content="{optgroupOption.label}" /> -</f:if></f:for></f:then><f:else><f:if condition="{option.selected}"><form:plainMail content="{option.label}" /> -</f:if></f:else></f:if></f:for><form:plainMail indent="-4"/></f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Submit.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Submit.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textarea.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textarea.html deleted file mode 100644 index 5c6ea67aaed5..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textarea.html +++ /dev/null @@ -1 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers}<f:if condition="{model.showElement}"><form:plainMail labelContent="{model}" content="{model.additionalArguments.text}" newLineAfterLabel="1" indent="4" /><form:plainMail indent="-4" /></f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textblock.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textblock.html deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textfield.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textfield.html deleted file mode 100644 index 76676300102b..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Textfield.html +++ /dev/null @@ -1 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers}<f:if condition="{model.showElement}"><form:plainMail labelContent="{model}" content="{model}" /></f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Upload.html b/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Upload.html deleted file mode 100644 index bb9573121a7a..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/PostProcessor/Mail/Plain/FlatElements/Upload.html +++ /dev/null @@ -1,2 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers}<f:if condition="{model.showElement}"><form:plainMail labelContent="{model}" newLineAfterLabel="1" indent="4" /><f:for each="{model.additionalArguments.uploadedFiles}" as="uploadedFile"><form:plainMail content="{uploadedFile.name}" /> -</f:for><form:plainMail indent="-4"/></f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/ErrorValidationMessage.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/ErrorValidationMessage.html deleted file mode 100644 index 891c7c7d1e20..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/ErrorValidationMessage.html +++ /dev/null @@ -1,3 +0,0 @@ -<f:if condition="{model.validationErrorMessages}"> - <strong><f:for each="{model.validationErrorMessages}" as="errorValidationMessage" iteration="iterator">{errorValidationMessage}<f:if condition="{iterator.isLast}"><f:else> - </f:else></f:if></f:for></strong> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/Label.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/Label.html deleted file mode 100644 index b4840a4b4892..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/Label.html +++ /dev/null @@ -1,5 +0,0 @@ -<label for="{model.additionalArguments.id}"> - {model.additionalArguments.label} - {f:render(partial: '{themeName}/Show/AdditionalElements/MandatoryValidationMessage', arguments: {model: model, themeName: themeName})} - {f:render(partial: '{themeName}/Show/AdditionalElements/ErrorValidationMessage', arguments: {model: model, themeName: themeName})} -</label> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/Legend.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/Legend.html deleted file mode 100644 index 7ec127594293..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/Legend.html +++ /dev/null @@ -1,7 +0,0 @@ -<f:if condition="{model.additionalArguments.legend}"> - <legend> - {model.additionalArguments.legend} - {f:render(partial: '{themeName}/Show/AdditionalElements/MandatoryValidationMessage', arguments: {model: model, themeName: themeName})} - {f:render(partial: '{themeName}/Show/AdditionalElements/ErrorValidationMessage', arguments: {model: model, themeName: themeName})} - </legend> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/MandatoryValidationMessage.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/MandatoryValidationMessage.html deleted file mode 100644 index 4b38cea54a68..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/AdditionalElements/MandatoryValidationMessage.html +++ /dev/null @@ -1,3 +0,0 @@ -<f:if condition="{model.mandatoryValidationMessages}"> - <em><f:for each="{model.mandatoryValidationMessages}" as="mandatoryValidationMessage" iteration="iterator">{mandatoryValidationMessage}<f:if condition="{iterator.isLast}"><f:else> - </f:else></f:if></f:for></em> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Checkboxgroup.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Checkboxgroup.html deleted file mode 100644 index fea70dee3ae7..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Checkboxgroup.html +++ /dev/null @@ -1,23 +0,0 @@ -<f:if condition="{model.showElement}"> - <f:then> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <fieldset - <f:for each="{model.htmlAttributes}" as="htmAttributeValue" key="htmAttributeKey"> - {htmAttributeKey}="{htmAttributeValue}" - </f:for> - > - {f:render(partial: '{themeName}/Show/AdditionalElements/Legend', arguments: {model: model, themeName: themeName})} - <ol> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Show/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </ol> - </fieldset> - </li> - </f:then> - <f:else> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Show/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </f:else> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Fieldset.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Fieldset.html deleted file mode 100644 index 8921d4f5cf80..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Fieldset.html +++ /dev/null @@ -1,25 +0,0 @@ -<f:if condition="{model.showElement}"> - <f:then> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <fieldset - <f:for each="{model.htmlAttributes}" as="htmAttributeValue" key="htmAttributeKey"> - {htmAttributeKey}="{htmAttributeValue}" - </f:for> - > - <f:if condition="{model.additionalArguments.legend}"> - <legend>{model.additionalArguments.legend}</legend> - </f:if> - <ol> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Show/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </ol> - </fieldset> - </li> - </f:then> - <f:else> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Show/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </f:else> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Form.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Form.html deleted file mode 100644 index fff6fb537549..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Form.html +++ /dev/null @@ -1,44 +0,0 @@ -<f:form - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - enctype="{model.additionalArguments.enctype}" - method="{model.additionalArguments.method}" - name="{model.additionalArguments.name}" - onreset="{model.additionalArguments.onreset}" - onsubmit="{model.additionalArguments.onsubmit}" - - absolute="{model.additionalArguments.absolute}" - action="{model.additionalArguments.action}" - actionUri="{model.additionalArguments.actionUri}" - addQueryString="{model.additionalArguments.addQueryString}" - additionalAttributes="{model.htmlAttributes}" - additionalParams="{model.additionalArguments.additionalParams}" - arguments="{model.additionalArguments.arguments}" - argumentsToBeExcludedFromQueryString="{model.additionalArguments.argumentsToBeExcludedFromQueryString}" - controller="{model.additionalArguments.controller}" - extensionName="{model.additionalArguments.extensionName}" - - format="{f:if(condition:'{model.additionalArguments.format}',then:'{model.additionalArguments.format}')}" - hiddenFieldClassName="{model.additionalArguments.hiddenFieldClassName}" - - object="{model}" - - pageType="{model.additionalArguments.pageType}" - pageUid="{model.additionalArguments.pageUid}" - pluginName="{model.additionalArguments.pluginName}" - section="{model.additionalArguments.section}"> - - <ol> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Show/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </ol> -</f:form> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Radiogroup.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Radiogroup.html deleted file mode 100644 index 562311e5c2e9..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/ContainerElements/Radiogroup.html +++ /dev/null @@ -1,23 +0,0 @@ -<f:if condition="{model.showElement}"> - <f:then> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <fieldset - <f:for each="{model.htmlAttributes}" as="htmAttributeValue" key="htmAttributeKey"> - {htmAttributeKey}="{htmAttributeValue}" - </f:for> - > - {f:render(partial: '{themeName}/Show/AdditionalElements/Legend', arguments: {model: model, themeName: themeName})} - <ol> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Show/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </ol> - </fieldset> - </li> - </f:then> - <f:else> - <f:for each="{model.childElements}" as="element"> - <f:render partial="{themeName}/Show/{element.partialPath}" arguments="{model: element, themeName: themeName}" /> - </f:for> - </f:else> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Button.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Button.html deleted file mode 100644 index c70ea7104167..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Button.html +++ /dev/null @@ -1,25 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.button - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - autofocus="{model.additionalArguments.autofocus}" - type="{model.additionalArguments.type}" - - additionalAttributes="{model.htmlAttributes}" - >{model.additionalArguments.value}</f:form.button> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/ButtonTag.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/ButtonTag.html deleted file mode 100644 index 7d3735a748c7..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/ButtonTag.html +++ /dev/null @@ -1,23 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.button - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - autofocus="{model.additionalArguments.autofocus}" - type="{model.additionalArguments.type}" - >{model.attributes.value.value}</f:form.button> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Checkbox.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Checkbox.html deleted file mode 100644 index 2782e80dff48..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Checkbox.html +++ /dev/null @@ -1,50 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:if condition="{model.additionalArguments.multiple}"> - <f:then> - <f:form.checkbox - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}][{model.name}]" - value="<f:if condition='{model.additionalArguments.value}'><f:then>{model.additionalArguments.value}</f:then><f:else>{model.additionalArguments.name}-{model.elementCounter}</f:else></f:if>" - - checked="{model.additionalArguments.checked}" - - errorClass="{model.additionalAttributes.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </f:then> - <f:else> - <f:form.checkbox - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="<f:if condition='{model.additionalArguments.value}'><f:then>{model.additionalArguments.value}</f:then><f:else>{model.additionalArguments.name}-{model.elementCounter}</f:else></f:if>" - - checked="{model.additionalArguments.checked}" - - errorClass="{model.additionalAttributes.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </f:else> - </f:if> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/ContentElement.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/ContentElement.html deleted file mode 100644 index 16f128849bec..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/ContentElement.html +++ /dev/null @@ -1,5 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <f:format.raw>{model.additionalArguments.content}</f:format.raw> - </li> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Header.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Header.html deleted file mode 100644 index 477d4985334b..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Header.html +++ /dev/null @@ -1,5 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <f:format.raw><{model.additionalArguments.headingSize}>{model.additionalArguments.content}</{model.additionalArguments.headingSize}></f:format.raw> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Hidden.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Hidden.html deleted file mode 100644 index 6f3c765a6eb9..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Hidden.html +++ /dev/null @@ -1,18 +0,0 @@ -<li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <f:form.hidden - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - additionalAttributes="{model.htmlAttributes}" - /> -</li> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Imagebutton.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Imagebutton.html deleted file mode 100644 index c60c4646aff2..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Imagebutton.html +++ /dev/null @@ -1,31 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.textfield - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - autofocus="{model.additionalArguments.autofocus}" - maxlength="{model.additionalArguments.maxlength}" - size="{model.additionalArguments.size}" - placeholder="{model.additionalArguments.placeholder}" - pattern="{model.additionalArguments.pattern}" - required="{model.additionalArguments.required}" - type="{model.additionalArguments.type}" - - errorClass="{model.additionalArguments.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </li> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Input.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Input.html deleted file mode 100644 index c60c4646aff2..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Input.html +++ /dev/null @@ -1,31 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.textfield - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - autofocus="{model.additionalArguments.autofocus}" - maxlength="{model.additionalArguments.maxlength}" - size="{model.additionalArguments.size}" - placeholder="{model.additionalArguments.placeholder}" - pattern="{model.additionalArguments.pattern}" - required="{model.additionalArguments.required}" - type="{model.additionalArguments.type}" - - errorClass="{model.additionalArguments.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </li> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/InputTypeButton.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/InputTypeButton.html deleted file mode 100644 index ec954789f047..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/InputTypeButton.html +++ /dev/null @@ -1,31 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.textfield - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - autofocus="{model.additionalArguments.autofocus}" - maxlength="{model.additionalArguments.maxlength}" - size="{model.additionalArguments.size}" - placeholder="{model.additionalArguments.placeholder}" - pattern="{model.additionalArguments.pattern}" - required="{model.additionalArguments.required}" - type="{model.additionalArguments.type}" - - errorClass="{model.additionalArguments.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Password.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Password.html deleted file mode 100644 index f384b7acae12..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Password.html +++ /dev/null @@ -1,26 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.password - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - maxlength="{model.additionalArguments.maxlength}" - size="{model.additionalArguments.size}" - - errorClass="{model.additionalArguments.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Radio.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Radio.html deleted file mode 100644 index 1b1e5167ac9b..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Radio.html +++ /dev/null @@ -1,25 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.radio - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="<f:if condition='{model.additionalArguments.value}'><f:then>{model.additionalArguments.value}</f:then><f:else>{model.additionalArguments.name}-{model.elementCounter}</f:else></f:if>" - - checked="{model.additionalArguments.checked}" - - errorClass="{model.additionalArguments.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Reset.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Reset.html deleted file mode 100644 index ec954789f047..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Reset.html +++ /dev/null @@ -1,31 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.textfield - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - autofocus="{model.additionalArguments.autofocus}" - maxlength="{model.additionalArguments.maxlength}" - size="{model.additionalArguments.size}" - placeholder="{model.additionalArguments.placeholder}" - pattern="{model.additionalArguments.pattern}" - required="{model.additionalArguments.required}" - type="{model.additionalArguments.type}" - - errorClass="{model.additionalArguments.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Select.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Select.html deleted file mode 100644 index 7270eb7eb0f2..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Select.html +++ /dev/null @@ -1,36 +0,0 @@ -{namespace form=TYPO3\CMS\Form\ViewHelpers} - -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <form:select - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{form:aggregateSelectOptions(model:model, returnSelectedValues: 1)}" - - multiple="{model.additionalArguments.multiple}" - size="{model.additionalArguments.size}" - - optionLabelField="label" - optionValueField="value" - options="{form:aggregateSelectOptions(model:model)}" - prependOptionLabel="{model.additionalArguments.prependOptionLabel}" - prependOptionValue="{model.additionalArguments.prependOptionValue}" - selectAllByDefault="{model.additionalArguments.selectAllByDefault}" - sortByOptionLabel="{model.additionalArguments.sortByOptionLabel}" - - errorClass="{model.additionalArguments.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Submit.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Submit.html deleted file mode 100644 index cf6c9d20d21c..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Submit.html +++ /dev/null @@ -1,20 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.submit - additionalAttributes="{model.htmlAttributes}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - /> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textarea.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textarea.html deleted file mode 100644 index 61ff619ca6a1..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textarea.html +++ /dev/null @@ -1,28 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.textarea - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.text}" - - autofocus="{model.additionalArguments.autofocus}" - rows="{model.additionalArguments.rows}" - cols="{model.additionalArguments.cols}" - placeholder="{model.additionalArguments.placeholder}" - - errorClass="{model.additionalArguments.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textblock.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textblock.html deleted file mode 100644 index 625809e71b66..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textblock.html +++ /dev/null @@ -1,5 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - <f:format.raw>{model.additionalArguments.text}</f:format.raw> - </li> -</f:if> \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textfield.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textfield.html deleted file mode 100644 index 14c1d62aefb9..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Textfield.html +++ /dev/null @@ -1,31 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.textfield - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - value="{model.additionalArguments.value}" - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - - autofocus="{model.additionalArguments.autofocus}" - maxlength="{model.additionalArguments.maxlength}" - size="{model.additionalArguments.size}" - placeholder="{model.additionalArguments.placeholder}" - pattern="{model.additionalArguments.pattern}" - required="{model.additionalArguments.required}" - type="{model.additionalArguments.type}" - - errorClass="{model.additionalArguments.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Upload.html b/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Upload.html deleted file mode 100644 index 9f8583097382..000000000000 --- a/typo3/sysext/form/Resources/Private/Partials/Default/Show/FlatElements/Upload.html +++ /dev/null @@ -1,25 +0,0 @@ -<f:if condition="{model.showElement}"> - <li class="csc-form-{model.elementCounter} csc-form-element csc-form-element-{model.elementTypeLowerCase}"> - {f:render(partial: '{themeName}/Show/AdditionalElements/Label', arguments: {model: model, themeName: themeName})} - - <f:form.upload - class="{model.additionalArguments.class}" - dir="{model.additionalArguments.dir}" - id="{model.additionalArguments.id}" - lang="{model.additionalArguments.lang}" - style="{model.additionalArguments.style}" - title="{model.additionalArguments.title}" - accesskey="{model.additionalArguments.accesskey}" - tabindex="{model.additionalArguments.tabindex}" - onclick="{model.additionalArguments.onclick}" - - name="{model.additionalArguments.prefix}[{model.additionalArguments.name}]" - value="{model.additionalArguments.value}" - - multiple="{model.additionalArguments.multiple}" - - errorClass="{model.additionalArguments.errorClass}" - additionalAttributes="{model.htmlAttributes}" - /> - </li> -</f:if> diff --git a/typo3/sysext/form/Resources/Private/Templates/Frontend/AfterProcess.html b/typo3/sysext/form/Resources/Private/Templates/Frontend/AfterProcess.html deleted file mode 100644 index 0592e2b79c6e..000000000000 --- a/typo3/sysext/form/Resources/Private/Templates/Frontend/AfterProcess.html +++ /dev/null @@ -1,5 +0,0 @@ -<f:layout name="Default" /> - -<f:section name="main"> - <f:format.raw>{postProcessorContent}</f:format.raw> -</f:section> diff --git a/typo3/sysext/form/Resources/Private/Templates/Frontend/Confirmation.html b/typo3/sysext/form/Resources/Private/Templates/Frontend/Confirmation.html deleted file mode 100644 index 0fbefd5917f8..000000000000 --- a/typo3/sysext/form/Resources/Private/Templates/Frontend/Confirmation.html +++ /dev/null @@ -1,6 +0,0 @@ -<f:layout name="Default" /> - -<f:section name="main"> - <f:format.raw>{message}</f:format.raw> - <f:render partial="{model.themeName}/Confirmation/ContainerElements/Form" arguments="{model: model, themeName: model.themeName}" /> -</f:section> diff --git a/typo3/sysext/form/Resources/Private/Templates/Frontend/Show.html b/typo3/sysext/form/Resources/Private/Templates/Frontend/Show.html deleted file mode 100644 index 31ed85e898be..000000000000 --- a/typo3/sysext/form/Resources/Private/Templates/Frontend/Show.html +++ /dev/null @@ -1,5 +0,0 @@ -<f:layout name="Default" /> - -<f:section name="main"> - <f:render partial="{model.themeName}/Show/ContainerElements/Form" arguments="{model: model, themeName: model.themeName}" /> -</f:section> diff --git a/typo3/sysext/form/Resources/Private/Templates/PostProcessor/Mail/Default/Html.html b/typo3/sysext/form/Resources/Private/Templates/PostProcessor/Mail/Default/Html.html deleted file mode 100644 index e50cfa8afb0f..000000000000 --- a/typo3/sysext/form/Resources/Private/Templates/PostProcessor/Mail/Default/Html.html +++ /dev/null @@ -1,5 +0,0 @@ -<f:layout name="Default" /> - -<f:section name="main"> - <f:render partial="ContainerElements/Form" arguments="{model: model}" /> -</f:section> diff --git a/typo3/sysext/form/Resources/Private/Templates/PostProcessor/Mail/Default/Plain.html b/typo3/sysext/form/Resources/Private/Templates/PostProcessor/Mail/Default/Plain.html deleted file mode 100644 index 66119ca554d0..000000000000 --- a/typo3/sysext/form/Resources/Private/Templates/PostProcessor/Mail/Default/Plain.html +++ /dev/null @@ -1,5 +0,0 @@ -<f:layout name="Default" /> - -<f:section name="main"> - <f:render partial="ContainerElements/Form" arguments="{model: model, themeName: model.themeName}" /> -</f:section> diff --git a/typo3/sysext/form/Resources/Private/Templates/Wizard.html b/typo3/sysext/form/Resources/Private/Templates/Wizard.html deleted file mode 100644 index f498835def3a..000000000000 --- a/typo3/sysext/form/Resources/Private/Templates/Wizard.html +++ /dev/null @@ -1,71 +0,0 @@ -<!-- ###FULLDOC### begin --> -<div class="typo3-fullDoc"> - <div id="typo3-docheader"> - <div class="typo3-docheader-functions"> - <div class="left">###CSH###</div> - <div class="right"></div> - </div> - <div class="typo3-docheader-buttons"> - <div class="left">###BUTTONLIST_LEFT###</div> - <div class="right">###BUTTONLIST_RIGHT###</div> - </div> - </div> - - <div id="typo3-docbody"> - <div id="typo3-inner-docbody"> - ###CONTENT### - </div> - </div> -</div> -<!-- ###FULLDOC### end --> - -<!-- Grouping the icons on top --> - -<!-- ###BUTTON_GROUP_WRAP### --> - <div class="buttongroup">###BUTTONS###</div> -<!-- ###BUTTON_GROUP_WRAP### --> - -<!-- ###BUTTON_GROUPS_LEFT### --> -<!-- ###BUTTON_GROUP1### -->###CLOSE###<!-- ###BUTTON_GROUP1### --> -<!-- ###BUTTON_GROUP2### -->###SAVE######SAVE_CLOSE###<!-- ###BUTTON_GROUP2### --> -<!-- ###BUTTON_GROUPS_LEFT### --> - -<!-- ###BUTTON_GROUPS_RIGHT### --> -<!-- ###BUTTON_GROUP1### -->###RELOAD###<!-- ###BUTTON_GROUP1### --> -<!-- ###BUTTON_GROUPS_RIGHT### --> -<!-- ###FULLDOC### begin --> -<div class="typo3-fullDoc"> - <!-- Page header with buttons, path details and csh --> - <div id="typo3-docheader"> - <div class="typo3-docheader-functions"> - <div class="left">###CSH###</div> - <div class="right"></div> - </div> - <div class="typo3-docheader-buttons"> - <div class="left">###BUTTONLIST_LEFT###</div> - <div class="right">###BUTTONLIST_RIGHT###</div> - </div> - </div> - <!-- Content of module, for instance listing, info or editing --> - <div id="typo3-docbody"> - <div id="typo3-inner-docbody"> - ###CONTENT### - </div> - </div> -</div> -<!-- ###FULLDOC### end --> - -<!-- Grouping the icons on top --> - -<!-- ###BUTTON_GROUP_WRAP### --> - <div class="buttongroup">###BUTTONS###</div> -<!-- ###BUTTON_GROUP_WRAP### --> - -<!-- ###BUTTON_GROUPS_LEFT### --> -<!-- ###BUTTON_GROUP1### -->###CLOSE###<!-- ###BUTTON_GROUP1### --> -<!-- ###BUTTON_GROUP2### -->###SAVE######SAVE_CLOSE###<!-- ###BUTTON_GROUP2### --> -<!-- ###BUTTON_GROUPS_LEFT### --> - -<!-- ###BUTTON_GROUPS_RIGHT### --> -<!-- ###BUTTON_GROUP1### -->###RELOAD###<!-- ###BUTTON_GROUP1### --> -<!-- ###BUTTON_GROUPS_RIGHT### --> diff --git a/typo3/sysext/form/Resources/Public/Css/form.css b/typo3/sysext/form/Resources/Public/Css/form.css index b0f77e2c8462..07ec5253b563 100644 --- a/typo3/sysext/form/Resources/Public/Css/form.css +++ b/typo3/sysext/form/Resources/Public/Css/form.css @@ -11,576 +11,823 @@ * * The TYPO3 project - inspiring people to share! */ -.std-em-selector { - font-size: 90%; - font-style: normal; +.t3-form-x-component { + position: absolute; + top: 0; + height: 100%; + line-height: normal; + background: #f5f5f5; } -.std-strong-selector { - display: block; - font-size: 90%; - font-weight: normal; - color: #C00; -} -.std-li-selector { - float: none; - width: auto; - margin-right: 0; - margin-left: 1em; +.t3-form-x-component a { + text-decoration: none; +} +.t3-form-x-component ol, +.t3-form-x-component ul { + list-style: none; + padding: 0; } -#fake-form { - /* style for each form element */ - /* labels as block, labels displayed above or below the input fields */ +.t3-form-x-component .ui-sortable-placeholder { + outline-offset: -1px !important; } -#fake-form ol { - padding-left: 0 !important; - list-style: none !important; +.t3-form-x-component-inner-wrapper { + padding: 1.5em; } -#fake-form li { +#t3-form-navigation-component { overflow: hidden; - padding: 0.5em; - margin-bottom: 0.5em; + left: 0; } -#fake-form li#element-placeholder { - padding: 0; - margin: 0; +#t3-form-structure-panel { + overflow: auto; + padding-top: 65px; + height: 100%; } -#fake-form li input + label, -#fake-form li textarea + label, -#fake-form li select + label { - float: none; - width: auto; - margin-right: 0; - margin-left: 1em; +#t3-form-structure-panel .icon { + z-index: 1; } -#fake-form li textarea + label { - vertical-align: top; +#t3-form-structure-panel #t3-form-navigation-component-tree-root-container, +#t3-form-structure-panel .tree li > div { + border: 1px solid transparent; + cursor: pointer; } -#fake-form textarea, -#fake-form input[type=text] { - border: 1px solid #c0c0c0; +#t3-form-structure-panel .tree .svg-wrapper svg { + overflow: visible; + position: relative; + top: -0.8em; + left: 0.6em; } -#fake-form input[type=text] { - width: 300px; - padding: 3px; +#t3-form-structure-panel .tree .svg-wrapper path { + fill: none; + shape-rendering: crispEdges; + stroke: #dddddd; + stroke-width: 1; } -#fake-form label { - display: block; - margin-right: 1em; - vertical-align: baseline; +#t3-form-structure-panel .tree li { + white-space: nowrap; } -#fake-form label em { - font-size: 90%; - font-style: normal; +#t3-form-structure-panel .tree li .icon-actions-pagetree-collapse { + margin-right: 0.3em; } -#fake-form label strong { - display: block; - font-size: 90%; - font-weight: normal; - color: #C00; +#t3-form-structure-panel .tree li .icon-actions-pagetree-collapse img { + transform: rotate(90deg); + transition: transform 0.2s; } -#fake-form .x-checkbox label, -#fake-form .x-radio label { - display: inline-block; +#t3-form-structure-panel .tree li.mjs-nestedSortable-collapsed > ol { + display: none; } -#fake-form legend { - border-bottom: 0; +#t3-form-structure-panel .tree li.mjs-nestedSortable-collapsed .icon-actions-pagetree-collapse img { + transform: rotate(0deg); + transition: transform 0.2s; +} +#t3-form-structure-panel .tree li small { + padding-left: 0.5em; + font-size: 80%; +} +#t3-form-structure-panel .tree .t3-form-icon { + margin-right: 0.5em; + margin-left: 0.5em; +} +#t3-form-structure-panel .tree .t3-form-element-has-children > div .t3-form-icon { + margin-left: 0.1em; +} +#t3-form-structure-panel .tree .sortable-hover { + outline: 1px solid #aaaaaa; +} +#t3-form-structure-panel .tree li > div:hover, +#t3-form-structure-panel .t3-form-form-element-selected, +#t3-form-structure-panel #t3-form-navigation-component-tree-root-container:hover, +#t3-form-structure-panel .t3-form-root-element-selected { + background-color: #f2f2f2; + border-color: #dcdcdc; + border-radius: 2px; + margin-left: -2em; + padding-left: 2em; + margin-right: -1.3em; +} +#t3-form-structure-panel .tree li > .t3-form-form-element-selected, +#t3-form-structure-panel .tree li > .t3-form-form-element-selected:hover, +#t3-form-structure-panel #t3-form-navigation-component-tree-root-container.t3-form-root-element-selected, +#t3-form-structure-panel #t3-form-navigation-component-tree-root-container.t3-form-root-element-selected:hover { + background-color: #fff; + border-color: #dcdcdc; } -#fake-form legend em { - font-size: 90%; - font-style: normal; +#t3-form-structure-panel .t3-form-x-component-inner-wrapper { + padding-top: 2.5em; } -#fake-form legend strong { - display: block; - font-size: 90%; - font-weight: normal; - color: #C00; +.form-group.t3-form-collection-element-remove-button, +.t3-form-inspector-finishers-editor-removeButton, +.form-group.t3-form-inspector-validators-editor-removeButton { + margin: 0 !important; + font-size: 0; } -#fake-form fieldset { - position: relative; +#t3-form-inspector-panels-container { + overflow: hidden; + right: 0; + padding-top: 65px; +} +#t3-form-inspector-panels { + overflow: auto; + height: 100%; +} +#t3-form-inspector { + padding: 1em 0.5em; +} +#t3-form-inspector h2, +#t3-form-inspector h3, +#t3-form-inspector h4 { margin: 0; - padding: 0; + padding: 0.1em 0.2em 0.2em 0.5em; + border-top: 1px solid #c3c3c3; + clear: both; + font: inherit; + font-weight: bold; } -#fake-form fieldset.submit { - border-style: none; +#t3-form-inspector h2 { + padding-bottom: 1em; + border: none; + border-bottom: 1px solid #c3c3c3; } -#fake-form fieldset.fieldset-horizontal { - border-width: 0; +#t3-form-inspector > h2:first-child { + border-top: none; } -#fake-form fieldset.fieldset-horizontal.label-below label { - display: block; - margin-left: 0; - margin-top: 0.2em; - font-size: 90%; - text-align: left; - color: #999999; +#t3-form-inspector h3 { + color: #000; + padding-top: 0.3em; + border: none; } -#fake-form fieldset.fieldset-horizontal label em { - display: inline; +#t3-form-inspector h4 { + padding: 0.8em 3em 0.8em 2.5em; + font-weight: 500; + background-color: #ddd; } -#fake-form fieldset.fieldset-horizontal ol { - padding: 0; +#t3-form-inspector h4 span[data-template-property="label"] { + vertical-align: top; } -#fake-form fieldset.fieldset-horizontal li { - float: left; - margin-right: 1em; - padding: 0; +#t3-form-inspector .t3-form-remove-element-button { + position: absolute; + top: 90px; + right: 2.5em; } -#fake-form fieldset.fieldset-subgroup { - margin-bottom: -2em; - border-style: none; +#t3-form-inspector .t3-form-control-group, +#t3-form-inspector .t3-form-add-collection-element { + margin: 1.5em 0.5em; + clear: both; } -#fake-form fieldset.fieldset-subgroup ol { - position: relative; - top: -1.4em; - padding: 0; +#t3-form-inspector .t3-form-inspector-editor-requiredValidator label { + cursor: pointer; } -#fake-form fieldset.fieldset-subgroup li { - padding: 0; +.t3-form-add-collection-element { + padding-bottom: 1em; } -#fake-form fieldset.fieldset-subgroup legend { - margin-left: 0; - padding: 0; +.t3-form-collection-container { + margin-top: -1em; + padding: 0.6em; } -#fake-form fieldset.fieldset-subgroup input + label { - float: none; - width: auto; - display: inline; - margin: 0 0 0 1em; +.t3-form-collection-container .ui-sortable-handle { + cursor: auto; } -#fake-form legend { - font-size: 12px; - font-weight: bold; - color: #000000; +.t3-form-collection-container h4 { + cursor: move; } -#fake-form legend em { +.t3-form-collection-container .icon-actions-view-table-expand { position: absolute; + left: 0.5em; +} +.t3-form-collection-container a.collapsed .icon-actions-view-table-expand svg { + transform: rotate(0deg); + transition: transform 0.2s; +} +.t3-form-collection-container a:not(.collapsed) .icon-actions-view-table-expand svg { + transform: rotate(90deg); + transition: transform 0.2s; +} +.t3-form-collection-element { + position: relative; + margin-bottom: 0.5em; + border: 1px solid #c3c3c3; + border-top: none; + background: #f5f5f5; } -#fake-form legend strong { +.t3-form-collection-element .t3-form-collection-element-remove-button { position: absolute; - top: 1.4em; + right: 0.5em; + top: 0.6em; } -#fake-form .labels-block label { - display: block; - float: none; - margin: 0 0 0.5em; - width: auto; +.property-grid .form-control { + min-width: initial; + min-width: auto; + font-size: 0.9em; } -#fake-form .labels-block input + label, -#fake-form .labels-block textarea + label { - margin: 0.5em 0 0; +.property-grid .table th { + font-size: 0.9em; } -/* labels alignment right */ -#fake-form .labels-alignment-right label, -#fake-form .labels-alignment-right .fieldset-subgroup legend, -#fake-form .labels-alignment-right.fieldset-subgroup legend { - text-align: right; +.property-grid .table > tbody > tr { + cursor: pointer; + background-color: #fafafa; } -#fake-form .labels-block fieldset.fieldset-subgroup, -#fake-form fieldset.labels-block.fieldset-subgroup { - margin-bottom: 0; +.property-grid .table > tbody > tr:last-child { + cursor: auto; } -#fake-form .labels-block .fieldset-subgroup legend, -#fake-form .labels-block.fieldset-subgroup legend { - width: auto; +.property-grid .table > tbody > tr > td { + padding: 0.6em 0.3em; + text-align: center; } -#fake-form .labels-block .fieldset-subgroup legend em, -#fake-form .labels-block.fieldset-subgroup legend em { - position: relative; +.property-grid .table > tbody > tr > td:first-child { + width: 35px; } -#fake-form .labels-block .fieldset-subgroup legend strong, -#fake-form .labels-block.fieldset-subgroup legend strong { - position: relative; - top: 0; +.property-grid .table > tbody > tr > td:nth-child(2), +.property-grid .table > tbody > tr > td:nth-child(3) { + width: 75px; } -#fake-form .labels-block .fieldset-subgroup ol, -#fake-form .labels-block.fieldset-subgroup ol { - top: 0; - margin: 0; - padding: 0.5em 0 0; +.property-grid .table > tbody > tr > td:nth-child(4) { + width: 65px; } -/* element HIDDEN */ -#fake-form .formwizard-element.hidden-element { - cursor: default; +.property-grid .table > tbody > tr > td:nth-child(5) { + width: 35px; } -#fake-form .formwizard-element .hidden-dummy-element { - margin: 0; - padding: 5px; - border: 1px dotted #A9A9A9; +.property-grid .table .btn { + background-color: #eee; + border-color: #bbb; } -/* styles for drag and drop content */ -.x-dd-drag-ghost .formwizard-element { - list-style: none; +.property-grid .sort-row-field { + cursor: move; } -.x-dd-drop-icon { - top: 7px; +.property-grid .ui-sortable-helper td { + border: none; } -.x-dd-drag-ghost ol { - margin: 5px 0; - padding: 0; - list-style: none; +.property-grid .ui-sortable-placeholder { + height: 45px; + border-left: 1px solid #c3c3c3 !important; + border-right: 1px solid #c3c3c3 !important; + outline-offset: -5px !important; } -.x-dd-drag-ghost .buttongroup, -.x-dd-drag-ghost label em, -.x-dd-drag-ghost label strong { - display: none; +#t3-form-stage-inner-container { + display: inline-block; + width: 90%; + text-align: left; } -.x-dd-drag-ghost label { - margin: 0 10px 0 5px; +@media (min-width: 1300px) { + #t3-form-stage-inner-container { + width: 600px; + } } -.x-dd-drag-ghost legend { - margin: 0 5px; - font-size: 14px; - font-weight: bold; - color: #000; +#t3-form-stage-container { + overflow: auto; + position: relative; + height: 100%; + text-align: left; +} +@media (min-width: 1300px) { + #t3-form-stage-container { + text-align: center; + } +} +#t3-form-stage-container ol, +#t3-form-stage-container ul { + list-style: none; +} +#t3-form-stage-container .form-section { border: none; } -.x-grid-panel .remove { - background-image: url("../Images/remove.gif"); - width: 15px; - height: 16px; +#t3-form-stage-container .panel-heading button { + outline: none; } -.x-dd-drag-proxy, -.x-dd-drop-nodrop { - background-color: #fff; - border-color: #c0c0c0; +#t3-form-stage-container .panel-heading .paginiation-label { + margin-right: 1em; } -.tab-content fieldset #formwizard { - display: inherit; +#t3-form-stage-container .t3-form-new-element-container { + height: 62px; + border: 1px dashed #ddd; + text-align: center; + padding-top: 31px; } -.tab-content fieldset.form-section { - float: left; - min-width: 380px; - width: 100%; - padding-bottom: 15px; +#t3-form-stage-container .t3-form-new-element-container .btn { + transform: translateY(-50%); +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract ol, +#t3-form-stage-container.t3-form-stage-viewmode-abstract ul { + padding-left: 40px; + padding-right: 1em; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-page-title { + margin: 0 0 0.5em; } -.tab-content fieldset.form-section:last-child { +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage-inner-container { + overflow: hidden; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-composit:not(.t3-form-element-toplevel) { margin-bottom: 1em; + padding-bottom: 1px; + outline: 1px solid #dddddd; + outline-offset: -1px; } -.tab-content fieldset.form-section ol#formwizard-right { - width: 100vw !important; - overflow: visible !important; - position: relative; - float: none; - left: 5px !important; - padding-top: 0; - padding-left: 0; - margin-right: 10px; - top: 30px !important; - display: table-cell; - height: auto !important; - list-style: none; - border-top-style: none; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-composit .sortable-hover { + outline-color: #777777; } -.tab-content fieldset.form-section ol#formwizard-right.hover { - left: 0; - width: auto; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-composit .t3-form-form-composit-element-selected { + outline-color: #0078e6; } -/* outer wrapper of whole wizard */ -#form-wizard-element { - z-index: 1; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-composit.sortable-hover > .ui-sortable-handle, +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-handle:hover { + border-color: #777777; } -#form-wizard-element #formwizard-left { - display: table-cell; - float: left !important; - margin-right: -1px; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-composit.sortable-hover > .ui-sortable-handle .t3-form-icon-container, +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-handle:hover .t3-form-icon-container { + background-color: #777777; } -#form-wizard-element #formwizard-left .x-tab-panel-body { - height: 100% !important; - overflow: visible !important; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-composit.sortable-hover > .ui-sortable-handle .t3-form-icon-container path, +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-handle:hover .t3-form-icon-container path { + fill: #fff; } -/* inner wrapper of whole wizard */ -#formwizard { - background-color: #F8F8F8; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable fieldset { + position: relative; + min-height: 130px; + padding-top: 5em; } -/* applied when a element is moved */ -#formwizard.hover-move { - cursor: move; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable fieldset legend { + position: absolute; + top: 1em; + display: inline-block; + width: 95%; } -/* left panel */ -#formwizard-left.x-border-panel { +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-handle { + overflow: hidden; position: relative; - left: auto; - top: auto; + height: 62px; + margin-bottom: 1em; + border: 1px solid #ddd; + background-color: #fff; } -/* tabs */ -#formwizard-left .x-tab-panel-header { - background-color: transparent; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-handle:hover .t3-form-validator-list { + right: 0; + transition: right 0.2s; } -/* tabs inner */ -#formwizard-left .x-tab-strip { - margin-bottom: 0; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-handle:hover .t3-form-element-info .element-content span, +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-handle:hover .t3-form-element-info .element-content div { + color: #5a5a5a; } -#formwizard-left .x-tab-strip-top .x-tab-left { - padding-right: 20px; -} -#formwizard-left .x-tab-strip-top .x-tab-right { - padding: 5px 10px 2px; - background-color: #EDEDED; - border-radius: 0; -} -#formwizard-left .x-tab-strip-top .x-tab-strip-active .x-tab-right, -#formwizard-left .x-tab-strip-top .x-tab-strip-active.x-tab-strip-over .x-tab-right { - background-color: transparent; - border-bottom-color: #F8F8F8; -} -#formwizard-left .x-tab-strip-top .x-tab-strip-over .x-tab-right { - background-color: #E1E1E1; -} -#formwizard-left li.validation-error .x-tab-left, -#formwizard-left div.validation-error .x-accordion-hd { - margin-right: 14px; - background-image: url("../../../../../t3skin/extjs/images/form/exclamation.gif"); - background-position: right 1px; - background-repeat: no-repeat; -} -/* content below tabs */ -#formwizard-left .x-tab-panel-body-content { - min-height: 330px; - padding: 10px; - background: transparent; - border: 1px solid #C0C0C0; - border-top-width: 0; -} -/* info messages (also for drag and drop) */ -#formwizard-left .message-information, -#fake-form .message-information, -.x-dd-drag-ghost .message-information { - margin: 10px 0 15px 16px; - padding: 12px 10px; - background-image: none; - border-radius: 0; - box-shadow: none; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-handle:hover .t3-form-validator-info .t3-form-icon { + margin-right: 75px; + transition: margin 0.2s; } -#formwizard-left .message-information p, -#fake-form .message-information p, -.x-dd-drag-ghost .message-information { - margin: 0; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-handle:has (.ui-sortable-handle:hover) { + border-color: transparent; } -/* intro info messages */ -#formwizard-left #formwizard-left-elements-intro, -#formwizard-left #formwizard-left-options-dummy, -#fake-form .message-information { - margin: 0 0 10px; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-handle span { + color: #5a5a5a; } -#formwizard-left .x-tab-panel-body, -#formwizard-left .x-accordion-hd { - background: transparent none; - border-width: 0; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-state-disabled { + cursor: auto; } -/* accordion */ -#formwizard-left .x-panel-accordion { - border-bottom: 1px solid #C7C7C7; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-state-disabled:hover { + background: none; } -#formwizard-left .x-panel-accordion:last-child { - border-bottom: medium none; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .ui-sortable-placeholder { + margin-bottom: 1em; } -/* headline of accordion */ -#formwizard-left .x-accordion-hd { - padding-left: 0; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-icon-container { + float: left; + width: 40px; + height: 100%; + padding: 1em; + cursor: move; + background-color: #ddd; } -/* toggle icon of accordion */ -#formwizard-left .x-accordion-hd .x-tool-toggle { - margin: 0; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-icon-container .t3-form-icon { + height: 100%; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-form-element-body { + height: 100%; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-info { + position: relative; float: left; - background-image: url("../Images/module-menu-down.png"); - background-position: 0 4px; + width: 55%; + height: 100%; + padding-left: 1em; } -#formwizard-left .x-panel-collapsed .x-accordion-hd .x-tool-toggle { - background-image: url("../Images/module-menu-right.png"); - background-position: 1px 3px; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-info:before, +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-info:after { + display: block; + content: ''; + position: absolute; + bottom: 0; + right: 0; + left: 0; } -#formwizard-left .x-accordion-hd .x-panel-header-text { - font-weight: bold; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-info:before { + height: 0.8em; + background: #fff; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-info:after { + bottom: 0.8em; + height: 1em; + background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, #fff 100%); } -#formwizard-left .x-accordion-hd .x-panel-body { - padding-left: 15px; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-info .element-label-container { + float: left; + position: relative; + width: 45%; + height: 100%; } -/* element inside accordion */ -#formwizard-left .x-form { - margin-left: 15px; - margin-top: 10px; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-info .element-label-container .element-label { + overflow: hidden; + position: absolute; + top: 50%; + width: 100%; + text-overflow: ellipsis; + transform: translateY(-50%); } -#formwizard-left .x-form fieldset { - padding: 0; +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-info .element-content { + padding-top: 1em; + white-space: nowrap; + font-size: 0.8em; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-info .element-content span, +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-element-info .element-content div { + color: #ddd; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-validator-info { + position: relative; + overflow: hidden; + float: right; + height: 100%; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-validator-info .t3-form-icon { + height: 100%; + z-index: 1; + margin-left: 1em; + transition: margin 0.3s; + filter: grayscale(100%); +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-validator-info .t3-form-validator-list { + position: absolute; + top: 0; + right: -100px; + width: 100px; + height: 100%; + padding: 1em 1em 1em 35px; + font-size: 0.8em; + transition: right 0.3s; + background-color: #ddd; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-validator-info .t3-form-validator-list:before, +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-validator-info .t3-form-validator-list:after { + display: block; + content: ''; + position: absolute; + bottom: 0; + right: 0; + left: 0; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-validator-info .t3-form-validator-list:before { + height: 1em; + background: #ddd; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-validator-info .t3-form-validator-list:after { + bottom: 1em; + height: 1em; + background: linear-gradient(to bottom, rgba(221, 221, 221, 0) 0%, #ddd 100%); +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract .t3-form-validator-info .validator-label { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: #5a5a5a; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected { + position: relative; + padding-top: 35px; + height: 97px; border: none; } -#formwizard-left .x-form fieldset legend { - padding: 0; - font-size: 12px; - color: #222222; +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .t3-form-form-element-body { + border: 1px solid #0078e6; } -#formwizard-left .x-panel-tbar { - margin-top: 10px; - padding-left: 15px; - padding-bottom: 0; - border-width: 0; +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .t3-form-icon-container { + background-color: #0078e6; } -#formwizard-left .x-panel-tbar .x-toolbar { - padding: 0; +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .t3-form-element-info .element-content span, +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .t3-form-element-info .element-content div { + color: #5a5a5a; } -#formwizard-left .x-table-layout { - margin-bottom: 10px; +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .t3-form-validator-list { + right: 0; + transition: right 0.2s; + background-color: #ebf3fb; } -/* generic element like textfield */ -#formwizard-left .formwizard-element { - margin-left: 12px; - background-color: transparent; - background-image: none; - border-color: transparent; +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .t3-form-validator-list:before { + background-color: #ebf3fb; } -#formwizard-left .formwizard-element.x-btn-over { - background-color: #D5D5D5; - background-image: -moz-linear-gradient(center top, #F6F6F6 10%, #D5D5D5 90%); - border-color: #C0C0C0; - border-radius: 0; +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .t3-form-validator-list:after { + background: linear-gradient(to bottom, rgba(235, 243, 251, 0) 0%, #ebf3fb 100%); } -#formwizard-left .formwizard-element .x-btn-mc { - text-align: left; +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .t3-form-validator-info .t3-form-icon { + margin-right: 75px; + filter: none; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container { + position: absolute; + top: 0; + right: 0; + width: 100%; + height: 35px; + border: 1px solid #0078e6; + background-color: #0078e6; + padding-right: 0.7em; + padding-top: 0.4em; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container:before, +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container:after { + position: absolute; + top: 0; + display: block; + width: 1px; + height: 100%; + content: ' '; + background-color: #0078e6; +} +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container:before { + left: -1px; } -/* form elements in left panel */ -.formwizard-left-elements-basic-button { - background-image: url("../Images/ui-button.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container:after { + right: -1px; } -.formwizard-left-elements-basic-checkbox { - background-image: url("../Images/ui-check-box.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container .dropdown-menu { + left: auto; + left: initial; + min-width: initial; + right: 0; + padding-left: 0; + padding-right: 0; + background-color: #005db3; } -.formwizard-left-elements-basic-fieldset { - background-image: url("../Images/ui-group-box.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container .dropdown-menu > li a:hover { + background-color: #006bcd; } -.formwizard-left-elements-basic-fileupload { - background-image: url("../Images/drive-upload.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container .caret { + color: #0078e6; } -.formwizard-left-elements-basic-hidden { - background-image: url("../Images/ui-text-field-hidden.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container .t3-form-dropdown-buttons .icon { + margin-right: 0.5em; } -.formwizard-left-elements-basic-password { - background-image: url("../Images/ui-text-field-password.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container .btn-toolbar { + float: right; } -.formwizard-left-elements-basic-radio { - background-image: url("../Images/ui-radio-button.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container .btn-toolbar .btn { + background-color: #fff; + border-color: #fff; } -.formwizard-left-elements-basic-reset { - background-image: url("../Images/broom.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container .btn-toolbar .btn:hover, +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container .btn-toolbar .btn.active { + background-color: #ebf3fb; } -.formwizard-left-elements-basic-select { - background-image: url("../Images/ui-combo-box.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .btn-toolbar-container .btn-toolbar .icon svg path { + fill: #0078e6; } -.formwizard-left-elements-basic-submit { - background-image: url("../Images/ui-button-default.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .meta-label { + display: inline-block; + top: 1em; + left: 5em; + bottom: auto; + font-size: 0.9em; + color: #fff; } -.formwizard-left-elements-basic-textarea { - background-image: url("../Images/ui-scroll-pane-text.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract #t3-form-stage .t3-form-form-element-selected .meta-label span { + color: #fff; } -.formwizard-left-elements-basic-textline { - background-image: url("../Images/ui-text-field.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract .panel.t3-form-form-stage-selected { + border-color: #0078e6; } -.formwizard-left-elements-predefined-checkboxgroup { - background-image: url("../Images/ui-check-boxes.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract .panel.t3-form-form-stage-selected > .panel-heading { + background-color: #0078e6; + border-color: #0078e6; + color: #fff; } -.formwizard-left-elements-predefined-email { - background-image: url("../Images/mail.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract .panel.t3-form-form-stage-selected > .panel-heading .btn { + background-color: #fff; + border-color: #fff; } -.formwizard-left-elements-predefined-name { - background-image: url("../Images/user-silhouette.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract .panel.t3-form-form-stage-selected > .panel-heading .btn:hover, +#t3-form-stage-container.t3-form-stage-viewmode-abstract .panel.t3-form-form-stage-selected > .panel-heading .btn.active { + background-color: #ebf3fb; } -.formwizard-left-elements-predefined-radiogroup { - background-image: url("../Images/ui-radio-buttons.png"); +#t3-form-stage-container.t3-form-stage-viewmode-abstract .panel.t3-form-form-stage-selected > .panel-heading .icon svg path { + fill: #0078e6; } -.formwizard-left-elements-content-header { - background-image: url("../Images/edit-heading.png"); +#t3-form-stage-container.t3-form-stage-viewmode-preview input[type="text"], +#t3-form-stage-container.t3-form-stage-viewmode-preview input[type="date"], +#t3-form-stage-container.t3-form-stage-viewmode-preview input[type="password"], +#t3-form-stage-container.t3-form-stage-viewmode-preview textarea, +#t3-form-stage-container.t3-form-stage-viewmode-preview select { + color: #000; + background-color: #e5e5e5; } -.formwizard-left-elements-content-textblock { - background-image: url("../Images/edit-textblock.png"); +#t3-form-stage-container.t3-form-stage-viewmode-preview ::-webkit-input-placeholder { + color: #737373; + font-style: italic; } -#formwizard-left .x-form-text { - height: 17px; +#t3-form-stage-container.t3-form-stage-viewmode-preview ::-moz-placeholder { + color: #737373; + font-style: italic; } -#formwizard-left .x-btn-text-icon .x-btn-icon-small-left .x-btn-text { - color: black; +#t3-form-stage-container.t3-form-stage-viewmode-preview :-ms-input-placeholder { + color: #737373; + font-style: italic; } -#formwizard-left .x-small-editor .x-form-text { - height: 13px !important; +#t3-form-stage-container.t3-form-stage-viewmode-preview ::placeholder { + color: #737373; + font-style: italic; } -/* icon in element field for applying entered text */ -#formwizard-left .x-form-field-wrap .x-form-submit-trigger { - background-image: url("../Images/submit-trigger.gif"); +#t3-form-stage-container.t3-form-stage-viewmode-preview input[type="date"] { + display: block; + width: 100%; + height: 32px; + padding: 0.6em; + font-size: 12px; + line-height: 1.5; + background-image: none; + border: 1px solid #c3c3c3; + border-radius: 2px; + box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075); + transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s; } -/* right panel */ -#formwizard-right { - min-height: 350px; - padding: 30px 0 10px 2px; - overflow: visible; +#t3-form-stage-container.t3-form-stage-viewmode-preview select[multiple="multiple"] { + height: auto; + min-height: 32px; } -#fake-form { - padding: 10px; - background-color: tansparent; - border: 1px solid #C0C0C0; +#t3-form-stage-container.t3-form-stage-viewmode-preview textarea { + min-height: 100px; } -#fake-form li { - overflow: visible; +#t3-form-stage-container.t3-form-stage-viewmode-preview legend.t3-form-form-element-selected { + border-color: #c3c3c3; } -/* visible area of element on right panel */ -#fake-form div.overflow-hidden { - overflow: hidden; +#t3-form-stage-container.t3-form-stage-viewmode-preview .form-navigation .btn-group span, +#t3-form-stage-container.t3-form-stage-viewmode-preview .form-navigation .btn-group button { + display: inline-block; + margin-right: 1em; } -#fake-form div.overflow-hidden input { - margin-left: 1px; +#t3-form-stage-container.t3-form-stage-viewmode-preview .preview-table-first-col { + width: 30%; } -/* wrap around all elements on right panel */ -#fake-form .formwizard-element { - border: 1px solid transparent; +#t3-form-stage-container.t3-form-stage-viewmode-preview .t3-form-element-preview { position: relative; + display: inline-block; + width: 100%; +} +#t3-form-stage-container.t3-form-stage-viewmode-preview .t3-form-new-element-container { + display: none; } -#fake-form .formwizard-element.hover, -#fake-form .formwizard-element.hidden.hover { - background-color: #F9FCFF; - border: 1px solid #C5DBE6; +#t3-form-stage-container.t3-form-stage-viewmode-preview .t3-form-element-toplevel > form > .tooltip { + top: 100px !important; } -#fake-form .formwizard-element.active, -#fake-form .formwizard-element.hidden.active { - background-color: #EAF7FF; - border: 1px solid #C5DBE6; +#t3-form-stage-container #t3-form-stage { + margin-bottom: 0; + padding-top: 0.5em; +} +#t3-form-stage-container #t3-form-stage > ol, +#t3-form-stage-container #t3-form-stage > ol > li > ol { + padding-left: 0; + padding-right: 0; } -#fake-form .formwizard-element.hidden { - min-height: 1em; - background-color: transparent; - border: 1px dotted #C5DBE6; +#t3-form-stage-container #t3-form-stage .t3-form-element-toplevel > .t3-form-form-element-selected { + height: auto; + padding-top: 0; } -/* toolbar on each element */ -#fake-form .formwizard-element div.buttongroup { +#t3-form-stage-container #t3-form-stage .t3-form-element-toplevel > .t3-form-form-element-selected .btn-toolbar-container { + display: none; +} +.meta-label { + z-index: 1; position: absolute; - right: 0; - top: -35px; + bottom: 1em; + left: 5.5em; display: none; + color: #0078e6; + line-height: 1.6; + font-size: 0.8em; +} +.ui-sortable-handle:hover > .meta-label { + display: inline-block; +} +.ui-sortable-placeholder, +.t3-form-element-composit.ui-sortable-placeholder { + background-color: #fff !important; + border: none !important; + outline: 1px dashed #c4dbab !important; + outline-offset: -2px !important; + visibility: visible !important; +} +.t3-form-icon { + margin-right: 1em; +} +.t3-form-validation-child-has-error { + color: #c83c3c; +} +#t3-form-navigation-component .t3-form-validation-errors, +#t3-form-stage-container .t3-form-validation-errors { + position: relative; + color: #c83c3c; +} +#t3-form-navigation-component .t3-form-validation-errors:before, +#t3-form-stage-container .t3-form-validation-errors:before { z-index: 1; - padding: 4px 3px 12px; - background-image: url("../Images/tooltip.png"); + position: absolute; + display: inline-block; + width: 15px; + height: 15px; + font-family: FontAwesome; + vertical-align: middle; + border-radius: 50%; + font-size: 1em; + line-height: 1.4; + text-align: center; + background: none; +} +#t3-form-navigation-component .t3-form-validation-errors:hover:before, +#t3-form-navigation-component .t3-form-validation-errors.t3-form-form-element-selected:before { + left: 2.4em; +} +#t3-form-navigation-component .t3-form-validation-errors:before { + margin-top: 0.2em; + color: #fff; + font-size: 10px; + font-weight: 800; + content: "\f12a"; + background-color: #c83c3c; } -#fake-form .formwizard-element.hover > div.buttongroup, -#fake-form .formwizard-element.active > div.buttongroup { - display: block; +.t3-form-validation-errors#t3-form-navigation-component-tree-root:before { + left: -2em !important; + margin-top: 0.1em; } -#fake-form .formwizard-element div.buttongroup button { - width: 16px; - height: 16px; - background-color: transparent; - border: 0 solid transparent; +#t3-form-stage-container .t3-form-validation-errors.ui-sortable-handle { + border-color: #c83c3c; } -#fake-form .formwizard-element div.buttongroup span { - margin: 0 3px; +#t3-form-stage-container .t3-form-validation-errors.ui-sortable-handle:before { + left: 4.5em; + margin-top: 1.9em; + content: "\f071"; } -#fake-form .formwizard-element div.buttongroup span.x-btn-over { - background-color: transparent; - background-image: none; +#t3-form-stage-container .t3-form-validation-errors.ui-sortable-handle .element-label { + padding-left: 1.5em; } -#fake-form .formwizard-element button.t3-icon-edit-delete { - background-image: url('../../../../core/Resources/Public/Icons/T3Icons/actions/actions-delete.svg'); +#t3-form-inspector-panels .t3-form-collection-element .t3-form-validation-errors { + display: inline-block; + color: #fff; + font-size: 0.8em; + font-weight: 700; + background-color: #c83c3c; + margin-top: 0.5em; + padding: 0.1em 0.5em; + border-radius: 2px; +} +#t3-form-inspector-panels .t3-form-validation-errors.t3-form-collection-element { + border-color: #c83c3c; +} +#t3-form-inspector-panels .t3-form-validation-errors.t3-form-collection-element h4 { + border-color: #c83c3c; + background-color: #c83c3c; + color: #fff; +} +#t3-form-inspector-panels .t3-form-validation-errors.t3-form-collection-element h4 path { + fill: #fff; +} +#t3-form-inspector-panels .t3-form-validation-errors.t3-form-collection-element .t3-form-collection-element-remove-button { + background: #fff; + border-color: transparent; } -#fake-form .formwizard-element button.t3-icon-document-open { - background-image: url('../../../../core/Resources/Public/Icons/T3Icons/actions/actions-open.svg'); +#t3-form-inspector-panels .t3-form-validation-errors.t3-form-collection-element .t3-form-collection-element-remove-button path { + fill: #c83c3c; } -@media only screen and (max-width: 615px) { - .tab-content fieldset.form-section ol#formwizard-right { - left: 0 !important; - } +.form-editor-loading-spinner { + width: 150px; + margin: 5em auto 0; + text-align: center; +} +.ui-sortable-handle { + cursor: pointer; +} +.module[data-module-name="web_FormFormbuilder_FormEditor"] { + overflow: hidden; +} +.module[data-module-name="web_FormFormbuilder_FormEditor"] .module-body, +.module[data-module-name="web_FormFormbuilder_FormEditor"] div[data-identifier="moduleWrapper"] { + height: 100%; +} +.module[data-module-name="web_FormFormbuilder_FormEditor"] .module-body { + padding-bottom: 0.5em; +} +.module[data-module-name="web_FormFormbuilder_FormEditor"] .module-docheader-bar-column-left button, +.module[data-module-name="web_FormFormbuilder_FormEditor"] .module-docheader-bar-column-left button:focus, +.module[data-module-name="web_FormFormbuilder_FormEditor"] .module-docheader-bar-column-left button:active { + outline: 0; + outline-color: initial; + outline-style: initial; + outline-width: 0px; +} +.module[data-module-name="web_FormFormbuilder_FormEditor"] .module-docheader-bar-column-left .btn-group { + margin-left: 25px; +} +.t3-form-element-new-page-button { + position: absolute; + left: 0.5em; } diff --git a/typo3/sysext/form/Resources/Public/Images/advanced-password.svg b/typo3/sysext/form/Resources/Public/Images/advanced-password.svg new file mode 100644 index 000000000000..14626a4e4b8a --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/advanced-password.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#676767;" d="M2,5.3h1.6V3.7H2V5.3z M4.5,5.3h1.6V3.7H4.5V5.3z M7,3.7v1.6h1.6V3.7H7z M2,12.3h1.6v-1.6H2V12.3z + M4.5,12.3h1.6v-1.6H4.5V12.3z M7,12.3h1.6v-1.6H7V12.3z"/> +<path style="fill:#9A9999;" d="M0,2v5h16V2H0z M15,6H1V3h14V6z M0,14h16V9H0V14z M1,10h14v3H1V10z"/> +<path style="fill:#FFFFFF;" d="M1,3v3h14V3H1z M3.6,5.3H2V3.7h1.6V5.3z M6.1,5.3H4.5V3.7h1.6V5.3z M8.6,5.3H7V3.7h1.6V5.3z M1,13h14 + v-3H1V13z M7,10.7h1.6v1.6H7V10.7z M4.5,10.7h1.6v1.6H4.5V10.7z M2,10.7h1.6v1.6H2V10.7z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/broom.png b/typo3/sysext/form/Resources/Public/Images/broom.png deleted file mode 100644 index 5aa25bd5380ddc618de9f4e839efb82a81a78f34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 710 zcmV;%0y+JOP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0007wNkl<ZILoDz z`%6<{7{{MClA@?YP0NB(q=YgStGr<ILdC#aSW~IIHSajH5Oh{!waifto6Wl-X=*u1 zY+_!1XjYn+lVoX7>d*ZV1>c^XN&S)(_`>JB@Vq>q_j?}BApo=ys9r0`#yq|%s4m;R zyKvQ9D_$w6F;`GSj=NS9K~$$Ab!{X`4?;=(KZ5XvT+vpaBigv8dKgY>^C^;Ms*+(i zM$*3oK}`OtWX9@THLO`;z^EW;_(l}U4wF<Gw0;6lM#s&CD=Vw8*=+bSREp(yMp&+h zms)?4(!5Ds5=in4JXqkv4Z$pkVai*IJ?&$yam!1i`kz*-6^n~sVX;^+-(P^mkup@L ziJ|)`BKn?&Q*xuJRp&L*S#j{Ky-NAf*_rlzVYKXr)L(+>_B?!hZ-Uuu#_a58m`o;2 zJ<Y;QuNHSNi0(VdEa3N2)n<snnHNQve3plix)cl;<M6sM9iK+aWy#XmO&AOYIGyv* z>-88nYhXwcJ+~8C@F-J|<_M`7si{B-N6mRm+Y2$(n-7OE0rP{`@!@$1=DUia)9KLM z+y@~<cV)bYt&IC4pz2f+RizQb&L^4Z(nq1UA{LW9d3e*FjfwgUEDV)lt|`~mY-b|% z+$;;!3PI5UL_Cz=NgS3WO!L6{jcPcmIZ<Dc9J?fjCW=&ah6QvvOwj2=?r%k20lse0 ztppLn_VJGE5oo^_24_bm>=kFB4W+59lXNaIgvcdWcOCGSMRJl?wu@-VKY>dMnvD%4 zwcn1v_iSQZNBfvy<~XBN_)|`52uUeNNmA^gbgu1Ze3@O$HfA#;M{zy2FgqC^W-sHv sw)-)=8LzeT?aWrjW9?!=8~vSs0XS=n3r&9tK>z>%07*qoM6N<$f@P~qSO5S3 diff --git a/typo3/sysext/form/Resources/Public/Images/captcha.jpg b/typo3/sysext/form/Resources/Public/Images/captcha.jpg deleted file mode 100644 index 6bf02825b6c29a47659ba88845d1c42b670f572d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1439 zcmb7;eLT|%9LIm#Y-1tYBo8BOT4s`m$wNr%gotgD$3wg-yW5Oik89!O@w&JW9WA!T zB+;ds=OVK6xIFZLl5QR*hg3`t+UdG`z54e)-|t_a&+Gg7zLigv<A9d8hnEKc0s#PM zZ2;vckP4{5V5%^v8Vat8P(vd$jP&$$)iw0d+6J2lSiCU-W3(BIx4~h}tgxF+Ev$%E zwuEg&BG#Nl+Cgx$A#Jk+sUZ+hC{$x@&2>?_=KovD767RV=m7-~kSPF0f*?qcvK=r0 z05Amf1@%9vzyK&50{#-ZXaOJy1Pp;fRp4s><RCCa1%M)9D71++T-%+JydfYe{-!BS zASC5nY3+Z#Xk%-)i%gAX=dPV=e_Q*t{)>zJ=GnjQ2%ruDtwBMMfEzHh^~bwnu*g{> zwKlJQuQ^2OIQepAKZZGAnNS9kg;(9U&eN!7Q&!m1Gql^8;>;1#gJxCoDW-m0Qm*W5 zu>3+f)=}b<9bC$35|_oMM}>1-eZ{-mS;0rW7$Ve5UGLf@FR4pY=S*J;ZR2rokMOg$ zQ~6KQ#!C5b_7aSu2ohO1KQyL&P^&0cJmsQq;~#kQau)zT`CcY@T-QPe7nvRy4#GPR z(LSvn*>UMzlA}N%Pt0N^S3(ES%+|2uqihE%$aq{P?eRC{clu`BnoT)B=^7wAZGRs# z5pb)vz%}LrWjWm8@#R7Bva^hl*`rcSpb8p4wI3GwIYE#=760kJJCc8zOvlbfaE0*- zzwZ6`Wv(k%E<OM3BF3k_nw9LSx_9Rsyt@#N6`i6@A1hpVkz0h&rk9t}GfWCfZDiKu zQbAJbg}mDKX~&fV-5sKYZqsLlFnZQ~|5)dP!W?5BuO!W44ExG`lq}rWon&Yqkgs<& zu(yf24a}B0yz5<e4g1691*f1_js<DrIEIQs=dq49yJz0ex(Gh8D+U*LDBca$qRrW; z>tlyP6pfbDl=9BbJQr~k6Qc63Jeq#=bzw)1J$Y>ya&kvt%bDN@sG*f#Whv$vwx1=S zYn9?`al})`_Vh;%+k4_!tka%S@2+`$oz@DH$uC!o3plq@ntawfgvNcgZwY)vTzMZo zwUfd0j><ieEiiM1hADyl>XXm;X*ve+b3$C|dAPJIBSmC9I(N>dm7{J9l-1WCp7P7m zs$(0#8-4KjRZ89mIVAP=EBoM+TWe&5zA=OX%9c>xNXo(k$-~vTOwH>h`iu4)X;87I zU21b%)}3fp&`yUwuRl65iy=!Y71T0tc3n*v$*|h$=q`Z^IAl|7WCluyH@-zvSh6HP zP@S_>aP1I|ONPAwJK;>2jhHHLf3H5R^~(u~C6MwVK6>Qg=a0*NuPes*U7i-91j$$= z_eeun<C(nUgYqpFOH)Nz+(CkG4OzX}aH3&sDIZsXciyMTN<@T~R9Mx9M)zMiqKNX$ z9p1B{GnR<OLL*nk*^nE3MRb3?a(?B$897DhnM6bKdHGsii<FhTyxBmbhuk|F9a#SA zsK6~mSm8cE)Ll51JO}L>&tuF#BVjKpc)lOs&YU~df2u%=rhK&Ewu)UYB``fZHq;5- z4&NY7SAnR>!n=DE>Gzf`avIvaGp#hg_rfcvlhz}N9;DeNJ5{bI!m5Va&T!KVJ?F~1 z`<F`-qu!ALyoE!|B9M!M>mwhGc(?5y+}rcQa_)C{(08$JDC)_pCFqbei#8>Ik*uc7 zy3>7!pTKc8E~=|j&6zYq$%)@+Dg8IIBhrEb_<qgDn7tkIKkIz#?HX8sr{CGl!d2<@ zt>0R|Sou`PBrd5twl2&_osTo0RuE-9vcgKI3m}fA^V_kQX<aG0ct=BkknW$T9R3?8 COkk7% diff --git a/typo3/sysext/form/Resources/Public/Images/checkbox.svg b/typo3/sysext/form/Resources/Public/Images/checkbox.svg new file mode 100644 index 000000000000..2dcb3d81cf8f --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/checkbox.svg @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#FFFFFF;" d="M15,15V1H1v14H15z M4.1,8l2.1,2.1l5.7-5.6l0.7,0.7l-5.7,5.6l-0.7,0.7l-0.7-0.7L3.4,8.7L4.1,8z"/> +<path style="fill:#9A9999;" d="M15,0H1H0v1v14v1h1h14h1v-1V1V0H15z M15,15H1V1h14V15z"/> +<polygon style="fill:#676767;" points="11.9,4.5 6.2,10.1 4.1,8 3.4,8.7 6.2,11.5 6.9,10.8 12.6,5.2 "/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/content-element.svg b/typo3/sysext/form/Resources/Public/Images/content-element.svg new file mode 100644 index 000000000000..8b61701617cd --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/content-element.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#FFFFFF;" d="M15,15V1H1v14H15z M9,2h5v2H9V2z M9,5h5v1H9V5z M9,7h5v1H9V7z M2,2h6v6H2V2z M2,9h12v1H2V9z M2,11h12 + v1H2V11z M2,13h12v1H2V13z"/> +<path style="fill:#9A9999;" d="M15,0H1H0v1v14v1h1h14h1v-1V1V0H15z M15,15H1V1h14V15z"/> +<path style="fill:#676767;" d="M8,2H2v6h6V2z M9,6h5V5H9V6z M9,8h5V7H9V8z M2,10h12V9H2V10z M2,12h12v-1H2V12z M2,14h12v-1H2V14z + M9,2v2h5V2H9z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/cursor-move.png b/typo3/sysext/form/Resources/Public/Images/cursor-move.png deleted file mode 100644 index de8bca52035bd0d4918c5ff98e68b963c936fe2e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 482 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H3?x5i&EW)6?EyX^u0Z<#|NlS|4v@LOe*J=r z0~vq+{)KSgzkh$@#tpdC)2B~?>@Q!wz(roXcya#x`G*f5LPSoVJ`I<GGd_O&2xLR7 z`?r?I7|78s3GxeOU|`~q(3r-|$2yZsfLCa`lgfgPn#MNECFgpXXfNHlN>p}_jv;%v zwB3An?;p+ofZFbRx;TbZ#N}Rio_E-Qhb1BMTF>_^x7jASv-Dp5oxlEggu<W2EF}VV z9ETcLMH~-Y<o9^dH9_CbvZx4?-_sVZo?dlYR#0_L@rOfwF*&+FHg9(?nkoC>p2U;O zy_+wex!~d(%J_z{)9%d$7aPmX%ins;{~z~0dwt!5FTejV{d05DKW)0I>b&e5>7vRf zzcY`$zO?63i8V{ZUoK_ezwHO_?=oqYEfx-!wBE4FY5Kycb3=Tk7d&wKy(4<=v1f97 zr-dF>w)WlDtB@#v?#@E_ru&<o*-aNN7dU69Z@fj|TwHv1#xwgLhN`z4#5bCOg3i;` K&t;ucLK6TtuefRe diff --git a/typo3/sysext/form/Resources/Public/Images/date-picker.svg b/typo3/sysext/form/Resources/Public/Images/date-picker.svg new file mode 100644 index 000000000000..c785eb2bc94f --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/date-picker.svg @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#FFFFFF;" d="M4.8,8h2.9V5H4.8H4.7v3H4.8z M1,5h3v3H1V5z M4.7,8.5v3h0.1h2.9v-3H4.8H4.7z M1,8.5h3v3H1V8.5z + M4.8,12H4.7v3h0.1h2.9v-3H4.8z M1,12h3v3H1V12z M12,5h3v3h-3V5z M11.2,8h0.1V5h-0.1H8.3v3H11.2z M8.3,8.5v3h2.9h0.1v-3h-0.1H8.3z + M12,8.5h3v3h-3V8.5z M12,12h3v3h-3V12z M11.2,12H8.3v3h2.9h0.1v-3H11.2z M4.8,2V1H3v1v2h1.8V2z M13,2V1h-1.8v1v2H13V2z"/> +<path style="fill:#8C8C8C;" d="M12,5h-0.7v3h-0.1H8.3V5H7.7v3H4.8H4.7V5H4v3H1v0.5h3v3H1V12h3v3h0.7v-3h0.1h2.9v3h0.6v-3h2.9h0.1v3 + H12v-3h3v-0.5h-3v-3h3V8h-3V5z M7.7,11.5H4.8H4.7v-3h0.1h2.9V11.5z M11.3,11.5L11.3,11.5h-3v-3h2.9h0.1 + C11.3,8.5,11.3,11.5,11.3,11.5z"/> +<path style="fill:#676767;" d="M15,2h-1V1V0h-1h-1.8h-1v1v1H5.7V1V0h-1H3H2v1v1H1H0v2v1v6.5V12v3v1h1h3.2h0.5h3h0.5h3h0.5H15h1v-1 + v-3v-0.5v-3V8V5V4V2H15z M11.2,2V1H13v1v2h-1.8V2z M3,2V1h1.8v1v2H3V2z M15,8v0.5v3V12v3h-3h-0.7h-0.1H8.3H7.7H4.8H4.7H4H1v-3v-0.5 + v-3V8V5h3h0.7h0.1h2.9h0.6h2.9h0.1H12h3V8z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/drive-upload.png b/typo3/sysext/form/Resources/Public/Images/drive-upload.png deleted file mode 100644 index f25934d2aa9e9836dff357459ffcb63eef2e07fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 589 zcmV-T0<!&yP)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D00004XF*Lt006O% z3;baP00001b5ch_0Itp)=>Px$6i`f5MNDaNK%eR_iq$ug-6wy_A$Gw%n&lD_5+HTJ zR8&+ji`F=k-aVV<D1gfV0000002peuQBhF<0000008LFzR8&+$LqiExp$!cUDk>^C zI5-1Onn9!9UAXBGU#cjp>=0k7M7i;)skIWa`oqJ+DZBfcnxzM|{}{Uev$L~dt=@mj z_l?>8Z@cq{)A?nw;)~b&li&V$#PwC7+J({hZMo}Ep4M-?=h@llv$Ma*$k+P%`pnGN zv$MI;(a~oAX0x-j-QD85yT-}M$m!|n`1ttn@bAFDzO%Elu&}V*-QIV1g}1l1#KgpZ ze}099g~Y_g=;-Iq&&}K0+u`Bhd3ktucX-Ii$9H#r#Kg(h*Vf(L-B1f>B>(^bDs)m# zQvg0lNJyDTh0JHn%*@OZ3B?U87F}9yZdiJAK==2b$e_sDirR|bP*C50I=49h005y$ zL_t&-(_>(O07gaz6u`#A!-mXf<~G#fW=8OtxD1SRwYivJd=_3k6H{Y-EnXI=04oOv zhq)P$U`1#Wkd+ZYu|+{qUJgY-Tvb^dML=3ZT^i026y<~hQ9-bPh>D%FyN`#1t)-F( zSU}9$)h#e2+RM?#LJTY*<>Kk@7aA7fZEqz7<njwkY6eFIh5H6XI;lwt^E2=XNwC9# bgb*J9Cjt^<dvNe!00000NkvXXu0mjfAJ-Db diff --git a/typo3/sysext/form/Resources/Public/Images/duplicate.svg b/typo3/sysext/form/Resources/Public/Images/duplicate.svg new file mode 100644 index 000000000000..c785b6d552c0 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/duplicate.svg @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path d="M14,4h-2V2V1h-1H2H1v1v9v1h1h2v2v1h1h9h1v-1V5V4H14z M4,4v1v6H2V2h9v2H5H4z M14,14H5V5h9V14z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/edit-heading.png b/typo3/sysext/form/Resources/Public/Images/edit-heading.png deleted file mode 100644 index 0817b4930a2e7d7b0fc97107cc2d0f4a984baf11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6Dm+~rLp09sooLAC;2_}o|Gmb` z9ZfC0(iZ|OSGp_?Tv?*`P=!Zm=1DcHPoLEFXJ6MX+tWOssY&v8x6e$@1GAdX%-}8R zGfFnou+ZePFPL-lRB2KA&bWZ0iIUY!bI!|#&oo-0;L!SZkzn3x!B+}SO(7E%!VYe) bFJwrWu#tUx!M=AuH!yg*`njxgN@xNAr_Mpe diff --git a/typo3/sysext/form/Resources/Public/Images/edit-textblock.png b/typo3/sysext/form/Resources/Public/Images/edit-textblock.png deleted file mode 100644 index a27655f38a72eb74a1aa6470700a8a6c0667e407..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!73?$#)eFPFv4DbnY{r~?zkckJ}El3FmN^_S4 z`2{mD9B^oOV6b3A!iRj904AWYj;D)b2uE~s!U6V4y}i5#cuK>kGZ;8WdIT_@$!IoY lX=P#MFjUz(MS-V@fniD?%esw0|JH$Q^K|udS?83{1ORLSD9``^ diff --git a/typo3/sysext/form/Resources/Public/Images/fieldset.svg b/typo3/sysext/form/Resources/Public/Images/fieldset.svg new file mode 100644 index 000000000000..915bea8c10c2 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/fieldset.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#9A9999;" d="M10,0v1h5v14H1V1h1V0H0v16h16V0H10z M2,14h12v-3.5H2V14z M3,11.5h10V13H3V11.5z M14,6.2H2v3.6h12V6.2 + z M13,8.8H3V7.2h10V8.8z M14,2H2v3.5h12V2z M13,4.5H3V3h10V4.5z"/> +<rect x="3" style="fill:#676767;" width="6" height="1"/> +<path style="fill:#FFFFFF;" d="M13,7.2H3v1.6h10V7.2z M13,11.5H3V13h10V11.5z M10,1V0H9v1H3V0H2v1H1v14h14V1H10z M14,14H2v-3.5h12 + V14z M14,9.8H2V6.2h12V9.8z M14,5.5H2V2h12V5.5z M13,3H3v1.5h10V3z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/file-upload.svg b/typo3/sysext/form/Resources/Public/Images/file-upload.svg new file mode 100644 index 000000000000..ae73a246e294 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/file-upload.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#9A9999;" d="M2,0v16h12V4l-4-4H2z M3,1h6v4h4v10H3V1z M10,1.4L12.6,4H10V1.4z"/> +<path style="fill:#676767;" d="M8,4L5,8h2v4h2V8h2L8,4z"/> +<path style="fill:#FFFFFF;" d="M10,4V1.4L12.6,4H10z M9,1H3v14h10v-5L9,5V1z M11,8H9v4H7V8H5l3-4L11,8z"/> +<path style="fill:#CBCBCB;" d="M13,5v5L9,5H13z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/finisher.svg b/typo3/sysext/form/Resources/Public/Images/finisher.svg new file mode 100644 index 000000000000..3d8c355369b3 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/finisher.svg @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#676767;" d="M12.9,3.8l-1.4-0.4c0-0.1-0.1-0.2-0.1-0.3l0.7-1.2c0-0.1,0-0.1,0-0.2L11.4,1c-0.1-0.1-0.1-0.1-0.2,0 + L9.9,1.6c-0.1,0-0.2-0.1-0.3-0.1L9.2,0.1C9.2,0,9.1,0,9,0H8C7.9,0,7.8,0,7.8,0.1L7.4,1.5c-0.1,0-0.2,0.1-0.3,0.1L5.9,0.9 + c-0.1,0-0.1,0-0.2,0L4.9,1.7c-0.1,0.1-0.1,0.1,0,0.2l0.7,1.2c0,0.1-0.1,0.2-0.1,0.3L4.1,3.8C4,3.8,4,3.9,4,4v1c0,0.1,0,0.1,0.1,0.2 + l1.4,0.4c0,0.1,0.1,0.2,0.1,0.3L4.9,7.1c0,0.1,0,0.1,0,0.2L5.6,8c0.1,0.1,0.1,0.1,0.2,0L7,7.3c0.1,0,0.2,0.1,0.3,0.1l0.4,1.4 + C7.8,9,7.9,9,8,9h1c0.1,0,0.1,0,0.2-0.1l0.4-1.4c0.1,0,0.2-0.1,0.3-0.1l1.2,0.7c0.1,0,0.1,0,0.2,0L12,7.4c0.1-0.1,0.1-0.1,0-0.2 + L11.3,6c0-0.1,0.1-0.2,0.1-0.3l1.4-0.4C13,5.2,13,5.1,13,5V4C13,3.9,13,3.8,12.9,3.8L12.9,3.8z M8.5,6.4c-1.1,0-1.9-0.9-1.9-1.9 + s0.9-1.9,1.9-1.9c1.1,0,1.9,0.9,1.9,1.9S9.6,6.4,8.5,6.4z M5.9,10.6L5.1,10c0-0.1,0-0.2,0-0.2l0.7-0.6V9.1L5.6,8.5L5.5,8.4L4.6,8.5 + c-0.1,0-0.1-0.1-0.2-0.1l0.1-0.9c0,0,0-0.1-0.1-0.1L3.8,7.1H3.7L3.1,7.9c-0.1,0-0.2,0-0.2,0L2.2,7.1H2.1L1.5,7.4L1.4,7.5l0.1,0.9 + c0,0.1-0.1,0.1-0.1,0.2l-1-0.1c0,0-0.1,0-0.1,0.1L0.1,9.2v0.1l0.8,0.6c0,0.1,0,0.2,0,0.2l-0.7,0.6v0.1l0.3,0.6l0.1,0.1l0.9-0.1 + c0.1,0.1,0.1,0.1,0.2,0.2l-0.1,0.9c0,0,0,0.1,0.1,0.1l0.6,0.3h0.1L3,12.1c0.1,0,0.2,0,0.2,0l0.6,0.7h0.1l0.6-0.3l0.1-0.1l-0.1-0.9 + c0.1-0.1,0.1-0.1,0.2-0.2l0.9,0.1c0,0,0.1,0,0.1-0.1L5.9,10.6C5.9,10.7,5.9,10.7,5.9,10.6L5.9,10.6z M2.5,11.2c-0.7-0.3-1-1-0.7-1.7 + s1-1,1.7-0.7s1,1,0.7,1.7S3.2,11.5,2.5,11.2z"/> +<path style="fill:#79A548;" d="M15.9,12.6l-4.4-3.3v2.2H7.1v2.2h4.4v2.1L15.9,12.6z"/> +<path style="fill:#FFFFFF;" d="M8.5,2.6c-1,0-1.9,0.9-1.9,1.9s0.8,1.9,1.9,1.9s1.9-0.9,1.9-1.9S9.6,2.6,8.5,2.6z M3.5,8.8 + c-0.7-0.3-1.4,0-1.7,0.7s0,1.4,0.7,1.7s1.4,0,1.7-0.7S4.2,9.1,3.5,8.8z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/image-upload.svg b/typo3/sysext/form/Resources/Public/Images/image-upload.svg new file mode 100644 index 000000000000..0b35d9c06bf8 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/image-upload.svg @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#FFFFFF;" d="M15,13V1H1v12H15z M8,4l3,4H9v4H7V8H5L8,4z"/> +<path style="fill:#9A9999;" d="M1,0H0v1v14v1h1h14h1V0H1z M15,13H1V1h14V13z"/> +<path style="fill:#676767;" d="M8,4L5,8h2v4h2V8h2L8,4z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/insert-after.svg b/typo3/sysext/form/Resources/Public/Images/insert-after.svg new file mode 100644 index 000000000000..7289fa568788 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/insert-after.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#FFFFFF;" d="M3,3h10v2H3V3z"/> +<path style="fill:#FFFFFF;" d="M15,0h-5v1h5v6H1V1h1V0H1H0v1v6v1h1h14h1V7V1V0H15z M13,2H3H2v1v2v1h1h10h1V5V3V2H13z M13,5H3V3h10V5 + z"/> +<path style="fill:#FFFFFF;" d="M3,0h6v1H3V0z M10.5,11.5h-2v-2h-1v2h-2v1h2v2h1v-2h2V11.5z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/insert-in.svg b/typo3/sysext/form/Resources/Public/Images/insert-in.svg new file mode 100644 index 000000000000..95e00bee41f4 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/insert-in.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#FFFFFF;" d="M3,3h10v2H3V3z"/> +<path style="fill:#FFFFFF;" d="M15,0h-5v1h5v14H1V1h1V0H1H0v1v14v1h1h14h1v-1V1V0H15z M13,2H3H2v1v2v1h1h10h1V5V3V2H13z M13,5H3V3 + h10V5z"/> +<path style="fill:#FFFFFF;" d="M3,0h6v1H3V0z M10.5,9.8h-2v-2h-1v2h-2v1h2v2h1v-2h2V9.8z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/mail.png b/typo3/sysext/form/Resources/Public/Images/mail.png deleted file mode 100644 index a5fdc0c2b2f204c3659adb15c00de5c2f70631bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 430 zcmV;f0a5;mP)<h;3K|Lk000e1NJLTq000mG000mO0{{R3C@l|D0002DP)t-s|NsA0 ze}4@wE&u=kQha<zZ*Nt9e@k?9LuzU{Utdpncr;pCFH}??LP859BtB(j00000007?W z>*(?E>+$il#l`ph{O|es^ZNS1($b-_vex0@?)CNT_xI!O?$X}g>F@8@=H}@3_RZVd z;pyq$?(V?R(YVRUz0S_v?d_eeuHEVB?eX#4<>lq<?BVR}^Z5A5+S=6P<Ep#6<MZ>S zwzk^o>HGct`u+XI*4D4U!1w$6|Ns92t|2D?000tnQchC<XNDSRZB1)$b7^&WerRz9 z7V+%{!2kdNlu1NER2b8R&qWTx02Bn!57gb=-CK$kx&OH+q-h{5c#E0Y4!~d7<_iW$ z-aJ-KjsT^o!DSJs3I+Jo-MahRJSH4aZAcEuFng7T0%15#duo?32;g;D?oG?KEwh(p zod?qO>V553l|Pr~l0Zk{HI$?{mZW(^G%#s4o)=@5GQbw)aa9&9aN92&2;%O8DEyp# Y0uiMcnb>Lly8r+H07*qoM6N<$g0}zOg#Z8m diff --git a/typo3/sysext/form/Resources/Public/Images/module-menu-down.png b/typo3/sysext/form/Resources/Public/Images/module-menu-down.png deleted file mode 100644 index 6829ed2920b732c50567cffe0b5b267ec15af5cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 121 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRR!2%?ApR4f$QVyOjjv*44eNP<ZJfOgE^un15 zs~;3Ktu9etE+q5dd%)C5-W3sZo|);g-0qK3U^(6<$QL@jFjAj&_s07Dnm^`LGalP% Vu$zfP&>LtLgQu&X%Q~loCIE5XC>Q_$ diff --git a/typo3/sysext/form/Resources/Public/Images/module-menu-right.png b/typo3/sysext/form/Resources/Public/Images/module-menu-right.png deleted file mode 100644 index 7b87ed4cb8c03959295736f6704716eee8dd97a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 114 zcmeAS@N?(olHy`uVBq!ia0vp^>_E)P!3HG%MVKuHQkI@Bjv*44lYjjGZ{NJCtIMRr z?Kn@sNq&bpJ>H+}Mcy+dS;kCK_#@eIxxn*KfwiaA##^1&w?_QsWH6CxlM3aCcmp(q N!PC{xWt~$(697+`C2;@% diff --git a/typo3/sysext/form/Resources/Public/Images/multi-checkbox.svg b/typo3/sysext/form/Resources/Public/Images/multi-checkbox.svg new file mode 100644 index 000000000000..b8ff71da1b12 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/multi-checkbox.svg @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#9A9999;" d="M4.3,16h7.5V8.5H4.3V16z M5.3,9.6h5.4v5.4H5.3V9.6z M4.3,0v7.5h7.5V0H4.3z M10.7,6.4H5.3V1.1h5.4V6.4 + z"/> +<path style="fill:#676767;" d="M7.3,12.9L6.4,12l-0.5,0.5l1.4,1.4l0.5-0.5l2.3-2.3l-0.5-0.5L7.3,12.9z M7.8,4.9l2.3-2.3L9.6,2.1 + L7.3,4.4L6.4,3.5L5.9,4l1.4,1.4L7.8,4.9z"/> +<path style="fill:#FFFFFF;" d="M5.3,14.9h5.4V9.6H5.3V14.9z M6.4,12l0.9,0.9l2.3-2.3l0.5,0.5l-2.3,2.3l-0.5,0.5l-1.4-1.4L6.4,12z + M5.3,1.1v5.4h5.4V1.1H5.3z M7.8,4.9L7.3,5.4L5.9,4l0.5-0.5l0.9,0.9l2.3-2.3l0.5,0.5L7.8,4.9z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/multi-select.svg b/typo3/sysext/form/Resources/Public/Images/multi-select.svg new file mode 100644 index 000000000000..2621d8de455f --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/multi-select.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#9A9999;" d="M4.3,16h7.5V8.5H4.3V16z M5.3,9.6h5.4v5.4H5.3V9.6z M4.3,0v7.5h7.5V0H4.3z M10.7,6.4H5.3V1.1h5.4V6.4 + z"/> +<path style="fill:#676767;" d="M8,13.4l-0.9-0.9L6.6,13L8,14.4L9.4,13l-0.5-0.5L8,13.4z M6.6,11.5L7.1,12L8,11.1L8.9,12l0.5-0.5 + L8,10.1L6.6,11.5z M8,4.9L7.1,4L6.6,4.5L8,5.9l1.4-1.4L8.9,4L8,4.9z M8,2.6l0.9,0.9L9.4,3L8,1.6L6.6,3l0.5,0.5L8,2.6z"/> +<g> + <path style="fill:#FFFFFF;" d="M5.3,1.1v5.4h5.4V1.1H5.3z M8,5.9L6.6,4.5L7.1,4L8,4.9L8.9,4l0.5,0.5L8,5.9z M8.9,3.5L8,2.6L7.1,3.5 + L6.6,3L8,1.6L9.4,3L8.9,3.5z"/> + <path style="fill:#FFFFFF;" d="M5.3,14.9h5.4V9.6H5.3V14.9z M8,10.1l1.4,1.4L8.9,12L8,11.1L7.1,12l-0.5-0.5L8,10.1z M7.1,12.5 + L8,13.4l0.9-0.9L9.4,13L8,14.4L6.6,13L7.1,12.5z"/> +</g> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/page.svg b/typo3/sysext/form/Resources/Public/Images/page.svg new file mode 100644 index 000000000000..26fbff422481 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/page.svg @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#999999;" d="M3,15V1h6v4h4v1.6h1V4l-4-4H2v16h3v-1H3z M10,1.4L12.6,4H10V1.4z"/> +<path style="fill:#CBCBCB;" d="M9.5,5.7l1-0.7H9L9.5,5.7z M10.5,5l2.2,1.6H13V5H10.5z"/> +<path style="fill:#FFE6BB;" d="M5.8,9.5l0.8,0.6V8.7L5.8,9.3V9.5z M9.6,6.6h1.7L10.5,6L9.6,6.6z M14.4,8.9v1.3l0.8-0.6V9.5L14.4,8.9 + z"/> +<path style="fill:#FFFFFF;" d="M9.5,5.7L9,5V1H3v14h2V8.9l1.6-1.1V6.6h1.6L9.5,5.7z M10,1.4V4h2.6L10,1.4z M7.4,7.4v3.3l3.1,2.3 + l3.1-2.2V7.4H7.4z"/> +<polygon style="fill:#666666;" points="6.6,6.6 6.6,10.1 7.4,10.7 7.4,7.4 13.6,7.4 13.6,10.8 14.4,10.2 14.4,6.6 "/> +<path style="fill:#B9B9B9;" d="M9.3,12.1h2.5l1.1-0.8H8.2L9.3,12.1z M8.2,10.5h4.7V9.7H8.2V10.5z M8.2,8.2V9h4.7V8.2H8.2z"/> +<path style="fill:#FFC857;" d="M5.8,10.4v4.8l2.7-2.8L5.8,10.4z M12.5,12.5l2.7,2.8v-4.8L12.5,12.5z M10.5,13.8l-1.3-0.9l-2.3,2.3 + h7.2l-2.3-2.3L10.5,13.8z"/> +<path style="fill:#E8A33D;" d="M10.5,6l0.8,0.6h1.4L10.5,5L8.2,6.6h1.4L10.5,6z M14.4,7.8v1.1l0.8,0.6v0.1L10.5,13L5.8,9.5V9.3 + l0.8-0.6v-1L5,8.9V16h11V8.9L14.4,7.8z M5.8,10.4l2.7,2l-2.7,2.8V10.4z M6.9,15.2l2.3-2.3l1.3,0.9l1.3-0.9l2.3,2.3H6.9z M15.2,15.3 + l-2.7-2.8l2.7-2V15.3z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/password.svg b/typo3/sysext/form/Resources/Public/Images/password.svg new file mode 100644 index 000000000000..2dc97b8d452a --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/password.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<rect x="2" y="7.2" style="fill:#676767;" width="1.6" height="1.6"/> +<rect x="4.5" y="7.2" style="fill:#676767;" width="1.6" height="1.6"/> +<rect x="7" y="7.2" style="fill:#676767;" width="1.6" height="1.6"/> +<path style="fill:#9A9999;" d="M0,5.5L0,5.5v2v3h1h14h1v-3v-2l0,0h-1 M15,9.5H1v-3h14V9.5z"/> +<path style="fill:#FFFFFF;" d="M1,6.5v3h14v-3H1z M3.6,8.8H2V7.2h1.6V8.8z M6.1,8.8H4.5V7.2h1.6V8.8z M8.6,8.8H7V7.2h1.6V8.8z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/radio-button.svg b/typo3/sysext/form/Resources/Public/Images/radio-button.svg new file mode 100644 index 000000000000..76634c4abfc1 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/radio-button.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#9A9999;" d="M8,0C3.6,0,0,3.6,0,8s3.6,8,8,8s8-3.6,8-8S12.4,0,8,0z M8,15c-3.9,0-7-3.1-7-7s3.1-7,7-7s7,3.1,7,7 + S11.9,15,8,15z"/> +<path style="fill:#FFFFFF;" d="M8,1C4.1,1,1,4.1,1,8s3.1,7,7,7s7-3.1,7-7S11.9,1,8,1z M8,12c-2.2,0-4-1.8-4-4s1.8-4,4-4s4,1.8,4,4 + S10.2,12,8,12z"/> +<path style="fill:#676767;" d="M8,4c2.2,0,4,1.8,4,4s-1.8,4-4,4s-4-1.8-4-4S5.8,4,8,4z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/remove.gif b/typo3/sysext/form/Resources/Public/Images/remove.gif deleted file mode 100644 index 04999ad062c2e9cd077e8f9b5d4b3453fcb6615a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 206 zcmZ?wbh9u|<Yy3I_`<-j#nABHx^<Jaw6;#0HcLxuouT30Y13X98ZOk*dMGRVucTy$ zp<$ht){o@mZ6zfiyuEL)TepAt^2dh`tuHA#TUWOwC+G9$&lfLVWWWIw|8qz91}L~B zmLvjcMtcSwkV25<4y;ZKRQpmY=T3K&(N0_-E~(RKdR=+V`_%W*6P%nQCQL|Rc6%~O m=FPl4^Gj|qYcPmtKj2ucAjiS}MahFhE%ji<**tq^25SIMxJnfO diff --git a/typo3/sysext/form/Resources/Public/Images/single-select.svg b/typo3/sysext/form/Resources/Public/Images/single-select.svg new file mode 100644 index 000000000000..961bfe934a36 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/single-select.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#FFFFFF;" d="M1,1v14h14V1H1z M8,12.9L4.5,9.4l0.7-0.7L8,11.5l2.8-2.8l0.7,0.7L8,12.9z M10.8,7.3L8,4.4L5.2,7.3 + L4.5,6.6l2.8-2.9L8,3l0.7,0.7l2.8,2.9L10.8,7.3z"/> +<path style="fill:#9A9999;" d="M15,0H1H0v1v14v1h1h14h1v-1V1V0H15z M15,15H1V1h14V15z"/> +<path style="fill:#676767;" d="M8,4.4l2.8,2.9l0.7-0.7L8.7,3.7L8,3L7.3,3.7L4.5,6.6l0.7,0.7L8,4.4z M8,11.5L5.2,8.7L4.5,9.4L8,12.9 + l3.5-3.5l-0.7-0.7L8,11.5z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/static-text.svg b/typo3/sysext/form/Resources/Public/Images/static-text.svg new file mode 100644 index 000000000000..13cc3f62aafa --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/static-text.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#FFFFFF;" d="M1,1v14h14V1H1z M14,14H2v-1h12V14z M14,12H2v-1h12V12z M14,10H2V9h12V10z M14,8H2V7h12V8z M14,6H2V5 + h12V6z M14,4H2V2h12V4z"/> +<path style="fill:#9A9999;" d="M15,0H1H0v1v14v1h1h14h1v-1V1V0H15z M15,15H1V1h14V15z"/> +<path style="fill:#676767;" d="M2,2v2h12V2H2z M2,6h12V5H2V6z M2,8h12V7H2V8z M2,10h12V9H2V10z M2,12h12v-1H2V12z M2,14h12v-1H2V14z + "/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/submit-trigger.gif b/typo3/sysext/form/Resources/Public/Images/submit-trigger.gif deleted file mode 100644 index fa0230f86d5a4b76689349300a6883d8619afebf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1109 zcmV-b1giT-Nk%v~VP*gr0QLX?xw*M1nlveuGbxNQlDU?Www98wlrhCZS>b9eq&gzX zGdRjgF}p%n-)Ld!ax1kw6VxP1(pys6V@=gwF{nKm#VRJeHzBz)E220Z$1a+^o|3zk zEv`JR%dk(^Un{ISlFgXH;l?R=EHSY_E2lawsXMdMw=u0hDw{Peo;b7Bw}!%vVZLmV z)S8CKj!Dm2v(vX$ykjkyH_qG7O}ANAvth>6$eYEWMYvP0+Ox*f$SsgHg1v}P*kGH- zpt#??t<JHQyqe$b;m+F6NX}W`?BJoxrA4k$EwMc@wLwR^Ra&-Wq0Od-!Hlliv!lzX zt<12(;>MxHq{8IKs?n}t$8IW?G%AfUE3Z3(yoi>wmq^Z7N3m0($D~ccT!O@kTEAwY zzM_`8nJs)UmcE*v!J?|nttxpfp~<EE{QUFt^QWh$(b3V^*x2CU;LOa-0000000000 z00000000000000000000000000000000000000000000000000A^!_lO;8|2VRT_w zO;7<p04x9i003qH7yv*A|9}93gM@{GYlw-8i;RtqkC2g)jcRH|G%O)%Atw_NCpndB z0B&wWTuUZyCMBLFOr>pWZEZS7H63joB%UNbw6blcsH&{4u%&C2mzkTLpP`krwYRyu zy}z_;Y~0=6-)sPH<K*S$Z)@l3<mv0`Yo+n>^U3b-qWJmvr1bsCzyJdRgy*lHGJOc8 z30w$lTQ@ORAY2gxE?l_+10W12@a|&9jUCBh)X4E1!BSFoK-eLLo-B_f-&LGx(Vn?> zHreGYce0>7oI4LD)W^@~PlgP88g(OdD86Y(12)x{#6gc1m5TO5;=qFoH^Go%>H0;R zFn?UVCUaW#szR}3|Fv-|gJQt}6c#R^AoES!8w~;M2FM#=!_9Gk1rs*R6LH`JgD(`m zS;|ZbyD9RfKqK$v-^Y<B^Q2kP?@n~5#{tc$)aYuP4oQ169W*uS)UAEKwijDAH`=Ui z?{=5F;P7>8(kNffym=Y`&Z94f8nvNx>B+A<r(Ru_Q=&_cc5fIy-qi2htDna=rHU6J zS$(Piqhtoy^Z<dxz+uB|!TI&AHB@`=y~U7z{ArU=6eA$ezz9FY*AEOFILHAN_b~*; z2M&Jlgn#r<Xd#A7E!g0L5vqY-8yj}`p?7M4@PY&sfItBQ4^ZI5KBcTcgaipRkpPYf z7!glcJZvEWsRmhKfFlMs3?z(?Knh8uk>*t*BaJrVsN++#B)MdhPe$2LH$hmTKn4|9 z5d=7<5g@>t6$n6pN#w{$r=56?qbHkuQdUYAGSDeQ7xQ@b#-DZGdDAyqoT0@`z!@Nd z0mzwSDW)Njvng`(gfT`K{;c+?Oulsk9Bje~S6)51rK)PHxVif3aj43=DsQx&R$QsV b$@*Kbwc++EQ^5+mD{;iKb}X(Y1p)v&*qjIh diff --git a/typo3/sysext/form/Resources/Public/Images/summary-page.svg b/typo3/sysext/form/Resources/Public/Images/summary-page.svg new file mode 100644 index 000000000000..51ca03e70e7d --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/summary-page.svg @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#79A548;" d="M8.8,6.8L7.6,8L7.1,7.5L6.8,7.8l0.8,0.8l0,0l0.3-0.3l1.2-1.2L8.8,6.8z M8.8,9.2l-1.2,1.3L7.1,10 + l-0.3,0.3l0.8,0.8l0,0l0.3-0.3l1.2-1.2L8.8,9.2z M8.8,11.8L7.6,13l-0.5-0.5l-0.3,0.3l0.8,0.8l0,0l0.3-0.3l1.2-1.2L8.8,11.8z"/> +<path style="fill:#FFFFFF;" d="M3,1v14h2V5h4V1H3z M10,4V1.4L12.6,4H10z M6,15h9V6H6V15z M9.2,7.4h5v1.2h-5V7.4z M9.2,9.9h5v1.2h-5 + V9.9z M9.2,12.4h5v1.2h-5V12.4z M7.1,7.5L7.6,8l1.2-1.2l0.3,0.3L7.9,8.3L7.6,8.6L6.8,7.8L7.1,7.5z M7.1,10l0.5,0.5l1.2-1.3l0.3,0.4 + l-1.2,1.2l-0.3,0.3l-0.8-0.8L7.1,10z M7.1,12.5L7.6,13l1.2-1.2l0.3,0.3l-1.2,1.2l-0.3,0.3l-0.8-0.8L7.1,12.5z"/> +<path style="fill:#676767;" d="M9.2,7.4h5v1.2h-5V7.4z M9.2,9.9h5v1.2h-5V9.9z M9.2,12.4h5v1.2h-5V12.4z M15,6v9H6V6H15 M16,5H5v11 + h11V5z"/> +<path style="fill:#999999;" d="M3,15V1h6v4h4h1V4l-4-4H2v16h3v-1H3z M10,1.4L12.6,4H10V1.4z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/text.svg b/typo3/sysext/form/Resources/Public/Images/text.svg new file mode 100644 index 000000000000..db92ec02974d --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/text.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#FFFFFF;" d="M1,9.5h3v-3H1V9.5z M5,6.5v3h10v-3H5z"/> +<path style="fill:#9A9999;" d="M0,10.5h4v-1H1v-3h3v-1H0V10.5z M5,5.5v1h10v3H5v1h11v-5H5z"/> +<polygon style="fill:#676767;" points="6.5,4.5 6.5,3.5 5,3.5 4,3.5 2.5,3.5 2.5,4.5 4,4.5 4,11.5 2.5,11.5 2.5,12.5 4,12.5 5,12.5 + 6.5,12.5 6.5,11.5 5,11.5 5,4.5 "/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/textarea.svg b/typo3/sysext/form/Resources/Public/Images/textarea.svg new file mode 100644 index 000000000000..4efeb102c7f1 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/textarea.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#FFFFFF;" d="M1,1v14h14V1H1z M10,13H2v-1h8V13z M14,11H2v-1h12V11z M14,9H2V8h12V9z M14,7H2V6h12V7z M14,5H2V4h12 + V5z M14,3H2V2h12V3z"/> +<path style="fill:#9A9999;" d="M15,0H1H0v1v14v1h1h14h1v-1V1V0H15z M15,15H1V1h14V15z"/> +<path style="fill:#676767;" d="M2,2v1h12V2H2z M2,5h12V4H2V5z M2,7h12V6H2V7z M2,9h12V8H2V9z M2,11h12v-1H2V11z M2,13h8v-1H2V13z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/Images/tooltip.png b/typo3/sysext/form/Resources/Public/Images/tooltip.png deleted file mode 100644 index 2dcfe228d861ba9ff9b5df20a85f9c59d0ce1c1e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 465 zcmeAS@N?(olHy`uVBq!ia0vp^ra-L1!3-qjHZ|1%smuVM5LX}#1P2Zr0MpbbfF@tM zbm`5TH;dGn7Xgh|C<*clX5eSwkYZ+MVq;|#;o;)sV-c6+6%Z1Z=2nuIk&_TrnVX}v z8>sfXr;B4qMcmsdC-V*)@UX5tp`z;bX<zE{-~Z)rni({Go8WeC>&)K|=H6>CsB%yF ztWYEu*^nK6?9`K>qnEDoyqLu48~x;>*+G@|2i}Ws8!TlPZ?4O6bS`lS-Fz`ii^=G% z+iFvmYa-HX58e2mws-CJuebKc{r;4F_s7i@siM=C_wM@sp<>6nTZ^tA{^-en{oA+0 zskdIAz4W(4@|XSX>i0FTj=eP7_QIs>rRQ_UmYIUtT@Sz5E?X|J@)*0pW{x|8Tb}(f z-S*;U#FV?r56s^@UwDCEx9#R<z3W0t@~*fZ?+e(Zn~=Xd?)SNM!Ije%EH_dAq_KhV e;4SO;bdULm^cMA*gm0Aw#fGP=pUXO@geCx)w5K@$ diff --git a/typo3/sysext/form/Resources/Public/Images/ui-button-default.png b/typo3/sysext/form/Resources/Public/Images/ui-button-default.png deleted file mode 100644 index 0c78dc6ae4fcb2d51a59243eecc9e1bf8892a055..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 322 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}MSxF;>;M1%flSV@g=UlPwp{wv zaqW-o>}PRXKRC>PWi|bg{oEHohGxTQm8zp!OP@>U?UZaeC$Rj3QSS|AK?M%?X3nq$ zdhM5hCIA6YW!SP?j>{hW`}gnJvu9PuzV_e#&%5r&`b$r~eEIU=!GmAFel?%}dH>VD zH*enb9+>_Ss7I<K$S;^d>dcv+Ka20~+Qn0RW*3n0^NdWxo->?cXE@)T69lS?_jGX# zk&v8v&|RoOL4f6ep}Rma---wS>tEz%=TEzPpgvQqU*%j-OW=md3S1L^lqp{Ge=u{l zq1>vsDciX2elZAcuZf#!**@WyQWTfSDjScLTvvQwPujNfB0rz;mmgo4=BW$#-`O<D PALI*9S3j3^P6<r_tdf+W diff --git a/typo3/sysext/form/Resources/Public/Images/ui-button.png b/typo3/sysext/form/Resources/Public/Images/ui-button.png deleted file mode 100644 index d13064cc74d7d1ceb080435719f78c55c40aa876..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 314 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}QGic~>;M1%flN_R(Rwh*$;koI zU%q_VzI}U1Nl9&OZBS59RaI3=O3K5B55Il;W?^B`-rj!a&K*`(R-i@^5fM*MPaq>E zCPokpfNFmJ{0TJn<HwKh-o5+&{rjg+pI*Fp@%HW8-@ku9e*C!N-f5tE2I-O@zhDNb z(zMu2t2Z;_*>bJ2-n_A2@K&5NH&rYrC~%*}5+{(ko-U3d6>)P<nF}==h_D`LUn_A! zvNPxY|E`ip<!3g0eZ;DK!#wHGft59VMNbzz?VliJljHKh<FDTjk;6qorx|w39m%?w wTCemvV9Hh5Rht}ax6WT7n{86-^o3V0mh&zDg>QyiK!L&F>FVdQ&MBb@0HGv`7XSbN diff --git a/typo3/sysext/form/Resources/Public/Images/ui-check-box.png b/typo3/sysext/form/Resources/Public/Images/ui-check-box.png deleted file mode 100644 index 45a96e975fd5b5270ae80f94b13670217eb88742..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 392 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM*ui0G|+7pUlFNl9HD%Uy6u`)Pq4)RTWSY$P^V7wXm?r$;ml$=1fXTil?V% zOiWBr5Xd5Azyk*V|NsB}`}d0%FTQ{O{`m3Znwpx|uV44{^kipe|NZ;-=g*(tzI_AQ z^6}%xckkXkefsp~&6}4mU!FdF`lnBy?%%%;H2K1X3qMQuP6Qe)Q4-`A%#d+8ODQr+ zDy=jr^IBG3PA(6(grume^9MD57oeI_PZ!4!iOX}(yNfkBh`0m>DNCx{E>l~);r~;{ zx8?6Xo_%%hl)=AKj`R2|8P;8Wr6Yb|O24*}Z$p6X`5UX88g@Lsoc(A|H{+$oOLH%M zpTLyzSf^#i#~F+&Jvz6YIJ_B}Rvb^S6{#t^dSl<Yb=<s_mA}jWKfS@YDvGo2xBug8 PkpDeh{an^LB{Ts5&|<2J diff --git a/typo3/sysext/form/Resources/Public/Images/ui-check-boxes.png b/typo3/sysext/form/Resources/Public/Images/ui-check-boxes.png deleted file mode 100644 index 7ed6644b6af67d114574a195802fe7f05a1392da..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 374 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM*uu0G|+7pUlF7f`YWPG+|-ky1KfGii)pazXB!e>+7YYq#PX`{r&y#+_?i3 z0fOl0Xb2-PFwn!pqp+~>-@kvqe*Jp$=FN*2FTQ;F^6c5O-@ku<|Ni~s$B#utMGF@$ zymsx{kt0XGefze3`}SSCb{#lyVEy{_ZEbDw@$sc${#HQaL`#DFf*CTi0!`B2NTrqL z=f2~Y%i$5PUbmb@@l-reL4~J_V~E7%-t(S9O%4JsfoVBuA}f}uPjTG)pK~SKqJoD_ z+s}0D_c*>&V@EEN)S(rwfga7;)+(`5HdmFO2HxK@O}#;{{#T(G)1}Szr#nCESpHfl zZ};k;#Aj~nDYqn;f5|Pr!>8Gy|21q<p`Y!l+HE(hLyorp`F~c_HJxL{oIhKwL4Njh L^>bP0l+XkKv^<<x diff --git a/typo3/sysext/form/Resources/Public/Images/ui-combo-box.png b/typo3/sysext/form/Resources/Public/Images/ui-combo-box.png deleted file mode 100644 index f4c3e2e1c4b30d3cc136d60d778eada7249381ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 326 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6BuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrH1%z3_B#43UszJ!q)UoG8KY;Q8)-llsDvCP}On+kY@EMLEex zNhveQC{ks~#>gWt<UMr$wMKZkw3{uz{<^$;o;sU#7z0BF<6VtUuddoMFudPZGU@W- zjFL7!Mu$xXpEK@<=mb4?U}soywd|_eqqEI!%BMH!WSh)8cm0^a;*7#KVhjeK&2=t_ z^S)zX2zaS2Fnh19F2jNkM$0a_U(uA_!NB0%;3f3=h$;g^!^{<zwSn%fJ7SZY&A?!L zpgm}crgb4N!>?OMmJ1nn9pYtJV9*&a+VOt<S7Qc-qR8ACvu3&e%!vd#Pxp*&$$$TV Wu+Zf}Q_X>9FnGH9xvX<aXaWGd!FgN& diff --git a/typo3/sysext/form/Resources/Public/Images/ui-group-box.png b/typo3/sysext/form/Resources/Public/Images/ui-group-box.png deleted file mode 100644 index 3f9e7eb1aacb916344a3d552c42d878ffde9001a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 325 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM*u!0G|+7pUlF5fB*pj0S*ohMlb-%)YjIrv9SSp*4Eatva%i?9uW}{KyE@p z0#G(RJv}ckucV};va+(ex*Dh$t`W{CO?>tVs8qHj$S;^7SI%ZuuS0sp(t?t_oZ#%b ztjvsbDU)K2Lbo`RuU^p}K!wSkE{-7*mt*_gg&GtDSfrV`Bn%S&{I~99pFZz$$<>^z zhfX^l`K4cVy<+|TY11}5y`!lg6mw`#%AK7aGGac-2Fohhrg_P&4+zr8(#(p|jIz?S qs?w}t(_}mL^8WIP0US<$Ox2e~F)RLEE$a@nj=|H_&t;ucLK6Tt!d?ad diff --git a/typo3/sysext/form/Resources/Public/Images/ui-labels.png b/typo3/sysext/form/Resources/Public/Images/ui-labels.png deleted file mode 100644 index f4b0c418d07125d0d10c74ed1d5f64a753914531..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 367 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM*uW0G|+7pUlFXoE$$tKYxFJVPRooV`E20$NKtuF)=ZqlF-o5%*;$HE31Hj zfa>b%<m6;KJ3FAbo}M01(A?Y{$VLN_l9DPaDrRP8|NsC0`}gmgH*a3Nc=77ht9S3- zef;?G+qZ8&fBt-C*ewOrCRY;V7tAmrBP}H_Fs(!?Eloeow>OO~%{PrlOh=eml{r1j zepNsxP-UK{i(`ny<<K+XLJbN$t`{}L)@Xd|s{QZ1g@Nh0e!iM`gLkc-sJV-=_(?`X zCrMSFlM6K-zd7}26N9RyutAG^#<RC~%|6ZB8aZ!X`SjAl&^jsEo{g77jO@1@Tzziq p9;HwAGsSm(JN@~2_`U3($9Z`wIgRy?O|t-b&C}J-Wt~$(69D{mesll; diff --git a/typo3/sysext/form/Resources/Public/Images/ui-radio-button.png b/typo3/sysext/form/Resources/Public/Images/ui-radio-button.png deleted file mode 100644 index cbdb9223daeca3226b7aad1f835d34e280363726..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 411 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf(g2?j*Z=?j1DVy;)%ErDRaI3$ zvb?<f_wV0DMMcx6PyhDq+nhObo<4mF6i-f0&d$z`jEsEy_U*@yAMf3}=kM=-;lc$s zH@E!!{Kt<U1I<xaR}TvdJ9zNmqeqX}*w{G0Ku}N+C<O${%E}N1galgg>C>kdFJA1~ zvj=GAlqpl%+S-2n`0?b)6QF6=uU}uhcyUux6VNe^=0e|rdNfOd{DK*z7<|nW8NMnt zeQLa@m>Q7w_3NeJ%PQ@kSZ%wnesfDHVo=gxU>4*Ov3u^L3)EEO>Eak7AvyP8t00r3 z0Ly`gckjwRzsLKDpSR1mVUhF??&CA}XnayT-ssVkcVg2*ZlR7vS?l?ILmhJJ?|rMC zT`PU$)2+f;$F6O5kSVV)`X&>@uEMc<-o}00%o8;Hrj^D{vS8YoQW-eSb)Kx_rQlF= l-`KhD6|UcY_<G_a@qDXG+_sf(k|RML_H^}gS?83{1OO1<u8#l! diff --git a/typo3/sysext/form/Resources/Public/Images/ui-radio-buttons.png b/typo3/sysext/form/Resources/Public/Images/ui-radio-buttons.png deleted file mode 100644 index 4face34b24a76003aab6cf739e1d9c5c36514d08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 447 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM*t;0X`wFKADAg@813M=g-ZXH_OV(>g($}J3E1rwY9ZCc5ZI&*RNmQ+}siq z6J1<fjvP6{&(Ht<{rjCecM1pyJbn5Us1gFy)z#VA*+W7?U;+$h&z{}Ce?JEYhk=2C zqobp@w|8D%9#G4J2M<1d`t%D7zI^%e=FOYu&z}Qr@9yqCeE9JB^XCf+3LZUr^z7NQ z$B!T1xpU{xp+m6~^gV#)YL*1~1v4D~c|trXPfE-$;AcpB`KjceXNrGv@JmSAWU{b} z{{CYwxw?GO@85rZTiCH)25M^dba4!kxZHdCxKNXWh)dw735h3|x|$}n<^1^XKY8Jk zodu8YmWIo={WOmhbanlg=a^*KH<iUAWMAatPz#>5v6p#1T)*5b(3Je)>)Wq;%BSu7 zotk;-O!1i)kAEk=Sbb+J|G}~&6|<gmigUF7D|-=U-nV;stj}>~fA90#xaUoi5oG#* ZTiS9lx7Lb?UJ+2(c)I$ztaD0e0swvc$cF#` diff --git a/typo3/sysext/form/Resources/Public/Images/ui-scroll-pane-text.png b/typo3/sysext/form/Resources/Public/Images/ui-scroll-pane-text.png deleted file mode 100644 index 85400c6120782dce91b6b472146bd451ddc8f188..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 344 zcmV-e0jK_nP)<h;3K|Lk000e1NJLTq000mG000mO1ONa4wfZ;e0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUy`bk7VR2Ufr!8=YHQ4|2s^PF3y3F8^@ zD-oY1I8xXGyFkz&rHd$$SU{8xWeL=@EFc?*p{8&m*(fqF$ewv`a!y>G*Z}~b0ESug zyVo}W0Q@om-_9c&+@sO@3`jbIpU%99E)_?hRshM`;M8C#`czCc=9r?<ns{{wo<yfO z#2CO>v9b-G#X1$gL1V5#YYQ0=UCIss6{uunK&(^opfN{-)<;{&fLNvC02QD@K?cM+ z6;ri^+Cq)iCVD`uQnrr@dw`5DvJZ&2G<Za<^$9R2dSCsJQ_N!I7yvNxZsfa>WyIe< qdzSzJl<oqQMZDeG5C8yx-oO9z0bAu3UL~La0000<MNUMnLSTY(C5PJp diff --git a/typo3/sysext/form/Resources/Public/Images/ui-text-field-hidden.png b/typo3/sysext/form/Resources/Public/Images/ui-text-field-hidden.png deleted file mode 100644 index 715e18d06c97240bd13dd813246348f8e66f5eaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 207 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Wq?nJ>;M1%IXO9NYHA7!3g*t8 z%gf7~lapg$V33%Y=;h^AU0od-8d_3Pa_`=~4<9}}e*F0R_wO%XzWnp&&u5cce}RVZ zmIV0)GnA*xN!dsjWy_f4W)$AI?hcf&@N{tusfb&9$WyFAfrs@V-z)>`yZ<-j{j7c^ zYP~~tTgfMpnN{6h^{&ff776ro98+g|>yvr&#shX9Ue>z?yXNx(&0z3!^>bP0l+XkK DF8NMy diff --git a/typo3/sysext/form/Resources/Public/Images/ui-text-field-password.png b/typo3/sysext/form/Resources/Public/Images/ui-text-field-password.png deleted file mode 100644 index fd0fa5a65549a2d27d28d7c372ae87224fc5422b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!Yk*IP>;M1%YiepZIXQWGc?}E< zJUu<Lv$IP|N<u?JtE;OM6BB{#w6wH~7cc(#^XKHrlOH~Oc>MTrbaeFh@84g(e0lHQ zy{4w7p0MR>fCh4x1o;Isq?bn)<z;5%`erwDdW!&s9X(wfLn`9d9(LzDpuod&!1u+C zwlDwfO)uT6Nndi1E3usE(QB6_#dVW47uqQu3c1K1n)>R*_Sj=n0^5Qwm@Q9ily~0D V#-4YKSp#SogQu&X%Q~loCIF)9S|I=c diff --git a/typo3/sysext/form/Resources/Public/Images/ui-text-field.png b/typo3/sysext/form/Resources/Public/Images/ui-text-field.png deleted file mode 100644 index bd818025869b33833d0178331322f1c19263d422..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 317 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJOS+@4BLl<6e(pbstUx|vage(c z!@6@aFM*td0G|+7pUgsDUf$~J>XMQYMlb-%)YQ~)a&iJ01_lP%+1a6?p^1r!o}Qj? z1waN+_T|f$j~_q&^XJcp4<GK`yZ8P3_l%5;uCA{9{QTP5+UDlw($doNOHY>qm5P)E z`2{mX`euZ#NKY>}Day>v&I?(RJ-^`fH1pR$`DjlU#}J9jQ_oonH7JNMUr^BC=#pOj z`fvWqu=*7;@vD|_eOQ}2Z_4EmlVw*HaWwm7db=F1s;^4AXjyJ?X<`4zt|jd!E6*Qk tXy3qgDA3c=J7{nCu}{lav`0N?mu9-o857kMeHLg9gQu&X%Q~loCIA*`awGr% diff --git a/typo3/sysext/form/Resources/Public/Images/user-silhouette.png b/typo3/sysext/form/Resources/Public/Images/user-silhouette.png deleted file mode 100644 index 6205ff18ad0d23bf2c0a310c28473c22881a190e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 509 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbMf)c~ImS0IfCgoK3Pl2uk#hD%vm zTEf|I1{W8|b_lSwwN+A53J3@Q>awx1(b3TXl7WGNKoNg`e|2?rh%z-bHMn|1LqnkL zUS3|Ip`o#{v5t<8&d$z}k&(f{!9a~cK|xVbQE_o`PEJnY;o%Vx5!Tk$K+}N6dU|>W z_{dxW8e~!u<QL4KUVQn=^1Y^aVt?*haQ60|S^t7Qem0Z(8_dJ?_2S|CtV)Jo@BO^@ z_5GKhzkk1YdH2P=S6|+Lf0knl)W6u%#WAEJ&i71c(P0Ay*Fe{_G(O|SD#1H8{8v@= zv!3?;`OHfGnyv+l4scv};4;m91LL2j1&02P8R9Iqx1D&;eDt~Sea=cp*)W69m&YQ6 zwiq#-jkCXSF0Vm<na3B+mbt-=X>&iw+xY3PzTADTQq&`s{V`YF#>-Rh9bk*UDW77` z$nEeZ%~Egu_ANUe{CRgQAtCBy+UX3b-S#TeEE)FJtq$D(ooU;3k(o>V8Dl`<=IQF^ Jvd$@?2>_0xp0WS{ diff --git a/typo3/sysext/form/Resources/Public/Images/validator.svg b/typo3/sysext/form/Resources/Public/Images/validator.svg new file mode 100644 index 000000000000..5b1ed6a243b6 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/Images/validator.svg @@ -0,0 +1,9 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 20.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve"> +<path style="fill:#0078e6;" d="M14,0H2H1v1v7c0,4.5,6.5,7.7,6.8,7.8L8,15.9l0.2-0.1C8.5,15.7,15,12.4,15,8V1V0H14z M8,14.8 + C6.9,14.2,2,11.5,2,8V1h12v7C14,11.4,9.1,14.2,8,14.8z"/> +<path style="fill:#FFFFFF;" d="M14,8V1H8v13.8C9.1,14.2,14,11.4,14,8z"/> +<path style="fill:#0078e6;" d="M2,1v7c0,3.5,4.9,6.2,6,6.8V1H2z"/> +</svg> diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor.js new file mode 100644 index 000000000000..adecd027bd49 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor.js @@ -0,0 +1,1114 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormEditor + */ +define(['jquery', + 'TYPO3/CMS/Form/Backend/FormEditor/Core', + ], function($, core) { + 'use strict'; + + /** + * Return a static method named "getInstance". + * Use this method to create the formeditor app. + */ + return (function(_core) { + + /** + * @private + * + * Hold the instance (Singleton Pattern) + */ + var _formEditorInstance = null; + + /** + * @public + * + * @param object _configuration + * @param object _mediator + * @param object _viewModel + * @return object + */ + function FormEditor(_configuration, _mediator, _viewModel) { + + /** + * @private + * + * @var bool + */ + var _isRunning = false; + + /** + * @private + * + * @var bool + */ + var _unsavedContent = false; + + /** + * @private + * + * @var bool + */ + var _previewMode = false; + + /** + * @public + * + * @return object + */ + function getPublisherSubscriber() { + return _core.getPublisherSubscriber(); + }; + + /** + * @public + * + * @return void + */ + function _saveApplicationState() { + + _getApplicationStateStack().addAndReset({ + formDefinition: _getApplicationStateStack().getCurrentState('formDefinition').clone(), + currentlySelectedPageIndex: _getApplicationStateStack().getCurrentState('currentlySelectedPageIndex'), + currentlySelectedFormElementIdentifierPath: _getApplicationStateStack().getCurrentState('currentlySelectedFormElementIdentifierPath') + }); + }; + + /** + * @public + * + * @return void + */ + function undoApplicationState() { + _getApplicationStateStack().incrementCurrentStackPointer(); + }; + + /** + * @public + * + * @return void + */ + function redoApplicationState() { + _getApplicationStateStack().decrementCurrentStackPointer(); + }; + + /** + * @public + * + * @return int + */ + function getMaximalApplicationStates() { + return _getApplicationStateStack().getMaximalStackSize(); + }; + + /** + * @public + * + * @return int + */ + function getCurrentApplicationStates() { + return _getApplicationStateStack().getCurrentStackSize(); + }; + + /** + * @public + * + * @return int + */ + function getCurrentApplicationStatePosition() { + return _getApplicationStateStack().getCurrentStackPointer(); + }; + + /** + * @public + * + * @param string type + * @return object + * @throws 1475378543 + */ + function getRunningAjaxRequest(type) { + assert(getUtility().isNonEmptyString(type), 'Invalid parameter "type"', 1475378543); + return _core.getRunningAjaxRequest(type); + }; + + /** + * @public + * + * @return object + */ + function getUtility() { + return _core.getUtility(); + }; + + /** + * @public + * + * @param mixed test + * @param string message + * @param int messageCode + * @return void + */ + function assert(test, message, messageCode) { + getUtility().assert(test, message, messageCode); + }; + + /** + * @public + * + * @param string propertyPath + * @param string collectionElementIdentifier + * @param string collectionName + * @param object formElement + * @param boolean allowEmptyReturnValue + * @return string + */ + function buildPropertyPath(propertyPath, collectionElementIdentifier, collectionName, formElement, allowEmptyReturnValue) { + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + formElement = _getRepository().findFormElement(formElement); + return getUtility().buildPropertyPath(propertyPath, collectionElementIdentifier, collectionName, formElement, allowEmptyReturnValue); + }; + + /** + * @public + * + * @param string validatorIdentifier + * @param function func + * @return void + */ + function addPropertyValidationValidator(validatorIdentifier, func) { + _getPropertyValidationService().addValidator(validatorIdentifier, func); + }; + + /** + * @public + * + * @param string propertyPath + * @return object + */ + function validateCurrentlySelectedFormElementProperty(propertyPath) { + return validateFormElementProperty( + getCurrentlySelectedFormElement(), + propertyPath + ); + }; + + /** + * @public + * + * @param object formElement + * @param string propertyPath + * @return object + */ + function validateFormElementProperty(formElement, propertyPath) { + formElement = _getRepository().findFormElement(formElement); + return _getPropertyValidationService().validateFormElementProperty(formElement, propertyPath); + }; + + /** + * @public + * + * @param object formElement + * @return object + */ + function validateFormElement(formElement) { + formElement = _getRepository().findFormElement(formElement); + return _getPropertyValidationService().validateFormElement(formElement); + }; + + /** + * @public + * + * @param object validationResults + * @return boolean + */ + function validationResultsHasErrors(validationResults) { + return _getPropertyValidationService().validationResultsHasErrors(validationResults); + }; + + /** + * @public + * + * @param object formElement + * @param boolean returnAfterFirstMatch + * @return object + */ + function validateFormElementRecursive(formElement, returnAfterFirstMatch) { + formElement = _getRepository().findFormElement(formElement); + return _getPropertyValidationService().validateFormElementRecursive(formElement, returnAfterFirstMatch); + }; + + /** + * @public + * + * @param bool unsavedContent + * @return void + * @throws 1475378544 + */ + function setUnsavedContent(unsavedContent) { + assert('boolean' === $.type(unsavedContent), 'Invalid parameter "unsavedContent"', 1475378544); + _unsavedContent = unsavedContent; + }; + + /** + * @public + * + * @return boolean + */ + function getUnsavedContent() { + return _unsavedContent; + }; + + /** + * @public + * + * @return object + */ + function getRootFormElement() { + return _getRepository().getRootFormElement(); + }; + + /** + * @public + * + * @return string + */ + function getCurrentlySelectedFormElement() { + return _getRepository().findFormElementByIdentifierPath(_getApplicationStateStack().getCurrentState('currentlySelectedFormElementIdentifierPath')); + }; + + /** + * @public + * + * @param string|object formElement + * @param boolean doNotRefreshCurrentlySelectedPageIndex + * @return void + * @publish core/currentlySelectedFormElementChanged + */ + function setCurrentlySelectedFormElement(formElement, doNotRefreshCurrentlySelectedPageIndex) { + doNotRefreshCurrentlySelectedPageIndex = !!doNotRefreshCurrentlySelectedPageIndex; + + formElement = _getRepository().findFormElement(formElement); + _getApplicationStateStack().setCurrentState('currentlySelectedFormElementIdentifierPath', formElement.get('__identifierPath')); + + if (!doNotRefreshCurrentlySelectedPageIndex) { + refreshCurrentlySelectedPageIndex(); + } + getPublisherSubscriber().publish('core/currentlySelectedFormElementChanged', [formElement]); + }; + + /** + * @public + * + * @param string identifierPath + * @return object + * @throws 1475378545 + */ + function getFormElementByIdentifierPath(identifierPath) { + assert(getUtility().isNonEmptyString(identifierPath), 'Invalid parameter "identifierPath"', 1475378545); + return _getRepository().findFormElementByIdentifierPath(identifierPath); + }; + + /** + * @public + * + * @param string identifierPath + * @return bool + */ + function isFormElementIdentifierUsed(formElementIdentifier) { + return _getRepository().isFormElementIdentifierUsed(formElementIdentifier); + } + + /** + * @public + * + * @param string formElementType + * @param string|object referenceFormElement + * @param boolean disablePublishersOnSet + * @return object + */ + function createAndAddFormElement(formElementType, referenceFormElement, disablePublishersOnSet) { + var formElement; + formElement = addFormElement(createFormElement(formElementType, disablePublishersOnSet), referenceFormElement, disablePublishersOnSet); + formElement.set('renderables', formElement.get('renderables')); + return formElement; + }; + + /** + * @public + * + * @param object formElement + * @param string|object referenceFormElement + * @param boolean disablePublishersOnSet + * @return object + * @throws 1475434337 + */ + function addFormElement(formElement, referenceFormElement, disablePublishersOnSet) { + _saveApplicationState(); + + if (getUtility().isUndefinedOrNull(referenceFormElement)) { + referenceFormElement = getCurrentlySelectedFormElement(); + } + referenceFormElement = _getRepository().findFormElement(referenceFormElement); + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475434337); + return _getRepository().addFormElement(formElement, referenceFormElement, true, disablePublishersOnSet); + }; + + /** + * @public + * + * @param string formElementType + * @param boolean disablePublishersOnSet + * @return object + * @throws 1475434336 + * @throws 1475435857 + */ + function createFormElement(formElementType, disablePublishersOnSet) { + var formElementDefinition, identifier; + assert(getUtility().isNonEmptyString(formElementType), 'Invalid parameter "formElementType"', 1475434336); + + identifier = _getRepository().getNextFreeFormElementIdentifier(formElementType); + formElementDefinition = getFormElementDefinitionByType(formElementType); + return _getFactory().createFormElement({ + type: formElementType, + identifier: identifier, + label: formElementDefinition['label'] || formElementType + }, undefined, undefined, undefined, disablePublishersOnSet); + }; + + /** + * @public + * + * @param string|object formElementToRemove + * @param boolean disablePublishersOnSet + * @return object + */ + function removeFormElement(formElementToRemove, disablePublishersOnSet) { + var parentFormElement; + _saveApplicationState(); + + formElementToRemove = _getRepository().findFormElement(formElementToRemove); + parentFormElement = formElementToRemove.get('__parentRenderable'); + _getRepository().removeFormElement(formElementToRemove, true, disablePublishersOnSet); + return parentFormElement; + }; + + /** + * @public + * + * @param string|object formElementToMove + * @param string position + * @param string|object referenceFormElement + * @param boolean disablePublishersOnSet + * @return string + * @throws 1475378551 + */ + function moveFormElement(formElementToMove, position, referenceFormElement, disablePublishersOnSet) { + _saveApplicationState(); + + formElementToMove = _getRepository().findFormElement(formElementToMove); + referenceFormElement = _getRepository().findFormElement(referenceFormElement); + + assert('after' === position || 'before' === position || 'inside' === position, 'Invalid position "' + position + '"', 1475378551); + + formElementToMove = _getRepository().moveFormElement(formElementToMove, position, referenceFormElement, true); + disablePublishersOnSet = !!disablePublishersOnSet; + if (!disablePublishersOnSet) { + formElementToMove.get('__parentRenderable').set('renderables', formElementToMove.get('__parentRenderable').get('renderables')); + } + return formElementToMove; + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param string collectionName + * @param string formElement + * @return object (dereferenced) + * @throws 1475378555 + * @throws 1475378556 + * @throws 1475446108 + */ + function getPropertyCollectionElementConfiguration(collectionElementIdentifier, collectionName, formElement) { + var collection, collectionElement, formElementDefinition; + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + formElement = _getRepository().findFormElement(formElement); + + assert(getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378555); + assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378556); + + formElementDefinition = getFormElementDefinitionByType(formElement.get('type')); + collection = formElementDefinition['propertyCollections'][collectionName]; + assert(!getUtility().isUndefinedOrNull(collection), 'Invalid collection name "' + collectionName + '"', 1475446108); + collectionElement = _getRepository().findCollectionElementByIdentifierPath(collectionElementIdentifier, collection); + + return $.extend(true, {}, collectionElement); + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param string collectionName + * @param string formElement + * @return int + * @throws 1475378557 + * @throws 1475378558 + */ + function getIndexFromPropertyCollectionElement(collectionElementIdentifier, collectionName, formElement) { + var indexFromPropertyCollectionElement; + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + formElement = _getRepository().findFormElement(formElement); + + assert(getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378557); + assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378558); + + indexFromPropertyCollectionElement = _getRepository().getIndexFromPropertyCollectionElementByIdentifier( + collectionElementIdentifier, + collectionName, + formElement + ); + + return indexFromPropertyCollectionElement; + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param string collectionName + * @param object formElement + * @param object collectionElementConfiguration + * @param string referenceCollectionElementIdentifier + * @return object + */ + function createAndAddPropertyCollectionElement(collectionElementIdentifier, collectionName, formElement, collectionElementConfiguration, referenceCollectionElementIdentifier) { + return addPropertyCollectionElement(createPropertyCollectionElement(collectionElementIdentifier, collectionName, collectionElementConfiguration), collectionName, formElement, referenceCollectionElementIdentifier); + }; + + /** + * @public + * + * @param object collectionElement + * @param string collectionName + * @param string|object formElement + * @param string referenceCollectionElementIdentifier + * @return object + * @throws 1475443300 + * @throws 1475443301 + */ + function addPropertyCollectionElement(collectionElement, collectionName, formElement, referenceCollectionElementIdentifier) { + var collection; + _saveApplicationState(); + + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + formElement = _getRepository().findFormElement(formElement); + + assert('object' === $.type(collectionElement), 'Invalid parameter "collectionElement"', 1475443301); + assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475443300); + + if (getUtility().isUndefinedOrNull(referenceCollectionElementIdentifier)) { + collection = formElement.get(collectionName); + if ('array' === $.type(collection) && collection.length > 0) { + referenceCollectionElementIdentifier = collection[collection.length - 1]['identifier']; + } + } + + return _getRepository().addPropertyCollectionElement( + collectionElement, + collectionName, + formElement, + referenceCollectionElementIdentifier, + false + ); + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param string collectionName + * @param object collectionElementConfiguration + * @return void + * @throws 1475378559 + * @throws 1475378560 + */ + function createPropertyCollectionElement(collectionElementIdentifier, collectionName, collectionElementConfiguration) { + assert(getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378559); + assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378560); + if ('object' !== $.type(collectionElementConfiguration)) { + collectionElementConfiguration = {}; + } + + return _getFactory().createPropertyCollectionElement(collectionElementIdentifier, collectionElementConfiguration, collectionName); + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param string collectionName + * @param string formElement + * @param bool disablePublishersOnSet + * @return void + * @throws 1475378561 + * @throws 1475378562 + */ + function removePropertyCollectionElement(collectionElementIdentifier, collectionName, formElement, disablePublishersOnSet) { + _saveApplicationState(); + + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + formElement = _getRepository().findFormElement(formElement); + + assert(getUtility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475378561); + assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475378562); + + _getRepository().removePropertyCollectionElementByIdentifier( + formElement, + collectionElementIdentifier, + collectionName, + true + ); + + disablePublishersOnSet = !!disablePublishersOnSet; + if (!disablePublishersOnSet) { + getPublisherSubscriber().publish('core/formElement/somePropertyChanged', ['__fakeProperty']); + } + }; + + /** + * @public + * + * @param string collectionElementToMove + * @param string position + * @param string referenceCollectionElement + * @param string collectionName + * @param object formElement + * @param boolean disablePublishersOnSet + * @return string + * @throws 1477404352 + * @throws 1477404353 + * @throws 1477404354 + * @throws 1477404355 + */ + function movePropertyCollectionElement(collectionElementToMove, position, referenceCollectionElement, collectionName, formElement, disablePublishersOnSet) { + _saveApplicationState(); + + formElement = _getRepository().findFormElement(formElement); + + assert('string' === $.type(collectionElementToMove), 'Invalid parameter "collectionElementToMove"', 1477404352); + assert('string' === $.type(referenceCollectionElement), 'Invalid parameter "referenceCollectionElement"', 1477404353); + assert('after' === position || 'before' === position, 'Invalid position "' + position + '"', 1477404354); + assert(getUtility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1477404355); + + return _getRepository().movePropertyCollectionElement(collectionElementToMove, position, referenceCollectionElement, collectionName, formElement, disablePublishersOnSet); + }; + + /** + * @public + * + * @param string elementType + * @param string formElementDefinitionKey + * @returnmixed + * @throws 1475378563 + */ + function getFormElementDefinitionByType(elementType, formElementDefinitionKey) { + var formElementDefinition; + assert(getUtility().isNonEmptyString(elementType), 'Invalid parameter "elementType"', 1475378563); + + formElementDefinition = _getRepository().getFormEditorDefinition('formElements', elementType); + + if (!getUtility().isUndefinedOrNull(formElementDefinitionKey)) { + formElementDefinition = formElementDefinition[formElementDefinitionKey]; + } + + if ('object' === $.type(formElementDefinition) || 'array' === $.type(formElementDefinition)) { + return $.extend(true, {}, formElementDefinition); + } else { + return formElementDefinition; + } + }; + + /** + * @public + * + * @param object formElement + * @param string formElementDefinitionKey + * @return mixed + */ + function getFormElementDefinition(formElement, formElementDefinitionKey) { + formElement = _getRepository().findFormElement(formElement); + return getFormElementDefinitionByType(formElement.get('type'), formElementDefinitionKey); + }; + + /** + * @public + * + * @param string collectionName + * @param string collectionElementIdentifier + * @return mixed + */ + function getFormEditorDefinition(definitionName, subject) { + return _getRepository().getFormEditorDefinition(definitionName, subject); + }; + + /** + * @public + * + * @param string validatorIdentifier + * @return object (dereferenced) + * @throws 1475672362 + */ + function getFormElementPropertyValidatorDefinition(validatorIdentifier) { + var validatorDefinition; + assert(getUtility().isNonEmptyString(validatorIdentifier), 'Invalid parameter "validatorIdentifier"', 1475672362); + + validatorDefinition = _getRepository().getFormEditorDefinition('formElementPropertyValidators', validatorIdentifier); + return $.extend(true, {}, validatorDefinition); + }; + + /** + * @public + * + * @return int + */ + function getCurrentlySelectedPageIndex() { + return _getApplicationStateStack().getCurrentState('currentlySelectedPageIndex'); + }; + + /** + * @public + * + * @return void + */ + function refreshCurrentlySelectedPageIndex() { + _getApplicationStateStack().setCurrentState('currentlySelectedPageIndex', getPageIndexFromFormElement(getCurrentlySelectedFormElement())); + }; + + /** + * @public + * + * @return object + * @throws 1477786068 + */ + function getCurrentlySelectedPage() { + var currentPage; + + currentPage = _getRepository().getRootFormElement().get('renderables')[getCurrentlySelectedPageIndex()]; + assert('object' === $.type(currentPage), 'No page found', 1477786068); + return currentPage; + }; + + /** + * @public + * + * @return object + */ + function getLastTopLevelElementOnCurrentPage() { + var lastRenderable, renderables; + + renderables = getCurrentlySelectedPage().get('renderables'); + if (getUtility().isUndefinedOrNull(renderables)) { + return undefined; + } + lastRenderable = renderables[renderables.length - 1]; + return lastRenderable; + }; + + /** + * @public + * + * @param object + * @return object + */ + function getLastFormElementWithinParentFormElement(formElement) { + var lastElement; + + formElement = _getRepository().findFormElement(formElement); + if (formElement.get('__identifierPath') === getRootFormElement().get('__identifierPath')) { + return formElement; + } + return formElement.get('__parentRenderable').get('renderables')[formElement.get('__parentRenderable').get('renderables').length - 1]; + }; + + /** + * @public + * + * @param object + * @return int + */ + function getPageIndexFromFormElement(formElement) { + formElement = _getRepository().findFormElement(formElement); + + return _getRepository().getIndexForEnclosingCompositeFormElementWhichIsOnTopLevelForFormElement( + formElement + ); + }; + + /** + * @public + * + * @return void + */ + function renderCurrentFormPage() { + renderFormPage(getCurrentlySelectedPageIndex()); + }; + + /** + * @public + * + * @param int pageIndex + * @return void + * @throws 1475446442 + */ + function renderFormPage(pageIndex) { + assert('number' === $.type(pageIndex), 'Invalid parameter "pageIndex"', 1475446442); + _getDataBackend().renderFormDefinitionPage(pageIndex); + }; + + /** + * @public + * + * @param object formElement + * @return object|null + */ + function findEnclosingCompositeFormElementWhichIsNotOnTopLevel(formElement) { + return _getRepository().findEnclosingCompositeFormElementWhichIsNotOnTopLevel( + _getRepository().findFormElement(formElement) + ); + }; + + /** + * @public + * + * @return boolean + */ + function isRootFormElementSelected() { + return (getCurrentlySelectedFormElement().get('__identifierPath') === getRootFormElement().get('__identifierPath')); + }; + + /** + * @public + * + * @return object + */ + function getViewModel() { + return _viewModel; + }; + + /** + * @public + * + * @return void + */ + function saveFormDefinition() { + _getDataBackend().saveFormDefinition(); + }; + + /** + * @private + * + * @return object + */ + function _getDataBackend() { + return _core.getDataBackend(); + }; + + /** + * @private + * + * @return object + */ + function _getFactory() { + return _core.getFactory(); + }; + + /** + * @private + * + * @return object + */ + function _getRepository() { + return _core.getRepository(); + }; + + /** + * @private + * + * @return object + */ + function _getPropertyValidationService() { + return _core.getPropertyValidationService(); + }; + + /** + * @public + * + * @return object + */ + function _getApplicationStateStack() { + return _core.getApplicationStateStack(); + }; + + /** + * @private + * + * @return void + * @publish ajax/beforeSend + * @publish ajax/complete + */ + function _ajaxSetup() { + $.ajaxSetup({ + beforeSend: function() { + getPublisherSubscriber().publish('ajax/beforeSend'); + }, + complete: function() { + getPublisherSubscriber().publish('ajax/complete'); + } + }); + }; + + /** + * @private + * + * @param object endpoints + * @param string prototypeName + * @param string formPersistenceIdentifier + * @return void + * @throws 1475379748 + * @throws 1475379749 + * @throws 1475927876 + */ + function _dataBackendSetup(endpoints, prototypeName, formPersistenceIdentifier) { + assert('object' === $.type(endpoints), 'Invalid parameter "endpoints"', 1475379748); + assert(getUtility().isNonEmptyString(prototypeName), 'Invalid parameter "prototypeName"', 1475927876); + assert(getUtility().isNonEmptyString(formPersistenceIdentifier), 'Invalid parameter "formPersistenceIdentifier"', 1475379749); + + _core.getDataBackend().setEndpoints(endpoints); + _core.getDataBackend().setPrototypeName(prototypeName); + _core.getDataBackend().setPersistenceIdentifier(formPersistenceIdentifier); + }; + + /** + * @private + * + * @param object formEditorDefinitions + * @return void + * @throws 1475379750 + */ + function _repositorySetup(formEditorDefinitions) { + assert('object' === $.type(formEditorDefinitions), 'Invalid parameter "formEditorDefinitions"', 1475379750); + + _getRepository().setFormEditorDefinitions(formEditorDefinitions); + } + + /** + * @private + * + * @param object additionalViewModelModules + * @return void + * @throws 1475379752 + * @throws 1475492374 + */ + function _viewSetup(additionalViewModelModules) { + assert('function' === $.type(_viewModel.bootstrap), 'The view model does not implement the method "bootstrap"', 1475492374); + + if (!getUtility().isUndefinedOrNull(additionalViewModelModules)) { + assert('array' === $.type(additionalViewModelModules), 'Invalid parameter "additionalViewModelModules"', 1475379752); + } else { + additionalViewModelModules = []; + } + _viewModel.bootstrap(_formEditorInstance, additionalViewModelModules); + }; + + /** + * @private + * + * @return void + * @throws 1475492032 + */ + function _mediatorSetup() { + assert('function' === $.type(_mediator.bootstrap), 'The mediator does not implement the method "bootstrap"', 1475492032); + _mediator.bootstrap(_formEditorInstance, _viewModel); + }; + + /** + * @private + * + * @param object rootFormElement + * @param int maximumUndoSteps + * @return void + * @throws 1475379751 + */ + function _applicationStateStackSetup(rootFormElement, maximumUndoSteps) { + assert('object' === $.type(rootFormElement), 'Invalid parameter "rootFormElement"', 1475379751); + + if ('number' !== $.type(maximumUndoSteps)) { + maximumUndoSteps = 10; + } + _getApplicationStateStack().setMaximalStackSize(maximumUndoSteps); + + _getApplicationStateStack().addAndReset({ + currentlySelectedPageIndex: 0, + currentlySelectedFormElementIdentifierPath: rootFormElement['identifier'] + }, true); + + _getApplicationStateStack().setCurrentState('formDefinition', _getFactory().createFormElement(rootFormElement, undefined, undefined, true)); + }; + + /** + * @private + * + * @return void + */ + function _bootstrap() { + _configuration = _configuration || {}; + + _mediatorSetup(); + _ajaxSetup(); + _dataBackendSetup(_configuration['endpoints'], _configuration['prototypeName'], _configuration['formPersistenceIdentifier']); + _repositorySetup(_configuration['formEditorDefinitions']); + _applicationStateStackSetup(_configuration['formDefinition'], _configuration['maximumUndoSteps']); + setCurrentlySelectedFormElement(_getRepository().getRootFormElement()); + + _viewSetup(_configuration['additionalViewModelModules']); + }; + + /** + * @public + * + * @return TYPO3/CMS/Form/Backend/FormEditor + * @throws 1473200696 + */ + function run() { + if (_isRunning) { + throw 'You can not run the app twice (1473200696)'; + } + + _bootstrap(); + _isRunning = true; + return this; + }; + + /** + * Publish the public methods. + * Implements the "Revealing Module Pattern". + */ + return { + getRootFormElement: getRootFormElement, + + createAndAddFormElement: createAndAddFormElement, + createFormElement: createFormElement, + addFormElement: addFormElement, + moveFormElement: moveFormElement, + removeFormElement: removeFormElement, + + getCurrentlySelectedFormElement: getCurrentlySelectedFormElement, + setCurrentlySelectedFormElement: setCurrentlySelectedFormElement, + + getFormElementByIdentifierPath: getFormElementByIdentifierPath, + isFormElementIdentifierUsed: isFormElementIdentifierUsed, + + createAndAddPropertyCollectionElement: createAndAddPropertyCollectionElement, + createPropertyCollectionElement: createPropertyCollectionElement, + addPropertyCollectionElement: addPropertyCollectionElement, + removePropertyCollectionElement: removePropertyCollectionElement, + movePropertyCollectionElement: movePropertyCollectionElement, + getIndexFromPropertyCollectionElement: getIndexFromPropertyCollectionElement, + getPropertyCollectionElementConfiguration: getPropertyCollectionElementConfiguration, + + saveFormDefinition: saveFormDefinition, + renderCurrentFormPage: renderCurrentFormPage, + renderFormPage: renderFormPage, + + getCurrentlySelectedPageIndex: getCurrentlySelectedPageIndex, + refreshCurrentlySelectedPageIndex: refreshCurrentlySelectedPageIndex, + getPageIndexFromFormElement: getPageIndexFromFormElement, + getCurrentlySelectedPage: getCurrentlySelectedPage, + getLastTopLevelElementOnCurrentPage: getLastTopLevelElementOnCurrentPage, + findEnclosingCompositeFormElementWhichIsNotOnTopLevel: findEnclosingCompositeFormElementWhichIsNotOnTopLevel, + isRootFormElementSelected: isRootFormElementSelected, + getLastFormElementWithinParentFormElement: getLastFormElementWithinParentFormElement, + + getFormElementDefinitionByType: getFormElementDefinitionByType, + getFormElementDefinition: getFormElementDefinition, + getFormElementPropertyValidatorDefinition: getFormElementPropertyValidatorDefinition, + getFormEditorDefinition: getFormEditorDefinition, + + getPublisherSubscriber: getPublisherSubscriber, + getRunningAjaxRequest: getRunningAjaxRequest, + + setUnsavedContent: setUnsavedContent, + getUnsavedContent: getUnsavedContent, + + addPropertyValidationValidator: addPropertyValidationValidator, + validateFormElementProperty: validateFormElementProperty, + validateCurrentlySelectedFormElementProperty: validateCurrentlySelectedFormElementProperty, + validateFormElement: validateFormElement, + validateFormElementRecursive: validateFormElementRecursive, + validationResultsHasErrors: validationResultsHasErrors, + + getUtility: getUtility, + assert: assert, + buildPropertyPath: buildPropertyPath, + + getViewModel: getViewModel, + undoApplicationState: undoApplicationState, + redoApplicationState: redoApplicationState, + getMaximalApplicationStates: getMaximalApplicationStates, + getCurrentApplicationStates: getCurrentApplicationStates, + getCurrentApplicationStatePosition: getCurrentApplicationStatePosition, + + run: run + }; + }; + + /** + * Emulation of static methods + */ + return { + /** + * @public + * @static + * + * Implement the "Singleton Pattern". + * + * Return a singleton instance of a + * "FormEditor" object. + * + * @param object configuration + * @param object mediator + * @param object viewModel + * @return object + */ + getInstance: function(configuration, mediator, viewModel) { + if(_formEditorInstance === null) { + _formEditorInstance = new FormEditor(configuration, mediator, viewModel); + } + return _formEditorInstance; + } + }; + })(core); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Core.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Core.js new file mode 100644 index 000000000000..0fd0b84ba7f2 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Core.js @@ -0,0 +1,2127 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormEditor/Core + */ +define(['jquery'], function($) { + 'use strict'; + + return (function($) { + + /** + * @private + * + * @var object + */ + var _dataBackendEndpoints = {}; + + /** + * @private + * + * @var string + */ + var _dataBackendPrototypeName = null; + + /** + * @private + * + * @var string + */ + var _dataBackendPersistenceIdentifier = null; + + /** + * @private + * + * @var object + */ + var _publisherSubscriberTopics = {}; + + /** + * @private + * + * @var int + */ + var _publisherSubscriberUid = -1; + + /** + * @private + * + * @var object + */ + var _repositoryFormEditorDefinitions = {}; + + /** + * @private + * + * @var object + */ + var _runningAjaxRequests = []; + + /** + * @private + * + * @var object + */ + var _propertyValidationServiceValidators = {}; + + /** + * @private + * + * @var int + */ + var _applicationStateStackSize = 10; + + /** + * @private + * + * @var int + */ + var _applicationStateStackPointer = 0; + + /** + * @private + * + * @var object + */ + var _applicationStateStack = []; + + /** + * @public + * + * @return object + */ + function utility() { + + /** + * @public + * + * @param mixed test + * @param string message + * @param int messageCode + * @return void + */ + function assert(test, message, messageCode) { + if ('function' === $.type(test)) { + test = (test() !== false); + } + if (!test) { + message = message || "Assertion failed"; + if (messageCode) { + message = message + ' (' + messageCode + ')'; + } + if ('undefined' !== typeof Error) { + throw new Error(message); + } + throw message; + } + }; + + /** + * @public + * + * @param mixed value + * @return bool + */ + function isUndefinedOrNull(value) { + return ('undefined' === $.type(value) || 'null' === $.type(value)); + }; + + /** + * @public + * + * @param mixed value + * @return bool + */ + function isNonEmptyString(value) { + return ('string' === $.type(value) && value.length > 0); + }; + + /** + * @public + * + * @param string propertyPath + * @param string collectionElementIdentifier + * @param string collectionName + * @param object formElement + * @param boolean allowEmptyReturnValue + * @return string + * @throws 1475412569 + * @throws 1475412570 + * @throws 1475415988 + * @throws 1475663210 + */ + function buildPropertyPath(propertyPath, collectionElementIdentifier, collectionName, formElement, allowEmptyReturnValue) { + var newPropertyPath = ''; + + allowEmptyReturnValue = !!allowEmptyReturnValue; + if (isNonEmptyString(collectionElementIdentifier) || isNonEmptyString(collectionName)) { + assert(isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475412569); + assert(isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475412570); + newPropertyPath = collectionName + '.' + repository().getIndexFromPropertyCollectionElementByIdentifier(collectionElementIdentifier, collectionName, formElement); + } else { + newPropertyPath = ''; + } + + if (!isUndefinedOrNull(propertyPath)) { + assert(isNonEmptyString(propertyPath), 'Invalid parameter "propertyPath"', 1475415988); + if (isNonEmptyString(newPropertyPath)) { + newPropertyPath = newPropertyPath + '.' + propertyPath; + } else { + newPropertyPath = propertyPath; + } + } + + if (!allowEmptyReturnValue) { + assert(isNonEmptyString(newPropertyPath), 'The property path could not be resolved', 1475663210); + } + return newPropertyPath; + }; + + /** + * @public + * + * @param object formElement + * @return object + * @throws 1475377782 + */ + function convertToSimpleObject(formElement) { + var childFormElements, simpleObject, objectData; + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475377782); + + simpleObject = {}; + objectData = ('function' === $.type(formElement.getObjectData)) ? formElement.getObjectData() : formElement; + childFormElements = objectData['renderables']; + delete objectData['renderables']; + + for (var key in objectData) { + if (!objectData.hasOwnProperty(key)) { + continue; + } + var value = objectData[key]; + if (key.match(/^__/)) { + continue; + } + + if ('object' === $.type(value)) { + simpleObject[key] = convertToSimpleObject(value); + } else if ('function' !== $.type(value) && 'undefined' !== $.type(value)) { + simpleObject[key] = value; + } + } + + if ('array' === $.type(childFormElements)) { + simpleObject['renderables'] = []; + for (var i = 0, len = childFormElements.length; i < len; ++i) { + simpleObject['renderables'].push(convertToSimpleObject(childFormElements[i])); + } + } + + return simpleObject; + }; + + /** + * Publish the public methods. + */ + return { + assert: assert, + convertToSimpleObject: convertToSimpleObject, + isNonEmptyString: isNonEmptyString, + isUndefinedOrNull: isUndefinedOrNull, + buildPropertyPath: buildPropertyPath + }; + }; + + /** + * @public + * + * @return object + */ + function propertyValidationService() { + + /** + * @public + * + * @param object formElement + * @param object validators + * @param string propertyPath + * @param string collectionElementIdentifier + * @param string collectionName + * @param object configuration + * @return void + * @throws 1475661025 + * @throws 1475661026 + * @throws 1479238074 + */ + function addValidatorIdentifiersToFormElementProperty(formElement, validators, propertyPath, collectionElementIdentifier, collectionName, configuration) { + var formElementIdentifierPath, propertyPath, propertyValidationServiceRegisteredValidators; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475661025); + utility().assert('array' === $.type(validators), 'Invalid parameter "validators"', 1475661026); + utility().assert('array' === $.type(validators), 'Invalid parameter "validators"', 1479238074); + + formElementIdentifierPath = formElement.get('__identifierPath'); + propertyPath = utility().buildPropertyPath(propertyPath, collectionElementIdentifier, collectionName, formElement); + + propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators'); + if (utility().isUndefinedOrNull(propertyValidationServiceRegisteredValidators[formElementIdentifierPath])) { + propertyValidationServiceRegisteredValidators[formElementIdentifierPath] = {}; + } + if (utility().isUndefinedOrNull(propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath])) { + propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath] = { + validators: [], + configuration: configuration + }; + } + for (var i = 0, len = validators.length; i < len; ++i) { + if (propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'].indexOf(validators[i]) === -1) { + propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'].push(validators[i]); + } + } + getApplicationStateStack().setCurrentState('propertyValidationServiceRegisteredValidators', propertyValidationServiceRegisteredValidators); + }; + + /** + * @public + * + * @param object formElement + * @param string propertyPath + * @return void + * @throws 1475700618 + * @throws 1475706896 + */ + function removeValidatorIdentifiersFromFormElementProperty(formElement, propertyPath) { + var formElementIdentifierPath, propertyValidationServiceRegisteredValidators, registeredValidators; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475700618); + utility().assert(utility().isNonEmptyString(propertyPath), 'Invalid parameter "propertyPath"', 1475706896); + + formElementIdentifierPath = formElement.get('__identifierPath'); + + registeredValidators = {}; + propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators'); + for (var registeredPropertyPath in propertyValidationServiceRegisteredValidators[formElementIdentifierPath]) { + if ( + !propertyValidationServiceRegisteredValidators[formElementIdentifierPath].hasOwnProperty(registeredPropertyPath) + || registeredPropertyPath.indexOf(propertyPath) > -1 + ) { + continue; + } + registeredValidators[registeredPropertyPath] = propertyValidationServiceRegisteredValidators[formElementIdentifierPath][registeredPropertyPath]; + } + propertyValidationServiceRegisteredValidators[formElementIdentifierPath] = registeredValidators; + getApplicationStateStack().setCurrentState('propertyValidationServiceRegisteredValidators', propertyValidationServiceRegisteredValidators); + }; + + /** + * @public + * + * @param string|object formElement + * @return void + * @throws 1475668189 + */ + function removeAllValidatorIdentifiersFromFormElement(formElement) { + var propertyValidationServiceRegisteredValidators, registeredValidators; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475668189); + + registeredValidators = {}; + propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators'); + for (var formElementIdentifierPath in propertyValidationServiceRegisteredValidators) { + if ( + !propertyValidationServiceRegisteredValidators.hasOwnProperty(formElementIdentifierPath) + || formElementIdentifierPath === formElement.get('__identifierPath') + || formElementIdentifierPath.indexOf(formElement.get('__identifierPath') + '/') > -1 + ) { + continue; + } + registeredValidators[formElementIdentifierPath] = propertyValidationServiceRegisteredValidators[formElementIdentifierPath]; + } + getApplicationStateStack().setCurrentState('propertyValidationServiceRegisteredValidators', registeredValidators); + }; + + /** + * @public + * + * @param string validatorIdentifier + * @param function func + * @return void + * @throws 1475669143 + * @throws 1475669144 + * @throws 1475669145 + */ + function addValidator(validatorIdentifier, func) { + utility().assert(utility().isNonEmptyString(validatorIdentifier), 'Invalid parameter "validatorIdentifier"', 1475669143); + utility().assert('function' === $.type(func), 'Invalid parameter "func"', 1475669144); + utility().assert('function' !== $.type(_propertyValidationServiceValidators[validatorIdentifier]), 'The validator "' + validatorIdentifier + '" is already registered', 1475669145); + + _propertyValidationServiceValidators[validatorIdentifier] = func; + }; + + /** + * @public + * + * @param object formElement + * @param string propertyPath + * @param string errorMessage + * @return object + * @throws 1475676517 + * @throws 1475676518 + */ + function validateFormElementProperty(formElement, propertyPath) { + var configuration, formElementIdentifierPath, propertyValidationServiceRegisteredValidators, validationResults; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475676517); + utility().assert(utility().isNonEmptyString(propertyPath), 'Invalid parameter "propertyPath"', 1475676518); + + formElementIdentifierPath = formElement.get('__identifierPath'); + + validationResults = []; + propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators'); + configuration = { + propertyValidatorsMode: 'AND' + }; + + if ( + !utility().isUndefinedOrNull(propertyValidationServiceRegisteredValidators[formElementIdentifierPath]) + && 'object' === $.type(propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]) + && 'array' === $.type(propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators']) + ) { + configuration = propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['configuration']; + for (var i = 0, len = propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'].length; i < len; ++i) { + var validatorIdentifier, validationResult; + + validatorIdentifier = propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'][i]; + if ('function' !== $.type(_propertyValidationServiceValidators[validatorIdentifier])) { + continue; + } + validationResult = _propertyValidationServiceValidators[validatorIdentifier](formElement, propertyPath); + + if (utility().isNonEmptyString(validationResult)) { + validationResults.push(validationResult); + } + } + } + + if ( + validationResults.length > 0 + && configuration['propertyValidatorsMode'] === 'OR' + && validationResults.length !== propertyValidationServiceRegisteredValidators[formElementIdentifierPath][propertyPath]['validators'].length + ) { + return []; + } + + return validationResults; + }; + + /** + * @public + * + * @param object formElement + * @return object + * @throws 1475749668 + */ + function validateFormElement(formElement) { + var formElementIdentifierPath, propertyValidationServiceRegisteredValidators, validationResults; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475749668); + + formElementIdentifierPath = formElement.get('__identifierPath'); + + validationResults = []; + propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators'); + if (!utility().isUndefinedOrNull(propertyValidationServiceRegisteredValidators[formElementIdentifierPath])) { + for (var registeredPropertyPath in propertyValidationServiceRegisteredValidators[formElementIdentifierPath]) { + var validationResult; + if (!propertyValidationServiceRegisteredValidators[formElementIdentifierPath].hasOwnProperty(registeredPropertyPath)) { + continue; + } + validationResult = { + propertyPath: registeredPropertyPath, + validationResults: validateFormElementProperty(formElement, registeredPropertyPath) + }; + validationResults.push(validationResult); + } + } + return validationResults; + }; + + /** + * @public + * + * @param array validationResults + * @return bool + * @throws 1478613477 + */ + function validationResultsHasErrors(validationResults) { + utility().assert('array' === $.type(validationResults), 'Invalid parameter "validationResults"', 1478613477); + + for (var i = 0, len = validationResults.length; i < len; ++i) { + for (var j = 0, len2 = validationResults[i]['validationResults'].length; j < len2; ++j) { + if ( + validationResults[i]['validationResults'][j]['validationResults'] + && validationResults[i]['validationResults'][j]['validationResults'].length > 0 + ) { + return true; + } + } + } + return false; + }; + + /** + * @public + * + * @param object formElement + * @param boolean returnAfterFirstMatch + * @param object validationResults + * @return object + * @throws 1475749668 + */ + function validateFormElementRecursive(formElement, returnAfterFirstMatch, validationResults) { + var formElements, validationResult, validationResults; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475756764); + returnAfterFirstMatch = !!returnAfterFirstMatch; + + validationResults = validationResults || []; + validationResult = { + formElementIdentifierPath: formElement.get('__identifierPath'), + validationResults: validateFormElement(formElement) + }; + validationResults.push(validationResult); + + if (returnAfterFirstMatch && validationResultsHasErrors(validationResults)) { + return validationResults; + } + + formElements = formElement.get('renderables'); + if ('array' === $.type(formElements)) { + for (var i = 0, len = formElements.length; i < len; ++i) { + validateFormElementRecursive(formElements[i], returnAfterFirstMatch, validationResults); + if (returnAfterFirstMatch && validationResultsHasErrors(validationResults)) { + return validationResults; + } + } + } + + return validationResults; + } + + /** + * @public + * + * @param object formElement + * @return void + * @throws 1475707334 + */ + function addValidatorIdentifiersFromFormElementPropertyCollections(formElement) { + var formElementTypeDefinition; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475707334); + + formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type')); + + if (!utility().isUndefinedOrNull(formElementTypeDefinition['propertyCollections'])) { + for (var collectionName in formElementTypeDefinition['propertyCollections']) { + if ( + !formElementTypeDefinition['propertyCollections'].hasOwnProperty(collectionName) + || 'array' !== $.type(formElementTypeDefinition['propertyCollections'][collectionName]) + ) { + continue; + } + for (var i = 0, len1 = formElementTypeDefinition['propertyCollections'][collectionName].length; i < len1; ++i) { + if ( + 'array' !== $.type(formElementTypeDefinition['propertyCollections'][collectionName][i]['editors']) + || repository().getIndexFromPropertyCollectionElementByIdentifier(formElementTypeDefinition['propertyCollections'][collectionName][i]['identifier'], collectionName, formElement) === -1 + ) { + continue; + } + for (var j = 0, len2 = formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'].length; j < len2; ++j) { + var configuration = {}; + + if ('array' !== $.type(formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'][j]['propertyValidators'])) { + continue; + } + + if ( + !utility().isUndefinedOrNull(formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'][j]['propertyValidatorsMode']) + && formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'][j]['propertyValidatorsMode'] === 'OR' + ) { + configuration['propertyValidatorsMode'] = 'OR'; + } else { + configuration['propertyValidatorsMode'] = 'AND'; + } + addValidatorIdentifiersToFormElementProperty( + formElement, + formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'][j]['propertyValidators'], + formElementTypeDefinition['propertyCollections'][collectionName][i]['editors'][j]['propertyPath'], + formElementTypeDefinition['propertyCollections'][collectionName][i]['identifier'], + collectionName, + configuration + ); + } + } + } + } + }; + + /** + * Publish the public methods. + */ + return { + addValidatorIdentifiersToFormElementProperty: addValidatorIdentifiersToFormElementProperty, + removeValidatorIdentifiersFromFormElementProperty: removeValidatorIdentifiersFromFormElementProperty, + removeAllValidatorIdentifiersFromFormElement: removeAllValidatorIdentifiersFromFormElement, + validateFormElementProperty: validateFormElementProperty, + validateFormElement: validateFormElement, + validateFormElementRecursive: validateFormElementRecursive, + validationResultsHasErrors: validationResultsHasErrors, + addValidator: addValidator, + addValidatorIdentifiersFromFormElementPropertyCollections: addValidatorIdentifiersFromFormElementPropertyCollections + }; + }; + + /** + * @public + * + * @param string ajaxRequestIdentifier + * @return object|null + * @throws 1475358064 + */ + function getRunningAjaxRequest(ajaxRequestIdentifier) { + utility().assert(utility().isNonEmptyString(ajaxRequestIdentifier), 'Invalid parameter "ajaxRequestIdentifier"', 1475358064); + return _runningAjaxRequests[ajaxRequestIdentifier] || null; + }; + + /** + * @public + * + * Implements the "Publish/Subscribe Pattern" + * + * @return object + * @credits Addy Osmani https://addyosmani.com/resources/essentialjsdesignpatterns/book/#highlighter_634280 + */ + function publisherSubscriber() { + + /** + * @public + * + * @param string topic + * @param mixed args + * @return void + * @throws 1475358066 + */ + function publish(topic, args) { + utility().assert(utility().isNonEmptyString(topic), 'Invalid parameter "topic"', 1475358066); + if (utility().isUndefinedOrNull(_publisherSubscriberTopics[topic])) { + return; + } + + for (var i = 0, len = _publisherSubscriberTopics[topic].length; i < len; ++i) { + _publisherSubscriberTopics[topic][i].func(topic, args); + } + }; + + /** + * @public + * + * @param string topic + * @param function func + * @return string + * @throws 1475358067 + */ + function subscribe(topic, func) { + utility().assert(utility().isNonEmptyString(topic), 'Invalid parameter "topic"', 1475358067); + utility().assert('function' === $.type(func), 'Invalid parameter "func"', 1475411986); + + if (utility().isUndefinedOrNull(_publisherSubscriberTopics[topic])) { + _publisherSubscriberTopics[topic] = []; + } + + var token = (++_publisherSubscriberUid).toString(); + _publisherSubscriberTopics[topic].push({ + token: token, + func: func + }); + return token; + }; + + /** + * @public + * + * @param string token + * @return null|string + * @throws 1475358068 + */ + function unsubscribe(token) { + utility().assert(utility().isNonEmptyString(token), 'Invalid parameter "token"', 1475358068); + + for (var key in _publisherSubscriberTopics) { + if (!_publisherSubscriberTopics.hasOwnProperty(key)) { + continue; + } + for (var i = 0, len = _publisherSubscriberTopics[key].length; i < len; ++i) { + if (_publisherSubscriberTopics[key][i].token === token) { + _publisherSubscriberTopics[key].splice(i, 1); + return token; + } + } + } + return null; + }; + + /** + * Publish the public methods. + */ + return { + publish: publish, + subscribe: subscribe, + unsubscribe: unsubscribe + }; + }; + + /** + * @private + * + * @param object modelToExtend + * @param object modelExtension + * @param string pathPrefix + * @return void + * @throws 1474640022 + * @throws 1475358069 + * @throws 1475358070 + * @publish core/formElement/somePropertyChanged + */ + function extendModel(modelToExtend, modelExtension, pathPrefix, disablePublishersOnSet) { + utility().assert('object' === $.type(modelToExtend), 'Invalid parameter "modelToExtend"', 1475358069); + utility().assert('object' === $.type(modelExtension) || 'array' === $.type(modelExtension), 'Invalid parameter "modelExtension"', 1475358070); + + disablePublishersOnSet = !!disablePublishersOnSet; + pathPrefix = pathPrefix || ''; + + if ($.isEmptyObject(modelExtension)) { + utility().assert('' !== pathPrefix, 'Empty path is not allowed', 1474640022); + modelToExtend.on(pathPrefix, 'core/formElement/somePropertyChanged'); + modelToExtend.set(pathPrefix, modelExtension, disablePublishersOnSet); + } else { + for (var key in modelExtension) { + if (!modelExtension.hasOwnProperty(key)) { + continue; + } + var path = (pathPrefix === '') ? key : pathPrefix + '.' + key; + + modelToExtend.on(path, 'core/formElement/somePropertyChanged'); + + if ('object' === $.type(modelExtension[key]) || 'array' === $.type(modelExtension[key])) { + extendModel(modelToExtend, modelExtension[key], path, disablePublishersOnSet); + } else { + modelToExtend.set(path, modelExtension[key], disablePublishersOnSet); + } + } + } + }; + + /** + * @private + * + * @param object modelExtension + * @return object + */ + function createModel(modelExtension) { + var newModel; + + modelExtension = modelExtension || {}; + + function M() { + + /** + * @private + */ + var _objectData = {}; + + /** + * @private + */ + var _publisherTopics = {}; + + /** + * @public + * + * @param string key + * @return mixed|undefined + * @throws 1475361755 + */ + function get(key) { + var firstPartOfPath, obj; + utility().assert(utility().isNonEmptyString(key), 'Invalid parameter "key"', 1475361755); + + obj = _objectData; + while (key.indexOf('.') > 0) { + firstPartOfPath = key.slice(0, key.indexOf('.')); + key = key.slice(firstPartOfPath.length + 1); + if (!obj.hasOwnProperty(firstPartOfPath)) { + return undefined; + } + obj = obj[firstPartOfPath]; + } + + return obj[key]; + }; + + /** + * @public + * + * @param string key + * @param mixed value + * @param bool disablePublishersOnSet + * @return void + * @throws 1475361756 + * @publish mixed + */ + function set(key, value, disablePublishersOnSet) { + var obj, oldValue, path; + utility().assert(utility().isNonEmptyString(key), 'Invalid parameter "key"', 1475361756); + disablePublishersOnSet = !!disablePublishersOnSet; + + oldValue = get(key); + obj = _objectData; + path = key; + + while (path.indexOf('.') > 0) { + var firstPartOfPath, nextPartOfPath; + + firstPartOfPath = path.slice(0, path.indexOf('.')); + path = path.slice(firstPartOfPath.length + 1); + if ($.isNumeric(firstPartOfPath)) { + firstPartOfPath = parseInt(firstPartOfPath); + } + if ('undefined' === $.type(obj[firstPartOfPath])) { + nextPartOfPath = path.slice(0, path.indexOf('.')); + if ($.isNumeric(nextPartOfPath)) { + obj[firstPartOfPath] = []; + } else { + obj[firstPartOfPath] = {}; + } + } + obj = obj[firstPartOfPath]; + } + obj[path] = value; + + if (!utility().isUndefinedOrNull(_publisherTopics[key]) && !disablePublishersOnSet) { + for (var i = 0, len = _publisherTopics[key].length; i < len; ++i) { + publisherSubscriber().publish(_publisherTopics[key][i], [key, value, oldValue, _objectData['__identifierPath']]); + } + } + }; + + /** + * @public + * + * @param string key + * @param string topicName + * @return void + * @throws 1475361757 + * @throws 1475361758 + */ + function on(key, topicName) { + utility().assert(utility().isNonEmptyString(key), 'Invalid parameter "key"', 1475361757); + utility().assert(utility().isNonEmptyString(topicName), 'Invalid parameter "topicName"', 1475361758); + + if ('array' !== $.type(_publisherTopics[key])) { + _publisherTopics[key] = []; + } + if (_publisherTopics[key].indexOf(topicName) === -1) { + _publisherTopics[key].push(topicName); + } + }; + + /** + * @public + * + * @param string key + * @param string topicName + * @return void + * @throws 1475361759 + * @throws 1475361760 + */ + function off(key, topicName) { + utility().assert(utility().isNonEmptyString(key), 'Invalid parameter "key"', 1475361759); + utility().assert(utility().isNonEmptyString(topicName), 'Invalid parameter "topicName"', 1475361760); + + if ('array' === $.type(_publisherTopics[key])) { + _publisherTopics[key] = _publisherTopics[key].filter(function (topicName) { + return topicName !== topicName; + }); + } + }; + + /** + * @public + * + * @return object (dereferenced) + */ + function getObjectData() { + return $.extend(true, {}, _objectData); + }; + + /** + * @public + * + * @return string + */ + function toString() { + var childFormElements, objectData; + + objectData = getObjectData(); + childFormElements = objectData['renderables'] || null; + delete objectData['renderables']; + + if (!utility().isUndefinedOrNull(objectData['__parentRenderable'])) { + objectData['__parentRenderable'] = objectData['__parentRenderable'].getObjectData()['__identifierPath'] + ' (filtered)'; + } + + if (null !== childFormElements) { + objectData['renderables'] = []; + for (var i = 0, len = childFormElements.length; i < len; ++i) { + var childFormElement = childFormElements[i]; + objectData['renderables'].push(JSON.parse(childFormElement.toString())); + } + } + + return JSON.stringify(objectData, null, 2); + }; + + /** + * @public + * + * @return object + */ + function clone() { + var childFormElements, newModel, newRenderables, objectData; + + objectData = getObjectData(); + childFormElements = objectData['renderables'] || null; + delete objectData['renderables']; + delete objectData['__parentRenderable']; + objectData['renderables'] = (childFormElements) ? true : null, + + newModel = new M(); + extendModel(newModel, objectData, '', true); + + if (null !== childFormElements) { + newRenderables = []; + for (var i = 0, len = childFormElements.length; i < len; ++i) { + var childFormElement = childFormElements[i]; + + childFormElement = childFormElement.clone(); + childFormElement.set('__parentRenderable', newModel, true); + newRenderables.push(childFormElement); + } + newModel.set('renderables', newRenderables, true); + } + + return newModel; + }; + + /** + * Publish the public methods. + */ + return { + get: get, + set: set, + + on: on, + off: off, + + getObjectData: getObjectData, + toString: toString, + clone: clone + }; + }; + + newModel = new M(); + extendModel(newModel, modelExtension, '', true); + + return newModel; + }; + + /** + * @public + * + * @return object + */ + function repository() { + + /** + * @public + * + * @param object typeDefinitions + * @return void + * @throws 1475364394 + */ + function setFormEditorDefinitions(formEditorDefinitions) { + utility().assert('object' === $.type(formEditorDefinitions), 'Invalid parameter "formEditorDefinitions"', 1475364394); + + for (var key1 in formEditorDefinitions) { + if (!formEditorDefinitions.hasOwnProperty(key1) || 'object' !== $.type(formEditorDefinitions[key1])) { + continue; + } + for (var key2 in formEditorDefinitions[key1]) { + if (!formEditorDefinitions[key1].hasOwnProperty(key2)) { + continue; + } + if ('object' !== $.type(formEditorDefinitions[key1][key2])) { + formEditorDefinitions[key1][key2] = {}; + } + } + } + _repositoryFormEditorDefinitions = formEditorDefinitions; + }; + + /** + * @public + * + * @param string typeName + * @param string subject + * @return object (dereferenced) + * @throws 1475364952 + * @throws 1475364953 + */ + function getFormEditorDefinition(definitionName, subject) { + utility().assert(utility().isNonEmptyString(definitionName), 'Invalid parameter "definitionName"', 1475364952); + utility().assert(utility().isNonEmptyString(subject), 'Invalid parameter "subject"', 1475364953); + return $.extend(true, {}, _repositoryFormEditorDefinitions[definitionName][subject]); + }; + + /** + * @public + * + * @return object + */ + function getRootFormElement() { + return getApplicationStateStack().getCurrentState('formDefinition'); + }; + + /** + * @public + * + * @param object formElement + * @param object referenceFormElement + * @param boolean registerPropertyValidators + * @param boolean disablePublishersOnSet + * @return object + * @throws 1475436224 + * @throws 1475364956 + */ + function addFormElement(formElement, referenceFormElement, registerPropertyValidators, disablePublishersOnSet) { + var enclosingCompositeFormElement, identifier, formElementTypeDefinition, parentFormElementsArray, referenceFormElementElements, referenceFormElementTypeDefinition; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475436224); + utility().assert('object' === $.type(referenceFormElement), 'Invalid parameter "referenceFormElement"', 1475364956); + + if (utility().isUndefinedOrNull(disablePublishersOnSet)) { + disablePublishersOnSet = true; + } + disablePublishersOnSet = !!disablePublishersOnSet; + + registerPropertyValidators = !!registerPropertyValidators; + formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type')); + referenceFormElementTypeDefinition = repository().getFormEditorDefinition('formElements', referenceFormElement.get('type')); + + if (!formElementTypeDefinition['_isTopLevelFormElement'] && referenceFormElementTypeDefinition['_isCompositeFormElement']) { + if ('array' !== $.type(referenceFormElement.get('renderables'))) { + referenceFormElement.set('renderables', [], disablePublishersOnSet); + } + + formElement.set('__parentRenderable', referenceFormElement, disablePublishersOnSet); + formElement.set('__identifierPath', referenceFormElement.get('__identifierPath') + '/' + formElement.get('identifier'), disablePublishersOnSet); + referenceFormElement.get('renderables').push(formElement); + } else { + if (referenceFormElement.get('__identifierPath') === getApplicationStateStack().getCurrentState('formDefinition').get('__identifierPath')) { + referenceFormElementElements = referenceFormElement.get('renderables'); + referenceFormElement = referenceFormElementElements[referenceFormElementElements.length - 1]; + } else if (formElementTypeDefinition['_isTopLevelFormElement'] && !referenceFormElementTypeDefinition['_isTopLevelFormElement']) { + referenceFormElement = findEnclosingCompositeFormElementWhichIsOnTopLevel(referenceFormElement); + } else if (formElementTypeDefinition['_isCompositeFormElement']) { + enclosingCompositeFormElement = findEnclosingCompositeFormElementWhichIsNotOnTopLevel(referenceFormElement); + if (enclosingCompositeFormElement) { + referenceFormElement = enclosingCompositeFormElement; + } + } + + formElement.set('__parentRenderable', referenceFormElement.get('__parentRenderable'), disablePublishersOnSet); + formElement.set('__identifierPath', referenceFormElement.get('__parentRenderable').get('__identifierPath') + '/' + formElement.get('identifier'), disablePublishersOnSet); + parentFormElementsArray = referenceFormElement.get('__parentRenderable').get('renderables'); + parentFormElementsArray.splice(parentFormElementsArray.indexOf(referenceFormElement) + 1, 0, formElement); + } + + if (registerPropertyValidators) { + if ('array' === $.type(formElementTypeDefinition['editors'])) { + for (var i = 0, len1 = formElementTypeDefinition['editors'].length; i < len1; ++i) { + var configuration = {}; + + if ('array' !== $.type(formElementTypeDefinition['editors'][i]['propertyValidators'])) { + continue; + } + + if ( + !utility().isUndefinedOrNull(formElementTypeDefinition['editors'][i]['propertyValidatorsMode']) + && formElementTypeDefinition['editors'][i]['propertyValidatorsMode'] === 'OR' + ) { + configuration['propertyValidatorsMode'] = 'OR'; + } else { + configuration['propertyValidatorsMode'] = 'AND'; + } + + propertyValidationService().addValidatorIdentifiersToFormElementProperty( + formElement, + formElementTypeDefinition['editors'][i]['propertyValidators'], + formElementTypeDefinition['editors'][i]['propertyPath'], + undefined, + undefined, + configuration + ); + } + } + } + + return formElement; + }; + + /** + * @param object formElement + * @param boolean removeRegisteredPropertyValidators + * @param boolean disablePublishersOnSet + * @return void + * @throws 1472553024 + * @throws 1475364957 + */ + function removeFormElement(formElement, removeRegisteredPropertyValidators, disablePublishersOnSet) { + var parentFormElementElements; + + if (utility().isUndefinedOrNull(disablePublishersOnSet)) { + disablePublishersOnSet = true; + } + disablePublishersOnSet = !!disablePublishersOnSet; + removeRegisteredPropertyValidators = !!removeRegisteredPropertyValidators; + + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364957); + utility().assert('object' === $.type(formElement.get('__parentRenderable')), 'Removing the root element is not allowed', 1472553024); + + parentFormElementElements = formElement.get('__parentRenderable').get('renderables'); + parentFormElementElements.splice(parentFormElementElements.indexOf(formElement), 1); + formElement.get('__parentRenderable').set('renderables', parentFormElementElements, disablePublishersOnSet); + + if (removeRegisteredPropertyValidators) { + propertyValidationService().removeAllValidatorIdentifiersFromFormElement(formElement); + } + }; + + /** + * @param object formElementToMove + * @param string position + * @param object referenceFormElement + * @param boolean disablePublishersOnSet + * @return object + * @throws 1475364958 + * @throws 1475364959 + * @throws 1475364960 + * @throws 1475364961 + * @throws 1475364962 + * @throws 1476993731 + * @throws 1476993732 + */ + function moveFormElement(formElementToMove, position, referenceFormElement, disablePublishersOnSet) { + var formElementToMoveTypeDefinition, referenceFormElementParentElements, referenceFormElementElements, referenceFormElementIndex, referenceFormElementTypeDefinition, reSetIdentifierPath; + utility().assert('object' === $.type(formElementToMove), 'Invalid parameter "formElementToMove"', 1475364958); + utility().assert('after' === position || 'before' === position || 'inside' === position, 'Invalid position "' + position + '"', 1475364959); + utility().assert('object' === $.type(referenceFormElement), 'Invalid parameter "referenceFormElement"', 1475364960); + + if (utility().isUndefinedOrNull(disablePublishersOnSet)) { + disablePublishersOnSet = true; + } + disablePublishersOnSet = !!disablePublishersOnSet; + + formElementToMoveTypeDefinition = repository().getFormEditorDefinition('formElements', formElementToMove.get('type')); + referenceFormElementTypeDefinition = repository().getFormEditorDefinition('formElements', referenceFormElement.get('type')); + + removeFormElement(formElementToMove, false); + reSetIdentifierPath = function(formElement, pathPrefix) { + var formElements, newIdentifierPath, oldIdentifierPath, propertyValidationServiceRegisteredValidators; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364961); + utility().assert(utility().isNonEmptyString(pathPrefix), 'Invalid parameter "pathPrefix"', 1475364962); + + oldIdentifierPath = formElement.get('__identifierPath'); + newIdentifierPath = pathPrefix + '/' + formElement.get('identifier'); + + propertyValidationServiceRegisteredValidators = getApplicationStateStack().getCurrentState('propertyValidationServiceRegisteredValidators'); + if (!utility().isUndefinedOrNull(propertyValidationServiceRegisteredValidators[oldIdentifierPath])) { + propertyValidationServiceRegisteredValidators[newIdentifierPath] = propertyValidationServiceRegisteredValidators[oldIdentifierPath]; + delete propertyValidationServiceRegisteredValidators[oldIdentifierPath]; + } + getApplicationStateStack().setCurrentState('propertyValidationServiceRegisteredValidators', propertyValidationServiceRegisteredValidators); + + formElement.set('__identifierPath', newIdentifierPath, disablePublishersOnSet); + formElements = formElement.get('renderables'); + if ('array' === $.type(formElements)) { + for (var i = 0, len = formElements.length; i < len; ++i) { + reSetIdentifierPath(formElements[i], formElement.get('__identifierPath')); + } + } + }; + + /** + * This is true on: + * * Drag a Element on a Page Element (tree) + * * Drag a Element on a Section Element (tree) + */ + if (position === 'inside') { + utility().assert(!formElementToMoveTypeDefinition['_isTopLevelFormElement'], 'This move is not allowed', 1476993731); + utility().assert(referenceFormElementTypeDefinition['_isCompositeFormElement'], 'This move is not allowed', 1476993732); + + formElementToMove.set('__parentRenderable', referenceFormElement, disablePublishersOnSet); + reSetIdentifierPath(formElementToMove, referenceFormElement.get('__identifierPath')); + + referenceFormElementElements = referenceFormElement.get('renderables'); + if (utility().isUndefinedOrNull(referenceFormElementElements)) { + referenceFormElementElements = []; + } + referenceFormElementElements.splice(0, 0, formElementToMove); + referenceFormElement.set('renderables', referenceFormElementElements, disablePublishersOnSet); + } else { + /** + * This is true on: + * * Drag a Page before another Page (tree) + * * Drag a Page after another Page (tree) + */ + if (formElementToMoveTypeDefinition['_isTopLevelFormElement'] && referenceFormElementTypeDefinition['_isTopLevelFormElement']) { + referenceFormElementParentElements = referenceFormElement.get('__parentRenderable').get('renderables'); + referenceFormElementIndex = referenceFormElementParentElements.indexOf(referenceFormElement); + + if (position === 'after') { + referenceFormElementParentElements.splice(referenceFormElementIndex + 1, 0, formElementToMove); + } else { + referenceFormElementParentElements.splice(referenceFormElementIndex, 0, formElementToMove); + } + + referenceFormElement.get('__parentRenderable').set('renderables', referenceFormElementParentElements, disablePublishersOnSet); + } else { + /** + * This is true on: + * * Drag a Element before another Element within the same level (tree) + * * Drag a Element after another Element within the same level (tree) + * * Drag a Element before another Element (stage) + * * Drag a Element after another Element (stage) + */ + if (formElementToMove.get('__parentRenderable').get('identifier') === referenceFormElement.get('__parentRenderable').get('identifier')) { + referenceFormElementParentElements = referenceFormElement.get('__parentRenderable').get('renderables'); + referenceFormElementIndex = referenceFormElementParentElements.indexOf(referenceFormElement); + } else { + /** + * This is true on: + * * Drag a Element before an Element on another page (tree) + * * Drag a Element after an Element on another page (tree) + */ + formElementToMove.set('__parentRenderable', referenceFormElement.get('__parentRenderable'), disablePublishersOnSet); + reSetIdentifierPath(formElementToMove, referenceFormElement.get('__parentRenderable').get('__identifierPath')); + + referenceFormElementParentElements = referenceFormElement.get('__parentRenderable').get('renderables'); + referenceFormElementIndex = referenceFormElementParentElements.indexOf(referenceFormElement); + } + + if (position === 'after') { + referenceFormElementParentElements.splice(referenceFormElementIndex + 1, 0, formElementToMove); + } else { + referenceFormElementParentElements.splice(referenceFormElementIndex, 0, formElementToMove); + } + + referenceFormElement.get('__parentRenderable').set('renderables', referenceFormElementParentElements, disablePublishersOnSet); + } + } + + return formElementToMove; + }; + + /** + * @param object formElement + * @return int + * @throws 1475364963 + */ + function getIndexForEnclosingCompositeFormElementWhichIsOnTopLevelForFormElement(formElement) { + var enclosingCompositeFormElementWhichIsOnTopLevel, formElementTypeDefinition; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364963); + + formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type')); + + if (formElementTypeDefinition['_isTopLevelFormElement'] && formElementTypeDefinition['_isCompositeFormElement']) { + enclosingCompositeFormElementWhichIsOnTopLevel = formElement; + } else if (formElement.get('__identifierPath') === getApplicationStateStack().getCurrentState('formDefinition').get('__identifierPath')) { + enclosingCompositeFormElementWhichIsOnTopLevel = getApplicationStateStack().getCurrentState('formDefinition').get('renderables')[0]; + } else { + enclosingCompositeFormElementWhichIsOnTopLevel = findEnclosingCompositeFormElementWhichIsOnTopLevel(formElement); + } + return enclosingCompositeFormElementWhichIsOnTopLevel.get('__parentRenderable').get('renderables').indexOf(enclosingCompositeFormElementWhichIsOnTopLevel); + }; + + /** + * @param object formElement + * @return object + * @throws 1472556223 + * @throws 1475364964 + */ + function findEnclosingCompositeFormElementWhichIsOnTopLevel(formElement) { + var formElementTypeDefinition; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364964); + utility().assert('object' === $.type(formElement.get('__parentRenderable')), 'The root element is never encloused by anything', 1472556223); + + formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type')); + while (!formElementTypeDefinition['_isTopLevelFormElement']) { + formElement = formElement.get('__parentRenderable'); + formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type')); + } + + return formElement; + }; + + /** + * @param object formElement + * @return object|null + * @throws 1475364965 + */ + function findEnclosingCompositeFormElementWhichIsNotOnTopLevel(formElement) { + var formElementTypeDefinition; + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475364965); + + formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type')); + while (!formElementTypeDefinition['_isCompositeFormElement']) { + if (formElementTypeDefinition['_isTopLevelFormElement']) { + return null; + } + formElement = formElement.get('__parentRenderable'); + formElementTypeDefinition = repository().getFormEditorDefinition('formElements', formElement.get('type')); + } + if (formElementTypeDefinition['_isTopLevelFormElement']) { + return null; + } + return formElement; + }; + + /** + * @param string identifier + * @returl bool + * @throws 1475364966 + */ + function isFormElementIdentifierUsed(identifier) { + var checkIdentifier, identifierFound; + utility().assert(utility().isNonEmptyString(identifier), 'Invalid parameter "identifier"', 1475364966); + + checkIdentifier = function(formElement) { + var formElements; + + if (formElement.get('identifier') === identifier) { + identifierFound = true; + } + + if (!identifierFound) { + formElements = formElement.get('renderables'); + if ('array' === $.type(formElements)) { + for (var i = 0, len = formElements.length; i < len; ++i) { + checkIdentifier(formElements[i]); + if (identifierFound) { + break; + } + } + } + } + } + + checkIdentifier(getApplicationStateStack().getCurrentState('formDefinition')); + return identifierFound; + }; + + /** + * @param string formElementType + * @return string + * @throws 1475373676 + */ + function getNextFreeFormElementIdentifier(formElementType) { + var i, prefix; + utility().assert(utility().isNonEmptyString(formElementType), 'Invalid parameter "formElementType"', 1475373676); + + prefix = formElementType.toLowerCase().replace(/[^a-z0-9]/g, '-') + '-'; + i = 1; + while (isFormElementIdentifierUsed(prefix + i)) { + i++; + } + return prefix + i; + }; + + /** + * @param string identifierPath + * @return object + * @throws 1472424333 + * @throws 1472424334 + * @throws 1472424330 + * @throws 1475373677 + */ + function findFormElementByIdentifierPath(identifierPath) { + var obj, pathParts, pathPartsLength, formElement, formElements; + + utility().assert(utility().isNonEmptyString(identifierPath), 'Invalid parameter "identifierPath"', 1475373677); + + formElement = getApplicationStateStack().getCurrentState('formDefinition'); + pathParts = identifierPath.split('/'); + pathPartsLength = pathParts.length; + + for (var i = 0; i < pathPartsLength; ++i) { + var key = pathParts[i]; + if (i === 0 || i === pathPartsLength) { + utility().assert(key === formElement.get('identifier'), '"' + key + '" does not exist in path "' + identifierPath + '"', 1472424333); + continue; + } + + formElements = formElement.get('renderables'); + if ('array' === $.type(formElements)) { + obj = null; + for (var j = 0, len = formElements.length; j < len; ++j) { + if (key === formElements[j].get('identifier')) { + obj = formElements[j]; + break; + } + } + + utility().assert('null' !== $.type(obj), 'Could not find form element "' + key + '" in path "' + identifierPath + '"', 1472424334); + formElement = obj; + } else { + utility().assert(false, 'No form elements found', 1472424330); + } + } + return formElement; + }; + + /** + * @param string|object formElement + * @return object + */ + function findFormElement(formElement) { + if ('object' === $.type(formElement)) { + formElement = formElement.get('__identifierPath'); + } + return findFormElementByIdentifierPath(formElement); + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param object collection + * @return undefined|object + * @throws 1475375281 + * @throws 1475375282 + */ + function findCollectionElementByIdentifierPath(collectionElementIdentifier, collection) { + utility().assert(utility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475375281); + utility().assert('array' === $.type(collection), 'Invalid parameter "collection"', 1475375282); + + for (var i = 0, len = collection.length; i < len; ++i) { + if (collection[i]['identifier'] === collectionElementIdentifier) { + return collection[i]; + } + } + + return undefined; + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param string collectionName + * @param object formElement + * @return int + * @throws 1475375283 + * @throws 1475375284 + * @throws 1475375285 + */ + function getIndexFromPropertyCollectionElementByIdentifier(collectionElementIdentifier, collectionName, formElement) { + var collection; + utility().assert(utility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475375283); + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475375284); + utility().assert(utility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475375285); + + collection = formElement.get(collectionName); + if ('array' === $.type(collection)) { + for (var i = 0, len = collection.length; i < len; ++i) { + if (collection[i]['identifier'] === collectionElementIdentifier) { + return i; + } + } + } + return -1; + }; + + /** + * @public + * + * @param object collectionElementToAdd + * @param string collectionName + * @param object formElement + * @param string referenceCollectionElementIdentifier + * @param boolean disablePublishersOnSet + * @return object + * @throws 1475375686 + * @throws 1475375687 + * @throws 1475375688 + * @throws 1477413154 + */ + function addPropertyCollectionElement(collectionElementToAdd, collectionName, formElement, referenceCollectionElementIdentifier, disablePublishersOnSet) { + var collection, formElementTypeDefinition, newCollection, newCollectionElementIndex; + utility().assert('object' === $.type(collectionElementToAdd), 'Invalid parameter "collectionElementToAdd"', 1475375686); + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475375687); + utility().assert(utility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475375688); + + if (utility().isUndefinedOrNull(disablePublishersOnSet)) { + disablePublishersOnSet = true; + } + disablePublishersOnSet = !!disablePublishersOnSet; + + collection = formElement.get(collectionName); + if ('array' !== $.type(collection)) { + extendModel(formElement, [], collectionName, true); + collection = formElement.get(collectionName); + } + + if (utility().isUndefinedOrNull(referenceCollectionElementIdentifier)) { + newCollectionElementIndex = 0; + } else { + newCollectionElementIndex = getIndexFromPropertyCollectionElementByIdentifier(referenceCollectionElementIdentifier, collectionName, formElement) + 1; + utility().assert(-1 < newCollectionElementIndex, 'Could not find collection element ' + referenceCollectionElementIdentifier + ' within collection ' + collectionName, 1477413154); + } + + collection.splice(newCollectionElementIndex, 0, collectionElementToAdd); + formElement.set(collectionName, collection, true); + + propertyValidationService().removeValidatorIdentifiersFromFormElementProperty(formElement, collectionName); + + for (var i = 0, len = collection.length; i < len; ++i) { + extendModel(formElement, collection[i], collectionName + '.' + i, true); + } + + formElement.set(collectionName, collection, true); + propertyValidationService().addValidatorIdentifiersFromFormElementPropertyCollections(formElement); + formElement.set(collectionName, collection, disablePublishersOnSet); + + return formElement; + }; + + /** + * @public + * + * @param object formElement + * @param string collectionElementIdentifier + * @param string collectionName + * @param boolean disablePublishersOnSet + * @return void + * @throws 1475375689 + * @throws 1475375690 + * @throws 1475375691 + * @throws 1475375692 + */ + function removePropertyCollectionElementByIdentifier(formElement, collectionElementIdentifier, collectionName, disablePublishersOnSet) { + var collection, collectionElementIndex; + utility().assert(utility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475375689); + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1475375690); + utility().assert(utility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475375691); + + collection = formElement.get(collectionName); + utility().assert('array' === $.type(collection), 'The collection "' + collectionName + '" does not exist', 1475375692); + + if (utility().isUndefinedOrNull(disablePublishersOnSet)) { + disablePublishersOnSet = true; + } + disablePublishersOnSet = !!disablePublishersOnSet; + + propertyValidationService().removeValidatorIdentifiersFromFormElementProperty(formElement, collectionName); + collectionElementIndex = getIndexFromPropertyCollectionElementByIdentifier(collectionElementIdentifier, collectionName, formElement); + collection.splice(collectionElementIndex, 1); + formElement.set(collectionName, collection, disablePublishersOnSet); + propertyValidationService().addValidatorIdentifiersFromFormElementPropertyCollections(formElement); + }; + + /** + * @param string collectionElementToMoveIdentifier + * @param string position + * @param string referenceCollectionElementIdentifier + * @param string position + * @param object formElement + * @param boolean disablePublishersOnSet + * @return void + * @throws 1477404484 + * @throws 1477404485 + * @throws 1477404486 + * @throws 1477404488 + * @throws 1477404489 + * @throws 1477404490 + */ + function movePropertyCollectionElement(collectionElementToMoveIdentifier, position, referenceCollectionElementIdentifier, collectionName, formElement, disablePublishersOnSet) { + var collection, collectionElementToMove, referenceCollectionElement, referenceCollectionElementIndex; + + utility().assert('after' === position || 'before' === position, 'Invalid position "' + position + '"', 1477404485); + utility().assert('string' === $.type(referenceCollectionElementIdentifier), 'Invalid parameter "referenceCollectionElementIdentifier"', 1477404486); + utility().assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1477404488); + + collection = formElement.get(collectionName); + utility().assert('array' === $.type(collection), 'The collection "' + collectionName + '" does not exist', 1477404490); + + collectionElementToMove = findCollectionElementByIdentifierPath(collectionElementToMoveIdentifier, collection); + utility().assert('object' === $.type(collectionElementToMove), 'Invalid parameter "collectionElementToMove"', 1477404484); + + removePropertyCollectionElementByIdentifier(formElement, collectionElementToMoveIdentifier, collectionName); + + referenceCollectionElementIndex = getIndexFromPropertyCollectionElementByIdentifier(referenceCollectionElementIdentifier, collectionName, formElement); + utility().assert(-1 < referenceCollectionElementIndex, 'Could not find collection element ' + referenceCollectionElementIdentifier + ' within collection ' + collectionName, 1477404489); + + if ('before' === position) { + referenceCollectionElement = collection[referenceCollectionElementIndex - 1]; + if (utility().isUndefinedOrNull(referenceCollectionElement)) { + referenceCollectionElementIdentifier = undefined; + } else { + referenceCollectionElementIdentifier = referenceCollectionElement['identifier']; + } + } + + addPropertyCollectionElement(collectionElementToMove, collectionName, formElement, referenceCollectionElementIdentifier, disablePublishersOnSet) + }; + + /** + * Publish the public methods. + */ + return { + getRootFormElement: getRootFormElement, + + getFormEditorDefinition: getFormEditorDefinition, + setFormEditorDefinitions: setFormEditorDefinitions, + + findFormElement: findFormElement, + findFormElementByIdentifierPath: findFormElementByIdentifierPath, + findEnclosingCompositeFormElementWhichIsNotOnTopLevel: findEnclosingCompositeFormElementWhichIsNotOnTopLevel, + findEnclosingCompositeFormElementWhichIsOnTopLevel: findEnclosingCompositeFormElementWhichIsOnTopLevel, + getIndexForEnclosingCompositeFormElementWhichIsOnTopLevelForFormElement: getIndexForEnclosingCompositeFormElementWhichIsOnTopLevelForFormElement, + + getNextFreeFormElementIdentifier: getNextFreeFormElementIdentifier, + isFormElementIdentifierUsed: isFormElementIdentifierUsed, + + addFormElement: addFormElement, + moveFormElement: moveFormElement, + removeFormElement: removeFormElement, + + findCollectionElementByIdentifierPath: findCollectionElementByIdentifierPath, + getIndexFromPropertyCollectionElementByIdentifier: getIndexFromPropertyCollectionElementByIdentifier, + addPropertyCollectionElement: addPropertyCollectionElement, + removePropertyCollectionElementByIdentifier: removePropertyCollectionElementByIdentifier, + movePropertyCollectionElement: movePropertyCollectionElement + }; + }; + + /** + * @public + * + * @return object + */ + function factory() { + + /** + * @public + * + * @param object configuration + * @param string identifierPathPrefix + * @param object parentFormElement + * @param boolean registerPropertyValidators + * @param boolean disablePublishersOnSet + * @return object + * @throws 1475375693 + * @throws 1475436040 + * @throws 1475604050 + */ + function createFormElement(configuration, identifierPathPrefix, parentFormElement, registerPropertyValidators, disablePublishersOnSet) { + var currentChildFormElements, collections, formElementTypeDefinition, identifierPath, rawChildFormElements, formElement; + utility().assert('object' === $.type(configuration), 'Invalid parameter "configuration"', 1475375693); + utility().assert(utility().isNonEmptyString(configuration['identifier']), '"identifier" must not be empty', 1475436040); + utility().assert(utility().isNonEmptyString(configuration['type']), '"type" must not be empty', 1475604050); + + registerPropertyValidators = !!registerPropertyValidators; + if (utility().isUndefinedOrNull(disablePublishersOnSet)) { + disablePublishersOnSet = true; + } + disablePublishersOnSet = !!disablePublishersOnSet; + + formElementTypeDefinition = repository().getFormEditorDefinition('formElements', configuration['type']); + rawChildFormElements = configuration['renderables']; + delete configuration['renderables']; + + collections = {}; + for (var collectionName in configuration) { + if (!configuration.hasOwnProperty(collectionName)) { + continue; + } + if (utility().isUndefinedOrNull(_repositoryFormEditorDefinitions[collectionName])) { + continue; + } + collections[collectionName] = configuration[collectionName]; + delete configuration[collectionName]; + } + + identifierPathPrefix = identifierPathPrefix || ''; + identifierPath = (identifierPathPrefix === '') ? configuration['identifier'] : identifierPathPrefix + '/' + configuration['identifier']; + + configuration = $.extend( + formElementTypeDefinition['predefinedDefaults'] || {}, + configuration, + { + renderables: (rawChildFormElements) ? true : null, + __parentRenderable: null, + __identifierPath: identifierPath + } + ); + + formElement = createModel(configuration); + formElement.set('__parentRenderable', parentFormElement || null, disablePublishersOnSet); + + for (var collectionName in collections) { + if (!collections.hasOwnProperty(collectionName)) { + continue; + } + + for (var i in collections[collectionName]) { + var previousCreatePropertyCollectionElementIdentifier, propertyCollectionElement; + if (!collections[collectionName].hasOwnProperty(i)) { + continue; + } + propertyCollectionElement = createPropertyCollectionElement( + collections[collectionName][i]['identifier'], + collections[collectionName][i], + collectionName + ); + if (i > 0) { + previousCreatePropertyCollectionElementIdentifier = collections[collectionName][i - 1]['identifier'] + } + repository().addPropertyCollectionElement(propertyCollectionElement, collectionName, formElement, previousCreatePropertyCollectionElementIdentifier, true); + } + } + + if (registerPropertyValidators) { + if ('array' === $.type(formElementTypeDefinition['editors'])) { + for (var i = 0, len1 = formElementTypeDefinition['editors'].length; i < len1; ++i) { + var configuration = {}; + + if ('array' !== $.type(formElementTypeDefinition['editors'][i]['propertyValidators'])) { + continue; + } + + if ( + !utility().isUndefinedOrNull(formElementTypeDefinition['editors'][i]['propertyValidatorsMode']) + && formElementTypeDefinition['editors'][i]['propertyValidatorsMode'] === 'OR' + ) { + configuration['propertyValidatorsMode'] = 'OR'; + } else { + configuration['propertyValidatorsMode'] = 'AND'; + } + + propertyValidationService().addValidatorIdentifiersToFormElementProperty( + formElement, + formElementTypeDefinition['editors'][i]['propertyValidators'], + formElementTypeDefinition['editors'][i]['propertyPath'], + undefined, + undefined, + configuration + ); + } + } + } + + if ('array' === $.type(rawChildFormElements)) { + currentChildFormElements = []; + for (var i = 0, len = rawChildFormElements.length; i < len; ++i) { + currentChildFormElements.push(createFormElement(rawChildFormElements[i], identifierPath, formElement, registerPropertyValidators, disablePublishersOnSet)); + } + formElement.set('renderables', currentChildFormElements, disablePublishersOnSet); + } + return formElement; + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param object collectionElementConfiguration + * @param string collectionName + * @return object + * @throws 1475377160 + * @throws 1475377161 + * @throws 1475377162 + */ + function createPropertyCollectionElement(collectionElementIdentifier, collectionElementConfiguration, collectionName) { + var collectionDefinition, collectionElementPresets; + utility().assert(utility().isNonEmptyString(collectionElementIdentifier), 'Invalid parameter "collectionElementIdentifier"', 1475377160); + utility().assert('object' === $.type(collectionElementConfiguration), 'Invalid parameter "collectionElementConfiguration"', 1475377161); + utility().assert(utility().isNonEmptyString(collectionName), 'Invalid parameter "collectionName"', 1475377162); + + collectionElementConfiguration['identifier'] = collectionElementIdentifier; + collectionDefinition = repository().getFormEditorDefinition(collectionName, collectionElementIdentifier); + if (collectionDefinition['predefinedDefaults']) { + collectionElementPresets = collectionDefinition['predefinedDefaults']; + } else { + collectionElementPresets = {}; + } + + return $.extend(collectionElementPresets, collectionElementConfiguration); + }; + + /** + * Publish the public methods. + */ + return { + createFormElement: createFormElement, + createPropertyCollectionElement: createPropertyCollectionElement + }; + }; + + /** + * @public + * + * @return object + */ + function dataBackend() { + + /** + * @public + * + * @param object endpoints + * @return void + * @throws 1475377488 + */ + function setEndpoints(endpoints) { + utility().assert('object' === $.type(endpoints), 'Invalid parameter "endpoints"', 1475377488); + _dataBackendEndpoints = endpoints; + }; + + /** + * @public + * + * @param string prototypeName + * @return void + * @throws 1475377489 + */ + function setPrototypeName(prototypeName) { + utility().assert(utility().isNonEmptyString(prototypeName), 'Invalid parameter "prototypeName"', 1475928095); + _dataBackendPrototypeName = prototypeName; + }; + + /** + * @public + * + * @param string persistenceIdentifier + * @return void + * @throws 1475377489 + */ + function setPersistenceIdentifier(persistenceIdentifier) { + utility().assert(utility().isNonEmptyString(persistenceIdentifier), 'Invalid parameter "persistenceIdentifier"', 1475377489); + _dataBackendPersistenceIdentifier = persistenceIdentifier; + }; + + /** + * @public + * + * @return void + * @publish core/ajax/saveFormDefinition/success + * @publish core/ajax/error + * @throws 1475520918 + */ + function saveFormDefinition() { + utility().assert(utility().isNonEmptyString(_dataBackendEndpoints['saveForm']), 'The endpoint "saveForm" is not configured', 1475520918); + + if (_runningAjaxRequests['saveForm']) { + _runningAjaxRequests['saveForm'].abort(); + } + + _runningAjaxRequests['saveForm'] = $.post(_dataBackendEndpoints['saveForm'], { + tx_form_web_formformbuilder: { + formPersistenceIdentifier: _dataBackendPersistenceIdentifier, + formDefinition: utility().convertToSimpleObject(getApplicationStateStack().getCurrentState('formDefinition')) + } + }, function(data, textStatus, jqXHR) { + if (_runningAjaxRequests['saveForm'] !== jqXHR) { + return; + } + _runningAjaxRequests['saveForm'] = null; + publisherSubscriber().publish('core/ajax/saveFormDefinition/success', [data]); + }).fail(function(jqXHR, textStatus, errorThrown) { + publisherSubscriber().publish('core/ajax/error', [jqXHR, textStatus, errorThrown]); + }); + }; + + /** + * @public + * + * @param int pageIndex + * @return void + * @publish core/ajax/renderFormDefinitionPage/success + * @publish core/ajax/error + * @throws 1473447677 + * @throws 1475377781 + * @throws 1475377782 + */ + function renderFormDefinitionPage(pageIndex) { + utility().assert($.isNumeric(pageIndex), 'Invalid parameter "pageIndex"', 1475377781); + utility().assert(utility().isNonEmptyString(_dataBackendEndpoints['formPageRenderer']), 'The endpoint "formPageRenderer" is not configured', 1473447677); + + if (_runningAjaxRequests['renderFormDefinitionPage']) { + _runningAjaxRequests['renderFormDefinitionPage'].abort(); + } + + _runningAjaxRequests['renderFormDefinitionPage'] = $.post(_dataBackendEndpoints['formPageRenderer'], { + tx_form_web_formformbuilder: { + formDefinition: utility().convertToSimpleObject(getApplicationStateStack().getCurrentState('formDefinition')), + pageIndex: pageIndex, + prototypeName: _dataBackendPrototypeName + } + }, function(data, textStatus, jqXHR) { + if (_runningAjaxRequests['renderFormDefinitionPage'] !== jqXHR) { + return; + } + _runningAjaxRequests['renderFormDefinitionPage'] = null; + publisherSubscriber().publish('core/ajax/renderFormDefinitionPage/success', [data, pageIndex]); + }).fail(function(jqXHR, textStatus, errorThrown) { + publisherSubscriber().publish('core/ajax/error', [jqXHR, textStatus, errorThrown]); + }); + }; + + /** + * Publish the public methods. + */ + return { + renderFormDefinitionPage: renderFormDefinitionPage, + saveFormDefinition: saveFormDefinition, + setEndpoints: setEndpoints, + setPersistenceIdentifier: setPersistenceIdentifier, + setPrototypeName: setPrototypeName + }; + }; + + /** + * @public + * + * @return object + */ + function getApplicationStateStack() { + + /** + * @public + * + * @param object applicationState + * @param bool disablePublishersOnSet + * @return void + * @publish core/applicationState/add + * @throws 1477847415 + */ + function add(applicationState, disablePublishersOnSet) { + utility().assert('object' === $.type(applicationState), 'Invalid parameter "applicationState"', 1477847415); + disablePublishersOnSet = !!disablePublishersOnSet; + + $.extend(applicationState, { + propertyValidationServiceRegisteredValidators: $.extend(true, {}, getCurrentState('propertyValidationServiceRegisteredValidators')) + }); + + _applicationStateStack.splice(0, 0, applicationState); + if (_applicationStateStack.length > _applicationStateStackSize) { + _applicationStateStack.splice(_applicationStateStackSize - 1, (_applicationStateStack.length - _applicationStateStackSize)); + } + + if (!disablePublishersOnSet) { + publisherSubscriber().publish('core/applicationState/add', [applicationState, getCurrentStackPointer(), getCurrentStackSize()]); + } + }; + + /** + * @public + * + * @param applicationState + * @param bool disablePublishersOnSet + * @return void + * @publish core/applicationState/add + * @throws 1477872641 + */ + function addAndReset(applicationState, disablePublishersOnSet) { + utility().assert('object' === $.type(applicationState), 'Invalid parameter "applicationState"', 1477872641); + + if (_applicationStateStackPointer > 0) { + _applicationStateStack.splice(0, _applicationStateStackPointer); + } + + _applicationStateStackPointer = 0; + add(applicationState, true); + + if (!disablePublishersOnSet) { + publisherSubscriber().publish('core/applicationState/add', [getCurrentState(), getCurrentStackPointer(), getCurrentStackSize()]); + } + }; + + /** + * @public + * + * @param string + * @return object + * @throws 1477932754 + */ + function getCurrentState(type) { + if (!utility().isUndefinedOrNull(type)) { + utility().assert( + 'formDefinition' === type + || 'currentlySelectedPageIndex' === type + || 'currentlySelectedFormElementIdentifierPath' === type + || 'propertyValidationServiceRegisteredValidators' === type, + + 'Invalid parameter "type"', 1477932754 + ); + + if ('undefined' === $.type(_applicationStateStack[_applicationStateStackPointer])) { + return undefined; + } + return _applicationStateStack[_applicationStateStackPointer][type]; + } + return _applicationStateStack[_applicationStateStackPointer]; + }; + + /** + * @public + * + * @param string + * @param mixed + * @return void + * @throws 1477934111 + */ + function setCurrentState(type, value) { + utility().assert( + 'formDefinition' === type + || 'currentlySelectedPageIndex' === type + || 'currentlySelectedFormElementIdentifierPath' === type + || 'propertyValidationServiceRegisteredValidators' === type, + + 'Invalid parameter "type"', 1477934111 + ); + _applicationStateStack[_applicationStateStackPointer][type] = value; + }; + + /** + * @public + * + * @param int + * @return void + * @throws 1477846933 + */ + function setMaximalStackSize(stackSize) { + utility().assert('number' === $.type(stackSize), 'Invalid parameter "size"', 1477846933); + _applicationStateStackSize = stackSize; + }; + + /** + * @public + * + * @return int + */ + function getMaximalStackSize() { + return _applicationStateStackSize; + }; + + /** + * @public + * + * @return int + */ + function getCurrentStackSize() { + return _applicationStateStack.length; + }; + + /** + * @public + * + * @return object + */ + function getCurrentStackPointer() { + return _applicationStateStackPointer; + }; + + /** + * @public + * + * @param int + * @return void + * @throws 1477852138 + */ + function setCurrentStackPointer(stackPointer) { + utility().assert('number' === $.type(stackPointer), 'Invalid parameter "size"', 1477852138); + if (stackPointer < 0) { + _applicationStateStackPointer = 0; + } else if (stackPointer > _applicationStateStack.length - 1) { + _applicationStateStackPointer = _applicationStateStack.length - 1; + } else { + _applicationStateStackPointer = stackPointer; + } + }; + + + /** + * @public + * + * @return void + */ + function decrementCurrentStackPointer() { + setCurrentStackPointer(--_applicationStateStackPointer); + }; + + /** + * @public + * + * @return void + */ + function incrementCurrentStackPointer() { + setCurrentStackPointer(++_applicationStateStackPointer); + }; + + /** + * Publish the public methods. + */ + return { + add: add, + addAndReset: addAndReset, + getCurrentState: getCurrentState, + setCurrentState: setCurrentState, + getCurrentStackPointer: getCurrentStackPointer, + setCurrentStackPointer: setCurrentStackPointer, + decrementCurrentStackPointer: decrementCurrentStackPointer, + incrementCurrentStackPointer: incrementCurrentStackPointer, + setMaximalStackSize: setMaximalStackSize, + getMaximalStackSize: getMaximalStackSize, + getCurrentStackSize: getCurrentStackSize + }; + }; + + /** + * Publish the public methods. + * Implements the "Revealing Module Pattern". + */ + return { + getDataBackend: dataBackend, + getFactory: factory, + getPublisherSubscriber: publisherSubscriber, + getRepository: repository, + getUtility: utility, + getPropertyValidationService: propertyValidationService, + getRunningAjaxRequest: getRunningAjaxRequest, + getApplicationStateStack: getApplicationStateStack + }; + })($); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Helper.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Helper.js new file mode 100644 index 000000000000..c0969bd6b373 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Helper.js @@ -0,0 +1,310 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormEditor/Helper + */ +define(['jquery'], function($) { + 'use strict'; + + return (function($) { + + /** + * @private + * + * @var object + */ + var _formEditorApp = null; + + /** + * @private + * + * @var object + */ + var _configuration = {}; + + /** + * @private + * + * @var object + */ + var _defaultConfiguration = { + domElementClassNames: { + active: 'active', + buttonCollectionElementRemove: 't3-form-collection-element-remove-button', + buttonFormEditor: 't3-form-button', + disabled: 'disabled', + hidden: 'hidden', + icon: 't3-form-icon', + jQueryUiStateDisabled: 'ui-state-disabled', + sortableHover: 'sortable-hover' + }, + domElementDataAttributeNames: { + elementIdentifier: 'data-element-identifier-path', + identifier: 'data-identifier', + template: 'data-template-name', + templateProperty: 'data-template-property' + }, + domElementSelectorPattern: { + bracesWithKey: '[{0}]', + bracesWithKeyValue: '[{0}="{1}"]', + class: '.{0}', + id: '#{0}', + keyValue: '{0}="{1}"' + } + }; + + /* ************************************************************* + * Private Methodes + * ************************************************************/ + + /** + * @private + * + * @return object + */ + function getFormEditorApp() { + return _formEditorApp; + }; + + /** + * @private + * + * @return object + */ + function getUtility() { + return getFormEditorApp().getUtility(); + }; + + /** + * @private + * + * @param mixed test + * @param string message + * @param int messageCode + * @return void + */ + function assert(test, message, messageCode) { + return getFormEditorApp().assert(test, message, messageCode); + }; + + /* ************************************************************* + * Public Methodes + * ************************************************************/ + + /** + * @public + * + * @param object + * @return this + * @throws 1478950623 + */ + function setConfiguration(configuration) { + assert('object' === $.type(configuration), 'Invalid parameter "configuration"', 1478950623); + _configuration = $.extend(true, _defaultConfiguration, configuration); + return this; + }; + + /** + * @public + * + * @param string + * @param array + * @return string + * @throws 1478801251 + * @throws 1478801252 + */ + function buildDomElementSelectorHelper(patternIdentifier, replacements) { + var newString; + assert( + !getUtility().isUndefinedOrNull(_configuration['domElementSelectorPattern'][patternIdentifier]), + 'Invalid parameter "patternIdentifier" (' + patternIdentifier + ')', + 1478801251 + ); + assert('array' === $.type(replacements), 'Invalid parameter "replacements"', 1478801252); + + newString = _configuration['domElementSelectorPattern'][patternIdentifier]; + for (var i = 0, len = replacements.length; i < len; ++i) { + newString = newString.replace('{' + i + '}', replacements[i]); + } + return newString; + }; + + /** + * @public + * + * @param string + * @param array + * @return string + * @throws 1478372374 + */ + function getDomElementSelector(selectorIdentifier, args) { + assert( + !getUtility().isUndefinedOrNull(_configuration['domElementSelectorPattern'][selectorIdentifier]), + 'Invalid parameter "selectorIdentifier" (' + selectorIdentifier + ')', + 1478372374 + ); + return buildDomElementSelectorHelper(selectorIdentifier, args); + }; + + /** + * @public + * + * @param string + * @param bool + * @return string + * @throws 1478803906 + */ + function getDomElementClassName(classNameIdentifier, asSelector) { + var className; + assert( + !getUtility().isUndefinedOrNull(_configuration['domElementClassNames'][classNameIdentifier]), + 'Invalid parameter "classNameIdentifier" (' + classNameIdentifier + ')', + 1478803906 + ); + + className = _configuration['domElementClassNames'][classNameIdentifier]; + if (!!asSelector) { + className = getDomElementSelector('class', [className]); + } + return className; + }; + + /** + * @public + * + * @param string + * @param bool + * @return string + * @throws 1479251518 + */ + function getDomElementIdName(idNameIdentifier, asSelector) { + var idName; + assert( + !getUtility().isUndefinedOrNull(_configuration['domElementIdNames'][idNameIdentifier]), + 'Invalid parameter "domElementIdNames" (' + idNameIdentifier + ')', + 1479251518 + ); + + idName = _configuration['domElementIdNames'][idNameIdentifier]; + if (!!asSelector) { + idName = getDomElementSelector('id', [idName]); + } + return idName; + }; + + /** + * @public + * + * @param string + * @param bool + * @return string + * @throws 1478806884 + */ + function getDomElementDataAttributeValue(dataAttributeValueIdentifier) { + assert( + !getUtility().isUndefinedOrNull(_configuration['domElementDataAttributeValues'][dataAttributeValueIdentifier]), + 'Invalid parameter "dataAttributeValueIdentifier" (' + dataAttributeValueIdentifier + ')', + 1478806884 + ); + return _configuration['domElementDataAttributeValues'][dataAttributeValueIdentifier]; + }; + + /** + * @public + * + * @param string + * @param string + * @param array + * @return string + * @throws 1478808035 + */ + function getDomElementDataAttribute(dataAttributeIdentifier, selectorIdentifier, additionalSelectorArgs) { + assert( + !getUtility().isUndefinedOrNull(_configuration['domElementDataAttributeNames'][dataAttributeIdentifier]), + 'Invalid parameter "dataAttributeIdentifier" (' + dataAttributeIdentifier + ')', + 1478808035 + ); + + if (getUtility().isUndefinedOrNull(selectorIdentifier)) { + return _configuration['domElementDataAttributeNames'][dataAttributeIdentifier]; + } + + additionalSelectorArgs = additionalSelectorArgs || []; + return getDomElementSelector( + selectorIdentifier, + [_configuration['domElementDataAttributeNames'][dataAttributeIdentifier]].concat(additionalSelectorArgs) + ); + }; + + /** + * @public + * + * Return a string like [data-identifier="someValue"] + * + * @return string + */ + function getDomElementDataIdentifierSelector(dataAttributeValueIdentifier) { + return getDomElementDataAttribute('identifier', 'bracesWithKeyValue', [getDomElementDataAttributeValue(dataAttributeValueIdentifier)]); + }; + + /** + * @public + * + * @param string + * @return object + */ + function getTemplate(templateName) { + return $(getDomElementDataAttribute('template', 'bracesWithKeyValue', [getDomElementDataAttributeValue(templateName)])); + }; + + /** + * @public + * + * @param string + * @param object + * @return object + */ + function getTemplatePropertyDomElement(templatePropertyName, templateDomElement) { + return $(getDomElementDataAttribute('templateProperty', 'bracesWithKeyValue', [templatePropertyName]), $(templateDomElement)); + }; + + /** + * @public + * + * @param object formEditorApp + * @return void + */ + function bootstrap(formEditorApp) { + _formEditorApp = formEditorApp; + }; + + /** + * Publish the public methods. + * Implements the "Revealing Module Pattern". + */ + return { + bootstrap: bootstrap, + buildDomElementSelectorHelper: buildDomElementSelectorHelper, + getDomElementClassName: getDomElementClassName, + getDomElementIdName: getDomElementIdName, + getDomElementDataAttribute: getDomElementDataAttribute, + getDomElementDataAttributeValue: getDomElementDataAttributeValue, + getDomElementDataIdentifierSelector: getDomElementDataIdentifierSelector, + getDomElementSelector: getDomElementSelector, + getTemplate: getTemplate, + getTemplatePropertyDomElement: getTemplatePropertyDomElement, + setConfiguration: setConfiguration + }; + })($); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/InspectorComponent.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/InspectorComponent.js new file mode 100644 index 000000000000..ae0855e02bd8 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/InspectorComponent.js @@ -0,0 +1,1777 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormEditor/InspectorComponent + */ + +/** + * Add legacy functions to be accessible in the global scope. + * This is needed by TYPO3/CMS/Recordlist/ElementBrowser + */ +var setFormValueFromBrowseWin; + +define(['jquery', + 'TYPO3/CMS/Form/Backend/FormEditor/Helper', + 'TYPO3/CMS/Backend/Icons', + 'TYPO3/CMS/Backend/Notification', + 'TYPO3/CMS/Form/Backend/Vendor/jquery.mjs.nestedSortable' + ], function($, Helper, Icons, Notification) { + 'use strict'; + + return (function($, Helper, Icons, Notification) { + + /** + * @private + * + * @var object + */ + var _configuration = null; + + /** + * @private + * + * @var object + */ + var _defaultConfiguration = { + domElementClassNames: { + buttonFormElementRemove: 't3-form-remove-element-button', + collectionElement: 't3-form-collection-element', + finisherEditorPrefix: 't3-form-inspector-finishers-editor-', + inspectorEditor: 'form-editor', + validatorEditorPrefix: 't3-form-inspector-validators-editor-' + }, + domElementDataAttributeNames: { + contentElementSelectorTarget: 'data-insert-target', + finisher: 'data-finisher-identifier', + validator: 'data-validator-identifier' + }, + domElementDataAttributeValues: { + collapse: 'actions-view-table-expand', + editorControlsWrapper: 'inspectorEditorControlsWrapper', + formElementHeaderEditor: 'inspectorFormElementHeaderEditor', + iconPage: 'apps-pagetree-page-default', + iconTtContent: 'mimetypes-x-content-text', + inspector: 'inspector', + 'Inspector-CheckboxEditor': 'Inspector-CheckboxEditor', + 'Inspector-CollectionElementHeaderEditor': 'Inspector-CollectionElementHeaderEditor', + 'Inspector-FinishersEditor': 'Inspector-FinishersEditor', + 'Inspector-FormElementHeaderEditor': 'Inspector-FormElementHeaderEditor', + 'Inspector-PropertyGridEditor': 'Inspector-PropertyGridEditor', + 'Inspector-RemoveElementEditor': 'Inspector-RemoveElementEditor', + 'Inspector-RequiredValidatorEditor': 'Inspector-RequiredValidatorEditor', + 'Inspector-SingleSelectEditor': 'Inspector-SingleSelectEditor', + 'Inspector-TextareaEditor': 'Inspector-TextareaEditor', + 'Inspector-TextEditor': 'Inspector-TextEditor', + 'Inspector-Typo3WinBrowserEditor': 'Inspector-Typo3WinBrowserEditor', + 'Inspector-ValidatorsEditor': 'Inspector-ValidatorsEditor', + inspectorFinishers: 'inspectorFinishers', + inspectorValidators: 'inspectorValidators', + propertyGridEditorAddRow: 'addRow', + propertyGridEditorAddRowItem: 'addRowItem', + propertyGridEditorContainer: 'propertyGridContainer', + propertyGridEditorDeleteRow: 'deleteRow', + propertyGridEditorLabel: 'label', + propertyGridEditorRowItem: 'rowItem', + propertyGridEditorSelectValue: 'selectValue', + propertyGridEditorSortRow: 'sortRow', + propertyGridEditorValue: 'value' + }, + domElementIdNames: { + finisherPrefix: 't3-form-inspector-finishers-', + validatorPrefix: 't3-form-inspector-validators-' + }, + isSortable: true + }; + + /** + * @private + * + * @var object + */ + var _formEditorApp = null; + + /* ************************************************************* + * Private Methodes + * ************************************************************/ + + /** + * @private + * + * @return void + * @throws 1478268638 + */ + function _helperSetup() { + assert('function' === $.type(Helper.bootstrap), + 'The view model helper does not implement the method "bootstrap"', + 1478268638 + ); + Helper.bootstrap(getFormEditorApp()); + }; + + /** + * @private + * + * @return object + */ + function getFormEditorApp() { + return _formEditorApp; + }; + + /** + * @private + * + * @return object + */ + function getViewModel() { + return getFormEditorApp().getViewModel(); + }; + + /** + * @private + * + * @param object + * @return object + */ + function getHelper(configuration) { + if (getUtility().isUndefinedOrNull(configuration)) { + return Helper.setConfiguration(_configuration); + } + return Helper.setConfiguration(configuration); + }; + + /** + * @private + * + * @return object + */ + function getUtility() { + return getFormEditorApp().getUtility(); + }; + + /** + * @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 object + */ + function getCurrentlySelectedFormElement() { + return getFormEditorApp().getCurrentlySelectedFormElement(); + }; + + /** + * @private + * + * @return object + */ + function getRootFormElement() { + return getFormEditorApp().getRootFormElement(); + }; + + /** + * @private + * + * @return object + */ + function getPublisherSubscriber() { + return getFormEditorApp().getPublisherSubscriber(); + }; + + /** + * @private + * + * @param object + * @param string + * @return mixed + */ + function getFormElementDefinition(formElement, formElementDefinitionKey) { + return getFormEditorApp().getFormElementDefinition(formElement, formElementDefinitionKey); + }; + + /** + * @private + * + * @param object + * @param object + * @param string + * @param string + * @return void + * @publish view/inspector/editor/insert/perform + */ + function _renderEditorDispatcher(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + switch (editorConfiguration['templateName']) { + case 'Inspector-FormElementHeaderEditor': + renderFormElementHeaderEditor( + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-CollectionElementHeaderEditor': + renderCollectionElementHeaderEditor( + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-TextEditor': + renderTextEditor( + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-FinishersEditor': + renderCollectionElementSelectionEditor( + 'finishers', + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-ValidatorsEditor': + renderCollectionElementSelectionEditor( + 'validators', + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-RemoveElementEditor': + renderRemoveElementEditor( + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-RequiredValidatorEditor': + renderRequiredValidatorEditor( + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-CheckboxEditor': + renderCheckboxEditor( + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-SingleSelectEditor': + renderSingleSelectEditor( + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-PropertyGridEditor': + renderPropertyGridEditor( + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-TextareaEditor': + renderTextareaEditor( + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + case 'Inspector-Typo3WinBrowserEditor': + renderTypo3WinBrowserEditor( + editorConfiguration, + editorHtml, + collectionElementIdentifier, + collectionName + ); + break; + } + getPublisherSubscriber().publish('view/inspector/editor/insert/perform', [ + editorConfiguration, editorHtml, collectionElementIdentifier, collectionName + ]); + }; + + /** + * @private + * + * opens a popup window with the element browser + * + * @param string mode + * @param string params + * @param int width + * @param int height + */ + function _openTypo3WinBrowser(mode, params, width, height) { + var openedPopupWindow, url; + url = TYPO3.settings.FormEditor.typo3WinBrowserUrl + + '&mode=' + mode + '&bparams=' + params; + openedPopupWindow = window.open( + url, + 'Typo3WinBrowser', + 'height=' + height + ',width=' + width + ',status=0,menubar=0,resizable=1,scrollbars=1' + ); + openedPopupWindow.focus(); + }; + + /** + * @private + * + * @param string + * @param string + * @return object + */ + function _getCollectionElementClass(collectionName, collectionElementIdentifier) { + if (collectionName === 'finishers') { + return getHelper() + .getDomElementClassName('finisherEditorPrefix') + collectionElementIdentifier; + } else { + return getHelper() + .getDomElementClassName('validatorEditorPrefix') + collectionElementIdentifier; + } + }; + + /** + * @private + * + * @param string + * @param string + * @param bool + * @return object + */ + function _getCollectionElementId(collectionName, collectionElementIdentifier, asSelector) { + if (collectionName === 'finishers') { + return getHelper() + .getDomElementIdName('finisherPrefix', asSelector) + collectionElementIdentifier; + } else { + return getHelper() + .getDomElementIdName('validatorPrefix', asSelector) + collectionElementIdentifier; + } + }; + + /** + * @private + * + * @param object + * @param string + * @return void + */ + function _addSortableCollectionElementsEvents(sortableDomElement, collectionName) { + sortableDomElement.addClass(getHelper().getDomElementClassName('sortable')).sortable({ + revert: 'true', + items: getHelper().getDomElementClassName('collectionElement', true), + cancel: getHelper().getDomElementClassName('jQueryUiStateDisabled', true) + ',input,select', + delay: 200, + update: function(e, o) { + var dataAttributeName, nextCollectionElementIdentifier, movedCollectionElementIdentifier, previousCollectionElementIdentifier; + + if (collectionName === 'finishers') { + dataAttributeName = getHelper().getDomElementDataAttribute('finisher'); + } else { + dataAttributeName = getHelper().getDomElementDataAttribute('validator'); + } + + movedCollectionElementIdentifier = $(o.item).attr(dataAttributeName); + previousCollectionElementIdentifier = $(o.item) + .prevAll(getHelper().getDomElementClassName('collectionElement', true)) + .first() + .attr(dataAttributeName); + nextCollectionElementIdentifier = $(o.item) + .nextAll(getHelper().getDomElementClassName('collectionElement', true)) + .first() + .attr(dataAttributeName); + + getPublisherSubscriber().publish('view/inspector/collectionElements/dnd/update', [ + movedCollectionElementIdentifier, + previousCollectionElementIdentifier, + nextCollectionElementIdentifier, + collectionName + ]); + } + }); + }; + + /** + * @private + * + * @param object editorHtml + * @param bool multiSelection + * @param string propertyPath + * @param string propertyPathPrefix + * @return void + */ + function _setPropertyGridData(editorHtml, multiSelection, propertyPath, propertyPathPrefix) { + var defaultValue, newPropertyData; + + if (multiSelection) { + defaultValue = []; + + $( getHelper().getDomElementDataIdentifierSelector('propertyGridEditorContainer') + ' ' + + getHelper().getDomElementDataIdentifierSelector('propertyGridEditorSelectValue') + ':checked', + $(editorHtml) + ).each(function(i) { + defaultValue.push( + $(this) + .closest(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorRowItem')) + .find(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorValue')) + .val() + ); + }); + getCurrentlySelectedFormElement().set(propertyPathPrefix + 'defaultValue', defaultValue); + } else { + getCurrentlySelectedFormElement().set( + propertyPathPrefix + 'defaultValue', + $( + getHelper().getDomElementDataIdentifierSelector('propertyGridEditorContainer') + ' ' + + getHelper().getDomElementDataIdentifierSelector('propertyGridEditorSelectValue') + ':checked', + $(editorHtml) + ).first() + .closest(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorRowItem')) + .find(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorValue')) + .val(), + true + ); + } + + newPropertyData = []; + $( + getHelper().getDomElementDataIdentifierSelector('propertyGridEditorContainer') + ' ' + + getHelper().getDomElementDataIdentifierSelector('propertyGridEditorRowItem'), + $(editorHtml) + ).each(function(i) { + var value, label, tmpObject; + + value = $(this) + .find(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorValue')) + .val(); + label = $(this) + .find(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorLabel')) + .val(); + + if ('' === value) { + value = label; + } + + tmpObject = {}; + tmpObject[value] = label; + newPropertyData.push({ + _label: label, + _value: value + }); + }); + + getCurrentlySelectedFormElement().set(propertyPathPrefix + propertyPath, newPropertyData); + }; + + /** + * @private + * + * @param object + * @return object + */ + function _getEditorControlsWrapperDomElement(editorDomElement) { + return $(getHelper().getDomElementDataIdentifierSelector('editorControlsWrapper'), $(editorDomElement)); + }; + + /** + * @private + * + * @param string + * @param object + * @return void + */ + function _validateCollectionElement(propertyPath, editorHtml) { + var hasError, propertyPrefix, validationResults; + + validationResults = getFormEditorApp().validateCurrentlySelectedFormElementProperty(propertyPath); + + if (validationResults.length > 0) { + getHelper() + .getTemplatePropertyDomElement('validationErrors', editorHtml) + .text(validationResults[0]); + getViewModel().setElementValidationErrorClass( + getHelper().getTemplatePropertyDomElement('validationErrors', editorHtml) + ); + getViewModel().setElementValidationErrorClass( + _getEditorControlsWrapperDomElement(editorHtml), + 'hasError' + ); + } else { + getHelper().getTemplatePropertyDomElement('validationErrors', editorHtml).text(''); + getViewModel().removeElementValidationErrorClass( + getHelper().getTemplatePropertyDomElement('validationErrors', editorHtml) + ); + getViewModel().removeElementValidationErrorClass( + _getEditorControlsWrapperDomElement(editorHtml), + 'hasError' + ); + } + + validationResults = getFormEditorApp().validateFormElement(getCurrentlySelectedFormElement()); + propertyPrefix = propertyPath.split('.'); + propertyPrefix = propertyPrefix[0] + '.' + propertyPrefix[1]; + + hasError = false; + for (var i = 0, len = validationResults.length; i < len; ++i) { + if ( + validationResults[i]['propertyPath'].indexOf(propertyPrefix, 0) === 0 + && validationResults[i]['validationResults'] + && validationResults[i]['validationResults'].length > 0 + ) { + hasError = true; + break; + } + } + + if (hasError) { + getViewModel().setElementValidationErrorClass( + _getEditorControlsWrapperDomElement(editorHtml).closest(getHelper().getDomElementClassName('collectionElement', true)) + ); + } else { + getViewModel().removeElementValidationErrorClass( + _getEditorControlsWrapperDomElement(editorHtml).closest(getHelper().getDomElementClassName('collectionElement', true)) + ); + } + }; + + /* ************************************************************* + * Public Methodes + * ************************************************************/ + + /** + * @public + * + * callback from TYPO3/CMS/Recordlist/ElementBrowser + * + * @param string fieldReference + * @param string elValue + * @param string elName + * @return void + */ + setFormValueFromBrowseWin = function(fieldReference, elValue, elName) { + var result; + result = elValue.split('_'); + + $(getHelper().getDomElementDataAttribute('contentElementSelectorTarget', 'bracesWithKeyValue', [fieldReference])) + .val(result.pop()) + .trigger('paste'); + }; + + /** + * @public + * + * @return object + */ + function getInspectorDomElement() { + return $(getHelper().getDomElementDataIdentifierSelector('inspector')); + }; + + /** + * @public + * + * @return object + */ + function getFinishersContainerDomElement() { + return $(getHelper().getDomElementDataIdentifierSelector('inspectorFinishers'), getInspectorDomElement()); + }; + + /** + * @public + * + * @return object + */ + function getValidatorsContainerDomElement() { + return $(getHelper().getDomElementDataIdentifierSelector('inspectorValidators'), getInspectorDomElement()); + }; + + /** + * @public + * + * @param string + * @param string + * @return object + */ + function getCollectionElementDomElement(collectionName, collectionElementIdentifier) { + if (collectionName === 'finishers') { + return $(getHelper().getDomElementDataAttribute( + 'finisher', + 'bracesWithKeyValue', + [collectionElementIdentifier] + ), getFinishersContainerDomElement()); + } else { + return $(getHelper().getDomElementDataAttribute( + 'validator', + 'bracesWithKeyValue', + [collectionElementIdentifier] + ), getValidatorsContainerDomElement()); + } + }; + + /** + * @public + * + * @param object + * @param function + * @return void + */ + function renderEditors(formElement, callback) { + var formElementTypeDefinition; + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + + getInspectorDomElement().off().empty(); + + formElementTypeDefinition = getFormElementDefinition(formElement); + if ('array' !== $.type(formElementTypeDefinition['editors'])) { + return; + } + + for (var i = 0, len = formElementTypeDefinition['editors'].length; i < len; ++i) { + var html, template; + + template = getHelper() + .getTemplate(formElementTypeDefinition['editors'][i]['templateName']) + .clone(); + if (!template.length) { + continue; + } + html = $(template.html()); + + $(html) + .first() + .addClass(getHelper().getDomElementClassName('inspectorEditor')); + getInspectorDomElement().append($(html)); + + _renderEditorDispatcher(formElementTypeDefinition['editors'][i], html); + } + + if ('function' === $.type(callback)) { + callback(); + } + }; + + /** + * @public + * + * @param string collectionName + * @param string collectionElementIdentifier + * @return void + * @publish view/inspector/collectionElements/dnd/update + * @throws 1478354853 + * @throws 1478354854 + */ + function renderCollectionElementEditors(collectionName, collectionElementIdentifier) { + var collapseWrapper, collectionContainer, collectionContainerElementWrapper, collectionElementConfiguration, collectionElementEditorsLength; + + assert( + getUtility().isNonEmptyString(collectionName), + 'Invalid parameter "collectionName"', + 1478354853 + ); + assert( + getUtility().isNonEmptyString(collectionElementIdentifier), + 'Invalid parameter "collectionElementIdentifier"', + 1478354854 + ); + + collectionElementConfiguration = getFormEditorApp().getPropertyCollectionElementConfiguration( + collectionElementIdentifier, + collectionName + ); + if ('array' !== $.type(collectionElementConfiguration['editors'])) { + return; + } + + collectionContainerElementWrapper = $('<div></div>'). + addClass(getHelper().getDomElementClassName('collectionElement')); + if (collectionName === 'finishers') { + collectionContainer = getFinishersContainerDomElement(); + collectionContainerElementWrapper + .attr(getHelper().getDomElementDataAttribute('finisher'), collectionElementIdentifier); + } else { + collectionContainer = getValidatorsContainerDomElement(); + collectionContainerElementWrapper + .attr(getHelper().getDomElementDataAttribute('validator'), collectionElementIdentifier); + } + collectionContainer.append(collectionContainerElementWrapper); + + collectionElementEditorsLength = collectionElementConfiguration['editors'].length; + if ( + collectionElementEditorsLength > 0 + && collectionElementConfiguration['editors'][0]['identifier'] === 'header' + ) { + collapseWrapper = $('<div role="tabpanel"></div>') + .addClass('panel-collapse collapse') + .prop('id', _getCollectionElementId( + collectionName, + collectionElementIdentifier + )); + } + + for (var i = 0; i < collectionElementEditorsLength; ++i) { + var html, template; + + template = getHelper() + .getTemplate(collectionElementConfiguration['editors'][i]['templateName']) + .clone(); + if (!template.length) { + continue; + } + html = $(template.html()); + + $(html).first() + .addClass(_getCollectionElementClass( + collectionName, + collectionElementConfiguration['editors'][i]['identifier'] + )) + .addClass(getHelper().getDomElementClassName('inspectorEditor')); + + if (i === 0 && collapseWrapper) { + getCollectionElementDomElement(collectionName, collectionElementIdentifier) + .append(html) + .append(collapseWrapper); + } else if ( + i === (collectionElementEditorsLength - 1) + && collapseWrapper + && collectionElementConfiguration['editors'][i]['identifier'] === 'removeButton' + ) { + getCollectionElementDomElement(collectionName, collectionElementIdentifier).append(html); + } else if (i > 0 && collapseWrapper) { + collapseWrapper.append(html); + } else { + getCollectionElementDomElement(collectionName, collectionElementIdentifier).append(html); + } + + _renderEditorDispatcher( + collectionElementConfiguration['editors'][i], + html, + collectionElementIdentifier, + collectionName + ); + } + + if ( + collectionElementEditorsLength === 2 + && collectionElementConfiguration['editors'][0]['identifier'] === 'header' + && collectionElementConfiguration['editors'][1]['identifier'] === 'removeButton' + ) { + $(getHelper().getDomElementDataIdentifierSelector('collapse'), collectionContainerElementWrapper).remove(); + } + + if (_configuration['isSortable']) { + _addSortableCollectionElementsEvents(collectionContainer, collectionName); + } + }; + + /** + * @public + * + * @string collectionName + * @param object editorConfiguration + * @param object editorHtml + * @return void + * @publish view/inspector/collectionElement/existing/selected + * @publish view/inspector/collectionElement/new/selected + * @throws 1475423098 + * @throws 1475423099 + * @throws 1475423100 + * @throws 1475423101 + * @throws 1478362968 + */ + function renderCollectionElementSelectionEditor(collectionName, editorConfiguration, editorHtml) { + var alreadySelectedCollectionElements, selectElement, collectionContainer; + assert( + getUtility().isNonEmptyString(collectionName), + 'Invalid configuration "collectionName"', + 1478362968 + ); + assert( + 'object' === $.type(editorConfiguration), + 'Invalid parameter "editorConfiguration"', + 1475423098 + ); + assert( + 'object' === $.type(editorHtml), + 'Invalid parameter "editorHtml"', + 1475423099 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['label']), + 'Invalid configuration "label"', + 1475423100 + ); + assert( + 'array' === $.type(editorConfiguration['selectOptions']), + 'Invalid configuration "selectOptions"', + 1475423101 + ); + + if (collectionName === 'finishers') { + collectionContainer = getFinishersContainerDomElement(); + alreadySelectedCollectionElements = getRootFormElement().get(collectionName); + } else { + collectionContainer = getValidatorsContainerDomElement(); + alreadySelectedCollectionElements = getCurrentlySelectedFormElement().get(collectionName); + } + + collectionContainer.off().empty(); + + getHelper().getTemplatePropertyDomElement('label', editorHtml).text(editorConfiguration['label']); + selectElement = getHelper().getTemplatePropertyDomElement('selectOptions', editorHtml); + + if (!getUtility().isUndefinedOrNull(alreadySelectedCollectionElements)) { + for (var i = 0, len = alreadySelectedCollectionElements.length; i < len; ++i) { + getPublisherSubscriber().publish('view/inspector/collectionElement/existing/selected', [ + alreadySelectedCollectionElements[i]['identifier'], + collectionName + ]); + } + } + + for (var i = 0, len1 = editorConfiguration['selectOptions'].length; i < len1; ++i) { + var appendOption = true; + if (!getUtility().isUndefinedOrNull(alreadySelectedCollectionElements)) { + for (var j = 0, len2 = alreadySelectedCollectionElements.length; j < len2; ++j) { + if (alreadySelectedCollectionElements[j]['identifier'] === editorConfiguration['selectOptions'][i]['value']) { + appendOption = false; + break; + } + } + } + if (appendOption) { + selectElement.append(new Option( + editorConfiguration['selectOptions'][i]['label'], + editorConfiguration['selectOptions'][i]['value'] + )); + } + } + + selectElement.on('change', function() { + if ($(this).val() !== '') { + var value = $(this).val(); + $('option[value="' + value + '"]', $(this)).remove(); + + getFormEditorApp().getPublisherSubscriber().publish( + 'view/inspector/collectionElement/new/selected', + [value, collectionName] + ); + } + }); + }; + + /** + * @public + * + * @param object editorConfiguration + * @param object editorHtml + * @param string collectionElementIdentifier + * @param string collectionName + * @return void + * @throws 1475421525 + * @throws 1475421526 + * @throws 1475421527 + * @throws 1475421528 + */ + function renderFormElementHeaderEditor(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + assert('object' === $.type(editorConfiguration), 'Invalid parameter "editorConfiguration"', 1475421525); + assert('object' === $.type(editorHtml), 'Invalid parameter "editorHtml"', 1475421526); + + Icons.getIcon( + getFormElementDefinition(getCurrentlySelectedFormElement(), 'iconIdentifier'), + Icons.sizes.small, + null, + Icons.states.default + ).done(function(icon) { + getHelper().getTemplatePropertyDomElement('header-label', editorHtml) + .append($(icon).addClass(getHelper().getDomElementClassName('icon'))) + .append(buildTitleByFormElement()); + }); + }; + + /** + * @public + * + * @param object editorConfiguration + * @param object editorHtml + * @param string collectionElementIdentifier + * @param string collectionName + * @return void + * @throws 1475421257 + * @throws 1475421258 + * @throws 1475421259 + */ + function renderCollectionElementHeaderEditor(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + var collectionElementConfiguration, setData; + + assert( + 'object' === $.type(editorConfiguration), + 'Invalid parameter "editorConfiguration"', + 1475421258 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['label']), + 'Invalid configuration "label"', + 1475421257 + ); + assert( + 'object' === $.type(editorHtml), + 'Invalid parameter "editorHtml"', + 1475421259 + ); + + setData = function(icon) { + getHelper() + .getTemplatePropertyDomElement('header-label', editorHtml) + .prepend($(icon)); + + Icons.getIcon( + getHelper().getDomElementDataAttributeValue('collapse'), + Icons.sizes.small, + null, + Icons.states.default, + Icons.markupIdentifiers.inline + ).done(function(icon) { + var iconWrap; + iconWrap = $('<a></a>') + .attr('href', _getCollectionElementId(collectionName, collectionElementIdentifier, true)) + .attr('data-toggle', 'collapse') + .attr('aria-expanded', 'true') + .attr('aria-controls', _getCollectionElementId(collectionName, collectionElementIdentifier)) + .addClass('collapsed') + .append($(icon)); + + getHelper() + .getTemplatePropertyDomElement('header-label', editorHtml) + .prepend(iconWrap); + }); + }; + + collectionElementConfiguration = getFormEditorApp().getFormEditorDefinition(collectionName, collectionElementIdentifier); + if (collectionName === 'validators') { + Icons.getIcon( + collectionElementConfiguration['iconIdentifier'], + Icons.sizes.small, + null, + Icons.states.default + ).done(function(icon) { + setData(icon); + }); + } else { + Icons.getIcon( + collectionElementConfiguration['iconIdentifier'], + Icons.sizes.small, + null, + Icons.states.default + ).done(function(icon) { + setData(icon); + }); + } + + if (editorConfiguration['label']) { + getHelper().getTemplatePropertyDomElement('label', editorHtml).append(editorConfiguration['label']); + } + }; + + /** + * @public + * + * @param object editorConfiguration + * @param object editorHtml + * @param string collectionElementIdentifier + * @param string collectionName + * @return void + * @throws 1475421053 + * @throws 1475421054 + * @throws 1475421055 + * @throws 1475421056 + */ + function renderTextEditor(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + var propertyPath, propertyData; + assert( + 'object' === $.type(editorConfiguration), + 'Invalid parameter "editorConfiguration"', + 1475421053 + ); + assert( + 'object' === $.type(editorHtml), + 'Invalid parameter "editorHtml"', + 1475421054 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['label']), + 'Invalid configuration "label"', + 1475421055 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['propertyPath']), + 'Invalid configuration "propertyPath"', + 1475421056 + ); + + getHelper() + .getTemplatePropertyDomElement('label', editorHtml) + .append(editorConfiguration['label']); + if (getUtility().isNonEmptyString(editorConfiguration['fieldExplanationText'])) { + getHelper() + .getTemplatePropertyDomElement('fieldExplanationText', editorHtml) + .text(editorConfiguration['fieldExplanationText']); + } else { + getHelper() + .getTemplatePropertyDomElement('fieldExplanationText', editorHtml) + .remove(); + } + + propertyPath = getFormEditorApp().buildPropertyPath( + editorConfiguration['propertyPath'], + collectionElementIdentifier, + collectionName + ); + propertyData = getCurrentlySelectedFormElement().get(propertyPath); + + _validateCollectionElement(propertyPath, editorHtml); + + getHelper().getTemplatePropertyDomElement('propertyPath', editorHtml).val(propertyData); + + getHelper().getTemplatePropertyDomElement('propertyPath', editorHtml).on('keyup paste', function() { + getCurrentlySelectedFormElement().set(propertyPath, $(this).val()); + _validateCollectionElement(propertyPath, editorHtml); + }); + }; + + /** + * @public + * + * @param object editorConfiguration + * @param object editorHtml + * @param string collectionElementIdentifier + * @param string collectionName + * @return void + * @throws 1475421048 + * @throws 1475421049 + * @throws 1475421050 + * @throws 1475421051 + * @throws 1475421052 + */ + function renderSingleSelectEditor(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + var propertyData, propertyPath, selectElement; + assert( + 'object' === $.type(editorConfiguration), + 'Invalid parameter "editorConfiguration"', + 1475421048 + ); + assert( + 'object' === $.type(editorHtml), + 'Invalid parameter "editorHtml"', + 1475421049 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['label']), + 'Invalid configuration "label"', + 1475421050 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['propertyPath']), + 'Invalid configuration "propertyPath"', + 1475421051 + ); + assert( + 'array' === $.type(editorConfiguration['selectOptions']), + 'Invalid configuration "selectOptions"', + 1475421052 + ); + + propertyPath = getFormEditorApp().buildPropertyPath( + editorConfiguration['propertyPath'], + collectionElementIdentifier, + collectionName + ); + + getHelper() + .getTemplatePropertyDomElement('label', editorHtml) + .append(editorConfiguration['label']); + + selectElement = getHelper() + .getTemplatePropertyDomElement('selectOptions', editorHtml); + + propertyData = getCurrentlySelectedFormElement().get(propertyPath); + + for (var i = 0, len = editorConfiguration['selectOptions'].length; i < len; ++i) { + var option; + + if (editorConfiguration['selectOptions'][i]['value'] === propertyData) { + option = new Option(editorConfiguration['selectOptions'][i]['label'], i, false, true); + } else { + option = new Option(editorConfiguration['selectOptions'][i]['label'], i); + } + $(option).data({value: editorConfiguration['selectOptions'][i]['value']}); + selectElement.append(option); + } + + selectElement.on('change', function() { + getCurrentlySelectedFormElement().set(propertyPath, $('option:selected', $(this)).data('value')); + }); + }; + + /** + * @public + * + * @param object editorConfiguration + * @param object editorHtml + * @param string collectionElementIdentifier + * @param string collectionName + * @return void + * @throws 1475419226 + * @throws 1475419227 + * @throws 1475419228 + * @throws 1475419229 + * @throws 1475419230 + * @throws 1475419231 + * @throws 1475419232 + */ + function renderPropertyGridEditor(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + var addRowTemplate, defaultValue, multiSelection, propertyData, propertyPathPrefix, rowItemTemplate, setData; + assert( + 'object' === $.type(editorConfiguration), + 'Invalid parameter "editorConfiguration"', + 1475419226 + ); + assert( + 'object' === $.type(editorHtml), + 'Invalid parameter "editorHtml"', + 1475419227 + ); + assert( + 'boolean' === $.type(editorConfiguration['enableAddRow']), + 'Invalid configuration "enableAddRow"', + 1475419228 + ); + assert( + 'boolean' === $.type(editorConfiguration['enableDeleteRow']), + 'Invalid configuration "enableDeleteRow"', + 1475419230 + ); + assert( + 'boolean' === $.type(editorConfiguration['isSortable']), + 'Invalid configuration "isSortable"', + 1475419229 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['propertyPath']), + 'Invalid configuration "propertyPath"', + 1475419231 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['label']), + 'Invalid configuration "label"', + 1475419232 + ); + + getHelper().getTemplatePropertyDomElement('label', editorHtml).append(editorConfiguration['label']); + propertyPathPrefix = getFormEditorApp().buildPropertyPath( + undefined, + collectionElementIdentifier, + collectionName, + undefined, + true + ); + if (getUtility().isNonEmptyString(propertyPathPrefix)) { + propertyPathPrefix = propertyPathPrefix + '.'; + } + + if (getUtility().isUndefinedOrNull(editorConfiguration['multiSelection'])) { + multiSelection = false; + } else { + multiSelection = !!editorConfiguration['multiSelection']; + } + + rowItemTemplate = $( + getHelper().getDomElementDataIdentifierSelector('propertyGridEditorRowItem'), + $(editorHtml) + ).clone(); + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorRowItem'), $(editorHtml)).remove(); + + if (!!editorConfiguration['enableDeleteRow']) { + $( getHelper().getDomElementDataIdentifierSelector('propertyGridEditorDeleteRow'), + $(rowItemTemplate) + ).on('click', function() { + if ($(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorRowItem'), $(editorHtml)).length > 1) { + $(this) + .closest(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorRowItem')) + .off() + .empty() + .remove(); + + _setPropertyGridData( + $(editorHtml), + multiSelection, + editorConfiguration['propertyPath'], + propertyPathPrefix + ); + } else { + Notification.error( + editorConfiguration['removeLastAvailableRowFlashMessageTitle'], + editorConfiguration['removeLastAvailableRowFlashMessageMessage'], + 2 + ); + } + }); + } else { + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorDeleteRow'), $(rowItemTemplate)) + .parent() + .off() + .empty(); + } + + if (!!editorConfiguration['isSortable']) { + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorContainer'), $(editorHtml)) + .addClass(getHelper().getDomElementClassName('sortable')) + .sortable({ + revert: 'true', + items: getHelper().getDomElementDataIdentifierSelector('propertyGridEditorRowItem'), + update: function(e, o) { + _setPropertyGridData($(editorHtml), multiSelection, editorConfiguration['propertyPath'], propertyPathPrefix); + } + }); + } else { + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorSortRow'), $(rowItemTemplate)) + .parent() + .off() + .empty(); + } + + $( getHelper().getDomElementDataIdentifierSelector('propertyGridEditorSelectValue'), + $(rowItemTemplate) + ).on('change', function() { + if (!multiSelection) { + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorSelectValue') + ':checked', $(editorHtml)) + .not($(this)) + .prop('checked', false); + } + _setPropertyGridData($(editorHtml), multiSelection, editorConfiguration['propertyPath'], propertyPathPrefix); + }); + + $( getHelper().getDomElementDataIdentifierSelector('propertyGridEditorLabel') + ',' + + getHelper().getDomElementDataIdentifierSelector('propertyGridEditorValue'), + $(rowItemTemplate) + ).on('keyup paste', function() { + _setPropertyGridData($(editorHtml), multiSelection, editorConfiguration['propertyPath'], propertyPathPrefix); + }); + + $( getHelper().getDomElementDataIdentifierSelector('propertyGridEditorLabel'), + $(rowItemTemplate) + ).on('focusout', function() { + if ('' === $(this) + .closest(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorRowItem')) + .find(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorValue')) + .val() + ) { + $(this) + .closest(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorRowItem')) + .find(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorValue')) + .val($(this).val()); + } + }); + + if (!!editorConfiguration['enableAddRow']) { + addRowTemplate = $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorAddRowItem'), $(editorHtml)).clone(); + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorAddRowItem'), $(editorHtml)).remove(); + + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorAddRow'), $(addRowTemplate)).on('click', function() { + $(this) + .closest(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorAddRowItem')) + .before($(rowItemTemplate).clone(true, true)); + }); + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorContainer'), $(editorHtml)) + .prepend($(addRowTemplate).clone(true, true)); + } else { + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorAddRowItem'), $(editorHtml)).remove(); + } + + defaultValue = {}; + if (multiSelection) { + if (!getUtility().isUndefinedOrNull(getCurrentlySelectedFormElement().get(propertyPathPrefix + 'defaultValue'))) { + defaultValue = getCurrentlySelectedFormElement().get(propertyPathPrefix + 'defaultValue'); + } + } else { + if (!getUtility().isUndefinedOrNull(getCurrentlySelectedFormElement().get(propertyPathPrefix + 'defaultValue'))) { + defaultValue = {0: getCurrentlySelectedFormElement().get(propertyPathPrefix + 'defaultValue')}; + } + } + propertyData = getCurrentlySelectedFormElement().get(propertyPathPrefix + editorConfiguration['propertyPath']) || {}; + + setData = function(label, value) { + var isPreselected, newRowTemplate; + + isPreselected = false; + newRowTemplate = $(rowItemTemplate).clone(true, true); + + for (var defaultValueKey in defaultValue) { + if (!defaultValue.hasOwnProperty(defaultValueKey)) { + continue; + } + if (defaultValue[defaultValueKey] === value) { + isPreselected = true; + break; + } + } + + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorLabel'), $(newRowTemplate)).val(label); + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorValue'), $(newRowTemplate)).val(value); + if (isPreselected) { + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorSelectValue'), $(newRowTemplate)) + .prop('checked', true); + } + + if (!!editorConfiguration['enableAddRow']) { + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorAddRowItem'), $(editorHtml)) + .before($(newRowTemplate)); + } else { + $(getHelper().getDomElementDataIdentifierSelector('propertyGridEditorContainer'), $(editorHtml)) + .prepend($(newRowTemplate)); + } + }; + + if ('object' === $.type(propertyData)) { + for (var propertyDataKey in propertyData) { + if (!propertyData.hasOwnProperty(propertyDataKey)) { + continue; + } + setData(propertyData[propertyDataKey], propertyDataKey); + } + } else { + for (var i = 0, len = propertyData.length; i < len; ++i) { + setData(propertyData[i]['_label'], propertyData[i]['_value']); + } + } + }; + + /** + * @public + * + * @param object editorConfiguration + * @param object editorHtml + * @param string collectionElementIdentifier + * @param string collectionName + * @return void + * @publish view/inspector/collectionElement/new/selected + * @publish view/inspector/removeCollectionElement/perform + * @throws 1475417093 + * @throws 1475417094 + * @throws 1475417095 + * @throws 1475417096 + */ + function renderRequiredValidatorEditor(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + var validatorIdentifier; + assert( + 'object' === $.type(editorConfiguration), + 'Invalid parameter "editorConfiguration"', + 1475417093 + ); + assert( + 'object' === $.type(editorHtml), + 'Invalid parameter "editorHtml"', + 1475417094 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['validatorIdentifier']), + 'Invalid configuration "validatorIdentifier"', + 1475417095 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['label']), + 'Invalid configuration "label"', + 1475417096 + ); + + validatorIdentifier = editorConfiguration['validatorIdentifier']; + getHelper().getTemplatePropertyDomElement('label', editorHtml).append(editorConfiguration['label']); + + if (-1 !== getFormEditorApp().getIndexFromPropertyCollectionElement(validatorIdentifier, 'validators')) { + $('input[type="checkbox"]', $(editorHtml)).prop('checked', true); + } + + $('input[type="checkbox"]', $(editorHtml)).on('change', function() { + if ($(this).is(":checked")) { + getPublisherSubscriber().publish( + 'view/inspector/collectionElement/new/selected', + [validatorIdentifier, 'validators'] + ); + } else { + getPublisherSubscriber().publish( + 'view/inspector/removeCollectionElement/perform', + [validatorIdentifier, 'validators'] + ); + } + }); + }; + + /** + * @public + * + * @param object editorConfiguration + * @param object editorHtml + * @param string collectionElementIdentifier + * @param string collectionName + * @return void + * @throws 1476218671 + * @throws 1476218672 + * @throws 1476218673 + * @throws 1476218674 + */ + function renderCheckboxEditor(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + var propertyData, propertyPath; + assert( + 'object' === $.type(editorConfiguration), + 'Invalid parameter "editorConfiguration"', + 1476218671 + ); + assert( + 'object' === $.type(editorHtml), + 'Invalid parameter "editorHtml"', + 1476218672 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['label']), + 'Invalid configuration "label"', + 1476218673 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['propertyPath']), + 'Invalid configuration "propertyPath"', + 1476218674 + ); + + getHelper() + .getTemplatePropertyDomElement('label', editorHtml) + .append(editorConfiguration['label']); + + propertyPath = getFormEditorApp() + .buildPropertyPath(editorConfiguration['propertyPath'], collectionElementIdentifier, collectionName); + propertyData = getCurrentlySelectedFormElement().get(propertyPath); + + if ( + ('boolean' === $.type(propertyData) && propertyData) + || propertyData === 'true' + || propertyData === 1 + || propertyData === "1" + ) { + $('input[type="checkbox"]', $(editorHtml)).prop('checked', true); + } + + $('input[type="checkbox"]', $(editorHtml)).on('change', function() { + if ($(this).is(":checked")) { + getCurrentlySelectedFormElement().set(propertyPath, true); + } else { + getCurrentlySelectedFormElement().set(propertyPath, false); + } + }); + }; + + /** + * @public + * + * @param object editorConfiguration + * @param object editorHtml + * @param string collectionElementIdentifier + * @param string collectionName + * @return void + * @throws 1475412567 + * @throws 1475412568 + * @throws 1475416098 + * @throws 1475416099 + */ + function renderTextareaEditor(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + var propertyPath, propertyData; + assert( + 'object' === $.type(editorConfiguration), + 'Invalid parameter "editorConfiguration"', + 1475412567 + ); + assert( + 'object' === $.type(editorHtml), + 'Invalid parameter "editorHtml"', + 1475412568 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['propertyPath']), + 'Invalid configuration "propertyPath"', + 1475416098 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['label']), + 'Invalid configuration "label"', + 1475416099 + ); + + propertyPath = getFormEditorApp() + .buildPropertyPath(editorConfiguration['propertyPath'], collectionElementIdentifier, collectionName); + + getHelper() + .getTemplatePropertyDomElement('label', editorHtml).append(editorConfiguration['label']); + + if (getUtility().isNonEmptyString(editorConfiguration['fieldExplanationText'])) { + getHelper() + .getTemplatePropertyDomElement('fieldExplanationText', editorHtml) + .text(editorConfiguration['fieldExplanationText']); + } else { + getHelper() + .getTemplatePropertyDomElement('fieldExplanationText', editorHtml) + .remove(); + } + + propertyData = getCurrentlySelectedFormElement().get(propertyPath); + $('textarea', $(editorHtml)).val(propertyData); + + $('textarea', $(editorHtml)).on('keyup paste', function() { + getCurrentlySelectedFormElement().set(propertyPath, $(this).val()); + }); + }; + + /** + * @public + * + * @param object editorConfiguration + * @param object editorHtml + * @param string collectionElementIdentifier + * @param string collectionName + * @return void + * @throws 1477300587 + * @throws 1477300588 + * @throws 1477300589 + * @throws 1477300590 + * @throws 1477318981 + * @throws 1477319859 + */ + function renderTypo3WinBrowserEditor(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + var iconType, propertyPath, propertyData; + assert( + 'object' === $.type(editorConfiguration), + 'Invalid parameter "editorConfiguration"', + 1477300587 + ); + assert( + 'object' === $.type(editorHtml), + 'Invalid parameter "editorHtml"', + 1477300588 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['label']), + 'Invalid configuration "label"', + 1477300589 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['buttonLabel']), + 'Invalid configuration "buttonLabel"', + 1477318981 + ); + assert( + getUtility().isNonEmptyString(editorConfiguration['propertyPath']), + 'Invalid configuration "propertyPath"', + 1477300590 + ); + assert( + 'tt_content' === editorConfiguration['browsableType'] || 'pages' === editorConfiguration['browsableType'], + 'Invalid configuration "browsableType"', + 1477319859 + ); + + getHelper() + .getTemplatePropertyDomElement('label', editorHtml) + .append(editorConfiguration['label']); + getHelper() + .getTemplatePropertyDomElement('buttonLabel', editorHtml) + .append(editorConfiguration['buttonLabel']); + + if (getUtility().isNonEmptyString(editorConfiguration['fieldExplanationText'])) { + getHelper() + .getTemplatePropertyDomElement('fieldExplanationText', editorHtml) + .text(editorConfiguration['fieldExplanationText']); + } else { + getHelper() + .getTemplatePropertyDomElement('fieldExplanationText', editorHtml) + .remove(); + } + + $('form', $(editorHtml)).prop('name', editorConfiguration['propertyPath']); + + iconType = ('tt_content' === editorConfiguration['browsableType']) + ? getHelper().getDomElementDataAttributeValue('iconTtContent') + : getHelper().getDomElementDataAttributeValue('iconPage'); + Icons.getIcon(iconType, Icons.sizes.small).done(function(icon) { + getHelper().getTemplatePropertyDomElement('image', editorHtml).append($(icon)); + }); + + getHelper().getTemplatePropertyDomElement('onclick', editorHtml).on('click', function() { + var insertTarget, randomIdentifier; + + randomIdentifier = Math.floor((Math.random() * 100000) + 1); + insertTarget = $(this) + .closest(getHelper().getDomElementDataIdentifierSelector('editorControlsWrapper')) + .find(getHelper().getDomElementDataAttribute('contentElementSelectorTarget', 'bracesWithKey')); + + insertTarget.attr(getHelper().getDomElementDataAttribute('contentElementSelectorTarget'), randomIdentifier); + _openTypo3WinBrowser('db', randomIdentifier + '|||' + editorConfiguration['browsableType']); + }); + + propertyPath = getFormEditorApp().buildPropertyPath(editorConfiguration['propertyPath'], collectionElementIdentifier, collectionName); + propertyData = getCurrentlySelectedFormElement().get(propertyPath); + + _validateCollectionElement(propertyPath, editorHtml); + getHelper() + .getTemplatePropertyDomElement('propertyPath', editorHtml) + .val(propertyData); + + getHelper().getTemplatePropertyDomElement('propertyPath', editorHtml).on('keyup paste', function() { + getCurrentlySelectedFormElement().set(propertyPath, $(this).val()); + _validateCollectionElement(propertyPath, editorHtml); + }); + }; + + /** + * @public + * + * @param object editorConfiguration + * @param object editorHtml + * @param string collectionElementIdentifier + * @param string collectionName + * @return void + * @throws 1475412563 + * @throws 1475412564 + */ + function renderRemoveElementEditor(editorConfiguration, editorHtml, collectionElementIdentifier, collectionName) { + assert('object' === $.type(editorConfiguration), 'Invalid parameter "editorConfiguration"', 1475412563); + assert('object' === $.type(editorHtml), 'Invalid parameter "editorHtml"', 1475412564); + + if (getUtility().isUndefinedOrNull(collectionElementIdentifier)) { + + $('button', $(editorHtml)) + .addClass( + getHelper().getDomElementClassName('buttonFormElementRemove') + ' ' + + getHelper().getDomElementClassName('buttonFormEditor') + ); + } else { + $('button', $(editorHtml)).addClass( + getHelper().getDomElementClassName('buttonCollectionElementRemove') + ); + } + + $('button', $(editorHtml)).on('click', function(e) { + if (getUtility().isUndefinedOrNull(collectionElementIdentifier)) { + getViewModel().showRemoveFormElementModal(); + } else { + getViewModel().showRemoveCollectionElementModal(collectionElementIdentifier, collectionName); + } + }); + }; + + /** + * @public + * + * @param string content + * @return void + */ + function setFormElementHeaderEditorContent(content) { + if (getFormEditorApp().getUtility().isUndefinedOrNull(content)) { + content = buildTitleByFormElement(); + } + + $(getHelper() + .getDomElementDataIdentifierSelector('formElementHeaderEditor'), getInspectorDomElement()) + .html(content); + }; + + /** + * @public + * + * @param object + * @return object + * @throws 1478967319 + */ + function buildTitleByFormElement(formElement) { + var label; + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1478967319); + + return $('<span></span>').text((formElement.get('label') + ? formElement.get('label') + : formElement.get('identifier'))); + }; + + /** + * @public + * + * @param object + * @param object + * @return this + */ + function bootstrap(formEditorApp, configuration) { + _formEditorApp = formEditorApp; + _configuration = $.extend(true, _defaultConfiguration, configuration || {}); + _helperSetup(); + return this; + }; + + /** + * Publish the public methods. + * Implements the "Revealing Module Pattern". + */ + return { + bootstrap: bootstrap, + buildTitleByFormElement: buildTitleByFormElement, + getCollectionElementDomElement: getCollectionElementDomElement, + getFinishersContainerDomElement: getFinishersContainerDomElement, + getInspectorDomElement: getInspectorDomElement, + getValidatorsContainerDomElement: getValidatorsContainerDomElement, + renderCheckboxEditor: renderCheckboxEditor, + renderCollectionElementEditors: renderCollectionElementEditors, + renderCollectionElementHeaderEditor: renderCollectionElementHeaderEditor, + renderCollectionElementSelectionEditor: renderCollectionElementSelectionEditor, + renderEditors: renderEditors, + renderFormElementHeaderEditor: renderFormElementHeaderEditor, + renderPropertyGridEditor: renderPropertyGridEditor, + renderRemoveElementEditor: renderRemoveElementEditor, + renderRequiredValidatorEditor: renderRequiredValidatorEditor, + renderSingleSelectEditor: renderSingleSelectEditor, + renderTextareaEditor: renderTextareaEditor, + renderTextEditor: renderTextEditor, + renderTypo3WinBrowserEditor: renderTypo3WinBrowserEditor, + setFormElementHeaderEditorContent: setFormElementHeaderEditorContent + }; + })($, Helper, Icons, Notification); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Mediator.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Mediator.js new file mode 100644 index 000000000000..475b0f2d8b8b --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/Mediator.js @@ -0,0 +1,1059 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormEditor/Mediator + */ +define(['jquery', + 'TYPO3/CMS/Form/Backend/FormEditor/Helper' + ], function($, Helper) { + 'use strict'; + + return (function($, Helper) { + + /** + * @private + * + * @var object + */ + var _formEditorApp = null; + + /** + * @private + * + * @var object + */ + var _viewModel = null; + + /* ************************************************************* + * Private Methodes + * ************************************************************/ + + /** + * @private + * + * @return void + * @throws 1478268638 + */ + function _helperSetup() { + assert('function' === $.type(Helper.bootstrap), + 'The view model helper does not implement the method "bootstrap"', + 1478268638 + ); + Helper.bootstrap(getFormEditorApp()); + }; + + /** + * @private + * + * @return object + */ + function getFormEditorApp() { + return _formEditorApp; + }; + + /** + * @private + * + * @return object + */ + function getViewModel() { + return _viewModel; + }; + + /** + * @private + * + * @return object + */ + function getUtility() { + return getFormEditorApp().getUtility(); + }; + + /** + * @private + * + * @param mixed test + * @param string message + * @param int messageCode + * @return void + */ + function assert(test, message, messageCode) { + return getFormEditorApp().assert(test, message, messageCode); + }; + + /** + * @private + * + * @param object + * @return object + */ + function getHelper(configuration) { + if (getUtility().isUndefinedOrNull(configuration)) { + return Helper.setConfiguration(getViewModel().getConfiguration()); + } + return Helper.setConfiguration(configuration); + }; + + /** + * @private + * + * @return object + */ + function getCurrentlySelectedFormElement() { + return getFormEditorApp().getCurrentlySelectedFormElement(); + }; + + /** + * @private + * + * @return object + */ + function getPublisherSubscriber() { + return getFormEditorApp().getPublisherSubscriber(); + }; + + /** + * @private + * + * @return object + */ + function getRootFormElement() { + return getFormEditorApp().getRootFormElement(); + }; + + /** + * @private + * + * @return void + */ + function _subscribeEvents() { + + /* ********************************************************* + * Misc + * ********************************************************/ + + /** + * @private + * + * @return string + */ + window.onbeforeunload = function(e) { + if (!getFormEditorApp().getUnsavedContent()) { + return; + } + e = e || window.event; + if (e) { + e.returnValue = getFormEditorApp().getFormElementDefinition(getRootFormElement(), 'modalCloseDialogMessage'); + } + return getFormEditorApp().getFormElementDefinition(getRootFormElement(), 'modalCloseDialogTitle'); + }; + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/ready + */ + getPublisherSubscriber().subscribe('view/ready', function(topic, args) { + getViewModel().onViewReadyBatch(); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = applicationState + * args[1] = stackPointer + * args[2] = stackSize + * @return void + * @subscribe core/applicationState/add + */ + getPublisherSubscriber().subscribe('core/applicationState/add', function(topic, args) { + getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderRedo'))); + if (args[2] > 1 && args[1] <= args[2]) { + getViewModel().enableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderUndo'))); + } else { + getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderUndo'))); + } + }); + + /* ********************************************************* + * Ajax + * ********************************************************/ + + /** + * @private + * + * @param string + * @param array + * args[0] = html + * @return void + * @subscribe core/ajax/saveFormDefinition/success + */ + getPublisherSubscriber().subscribe('core/ajax/saveFormDefinition/success', function(topic, args) { + getFormEditorApp().setUnsavedContent(false); + getViewModel().showSaveSuccessMessage(); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = html + * args[1] = pageIndex + * @return void + * @subscribe core/ajax/renderFormDefinitionPage/success + */ + getPublisherSubscriber().subscribe('core/ajax/renderFormDefinitionPage/success', function(topic, args) { + getViewModel().renderPreviewStageArea(args[0]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = jqXHR + * args[1] = textStatus + * args[2] = errorThrown + * @return void + * @subscribe core/ajax/saveFormDefinition/error + */ + getPublisherSubscriber().subscribe('core/ajax/error', function(topic, args) { + if (args[0].status !== 0) { + getViewModel().showErrorFlashMessage(args[1], args[2]); + getViewModel().renderPreviewStageArea(args[0].responseText); + } + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe ajax/beforeSend + */ + getPublisherSubscriber().subscribe('ajax/beforeSend', function(topic, args) { + getViewModel().showSaveButtonSpinnerIcon(); + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe ajax/complete + */ + getPublisherSubscriber().subscribe('ajax/complete', function(topic, args) { + getViewModel().showSaveButtonSaveIcon(); + }); + + /* ********************************************************* + * Header + * ********************************************************/ + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/header/button/save/clicked + */ + getPublisherSubscriber().subscribe('view/header/button/save/clicked', function(topic, args) { + + if (getFormEditorApp().validationResultsHasErrors(getFormEditorApp().validateFormElementRecursive(getRootFormElement(), true))) { + getViewModel().showValidationErrorsModal(); + } else { + getFormEditorApp().saveFormDefinition(); + } + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = targetEvent + * @return void + * @subscribe view/header/button/newPage/clicked + */ + getPublisherSubscriber().subscribe('view/header/button/newPage/clicked', function(topic, args) { + if (getFormEditorApp().isRootFormElementSelected()) { + getViewModel().selectPageBatch(0); + } + getViewModel().showInsertPagesModal(args[0]); + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/header/button/close/clicked + */ + getPublisherSubscriber().subscribe('view/header/button/close/clicked', function(topic, args) { + getViewModel().showCloseConfirmationModal(); + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/undoButton/clicked + */ + getPublisherSubscriber().subscribe('view/undoButton/clicked', function(topic, args) { + getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderUndo'))); + getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderRedo'))); + getFormEditorApp().undoApplicationState(); + + if (getViewModel().getPreviewMode()) { + getFormEditorApp().renderCurrentFormPage(); + } else { + getViewModel().renderAbstractStageArea(); + } + getFormEditorApp().setUnsavedContent(true); + + getViewModel().renewStructure(); + getViewModel().renderPagination(); + getViewModel().renderInspectorEditors(); + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/redoButton/clicked + */ + getPublisherSubscriber().subscribe('view/redoButton/clicked', function(topic, args) { + getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderUndo'))); + getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderRedo'))); + getFormEditorApp().redoApplicationState(); + + if (getViewModel().getPreviewMode()) { + getFormEditorApp().renderCurrentFormPage(); + } else { + getViewModel().renderAbstractStageArea(); + } + getFormEditorApp().setUnsavedContent(true); + + getViewModel().renewStructure(); + getViewModel().renderPagination(); + getViewModel().renderInspectorEditors(); + }); + + /* ********************************************************* + * Stage + * ********************************************************/ + + /** + * @private + * + * @param string + * @param array + * args[0] = formElementIdentifierPath + * @return void + * @subscribe view/stage/element/clicked + */ + getPublisherSubscriber().subscribe('view/stage/element/clicked', function(topic, args) { + if (getCurrentlySelectedFormElement().get('__identifierPath') !== args[0]) { + getFormEditorApp().setCurrentlySelectedFormElement(args[0]); + getViewModel().renewStructure(); + getViewModel().refreshSelectedElementItemsBatch(); + getViewModel().addAbstractViewValidationResults(); + getViewModel().renderInspectorEditors(); + } + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = targetEvent + * @return void + * @subscribe view/stage/abstract/elementToolbar/button/newElement/clicked + */ + getPublisherSubscriber().subscribe('view/stage/abstract/elementToolbar/button/newElement/clicked', function(topic, args) { + if (getFormEditorApp().isRootFormElementSelected()) { + getViewModel().selectPageBatch(0); + } + getViewModel().showInsertElementsModal(args[0]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = targetEvent + * @return void + * @subscribe view/newElementButton/clicked + */ + getPublisherSubscriber().subscribe('view/stage/abstract/button/newElement/clicked', function(topic, args) { + if (getFormEditorApp().isRootFormElementSelected()) { + getViewModel().selectPageBatch(0); + } + getViewModel().showInsertElementsModal(args[0]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = draggedFormElementDomElement + * args[1] = draggedFormPlaceholderDomElement + * @return void + * @subscribe view/stage/abstract/dnd/start + */ + getPublisherSubscriber().subscribe('view/stage/abstract/dnd/start', function(topic, args) { + getViewModel().onAbstractViewDndStartBatch(args[0], args[1]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = draggedFormElementIdentifierPath + * @return void + * @subscribe view/stage/abstract/dnd/stop + */ + getPublisherSubscriber().subscribe('view/stage/abstract/dnd/stop', function(topic, args) { + getFormEditorApp().setCurrentlySelectedFormElement(args[0]); + getViewModel().renewStructure(); + getViewModel().refreshSelectedElementItemsBatch(); + getViewModel().addAbstractViewValidationResults(); + getViewModel().renderInspectorEditors(); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = placeholderDomElement + * args[1] = parentFormElementIdentifierPath + * args[2] = enclosingCompositeFormElement + * @return void + * @subscribe view/stage/abstract/dnd/change + */ + getPublisherSubscriber().subscribe('view/stage/abstract/dnd/change', function(topic, args) { + getViewModel().onAbstractViewDndChangeBatch(args[0], args[1], args[2]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = movedDomElement + * args[1] = movedFormElementIdentifierPath + * args[2] = previousFormElementIdentifierPath + * args[3] = nextFormElementIdentifierPath + * @return void + * @subscribe view/stage/abstract/dnd/update + */ + getPublisherSubscriber().subscribe('view/stage/abstract/dnd/update', function(topic, args) { + getViewModel().onAbstractViewDndUpdateBatch(args[0], args[1], args[2], args[3]); + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/viewModeButton/abstract/clicked + */ + getPublisherSubscriber().subscribe('view/viewModeButton/abstract/clicked', function(topic, args) { + if (getViewModel().getPreviewMode()) { + getViewModel().setPreviewMode(false); + getViewModel().renderAbstractStageArea(); + } + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/viewModeButton/preview/clicked + */ + getPublisherSubscriber().subscribe('view/viewModeButton/preview/clicked', function(topic, args) { + if (!getViewModel().getPreviewMode()) { + getViewModel().setPreviewMode(true); + getFormEditorApp().renderCurrentFormPage(); + } + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/paginationPrevious/clicked + */ + getPublisherSubscriber().subscribe('view/paginationPrevious/clicked', function(topic, args) { + getViewModel().selectPageBatch(getFormEditorApp().getCurrentlySelectedPageIndex() - 1); + if (getViewModel().getPreviewMode()) { + getFormEditorApp().renderCurrentFormPage(); + } else { + getViewModel().renderAbstractStageArea(); + } + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/paginationNext/clicked + */ + getPublisherSubscriber().subscribe('view/paginationNext/clicked', function(topic, args) { + getViewModel().selectPageBatch(getFormEditorApp().getCurrentlySelectedPageIndex() + 1); + if (getViewModel().getPreviewMode()) { + getFormEditorApp().renderCurrentFormPage(); + } else { + getViewModel().renderAbstractStageArea(); + } + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/stage/abstract/render/postProcess + */ + getPublisherSubscriber().subscribe('view/stage/abstract/render/postProcess', function(topic, args) { + getViewModel().renderUndoRedo(); + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/stage/preview/render/postProcess + */ + getPublisherSubscriber().subscribe('view/stage/preview/render/postProcess', function(topic, args) { + getViewModel().renderUndoRedo(); + }); + + /* ********************************************************* + * Structure + * ********************************************************/ + + /** + * @private + * + * @param string + * @param array + * args[0] = formElementIdentifierPath + * @return void + * @subscribe view/tree/node/clicked + */ + getPublisherSubscriber().subscribe('view/tree/node/clicked', function(topic, args) { + var oldPageIndex; + if (getCurrentlySelectedFormElement().get('__identifierPath') !== args[0]) { + oldPageIndex = getFormEditorApp().getCurrentlySelectedPageIndex(); + getFormEditorApp().setCurrentlySelectedFormElement(args[0]); + if (oldPageIndex !== getFormEditorApp().getCurrentlySelectedPageIndex()) { + getViewModel().renderAbstractStageArea(); + } else { + getViewModel().renderAbstractStageArea(false); + } + getViewModel().renderPagination(); + getViewModel().renderInspectorEditors(); + } + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/structure/root/selected + */ + getPublisherSubscriber().subscribe('view/structure/root/selected', function(topic, args) { + if (!getFormEditorApp().isRootFormElementSelected()) { + getViewModel().addStructureRootElementSelection(); + getFormEditorApp().setCurrentlySelectedFormElement(getRootFormElement()); + getViewModel().renderAbstractStageArea(); + getViewModel().renewStructure(); + getViewModel().renderPagination(); + getViewModel().renderInspectorEditors(); + } + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = targetEvent + * @return void + * @subscribe view/header/button/newPage/clicked + */ + getPublisherSubscriber().subscribe('view/structure/button/newPage/clicked', function(topic, args) { + if (getFormEditorApp().isRootFormElementSelected()) { + getViewModel().selectPageBatch(0); + } + getViewModel().showInsertPagesModal(args[0]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = draggedFormElementIdentifierPath + * @return void + * @subscribe view/tree/dnd/stop + */ + getPublisherSubscriber().subscribe('view/tree/dnd/stop', function(topic, args) { + getFormEditorApp().setCurrentlySelectedFormElement(args[0]); + getViewModel().renewStructure(); + getViewModel().renderPagination(); + getViewModel().renderAbstractStageArea(); + getViewModel().renderInspectorEditors(); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = placeholderDomElement + * args[1] = parentFormElementIdentifierPath + * args[2] = enclosingCompositeFormElement + * @return void + * @subscribe view/tree/dnd/change + */ + getPublisherSubscriber().subscribe('view/tree/dnd/change', function(topic, args) { + getViewModel().onStructureDndChangeBatch(args[0], args[1], args[2]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = movedDomElement + * args[1] = movedFormElementIdentifierPath + * args[2] = previousFormElementIdentifierPath + * args[3] = nextFormElementIdentifierPath + * @return void + * @subscribe view/tree/dnd/update + */ + getPublisherSubscriber().subscribe('view/tree/dnd/update', function(topic, args) { + getViewModel().onStructureDndUpdateBatch(args[0], args[1], args[2], args[3]); + }); + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/structure/renew/postProcess + */ + getPublisherSubscriber().subscribe('view/structure/renew/postProcess', function(topic, args) { + getViewModel().addStructureValidationResults(); + }); + + /* ********************************************************* + * Inspector + * ********************************************************/ + + /** + * @private + * + * @param string + * @param array + * args[0] = collectionElementIdentifier + * args[1] = collectionName + * args[2] = formElement + * @return void + * @subscribe view/inspector/removeCollectionElement/perform + */ + getPublisherSubscriber().subscribe('view/inspector/removeCollectionElement/perform', function(topic, args) { + getViewModel().removePropertyCollectionElement(args[0], args[1], args[2]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = collectionElementIdentifier + * args[1] = collectionName + * @return void + * @subscribe view/inspector/collectionElement/selected + */ + getPublisherSubscriber().subscribe('view/inspector/collectionElement/new/selected', function(topic, args) { + getViewModel().createAndAddPropertyCollectionElement(args[0], args[1]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = collectionElementIdentifier + * args[1] = collectionName + * @return void + * @subscribe view/inspector/collectionElement/selected + */ + getPublisherSubscriber().subscribe('view/inspector/collectionElement/existing/selected', function(topic, args) { + getViewModel().renderInspectorCollectionElementEditors(args[1], args[0]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = movedCollectionElementIdentifier + * args[1] = previousCollectionElementIdentifier + * args[2] = nextCollectionElementIdentifier + * args[3] = collectionName + * @return void + * @subscribe view/inspector/collectionElements/dnd/update + * @throws 1477407673 + */ + getPublisherSubscriber().subscribe('view/inspector/collectionElements/dnd/update', function(topic, args) { + if (args[2]) { + getViewModel().movePropertyCollectionElement(args[0], 'before', args[2], args[3]); + } else if (args[1]) { + getViewModel().movePropertyCollectionElement(args[0], 'after', args[1], args[3]); + } else { + assert(false, 'Next element or previous element need to be set.', 1477407673); + } + }); + + /* ********************************************************* + * Form element + * ********************************************************/ + + /** + * @private + * + * @param string + * @param array + * args[0] = propertyPath + * args[1] = value + * args[2] = oldValue + * args[3] = formElementIdentifierPath + * @return void + * @subscribe core/formElement/somePropertyChanged + */ + getPublisherSubscriber().subscribe('core/formElement/somePropertyChanged', function(topic, args) { + var hasError, validationElement, validationResults; + + validationResults = []; + if ('renderables' !== args[0]) { + if (!getFormEditorApp().isRootFormElementSelected() && 'label' === args[0]) { + getViewModel().getStructure().setTreeNodeTitle(); + getViewModel().setInspectorFormElementHeaderEditorContent(); + } else if (!getFormEditorApp().getUtility().isUndefinedOrNull(args[3]) && getRootFormElement().get('__identifierPath') === args[3]) { + getViewModel().setStructureRootElementTitle(); + getViewModel().setStageHeadline(); + getViewModel().setInspectorFormElementHeaderEditorContent(); + } + + if (getViewModel().getPreviewMode()) { + getFormEditorApp().renderCurrentFormPage(); + } else { + getViewModel().renderAbstractStageArea(false, false); + } + getViewModel().addStructureValidationResults(); + } + + getFormEditorApp().setUnsavedContent(true); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = parentFormElement + * @return void + * @subscribe view/formElement/removed + */ + getPublisherSubscriber().subscribe('view/formElement/removed', function(topic, args) { + getFormEditorApp().setCurrentlySelectedFormElement(args[0]); + getViewModel().renewStructure(); + getViewModel().renderAbstractStageArea(); + getViewModel().renderPagination(); + getViewModel().renderInspectorEditors(); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = newFormElement + * @return void + * @subscribe view/formElement/inserted + */ + getPublisherSubscriber().subscribe('view/formElement/inserted', function(topic, args) { + getFormEditorApp().setCurrentlySelectedFormElement(args[0]); + getViewModel().renewStructure(); + getViewModel().renderAbstractStageArea(); + getViewModel().renderPagination(); + getViewModel().renderInspectorEditors(); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = collectionElementIdentifier + * args[1] = collectionName + * args[2] = formElement + * args[3] = collectionElementConfiguration + * args[4] = referenceCollectionElementIdentifier + * @return void + * @subscribe view/collectionElement/new/added + */ + getPublisherSubscriber().subscribe('view/collectionElement/new/added', function(topic, args) { + getViewModel().renderInspectorEditors(); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = movedCollectionElementIdentifier + * args[1] = previousCollectionElementIdentifier + * args[2] = nextCollectionElementIdentifier + * args[3] = collectionName + * @return void + * @subscribe view/collectionElement/moved + * @throws 1477407673 + */ + getPublisherSubscriber().subscribe('view/collectionElement/moved', function(topic, args) { + getViewModel().renderInspectorEditors(undefined, false); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = collectionElementIdentifier + * args[1] = collectionName + * args[2] = formElement + * @return void + * @subscribe view/collectionElement/removed + */ + getPublisherSubscriber().subscribe('view/collectionElement/removed', function(topic, args) { + getViewModel().renderInspectorEditors(undefined, false); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = formElementType + * @return void + * @subscribe view/insertElements/perform/bottom + */ + getPublisherSubscriber().subscribe('view/insertElements/perform/bottom', function(topic, args) { + var lastRenderable; + + lastRenderable = getFormEditorApp().getLastTopLevelElementOnCurrentPage(); + if (!lastRenderable) { + getViewModel().createAndAddFormElement(args[0], getFormEditorApp().getCurrentlySelectedPage()); + } else { + if ( + !getFormEditorApp().getFormElementDefinition(lastRenderable, '_isTopLevelFormElement') + && getFormEditorApp().getFormElementDefinition(lastRenderable, '_isCompositeFormElement') + ) { + getViewModel().createAndAddFormElement(args[0], getFormEditorApp().getCurrentlySelectedPage()); + } else { + getViewModel().createAndAddFormElement(args[0], lastRenderable); + } + } + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = formElementType + * @return void + * @publish view/formElement/inserted + * @subscribe view/insertElements/perform/after + */ + getPublisherSubscriber().subscribe('view/insertElements/perform/after', function(topic, args) { + var newFormElement; + newFormElement = getViewModel().createAndAddFormElement(args[0], undefined, true); + newFormElement = getViewModel().moveFormElement(newFormElement, 'after', getFormEditorApp().getCurrentlySelectedFormElement()); + getPublisherSubscriber().publish('view/formElement/inserted', [newFormElement]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = formElementType + * @return void + * @subscribe view/insertElements/perform/inside + */ + getPublisherSubscriber().subscribe('view/insertElements/perform/inside', function(topic, args) { + getViewModel().createAndAddFormElement(args[0]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = formElementType + * @return void + * @subscribe view/insertElements/perform/after + */ + getPublisherSubscriber().subscribe('view/insertPages/perform', function(topic, args) { + getViewModel().createAndAddFormElement(args[0]); + }); + + /* ********************************************************* + * Modals + * ********************************************************/ + + /** + * @private + * + * @param string + * @param array + * @return void + * @subscribe view/modal/close/perform + */ + getPublisherSubscriber().subscribe('view/modal/close/perform', function(topic, args) { + getFormEditorApp().setUnsavedContent(false); + getViewModel().closeEditor(); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = formElement + * @return void + * @subscribe view/modal/removeFormElement/perform + */ + getPublisherSubscriber().subscribe('view/modal/removeFormElement/perform', function(topic, args) { + getViewModel().removeFormElement(args[0]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = collectionElementIdentifier + * args[1] = collectionName + * args[2] = formElement + * @return void + * @subscribe view/modal/removeCollectionElement/perform + */ + getPublisherSubscriber().subscribe('view/modal/removeCollectionElement/perform', function(topic, args) { + getViewModel().removePropertyCollectionElement(args[0], args[1], args[2]); + }); + + /** + * @private + * + * @param string + * @param array + * args[0] = formElementIdentifierPath + * @return void + * @subscribe view/modal/validationErrors/element/clicked + */ + getPublisherSubscriber().subscribe('view/modal/validationErrors/element/clicked', function(topic, args) { + var oldPageIndex; + if (getCurrentlySelectedFormElement().get('__identifierPath') !== args[0]) { + oldPageIndex = getFormEditorApp().getCurrentlySelectedPageIndex(); + getFormEditorApp().setCurrentlySelectedFormElement(args[0]); + + if (getViewModel().getPreviewMode()) { + getViewModel().setPreviewMode(false); + } + + if (oldPageIndex !== getFormEditorApp().getCurrentlySelectedPageIndex()) { + getViewModel().renderAbstractStageArea(); + } else { + getViewModel().renderAbstractStageArea(false); + } + + getViewModel().renderPagination(); + getViewModel().renderInspectorEditors(); + } + }); + }; + + /** + * @public + * + * @param object + * @param object + * @return void + */ + function bootstrap(formEditorApp, viewModel) { + _formEditorApp = formEditorApp; + _viewModel = viewModel; + _helperSetup(); + _subscribeEvents(); + }; + + /** + * Implements the "Revealing Module Pattern". + */ + return { + /** + * Publish the public methods. + */ + bootstrap: bootstrap + }; + })($, Helper); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/ModalsComponent.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/ModalsComponent.js new file mode 100644 index 000000000000..7f81a37fd0d6 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/ModalsComponent.js @@ -0,0 +1,493 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormEditor/ModalsComponent + */ +define(['jquery', + 'TYPO3/CMS/Form/Backend/FormEditor/Helper', + 'TYPO3/CMS/Backend/Modal', + 'TYPO3/CMS/Backend/Severity', + 'TYPO3/CMS/Backend/Icons' + ], function($, Helper, Modal, Severity, Icons) { + 'use strict'; + + return (function($, Helper, Modal, Severity, Icons) { + + /** + * @private + * + * @var object + */ + var _configuration = null; + + /** + * @private + * + * @var object + */ + var _defaultConfiguration = { + domElementClassNames: { + buttonDefault: 'btn-default', + buttonInfo: 'btn-info', + buttonWarning: 'btn-warning' + }, + domElementDataAttributeNames: { + elementType: 'element-type' + }, + domElementDataAttributeValues: { + rowItem: 'rowItem', + rowLink: 'rowLink', + rowsContainer: 'rowsContainer', + templateInsertElements: 'Modal-InsertElements', + templateInsertPages: 'Modal-InsertPages', + templateValidationErrors: 'Modal-ValidationErrors' + } + }; + + /** + * @private + * + * @var object + */ + var _formEditorApp = null; + + /* ************************************************************* + * Private Methodes + * ************************************************************/ + + /** + * @private + * + * @return void + * @throws 1478268638 + */ + function _helperSetup() { + assert('function' === $.type(Helper.bootstrap), + 'The view model helper does not implement the method "bootstrap"', + 1478268638 + ); + Helper.bootstrap(getFormEditorApp()); + }; + + /** + * @private + * + * @return object + */ + function getFormEditorApp() { + return _formEditorApp; + }; + + /** + * @public + * + * @param object + * @return object + */ + function getHelper(configuration) { + if (getUtility().isUndefinedOrNull(configuration)) { + return Helper.setConfiguration(_configuration); + } + return Helper.setConfiguration(configuration); + }; + + /** + * @private + * + * @return object + */ + function getUtility() { + return getFormEditorApp().getUtility(); + }; + + /** + * @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 object + */ + function getRootFormElement() { + return getFormEditorApp().getRootFormElement(); + }; + + /** + * @private + * + * @return object + */ + function getPublisherSubscriber() { + return getFormEditorApp().getPublisherSubscriber(); + }; + + /** + * @private + * + * @param object + * @param string + * @return mixed + */ + function getFormElementDefinition(formElement, formElementDefinitionKey) { + return getFormEditorApp().getFormElementDefinition(formElement, formElementDefinitionKey); + }; + + /** + * @public + * + * @param string publisherTopicName + * @param object publisherTopicArguments + * @return void + * @throws 1478889044 + * @throws 1478889049 + */ + function _showRemoveElementModal(publisherTopicName, publisherTopicArguments) { + var modalButtons = []; + + assert( + getUtility().isNonEmptyString(publisherTopicName), + 'Invalid parameter "publisherTopicName"', + 1478889049 + ); + assert( + 'array' === $.type(publisherTopicArguments), + 'Invalid parameter "formElement"', + 1478889044 + ); + + modalButtons.push({ + text: getFormElementDefinition(getRootFormElement(), 'modalRemoveElementCancleButton'), + active: true, + btnClass: getHelper().getDomElementClassName('buttonDefault'), + name: 'cancel', + trigger: function () { + Modal.currentModal.trigger('modal-dismiss'); + } + }); + + modalButtons.push({ + text: getFormElementDefinition(getRootFormElement(), 'modalRemoveElementConfirmButton'), + active: true, + btnClass: getHelper().getDomElementClassName('buttonWarning'), + name: 'confirm', + trigger: function () { + getPublisherSubscriber().publish(publisherTopicName, publisherTopicArguments); + Modal.currentModal.trigger('modal-dismiss'); + } + }); + + Modal.show( + getFormElementDefinition(getRootFormElement(), 'modalRemoveElementDialogTitle'), + getFormElementDefinition(getRootFormElement(), 'modalRemoveElementDialogMessage'), + Severity.warning, + modalButtons + ); + }; + + /** + * @private + * + * @param object modalContent + * @param string publisherTopicName + * @return void + * @publish mixed + * @throws 1478910954 + */ + function _insertElementsModalSetup(modalContent, publisherTopicName) { + assert( + getUtility().isNonEmptyString(publisherTopicName), + 'Invalid parameter "publisherTopicName"', + 1478910954 + ); + + $('a', modalContent).on("click", function(e) { + getPublisherSubscriber().publish(publisherTopicName, [$(this).data(getHelper().getDomElementDataAttribute('elementType'))]); + $('a', modalContent).off(); + Modal.currentModal.trigger('modal-dismiss'); + }); + }; + + /** + * @private + * + * @param object modalContent + * @param object validationResults + * @return void + * @publish view/modal/validationErrors/element/clicked + * @throws 1479161268 + */ + function _validationErrorsModalSetup(modalContent, validationResults) { + var formElement, newRowItem, rowItemTemplate; + + assert( + 'array' === $.type(validationResults), + 'Invalid parameter "validationResults"', + 1479161268 + ); + + rowItemTemplate = $( + getHelper().getDomElementDataIdentifierSelector('rowItem'), + modalContent + ).clone(); + + $(getHelper().getDomElementDataIdentifierSelector('rowItem'), modalContent).remove(); + + for (var i = 0, len = validationResults.length; i < len; ++i) { + var hasError = false, validationElement; + for (var j = 0, len2 = validationResults[i]['validationResults'].length; j < len2; ++j) { + if ( + validationResults[i]['validationResults'][j]['validationResults'] + && validationResults[i]['validationResults'][j]['validationResults'].length > 0 + ) { + hasError = true; + break; + } + } + + if (hasError) { + formElement = getFormEditorApp() + .getFormElementByIdentifierPath(validationResults[i]['formElementIdentifierPath']); + newRowItem = rowItemTemplate.clone(); + $(getHelper().getDomElementDataIdentifierSelector('rowLink'), newRowItem) + .attr( + getHelper().getDomElementDataAttribute('elementIdentifier'), + validationResults[i]['formElementIdentifierPath'] + ) + .html(_buildTitleByFormElement(formElement)); + $(getHelper().getDomElementDataIdentifierSelector('rowsContainer'), modalContent) + .append(newRowItem); + } + } + + $('a', modalContent).on("click", function(e) { + getPublisherSubscriber().publish('view/modal/validationErrors/element/clicked', [ + $(this).attr(getHelper().getDomElementDataAttribute('elementIdentifier')) + ]); + $('a', modalContent).off(); + Modal.currentModal.trigger('modal-dismiss'); + }); + }; + + /** + * @private + * + * @param object + * @return object + * @throws 1479162557 + */ + function _buildTitleByFormElement(formElement) { + var label; + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1479162557); + + return $('<span></span>').text((formElement.get('label') + ? formElement.get('label') + : formElement.get('identifier'))); + }; + + /* ************************************************************* + * Public Methodes + * ************************************************************/ + + /** + * @public + * + * @param object formElement + * @return void + * @publish view/modal/removeFormElement/perform + */ + function showRemoveFormElementModal(formElement) { + _showRemoveElementModal('view/modal/removeFormElement/perform', [formElement]); + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param string collectionName + * @param object formElement + * @return void + * @publish view/modal/removeCollectionElement/perform + * @throws 1478894420 + * @throws 1478894421 + */ + function showRemoveCollectionElementModal(collectionElementIdentifier, collectionName, formElement) { + assert( + getUtility().isNonEmptyString(collectionElementIdentifier), + 'Invalid parameter "collectionElementIdentifier"', + 1478894420 + ); + assert( + getUtility().isNonEmptyString(collectionName), + 'Invalid parameter "collectionName"', + 1478894421 + ); + + _showRemoveElementModal('view/modal/removeCollectionElement/perform', [collectionElementIdentifier, collectionName, formElement]); + }; + + /** + * @public + * + * @return void + * @publish view/modal/close/perform + */ + function showCloseConfirmationModal() { + var modalButtons = []; + + modalButtons.push({ + text: getFormElementDefinition(getRootFormElement(), 'modalCloseCancleButton'), + active: true, + btnClass: getHelper().getDomElementClassName('buttonDefault'), + name: 'cancel', + trigger: function () { + Modal.currentModal.trigger('modal-dismiss'); + } + }); + + modalButtons.push({ + text: getFormElementDefinition(getRootFormElement(), 'modalCloseConfirmButton'), + active: true, + btnClass: getHelper().getDomElementClassName('buttonWarning'), + name: 'confirm', + trigger: function () { + getPublisherSubscriber().publish('view/modal/close/perform', []); + Modal.currentModal.trigger('modal-dismiss'); + } + }); + + Modal.show( + getFormElementDefinition(getRootFormElement(), 'modalCloseDialogTitle'), + getFormElementDefinition(getRootFormElement(), 'modalCloseDialogMessage'), + Severity.warning, + modalButtons + ); + }; + + /** + * @public + * + * @param string + * @param string + * @return void + */ + function showInsertElementsModal(publisherTopicName) { + var html, template; + + template = getHelper().getTemplate('templateInsertElements'); + if (template.length > 0) { + html = $(template.html()); + _insertElementsModalSetup(html, publisherTopicName); + + Modal.show( + getFormElementDefinition(getRootFormElement(), 'modalInsertElementsDialogTitle'), + $(html), + Severity.info + ); + } + }; + + /** + * @public + * + * @param string + * @param string + * @return void + */ + function showInsertPagesModal(publisherTopicName) { + var html, template; + + template = getHelper().getTemplate('templateInsertPages'); + if (template.length > 0) { + html = $(template.html()); + _insertElementsModalSetup(html, publisherTopicName); + + Modal.show( + getFormElementDefinition(getRootFormElement(), 'modalInsertPagesDialogTitle'), + $(html), + Severity.info + ); + } + }; + + /** + * @public + * + * @param object + * @return void + */ + function showValidationErrorsModal(validationResults) { + var html, template, modalButtons = []; + + modalButtons.push({ + text: getFormElementDefinition(getRootFormElement(), 'modalValidationErrorsConfirmButton'), + active: true, + btnClass: getHelper().getDomElementClassName('buttonDefault'), + name: 'confirm', + trigger: function () { + Modal.currentModal.trigger('modal-dismiss'); + } + }); + + template = getHelper().getTemplate('templateValidationErrors'); + if (template.length > 0) { + html = $(template.html()).clone(); + _validationErrorsModalSetup(html, validationResults); + + Modal.show( + getFormElementDefinition(getRootFormElement(), 'modalValidationErrorsDialogTitle'), + html, + Severity.error, + modalButtons + ); + } + } + + /** + * @public + * + * @param object + * @param object + * @return this + */ + function bootstrap(formEditorApp, configuration) { + _formEditorApp = formEditorApp; + _configuration = $.extend(true, _defaultConfiguration, configuration || {}); + _helperSetup(); + return this; + }; + + /** + * Publish the public methods. + * Implements the "Revealing Module Pattern". + */ + return { + bootstrap: bootstrap, + showCloseConfirmationModal: showCloseConfirmationModal, + showInsertElementsModal: showInsertElementsModal, + showInsertPagesModal: showInsertPagesModal, + showRemoveCollectionElementModal: showRemoveCollectionElementModal, + showRemoveFormElementModal: showRemoveFormElementModal, + showValidationErrorsModal: showValidationErrorsModal + }; + })($, Helper, Modal, Severity, Icons); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/StageComponent.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/StageComponent.js new file mode 100644 index 000000000000..f69b522f65ea --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/StageComponent.js @@ -0,0 +1,1119 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormEditor/StageComponent + */ +define(['jquery', + 'TYPO3/CMS/Form/Backend/FormEditor/Helper', + 'TYPO3/CMS/Backend/Icons', + 'TYPO3/CMS/Form/Backend/Vendor/jquery.mjs.nestedSortable' + ], function($, Helper, Icons) { + 'use strict'; + + return (function($, Helper, Icons) { + + /** + * @private + * + * @var object + */ + var _configuration = null; + + /** + * @private + * + * @var object + */ + var _defaultConfiguration = { + domElementClassNames: { + formElementIsComposit: 't3-form-element-composit', + formElementIsTopLevel: 't3-form-element-toplevel', + noNesting: 'mjs-nestedSortable-no-nesting', + selected: 'selected', + sortable: 'sortable', + previewViewPreviewElement: 't3-form-element-preview' + }, + domElementDataAttributeNames: { + abstractType: 'data-element-abstract-type', + noSorting: 'data-no-sorting' + }, + domElementDataAttributeValues: { + abstractViewToolbar: 'elementToolbar', + abstractViewToolbarNewElement: 'stageElementToolbarNewElement', + abstractViewToolbarNewElementSplitButton: 'stageElementToolbarNewElementSplitButton', + abstractViewToolbarNewElementSplitButtonAfter: 'stageElementToolbarNewElementSplitButtonAfter', + abstractViewToolbarNewElementSplitButtonInside: 'stageElementToolbarNewElementSplitButtonInside', + abstractViewToolbarRemoveElement: 'stageElementToolbarRemoveElement', + buttonHeaderRedo: 'redoButton', + buttonHeaderUndo: 'undoButton', + buttonPaginationPrevious: 'buttonPaginationPrevious', + buttonPaginationNext: 'buttonPaginationNext', + 'FormElement-_ElementToolbar': 'FormElement-_ElementToolbar', + 'FormElement-_UnknownElement': 'FormElement-_UnknownElement', + 'FormElement-AdvancedPassword': 'FormElement-AdvancedPassword', + 'FormElement-Checkbox': 'FormElement-Checkbox', + 'FormElement-ContentElement': 'FormElement-ContentElement', + 'FormElement-DatePicker': 'FormElement-DatePicker', + 'FormElement-Fieldset': 'FormElement-Fieldset', + 'FormElement-FileUpload': 'FormElement-FileUpload', + 'FormElement-Hidden': 'FormElement-Hidden', + 'FormElement-ImageUpload': 'FormElement-ImageUpload', + 'FormElement-MultiCheckbox': 'FormElement-MultiCheckbox', + 'FormElement-MultiSelect': 'FormElement-MultiSelect', + 'FormElement-Page': 'FormElement-Page', + 'FormElement-Password': 'FormElement-Password', + 'FormElement-RadioButton': 'FormElement-RadioButton', + 'FormElement-SingleSelect': 'FormElement-SingleSelect', + 'FormElement-StaticText': 'FormElement-StaticText', + 'FormElement-SummaryPage': 'FormElement-SummaryPage', + 'FormElement-Text': 'FormElement-Text', + 'FormElement-Textarea': 'FormElement-Textarea', + formElementIcon: 'elementIcon', + iconValidator: 't3-form-icon-validator', + multiValueContainer: 'multiValueContainer', + paginationTitle: 'paginationTitle', + stageHeadline: 'formDefinitionLabel', + stagePanel: 'stagePanel', + validatorsContainer: 'validatorsContainer', + validatorIcon: 'validatorIcon' + }, + isSortable: true + }; + + /** + * @private + * + * @var object + */ + var _formEditorApp = null; + + /** + * @private + * + * @var object + */ + var _stageDomElement = null; + + /* ************************************************************* + * Private Methodes + * ************************************************************/ + + /** + * @private + * + * @return void + * @throws 1478268638 + */ + function _helperSetup() { + assert('function' === $.type(Helper.bootstrap), + 'The view model helper does not implement the method "bootstrap"', + 1478268638 + ); + Helper.bootstrap(getFormEditorApp()); + }; + + /** + * @private + * + * @return object + */ + function getFormEditorApp() { + return _formEditorApp; + }; + + /** + * @public + * + * @param object + * @return object + */ + function getHelper(configuration) { + if (getUtility().isUndefinedOrNull(configuration)) { + return Helper.setConfiguration(_configuration); + } + return Helper.setConfiguration(configuration); + }; + + /** + * @private + * + * @return object + */ + function getUtility() { + return getFormEditorApp().getUtility(); + }; + + /** + * @private + * + * @return object + */ + function getViewModel() { + return getFormEditorApp().getViewModel(); + }; + + /** + * @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 object + */ + function getRootFormElement() { + return getFormEditorApp().getRootFormElement(); + }; + + /** + * @private + * + * @return object + */ + function getCurrentlySelectedFormElement() { + return getFormEditorApp().getCurrentlySelectedFormElement(); + }; + + /** + * @private + * + * @return object + */ + function getPublisherSubscriber() { + return getFormEditorApp().getPublisherSubscriber(); + }; + + /** + * @private + * + * @param object + * @param string + * @return mixed + */ + function getFormElementDefinition(formElement, formElementDefinitionKey) { + return getFormEditorApp().getFormElementDefinition(formElement, formElementDefinitionKey); + }; + + /** + * @private + * + * @return object + * @return string + * @return void + */ + function _setTemplateTextContent(domElement, content) { + if (getUtility().isNonEmptyString(content)) { + $(domElement).text(content); + } + } + + /** + * @private + * + * @param object + * @param object + * @return void + * @publish view/stage/abstract/render/template/perform + */ + function _renderTemplateDispatcher(formElement, template) { + switch (formElement.get('type')) { + case 'Checkbox': + renderCheckboxTemplate(formElement, template); + break; + case 'FileUpload': + case 'ImageUpload': + renderFileUploadTemplates(formElement, template); + break; + case 'SingleSelect': + case 'RadioButton': + case 'MultiSelect': + case 'MultiCheckbox': + renderSelectTemplates(formElement, template); + break; + case 'Textarea': + case 'AdvancedPassword': + case 'Password': + case 'Text': + renderSimpleTemplateWithValidators(formElement, template); + break; + case 'Fieldset': + case 'SummaryPage': + case 'Page': + case 'DatePicker': + case 'StaticText': + case 'Hidden': + case 'ContentElement': + renderSimpleTemplate(formElement, template); + break; + } + getPublisherSubscriber().publish('view/stage/abstract/render/template/perform', [formElement, template]); + }; + + /** + * @private + * + * @param object + * @return object + * @throws 1478987818 + */ + function _renderNestedSortableListItem(formElement) { + var childFormElements, childList, listItem, template; + + listItem = $('<li></li>'); + if (!getFormElementDefinition(formElement, '_isCompositeFormElement')) { + listItem.addClass(getHelper().getDomElementClassName('noNesting')); + } + + if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) { + listItem.addClass(getHelper().getDomElementClassName('formElementIsTopLevel')); + } + if (getFormElementDefinition(formElement, '_isCompositeFormElement')) { + listItem.addClass(getHelper().getDomElementClassName('formElementIsComposit')); + } + + try { + template = getHelper().getTemplate('FormElement-' + formElement.get('type')).clone(); + } catch(error) { + template = getHelper().getTemplate('FormElement-_UnknownElement').clone(); + assert( + template.length, + 'No template found for element "' + formElement.get('__identifierPath') + '"', + 1478987818 + ); + } + + template = $('<div></div>') + .attr(getHelper().getDomElementDataAttribute('elementIdentifier'), formElement.get('__identifierPath')) + .append($(template.html())); + + if (getFormElementDefinition(formElement, '_isCompositeFormElement')) { + template.attr(getHelper().getDomElementDataAttribute('abstractType'), 'isCompositeFormElement'); + } + if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) { + template.attr(getHelper().getDomElementDataAttribute('abstractType'), 'isTopLevelFormElement'); + } + listItem.append(template); + + _renderTemplateDispatcher(formElement, template); + + childFormElements = formElement.get('renderables'); + childList = null; + if ('array' === $.type(childFormElements)) { + childList = $('<ol></ol>'); + if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) { + childList.addClass(getHelper().getDomElementClassName('sortable')); + } + for (var i = 0, len = childFormElements.length; i < len; ++i) { + childList.append(_renderNestedSortableListItem(childFormElements[i])); + } + } + + if (childList) { + listItem.append(childList); + } + return listItem; + }; + + /** + * @private + * + * @return void + * @publish view/stage/abstract/dnd/start + * @publish view/stage/abstract/dnd/stop + * @publish view/stage/abstract/dnd/change + * @publish view/stage/abstract/dnd/update + */ + function _addSortableEvents() { + $('ol.' + getHelper().getDomElementClassName('sortable'), _stageDomElement).nestedSortable({ + forcePlaceholderSize: true, + handle: 'div' + getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'), + helper: 'clone', + items: 'li:not(' + getHelper().getDomElementDataAttribute('noSorting', 'bracesWithKey') + ')', + opacity: .6, + revert: 250, + delay: 200, + tolerance: 'pointer', + toleranceElement: '> div', + + start: function(e, o) { + getPublisherSubscriber().publish('view/stage/abstract/dnd/start', [$(o.item), $(o.placeholder)]); + }, + stop: function(e, o) { + getPublisherSubscriber().publish('view/stage/abstract/dnd/stop', [ + getAbstractViewFormElementIdentifierPathWithinDomElement($(o.item)) + ]); + }, + change: function(e, o) { + var enclosingCompositeFormElement, parentFormElementIdentifierPath; + + parentFormElementIdentifierPath = getAbstractViewParentFormElementIdentifierPathWithinDomElement($(o.placeholder)); + if (parentFormElementIdentifierPath) { + enclosingCompositeFormElement = getFormEditorApp() + .findEnclosingCompositeFormElementWhichIsNotOnTopLevel(parentFormElementIdentifierPath); + } + getPublisherSubscriber().publish('view/stage/abstract/dnd/change', [ + $(o.placeholder), + parentFormElementIdentifierPath, enclosingCompositeFormElement + ]); + }, + update: function(e, o) { + var nextFormElementIdentifierPath, movedFormElement, movedFormElementIdentifierPath, parentFormElementIdentifierPath, previousFormElementIdentifierPath; + + movedFormElementIdentifierPath = getAbstractViewFormElementIdentifierPathWithinDomElement($(o.item)); + previousFormElementIdentifierPath = getAbstractViewSiblingFormElementIdentifierPathWithinDomElement($(o.item), 'prev'); + nextFormElementIdentifierPath = getAbstractViewSiblingFormElementIdentifierPathWithinDomElement($(o.item), 'next'); + + getPublisherSubscriber().publish('view/stage/abstract/dnd/update', [ + $(o.item), + movedFormElementIdentifierPath, + previousFormElementIdentifierPath, + nextFormElementIdentifierPath + ]); + } + }); + }; + + /* ************************************************************* + * Public Methodes + * ************************************************************/ + + /** + * @public + * + * @return object + */ + function getStageDomElement() { + return _stageDomElement; + }; + + /** + * @public + * + * @param object + * @return object + * @throws 1479037151 + */ + function buildTitleByFormElement(formElement) { + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getRootFormElement(); + } + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1479037151); + + return $('<span></span>') + .text((formElement.get('label') ? formElement.get('label') : formElement.get('identifier'))); + }; + + /** + * @public + * + * @param string title + * @return void + */ + function setStageHeadline(title) { + if (getUtility().isUndefinedOrNull(title)) { + title = buildTitleByFormElement(); + } + + $(getHelper().getDomElementDataIdentifierSelector('stageHeadline')).html(title); + }; + + /** + * @public + * + * @return object + */ + function getStagePanelDomElement() { + return $(getHelper().getDomElementDataIdentifierSelector('stagePanel')); + }; + + /** + * @public + * + * @return void + */ + function renderPagination() { + var pageCount; + + pageCount = getRootFormElement().get('renderables').length; + + getViewModel().enableButton($(getHelper().getDomElementDataIdentifierSelector('buttonPaginationPrevious'))); + getViewModel().enableButton($(getHelper().getDomElementDataIdentifierSelector('buttonPaginationNext'))); + + if (getFormEditorApp().getCurrentlySelectedPageIndex() === 0) { + getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonPaginationPrevious'))); + } + + if (pageCount === 1 || getFormEditorApp().getCurrentlySelectedPageIndex() === (pageCount - 1)) { + getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonPaginationNext'))); + } + + $(getHelper().getDomElementDataIdentifierSelector('paginationTitle')).text( + getFormElementDefinition(getRootFormElement(), 'paginationTitle') + .replace('{0}', getFormEditorApp().getCurrentlySelectedPageIndex() + 1) + .replace('{1}', pageCount) + ); + }; + + /** + * @public + * + * @return void + */ + function renderUndoRedo() { + getViewModel().enableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderUndo'))); + getViewModel().enableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderRedo'))); + + if (getFormEditorApp().getCurrentApplicationStatePosition() + 1 >= getFormEditorApp().getCurrentApplicationStates()) { + getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderUndo'))); + } + if (getFormEditorApp().getCurrentApplicationStatePosition() === 0) { + getViewModel().disableButton($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderRedo'))); + } + }; + + /** + * @public + * + * @param object + * @return string + */ + function getAllFormElementDomElements() { + return $( getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'), + _stageDomElement + ); + }; + + /* ************************************************************* + * Abstract stage + * ************************************************************/ + + /** + * @public + * + * @param int + * @return object + * @throws 1478721208 + */ + function renderFormDefinitionPageAsSortableList(pageIndex) { + assert( + 'number' === $.type(pageIndex), + 'Invalid parameter "pageIndex"', + 1478721208 + ); + + return $('<ol></ol>') + .append(_renderNestedSortableListItem(getRootFormElement().get('renderables')[pageIndex])); + }; + + /** + * @public + * + * @param object + * @return string + */ + function getAbstractViewParentFormElementWithinDomElement(element) { + return $(element) + .parent() + .closest('li') + .find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')) + .first(); + }; + + /** + * @public + * + * @param object + * @return string + */ + function getAbstractViewParentFormElementIdentifierPathWithinDomElement(element) { + return getAbstractViewParentFormElementWithinDomElement(element) + .attr(getHelper().getDomElementDataAttribute('elementIdentifier')); + }; + + /** + * @public + * + * @param object + * @return string + */ + function getAbstractViewFormElementWithinDomElement(element) { + return $(element) + .find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')) + .first(); + }; + + /** + * @public + * + * @param object + * @return string + */ + function getAbstractViewFormElementIdentifierPathWithinDomElement(element) { + return getAbstractViewFormElementWithinDomElement($(element)) + .attr(getHelper().getDomElementDataAttribute('elementIdentifier')); + }; + + /** + * @private + * + * @param object + * @param string + * @return string + */ + function getAbstractViewSiblingFormElementIdentifierPathWithinDomElement(element, position) { + var formElementIdentifierPath; + + if (getUtility().isUndefinedOrNull(position)) { + position = 'prev'; + } + formElementIdentifierPath = getAbstractViewFormElementIdentifierPathWithinDomElement(element); + element = (position === 'prev') ? $(element).prev('li') : $(element).next('li'); + return element.find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')) + .not(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKeyValue', [formElementIdentifierPath])) + .first() + .attr(getHelper().getDomElementDataAttribute('elementIdentifier')); + }; + + /** + * @public + * + * @param string|object + * @return object + */ + function getAbstractViewFormElementDomElement(formElement) { + var formElementIdentifierPath; + + if ('string' === $.type(formElement)) { + formElementIdentifierPath = formElement; + } else { + if (getUtility().isUndefinedOrNull(formElement)) { + formElementIdentifierPath = getCurrentlySelectedFormElement().get('__identifierPath'); + } else { + formElementIdentifierPath = formElement.get('__identifierPath'); + } + } + return $(getHelper() + .getDomElementDataAttribute('elementIdentifier', 'bracesWithKeyValue', [formElementIdentifierPath]), _stageDomElement); + }; + + /** + * @public + * + * @return void + */ + function removeAllStageToolbars() { + $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbar'), _stageDomElement).off().empty().remove(); + }; + + /** + * @public + * + * @param object + * @return object + * @publish view/insertElements/perform/after + * @publish view/insertElements/perform/inside + * @throws 1479035778 + */ + function createAbstractViewFormElementToolbar(formElement) { + var formElementTypeDefinition, template; + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1479035778); + + formElementTypeDefinition = getFormElementDefinition(formElement); + if (formElementTypeDefinition['_isTopLevelFormElement']) { + return $(); + } + + template = getHelper().getTemplate('FormElement-_ElementToolbar').clone(); + if (!template.length) { + return $(); + } + + template = $($(template.html())); + + getHelper().getTemplatePropertyDomElement('_type', template).text(formElement.get('type')); + getHelper().getTemplatePropertyDomElement('_identifier', template).text(formElement.get('identifier')); + + if (formElementTypeDefinition['_isCompositeFormElement']) { + getViewModel().hideComponent($(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarNewElement'), template)); + + $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarNewElementSplitButtonAfter'), template).on('click', function(e) { + getPublisherSubscriber().publish('view/stage/abstract/elementToolbar/button/newElement/clicked', ['view/insertElements/perform/after']); + }); + $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarNewElementSplitButtonInside'), template).on('click', function(e) { + getPublisherSubscriber().publish('view/stage/abstract/elementToolbar/button/newElement/clicked', ['view/insertElements/perform/inside']); + }); + } else { + getViewModel().hideComponent($(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarNewElementSplitButton'), template)); + + $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarNewElement'), template).on('click', function(e) { + getPublisherSubscriber().publish('view/stage/abstract/elementToolbar/button/newElement/clicked', ['view/insertElements/perform/after']); + }); + } + + $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbarRemoveElement'), template).on('click', function(e) { + getViewModel().showRemoveFormElementModal(); + }); + + return template; + }; + + /** + * @public + * + * @param object + * @param object + * @param bool + * @return void + */ + function createAndAddAbstractViewFormElementToolbar(selectedFormElementDomElement, formElement, useFadeEffect) { + var toolbar; + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + + if (useFadeEffect) { + createAbstractViewFormElementToolbar(formElement).fadeOut(0, function() { + selectedFormElementDomElement.prepend($(this)); + $(getHelper().getDomElementDataIdentifierSelector('abstractViewToolbar'), selectedFormElementDomElement).fadeIn('fast'); + }); + } else { + selectedFormElementDomElement.prepend(createAbstractViewFormElementToolbar(formElement)); + } + + }; + + /** + * @public + * + * @param int + * @param function + * @return void + * @publish view/stage/dnd/stop + * @publish view/stage/element/clicked + * @throws 1478169511 + */ + function renderAbstractStageArea(pageIndex, callback) { + if (getUtility().isUndefinedOrNull(pageIndex)) { + pageIndex = getFormEditorApp().getCurrentlySelectedPageIndex(); + } + _stageDomElement.off().empty().append(renderFormDefinitionPageAsSortableList(pageIndex)); + + _stageDomElement.on("click", function(e) { + var formElementIdentifierPath; + + formElementIdentifierPath = $(e.target) + .closest(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')) + .attr(getHelper().getDomElementDataAttribute('elementIdentifier')); + if ( + getUtility().isUndefinedOrNull(formElementIdentifierPath) + || !getUtility().isNonEmptyString(formElementIdentifierPath) + ) { + return; + } + + getPublisherSubscriber().publish('view/stage/element/clicked', [formElementIdentifierPath]); + }); + + if (_configuration['isSortable']) { + _addSortableEvents(); + } + + if ('function' === $.type(callback)) { + callback(); + } + }; + + + /* ************************************************************* + * Preview stage + * ************************************************************/ + + /** + * @public + * + * @param string html + * @return void + * @throws 1475424409 + */ + function renderPreviewStageArea(html) { + assert(getUtility().isNonEmptyString(html), 'Invalid parameter "html"', 1475424409); + + _stageDomElement.off().empty().html(html); + + $(':input', _stageDomElement).prop('disabled', 'disabled').on('click dblclick select focus keydown keypress keyup mousedown mouseup', function(e) { + return e.preventDefault(); + }); + + $('form', _stageDomElement).submit(function(e) { + return e.preventDefault(); + }); + + getAllFormElementDomElements().each(function(i, element) { + var formElement, metaLabel; + + formElement = getFormEditorApp() + .getFormElementByIdentifierPath($(this).data('elementIdentifierPath')); + + if ( + !getFormElementDefinition(formElement, '_isTopLevelFormElement') + && getFormElementDefinition(formElement, '_isCompositeFormElement') + ) { + $(this).tooltip({ + title: 'identifier: ' + formElement.get('identifier') + ' (type: ' + formElement.get('type') + ')', + placement : 'rigth' + }); + } else if ( + !getFormElementDefinition(formElement, '_isTopLevelFormElement') + && !getFormElementDefinition(formElement, '_isCompositeFormElement') + ) { + $(this).tooltip({ + title: 'identifier: ' + formElement.get('identifier') + ' (type: ' + formElement.get('type') + ')', + placement : 'left' + }); + } + + if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) { + $(this).addClass(getHelper().getDomElementClassName('formElementIsTopLevel')); + } + if (getFormElementDefinition(formElement, '_isCompositeFormElement')) { + $(this).addClass(getHelper().getDomElementClassName('formElementIsComposit')); + } + }); + + }; + + /* ************************************************************* + * Template rendering + * ************************************************************/ + + /** + * @public + * + * @param object + * @param template + * @param function + * @return void + */ + function eachTemplateProperty(formElement, template, callback) { + $(getHelper().getDomElementDataAttribute('templateProperty', 'bracesWithKey'), template).each(function(i, element) { + var propertyPath, propertyValue; + + propertyPath = $(element).attr(getHelper().getDomElementDataAttribute('templateProperty')); + propertyValue = formElement.get(propertyPath); + + if ('function' === $.type(callback)) { + callback(propertyPath, propertyValue, element); + } + }); + }; + + /** + * @private + * + * @return object + * @return object + * @return void + */ + function renderCheckboxTemplate(formElement, template) { + renderSimpleTemplateWithValidators(formElement, template); + + eachTemplateProperty(formElement, template, function(propertyPath, propertyValue, domElement) { + if ( + ('boolean' === $.type(propertyValue) && propertyValue) + || propertyValue === 'true' + || propertyValue === 1 + || propertyValue === "1" + ) { + $(domElement).addClass(getHelper().getDomElementClassName('noNesting')); + } + }); + }; + + /** + * @public + * + * @return object + * @return object + * @return void + * @throws 1479035696 + */ + function renderSimpleTemplate(formElement, template) { + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1479035696); + + eachTemplateProperty(formElement, template, function(propertyPath, propertyValue, domElement) { + _setTemplateTextContent(domElement, propertyValue); + }); + + Icons.getIcon( + getFormElementDefinition(formElement, 'iconIdentifier'), + Icons.sizes.small, + null, + Icons.states.default, + Icons.markupIdentifiers.inline + ).done(function(icon) { + $(getHelper().getDomElementDataIdentifierSelector('formElementIcon'), template) + .append($(icon).addClass(getHelper().getDomElementClassName('icon'))); + }); + + getHelper() + .getTemplatePropertyDomElement('_type', template) + .append(formElement.get('type')); + getHelper() + .getTemplatePropertyDomElement('_identifier', template) + .append(formElement.get('identifier')); + }; + + /** + * @public + * + * @return object + * @return object + * @return void + * @throws 1479035674 + */ + function renderSimpleTemplateWithValidators(formElement, template) { + var validators, validatorsCountWithoutRequired, validatorsTemplateContent; + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1479035674); + + renderSimpleTemplate(formElement, template); + + validatorsTemplateContent = $( + getHelper().getDomElementDataIdentifierSelector('validatorsContainer'), + $(template) + ).clone(); + + $(getHelper().getDomElementDataIdentifierSelector('validatorsContainer'), $(template)).empty(); + validators = formElement.get('validators'); + + if ('array' === $.type(validators)) { + validatorsCountWithoutRequired = 0; + if (validators.length > 0) { + for (var i = 0, len = validators.length; i < len; ++i) { + var collectionElementConfiguration, rowTemplate; + + if ('NotEmpty' === validators[i]['identifier']) { + getHelper() + .getTemplatePropertyDomElement('_required', template) + .text('*'); + continue; + } + validatorsCountWithoutRequired++; + + collectionElementConfiguration = getFormEditorApp() + .getFormEditorDefinition('validators', validators[i]['identifier']); + rowTemplate = $($(validatorsTemplateContent).clone()); + + getHelper() + .getTemplatePropertyDomElement('_label', rowTemplate) + .append(collectionElementConfiguration['label']); + $(getHelper().getDomElementDataIdentifierSelector('validatorsContainer'), $(template)) + .append(rowTemplate.html()); + } + + if (validatorsCountWithoutRequired > 0) { + Icons.getIcon( + getHelper().getDomElementDataAttributeValue('iconValidator'), + Icons.sizes.small, + null, + Icons.states.default, + Icons.markupIdentifiers.inline + ).done(function(icon) { + $(getHelper().getDomElementDataIdentifierSelector('validatorIcon'), $(template)) + .append($(icon).addClass(getHelper().getDomElementClassName('icon'))); + }); + } + } + } + }; + + /** + * @public + * + * @return object + * @return object + * @return void + */ + function renderSelectTemplates(formElement, template) { + var appendMultiValue, defaultValue, multiValueTemplateContent, propertyPath, propertyValue; + + multiValueTemplateContent = $( + getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), + $(template) + ).clone(); + $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template)).empty(); + + renderSimpleTemplateWithValidators(formElement, template); + + propertyPath = $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template)) + .attr(getHelper().getDomElementDataAttribute('templateProperty')); + + propertyValue = formElement.get(propertyPath); + + appendMultiValue = function(label, value, defaultValue) { + var isPreselected, rowTemplate; + + isPreselected = false; + rowTemplate = $($(multiValueTemplateContent).clone()); + + for (var defaultValueKey in defaultValue) { + if (!defaultValue.hasOwnProperty(defaultValueKey)) { + continue; + } + if (defaultValue[defaultValueKey] === value) { + isPreselected = true; + break; + } + } + + getHelper().getTemplatePropertyDomElement('_label', rowTemplate).append(label); + + if (isPreselected) { + getHelper().getTemplatePropertyDomElement('_label', rowTemplate).addClass( + getHelper().getDomElementClassName('selected') + ); + } + + $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template)) + .append(rowTemplate.html()); + }; + + defaultValue = formElement.get('defaultValue'); + + if (getFormEditorApp().getUtility().isUndefinedOrNull(defaultValue)) { + defaultValue = {}; + } else if ('string' === $.type(defaultValue)) { + defaultValue = {0: defaultValue}; + } + + if ('object' === $.type(propertyValue)) { + for (var propertyValueKey in propertyValue) { + if (!propertyValue.hasOwnProperty(propertyValueKey)) { + continue; + } + appendMultiValue(propertyValue[propertyValueKey], propertyValueKey, defaultValue); + } + } else if ('array' === $.type(propertyValue)) { + for (var i = 0, len = propertyValue.length; i < len; ++i) { + appendMultiValue(propertyValue[i]['_label'], propertyValue[i]['_value'], defaultValue); + } + } + }; + + /** + * @public + * + * @return object + * @return object + * @return void + */ + function renderFileUploadTemplates(formElement, template) { + var appendMultiValue, multiValueTemplateContent, propertyPath, propertyValue; + + multiValueTemplateContent = $( + getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), + $(template) + ).clone(); + $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template)).empty(); + + renderSimpleTemplateWithValidators(formElement, template); + + propertyPath = $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template)) + .attr(getHelper().getDomElementDataAttribute('templateProperty')); + propertyValue = formElement.get(propertyPath); + + appendMultiValue = function(value) { + var rowTemplate; + + rowTemplate = $($(multiValueTemplateContent).clone()); + + getHelper().getTemplatePropertyDomElement('_value', rowTemplate).append(value); + $(getHelper().getDomElementDataIdentifierSelector('multiValueContainer'), $(template)) + .append(rowTemplate.html()); + }; + + if ('object' === $.type(propertyValue)) { + for (var propertyValueKey in propertyValue) { + if (!propertyValue.hasOwnProperty(propertyValueKey)) { + continue; + } + appendMultiValue(propertyValue[propertyValueKey]); + } + } else if ('array' === $.type(propertyValue)) { + for (var i = 0, len = propertyValue.length; i < len; ++i) { + appendMultiValue(propertyValue[i]); + } + } + }; + + /** + * @public + * + * @param object + * @param object + * @param object + * @return this + * @throws 1478992119 + */ + function bootstrap(formEditorApp, appendToDomElement, configuration) { + _formEditorApp = formEditorApp; + assert('object' === $.type(appendToDomElement), 'Invalid parameter "appendToDomElement"', 1478992119); + + _stageDomElement = $(appendToDomElement); + _configuration = $.extend(true, _defaultConfiguration, configuration || {}); + _helperSetup(); + return this; + }; + + /** + * Publish the public methods. + * Implements the "Revealing Module Pattern". + */ + return { + bootstrap: bootstrap, + buildTitleByFormElement: buildTitleByFormElement, + createAndAddAbstractViewFormElementToolbar: createAndAddAbstractViewFormElementToolbar, + createAbstractViewFormElementToolbar: createAbstractViewFormElementToolbar, + eachTemplateProperty: eachTemplateProperty, + getAbstractViewFormElementDomElement: getAbstractViewFormElementDomElement, + getAbstractViewFormElementWithinDomElement: getAbstractViewFormElementWithinDomElement, + getAbstractViewFormElementIdentifierPathWithinDomElement: getAbstractViewFormElementIdentifierPathWithinDomElement, + getAbstractViewParentFormElementWithinDomElement: getAbstractViewParentFormElementWithinDomElement, + getAbstractViewParentFormElementIdentifierPathWithinDomElement: getAbstractViewParentFormElementIdentifierPathWithinDomElement, + getAbstractViewSiblingFormElementIdentifierPathWithinDomElement: getAbstractViewSiblingFormElementIdentifierPathWithinDomElement, + getAllFormElementDomElements: getAllFormElementDomElements, + getStageDomElement: getStageDomElement, + getStagePanelDomElement: getStagePanelDomElement, + removeAllStageToolbars: removeAllStageToolbars, + renderAbstractStageArea: renderAbstractStageArea, + renderCheckboxTemplate: renderCheckboxTemplate, + renderFileUploadTemplates: renderFileUploadTemplates, + renderFormDefinitionPageAsSortableList: renderFormDefinitionPageAsSortableList, + renderPagination: renderPagination, + renderPreviewStageArea: renderPreviewStageArea, + renderSelectTemplates: renderSelectTemplates, + renderSimpleTemplate: renderSimpleTemplate, + renderSimpleTemplateWithValidators: renderSimpleTemplateWithValidators, + renderUndoRedo: renderUndoRedo, + setStageHeadline: setStageHeadline + }; + })($, Helper, Icons); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/TreeComponent.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/TreeComponent.js new file mode 100644 index 000000000000..00d890ff3b5a --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/TreeComponent.js @@ -0,0 +1,648 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormEditor/TreeComponent + */ +define(['jquery', + 'TYPO3/CMS/Form/Backend/FormEditor/Helper', + 'TYPO3/CMS/Backend/Icons', + 'TYPO3/CMS/Form/Backend/Vendor/jquery.mjs.nestedSortable' + ], function($, Helper, Icons) { + 'use strict'; + + return (function($, Helper, Icons) { + + /** + * @private + * + * @var object + */ + var _configuration = null; + + /** + * @private + * + * @var object + */ + var _expanderStates = {}; + + /** + * @private + * + * @var object + */ + var _defaultConfiguration = { + domElementClassNames: { + collapsed: 'mjs-nestedSortable-collapsed', + expanded: 'mjs-nestedSortable-expanded', + hasChildren: 't3-form-element-has-children', + sortable: 'sortable', + svgLinkWrapper: 'svg-wrapper', + noNesting: 'mjs-nestedSortable-no-nesting' + }, + domElementDataAttributeNames: { + abstractType: 'data-element-abstract-type' + }, + domElementDataAttributeValues: { + collapse: 'actions-pagetree-collapse', + expander: 'treeExpander', + title: 'treeTitle' + }, + isSortable: true, + svgLink: { + height: 15, + paths: { + angle: 'M0 0 V21 H15', + vertical: 'M0 0 V21 H0', + hidden: 'M0 0 V0 H0' + }, + width: 15 + } + }; + + /** + * @private + * + * @var object + */ + var _formEditorApp = null; + + /** + * @private + * + * @var object + */ + var _treeDomElement = null; + + /* ************************************************************* + * Private Methodes + * ************************************************************/ + + /** + * @private + * + * @return void + * @throws 1478268638 + */ + function _helperSetup() { + assert('function' === $.type(Helper.bootstrap), + 'The view model helper does not implement the method "bootstrap"', + 1478268638 + ); + Helper.bootstrap(getFormEditorApp()); + }; + + /** + * @private + * + * @return object + */ + function getFormEditorApp() { + return _formEditorApp; + }; + + /** + * @public + * + * @param object + * @return object + */ + function getHelper(configuration) { + if (getUtility().isUndefinedOrNull(configuration)) { + return Helper.setConfiguration(_configuration); + } + return Helper.setConfiguration(configuration); + }; + + /** + * @private + * + * @return object + */ + function getUtility() { + return getFormEditorApp().getUtility(); + }; + + /** + * @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 object + */ + function getRootFormElement() { + return getFormEditorApp().getRootFormElement(); + }; + + /** + * @private + * + * @return object + */ + function getCurrentlySelectedFormElement() { + return getFormEditorApp().getCurrentlySelectedFormElement(); + }; + + /** + * @private + * + * @return object + */ + function getPublisherSubscriber() { + return getFormEditorApp().getPublisherSubscriber(); + }; + + /** + * @private + * + * @param object + * @param string + * @return mixed + */ + function getFormElementDefinition(formElement, formElementDefinitionKey) { + return getFormEditorApp().getFormElementDefinition(formElement, formElementDefinitionKey); + }; + + /** + * @private + * + * @return object + */ + function _getLinkSvg(type) { + return $('<span class="' + getHelper().getDomElementClassName('svgLinkWrapper') + '">' + + '<svg version="1.1" width="' + _configuration['svgLink']['width'] + '" height="' + _configuration['svgLink']['height'] + '">' + + '<path class="link" d="' + _configuration['svgLink']['paths'][type] + '">' + + '</svg>' + + '</span>'); + }; + + /** + * @private + * + * @param object + * @return object + * @publish view/tree/render/listItemAdded + * @throws 1478715704 + */ + function _renderNestedSortableListItem(formElement) { + var childFormElements, childList, expanderItem, isLastFormElementWithinParentFormElement, listItem, listItemContent, searchElement; + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1478715704); + + isLastFormElementWithinParentFormElement = false; + if (formElement.get('__identifierPath') === getFormEditorApp().getLastFormElementWithinParentFormElement(formElement).get('__identifierPath')) { + isLastFormElementWithinParentFormElement = true; + } + + listItem = $('<li></li>'); + if (!getFormElementDefinition(formElement, '_isCompositeFormElement')) { + listItem.addClass(getHelper().getDomElementClassName('noNesting')); + } + + listItemContent = $('<div></div>') + .attr(getHelper().getDomElementDataAttribute('elementIdentifier'), formElement.get('__identifierPath')) + .append( + $('<span></span>') + .attr(getHelper().getDomElementDataAttribute('identifier'), getHelper().getDomElementDataAttributeValue('title')) + .html(buildTitleByFormElement(formElement)) + ); + + if (getFormElementDefinition(formElement, '_isCompositeFormElement')) { + listItemContent.attr(getHelper().getDomElementDataAttribute('abstractType'), 'isCompositeFormElement'); + } + if (getFormElementDefinition(formElement, '_isTopLevelFormElement')) { + listItemContent.attr(getHelper().getDomElementDataAttribute('abstractType'), 'isTopLevelFormElement'); + } + + expanderItem = $('<span></span>').attr('data-identifier', getHelper().getDomElementDataAttributeValue('expander')); + listItemContent.prepend(expanderItem); + + Icons.getIcon(getFormElementDefinition(formElement, 'iconIdentifier'), Icons.sizes.small, null, Icons.states.default).done(function(icon) { + expanderItem.after( + $(icon).addClass(getHelper().getDomElementClassName('icon')) + .tooltip({ + title: 'identifier: ' + formElement.get('identifier'), + placement: 'right' + }) + ); + + if (getFormElementDefinition(formElement, '_isCompositeFormElement')) { + if (formElement.get('renderables') && formElement.get('renderables').length > 0) { + Icons.getIcon(getHelper().getDomElementDataAttributeValue('collapse'), Icons.sizes.small).done(function(icon) { + expanderItem.before(_getLinkSvg('angle')).html($(icon)); + listItem.addClass(getHelper().getDomElementClassName('hasChildren')); + }); + } else { + expanderItem.before(_getLinkSvg('angle')).remove(); + } + } else { + listItemContent.prepend(_getLinkSvg('angle')); + expanderItem.remove(); + } + + searchElement = formElement.get('__parentRenderable'); + while (searchElement) { + if (searchElement.get('__identifierPath') === getRootFormElement().get('__identifierPath')) { + break; + } + + if (searchElement.get('__identifierPath') === getFormEditorApp().getLastFormElementWithinParentFormElement(searchElement).get('__identifierPath')) { + listItemContent.prepend(_getLinkSvg('hidden')); + } else { + listItemContent.prepend(_getLinkSvg('vertical')); + } + searchElement = searchElement.get('__parentRenderable'); + } + }); + listItem.append(listItemContent); + + getPublisherSubscriber().publish('view/tree/render/listItemAdded', [listItem, formElement]); + childFormElements = formElement.get('renderables'); + childList = null; + if ('array' === $.type(childFormElements)) { + childList = $('<ol></ol>'); + for (var i = 0, len = childFormElements.length; i < len; ++i) { + childList.append(_renderNestedSortableListItem(childFormElements[i])); + } + } + + if (childList) { + listItem.append(childList); + } + return listItem; + }; + + /** + * @private + * + * @return void + * @publish view/tree/dnd/stop + * @publish view/tree/dnd/change + * @publish view/tree/dnd/update + */ + function _addSortableEvents() { + $('ol.' + getHelper().getDomElementClassName('sortable'), _treeDomElement).nestedSortable({ + forcePlaceholderSize: true, + protectRoot: true, + isTree: true, + handle: 'div' + getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'), + helper: 'clone', + items: 'li', + opacity: .6, + revert: 250, + delay: 200, + tolerance: 'pointer', + toleranceElement: '> div', + + stop: function(e, o) { + getPublisherSubscriber().publish('view/tree/dnd/stop', [getTreeNodeIdentifierPathWithinDomElement($(o.item))]); + }, + change: function(e, o) { + var enclosingCompositeFormElement, parentFormElementIdentifierPath; + + parentFormElementIdentifierPath = getParentTreeNodeIdentifierPathWithinDomElement($(o.placeholder)); + if (parentFormElementIdentifierPath) { + enclosingCompositeFormElement = getFormEditorApp().findEnclosingCompositeFormElementWhichIsNotOnTopLevel(parentFormElementIdentifierPath); + } + getPublisherSubscriber().publish('view/tree/dnd/change', [$(o.placeholder), parentFormElementIdentifierPath, enclosingCompositeFormElement]); + }, + update: function(e, o) { + var nextFormElementIdentifierPath, movedFormElementIdentifierPath, previousFormElementIdentifierPath; + + movedFormElementIdentifierPath = getTreeNodeIdentifierPathWithinDomElement($(o.item)); + previousFormElementIdentifierPath = getSiblingTreeNodeIdentifierPathWithinDomElement($(o.item), 'prev'); + nextFormElementIdentifierPath = getSiblingTreeNodeIdentifierPathWithinDomElement($(o.item), 'next'); + + getPublisherSubscriber().publish('view/tree/dnd/update', [$(o.item), movedFormElementIdentifierPath, previousFormElementIdentifierPath, nextFormElementIdentifierPath]); + } + }); + }; + + /** + * @private + * + * @return void + */ + function _saveExpanderStates() { + var addStates; + + addStates = function(formElement) { + var childFormElements, treeNode; + + if (getFormElementDefinition(formElement, '_isCompositeFormElement')) { + treeNode = getTreeNode(formElement); + if (treeNode.length) { + if (treeNode.closest('li').hasClass(getHelper().getDomElementClassName('expanded'))) { + _expanderStates[formElement.get('__identifierPath')] = true; + } else { + _expanderStates[formElement.get('__identifierPath')] = false; + } + } + + if (getUtility().isUndefinedOrNull(_expanderStates[formElement.get('__identifierPath')])) { + _expanderStates[formElement.get('__identifierPath')] = true; + } + } + + childFormElements = formElement.get('renderables'); + if ('array' === $.type(childFormElements)) { + for (var i = 0, len = childFormElements.length; i < len; ++i) { + addStates(childFormElements[i]); + } + } + }; + addStates(getRootFormElement()); + + for (var identifierPath in _expanderStates) { + if (!_expanderStates.hasOwnProperty(identifierPath)) { + continue; + } + try { + getFormEditorApp().getFormElementByIdentifierPath(identifierPath); + } catch(error) { + delete _expanderStates[identifierPath]; + } + } + }; + + /** + * @private + * + * @return void + */ + function _loadExpanderStates() { + for (var identifierPath in _expanderStates) { + var treeNode; + + if (!_expanderStates.hasOwnProperty(identifierPath)) { + continue; + } + treeNode = getTreeNode(identifierPath); + if (treeNode.length) { + if (_expanderStates[identifierPath]) { + treeNode.closest('li') + .removeClass(getHelper().getDomElementClassName('collapsed')) + .addClass(getHelper().getDomElementClassName('expanded')); + } else { + treeNode.closest('li') + .addClass(getHelper().getDomElementClassName('collapsed')) + .removeClass(getHelper().getDomElementClassName('expanded')); + } + } + } + }; + + /* ************************************************************* + * Public Methodes + * ************************************************************/ + + /** + * @public + * + * @param object + * @return object + * @throws 1478721208 + */ + function renderCompositeFormElementChildsAsSortableList(formElement) { + var elementList; + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1478721208); + + elementList = $('<ol></ol>').addClass(getHelper().getDomElementClassName('sortable')); + if ('array' === $.type(formElement.get('renderables'))) { + for (var i = 0, len = formElement.get('renderables').length; i < len; ++i) { + elementList.append(_renderNestedSortableListItem(formElement.get('renderables')[i])); + } + } + return elementList; + }; + + /** + * @public + * + * @return void + * @param object + * @publish view/tree/node/clicked + */ + function renew(formElement) { + if (getFormEditorApp().getUtility().isUndefinedOrNull(formElement)) { + formElement = getRootFormElement(); + } + _saveExpanderStates(); + _treeDomElement.off().empty().append(renderCompositeFormElementChildsAsSortableList(formElement)); + + _treeDomElement.on("click", function(e) { + var formElementIdentifierPath; + + formElementIdentifierPath = $(e.target) + .closest(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')) + .attr(getHelper().getDomElementDataAttribute('elementIdentifier')); + if (getUtility().isUndefinedOrNull(formElementIdentifierPath) || !getUtility().isNonEmptyString(formElementIdentifierPath)) { + return; + } + getPublisherSubscriber().publish('view/tree/node/clicked', [formElementIdentifierPath]); + }); + + $(getHelper().getDomElementDataIdentifierSelector('expander'), _treeDomElement).on('click', function() { + $(this).closest('li').toggleClass(getHelper().getDomElementClassName('collapsed')).toggleClass(getHelper().getDomElementClassName('expanded')); + }); + + if (_configuration['isSortable']) { + _addSortableEvents(); + } + _loadExpanderStates(); + }; + + /** + * @public + * + * @param object + * @return string + */ + function getAllTreeNodes() { + return $(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey'), _treeDomElement); + }; + + /** + * @public + * + * @param object + * @return string + */ + function getTreeNodeWithinDomElement(element) { + return $(element).find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')).first(); + }; + + /** + * @public + * + * @param object + * @return string + */ + function getTreeNodeIdentifierPathWithinDomElement(element) { + return getTreeNodeWithinDomElement($(element)).attr(getHelper().getDomElementDataAttribute('elementIdentifier')); + }; + + /** + * @public + * + * @param object + * @return string + */ + function getParentTreeNodeWithinDomElement(element) { + return $(element).parent().closest('li').find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')).first(); + }; + + /** + * @public + * + * @param object + * @return string + */ + function getParentTreeNodeIdentifierPathWithinDomElement(element) { + return getParentTreeNodeWithinDomElement(element).attr(getHelper().getDomElementDataAttribute('elementIdentifier')); + }; + + /** + * @private + * + * @param object + * @param string + * @return string + */ + function getSiblingTreeNodeIdentifierPathWithinDomElement(element, position) { + var formElementIdentifierPath; + + if (getUtility().isUndefinedOrNull(position)) { + position = 'prev'; + } + formElementIdentifierPath = getTreeNodeIdentifierPathWithinDomElement(element); + element = (position === 'prev') ? $(element).prev('li') : $(element).next('li'); + return element.find(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKey')) + .not(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKeyValue', [formElementIdentifierPath])) + .first() + .attr(getHelper().getDomElementDataAttribute('elementIdentifier')); + }; + + /** + * @public + * + * @param string + * @param object + * @return void + */ + function setTreeNodeTitle(title, formElement) { + if (getUtility().isUndefinedOrNull(title)) { + title = buildTitleByFormElement(formElement); + } + + $(getHelper().getDomElementDataIdentifierSelector('title'), getTreeNode(formElement)).html(title); + }; + + /** + * @public + * + * @param string|object + * @return object + */ + function getTreeNode(formElement) { + var formElementIdentifierPath; + + if ('string' === $.type(formElement)) { + formElementIdentifierPath = formElement; + } else { + if (getUtility().isUndefinedOrNull(formElement)) { + formElementIdentifierPath = getCurrentlySelectedFormElement().get('__identifierPath'); + } else { + formElementIdentifierPath = formElement.get('__identifierPath'); + } + } + return $(getHelper().getDomElementDataAttribute('elementIdentifier', 'bracesWithKeyValue', [formElementIdentifierPath]), _treeDomElement); + }; + + /** + * @public + * + * @param object + * @return object + * @throws 1478719287 + */ + function buildTitleByFormElement(formElement) { + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + assert('object' === $.type(formElement), 'Invalid parameter "formElement"', 1478719287); + + return $('<span></span>') + .text((formElement.get('label') ? formElement.get('label') : formElement.get('identifier'))) + .append($('<small></small>').text("(" + getFormElementDefinition(formElement, 'label') + ")")); + }; + + /** + * @public + * + * @return object + */ + function getTreeDomElement() { + return _treeDomElement; + }; + + /** + * @public + * + * @param object + * @param object + * @param object + * @return this + * @throws 1478714814 + */ + function bootstrap(formEditorApp, appendToDomElement, configuration) { + _formEditorApp = formEditorApp; + assert('object' === $.type(appendToDomElement), 'Invalid parameter "appendToDomElement"', 1478714814); + + _treeDomElement = $(appendToDomElement); + _configuration = $.extend(true, _defaultConfiguration, configuration || {}); + _helperSetup(); + return this; + }; + + /** + * Publish the public methods. + * Implements the "Revealing Module Pattern". + */ + return { + bootstrap: bootstrap, + buildTitleByFormElement: buildTitleByFormElement, + getAllTreeNodes: getAllTreeNodes, + getParentTreeNodeWithinDomElement: getParentTreeNodeWithinDomElement, + getParentTreeNodeIdentifierPathWithinDomElement: getParentTreeNodeIdentifierPathWithinDomElement, + getSiblingTreeNodeIdentifierPathWithinDomElement: getSiblingTreeNodeIdentifierPathWithinDomElement, + getTreeDomElement: getTreeDomElement, + getTreeNode: getTreeNode, + getTreeNodeWithinDomElement: getTreeNodeWithinDomElement, + getTreeNodeIdentifierPathWithinDomElement: getTreeNodeIdentifierPathWithinDomElement, + renderCompositeFormElementChildsAsSortableList: renderCompositeFormElementChildsAsSortableList, + renew: renew, + setTreeNodeTitle: setTreeNodeTitle + }; + })($, Helper, Icons); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/ViewModel.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/ViewModel.js new file mode 100644 index 000000000000..f4fd8e48209a --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormEditor/ViewModel.js @@ -0,0 +1,1697 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormEditor/ViewModel + */ +define(['jquery', + 'TYPO3/CMS/Form/Backend/FormEditor/TreeComponent', + 'TYPO3/CMS/Form/Backend/FormEditor/ModalsComponent', + 'TYPO3/CMS/Form/Backend/FormEditor/InspectorComponent', + 'TYPO3/CMS/Form/Backend/FormEditor/StageComponent', + 'TYPO3/CMS/Form/Backend/FormEditor/Helper', + 'TYPO3/CMS/Backend/Icons', + 'TYPO3/CMS/Backend/Notification' + ], function($, TreeComponent, ModalsComponent, InspectorComponent, StageComponent, Helper, Icons, Notification) { + 'use strict'; + + return (function($, TreeComponent, ModalsComponent, InspectorComponent, StageComponent, Helper, Icons, Notification) { + + /** + * @private + * + * @var object + */ + var _configuration = { + domElementClassNames: { + formElementIsComposit: 't3-form-element-composit', + formElementIsTopLevel: 't3-form-element-toplevel', + hasError: 'has-error', + headerButtonBar: 'module-docheader-bar-column-left', + selectedCompositFormElement: 't3-form-form-composit-element-selected', + selectedFormElement: 't3-form-form-element-selected', + selectedRootFormElement: 't3-form-root-element-selected', + selectedStagePanel: 't3-form-form-stage-selected', + sortableHover: 'sortable-hover', + stageViewModeAbstract: 't3-form-stage-viewmode-abstract', + stageViewModePreview: 't3-form-stage-viewmode-preview', + validationErrors: 't3-form-validation-errors', + validationChildHasErrors: 't3-form-validation-child-has-error' + }, + domElementDataAttributeNames: { + abstractType: 'data-element-abstract-type' + }, + domElementDataAttributeValues: { + buttonHeaderClose: 'closeButton', + buttonHeaderNewPage: 'headerNewPage', + buttonHeaderPaginationNext: 'buttonPaginationNext', + buttonHeaderPaginationPrevious: 'buttonPaginationPrevious', + buttonHeaderRedo: 'redoButton', + buttonHeaderSave: 'saveButton', + buttonHeaderUndo: 'undoButton', + buttonHeaderViewModeAbstract: 'buttonViewModeAbstract', + buttonHeaderViewModePreview: 'buttonViewModePreview', + buttonStageNewElementBottom: 'stageNewElementBottom', + buttonStructureNewPage: 'treeNewPageBottom', + iconMailform: 'content-elements-mailform', + iconSave: 'actions-document-save', + iconSaveSpinner: 'spinner-circle-dark', + inspectorSection: 'inspectorSection', + moduleLoadingIndicator: 'moduleLoadingIndicator', + moduleWrapper: 'moduleWrapper', + stageArea: 'stageArea', + stageContainer: 'stageContainer', + stageNewElementRow: 'stageNewElementRow', + stagePanelHeading: 'panelHeading', + stageSection: 'stageSection', + structure: 'structure-element', + structureSection: 'structureSection', + structureRootContainer: 'treeRootContainer', + structureRootElement: 'treeRootElement' + }, + panels: { + structure: { + width: 300 + }, + stage: { + marginLeft: 300, + marginRight: 325, + marginLeftCollapsed: 0, + marginRightCollapsed: -25 + }, + inspector: { + width: 350 + } + } + }; + + /** + * @private + * + * @var bool + */ + var _previewMode = false; + + /** + * @private + * + * @var object + */ + var _formEditorApp = null; + + /** + * @private + * + * @var object + */ + var _structureComponent = null; + + /** + * @private + * + * @var object + */ + var _modalsComponent = null; + + /** + * @private + * + * @var object + */ + var _inspectorsComponent = null; + + /** + * @private + * + * @var object + */ + var _stageComponent = null; + + /* ************************************************************* + * Private Methodes + * ************************************************************/ + + /** + * @private + * + * @return object + */ + function getRootFormElement() { + return getFormEditorApp().getRootFormElement(); + }; + + /** + * @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 object + */ + function getUtility() { + return getFormEditorApp().getUtility(); + }; + + /** + * @private + * + * @return object + */ + function getCurrentlySelectedFormElement() { + return getFormEditorApp().getCurrentlySelectedFormElement(); + }; + + + /** + * @private + * + * @return object + */ + function getPublisherSubscriber() { + return getFormEditorApp().getPublisherSubscriber(); + }; + + /** + * @private + * + * @return void + */ + function _addPropertyValidators() { + getFormEditorApp().addPropertyValidationValidator('NotEmpty', function(formElement, propertyPath) { + if (formElement.get(propertyPath) === '') { + return getFormEditorApp().getFormElementPropertyValidatorDefinition('NotEmpty')['errorMessage'] || 'invalid value'; + } + }); + + getFormEditorApp().addPropertyValidationValidator('Integer', function(formElement, propertyPath) { + if (!$.isNumeric(formElement.get(propertyPath))) { + return getFormEditorApp().getFormElementPropertyValidatorDefinition('Integer')['errorMessage'] || 'invalid value'; + } + }); + + getFormEditorApp().addPropertyValidationValidator('NaiveEmail', function(formElement, propertyPath) { + if (!formElement.get(propertyPath).match(/\S+@\S+\.\S+/)) { + return getFormEditorApp().getFormElementPropertyValidatorDefinition('NaiveEmail')['errorMessage'] || 'invalid value'; + } + }); + + getFormEditorApp().addPropertyValidationValidator('NaiveEmailOrEmpty', function(formElement, propertyPath) { + if (formElement.get(propertyPath).length > 0 && !formElement.get(propertyPath).match(/\S+@\S+\.\S+/)) { + return getFormEditorApp().getFormElementPropertyValidatorDefinition('NaiveEmailOrEmpty')['errorMessage'] || 'invalid value'; + } + }); + + getFormEditorApp().addPropertyValidationValidator('FormElementIdentifierWithinCurlyBracesInclusive', function(formElement, propertyPath) { + var match, regex; + regex = /\{([a-z0-9-_]+)?\}/gi; + match = regex.exec(formElement.get(propertyPath)); + if (match && ((match[1] && !getFormEditorApp().isFormElementIdentifierUsed(match[1])) || !match[1])) { + return getFormEditorApp().getFormElementPropertyValidatorDefinition('FormElementIdentifierWithinCurlyBracesInclusive')['errorMessage'] || 'invalid value'; + } + }); + + getFormEditorApp().addPropertyValidationValidator('FormElementIdentifierWithinCurlyBracesExclusive', function(formElement, propertyPath) { + var match, regex; + regex = /^\{([a-z0-9-_]+)?\}$/i; + match = regex.exec(formElement.get(propertyPath)); + if (!match || ((match[1] && !getFormEditorApp().isFormElementIdentifierUsed(match[1])) || !match[1])) { + return getFormEditorApp().getFormElementPropertyValidatorDefinition('FormElementIdentifierWithinCurlyBracesInclusive')['errorMessage'] || 'invalid value'; + } + }); + }; + + /** + * @private + * + * @param object additionalViewModelModules + * @return void + * @publish view/ready + * @throws 1475425785 + */ + function _loadAdditionalModules(additionalViewModelModules) { + var additionalViewModelModulesLength, isLastElement, loadedAdditionalViewModelModules; + + if ('array' !== $.type(additionalViewModelModules)) { + return; + } + additionalViewModelModulesLength = additionalViewModelModules.length; + + if (additionalViewModelModulesLength > 0) { + loadedAdditionalViewModelModules = 0; + for (var i = 0; i < additionalViewModelModulesLength; ++i) { + require([additionalViewModelModules[i]], function (additionalViewModelModule) { + assert( + 'function' === $.type(additionalViewModelModule.bootstrap), + 'The module "' + additionalViewModelModules[i] + '" does not implement the method "bootstrap"', + 1475425785 + ); + additionalViewModelModule.bootstrap(getFormEditorApp()); + + loadedAdditionalViewModelModules++; + if (additionalViewModelModulesLength === loadedAdditionalViewModelModules) { + getPublisherSubscriber().publish('view/ready'); + } + }); + } + } else { + getPublisherSubscriber().publish('view/ready'); + } + }; + + /** + * @private + * + * @return void + * @throws 1478268638 + */ + function _helperSetup() { + assert('function' === $.type(Helper.bootstrap), + 'The view model helper does not implement the method "bootstrap"', + 1478268638 + ); + + Helper.bootstrap(getFormEditorApp()); + }; + + /** + * @private + * + * @return void + * @throws 1478268639 + */ + function _structureComponentSetup() { + assert( + 'function' === $.type(TreeComponent.bootstrap), + 'The structure component does not implement the method "bootstrap"', + 1478268639 + ); + + _structureComponent = TreeComponent.bootstrap( + getFormEditorApp(), + $(getHelper().getDomElementDataAttribute('identifier', 'bracesWithKeyValue', [ + getHelper().getDomElementDataAttributeValue('structure') + ])) + ); + + $( getHelper().getDomElementDataIdentifierSelector('iconMailform'), + $(getHelper().getDomElementDataIdentifierSelector('structureRootContainer')) + ).tooltip({ + title: 'identifier: ' + getRootFormElement().get('identifier'), + placement: 'right' + }); + }; + + /** + * @private + * + * @return void + * @throws 1478895106 + */ + function _modalsComponentSetup() { + assert( + 'function' === $.type(ModalsComponent.bootstrap), + 'The modals component does not implement the method "bootstrap"', + 1478895106 + ); + _modalsComponent = ModalsComponent.bootstrap(getFormEditorApp()); + }; + + /** + * @private + * + * @return void + * @throws 1478895106 + */ + function _inspectorsComponentSetup() { + assert( + 'function' === $.type(InspectorComponent.bootstrap), + 'The inspector component does not implement the method "bootstrap"', + 1478895106 + ); + _inspectorsComponent = InspectorComponent.bootstrap(getFormEditorApp()); + }; + + /** + * @private + * + * @return void + * @throws 1478986610 + */ + function _stageComponentSetup() { + assert( + 'function' === $.type(InspectorComponent.bootstrap), + 'The stage component does not implement the method "bootstrap"', + 1478986610 + ); + _stageComponent = StageComponent.bootstrap( + getFormEditorApp(), + $(getHelper().getDomElementDataAttribute('identifier', 'bracesWithKeyValue', [ + getHelper().getDomElementDataAttributeValue('stageArea') + ])) + ); + + getStage().getStagePanelDomElement().on("click", function(e) { + if ( + $(e.target).attr(getHelper().getDomElementDataAttribute('identifier')) === getHelper().getDomElementDataAttributeValue('stagePanelHeading') + || $(e.target).attr(getHelper().getDomElementDataAttribute('identifier')) === getHelper().getDomElementDataAttributeValue('stageSection') + || $(e.target).attr(getHelper().getDomElementDataAttribute('identifier')) === getHelper().getDomElementDataAttributeValue('stageArea') + ) { + selectPageBatch(getFormEditorApp().getCurrentlySelectedPageIndex()); + } + getPublisherSubscriber().publish('view/stage/panel/clicked', []); + }); + }; + + /** + * @private + * + * @return void + * @publish view/header/button/save/clicked + * @publish view/stage/abstract/button/newElement/clicked + * @publish view/header/button/newPage/clicked + * @publish view/structure/button/newPage/clicked + * @publish view/header/button/close/clicked + */ + function _buttonsSetup() { + $(getHelper().getDomElementDataIdentifierSelector('buttonHeaderSave')).on("click", function(e) { + getPublisherSubscriber().publish('view/header/button/save/clicked', []); + }); + + $(getHelper().getDomElementDataIdentifierSelector('buttonStageNewElementBottom')).on('click', function(e) { + getPublisherSubscriber().publish('view/stage/abstract/button/newElement/clicked', ['view/insertElements/perform/bottom']); + }); + + $(getHelper().getDomElementDataIdentifierSelector('buttonHeaderNewPage')).on('click', function(e) { + getPublisherSubscriber().publish('view/header/button/newPage/clicked', ['view/insertPages/perform']); + }); + + $(getHelper().getDomElementDataIdentifierSelector('buttonStructureNewPage')).on('click', function(e) { + getPublisherSubscriber().publish('view/structure/button/newPage/clicked', ['view/insertPages/perform']); + }); + + $(getHelper().getDomElementDataIdentifierSelector('buttonHeaderClose')).on('click', function(e) { + if (!getFormEditorApp().getUnsavedContent()) { + return; + } + e.preventDefault(); + getPublisherSubscriber().publish('view/header/button/close/clicked', []); + }); + + $(getHelper().getDomElementDataIdentifierSelector('buttonHeaderUndo')).on('click', function(e) { + getPublisherSubscriber().publish('view/undoButton/clicked', []); + }); + + $(getHelper().getDomElementDataIdentifierSelector('buttonHeaderRedo')).on('click', function(e) { + getPublisherSubscriber().publish('view/redoButton/clicked', []); + }); + + $(getHelper().getDomElementDataIdentifierSelector('buttonHeaderViewModeAbstract')).on('click', function(e) { + getPublisherSubscriber().publish('view/viewModeButton/abstract/clicked', []); + }); + + $(getHelper().getDomElementDataIdentifierSelector('buttonHeaderViewModePreview')).on('click', function(e) { + getPublisherSubscriber().publish('view/viewModeButton/preview/clicked', []); + }); + + $(getHelper().getDomElementDataIdentifierSelector('structureRootContainer')).on("click", function(e) { + getPublisherSubscriber().publish('view/structure/root/selected'); + }); + + $(getHelper().getDomElementDataIdentifierSelector('buttonHeaderPaginationNext')).on('click', function(e) { + getPublisherSubscriber().publish('view/paginationNext/clicked', []); + }); + + $(getHelper().getDomElementDataIdentifierSelector('buttonHeaderPaginationPrevious')).on('click', function(e) { + getPublisherSubscriber().publish('view/paginationPrevious/clicked', []); + }); + }; + + /* ************************************************************* + * Public Methodes + * ************************************************************/ + + /** + * @public + * + * @return object + */ + function getFormEditorApp() { + return _formEditorApp; + }; + + /** + * @public + * + * @param object + * @return object + */ + function getHelper(configuration) { + if (getUtility().isUndefinedOrNull(configuration)) { + return Helper.setConfiguration(_configuration); + } + return Helper.setConfiguration(configuration); + }; + + /** + * @public + * + * @param object formElement + * @param string formElementDefinitionKey + * @return mixed + */ + function getFormElementDefinition(formElement, formElementDefinitionKey) { + return getFormEditorApp().getFormElementDefinition(formElement, formElementDefinitionKey); + }; + + /** + * @public + * + * @return object (derefernced) + */ + function getConfiguration() { + return $.extend(true, {}, _configuration); + }; + + /** + * @public + * + * @return int + */ + function getPreviewMode() { + return _previewMode; + }; + + /** + * @public + * + * @param bool + * @return void + */ + function setPreviewMode(previewMode) { + _previewMode = !!previewMode; + }; + + /* ************************************************************* + * Structure + * ************************************************************/ + + /** + * @public + * + * @return object + */ + function getStructure() { + return _structureComponent; + }; + + /** + * @public + * + * @return void + * @publish view/structure/renew/postProcess + */ + function renewStructure() { + getStructure().renew(); + getPublisherSubscriber().publish('view/structure/renew/postProcess'); + }; + + /** + * @public + * + * @param object + * @return void + */ + function addStructureSelection(formElement) { + getStructure().getTreeNode(formElement).addClass(getHelper().getDomElementClassName('selectedFormElement')); + }; + + /** + * @public + * + * @param object + * @return void + */ + function removeStructureSelection(formElement) { + getStructure().getTreeNode(formElement).removeClass(getHelper().getDomElementClassName('selectedFormElement')); + }; + + /** + * @public + * + * @return void + */ + function removeAllStructureSelections() { + $(getHelper().getDomElementClassName('selectedFormElement', true), getStructure().getTreeDomElement()) + .removeClass(getHelper().getDomElementClassName('selectedFormElement')); + }; + + /** + * @public + * + * @return object + */ + function getStructureRootElement() { + return $(getHelper().getDomElementDataAttribute('identifier', 'bracesWithKeyValue', [ + getHelper().getDomElementDataAttributeValue('structureRootElement') + ])); + }; + + /** + * @public + * + * @return void + */ + function removeStructureRootElementSelection() { + $(getHelper().getDomElementDataAttribute('identifier', 'bracesWithKeyValue', [ + getHelper().getDomElementDataAttributeValue('structureRootContainer') + ])).removeClass(getHelper().getDomElementClassName('selectedRootFormElement')); + }; + + /** + * @public + * + * @return void + */ + function addStructureRootElementSelection() { + $(getHelper().getDomElementDataAttribute('identifier', 'bracesWithKeyValue', [ + getHelper().getDomElementDataAttributeValue('structureRootContainer') + ])).addClass(getHelper().getDomElementClassName('selectedRootFormElement')); + }; + + /** + * @public + * + * @param string title + * @return void + */ + function setStructureRootElementTitle(title) { + if (getUtility().isUndefinedOrNull(title)) { + title = $('<span></span>') + .text((getRootFormElement().get('label') ? getRootFormElement().get('label') : getRootFormElement().get('identifier'))) + .html(); + } + getStructureRootElement().text(title); + }; + + /** + * @public + * + * @return void + */ + function addStructureValidationResults() { + var validationResults; + + getStructure().getAllTreeNodes() + .removeClass(getHelper().getDomElementClassName('validationErrors')) + .removeClass(getHelper().getDomElementClassName('validationChildHasErrors')); + + removeElementValidationErrorClass(getStructureRootElement()); + + validationResults = getFormEditorApp().validateFormElementRecursive(getRootFormElement()); + for (var i = 0, len = validationResults.length; i < len; ++i) { + var hasError = false, pathParts, validationElement; + for (var j = 0, len2 = validationResults[i]['validationResults'].length; j < len2; ++j) { + if ( + validationResults[i]['validationResults'][j]['validationResults'] + && validationResults[i]['validationResults'][j]['validationResults'].length > 0 + ) { + hasError = true; + break; + } + } + + if (hasError) { + if (i === 0) { + setElementValidationErrorClass(getStructureRootElement()); + } else { + validationElement = getStructure().getTreeNode(validationResults[i]['formElementIdentifierPath']); + setElementValidationErrorClass(validationElement); + + pathParts = validationResults[i]['formElementIdentifierPath'].split('/'); + while(pathParts.pop()) { + validationElement = getStructure().getTreeNode(pathParts.join('/')); + if ('object' === $.type(validationElement)) { + setElementValidationErrorClass(validationElement, 'validationChildHasErrors'); + } + } + } + } + } + }; + + /* ************************************************************* + * Modals + * ************************************************************/ + + /** + * @public + * + * @return object + */ + function getModals() { + return _modalsComponent + }; + + /** + * @public + * + * @param object formElement + * @return void + */ + function showRemoveFormElementModal(formElement) { + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + getModals().showRemoveFormElementModal(formElement); + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param string collectionName + * @param object formElement + * @return void + */ + function showRemoveCollectionElementModal(collectionElementIdentifier, collectionName, formElement) { + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + getModals().showRemoveCollectionElementModal(collectionElementIdentifier, collectionName, formElement); + }; + + /** + * @public + * + * @return void + */ + function showCloseConfirmationModal() { + getModals().showCloseConfirmationModal(); + }; + + /** + * @public + * + * @param string targetEvent + * @return void + */ + function showInsertElementsModal(targetEvent) { + getModals().showInsertElementsModal(targetEvent); + }; + + /** + * @public + * + * @param string targetEvent + * @return void + */ + function showInsertPagesModal(targetEvent) { + getModals().showInsertPagesModal(targetEvent); + }; + + /** + * @public + * + * @param bool + * @return void + */ + function showValidationErrorsModal() { + var validationResults; + validationResults = getFormEditorApp().validateFormElementRecursive(getRootFormElement()); + + getModals().showValidationErrorsModal(validationResults); + }; + + /* ************************************************************* + * Inspector + * ************************************************************/ + + /** + * @public + * + * @return object + */ + function getInspector() { + return _inspectorsComponent + }; + + /** + * @public + * + * @param object + * @param bool + * @return void + */ + function renderInspectorEditors(formElement, useFadeEffect) { + var render; + if (getUtility().isUndefinedOrNull(useFadeEffect)) { + useFadeEffect = true; + } + + /** + * @private + * + * @param function + * @return void + */ + render = function(callback) { + getInspector().renderEditors(formElement, callback); + }; + + if (!!useFadeEffect) { + getInspector().getInspectorDomElement().fadeOut('fast', function() { + render(function() { + getInspector().getInspectorDomElement().fadeIn('fast'); + }); + }); + } else { + render(); + } + }; + + /** + * @public + * + * @param string + * @param string + * @return void + */ + function renderInspectorCollectionElementEditors(collectionName, collectionElementIdentifier) { + getInspector().renderCollectionElementEditors(collectionName, collectionElementIdentifier); + }; + + /** + * @public + * + * @param string content + * @return void + */ + function setInspectorFormElementHeaderEditorContent(content) { + getInspector().setFormElementHeaderEditorContent(content); + }; + + /* ************************************************************* + * Stage + * ************************************************************/ + + /** + * @public + * + * @return object + */ + function getStage() { + return _stageComponent; + }; + + /** + * @public + * + * @param string title + * @return void + */ + function setStageHeadline(title) { + getStage().setStageHeadline(title); + }; + + /** + * @public + * + * @return void + */ + function addStagePanelSelection() { + getStage().getStagePanelDomElement().addClass(getHelper().getDomElementClassName('selectedStagePanel')); + }; + + /** + * @public + * + * @return void + */ + function removeStagePanelSelection() { + getStage().getStagePanelDomElement().removeClass(getHelper().getDomElementClassName('selectedStagePanel')); + }; + + /** + * @public + * + * @return void + */ + function renderPagination() { + getStage().renderPagination(); + }; + + /** + * @public + * + * @return void + */ + function renderUndoRedo() { + getStage().renderUndoRedo(); + }; + + /** + * @public + * + * @param bool + * @param bool + * @return void + * @publish view/stage/abstract/render/postProcess + * @publish view/stage/abstract/render/preProcess + */ + function renderAbstractStageArea(useFadeEffect, toolbarUseFadeEffect) { + var render, renderPostProcess; + + $(getHelper().getDomElementDataIdentifierSelector('structureSection')) + .animate({ + 'left': '0px' + }, 'slow'); + $(getHelper().getDomElementDataIdentifierSelector('inspectorSection')) + .animate({ + 'right': '0px' + }, 'slow'); + $(getHelper().getDomElementDataIdentifierSelector('stageContainer')) + .animate({ + 'margin-left': _configuration['panels']['stage']['marginLeft'] + 'px', + 'margin-right': _configuration['panels']['stage']['marginRight'] + 'px' + }, 'slow'); + + if (getUtility().isUndefinedOrNull(useFadeEffect)) { + useFadeEffect = true; + } + + if (getUtility().isUndefinedOrNull(toolbarUseFadeEffect)) { + toolbarUseFadeEffect = true; + } + + setButtonActive($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderViewModeAbstract'))); + removeButtonActive($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderViewModePreview'))); + + /** + * @private + * + * @param function + * @return void + */ + render = function(callback) { + $(getHelper().getDomElementDataIdentifierSelector('stageContainer')) + .addClass(getHelper().getDomElementClassName('stageViewModeAbstract')) + .removeClass(getHelper().getDomElementClassName('stageViewModePreview')); + + getStage().renderAbstractStageArea(undefined, callback); + }; + + /** + * @private + * + * @return void + */ + renderPostProcess = function() { + var formElementTypeDefinition; + + formElementTypeDefinition = getFormElementDefinition(getCurrentlySelectedFormElement()); + getStage().getAllFormElementDomElements().hover(function() { + getStage().getAllFormElementDomElements().parent().removeClass(getHelper().getDomElementClassName('sortableHover')); + if ( + $(this).parent().hasClass(getHelper().getDomElementClassName('formElementIsComposit')) + && !$(this).parent().hasClass(getHelper().getDomElementClassName('formElementIsTopLevel')) + ) { + $(this).parent().addClass(getHelper().getDomElementClassName('sortableHover')); + } + }); + + showComponent($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderNewPage'))); + if ( + formElementTypeDefinition['_isTopLevelFormElement'] + && !formElementTypeDefinition['_isCompositeFormElement'] + && !getFormEditorApp().isRootFormElementSelected() + ) { + hideComponent($(getHelper().getDomElementDataIdentifierSelector('buttonStageNewElementBottom'))); + hideComponent($(getHelper().getDomElementDataIdentifierSelector('stageNewElementRow'))); + } else { + showComponent($(getHelper().getDomElementDataIdentifierSelector('buttonStageNewElementBottom'))); + showComponent($(getHelper().getDomElementDataIdentifierSelector('stageNewElementRow'))); + } + + refreshSelectedElementItemsBatch(toolbarUseFadeEffect); + getPublisherSubscriber().publish('view/stage/abstract/render/postProcess'); + }; + + if (useFadeEffect) { + $(getHelper().getDomElementDataIdentifierSelector('stageSection')).fadeOut(400, function() { + render(function() { + getPublisherSubscriber().publish('view/stage/abstract/render/preProcess'); + $(getHelper().getDomElementDataIdentifierSelector('stageSection')).fadeIn(400); + renderPostProcess(); + getPublisherSubscriber().publish('view/stage/abstract/render/postProcess'); + }); + }); + } else { + render(function() { + getPublisherSubscriber().publish('view/stage/abstract/render/preProcess'); + renderPostProcess(); + getPublisherSubscriber().publish('view/stage/abstract/render/postProcess'); + }); + } + }; + + /** + * @public + * + * @param string html + * @return void + * @publish view/stage/preview/render/postProcess + */ + function renderPreviewStageArea(html) { + $(getHelper().getDomElementDataIdentifierSelector('structureSection')) + .animate({ + 'left': '-=' + _configuration['panels']['structure']['width'] + 'px' + }, 'slow'); + $(getHelper().getDomElementDataIdentifierSelector('inspectorSection')) + .animate({ + 'right': '-=' + _configuration['panels']['inspector']['width'] + 'px' + }, 'slow'); + $(getHelper().getDomElementDataIdentifierSelector('stageContainer')) + .animate({ + 'margin-left': _configuration['panels']['stage']['marginLeftCollapsed'] + 'px', + 'margin-right': _configuration['panels']['stage']['marginRightCollapsed'] + 'px' + }, 'slow'); + + setButtonActive($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderViewModePreview'))); + removeButtonActive($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderViewModeAbstract'))); + + $(getHelper().getDomElementDataIdentifierSelector('stageSection')).fadeOut(400, function() { + $(getHelper().getDomElementDataIdentifierSelector('stageContainer')) + .addClass(getHelper().getDomElementClassName('stageViewModePreview')) + .removeClass(getHelper().getDomElementClassName('stageViewModeAbstract')); + + hideComponent($(getHelper().getDomElementDataIdentifierSelector('buttonStageNewElementBottom'))); + hideComponent($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderNewPage'))); + + getStage().renderPreviewStageArea(html); + $(getHelper().getDomElementDataIdentifierSelector('stageSection')).fadeIn(400); + getPublisherSubscriber().publish('view/stage/preview/render/postProcess'); + }); + }; + + /** + * @public + * + * @return void + */ + function addAbstractViewValidationResults() { + var validationResults; + + validationResults = getFormEditorApp().validateFormElementRecursive(getRootFormElement()); + for (var i = 0, len = validationResults.length; i < len; ++i) { + var hasError = false, validationElement; + for (var j = 0, len2 = validationResults[i]['validationResults'].length; j < len2; ++j) { + if ( + validationResults[i]['validationResults'][j]['validationResults'] + && validationResults[i]['validationResults'][j]['validationResults'].length > 0 + ) { + hasError = true; + break; + } + } + + if (hasError) { + if (i > 0) { + validationElement = getStage().getAbstractViewFormElementDomElement(validationResults[i]['formElementIdentifierPath']); + setElementValidationErrorClass(validationElement); + } + } + } + }; + + /* ************************************************************* + * Form element methods + * ************************************************************/ + + /** + * @public + * + * @param string formElementType + * @param string|object referenceFormElement + * @param bool + * @return object + * @publish view/formElement/inserted + */ + function createAndAddFormElement(formElementType, referenceFormElement, disablePublishersOnSet) { + var newFormElement; + + newFormElement = getFormEditorApp().createAndAddFormElement(formElementType, referenceFormElement); + if (!!!disablePublishersOnSet) { + getPublisherSubscriber().publish('view/formElement/inserted', [newFormElement]); + } + return newFormElement; + }; + + /** + * @public + * + * @param string|object formElementToMove + * @param string position + * @param string|object referenceFormElement + * @param bool + * @return object + * @publish view/formElement/moved + */ + function moveFormElement(formElementToMove, position, referenceFormElement, disablePublishersOnSet) { + var movedFormElement; + + movedFormElement = getFormEditorApp().moveFormElement(formElementToMove, position, referenceFormElement, false); + if (!!!disablePublishersOnSet) { + getPublisherSubscriber().publish('view/formElement/moved', [movedFormElement]); + } + return movedFormElement; + }; + + /** + * @public + * + * @param object formElement + * @param bool + * @return object + * @publish view/formElement/removed + */ + function removeFormElement(formElement, disablePublishersOnSet) { + var parentFormElement; + + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + + if ( + getFormElementDefinition(formElement, '_isTopLevelFormElement') + && getFormElementDefinition(formElement, '_isCompositeFormElement') + && getRootFormElement().get('renderables').length === 1 + ) { + Notification.error( + getFormElementDefinition(getRootFormElement(), 'modalRemoveElementLastAvailablePageFlashMessageTitle'), + getFormElementDefinition(getRootFormElement(), 'modalRemoveElementLastAvailablePageFlashMessageMessage'), + 2 + ); + } else { + parentFormElement = getFormEditorApp().removeFormElement(formElement, false); + if (!!!disablePublishersOnSet) { + getPublisherSubscriber().publish('view/formElement/removed', [parentFormElement]); + } + } + return parentFormElement; + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param string collectionName + * @param object formElement + * @param object collectionElementConfiguration + * @param string referenceCollectionElementIdentifier + * @param bool + * @return void + * @publish view/collectionElement/new/added + */ + function createAndAddPropertyCollectionElement(collectionElementIdentifier, collectionName, formElement, collectionElementConfiguration, referenceCollectionElementIdentifier, disablePublishersOnSet) { + getFormEditorApp().createAndAddPropertyCollectionElement( + collectionElementIdentifier, + collectionName, + formElement, + collectionElementConfiguration, + referenceCollectionElementIdentifier + ); + if (!!!disablePublishersOnSet) { + getPublisherSubscriber().publish('view/collectionElement/new/added', [ + collectionElementIdentifier, + collectionName, + formElement, + collectionElementConfiguration, + referenceCollectionElementIdentifier + ]); + } + }; + + /** + * @public + * + * @param string collectionElementToMove + * @param string position + * @param string referenceCollectionElement + * @param string collectionName + * @param object formElement + * @param bool + * @return void + */ + function movePropertyCollectionElement(collectionElementToMove, position, referenceCollectionElement, collectionName, formElement, disablePublishersOnSet) { + if (getUtility().isUndefinedOrNull(formElement)) { + formElement = getCurrentlySelectedFormElement(); + } + getFormEditorApp().movePropertyCollectionElement( + collectionElementToMove, + position, + referenceCollectionElement, + collectionName, + formElement, + false + ); + if (!!!disablePublishersOnSet) { + getPublisherSubscriber().publish('view/collectionElement/moved', [ + collectionElementToMove, + position, + referenceCollectionElement, + collectionName, + formElement] + ); + } + }; + + /** + * @public + * + * @param string collectionElementIdentifier + * @param string collectionName + * @param object formElement + * @param bool + * @return void + * @publish view/collectionElement/removed + */ + function removePropertyCollectionElement(collectionElementIdentifier, collectionName, formElement, disablePublishersOnSet) { + getFormEditorApp().removePropertyCollectionElement(collectionElementIdentifier, collectionName, formElement); + if (!!!disablePublishersOnSet) { + getPublisherSubscriber().publish('view/collectionElement/removed', [ + collectionElementIdentifier, + collectionName, + formElement] + ); + } + }; + + /* ************************************************************* + * Batch methodes + * ************************************************************/ + + /** + * @public + * + * @param bool + * @return void + */ + function refreshSelectedElementItemsBatch(toolbarUseFadeEffect) { + var formElementTypeDefinition, selectedElement; + + if (getUtility().isUndefinedOrNull(toolbarUseFadeEffect)) { + toolbarUseFadeEffect = true; + } + + formElementTypeDefinition = getFormElementDefinition(getCurrentlySelectedFormElement()); + + getStage().removeAllStageToolbars(); + removeAllStageElementSelectionsBatch(); + removeAllStructureSelections(); + + if (!getFormEditorApp().isRootFormElementSelected()) { + removeStructureRootElementSelection(); + addStructureSelection(); + + selectedElement = getStage().getAbstractViewFormElementDomElement(); + + if (formElementTypeDefinition['_isTopLevelFormElement']) { + addStagePanelSelection(); + } else { + selectedElement.addClass(getHelper().getDomElementClassName('selectedFormElement')); + getStage().createAndAddAbstractViewFormElementToolbar(selectedElement, undefined, toolbarUseFadeEffect); + } + + getStage().getAllFormElementDomElements().parent().removeClass(getHelper().getDomElementClassName('selectedCompositFormElement')); + if (!formElementTypeDefinition['_isTopLevelFormElement'] && formElementTypeDefinition['_isCompositeFormElement']) { + selectedElement.parent().addClass(getHelper().getDomElementClassName('selectedCompositFormElement')); + } + } + }; + + /** + * @public + * + * @param int + * @return void + * @throws 1478651732 + * @throws 1478651733 + * @throws 1478651734 + */ + function selectPageBatch(pageIndex) { + assert('number' === $.type(pageIndex), 'Invalid parameter "pageIndex"', 1478651732); + assert(pageIndex >= 0, 'Invalid parameter "pageIndex"', 1478651733); + assert(pageIndex < getRootFormElement().get('renderables').length, 'Invalid parameter "pageIndex"', 1478651734); + + getFormEditorApp().setCurrentlySelectedFormElement(getRootFormElement().get('renderables')[pageIndex]); + renewStructure(); + renderPagination() + refreshSelectedElementItemsBatch(); + renderInspectorEditors(); + }; + + /** + * @public + * + * @return void + */ + function removeAllStageElementSelectionsBatch() { + getStage().getAllFormElementDomElements().removeClass(getHelper().getDomElementClassName('selectedFormElement')); + removeStagePanelSelection(); + getStage().getAllFormElementDomElements().parent().removeClass(getHelper().getDomElementClassName('sortableHover')); + }; + + /** + * @public + * + * @return void + */ + function onViewReadyBatch() { + $(getHelper().getDomElementDataIdentifierSelector('structureSection')) + .css({ + width: _configuration['panels']['structure']['width'] + 'px', + left: '-=' + _configuration['panels']['structure']['width'] + 'px' + }); + $(getHelper().getDomElementDataIdentifierSelector('inspectorSection')) + .css({ + width: _configuration['panels']['inspector']['width'] + 'px', + right: '-=' + _configuration['panels']['inspector']['width'] + 'px' + }); + + $(getHelper().getDomElementClassName('headerButtonBar', true)) + .css({ + 'margin-left': _configuration['panels']['structure']['width'] + 'px' + }); + + $(getHelper().getDomElementDataIdentifierSelector('stageContainer')) + .css({ + 'margin-left': _configuration['panels']['stage']['marginLeft'] + 'px', + 'margin-right': _configuration['panels']['stage']['marginRight'] + 'px' + }); + + hideComponent($(getHelper().getDomElementDataIdentifierSelector('buttonStageNewElementBottom'))); + hideComponent($(getHelper().getDomElementDataIdentifierSelector('stageNewElementRow'))); + + setStageHeadline(); + setStructureRootElementTitle(); + renderAbstractStageArea(false); + renewStructure(); + addStructureRootElementSelection(); + renderInspectorEditors(); + renderPagination(); + + hideComponent($(getHelper().getDomElementDataIdentifierSelector('moduleLoadingIndicator'))); + showComponent($(getHelper().getDomElementDataIdentifierSelector('moduleWrapper'))); + showComponent($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderSave'))); + showComponent($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderClose'))); + showComponent($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderNewPage'))); + showComponent($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderUndo'))); + showComponent($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderRedo'))); + setButtonActive($(getHelper().getDomElementDataIdentifierSelector('buttonHeaderViewModeAbstract'))); + }; + + /** + * @public + * + * @param object + * @param object + * @return void + */ + function onAbstractViewDndStartBatch(draggedFormElementDomElement, draggedFormPlaceholderDomElement) { + draggedFormPlaceholderDomElement.removeClass(getHelper().getDomElementClassName('sortableHover')); + }; + + /** + * @public + * + * @param object + * @param string + * @param object + * @return void + */ + function onAbstractViewDndChangeBatch(placeholderDomElement, parentFormElementIdentifierPath, enclosingCompositeFormElement) { + getStage().getAllFormElementDomElements().parent().removeClass(getHelper().getDomElementClassName('sortableHover')); + if (enclosingCompositeFormElement) { + getStage().getAbstractViewParentFormElementWithinDomElement(placeholderDomElement).parent().addClass(getHelper().getDomElementClassName('sortableHover')); + } + }; + + /** + * @public + * + * @param object + * @param string + * @param string + * @param string + * @return void + * @throws 1472502237 + */ + function onAbstractViewDndUpdateBatch(movedDomElement, movedFormElementIdentifierPath, previousFormElementIdentifierPath, nextFormElementIdentifierPath) { + var movedFormElement, parentFormElementIdentifierPath; + if (nextFormElementIdentifierPath) { + movedFormElement = moveFormElement(movedFormElementIdentifierPath, 'before', nextFormElementIdentifierPath); + } else if (previousFormElementIdentifierPath) { + movedFormElement = moveFormElement(movedFormElementIdentifierPath, 'after', previousFormElementIdentifierPath); + } else { + parentFormElementIdentifierPath = getStage().getAbstractViewParentFormElementIdentifierPathWithinDomElement(movedDomElement); + if (parentFormElementIdentifierPath) { + movedFormElement = moveFormElement(movedFormElementIdentifierPath, 'inside', parentFormElementIdentifierPath); + } else { + assert(false, 'Next element, previous or parent element need to be set.', 1472502237); + } + } + + getStage() + .getAbstractViewFormElementWithinDomElement(movedDomElement) + .attr( + getHelper().getDomElementDataAttribute('elementIdentifier'), + movedFormElement.get('__identifierPath') + ); + }; + + /** + * @public + * + * @param object + * @param string + * @param object + * @return void + */ + function onStructureDndChangeBatch(placeholderDomElement, parentFormElementIdentifierPath, enclosingCompositeFormElement) { + getStructure() + .getAllTreeNodes() + .parent() + .removeClass(getHelper().getDomElementClassName('sortableHover')); + + getStage() + .getAllFormElementDomElements() + .parent() + .removeClass(getHelper().getDomElementClassName('sortableHover')); + + if (enclosingCompositeFormElement) { + getStructure() + .getParentTreeNodeWithinDomElement(placeholderDomElement) + .parent() + .addClass(getHelper().getDomElementClassName('sortableHover')); + + getStage() + .getAbstractViewFormElementDomElement(enclosingCompositeFormElement) + .parent() + .addClass(getHelper().getDomElementClassName('sortableHover')); + } + }; + + /** + * @public + * + * @param object + * @param string + * @param string + * @param string + * @return void + * @throws 1479048646 + */ + function onStructureDndUpdateBatch(movedDomElement, movedFormElementIdentifierPath, previousFormElementIdentifierPath, nextFormElementIdentifierPath) { + var movedFormElement, parentFormElementIdentifierPath; + if (nextFormElementIdentifierPath) { + movedFormElement = moveFormElement(movedFormElementIdentifierPath, 'before', nextFormElementIdentifierPath); + } else if (previousFormElementIdentifierPath) { + movedFormElement = moveFormElement(movedFormElementIdentifierPath, 'after', previousFormElementIdentifierPath); + } else { + parentFormElementIdentifierPath = getStructure().getParentTreeNodeIdentifierPathWithinDomElement(movedDomElement); + if (parentFormElementIdentifierPath) { + movedFormElement = moveFormElement(movedFormElementIdentifierPath, 'inside', parentFormElementIdentifierPath); + } else { + getFormEditorApp().assert(false, 'Next element, previous or parent element need to be set.', 1479048646); + } + } + + getStructure() + .getTreeNodeWithinDomElement(movedDomElement) + .attr( + getHelper().getDomElementDataAttribute('elementIdentifier'), + movedFormElement.get('__identifierPath') + ); + }; + + /* ************************************************************* + * Misc + * ************************************************************/ + + /** + * @public + * + * @return void + */ + function closeEditor() { + document.location.href = $(getHelper().getDomElementDataIdentifierSelector('buttonHeaderClose')).prop('href'); + }; + + /** + * @public + * + * @param object + * @param string + * @return void + */ + function setElementValidationErrorClass(element, classIdentifier) { + if (getFormEditorApp().getUtility().isUndefinedOrNull(classIdentifier)) { + element.addClass(getHelper().getDomElementClassName('validationErrors')); + } else { + element.addClass(getHelper().getDomElementClassName(classIdentifier)); + } + }; + + /** + * @public + * + * @param object + * @param string + * @return void + */ + function removeElementValidationErrorClass(element, classIdentifier) { + if (getFormEditorApp().getUtility().isUndefinedOrNull(classIdentifier)) { + element.removeClass(getHelper().getDomElementClassName('validationErrors')); + } else { + element.removeClass(getHelper().getDomElementClassName(classIdentifier)); + } + }; + + /** + * @public + * + * @param object + * @return void + */ + function showComponent(element) { + element.removeClass(getHelper().getDomElementClassName('hidden')).show(); + }; + + /** + * @public + * + * @param object + * @return void + */ + function hideComponent(element) { + element.addClass(getHelper().getDomElementClassName('hidden')).hide(); + }; + + /** + * @public + * + * @param object + * @return void + */ + function enableButton(buttonElement) { + buttonElement.prop('disabled', false).removeClass(getHelper().getDomElementClassName('disabled')); + }; + + /** + * @public + * + * @param object + * @return void + */ + function disableButton(buttonElement) { + buttonElement.prop('disabled', 'disabled').addClass(getHelper().getDomElementClassName('disabled')); + }; + + /** + * @public + * + * @param object + * @return void + */ + function setButtonActive(buttonElement) { + buttonElement.addClass(getHelper().getDomElementClassName('active')); + }; + + /** + * @public + * + * @param object + * @return void + */ + function removeButtonActive(buttonElement) { + buttonElement.removeClass(getHelper().getDomElementClassName('active')); + }; + + /** + * @public + * + * @return void + */ + function showSaveButtonSpinnerIcon() { + Icons.getIcon(getHelper().getDomElementDataAttributeValue('iconSaveSpinner'), Icons.sizes.small).done(function(markup) { + $(getHelper().getDomElementDataIdentifierSelector('iconSave')).replaceWith($(markup)); + }); + }; + + /** + * @public + * + * @return void + */ + function showSaveButtonSaveIcon() { + Icons.getIcon(getHelper().getDomElementDataAttributeValue('iconSave'), Icons.sizes.small).done(function(markup) { + $(getHelper().getDomElementDataIdentifierSelector('iconSaveSpinner')).replaceWith($(markup)); + }); + }; + + /** + * @public + * + * @return void + */ + function showSaveSuccessMessage() { + Notification.success( + getFormElementDefinition(getRootFormElement(), 'saveSuccessFlashMessageTitle'), + getFormElementDefinition(getRootFormElement(), 'saveSuccessFlashMessageMessage'), + 2 + ); + }; + + /** + * @public + * + * @param string + * @param string + * @return void + */ + function showErrorFlashMessage(title, message) { + Notification.error(title, message, 2); + }; + + /** + * @public + * + * @param object formEditorApp + * @param object additionalViewModelModules + * @return void + */ + function bootstrap(formEditorApp, additionalViewModelModules) { + _formEditorApp = formEditorApp; + + _helperSetup(); + _structureComponentSetup(); + _modalsComponentSetup(); + _inspectorsComponentSetup(); + _stageComponentSetup(); + _buttonsSetup(); + _addPropertyValidators(); + _loadAdditionalModules(additionalViewModelModules); + }; + + /** + * Publish the public methods. + * Implements the "Revealing Module Pattern". + */ + return { + addAbstractViewValidationResults: addAbstractViewValidationResults, + addStagePanelSelection: addStagePanelSelection, + addStructureRootElementSelection: addStructureRootElementSelection, + addStructureSelection: addStructureSelection, + addStructureValidationResults: addStructureValidationResults, + bootstrap: bootstrap, + closeEditor: closeEditor, + createAndAddFormElement: createAndAddFormElement, + createAndAddPropertyCollectionElement: createAndAddPropertyCollectionElement, + disableButton: disableButton, + enableButton: enableButton, + getConfiguration: getConfiguration, + getFormEditorApp: getFormEditorApp, + getFormElementDefinition: getFormElementDefinition, + getHelper: getHelper, + getInspector: getInspector, + getModals: getModals, + getPreviewMode: getPreviewMode, + getStage: getStage, + getStructure: getStructure, + getStructureRootElement: getStructureRootElement, + hideComponent: hideComponent, + moveFormElement: moveFormElement, + movePropertyCollectionElement: movePropertyCollectionElement, + onAbstractViewDndChangeBatch: onAbstractViewDndChangeBatch, + onAbstractViewDndStartBatch: onAbstractViewDndStartBatch, + onAbstractViewDndUpdateBatch: onAbstractViewDndUpdateBatch, + onStructureDndChangeBatch: onStructureDndChangeBatch, + onStructureDndUpdateBatch: onStructureDndUpdateBatch, + onViewReadyBatch: onViewReadyBatch, + refreshSelectedElementItemsBatch: refreshSelectedElementItemsBatch, + removeAllStageElementSelectionsBatch: removeAllStageElementSelectionsBatch, + removeAllStructureSelections: removeAllStructureSelections, + removeButtonActive: removeButtonActive, + removeElementValidationErrorClass: removeElementValidationErrorClass, + removeFormElement: removeFormElement, + removePropertyCollectionElement: removePropertyCollectionElement, + removeStagePanelSelection: removeStagePanelSelection, + removeStructureRootElementSelection: removeStructureRootElementSelection, + removeStructureSelection: removeStructureSelection, + renderAbstractStageArea: renderAbstractStageArea, + renderInspectorEditors: renderInspectorEditors, + renderInspectorCollectionElementEditors: renderInspectorCollectionElementEditors, + renderPagination: renderPagination, + renderPreviewStageArea: renderPreviewStageArea, + renewStructure: renewStructure, + renderUndoRedo: renderUndoRedo, + selectPageBatch: selectPageBatch, + setButtonActive: setButtonActive, + setElementValidationErrorClass: setElementValidationErrorClass, + setInspectorFormElementHeaderEditorContent: setInspectorFormElementHeaderEditorContent, + setPreviewMode: setPreviewMode, + setStageHeadline: setStageHeadline, + setStructureRootElementTitle: setStructureRootElementTitle, + showCloseConfirmationModal: showCloseConfirmationModal, + showComponent: showComponent, + showErrorFlashMessage: showErrorFlashMessage, + showInsertElementsModal: showInsertElementsModal, + showInsertPagesModal: showInsertPagesModal, + showRemoveFormElementModal: showRemoveFormElementModal, + showRemoveCollectionElementModal: showRemoveCollectionElementModal, + showSaveButtonSaveIcon: showSaveButtonSaveIcon, + showSaveButtonSpinnerIcon: showSaveButtonSpinnerIcon, + showSaveSuccessMessage: showSaveSuccessMessage, + showValidationErrorsModal: showValidationErrorsModal + }; + })($, TreeComponent, ModalsComponent, InspectorComponent, StageComponent, Helper, Icons, Notification); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormManager.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormManager.js new file mode 100644 index 000000000000..26a70aa1941d --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormManager.js @@ -0,0 +1,240 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormManager + */ +define(['jquery'], function($) { + 'use strict'; + + /** + * Return a static method named "getInstance". + * Use this method to create the formmanager app. + */ + return (function() { + + /** + * @private + * + * Hold the instance (Singleton Pattern) + */ + var _formManagerInstance = null; + + /** + * @public + * + * @param object _configuration + * @param object _viewModel + * @return object + */ + function FormManager(_configuration, _viewModel) { + + /** + * @private + * + * @var bool + */ + var _isRunning = false; + + /** + * @public + * + * @param mixed test + * @param string message + * @param int messageCode + * @return void + */ + function assert(test, message, messageCode) { + if ('function' === $.type(test)) { + test = (test() !== false); + } + if (!test) { + message = message || "Assertion failed"; + if (messageCode) { + message = message + ' (' + messageCode + ')'; + } + if ('undefined' !== typeof Error) { + throw new Error(message); + } + throw message; + } + }; + + /** + * @private + * + * @var bool + */ + var _isRunning = false; + + /** + * @public + * + * @return object + */ + function getPrototypes() { + var prototypes = []; + + if ('array' === $.type(_configuration['selectablePrototypesConfiguration'])) { + for (var i = 0, len = _configuration['selectablePrototypesConfiguration'].length; i < len; ++i) { + prototypes.push({ + label: _configuration['selectablePrototypesConfiguration'][i]['label'], + value: _configuration['selectablePrototypesConfiguration'][i]['identifier'], + }); + } + } + return prototypes; + }; + + /** + * @public + * + * @param string prototypeName + * @return object + */ + function getTemplatesForPrototype(prototypeName) { + var templates = []; + assert('string' === $.type(prototypeName), 'Invalid parameter "prototypeName"', 1475945286); + if ('array' === $.type(_configuration['selectablePrototypesConfiguration'])) { + for (var i = 0, len1 = _configuration['selectablePrototypesConfiguration'].length; i < len1; ++i) { + if (_configuration['selectablePrototypesConfiguration'][i]['identifier'] !== prototypeName) { + continue; + } + if ('array' === $.type(_configuration['selectablePrototypesConfiguration'][i]['newFormTemplates'])) { + for (var j = 0, len2 = _configuration['selectablePrototypesConfiguration'][i]['newFormTemplates'].length; j < len2; ++j) { + templates.push({ + label: _configuration['selectablePrototypesConfiguration'][i]['newFormTemplates'][j]['label'], + value: _configuration['selectablePrototypesConfiguration'][i]['newFormTemplates'][j]['templatePath'], + }); + } + } + } + } + + return templates; + }; + + /** + * @public + * + * @param string prototypeName + * @return object + */ + function getAccessibleFormStorageFolders() { + var folders = []; + + if ('array' === $.type(_configuration['accessibleFormStorageFolders'])) { + for (var i = 0, len1 = _configuration['accessibleFormStorageFolders'].length; i < len1; ++i) { + folders.push({ + label: _configuration['accessibleFormStorageFolders'][i]['label'], + value: _configuration['accessibleFormStorageFolders'][i]['value'], + }); + } + } + return folders; + }; + + /** + * @public + * + * @param string prototypeName + * @return object + * @throws 1477506508 + */ + function getAjaxEndpoint(endpointName) { + var templates = []; + assert(typeof _configuration['endpoints'][endpointName] !== 'undefined', 'Endpoint ' + endpointName + ' does not exist', 1477506508); + + return _configuration['endpoints'][endpointName]; + }; + + /** + * @private + * + * @return void + * @throws 1475942906 + */ + function _viewSetup() { + assert('function' === $.type(_viewModel.bootstrap), 'The view model does not implement the method "bootstrap"', 1475942906); + _viewModel.bootstrap(_formManagerInstance); + }; + + /** + * @private + * + * @return void + * @throws 1477506504 + */ + function _bootstrap() { + _configuration = _configuration || {}; + assert('object' === $.type(_configuration['endpoints']), 'Invalid parameter "endpoints"', 1477506504); + _viewSetup(); + }; + + /** + * @public + * + * @return TYPO3/CMS/Form/Backend/FormManager + * @throws 1475942618 + */ + function run() { + if (_isRunning) { + throw 'You can not run the app twice (1475942618)'; + } + + _bootstrap(); + _isRunning = true; + return this; + }; + + /** + * Publish the public methods. + * Implements the "Revealing Module Pattern". + */ + return { + getPrototypes: getPrototypes, + getTemplatesForPrototype: getTemplatesForPrototype, + getAccessibleFormStorageFolders: getAccessibleFormStorageFolders, + getAjaxEndpoint: getAjaxEndpoint, + + assert: assert, + run: run + }; + }; + + /** + * Emulation of static methods + */ + return { + /** + * @public + * @static + * + * Implement the "Singleton Pattern". + * + * Return a singleton instance of a + * "FormManager" object. + * + * @param object configuration + * @param object viewModel + * @return object + */ + getInstance: function(configuration, viewModel) { + if(_formManagerInstance === null) { + _formManagerInstance = new FormManager(configuration, viewModel); + } + return _formManagerInstance; + } + }; + })(); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormManager/ViewModel.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormManager/ViewModel.js new file mode 100644 index 000000000000..d379fe6ecb0f --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/FormManager/ViewModel.js @@ -0,0 +1,534 @@ +/* + * 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! + */ + +/** + * Module: TYPO3/CMS/Form/Backend/FormManager/ViewModel + */ +define(['jquery', + 'TYPO3/CMS/Backend/Modal', + 'TYPO3/CMS/Backend/Severity', + 'TYPO3/CMS/Backend/Wizard', + 'TYPO3/CMS/Backend/Icons', + 'TYPO3/CMS/Backend/Notification' + ], function($, Modal, Severity, Wizard, Icons, Notification) { + 'use strict'; + + return (function($, Modal, Severity, Wizard, Icons,Notification) { + + /** + * @private + * + * @var object + */ + var _formManagerApp = null; + + /** + * @private + * + * @var object + */ + var _domElementIdentifierCache = {}; + + /** + * @private + * + * @return void + */ + function _domElementIdentifierCacheSetup() { + _domElementIdentifierCache = { + newFormModalTrigger: { identifier: '[data-identifier="newForm"]' }, + duplicateFormModalTrigger: { identifier: '[data-identifier="duplicateForm"]' }, + removeFormModalTrigger: { identifier: '[data-identifier="removeForm"]' }, + + newFormName: { identifier: '[data-identifier="newFormName"]' }, + newFormSavePath: { identifier: '[data-identifier="newFormSavePath"]' }, + advancedWizard: { identifier: '[data-identifier="advancedWizard"]' }, + newFormPrototypeName: { identifier: '[data-identifier="newFormPrototypeName"]' }, + newFormTemplate: { identifier: '[data-identifier="newFormTemplate"]' }, + + duplicateFormName: { identifier: '[data-identifier="duplicateFormName"]' }, + duplicateFormSavePath: { identifier: '[data-identifier="duplicateFormSavePath"]' }, + + showReferences: { identifier: '[data-identifier="showReferences"]' }, + referenceLink: { identifier: '[data-identifier="referenceLink"]' }, + + tooltip: { identifier: '[data-toggle="tooltip"]' } + } + }; + + /** + * @private + * + * @return void + * @throws 1477506500 + * @throws 1477506501 + * @throws 1477506502 + */ + function _newFormSetup() { + $(getDomElementIdentifier('newFormModalTrigger')).on('click', function(e) { + e.preventDefault(); + + /** + * Wizard step 1 + */ + Wizard.addSlide('new-form-step-1', TYPO3.lang['formManager.newFormWizard.step1.title'], '', Severity.info, function(slide) { + var advandecWizardHasOptions, folders, html, modal, nextButton, prototypes, savePathSelect, templates; + + modal = Wizard.setup.$carousel.closest('.modal'); + nextButton = modal.find('.modal-footer').find('button[name="next"]'); + + folders = _formManagerApp.getAccessibleFormStorageFolders(); + _formManagerApp.assert(folders.length > 0, 'No accessible form storage folders', 1477506500); + + Wizard.set('savePath', folders[0]['value']); + if (folders.length > 1) { + savePathSelect = $('<select class="new-form-save-path form-control" data-identifier="newFormSavePath" />'); + for (var i = 0, len = folders.length; i < len; ++i) { + var option = new Option(folders[i]['label'], folders[i]['value']); + $(savePathSelect).append(option); + } + } + + prototypes = _formManagerApp.getPrototypes(); + + _formManagerApp.assert(prototypes.length > 0, 'No prototypes available', 1477506501); + Wizard.set('prototypeName', prototypes[0]['value']); + + templates = _formManagerApp.getTemplatesForPrototype(prototypes[0]['value']); + _formManagerApp.assert(templates.length > 0, 'No templates available', 1477506502); + Wizard.set('templatePath', templates[0]['value']); + + html = '<div class="new-form-modal">' + + '<div class="form-horizontal">' + + '<div>' + + '<label class="control-label">' + TYPO3.lang['formManager.form_name'] + '</label>' + + '<input class="new-form-name form-control has-error" data-identifier="newFormName" />'; + + if (savePathSelect) { + html += '<label class="control-label">' + TYPO3.lang['formManager.form_save_path'] + '</label>' + $(savePathSelect)[0].outerHTML; + } + + if (prototypes.length > 1 || templates.length > 1) { + html += '<label class="control-label">' + TYPO3.lang['formManager.newFormWizard.step1.advanced'] + '</label>' + + '<div class="t3-form-controls"><input type="checkbox" class="new-form-advance-wizard" data-identifier="advancedWizard" /></div>'; + } + + html += '</div>' + + '</div>' + + '</div>'; + + slide.html(html); + $(getDomElementIdentifier('newFormName'), modal).focus(); + + $(getDomElementIdentifier('newFormName'), modal).on('keyup paste', function(e) { + if ($(this).val().length > 0) { + $(this).removeClass('has-error'); + Wizard.unlockNextStep(); + Wizard.set('formName', $(this).val()); + } else { + $(this).addClass('has-error'); + Wizard.lockNextStep(); + } + }); + + $(getDomElementIdentifier('newFormSavePath'), modal).on('change', function(e) { + Wizard.set('savePath', $(getDomElementIdentifier('newFormSavePath') + ' option:selected', modal).val()); + }); + + $(getDomElementIdentifier('advancedWizard'), modal).on('change', function(e) { + if ($(this).is(':checked')) { + Wizard.set('advancedWizard', true); + } else { + Wizard.set('advancedWizard', false); + } + }); + + nextButton.on('click', function() { + Wizard.setup.forceSelection = false; + Icons.getIcon('spinner-circle-dark', Icons.sizes.large, null, null).done(function(markup) { + slide.html($('<div />', {class: 'text-center'}).append(markup)); + }); + }); + }); + + /** + * Wizard step 2 + */ + Wizard.addSlide('new-form-step-2', TYPO3.lang['formManager.newFormWizard.step2.title'], '', Severity.info, function(slide, settings) { + var addOnTemplateChangeEvents, html, modal, nextButton, prototypes, prototypeNameSelect, templates, templateSelect; + + if (settings['advancedWizard'] !== true) { + Wizard.unlockNextStep().trigger('click'); + return; + } + + modal = Wizard.setup.$carousel.closest('.modal'); + nextButton = modal.find('.modal-footer').find('button[name="next"]'); + + prototypeNameSelect = $('<select class="new-form-prototype-name form-control" data-identifier="newFormPrototypeName" />'); + templateSelect = $('<select class="new-form-template form-control" data-identifier="newFormTemplate" />'); + + prototypes = _formManagerApp.getPrototypes(); + templates = {}; + if (prototypes.length > 0) { + for (var i = 0, len = prototypes.length; i < len; ++i) { + var option = new Option(prototypes[i]['label'], prototypes[i]['value']); + $(prototypeNameSelect).append(option); + } + + templates = _formManagerApp.getTemplatesForPrototype(prototypes[0]['value']); + for (var i = 0, len = templates.length; i < len; ++i) { + var option = new Option(templates[i]['label'], templates[i]['value']); + $(templateSelect).append(option); + } + } + + html = '<div class="new-form-modal">' + + '<div class="form-horizontal">' + + '<div>'; + + if (prototypes.length > 1) { + html += '<label class="control-label">' + TYPO3.lang['formManager.form_prototype'] + '</label>' + $(prototypeNameSelect)[0].outerHTML; + } + if (templates.length > 1) { + html += '<label class="control-label">' + TYPO3.lang['formManager.form_template'] + '</label>' + $(templateSelect)[0].outerHTML; + } + + html += '</div>' + + '</div>' + + '</div>'; + + slide.html(html); + if (prototypes.length > 1) { + $(getDomElementIdentifier('newFormPrototypeName'), modal).focus(); + } else if (templates.length > 1) { + $(getDomElementIdentifier('newFormTemplate'), modal).focus(); + } + + addOnTemplateChangeEvents = function() { + $(getDomElementIdentifier('newFormTemplate'), modal).on('change', function(e) { + Wizard.set('templatePath', $(getDomElementIdentifier('newFormTemplate') + ' option:selected', modal).val()); + }); + }; + + $(getDomElementIdentifier('newFormPrototypeName'), modal).on('change', function(e) { + Wizard.set('prototypeName', $(this).val()); + templates = _formManagerApp.getTemplatesForPrototype($(this).val()); + $(getDomElementIdentifier('newFormTemplate'), modal).off().empty(); + for (var i = 0, len = templates.length; i < len; ++i) { + var option = new Option(templates[i]['label'], templates[i]['value']); + $(getDomElementIdentifier('newFormTemplate'), modal).append(option); + Wizard.set('templatePath', templates[0]['value']); + } + addOnTemplateChangeEvents(); + }); + + addOnTemplateChangeEvents(); + + nextButton.on('click', function() { + Icons.getIcon('spinner-circle-dark', Icons.sizes.large, null, null).done(function(markup) { + slide.html($('<div />', {class: 'text-center'}).append(markup)); + }); + }); + }); + + /** + * Wizard step 3 + */ + Wizard.addSlide('new-form-step-3', TYPO3.lang['formManager.newFormWizard.step3.title'], TYPO3.lang['formManager.newFormWizard.step3.message'], Severity.info); + + /** + * Wizard step 4 + */ + Wizard.addFinalProcessingSlide(function() { + $.post(_formManagerApp.getAjaxEndpoint('create'), { + tx_form_web_formformbuilder: { + formName: Wizard.setup.settings['formName'], + templatePath: Wizard.setup.settings['templatePath'], + prototypeName: Wizard.setup.settings['prototypeName'], + savePath: Wizard.setup.settings['savePath'] + } + }, function(data, textStatus, jqXHR) { + document.location = data; + Wizard.dismiss(); + }).fail(function(jqXHR, textStatus, errorThrown) { + Notification.error(textStatus, errorThrown, 2); + Wizard.dismiss(); + }); + }).done(function() { + Wizard.show(); + }); + }); + }; + + /** + * @private + * + * @return void + */ + function _removeFormSetup() { + $(getDomElementIdentifier('removeFormModalTrigger')).on('click', function(e) { + var modalButtons = [], that; + + e.preventDefault(); + that = $(this) + + modalButtons.push({ + text: TYPO3.lang['formManager.cancel'], + active: true, + btnClass: 'btn-default', + name: 'cancel', + trigger: function () { + Modal.currentModal.trigger('modal-dismiss'); + } + }); + + modalButtons.push({ + text: TYPO3.lang['formManager.remove_form'], + active: true, + btnClass: 'btn-warning', + name: 'createform', + trigger: function () { + document.location = _formManagerApp.getAjaxEndpoint('delete') + '&tx_form_web_formformbuilder[formPersistenceIdentifier]=' + that.data('formPersistenceIdentifier'); + Modal.currentModal.trigger('modal-dismiss'); + } + }); + + Modal.show( + TYPO3.lang['formManager.remove_form_title'], + TYPO3.lang['formManager.remove_form_message'], + Severity.warning, + modalButtons + ); + }); + }; + + /** + * @private + * + * @return void + * @throws 1477649539 + */ + function _duplicateFormSetup() { + $(getDomElementIdentifier('duplicateFormModalTrigger')).on('click', function(e) { + var that; + + e.preventDefault(); + that = $(this); + + /** + * Wizard step 1 + */ + Wizard.addSlide('duplicate-form-step-1', TYPO3.lang['formManager.duplicateFormWizard.step1.title'].replace('{0}', that.data('formName')), '', Severity.info, function(slide) { + var folders, html, modal, nextButton, savePathSelect; + + modal = Wizard.setup.$carousel.closest('.modal'); + nextButton = modal.find('.modal-footer').find('button[name="next"]'); + + folders = _formManagerApp.getAccessibleFormStorageFolders(); + _formManagerApp.assert(folders.length > 0, 'No accessible form storage folders', 1477649539); + + Wizard.set('formPersistenceIdentifier', that.data('formPersistenceIdentifier')); + Wizard.set('savePath', folders[0]['value']); + if (folders.length > 1) { + savePathSelect = $('<select class="duplicate-form-save-path form-control" data-identifier="duplicateFormSavePath" />'); + for (var i = 0, len = folders.length; i < len; ++i) { + var option = new Option(folders[i]['label'], folders[i]['value']); + $(savePathSelect).append(option); + } + } + + html = '<div class="duplicate-form-modal">' + + '<div class="form-horizontal">' + + '<div>' + + '<label class="control-label">' + TYPO3.lang['formManager.new_form_name'] + '</label>' + + '<input class="duplicate-form-name form-control has-error" data-identifier="duplicateFormName" />'; + + if (savePathSelect) { + html += '<label class="control-label">' + TYPO3.lang['formManager.form_save_path'] + '</label>' + $(savePathSelect)[0].outerHTML; + } + + html += '</div>' + + '</div>' + + '</div>'; + + slide.html(html); + $(getDomElementIdentifier('duplicateFormName'), modal).focus(); + + $(getDomElementIdentifier('duplicateFormName'), modal).on('keyup paste', function(e) { + if ($(this).val().length > 0) { + $(this).removeClass('has-error'); + Wizard.unlockNextStep(); + Wizard.set('formName', $(this).val()); + } else { + $(this).addClass('has-error'); + Wizard.lockNextStep(); + } + }); + + $(getDomElementIdentifier('duplicateFormSavePath'), modal).on('change', function(e) { + Wizard.set('savePath', $(getDomElementIdentifier('duplicateFormSavePath') + ' option:selected', modal).val()); + }); + }); + + /** + * Wizard step 2 + */ + Wizard.addFinalProcessingSlide(function() { + $.post(_formManagerApp.getAjaxEndpoint('duplicate'), { + tx_form_web_formformbuilder: { + formName: Wizard.setup.settings['formName'], + formPersistenceIdentifier: Wizard.setup.settings['formPersistenceIdentifier'], + savePath: Wizard.setup.settings['savePath'] + } + }, function(data, textStatus, jqXHR) { + document.location = data; + Wizard.dismiss(); + }).fail(function(jqXHR, textStatus, errorThrown) { + Notification.error(textStatus, errorThrown, 2); + Wizard.dismiss(); + }); + }).done(function() { + Wizard.show(); + }); + }); + }; + + /** + * @private + * + * @return void + */ + function _showReferencesSetup() { + $(getDomElementIdentifier('showReferences')).on('click', function(e) { + var that, url; + + e.preventDefault(); + that = this; + url = _formManagerApp.getAjaxEndpoint('references') + '&tx_form_web_formformbuilder[formPersistenceIdentifier]=' + $(this).data('formPersistenceIdentifier'); + + $.get(url, function(data, textStatus, jqXHR) { + var html, modalButtons = [], referencesLength; + + modalButtons.push({ + text: TYPO3.lang['formManager.cancel'], + active: true, + btnClass: 'btn-default', + name: 'cancel', + trigger: function () { + Modal.currentModal.trigger('modal-dismiss'); + } + }); + + referencesLength = data['references'].length; + if (referencesLength > 0) { + html = '<div>' + + '<h3>' + TYPO3.lang['formManager.references.headline'].replace('{0}', $(that).data('formName')) + '</h3>' + + '</div>' + + '<div class="table-fit">' + + '<table id="forms" class="table table-striped table-condensed">' + + '<thead>' + + '<tr>' + + '<th>' + TYPO3.lang['formManager.page'] + '</th>' + + '<th>' + TYPO3.lang['formManager.record'] + '</th>' + + '</tr>' + + '</thead>' + + '<tbody>'; + + for (var i = 0, len = data['references'].length; i < len; ++i) { + html += '<tr>' + + '<td>' + data['references'][i]['recordPageTitle'] + '</td>' + + '<td>' + + data['references'][i]['recordIcon'] + + '<a href="' + data['references'][i]['recordEditUrl'] + '" data-identifier="referenceLink">' + + data['references'][i]['recordTitle'] + ' (uid: ' + data['references'][i]['recordUid'] + ')' + + '</a>' + + '</td>' + + '</tr>'; + } + + html += '</tbody>' + + '</table>' + + '</div>'; + } else { + html = '<div>' + + '<h1>' + TYPO3.lang['formManager.references.title'].replace('{0}', data['formPersistenceIdentifier']) + '</h1>' + + '</div>' + + '<div>' + TYPO3.lang['formManager.no_references'] + '</div>'; + } + + html = $(html); + $(getDomElementIdentifier('referenceLink'), html).on('click', function(e) { + e.preventDefault(); + Modal.currentModal.trigger('modal-dismiss'); + document.location = $(this).prop('href'); + }); + + Modal.show( + TYPO3.lang['formManager.references.title'], + html, + Severity.info, + modalButtons + ); + }).fail(function(jqXHR, textStatus, errorThrown) { + if (jqXHR.status !== 0) { + Notification.error(textStatus, errorThrown, 2); + } + }); + }); + }; + + /** + * @public + * + * @param string elementIdentifier + * @param string type + * @return mixed|undefined + * @throws 1477506413 + * @throws 1477506414 + */ + function getDomElementIdentifier(elementIdentifier, type) { + _formManagerApp.assert(elementIdentifier.length > 0, 'Invalid parameter "elementIdentifier"', 1477506413); + _formManagerApp.assert(typeof _domElementIdentifierCache[elementIdentifier] !== "undefined", 'elementIdentifier "' + elementIdentifier + '" does not exist', 1477506414); + if (typeof type === "undefined") { + type = 'identifier'; + } + + return _domElementIdentifierCache[elementIdentifier][type] || undefined; + }; + + /** + * @public + * + * @param object formManagerApp + * @return void + */ + function bootstrap(formManagerApp) { + _formManagerApp = formManagerApp; + _domElementIdentifierCacheSetup(); + _removeFormSetup(); + _newFormSetup(); + _duplicateFormSetup(); + _showReferencesSetup(); + $(getDomElementIdentifier('tooltip')).tooltip(); + }; + + /** + * Publish the public methods. + * Implements the "Revealing Module Pattern". + */ + return { + bootstrap: bootstrap, + }; + })($, Modal, Severity, Wizard, Icons, Notification); +}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Backend/Vendor/jquery.mjs.nestedSortable.js b/typo3/sysext/form/Resources/Public/JavaScript/Backend/Vendor/jquery.mjs.nestedSortable.js new file mode 100644 index 000000000000..38bf5f7eb0d7 --- /dev/null +++ b/typo3/sysext/form/Resources/Public/JavaScript/Backend/Vendor/jquery.mjs.nestedSortable.js @@ -0,0 +1,907 @@ +/* + * jQuery UI Nested Sortable + * v 2.1a / 2016-02-04 + * https://github.com/ilikenwf/nestedSortable + * + * Depends on: + * jquery.ui.sortable.js 1.10+ + * + * Copyright (c) 2010-2016 Manuele J Sarfatti and contributors + * Licensed under the MIT License + * http://www.opensource.org/licenses/mit-license.php + */ +(function( factory ) { + "use strict"; + + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define([ + "jquery", + "jquery-ui/sortable" + ], factory ); + } else { + + // Browser globals + factory( window.jQuery ); + } +}(function($) { + "use strict"; + + function isOverAxis( x, reference, size ) { + return ( x > reference ) && ( x < ( reference + size ) ); + } + + $.widget("mjs.nestedSortable", $.extend({}, $.ui.sortable.prototype, { + + options: { + disableParentChange: false, + doNotClear: false, + expandOnHover: 700, + isAllowed: function() { return true; }, + isTree: false, + listType: "ol", + maxLevels: 0, + protectRoot: false, + rootID: null, + rtl: false, + startCollapsed: false, + tabSize: 20, + + branchClass: "mjs-nestedSortable-branch", + collapsedClass: "mjs-nestedSortable-collapsed", + disableNestingClass: "mjs-nestedSortable-no-nesting", + errorClass: "mjs-nestedSortable-error", + expandedClass: "mjs-nestedSortable-expanded", + hoveringClass: "mjs-nestedSortable-hovering", + leafClass: "mjs-nestedSortable-leaf", + disabledClass: "mjs-nestedSortable-disabled" + }, + + _create: function() { + var self = this, + err; + + this.element.data("ui-sortable", this.element.data("mjs-nestedSortable")); + + // mjs - prevent browser from freezing if the HTML is not correct + if (!this.element.is(this.options.listType)) { + err = "nestedSortable: " + + "Please check that the listType option is set to your actual list type"; + + throw new Error(err); + } + + // if we have a tree with expanding/collapsing functionality, + // force 'intersect' tolerance method + if (this.options.isTree && this.options.expandOnHover) { + this.options.tolerance = "intersect"; + } + + $.ui.sortable.prototype._create.apply(this, arguments); + + // prepare the tree by applying the right classes + // (the CSS is responsible for actual hide/show functionality) + if (this.options.isTree) { + $(this.items).each(function() { + var $li = this.item, + hasCollapsedClass = $li.hasClass(self.options.collapsedClass), + hasExpandedClass = $li.hasClass(self.options.expandedClass); + + if ($li.children(self.options.listType).length) { + $li.addClass(self.options.branchClass); + // expand/collapse class only if they have children + + if ( !hasCollapsedClass && !hasExpandedClass ) { + if (self.options.startCollapsed) { + $li.addClass(self.options.collapsedClass); + } else { + $li.addClass(self.options.expandedClass); + } + } + } else { + $li.addClass(self.options.leafClass); + } + }); + } + }, + + _destroy: function() { + this.element + .removeData("mjs-nestedSortable") + .removeData("ui-sortable"); + return $.ui.sortable.prototype._destroy.apply(this, arguments); + }, + + _mouseDrag: function(event) { + var i, + item, + itemElement, + intersection, + self = this, + o = this.options, + scrolled = false, + $document = $(document), + previousTopOffset, + parentItem, + level, + childLevels, + itemAfter, + itemBefore, + newList, + method, + a, + previousItem, + nextItem, + helperIsNotSibling; + + //Compute the helpers position + this.position = this._generatePosition(event); + this.positionAbs = this._convertPositionTo("absolute"); + + if (!this.lastPositionAbs) { + this.lastPositionAbs = this.positionAbs; + } + + //Do scrolling + if (this.options.scroll) { + if (this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") { + + if ( + ( + this.overflowOffset.top + + this.scrollParent[0].offsetHeight + ) - + event.pageY < + o.scrollSensitivity + ) { + scrolled = this.scrollParent.scrollTop() + o.scrollSpeed; + this.scrollParent.scrollTop(scrolled); + } else if ( + event.pageY - + this.overflowOffset.top < + o.scrollSensitivity + ) { + scrolled = this.scrollParent.scrollTop() - o.scrollSpeed; + this.scrollParent.scrollTop(scrolled); + } + + if ( + ( + this.overflowOffset.left + + this.scrollParent[0].offsetWidth + ) - + event.pageX < + o.scrollSensitivity + ) { + scrolled = this.scrollParent.scrollLeft() + o.scrollSpeed; + this.scrollParent.scrollLeft(scrolled); + } else if ( + event.pageX - + this.overflowOffset.left < + o.scrollSensitivity + ) { + scrolled = this.scrollParent.scrollLeft() - o.scrollSpeed; + this.scrollParent.scrollLeft(scrolled); + } + + } else { + + if ( + event.pageY - + $document.scrollTop() < + o.scrollSensitivity + ) { + scrolled = $document.scrollTop() - o.scrollSpeed; + $document.scrollTop(scrolled); + } else if ( + $(window).height() - + ( + event.pageY - + $document.scrollTop() + ) < + o.scrollSensitivity + ) { + scrolled = $document.scrollTop() + o.scrollSpeed; + $document.scrollTop(scrolled); + } + + if ( + event.pageX - + $document.scrollLeft() < + o.scrollSensitivity + ) { + scrolled = $document.scrollLeft() - o.scrollSpeed; + $document.scrollLeft(scrolled); + } else if ( + $(window).width() - + ( + event.pageX - + $document.scrollLeft() + ) < + o.scrollSensitivity + ) { + scrolled = $document.scrollLeft() + o.scrollSpeed; + $document.scrollLeft(scrolled); + } + + } + + if (scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) { + $.ui.ddmanager.prepareOffsets(this, event); + } + } + + //Regenerate the absolute position used for position checks + this.positionAbs = this._convertPositionTo("absolute"); + + // mjs - find the top offset before rearrangement, + previousTopOffset = this.placeholder.offset().top; + + //Set the helper position + if (!this.options.axis || this.options.axis !== "y") { + this.helper[0].style.left = this.position.left + "px"; + } + if (!this.options.axis || this.options.axis !== "x") { + this.helper[0].style.top = (this.position.top) + "px"; + } + + // mjs - check and reset hovering state at each cycle + this.hovering = this.hovering ? this.hovering : null; + this.mouseentered = this.mouseentered ? this.mouseentered : false; + + // mjs - let's start caching some variables + (function() { + var _parentItem = this.placeholder.parent().parent(); + if (_parentItem && _parentItem.closest(".ui-sortable").length) { + parentItem = _parentItem; + } + }.call(this)); + + level = this._getLevel(this.placeholder); + childLevels = this._getChildLevels(this.helper); + newList = document.createElement(o.listType); + + //Rearrange + for (i = this.items.length - 1; i >= 0; i--) { + + //Cache variables and intersection, continue if no intersection + item = this.items[i]; + itemElement = item.item[0]; + intersection = this._intersectsWithPointer(item); + if (!intersection) { + continue; + } + + // Only put the placeholder inside the current Container, skip all + // items form other containers. This works because when moving + // an item from one container to another the + // currentContainer is switched before the placeholder is moved. + // + // Without this moving items in "sub-sortables" can cause the placeholder to jitter + // beetween the outer and inner container. + if (item.instance !== this.currentContainer) { + continue; + } + + // No action if intersected item is disabled + // and the element above or below in the direction we're going is also disabled + if (itemElement.className.indexOf(o.disabledClass) !== -1) { + // Note: intersection hardcoded direction values from + // jquery.ui.sortable.js:_intersectsWithPointer + if (intersection === 2) { + // Going down + itemAfter = this.items[i + 1]; + if (itemAfter && itemAfter.item.hasClass(o.disabledClass)) { + continue; + } + + } else if (intersection === 1) { + // Going up + itemBefore = this.items[i - 1]; + if (itemBefore && itemBefore.item.hasClass(o.disabledClass)) { + continue; + } + } + } + + method = intersection === 1 ? "next" : "prev"; + + // cannot intersect with itself + // no useless actions that have been done before + // no action if the item moved is the parent of the item checked + if (itemElement !== this.currentItem[0] && + this.placeholder[method]()[0] !== itemElement && + !$.contains(this.placeholder[0], itemElement) && + ( + this.options.type === "semi-dynamic" ? + !$.contains(this.element[0], itemElement) : + true + ) + ) { + + // mjs - we are intersecting an element: + // trigger the mouseenter event and store this state + if (!this.mouseentered) { + $(itemElement).mouseenter(); + this.mouseentered = true; + } + + // mjs - if the element has children and they are hidden, + // show them after a delay (CSS responsible) + if (o.isTree && $(itemElement).hasClass(o.collapsedClass) && o.expandOnHover) { + if (!this.hovering) { + $(itemElement).addClass(o.hoveringClass); + this.hovering = window.setTimeout(function() { + $(itemElement) + .removeClass(o.collapsedClass) + .addClass(o.expandedClass); + + self.refreshPositions(); + self._trigger("expand", event, self._uiHash()); + }, o.expandOnHover); + } + } + + this.direction = intersection === 1 ? "down" : "up"; + + // mjs - rearrange the elements and reset timeouts and hovering state + if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) { + $(itemElement).mouseleave(); + this.mouseentered = false; + $(itemElement).removeClass(o.hoveringClass); + if (this.hovering) { + window.clearTimeout(this.hovering); + } + this.hovering = null; + + // mjs - do not switch container if + // it's a root item and 'protectRoot' is true + // or if it's not a root item but we are trying to make it root + if (o.protectRoot && + !( + this.currentItem[0].parentNode === this.element[0] && + // it's a root item + itemElement.parentNode !== this.element[0] + // it's intersecting a non-root item + ) + ) { + if (this.currentItem[0].parentNode !== this.element[0] && + itemElement.parentNode === this.element[0] + ) { + + if ( !$(itemElement).children(o.listType).length) { + itemElement.appendChild(newList); + if (o.isTree) { + $(itemElement) + .removeClass(o.leafClass) + .addClass(o.branchClass + " " + o.expandedClass); + } + } + + if (this.direction === "down") { + a = $(itemElement).prev().children(o.listType); + } else { + a = $(itemElement).children(o.listType); + } + + if (a[0] !== undefined) { + this._rearrange(event, null, a); + } + + } else { + this._rearrange(event, item); + } + } else if (!o.protectRoot) { + this._rearrange(event, item); + } + } else { + break; + } + + // Clear emtpy ul's/ol's + this._clearEmpty(itemElement); + + this._trigger("change", event, this._uiHash()); + break; + } + } + + // mjs - to find the previous sibling in the list, + // keep backtracking until we hit a valid list item. + (function() { + var _previousItem = this.placeholder.prev(); + if (_previousItem.length) { + previousItem = _previousItem; + } else { + previousItem = null; + } + }.call(this)); + + if (previousItem != null) { + while ( + previousItem[0].nodeName.toLowerCase() !== "li" || + previousItem[0].className.indexOf(o.disabledClass) !== -1 || + previousItem[0] === this.currentItem[0] || + previousItem[0] === this.helper[0] + ) { + if (previousItem[0].previousSibling) { + previousItem = $(previousItem[0].previousSibling); + } else { + previousItem = null; + break; + } + } + } + + // mjs - to find the next sibling in the list, + // keep stepping forward until we hit a valid list item. + (function() { + var _nextItem = this.placeholder.next(); + if (_nextItem.length) { + nextItem = _nextItem; + } else { + nextItem = null; + } + }.call(this)); + + if (nextItem != null) { + while ( + nextItem[0].nodeName.toLowerCase() !== "li" || + nextItem[0].className.indexOf(o.disabledClass) !== -1 || + nextItem[0] === this.currentItem[0] || + nextItem[0] === this.helper[0] + ) { + if (nextItem[0].nextSibling) { + nextItem = $(nextItem[0].nextSibling); + } else { + nextItem = null; + break; + } + } + } + + this.beyondMaxLevels = 0; + + // mjs - if the item is moved to the left, send it one level up + // but only if it's at the bottom of the list + if (parentItem != null && + nextItem == null && + !(o.protectRoot && parentItem[0].parentNode == this.element[0]) && + ( + o.rtl && + ( + this.positionAbs.left + + this.helper.outerWidth() > parentItem.offset().left + + parentItem.outerWidth() + ) || + !o.rtl && (this.positionAbs.left < parentItem.offset().left) + ) + ) { + + parentItem.after(this.placeholder[0]); + helperIsNotSibling = !parentItem + .children(o.listItem) + .children("li:visible:not(.ui-sortable-helper)") + .length; + if (o.isTree && helperIsNotSibling) { + parentItem + .removeClass(this.options.branchClass + " " + this.options.expandedClass) + .addClass(this.options.leafClass); + } + if(typeof parentItem !== 'undefined') + this._clearEmpty(parentItem[0]); + this._trigger("change", event, this._uiHash()); + // mjs - if the item is below a sibling and is moved to the right, + // make it a child of that sibling + } else if (previousItem != null && + !previousItem.hasClass(o.disableNestingClass) && + ( + previousItem.children(o.listType).length && + previousItem.children(o.listType).is(":visible") || + !previousItem.children(o.listType).length + ) && + !(o.protectRoot && this.currentItem[0].parentNode === this.element[0]) && + ( + o.rtl && + ( + this.positionAbs.left + + this.helper.outerWidth() < + previousItem.offset().left + + previousItem.outerWidth() - + o.tabSize + ) || + !o.rtl && + (this.positionAbs.left > previousItem.offset().left + o.tabSize) + ) + ) { + + this._isAllowed(previousItem, level, level + childLevels + 1); + + if (!previousItem.children(o.listType).length) { + previousItem[0].appendChild(newList); + if (o.isTree) { + previousItem + .removeClass(o.leafClass) + .addClass(o.branchClass + " " + o.expandedClass); + } + } + + // mjs - if this item is being moved from the top, add it to the top of the list. + if (previousTopOffset && (previousTopOffset <= previousItem.offset().top)) { + previousItem.children(o.listType).prepend(this.placeholder); + } else { + // mjs - otherwise, add it to the bottom of the list. + previousItem.children(o.listType)[0].appendChild(this.placeholder[0]); + } + if(typeof parentItem !== 'undefined') + this._clearEmpty(parentItem[0]); + this._trigger("change", event, this._uiHash()); + } else { + this._isAllowed(parentItem, level, level + childLevels); + } + + //Post events to containers + this._contactContainers(event); + + //Interconnect with droppables + if ($.ui.ddmanager) { + $.ui.ddmanager.drag(this, event); + } + + //Call callbacks + this._trigger("sort", event, this._uiHash()); + + this.lastPositionAbs = this.positionAbs; + return false; + + }, + + _mouseStop: function(event) { + // mjs - if the item is in a position not allowed, send it back + if (this.beyondMaxLevels) { + + this.placeholder.removeClass(this.options.errorClass); + + if (this.domPosition.prev) { + $(this.domPosition.prev).after(this.placeholder); + } else { + $(this.domPosition.parent).prepend(this.placeholder); + } + + this._trigger("revert", event, this._uiHash()); + + } + + // mjs - clear the hovering timeout, just to be sure + $("." + this.options.hoveringClass) + .mouseleave() + .removeClass(this.options.hoveringClass); + + this.mouseentered = false; + if (this.hovering) { + window.clearTimeout(this.hovering); + } + this.hovering = null; + + this._relocate_event = event; + this._pid_current = $(this.domPosition.parent).parent().attr("id"); + this._sort_current = this.domPosition.prev ? $(this.domPosition.prev).next().index() : 0; + $.ui.sortable.prototype._mouseStop.apply(this, arguments); //asybnchronous execution, @see _clear for the relocate event. + }, + + // mjs - this function is slightly modified + // to make it easier to hover over a collapsed element and have it expand + _intersectsWithSides: function(item) { + + var half = this.options.isTree ? .8 : .5, + isOverBottomHalf = isOverAxis( + this.positionAbs.top + this.offset.click.top, + item.top + (item.height * half), + item.height + ), + isOverTopHalf = isOverAxis( + this.positionAbs.top + this.offset.click.top, + item.top - (item.height * half), + item.height + ), + isOverRightHalf = isOverAxis( + this.positionAbs.left + this.offset.click.left, + item.left + (item.width / 2), + item.width + ), + verticalDirection = this._getDragVerticalDirection(), + horizontalDirection = this._getDragHorizontalDirection(); + + if (this.floating && horizontalDirection) { + return ( + (horizontalDirection === "right" && isOverRightHalf) || + (horizontalDirection === "left" && !isOverRightHalf) + ); + } else { + return verticalDirection && ( + (verticalDirection === "down" && isOverBottomHalf) || + (verticalDirection === "up" && isOverTopHalf) + ); + } + + }, + + _contactContainers: function() { + + if (this.options.protectRoot && this.currentItem[0].parentNode === this.element[0] ) { + return; + } + + $.ui.sortable.prototype._contactContainers.apply(this, arguments); + + }, + + _clear: function() { + var i, + item; + + $.ui.sortable.prototype._clear.apply(this, arguments); + + //relocate event + if (!(this._pid_current === this._uiHash().item.parent().parent().attr("id") && + this._sort_current === this._uiHash().item.index())) { + this._trigger("relocate", this._relocate_event, this._uiHash()); + } + + // mjs - clean last empty ul/ol + for (i = this.items.length - 1; i >= 0; i--) { + item = this.items[i].item[0]; + this._clearEmpty(item); + } + + }, + + serialize: function(options) { + + var o = $.extend({}, this.options, options), + items = this._getItemsAsjQuery(o && o.connected), + str = []; + + $(items).each(function() { + var res = ($(o.item || this).attr(o.attribute || "id") || "") + .match(o.expression || (/(.+)[-=_](.+)/)), + pid = ($(o.item || this).parent(o.listType) + .parent(o.items) + .attr(o.attribute || "id") || "") + .match(o.expression || (/(.+)[-=_](.+)/)); + + if (res) { + str.push( + ( + (o.key || res[1]) + + "[" + + (o.key && o.expression ? res[1] : res[2]) + "]" + ) + + "=" + + (pid ? (o.key && o.expression ? pid[1] : pid[2]) : o.rootID)); + } + }); + + if (!str.length && o.key) { + str.push(o.key + "="); + } + + return str.join("&"); + + }, + + toHierarchy: function(options) { + + var o = $.extend({}, this.options, options), + ret = []; + + $(this.element).children(o.items).each(function() { + var level = _recursiveItems(this); + ret.push(level); + }); + + return ret; + + function _recursiveItems(item) { + var id = ($(item).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[-=_](.+)/)), + currentItem; + + var data = $(item).data(); + if (data.nestedSortableItem) { + delete data.nestedSortableItem; // Remove the nestedSortableItem object from the data + } + + if (id) { + currentItem = { + "id": id[2] + }; + + currentItem = $.extend({}, currentItem, data); // Combine the two objects + + if ($(item).children(o.listType).children(o.items).length > 0) { + currentItem.children = []; + $(item).children(o.listType).children(o.items).each(function() { + var level = _recursiveItems(this); + currentItem.children.push(level); + }); + } + return currentItem; + } + } + }, + + toArray: function(options) { + + var o = $.extend({}, this.options, options), + sDepth = o.startDepthCount || 0, + ret = [], + left = 1; + + if (!o.excludeRoot) { + ret.push({ + "item_id": o.rootID, + "parent_id": null, + "depth": sDepth, + "left": left, + "right": ($(o.items, this.element).length + 1) * 2 + }); + left++; + } + + $(this.element).children(o.items).each(function() { + left = _recursiveArray(this, sDepth, left); + }); + + ret = ret.sort(function(a, b) { return (a.left - b.left); }); + + return ret; + + function _recursiveArray(item, depth, _left) { + + var right = _left + 1, + id, + pid, + parentItem; + + if ($(item).children(o.listType).children(o.items).length > 0) { + depth++; + $(item).children(o.listType).children(o.items).each(function() { + right = _recursiveArray($(this), depth, right); + }); + depth--; + } + + id = ($(item).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[-=_](.+)/)); + + if (depth === sDepth) { + pid = o.rootID; + } else { + parentItem = ($(item).parent(o.listType) + .parent(o.items) + .attr(o.attribute || "id")) + .match(o.expression || (/(.+)[-=_](.+)/)); + pid = parentItem[2]; + } + + if (id) { + var data = $(item).children('div').data(); + var itemObj = $.extend( data, { + "id":id[2], + "parent_id":pid, + "depth":depth, + "left":_left, + "right":right + } ); + ret.push( itemObj ); + } + + _left = right + 1; + return _left; + } + + }, + + _clearEmpty: function (item) { + function replaceClass(elem, search, replace, swap) { + if (swap) { + search = [replace, replace = search][0]; + } + + $(elem).removeClass(search).addClass(replace); + } + + var o = this.options, + childrenList = $(item).children(o.listType), + hasChildren = childrenList.has('li').length; + + var doNotClear = + o.doNotClear || + hasChildren || + o.protectRoot && $(item)[0] === this.element[0]; + + if (o.isTree) { + replaceClass(item, o.branchClass, o.leafClass, doNotClear); + } + + if (!doNotClear) { + childrenList.parent().removeClass(o.expandedClass); + childrenList.remove(); + } + }, + + _getLevel: function(item) { + + var level = 1, + list; + + if (this.options.listType) { + list = item.closest(this.options.listType); + while (list && list.length > 0 && !list.is(".ui-sortable")) { + level++; + list = list.parent().closest(this.options.listType); + } + } + + return level; + }, + + _getChildLevels: function(parent, depth) { + var self = this, + o = this.options, + result = 0; + depth = depth || 0; + + $(parent).children(o.listType).children(o.items).each(function(index, child) { + result = Math.max(self._getChildLevels(child, depth + 1), result); + }); + + return depth ? result + 1 : result; + }, + + _isAllowed: function(parentItem, level, levels) { + var o = this.options, + // this takes into account the maxLevels set to the recipient list + maxLevels = this + .placeholder + .closest(".ui-sortable") + .nestedSortable("option", "maxLevels"), + + // Check if the parent has changed to prevent it, when o.disableParentChange is true + oldParent = this.currentItem.parent().parent(), + disabledByParentchange = o.disableParentChange && ( + //From somewhere to somewhere else, except the root + typeof parentItem !== 'undefined' && !oldParent.is(parentItem) || + typeof parentItem === 'undefined' && oldParent.is("li") //From somewhere to the root + ); + // mjs - is the root protected? + // mjs - are we nesting too deep? + if ( + disabledByParentchange || + !o.isAllowed(this.placeholder, parentItem, this.currentItem) + ) { + this.placeholder.addClass(o.errorClass); + if (maxLevels < levels && maxLevels !== 0) { + this.beyondMaxLevels = levels - maxLevels; + } else { + this.beyondMaxLevels = 1; + } + } else { + if (maxLevels < levels && maxLevels !== 0) { + this.placeholder.addClass(o.errorClass); + this.beyondMaxLevels = levels - maxLevels; + } else { + this.placeholder.removeClass(o.errorClass); + this.beyondMaxLevels = 0; + } + } + } + + })); + + $.mjs.nestedSortable.prototype.options = $.extend( + {}, + $.ui.sortable.prototype.options, + $.mjs.nestedSortable.prototype.options + ); +})); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard.js deleted file mode 100644 index 3be1fa180bd7..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard.js +++ /dev/null @@ -1,276 +0,0 @@ -/* - * 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! - */ - -function configureWizardApplication() { - var basicdeps = [ - //'TYPO3', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Elements/ButtonGroup', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Button', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Checkbox', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Fieldset', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Fileupload', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Hidden', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Password', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Radio', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Reset', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Select', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Submit', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Textarea', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Textline' - ]; - requirejs.config({shim: { - //'extjs': {exports: 'Ext'}, - //'TYPO3': {exports: 'TYPO3'}, - 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.isemptyobject': {exports: 'Ext.isemptyobject', deps: []}, - 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.merge': {exports: 'Ext.merge', deps: []}, - 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.spinner': {exports: 'Ext.ux.Spinner', deps: []}, - 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.textfieldsubmit': {exports: 'Ext.ux.form.textfieldsubmit', deps: []}, - 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.spinnerfield': {exports: 'Ext.ux.form.SpinnerField', deps: ['TYPO3/CMS/Form/Wizard/Ux/Ext.ux.spinner']}, - 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.FakeFormPanel': {exports: 'Ext.ux.form.FakeFormPanel', deps: []}, - 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.ValueCheckbox': {exports: 'Ext.ux.form.ValueCheckbox', deps: []}, - 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.grid.CheckColumn': {exports: 'Ext.ux.grid.CheckColumn', deps: []}, - 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.grid.SingleSelectCheckColumn': {exports: 'Ext.ux.grid.SingleSelectCheckColumn', deps: ['TYPO3/CMS/Form/Wizard/Ux/Ext.ux.grid.CheckColumn']}, - 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.grid.ItemDeleter': {exports: 'Ext.ux.grid.ItemDeleter', deps: []}, - 'TYPO3/CMS/Form/Wizard/Settings': {exports: 'TYPO3.Form.Wizard.Settings', deps: []}, // defined during require callback - 'TYPO3/CMS/Form/Wizard/Helpers/History': {exports: 'TYPO3.Form.Wizard.Helpers.History', deps: []}, - 'TYPO3/CMS/Form/Wizard/Helpers/Element': {exports: 'TYPO3.Form.Wizard.Helpers.Element', deps: []}, - 'TYPO3/CMS/Form/Wizard/Elements/Dummy': {exports: 'TYPO3.Form.Wizard.Elements.Dummy', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/ButtonGroup': {exports: 'TYPO3.Form.Wizard.ButtonGroup', deps: []}, - 'TYPO3/CMS/Form/Wizard/Elements/Container': {exports: 'TYPO3.Form.Wizard.Container', deps: ['TYPO3/CMS/Form/Wizard/Elements/Dummy', 'TYPO3/CMS/Form/Wizard/Elements/Content/Header', 'TYPO3/CMS/Form/Wizard/Elements/Predefined/RadioGroup', 'TYPO3/CMS/Form/Wizard/Elements/Predefined/Email', 'TYPO3/CMS/Form/Wizard/Elements/Predefined/CheckboxGroup', 'TYPO3/CMS/Form/Wizard/Elements/Predefined/Name']}, - 'TYPO3/CMS/Form/Wizard/Elements/Elements': {exports: 'TYPO3.Form.Wizard.Elements', deps: ['TYPO3/CMS/Form/Wizard/Helpers/Element', 'TYPO3/CMS/Form/Wizard/Helpers/History', 'TYPO3/CMS/Form/Wizard/Elements/ButtonGroup']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Form': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Form', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements', 'TYPO3/CMS/Form/Wizard/Elements/Container']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Right': {exports: 'TYPO3.Form.Wizard.Viewport.Right', deps: ['TYPO3/CMS/Form/Wizard/Viewport', 'TYPO3/CMS/Form/Wizard/Elements/Basic/Form']}, - 'TYPO3/CMS/Form/Wizard/Elements/Content/Header': {exports: 'TYPO3.Form.Wizard.Elements.Content.Header', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Content/Textblock': {exports: 'TYPO3.Form.Wizard.Elements.Content.Textblock', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Elements/Content': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Elements.Content', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Elements/ButtonGroup', 'TYPO3/CMS/Form/Wizard/Elements/Content/Header', 'TYPO3/CMS/Form/Wizard/Elements/Content/Textblock']}, - 'TYPO3/CMS/Form/Wizard/Elements/Predefined/Email': {exports: 'TYPO3.Form.Wizard.Elements.Predefined.Email', deps: ['TYPO3/CMS/Form/Wizard/Elements/Basic/Textline']}, - 'TYPO3/CMS/Form/Wizard/Elements/Predefined/CheckboxGroup': {exports: 'TYPO3.Form.Wizard.Elements.Predefined.CheckboxGroup', deps: ['TYPO3/CMS/Form/Wizard/Elements/Basic/Fieldset', 'TYPO3/CMS/Form/Wizard/Elements/Basic/Checkbox']}, - 'TYPO3/CMS/Form/Wizard/Elements/Predefined/Name': {exports: 'TYPO3.Form.Wizard.Elements.Predefined.Name', deps: ['TYPO3/CMS/Form/Wizard/Elements/Basic/Fieldset', 'TYPO3/CMS/Form/Wizard/Elements/Basic/Textline']}, - 'TYPO3/CMS/Form/Wizard/Elements/Predefined/RadioGroup': {exports: 'TYPO3.Form.Wizard.Elements.Predefined.RadioGroup', deps: ['TYPO3/CMS/Form/Wizard/Elements/Basic/Fieldset', 'TYPO3/CMS/Form/Wizard/Elements/Basic/Radio', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.merge']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Elements/Predefined': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Elements.Predefined', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Elements/ButtonGroup']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Textline': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Textline', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Textarea': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Textarea', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Submit': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Submit', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Select': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Select', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Reset': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Reset', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Radio': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Radio', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Password': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Password', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Hidden': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Hidden', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Fileupload': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Fileupload', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Fieldset': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Fieldset', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Checkbox': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Checkbox', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Button': {exports: 'TYPO3.Form.Wizard.Elements.Basic.Button', deps: ['TYPO3/CMS/Form/Wizard/Elements/Elements']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Elements/Basic': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Elements.Basic', deps: basicdeps}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Elements/ButtonGroup': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Elements']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Elements': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Elements', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Options': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Options', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.grid.SingleSelectCheckColumn', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.grid.ItemDeleter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Label': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Label', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.FakeFormPanel']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Dummy': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Dummy', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Alphabetic': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Alphabetic', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Alphanumeric': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Alphanumeric', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Currency': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Currency', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Digit': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Digit', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Integer': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Integer', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/LowerCase': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.LowerCase', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/RegExp': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.RegExp', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/StripNewLines': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.StripNewLines', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/TitleCase': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.TitleCase', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Trim': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Trim', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/UpperCase': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.UpperCase', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Filter']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Dummy']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Legend': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Legend', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.FakeFormPanel']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.FakeFormPanel']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Required': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Required', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Dummy': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Dummy', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Alphabetic': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Alphabetic', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Alphanumeric': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Alphanumeric', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Between': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Between', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Date': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Date', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Digit': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Digit', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Email': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Email', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Equals': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Equals', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/FileAllowedTypes': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileAllowedTypes', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/FileMaximumSize': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileMaximumSize', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/FileMinimumSize': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileMinimumSize', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Float': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Float', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/GreaterThan': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.GreaterThan', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/InArray': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.InArray', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Integer': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Integer', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Ip': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Ip', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Length': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Length', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/LessThan': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.LessThan', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/RegExp': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.RegExp', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Uri': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Uri', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Rule']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Dummy']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Various': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Various', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.FakeFormPanel']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Attributes': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Attributes', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.ValueCheckbox', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.FakeFormPanel', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.spinnerfield']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Panel': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Panel', deps: [ - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Legend', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Label', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Alphabetic', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Alphanumeric', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Currency', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Digit', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Integer', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/LowerCase', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/RegExp', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/StripNewLines', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/TitleCase', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/Trim', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters/UpperCase', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Filters', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Various', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Alphabetic', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Alphanumeric', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Between', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Date', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Digit', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Email', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Equals', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/FileAllowedTypes', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/FileMaximumSize', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/FileMinimumSize', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Float', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/GreaterThan', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/InArray', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Integer', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Ip', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Length', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/LessThan', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/RegExp', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Required', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation/Uri', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Validation', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Options' - ]}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Dummy': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options.Dummy', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Options', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessors/Dummy': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Dummy', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Form']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessors/PostProcessor': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.PostProcessor', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Form']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessors/Mail': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Mail', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Form', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessors/PostProcessor']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessors/Redirect': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Redirect', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Form', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessors/PostProcessor']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessor': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessor', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Form', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.isemptyobject', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessors/Dummy', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessors/Mail', 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessors/Redirect']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/Attributes': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Form.Attributes', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Attributes']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/Prefix': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Form.Prefix', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Form', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.textfieldsubmit', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.FakeFormPanel']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/Behaviour': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Form.Behaviour', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left/Form', 'TYPO3/CMS/Form/Wizard/Ux/Ext.ux.form.FakeFormPanel']}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form': {exports: 'TYPO3.Form.Wizard.Viewport.Left.Form', deps: ['TYPO3/CMS/Form/Wizard/Viewport/Left'/*, 'TYPO3/CMS/Form/Wizard/Settings'*/]}, - 'TYPO3/CMS/Form/Wizard/Viewport/Left': {exports: 'TYPO3.Form.Wizard.Viewport.Left', deps: ['TYPO3/CMS/Form/Wizard/Viewport'/*, 'TYPO3/CMS/Form/Wizard/Settings'*/]}, - 'TYPO3/CMS/Form/Wizard/Viewport': {exports: 'TYPO3.Form.Wizard.Viewport', deps: []} - }}); -} -configureWizardApplication(); - -/** - * Initialization script of TYPO3 form Wizard - */ -define('TYPO3/CMS/Form/Wizard', [ - //'extjs', - //'TYPO3', - 'TYPO3/CMS/Backend/SplitButtons', - 'TYPO3/CMS/Form/Wizard/Settings', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Elements/Content', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Elements/Predefined', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Elements/Basic', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Elements', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Forms/Options', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Panel', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options/Dummy', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Options', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/PostProcessor', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/Attributes', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/Prefix', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form/Behaviour', - 'TYPO3/CMS/Form/Wizard/Viewport/Left/Form', - 'TYPO3/CMS/Form/Wizard/Viewport/Left', - 'TYPO3/CMS/Form/Wizard/Elements/Basic/Form', - 'TYPO3/CMS/Form/Wizard/Viewport/Right', - 'TYPO3/CMS/Form/Wizard/Viewport' -], function (//Ext, - //TYPO3, - SplitButtons, - TYPO3_CMS_Form_Wizard_Settings, - TYPO3_CMS_Form_Wizard_Viewport_Left_Elements_Content, - TYPO3_CMS_Form_Wizard_Viewport_Left_Elements_Predefined, - TYPO3_CMS_Form_Wizard_Viewport_Left_Elements_Basic, - TYPO3_CMS_Form_Wizard_Viewport_Left_Elements, - TYPO3_CMS_Form_Wizard_Viewport_Left_Options_Forms_Options, - TYPO3_CMS_Form_Wizard_Viewport_Left_Options_Panel, - TYPO3_CMS_Form_Wizard_Viewport_Left_Options_Dummy, - TYPO3_CMS_Form_Wizard_Viewport_Left_Options, - TYPO3_CMS_Form_Wizard_Viewport_Left_Form_PostProcessor, - TYPO3_CMS_Form_Wizard_Viewport_Left_Form_Attributes, - TYPO3_CMS_Form_Wizard_Viewport_Left_Form_Prefix, - TYPO3_CMS_Form_Wizard_Viewport_Left_Form_Behaviour, - TYPO3_CMS_Form_Wizard_Viewport_Left_Form, - TYPO3_CMS_Form_Wizard_Viewport_Left, - TYPO3_CMS_Form_Wizard_Elements_Basic_Form, - TYPO3_CMS_Form_Wizard_Viewport_Right, - TYPO3_CMS_Form_Wizard_Viewport -) { - /** - * called when built as Object with "new" - * - * @constructor - */ - var Wizard = function() { - this.initialize(); - }; - - Wizard.prototype.initialize = function() { - Ext.onReady(function() { - var transportElId = Ext.get('form-wizard-element-container').dom.getAttribute('rel'); - var transportEl = Ext.get(transportElId).dom; - var viewport = new TYPO3.Form.Wizard.Viewport({ - renderTo: 'form-wizard-element', - transportEl: transportEl, - splitButtons: SplitButtons - }); - // When the window is resized, the viewport has to be resized as well - Ext.EventManager.onWindowResize(viewport.doLayout, viewport); - var relayoutFunction = function(ev) { - // bootstrap tab handling - var controlsId = ev.target.getAttribute('aria-controls'); - if(controlsId) { - var wizardEl = ev.target.parentNode.parentNode.parentNode.querySelector('#' + controlsId + ' ' + '#form-wizard-element'); - if(wizardEl) { - // we are earlier then bootstrap tab - setTimeout(function(){ - viewport.doLayout(); - }, 200); - } - } - }; - - // register tab change events - /** @var tabsLinks {NodeList} */ - var tabsLinks = document.querySelectorAll('a[data-toggle="tab"]'); - /** @see https://code.google.com/p/v8/issues/detail?id=3953 */ - for(var i = 0; i < tabsLinks.length; i++) { - var e = tabsLinks[i]; - // event not called, maybe jQuery only - //e.addEventListener('shown.bs.tab', relayoutFunction, false); - e.addEventListener('click', relayoutFunction, false); - } - }); - }; - - /** - * executed when module required, return value will be 'this' - * @return Wizard - */ - return function() { - return new Wizard(); - }(); -}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Button.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Button.js deleted file mode 100644 index bfb59a933013..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Button.js +++ /dev/null @@ -1,107 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The BUTTON element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Button - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Button = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'button', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'front\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '<input {[this.getAttributes(values.attributes)]} />', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'back\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'autofocus': '', - 'disabled': '', - 'name': '', - 'type': 'button', - 'value': TYPO3.l10n.localize('tx_form_domain_model_element_button.value') - }, - filters: {}, - label: { - value: TYPO3.l10n.localize('elements_label') - }, - layout: 'front', - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Button.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-button', TYPO3.Form.Wizard.Elements.Basic.Button); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Checkbox.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Checkbox.js deleted file mode 100644 index 5091d747a038..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Checkbox.js +++ /dev/null @@ -1,110 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The CHECKBOX element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Checkbox - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Checkbox = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'x-checkbox', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'front\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '<input {[this.getAttributes(values.attributes)]} />', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'back\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'autofocus': '', - 'checked': '', - 'disabled': '', - 'name': '', - 'readonly': '', - 'required': '', - 'type': 'checkbox', - 'value': '' - }, - filters: {}, - label: { - value: TYPO3.l10n.localize('elements_label') - }, - layout: 'back', - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Checkbox.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-checkbox', TYPO3.Form.Wizard.Elements.Basic.Checkbox); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Fieldset.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Fieldset.js deleted file mode 100644 index d8558e68f1fe..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Fieldset.js +++ /dev/null @@ -1,130 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The FIELDSET element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Fieldset - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Fieldset = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'fieldset', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div>', - '<fieldset {[this.getAttributes(values.attributes)]}>', - '<tpl for="legend">', - '<tpl if="value">', - '<legend>{value}</legend>', - '</tpl>', - '</tpl>', - '<ol></ol>', - '</fieldset>', - '</div>', - { - compiled: true, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * @cfg {Array} elementContainer - * Configuration for the containerComponent - */ - elementContainer: { - hasDragAndDrop: true - }, - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - "class": '', - dir: '', - id: '', - lang: '', - style: '' - }, - legend: { - value: TYPO3.l10n.localize('elements_legend') - } - } - }); - - TYPO3.Form.Wizard.Elements.Basic.Fieldset.superclass.constructor.apply(this, arguments); - }, - - /** - * Constructor - */ - initComponent: function() { - var config = {}; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // Initialize the container component - this.containerComponent = new TYPO3.Form.Wizard.Container(this.elementContainer); - - // call parent - TYPO3.Form.Wizard.Elements.Basic.Fieldset.superclass.initComponent.apply(this, arguments); - - // Initialize events after rendering - this.on('afterrender', this.afterRender, this); - }, - - /** - * Called by the 'afterrender' event. - * - * Add the container component to this component - */ - afterRender: function() { - this.addContainerAfterRender(); - - // Call parent - TYPO3.Form.Wizard.Elements.Basic.Form.superclass.afterRender.call(this); - }, - - /** - * Add the container component to this component - * - * Because we are using a XTemplate for rendering this component, we can - * only add the container after rendering, because the <ol> tag needs to be - * replaced with this container. - * - * The container needs to be rerendered when a configuration parameter - * (legend or attributes) of the ownerCt, for instance fieldset, has changed - * otherwise it will not show up - */ - addContainerAfterRender: function() { - this.containerComponent.applyToMarkup(this.getEl().child('ol')); - this.containerComponent.rendered = false; - this.containerComponent.render(); - this.containerComponent.doLayout(); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-fieldset', TYPO3.Form.Wizard.Elements.Basic.Fieldset); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Fileupload.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Fileupload.js deleted file mode 100644 index 9c65d00e1167..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Fileupload.js +++ /dev/null @@ -1,111 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The FILEUPLOAD element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Fileupload - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Fileupload = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'fileupload', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'front\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '<input {[this.getAttributes(values.attributes)]} />', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'back\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'accept': '', - 'autofocus': '', - 'disabled': '', - 'multiple': '', - 'name': '', - 'readonly': '', - 'required': '', - 'type': 'file', - 'value': '' - }, - filters: {}, - label: { - value: TYPO3.l10n.localize('elements_label') - }, - layout: 'front', - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Fileupload.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-fileupload', TYPO3.Form.Wizard.Elements.Basic.Fileupload); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Form.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Form.js deleted file mode 100644 index d14d96e50e77..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Form.js +++ /dev/null @@ -1,176 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The FORM element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Form - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Form = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {Mixed} autoEl - * A tag name or DomHelper spec used to create the Element which will - * encapsulate this Component. - */ - autoEl: 'li', - - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'form', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - * - * Adding novalidate attribute avoids HTML5 validation of elements. - */ - tpl: new Ext.XTemplate( - '<div id="fake-form" {[this.getAttributes(values.attributes)]} novalidate="novalidate">', - '<ol></ol>', - '</div>', - { - compiled: true, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * @cfg {Boolean} isEditable - * Defines whether the element is editable. If the item is editable, - * a button group with remove and edit buttons will be added to this element - * and when the the element is clicked, an event is triggered to edit the - * element. Some elements, like the dummy, don't need this. - */ - isEditable: false, - - /** - * @cfg {Array} elementContainer - * Configuration for the containerComponent - */ - elementContainer: { - hasDragAndDrop: true - }, - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'accept': '', - 'accept-charset': '', - 'action': '', - 'autocomplete': '', - 'enctype': 'application/x-www-form-urlencoded', - 'method': 'post', - 'novalidate': '' - }, - prefix: 'tx_form', - confirmation: true, - postProcessor: { - mail: { - recipientEmail: '', - senderEmail: '' - }, - redirect: { - destination: '' - } - } - } - }); - TYPO3.Form.Wizard.Elements.Basic.Form.superclass.constructor.apply(this, arguments); - }, - - /** - * Constructor - */ - initComponent: function() { - var config = {}; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // Initialize the container component - this.containerComponent = new TYPO3.Form.Wizard.Container(this.elementContainer); - - // Call parent - TYPO3.Form.Wizard.Elements.Basic.Form.superclass.initComponent.apply(this, arguments); - - // Initialize events after rendering - this.on('afterrender', this.afterRender, this); - }, - - /** - * Called by the 'afterrender' event. - * - * Add the container component to this component - * Stop the submit event of the form, because this form does not need to be - * submitted - */ - afterRender: function() { - this.addContainerAfterRender(); - - // Call parent - TYPO3.Form.Wizard.Elements.Basic.Form.superclass.afterRender.call(this); - }, - - /** - * Add the container component to this component - * - * Because we are using a XTemplate for rendering this component, we can - * only add the container after rendering, because the <ol> tag needs to be - * replaced with this container. - */ - addContainerAfterRender: function() { - this.containerComponent.applyToMarkup(this.getEl().child('ol')); - this.containerComponent.rendered = false; - this.containerComponent.render(); - this.containerComponent.doLayout(); - }, - - /** - * Remove a post processor from this element - * - * @param type - */ - removePostProcessor: function(type) { - if (this.configuration.postProcessor[type]) { - delete this.configuration.postProcessor[type]; - TYPO3.Form.Wizard.Helpers.History.setHistory(); - } - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-form', TYPO3.Form.Wizard.Elements.Basic.Form); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Hidden.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Hidden.js deleted file mode 100644 index abd6067af550..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Hidden.js +++ /dev/null @@ -1,88 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The HIDDEN element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Hidden - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Hidden = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'hidden-element', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<p class="hidden-dummy-element">{[this.getAttributes(values.attributes, \'name\')]}</p>', - '<input {[this.getAttributes(values.attributes)]} />', - '</div>', - { - compiled: true, - getAttributes: function(attributes, filterBy) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (typeof filterBy != 'undefined') { - if (key == filterBy) { - attributesHtml = value; - return; - } else { - return; - } - } - - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'name': '', - 'type': 'hidden', - 'value': '' - }, - filters: {}, - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Hidden.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-hidden', TYPO3.Form.Wizard.Elements.Basic.Hidden); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Password.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Password.js deleted file mode 100644 index 60d2ac8c66bd..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Password.js +++ /dev/null @@ -1,115 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The PASSWORD element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Password - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Password = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'password', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'front\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '<input {[this.getAttributes(values.attributes)]} />', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'back\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'autocomplete': '', - 'autofocus': '', - 'disabled': '', - 'maxlength': '', - 'minlength': '', - 'name': '', - 'pattern': '', - 'placeholder': '', - 'readonly': '', - 'required': '', - 'size': '', - 'type': 'password', - 'value': '' - }, - filters: {}, - label: { - value: TYPO3.l10n.localize('elements_label') - }, - layout: 'front', - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Password.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-password', TYPO3.Form.Wizard.Elements.Basic.Password); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Radio.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Radio.js deleted file mode 100644 index b5cf1d6711fc..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Radio.js +++ /dev/null @@ -1,110 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The RADIO element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Radio - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Radio = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'x-radio', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'front\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '<input {[this.getAttributes(values.attributes)]} />', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'back\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'autofocus': '', - 'checked': '', - 'disabled': '', - 'name': '', - 'readonly': '', - 'required': '', - 'type': 'radio', - 'value': '' - }, - filters: {}, - label: { - value: TYPO3.l10n.localize('elements_label') - }, - layout: 'back', - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Radio.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-radio', TYPO3.Form.Wizard.Elements.Basic.Radio); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Reset.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Reset.js deleted file mode 100644 index 9893aa7d5dcb..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Reset.js +++ /dev/null @@ -1,137 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The RESET element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Reset - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Reset = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'reset', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'front\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '<input {[this.getAttributes(values.attributes)]} />', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'back\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - */ - initComponent: function() { - - // call parent - TYPO3.Form.Wizard.Elements.Basic.Reset.superclass.initComponent.apply(this, arguments); - - // Initialize events after rendering - this.on('afterrender', this.afterRender, this); - }, - - /** - * Called by the 'afterrender' event. - * - * Stop click propagation - */ - afterRender: function() { - this.getEl().addListener('click', function(e) { - if(e.type == 'click') { - e.stopEvent(); - } - }); - - // Call parent - TYPO3.Form.Wizard.Elements.Basic.Reset.superclass.afterRender.call(this); - }, - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'autofocus': '', - 'checked': '', - 'disabled': '', - 'name': '', - 'required': '', - 'type': 'reset', - 'value': TYPO3.l10n.localize('tx_form_domain_model_element_reset.value') - }, - filters: {}, - label: { - value: '' - }, - layout: 'front', - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Reset.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-reset', TYPO3.Form.Wizard.Elements.Basic.Reset); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Select.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Select.js deleted file mode 100644 index f8e156060682..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Select.js +++ /dev/null @@ -1,130 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The SELECT element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Select - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Select = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'select', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'front\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '<select {[this.getAttributes(values.attributes)]}>', - '<tpl for="options">', - '<option {[this.getAttributes(values.attributes)]}>{text}</option>', - '</tpl>', - '</select>', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'back\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'autofocus': '', - 'disabled': '', - 'multiple': '', - 'name': '', - 'required': '', - 'size': '' - }, - filters: {}, - label: { - value: TYPO3.l10n.localize('elements_label') - }, - options: [ - { - text: TYPO3.l10n.localize('elements_option_1'), - attributes: { - value: TYPO3.l10n.localize('elements_value_1') - } - }, { - text: TYPO3.l10n.localize('elements_option_2'), - attributes: { - value: TYPO3.l10n.localize('elements_value_2') - } - }, { - text: TYPO3.l10n.localize('elements_option_3'), - attributes: { - value: TYPO3.l10n.localize('elements_value_3') - } - } - ], - layout: 'front', - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Select.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-select', TYPO3.Form.Wizard.Elements.Basic.Select); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Submit.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Submit.js deleted file mode 100644 index 7e683baba668..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Submit.js +++ /dev/null @@ -1,135 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The SUBMIT element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Submit - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Submit = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'submit', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'front\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '<input {[this.getAttributes(values.attributes)]} />', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'back\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - */ - initComponent: function() { - - // call parent - TYPO3.Form.Wizard.Elements.Basic.Submit.superclass.initComponent.apply(this, arguments); - - // Initialize events after rendering - this.on('afterrender', this.afterRender, this); - }, - - /** - * Called by the 'afterrender' event. - * - * Stop click propagation - */ - afterRender: function() { - this.getEl().addListener('click', function(e) { - if(e.type == 'click') { - e.stopEvent(); - } - }); - - // Call parent - TYPO3.Form.Wizard.Elements.Basic.Submit.superclass.afterRender.call(this); - }, - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'autofocus': '', - 'disabled': '', - 'name': '', - 'type': 'submit', - 'value': TYPO3.l10n.localize('tx_form_domain_model_element_submit.value') - }, - filters: {}, - label: { - value: '' - }, - layout: 'front', - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Submit.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-submit', TYPO3.Form.Wizard.Elements.Basic.Submit); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Textarea.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Textarea.js deleted file mode 100644 index 267391c6ae94..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Textarea.js +++ /dev/null @@ -1,118 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The TEXTAREA element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Textarea - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Textarea = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'textarea', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'front\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '<textarea {[this.getAttributes(values.attributes)]}>{values.attributes.text}</textarea>', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'back\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'autofocus': '', - 'cols': '40', - 'disabled': '', - 'inputmode': '', - 'maxlength': '', - 'minlength': '', - 'name': '', - 'placeholder': '', - 'readonly': '', - 'required': '', - 'rows': '5', - 'selectionDirection': '', - 'selectionEnd': '', - 'selectionStart': '', - 'text': '', - 'wrap': '' - }, - filters: {}, - label: { - value: TYPO3.l10n.localize('elements_label') - }, - layout: 'front', - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Textarea.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-textarea', TYPO3.Form.Wizard.Elements.Basic.Textarea); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Textline.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Textline.js deleted file mode 100644 index 523f040be5cc..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Basic/Textline.js +++ /dev/null @@ -1,117 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Basic'); - -/** - * The TEXTLINE element - * - * @class TYPO3.Form.Wizard.Elements.Basic.Textline - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Basic.Textline = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'textline', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'front\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '<input {[this.getAttributes(values.attributes)]} />', - '<tpl for="label">', - '<tpl if="value && parent.layout == \'back\'">', - '<label for="">{value}{[this.getMessage(parent.validation)]}</label>', - '</tpl>', - '</tpl>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - 'accesskey': '', - 'class': '', - 'contenteditable': '', - 'contextmenu': '', - 'dir': '', - 'draggable': '', - 'dropzone': '', - 'hidden': '', - 'id': '', - 'lang': '', - 'spellcheck': '', - 'style': '', - 'tabindex': '', - 'title': '', - 'translate': '', - - 'autocomplete': '', - 'autofocus': '', - 'disabled': '', - 'inputmode': '', - 'list': '', - 'maxlength': '', - 'minlength': '', - 'name': '', - 'pattern': '', - 'placeholder': '', - 'readonly': '', - 'required': '', - 'size': '', - 'type': 'text', - 'value': '' - }, - filters: {}, - label: { - value: TYPO3.l10n.localize('elements_label') - }, - layout: 'front', - validation: {} - } - }); - TYPO3.Form.Wizard.Elements.Basic.Textline.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-basic-textline', TYPO3.Form.Wizard.Elements.Basic.Textline); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/ButtonGroup.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/ButtonGroup.js deleted file mode 100644 index 434f558a74e2..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/ButtonGroup.js +++ /dev/null @@ -1,89 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard'); - -/** - * Button group to show on top of the form elements - * - * Most elements contain buttons to delete or edit the item. These buttons are - * grouped in this component - * - * @class TYPO3.Form.Wizard.ButtonGroup - * @extends Ext.Container - */ -TYPO3.Form.Wizard.ButtonGroup = Ext.extend(Ext.Container, { - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'buttongroup', - - /** - * @cfg {Object|Function} defaults - * This option is a means of applying default settings to all added items - * whether added through the items config or via the add or insert methods. - */ - defaults: { - xtype: 'button', - template: new Ext.Template( - '<span id="{4}"><button type="{0}" class="{3}"></button></span>' - ), - tooltipType: 'title' - }, - - /** @cfg {Boolean} forceLayout - * If true the container will force a layout initially even if hidden or - * collapsed. This option is useful for forcing forms to render in collapsed - * or hidden containers. (defaults to false). - */ - forceLayout: true, - - /** - * Constructor - */ - initComponent: function() { - var config = { - items: [ - { - iconCls: 't3-icon t3-icon-actions t3-icon-actions-edit t3-icon-edit-delete', - tooltip: TYPO3.l10n.localize('elements_button_delete'), - handler: this.removeElement, - scope: this - }, { - iconCls: 't3-icon t3-icon-actions t3-icon-actions-document t3-icon-document-open', - tooltip: TYPO3.l10n.localize('elements_button_edit'), - handler: this.setActive, - scope: this - } - ] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.ButtonGroup.superclass.initComponent.apply(this, arguments); - }, - - /** - * Called by the click event of the remove button - * - * When clicking the remove button a confirmation will be asked by the - * container this button group is in. - */ - removeElement: function(button, event) { - event.stopPropagation(); - this.ownerCt.confirmDeleteElement(); - }, - - /** - * Called by the click event of the edit button - * - * Tells the element helper that this component is set as the active one - */ - setActive: function(button, event) { - this.ownerCt.setActive(event, event.getTarget()); - } -}); - -Ext.reg('typo3-form-wizard-buttongroup', TYPO3.Form.Wizard.ButtonGroup); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Container.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Container.js deleted file mode 100644 index 1c2080e7931b..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Container.js +++ /dev/null @@ -1,569 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard'); - -/** - * Container abstract - * - * There are only two containers in a form, the form itself and fieldsets. - * - * @class TYPO3.Form.Wizard.Elements.Container - * @extends Ext.Container - */ -TYPO3.Form.Wizard.Container = Ext.extend(Ext.Container, { - /** - * @cfg {Mixed} autoEl - * A tag name or DomHelper spec used to create the Element which will - * encapsulate this Component. - */ - autoEl: 'ol', - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'formwizard-container', - - /** - * @cfg {Object|Function} defaults - * This option is a means of applying default settings to all added items - * whether added through the items config or via the add or insert methods. - */ - defaults: { - autoHeight: true - }, - - /** - * Constructor - * - * Add the dummy to the container - */ - constructor: function(config) { - Ext.apply(this, { - items: [ - { - xtype: 'typo3-form-wizard-elements-dummy' - } - ] - }); - TYPO3.Form.Wizard.Container.superclass.constructor.apply(this, arguments); - }, - - - /** - * Constructor - */ - initComponent: function() { - var config = {}; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Container.superclass.initComponent.apply(this, arguments); - - // Initialize the drag and drop zone after rendering - if (this.hasDragAndDrop) { - this.on('render', this.initializeDragAndDrop, this); - } - - this.on('render', this.checkOnEmpty, this); - - // Initialize the remove event, which will be fired when a component is removed from this container - this.on('remove', this.checkOnEmpty, this); - }, - - /** - * Initialize the drag and drop zones - * - * @param container - */ - initializeDragAndDrop: function(container) { - /** - * Initialize the drag zone - * - * A container can contain elements which can be moved within this and - * other (nested) containers. - */ - container.dragZone = new Ext.dd.DragZone(container.getEl(), { - /** - * Called when a mousedown occurs in this container. Looks in Ext.dd.Registry - * for a valid target to drag based on the mouse down. Override this method - * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned - * object has a "ddel" attribute (with an HTML Element) for other functions to work. - * @param {EventObject} element The mouse down event element - * @return {Object} The dragData - */ - getDragData: function(element) { - var sourceElement = element.getTarget('.formwizard-element'); - var sourceComponent = Ext.getCmp(sourceElement.id); - if (sourceElement && sourceComponent.isEditable) { - clonedElement = sourceElement.cloneNode(true); - clonedElement.id = Ext.id(); - return container.dragData = { - sourceEl: sourceElement, - repairXY: Ext.fly(sourceElement).getXY(), - ddel: clonedElement - }; - } - }, - - onStartDrag: function(x, y) { - Ext.getCmp('formwizard').addClass('hover-move'); - }, - - endDrag: function(event) { - Ext.getCmp('formwizard').removeClass('hover-move'); - }, - - /** - * Called before a repair of an invalid drop to get the XY to animate to. - * By default returns the XY of this.dragData.ddel - * @param {EventObject} e The mouse up event - * @return {Array} The xy location (e.g. [100, 200]) - */ - getRepairXY: function(e) { - return container.dragData.repairXY; - } - }); - - /** - * Initialize the drop zone - * - * A container can receive other form elements or other (nested) containers. - */ - container.dropZone = new Ext.dd.DropZone(container.getEl(), { - /** - * Returns a custom data object associated with the DOM node that is the target of the event. By default - * this looks up the event target in the Ext.dd.Registry, although you can override this method to - * provide your own custom lookup. - * - * The override has been done here to define if we are having this event on the container or a form element. - * - * @param {Event} e The event - * @return {Object} data The custom data - */ - getTargetFromEvent: function(event) { - - var containerElement = container.getEl(); - var formElementTarget = event.getTarget('.formwizard-element', 10, true); - var formContainerTarget = event.getTarget('.formwizard-container', 10, true); - var placeholderTarget = event.getTarget('#element-placeholder', 10, false); - - if (placeholderTarget) { - formElementTarget = Ext.DomQuery.selectNode('.target-hover'); - } - - if ( - container.hasDragAndDrop && - formContainerTarget && - formElementTarget && - formContainerTarget.findParentNode('li', 10, true) == formElementTarget && - formContainerTarget == containerElement - ) { - return null; - // We are having this event on a form element - } else if ( - container.hasDragAndDrop && - formElementTarget - ) { - if (placeholderTarget) { - return formElementTarget; - } - return event.getTarget('.formwizard-element'); - // We are having this event on a container - } else { - return null; - } - }, - - /** - * Called while the DropZone determines that a Ext.dd.DragSource is being dragged over it, - * but not over any of its registered drop nodes. The default implementation returns this.dropNotAllowed, so - * it should be overridden to provide the proper feedback if necessary. - * - * And so we did ;-) We are not using containers which can receive different elements, so we always return - * Ext.dd.DropZone.prototype.dropAllowed CSS class. - * - * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone - * @param {Event} e The event - * @param {Object} data An object containing arbitrary data supplied by the drag source - * @return {String} status The CSS class that communicates the drop status back to the source so that the - * underlying Ext.dd.StatusProxy can be updated - */ - onContainerOver: function(dd, e, data) { - if (Ext.get('element-placeholder')) { - Ext.get('element-placeholder').remove(); - } - return Ext.dd.DropZone.prototype.dropAllowed; - }, - - /** - * Called when the DropZone determines that a Ext.dd.DragSource has been dropped on it, - * but not on any of its registered drop nodes. The default implementation returns false, so it should be - * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to - * be able to accept drops. It should return true when valid so that the drag source's repair action does not run. - * - * This is a tricky part. Because we are using multiple dropzones which are on top of each other, the event will - * be called multiple times, for each group one time. We cannot prevent this by disabling event bubbling and we - * dont't want to override the core of ExtJS. To prevent multiple creation of the same object, we add the variable - * 'processed' to the 'data' object. If it has been processed on drop, it will not be done a second time. - * - * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone - * @param {Event} e The event - * @param {Object} data An object containing arbitrary data supplied by the drag source - * @return {Boolean} True if the drop was valid, else false - */ - onContainerDrop: function(dd, e, data) { - if ( - container.hasDragAndDrop && - !data.processed - ) { - var dropComponent = Ext.getCmp(data.sourceEl.id); - container.dropElement(dropComponent, 'container'); - data.processed = true; - } - return true; - }, - - /** - * Called when the DropZone determines that a Ext.dd.DragSource has entered a drop node - * that has either been registered or detected by a configured implementation of getTargetFromEvent. - * This method has no default implementation and should be overridden to provide - * node-specific processing if necessary. - * - * Our implementation adds a dummy placeholder before or after the element the user is hovering over. - * This placeholder will show the user where the dragged element will be dropped in the form. - * - * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from - * getTargetFromEvent for this node) - * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone - * @param {Event} e The event - * @param {Object} data An object containing arbitrary data supplied by the drag source - */ - onNodeEnter : function(target, dd, e, data) { - if ( - Ext.get(data.sourceEl).hasClass('formwizard-element') && - target.id != data.sourceEl.id - ) { - var dropPosition = this.getDropPosition(target, dd); - if (dropPosition == 'above') { - Ext.DomHelper.insertBefore(target, { - tag: 'li', - id: 'element-placeholder', - html: ' ' - }); - } else { - Ext.DomHelper.insertAfter(target, { - tag: 'li', - id: 'element-placeholder', - html: ' ' - }); - } - Ext.fly(target).addClass('target-hover'); - } - }, - - /** - * Called when the DropZone determines that a Ext.dd.DragSource has been dragged out of - * the drop node without dropping. This method has no default implementation and should be overridden to provide - * node-specific processing if necessary. - * - * Removes the temporary placeholder and the hover class from the element - * - * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from - * getTargetFromEvent for this node) - * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone - * @param {Event} e The event - * @param {Object} data An object containing arbitrary data supplied by the drag source - */ - onNodeOut : function(target, dd, e, data) { - if ( - Ext.get(data.sourceEl).hasClass('formwizard-element') && - target.id != data.sourceEl.id - ) { - if (e.type != 'mouseup') { - if (Ext.get('element-placeholder')) { - Ext.get('element-placeholder').remove(); - } - Ext.fly(target).removeClass('target-hover'); - } - } - }, - - /** - * Called while the DropZone determines that a Ext.dd.DragSource is over a drop node - * that has either been registered or detected by a configured implementation of getTargetFromEvent. - * The default implementation returns this.dropNotAllowed, so it should be - * overridden to provide the proper feedback. - * - * Based on the cursor position on the node we are hovering over, the temporary placeholder will be put - * above or below this node. If the position changes, the placeholder will be removed and put at the - * right spot. - * - * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from - * getTargetFromEvent for this node) - * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone - * @param {Event} e The event - * @param {Object} data An object containing arbitrary data supplied by the drag source - * @return {String} status The CSS class that communicates the drop status back to the source so that the - * underlying Ext.dd.StatusProxy can be updated - */ - onNodeOver: function(target, dd, e, data) { - if ( - Ext.get(data.sourceEl).hasClass('formwizard-element') && - target.id != data.sourceEl.id - ) { - var dropPosition = this.getDropPosition(target, dd); - // The position of the target moved to the top - if ( - dropPosition == 'above' && - target.nextElementSibling && - target.nextElementSibling.id == 'element-placeholder' - ) { - Ext.get('element-placeholder').remove(); - Ext.DomHelper.insertBefore(target, { - tag: 'li', - id: 'element-placeholder', - html: ' ' - }); - } else if ( - dropPosition == 'below' && - target.previousElementSibling && - target.previousElementSibling.id == 'element-placeholder' - ) { - Ext.get('element-placeholder').remove(); - Ext.DomHelper.insertAfter(target, { - tag: 'li', - id: 'element-placeholder', - html: ' ' - }); - } - return Ext.dd.DropZone.prototype.dropAllowed; - } else { - return Ext.dd.DropZone.prototype.dropNotAllowed; - } - }, - - /** - * Called when the DropZone determines that a Ext.dd.DragSource has been dropped onto - * the drop node. The default implementation returns false, so it should be overridden to provide the - * appropriate processing of the drop event and return true so that the drag source's repair action does not run. - * - * Like onContainerDrop this is a tricky part. Because we are using multiple dropzones which are on top of each other, the event will - * be called multiple times, for each group one time. We cannot prevent this by disabling event bubbling and we - * dont't want to override the core of ExtJS. To prevent multiple creation of the same object, we add the variable - * 'processed' to the 'data' object. If it has been processed on drop, it will not be done a second time. - * - * - * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from - * getTargetFromEvent for this node) - * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone - * @param {Event} e The event - * @param {Object} data An object containing arbitrary data supplied by the drag source - * @return {Boolean} True if the drop was valid, else false - */ - onNodeDrop : function(target, dd, e, data) { - if ( - Ext.get(data.sourceEl).hasClass('formwizard-element') && - target.id != data.sourceEl.id && - !data.processed - ) { - - var dropPosition = this.getDropPosition(target, dd); - var dropComponent = Ext.getCmp(data.sourceEl.id); - container.dropElement(dropComponent, dropPosition, target); - data.processed = true; - return true; - } - }, - /** - * Defines whether we are hovering at the top or bottom half of a node - * - * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from - * getTargetFromEvent for this node) - * @param {Ext.dd.DragSource} source The drag source that was dragged over this drop zone - * @return {String} above when hovering over the top half, below if at the bottom half. - */ - getDropPosition: function(target, dd) { - var top = Ext.lib.Dom.getY(target); - var bottom = top + target.offsetHeight; - var center = ((bottom - top) / 2) + top; - var yPosition = dd.lastPageY + dd.deltaY; - if (yPosition < center) { - return 'above'; - } else if (yPosition >= center) { - return 'below'; - } - } - }); - }, - - /** - * Called by the dropzones onContainerDrop or onNodeDrop. - * Adds the component to the container. - * - * This function will look if it is a new element from the left buttons, if - * it is an existing element which is moved within this or from another - * container. It also decides if it is dropped within an empty container or - * if it needs a position within the existing elements of this container. - * - * @param component - * @param position - * @param target - */ - dropElement: function(component, position, target) { - // Check if there are errors in the current active element - var optionsTabIsValid = Ext.getCmp('formwizard-left-options').tabIsValid(); - - var id = component.id; - var droppedElement = {}; - - if (Ext.get('element-placeholder')) { - Ext.get('element-placeholder').remove(); - } - // Only add or move an element when there is no error in the current active element - if (optionsTabIsValid) { - // New element in container - if (position == 'container') { - // Check if the dummy is present, which means there are no elements - var dummy = this.findById('dummy'); - if (dummy) { - this.remove(dummy, true); - } - // Add the new element to the container - if (component.xtype != 'button') { - droppedElement = this.add( - component - ); - } else { - droppedElement = this.add({ - xtype: 'typo3-form-wizard-elements-' + id - }); - } - - // Moved an element within this container - } else if (this.findById(id)) { - droppedElement = this.findById(id); - var movedElementIndex = 0; - var targetIndex = this.items.findIndex('id', target.id); - - if (position == 'above') { - movedElementIndex = targetIndex; - } else { - movedElementIndex = targetIndex + 1; - } - - // Tricky part, because this.remove does not remove the DOM element - // See http://www.sencha.com/forum/showthread.php?102190 - // 1. remove component from container w/o destroying (2nd argument false) - // 2. remove component's element from container and append it to body - // 3. add/insert the component to the correct place back in the container - // 4. call doLayout() on the container - this.remove(droppedElement, false); - var element = Ext.get(droppedElement.id); - element.appendTo(Ext.getBody()); - - this.insert( - movedElementIndex, - droppedElement - ); - - // New element for this container coming from another one - } else { - var index = 0; - var targetIndex = this.items.findIndex('id', target.id); - - if (position == 'above') { - index = targetIndex; - } else { - index = targetIndex + 1; - } - - // Element moved - if (component.xtype != 'button') { - droppedElement = this.insert( - index, - component - ); - // Coming from buttons - } else { - droppedElement = this.insert( - index, - { - xtype: 'typo3-form-wizard-elements-' + id - } - ); - } - } - this.doLayout(); - TYPO3.Form.Wizard.Helpers.History.setHistory(); - TYPO3.Form.Wizard.Helpers.Element.setActive(droppedElement); - - // The current active element has errors, show it! - } else { - Ext.MessageBox.show({ - title: TYPO3.l10n.localize('options_error'), - msg: TYPO3.l10n.localize('options_error_message'), - icon: Ext.MessageBox.ERROR, - buttons: Ext.MessageBox.OK - }); - } - }, - - /** - * Remove the element from this container - * - * @param element - */ - removeElement: function(element) { - this.remove(element); - TYPO3.Form.Wizard.Helpers.History.setHistory(); - }, - - /** - * Called by the 'remove' event of this container. - * - * If an item has been removed from this container, except for the dummy - * element, it will look if there are other items existing. If not, it will - * put the dummy in this container to tell the user the container needs items. - * - * @param container - * @param component - */ - checkOnEmpty: function(container, component) { - if (component && component.id != 'dummy' || !component) { - if (this.items.getCount() == 0) { - this.add({ - xtype: 'typo3-form-wizard-elements-dummy' - }); - this.doLayout(); - } - } - }, - - /** - * Called by the parent of this component when a change has been made in the - * form. - * - * Constructs an array out of this component and the children to add it to - * the history or to use when saving the form - * - * @returns {Array} - */ - getConfiguration: function() { - var historyConfiguration = { - hasDragAndDrop: this.hasDragAndDrop - }; - - if (this.items) { - historyConfiguration.items = []; - this.items.each(function(item, index, length) { - historyConfiguration.items.push(item.getConfiguration()); - }, this); - } - return historyConfiguration; - } -}); - -Ext.reg('typo3-form-wizard-container', TYPO3.Form.Wizard.Container); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Content/Header.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Content/Header.js deleted file mode 100644 index 3713f8506983..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Content/Header.js +++ /dev/null @@ -1,71 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Content'); - -/** - * The content HEADER element - * - * @class TYPO3.Form.Wizard.Elements.Content.Header - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Content.Header = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'header', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="various">', - '<{headingSize} {[this.getAttributes(parent.attributes)]}>', - '{content}', - '</{type}>', - '</tpl>', - '</div>', - { - compiled: true, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - "class": 'content-header', - dir: '', - id: '', - lang: '', - style: '', - title: '' - }, - various: { - headingSize: 'h1', - content: TYPO3.l10n.localize('elements_header_content') - } - } - }); - TYPO3.Form.Wizard.Elements.Content.Header.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-content-header', TYPO3.Form.Wizard.Elements.Content.Header); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Content/Textblock.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Content/Textblock.js deleted file mode 100644 index 01f5ee01763c..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Content/Textblock.js +++ /dev/null @@ -1,70 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Content'); - -/** - * The content HEADER element - * - * @class TYPO3.Form.Wizard.Elements.Content.Header - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Content.Textblock = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} elementClass - * An extra CSS class that will be added to this component's Element - */ - elementClass: 'textblock', - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<tpl for="various">', - '<div {[this.getAttributes(parent.attributes)]}>', - '{text:nl2br}', - '</{type}>', - '</tpl>', - '</div>', - { - compiled: true, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - attributes: { - "class": '', - dir: '', - id: '', - lang: '', - style: '', - title: '' - }, - various: { - text: TYPO3.l10n.localize('elements_textblock_content') - } - } - }); - TYPO3.Form.Wizard.Elements.Content.Textblock.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-content-textblock', TYPO3.Form.Wizard.Elements.Content.Textblock); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Dummy.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Dummy.js deleted file mode 100644 index b7ec2e97868a..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Dummy.js +++ /dev/null @@ -1,69 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements'); - -/** - * The dummy element - * - * This type will be shown when there is no element in a container which will be - * form or fieldset and will be removed when there is an element added. - * - * @class TYPO3.Form.Wizard.Elements.Dummy - * @extends TYPO3.Form.Wizard.Elements - */ -TYPO3.Form.Wizard.Elements.Dummy = Ext.extend(TYPO3.Form.Wizard.Elements, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'dummy', - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'dummy typo3-message message-information', - - /** - * @cfg {Object} configuration - * The configuration of this element. - * This object contains the configuration of this component. It will be - * copied to the 'data' variable before rendering. 'data' is deleted after - * rendering the xtemplate, so we need a copy. - */ - configuration: { - title: TYPO3.l10n.localize('elements_dummy_title'), - description: TYPO3.l10n.localize('elements_dummy_description') - }, - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<p><strong>{title}</strong></p>', - '<p>{description}</p>' - ), - - /** - * @cfg {Boolean} isEditable - * Defines whether the element is editable. If the item is editable, - * a button group with remove and edit buttons will be added to this element - * and when the the element is clicked, an event is triggered to edit the - * element. Some elements, like the dummy, don't need this. - */ - isEditable: false -}); - -Ext.reg('typo3-form-wizard-elements-dummy', TYPO3.Form.Wizard.Elements.Dummy); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Elements.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Elements.js deleted file mode 100644 index d20e37372146..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Elements.js +++ /dev/null @@ -1,301 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements'); - -/** - * Elements abstract - * - * @class TYPO3.Form.Wizard.Elements - * @extends Ext.Container - */ -TYPO3.Form.Wizard.Elements = Ext.extend(Ext.Container, { - /** - * @cfg {Mixed} autoEl - * A tag name or DomHelper spec used to create the Element which will - * encapsulate this Component. - */ - autoEl: 'li', - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'formwizard-element', - - /** - * @cfg {Object} buttonGroup - * Reference to the button group - */ - buttonGroup: null, - - /** - * @cfg {Boolean} isEditable - * Defines whether the element is editable. If the item is editable, - * a button group with remove and edit buttons will be added to this element - * and when the the element is clicked, an event is triggered to edit the - * element. Some elements, like the dummy, don't need this. - */ - isEditable: true, - - /** - * @cfg {Object} configuration - * The configuration of this element. - * This object contains the configuration of this component. It will be - * copied to the 'data' variable before rendering. 'data' is deleted after - * rendering the xtemplate, so we need a copy. - */ - configuration: {}, - - /** - * Constructor - */ - initComponent: function() { - this.addEvents({ - 'configurationChange': true - }); - - var config = {}; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Elements.superclass.initComponent.apply(this, arguments); - - // Add the elementClass to the component - this.addClass(this.elementClass); - - // Add the listener setactive for the element helper - TYPO3.Form.Wizard.Helpers.Element.on('setactive', this.toggleActive, this); - - // Set the data before rendering - this.on('beforerender', this.beforeRender, this); - - // Initialize events after rendering - this.on('afterrender', this.makeEditable, this); - - // Remove event listeners after the detruction of this component - this.on('destroy', this.onDestroy, this); - }, - - /** - * Copy this.configuration to this.data before rendering - * - * When using tpl together with data, the data variable will be deleted - * after rendering the component. We do not want to lose this data, so we - * store it in a different variable 'configuration' which will be copied to - * data just before rendering - * - * All strings within the configuration object are HTML encoded first before - * displaying - * - * @param component This component - */ - beforeRender: function(component) { - this.data = this.encodeConfiguration(this.configuration); - }, - - /** - * Html encode all strings in the configuration of an element - * - * @param unencodedData The configuration object - * @returns {Object} - */ - encodeConfiguration: function(unencodedData) { - var encodedData = {}; - - Ext.iterate(unencodedData, function (key, value, object) { - if (Ext.isString(value)) { - encodedData[key] = Ext.util.Format.htmlEncode(value); - } else if (Ext.isObject(value)) { - encodedData[key] = this.encodeConfiguration(value); - } else { - encodedData[key] = value; - } - }, this); - - return encodedData; - }, - - /** - * Add the buttongroup and a click event listener to this component when the - * component is editable. - */ - makeEditable: function() { - if (this.isEditable) { - if (!this.buttonGroup) { - this.add({ - xtype: 'typo3-form-wizard-buttongroup', - ref: 'buttonGroup' - }); - } - this.el.un('click', this.setActive, this); - this.el.on('click', this.setActive, this); - // Add hover class. Normally this would be done with overCls, - // but this does not take bubbling (propagation) into account - this.el.hover( - function(){ - Ext.fly(this).addClass('hover'); - }, - function(){ - Ext.fly(this).removeClass('hover'); - }, - this.el, - { - stopPropagation: true - } - ); - } - }, - - /** - * Called on a click event of this component or when the element is added - * - * Tells the element helper that this component is set as the active one and - * swallows the click event to prevent bubbling - * - * @param event - * @param target - * @param object - */ - setActive: function(event, target, object) { - TYPO3.Form.Wizard.Helpers.Element.setActive(this); - event.stopPropagation(); - }, - - /** - * Called when the element helper is firing the setactive event - * - * Adds an extra class 'active' to the element when the current component is - * the active one, otherwise removes the class 'active' when this component - * has this class - * @param component - */ - toggleActive: function(component) { - if (this.isEditable) { - var element = this.getEl(); - - if (component && component.getId() == this.getId()) { - if (!element.hasClass('active')) { - element.addClass('active'); - } - } else if (element.hasClass('active')) { - element.removeClass('active'); - } - } - }, - - /** - * Display a confirmation box when the delete button has been pressed. - * - * @param event - * @param target - * @param object - */ - confirmDeleteElement: function(event, target, object) { - Ext.MessageBox.confirm( - TYPO3.l10n.localize('elements_confirm_delete_title'), - TYPO3.l10n.localize('elements_confirm_delete_description'), - this.deleteElement, - this - ); - }, - - /** - * Delete the component when the yes button of the confirmation box has been - * pressed. - * - * @param button The button which has been pressed (yes / no) - */ - deleteElement: function(button) { - if (button == 'yes') { - this.ownerCt.removeElement(this); - } - }, - - /** - * Called by the parent of this component when a change has been made in the - * form. - * - * Constructs an array out of this component and the children to add it to - * the history or to use when saving the form - * - * @returns {Array} - */ - getConfiguration: function() { - var historyConfiguration = { - configuration: this.configuration, - isEditable: this.isEditable, - xtype: this.xtype - }; - - if (this.containerComponent) { - historyConfiguration.elementContainer = this.containerComponent.getConfiguration(); - } - return historyConfiguration; - }, - - /** - * Called when a configuration property has changed in the options tab - * - * Overwrites the configuration with the configuration from the form, - * adds a new snapshot to the history and renders this component again. - * @param formConfiguration - */ - setConfigurationValue: function(formConfiguration) { - Ext.merge(this.configuration, formConfiguration); - TYPO3.Form.Wizard.Helpers.History.setHistory(); - this.rendered = false; - this.render(); - this.doLayout(); - this.fireEvent('configurationChange', this); - }, - - /** - * Remove a validation rule from this element - * - * @param type - */ - removeValidationRule: function(type) { - if (this.configuration.validation[type]) { - delete this.configuration.validation[type]; - TYPO3.Form.Wizard.Helpers.History.setHistory(); - if (this.xtype != 'typo3-form-wizard-elements-basic-form') { - this.rendered = false; - this.render(); - this.doLayout(); - } - } - }, - - /** - * Remove a filter from this element - * - * @param type - */ - removeFilter: function(type) { - if (this.configuration.filters[type]) { - delete this.configuration.filters[type]; - TYPO3.Form.Wizard.Helpers.History.setHistory(); - if (this.xtype != 'typo3-form-wizard-elements-basic-form') { - this.rendered = false; - this.render(); - this.doLayout(); - } - } - }, - - /** - * Fires after the component is destroyed. - * - * Removes the listener for the 'setactive' event of the element helper. - * Tells the element helper this element is destroyed and if set active, - * it should be unset as active. - */ - onDestroy: function() { - TYPO3.Form.Wizard.Helpers.Element.un('setactive', this.toggleActive, this); - TYPO3.Form.Wizard.Helpers.Element.unsetActive(this); - } -}); - -Ext.reg('typo3-form-wizard-elements',TYPO3.Form.Wizard.Elements); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/CheckboxGroup.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/CheckboxGroup.js deleted file mode 100644 index eb7bb3d25105..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/CheckboxGroup.js +++ /dev/null @@ -1,154 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Predefined'); - -/** - * The predefined CHECKBOX GROUP element - * - * @class TYPO3.Form.Wizard.Elements.Predefined.CheckboxGroup - * @extends TYPO3.Form.Wizard.Elements.Basic.Fieldset - */ -TYPO3.Form.Wizard.Elements.Predefined.CheckboxGroup = Ext.extend(TYPO3.Form.Wizard.Elements.Basic.Fieldset, { - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<fieldset {[this.getAttributes(values.attributes)]}>', - '<tpl for="legend">', - '<tpl if="value">', - '<legend>{value}{[this.getMessage(parent.validation)]}</legend>', - '</tpl>', - '</tpl>', - '<ol></ol>', - '</fieldset>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Initialize the component - */ - initComponent: function() { - var config = { - elementContainer: { - hasDragAndDrop: false - }, - configuration: { - attributes: { - "class": 'fieldset-subgroup', - dir: '', - id: '', - lang: '', - style: '' - }, - legend: { - value: TYPO3.l10n.localize('elements_legend') - }, - options: [ - { - text: TYPO3.l10n.localize('elements_option_1'), - attributes: { - value: TYPO3.l10n.localize('elements_value_1') - } - },{ - text: TYPO3.l10n.localize('elements_option_2'), - attributes: { - value: TYPO3.l10n.localize('elements_value_2') - } - },{ - text: TYPO3.l10n.localize('elements_option_3'), - attributes: { - value: TYPO3.l10n.localize('elements_value_3') - } - } - ], - various: { - name: '' - }, - validation: {} - } - }; - - // apply config - Ext.apply(this, Ext.apply(config, this.initialConfig)); - - // call parent - TYPO3.Form.Wizard.Elements.Predefined.CheckboxGroup.superclass.initComponent.apply(this, arguments); - - this.on('configurationChange', this.rebuild, this); - - this.on('afterrender', this.rebuild, this); - }, - - /** - * Add the radio buttons to the containerComponent of this fieldset, - * according to the configuration options. - * - * @param component - */ - rebuild: function(component) { - this.containerComponent.removeAll(); - if (this.configuration.options.length > 0) { - var dummy = this.containerComponent.findById('dummy'); - if (dummy) { - this.containerComponent.remove(dummy, true); - } - Ext.each(this.configuration.options, function(option, index, length) { - var checkbox = this.containerComponent.add({ - xtype: 'typo3-form-wizard-elements-basic-checkbox', - isEditable: false, - cls: '' - }); - var optionValue = ''; - if (option.attributes && option.attributes.value) { - optionValue = option.attributes.value; - } - var checkboxConfiguration = { - label: { - value: option.text - }, - attributes: { - value: optionValue - } - }; - if ( - option.attributes && - option.attributes.selected && - option.attributes.selected == 'selected' - ) { - checkboxConfiguration.attributes.checked = 'checked'; - } - Ext.merge(checkbox.configuration, checkboxConfiguration); - }, this); - this.containerComponent.doLayout(); - } - } -}); - -Ext.reg('typo3-form-wizard-elements-predefined-checkboxgroup', TYPO3.Form.Wizard.Elements.Predefined.CheckboxGroup); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/Email.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/Email.js deleted file mode 100644 index 84eb97adfc56..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/Email.js +++ /dev/null @@ -1,46 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Predefined'); - -/** - * The predefined EMAIL element - * - * @class TYPO3.Form.Wizard.Elements.Predefined.Email - * @extends TYPO3.Form.Wizard.Elements.Basic.Textline - */ -TYPO3.Form.Wizard.Elements.Predefined.Email = Ext.extend(TYPO3.Form.Wizard.Elements.Basic.Textline, { - /** - * Initialize the component - */ - initComponent: function() { - var config = { - configuration: { - attributes: { - name: 'email', - type: 'email' - }, - label: { - value: TYPO3.l10n.localize('elements_label_email') - }, - validation: { - required: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_required.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_required.error') - }, - email: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_email.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_email.error') - } - } - } - }; - - // MERGE config - Ext.merge(this, config); - - // call parent - TYPO3.Form.Wizard.Elements.Predefined.Email.superclass.initComponent.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-elements-predefined-email', TYPO3.Form.Wizard.Elements.Predefined.Email); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/Name.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/Name.js deleted file mode 100644 index 8cc1437d3b3d..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/Name.js +++ /dev/null @@ -1,156 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Predefined'); - -/** - * The predefined NAME element - * - * @class TYPO3.Form.Wizard.Elements.Predefined.Name - * @extends TYPO3.Form.Wizard.Elements.Basic.Fieldset - */ -TYPO3.Form.Wizard.Elements.Predefined.Name = Ext.extend(TYPO3.Form.Wizard.Elements.Basic.Fieldset, { - /** - * Initialize the component - */ - initComponent: function() { - var config = { - configuration: { - attributes: { - "class": 'predefined-name fieldset-subgroup fieldset-horizontal label-below', - dir: '', - id: '', - lang: '', - style: '' - }, - legend: { - value: TYPO3.l10n.localize('elements_legend_name') - }, - various: { - prefix: true, - suffix: true, - middleName: true - } - } - }; - - // apply config - Ext.apply(this, Ext.apply(config, this.initialConfig)); - - // call parent - TYPO3.Form.Wizard.Elements.Predefined.Name.superclass.initComponent.apply(this, arguments); - - this.on('configurationChange', this.rebuild, this); - - this.on('afterrender', this.rebuild, this); - }, - - /** - * Add the fields to the containerComponent of this fieldset, - * according to the configuration options. - * - * @param component - */ - rebuild: function(component) { - this.containerComponent.removeAll(); - var dummy = this.containerComponent.findById('dummy'); - if (dummy) { - this.containerComponent.remove(dummy, true); - } - if (this.configuration.various.prefix) { - var prefix = this.containerComponent.add({ - xtype: 'typo3-form-wizard-elements-basic-textline', - isEditable: false, - cls: '', - configuration: { - label: { - value: TYPO3.l10n.localize('elements_label_prefix') - }, - attributes: { - name: 'prefix', - size: 4 - }, - layout: 'back' - } - }); - } - var firstName = this.containerComponent.add({ - xtype: 'typo3-form-wizard-elements-basic-textline', - isEditable: false, - cls: '', - configuration: { - label: { - value: TYPO3.l10n.localize('elements_label_firstname') - }, - attributes: { - name: 'firstName', - size: 10 - }, - layout: 'back', - validation: { - required: { - showMessage: true, - message: '*', - error: 'Required' - } - } - } - }); - if (this.configuration.various.middleName) { - var middleName = this.containerComponent.add({ - xtype: 'typo3-form-wizard-elements-basic-textline', - isEditable: false, - cls: '', - configuration: { - label: { - value: TYPO3.l10n.localize('elements_label_middlename') - }, - attributes: { - name: 'middleName', - size: 6 - }, - layout: 'back' - } - }); - } - var lastName = this.containerComponent.add({ - xtype: 'typo3-form-wizard-elements-basic-textline', - isEditable: false, - cls: '', - configuration: { - label: { - value: TYPO3.l10n.localize('elements_label_lastname') - }, - attributes: { - name: 'lastName', - size: 15 - }, - layout: 'back', - validation: { - required: { - showMessage: true, - message: '*', - error: 'Required' - } - } - } - }); - if (this.configuration.various.suffix) { - var suffix = this.containerComponent.add({ - xtype: 'typo3-form-wizard-elements-basic-textline', - isEditable: false, - cls: '', - configuration: { - label: { - value: TYPO3.l10n.localize('elements_label_suffix') - }, - attributes: { - name: 'suffix', - size: 4 - }, - layout: 'back' - } - }); - } - this.containerComponent.doLayout(); - } -}); - -Ext.reg('typo3-form-wizard-elements-predefined-name', TYPO3.Form.Wizard.Elements.Predefined.Name); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/RadioGroup.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/RadioGroup.js deleted file mode 100644 index f93d1470fdc8..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Elements/Predefined/RadioGroup.js +++ /dev/null @@ -1,154 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Elements.Predefined'); - -/** - * The predefined RADIO GROUP element - * - * @class TYPO3.Form.Wizard.Elements.Predefined.RadioGroup - * @extends TYPO3.Form.Wizard.Elements.Basic.Fieldset - */ -TYPO3.Form.Wizard.Elements.Predefined.RadioGroup = Ext.extend(TYPO3.Form.Wizard.Elements.Basic.Fieldset, { - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<div class="overflow-hidden">', - '<fieldset {[this.getAttributes(values.attributes)]}>', - '<tpl for="legend">', - '<tpl if="value">', - '<legend>{value}{[this.getMessage(parent.validation)]}</legend>', - '</tpl>', - '</tpl>', - '<ol></ol>', - '</fieldset>', - '</div>', - { - compiled: true, - getMessage: function(rules) { - var messageHtml = ''; - var messages = []; - Ext.iterate(rules, function(rule, configuration) { - if (configuration.showMessage) { - messages.push(configuration.message); - } - }, this); - - messageHtml = ' <em>' + messages.join(', ') + '</em>'; - return messageHtml; - - }, - getAttributes: function(attributes) { - var attributesHtml = ''; - Ext.iterate(attributes, function(key, value) { - if (value) { - attributesHtml += key + '="' + value + '" '; - } - }, this); - return attributesHtml; - } - } - ), - - /** - * Initialize the component - */ - initComponent: function() { - var config = { - elementContainer: { - hasDragAndDrop: false - }, - configuration: { - attributes: { - "class": 'fieldset-subgroup', - dir: '', - id: '', - lang: '', - style: '' - }, - legend: { - value: TYPO3.l10n.localize('elements_legend') - }, - options: [ - { - text: TYPO3.l10n.localize('elements_option_1'), - attributes: { - value: TYPO3.l10n.localize('elements_value_1') - } - },{ - text: TYPO3.l10n.localize('elements_option_2'), - attributes: { - value: TYPO3.l10n.localize('elements_value_2') - } - },{ - text: TYPO3.l10n.localize('elements_option_3'), - attributes: { - value: TYPO3.l10n.localize('elements_value_3') - } - } - ], - various: { - name: '' - }, - validation: {} - } - }; - - // apply config - Ext.apply(this, Ext.apply(config, this.initialConfig)); - - // call parent - TYPO3.Form.Wizard.Elements.Predefined.RadioGroup.superclass.initComponent.apply(this, arguments); - - this.on('configurationChange', this.rebuild, this); - - this.on('afterrender', this.rebuild, this); - }, - - /** - * Add the radio buttons to the containerComponent of this fieldset, - * according to the configuration options. - * - * @param component - */ - rebuild: function(component) { - this.containerComponent.removeAll(); - if (this.configuration.options.length > 0) { - var dummy = this.containerComponent.findById('dummy'); - if (dummy) { - this.containerComponent.remove(dummy, true); - } - Ext.each(this.configuration.options, function(option, index, length) { - var radio = this.containerComponent.add({ - xtype: 'typo3-form-wizard-elements-basic-radio', - isEditable: false, - cls: '' - }); - var optionValue = ''; - if (option.attributes && option.attributes.value) { - optionValue = option.attributes.value; - } - var radioConfiguration = { - label: { - value: option.text - }, - attributes: { - value: optionValue - } - }; - if ( - option.attributes && - option.attributes.selected && - option.attributes.selected == 'selected' - ) { - radioConfiguration.attributes.checked = 'checked'; - } - Ext.merge(radio.configuration, radioConfiguration); - }, this); - this.containerComponent.doLayout(); - } - } -}); - -Ext.reg('typo3-form-wizard-elements-predefined-radiogroup', TYPO3.Form.Wizard.Elements.Predefined.RadioGroup); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Helpers/Element.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Helpers/Element.js deleted file mode 100644 index 2b38ef26f963..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Helpers/Element.js +++ /dev/null @@ -1,71 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Helpers'); - -TYPO3.Form.Wizard.Helpers.Element = Ext.extend(Ext.util.Observable, { - /** - * @cfg {Object} active - * The current active form element - */ - active: null, - - /** - * Constructor - * - * @param config - */ - constructor: function(config){ - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'setactive': true - }); - - // Call our superclass constructor to complete construction process. - TYPO3.Form.Wizard.Helpers.Element.superclass.constructor.call(this, config); - }, - - /** - * Fires the setactive event when a component is set as active - * - * @param component - */ - setActive: function(component) { - var optionsTabIsValid = Ext.getCmp('formwizard-left-options').tabIsValid(); - - if (optionsTabIsValid) { - if (component == this.active) { - this.active = null; - } else { - this.active = component; - } - this.fireEvent('setactive', this.active); - } else { - Ext.MessageBox.show({ - title: TYPO3.l10n.localize('options_error'), - msg: TYPO3.l10n.localize('options_error_message'), - icon: Ext.MessageBox.ERROR, - buttons: Ext.MessageBox.OK - }); - } - }, - - /** - * Fires the setactive event when a component is unset. - * - * This means when the element is destroyed or when the form is reloaded - * using undo or redo - * - * @param component - */ - unsetActive: function(component) { - if ( - this.active && ( - (component && component.getId() == this.active.getId()) || - !component - ) - ){ - this.active = null; - this.fireEvent('setactive'); - } - } -}); - -TYPO3.Form.Wizard.Helpers.Element = new TYPO3.Form.Wizard.Helpers.Element(); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Helpers/History.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Helpers/History.js deleted file mode 100644 index a92be63cc7e8..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Helpers/History.js +++ /dev/null @@ -1,139 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Helpers'); - -TYPO3.Form.Wizard.Helpers.History = Ext.extend(Ext.util.Observable, { - /** - * @cfg {Integer} maximum - * Maximum steps to go back or forward in history - */ - maximum: 20, - - /** - * @cfg {Integer} marker - * The current step in the history - */ - marker: 0, - - /** - * @cfg {Array} history - * Holds the configuration for each step in history - */ - history: [], - - /** - * #cfg {String} undoButtonId - * The id of the undo button - */ - undoButtonId: 'formwizard-history-undo', - - /** - * #cfg {String} redoButtonId - * The id of the redo button - */ - redoButtonId: 'formwizard-history-redo', - - /** - * Constructor - * - * @param config - */ - constructor: function(config){ - // Call our superclass constructor to complete construction process. - TYPO3.Form.Wizard.Helpers.History.superclass.constructor.call(this, config); - }, - - /** - * Called when a component is added to a container or there was a change in - * one of the form components - * - * Gets the configuration of all (nested) components, starting at - * viewport-right, and adds this configuration to the history - * - * @returns {void} - */ - setHistory: function() { - var configuration = Ext.getCmp('formwizard-right').getConfiguration(); - this.addToHistory(configuration); - }, - - /** - * Add a snapshot to the history - * - * @param {Object} configuration The form configuration snapshot - * @return {void} - */ - addToHistory: function(configuration) { - while (this.history.length > this.marker) { - this.history.pop(); - } - this.history.push(Ext.encode(configuration)); - while (this.history.length > this.maximum) { - this.history.shift(); - } - this.marker = this.history.length; - }, - - /** - * Get the current snapshot from the history - * - * @return {Object} The current snapshot - */ - refresh: function() { - var refreshObject = Ext.decode(this.history[this.marker-1]); - Ext.getCmp('formwizard-right').loadConfiguration(refreshObject); - }, - - /** - * Get the previous snapshot from the history if available - * - * Unsets the active element, because this element will not be available anymore - * - * @return {Object} The previous snapshot - */ - undo: function() { - if (this.marker >= 1) { - this.marker--; - var undoObject = Ext.decode(this.history[this.marker-1]); - Ext.getCmp('formwizard-right').loadConfiguration(undoObject); - TYPO3.Form.Wizard.Helpers.Element.unsetActive(); - } - }, - - /** - * Get the next snapshot from the history if available - * - * Unsets the active element, because this element will not be available anymore - * - * @return {Object} The next snapshot - */ - redo: function() { - if (this.history.length > this.marker) { - this.marker++; - var redoObject = Ext.decode(this.history[this.marker-1]); - Ext.getCmp('formwizard-right').loadConfiguration(redoObject); - TYPO3.Form.Wizard.Helpers.Element.unsetActive(); - } - }, - - /** - * Turn the undo/redo buttons on or off - * according to marker in the history - * - * @return {void} - */ - buttons: function() { - var undoButton = Ext.get(this.undoButtonId); - var redoButton = Ext.get(this.redoButtonId); - if (this.marker > 1) { - undoButton.show(); - } else { - undoButton.hide(); - } - if (this.history.length > this.marker) { - redoButton.show(); - } else { - redoButton.hide(); - } - } -}); - -TYPO3.Form.Wizard.Helpers.History = new TYPO3.Form.Wizard.Helpers.History(); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.FakeFormPanel.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.FakeFormPanel.js deleted file mode 100644 index dbbc63aed358..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.FakeFormPanel.js +++ /dev/null @@ -1,35 +0,0 @@ -Ext.ns('Ext.ux.form'); - -/** - * @class Ext.ux.form.FakeFormPanel - * @extends Ext.form.FormPanel - * - * @xtype typo3-form-wizard-fakeformpanel - */ -Ext.ux.form.FakeFormPanel = Ext.extend(Ext.form.FormPanel, { - - initComponent : function(){ - this.form = this.createForm(); - Ext.FormPanel.superclass.initComponent.call(this); - - this.bodyCfg = { - tag: 'div', - cls: this.baseCls + '-body', - method : this.method || 'POST', - id : this.formId || Ext.id() - }; - if(this.fileUpload) { - this.bodyCfg.enctype = 'multipart/form-data'; - } - this.initItems(); - - this.addEvents( - 'clientvalidation' - ); - - this.relayEvents(this.form, ['beforeaction', 'actionfailed', 'actioncomplete']); - } - -}); - -Ext.reg('typo3-form-wizard-fakeformpanel', Ext.ux.form.FakeFormPanel); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.ValueCheckbox.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.ValueCheckbox.js deleted file mode 100644 index b40f821dc30b..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.ValueCheckbox.js +++ /dev/null @@ -1,21 +0,0 @@ -Ext.ns('Ext.ux.form'); - -/** - * @class Ext.ux.form.ValueCheckbox - * @extends Ext.form.Checkbox - * getValue returns inputValue when checked - * - * @see TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Attributes.initComponent - * @xtype typo3-form-wizard-valuecheckbox - */ -Ext.ux.form.ValueCheckbox = Ext.extend(Ext.form.Checkbox, { - - getValue : function(){ - var checked = Ext.ux.form.ValueCheckbox.superclass.getValue.call(this); - if(this.inputValue !== undefined && checked) - return this.inputValue; - return checked; - } -}); - -Ext.reg('typo3-form-wizard-valuecheckbox', Ext.ux.form.ValueCheckbox); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.spinnerfield.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.spinnerfield.js deleted file mode 100644 index 92765c60c004..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.spinnerfield.js +++ /dev/null @@ -1,62 +0,0 @@ -/*! - * Ext JS Library 3.3.1 - * Copyright(c) 2006-2010 Sencha Inc. - * licensing@sencha.com - * http://www.sencha.com/license - */ -Ext.ns('Ext.ux.form'); - -/** - * @class Ext.ux.form.SpinnerField - * @extends Ext.form.NumberField - * Creates a field utilizing Ext.ux.Spinner - * @xtype spinnerfield - */ -Ext.ux.form.SpinnerField = Ext.extend(Ext.form.NumberField, { - actionMode: 'wrap', - deferHeight: true, - autoSize: Ext.emptyFn, - // onBlur function shall use the inherited handler function - // onBlur: Ext.emptyFn, - adjustSize: Ext.BoxComponent.prototype.adjustSize, - - constructor: function(config) { - var spinnerConfig = Ext.copyTo({}, config, 'incrementValue,alternateIncrementValue,accelerate,defaultValue,triggerClass,splitterClass'); - - var spl = this.spinner = new Ext.ux.Spinner(spinnerConfig); - - var plugins = config.plugins - ? (Ext.isArray(config.plugins) - ? config.plugins.push(spl) - : [config.plugins, spl]) - : spl; - - Ext.ux.form.SpinnerField.superclass.constructor.call(this, Ext.apply(config, {plugins: plugins})); - }, - - // private - getResizeEl: function(){ - return this.wrap; - }, - - // private - getPositionEl: function(){ - return this.wrap; - }, - - // private - alignErrorIcon: function(){ - if (this.wrap) { - this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]); - } - }, - - validateBlur: function(){ - return true; - } -}); - -Ext.reg('spinnerfield', Ext.ux.form.SpinnerField); - -//backwards compat -Ext.form.SpinnerField = Ext.ux.form.SpinnerField; diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.textfieldsubmit.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.textfieldsubmit.js deleted file mode 100644 index 8c22516c7af4..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.form.textfieldsubmit.js +++ /dev/null @@ -1,52 +0,0 @@ -/*! - * Ext JS Library 3.3.1 - * Copyright(c) 2006-2010 Sencha Inc. - * licensing@sencha.com - * http://www.sencha.com/license - */ -Ext.ns('Ext.ux.form'); - -/** - * @class Ext.ux.form.TextFieldSubmit - * @extends Ext.form.TriggerField - * Creates a text field with a submit trigger button - * @xtype textfieldsubmit - */ -Ext.ux.form.TextFieldSubmit = Ext.extend(Ext.form.TriggerField, { - hideTrigger: true, - - triggerClass: 'x-form-submit-trigger', - - enableKeyEvents: true, - - onTriggerClick: function() { - this.setHideTrigger(true); - if (this.isValid()) { - this.fireEvent('triggerclick', this); - } else { - this.setValue(this.startValue); - } - }, - - initEvents: function() { - Ext.ux.form.TextFieldSubmit.superclass.initEvents.call(this); - this.on('keyup', function(field, event) { - if (event.getKey() != event.ENTER && this.isValid()) { - this.setHideTrigger(false); - } else { - this.setHideTrigger(true); - } - }); - this.on('keypress', function(field, event) { - if (event.getKey() == event.ENTER) { - event.stopEvent(); - this.onTriggerClick(); - } - }, this); - } -}); - -Ext.reg('textfieldsubmit', Ext.ux.form.TextFieldSubmit); - -//backwards compat -Ext.form.TextFieldSubmit = Ext.ux.form.TextFieldSubmit; diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.CheckColumn.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.CheckColumn.js deleted file mode 100644 index 806364d47a23..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.CheckColumn.js +++ /dev/null @@ -1,84 +0,0 @@ -/*! - * Ext JS Library 3.1.1 - * Copyright(c) 2006-2010 Ext JS, LLC - * licensing@extjs.com - * http://www.extjs.com/license - */ -Ext.ns('Ext.ux.grid'); - -/** - * @class Ext.ux.grid.CheckColumn - * @extends Object - * GridPanel plugin to add a column with check boxes to a grid. - * <p>Example usage:</p> - * <pre><code> -// create the column -var checkColumn = new Ext.grid.CheckColumn({ - header: 'Indoor?', - dataIndex: 'indoor', - id: 'check', - width: 55 -}); - -// add the column to the column model -var cm = new Ext.grid.ColumnModel([{ - header: 'Foo', - ... - }, - checkColumn -]); - -// create the grid -var grid = new Ext.grid.EditorGridPanel({ - ... - cm: cm, - plugins: [checkColumn], // include plugin - ... -}); - * </code></pre> - * In addition to storing a Boolean value within the record data, this - * class toggles a css class between <tt>'x-grid3-check-col'</tt> and - * <tt>'x-grid3-check-col-on'</tt> to alter the background image used for - * a column. - */ -Ext.ux.grid.CheckColumn = function(config){ - Ext.apply(this, config); - if(!this.id){ - this.id = Ext.id(); - } - this.renderer = this.renderer.createDelegate(this); -}; - -Ext.ux.grid.CheckColumn.prototype ={ - init : function(grid){ - this.grid = grid; - this.grid.on('render', function(){ - var view = this.grid.getView(); - view.mainBody.on('mousedown', this.onMouseDown, this); - }, this); - }, - - onMouseDown : function(e, t){ - if(Ext.fly(t).hasClass(this.createId())){ - e.stopEvent(); - var index = this.grid.getView().findRowIndex(t); - var record = this.grid.store.getAt(index); - record.set(this.dataIndex, !record.data[this.dataIndex]); - } - }, - - renderer : function(v, p, record){ - p.css += ' x-grid3-check-col-td'; - return String.format('<div class="x-grid3-check-col{0} {1}"> </div>', v ? '-on' : '', this.createId()); - }, - - createId : function(){ - return 'x-grid3-cc-' + this.id; - } -}; - -// register ptype -Ext.preg('checkcolumn', Ext.ux.grid.CheckColumn); - -// backwards compat -Ext.grid.CheckColumn = Ext.ux.grid.CheckColumn; \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.ItemDeleter.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.ItemDeleter.js deleted file mode 100644 index bbc94fe92a43..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.ItemDeleter.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - */ -Ext.ns('Ext.ux.grid'); - -Ext.ux.grid.ItemDeleter = Ext.extend(Ext.grid.RowSelectionModel, { - width: 25, - sortable: false, - dataIndex: 0, // this is needed, otherwise there will be an error - - menuDisabled: true, - fixed: true, - id: 'deleter', - header: TYPO3.l10n.localize('fieldoptions_delete'), - - initEvents: function(){ - Ext.ux.grid.ItemDeleter.superclass.initEvents.call(this); - this.grid.on('cellclick', function(grid, rowIndex, columnIndex, e){ - if(columnIndex==grid.getColumnModel().getIndexById('deleter')) { - var record = grid.getStore().getAt(rowIndex); - grid.getStore().remove(record); - grid.getView().refresh(); - } - }); - }, - - renderer: function(v, p, record, rowIndex){ - return '<div class="remove"> </div>'; - } -}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.SingleSelectCheckColumn.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.SingleSelectCheckColumn.js deleted file mode 100644 index 6292fe6fae94..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.grid.SingleSelectCheckColumn.js +++ /dev/null @@ -1,15 +0,0 @@ -Ext.ux.grid.SingleSelectCheckColumn = Ext.extend(Ext.ux.grid.CheckColumn, { - onMouseDown : function(e, t){ - if(Ext.fly(t).hasClass('x-grid3-cc-'+this.id)){ - e.stopEvent(); - var index = this.grid.getView().findRowIndex(t), - dataIndex = this.dataIndex; - this.grid.store.each(function(record, i){ - var value = (i == index && record.get(dataIndex) != true); - if(value != record.get(dataIndex)){ - record.set(dataIndex, value); - } - }); - } - } -}); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.isemptyobject.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.isemptyobject.js deleted file mode 100644 index 12235642ce3a..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.isemptyobject.js +++ /dev/null @@ -1,8 +0,0 @@ -Ext.apply(Ext, { - isEmptyObject: function(o) { - for(var p in o) { - return false; - }; - return true; - } -}); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.merge.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.merge.js deleted file mode 100644 index 70b66c201d8e..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.merge.js +++ /dev/null @@ -1,26 +0,0 @@ -Ext.apply(Ext, { - merge: function(o, c) { - if (o && c && typeof c == 'object') { - for (var p in c){ - if ((typeof o[p] == 'object') && (typeof c[p] == 'object')) { - Ext.merge(o[p], c[p]); - } else { - o[p] = c[p]; - } - } - } - return o; - }, - mergeIf: function(o, c) { - if (o && c && typeof c == 'object') { - for (var p in c){ - if ((typeof o[p] == 'object') && (typeof c[p] == 'object')) { - Ext.mergeIf(o[p], c[p]); - } else if (typeof o[p] == 'undefined') { - o[p] = c[p]; - } - } - } - return o; - } -}); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.spinner.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.spinner.js deleted file mode 100644 index edd7d1efd1a5..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Ux/Ext.ux.spinner.js +++ /dev/null @@ -1,443 +0,0 @@ -/*! - * Ext JS Library 3.3.1 - * Copyright(c) 2006-2010 Sencha Inc. - * licensing@sencha.com - * http://www.sencha.com/license - */ -/** - * @class Ext.ux.Spinner - * @extends Ext.util.Observable - * Creates a Spinner control utilized by Ext.ux.form.SpinnerField - */ -Ext.ux.Spinner = Ext.extend(Ext.util.Observable, { - incrementValue: 1, - alternateIncrementValue: 5, - triggerClass: 'x-form-spinner-trigger', - splitterClass: 'x-form-spinner-splitter', - alternateKey: Ext.EventObject.shiftKey, - defaultValue: 0, - accelerate: false, - - constructor: function(config){ - Ext.ux.Spinner.superclass.constructor.call(this, config); - Ext.apply(this, config); - this.mimicing = false; - }, - - init: function(field){ - this.field = field; - - field.afterMethod('onRender', this.doRender, this); - field.afterMethod('onEnable', this.doEnable, this); - field.afterMethod('onDisable', this.doDisable, this); - field.afterMethod('afterRender', this.doAfterRender, this); - field.afterMethod('onResize', this.doResize, this); - field.afterMethod('onFocus', this.doFocus, this); - field.beforeMethod('onDestroy', this.doDestroy, this); - }, - - doRender: function(ct, position){ - var el = this.el = this.field.getEl(); - var f = this.field; - - if (!f.wrap) { - f.wrap = this.wrap = el.wrap({ - cls: "x-form-field-wrap" - }); - } - else { - this.wrap = f.wrap.addClass('x-form-field-wrap'); - } - - this.trigger = this.wrap.createChild({ - tag: "img", - src: Ext.BLANK_IMAGE_URL, - cls: "x-form-trigger " + this.triggerClass - }); - - if (!f.width) { - this.wrap.setWidth(el.getWidth() + this.trigger.getWidth()); - } - - this.splitter = this.wrap.createChild({ - tag: 'div', - cls: this.splitterClass, - style: 'width:13px; height:2px;' - }); - this.splitter.setRight((Ext.isIE) ? 1 : 2).setTop(10).show(); - - this.proxy = this.trigger.createProxy('', this.splitter, true); - this.proxy.addClass("x-form-spinner-proxy"); - this.proxy.setStyle('left', '0px'); - this.proxy.setSize(14, 1); - this.proxy.hide(); - this.dd = new Ext.dd.DDProxy(this.splitter.dom.id, "SpinnerDrag", { - dragElId: this.proxy.id - }); - - this.initTrigger(); - this.initSpinner(); - }, - - doAfterRender: function(){ - var y; - if (Ext.isIE && this.el.getY() != (y = this.trigger.getY())) { - this.el.position(); - this.el.setY(y); - } - }, - - doEnable: function(){ - if (this.wrap) { - this.disabled = false; - this.wrap.removeClass(this.field.disabledClass); - } - }, - - doDisable: function(){ - if (this.wrap) { - this.disabled = true; - this.wrap.addClass(this.field.disabledClass); - this.el.removeClass(this.field.disabledClass); - } - }, - - doResize: function(w, h){ - if (typeof w == 'number') { - this.el.setWidth(w - this.trigger.getWidth()); - } - this.wrap.setWidth(this.el.getWidth() + this.trigger.getWidth()); - }, - - doFocus: function(){ - if (!this.mimicing) { - this.wrap.addClass('x-trigger-wrap-focus'); - this.mimicing = true; - Ext.get(Ext.isIE ? document.body : document).on("mousedown", this.mimicBlur, this, { - delay: 10 - }); - this.el.on('keydown', this.checkTab, this); - } - }, - - // private - checkTab: function(e){ - if (e.getKey() == e.TAB) { - this.triggerBlur(); - } - }, - - // private - mimicBlur: function(e){ - if (!this.wrap.contains(e.target) && this.field.validateBlur(e)) { - this.triggerBlur(); - } - }, - - // private - triggerBlur: function(){ - this.mimicing = false; - Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this); - this.el.un("keydown", this.checkTab, this); - this.field.beforeBlur(); - this.wrap.removeClass('x-trigger-wrap-focus'); - this.field.onBlur.call(this.field); - }, - - initTrigger: function(){ - this.trigger.addClassOnOver('x-form-trigger-over'); - this.trigger.addClassOnClick('x-form-trigger-click'); - }, - - initSpinner: function(){ - this.field.addEvents({ - 'spin': true, - 'spinup': true, - 'spindown': true - }); - - this.keyNav = new Ext.KeyNav(this.el, { - "up": function(e){ - e.preventDefault(); - this.onSpinUp(); - }, - - "down": function(e){ - e.preventDefault(); - this.onSpinDown(); - }, - - "pageUp": function(e){ - e.preventDefault(); - this.onSpinUpAlternate(); - }, - - "pageDown": function(e){ - e.preventDefault(); - this.onSpinDownAlternate(); - }, - - scope: this - }); - - this.repeater = new Ext.util.ClickRepeater(this.trigger, { - accelerate: this.accelerate - }); - this.field.mon(this.repeater, "click", this.onTriggerClick, this, { - preventDefault: true - }); - - this.field.mon(this.trigger, { - mouseover: this.onMouseOver, - mouseout: this.onMouseOut, - mousemove: this.onMouseMove, - mousedown: this.onMouseDown, - mouseup: this.onMouseUp, - scope: this, - preventDefault: true - }); - - this.field.mon(this.wrap, "mousewheel", this.handleMouseWheel, this); - - this.dd.setXConstraint(0, 0, 10); - this.dd.setYConstraint(1500, 1500, 10); - this.dd.endDrag = this.endDrag.createDelegate(this); - this.dd.startDrag = this.startDrag.createDelegate(this); - this.dd.onDrag = this.onDrag.createDelegate(this); - }, - - onMouseOver: function(){ - if (this.disabled) { - return; - } - var middle = this.getMiddle(); - this.tmpHoverClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-overup' : 'x-form-spinner-overdown'; - this.trigger.addClass(this.tmpHoverClass); - }, - - //private - onMouseOut: function(){ - this.trigger.removeClass(this.tmpHoverClass); - }, - - //private - onMouseMove: function(){ - if (this.disabled) { - return; - } - var middle = this.getMiddle(); - if (((Ext.EventObject.getPageY() > middle) && this.tmpHoverClass == "x-form-spinner-overup") || - ((Ext.EventObject.getPageY() < middle) && this.tmpHoverClass == "x-form-spinner-overdown")) { - } - }, - - //private - onMouseDown: function(){ - if (this.disabled) { - return; - } - var middle = this.getMiddle(); - this.tmpClickClass = (Ext.EventObject.getPageY() < middle) ? 'x-form-spinner-clickup' : 'x-form-spinner-clickdown'; - this.trigger.addClass(this.tmpClickClass); - }, - - //private - onMouseUp: function(){ - this.trigger.removeClass(this.tmpClickClass); - }, - - //private - onTriggerClick: function(){ - if (this.disabled || this.el.dom.readOnly) { - return; - } - var middle = this.getMiddle(); - var ud = (Ext.EventObject.getPageY() < middle) ? 'Up' : 'Down'; - this['onSpin' + ud](); - }, - - //private - getMiddle: function(){ - var t = this.trigger.getTop(); - var h = this.trigger.getHeight(); - var middle = t + (h / 2); - return middle; - }, - - //private - //checks if control is allowed to spin - isSpinnable: function(){ - if (this.disabled || this.el.dom.readOnly) { - Ext.EventObject.preventDefault(); //prevent scrolling when disabled/readonly - return false; - } - return true; - }, - - handleMouseWheel: function(e){ - //disable scrolling when not focused - if (this.wrap.hasClass('x-trigger-wrap-focus') == false) { - return; - } - - var delta = e.getWheelDelta(); - if (delta > 0) { - this.onSpinUp(); - e.stopEvent(); - } else { - if (delta < 0) { - this.onSpinDown(); - e.stopEvent(); - } - } - }, - - //private - startDrag: function(){ - this.proxy.show(); - this._previousY = Ext.fly(this.dd.getDragEl()).getTop(); - }, - - //private - endDrag: function(){ - this.proxy.hide(); - }, - - //private - onDrag: function(){ - if (this.disabled) { - return; - } - var y = Ext.fly(this.dd.getDragEl()).getTop(); - var ud = ''; - - if (this._previousY > y) { - ud = 'Up'; - } //up - if (this._previousY < y) { - ud = 'Down'; - } //down - if (ud != '') { - this['onSpin' + ud](); - } - - this._previousY = y; - }, - - //private - onSpinUp: function(){ - if (this.isSpinnable() == false) { - return; - } - if (Ext.EventObject.shiftKey == true) { - this.onSpinUpAlternate(); - return; - } - else { - this.spin(false, false); - } - this.field.fireEvent("spin", this.field); - this.field.fireEvent("spinup", this.field); - }, - - //private - onSpinDown: function(){ - if (this.isSpinnable() == false) { - return; - } - if (Ext.EventObject.shiftKey == true) { - this.onSpinDownAlternate(); - return; - } - else { - this.spin(true, false); - } - this.field.fireEvent("spin", this.field); - this.field.fireEvent("spindown", this.field); - }, - - //private - onSpinUpAlternate: function(){ - if (this.isSpinnable() == false) { - return; - } - this.spin(false, true); - this.field.fireEvent("spin", this); - this.field.fireEvent("spinup", this); - }, - - //private - onSpinDownAlternate: function(){ - if (this.isSpinnable() == false) { - return; - } - this.spin(true, true); - this.field.fireEvent("spin", this); - this.field.fireEvent("spindown", this); - }, - - spin: function(down, alternate){ - var v = parseFloat(this.field.getValue()); - var incr = (alternate == true) ? this.alternateIncrementValue : this.incrementValue; - (down == true) ? v -= incr : v += incr; - - v = (isNaN(v)) ? this.defaultValue : v; - v = this.fixBoundaries(v); - this.field.setRawValue(v); - }, - - fixBoundaries: function(value){ - var v = value; - - if (this.field.minValue != undefined && v < this.field.minValue) { - v = this.field.minValue; - } - if (this.field.maxValue != undefined && v > this.field.maxValue) { - v = this.field.maxValue; - } - - return this.fixPrecision(v); - }, - - // private - fixPrecision: function(value){ - var nan = isNaN(value); - if (!this.field.allowDecimals || this.field.decimalPrecision == -1 || nan || !value) { - return nan ? '' : value; - } - return parseFloat(parseFloat(value).toFixed(this.field.decimalPrecision)); - }, - - doDestroy: function(){ - if (this.trigger) { - this.trigger.remove(); - } - if (this.wrap) { - this.wrap.remove(); - delete this.field.wrap; - } - - if (this.splitter) { - this.splitter.remove(); - } - - if (this.dd) { - this.dd.unreg(); - this.dd = null; - } - - if (this.proxy) { - this.proxy.remove(); - } - - if (this.repeater) { - this.repeater.purgeListeners(); - } - if (this.mimicing){ - Ext.get(Ext.isIE ? document.body : document).un("mousedown", this.mimicBlur, this); - } - } -}); - -//backwards compat -Ext.form.Spinner = Ext.ux.Spinner; \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport.js deleted file mode 100644 index e982b3bafa76..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport.js +++ /dev/null @@ -1,275 +0,0 @@ -Ext.namespace('TYPO3.Form', 'TYPO3.Form.Wizard'); - -/** - * The viewport - * - * @class TYPO3.Form.Wizard.Viewport - * @extends Ext.Container - */ -TYPO3.Form.Wizard.Viewport = Ext.extend(Ext.Container, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard', - - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {Mixed} renderTo - * Specify the id of the element, a DOM element or an existing Element that - * this component will be rendered into. - */ - renderTo: 'typo3-inner-docbody', - - /** - * @cfg {String} layout - * In order for child items to be correctly sized and positioned, typically - * a layout manager must be specified through the layout configuration option. - * - * The sizing and positioning of child items is the responsibility of the - * Container's layout manager which creates and manages the type of layout - * you have in mind. - */ - layout: 'border', - - /** - * Constructor - * - * Add the left and right part to the viewport - * Add the history buttons - * @todo Move the buttons to the docheader - */ - initComponent: function() { - var config = { - items: [ - { - xtype: 'typo3-form-wizard-viewport-left' - },{ - xtype: 'typo3-form-wizard-viewport-right' - } - ] - }; - - // Add the buttons to the docheader - this.splitButtons.addPreSubmitCallback(this.save); - this.hijackTBE(); - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.superclass.initComponent.apply(this, arguments); - }, - - /** - * hijack TBE save method - */ - hijackTBE: function() { - /** - * @see TBE_EDITOR.submitForm - */ - TBE_EDITOR.submitForm = function() { - if (TBE_EDITOR.doSaveFieldName) { - document[TBE_EDITOR.formname][TBE_EDITOR.doSaveFieldName].value=1; - } - // Set a short timeout to allow other JS processes to complete, in particular those from - // EXT:backend/Resources/Public/JavaScript/FormEngine.js (reference: http://forge.typo3.org/issues/58755). - // TODO: This should be solved in a better way when this script is refactored. - window.setTimeout(function() { - var form0 = document.getElementsByName(TBE_EDITOR.formname).item(0); - if(form0 && form0.dataset.typo3_formwizard == 'wait') { - TBE_EDITOR.submitForm(); - return; - } - document.getElementsByName(TBE_EDITOR.formname).item(0).submit(); - }, 10); - } - }, - - /** - * Add the buttons to the docheader - * - * All buttons except close will be handled by the form wizard javascript - * The save and history buttons are put into separate buttongroups, click - * event listeners are added. - */ - addButtonsToDocHeader: function() { - var docHeaderRow1 = Ext.get('typo3-docheader'); - var docHeaderButtonsBar = docHeaderRow1.first('.typo3-docheader-buttons'); - var docHeaderRow1ButtonsLeft = docHeaderButtonsBar.first('.left'); - - var saveButtonGroup = Ext.DomHelper.append(docHeaderRow1ButtonsLeft, { - tag: 'div', - cls: 'buttongroup' - }); - - var save = new Ext.Element( - Ext.DomHelper.append(saveButtonGroup, { - tag: 'span', - cls: 't3-icon t3-icon-actions t3-icon-actions-document t3-icon-document-save', - id: 'formwizard-save', - title: TYPO3.l10n.localize('save') - }) - ); - - var saveAndClose = new Ext.Element( - Ext.DomHelper.append(saveButtonGroup, { - tag: 'span', - cls: 't3-icon t3-icon-actions t3-icon-actions-document t3-icon-document-save-close', - id: 'formwizard-saveandclose', - title: TYPO3.l10n.localize('saveAndClose') - }) - ); - - save.on('click', this.save, this); - saveAndClose.on('click', this.saveAndClose, this); - - var historyButtonGroup = Ext.DomHelper.append(docHeaderRow1ButtonsLeft, { - tag: 'div', - cls: 'buttongroup' - }); - - var undo = new Ext.Element( - Ext.DomHelper.append(historyButtonGroup, { - tag: 'span', - cls: 't3-icon t3-icon-actions t3-icon-actions-document t3-icon-view-go-back', - id: 'formwizard-history-undo', - title: TYPO3.l10n.localize('history_undo') - }) - ); - - var redo = new Ext.Element( - Ext.DomHelper.append(historyButtonGroup, { - tag: 'span', - cls: 't3-icon t3-icon-actions t3-icon-actions-document t3-icon-view-go-forward', - id: 'formwizard-history-redo', - title: TYPO3.l10n.localize('history_redo') - }) - ); - - undo.hide(); - undo.on('click', this.undo, this); - - redo.hide(); - redo.on('click', this.redo, this); - }, - - /** - * @returns {Element} - */ - getEditForm: function() { - return document.querySelector('[name=editform]'); - }, - - /** - * Save the form - * - * @param event - * @param element - * @param object - */ - save: function(event, element, object) { - var configuration = Ext.getCmp('formwizard-right').getConfiguration(); - var wizardUrl = TYPO3.Form.Wizard.Settings.ajaxUrl; - var url = wizardUrl.substring(wizardUrl.indexOf('&P')); - url = TYPO3.settings.ajaxUrls['formwizard_save'] + url; - - // prepare config json - var encodedConfiguration = Ext.encode(configuration); - var formData = new FormData(); - formData.append('configuration', encodedConfiguration); - formData.append('action', 'save'); - - // get domElement - var Viewport = Ext.getCmp('formwizard'); - - // synchronous ajax request - var r = new XMLHttpRequest(); - r.open("POST", url, true); - r.onreadystatechange = function () { - if (this.readyState != 4 || this.status != 200) return; - // form ready - var editForm = Viewport.getEditForm(); - if(editForm) { - editform.dataset.typo3_formwizard = 'ready'; - } - var responseObject = Ext.decode(this.responseText); - Viewport.transportEl.value = responseObject.fakeTs; - }; - // form not ready - var editForm = Viewport.getEditForm(); - if(editForm) { - editform.dataset.typo3_formwizard = 'wait'; - } - r.send(formData); - }, - - /** - * Save the form and close the wizard - * - * @param event - * @param element - * @param object - */ - saveAndClose: function(event, element, object) { - var configuration = Ext.getCmp('formwizard-right').getConfiguration(); - var url = document.location.href.substring(document.location.href.indexOf('&P')); - url = TYPO3.settings.ajaxUrls['formwizard_save'] + url; - Ext.Ajax.request({ - url: url, - method: 'POST', - params: { - configuration: Ext.encode(configuration) - }, - success: function(response, opts) { - var urlParameters = Ext.urlDecode(document.location.search.substring(1)); - document.location = urlParameters['P[returnUrl]']; - }, - failure: function(response, opts) { - Ext.MessageBox.alert( - TYPO3.l10n.localize('action_save'), - TYPO3.l10n.localize('action_save_error') + ' ' + response.status - ); - }, - scope: this - }); - }, - - /** - * Get the previous snapshot from the history if available - * - * @param event - * @param element - * @param object - */ - undo: function(event, element, object) { - TYPO3.Form.Wizard.Helpers.History.undo(); - }, - - /** - * Get the next snapshot from the history if available - * - * @param event - * @param element - * @param object - */ - redo: function(event, element, object) { - TYPO3.Form.Wizard.Helpers.History.redo(); - } -}); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left.js deleted file mode 100644 index 586d7defb1f0..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left.js +++ /dev/null @@ -1,129 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport'); - -/** - * The tabpanel on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left - * @extends Ext.TabPanel - */ -TYPO3.Form.Wizard.Viewport.Left = Ext.extend(Ext.TabPanel, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left', - - /** - * @cfg {Integer} width - * The width of this component in pixels (defaults to auto). - */ - width: 350, - - /** - * @cfg {String/Number} activeTab A string id or the numeric index of the tab that should be initially - * activated on render (defaults to undefined). - */ - activeTab: 0, - - /** - * @cfg {String} region - * Note: this config is only used when this BoxComponent is rendered - * by a Container which has been configured to use the BorderLayout - * layout manager (e.g. specifying layout:'border'). - */ - region: 'west', - - /** - * @cfg {Boolean} autoScroll - * true to use overflow:'auto' on the components layout element and show - * scroll bars automatically when necessary, false to clip any overflowing - * content (defaults to false). - */ - autoScroll: true, - - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset border, - * but this can be further altered by setting {@link #bodyBorder} to false. - */ - border: false, - - /** - * @cfg {Object|Function} defaults - * - * This option is a means of applying default settings to all added items - * whether added through the items config or via the add or insert methods. - */ - defaults: { - autoHeight: true, - autoWidth: true - }, - - /** - * Constructor - * - * Add the tabs to the tabpanel - */ - initComponent: function() { - var allowedTabs = TYPO3.Form.Wizard.Settings.defaults.showTabs.split(/[, ]+/); - var tabs = []; - - Ext.each(allowedTabs, function(option, index, length) { - var tabXtype = 'typo3-form-wizard-viewport-left-' + option; - if (Ext.ComponentMgr.isRegistered(tabXtype)) { - tabs.push({ - xtype: tabXtype - }); - } - }, this); - - var config = { - items: tabs - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.superclass.initComponent.apply(this, arguments); - - // Set the focus when a tab has changed. We need this to remove focus from forms - this.on('tabchange', this.setFocus, this); - }, - - /** - * Set the focus to a tab - * - * doLayout is necessary, because the tabs are sometimes emptied and filled - * again, for instance by the history. Otherwise after a history undo or redo - * the options and form tabs are empty. - * - * @param tabPanel - * @param tab - */ - setFocus: function(tabPanel, tab) { - tabPanel.doLayout(); - tab.el.focus(); - }, - - /** - * Set the options tab as active tab - * - * Called by the options panel when an element has been selected - */ - setOptionsTab: function() { - this.setActiveTab('formwizard-left-options'); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left', TYPO3.Form.Wizard.Viewport.Left); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements.js deleted file mode 100644 index 72a06a0d7fb3..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements.js +++ /dev/null @@ -1,96 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left'); - -/** - * The elements panel in the elements tab on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left.Elements - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Elements = Ext.extend(Ext.Panel, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-elements', - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'x-tab-panel-body-content', - - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('left_elements'), - - /** - * Constructor - * - * Add the form elements to the tab - */ - initComponent: function() { - var allowedAccordions = TYPO3.Form.Wizard.Settings.defaults.tabs.elements.showAccordions.split(/[, ]+/); - var accordions = []; - - Ext.each(allowedAccordions, function(option, index, length) { - var accordionXtype = 'typo3-form-wizard-viewport-left-elements-' + option; - if (Ext.ComponentMgr.isRegistered(accordionXtype)) { - accordions.push({ - xtype: accordionXtype - }); - } - }, this); - - var config = { - items: [ - { - xtype: 'container', - id: 'formwizard-left-elements-intro', - tpl: new Ext.XTemplate( - '<tpl for=".">', - '<p><strong>{title}</strong></p>', - '<p>{description}</p>', - '</tpl>' - ), - data: [{ - title: TYPO3.l10n.localize('left_elements_intro_title'), - description: TYPO3.l10n.localize('left_elements_intro_description') - }], - cls: 'formwizard-left-dummy typo3-message message-information' - }, { - xtype: 'panel', - layout: 'accordion', - border: false, - padding: 0, - defaults: { - autoHeight: true, - cls: 'x-panel-accordion' - }, - items: accordions - } - ] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Elements.superclass.initComponent.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-elements', TYPO3.Form.Wizard.Viewport.Left.Elements); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Basic.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Basic.js deleted file mode 100644 index 4f2e0fdc0f8d..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Basic.js +++ /dev/null @@ -1,178 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Elements'); - -/** - * The basic elements in the elements tab on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left.Elements.Basic - * @extends TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup - */ -TYPO3.Form.Wizard.Viewport.Left.Elements.Basic = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-elements-basic', - - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('left_elements_basic'), - - /** - * Constructor - * - * Add the buttons to the accordion - */ - initComponent: function() { - var allowedButtons = TYPO3.Form.Wizard.Settings.defaults.tabs.elements.accordions.basic.showButtons.split(/[, ]+/); - var buttons = []; - - Ext.each(allowedButtons, function(option, index, length) { - switch (option) { - case 'button': - buttons.push({ - text: TYPO3.l10n.localize('basic_button'), - id: 'basic-button', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-button', - scope: this - }); - break; - case 'checkbox': - buttons.push({ - text: TYPO3.l10n.localize('basic_checkbox'), - id: 'basic-checkbox', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-checkbox', - scope: this - }); - break; - case 'fieldset': - buttons.push({ - text: TYPO3.l10n.localize('basic_fieldset'), - id: 'basic-fieldset', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-fieldset', - scope: this - }); - break; - case 'fileupload': - buttons.push({ - text: TYPO3.l10n.localize('basic_fileupload'), - id: 'basic-fileupload', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-fileupload', - scope: this - }); - break; - case 'hidden': - buttons.push({ - text: TYPO3.l10n.localize('basic_hidden'), - id: 'basic-hidden', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-hidden', - scope: this - }); - break; - case 'password': - buttons.push({ - text: TYPO3.l10n.localize('basic_password'), - id: 'basic-password', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-password', - scope: this - }); - break; - case 'radio': - buttons.push({ - text: TYPO3.l10n.localize('basic_radio'), - id: 'basic-radio', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-radio', - scope: this - }); - break; - case 'reset': - buttons.push({ - text: TYPO3.l10n.localize('basic_reset'), - id: 'basic-reset', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-reset', - scope: this - }); - break; - case 'select': - buttons.push({ - text: TYPO3.l10n.localize('basic_select'), - id: 'basic-select', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-select', - scope: this - }); - break; - case 'submit': - buttons.push({ - text: TYPO3.l10n.localize('basic_submit'), - id: 'basic-submit', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-submit', - scope: this - }); - break; - case 'textarea': - buttons.push({ - text: TYPO3.l10n.localize('basic_textarea'), - id: 'basic-textarea', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-textarea', - scope: this - }); - break; - case 'textline': - buttons.push({ - text: TYPO3.l10n.localize('basic_textline'), - id: 'basic-textline', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-basic-textline', - scope: this - }); - break; - } - }, this); - - var config = { - items: buttons - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Elements.Basic.superclass.initComponent.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-elements-basic', TYPO3.Form.Wizard.Viewport.Left.Elements.Basic); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/ButtonGroup.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/ButtonGroup.js deleted file mode 100644 index d7d91ed227f0..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/ButtonGroup.js +++ /dev/null @@ -1,122 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Elements'); - -/** - * The button group abstract for the elements tab on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup - * @extends Ext.ButtonGroup - */ -TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup = Ext.extend(Ext.Panel, { - /** - * @cfg {Object|Function} defaults - * This option is a means of applying default settings to all added items - * whether added through the items config or via the add or insert methods. - */ - defaults: { - xtype: 'button', - scale: 'small', - width: 140, - iconAlign: 'left', - cls: 'formwizard-element' - }, - - cls: 'formwizard-buttongroup', - - /** - * @cfg {Boolean} autoHeight - * true to use height:'auto', false to use fixed height (defaults to false). - * Note: Setting autoHeight: true means that the browser will manage the panel's height - * based on its contents, and that Ext will not manage it at all. If the panel is within a layout that - * manages dimensions (fit, border, etc.) then setting autoHeight: true - * can cause issues with scrolling and will not generally work as expected since the panel will take - * on the height of its contents rather than the height required by the Ext layout. - */ - autoHeight: true, - - /** - * @cfg {Number/String} padding - * A shortcut for setting a padding style on the body element. The value can - * either be a number to be applied to all sides, or a normal css string - * describing padding. - */ - padding: 0, - - /** - * @cfg {String} layout - * In order for child items to be correctly sized and positioned, typically - * a layout manager must be specified through the layout configuration option. - * - * The sizing and positioning of child items is the responsibility of the - * Container's layout manager which creates and manages the type of layout - * you have in mind. - */ - layout: 'table', - - /** - * @cfg {Object} layoutConfig - * This is a config object containing properties specific to the chosen - * layout if layout has been specified as a string. - */ - layoutConfig: { - columns: 2 - }, - - /** - * Constructor - * - * Add the buttons to the accordion - */ - initComponent: function() { - var config = {}; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup.superclass.initComponent.apply(this, arguments); - - // Initialize the dragzone after rendering - this.on('render', this.initializeDrag, this); - }, - - /** - * Initialize the drag zone. - * - * @param buttonGroup - */ - initializeDrag: function(buttonGroup) { - buttonGroup.dragZone = new Ext.dd.DragZone(buttonGroup.getEl(), { - getDragData: function(element) { - var sourceElement = element.getTarget('.formwizard-element'); - if (sourceElement) { - clonedElement = sourceElement.cloneNode(true); - clonedElement.id = Ext.id(); - return buttonGroup.dragData = { - sourceEl: sourceElement, - repairXY: Ext.fly(sourceElement).getXY(), - ddel: clonedElement - }; - } - }, - getRepairXY: function() { - return buttonGroup.dragData.repairXY; - } - }); - }, - - /** - * Called when a button has been double clicked - * - * Tells the form in the right container to add a new element, according to - * the button which has been clicked. - * - * @param button - * @param event - */ - onDoubleClick: function(button, event) { - var formContainer = Ext.getCmp('formwizard-right').get(0).containerComponent; - formContainer.dropElement(button, 'container'); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-elements-buttongroup', TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Content.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Content.js deleted file mode 100644 index bfcb0c3f9da7..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Content.js +++ /dev/null @@ -1,78 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Elements'); - -/** - * The content elements in the elements tab on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left.Elements.Content - * @extends TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup - */ -TYPO3.Form.Wizard.Viewport.Left.Elements.Content = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-elements-content', - - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('left_elements_content'), - - /** - * Constructor - * - * Add the buttons to the accordion - */ - initComponent: function() { - var allowedButtons = TYPO3.Form.Wizard.Settings.defaults.tabs.elements.accordions.content.showButtons.split(/[, ]+/); - var buttons = []; - - Ext.each(allowedButtons, function(option, index, length) { - switch (option) { - case 'header': - buttons.push({ - text: TYPO3.l10n.localize('content_header'), - id: 'content-header', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-content-header', - scope: this - }); - break; - case 'textblock': - buttons.push({ - text: TYPO3.l10n.localize('content_textblock'), - id: 'content-textblock', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-content-textblock', - scope: this - }); - break; - } - }, this); - - var config = { - items: buttons - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Elements.Content.superclass.initComponent.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-elements-content', TYPO3.Form.Wizard.Viewport.Left.Elements.Content); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Predefined.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Predefined.js deleted file mode 100644 index d1596e90e3bc..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Elements/Predefined.js +++ /dev/null @@ -1,98 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Elements'); - -/** - * The predefined elements in the elements tab on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left.Elements.Predefined - * @extends TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup - */ -TYPO3.Form.Wizard.Viewport.Left.Elements.Predefined = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Elements.ButtonGroup, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-elements-predefined', - - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('left_elements_predefined'), - - /** - * Constructor - * - * Add the buttons to the accordion - */ - initComponent: function() { - var allowedButtons = TYPO3.Form.Wizard.Settings.defaults.tabs.elements.accordions.predefined.showButtons.split(/[, ]+/); - var buttons = []; - - Ext.each(allowedButtons, function(option, index, length) { - switch (option) { - case 'email': - buttons.push({ - text: TYPO3.l10n.localize('predefined_email'), - id: 'predefined-email', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-predefined-email', - scope: this - }); - break; - case 'radiogroup': - buttons.push({ - text: TYPO3.l10n.localize('predefined_radiogroup'), - id: 'predefined-radiogroup', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-predefined-radiogroup', - scope: this - }); - break; - case 'checkboxgroup': - buttons.push({ - text: TYPO3.l10n.localize('predefined_checkboxgroup'), - id: 'predefined-checkboxgroup', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-predefined-checkboxgroup', - scope: this - }); - break; - case 'name': - buttons.push({ - text: TYPO3.l10n.localize('predefined_name'), - id: 'predefined-name', - clickEvent: 'dblclick', - handler: this.onDoubleClick, - iconCls: 'formwizard-left-elements-predefined-name', - scope: this - }); - break; - } - }, this); - - var config = { - items: buttons - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Elements.Predefined.superclass.initComponent.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-elements-predefined', TYPO3.Form.Wizard.Viewport.Left.Elements.Predefined); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form.js deleted file mode 100644 index 89264b985447..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form.js +++ /dev/null @@ -1,194 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.LeftTYPO3.Form.Wizard.Elements'); - -/** - * The form tab on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left.Form - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Form = Ext.extend(Ext.Panel, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-form', - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'x-tab-panel-body-content', - - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('left_form'), - - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {Object|Function} defaults - * This option is a means of applying default settings to all added items - * whether added through the items config or via the add or insert methods. - */ - defaults: { - //autoHeight: true, - border: false, - padding: 0 - }, - - /** - * @cfg {Object} validAccordions - * Keeps track which accordions are valid. Accordions contain forms which - * do client validation. If there is a validation change in a form in the - * accordion, a validation event will be fired, which changes one of these - * values - */ - validAccordions: { - behaviour: true, - prefix: true, - attributes: true, - postProcessor: true - }, - - /** - * Constructor - * - * Add the form elements to the tab - */ - initComponent: function() { - var config = { - items: [{ - xtype: 'panel', - layout: 'accordion', - ref: 'accordion', - defaults: { - autoHeight: true, - cls: 'x-panel-accordion' - } - }] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Form.superclass.initComponent.apply(this, arguments); - }, - - /** - * Called whenever a form has been added to the right container - * - * Sets element to the form component and calls the function to add the - * attribute fields - * - * @param form - */ - setForm: function(form) { - var allowedAccordions = TYPO3.Form.Wizard.Settings.defaults.tabs.form.showAccordions.split(/[, ]+/); - - this.accordion.removeAll(); - if (form) { - Ext.each(allowedAccordions, function(option, index, length) { - switch (option) { - case 'behaviour': - this.accordion.add({ - xtype: 'typo3-form-wizard-viewport-left-form-behaviour', - element: form, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - break; - case 'prefix': - this.accordion.add({ - xtype: 'typo3-form-wizard-viewport-left-form-prefix', - element: form, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - break; - case 'attributes': - this.accordion.add({ - xtype: 'typo3-form-wizard-viewport-left-form-attributes', - element: form, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - break; - case 'postProcessor': - this.accordion.add({ - xtype: 'typo3-form-wizard-viewport-left-form-postprocessor', - element: form, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - break; - } - }, this); - } - this.doLayout(); - }, - - /** - * Called by the validation listeners of the accordions - * - * Checks if all accordions are valid. If not, adds a class to the tab - * - * @param {String} accordion The accordion which fires the event - * @param {Boolean} isValid Accordion is valid or not - */ - validation: function(accordion, isValid) { - this.validAccordions[accordion] = isValid; - var tabIsValid = true; - Ext.iterate(this.validAccordions, function(key, value) { - if (!value) { - tabIsValid = false; - } - }, this); - if (this.tabEl) { - var tabEl = Ext.get(this.tabEl); - if (tabIsValid && tabEl.hasClass('validation-error')) { - tabEl.removeClass('validation-error'); - } else if (!tabIsValid && !tabEl.hasClass('validation-error')) { - tabEl.addClass('validation-error'); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-form', TYPO3.Form.Wizard.Viewport.Left.Form); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Attributes.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Attributes.js deleted file mode 100644 index a98943f5110e..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Attributes.js +++ /dev/null @@ -1,26 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Form'); - -/** - * The attributes panel in the accordion of the form tab on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left.Form.Attributes - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Attributes - */ -TYPO3.Form.Wizard.Viewport.Left.Form.Attributes = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Attributes, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-form-attributes' -}); - -Ext.reg('typo3-form-wizard-viewport-left-form-attributes', TYPO3.Form.Wizard.Viewport.Left.Form.Attributes); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Behaviour.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Behaviour.js deleted file mode 100644 index f097e49376c1..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Behaviour.js +++ /dev/null @@ -1,128 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Form'); - -/** - * The behaviour panel in the accordion of the form tab on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left.Form.Behaviour - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Form.Behaviour = Ext.extend(Ext.ux.form.FakeFormPanel, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-form-behaviour', - - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('form_behaviour'), - - /** - * @cfg {String} defaultType - * - * The default xtype of child Components to create in this Container when - * a child item is specified as a raw configuration object, - * rather than as an instantiated Component. - * - * Defaults to 'panel', except Ext.menu.Menu which defaults to 'menuitem', - * and Ext.Toolbar and Ext.ButtonGroup which default to 'button'. - */ - defaultType: 'textfield', - - /** - * @cfg {Object} element - * The form component - */ - element: null, - - /** - * Constructor - * - * @param config - */ - constructor: function(config){ - // Call our superclass constructor to complete construction process. - TYPO3.Form.Wizard.Viewport.Left.Form.Behaviour.superclass.constructor.call(this, config); - }, - - /** - * Constructor - * - * Add the form elements to the tab - */ - initComponent: function() { - var config = { - items: [{ - xtype: 'fieldset', - title: '', - ref: 'fieldset', - autoHeight: true, - border: false, - defaults: { - width: 150, - msgTarget: 'side' - }, - defaultType: 'checkbox', - items: [ - { - fieldLabel: TYPO3.l10n.localize('behaviour_confirmation_page'), - name: 'confirmation', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - } - ] - }] - }; - - // Apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // Call parent - TYPO3.Form.Wizard.Viewport.Left.Form.Behaviour.superclass.initComponent.apply(this, arguments); - - // Fill the form with the configuration values - this.fillForm(); - }, - - - /** - * Store a changed value from the form in the element - * - * @param {Object} field The field which has changed - */ - storeValue: function(field) { - var fieldName = field.getName(); - - var formConfiguration = {}; - formConfiguration[fieldName] = field.getValue(); - - this.element.setConfigurationValue(formConfiguration); - }, - - /** - * Fill the form with the configuration of the element - * - * @return void - */ - fillForm: function() { - this.getForm().setValues(this.element.configuration); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-form-behaviour', TYPO3.Form.Wizard.Viewport.Left.Form.Behaviour); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessor.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessor.js deleted file mode 100644 index 001bdf8d6429..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessor.js +++ /dev/null @@ -1,210 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Form'); - -/** - * The post processor accordion panel in the form options in the left tabpanel - * - * @class TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessor - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessor = Ext.extend(Ext.Panel, { - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('form_postprocessor'), - - /** - * @cfg {Object} validPostProcessors - * Keeps track which post processors are valid. Post processors contain forms which - * do client validation. If there is a validation change in a form in the - * post processor, a validation event will be fired, which changes one of these - * values - */ - validPostProcessors: { - mail: true - }, - - /** - * Constructor - * - * Add the post processors to the accordion - */ - initComponent: function() { - var postProcessors = this.getPostProcessorsBySettings(); - - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - var config = { - items: [{ - xtype: 'typo3-form-wizard-viewport-left-form-postprocessors-dummy', - ref: 'dummy' - }], - tbar: [ - { - xtype: 'combo', - hideLabel: true, - name: 'postprocessor', - ref: 'postprocessor', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'postprocessor', - emptyText: TYPO3.l10n.localize('postprocessor_emptytext'), - width: 150, - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: postProcessors - }), - listeners: { - 'select': { - scope: this, - fn: this.addPostProcessor - } - } - } - ] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessor.superclass.initComponent.apply(this, arguments); - - // Initialize the post processors when they are available for this element - this.initPostProcessors(); - }, - - /** - * Called when constructing the post processor accordion - * - * Checks if the form already has post processors and loads these instead of the dummy - */ - initPostProcessors: function() { - var postProcessors = this.element.configuration.postProcessor; - if (!Ext.isEmptyObject(postProcessors)) { - this.remove(this.dummy); - Ext.iterate(postProcessors, function(key, value) { - var xtype = 'typo3-form-wizard-viewport-left-form-postprocessors-' + key; - if (Ext.ComponentMgr.isRegistered(xtype)) { - this.add({ - xtype: xtype, - element: this.element, - configuration: value, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - } - }, this); - } - }, - - /** - * Add a post processor to the list - * - * @param comboBox - * @param record - * @param index - */ - addPostProcessor: function(comboBox, record, index) { - var postProcessor = comboBox.getValue(); - var xtype = 'typo3-form-wizard-viewport-left-form-postprocessors-' + postProcessor; - - if (!Ext.isEmpty(this.findByType(xtype))) { - Ext.MessageBox.alert(TYPO3.l10n.localize('postprocessor_alert_title'), TYPO3.l10n.localize('postprocessor_alert_description')); - } else { - this.remove(this.dummy); - - this.add({ - xtype: xtype, - element: this.element, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - this.doLayout(); - } - }, - - /** - * Remove a post processor from the list - * - * Shows dummy when there is no post processor for the form - * - * @param component - */ - removePostProcessor: function(component) { - this.remove(component); - this.validation(component.processor, true); - if (this.items.length == 0) { - this.add({ - xtype: 'typo3-form-wizard-viewport-left-form-postprocessors-dummy', - ref: 'dummy' - }); - } - this.doLayout(); - }, - - getPostProcessorsBySettings: function() { - var postProcessors = []; - - var allowedPostProcessors = []; - try { - allowedPostProcessors = TYPO3.Form.Wizard.Settings.defaults.tabs.form.accordions.postProcessor.showPostProcessors.split(/[, ]+/); - } catch (error) { - // The object has not been found - allowedPostProcessors = [ - 'mail' - ]; - } - - Ext.iterate(allowedPostProcessors, function(item, index, allItems) { - postProcessors.push({label: TYPO3.l10n.localize('postprocessor_' + item), value: item}); - }, this); - - return postProcessors; - }, - - /** - * Called by the validation listeners of the post processors - * - * Checks if all post processors are valid. If not, adds a class to the accordion - * - * @param {String} postProcessor The post processor which fires the event - * @param {Boolean} isValid Post processor is valid or not - */ - validation: function(postProcessor, isValid) { - this.validPostProcessors[postProcessor] = isValid; - var accordionIsValid = true; - Ext.iterate(this.validPostProcessors, function(key, value) { - if (!value) { - accordionIsValid = false; - } - }, this); - if (this.el) { - if (accordionIsValid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', 'postProcessor', accordionIsValid); - } else if (!accordionIsValid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', 'postProcessor', accordionIsValid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-form-postprocessor', TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessor); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Dummy.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Dummy.js deleted file mode 100644 index e5e00b3426ce..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Dummy.js +++ /dev/null @@ -1,52 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors'); - -/** - * The dummy item when no post processor is defined for the form - * - * @class TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Dummy - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Dummy = Ext.extend(Ext.Panel, { - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {Number/String} padding - * A shortcut for setting a padding style on the body element. The value can - * either be a number to be applied to all sides, or a normal css string - * describing padding. - */ - padding: 0, - - cls: 'formwizard-left-dummy typo3-message message-information', - - /** - * @cfg {Mixed} data - * The initial set of data to apply to the tpl to update the content area of - * the Component. - */ - data: [{ - title: TYPO3.l10n.localize('postprocessor_dummy_title'), - description: TYPO3.l10n.localize('postprocessor_dummy_description') - }], - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<tpl for=".">', - '<p><strong>{title}</strong></p>', - '<p>{description}</p>', - '</tpl>' - ) -}); - -Ext.reg('typo3-form-wizard-viewport-left-form-postprocessors-dummy', TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Dummy); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Mail.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Mail.js deleted file mode 100644 index 614b3089c006..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Mail.js +++ /dev/null @@ -1,34 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors'); - -/** - * The mail post processor - * - * @class TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Mail - * @extends TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.PostProcessor - */ -TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Mail = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.PostProcessor, { - /** - * @cfg {String} processor - * - * The name of this processor - */ - processor: 'mail', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - recipientEmail: '', - senderEmail: '' - } - }); - TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Mail.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-form-postprocessors-mail', TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Mail); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/PostProcessor.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/PostProcessor.js deleted file mode 100644 index 699b27a418f2..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/PostProcessor.js +++ /dev/null @@ -1,297 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors'); - -/** - * The post processor abstract - * - * @class TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.PostProcessor - * @extends Ext.FormPanel - */ -TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.PostProcessor = Ext.extend(Ext.ux.form.FakeFormPanel, { - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {Number/String} padding - * A shortcut for setting a padding style on the body element. The value can - * either be a number to be applied to all sides, or a normal css string - * describing padding. - */ - padding: 0, - - /** - * @cfg {String} defaultType - * - * The default xtype of child Components to create in this Container when - * a child item is specified as a raw configuration object, - * rather than as an instantiated Component. - * - * Defaults to 'panel', except Ext.menu.Menu which defaults to 'menuitem', - * and Ext.Toolbar and Ext.ButtonGroup which default to 'button'. - */ - defaultType: 'textfield', - - /** - * @cfg {String} processor - * - * The name of this processor - */ - processor: '', - - /** - * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and - * regularly fires the clientvalidation event passing that state. - * When monitoring valid state, the FormPanel enables/disables any of its configured - * buttons which have been configured with formBind: true depending - * on whether the form is valid or not. Defaults to false - */ - monitorValid: true, - - /** - * Constructor - */ - initComponent: function() { - var fields = this.getFieldsBySettings(); - var formItems = new Array(); - - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - Ext.iterate(fields, function(item, index, allItems) { - switch(item) { - case 'recipientEmail': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('postprocessor_properties_recipientemail'), - name: 'recipientEmail', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'senderEmail': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('postprocessor_properties_senderemail'), - name: 'senderEmail', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'subject': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('postprocessor_properties_subject'), - name: 'subject', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'destination': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('postprocessor_properties_destination'), - name: 'destination', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - } - }, this); - - formItems.push({ - xtype: 'button', - text: TYPO3.l10n.localize('button_remove'), - handler: this.removePostProcessor, - scope: this - }); - - var config = { - items: [ - { - xtype: 'fieldset', - title: TYPO3.l10n.localize('postprocessor_' + this.processor), - autoHeight: true, - defaults: { - width: 128, - msgTarget: 'side' - }, - defaultType: 'textfieldsubmit', - items: formItems - } - ] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.PostProcessor.superclass.initComponent.apply(this, arguments); - - // Initialize clientvalidation event - this.on('clientvalidation', this.validation, this); - - // Strange, but we need to call doLayout() after render - this.on('afterrender', this.newOrExistingPostProcessor, this); - }, - - /** - * Decide whether this is a new or an existing one - * - * If new, the default configuration has to be added to the processors - * of the form, otherwise we can fill the form with the existing configuration - */ - newOrExistingPostProcessor: function() { - this.doLayout(); - // Existing processor - if (this.element.configuration.postProcessor[this.processor]) { - this.fillForm(); - // New processor - } else { - this.addProcessorToElement(); - } - }, - - /** - * Fill the form with the configuration of the element - * - * When filling, the events of all form elements should be suspended, - * otherwise the values are written back to the element, for instance on a - * check event on a checkbox. - */ - fillForm: function() { - this.suspendEventsBeforeFilling(); - this.getForm().setValues(this.element.configuration.postProcessor[this.processor]); - this.resumeEventsAfterFilling(); - }, - - /** - * Suspend the events on all items within this component - */ - suspendEventsBeforeFilling: function() { - this.cascade(function(item) { - item.suspendEvents(); - }); - }, - - /** - * Resume the events on all items within this component - */ - resumeEventsAfterFilling: function() { - this.cascade(function(item) { - item.resumeEvents(); - }); - }, - - /** - * Add this processor to the element - */ - addProcessorToElement: function() { - var formConfiguration = {postProcessor: {}}; - formConfiguration.postProcessor[this.processor] = this.configuration; - - this.element.setConfigurationValue(formConfiguration); - - this.fillForm(); - }, - - /** - * Store a changed value from the form in the element - * - * @param {Object} field The field which has changed - */ - storeValue: function(field) { - if (field.isValid()) { - var fieldName = field.getName(); - - var formConfiguration = {postProcessor: {}}; - formConfiguration.postProcessor[this.processor] = {}; - formConfiguration.postProcessor[this.processor][fieldName] = field.getValue(); - - this.element.setConfigurationValue(formConfiguration); - } - }, - - /** - * Remove the processor - * - * Called when the remove button of this processor has been clicked - */ - removePostProcessor: function() { - this.ownerCt.removePostProcessor(this); - this.element.removePostProcessor(this.processor); - }, - - /** - * Get the fields for the element - * - * Based on the TSconfig general allowed fields - * and the TSconfig allowed fields for this type of element - * - * @returns object - */ - getFieldsBySettings: function() { - var fields = []; - var processorFields = this.configuration; - - var allowedFields = []; - try { - allowedFields = TYPO3.Form.Wizard.Settings.defaults.tabs.form.accordions.postProcessor.postProcessors[this.processor].showProperties.split(/[, ]+/); - } catch (error) { - // The object has not been found or constructed wrong - allowedFields = [ - 'recipientEmail', - 'senderEmail' - ]; - } - - Ext.iterate(allowedFields, function(item, index, allItems) { - fields.push(item); - }, this); - - return fields; - }, - - /** - * Called by the clientvalidation event - * - * Adds or removes the error class if the form is valid or not - * - * @param {Object} formPanel This formpanel - * @param {Boolean} valid True if the client validation is true - */ - validation: function(formPanel, valid) { - if (this.el) { - if (valid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', this.processor, valid); - } else if (!valid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', this.processor, valid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-form-postprocessors-postprocessor', TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.PostProcessor); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Redirect.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Redirect.js deleted file mode 100644 index 5ef78ddc45e0..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/PostProcessors/Redirect.js +++ /dev/null @@ -1,33 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors'); - -/** - * The redirect post processor - * - * @class TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Redirect - * @extends TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.PostProcessor - */ -TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Redirect = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.PostProcessor, { - /** - * @cfg {String} processor - * - * The name of this processor - */ - processor: 'redirect', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - destination: '', - } - }); - TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Redirect.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-form-postprocessors-redirect', TYPO3.Form.Wizard.Viewport.Left.Form.PostProcessors.Redirect); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Prefix.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Prefix.js deleted file mode 100644 index 42ae6975ba42..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Form/Prefix.js +++ /dev/null @@ -1,168 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Form'); - -/** - * The prefix panel in the accordion of the form tab on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left.Form.Prefix - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Form.Prefix = Ext.extend(Ext.ux.form.FakeFormPanel, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-form-prefix', - - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('form_prefix'), - - /** @cfg {String} defaultType - * - * The default xtype of child Components to create in this Container when - * a child item is specified as a raw configuration object, - * rather than as an instantiated Component. - * - * Defaults to 'panel', except Ext.menu.Menu which defaults to 'menuitem', - * and Ext.Toolbar and Ext.ButtonGroup which default to 'button'. - */ - defaultType: 'textfield', - - /** - * @cfg {Object} element - * The form component - */ - element: null, - - /** - * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and - * regularly fires the clientvalidation event passing that state. - * When monitoring valid state, the FormPanel enables/disables any of its configured - * buttons which have been configured with formBind: true depending - * on whether the form is valid or not. Defaults to false - */ - monitorValid: true, - - /** - * Constructor - * - * @param config - */ - constructor: function(config){ - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - // Call our superclass constructor to complete construction process. - TYPO3.Form.Wizard.Viewport.Left.Form.Prefix.superclass.constructor.call(this, config); - }, - - /** - * Constructor - * - * Add the form elements to the tab - */ - initComponent: function() { - var config = { - items: [{ - xtype: 'fieldset', - title: '', - ref: 'fieldset', - autoHeight: true, - border: false, - defaults: { - width: 150, - msgTarget: 'side' - }, - defaultType: 'textfieldsubmit', - items: [ - { - fieldLabel: TYPO3.l10n.localize('prefix_prefix'), - name: 'prefix', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - } - ] - }] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Form.Prefix.superclass.initComponent.apply(this, arguments); - - // Initialize clientvalidation event - this.on('clientvalidation', this.validation, this); - - // Fill the form with the configuration values - this.fillForm(); - }, - - - /** - * Store a changed value from the form in the element - * - * @param {Object} field The field which has changed - */ - storeValue: function(field) { - if (field.isValid()) { - var fieldName = field.getName(); - - var formConfiguration = {}; - formConfiguration[fieldName] = field.getValue(); - - this.element.setConfigurationValue(formConfiguration); - } - }, - - /** - * Fill the form with the configuration of the element - * - * @param record The current question - * @return void - */ - fillForm: function() { - this.getForm().setValues(this.element.configuration); - }, - - /** - * Called by the clientvalidation event - * - * Adds or removes the error class if the form is valid or not - * - * @param {Object} formPanel This formpanel - * @param {Boolean} valid True if the client validation is true - */ - validation: function(formPanel, valid) { - if (this.el) { - if (valid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', 'prefix', valid); - } else if (!valid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', 'prefix', valid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-form-prefix', TYPO3.Form.Wizard.Viewport.Left.Form.Prefix); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options.js deleted file mode 100644 index 6e51602abec2..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options.js +++ /dev/null @@ -1,170 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.LeftTYPO3.Form.Wizard.Elements'); - -/** - * The options tab on the left side - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Options = Ext.extend(Ext.Panel, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-options', - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'x-tab-panel-body-content', - - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('left_options'), - - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {Number/String} padding - * A shortcut for setting a padding style on the body element. The value can - * either be a number to be applied to all sides, or a normal css string - * describing padding. - */ - padding: 0, - - /** - * @cfg {Object} validAccordions - * Keeps track which accordions are valid. Accordions contain forms which - * do client validation. If there is a validation change in a form in the - * accordion, a validation event will be fired, which changes one of these - * values - */ - validAccordions: { - attributes: true, - filters: true, - label: true, - legend: true, - options: true, - validation: true, - various: true - }, - - /** - * Constructor - * - * Add the form elements to the tab - */ - initComponent: function() { - var config = { - items: [{ - xtype: 'typo3-form-wizard-viewport-left-options-dummy' - }] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.superclass.initComponent.apply(this, arguments); - - // if the active element changes in helper, this should be reflected here - TYPO3.Form.Wizard.Helpers.Element.on('setactive', this.toggleActive, this); - }, - - /** - * Load options form according to element type - * - * This will be called whenever the current element changes - * - * @param component The current element - * @return void - */ - toggleActive: function(component) { - if (component) { - this.removeAll(); - this.add({ - xtype: 'typo3-form-wizard-viewport-left-options-panel', - element: component, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - this.ownerCt.setOptionsTab(); - } else { - this.removeAll(); - this.add({ - xtype: 'typo3-form-wizard-viewport-left-options-dummy' - }); - } - Ext.get(this.tabEl).removeClass('validation-error'); - Ext.iterate(this.validAccordions, function(key, value) { - this.validAccordions[key] = true; - }, this); - this.doLayout(); - }, - - /** - * Checks if a tab is valid by iterating all accordions on validity - * - * @returns {Boolean} - */ - tabIsValid: function() { - var valid = true; - - Ext.iterate(this.validAccordions, function(key, value) { - if (!value) { - valid = false; - } - }, this); - - return valid; - }, - - /** - * Called by the validation listeners of the accordions - * - * Checks if all accordions are valid. If not, adds a class to the tab - * - * @param {String} accordion The accordion which fires the event - * @param {Boolean} isValid Accordion is valid or not - */ - validation: function(accordion, isValid) { - this.validAccordions[accordion] = isValid; - var tabIsValid = this.tabIsValid(); - - if (this.tabEl) { - var tabEl = Ext.get(this.tabEl); - if (tabIsValid && tabEl.hasClass('validation-error')) { - tabEl.removeClass('validation-error'); - } else if (!tabIsValid && !tabEl.hasClass('validation-error')) { - tabEl.addClass('validation-error'); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options', TYPO3.Form.Wizard.Viewport.Left.Options); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Dummy.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Dummy.js deleted file mode 100644 index 9a40ae1b74e9..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Dummy.js +++ /dev/null @@ -1,65 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options'); - -/** - * The options panel for a dummy item - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Dummy - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Dummy = Ext.extend(Ext.Panel, { - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-options-dummy', - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'formwizard-left-dummy typo3-message message-information', - - /** - * @cfg {Mixed} data - * The initial set of data to apply to the tpl to update the content area of - * the Component. - */ - data: [{ - title: TYPO3.l10n.localize('options_dummy_title'), - description: TYPO3.l10n.localize('options_dummy_description') - }], - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<tpl for=".">', - '<p><strong>{title}</strong></p>', - '<p>{description}</p>', - '</tpl>' - ) -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-dummy', TYPO3.Form.Wizard.Viewport.Left.Options.Dummy); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Attributes.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Attributes.js deleted file mode 100644 index a45bb1183d2b..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Attributes.js +++ /dev/null @@ -1,1149 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms'); - -/** - * The attributes properties of the element - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Attributes - * @extends Ext.FormPanel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Attributes = Ext.extend(Ext.ux.form.FakeFormPanel, { - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('options_attributes'), - - /** @cfg {String} defaultType - * - * The default xtype of child Components to create in this Container when - * a child item is specified as a raw configuration object, - * rather than as an instantiated Component. - * - * Defaults to 'panel', except Ext.menu.Menu which defaults to 'menuitem', - * and Ext.Toolbar and Ext.ButtonGroup which default to 'button'. - */ - defaultType: 'textfieldsubmit', - - /** - * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and - * regularly fires the clientvalidation event passing that state. - * When monitoring valid state, the FormPanel enables/disables any of its configured - * buttons which have been configured with formBind: true depending - * on whether the form is valid or not. Defaults to false - */ - monitorValid: true, - - /** - * Constructor - * - * @param config - */ - constructor: function(config){ - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - // Call our superclass constructor to complete construction process. - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Attributes.superclass.constructor.call(this, config); - }, - - /** - * Constructor - * - * Add the form elements to the accordion - */ - initComponent: function() { - var attributes = this.getAttributesBySettings(); - var formItems = new Array(); - - Ext.iterate(attributes, function(item, index, allItems) { - switch(item) { - case 'accept': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_accept'), - name: 'accept', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'accept-charset': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_acceptcharset'), - name: 'accept-charset', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'accesskey': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_accesskey'), - name: 'accesskey', - maxlength: 1, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'action': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_action'), - name: 'action', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'alt': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_alt'), - name: 'alt', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'autocomplete': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_autocomplete'), - name: 'autocomplete', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'autocomplete', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_autocomplete_none'), value: ''}, - {label: TYPO3.l10n.localize('attributes_autocomplete_off'), value: 'off'}, - {label: TYPO3.l10n.localize('attributes_autocomplete_on'), value: 'on'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'autofocus': - formItems.push({ - xtype: 'typo3-form-wizard-valuecheckbox', - fieldLabel: TYPO3.l10n.localize('attributes_autofocus'), - name: 'autofocus', - inputValue: 'autofocus', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'checked': - formItems.push({ - xtype: 'typo3-form-wizard-valuecheckbox', - fieldLabel: TYPO3.l10n.localize('attributes_checked'), - name: 'checked', - inputValue: 'checked', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'class': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_class'), - name: 'class', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'cols': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_cols'), - name: 'cols', - xtype: 'spinnerfield', - allowBlank: false, - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'contenteditable': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_contenteditable'), - name: 'contenteditable', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'contenteditable', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_contenteditable_none'), value: ''}, - {label: TYPO3.l10n.localize('attributes_contenteditable_true'), value: 'true'}, - {label: TYPO3.l10n.localize('attributes_contenteditable_false'), value: 'false'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'contextmenu': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_contextmenu'), - name: 'contextmenu', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'dir': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_dir'), - name: 'dir', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'dir', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_dir_ltr'), value: 'ltr'}, - {label: TYPO3.l10n.localize('attributes_dir_rtl'), value: 'rtl'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'disabled': - formItems.push({ - xtype: 'typo3-form-wizard-valuecheckbox', - fieldLabel: TYPO3.l10n.localize('attributes_disabled'), - name: 'disabled', - inputValue: 'disabled', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'draggable': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_draggable'), - name: 'draggable', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'draggable', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_draggable_none'), value: ''}, - {label: TYPO3.l10n.localize('attributes_draggable_false'), value: 'false'}, - {label: TYPO3.l10n.localize('attributes_draggable_true'), value: 'true'}, - {label: TYPO3.l10n.localize('attributes_draggable_auto'), value: 'auto'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'dropzone': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_dropzone'), - name: 'dropzone', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'enctype': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_enctype'), - name: 'enctype', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'enctype', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_enctype_1'), value: 'application/x-www-form-urlencoded'}, - {label: TYPO3.l10n.localize('attributes_enctype_2'), value: 'multipart/form-data'}, - {label: TYPO3.l10n.localize('attributes_enctype_3'), value: 'text/plain'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'height': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_height'), - name: 'height', - xtype: 'spinnerfield', - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'hidden': - formItems.push({ - xtype: 'typo3-form-wizard-valuecheckbox', - fieldLabel: TYPO3.l10n.localize('attributes_hidden'), - name: 'hidden', - inputValue: 'hidden', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'id': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_id'), - name: 'id', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'inputmode': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_inputmode'), - name: 'inputmode', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'inputmode', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_inputmode_none'), value: ''}, - {label: TYPO3.l10n.localize('attributes_inputmode_verbatim'), value: 'verbatim'}, - {label: TYPO3.l10n.localize('attributes_inputmode_latin'), value: 'latin'}, - {label: TYPO3.l10n.localize('attributes_inputmode_latin-name'), value: 'latin-name'}, - {label: TYPO3.l10n.localize('attributes_inputmode_latin-prose'), value: 'latin-prose'}, - {label: TYPO3.l10n.localize('attributes_inputmode_full-width-latin'), value: 'full-width-latin'}, - {label: TYPO3.l10n.localize('attributes_inputmode_kana'), value: 'kana'}, - {label: TYPO3.l10n.localize('attributes_inputmode_kana-name'), value: 'kana-name'}, - {label: TYPO3.l10n.localize('attributes_inputmode_katakana'), value: 'katakana'}, - {label: TYPO3.l10n.localize('attributes_inputmode_numeric'), value: 'numeric'}, - {label: TYPO3.l10n.localize('attributes_inputmode_tel'), value: 'tel'}, - {label: TYPO3.l10n.localize('attributes_inputmode_email'), value: 'email'}, - {label: TYPO3.l10n.localize('attributes_inputmode_url'), value: 'url'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'label': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_label'), - name: 'label', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'lang': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_lang'), - name: 'lang', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'list': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_list'), - name: 'list', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'max': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_max'), - name: 'max', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'maxlength': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_maxlength'), - name: 'maxlength', - xtype: 'spinnerfield', - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'method': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_method'), - name: 'method', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'method', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_method_get'), value: 'get'}, - {label: TYPO3.l10n.localize('attributes_method_post'), value: 'post'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'min': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_min'), - name: 'min', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'minlength': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_minlength'), - name: 'minlength', - xtype: 'spinnerfield', - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'multiple': - formItems.push({ - xtype: 'typo3-form-wizard-valuecheckbox', - fieldLabel: TYPO3.l10n.localize('attributes_multiple'), - name: 'multiple', - inputValue: 'multiple', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'name': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_name'), - name: 'name', - allowBlank:false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'novalidate': - formItems.push({ - xtype: 'typo3-form-wizard-valuecheckbox', - fieldLabel: TYPO3.l10n.localize('attributes_novalidate'), - name: 'novalidate', - inputValue: 'novalidate', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'pattern': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_pattern'), - name: 'pattern', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'placeholder': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_placeholder'), - name: 'placeholder', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'readonly': - formItems.push({ - xtype: 'typo3-form-wizard-valuecheckbox', - fieldLabel: TYPO3.l10n.localize('attributes_readonly'), - name: 'readonly', - inputValue: 'readonly', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'required': - formItems.push({ - xtype: 'typo3-form-wizard-valuecheckbox', - fieldLabel: TYPO3.l10n.localize('attributes_required'), - name: 'required', - inputValue: 'required', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'rows': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_rows'), - name: 'rows', - xtype: 'spinnerfield', - allowBlank: false, - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'selected': - formItems.push({ - xtype: 'typo3-form-wizard-valuecheckbox', - fieldLabel: TYPO3.l10n.localize('attributes_selected'), - name: 'selected', - inputValue: 'selected', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'selectionDirection': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_selectionDirection'), - name: 'selectionDirection', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'selectionDirection', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_selectionDirection_none'), value: ''}, - {label: TYPO3.l10n.localize('attributes_selectionDirection_forward'), value: 'forward'}, - {label: TYPO3.l10n.localize('attributes_selectionDirection_backward'), value: 'backward'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'selectionEnd': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_selectionEnd'), - name: 'selectionEnd', - xtype: 'spinnerfield', - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'selectionStart': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_selectionStart'), - name: 'selectionStart', - xtype: 'spinnerfield', - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'size': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_size'), - name: 'size', - xtype: 'spinnerfield', - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'spellcheck': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_spellcheck'), - name: 'spellcheck', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'spellcheck', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_spellcheck_none'), value: ''}, - {label: TYPO3.l10n.localize('attributes_spellcheck_true'), value: 'true'}, - {label: TYPO3.l10n.localize('attributes_spellcheck_false'), value: 'false'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'src': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_src'), - name: 'src', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'step': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_step'), - name: 'step', - xtype: 'spinnerfield', - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'style': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_style'), - name: 'style', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'tabindex': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_tabindex'), - name: 'tabindex', - xtype: 'spinnerfield', - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'text': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_text'), - xtype: 'textarea', - name: 'text', - allowBlank: true, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'title': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_title'), - name: 'title', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'translate': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_translate'), - name: 'translate', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'translate', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_translate_none'), value: ''}, - {label: TYPO3.l10n.localize('attributes_translate_no'), value: 'no'}, - {label: TYPO3.l10n.localize('attributes_translate_yes'), value: 'yes'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'type': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_type'), - name: 'type', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'type', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_type_color'), value: 'color'}, - {label: TYPO3.l10n.localize('attributes_type_date'), value: 'date'}, - {label: TYPO3.l10n.localize('attributes_type_datetime'), value: 'datetime'}, - {label: TYPO3.l10n.localize('attributes_type_datetime-local'), value: 'datetime-local'}, - {label: TYPO3.l10n.localize('attributes_type_email'), value: 'email'}, - {label: TYPO3.l10n.localize('attributes_type_month'), value: 'month'}, - {label: TYPO3.l10n.localize('attributes_type_number'), value: 'number'}, - {label: TYPO3.l10n.localize('attributes_type_search'), value: 'search'}, - {label: TYPO3.l10n.localize('attributes_type_tel'), value: 'tel'}, - {label: TYPO3.l10n.localize('attributes_type_text'), value: 'text'}, - {label: TYPO3.l10n.localize('attributes_type_time'), value: 'time'}, - {label: TYPO3.l10n.localize('attributes_type_url'), value: 'url'}, - {label: TYPO3.l10n.localize('attributes_type_week'), value: 'week'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'value': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_value'), - name: 'value', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'width': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_width'), - name: 'width', - xtype: 'spinnerfield', - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'wrap': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('attributes_wrap'), - name: 'wrap', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'wrap', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('attributes_wrap_none'), value: ''}, - {label: TYPO3.l10n.localize('attributes_wrap_soft'), value: 'soft'}, - {label: TYPO3.l10n.localize('attributes_wrap_hard'), value: 'hard'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - } - }, this); - - var config = { - items: [{ - xtype: 'fieldset', - title: '', - autoHeight: true, - border: false, - defaults: { - width: 150, - msgTarget: 'side' - }, - defaultType: 'textfieldsubmit', - items: formItems - }] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Attributes.superclass.initComponent.apply(this, arguments); - - // Initialize clientvalidation event - this.on('clientvalidation', this.validation, this); - - // Fill the form with the configuration values - this.fillForm(); - }, - - /** - * Store a changed value from the form in the element - * - * @param {Object} field The field which has changed - */ - storeValue: function(field) { - if (field.isValid()) { - var fieldName = field.getName(); - - var formConfiguration = {attributes: {}}; - formConfiguration.attributes[fieldName] = field.getValue(); - - this.element.setConfigurationValue(formConfiguration); - } - }, - - /** - * Fill the form with the configuration of the element - * - * @return void - */ - fillForm: function() { - this.getForm().setValues(this.element.configuration.attributes); - }, - - /** - * Get the attributes for the element - * - * Based on the elements attributes, the TSconfig general allowed attributes - * and the TSconfig allowed attributes for this type of element - * - * @returns object - */ - getAttributesBySettings: function() { - var attributes = []; - var elementAttributes = this.element.configuration.attributes; - var elementType = this.element.xtype.split('-').pop(); - - var allowedGeneralAttributes = []; - try { - allowedGeneralAttributes = TYPO3.Form.Wizard.Settings.defaults.tabs.options.accordions.attributes.showProperties.split(/[, ]+/); - } catch (error) { - // The object has not been found or constructed wrong - allowedGeneralAttributes = [ - 'accept', - 'acceptcharset', - 'accesskey', - 'action', - 'alt', - 'checked', - 'class', - 'cols', - 'dir', - 'disabled', - 'enctype', - 'id', - 'label', - 'lang', - 'maxlength', - 'method', - 'multiple', - 'name', - 'readonly', - 'rows', - 'selected', - 'size', - 'src', - 'style', - 'tabindex', - 'title', - 'type', - 'value' - ]; - } - - var allowedElementAttributes = []; - try { - allowedElementAttributes = TYPO3.Form.Wizard.Settings.elements[elementType].accordions.attributes.showProperties.split(/[, ]+/); - } catch (error) { - // The object has not been found - allowedElementAttributes = allowedGeneralAttributes; - } - - Ext.iterate(allowedElementAttributes, function(item, index, allItems) { - if (allowedGeneralAttributes.indexOf(item) > -1 && Ext.isDefined(elementAttributes[item])) { - attributes.push(item); - } - }, this); - - return attributes; - }, - - /** - * Called by the clientvalidation event - * - * Adds or removes the error class if the form is valid or not - * - * @param {Object} formPanel This formpanel - * @param {Boolean} valid True if the client validation is true - */ - validation: function(formPanel, valid) { - if (this.el) { - if (valid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', 'attributes', valid); - } else if (!valid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', 'attributes', valid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-attributes', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Attributes); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters.js deleted file mode 100644 index 0b835f396245..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters.js +++ /dev/null @@ -1,243 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms'); - -/** - * The filters accordion panel in the element options in the left tabpanel - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters = Ext.extend(Ext.Panel, { - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('options_filters'), - - /** - * @cfg {Object} validFilters - * Keeps track which filters are valid. Filters contain forms which - * do client validation. If there is a validation change in a form in the - * filter, a validation event will be fired, which changes one of these - * values - */ - validFilters: { - alphabetic: true, - alphanumeric: true, - currency: true, - digit: true, - integer: true, - lowercase: true, - regexp: true, - stripnewlines: true, - titlecase: true, - trim: true, - uppercase: true - }, - - /** - * Constructor - * - * Add the form elements to the accordion - */ - initComponent: function() { - var filters = this.getFiltersBySettings(); - - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - var config = { - items: [{ - xtype: 'typo3-form-wizard-viewport-left-options-forms-filters-dummy', - ref: 'dummy' - }], - tbar: [ - { - xtype: 'combo', - hideLabel: true, - name: 'filters', - ref: 'filters', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'filters', - emptyText: TYPO3.l10n.localize('filters_emptytext'), - width: 150, - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: filters - }), - listeners: { - 'select': { - scope: this, - fn: this.addFilter - } - } - } - ] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.superclass.initComponent.apply(this, arguments); - - // Initialize the filters when they are available for this element - this.initFilters(); - }, - - /** - * Called when constructing the filters accordion - * - * Checks if the element already has filters and loads these instead of the dummy - */ - initFilters: function() { - var filters = this.element.configuration.filters; - if (!Ext.isEmptyObject(filters)) { - this.remove(this.dummy); - Ext.iterate(filters, function(key, value) { - this.add({ - xtype: 'typo3-form-wizard-viewport-left-options-forms-filters-' + key, - element: this.element, - configuration: value, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - }, this); - } - }, - - /** - * Add a filter to the filters list - * - * @param comboBox - * @param record - * @param index - */ - addFilter: function(comboBox, record, index) { - var filter = comboBox.getValue(); - var xtype = 'typo3-form-wizard-viewport-left-options-forms-filters-' + filter; - - if (!Ext.isEmpty(this.findByType(xtype))) { - Ext.MessageBox.alert(TYPO3.l10n.localize('filters_alert_title'), TYPO3.l10n.localize('filters_alert_description')); - } else { - this.remove(this.dummy); - - this.add({ - xtype: xtype, - element: this.element, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - this.doLayout(); - } - }, - - /** - * Remove a filter from the filters list - * - * Shows dummy when there is no filter for this element - * - * @param component - */ - removeFilter: function(component) { - this.remove(component); - this.validation(component.filter, true); - if (this.items.length == 0) { - this.add({ - xtype: 'typo3-form-wizard-viewport-left-options-forms-filters-dummy', - ref: 'dummy' - }); - } - this.doLayout(); - }, - - /** - * Get the allowed filters by the TSconfig settings - * - * @returns {Array} - */ - getFiltersBySettings: function() { - var filters = []; - var elementType = this.element.xtype.split('-').pop(); - - var allowedDefaultFilters = []; - try { - allowedDefaultFilters = TYPO3.Form.Wizard.Settings.defaults.tabs.options.accordions.filtering.showFilters.split(/[, ]+/); - } catch (error) { - // The object has not been found - allowedDefaultFilters = [ - 'alphabetic', - 'alphanumeric', - 'currency', - 'digit', - 'integer', - 'lowercase', - 'regexp', - 'stripnewlines', - 'titlecase', - 'trim', - 'uppercase' - ]; - } - - var allowedElementFilters = []; - try { - allowedElementFilters = TYPO3.Form.Wizard.Settings.elements[elementType].accordions.filtering.showFilters.split(/[, ]+/); - } catch (error) { - // The object has not been found or constructed wrong - allowedElementFilters = allowedDefaultFilters; - } - - Ext.iterate(allowedElementFilters, function(item, index, allItems) { - if (allowedDefaultFilters.indexOf(item) > -1) { - filters.push({label: TYPO3.l10n.localize('filters_' + item), value: item}); - } - }, this); - - return filters; - }, - - /** - * Called by the validation listeners of the filters - * - * Checks if all filters are valid. If not, adds a class to the accordion - * - * @param {String} filter The filter which fires the event - * @param {Boolean} isValid Rule is valid or not - */ - validation: function(filter, isValid) { - this.validFilters[filter] = isValid; - var accordionIsValid = true; - Ext.iterate(this.validFilters, function(key, value) { - if (!value) { - accordionIsValid = false; - } - }, this); - if (this.el) { - if (accordionIsValid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', 'filters', isValid); - } else if (!accordionIsValid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', 'filters', isValid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Alphabetic.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Alphabetic.js deleted file mode 100644 index ae12e8e3480b..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Alphabetic.js +++ /dev/null @@ -1,33 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The alphabetic filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Alphabetic - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Alphabetic = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'alphabetic', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - allowWhiteSpace: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Alphabetic.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-alphabetic', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Alphabetic); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Alphanumeric.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Alphanumeric.js deleted file mode 100644 index 1929ac1addf5..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Alphanumeric.js +++ /dev/null @@ -1,33 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The alphanumeric filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Alphanumeric - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Alphanumeric = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'alphanumeric', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - allowWhiteSpace: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Alphanumeric.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-alphanumeric', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Alphanumeric); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Currency.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Currency.js deleted file mode 100644 index 3878dc6738ff..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Currency.js +++ /dev/null @@ -1,34 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The currency filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Currency - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Currency = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'currency', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - decimalPoint: '.', - thousandSeparator: ',' - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Currency.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-currency', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Currency); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Digit.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Digit.js deleted file mode 100644 index 4fd4c4804d45..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Digit.js +++ /dev/null @@ -1,18 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The digit filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Digit - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Digit = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'digit' -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-digit', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Digit); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Dummy.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Dummy.js deleted file mode 100644 index f8b71a3f8126..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Dummy.js +++ /dev/null @@ -1,58 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The dummy item when no filter is defined for an element - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Dummy - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Dummy = Ext.extend(Ext.Panel, { - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {Number/String} padding - * A shortcut for setting a padding style on the body element. The value can - * either be a number to be applied to all sides, or a normal css string - * describing padding. - */ - padding: 0, - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'formwizard-left-dummy typo3-message message-information', - - /** - * @cfg {Mixed} data - * The initial set of data to apply to the tpl to update the content area of - * the Component. - */ - data: [{ - title: TYPO3.l10n.localize('filters_dummy_title'), - description: TYPO3.l10n.localize('filters_dummy_description') - }], - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<tpl for=".">', - '<p><strong>{title}</strong></p>', - '<p>{description}</p>', - '</tpl>' - ) -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-dummy', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Dummy); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Filter.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Filter.js deleted file mode 100644 index 3592489c6c1b..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Filter.js +++ /dev/null @@ -1,345 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The filter abstract - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - * @extends Ext.FormPanel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter = Ext.extend(Ext.ux.form.FakeFormPanel, { - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {Number/String} padding - * A shortcut for setting a padding style on the body element. The value can - * either be a number to be applied to all sides, or a normal css string - * describing padding. - */ - padding: 0, - - /** - * @cfg {String} defaultType - * - * The default xtype of child Components to create in this Container when - * a child item is specified as a raw configuration object, - * rather than as an instantiated Component. - * - * Defaults to 'panel', except Ext.menu.Menu which defaults to 'menuitem', - * and Ext.Toolbar and Ext.ButtonGroup which default to 'button'. - */ - defaultType: 'textfield', - - /** - * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and - * regularly fires the clientvalidation event passing that state. - * When monitoring valid state, the FormPanel enables/disables any of its configured - * buttons which have been configured with formBind: true depending - * on whether the form is valid or not. Defaults to false - */ - monitorValid: true, - - /** - * @cfg {Object} Default filter configuration - */ - configuration: {}, - - /** - * Constructor - */ - initComponent: function() { - var fields = this.getFieldsBySettings(); - var formItems = new Array(); - - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - Ext.iterate(fields, function(item, index, allItems) { - switch(item) { - case 'allowWhiteSpace': - formItems.push({ - xtype: 'checkbox', - fieldLabel: TYPO3.l10n.localize('filters_properties_allowwhitespace'), - name: 'allowWhiteSpace', - inputValue: '1', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'decimalPoint': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('filters_properties_decimalpoint'), - name: 'decimalPoint', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'thousandSeparator': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('filters_properties_thousandseparator'), - name: 'thousandSeparator', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'expression': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('filters_properties_expression'), - name: 'expression', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'characterList': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('filters_properties_characterlist'), - name: 'characterList', - allowBlank: true, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - } - }, this); - - if (Ext.isEmpty(formItems)) { - formItems.push({ - xtype: 'box', - autoEl: { - tag: 'div' - }, - width: 256, - cls: 'typo3-message message-information', - data: [{ - title: TYPO3.l10n.localize('filters_properties_none_title'), - description: TYPO3.l10n.localize('filters_properties_none') - }], - tpl: new Ext.XTemplate( - '<tpl for=".">', - '<p><strong>{title}</strong></p>', - '<p>{description}</p>', - '</tpl>' - ) - - }); - } - - formItems.push({ - xtype: 'button', - text: TYPO3.l10n.localize('button_remove'), - handler: this.removeFilter, - scope: this - }); - - var config = { - items: [ - { - xtype: 'fieldset', - title: this.filter, - autoHeight: true, - defaults: { - width: 128, - msgTarget: 'side' - }, - defaultType: 'textfieldsubmit', - items: formItems - } - ] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter.superclass.initComponent.apply(this, arguments); - - // Initialize clientvalidation event - this.on('clientvalidation', this.validation, this); - - // Strange, but we need to call doLayout() after render - this.on('afterrender', this.newOrExistingFilter, this); - }, - - /** - * Decide whether this is a new or an existing one - * - * If new, the default configuration has to be added to the filters - * of the element, otherwise we can fill the form with the existing configuration - */ - newOrExistingFilter: function() { - this.doLayout(); - // Existing filter - if (this.element.configuration.filters[this.filter]) { - this.fillForm(); - // New filter - } else { - this.addFilterToElement(); - } - }, - - /** - * Fill the form with the configuration of the element - * - * When filling, the events of all form elements should be suspended, - * otherwise the values are written back to the element, for instance on a - * check event on a checkbox. - */ - fillForm: function() { - this.suspendEventsBeforeFilling(); - this.getForm().setValues(this.element.configuration.filters[this.filter]); - this.resumeEventsAfterFilling(); - }, - - /** - * Suspend the events on all items within this component - */ - suspendEventsBeforeFilling: function() { - this.cascade(function(item) { - item.suspendEvents(); - }); - }, - - /** - * Resume the events on all items within this component - */ - resumeEventsAfterFilling: function() { - this.cascade(function(item) { - item.resumeEvents(); - }); - }, - - /** - * Add this filter to the element - */ - addFilterToElement: function() { - var formConfiguration = {filters: {}}; - formConfiguration.filters[this.filter] = this.configuration; - - this.element.setConfigurationValue(formConfiguration); - - this.fillForm(); - }, - - /** - * Store a changed value from the form in the element - * - * @param {Object} field The field which has changed - */ - storeValue: function(field) { - if (field.isValid()) { - var fieldName = field.getName(); - - var formConfiguration = {filters: {}}; - formConfiguration.filters[this.filter] = {}; - formConfiguration.filters[this.filter][fieldName] = field.getValue(); - - this.element.setConfigurationValue(formConfiguration); - } - }, - - /** - * Remove the filter - * - * Called when the remove button of this filter has been clicked - */ - removeFilter: function() { - this.ownerCt.removeFilter(this); - this.element.removeFilter(this.filter); - }, - - /** - * Get the fields for the element - * - * Based on the TSconfig general allowed fields - * and the TSconfig allowed fields for this type of element - * - * @returns object - */ - getFieldsBySettings: function() { - var fields = []; - var filterFields = this.configuration; - var elementType = this.element.xtype.split('-').pop(); - - var allowedGeneralFields = []; - try { - allowedGeneralFields = TYPO3.Form.Wizard.Settings.defaults.tabs.options.accordions.filtering.filters[this.filter].showProperties.split(/[, ]+/); - } catch (error) { - // The object has not been found or constructed wrong - allowedGeneralFields = [ - 'allowWhiteSpace', - 'decimalPoint', - 'thousandSeparator', - 'expression', - 'characterList' - ]; - } - - var allowedElementFields = []; - try { - allowedElementFields = TYPO3.Form.Wizard.Settings.elements[elementType].accordions.filtering.filters[this.filter].showProperties.split(/[, ]+/); - } catch (error) { - // The object has not been found or constructed wrong - allowedElementFields = allowedGeneralFields; - } - - Ext.iterate(allowedElementFields, function(item, index, allItems) { - if (allowedGeneralFields.indexOf(item) > -1 && Ext.isDefined(filterFields[item])) { - fields.push(item); - } - }, this); - - return fields; - }, - - /** - * Called by the clientvalidation event - * - * Adds or removes the error class if the form is valid or not - * - * @param {Object} formPanel This formpanel - * @param {Boolean} valid True if the client validation is true - */ - validation: function(formPanel, valid) { - if (this.el) { - if (valid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', this.filter, valid); - } else if (!valid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', this.filter, valid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-filter', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Integer.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Integer.js deleted file mode 100644 index a2931b6d6aef..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Integer.js +++ /dev/null @@ -1,18 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The integer filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Integer - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Integer = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'integer' -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-integer', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Integer); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/LowerCase.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/LowerCase.js deleted file mode 100644 index 96f2ac09cbf5..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/LowerCase.js +++ /dev/null @@ -1,18 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The lower case filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.LowerCase - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.LowerCase = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'lowercase' -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-lowercase', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.LowerCase); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/RegExp.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/RegExp.js deleted file mode 100644 index 242fdb6dc33a..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/RegExp.js +++ /dev/null @@ -1,33 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The regular expression filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.RegExp - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.RegExp = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'regexp', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - expression: '' - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.RegExp.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-regexp', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.RegExp); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/StripNewLines.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/StripNewLines.js deleted file mode 100644 index c37ced00a713..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/StripNewLines.js +++ /dev/null @@ -1,18 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The strip new lines filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.StripNewLines - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.StripNewLines = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'stripnewlines' -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-stripnewlines', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.StripNewLines); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/TitleCase.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/TitleCase.js deleted file mode 100644 index df367ce4bb47..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/TitleCase.js +++ /dev/null @@ -1,18 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The title case filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.TitleCase - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.TitleCase = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'titlecase' -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-titlecase', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.TitleCase); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Trim.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Trim.js deleted file mode 100644 index 32c9a4b4c7e9..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/Trim.js +++ /dev/null @@ -1,33 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The trim filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Trim - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Trim = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'trim', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - characterList: '' - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Trim.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-trim', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Trim); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/UpperCase.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/UpperCase.js deleted file mode 100644 index 680b40822d3a..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Filters/UpperCase.js +++ /dev/null @@ -1,18 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters'); - -/** - * The upper case filter - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.UpperCase - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.UpperCase = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.Filter, { - /** - * @cfg {String} filter - * - * The name of this filter - */ - filter: 'uppercase' -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-filters-uppercase', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Filters.UpperCase); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Label.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Label.js deleted file mode 100644 index 2f46d194ec94..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Label.js +++ /dev/null @@ -1,229 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms'); - -/** - * The label properties and the layout of the element - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Label - * @extends Ext.FormPanel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Label = Ext.extend(Ext.ux.form.FakeFormPanel, { - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('options_label'), - - /** @cfg {String} defaultType - * - * The default xtype of child Components to create in this Container when - * a child item is specified as a raw configuration object, - * rather than as an instantiated Component. - * - * Defaults to 'panel', except Ext.menu.Menu which defaults to 'menuitem', - * and Ext.Toolbar and Ext.ButtonGroup which default to 'button'. - */ - defaultType: 'textfield', - - /** - * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and - * regularly fires the clientvalidation event passing that state. - * When monitoring valid state, the FormPanel enables/disables any of its configured - * buttons which have been configured with formBind: true depending - * on whether the form is valid or not. Defaults to false - */ - monitorValid: true, - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'x-panel-accordion', - - /** - * Constructor - * - * Add the form elements to the accordion - */ - initComponent: function() { - var fields = this.getFieldsBySettings(); - var formItems = new Array(); - - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - Ext.iterate(fields, function(item, index, allItems) { - switch(item) { - case 'label': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('label_label'), - name: 'label', - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'layout': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('label_layout'), - name: 'layout', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'layout', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('label_layout_front'), value: 'front'}, - {label: TYPO3.l10n.localize('label_layout_back'), value: 'back'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - default: - } - }, this); - - var config = { - items: [{ - xtype: 'fieldset', - title: '', - border: false, - autoHeight: true, - defaults: { - width: 150, - msgTarget: 'side' - }, - defaultType: 'textfieldsubmit', - items: formItems - }] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Label.superclass.initComponent.apply(this, arguments); - - // Initialize clientvalidation event - this.on('clientvalidation', this.validation, this); - - // Fill the form with the configuration values - this.fillForm(); - }, - - /** - * Store a changed value from the form in the element - * - * @param {Object} field The field which has changed - */ - storeValue: function(field) { - if (field.isValid()) { - var fieldName = field.getName(); - - if (fieldName == 'label') { - var formConfiguration = { - label: { - value: field.getValue() - } - }; - } else { - var formConfiguration = {}; - formConfiguration[fieldName] = field.getValue(); - } - this.element.setConfigurationValue(formConfiguration); - } - }, - - /** - * Fill the form with the configuration of the element - * - * @param record The current question - * @return void - */ - fillForm: function() { - this.getForm().setValues({ - label: this.element.configuration.label.value, - layout: this.element.configuration.layout - }); - }, - - /** - * Get the fields for the element - * - * Based on the TSconfig general allowed fields - * and the TSconfig allowed fields for this type of element - * - * @returns object - */ - getFieldsBySettings: function() { - var fields = []; - var elementType = this.element.xtype.split('-').pop(); - - var allowedGeneralFields = []; - try { - allowedGeneralFields = TYPO3.Form.Wizard.Settings.defaults.tabs.options.accordions.label.showProperties.split(/[, ]+/); - } catch (error) { - // The object has not been found or constructed wrong - allowedGeneralFields = [ - 'label', - 'layout' - ]; - } - - var allowedElementFields = []; - try { - allowedElementFields = TYPO3.Form.Wizard.Settings.elements[elementType].accordions.label.showProperties.split(/[, ]+/); - } catch (error) { - // The object has not been found or constructed wrong - allowedElementFields = allowedGeneralFields; - } - - Ext.iterate(allowedElementFields, function(item, index, allItems) { - if (allowedGeneralFields.indexOf(item) > -1) { - fields.push(item); - } - }, this); - - return fields; - }, - - /** - * Called by the clientvalidation event - * - * Adds or removes the error class if the form is valid or not - * - * @param {Object} formPanel This formpanel - * @param {Boolean} valid True if the client validation is true - */ - validation: function(formPanel, valid) { - if (this.el) { - if (valid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', 'label', valid); - } else if (!valid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', 'label', valid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-label', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Label); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Legend.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Legend.js deleted file mode 100644 index 701e7bb793ee..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Legend.js +++ /dev/null @@ -1,152 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms'); - -/** - * The legend properties of the element - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Legend - * @extends Ext.FormPanel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Legend = Ext.extend(Ext.ux.form.FakeFormPanel, { - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('options_legend'), - - /** @cfg {String} defaultType - * - * The default xtype of child Components to create in this Container when - * a child item is specified as a raw configuration object, - * rather than as an instantiated Component. - * - * Defaults to 'panel', except Ext.menu.Menu which defaults to 'menuitem', - * and Ext.Toolbar and Ext.ButtonGroup which default to 'button'. - */ - defaultType: 'textfield', - - /** - * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and - * regularly fires the clientvalidation event passing that state. - * When monitoring valid state, the FormPanel enables/disables any of its configured - * buttons which have been configured with formBind: true depending - * on whether the form is valid or not. Defaults to false - */ - monitorValid: true, - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'x-panel-accordion', - - /** - * Constructor - * - * Add the form elements to the accordion - */ - initComponent: function() { - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - var config = { - items: [{ - xtype: 'fieldset', - title: '', - autoHeight: true, - border: false, - defaults: { - width: 150, - msgTarget: 'side' - }, - defaultType: 'textfieldsubmit', - items: [ - { - fieldLabel: TYPO3.l10n.localize('legend_legend'), - name: 'legend', - enableKeyEvents: true, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - } - ] - }] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Legend.superclass.initComponent.apply(this, arguments); - - // Initialize clientvalidation event - this.on('clientvalidation', this.validation, this); - - // Fill the form with the configuration values - this.fillForm(); - }, - - /** - * Store a changed value from the form in the element - * - * @param {Object} field The field which has changed - */ - storeValue: function(field) { - if (field.isValid()) { - var fieldName = field.getName(); - - if (fieldName == 'legend') { - var formConfiguration = { - legend: { - value: field.getValue() - } - }; - } else { - var formConfiguration = {}; - formConfiguration[fieldName] = field.getValue(); - } - this.element.setConfigurationValue(formConfiguration); - } - }, - - /** - * Fill the form with the configuration of the element - * - * @param record The current question - * @return void - */ - fillForm: function() { - this.getForm().setValues({ - legend: this.element.configuration.legend.value - }); - }, - - /** - * Called by the clientvalidation event - * - * Adds or removes the error class if the form is valid or not - * - * @param {Object} formPanel This formpanel - * @param {Boolean} valid True if the client validation is true - */ - validation: function(formPanel, valid) { - if (this.el) { - if (valid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', 'legend', valid); - } else if (!valid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', 'legend', valid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-legend', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Legend); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Options.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Options.js deleted file mode 100644 index a4772e717646..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Options.js +++ /dev/null @@ -1,249 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms'); - -/** - * The options properties of the element - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Options - * @extends Ext.FormPanel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Options = Ext.extend(Ext.grid.EditorGridPanel, { - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('options_fieldoptions'), - - /** - * @cfg {String} autoExpandColumn - * The id of a column in this grid that should expand to fill unused space. - * This value specified here can not be 0. - */ - autoExpandColumn: 'text', - - /** - * @cfg {Number/String} padding - * A shortcut for setting a padding style on the body element. The value can - * either be a number to be applied to all sides, or a normal css string - * describing padding. - */ - padding: '10px 0 10px 15px', - - /** - * @cfg {Number} clicksToEdit - * The number of clicks on a cell required to display the cell's editor (defaults to 2). - * Setting this option to 'auto' means that mousedown on the selected cell starts - * editing that cell. - */ - clicksToEdit: 1, - - /** - * @cfg {Object} viewConfig A config object that will be applied to the grid's UI view. Any of - * the config options available for Ext.grid.GridView can be specified here. This option - * is ignored if view is specified. - */ - viewConfig: { - forceFit: true, - emptyText: TYPO3.l10n.localize('fieldoptions_emptytext'), - scrollOffset: 0 - }, - - /** - * Constructor - * - * Configure store and columns for the grid - */ - initComponent: function () { - var optionRecord = Ext.data.Record.create([ - { - name: 'text', - mapping: 'text', - type: 'string' - }, { - name: 'selected', - convert: this.convertSelected, - type: 'bool' - }, { - name: 'value', - convert: this.convertValue, - type: 'string' - } - ]); - - var store = new Ext.data.JsonStore({ - idIndex: 1, - fields: optionRecord, - data: this.element.configuration.options, - autoDestroy: true, - autoSave: true, - listeners: { - 'add': { - scope: this, - fn: this.storeOptions - }, - 'remove': { - scope: this, - fn: this.storeOptions - }, - 'update': { - scope: this, - fn: this.storeOptions - } - } - }); - - var checkColumn = new Ext.ux.grid.SingleSelectCheckColumn({ - id: 'selected', - header: TYPO3.l10n.localize('fieldoptions_selected'), - dataIndex: 'selected', - width: 20 - }); - - var itemDeleter = new Ext.ux.grid.ItemDeleter(); - - var config = { - store: store, - cm: new Ext.grid.ColumnModel({ - defaults: { - sortable: false - }, - columns: [ - { - width: 40, - id: 'data', - header: TYPO3.l10n.localize('fieldoptions_text'), - dataIndex: 'text', - editor: new Ext.ux.form.TextFieldSubmit({ - allowBlank: false, - listeners: { - 'triggerclick': function (field) { - field.gridEditor.record.set('text', field.getValue()); - } - } - }) - }, - checkColumn, - { - width: 40, - id: 'value', - header: TYPO3.l10n.localize('fieldoptions_value'), - dataIndex: 'value', - editor: new Ext.ux.form.TextFieldSubmit({ - allowBlank: true, - listeners: { - 'triggerclick': function (field) { - field.gridEditor.record.set('value', field.getValue()); - } - } - }) - }, - itemDeleter - ] - }), - selModel: itemDeleter, - plugins: [checkColumn], - tbar: [{ - text: TYPO3.l10n.localize('fieldoptions_button_add'), - handler: this.addOption, - scope: this - }] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Options.superclass.initComponent.apply(this, arguments); - }, - - /** - * Adds a new record to the grid - * - * Called when the button to add option in the top bar has been clicked - */ - addOption: function () { - var option = this.store.recordType; - var newOption = new option({ - text: TYPO3.l10n.localize('fieldoptions_new'), - selected: false, - value: TYPO3.l10n.localize('fieldoptions_value') - }); - this.stopEditing(); - this.store.add(newOption); - this.startEditing(0, 0); - }, - - /** - * Stores the options in the element whenever a change has been done to the - * grid, like add, remove or update - * - * @param store - * @param record - */ - storeOptions: function (store, record) { - if (record && record.dirty) { - record.commit(); - } else { - var option = {}; - var options = []; - this.store.each(function (record) { - var option = { - text: record.get('text') - }; - if (record.get('selected')) { - if (!option.attributes) { - option.attributes = {}; - } - option.attributes['selected'] = 'selected'; - } - if (record.get('value')) { - if (!option.attributes) { - option.attributes = {}; - } - option.attributes['value'] = record.get('value'); - } - options.push(option); - }); - this.element.configuration.options = []; - var formConfiguration = { - options: options - }; - this.element.setConfigurationValue(formConfiguration); - } - }, - - /** - * Convert and remap the "selected" attribute. In HTML the attribute needs - * be as selected="selected", while the grid uses a boolean. - * - * @param v - * @param record - * @returns {Boolean} - */ - convertSelected: function (v, record) { - if (record.attributes && record.attributes.selected) { - if (record.attributes.selected == 'selected') { - return true; - } - } - return false; - }, - - /** - * Remap value from different locations - * - * @param v - * @param record - * @returns {string} - */ - convertValue: function (v, record) { - if (record.attributes && record.attributes.value) { - return record.attributes.value; - } else if (record.data) { - return record.data; - } - return ''; - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-options', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Options); diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation.js deleted file mode 100644 index 11618cfc04b9..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation.js +++ /dev/null @@ -1,275 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms'); - -/** - * The validation accordion panel in the element options in the left tabpanel - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation = Ext.extend(Ext.Panel, { - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('options_validation'), - - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-left-options-validation', - - /** - * @cfg {Object} validRules - * Keeps track which rules are valid. Rules contain forms which - * do client validation. If there is a validation change in a form in the - * rule, a validation event will be fired, which changes one of these - * values - */ - validRules: { - alphabetic: true, - alphanumeric: true, - between: true, - date: true, - digit: true, - email: true, - equals: true, - fileallowedtypes: true, - float: true, - greaterthan: true, - inarray: true, - integer: true, - ip: true, - length: true, - lessthan: true, - regexp: true, - required: true, - uri: true - }, - - /** - * Constructor - * - * Add the form elements to the accordion - */ - initComponent: function() { - var rules = this.getRulesBySettings(); - - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - var config = { - items: [{ - xtype: 'typo3-form-wizard-viewport-left-options-forms-validation-dummy', - ref: 'dummy' - }], - tbar: [ - { - xtype: 'combo', - hideLabel: true, - name: 'rules', - ref: 'rules', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'rules', - emptyText: TYPO3.l10n.localize('validation_emptytext'), - width: 150, - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: rules - }), - listeners: { - 'select': { - scope: this, - fn: this.addRule - } - } - } - ] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.superclass.initComponent.apply(this, arguments); - - // Initialize the rules when they are available for this element - this.initRules(); - }, - - /** - * Called when constructing the validation accordion - * - * Checks if the element already has rules and loads these instead of the dummy - */ - initRules: function() { - var rules = this.element.configuration.validation; - if (!Ext.isEmptyObject(rules)) { - this.remove(this.dummy); - Ext.iterate(rules, function(key, value) { - var xtype = 'typo3-form-wizard-viewport-left-options-forms-validation-' + key; - if (Ext.ComponentMgr.isRegistered(xtype)) { - this.add({ - xtype: xtype, - element: this.element, - configuration: value, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - } - }, this); - } - }, - - /** - * Add a rule to the validation list - * - * @param comboBox - * @param record - * @param index - */ - addRule: function(comboBox, record, index) { - var rule = comboBox.getValue(); - var xtype = 'typo3-form-wizard-viewport-left-options-forms-validation-' + rule; - - if (!Ext.isEmpty(this.findByType(xtype))) { - Ext.MessageBox.alert(TYPO3.l10n.localize('validation_alert_title'), TYPO3.l10n.localize('validation_alert_description')); - } else { - this.remove(this.dummy); - - this.add({ - xtype: xtype, - element: this.element, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - this.doLayout(); - } - }, - - /** - * Remove a rule from the validation list - * - * Shows dummy when there is no validation rule for this element - * - * @param component - */ - removeRule: function(component) { - this.remove(component); - this.validation(component.rule, true); - if (this.items.length == 0) { - this.add({ - xtype: 'typo3-form-wizard-viewport-left-options-forms-validation-dummy', - ref: 'dummy' - }); - } - this.doLayout(); - }, - - /** - * Get the rules by the TSconfig settings - * - * @returns {Array} - */ - getRulesBySettings: function() { - var rules = []; - var elementType = this.element.xtype.split('-').pop(); - - var allowedDefaultRules = []; - try { - allowedDefaultRules = TYPO3.Form.Wizard.Settings.defaults.tabs.options.accordions.validation.showRules.split(/[, ]+/); - } catch (error) { - // The object has not been found - allowedDefaultRules = [ - 'alphabetic', - 'alphanumeric', - 'between', - 'date', - 'digit', - 'email', - 'equals', - 'fileallowedtypes', - 'float', - 'greaterthan', - 'inarray', - 'integer', - 'ip', - 'length', - 'lessthan', - 'regexp', - 'required', - 'uri' - ]; - } - - var allowedElementRules = []; - try { - allowedElementRules = TYPO3.Form.Wizard.Settings.elements[elementType].accordions.validation.showRules.split(/[, ]+/); - } catch (error) { - // The object has not been found or constructed wrong - allowedElementRules = allowedDefaultRules; - } - - Ext.iterate(allowedElementRules, function(item, index, allItems) { - if (allowedDefaultRules.indexOf(item) > -1) { - rules.push({label: TYPO3.l10n.localize('validation_' + item), value: item}); - } - }, this); - - return rules; - }, - - /** - * Called by the validation listeners of the rules - * - * Checks if all rules are valid. If not, adds a class to the accordion - * - * @param {String} rule The rule which fires the event - * @param {Boolean} isValid Rule is valid or not - */ - validation: function(rule, isValid) { - this.validRules[rule] = isValid; - var accordionIsValid = true; - Ext.iterate(this.validRules, function(key, value) { - if (!value) { - accordionIsValid = false; - } - }, this); - if (this.el) { - if (accordionIsValid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', 'validation', isValid); - } else if (!accordionIsValid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', 'validation', isValid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Alphabetic.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Alphabetic.js deleted file mode 100644 index eb9fd1a5f507..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Alphabetic.js +++ /dev/null @@ -1,36 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The alphabetic validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Alphabetic - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Alphabetic = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'alphabetic', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_alphabetic.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_alphabetic.error'), - allowWhiteSpace: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Alphabetic.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-alphabetic', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Alphabetic); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Alphanumeric.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Alphanumeric.js deleted file mode 100644 index bbd4cdf0f1f8..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Alphanumeric.js +++ /dev/null @@ -1,36 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The alphanumeric validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Alphanumeric - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Alphanumeric = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'alphanumeric', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_alphanumeric.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_alphanumeric.error'), - allowWhiteSpace: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Alphanumeric.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-alphanumeric', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Alphanumeric); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Between.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Between.js deleted file mode 100644 index bb45fc96b04a..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Between.js +++ /dev/null @@ -1,38 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The between validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Between - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Between = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'between', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_between.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_between.error'), - minimum: 0, - maximum: 0, - inclusive: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Between.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-between', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Between); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Date.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Date.js deleted file mode 100644 index ac222c02fb1c..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Date.js +++ /dev/null @@ -1,36 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The date validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Date - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Date = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'date', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_date.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_date.error'), - format: '%e-%m-%Y' - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Date.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-date', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Date); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Digit.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Digit.js deleted file mode 100644 index 501dafd326ab..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Digit.js +++ /dev/null @@ -1,35 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The digit validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Digit - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Digit = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'digit', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_digit.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_digit.error') - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Digit.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-digit', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Digit); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Dummy.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Dummy.js deleted file mode 100644 index 603b73edfa39..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Dummy.js +++ /dev/null @@ -1,58 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The dummy item when no validation rule is defined for an element - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Dummy - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Dummy = Ext.extend(Ext.Panel, { - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {Number/String} padding - * A shortcut for setting a padding style on the body element. The value can - * either be a number to be applied to all sides, or a normal css string - * describing padding. - */ - padding: 0, - - /** - * @cfg {String} cls - * An optional extra CSS class that will be added to this component's - * Element (defaults to ''). This can be useful for adding customized styles - * to the component or any of its children using standard CSS rules. - */ - cls: 'formwizard-left-dummy typo3-message message-information', - - /** - * @cfg {Mixed} data - * The initial set of data to apply to the tpl to update the content area of - * the Component. - */ - data: [{ - title: TYPO3.l10n.localize('validation_dummy_title'), - description: TYPO3.l10n.localize('validation_dummy_description') - }], - - /** - * @cfg {Mixed} tpl - * An Ext.Template, Ext.XTemplate or an array of strings to form an - * Ext.XTemplate. Used in conjunction with the data and tplWriteMode - * configurations. - */ - tpl: new Ext.XTemplate( - '<tpl for=".">', - '<p><strong>{title}</strong></p>', - '<p>{description}</p>', - '</tpl>' - ) -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-dummy', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Dummy); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Email.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Email.js deleted file mode 100644 index 391e7725d445..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Email.js +++ /dev/null @@ -1,35 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The email validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Email - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Email = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'email', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_email.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_email.error') - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Email.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-email', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Email); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Equals.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Equals.js deleted file mode 100644 index f80f8c1385e3..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Equals.js +++ /dev/null @@ -1,36 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The equals validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Email - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Equals = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'equals', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_equals.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_equals.error'), - field: '' - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Equals.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-equals', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Equals); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileAllowedTypes.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileAllowedTypes.js deleted file mode 100644 index ba7baa124e73..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileAllowedTypes.js +++ /dev/null @@ -1,36 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.FileAllowedTypes'); - -/** - * The allowed file types rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileAllowedTypes - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileAllowedTypes = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'fileallowedtypes', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_fileallowedtypes.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_fileallowedtypes.error'), - types: '' - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileAllowedTypes.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-fileallowedtypes', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileAllowedTypes); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileMaximumSize.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileMaximumSize.js deleted file mode 100644 index 967e7226895c..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileMaximumSize.js +++ /dev/null @@ -1,36 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.FileMaximumSize'); - -/** - * The maximum file size rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileMaximumSize - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileMaximumSize = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'filemaximumsize', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_filemaximumsize.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_filemaximumsize.error'), - maximum: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileMaximumSize.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-filemaximumsize', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileMaximumSize); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileMinimumSize.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileMinimumSize.js deleted file mode 100644 index 5304191023b4..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/FileMinimumSize.js +++ /dev/null @@ -1,36 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.FileMinimumSize'); - -/** - * The minimum file size rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileMinimumSize - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileMinimumSize = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'fileminimumsize', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_fileminimumsize.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_fileminimumsize.error'), - minimum: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileMinimumSize.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-fileminimumsize', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.FileMinimumSize); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Float.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Float.js deleted file mode 100644 index e189f462b8b8..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Float.js +++ /dev/null @@ -1,35 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The float validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Float - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Float = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'float', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_float.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_float.error') - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Float.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-float', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Float); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/GreaterThan.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/GreaterThan.js deleted file mode 100644 index 795a460c40d0..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/GreaterThan.js +++ /dev/null @@ -1,36 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The greater than validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.GreaterThan - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.GreaterThan = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'greaterthan', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_greaterthan.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_greaterthan.error'), - minimum: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.GreaterThan.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-greaterthan', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.GreaterThan); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/InArray.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/InArray.js deleted file mode 100644 index 0f922089db45..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/InArray.js +++ /dev/null @@ -1,37 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The in arrayt validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.InArray - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.InArray = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'inarray', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_inarray.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_inarray.error'), - array: '', - strict: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.InArray.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-inarray', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.InArray); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Integer.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Integer.js deleted file mode 100644 index 5219315f26e2..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Integer.js +++ /dev/null @@ -1,35 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The integer validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Integer - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Integer = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'integer', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_integer.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_integer.error') - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Integer.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-integer', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Integer); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Ip.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Ip.js deleted file mode 100644 index 7e65761eb46f..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Ip.js +++ /dev/null @@ -1,35 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The IP validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Ip - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Ip = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'ip', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_ip.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_ip.error') - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Ip.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-ip', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Ip); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Length.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Length.js deleted file mode 100644 index 7ba06c3ee70c..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Length.js +++ /dev/null @@ -1,37 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The length validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Length - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Length = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'length', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_length.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_length.error'), - minimum: 0, - maximum: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Length.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-length', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Length); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/LessThan.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/LessThan.js deleted file mode 100644 index a3db3b6bc155..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/LessThan.js +++ /dev/null @@ -1,36 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The less than validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.LessThan - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.LessThan = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'lessthan', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_lessthan.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_lessthan.error'), - maximum: 0 - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.LessThan.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-lessthan', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.LessThan); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/RegExp.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/RegExp.js deleted file mode 100644 index 059eda36154e..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/RegExp.js +++ /dev/null @@ -1,36 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The regular expression validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.RegExp - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.RegExp = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'regexp', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_regexp.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_regexp.error'), - expression: '' - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.RegExp.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-regexp', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.RegExp); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Required.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Required.js deleted file mode 100644 index f15fe85c2800..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Required.js +++ /dev/null @@ -1,35 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The required validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Required - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Required = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'required', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_required.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_required.error') - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Required.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-required', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Required); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Rule.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Rule.js deleted file mode 100644 index 7dfc5d061f21..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Rule.js +++ /dev/null @@ -1,436 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The validation rules abstract - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - * @extends Ext.FormPanel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule = Ext.extend(Ext.ux.form.FakeFormPanel, { - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {Number/String} padding - * A shortcut for setting a padding style on the body element. The value can - * either be a number to be applied to all sides, or a normal css string - * describing padding. - */ - padding: 0, - - /** - * @cfg {String} defaultType - * - * The default xtype of child Components to create in this Container when - * a child item is specified as a raw configuration object, - * rather than as an instantiated Component. - * - * Defaults to 'panel', except Ext.menu.Menu which defaults to 'menuitem', - * and Ext.Toolbar and Ext.ButtonGroup which default to 'button'. - */ - defaultType: 'textfield', - - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: '', - - /** - * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and - * regularly fires the clientvalidation event passing that state. - * When monitoring valid state, the FormPanel enables/disables any of its configured - * buttons which have been configured with formBind: true depending - * on whether the form is valid or not. Defaults to false - */ - monitorValid: true, - - /** - * Constructor - */ - initComponent: function() { - var fields = this.getFieldsBySettings(); - var formItems = new Array(); - - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - Ext.iterate(fields, function(item, index, allItems) { - switch(item) { - case 'message': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('validation_properties_message'), - name: 'message', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'error': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('validation_properties_error'), - name: 'error', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'showMessage': - formItems.push({ - xtype: 'checkbox', - fieldLabel: TYPO3.l10n.localize('validation_properties_showmessage'), - name: 'showMessage', - inputValue: '1', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'allowWhiteSpace': - formItems.push({ - xtype: 'checkbox', - fieldLabel: TYPO3.l10n.localize('validation_properties_allowwhitespace'), - name: 'allowWhiteSpace', - inputValue: '1', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'minimum': - formItems.push({ - xtype: 'spinnerfield', - fieldLabel: TYPO3.l10n.localize('validation_properties_minimum'), - name: 'minimum', - minValue: 0, - accelerate: true, - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'maximum': - formItems.push({ - xtype: 'spinnerfield', - fieldLabel: TYPO3.l10n.localize('validation_properties_maximum'), - name: 'maximum', - minValue: 0, - accelerate: true, - listeners: { - 'spin': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'inclusive': - formItems.push({ - xtype: 'checkbox', - fieldLabel: TYPO3.l10n.localize('validation_properties_inclusive'), - name: 'inclusive', - inputValue: '1', - listeners: { - 'check': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'format': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('validation_properties_format'), - name: 'format', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'field': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('validation_properties_field'), - name: 'field', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'array': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('validation_properties_array'), - name: 'array', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'expression': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('validation_properties_expression'), - name: 'expression', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - case 'types': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('validation_properties_types'), - name: 'types', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - } - }, this); - - formItems.push({ - xtype: 'button', - text: TYPO3.l10n.localize('button_remove'), - handler: this.removeRule, - scope: this - }); - - var config = { - items: [ - { - xtype: 'fieldset', - title: TYPO3.l10n.localize('validation_' + this.rule), - autoHeight: true, - defaults: { - width: 128, - msgTarget: 'side' - }, - defaultType: 'textfieldsubmit', - items: formItems - } - ] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule.superclass.initComponent.apply(this, arguments); - - // Initialize clientvalidation event - this.on('clientvalidation', this.validation, this); - - // Strange, but we need to call doLayout() after render - this.on('afterrender', this.newOrExistingRule, this); - }, - - /** - * Decide whether this is a new or an existing one - * - * If new, the default configuration has to be added to the validation rules - * of the element, otherwise we can fill the form with the existing configuration - */ - newOrExistingRule: function() { - this.doLayout(); - // Existing rule - if (this.element.configuration.validation[this.rule]) { - this.fillForm(); - // New rule - } else { - this.addRuleToElement(); - } - }, - - /** - * Fill the form with the configuration of the element - * - * When filling, the events of all form elements should be suspended, - * otherwise the values are written back to the element, for instance on a - * check event on a checkbox. - */ - fillForm: function() { - this.suspendEventsBeforeFilling(); - this.getForm().setValues(this.element.configuration.validation[this.rule]); - this.resumeEventsAfterFilling(); - }, - - /** - * Suspend the events on all items within this component - */ - suspendEventsBeforeFilling: function() { - this.cascade(function(item) { - item.suspendEvents(); - }); - }, - - /** - * Resume the events on all items within this component - */ - resumeEventsAfterFilling: function() { - this.cascade(function(item) { - item.resumeEvents(); - }); - }, - - /** - * Add this rule to the element - */ - addRuleToElement: function() { - var formConfiguration = {validation: {}}; - formConfiguration.validation[this.rule] = this.configuration; - - this.element.setConfigurationValue(formConfiguration); - - this.fillForm(); - }, - - /** - * Store a changed value from the form in the element - * - * @param {Object} field The field which has changed - */ - storeValue: function(field) { - if (field.isValid()) { - var fieldName = field.getName(); - - var formConfiguration = {validation: {}}; - formConfiguration.validation[this.rule] = {}; - formConfiguration.validation[this.rule][fieldName] = field.getValue(); - - this.element.setConfigurationValue(formConfiguration); - } - }, - - /** - * Remove the rule - * - * Called when the remove button of this rule has been clicked - */ - removeRule: function() { - this.ownerCt.removeRule(this); - this.element.removeValidationRule(this.rule); - }, - - /** - * Get the fields for the element - * - * Based on the TSconfig general allowed fields - * and the TSconfig allowed fields for this type of element - * - * @returns object - */ - getFieldsBySettings: function() { - var fields = []; - var ruleFields = this.configuration; - var elementType = this.element.xtype.split('-').pop(); - - var allowedGeneralFields = []; - try { - allowedGeneralFields = TYPO3.Form.Wizard.Settings.defaults.tabs.options.accordions.validation.rules[this.rule].showProperties.split(/[, ]+/); - } catch (error) { - // The object has not been found or constructed wrong - allowedGeneralFields = [ - 'message', - 'error', - 'showMessage', - 'allowWhiteSpace', - 'minimum', - 'maximum', - 'inclusive', - 'format', - 'field', - 'array', - 'strict', - 'expression' - ]; - } - - var allowedElementFields = []; - try { - allowedElementFields = TYPO3.Form.Wizard.Settings.elements[elementType].accordions.validation.rules[this.rule].showProperties.split(/[, ]+/); - } catch (error) { - // The object has not been found or constructed wrong - allowedElementFields = allowedGeneralFields; - } - - Ext.iterate(allowedElementFields, function(item, index, allItems) { - if (allowedGeneralFields.indexOf(item) > -1 && Ext.isDefined(ruleFields[item])) { - fields.push(item); - } - }, this); - - return fields; - }, - - /** - * Called by the clientvalidation event - * - * Adds or removes the error class if the form is valid or not - * - * @param {Object} formPanel This formpanel - * @param {Boolean} valid True if the client validation is true - */ - validation: function(formPanel, valid) { - if (this.el) { - if (valid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', this.rule, valid); - } else if (!valid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', this.rule, valid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-rule', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Uri.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Uri.js deleted file mode 100644 index 5503a70b9acf..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Validation/Uri.js +++ /dev/null @@ -1,35 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation'); - -/** - * The uri validation rule - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Uri - * @extends TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Uri = Ext.extend(TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Rule, { - /** - * @cfg {String} rule - * - * The name of this rule - */ - rule: 'uri', - - /** - * Constructor - * - * Add the configuration object to this component - * @param config - */ - constructor: function(config) { - Ext.apply(this, { - configuration: { - showMessage: 1, - message: TYPO3.l10n.localize('tx_form_system_validate_uri.message'), - error: TYPO3.l10n.localize('tx_form_system_validate_uri.error') - } - }); - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Uri.superclass.constructor.apply(this, arguments); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-validation-uri', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Validation.Uri); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Various.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Various.js deleted file mode 100644 index 499fa4e8566d..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Forms/Various.js +++ /dev/null @@ -1,290 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options.Forms'); - -/** - * The various properties of the element - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Various - * @extends Ext.FormPanel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Various = Ext.extend(Ext.ux.form.FakeFormPanel, { - /** - * @cfg {String} title - * The title text to be used as innerHTML (html tags are accepted) to - * display in the panel header (defaults to ''). - */ - title: TYPO3.l10n.localize('options_various'), - - /** @cfg {String} defaultType - * - * The default xtype of child Components to create in this Container when - * a child item is specified as a raw configuration object, - * rather than as an instantiated Component. - * - * Defaults to 'panel', except Ext.menu.Menu which defaults to 'menuitem', - * and Ext.Toolbar and Ext.ButtonGroup which default to 'button'. - */ - defaultType: 'textfield', - - /** - * @cfg {Boolean} monitorValid If true, the form monitors its valid state client-side and - * regularly fires the clientvalidation event passing that state. - * When monitoring valid state, the FormPanel enables/disables any of its configured - * buttons which have been configured with formBind: true depending - * on whether the form is valid or not. Defaults to false - */ - monitorValid: true, - - /** - * Constructor - * - * Add the form elements to the accordion - */ - initComponent: function() { - var various = this.element.configuration.various; - var formItems = new Array(); - - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - Ext.iterate(various, function(key, value) { - switch(key) { - case 'name': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('various_properties_name'), - name: 'name', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'content': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('various_properties_content'), - xtype: 'textarea', - name: 'content', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'text': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('various_properties_text'), - xtype: 'textarea', - name: 'text', - allowBlank: false, - listeners: { - 'triggerclick': { - scope: this, - fn: this.storeValue - }, - 'blur': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'headingSize': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('various_properties_headingsize'), - name: 'headingSize', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'headingSize', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: 'H1', value: 'h1'}, - {label: 'H2', value: 'h2'}, - {label: 'H3', value: 'h3'}, - {label: 'H4', value: 'h4'}, - {label: 'H5', value: 'h5'} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'prefix': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('various_properties_prefix'), - name: 'prefix', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'prefix', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('yes'), value: true}, - {label: TYPO3.l10n.localize('no'), value: false} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'suffix': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('various_properties_suffix'), - name: 'suffix', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'suffix', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('yes'), value: true}, - {label: TYPO3.l10n.localize('no'), value: false} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - case 'middleName': - formItems.push({ - fieldLabel: TYPO3.l10n.localize('various_properties_middlename'), - name: 'middleName', - xtype: 'combo', - mode: 'local', - triggerAction: 'all', - forceSelection: true, - editable: false, - hiddenName: 'middleName', - displayField: 'label', - valueField: 'value', - store: new Ext.data.JsonStore({ - fields: ['label', 'value'], - data: [ - {label: TYPO3.l10n.localize('yes'), value: true}, - {label: TYPO3.l10n.localize('no'), value: false} - ] - }), - listeners: { - 'select': { - scope: this, - fn: this.storeValue - } - } - }); - break; - } - }, this); - - var config = { - items: [{ - xtype: 'fieldset', - title: '', - autoHeight: true, - border: false, - defaults: { - width: 150, - msgTarget: 'side' - }, - defaultType: 'textfieldsubmit', - items: formItems - }] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Various.superclass.initComponent.apply(this, arguments); - - // Initialize clientvalidation event - this.on('clientvalidation', this.validation, this); - - // Fill the form with the configuration values - this.fillForm(); - }, - - /** - * Store a changed value from the form in the element - * - * @param {Object} field The field which has changed - */ - storeValue: function(field) { - if (field.isValid()) { - var fieldName = field.getName(); - - var formConfiguration = {various: {}}; - formConfiguration.various[fieldName] = field.getValue(); - - this.element.setConfigurationValue(formConfiguration); - } - }, - - /** - * Fill the form with the configuration of the element - * - * @return void - */ - fillForm: function() { - this.getForm().setValues(this.element.configuration.various); - }, - - /** - * Called by the clientvalidation event - * - * Adds or removes the error class if the form is valid or not - * - * @param {Object} formPanel This formpanel - * @param {Boolean} valid True if the client validation is true - */ - validation: function(formPanel, valid) { - if (this.el) { - if (valid && this.el.hasClass('validation-error')) { - this.removeClass('validation-error'); - this.fireEvent('validation', 'various', valid); - } else if (!valid && !this.el.hasClass('validation-error')) { - this.addClass('validation-error'); - this.fireEvent('validation', 'various', valid); - } - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-forms-various', TYPO3.Form.Wizard.Viewport.Left.Options.Forms.Various); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Panel.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Panel.js deleted file mode 100644 index acff06a5a3d0..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Left/Options/Panel.js +++ /dev/null @@ -1,148 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport.Left.Options'); - -/** - * The options panel - * - * @class TYPO3.Form.Wizard.Viewport.Left.Options.Panel - * @extends Ext.Panel - */ -TYPO3.Form.Wizard.Viewport.Left.Options.Panel = Ext.extend(Ext.Panel, { - /** - * @cfg {Object} element - * The element for the options form - */ - element: null, - - /** - * @cfg {Boolean} border - * True to display the borders of the panel's body element, false to hide - * them (defaults to true). By default, the border is a 2px wide inset - * border, but this can be further altered by setting bodyBorder to false. - */ - border: false, - - /** - * @cfg {Object|Function} defaults - * This option is a means of applying default settings to all added items - * whether added through the items config or via the add or insert methods. - */ - defaults: { - autoHeight: true, - border: false, - padding: 0 - }, - - /** - * Constructor - * - * Add the form elements to the tab - */ - initComponent: function() { - var accordions = this.getAccordionsBySettings(); - var accordionItems = new Array(); - - // Adds the specified events to the list of events which this Observable may fire. - this.addEvents({ - 'validation': true - }); - - Ext.iterate(accordions, function(item, index, allItems) { - var accordionXtype = 'typo3-form-wizard-viewport-left-options-forms-' + item; - accordionItems.push({ - xtype: accordionXtype, - element: this.element, - listeners: { - 'validation': { - fn: this.validation, - scope: this - } - } - }); - }, this); - - var config = { - items: [{ - xtype: 'panel', - layout: 'accordion', - ref: 'accordion', - defaults: { - autoHeight: true, - cls: 'x-panel-accordion' - }, - items: accordionItems - }] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Left.Options.Panel.superclass.initComponent.apply(this, arguments); - }, - - /** - * Adds the accordions depending on the TSconfig settings - * - * It will first look at showAccordions for the tab, then it will filter it - * down with the accordions allowed for the element. - * - * @returns {Array} - */ - getAccordionsBySettings: function() { - var accordions = []; - if (this.element) { - var elementType = this.element.xtype.split('-').pop(); - - var allowedDefaultAccordions = []; - try { - allowedDefaultAccordions = TYPO3.Form.Wizard.Settings.defaults.tabs.options.showAccordions.split(/[, ]+/); - } catch (error) { - // The object has not been found - allowedDefaultAccordions = [ - 'legend', - 'label', - 'attributes', - 'options', - 'validation', - 'filters', - 'various' - ]; - } - - var allowedElementAccordions = []; - try { - allowedElementAccordions = TYPO3.Form.Wizard.Settings.elements[elementType].showAccordions.split(/[, ]+/); - } catch (error) { - // The object has not been found - allowedElementAccordions = allowedDefaultAccordions; - } - - Ext.iterate(allowedElementAccordions, function(item, index, allItems) { - var accordionXtype = 'typo3-form-wizard-viewport-left-options-forms-' + item; - if ( - Ext.isDefined(this.element.configuration[item]) && - allowedElementAccordions.indexOf(item) > -1 && - Ext.ComponentMgr.isRegistered(accordionXtype) - ) { - accordions.push(item); - } - }, this); - } - - return accordions; - }, - - /** - * Fire the validation event - * - * This is only a pass-through for the accordion validation events - * - * @param accordion - * @param valid - */ - validation: function(accordion, valid) { - this.fireEvent('validation', accordion, valid); - } -}); - -Ext.reg('typo3-form-wizard-viewport-left-options-panel', TYPO3.Form.Wizard.Viewport.Left.Options.Panel); \ No newline at end of file diff --git a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Right.js b/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Right.js deleted file mode 100644 index 994fee3aec4c..000000000000 --- a/typo3/sysext/form/Resources/Public/JavaScript/Wizard/Viewport/Right.js +++ /dev/null @@ -1,144 +0,0 @@ -Ext.namespace('TYPO3.Form.Wizard.Viewport'); - -/** - * The form container on the right side - * - * @class TYPO3.Form.Wizard.Viewport.Right - * @extends TYPO3.Form.Wizard.Elements.Container - */ -TYPO3.Form.Wizard.Viewport.Right = Ext.extend(Ext.Container, { - /** - * @cfg {String} id - * The unique id of this component (defaults to an auto-assigned id). - * You should assign an id if you need to be able to access the component - * later and you do not have an object reference available - * (e.g., using Ext.getCmp). - * - * Note that this id will also be used as the element id for the containing - * HTML element that is rendered to the page for this component. - * This allows you to write id-based CSS rules to style the specific - * instance of this component uniquely, and also to select sub-elements - * using this component's id as the parent. - */ - id: 'formwizard-right', - - /** - * @cfg {Mixed} autoEl - * A tag name or DomHelper spec used to create the Element which will - * encapsulate this Component. - */ - autoEl: 'ol', - - /** - * @cfg {String} region - * Note: this config is only used when this BoxComponent is rendered - * by a Container which has been configured to use the BorderLayout - * layout manager (e.g. specifying layout:'border'). - */ - region: 'center', - - /** - * @cfg {Boolean} autoScroll - * true to use overflow:'auto' on the components layout element and show - * scroll bars automatically when necessary, false to clip any overflowing - * content (defaults to false). - */ - autoScroll: true, - - /** - * Constructor - */ - initComponent: function() { - var config = { - items: [ - { - xtype: 'typo3-form-wizard-elements-basic-form' - } - ] - }; - - // apply config - Ext.apply(this, Ext.apply(this.initialConfig, config)); - - // call parent - TYPO3.Form.Wizard.Viewport.Right.superclass.initComponent.apply(this, arguments); - - // Initialize the form after rendering - this.on('afterrender', this.initializeForm, this); - }, - - /** - * Initialize the form after rendering - */ - initializeForm: function() { - this.loadForm(); - - }, - - /** - * Load the form from config - * - * Loads the configuration and initializes the history - */ - loadForm: function() { - this.loadConfiguration(TYPO3.Form.Wizard.Settings.Configuration); - this.initializeHistory(); - }, - - /** - * Initialize the history - * - * After the form has been rendered for the first time, we need to add the - * initial configuration to the history, so it is possible to go back to the - * initial state of the form when it was loaded. - */ - initializeHistory: function() { - TYPO3.Form.Wizard.Helpers.History.setHistory(); - this.setForm(); - }, - - /** - * Called by the history class when a change has been made in the form - * - * Constructs an array out of this component and the children to add it to - * the history or to use when saving the form - * - * @returns {Array} - */ - getConfiguration: function() { - var historyConfiguration = new Array; - - if (this.items) { - this.items.each(function(item, index, length) { - historyConfiguration.push(item.getConfiguration()); - }, this); - } - return historyConfiguration; - }, - - /** - * Load a previous configuration from the history - * - * Removes all the components from this container and adds the components - * from the history configuration depending on the 'undo' or 'redo' action. - * - * @param historyConfiguration - */ - loadConfiguration: function(historyConfiguration) { - this.removeAll(); - this.add(historyConfiguration); - this.doLayout(); - this.setForm(); - }, - - /** - * Pass the form configuration to the left form tab - */ - setForm: function() { - if (Ext.getCmp('formwizard-left-form')) { - Ext.getCmp('formwizard-left-form').setForm(this.get(0)); - } - } -}); - -Ext.reg('typo3-form-wizard-viewport-right', TYPO3.Form.Wizard.Viewport.Right); diff --git a/typo3/sysext/form/Tests/Unit/Controller/AbstractBackendControllerTest.php b/typo3/sysext/form/Tests/Unit/Controller/AbstractBackendControllerTest.php new file mode 100644 index 000000000000..ddbf506ca171 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Controller/AbstractBackendControllerTest.php @@ -0,0 +1,48 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Controller; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Form\Controller\AbstractBackendController; + +/** + * Test case + */ +class AbstractBackendControllerTest extends UnitTestCase +{ + + /** + * @test + */ + public function resolveResourcePathsExpectResolve() + { + $mockController = $this->getAccessibleMockForAbstractClass( + AbstractBackendController::class, + [], + '', + false + ); + + $input = [ + 0 => 'EXT:form/Resources/Public/Css/form.css' + ]; + + $expected = [ + 0 => 'typo3/sysext/form/Resources/Public/Css/form.css' + ]; + + $this->assertSame($expected, $mockController->_call('resolveResourcePaths', $input)); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Controller/Fixtures/BackendUtilityFixture.php b/typo3/sysext/form/Tests/Unit/Controller/Fixtures/BackendUtilityFixture.php new file mode 100644 index 000000000000..45177c90790b --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Controller/Fixtures/BackendUtilityFixture.php @@ -0,0 +1,60 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Tests\Unit\Controller\Fixtures; + +/* + * 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! + */ + +/** + * Fixture for BackendUtility methods + */ +class BackendUtilityFixture +{ + + /** + * @param string $table + * @param int $uid + * @param string $fields + * @param string $where + * @param bool $useDeleteClause + * @return array + */ + public static function getRecord($table, $uid, $fields = '*', $where = '', $useDeleteClause = true) + { + return [ + 'uid' => 1, + ]; + } + + /** + * @param string $table + * @param array $row + * @param bool $prep + * @param bool $forceResult + * @return string + */ + public static function getRecordTitle($table, $row, $prep = false, $forceResult = true) + { + return 'record title'; + } + + /** + * @param string $moduleName + * @param array $urlParameters + * @return string + */ + public static function getModuleUrl($moduleName, $urlParameters = []) + { + return '/typo3/index.php?some=param'; + } +} diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Button.html b/typo3/sysext/form/Tests/Unit/Controller/Fixtures/BlankForm.yaml similarity index 100% rename from typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Button.html rename to typo3/sysext/form/Tests/Unit/Controller/Fixtures/BlankForm.yaml diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/ButtonTag.html b/typo3/sysext/form/Tests/Unit/Controller/Fixtures/SimpleContactForm.yaml similarity index 100% rename from typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/ButtonTag.html rename to typo3/sysext/form/Tests/Unit/Controller/Fixtures/SimpleContactForm.yaml diff --git a/typo3/sysext/form/Tests/Unit/Controller/FormEditorControllerTest.php b/typo3/sysext/form/Tests/Unit/Controller/FormEditorControllerTest.php new file mode 100644 index 000000000000..92a7e48d9c21 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Controller/FormEditorControllerTest.php @@ -0,0 +1,424 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Controller; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Controller\FormEditorController; +use TYPO3\CMS\Form\Domain\Exception\RenderingException; +use TYPO3\CMS\Form\Service\TranslationService; + +/** + * Test case + */ +class FormEditorControllerTest extends UnitTestCase +{ + + /** + * @var array A backup of registered singleton instances + */ + protected $singletonInstances = []; + + /** + * Set up + */ + public function setUp() + { + $this->singletonInstances = GeneralUtility::getSingletonInstances(); + } + + /** + * Tear down + */ + public function tearDown() + { + GeneralUtility::resetSingletonInstances($this->singletonInstances); + parent::tearDown(); + } + + /** + * @test + */ + public function getInsertRenderablesPanelConfigurationReturnsGroupedAndSortedConfiguration() + { + $mockController = $this->getAccessibleMock(FormEditorController::class, [ + 'dummy' + ], [], '', false); + + $objectMangerProphecy = $this->prophesize(ObjectManager::class); + GeneralUtility::setSingletonInstance(ObjectManager::class, $objectMangerProphecy->reveal()); + + $mockTranslationService = $this->getAccessibleMock(TranslationService::class, [ + 'translate' + ], [], '', false); + + $mockTranslationService + ->expects($this->any()) + ->method('translate') + ->willReturnArgument(4); + + $objectMangerProphecy + ->get(TranslationService::class) + ->willReturn($mockTranslationService); + + $mockController->_set('prototypeConfiguration', [ + 'formEditor' => [ + 'formElementGroups' => [ + 'input' => [ + 'label' => 'Basic elements', + ], + 'select' => [ + 'label' => 'Select elements', + ], + ], + ], + ]); + + $input = [ + 'Password' => [ + 'group' => 'input', + 'groupSorting' => 110, + 'iconIdentifier' => 't3-form-icon-password', + 'label' => 'Password label', + ], + 'Text' => [ + 'group' => 'input', + 'groupSorting' => 100, + 'iconIdentifier' => 't3-form-icon-text', + 'label' => 'Text label', + ], + 'SingleSelect' => [ + 'group' => 'select', + 'groupSorting' => 100, + 'iconIdentifier' => 't3-form-icon-single-select', + 'label' => 'Single select label', + ], + ]; + + $expected = [ + 0 => [ + 'key' => 'input', + 'elements' => [ + 0 => [ + 'key' => 'Text', + 'cssKey' => 'text', + 'label' => 'Text label', + 'sorting' => 100, + 'iconIdentifier' => 't3-form-icon-text', + ], + 1 => [ + 'key' => 'Password', + 'cssKey' => 'password', + 'label' => 'Password label', + 'sorting' => 110, + 'iconIdentifier' => 't3-form-icon-password', + ], + ], + 'label' => 'Basic elements', + ], + 1 => [ + 'key' => 'select', + 'elements' => [ + 0 => [ + 'key' => 'SingleSelect', + 'cssKey' => 'singleselect', + 'label' => 'Single select label', + 'sorting' => 100, + 'iconIdentifier' => 't3-form-icon-single-select', + ], + ], + 'label' => 'Select elements', + ], + ]; + + $this->assertSame($expected, $mockController->_call('getInsertRenderablesPanelConfiguration', $input)); + } + + /** + * @test + */ + public function getFormEditorDefinitionsReturnReducedConfiguration() + { + $mockController = $this->getAccessibleMock(FormEditorController::class, [ + 'dummy' + ], [], '', false); + + $objectMangerProphecy = $this->prophesize(ObjectManager::class); + GeneralUtility::setSingletonInstance(ObjectManager::class, $objectMangerProphecy->reveal()); + + $mockTranslationService = $this->getAccessibleMock(TranslationService::class, [ + 'translateValuesRecursive' + ], [], '', false); + + $mockTranslationService + ->expects($this->any()) + ->method('translateValuesRecursive') + ->willReturnArgument(0); + + $objectMangerProphecy + ->get(TranslationService::class) + ->willReturn($mockTranslationService); + + $mockController->_set('prototypeConfiguration', [ + 'formEditor' => [ + 'someOtherValues' => [ + 'horst' => [ + 'key' => 'value', + ], + 'gertrud' => [ + 'key' => 'value', + ], + ], + 'formElementPropertyValidatorsDefinition' => [ + 'NotEmpty' => [ + 'key' => 'value', + ], + ], + ], + 'formElementsDefinition' => [ + 'Form' => [ + 'formEditor' => [ + 'key' => 'value', + ], + 'someOtherValues' => [ + 'horst' => [ + 'key' => 'value', + ], + 'gertrud' => [ + 'key' => 'value', + ], + ], + ], + 'Text' => [ + 'formEditor' => [ + 'key' => 'value', + ], + 'someOtherValues' => [ + 'horst' => [ + 'key' => 'value', + ], + 'gertrud' => [ + 'key' => 'value', + ], + ], + ], + ], + 'finishersDefinition' => [ + 'Confirmation' => [ + 'formEditor' => [ + 'key' => 'value', + ], + 'someOtherValues' => [ + 'horst' => [ + 'key' => 'value', + ], + 'gertrud' => [ + 'key' => 'value', + ], + ], + ], + 'EmailToSender' => [ + 'formEditor' => [ + 'key' => 'value', + ], + 'someOtherValues' => [ + 'horst' => [ + 'key' => 'value', + ], + 'gertrud' => [ + 'key' => 'value', + ], + ], + ], + ], + 'someOtherValues' => [ + 'horst' => [ + 'key' => 'value', + ], + 'gertrud' => [ + 'key' => 'value', + ], + ], + ]); + + $expected = [ + 'formElements' => [ + 'Form' => [ + 'key' => 'value', + ], + 'Text' => [ + 'key' => 'value', + ], + ], + 'finishers' => [ + 'Confirmation' => [ + 'key' => 'value', + ], + 'EmailToSender' => [ + 'key' => 'value', + ], + ], + 'formElementPropertyValidators' => [ + 'NotEmpty' => [ + 'key' => 'value', + ], + ], + ]; + + $this->assertSame($expected, $mockController->_call('getFormEditorDefinitions')); + } + + /** + * @test + */ + public function convertJsonArrayToAssociativeArrayReturnTransformedArray() + { + $mockController = $this->getAccessibleMock(FormEditorController::class, [ + 'dummy' + ], [], '', false); + + $input = [ + 'francine' => 'stan', + 'properties' => [ + 'options' => [ + 0 => [ + '_label' => 'label', + '_value' => 'value', + ], + ], + ], + ]; + + $expected = [ + 'francine' => 'stan', + 'properties' => [ + 'options' => [ + 'value' => 'label', + ], + ], + ]; + + $this->assertSame($expected, $mockController->_call('convertJsonArrayToAssociativeArray', $input)); + } + + /** + * @test + */ + public function renderFormEditorTemplatesThrowsExceptionIfTemplateRootPathsNotSet() + { + $this->expectException(RenderingException::class); + $this->expectExceptionCode(1480294720); + + $mockController = $this->getAccessibleMock(FormEditorController::class, [ + 'dummy' + ], [], '', false); + + $mockController->_call('renderFormEditorTemplates', [], []); + } + + /** + * @test + */ + public function renderFormEditorTemplatesThrowsExceptionIfTemplateRootPathsNotArray() + { + $this->expectException(RenderingException::class); + $this->expectExceptionCode(1480294720); + + $mockController = $this->getAccessibleMock(FormEditorController::class, [ + 'dummy' + ], [], '', false); + + $input = [ + 'templateRootPaths' => '', + ]; + $mockController->_call('renderFormEditorTemplates', $input, []); + } + + /** + * @test + */ + public function renderFormEditorTemplatesThrowsExceptionIfLayoutRootPathsNotSet() + { + $this->expectException(RenderingException::class); + $this->expectExceptionCode(1480294721); + + $mockController = $this->getAccessibleMock(FormEditorController::class, [ + 'dummy' + ], [], '', false); + + $input = [ + 'templateRootPaths' => [], + ]; + $mockController->_call('renderFormEditorTemplates', $input, []); + } + + /** + * @test + */ + public function renderFormEditorTemplatesThrowsExceptionIfLayoutRootPathsNotArray() + { + $this->expectException(RenderingException::class); + $this->expectExceptionCode(1480294721); + + $mockController = $this->getAccessibleMock(FormEditorController::class, [ + 'dummy' + ], [], '', false); + + $input = [ + 'templateRootPaths' => [], + 'layoutRootPaths' => '', + ]; + $mockController->_call('renderFormEditorTemplates', $input, []); + } + + /** + * @test + */ + public function renderFormEditorTemplatesThrowsExceptionIfPartialRootPathsNotSet() + { + $this->expectException(RenderingException::class); + $this->expectExceptionCode(1480294722); + + $mockController = $this->getAccessibleMock(FormEditorController::class, [ + 'dummy' + ], [], '', false); + + $input = [ + 'templateRootPaths' => [], + 'layoutRootPaths' => [], + ]; + $mockController->_call('renderFormEditorTemplates', $input, []); + } + + /** + * @test + */ + public function renderFormEditorTemplatesThrowsExceptionIfPartialRootPathsNotArray() + { + $this->expectException(RenderingException::class); + $this->expectExceptionCode(1480294722); + + $mockController = $this->getAccessibleMock(FormEditorController::class, [ + 'dummy' + ], [], '', false); + + $input = [ + 'templateRootPaths' => [], + 'layoutRootPaths' => [], + ]; + $mockController->_call('renderFormEditorTemplates', $input, []); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Controller/FormFrontendControllerTest.php b/typo3/sysext/form/Tests/Unit/Controller/FormFrontendControllerTest.php new file mode 100644 index 000000000000..8c79fddb7a15 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Controller/FormFrontendControllerTest.php @@ -0,0 +1,418 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Controller; + +/* + * 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 Prophecy\Argument; +use TYPO3\CMS\Core\Tests\UnitTestCase; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Controller\FormFrontendController; +use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService; +use TYPO3\CMS\Form\Mvc\Configuration\TypoScriptService; + +/** + * Test case + */ +class FormFrontendControllerTest extends UnitTestCase +{ + + /** + * @test + */ + public function overrideByFlexFormSettingsReturnsNoOverriddenConfigurationIfFlexformOverridesDisabled() + { + $mockController = $this->getAccessibleMock(FormFrontendController::class, [ + 'dummy' + ], [], '', false); + + $configurationServiceProphecy = $this->prophesize(ConfigurationService::class); + + $objectManagerMock = $this->createMock(ObjectManager::class); + $objectManagerMock + ->expects($this->any()) + ->method('get') + ->with(ConfigurationService::class) + ->willReturn($configurationServiceProphecy->reveal()); + + $mockController->_set('objectManager', $objectManagerMock); + + $configurationServiceProphecy->getPrototypeConfiguration(Argument::cetera())->willReturn([ + 'finishersDefinition' => [ + 'EmailToReceiver' => [ + 'FormEngine' => [ + 'elements' => [ + 'subject' => [], + 'recipientAddress' => [], + 'format' => [], + ], + ], + ], + ], + ]); + + $mockController->_set('settings', [ + 'overrideFinishers' => 0, + 'finishers' => [ + 'EmailToReceiver' => [ + 'subject' => 'Mesage Subject overridden', + 'recipientAddress' => 'your.company@example.com overridden', + 'format' => 'html overridden', + ], + ], + ]); + + $input = [ + 'identifier' => 'ext-form-identifier', + 'prototypeName' => 'standard', + 'finishers' => [ + 0 => [ + 'identifier' => 'EmailToReceiver', + 'options' => [ + 'subject' => 'Mesage Subject', + 'recipientAddress' => 'your.company@example.com', + 'format' => 'html', + ], + ], + ], + ]; + + $expected = [ + 'identifier' => 'ext-form-identifier', + 'prototypeName' => 'standard', + 'finishers' => [ + 0 => [ + 'identifier' => 'EmailToReceiver', + 'options' => [ + 'subject' => 'Mesage Subject', + 'recipientAddress' => 'your.company@example.com', + 'format' => 'html', + ], + ], + ], + ]; + + $this->assertSame($expected, $mockController->_call('overrideByFlexFormSettings', $input)); + } + + /** + * @test + */ + public function overrideByFlexFormSettingsReturnsOverriddenConfigurationIfFlexformOverridesEnabled() + { + $mockController = $this->getAccessibleMock(FormFrontendController::class, [ + 'dummy' + ], [], '', false); + + $configurationServiceProphecy = $this->prophesize(ConfigurationService::class); + + $objectManagerMock = $this->createMock(ObjectManager::class); + $objectManagerMock + ->expects($this->any()) + ->method('get') + ->with(ConfigurationService::class) + ->willReturn($configurationServiceProphecy->reveal()); + + $mockController->_set('objectManager', $objectManagerMock); + + $configurationServiceProphecy->getPrototypeConfiguration(Argument::cetera())->willReturn([ + 'finishersDefinition' => [ + 'EmailToReceiver' => [ + 'FormEngine' => [ + 'elements' => [ + 'subject' => [], + 'recipientAddress' => [], + 'format' => [], + ], + ], + ], + ], + ]); + + $mockController->_set('settings', [ + 'overrideFinishers' => 1, + 'finishers' => [ + 'EmailToReceiver' => [ + 'subject' => 'Mesage Subject overridden', + 'recipientAddress' => 'your.company@example.com overridden', + 'format' => 'html overridden', + ], + ], + ]); + + $input = [ + 'identifier' => 'ext-form-identifier', + 'prototypeName' => 'standard', + 'finishers' => [ + 0 => [ + 'identifier' => 'EmailToReceiver', + 'options' => [ + 'subject' => 'Mesage Subject', + 'recipientAddress' => 'your.company@example.com', + 'format' => 'html', + ], + ], + ], + ]; + + $expected = [ + 'identifier' => 'ext-form-identifier', + 'prototypeName' => 'standard', + 'finishers' => [ + 0 => [ + 'identifier' => 'EmailToReceiver', + 'options' => [ + 'subject' => 'Mesage Subject overridden', + 'recipientAddress' => 'your.company@example.com overridden', + 'format' => 'html overridden', + ], + ], + ], + ]; + + $this->assertSame($expected, $mockController->_call('overrideByFlexFormSettings', $input)); + } + + /** + * @test + */ + public function overrideByFlexFormSettingsReturnsNotOverriddenConfigurationKeyIfFlexformOverridesAreNotRepresentedInFormEngineConfiguration() + { + $mockController = $this->getAccessibleMock(FormFrontendController::class, [ + 'dummy' + ], [], '', false); + + $configurationServiceProphecy = $this->prophesize(ConfigurationService::class); + + $objectManagerMock = $this->createMock(ObjectManager::class); + $objectManagerMock + ->expects($this->any()) + ->method('get') + ->with(ConfigurationService::class) + ->willReturn($configurationServiceProphecy->reveal()); + + $mockController->_set('objectManager', $objectManagerMock); + + $configurationServiceProphecy->getPrototypeConfiguration(Argument::cetera())->willReturn([ + 'finishersDefinition' => [ + 'EmailToReceiver' => [ + 'FormEngine' => [ + 'elements' => [ + 'subject' => [], + 'recipientAddress' => [], + ], + ], + ], + ], + ]); + + $mockController->_set('settings', [ + 'overrideFinishers' => 1, + 'finishers' => [ + 'EmailToReceiver' => [ + 'subject' => 'Mesage Subject overridden', + 'recipientAddress' => 'your.company@example.com overridden', + 'format' => 'html overridden', + ], + ], + ]); + + $input = [ + 'identifier' => 'ext-form-identifier', + 'prototypeName' => 'standard', + 'finishers' => [ + 0 => [ + 'identifier' => 'EmailToReceiver', + 'options' => [ + 'subject' => 'Mesage Subject', + 'recipientAddress' => 'your.company@example.com', + 'format' => 'html', + ], + ], + ], + ]; + + $expected = [ + 'identifier' => 'ext-form-identifier', + 'prototypeName' => 'standard', + 'finishers' => [ + 0 => [ + 'identifier' => 'EmailToReceiver', + 'options' => [ + 'subject' => 'Mesage Subject overridden', + 'recipientAddress' => 'your.company@example.com overridden', + 'format' => 'html', + ], + ], + ], + ]; + + $this->assertSame($expected, $mockController->_call('overrideByFlexFormSettings', $input)); + } + + /** + * @test + */ + public function overrideByTypoScriptSettingsReturnsNotOverriddenConfigurationIfNoTypoScriptOverridesExists() + { + $mockController = $this->getAccessibleMock(FormFrontendController::class, [ + 'dummy' + ], [], '', false); + + $typoScriptServiceProphecy = $this->prophesize(TypoScriptService::class); + + $objectManagerMock = $this->createMock(ObjectManager::class); + $objectManagerMock + ->expects($this->any()) + ->method('get') + ->with(TypoScriptService::class) + ->willReturn($typoScriptServiceProphecy->reveal()); + + $mockController->_set('objectManager', $objectManagerMock); + + $typoScriptServiceProphecy + ->resolvePossibleTypoScriptConfiguration(Argument::cetera()) + ->willReturnArgument(0); + + $mockController->_set('settings', [ + 'formDefinitionOverrides' => [ + ], + ]); + + $input = [ + 'identifier' => 'ext-form-identifier', + 'prototypeName' => 'standard', + 'label' => 'Label', + 'renderables' => [ + 0 => [ + 'identifier' => 'page-1', + 'type' => 'Page', + 'label' => 'Label', + 'renderables' => [ + 0 => [ + 'identifier' => 'text-1', + 'type' => 'Text', + 'label' => 'Label', + ], + ], + ], + ], + ]; + + $expected = [ + 'identifier' => 'ext-form-identifier', + 'prototypeName' => 'standard', + 'label' => 'Label', + 'renderables' => [ + 0 => [ + 'identifier' => 'page-1', + 'type' => 'Page', + 'label' => 'Label', + 'renderables' => [ + 0 => [ + 'identifier' => 'text-1', + 'type' => 'Text', + 'label' => 'Label', + ], + ], + ], + ], + ]; + + $this->assertSame($expected, $mockController->_call('overrideByTypoScriptSettings', $input)); + } + + /** + * @test + */ + public function overrideByTypoScriptSettingsReturnsOverriddenConfigurationIfTypoScriptOverridesExists() + { + $mockController = $this->getAccessibleMock(FormFrontendController::class, [ + 'dummy' + ], [], '', false); + + $typoScriptServiceProphecy = $this->prophesize(TypoScriptService::class); + + $objectManagerMock = $this->createMock(ObjectManager::class); + $objectManagerMock + ->expects($this->any()) + ->method('get') + ->with(TypoScriptService::class) + ->willReturn($typoScriptServiceProphecy->reveal()); + + $mockController->_set('objectManager', $objectManagerMock); + + $typoScriptServiceProphecy + ->resolvePossibleTypoScriptConfiguration(Argument::cetera()) + ->willReturnArgument(0); + + $mockController->_set('settings', [ + 'formDefinitionOverrides' => [ + 'ext-form-identifier' => [ + 'label' => 'Label override', + 'renderables' => [ + 0 => [ + 'renderables' => [ + 0 => [ + 'label' => 'Label override', + ], + ], + ], + ], + ], + ], + ]); + + $input = [ + 'identifier' => 'ext-form-identifier', + 'prototypeName' => 'standard', + 'label' => 'Label', + 'renderables' => [ + 0 => [ + 'identifier' => 'page-1', + 'type' => 'Page', + 'label' => 'Label', + 'renderables' => [ + 0 => [ + 'identifier' => 'text-1', + 'type' => 'Text', + 'label' => 'Label', + ], + ], + ], + ], + ]; + + $expected = [ + 'identifier' => 'ext-form-identifier', + 'prototypeName' => 'standard', + 'label' => 'Label override', + 'renderables' => [ + 0 => [ + 'identifier' => 'page-1', + 'type' => 'Page', + 'label' => 'Label', + 'renderables' => [ + 0 => [ + 'identifier' => 'text-1', + 'type' => 'Text', + 'label' => 'Label override', + ], + ], + ], + ], + ]; + + $this->assertSame($expected, $mockController->_call('overrideByTypoScriptSettings', $input)); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Controller/FormManagerControllerTest.php b/typo3/sysext/form/Tests/Unit/Controller/FormManagerControllerTest.php new file mode 100644 index 000000000000..83ac02029597 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Controller/FormManagerControllerTest.php @@ -0,0 +1,410 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Controller; + +/* + * 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 Prophecy\Argument; +use TYPO3\CMS\Core\Resource\Folder; +use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Tests\UnitTestCase; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext; +use TYPO3\CMS\Extbase\Mvc\Web\Routing\UriBuilder; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Controller\FormManagerController; +use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager; +use TYPO3\CMS\Form\Service\TranslationService; +use TYPO3\CMS\Form\Tests\Unit\Controller\Fixtures\BackendUtilityFixture; + +/** + * Test case + */ +class FormManagerControllerTest extends UnitTestCase +{ + + /** + * @var array A backup of registered singleton instances + */ + protected $singletonInstances = []; + + /** + * Set up + */ + public function setUp() + { + $this->singletonInstances = GeneralUtility::getSingletonInstances(); + } + + /** + * Tear down + */ + public function tearDown() + { + GeneralUtility::resetSingletonInstances($this->singletonInstances); + parent::tearDown(); + } + + /** + * @test + */ + public function getAccessibleFormStorageFoldersReturnsProcessedArray() + { + $mockController = $this->getAccessibleMock(FormManagerController::class, [ + 'dummy' + ], [], '', false); + + $formPersistenceManagerProphecy = $this->prophesize(FormPersistenceManager::class); + + $mockStorage = $this->getMockBuilder(ResourceStorage::class) + ->disableOriginalConstructor() + ->getMock(); + $mockController->_set('formPersistenceManager', $formPersistenceManagerProphecy->reveal()); + + $folder1 = new Folder($mockStorage, '/user_upload/', 'user_upload'); + $folder2 = new Folder($mockStorage, '/forms/', 'forms'); + + $formPersistenceManagerProphecy->getAccessibleFormStorageFolders(Argument::cetera())->willReturn([ + '1:/user_upload/' => $folder1, + '2:/forms/' => $folder2, + ]); + + $expected = [ + 0 => [ + 'label' => 'user_upload', + 'value' => '1:/user_upload/', + ], + 1 => [ + 'label' => 'forms', + 'value' => '2:/forms/', + ], + ]; + + $this->assertSame($expected, $mockController->_call('getAccessibleFormStorageFolders')); + } + + /** + * @test + */ + public function getFormManagerAppInitialDataReturnsProcessedArray() + { + $objectMangerProphecy = $this->prophesize(ObjectManager::class); + GeneralUtility::setSingletonInstance(ObjectManager::class, $objectMangerProphecy->reveal()); + + $mockTranslationService = $this->getAccessibleMock(TranslationService::class, [ + 'translateValuesRecursive' + ], [], '', false); + + $mockTranslationService + ->expects($this->any()) + ->method('translateValuesRecursive') + ->willReturnArgument(0); + + $objectMangerProphecy + ->get(TranslationService::class) + ->willReturn($mockTranslationService); + + $mockController = $this->getAccessibleMock(FormManagerController::class, [ + 'getAccessibleFormStorageFolders' + ], [], '', false); + + $mockUriBuilder = $this->createMock(UriBuilder::class); + $mockControllerContext = $this->createMock(ControllerContext::class); + $mockControllerContext + ->expects($this->any()) + ->method('getUriBuilder') + ->will($this->returnValue($mockUriBuilder)); + + $mockController->_set('controllerContext', $mockControllerContext); + + $mockController->_set('formSettings', [ + 'formManager' => [ + 'selectablePrototypesConfiguration' => [], + ], + ]); + + $mockUriBuilder->expects($this->any())->method('uriFor')->willReturn( + '/typo3/index.php?some=param' + ); + + $mockController + ->expects($this->any()) + ->method('getAccessibleFormStorageFolders') + ->willReturn([ + 0 => [ + 'label' => 'user_upload', + 'value' => '1:/user_upload/', + ], + ]); + + $expected = [ + 'selectablePrototypesConfiguration' => [], + 'accessibleFormStorageFolders' => [ + 0 => [ + 'label' => 'user_upload', + 'value' => '1:/user_upload/', + ], + ], + 'endpoints' => [ + 'create' => '/typo3/index.php?some=param', + 'duplicate' => '/typo3/index.php?some=param', + 'delete' => '/typo3/index.php?some=param', + 'references' => '/typo3/index.php?some=param', + ], + ]; + + $this->assertSame(json_encode($expected), $mockController->_call('getFormManagerAppInitialData')); + } + + /** + * @test + */ + public function getAvailableFormDefinitionsReturnsProcessedArray() + { + $mockController = $this->getAccessibleMock(FormManagerController::class, [ + 'getReferences' + ], [], '', false); + + $formPersistenceManagerProphecy = $this->prophesize(FormPersistenceManager::class); + $mockController->_set('formPersistenceManager', $formPersistenceManagerProphecy->reveal()); + + $formPersistenceManagerProphecy->listForms(Argument::cetera())->willReturn([ + 0 => [ + 'identifier' => 'ext-form-identifier', + 'name' => 'some name', + 'persistenceIdentifier' => '1:/user_uploads/someFormName.yaml', + 'readOnly' => false, + 'location' => 'storage', + 'duplicateIdentifier' => false, + ], + ]); + + $mockController + ->expects($this->any()) + ->method('getReferences') + ->willReturn([ + 'someRow', + 'anotherRow', + ]); + + $expected = [ + 0 => [ + 'identifier' => 'ext-form-identifier', + 'name' => 'some name', + 'persistenceIdentifier' => '1:/user_uploads/someFormName.yaml', + 'readOnly' => false, + 'location' => 'storage', + 'duplicateIdentifier' => false, + 'referenceCount' => 2, + ], + ]; + + $this->assertSame($expected, $mockController->_call('getAvailableFormDefinitions')); + } + + /** + * @test + */ + public function getProcessedReferencesRowsThrowsExceptionIfPersistenceIdentifierIsEmpty() + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionCode(1477071939); + + $mockController = $this->getAccessibleMock(FormManagerController::class, [ + 'dummy' + ], [], '', false); + + $mockController->_call('getProcessedReferencesRows', ''); + } + + /** + * @test + */ + public function getProcessedReferencesRowsReturnsProcessedArray() + { + $mockController = $this->getAccessibleMock(FormManagerController::class, [ + 'getReferences', + 'getBackendUtility', + ], [], '', false); + + $mockController + ->expects($this->any()) + ->method('getBackendUtility') + ->willReturn(BackendUtilityFixture::class); + + $mockController + ->expects($this->any()) + ->method('getReferences') + ->willReturn([ + 0 => [ + 'tablename' => 'tt_content', + 'recuid' => -1, + ], + ]); + + $expected = [ + 0 => [ + 'recordPageTitle' => 'record title', + 'recordTitle' => 'record title', + 'recordIcon' => +'<span class="t3js-icon icon icon-size-small icon-state-default icon-default-not-found" data-identifier="default-not-found"> + <span class="icon-markup"> +<img src="typo3/sysext/core/Resources/Public/Icons/T3Icons/default/default-not-found.svg" width="16" height="16" /> + </span> + +</span>', + 'recordUid' => -1, + 'recordEditUrl' => '/typo3/index.php?some=param', + ], + ]; + + $this->assertSame($expected, $mockController->_call('getProcessedReferencesRows', 'fake')); + } + + /** + * @test + */ + public function isValidTemplatePathReturnsTrueIfTemplateIsDefinedAndExists() + { + $mockController = $this->getAccessibleMock(FormManagerController::class, [ + 'dummy' + ], [], '', false); + + $mockController->_set('formSettings', [ + 'formManager' => [ + 'selectablePrototypesConfiguration' => [ + 0 => [ + 'identifier' => 'standard', + 'label' => 'some label', + 'newFormTemplates' => [ + 0 => [ + 'templatePath' => 'EXT:form/Tests/Unit/Controller/Fixtures/BlankForm.yaml', + 'label' => 'some label', + ], + 1 => [ + 'templatePath' => 'EXT:form/Tests/Unit/Controller/Fixtures/SimpleContactForm.yaml', + 'label' => 'some other label', + ], + ], + ], + ], + ], + ]); + + $this->assertTrue($mockController->_call('isValidTemplatePath', 'standard', 'EXT:form/Tests/Unit/Controller/Fixtures/SimpleContactForm.yaml')); + } + + /** + * @test + */ + public function isValidTemplatePathReturnsFalseIfTemplateIsDefinedButNotExists() + { + $mockController = $this->getAccessibleMock(FormManagerController::class, [ + 'dummy' + ], [], '', false); + + $mockController->_set('formSettings', [ + 'formManager' => [ + 'selectablePrototypesConfiguration' => [ + 0 => [ + 'identifier' => 'standard', + 'label' => 'some label', + 'newFormTemplates' => [ + 0 => [ + 'templatePath' => 'EXT:form/Tests/Unit/Controller/Fixtures/BlankForm.yaml', + 'label' => 'some label', + ], + 1 => [ + 'templatePath' => 'EXT:form/Tests/Unit/Controller/Fixtures/SimpleContactForm.yaml', + 'label' => 'some other label', + ], + ], + ], + ], + ], + ]); + + $this->assertFalse($mockController->_call('isValidTemplatePath', 'standard', 'EXT:form/Tests/Unit/Controller/Fixtures/NonExistingForm.yaml')); + } + + /** + * @test + */ + public function isValidTemplatePathReturnsFalseIfTemplateIsNotDefinedAndExists() + { + $mockController = $this->getAccessibleMock(FormManagerController::class, [ + 'dummy' + ], [], '', false); + + $mockController->_set('formSettings', [ + 'formManager' => [ + 'selectablePrototypesConfiguration' => [ + 0 => [ + 'identifier' => 'standard', + 'label' => 'some label', + 'newFormTemplates' => [ + 0 => [ + 'templatePath' => 'EXT:form/Tests/Unit/Controller/Fixtures/BlankForm.yaml', + 'label' => 'some label', + ], + 1 => [ + 'templatePath' => 'EXT:form/Tests/Unit/Controller/Fixtures/SimpleContactForm.yaml', + 'label' => 'some other label', + ], + ], + ], + 1 => [ + 'identifier' => 'other', + 'label' => 'some label', + 'newFormTemplates' => [ + 0 => [ + 'templatePath' => 'EXT:form/Tests/Unit/Controller/Fixtures/BlankForm.yaml', + 'label' => 'some label', + ], + ], + ], + ], + ], + ]); + + $this->assertFalse($mockController->_call('isValidTemplatePath', 'other', 'EXT:form/Tests/Unit/Controller/Fixtures/SimpleContactForm.yaml')); + } + + /** + * @test + */ + public function convertFormNameToIdentifierRemoveSpaces() + { + $mockController = $this->getAccessibleMock(FormManagerController::class, [ + 'dummy' + ], [], '', false); + + $input = 'test form'; + $expected = 'testform'; + $this->assertSame($expected, $mockController->_call('convertFormNameToIdentifier', $input)); + } + + /** + * @test + */ + public function convertFormNameToIdentifierRemoveSpecialChars() + { + $mockController = $this->getAccessibleMock(FormManagerController::class, [ + 'dummy' + ], [], '', false); + + $input = 'test form ä#!_-01'; + $expected = 'testform_-01'; + $this->assertSame($expected, $mockController->_call('convertFormNameToIdentifier', $input)); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Domain/Configuration/ConfigurationServiceTest.php b/typo3/sysext/form/Tests/Unit/Domain/Configuration/ConfigurationServiceTest.php new file mode 100644 index 000000000000..76a6ab53d800 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Domain/Configuration/ConfigurationServiceTest.php @@ -0,0 +1,71 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Domain\Configuration; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Form\Domain\Configuration\ConfigurationService; +use TYPO3\CMS\Form\Domain\Configuration\Exception\PrototypeNotFoundException; + +/** + * Test case + */ +class ConfigurationServiceTest extends UnitTestCase +{ + + /** + * @test + */ + public function getPrototypeConfigurationReturnsPrototypeConfiguration() + { + $mockConfigurationService = $this->getAccessibleMock(ConfigurationService::class, [ + 'dummy' + ], [], '', false); + + $mockConfigurationService->_set('formSettings', [ + 'prototypes' => [ + 'standard' => [ + 'key' => 'value', + ], + ], + ]); + + $expected = [ + 'key' => 'value', + ]; + + $this->assertSame($expected, $mockConfigurationService->getPrototypeConfiguration('standard')); + } + + /** + * @test + */ + public function getPrototypeConfigurationThrowsExceptionIfNoPrototypeFound() + { + $mockConfigurationService = $this->getAccessibleMock(ConfigurationService::class, [ + 'dummy' + ], [], '', false); + + $this->expectException(PrototypeNotFoundException::class); + $this->expectExceptionCode(1475924277); + + $mockConfigurationService->_set('formSettings', [ + 'prototypes' => [ + 'noStandard' => [], + ], + ]); + + $mockConfigurationService->getPrototypeConfiguration('standard'); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Domain/Model/ConfigurationTest.php b/typo3/sysext/form/Tests/Unit/Domain/Model/ConfigurationTest.php deleted file mode 100644 index ca41160d9614..000000000000 --- a/typo3/sysext/form/Tests/Unit/Domain/Model/ConfigurationTest.php +++ /dev/null @@ -1,136 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Domain; - -/* - * 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\Core\Tests\UnitTestCase; -use TYPO3\CMS\Form\Domain\Model\Configuration; -use TYPO3\CMS\Form\Domain\Repository\TypoScriptRepository; - -/** - * Test case for class \TYPO3\CMS\Form\Domain\Model\Configuration - */ -class ConfigurationTest extends UnitTestCase -{ - /** - * @var Configuration - */ - protected $subject = null; - - /* - * @var TypoScriptRepository|\Prophecy\Prophecy\ObjectProphecy - */ - protected $typoScriptRepositoryProphecy; - - /** - * Sets up this test case. - */ - protected function setUp() - { - parent::setUp(); - $this->typoScriptRepositoryProphecy = $this->prophesize(TypoScriptRepository::class); - $this->subject = $this->getAccessibleMock(Configuration::class, ['__none']); - $this->subject->_set('typoScriptRepository', $this->typoScriptRepositoryProphecy->reveal()); - } - - /** - * Tears down this test case. - */ - protected function tearDown() - { - parent::tearDown(); - unset($this->typoScriptRepositoryProphecy); - unset($this->subject); - } - - /** - * @param array $typoScript - * @param string $globalThemeName - * @param array $expected - * - * @test - * @dataProvider propertiesAreUpdatedFromTypoScriptDataProvider - */ - public function propertiesAreUpdatedFromTypoScript(array $typoScript, $globalThemeName, array $expected) - { - $this->typoScriptRepositoryProphecy - ->getModelConfigurationByScope('FORM', 'themeName') - ->willReturn($globalThemeName); - - $this->subject->setTypoScript($typoScript); - $this->assertEquals($expected['prefix'], $this->subject->getPrefix()); - $this->assertEquals($expected['contentElementRendering'], $this->subject->getContentElementRendering()); - } - - /** - * @return array - */ - public function propertiesAreUpdatedFromTypoScriptDataProvider() - { - return [ - '#1' => [ - [ - 'prefix' => '', - 'themeName' => '', - 'disableContentElement' => false, - ], - '', - [ - 'prefix' => 'form', - 'themeName' => 'Default', - 'contentElementRendering' => true, - ], - ], - '#2' => [ - [ - 'prefix' => 'somePrefix', - 'themeName' => 'someTheme', - 'disableContentElement' => true, - ], - '', - [ - 'prefix' => 'somePrefix', - 'themeName' => 'someTheme', - 'contentElementRendering' => false, - ], - ], - '#3' => [ - [ - 'prefix' => 'somePrefix', - 'themeName' => 'someTheme', - 'disableContentElement' => true, - ], - '', - [ - 'prefix' => 'somePrefix', - 'themeName' => 'someTheme', - 'contentElementRendering' => false, - ], - ], - '#4' => [ - [ - 'prefix' => 'somePrefix', - 'themeName' => 'someTheme', - 'disableContentElement' => true, - ], - '', - [ - 'prefix' => 'somePrefix', - 'themeName' => 'someTheme', - 'contentElementRendering' => false, - ], - ], - ]; - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/AlphabeticFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/AlphabeticFilterTest.php deleted file mode 100644 index 5c45350e5dd4..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/AlphabeticFilterTest.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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! - */ - -/** - * Test case - */ -class AlphabeticFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\Domain\Filter\AlphabeticFilter - */ - protected $subject = null; - - /** - * Set up - */ - protected function setUp() - { - $this->subject = new \TYPO3\CMS\Form\Domain\Filter\AlphabeticFilter(); - } - - /** - * @test - */ - public function filterForStringWithUnicodeCharactersAndSpacesReturnsInputString() - { - $input = 'My name contains äøüößØœ'; - // This is default, but let's be explicit: - $this->subject->setAllowWhiteSpace(true); - $this->assertSame($input, $this->subject->filter($input)); - } - - /** - * @test - */ - public function filterForStringWithUnicodeCharactersAndSpacesWithAllowWhitespaceSetToFalseReturnsInputStringWithoutSpaces() - { - $input = 'My name contains äøüößØœ'; - $expected = 'MynamecontainsäøüößØœ'; - $this->subject->setAllowWhiteSpace(false); - $this->assertSame($expected, $this->subject->filter($input)); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/AlphanumericFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/AlphanumericFilterTest.php deleted file mode 100644 index 3d1c6a30d375..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/AlphanumericFilterTest.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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! - */ - -/** - * Test case - */ -class AlphanumericFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\Domain\Filter\AlphanumericFilter - */ - protected $subject = null; - - /** - * Set up - */ - protected function setUp() - { - $this->subject = new \TYPO3\CMS\Form\Domain\Filter\AlphanumericFilter(); - } - - /** - * @test - */ - public function filterForStringWithUnicodeCharactersAndSpacesReturnsInputString() - { - $input = 'My name contains äøüößØœ'; - // This is default, but let's be explicit: - $this->subject->setAllowWhiteSpace(true); - $this->assertSame($input, $this->subject->filter($input)); - } - - /** - * @test - */ - public function filterForStringWithUnicodeCharactersAndSpacesWithAllowWhitespaceSetToFalseReturnsInputStringWithoutSpaces() - { - $input = 'My name contains äøüößØœ'; - $expected = 'MynamecontainsäøüößØœ'; - $this->subject->setAllowWhiteSpace(false); - $this->assertSame($expected, $this->subject->filter($input)); - } - - /** - * @test - */ - public function filterAllowsNumericCharacters() - { - $this->assertSame('foo23bar', $this->subject->filter('foo23bar')); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/CurrencyFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/CurrencyFilterTest.php deleted file mode 100644 index b141ae4db01e..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/CurrencyFilterTest.php +++ /dev/null @@ -1,102 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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! - */ - -/** - * Test case - */ -class CurrencyFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\Domain\Filter\CurrencyFilter - */ - protected $subject; - - protected function setUp() - { - $this->subject = new \TYPO3\CMS\Form\Domain\Filter\CurrencyFilter(); - } - - public function validDataProvider() - { - return [ - '1200 => 1.200,00' => [ - '1200', // input - '.', // thousand separator - ',', // decimal point - '1.200,00' // expected - ], - '0 => 0,00' => [ - '0', - null, - ',', - '0,00' - ], - '3333.33 => 3,333.33' => [ - '3333.33', - ',', - '.', - '3,333.33' - ], - '1099.33 => 1 099,33' => [ - '1099.33', - ' ', - ',', - '1 099,33' - ], - '1200,00 => 1.200,00' => [ - '1200,00', // input - '.', // thousand separator - ',', // decimal point - '1.200,00' // expected - ], - '1.200,00 => 1.200,00' => [ - '1.200,00', // input - '.', // thousand separator - ',', // decimal point - '1.200,00' // expected - ], - '1.200 => 1.200,00' => [ - '1.200', // input - '.', // thousand separator - ',', // decimal point - '1.200,00' // expected - ], - '-1 => -1,00' => [ - '-1', // input - '.', // thousand separator - ',', // decimal point - '-1,00' // expected - ], - '1.200 => 1.200,00' => [ - '1.200', // input - '.', // thousand separator - ',', // decimal point - '1.200,00' // expected - ], - ]; - } - - /** - * @test - * @dataProvider validDataProvider - */ - public function filterForVariousIntegerInputsReturnsFormattedCurrencyNotation($input, $thousandSeparator, $decimalPoint, $expected) - { - $this->subject->setThousandSeparator($thousandSeparator); - $this->subject->setDecimalsPoint($decimalPoint); - $this->assertSame($expected, $this->subject->filter($input)); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/DigitFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/DigitFilterTest.php deleted file mode 100644 index e438af25b075..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/DigitFilterTest.php +++ /dev/null @@ -1,54 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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! - */ - -/** - * Test case - */ -class DigitFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\Domain\Filter\DigitFilter - */ - protected $subject; - - protected function setUp() - { - $this->subject = new \TYPO3\CMS\Form\Domain\Filter\DigitFilter(); - } - - public function validDataProvider() - { - return [ - '1,00 -> 100' => ['1,00', '100'], - '1E+49 -> 149' => ['1E+49', '149'], - '100 -> 100' => ['100', '100'], - '00000 -> 00000' => ['00000', '00000'], - 'ABCD -> ""' => ['ABCD', ''], - ]; - } - - /** - * @test - * @dataProvider validDataProvider - */ - public function filterForStringsReturnsStringsFilteredToOnlyContainDigits($input, $expected) - { - $this->assertSame( - $expected, - $this->subject->filter($input) - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/IntegerFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/IntegerFilterTest.php deleted file mode 100644 index 3194a6b0223f..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/IntegerFilterTest.php +++ /dev/null @@ -1,55 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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! - */ - -/** - * Test case - */ -class IntegerFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\Domain\Filter\IntegerFilter - */ - protected $subject; - - protected function setUp() - { - $this->subject = new \TYPO3\CMS\Form\Domain\Filter\IntegerFilter(); - } - - public function dataProvider() - { - return [ - '"1" -> 1' => ['1', 1], - '1 -> 1' => [1, 1], - '1.1 -> 1' => [1.1, 1], - 'a -> 0' => ['a', 0], - 'a42 -> 0' => ['a42', 0], - '-100.00 -> -100' => [-100.00, -100], - ]; - } - - /** - * @test - * @dataProvider dataProvider - */ - public function filterForVariousInputReturnsInputCastedToInteger($input, $expected) - { - $this->assertSame( - $expected, - $this->subject->filter($input) - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/LowerCaseFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/LowerCaseFilterTest.php deleted file mode 100644 index 9016aee750e0..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/LowerCaseFilterTest.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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! - */ - -/** - * Test case - */ -class LowerCaseFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\Domain\Filter\LowerCaseFilter - */ - protected $subject; - - protected function setUp() - { - $this->subject = new \TYPO3\CMS\Form\Domain\Filter\LowerCaseFilter(); - $GLOBALS['TSFE'] = new \stdClass(); - $GLOBALS['TSFE']->csConvObj = new \TYPO3\CMS\Core\Charset\CharsetConverter(); - } - - public function dataProvider() - { - return [ - 'a -> a' => ['a', 'a'], - 'A -> a' => ['A', 'a'], - 'AaA -> aaa' => ['AaA', 'aaa'], - 'ÜßbÉØ -> üßbéø' => ['ÜßbÉØ', 'üßbéø'], - '01A23b -> 01a23b' => ['01A23b', '01a23b'], - ]; - } - - /** - * @test - * @dataProvider dataProvider - */ - public function filterForVariousInputReturnsLowercasedInput($input, $expected) - { - $this->assertSame( - $expected, - $this->subject->filter($input) - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/RegExpFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/RegExpFilterTest.php deleted file mode 100644 index 8d1404b495fb..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/RegExpFilterTest.php +++ /dev/null @@ -1,65 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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! - */ - -/** - * Test case - */ -class RegExpFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\Domain\Filter\RegExpFilter - */ - protected $subject; - - protected function setUp() - { - $this->subject = new \TYPO3\CMS\Form\Domain\Filter\RegExpFilter(); - } - - public function dataProvider() - { - return [ - 'a-a -> aa for /-/' => [ - 'a-a', - '/-/', - 'aa' - ], - 'aaa -> "" for /.+/' => [ - 'aaa', - '/.+/', - '' - ], - 'aAa -> aa for /[^a]+/' => [ - 'aAa', - '/[^a]+/', - 'aa' - ], - ]; - } - - /** - * @test - * @dataProvider dataProvider - */ - public function filterForStringReturnsInputWithoutCharactersMatchedByRegularExpression($input, $regularExpression, $expected) - { - $this->subject->setRegularExpression($regularExpression); - $this->assertSame( - $expected, - $this->subject->filter($input) - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/StripNewLinesFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/StripNewLinesFilterTest.php deleted file mode 100644 index 9aa3b16006f0..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/StripNewLinesFilterTest.php +++ /dev/null @@ -1,74 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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! - */ - -/** - * Test case - */ -class StripNewLinesFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\Domain\Filter\StripNewLinesFilter - */ - protected $subject = null; - - /** - * Set up - */ - protected function setUp() - { - $this->subject = new \TYPO3\CMS\Form\Domain\Filter\StripNewLinesFilter(); - } - - public function dataProviderWithNewlines() - { - return [ - 'some\rtext' => ["some\rtext", 'some text'], - 'some\ntext' => ["some\ntext", 'some text'], - 'some\r\ntext' => ["some\r\ntext", 'some text'], - 'somechr(13)text' => ['some' . chr(13) . 'text', 'some text'], - 'somechr(10)text' => ['some' . chr(10) . 'text', 'some text'], - 'somechr(13)chr(10)text' => ['some' . chr(13) . chr(10) . 'text', 'some text'], - 'someCRtext' => ['some' . CR . 'text', 'some text'], - 'someLFtext' => ['some' . LF . 'text', 'some text'], - 'someCRLFtext' => ['some' . CRLF . 'text', 'some text'], - 'some^Mtext' => ['some -text', 'some text'], - 'trailing newline\r' => ["trailing newline\n", 'trailing newline '], - 'trailing newline\n' => ["trailing newline\r", 'trailing newline '], - 'trailing newline\r\n' => ["trailing newline\r\n", 'trailing newline '], - 'trailing newlinechr(13)' => ['trailing newline' . chr(13), 'trailing newline '], - 'trailing newlinechr(10)' => ['trailing newline' . chr(10), 'trailing newline '], - 'trailing newlinechr(13)chr(10)' => ['trailing newline' . chr(13) . chr(10), 'trailing newline '], - 'trailing newlineCR' => ['trailing newline' . CR, 'trailing newline '], - 'trailing newlineLF' => ['trailing newline' . LF, 'trailing newline '], - 'trailing newlineCRLF' => ['trailing newline' . CRLF, 'trailing newline '], - 'trailing newline^M' => ['trailing newline -', 'trailing newline '] - ]; - } - - /** - * @test - * @dataProvider dataProviderWithNewlines - */ - public function filterForStringWithNewlineReturnsStringWithoutNewline($input, $expected) - { - $this->assertSame( - $expected, - $this->subject->filter($input) - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/TitleCaseFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/TitleCaseFilterTest.php deleted file mode 100644 index 79a40462f3f4..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/TitleCaseFilterTest.php +++ /dev/null @@ -1,64 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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\Core\Charset\CharsetConverter; -use TYPO3\CMS\Form\Domain\Filter\TitleCaseFilter; - -/** - * Test case - */ -class TitleCaseFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var TitleCaseFilter - */ - protected $subject = null; - - /** - * Set up - */ - protected function setUp() - { - $this->subject = new TitleCaseFilter(); - $GLOBALS['TSFE'] = new \stdClass(); - $GLOBALS['TSFE']->csConvObj = new CharsetConverter(); - } - - /** - * @return array - */ - public function stringProvider() - { - return [ - 'some text' => ['some text', 'Some Text'], - 'some Text' => ['some Text', 'Some Text'], - 'Ein Maß' => ['Ein Maß', 'Ein Maß'], - '¿por que?' => ['¿por que?', '¿por Que?'], - ]; - } - - /** - * @test - * @dataProvider stringProvider - */ - public function filterForStringReturnsStringWithUppercasedWords($input, $expected) - { - $this->assertSame( - $expected, - $this->subject->filter($input) - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/TrimFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/TrimFilterTest.php deleted file mode 100644 index d091186d8157..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/TrimFilterTest.php +++ /dev/null @@ -1,81 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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! - */ - -/** - * Test case - */ -class TrimFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\Domain\Filter\TrimFilter - */ - protected $subject = null; - - /** - * Set up - */ - protected function setUp() - { - $this->subject = new \TYPO3\CMS\Form\Domain\Filter\TrimFilter(); - } - - public function stringProvider() - { - return [ - '\tsome text ' => ["\tsome text ", 'some text'], - 'some text ' => ['some text ', 'some text'], - 'some text^M' => ['some text -', 'some text'], - ]; - } - - public function stringProviderForCharacterList() - { - return [ - '$some text;' => ['$some text;', 'some text', '$;'], - '$some text ' => ['$some text ', 'some text', '$ '], - '^Msome text ' => [' -some text ', 'some text', ' - '], - ]; - } - - /** - * @test - * @dataProvider stringProvider - */ - public function filterForStringWithWhitespaceInFrontAndEndReturnsStringWithoutThisWhitespace($input, $expected) - { - $this->assertSame( - $expected, - $this->subject->filter($input) - ); - } - - /** - * @test - * @dataProvider stringProviderForCharacterList - */ - public function filterForStringWithCharactersInCharacterListReturnsStringWithoutTheseCharacters($input, $expected, $characterList) - { - $this->subject->setCharacterList($characterList); - - $this->assertSame( - $expected, - $this->subject->filter($input) - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Filter/UpperCaseFilterTest.php b/typo3/sysext/form/Tests/Unit/Filter/UpperCaseFilterTest.php deleted file mode 100644 index 37319a272a29..000000000000 --- a/typo3/sysext/form/Tests/Unit/Filter/UpperCaseFilterTest.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Filter; - -/* - * 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! - */ - -/** - * Test case - */ -class UpperCaseFilterTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\Domain\Filter\UpperCaseFilter - */ - protected $subject = null; - - /** - * Set up - */ - protected function setUp() - { - $this->subject = new \TYPO3\CMS\Form\Domain\Filter\UpperCaseFilter(); - $GLOBALS['TSFE'] = new \stdClass(); - $GLOBALS['TSFE']->csConvObj = new \TYPO3\CMS\Core\Charset\CharsetConverter(); - } - - public function stringProvider() - { - return [ - 'asdf' => ['asdf', 'ASDF'], - 'as?df' => ['as?df', 'AS?DF'], - ]; - } - - /** - * @test - * @dataProvider stringProvider - */ - public function filterForStringReturnsUppercasedString($input, $expected) - { - $this->assertSame( - $expected, - $this->subject->filter($input) - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithFormPrefixFixture.php b/typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithFormPrefixFixture.php deleted file mode 100644 index eb7df1475aa1..000000000000 --- a/typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithFormPrefixFixture.php +++ /dev/null @@ -1,47 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Fixtures; - -/* - * 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\Form\PostProcess\AbstractPostProcessor; -use TYPO3\CMS\Form\PostProcess\PostProcessorInterface; - -/** - * Post processor with form prefix fixture - */ -class PostProcessorWithFormPrefixFixture extends AbstractPostProcessor implements PostProcessorInterface -{ - /** - * @param \TYPO3\CMS\Form\Domain\Model\Element $form - * @param array $typoScript - */ - public function __construct(\TYPO3\CMS\Form\Domain\Model\Element $form, array $typoScript) - { - } - - /** - * @param \TYPO3\CMS\Form\Mvc\Controller\ControllerContext $controllerContext - */ - public function setControllerContext(\TYPO3\CMS\Form\Mvc\Controller\ControllerContext $controllerContext) - { - } - - /** - * @return string - */ - public function process() - { - return 'processedWithPrefix'; - } -} diff --git a/typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithoutFormPrefixFixture.php b/typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithoutFormPrefixFixture.php deleted file mode 100644 index 2bfb626408ab..000000000000 --- a/typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithoutFormPrefixFixture.php +++ /dev/null @@ -1,40 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Fixtures; - -/* - * 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\Form\PostProcess\AbstractPostProcessor; -use TYPO3\CMS\Form\PostProcess\PostProcessorInterface; - -/** - * Post processor without form prefix fixture - */ -class PostProcessorWithoutFormPrefixFixture extends AbstractPostProcessor implements PostProcessorInterface -{ - /** - * @param \TYPO3\CMS\Form\Domain\Model\Element $form - * @param array $typoScript - */ - public function __construct(\TYPO3\CMS\Form\Domain\Model\Element $form, array $typoScript) - { - } - - /** - * @return string - */ - public function process() - { - return 'processedWithoutPrefix'; - } -} diff --git a/typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithoutInterfaceFixture.php b/typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithoutInterfaceFixture.php deleted file mode 100644 index 5b49b62e84e3..000000000000 --- a/typo3/sysext/form/Tests/Unit/Fixtures/PostProcessorWithoutInterfaceFixture.php +++ /dev/null @@ -1,37 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Fixtures; - -/* - * 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! - */ - -/** - * Post processor with form prefix fixture - */ -class PostProcessorWithoutInterfaceFixture -{ - /** - * @param \TYPO3\CMS\Form\Domain\Model\Element $form - * @param array $typoScript - */ - public function __construct(\TYPO3\CMS\Form\Domain\Model\Element $form, array $typoScript) - { - } - - /** - * @return string - */ - public function process() - { - return 'withoutInterface'; - } -} diff --git a/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php b/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php new file mode 100644 index 000000000000..bea5013271e7 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Hooks/DataStructureIdentifierHookTest.php @@ -0,0 +1,240 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Hooks; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Form\Hooks\DataStructureIdentifierHook; +use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager; +use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManagerInterface; + +/** + * Test case + */ +class DataStructureIdentifierHookTest extends UnitTestCase +{ + /** + * @var array A backup of registered singleton instances + */ + protected $singletonInstances = []; + + /** + * Set up + */ + public function setUp() + { + $this->singletonInstances = GeneralUtility::getSingletonInstances(); + } + + /** + * Tear down + */ + public function tearDown() + { + GeneralUtility::resetSingletonInstances($this->singletonInstances); + parent::tearDown(); + } + + /** + * @test + */ + public function getDataStructureIdentifierPostProcessReturnsIdentifierForNotMatchingScenario() + { + $givenIdentifier = ['aKey' => 'aValue']; + $result = (new DataStructureIdentifierHook())->getDataStructureIdentifierPostProcess( + [], 'aTable', 'aField', [], $givenIdentifier + ); + $this->assertEquals($givenIdentifier, $result); + } + + /** + * @test + */ + public function getDataStructureIdentifierPostProcessAddDefaultValuesForNewRecord() + { + $result = (new DataStructureIdentifierHook())->getDataStructureIdentifierPostProcess( + [], 'tt_content', 'pi_flexform', ['CType' => 'form_formframework'], [] + ); + $this->assertEquals( + ['ext-form-persistenceIdentifier' => '', 'ext-form-overrideFinishers' => false], + $result + ); + } + + /** + * @test + */ + public function getDataStructureIdentifierPostProcessAddsGivenPersistenceIdentifier() + { + $row = [ + 'CType' => 'form_formframework', + 'pi_flexform' => '<?xml version="1.0" encoding="utf-8" standalone="yes" ?> + <T3FlexForms> + <data> + <sheet index="sDEF"> + <language index="lDEF"> + <field index="settings.persistenceIdentifier"> + <value index="vDEF">1:user_upload/karl.yml</value> + </field> + </language> + </sheet> + </data> + </T3FlexForms> + ', + ]; + $incomingIdentifier = [ + 'aKey' => 'aValue', + ]; + $expected = [ + 'aKey' => 'aValue', + 'ext-form-persistenceIdentifier' => '1:user_upload/karl.yml', + 'ext-form-overrideFinishers' => false, + ]; + $result = (new DataStructureIdentifierHook())->getDataStructureIdentifierPostProcess( + [], 'tt_content', 'pi_flexform', $row, $incomingIdentifier + ); + $this->assertEquals($expected, $result); + } + + /** + * @test + */ + public function getDataStructureIdentifierPostProcessAddsOverrideFinisherValue() + { + $row = [ + 'CType' => 'form_formframework', + 'pi_flexform' => '<?xml version="1.0" encoding="utf-8" standalone="yes" ?> + <T3FlexForms> + <data> + <sheet index="sDEF"> + <language index="lDEF"> + <field index="settings.overrideFinishers"> + <value index="vDEF">1</value> + </field> + </language> + </sheet> + </data> + </T3FlexForms> + ', + ]; + $expected = [ + 'ext-form-persistenceIdentifier' => '', + 'ext-form-overrideFinishers' => true, + ]; + $result = (new DataStructureIdentifierHook())->getDataStructureIdentifierPostProcess( + [], 'tt_content', 'pi_flexform', $row, [] + ); + $this->assertEquals($expected, $result); + } + + /** + * @test + */ + public function parseDataStructureByIdentifierPostProcessReturnsDataStructureUnchanged() + { + $dataStructure = ['foo' => 'bar']; + $expected = $dataStructure; + $result = (new DataStructureIdentifierHook())->parseDataStructureByIdentifierPostProcess( + $dataStructure, [] + ); + $this->assertEquals($expected, $result); + } + + /** + * @test + */ + public function parseDataStructureByIdentifierPostProcessAddsExistingFormItems() + { + $objectMangerProphecy = $this->prophesize(ObjectManager::class); + GeneralUtility::setSingletonInstance(ObjectManager::class, $objectMangerProphecy->reveal()); + $formPersistenceManagerProphecy = $this->prophesize(FormPersistenceManager::class); + $objectMangerProphecy->get(FormPersistenceManagerInterface::class) + ->willReturn($formPersistenceManagerProphecy->reveal()); + + $existingForms = [ + [ + 'persistenceIdentifier' => 'hugo1', + 'name' => 'myHugo1', + ], + [ + 'persistenceIdentifier' => 'hugo2', + 'name' => 'myHugo2', + ] + ]; + $formPersistenceManagerProphecy->listForms()->shouldBeCalled()->willReturn($existingForms); + + $incomingDataStructure = [ + 'sheets' => [ + 'sDEF' => [ + 'ROOT' => [ + 'el' => [ + 'settings.persistenceIdentifier' => [ + 'TCEforms' => [ + 'config' => [ + 'items' => [ + 0 => [ + 0 => 'default, no value', + 1 => '', + ], + ], + ], + ], + ], + ], + ], + ], + ], + ]; + + $expected = [ + 'sheets' => [ + 'sDEF' => [ + 'ROOT' => [ + 'el' => [ + 'settings.persistenceIdentifier' => [ + 'TCEforms' => [ + 'config' => [ + 'items' => [ + 0 => [ + 0 => 'default, no value', + 1 => '', + ], + 1 => [ + 0 => 'myHugo1 (hugo1)', + 1 => 'hugo1', + ], + 2 => [ + 0 => 'myHugo2 (hugo2)', + 1 => 'hugo2', + ], + ], + ], + ], + ], + ], + ], + ], + ], + ]; + + $result = (new DataStructureIdentifierHook())->parseDataStructureByIdentifierPostProcess( + $incomingDataStructure, + ['ext-form-persistenceIdentifier' => ''] + ); + + $this->assertEquals($expected, $result); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Header.yaml b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Header.yaml new file mode 100644 index 000000000000..ab19ab69cdc8 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Header.yaml @@ -0,0 +1,6 @@ +# Header 1 +# Header 2 + +yaml + +# Comment \ No newline at end of file diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Invalid.yaml b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Invalid.yaml new file mode 100644 index 000000000000..261338580151 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/Fixtures/Invalid.yaml @@ -0,0 +1,2 @@ +key + yek \ No newline at end of file diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/InheritancesResolverServiceTest.php b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/InheritancesResolverServiceTest.php new file mode 100644 index 000000000000..0e7b33216a6a --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/InheritancesResolverServiceTest.php @@ -0,0 +1,429 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Configuration; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Form\Mvc\Configuration\Exception\CycleInheritancesException; +use TYPO3\CMS\Form\Mvc\Configuration\InheritancesResolverService; + +/** + * Test case + */ +class InheritancesResolverServiceTest extends UnitTestCase +{ + /** + * @var InheritancesResolverService + */ + protected $subject; + + protected function setUp() + { + $this->subject = new InheritancesResolverService(); + } + + /** + * @test + */ + public function getMergedConfigurationSimpleInheritance() + { + $input = [ + 'Form' => [ + 'klaus01' => [ + 'key01' => 'value', + 'key02' => [ + 'key03' => 'value', + ], + ], + 'klaus02' => [ + '__inheritances' => [ + 10 => 'Form.klaus01', + ], + ], + ], + ]; + + $expected = [ + 'Form' => [ + 'klaus01' => [ + 'key01' => 'value', + 'key02' => [ + 'key03' => 'value' + ], + ], + 'klaus02' => [ + 'key01' => 'value', + 'key02' => [ + 'key03' => 'value', + ], + ], + ], + ]; + + $this->assertSame($expected, $this->subject->reset()->setReferenceConfiguration($input)->getResolvedConfiguration($input)); + } + + /** + * @test + */ + public function getMergedConfigurationSimpleInheritanceOverrideValue() + { + $input = [ + 'Form' => [ + 'klaus01' => [ + 'key' => 'value', + ], + 'klaus02' => [ + '__inheritances' => [ + 10 => 'Form.klaus01', + ], + 'key' => 'value override', + ], + ], + ]; + + $expected = [ + 'Form' => [ + 'klaus01' => [ + 'key' => 'value', + ], + 'klaus02' => [ + 'key' => 'value override', + ], + ], + ]; + + $this->assertSame($expected, $this->subject->reset()->setReferenceConfiguration($input)->getResolvedConfiguration($input)); + } + + /** + * @test + */ + public function getMergedConfigurationSimpleInheritanceRemoveValue() + { + $input = [ + 'Form' => [ + 'klaus01' => [ + 'key01' => [ + 'key02' => 'value', + ], + 'key02' => [ + 10 => [ + 'key' => 'value', + ], + 20 => [ + 'key' => 'value', + ], + ], + ], + 'klaus02' => [ + '__inheritances' => [ + 10 => 'Form.klaus01', + ], + 'key01' => null, + 'key02' => [ + 10 => null, + 20 => [ + 'key' => null, + ], + ], + ], + ], + ]; + + $expected = [ + 'Form' => [ + 'klaus01' => [ + 'key01' => [ + 'key02' => 'value', + ], + 'key02' => [ + 10 => [ + 'key' => 'value', + ], + 20 => [ + 'key' => 'value', + ], + ], + ], + 'klaus02' => [ + 'key01' => null, + 'key02' => [ + 10 => null, + 20 => [ + 'key' => null, + ], + ], + ], + ], + ]; + + $this->assertSame($expected, $this->subject->reset()->setReferenceConfiguration($input)->getResolvedConfiguration($input)); + } + + /** + * @test + */ + public function getMergedConfigurationSimpleMixin() + { + $input = [ + 'Form' => [ + 'mixin01' => [ + 'key' => 'value', + ], + 'klaus01' => [ + '__inheritances' => [ + 10 => 'Form.mixin01', + ], + ], + 'klaus02' => [ + 'key' => [ + '__inheritances' => [ + 10 => 'Form.mixin01', + ], + ], + ], + ], + ]; + + $expected = [ + 'Form' => [ + 'mixin01' => [ + 'key' => 'value', + ], + 'klaus01' => [ + 'key' => 'value', + ], + 'klaus02' => [ + 'key' => [ + 'key' => 'value', + ], + ], + ], + ]; + + $this->assertSame($expected, $this->subject->reset()->setReferenceConfiguration($input)->getResolvedConfiguration($input)); + } + + /** + * @test + */ + public function getMergedConfigurationAdvancedMixin() + { + $input = [ + 'Form' => [ + 'mixin01' => [ + 'key01' => 'value01', + 'key02' => 'value02', + ], + 'mixin02' => [ + '__inheritances' => [ + 10 => 'Form.mixin01', + ], + ], + 'mixin03' => [ + 'key03' => 'value03', + ], + + 'klaus01' => [ + '__inheritances' => [ + 10 => 'Form.mixin01', + ], + 'key01' => 'value01 override 01', + ], + 'klaus02' => [ + '__inheritances' => [ + 10 => 'Form.klaus01', + 20 => 'Form.mixin03', + ], + 'key01' => 'value01 override 02', + 'key02' => [ + 'horst01' => 'gerda01' + ], + 'key03' => [ + '__inheritances' => [ + 10 => 'Form.mixin02', + ], + 'key02' => null, + ], + ], + 'klaus03' => [ + '__inheritances' => [ + 10 => 'Form.klaus02', + ], + ], + ], + ]; + + $expected = [ + 'Form' => [ + 'mixin01' => [ + 'key01' => 'value01', + 'key02' => 'value02', + ], + 'mixin02' => [ + 'key01' => 'value01', + 'key02' => 'value02', + ], + 'mixin03' => [ + 'key03' => 'value03', + ], + 'klaus01' => [ + 'key01' => 'value01 override 01', + 'key02' => 'value02', + ], + 'klaus02' => [ + 'key01' => 'value01 override 02', + 'key02' => [ + 'horst01' => 'gerda01' + ], + 'key03' => [ + 'key01' => 'value01', + 'key02' => null, + ], + ], + 'klaus03' => [ + 'key01' => 'value01 override 02', + 'key02' => [ + 'horst01' => 'gerda01' + ], + 'key03' => [ + 'key01' => 'value01', + 'key02' => null, + ], + ], + ], + ]; + + $this->assertSame($expected, $this->subject->reset()->setReferenceConfiguration($input)->getResolvedConfiguration($input)); + } + + /** + * @test + */ + public function getResolvedConfigurationThrowsExceptionIfCycleDepenciesOnSameLevelIsFound() + { + $input = [ + 'TYPO3' => [ + 'CMS' => [ + 'Form' => [ + 'someKey' => [ + '__inheritances' => [ + 10 => 'TYPO3.CMS.Form.anotherKey', + ], + ], + 'anotherKey' => [ + '__inheritances' => [ + 10 => 'TYPO3.CMS.Form.someKey', + ], + ], + ], + ], + ], + ]; + + $this->expectException(CycleInheritancesException::class); + $this->expectExceptionCode(1474900797); + + $this->subject->reset()->setReferenceConfiguration($input)->getResolvedConfiguration($input); + } + + /** + * @test + */ + public function getResolvedConfigurationThrowsExceptionIfCycleDepenciesOnSameLevelWithGapIsFound() + { + $input = [ + 'TYPO3' => [ + 'CMS' => [ + 'Form' => [ + 'klaus1' => [ + '__inheritances' => [ + 10 => 'TYPO3.CMS.Form.klaus2', + ], + ], + 'klaus2' => [ + '__inheritances' => [ + 10 => 'TYPO3.CMS.Form.mixin1', + ], + ], + 'mixin1' => [ + '__inheritances' => [ + 10 => 'TYPO3.CMS.Form.mixin2', + ], + ], + 'mixin2' => [ + '__inheritances' => [ + 10 => 'TYPO3.CMS.Form.klaus2', + ], + ], + ], + ], + ], + ]; + + $this->expectException(CycleInheritancesException::class); + $this->expectExceptionCode(1474900799); + + $this->subject->reset()->setReferenceConfiguration($input)->getResolvedConfiguration($input); + } + + /** + * @test + */ + public function getResolvedConfigurationThrowsExceptionIfCycleDepenciesOnHigherLevelIsFound() + { + $input = [ + 'TYPO3' => [ + 'CMS' => [ + 'Form' => [ + 'klaus1' => [ + 'key01' => 'value', + 'key02' => [ + '__inheritances' => [ + 10 => 'TYPO3.CMS.Form.mixin01', + ], + ], + ], + 'klaus2' => [ + '__inheritances' => [ + 10 => 'TYPO3.CMS.Form.klaus1', + ], + 'key02' => [ + '__inheritances' => [ + 10 => 'TYPO3.CMS.Form.mixin01', + 20 => 'TYPO3.CMS.Form.mixin02', + ], + ], + ], + 'mixin01' => [ + 'liselotte01' => 'value', + ], + 'mixin02' => [ + '__inheritances' => [ + 10 => 'TYPO3.CMS.Form.klaus2', + ], + 'liselotte02' => 'value', + ], + ], + ], + ], + ]; + + $this->expectException(CycleInheritancesException::class); + $this->expectExceptionCode(1474900797); + + $this->subject->reset()->setReferenceConfiguration($input)->getResolvedConfiguration($input); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/TypoScriptServiceTest.php b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/TypoScriptServiceTest.php new file mode 100644 index 000000000000..31e89dbbbedd --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/TypoScriptServiceTest.php @@ -0,0 +1,69 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Configuration; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Form\Mvc\Configuration\TypoScriptService; +use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer; + +/** + * Test case + */ +class TypoScriptServiceTest extends UnitTestCase +{ + /** + * @test + */ + public function resolveTypoScriptConfigurationReturnsResolvedConfiguration() + { + $mockTypoScriptService = $this->getAccessibleMock(TypoScriptService::class, [ + 'getTypoScriptFrontendController' + ], [], '', false); + + $mockContentObjectRenderer = $this->getMockBuilder( + ContentObjectRenderer::class + )->getMock(); + + $fakeTypoScriptFrontendController = new \stdClass; + $fakeTypoScriptFrontendController->cObj = $mockContentObjectRenderer; + + $mockContentObjectRenderer + ->expects($this->any()) + ->method('cObjGetSingle') + ->with('TEXT', ['value' => 'rambo']) + ->will($this->returnValue('rambo')); + + $mockTypoScriptService + ->expects($this->any()) + ->method('getTypoScriptFrontendController') + ->willReturn($fakeTypoScriptFrontendController); + + $input = [ + 'key.' => [ + 'john' => 'TEXT', + 'john.' => [ + 'value' => 'rambo' + ], + ], + ]; + $expected = [ + 'key' => [ + 'john' => 'rambo', + ], + ]; + + $this->assertSame($expected, $mockTypoScriptService->_call('resolveTypoScriptConfiguration', $input)); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Configuration/YamlSourceTest.php b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/YamlSourceTest.php new file mode 100644 index 000000000000..faf1ab90929a --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Mvc/Configuration/YamlSourceTest.php @@ -0,0 +1,107 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Configuration; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Form\Mvc\Configuration\Exception\NoSuchFileException; +use TYPO3\CMS\Form\Mvc\Configuration\Exception\ParseErrorException; +use TYPO3\CMS\Form\Mvc\Configuration\YamlSource; + +/** + * Test case + */ +class YamlSourceTest extends UnitTestCase +{ + + /** + * @test + */ + public function loadThrowsExceptionIfFileToLoadNotExists() + { + $this->expectException(NoSuchFileException::class); + $this->expectExceptionCode(1471473378); + + $mockYamlSource = $this->getAccessibleMock(YamlSource::class, [ + 'dummy', + ], [], '', false); + + $input = [ + 'EXT:form/Resources/Forms/_example.yaml' + ]; + + $mockYamlSource->_call('load', $input); + } + + /** + * @test + */ + public function loadThrowsExceptionIfFileToLoadIsNotValidYamlUseSymfonyParser() + { + if (!extension_loaded('yaml')) { + $this->expectException(ParseErrorException::class); + $this->expectExceptionCode(1480195405); + + $mockYamlSource = $this->getAccessibleMock(YamlSource::class, [ + 'dummy', + ], [], '', false); + + $input = [ + 'EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/Invalid.yaml' + ]; + + $mockYamlSource->_call('load', $input); + } + } + + /** + * @test + */ + public function loadThrowsExceptionIfFileToLoadIsNotValidYamlUsePhpExtensionParser() + { + if (extension_loaded('yaml')) { + $this->expectException(ParseErrorException::class); + $this->expectExceptionCode(1391894094); + + $mockYamlSource = $this->getAccessibleMock(YamlSource::class, [ + 'dummy', + ], [], '', false); + + $input = [ + 'EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/Invalid.yaml' + ]; + + $mockYamlSource->_call('load', $input); + } + } + + /** + * @test + */ + public function getHeaderFromFileReturnsHeaderPart() + { + $mockYamlSource = $this->getAccessibleMock(YamlSource::class, [ + 'dummy', + ], [], '', false); + + $input = GeneralUtility::getFileAbsFileName('EXT:form/Tests/Unit/Mvc/Configuration/Fixtures/Header.yaml'); + $expected = +'# Header 1 +# Header 2 +'; + + $this->assertSame($expected, $mockYamlSource->_call('getHeaderFromFile', $input)); + } +} diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/ContentElement.html b/typo3/sysext/form/Tests/Unit/Mvc/Persistence/Fixtures/BlankForm.txt similarity index 100% rename from typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/ContentElement.html rename to typo3/sysext/form/Tests/Unit/Mvc/Persistence/Fixtures/BlankForm.txt diff --git a/typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Header.html b/typo3/sysext/form/Tests/Unit/Mvc/Persistence/Fixtures/BlankForm.yaml similarity index 100% rename from typo3/sysext/form/Resources/Private/Partials/Default/Confirmation/FlatElements/Header.html rename to typo3/sysext/form/Tests/Unit/Mvc/Persistence/Fixtures/BlankForm.yaml diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Persistence/FormPersistenceManagerTest.php b/typo3/sysext/form/Tests/Unit/Mvc/Persistence/FormPersistenceManagerTest.php new file mode 100644 index 000000000000..c4c32805db78 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Mvc/Persistence/FormPersistenceManagerTest.php @@ -0,0 +1,617 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Persistence; + +/* + * 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\Core\Resource\File; +use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Resource\StorageRepository; +use TYPO3\CMS\Core\Tests\UnitTestCase; +use TYPO3\CMS\Form\Mvc\Persistence\Exception\NoUniqueIdentifierException; +use TYPO3\CMS\Form\Mvc\Persistence\Exception\PersistenceManagerException; +use TYPO3\CMS\Form\Mvc\Persistence\FormPersistenceManager; + +/** + * Test case + */ +class FormPersistenceManagerTest extends UnitTestCase +{ + + /** + * @test + */ + public function loadThrowsExceptionIfPersistenceIdentifierHasNoYamlExtension() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1477679819); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'dummy' + ], [], '', false); + + $input = '-1:/user_uploads/_example.php'; + $mockFormPersistenceManager->_call('load', $input); + } + + /** + * @test + */ + public function saveThrowsExceptionIfPersistenceIdentifierHasNoYamlExtension() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1477679820); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'dummy' + ], [], '', false); + + $input = '-1:/user_uploads/_example.php'; + $mockFormPersistenceManager->_call('save', $input, []); + } + + /** + * @test + */ + public function saveThrowsExceptionIfPersistenceIdentifierIsAExtensionLocationAndSaveToExtensionLocationIsNotAllowed() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1477680881); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'dummy' + ], [], '', false); + + $mockFormPersistenceManager->_set('formSettings', [ + 'persistenceManager' => [ + 'allowSaveToExtensionPaths' => false, + ], + ]); + + $input = 'EXT:form/Resources/Forms/_example.yaml'; + $mockFormPersistenceManager->_call('save', $input, []); + } + + /** + * @test + */ + public function deleteThrowsExceptionIfPersistenceIdentifierHasNoYamlExtension() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1472239534); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'dummy' + ], [], '', false); + + $input = '-1:/user_uploads/_example.php'; + $mockFormPersistenceManager->_call('delete', $input); + } + + /** + * @test + */ + public function deleteThrowsExceptionIfPersistenceIdentifierFileDoesNotExists() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1472239535); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'exists' + ], [], '', false); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('exists') + ->willReturn(false); + + $input = '-1:/user_uploads/_example.yaml'; + $mockFormPersistenceManager->_call('delete', $input); + } + + /** + * @test + */ + public function deleteThrowsExceptionIfPersistenceIdentifierIsExtensionLocation() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1472239536); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'exists' + ], [], '', false); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('exists') + ->willReturn(true); + + $input = 'EXT:form/Resources/Forms/_example.yaml'; + $mockFormPersistenceManager->_call('delete', $input); + } + + /** + * @test + */ + public function deleteThrowsExceptionIfPersistenceIdentifierIsStorageLocationAndDeleteFromStorageIsNotAllowed() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1472239516); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'getStorageByUid', + 'exists' + ], [], '', false); + + $mockStorage = $this->getMockBuilder(ResourceStorage::class) + ->disableOriginalConstructor() + ->getMock(); + + $mockStorage + ->expects($this->any()) + ->method('checkFileActionPermission') + ->willReturn(false); + + $file = new File(['identifier' => '', 'mime_type' => ''], $mockStorage); + $mockStorage + ->expects($this->any()) + ->method('getFile') + ->willReturn($file); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('getStorageByUid') + ->willReturn($mockStorage); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('exists') + ->willReturn(true); + + $input = '-1:/user_uploads/_example.yaml'; + $mockFormPersistenceManager->_call('delete', $input); + } + + /** + * @test + */ + public function existsReturnsTrueIfPersistenceIdentifierIsExtensionLocationAndFileExistsAndFileHasYamlExtension() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'dummy' + ], [], '', false); + + $input = 'EXT:form/Tests/Unit/Mvc/Persistence/Fixtures/BlankForm.yaml'; + $this->assertTrue($mockFormPersistenceManager->_call('exists', $input)); + } + + /** + * @test + */ + public function existsReturnsFalseIfPersistenceIdentifierIsExtensionLocationAndFileExistsAndFileHasNoYamlExtension() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'dummy' + ], [], '', false); + + $input = 'EXT:form/Tests/Unit/Mvc/Persistence/Fixtures/BlankForm.txt'; + $this->assertFalse($mockFormPersistenceManager->_call('exists', $input)); + } + + /** + * @test + */ + public function existsReturnsFalseIfPersistenceIdentifierIsExtensionLocationAndFileNotExistsAndFileHasYamlExtension() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'dummy' + ], [], '', false); + + $input = 'EXT:form/Tests/Unit/Mvc/Persistence/Fixtures/_BlankForm.yaml'; + $this->assertFalse($mockFormPersistenceManager->_call('exists', $input)); + } + + /** + * @test + */ + public function existsReturnsTrueIfPersistenceIdentifierIsStorageLocationAndFileExistsAndFileHasYamlExtension() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'getStorageByUid' + ], [], '', false); + + $mockStorage = $this->getMockBuilder(ResourceStorage::class) + ->disableOriginalConstructor() + ->getMock(); + $mockStorage + ->expects($this->any()) + ->method('hasFile') + ->willReturn(true); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('getStorageByUid') + ->willReturn($mockStorage); + + $input = '-1:/user_uploads/_example.yaml'; + $this->assertTrue($mockFormPersistenceManager->_call('exists', $input)); + } + + /** + * @test + */ + public function existsReturnsFalseIfPersistenceIdentifierIsStorageLocationAndFileExistsAndFileNoYamlExtension() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'getStorageByUid' + ], [], '', false); + + $mockStorage = $this->getMockBuilder(ResourceStorage::class) + ->disableOriginalConstructor() + ->getMock(); + $mockStorage + ->expects($this->any()) + ->method('hasFile') + ->willReturn(true); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('getStorageByUid') + ->willReturn($mockStorage); + + $input = '-1:/user_uploads/_example.php'; + $this->assertFalse($mockFormPersistenceManager->_call('exists', $input)); + } + + /** + * @test + */ + public function existsReturnsFalseIfPersistenceIdentifierIsStorageLocationAndFileNotExistsAndFileHasYamlExtension() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'getStorageByUid' + ], [], '', false); + + $mockStorage = $this->getMockBuilder(ResourceStorage::class) + ->disableOriginalConstructor() + ->getMock(); + $mockStorage + ->expects($this->any()) + ->method('hasFile') + ->willReturn(false); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('getStorageByUid') + ->willReturn($mockStorage); + + $input = '-1:/user_uploads/_example.yaml'; + $this->assertFalse($mockFormPersistenceManager->_call('exists', $input)); + } + + /** + * @test + */ + public function getUniquePersistenceIdentifierAppendNumberIfPersistenceIdentifierExists() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'exists' + ], [], '', false); + + $mockFormPersistenceManager + ->expects($this->at(0)) + ->method('exists') + ->willReturn(true); + + $mockFormPersistenceManager + ->expects($this->at(1)) + ->method('exists') + ->willReturn(true); + + $mockFormPersistenceManager + ->expects($this->at(2)) + ->method('exists') + ->willReturn(false); + + $input = 'example'; + $expected = '-1:/user_uploads/example_2.yaml'; + $this->assertSame($expected, $mockFormPersistenceManager->_call('getUniquePersistenceIdentifier', $input, '-1:/user_uploads/')); + } + + /** + * @test + */ + public function getUniquePersistenceIdentifierAppendTimestampIfPersistenceIdentifierExists() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'exists' + ], [], '', false); + + for ($attempts = 0; $attempts <= 99; $attempts++) { + $mockFormPersistenceManager + ->expects($this->at($attempts)) + ->method('exists') + ->willReturn(true); + } + + $mockFormPersistenceManager + ->expects($this->at(100)) + ->method('exists') + ->willReturn(false); + + $input = 'example'; + $expected = '#^-1:/user_uploads/example_([0-9]{10}).yaml$#'; + + $returnValue = $mockFormPersistenceManager->_call('getUniquePersistenceIdentifier', $input, '-1:/user_uploads/'); + $this->assertEquals(1, preg_match($expected, $returnValue)); + } + + /** + * @test + */ + public function getUniqueIdentifierThrowsExceptionIfIdentifierExists() + { + $this->expectException(NoUniqueIdentifierException::class); + $this->expectExceptionCode(1477688567); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'checkForDuplicateIdentifier' + ], [], '', false); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('checkForDuplicateIdentifier') + ->willReturn(true); + + $input = 'example'; + $mockFormPersistenceManager->_call('getUniqueIdentifier', $input); + } + + /** + * @test + */ + public function getUniqueIdentifierAppendTimestampIfIdentifierExists() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'checkForDuplicateIdentifier' + ], [], '', false); + + for ($attempts = 0; $attempts <= 99; $attempts++) { + $mockFormPersistenceManager + ->expects($this->at($attempts)) + ->method('checkForDuplicateIdentifier') + ->willReturn(true); + } + + $mockFormPersistenceManager + ->expects($this->at(100)) + ->method('checkForDuplicateIdentifier') + ->willReturn(false); + + $input = 'example'; + $expected = '#^example_([0-9]{10})$#'; + + $returnValue = $mockFormPersistenceManager->_call('getUniqueIdentifier', $input); + $this->assertEquals(1, preg_match($expected, $returnValue)); + } + + /** + * @test + */ + public function checkForDuplicateIdentifierReturnsTrueIfIdentifierIsUsed() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'listForms' + ], [], '', false); + + $mockFormPersistenceManager + ->expects($this->at($attempts)) + ->method('listForms') + ->willReturn([ + 0 => [ + 'identifier' => 'example', + ], + ]); + + $input = 'example'; + $this->assertTrue($mockFormPersistenceManager->_call('checkForDuplicateIdentifier', $input)); + } + + /** + * @test + */ + public function checkForDuplicateIdentifierReturnsFalseIfIdentifierIsUsed() + { + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'listForms' + ], [], '', false); + + $mockFormPersistenceManager + ->expects($this->at($attempts)) + ->method('listForms') + ->willReturn([ + 0 => [ + 'identifier' => 'example', + ], + ]); + + $input = 'other-example'; + $this->assertFalse($mockFormPersistenceManager->_call('checkForDuplicateIdentifier', $input)); + } + + /** + * @test + */ + public function getFileByIdentifierThrowsExceptionIfReadFromStorageIsNotAllowed() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1471630578); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'getStorageByUid', + ], [], '', false); + + $mockStorage = $this->getMockBuilder(ResourceStorage::class) + ->disableOriginalConstructor() + ->getMock(); + + $mockStorage + ->expects($this->any()) + ->method('checkFileActionPermission') + ->willReturn(false); + + $file = new File(['identifier' => '', 'mime_type' => ''], $mockStorage); + $mockStorage + ->expects($this->any()) + ->method('getFile') + ->willReturn($file); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('getStorageByUid') + ->willReturn($mockStorage); + + $input = '-1:/user_uploads/example.yaml'; + $mockFormPersistenceManager->_call('getFileByIdentifier', $input); + } + + /** + * @test + */ + public function getOrCreateFileThrowsExceptionIfFolderNotExistsInStorage() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1471630579); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'getStorageByUid', + ], [], '', false); + + $mockStorage = $this->getMockBuilder(ResourceStorage::class) + ->disableOriginalConstructor() + ->getMock(); + + $mockStorage + ->expects($this->any()) + ->method('hasFolder') + ->willReturn(false); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('getStorageByUid') + ->willReturn($mockStorage); + + $input = '-1:/user_uploads/example.yaml'; + $mockFormPersistenceManager->_call('getOrCreateFile', $input); + } + + /** + * @test + */ + public function getOrCreateFileThrowsExceptionIfWriteToStorageIsNotAllowed() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1471630580); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'getStorageByUid', + ], [], '', false); + + $mockStorage = $this->getMockBuilder(ResourceStorage::class) + ->disableOriginalConstructor() + ->getMock(); + + $mockStorage + ->expects($this->any()) + ->method('hasFolder') + ->willReturn(true); + + $mockStorage + ->expects($this->any()) + ->method('checkFileActionPermission') + ->willReturn(false); + + $file = new File(['identifier' => '', 'mime_type' => ''], $mockStorage); + $mockStorage + ->expects($this->any()) + ->method('getFile') + ->willReturn($file); + + $mockFormPersistenceManager + ->expects($this->any()) + ->method('getStorageByUid') + ->willReturn($mockStorage); + + $input = '-1:/user_uploads/example.yaml'; + $mockFormPersistenceManager->_call('getOrCreateFile', $input); + } + + /** + * @test + */ + public function getStorageByUidThrowsExceptionIfStorageNotExists() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1471630581); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'dummy', + ], [], '', false); + + $mockStorageRepository = $this->getMockBuilder(StorageRepository::class) + ->disableOriginalConstructor() + ->getMock(); + + $mockStorageRepository + ->expects($this->any()) + ->method('findByUid') + ->willReturn(null); + + $mockFormPersistenceManager->_set('storageRepository', $mockStorageRepository); + $mockFormPersistenceManager->_call('getStorageByUid', -1); + } + + /** + * @test + */ + public function getStorageByUidThrowsExceptionIfStorageIsNotBrowsable() + { + $this->expectException(PersistenceManagerException::class); + $this->expectExceptionCode(1471630581); + + $mockFormPersistenceManager = $this->getAccessibleMock(FormPersistenceManager::class, [ + 'dummy', + ], [], '', false); + + $mockStorageRepository = $this->getMockBuilder(StorageRepository::class) + ->disableOriginalConstructor() + ->getMock(); + + $mockStorage = $this->getMockBuilder(ResourceStorage::class) + ->disableOriginalConstructor() + ->getMock(); + + $mockStorage + ->expects($this->any()) + ->method('isBrowsable') + ->willReturn(false); + + $mockStorageRepository + ->expects($this->any()) + ->method('findByUid') + ->willReturn($mockStorage); + + $mockFormPersistenceManager->_set('storageRepository', $mockStorageRepository); + $mockFormPersistenceManager->_call('getStorageByUid', -1); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Mvc/ProcessingRuleTest.php b/typo3/sysext/form/Tests/Unit/Mvc/ProcessingRuleTest.php new file mode 100644 index 000000000000..714658aaa8f9 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Mvc/ProcessingRuleTest.php @@ -0,0 +1,120 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Configuration; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Core\Utility\GeneralUtility; +use TYPO3\CMS\Extbase\Error\Result; +use TYPO3\CMS\Extbase\Object\ObjectManager; +use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator; +use TYPO3\CMS\Extbase\Validation\Validator\ConjunctionValidator; +use TYPO3\CMS\Form\Mvc\ProcessingRule; +use TYPO3\CMS\Form\Tests\Unit\Mvc\Validation\Fixtures\TestValidator; + +/** + * Test case + */ +class ProcessingRuleTest extends UnitTestCase +{ + + /** + * @var array A backup of registered singleton instances + */ + protected $singletonInstances = []; + + /** + * Set up + */ + public function setUp() + { + $this->singletonInstances = GeneralUtility::getSingletonInstances(); + } + + /** + * Tear down + */ + public function tearDown() + { + GeneralUtility::resetSingletonInstances($this->singletonInstances); + parent::tearDown(); + } + + /** + * @test + */ + public function addValidatorAddValidator() + { + $mockProcessingRule = $this->getAccessibleMock(ProcessingRule::class, [ + 'dummy' + ], [], '', false); + + $mockProcessingRule->_set('validator', new ConjunctionValidator([])); + $mockProcessingRule->addValidator(new TestValidator()); + $validators = $mockProcessingRule->_get('validator')->getValidators(); + $validators->rewind(); + $this->assertInstanceOf(AbstractValidator::class, $validators->current()); + } + + /** + * @test + */ + public function processNoPropertyMappingReturnsNotModifiedValue() + { + $objectMangerProphecy = $this->prophesize(ObjectManager::class); + GeneralUtility::setSingletonInstance(ObjectManager::class, $objectMangerProphecy->reveal()); + $resultProphecy = $this->prophesize(Result::class); + + $objectMangerProphecy + ->get(Result::class) + ->willReturn($resultProphecy->reveal()); + + $mockProcessingRule = $this->getAccessibleMock(ProcessingRule::class, [ + 'dummy' + ], [], '', false); + + $mockProcessingRule->_set('dataType', null); + $mockProcessingRule->_set('processingMessages', $resultProphecy->reveal()); + $mockProcessingRule->_set('validator', new ConjunctionValidator([])); + + $input = 'someValue'; + $this->assertSame($input, $mockProcessingRule->_call('process', $input)); + } + + /** + * @test + */ + public function processNoPropertyMappingAndHasErrorsIfValidatorContainsErrors() + { + $objectMangerProphecy = $this->prophesize(ObjectManager::class); + GeneralUtility::setSingletonInstance(ObjectManager::class, $objectMangerProphecy->reveal()); + + $objectMangerProphecy + ->get(Result::class) + ->willReturn(new Result); + + $mockProcessingRule = $this->getAccessibleMock(ProcessingRule::class, [ + 'dummy' + ], [], '', true); + + $mockProcessingRule->_set('dataType', null); + $mockProcessingRule->_set('validator', new ConjunctionValidator([])); + $mockProcessingRule->addValidator(new TestValidator()); + + $input = 'addError'; + $mockProcessingRule->_call('process', $input); + + $this->assertTrue($mockProcessingRule->_get('processingMessages')->hasErrors()); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Validation/CountValidatorTest.php b/typo3/sysext/form/Tests/Unit/Mvc/Validation/CountValidatorTest.php new file mode 100644 index 000000000000..bcff4b5c87a8 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Mvc/Validation/CountValidatorTest.php @@ -0,0 +1,120 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Validation; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Form\Mvc\Validation\CountValidator; + +/** + * Test case + */ +class CountValidatorTest extends UnitTestCase +{ + + /** + * @test + */ + public function CountValidatorReturnsFalseIfInputItemsCountIsEqualToMaximum() + { + $options = ['minimum' => 1, 'maximum' => 2]; + $validator = $this->getMockBuilder(CountValidator::class) + ->setMethods(['translateErrorMessage']) + ->setConstructorArgs([$options]) + ->getMock(); + + $input = [ + 'klaus', + 'steve' + ]; + + $this->assertFalse($validator->validate($input)->hasErrors()); + } + + /** + * @test + */ + public function CountValidatorReturnsFalseIfInputItemsCountIsEqualToMinimum() + { + $options = ['minimum' => 2, 'maximum' => 3]; + $validator = $this->getMockBuilder(CountValidator::class) + ->setMethods(['translateErrorMessage']) + ->setConstructorArgs([$options]) + ->getMock(); + + $input = [ + 'klaus', + 'steve' + ]; + + $this->assertFalse($validator->validate($input)->hasErrors()); + } + + /** + * @test + */ + public function CountValidatorReturnsFalseIfInputItemsCountIsEqualToMinimumAndMaximum() + { + $options = ['minimum' => 2, 'maximum' => 2]; + $validator = $this->getMockBuilder(CountValidator::class) + ->setMethods(['translateErrorMessage']) + ->setConstructorArgs([$options]) + ->getMock(); + + $input = [ + 'klaus', + 'steve' + ]; + + $this->assertFalse($validator->validate($input)->hasErrors()); + } + + /** + * @test + */ + public function CountValidatorReturnsTrueIfInputCountHasMoreItemsAsMaximumValue() + { + $options = ['minimum' => 1, 'maximum' => 2]; + $validator = $this->getMockBuilder(CountValidator::class) + ->setMethods(['translateErrorMessage']) + ->setConstructorArgs([$options]) + ->getMock(); + + $input = [ + 'klaus', + 'steve', + 'francine' + ]; + + $this->assertTrue($validator->validate($input)->hasErrors()); + } + + /** + * @test + */ + public function CountValidatorReturnsTrueIfInputCountHasLessItemsAsMinimumValue() + { + $options = ['minimum' => 2, 'maximum' => 3]; + $validator = $this->getMockBuilder(CountValidator::class) + ->setMethods(['translateErrorMessage']) + ->setConstructorArgs([$options]) + ->getMock(); + + $input = [ + 'klaus', + ]; + + $this->assertTrue($validator->validate($input)->hasErrors()); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Validation/EmptyValidatorTest.php b/typo3/sysext/form/Tests/Unit/Mvc/Validation/EmptyValidatorTest.php new file mode 100644 index 000000000000..fce017f58ec2 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Mvc/Validation/EmptyValidatorTest.php @@ -0,0 +1,123 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Validation; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Form\Mvc\Validation\EmptyValidator; + +/** + * Test case + */ +class EmptyValidatorTest extends UnitTestCase +{ + + /** + * @test + */ + public function EmptyValidatorReturnsFalseIfInputIsEmptyString() + { + $validator = $this->getMockBuilder(EmptyValidator::class) + ->setMethods(['translateErrorMessage']) + ->getMock(); + + $input = ''; + + $this->assertFalse($validator->validate($input)->hasErrors()); + } + + /** + * @test + */ + public function EmptyValidatorReturnsFalseIfInputIsNull() + { + $validator = $this->getMockBuilder(EmptyValidator::class) + ->setMethods(['translateErrorMessage']) + ->getMock(); + + $input = null; + + $this->assertFalse($validator->validate($input)->hasErrors()); + } + + /** + * @test + */ + public function EmptyValidatorReturnsFalseIfInputIsEmptyArray() + { + $validator = $this->getMockBuilder(EmptyValidator::class) + ->setMethods(['translateErrorMessage']) + ->getMock(); + + $input = []; + + $this->assertFalse($validator->validate($input)->hasErrors()); + } + + /** + * @test + */ + public function EmptyValidatorReturnsFalseIfInputIsZero() + { + $validator = $this->getMockBuilder(EmptyValidator::class) + ->setMethods(['translateErrorMessage']) + ->getMock(); + + $input = 0; + + $this->assertFalse($validator->validate($input)->hasErrors()); + } + + /** + * @test + */ + public function EmptyValidatorReturnsFalseIfInputIsZeroAsString() + { + $validator = $this->getMockBuilder(EmptyValidator::class) + ->setMethods(['translateErrorMessage']) + ->getMock(); + + $input = '0'; + + $this->assertFalse($validator->validate($input)->hasErrors()); + } + + /** + * @test + */ + public function EmptyValidatorReturnsTrueIfInputIsNonEmptyString() + { + $validator = $this->getMockBuilder(EmptyValidator::class) + ->setMethods(['translateErrorMessage']) + ->getMock(); + + $input = 'hellö'; + + $this->assertTrue($validator->validate($input)->hasErrors()); + } + + /** + * @test + */ + public function EmptyValidatorReturnsTrueIfInputIsNonEmptyArray() + { + $validator = $this->getMockBuilder(EmptyValidator::class) + ->setMethods(['translateErrorMessage']) + ->getMock(); + + $input = ['hellö']; + + $this->assertTrue($validator->validate($input)->hasErrors()); + } +} diff --git a/typo3/sysext/form/Classes/Domain/Filter/DigitFilter.php b/typo3/sysext/form/Tests/Unit/Mvc/Validation/Fixtures/TestValidator.php similarity index 54% rename from typo3/sysext/form/Classes/Domain/Filter/DigitFilter.php rename to typo3/sysext/form/Tests/Unit/Mvc/Validation/Fixtures/TestValidator.php index 95d49c510d93..1ddc62cea40f 100644 --- a/typo3/sysext/form/Classes/Domain/Filter/DigitFilter.php +++ b/typo3/sysext/form/Tests/Unit/Mvc/Validation/Fixtures/TestValidator.php @@ -1,5 +1,5 @@ <?php -namespace TYPO3\CMS\Form\Domain\Filter; +namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Validation\Fixtures; /* * This file is part of the TYPO3 CMS project. @@ -14,21 +14,23 @@ namespace TYPO3\CMS\Form\Domain\Filter; * The TYPO3 project - inspiring people to share! */ +use TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator; + /** - * Digit filter + * Validator for unit tetst + * + * @api */ -class DigitFilter extends AbstractFilter implements FilterInterface +class TestValidator extends AbstractValidator { /** - * Return filtered value - * Remove all but digits - * - * @param string $value - * @return string + * @param mixed $value + * @return void */ - public function filter($value) + public function isValid($value) { - $pattern = '/[^0-9]/'; - return preg_replace($pattern, '', (string)$value); + if ($value === 'addError') { + $this->addError('Error', 1480201569); + } } } diff --git a/typo3/sysext/form/Tests/Unit/Mvc/Validation/MimeTypeValidatorTest.php b/typo3/sysext/form/Tests/Unit/Mvc/Validation/MimeTypeValidatorTest.php new file mode 100644 index 000000000000..ed234322b4c0 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Mvc/Validation/MimeTypeValidatorTest.php @@ -0,0 +1,109 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Mvc\Validation; + +/* + * 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\Core\Resource\File; +use TYPO3\CMS\Core\Resource\ResourceStorage; +use TYPO3\CMS\Core\Tests\UnitTestCase; +use TYPO3\CMS\Form\Mvc\Validation\Exception\InvalidValidationOptionsException; +use TYPO3\CMS\Form\Mvc\Validation\MimeTypeValidator; + +/** + * Test case + */ +class MimeTypeValidatorTest extends UnitTestCase +{ + + /** + * @test + */ + public function MimeTypeValidatorThrowsExceptionIfAllowedMimeTypesOptionIsString() + { + $this->expectException(InvalidValidationOptionsException::class); + $this->expectExceptionCode(1471713296); + + $options = ['allowedMimeTypes' => '']; + $validator = $this->getMockBuilder(MimeTypeValidator::class) + ->setMethods(['translateErrorMessage']) + ->setConstructorArgs(['options' => $options]) + ->getMock(); + + $validator->validate(true); + } + + /** + * @test + */ + public function MimeTypeValidatorThrowsExceptionIfAllowedMimeTypesOptionIsEmptyArray() + { + $this->expectException(InvalidValidationOptionsException::class); + $this->expectExceptionCode(1471713296); + + $options = ['allowedMimeTypes' => []]; + $validator = $this->getMockBuilder(MimeTypeValidator::class) + ->setMethods(['translateErrorMessage']) + ->setConstructorArgs(['options' => $options]) + ->getMock(); + + $validator->validate(true); + } + + /** + * @test + */ + public function MimeTypeValidatorReturnsTrueIfFileResourceIsNotAllowedMimeType() + { + $options = ['allowedMimeTypes' => ['image/jpeg']]; + $validator = $this->getMockBuilder(MimeTypeValidator::class) + ->setMethods(['translateErrorMessage']) + ->setConstructorArgs(['options' => $options]) + ->getMock(); + + $mockedStorage = $this->getMockBuilder(ResourceStorage::class) + ->disableOriginalConstructor() + ->getMock(); + + $file = new File(['identifier' => '/foo', 'mime_type' => 'image/png'], $mockedStorage); + $this->assertTrue($validator->validate($file)->hasErrors()); + } + + /** + * @test + */ + public function MimeTypeValidatorReturnsFalseIfInputIsEmptyString() + { + $options = ['allowedMimeTypes' => ['fake']]; + $validator = $this->getMockBuilder(MimeTypeValidator::class) + ->setMethods(['translateErrorMessage']) + ->setConstructorArgs(['options' => $options]) + ->getMock(); + + $this->assertFalse($validator->validate('')->hasErrors()); + } + + /** + * @test + */ + public function MimeTypeValidatorReturnsTrueIfInputIsNoFileResource() + { + $options = ['allowedMimeTypes' => ['fake']]; + $validator = $this->getMockBuilder(MimeTypeValidator::class) + ->setMethods(['translateErrorMessage']) + ->setConstructorArgs(['options' => $options]) + ->getMock(); + + $this->assertTrue($validator->validate('string')->hasErrors()); + } +} diff --git a/typo3/sysext/form/Tests/Unit/PostProcess/MailPostProcessorTest.php b/typo3/sysext/form/Tests/Unit/PostProcess/MailPostProcessorTest.php deleted file mode 100644 index 510986f83dec..000000000000 --- a/typo3/sysext/form/Tests/Unit/PostProcess/MailPostProcessorTest.php +++ /dev/null @@ -1,100 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\PostProcess; - -/* - * 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! - */ - -/** - * Test case - */ -class MailPostProcessorTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * @var \TYPO3\CMS\Form\PostProcess\MailPostProcessor - */ - protected $mailPostProcessor; - - /** - * Set up - * - * @return void - */ - protected function setUp() - { - parent::setUp(); - $this->mailPostProcessor = $this->getAccessibleMock( - \TYPO3\CMS\Form\PostProcess\MailPostProcessor::class, - ['__none'], - [], - '', - false - ); - } - - /** - * Data provider for filterValidEmailsReturnsOnlyValidAddresses - * - * @return array input string, expected return array - * @TODO: Add a umlaut domain test case - */ - public function filterValidEmailsProvider() - { - return [ - 'empty string' => [ - '', - [], - ], - 'string not representing an email' => [ - 'notAnAddress', - [], - ], - 'simple single valid address' => [ - 'someone@example.com', - [ - 'someone@example.com', - ], - ], - 'multiple valid simple addresses' => [ - 'someone@example.com, foo@bar.com', - [ - 'someone@example.com', - 'foo@bar.com', - ], - ], - 'multiple addresses with personal part' => [ - 'Foo <foo@example.com>, <bar@example.com>, "Foo, bar" <foo.bar@example.com>', - [ - 'bar@example.com', - 'foo@example.com' => 'Foo', - 'foo.bar@example.com' => '"Foo, bar"', - ], - ], - 'list with invalid addresses is filtered' => [ - 'invalid, @invalid, someone@example.com', - [ - 'someone@example.com', - ], - ], - ]; - } - - /** - * @test - * @dataProvider filterValidEmailsProvider - */ - public function filterValidEmailsReturnsOnlyValidAddresses($input, $expected) - { - $actualResult = $this->mailPostProcessor->_call('filterValidEmails', $input); - $this->assertEquals($expected, $actualResult); - } -} diff --git a/typo3/sysext/form/Tests/Unit/PostProcess/PostProcessorTest.php b/typo3/sysext/form/Tests/Unit/PostProcess/PostProcessorTest.php deleted file mode 100644 index 7429417e5746..000000000000 --- a/typo3/sysext/form/Tests/Unit/PostProcess/PostProcessorTest.php +++ /dev/null @@ -1,149 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\PostProcess; - -/* - * 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 Prophecy\Argument; -use TYPO3\CMS\Core\Tests\UnitTestCase; -use TYPO3\CMS\Extbase\Object\ObjectManager; -use TYPO3\CMS\Form\Domain\Model\Element; -use TYPO3\CMS\Form\Mvc\Controller\ControllerContext; -use TYPO3\CMS\Form\PostProcess\PostProcessor; -use TYPO3\CMS\Form\Tests\Unit\Fixtures\PostProcessorWithFormPrefixFixture; -use TYPO3\CMS\Form\Tests\Unit\Fixtures\PostProcessorWithoutFormPrefixFixture; -use TYPO3\CMS\Form\Tests\Unit\Fixtures\PostProcessorWithoutInterfaceFixture; - -/** - * Testcase for PostProcessor - */ -class PostProcessorTest extends UnitTestCase -{ - /** - * @var array A backup of registered singleton instances - */ - protected $singletonInstances = []; - - /** - * @var Element|\Prophecy\Prophecy\ObjectProphecy - */ - protected $elementProphecy; - - /** - * @var ObjectManager|\Prophecy\Prophecy\ObjectProphecy - */ - protected $objectManagerProphecy; - - /** - * @var ControllerContext|\Prophecy\Prophecy\ObjectProphecy - */ - protected $controllerContextProphecy; - - /** - * Sets up this test case. - */ - protected function setUp() - { - parent::setUp(); - $this->elementProphecy = $this->prophesize(Element::class); - $this->objectManagerProphecy = $this->prophesize(ObjectManager::class); - $this->controllerContextProphecy = $this->prophesize(ControllerContext::class); - } - - /** - * Tears down this test case. - */ - protected function tearDown() - { - parent::tearDown(); - unset($this->elementProphecy); - unset($this->objectManagerProphecy); - unset($this->controllerContextProphecy); - } - - /** - * @test - */ - public function processFindsClassSpecifiedByTypoScriptWithoutFormPrefix() - { - $typoScript = [ - 10 => $this->getUniqueId('postprocess'), - 20 => PostProcessorWithoutFormPrefixFixture::class - ]; - - $this->objectManagerProphecy - ->get(Argument::cetera()) - ->will(function ($arguments) { - return new $arguments[0]($arguments[1], $arguments[2]); - }); - - $subject = $this->createSubject($typoScript); - $this->assertEquals('processedWithoutPrefix', $subject->process()); - } - - /** - * @test - */ - public function processFindsClassSpecifiedByTypoScriptWithFormPrefix() - { - $typoScript = [ - 10 => $this->getUniqueId('postprocess'), - 20 => PostProcessorWithFormPrefixFixture::class - ]; - - $this->objectManagerProphecy - ->get(Argument::cetera()) - ->will(function ($arguments) { - return new $arguments[0]($arguments[1], $arguments[2]); - }); - - $subject = $this->createSubject($typoScript); - $this->assertEquals('processedWithPrefix', $subject->process()); - } - - /** - * @test - */ - public function processReturnsEmptyStringIfSpecifiedPostProcessorDoesNotImplementTheInterface() - { - $typoScript = [ - 10 => $this->getUniqueId('postprocess'), - 20 => PostProcessorWithoutInterfaceFixture::class - ]; - - $this->objectManagerProphecy - ->get(Argument::cetera()) - ->will(function ($arguments) { - return new $arguments[0]($arguments[1], $arguments[2]); - }); - - $subject = $this->createSubject($typoScript); - $this->assertEquals('', $subject->process()); - } - - /** - * @param array $typoScript - * @return PostProcessor|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface - */ - protected function createSubject(array $typoScript) - { - $subject = $this->getAccessibleMock( - PostProcessor::class, - ['__none'], - [$this->elementProphecy->reveal(), $typoScript] - ); - $subject->_set('controllerContext', $this->controllerContextProphecy->reveal()); - $subject->_set('objectManager', $this->objectManagerProphecy->reveal()); - return $subject; - } -} diff --git a/typo3/sysext/form/Tests/Unit/Utility/ArrayUtilityTest.php b/typo3/sysext/form/Tests/Unit/Utility/ArrayUtilityTest.php new file mode 100644 index 000000000000..9fbdc4fa1356 --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/Utility/ArrayUtilityTest.php @@ -0,0 +1,251 @@ +<?php +namespace TYPO3\CMS\Form\Tests\Unit\Utility; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Form\Domain\Exception\TypeDefinitionNotValidException; +use TYPO3\CMS\Form\Utility\ArrayUtility; + +/** + * Test case + */ +class ArrayUtilityTest extends UnitTestCase +{ + + /** + * @test + */ + public function assertAllArrayKeysAreValidThrowsExceptionOnNotAllowedArrayKeys() + { + $this->expectException(TypeDefinitionNotValidException::class); + $this->expectExceptionCode(1325697085); + + $arrayToTest = [ + 'roger' => '', + 'francine' => '', + 'stan' => '', + ]; + + $allowedArrayKeys = [ + 'roger', + 'francine', + ]; + + ArrayUtility::assertAllArrayKeysAreValid($arrayToTest, $allowedArrayKeys); + } + + /** + * @test + */ + public function assertAllArrayKeysAreValidReturnsNullOnAllowedArrayKeys() + { + $arrayToTest = [ + 'roger' => '', + 'francine' => '', + 'stan' => '', + ]; + + $allowedArrayKeys = [ + 'roger', + 'francine', + 'stan', + ]; + + $this->assertNull(ArrayUtility::assertAllArrayKeysAreValid($arrayToTest, $allowedArrayKeys)); + } + + /** + * @test + */ + public function sortNumericArrayKeysRecursiveExpectSorting() + { + $input = [ + 20 => 'b', + 10 => 'a', + 40 => 'd', + 30 => 'c', + 50 => [ + 20 => 'a', + 10 => 'b', + ], + ]; + + $expected = [ + 10 => 'a', + 20 => 'b', + 30 => 'c', + 40 => 'd', + 50 => [ + 10 => 'b', + 20 => 'a', + ], + ]; + + $this->assertSame($expected, ArrayUtility::sortNumericArrayKeysRecursive($input)); + } + + /** + * @test + */ + public function sortNumericArrayKeysRecursiveExpectNoSorting() + { + $input = [ + 'b' => 'b', + 10 => 'a', + 40 => 'd', + 30 => 'c', + ]; + + $expected = [ + 'b' => 'b', + 10 => 'a', + 40 => 'd', + 30 => 'c', + ]; + + $this->assertSame($expected, ArrayUtility::sortNumericArrayKeysRecursive($input)); + } + + /** + * @test + */ + public function reIndexNumericArrayKeysRecursiveExpectReindexing() + { + $input = [ + 20 => 'b', + 10 => 'a', + 40 => 'd', + 30 => 'c', + 50 => [ + 20 => 'a', + 10 => 'b', + ], + ]; + + $expected = [ + 0 => 'b', + 1 => 'a', + 2 => 'd', + 3 => 'c', + 4 => [ + 0 => 'a', + 1 => 'b', + ], + ]; + + $this->assertSame($expected, ArrayUtility::reIndexNumericArrayKeysRecursive($input)); + } + + /** + * @test + */ + public function reIndexNumericArrayKeysRecursiveExpectNoReindexing() + { + $input = [ + 'a' => 'b', + 10 => 'a', + 40 => 'd', + 30 => 'c', + 50 => [ + 20 => 'a', + 10 => 'b', + ], + ]; + + $expected = [ + 'a' => 'b', + 10 => 'a', + 40 => 'd', + 30 => 'c', + 50 => [ + 0 => 'a', + 1 => 'b', + ], + ]; + + $this->assertSame($expected, ArrayUtility::reIndexNumericArrayKeysRecursive($input)); + } + + /** + * @test + */ + public function removeNullValuesRecursiveExpectRemoval() + { + $input = [ + 'a' => 'a', + 'b' => [ + 'c' => null, + 'd' => 'd', + ], + ]; + + $expected = [ + 'a' => 'a', + 'b' => [ + 'd' => 'd', + ], + ]; + + $this->assertSame($expected, ArrayUtility::removeNullValuesRecursive($input)); + } + + /** + * @test + */ + public function stripTagsFromValuesRecursiveExpectRemoval() + { + $input = [ + 'a' => 'a', + 'b' => [ + 'c' => '<b>i am evil</b>', + 'd' => 'd', + ], + ]; + + $expected = [ + 'a' => 'a', + 'b' => [ + 'c' => 'i am evil', + 'd' => 'd', + ], + ]; + + $this->assertSame($expected, ArrayUtility::stripTagsFromValuesRecursive($input)); + } + + /** + * @test + */ + public function convertBooleanStringsToBooleanRecursiveExpectConverting() + { + $input = [ + 'a' => 'a', + 'b' => [ + 'c' => 'true', + 'd' => 'd', + ], + ]; + + $expected = [ + 'a' => 'a', + 'b' => [ + 'c' => true, + 'd' => 'd', + ], + ]; + + $this->assertSame($expected, ArrayUtility::convertBooleanStringsToBooleanRecursive($input)); + } +} diff --git a/typo3/sysext/form/Tests/Unit/Utility/TypoScriptToJsonConverterTest.php b/typo3/sysext/form/Tests/Unit/Utility/TypoScriptToJsonConverterTest.php deleted file mode 100644 index 3b641ba06116..000000000000 --- a/typo3/sysext/form/Tests/Unit/Utility/TypoScriptToJsonConverterTest.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Utility; - -/* - * 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\Core\Tests\AccessibleObjectInterface; -use TYPO3\CMS\Core\Tests\UnitTestCase; -use TYPO3\CMS\Form\Domain\Model\Json\FormJsonElement; -use TYPO3\CMS\Form\Utility\TypoScriptToJsonConverter; - -/** - * Test case for \TYPO3\CMS\Form\Utility\TypoScriptToJsonConverter - */ -class TypoScriptToJsonConverterTest extends UnitTestCase -{ - /** - * Checks if calling protected method getChildElementsByIntegerKey with different data - * calls the addMethod in the mocked FormJsonElement for an expected method count. - * - * @dataProvider getChildElementsByIntegerKeyCallsAddElementDataProvider - * @param array $typoScript - * @param int $methodCount - * @test - */ - public function getChildElementsByIntegerKeyCallsAddElement(array $typoScript, $methodCount) - { - /** @var FormJsonElement|\PHPUnit_Framework_MockObject_MockObject $mockSubject */ - $parentElement = $this->getMockBuilder(FormJsonElement::class) - ->setMethods(['addElement']) - ->getMock(); - // check if method gets called exactly X times - $parentElement->expects($this->exactly($methodCount))->method('addElement'); - - /** @var TypoScriptToJsonConverter|\PHPUnit_Framework_MockObject_MockObject|AccessibleObjectInterface $subjectAccessible */ - $accessibleSubject = $this->getAccessibleMock(TypoScriptToJsonConverter::class, ['dummy']); - $accessibleSubject->_call('getChildElementsByIntegerKey', $parentElement, $typoScript); - } - - /** - * Data provider for test method getChildElementsByIntegerKeyCallsAddElement. - * - * @return array - */ - public function getChildElementsByIntegerKeyCallsAddElementDataProvider() - { - return [ - [ - 'typoscript' => [ - 'prefix' => 'tx_form', - 'confirmation' => '1', - 'postProcessor.' => [ - '1' => 'mail', - '1.' => [ - 'recipientEmail' => '', - 'senderEmail' => '', - ], - ], - '10' => 'FILEUPLOAD', - '10.' => [ - 'name' => 'foo', - 'type' => 'file', - 'label.' => [ - 'value' => 'Edit this label', - ], - ], - ], - 1 - ], - ]; - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/AbstractValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/AbstractValidatorTest.php deleted file mode 100644 index b934c76de3e5..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/AbstractValidatorTest.php +++ /dev/null @@ -1,51 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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\Form\Domain\Validator\AbstractValidator; -use TYPO3\CMS\Form\Utility\FormUtility; - -/** - * Test case - */ -abstract class AbstractValidatorTest extends \TYPO3\CMS\Core\Tests\UnitTestCase -{ - /** - * Must be filled with subject class name - * in specific test implementation. - * - * @var string - */ - protected $subjectClassName = null; - - /** - * @param array $options - * @return AbstractValidator|\PHPUnit_Framework_MockObject_MockObject - */ - protected function createSubject(array $options) - { - /** @var AbstractValidator $subject */ - $subject = $this->getMockBuilder($this->subjectClassName) - ->setMethods(['getLocalLanguageLabel', 'humanReadableDateFormat']) - ->setConstructorArgs(['options' => $options]) - ->getMock(); - - /** @var FormUtility $formUtilityMock */ - $formUtilityMock = $this->createMock(FormUtility::class); - $subject->setFormUtility($formUtilityMock); - - return $subject; - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/AlphabeticValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/AlphabeticValidatorTest.php deleted file mode 100644 index 0fd578f3490e..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/AlphabeticValidatorTest.php +++ /dev/null @@ -1,138 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class AlphabeticValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\AlphabeticValidator::class; - - /** - * @return array - */ - public function validDataProviderWithoutWhitespace() - { - return [ - 'ascii without spaces' => ['thisismyinput'], - 'accents without spaces' => ['éóéà èò'], - 'umlauts without spaces' => ['üöä'], - 'empty string' => [''] - ]; - } - - /** - * @return array - */ - public function validDataProviderWithWhitespace() - { - return [ - 'ascii with spaces' => ['This is my input'], - 'accents with spaces' => ['Sigur Rós'], - 'umlauts with spaces' => ['Hürriyet Daily News'], - 'space' => [' '], - 'empty string' => [''] - ]; - } - - /** - * @return array - */ - public function invalidDataProviderWithoutWhitespace() - { - return [ - 'ascii with dash' => ['my-name'], - 'accents with underscore' => ['Sigur_Rós'], - 'umlauts with periods' => ['Hürriyet.Daily.News'], - 'space' => [' '], - ]; - } - - /** - * @return array - */ - public function invalidDataProviderWithWhitespace() - { - return [ - 'ascii with spaces and dashes' => ['This is my-name'], - 'accents with spaces and underscores' => ['Listen to Sigur_Rós_Band'], - 'umlauts with spaces and periods' => ['Go get the Hürriyet.Daily.News'] - ]; - } - - /** - * @param string $input - * @test - * @dataProvider validDataProviderWithoutWhitespace - */ - public function validateForValidInputWithoutAllowedWhitespaceHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @param string $input - * @test - * @dataProvider validDataProviderWithWhitespace - */ - public function validateForValidInputWithWhitespaceAllowedHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error'), 'allowWhiteSpace' => true]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @param string $input - * @test - * @dataProvider invalidDataProviderWithoutWhitespace - */ - public function validateForInvalidInputWithoutAllowedWhitespaceHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @param string $input - * @test - * @dataProvider invalidDataProviderWithWhitespace - */ - public function validateForInvalidInputWithWhitespaceAllowedHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error'), 'allowWhiteSpace' => true]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input)->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/AlphanumericValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/AlphanumericValidatorTest.php deleted file mode 100644 index 697c8b33187c..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/AlphanumericValidatorTest.php +++ /dev/null @@ -1,138 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class AlphanumericValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\AlphanumericValidator::class; - - /** - * @return array - */ - public function validDataProviderWithoutWhitespace() - { - return [ - 'ascii without spaces' => ['thisismyinput4711'], - 'accents without spaces' => ['éóéà èò4711'], - 'umlauts without spaces' => ['üöä4711'], - 'empty string' => [''] - ]; - } - - /** - * @return array - */ - public function validDataProviderWithWhitespace() - { - return [ - 'ascii with spaces' => ['This is my input 4711'], - 'accents with spaces' => ['Sigur Rós 4711'], - 'umlauts with spaces' => ['Hürriyet Daily News 4711'], - 'space' => [' '], - 'empty string' => [''] - ]; - } - - /** - * @return array - */ - public function invalidDataProviderWithoutWhitespace() - { - return [ - 'ascii with dash' => ['my-name-4711'], - 'accents with underscore' => ['Sigur_Rós_4711'], - 'umlauts with periods' => ['Hürriyet.Daily.News.4711'], - 'space' => [' '], - ]; - } - - /** - * @return array - */ - public function invalidDataProviderWithWhitespace() - { - return [ - 'ascii with spaces and dashes' => ['This is my-name 4711'], - 'accents with spaces and underscores' => ['Listen to Sigur_Rós_Band 4711'], - 'umlauts with spaces and periods' => ['Go get the Hürriyet.Daily.News 4711'] - ]; - } - - /** - * @param string $input - * @test - * @dataProvider validDataProviderWithoutWhitespace - */ - public function validateForValidInputWithoutAllowedWhitespaceHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @param string $input - * @test - * @dataProvider validDataProviderWithWhitespace - */ - public function validateForValidInputWithAllowedWhitespaceHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error'), 'allowWhiteSpace' => true]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @param string $input - * @test - * @dataProvider invalidDataProviderWithoutWhitespace - */ - public function validateForInvalidInputWithoutAllowedWhitespaceHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @param string $input - * @test - * @dataProvider invalidDataProviderWithWhitespace - */ - public function validateForInvalidInputWithAllowedWhitespaceHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error'), 'allowWhiteSpace' => true]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input)->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/BetweenValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/BetweenValidatorTest.php deleted file mode 100644 index a641096f5854..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/BetweenValidatorTest.php +++ /dev/null @@ -1,146 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class BetweenValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\BetweenValidator::class; - - /** - * @return array - */ - public function validNonInclusiveDataProvider() - { - return [ - '3 < 5 < 7' => [[3, 5, 7]], - '0 < 10 < 20' => [[0, 10, 20]], - '-10 < 0 < 10' => [[-10, 0, 10]], - '-20 < -10 < 0' => [[-20, -10, 0]], - '1 < 2 < 3' => [[1, 2, 3]], - '1 < 1.01 < 1.1' => [[1, 1.01, 1.1]], - ]; - } - - /** - * @return array - */ - public function invalidNonInclusiveDataProvider() - { - return [ - '1 < 1 < 2' => [[1, 1, 2]], - '1 < 2 < 2' => [[1, 2, 2]], - '1.1 < 1.1 < 1.2' => [[1.1, 1.1, 1.2]], - '1.1 < 1.2 < 1.2' => [[1.1, 1.2, 1.2]], - '-10.1234 < -10.12340 < 10' => [[-10.1234, -10.12340, 10]], - '100 < 0 < -100' => [[100, 0, -100]] - ]; - } - - /** - * @return array - */ - public function validInclusiveDataProvider() - { - return [ - '1 ≤ 1 ≤ 1' => [[1, 1, 1]], - '-10.1234 ≤ -10.12340 ≤ 10' => [[-10.1234, -10.12340, 10]], - '-10.1234 ≤ -10 ≤ 10' => [[-10.1234, -10.12340, 10]], - ]; - } - - public function invalidInclusiveDataProvider() - { - return [ - '-10.1234 ≤ -10.12345 ≤ 10' => [[-10.1234, -10.12345, 10]], - '100 ≤ 0 ≤ -100' => [[100, 0, -100]] - ]; - } - - /** - * @param array $input - * @test - * @dataProvider validNonInclusiveDataProvider - */ - public function validateWithValidInputAndWithoutInclusiveHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['minimum'] = $input[0]; - $options['maximum'] = $input[2]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input[1])->getErrors() - ); - } - - /** - * @param array $input - * @test - * @dataProvider validInclusiveDataProvider - */ - public function validateWithValidInputAndWithInclusiveHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['minimum'] = $input[0]; - $options['maximum'] = $input[2]; - $options['inclusive'] = true; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input[1])->getErrors() - ); - } - - /** - * @param array $input - * @test - * @dataProvider invalidNonInclusiveDataProvider - */ - public function validateWithInvalidInputAndWithoutInclusiveHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['minimum'] = $input[0]; - $options['maximum'] = $input[2]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input[1])->getErrors() - ); - } - - /** - * @param array $input - * @test - * @dataProvider invalidInclusiveDataProvider - */ - public function validateWithInvalidInputAndWithInclusiveHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['minimum'] = $input[0]; - $options['maximum'] = $input[2]; - $options['inclusive'] = true; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input[1])->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/DateValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/DateValidatorTest.php deleted file mode 100644 index 38df7a6cd472..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/DateValidatorTest.php +++ /dev/null @@ -1,86 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class DateValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\DateValidator::class; - - /** - * @return array - */ - public function validDateProvider() - { - return [ - '28-03-2012' => [['%e-%m-%Y', '28-03-2012']], - '8-03-2012' => [['%e-%m-%Y', '8-03-2012']], - '29-02-2012' => [['%d-%m-%Y', '29-02-2012']] - ]; - } - - /** - * @return array - */ - public function invalidDateProvider() - { - return [ - '32-03-2012' => [['%d-%m-%Y', '32-03-2012']], - '31-13-2012' => [['%d-%m-%Y', '31-13-2012']], - '29-02-2011' => [['%d-%m-%Y', '29-02-2011']] - ]; - } - - /** - * @test - * @dataProvider validDateProvider - * @param array $input - */ - public function validateForValidInputHasEmptyErrorResult(array $input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['format'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input[1])->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidDateProvider - * @param array $input - */ - public function validateForInvalidInputHasNotEmptyErrorResult(array $input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['format'] = $input[0]; - $subject = $this->createSubject($options); - - $subject->expects($this->once()) - ->method('humanReadableDateFormat') - ->willReturnArgument(0); - - $this->assertNotEmpty( - $subject->validate($input[1])->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/DigitValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/DigitValidatorTest.php deleted file mode 100644 index b4973e2aa9ce..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/DigitValidatorTest.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class DigitValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\DigitValidator::class; - - /** - * @return array - */ - public function validDigitProvider() - { - return [ - 'stringified integer' => ['2012'], - 'stringified integer with leading zeros' => ['0002'], - ]; - } - - /** - * @return array - */ - public function invalidDigitProvider() - { - return [ - 'stringified float' => ['0.2012'], - 'stringified scientific' => ['1.9E+11'] - ]; - } - - /** - * @test - * @dataProvider validDigitProvider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidDigitProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input)->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/EmailValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/EmailValidatorTest.php deleted file mode 100644 index 9eea1514afdd..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/EmailValidatorTest.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class EmailValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\EmailValidator::class; - - /** - * @return array - */ - public function validEmailProvider() - { - return [ - 'a@b.de' => ['a@b.de'], - 'somebody@mymac.local' => ['somebody@mymac.local'], - 'empty value' => [''], - 'unexpected value' => [[]], - ]; - } - - /** - * @return array - */ - public function invalidEmailProvider() - { - return [ - 'myemail@' => ['myemail@'], - 'myemail' => ['myemail'], - 'somebody@localhost' => ['somebody@localhost'], - ]; - } - - /** - * @test - * @dataProvider validEmailProvider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidEmailProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input)->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/EqualsValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/EqualsValidatorTest.php deleted file mode 100644 index 1baee2ebc077..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/EqualsValidatorTest.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class EqualsValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\EqualsValidator::class; - - /** - * @return array - */ - public function validPairProvider() - { - return [ - 'something === something' => [['something', 'something']], - '4 === 4' => [[4, 4]] - ]; - } - - /** - * @return array - */ - public function invalidPairProvider() - { - return [ - 'somethingElse !== something' => [['somethingElse', 'something']], - '4 !== 3' => [[4, 3]] - ]; - } - - /** - * @test - * @dataProvider validPairProvider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['field'] = uniqid('field'); - $subject = $this->createSubject($options); - $subject->setRawArgument([$options['field'] => $input[0]]); - - $this->assertEmpty( - $subject->validate($input[1])->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidPairProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['field'] = uniqid('field'); - $subject = $this->createSubject($options); - $subject->setRawArgument([$options['field'] => $input[0]]); - - $this->assertNotEmpty( - $subject->validate($input[1])->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/FileAllowedTypesValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/FileAllowedTypesValidatorTest.php deleted file mode 100644 index 776bfba43ff0..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/FileAllowedTypesValidatorTest.php +++ /dev/null @@ -1,114 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class FileAllowedTypesValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\FileAllowedTypesValidator::class; - - /** - * @return array - */ - public function validTypesProvider() - { - return [ - 'pdf in (pdf)' => [ - 'application/pdf', - [ - 'type' => 'application/pdf', - ], - ], - 'pdf in (pdf, json)' => [ - 'application/pdf, application/json', - [ - 'type' => 'application/pdf', - ], - ], - ]; - } - - /** - * @return array - */ - public function invalidTypesProvider() - { - return [ - 'xml in (pdf, json)' => [ - 'application/pdf, application/json', - [ - 'type' => 'application/xml', - ], - ], - 'xml in (pdf)' => [ - 'application/pdf', - [ - 'type' => 'application/xml', - ], - ], - 'empty mimetype' => [ - 'application/pdf, application/json', - [ - 'type' => '', - ], - ], - 'empty value' => [ - 'application/pdf, application/json', - '', - ], - ]; - } - - /** - * @test - * @param string $types - * @param array $value - * @dataProvider validTypesProvider - */ - public function validateForValidInputHasEmptyErrorResult($types, $value) - { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - 'types' => $types, - ]; - $subject = $this->createSubject($options); - - $this->assertEmpty($subject->validate($value)->getErrors()); - } - - /** - * @test - * @param string $types - * @param array $value - * @dataProvider invalidTypesProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($types, $value) - { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - 'types' => $types, - ]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty($subject->validate($value)->getErrors()); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/FileMaximumSizeValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/FileMaximumSizeValidatorTest.php deleted file mode 100644 index 5dff12f00743..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/FileMaximumSizeValidatorTest.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class FileMaximumSizeValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\FileMaximumSizeValidator::class; - - protected function setUp() - { - $this->markTestSkipped('validate() instead of isValid() needs to be used'); - } - - public function validSizesProvider() - { - return [ - '11B for max. 12B' => [[12, 11]], - '12B for max. 12B' => [[12, 12]] - ]; - } - - public function invalidSizesProvider() - { - return [ - '12B for max. 11B' => [[11, 12]] - ]; - } - - /** - * @test - * @dataProvider validSizesProvider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['maximum'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input[1])->getErrors() - ); - } - - /** - * @test - * @dataProvider inValidSizesProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['maximum'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input[1])->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/FileMinimumSizeValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/FileMinimumSizeValidatorTest.php deleted file mode 100644 index 7b50869bcd55..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/FileMinimumSizeValidatorTest.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class FileMinimumSizeValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\FileMinimumSizeValidator::class; - - protected function setUp() - { - $this->markTestSkipped('validate() instead of isValid() needs to be used'); - } - - public function validSizesProvider() - { - return [ - '12B for min. 11B' => [[11, 12]], - '12B for min. 12B' => [[12, 12]] - ]; - } - - public function invalidSizesProvider() - { - return [ - '11B for min. 12B' => [[12, 11]] - ]; - } - - /** - * @test - * @dataProvider validSizesProvider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['minimum'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input[1])->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidSizesProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['minimum'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input[1])->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/FloatValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/FloatValidatorTest.php deleted file mode 100644 index 5cfc288cb42e..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/FloatValidatorTest.php +++ /dev/null @@ -1,63 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class FloatValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\FloatValidator::class; - - /** - * @return array - */ - public function validFloatProvider() - { - return [ - '12.1 for en_US locale' => [ - '12.1', - 'en_US.utf8', - ], - '12,1 for de_DE locale' => [ - '12,1', - 'de_DE.utf8', - ], - ]; - } - - /** - * @test - * @dataProvider validFloatProvider - */ - public function validateForValidInputHasEmptyErrorResult($inputValue, $locale) - { - try { - $this->setLocale(LC_NUMERIC, $locale); - } catch (\PHPUnit_Framework_Exception $e) { - $this->markTestSkipped('Locale ' . $locale . ' is not available.'); - } - - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($inputValue)->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/GreaterThanValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/GreaterThanValidatorTest.php deleted file mode 100644 index a30d1168d874..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/GreaterThanValidatorTest.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class GreaterThanValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\GreaterThanValidator::class; - - /** - * @return array - */ - public function validNumberProvider() - { - return [ - '13 > 12' => [[12, 13]], - ]; - } - - /** - * @return array - */ - public function invalidNumberProvider() - { - return [ - '(int)12.1 > 12' => [[12, 12.1]], - '(int)12 > 12' => [[12, 12]], - '(int)11.99 > 12' => [[12, 11.99]] - ]; - } - - /** - * @test - * @dataProvider validNumberProvider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['minimum'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input[1])->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidNumberProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['minimum'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input[1])->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/InArrayValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/InArrayValidatorTest.php deleted file mode 100644 index 40a8be95ad19..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/InArrayValidatorTest.php +++ /dev/null @@ -1,800 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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\Form\Domain\Validator\InArrayValidator; - -/** - * Test case - */ -class InArrayValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = InArrayValidator::class; - - /** - * used for tests with valid input - * will result in no errors returned - * - * @return array - */ - public function validArrayForStringConfigurationProvider() - { - return [ - '12 in (12, 13, 14)' => [ - '12', - '12,13,14', - ], - '1 in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa)' => [ - '1', - '5,3234,oOIUoi8,3434,343,34,3,1,333434,1234,ssd,ysdfsa', - ], - 'Pizza in (Pizza, Lasange, Strogonvo)' => [ - 'Pizza', - 'Pizza,Lasange,Strogonvo', - ], - 'Rißtissen in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - 'Rißtissen', - 'Rißtissen,Ãœberligen,Karlsruhe', - ], - - '[12 and 14] in (12, 13, 14)' => [ - ['12', '14'], - '12,13,14', - ], - '[1 and ssd] in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa)' => [ - ['1', 'ssd'], - '5,3234,oOIUoi8,3434,343,34,3,1,333434,1234,ssd,ysdfsa', - ], - '[Pizza and Strogonvo] in (Pizza, Lasange, Strogonvo)' => [ - ['Pizza', 'Strogonvo'], - 'Pizza,Lasange,Strogonvo', - ], - '[Rißtissen and Karlsruhe] in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - ['Rißtissen', 'Karlsruhe'], - 'Rißtissen,Ãœberligen,Karlsruhe', - ], - ]; - } - - /** - * used for tests with valid input - * will result in no errors returned - * - * @return array - */ - public function validArrayForArrayConfigurationProvider() - { - return [ - '12 in [12, 13, 14]' => [ - '12', - ['12', '13', '14'], - ], - '1 in [5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa]' => [ - '1', - ['5', '3234', 'oOIUoi8', '3434', '343', '34', '3', '1', '333434', '1234', 'ssd', 'ysdfsa'], - ], - 'Pizza in [Pizza, Lasange, Strogonvo]' => [ - ['Pizza', 'Strogonvo'], - ['Pizza', 'Lasange', 'Strogonvo'], - ], - 'Rißtissen in [Rißtissen, Ãœberligen, Karlsruhe]' => [ - ['Rißtissen', 'Karlsruhe'], - ['Rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - - '[12 and 14] in [12, 13, 14]' => [ - ['12', '14'], - ['12', '13', '14'], - ], - '[1 and ssd] in [5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa]' => [ - ['1', 'ssd'], - ['5', '3234', 'oOIUoi8', '3434', '343', '34', '3', '1', '333434', '1234', 'ssd', 'ysdfsa'], - ], - '[Pizza and Strogonvo] in [Pizza, Lasange, Strogonvo]' => [ - 'Pizza', - ['Pizza', 'Lasange', 'Strogonvo'], - ], - '[Rißtissen and Karlsruhe] in [Rißtissen, Ãœberligen, Karlsruhe]' => [ - 'Rißtissen', - ['Rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - ]; - } - - /** - * used for test with invalid input - * will result in errors returned - * - * @return array - */ - public function invalidArrayForStringConfigurationProvider() - { - return [ - '12 in (11, 13, 14)' => [ - '12', - '11,13,14', - ], - '1 in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 333434, 1234, ssd, ysdfsa)' => [ - '1', - '5,3234,oOIUoi8,3434,343,34,3,333434,1234,ssd,ysdfsa', - ], - 'pizza in (Pizza, Lasange, Strogonvo)' => [ - 'pizza', - 'Pizza,Lasange,Strogonvo', - ], - 'Eimeldingen in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - 'Eimeldingen', - 'Rißtissen,Ãœberligen,Karlsruhe', - ], - 'überligen in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - 'überligen', - 'Rißtissen,Ãœberligen,Karlsruhe', - ], - - '[12 and 14] in (11, 13, 14)' => [ - ['12', '14'], - '11,13,14', - ], - '[1 and ssd] in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 333434, 1234, ssd, ysdfsa)' => [ - ['1', 'ssd'], - '5,3234,oOIUoi8,3434,343,34,3,333434,1234,ssd,ysdfsa', - ], - '[pizza and Lasange] in (Pizza, Lasange, Strogonvo)' => [ - ['pizza', 'Lasange'], - 'Pizza,Lasange,Strogonvo', - ], - '[Eimeldingen and Ãœberligen] in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - ['Eimeldingen', 'Ãœberligen'], - 'Rißtissen,Ãœberligen,Karlsruhe', - ], - '[Eimeldingen and überligen] in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - ['Eimeldingen', 'überligen'], - 'Rißtissen,Ãœberligen,Karlsruhe', - ], - ]; - } - - /** - * used for test with invalid input - * will result in errors returned - * - * @return array - */ - public function invalidArrayForArrayConfigurationProvider() - { - return [ - '12 in (11, 13, 14)' => [ - '12', - ['11', '13', '14'], - ], - '1 in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 333434, 1234, ssd, ysdfsa)' => [ - '1', - ['5', '3234', 'oOIUoi8', '3434', '343', '34', '3', '333434', '1234', 'ssd', 'ysdfsa'], - ], - 'pizza in (Pizza, Lasange, Strogonvo)' => [ - 'pizza', - ['Pizza', 'Lasange', 'Strogonvo'], - ], - 'Eimeldingen in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - 'Eimeldingen', - ['Rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - 'überligen in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - 'überligen', - ['Rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - - '[12 and 14] in (11, 13, 14)' => [ - ['12', '14'], - ['11', '13', '14'], - ], - '[1 and ssd] in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 333434, 1234, ssd, ysdfsa)' => [ - ['1', 'ssd'], - ['5', '3234', 'oOIUoi8', '3434', '343', '34', '3', '333434', '1234', 'ssd', 'ysdfsa'], - ], - '[pizza and Lasange] in (Pizza, Lasange, Strogonvo)' => [ - ['pizza', 'Lasange'], - ['Pizza', 'Lasange', 'Strogonvo'], - ], - '[Eimeldingen and Ãœberligen] in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - ['Eimeldingen', 'Ãœberligen'], - ['Rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - '[Eimeldingen and überligen] in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - ['Eimeldingen', 'überligen'], - ['Rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - ]; - } - - /** - * used for tests with valid input - * ignorecase is set to true - * results in no errors returned - * - * @return array - */ - public function validArrayForStringConfigurationIgnoreCaseProvider() - { - return [ - '12 in (12, 13, 14)' => [ - '12', - '12,13,14', - ], - '1 in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa)' => [ - '1', - '5,3234,oOIUoi8,3434,343,34,3,1,333434,1234,ssd,ysdfsa', - ], - 'pizza in (Pizza, Lasange, Strogonvo)' => [ - 'pizza', - 'Pizza,Lasange,Strogonvo', - ], - 'Pizza in (pizza, lasange, strogonvo)' => [ - 'Pizza', - 'pizza,lasange,strogonvo', - ], - 'Rißtissen in (rißtissen, Ãœberligen, Karlsruhe)' => [ - 'Rißtissen', - 'rißtissen,Ãœberligen,Karlsruhe', - ], - 'überligen in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - 'überligen', - 'Rißtissen,Ãœberligen,Karlsruhe', - ], - - '[12 and 14] in (12, 13, 14)' => [ - ['12', '14'], - '12,13,14', - ], - '[1 and Ssd] in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa)' => [ - ['1', 'Ssd'], - '5,3234,oOIUoi8,3434,343,34,3,1,333434,1234,ssd,ysdfsa', - ], - '[pizza and Lasange] in (Pizza, Lasange, Strogonvo)' => [ - ['pizza', 'Lasange'], - 'Pizza,Lasange,Strogonvo', - ], - '[Pizza and lasange] in (pizza, lasange, strogonvo)' => [ - ['Pizza', 'lasange'], - 'pizza,lasange,strogonvo', - ], - '[Rißtissen and Karlsruhe] in (rißtissen, Ãœberligen, Karlsruhe)' => [ - ['Rißtissen', 'Karlsruhe'], - 'rißtissen,Ãœberligen,Karlsruhe', - ], - '[überligen and Karlsruhe] in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - ['überligen', 'Karlsruhe'], - 'Rißtissen,Ãœberligen,Karlsruhe', - ], - ]; - } - - /** - * used for tests with valid input - * ignorecase is set to true - * results in no errors returned - * - * @return array - */ - public function validArrayForArrayConfigurationIgnoreCaseProvider() - { - return [ - '12 in [12, 13, 14]' => [ - '12', - ['12', '13', '14'], - ], - '1 in [5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa]' => [ - '1', - ['5', '3234', 'oOIUoi8', '3434', '343', '34', '3', '1', '333434', '1234', 'ssd', 'ysdfsa'], - ], - 'pizza in [Pizza, Lasange, Strogonvo]' => [ - 'pizza', - ['Pizza', 'Lasange', 'Strogonvo'], - ], - 'Pizza in [pizza, lasange, strogonvo]' => [ - 'Pizza', - ['pizza', 'lasange', 'strogonvo'], - ], - 'Rißtissen in (rißtissen, Ãœberligen, Karlsruhe)' => [ - 'Rißtissen', - ['rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - 'überligen in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - 'überligen', - ['Rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - - '[12 and 14] in (12, 13, 14)' => [ - ['12', '14'], - ['12', '13', '14'], - ], - '[1 and Ssd] in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa)' => [ - ['1', 'Ssd'], - ['5', '3234', 'oOIUoi8', '3434', '343', '34', '3', '1', '333434', '1234', 'ssd', 'ysdfsa'], - ], - '[pizza and Lasange] in (Pizza, Lasange, Strogonvo)' => [ - ['pizza', 'Lasange'], - ['Pizza', 'Lasange', 'Strogonvo'], - ], - '[Pizza and lasange] in (pizza, lasange, strogonvo)' => [ - ['Pizza', 'lasange'], - ['pizza', 'lasange', 'strogonvo'], - ], - '[Rißtissen and Karlsruhe] in (rißtissen, Ãœberligen, Karlsruhe)' => [ - ['Rißtissen', 'Karlsruhe'], - ['rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - '[überligen and Karlsruhe] in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - ['überligen', 'Karlsruhe'], - ['Rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - ]; - } - - /** - * used for tests with invalid input - * ignorecase is set to true - * results in errors returned - * - * @return array - */ - public function invalidArrayForStringConfigurationIgnoreCaseProvider() - { - return [ - 'zwölf in (12, 13, 14)' => [ - 'zwölf', - '12,13,14', - ], - '7 in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa)' => [ - '7', - '5,3234,oOIUoi8,3434,343,34,3,1,333434,1234,ssd,ysdfsa', - ], - 'riss in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - 'riss', - 'Rißtissen,Ãœberligen,Karlsruhe', - ], - 'pizzas in (Pizza, Lasange, Strogonvo)' => [ - 'pizzas', - 'Pizza,Lasange,Strogonvo', - ], - 'lusange in (Pizza, Lasange, Strogonvo)' => [ - 'lusange', - 'Pizza,Lasange,Strogonvo', - ], - - '[zwölf and 14] in (12, 13, 14)' => [ - ['zwölf', '14'], - '12,13,14', - ], - '[7 and Ssd] in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa)' => [ - ['7', 'Ssd'], - '5,3234,oOIUoi8,3434,343,34,3,1,333434,1234,ssd,ysdfsa', - ], - '[riss and Karlsruhe] in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - ['riss', 'Karlsruhe'], - 'Rißtissen,Ãœberligen,Karlsruhe', - ], - '[pizzas and Lasange] in (Pizza, Lasange, Strogonvo)' => [ - ['pizzas', 'Lasange'], - 'Pizza,Lasange,Strogonvo', - ], - '[lusange and Strogonvo] in (Pizza, Lasange, Strogonvo)' => [ - ['lusange', 'Strogonvo'], - 'Pizza,Lasange,Strogonvo', - ], - ]; - } - - /** - * used for tests with invalid input - * ignorecase is set to true - * results in errors returned - * - * @return array - */ - public function invalidArrayForArrayConfigurationIgnoreCaseProvider() - { - return [ - 'zwölf in (12, 13, 14)' => [ - 'zwölf', - ['12', '13', '14'], - ], - '7 in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa)' => [ - '7', - ['5', '3234', 'oOIUoi8', '3434', '343', '34', '3', '1', '333434', '1234', 'ssd', 'ysdfsa'], - ], - 'riss in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - 'riss', - ['Rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - 'pizzas in (Pizza, Lasange, Strogonvo)' => [ - 'pizzas', - ['Pizza', 'Lasange', 'Strogonvo'], - ], - 'lusange in (Pizza, Lasange, Strogonvo)' => [ - 'lusange', - ['Pizza', 'Lasange', 'Strogonvo'], - ], - - '[zwölf and 14] in (12, 13, 14)' => [ - ['zwölf', '14'], - ['12', '13', '14'], - ], - '[7 and Ssd] in (5, 3234, oOIUoi8, 3434, 343, 34, 3, 1, 333434, 1234, ssd, ysdfsa)' => [ - ['7', 'Ssd'], - ['5', '3234', 'oOIUoi8', '3434', '343', '34', '3', '1', '333434', '1234', 'ssd', 'ysdfsa'], - ], - '[riss and Karlsruhe] in (Rißtissen, Ãœberligen, Karlsruhe)' => [ - ['riss', 'Karlsruhe'], - ['Rißtissen', 'Ãœberligen', 'Karlsruhe'], - ], - '[pizzas and Lasange] in (Pizza, Lasange, Strogonvo)' => [ - ['pizzas', 'Lasange'], - ['Pizza', 'Lasange', 'Strogonvo'], - ], - '[lusange and Strogonvo] in (Pizza, Lasange, Strogonvo)' => [ - ['lusange', 'Strogonvo'], - ['Pizza', 'Lasange', 'Strogonvo'], - ], - ]; - } - - /** - * @test - * @dataProvider validArrayForStringConfigurationProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForValidInputOnStringConfigurationReturnsNoErrors($value, $allowedOptionsString) - { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - - $options['array'] = $allowedOptionsString; - $subject = $this->createSubject($options); - - $this->assertFalse($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider validArrayForArrayConfigurationProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForValidInputOnArrayConfigurationReturnsNoErrors($value, $allowedOptionsString) - { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array.'] = $allowedOptionsString; - $subject = $this->createSubject($options); - - $this->assertFalse($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider invalidArrayForStringConfigurationProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForInvalidInputOnStringConfigurationReturnsErrors($value, $allowedOptionsString) - { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array'] = $allowedOptionsString; - $subject = $this->createSubject($options); - - $this->assertTrue($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider invalidArrayForArrayConfigurationProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForInvalidInputOnArrayConfigurationReturnsErrors($value, $allowedOptionsString) - { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array.'] = $allowedOptionsString; - $subject = $this->createSubject($options); - - $this->assertTrue($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider validArrayForStringConfigurationProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForValidInputOnStringConfigurationWithStrictComparisonReturnsNoErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array'] = $allowedOptionsString; - $options['strict'] = true; - $subject = $this->createSubject($options); - - $this->assertFalse($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider validArrayForArrayConfigurationProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForValidInputOnArrayConfigurationWithStrictComparisonReturnsNoErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array.'] = $allowedOptionsString; - $options['strict'] = true; - $subject = $this->createSubject($options); - - $this->assertFalse($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider invalidArrayForStringConfigurationProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForInvalidInputOnStringConfigurationWithStrictComparisonReturnsErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array'] = $allowedOptionsString; - $options['strict'] = true; - $subject = $this->createSubject($options); - - $this->assertTrue($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider invalidArrayForArrayConfigurationProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForInvalidInputOnArrayConfigurationWithStrictComparisonReturnsErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array.'] = $allowedOptionsString; - $options['strict'] = true; - $subject = $this->createSubject($options); - - $this->assertTrue($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider validArrayForStringConfigurationIgnoreCaseProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForValidInputOnStringConfigurationWithIgnoreCaseReturnsNoErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array'] = $allowedOptionsString; - $options['ignorecase'] = true; - $subject = $this->createSubject($options); - - $this->assertFalse($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider validArrayForArrayConfigurationIgnoreCaseProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForValidInputOnArrayConfigurationWithIgnoreCaseReturnsNoErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array.'] = $allowedOptionsString; - $options['ignorecase'] = true; - $subject = $this->createSubject($options); - - $this->assertFalse($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider invalidArrayForStringConfigurationIgnoreCaseProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForInvalidInputOnStringConfigurationWithIgnoreCaseReturnsErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array'] = $allowedOptionsString; - $options['ignorecase'] = true; - $subject = $this->createSubject($options); - - $this->assertTrue($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider invalidArrayForArrayConfigurationIgnoreCaseProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForInvalidInputOnArrayConfigurationWithIgnoreCaseReturnsErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array.'] = $allowedOptionsString; - $options['ignorecase'] = true; - $subject = $this->createSubject($options); - - $this->assertTrue($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider validArrayForStringConfigurationIgnoreCaseProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForValidInputOnStringConfigurationWithIgnoreCaseAndStrictReturnsNoErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array'] = $allowedOptionsString; - $options['ignorecase'] = true; - $options['strict'] = true; - $subject = $this->createSubject($options); - - $this->assertFalse($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider validArrayForArrayConfigurationIgnoreCaseProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForValidInputOnArrayConfigurationWithIgnoreCaseAndStrictReturnsNoErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array.'] = $allowedOptionsString; - $options['ignorecase'] = true; - $options['strict'] = true; - $subject = $this->createSubject($options); - - $this->assertFalse($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider invalidArrayForStringConfigurationIgnoreCaseProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForInvalidInputOnStringConfigurationWithIgnoreCaseAndStrictReturnsErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array'] = $allowedOptionsString; - $options['ignorecase'] = true; - $options['strict'] = true; - $subject = $this->createSubject($options); - - $this->assertTrue($subject->validate($value)->hasErrors()); - } - - /** - * @test - * @dataProvider invalidArrayForArrayConfigurationIgnoreCaseProvider - * - * @param string $value - * @param string $allowedOptionsString - */ - public function validateForInvalidInputOnArrayConfigurationWithIgnoreCaseAndStrictReturnsErrors( - $value, - $allowedOptionsString - ) { - $options = [ - 'element' => uniqid('test'), - 'errorMessage' => uniqid('error'), - ]; - $options['array.'] = $allowedOptionsString; - $options['ignorecase'] = true; - $options['strict'] = true; - $subject = $this->createSubject($options); - - $this->assertTrue($subject->validate($value)->hasErrors()); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/IntegerValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/IntegerValidatorTest.php deleted file mode 100644 index db8e2f167429..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/IntegerValidatorTest.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class IntegerValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\IntegerValidator::class; - - public function validateForValidInputHasEmptyErrorResultDataProvider() - { - return [ - '12 for de locale' => [ - 12, - 'de_DE.utf8' - ], - ]; - } - - /** - * @test - * @dataProvider validateForValidInputHasEmptyErrorResultDataProvider - */ - public function validateForValidInputHasEmptyErrorResult($value, $locale) - { - try { - $this->setLocale(LC_NUMERIC, $locale); - } catch (\PHPUnit_Framework_Exception $e) { - $this->markTestSkipped('Locale ' . $locale . ' is not available.'); - } - - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($value)->getErrors() - ); - } - - public function validateForInvalidInputHasNotEmptyErrorResultDataProvider() - { - return [ - '12.1 for en_US locale' => [ - 12.1, - 'en_US.utf8' - ], - '12,1 for de_DE locale' => [ - '12,1', - 'de_DE.utf8' - ], - ]; - } - - /** - * @test - * @dataProvider validateForInvalidInputHasNotEmptyErrorResultDataProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($value, $locale) - { - try { - $this->setLocale(LC_NUMERIC, $locale); - } catch (\PHPUnit_Framework_Exception $e) { - $this->markTestSkipped('Locale ' . $locale . ' is not available.'); - } - - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($value)->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/IpValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/IpValidatorTest.php deleted file mode 100644 index 1c1f0bf39832..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/IpValidatorTest.php +++ /dev/null @@ -1,78 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class IpValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\IpValidator::class; - - /** - * @return array - */ - public function validIpv4Provider() - { - return [ - '127.0.0.1' => ['127.0.0.1'], - '10.0.0.4' => ['10.0.0.4'], - '192.168.0.4' => ['192.168.0.4'], - '0.0.0.0' => ['0.0.0.0'] - ]; - } - - /** - * @return array - */ - public function invalidIpv4Provider() - { - return [ - '127.0.0.256' => ['127.0.0.256'], - '256.0.0.2' => ['256.0.0.2'] - ]; - } - - /** - * @test - * @dataProvider validIpv4Provider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidIpv4Provider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input)->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/LengthValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/LengthValidatorTest.php deleted file mode 100644 index d78086ab6026..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/LengthValidatorTest.php +++ /dev/null @@ -1,127 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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 Prophecy\Argument; -use TYPO3\CMS\Core\Charset\CharsetConverter; -use TYPO3\CMS\Form\Domain\Validator\LengthValidator; - -/** - * Test case - */ -class LengthValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\LengthValidator::class; - - /** - * @var \Prophecy\Prophecy\ObjectProphecy - */ - protected $charsetConverterProphecy; - - /** - * Set up - */ - protected function setUp() - { - parent::setUp(); - $this->charsetConverterProphecy = $this->prophesize(CharsetConverter::class); - $this->charsetConverterProphecy - ->strlen(Argument::cetera()) - ->will(function ($arguments) { - return mb_strlen($arguments[1], $arguments[0]); - }); - } - - protected function tearDown() - { - parent::tearDown(); - unset($this->charsetConverterProphecy); - } - - /** - * @return array - */ - public function validLengthProvider() - { - return [ - '4 ≤ length(myString) ≤ 8' => [ - [4, 8, 'mäString'] - ], - '8 ≤ length(myString) ≤ 8' => [ - [8, 8, 'möString'] - ], - '4 ≤ length(myString)' => [ - [4, null, 'myString'] - ], - '4 ≤ length(asdf) ≤ 4' => [ - [4, 4, 'asdf'] - ], - ]; - } - - /** - * @test - * @dataProvider validLengthProvider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['minimum'] = $input[0]; - $options['maximum'] = $input[1]; - /** @var LengthValidator $subject */ - $subject = $this->createSubject($options); - $subject->injectCharsetConverter($this->charsetConverterProphecy->reveal()); - - $this->assertEmpty( - $subject->validate($input[2])->getErrors() - ); - } - - /** - * @return array - */ - public function invalidLengthProvider() - { - return [ - '4 ≤ length(my) ≤ 12' => [ - [4, 12, 'my'] - ], - '4 ≤ length(my long string) ≤ 12' => [ - [4, 12, 'my long string'] - ], - ]; - } - - /** - * @test - * @dataProvider invalidLengthProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['minimum'] = $input[0]; - $options['maximum'] = $input[1]; - /** @var LengthValidator $subject */ - $subject = $this->createSubject($options); - $subject->injectCharsetConverter($this->charsetConverterProphecy->reveal()); - - $this->assertNotEmpty( - $subject->validate($input[2])->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/LessThanValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/LessThanValidatorTest.php deleted file mode 100644 index e665e6058ced..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/LessThanValidatorTest.php +++ /dev/null @@ -1,79 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class LessThanValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\LessThanValidator::class; - - /** - * @return array - */ - public function validValueProvider() - { - return [ - '4 < 8' => [[8, 4]], - '7.9 < 8' => [[8, 7.9]], - ]; - } - - /** - * @return array - */ - public function invalidValueProvider() - { - return [ - '8 < 4' => [[4, 8]], - '8 < 8' => [[8, 8]], - '8.1 < 8' => [[8, 8.1]], - ]; - } - - /** - * @test - * @dataProvider validValueProvider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['maximum'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input[1])->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidValueProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['maximum'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input[1])->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/RegExpValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/RegExpValidatorTest.php deleted file mode 100644 index 4e3708bfec44..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/RegExpValidatorTest.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class RegExpValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\RegExpValidator::class; - - /** - * @return array - */ - public function validDataProvider() - { - return [ - '/^a/ matches a' => [['/^a/', 'a']], - ]; - } - - /** - * @return array - */ - public function invalidDataProvider() - { - return [ - '/[^\d]/ matches 8' => [['/[^\d]/', 8]], - ]; - } - - /** - * @test - * @dataProvider validDataProvider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['expression'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input[1])->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidDataProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $options['expression'] = $input[0]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input[1])->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/RequiredValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/RequiredValidatorTest.php deleted file mode 100644 index 13ab06124689..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/RequiredValidatorTest.php +++ /dev/null @@ -1,87 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class RequiredValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\RequiredValidator::class; - - /** - * @return array - */ - public function validDataProvider() - { - return [ - 'string "a"' => ['a'], - 'string "a b"' => ['a b'], - 'string "0"' => ['0'], - 'value 0' => [0], - 'array with string "a"' => [['a']], - 'array with string "a b"' => [['a b']], - 'array with string "0"' => [['0']], - 'array with value 0' => [[0]], - 'array with strings "a" and "b"' => [['a', 'b']], - 'array with empty string and "a"' => [['', 'a']], - 'array with empty string and "0"' => [['', '0']], - 'array with empty string and 0' => [['', 0]], - ]; - } - - /** - * @return array - */ - public function invalidDataProvider() - { - return [ - 'empty string' => [''], - 'array with empty string' => [['']], - 'array with empty strings' => [['', '']] - ]; - } - - /** - * @test - * @dataProvider validDataProvider - */ - public function validateForValidDataHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidDataProvider - */ - public function validateForInvalidDataHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input)->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/Validator/UriValidatorTest.php b/typo3/sysext/form/Tests/Unit/Validator/UriValidatorTest.php deleted file mode 100644 index 6367b8000427..000000000000 --- a/typo3/sysext/form/Tests/Unit/Validator/UriValidatorTest.php +++ /dev/null @@ -1,76 +0,0 @@ -<?php -namespace TYPO3\CMS\Form\Tests\Unit\Validator; - -/* - * 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! - */ - -/** - * Test case - */ -class UriValidatorTest extends AbstractValidatorTest -{ - /** - * @var string - */ - protected $subjectClassName = \TYPO3\CMS\Form\Domain\Validator\UriValidator::class; - - /** - * @return array - */ - public function validDataProvider() - { - return [ - 'http://example.net' => ['http://example.net'], - 'https://example.net' => ['https://example.net'], - 'http://a:b@example.net' => ['http://a:b@example.net'], - ]; - } - - /** - * @return array - */ - public function invalidDataProvider() - { - return [ - 'index.php' => ['index.php'] - ]; - } - - /** - * @test - * @dataProvider validDataProvider - */ - public function validateForValidInputHasEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertEmpty( - $subject->validate($input)->getErrors() - ); - } - - /** - * @test - * @dataProvider invalidDataProvider - */ - public function validateForInvalidInputHasNotEmptyErrorResult($input) - { - $options = ['element' => uniqid('test'), 'errorMessage' => uniqid('error')]; - $subject = $this->createSubject($options); - - $this->assertNotEmpty( - $subject->validate($input)->getErrors() - ); - } -} diff --git a/typo3/sysext/form/Tests/Unit/ViewHelpers/Form/DatePickerViewHelperTest.php b/typo3/sysext/form/Tests/Unit/ViewHelpers/Form/DatePickerViewHelperTest.php new file mode 100644 index 000000000000..edb28f043f6c --- /dev/null +++ b/typo3/sysext/form/Tests/Unit/ViewHelpers/Form/DatePickerViewHelperTest.php @@ -0,0 +1,143 @@ +<?php +declare(strict_types=1); +namespace TYPO3\CMS\Form\Tests\Unit\ViewHelpers\Form; + +/* + * 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\Core\Tests\UnitTestCase; +use TYPO3\CMS\Form\ViewHelpers\Form\DatePickerViewHelper; + +/** + * Test case + */ +class DatePickerViewHelperTest extends UnitTestCase +{ + + /** + * @var \TYPO3\CMS\Form\ViewHelpers\Form\DatePickerViewHelper + */ + protected $subject = null; + + /** + * Set up + * + * @return void + */ + protected function setUp() + { + $this->subject = $this->getAccessibleMock(DatePickerViewHelper::class, [ + 'dummy' + ], [], '', false); + } + + /** + * @test + */ + public function convertDateFormatToDatePickerFormatReturnsTransformedFormat01() + { + $input = 'd'; + $expected = 'dd'; + $this->assertSame($expected, $this->subject->_call('convertDateFormatToDatePickerFormat', $input)); + } + + /** + * @test + */ + public function convertDateFormatToDatePickerFormatReturnsTransformedFormat02() + { + $input = 'D'; + $expected = 'D'; + $this->assertSame($expected, $this->subject->_call('convertDateFormatToDatePickerFormat', $input)); + } + + /** + * @test + */ + public function convertDateFormatToDatePickerFormatReturnsTransformedFormat03() + { + $input = 'j'; + $expected = 'o'; + $this->assertSame($expected, $this->subject->_call('convertDateFormatToDatePickerFormat', $input)); + } + + /** + * @test + */ + public function convertDateFormatToDatePickerFormatReturnsTransformedFormat04() + { + $input = 'l'; + $expected = 'DD'; + $this->assertSame($expected, $this->subject->_call('convertDateFormatToDatePickerFormat', $input)); + } + + /** + * @test + */ + public function convertDateFormatToDatePickerFormatReturnsTransformedFormat05() + { + $input = 'F'; + $expected = 'MM'; + $this->assertSame($expected, $this->subject->_call('convertDateFormatToDatePickerFormat', $input)); + } + + /** + * @test + */ + public function convertDateFormatToDatePickerFormatReturnsTransformedFormat06() + { + $input = 'm'; + $expected = 'mm'; + $this->assertSame($expected, $this->subject->_call('convertDateFormatToDatePickerFormat', $input)); + } + + /** + * @test + */ + public function convertDateFormatToDatePickerFormatReturnsTransformedFormat07() + { + $input = 'M'; + $expected = 'M'; + $this->assertSame($expected, $this->subject->_call('convertDateFormatToDatePickerFormat', $input)); + } + + /** + * @test + */ + public function convertDateFormatToDatePickerFormatReturnsTransformedFormat08() + { + $input = 'n'; + $expected = 'm'; + $this->assertSame($expected, $this->subject->_call('convertDateFormatToDatePickerFormat', $input)); + } + + /** + * @test + */ + public function convertDateFormatToDatePickerFormatReturnsTransformedFormat09() + { + $input = 'Y'; + $expected = 'yy'; + $this->assertSame($expected, $this->subject->_call('convertDateFormatToDatePickerFormat', $input)); + } + + /** + * @test + */ + public function convertDateFormatToDatePickerFormatReturnsTransformedFormat10() + { + $input = 'y'; + $expected = 'y'; + $this->assertSame($expected, $this->subject->_call('convertDateFormatToDatePickerFormat', $input)); + } +} diff --git a/typo3/sysext/form/composer.json b/typo3/sysext/form/composer.json index 33e88b4a256d..532111b80b27 100644 --- a/typo3/sysext/form/composer.json +++ b/typo3/sysext/form/composer.json @@ -1,31 +1,31 @@ { - "name": "typo3/cms-form", - "type": "typo3-cms-framework", - "description": "TYPO3 Core", - "homepage": "https://typo3.org", - "license": ["GPL-2.0+"], + "name": "typo3/cms-form", + "type": "typo3-cms-framework", + "description": "Extensible and flexible API and editor for building web forms", + "homepage": "https://typo3.org", + "license": ["GPL-2.0+"], - "require": { - "typo3/cms-core": "*" - }, - "replace": { - "form": "*" - }, - "extra": { - "typo3/cms": { - "Package": { - "partOfFactoryDefault": true - } - } - }, - "autoload": { - "psr-4": { - "TYPO3\\CMS\\Form\\": "Classes/" - } - }, - "autoload-dev": { - "psr-4": { - "TYPO3\\CMS\\Form\\Tests\\": "Tests/" - } - } + "require": { + "typo3/cms-core": "*" + }, + "replace": { + "form": "*" + }, + "extra": { + "typo3/cms": { + "Package": { + "partOfFactoryDefault": true + } + } + }, + "autoload": { + "psr-4": { + "TYPO3\\CMS\\Form\\": "Classes/" + } + }, + "autoload-dev": { + "psr-4": { + "TYPO3\\CMS\\Form\\Tests\\": "Tests/" + } + } } diff --git a/typo3/sysext/form/ext_emconf.php b/typo3/sysext/form/ext_emconf.php index 22ce0118f671..08321e979ce5 100644 --- a/typo3/sysext/form/ext_emconf.php +++ b/typo3/sysext/form/ext_emconf.php @@ -1,14 +1,14 @@ <?php $EM_CONF[$_EXTKEY] = [ 'title' => 'Form', - 'description' => 'Form Library, Plugin and Wizard', - 'category' => 'plugin', + 'description' => 'Form Library, Plugin and Editor', + 'category' => 'misc', 'state' => 'beta', 'uploadfolder' => 0, 'createDirs' => '', 'clearCacheOnLoad' => 1, - 'author' => 'Patrick Broens, Ralf Zimmermann', - 'author_email' => 'patrick@patrickbroens.nl, ralf.zimmermann@tritum.de', + 'author' => 'Form Team', + 'author_email' => '', 'author_company' => '', 'version' => '8.5.0', 'constraints' => [ diff --git a/typo3/sysext/form/ext_localconf.php b/typo3/sysext/form/ext_localconf.php index 511c41d247fd..0be93ed71c1e 100644 --- a/typo3/sysext/form/ext_localconf.php +++ b/typo3/sysext/form/ext_localconf.php @@ -1,47 +1,66 @@ <?php defined('TYPO3_MODE') or die(); -if (TYPO3_MODE === 'BE') { - // Apply PageTSconfig +call_user_func(function () { + // Register FE plugin + \TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin( + 'TYPO3.CMS.Form', + 'Formframework', + ['FormFrontend' => 'render, perform'], + ['FormFrontend' => 'perform'], + \TYPO3\CMS\Extbase\Utility\ExtensionUtility::PLUGIN_TYPE_CONTENT_ELEMENT + ); + + // Add new content element wizard entry \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addPageTSConfig( '<INCLUDE_TYPOSCRIPT: source="FILE:EXT:form/Configuration/PageTS/modWizards.ts">' ); - // Add default User TS Config FORM configuration - \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::addUserTSConfig( - '<INCLUDE_TYPOSCRIPT: source="FILE:EXT:form/Configuration/UserTSconfig/userTSConfig.txt">' + // FE file upload processing + \TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter( + \TYPO3\CMS\Form\Mvc\Property\TypeConverter\UploadedFileReferenceConverter::class ); - // Backend view - $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['cms/layout/class.tx_cms_layout.php']['tt_content_drawItem']['mailform'] = - \TYPO3\CMS\Form\Hooks\PageLayoutView\MailformPreviewRenderer::class; - - $GLOBALS['TYPO3_CONF_VARS']['SYS']['formEngine']['nodeRegistry'][1440772316] = [ - 'nodeName' => 'formwizard', - 'priority' => 40, - 'class' => \TYPO3\CMS\Form\View\Wizard\Element\FormWizardElement::class, - ]; -} - -// Extbase handling -\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerTypeConverter( - \TYPO3\CMS\Form\Domain\Property\TypeConverter\ArrayToValidationElementConverter::class -); - -\TYPO3\CMS\Extbase\Utility\ExtensionUtility::configurePlugin( - 'TYPO3.CMS.Form', - 'Form', - ['Frontend' => 'show, confirmation, dispatchConfirmationButtonClick, process, afterProcess'], - ['Frontend' => 'show, confirmation, dispatchConfirmationButtonClick, process, afterProcess'] -); + // Hook to enrich tt_content form flex element with finisher settings and form list drop down + $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][\TYPO3\CMS\Core\Configuration\FlexForm\FlexFormTools::class]['flexParsing'][ + \TYPO3\CMS\Form\Hooks\DataStructureIdentifierHook::class + ] = \TYPO3\CMS\Form\Hooks\DataStructureIdentifierHook::class; -\TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\SignalSlot\Dispatcher::class) - ->connect( - \TYPO3\CMS\Form\Domain\Builder\FormBuilder::class, - 'txFormHandleIncomingValues', - \TYPO3\CMS\Form\Hooks\HandleIncomingFormValues::class, - 'handleIncomingFormValues' - ); + // Hook to count used forms elements in tt_content + $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['GLOBAL']['softRefParser']['formPersistenceIdentifier'] = + \TYPO3\CMS\Form\Hooks\SoftReferenceParserHook::class; -// Register the extbase plugin as shorthand for typoscript 10 = FORM -$GLOBALS['TYPO3_CONF_VARS']['FE']['ContentObjects']['FORM'] = \TYPO3\CMS\Form\ContentObject\FormContentObject::class; + // Add a bunch of icons to icon registry + $iconIdentifiers = [ + 'advanced-password', + 'checkbox', + 'content-element', + 'date-picker', + 'duplicate', + 'fieldset', + 'file-upload', + 'finisher', + 'image-upload', + 'insert-after', + 'insert-in', + 'multi-checkbox', + 'multi-select', + 'page', + 'password', + 'radio-button', + 'single-select', + 'static-text', + 'summary-page', + 'text', + 'textarea', + 'validator' + ]; + $iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class); + foreach ($iconIdentifiers as $iconIdentifier) { + $iconRegistry->registerIcon( + 't3-form-icon-' . $iconIdentifier, + \TYPO3\CMS\Core\Imaging\IconProvider\SvgIconProvider::class, + ['source' => 'EXT:form/Resources/Public/Images/' . $iconIdentifier . '.svg'] + ); + } +}); diff --git a/typo3/sysext/form/ext_tables.php b/typo3/sysext/form/ext_tables.php new file mode 100644 index 000000000000..c036107a9a71 --- /dev/null +++ b/typo3/sysext/form/ext_tables.php @@ -0,0 +1,21 @@ +<?php +defined('TYPO3_MODE') or die(); + +// Register the backend module Web->Forms +\TYPO3\CMS\Extbase\Utility\ExtensionUtility::registerModule( + 'TYPO3.CMS.Form', + 'web', + 'formbuilder', + '', + [ + 'FormManager' => 'index, show, create, duplicate, references, delete', + 'FormEditor' => 'index, saveForm, renderFormPage, renderRenderableOptions', + ], + [ + 'access' => 'user,group', + 'icon' => 'EXT:form/Resources/Public/Icons/Extension.png', + 'labels' => 'LLL:EXT:form/Resources/Private/Language/locallang_module.xlf', + 'navigationComponentId' => '', + 'inheritNavigationComponentFromMainModule' => false + ] +); diff --git a/typo3/sysext/form/ext_tables.sql b/typo3/sysext/form/ext_tables.sql deleted file mode 100644 index 3fe240733fd6..000000000000 --- a/typo3/sysext/form/ext_tables.sql +++ /dev/null @@ -1,6 +0,0 @@ -# -# Table structure for table 'tt_content' -# -CREATE TABLE tt_content ( - tx_form_predefinedform varchar(255) DEFAULT '' NOT NULL -); diff --git a/typo3/sysext/form/ext_typoscript_setup.txt b/typo3/sysext/form/ext_typoscript_setup.txt new file mode 100644 index 000000000000..a47e55edca49 --- /dev/null +++ b/typo3/sysext/form/ext_typoscript_setup.txt @@ -0,0 +1,2 @@ +<INCLUDE_TYPOSCRIPT: source="FILE:EXT:form/Configuration/TypoScript/setup.txt"> +<INCLUDE_TYPOSCRIPT: source="FILE:EXT:form/Configuration/TypoScript/backend.txt"> \ No newline at end of file -- GitLab