From 1c9819f99cbc79b4b844450809c4707d4cbbc1ba Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Sun, 6 Aug 2017 17:43:41 +0200 Subject: [PATCH 01/27] [CLEANUP] Remove doc and reference to removed method --- Classes/Service/FormService.php | 4 -- Documentation/04-DeveloperManual/PHP/Misc.rst | 27 --------- .../07-CheatSheets/PhpCheatSheet.rst | 56 ------------------- .../04-DeveloperManual/PHP/Misc.rst | 27 --------- .../07-CheatSheets/PhpCheatSheet.rst | 56 ------------------- 5 files changed, 170 deletions(-) diff --git a/Classes/Service/FormService.php b/Classes/Service/FormService.php index 4ff51c0..2e34675 100644 --- a/Classes/Service/FormService.php +++ b/Classes/Service/FormService.php @@ -24,10 +24,6 @@ * * - Access to the last submitted form which validation failed: * `$myFailedForm = FormUtility::getFormWithErrors(MyForm::class);` - * - * - Automatic redirect if required argument is missing: - * - * @see `onRequiredArgumentIsMissing()` */ class FormService implements SingletonInterface { diff --git a/Documentation/04-DeveloperManual/PHP/Misc.rst b/Documentation/04-DeveloperManual/PHP/Misc.rst index 98e55d1..303693b 100644 --- a/Documentation/04-DeveloperManual/PHP/Misc.rst +++ b/Documentation/04-DeveloperManual/PHP/Misc.rst @@ -21,33 +21,6 @@ Where :php:`$formClassName` is the name of the class of the form model (for inst ----- -Redirect if the form is not sent --------------------------------- - -You have access to a function to **redirect the current action** if a **required argument for an action is not filled**: :php:`\Romm\Formz\Utility\FormUtility::onRequiredArgumentIsMissing()` - -This is useful when a user tries to access the submit action of the form controller. Indeed, this action is called only when the submitted form is valid. But a user can still access a URL which calls this action, without even submitting the form. In a normal scope, it can throw a fatal error. You may then use the action initialization function, which looks like ``initializeActionName()``, and call the function above. - -.. code-block:: php - - public function initializeSubmitFormAction() - { - FormUtility::onRequiredArgumentIsMissing( - $this->arguments, - $this->request, - function($missingArgumentName) { - $this->redirect('myIndex'); - } - ); - } - - public function submitFormAction(FormExample $myForm) - { - // ... - } - ------ - Create a custom activation condition ------------------------------------ diff --git a/Documentation/07-CheatSheets/PhpCheatSheet.rst b/Documentation/07-CheatSheets/PhpCheatSheet.rst index ecf2483..568e9e1 100644 --- a/Documentation/07-CheatSheets/PhpCheatSheet.rst +++ b/Documentation/07-CheatSheets/PhpCheatSheet.rst @@ -287,59 +287,3 @@ The function :php:`getFormWithErrors` returns a form submitted during the last r } } } - -Redirect an action if an argument is missing -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Using Extbase, a user can try to access to the submission action, without actually submitting the form. For instance, he submits the form, then pastes the result URL in a new tab: Extbase will think the user submitted the form, but he didn't. In normal time, it would throw a fatal error. - -FormZ provides the function :php:`onRequiredArgumentIsMissing`, which will check that a required argument is missing, and run actions otherwise. - -**Example:** - -.. code-block:: php - :emphasize-lines: 22 - - arguments, - $this->request, - function() { - // If the argument `$exForm` is missing, we redirect to the - // action "showForm". - $this->redirect('showForm'); - } - ); - } - - /** - * Action called when the Example form is submitted. - * - * @param ExampleForm $exForm - * @validate $exForm MyVendor.MyExtension:Form\ExampleFormValidator - */ - public function submitFormAction(ExampleForm $exForm) - { - // The form is valid: should you save it? Process it? Your call! - } - } diff --git a/Documentation/Localization.fr_FR/04-DeveloperManual/PHP/Misc.rst b/Documentation/Localization.fr_FR/04-DeveloperManual/PHP/Misc.rst index 1deada3..10beff7 100644 --- a/Documentation/Localization.fr_FR/04-DeveloperManual/PHP/Misc.rst +++ b/Documentation/Localization.fr_FR/04-DeveloperManual/PHP/Misc.rst @@ -21,33 +21,6 @@ Où :php:`$formClassName` est le nom de classe du modèle du formulaire (exemple ----- -Redirection si formulaire inexistant ------------------------------------- - -Vous avez accès à une fonction qui vous permet de **rediriger l'action actuelle** si un **argument requis d'une action n'est pas rempli** : :php:`\Romm\Formz\Utility\FormUtility::onRequiredArgumentIsMissing()` - -C'est notamment utile lorsqu'un utilisateur essaie d'accéder à l'action du contrôleur de soumission du formulaire. En effet, cette action n'est véritablement appelée que lorsque le formulaire envoyé est valide. Mais il est possible pour un utilisateur d'accéder à une URL qui appelle cette action, sans même soumettre le formulaire. En temps normal, cela peut engendrer une erreur fatale. Vous pouvez donc utiliser la fonction d'initialisation de l'action, qui est de la forme ``initializeActionName()``, et y appeler la fonction ci-dessus. - -.. code-block:: php - - public function initializeSubmitFormAction() - { - FormUtility::onRequiredArgumentIsMissing( - $this->arguments, - $this->request, - function($missingArgumentName) { - $this->redirect('myIndex'); - } - ); - } - - public function submitFormAction(FormExample $myForm) - { - // ... - } - ------ - Créer un type de condition d'activation personnalisé ---------------------------------------------------- diff --git a/Documentation/Localization.fr_FR/07-CheatSheets/PhpCheatSheet.rst b/Documentation/Localization.fr_FR/07-CheatSheets/PhpCheatSheet.rst index 8854ec8..1ea7cf6 100644 --- a/Documentation/Localization.fr_FR/07-CheatSheets/PhpCheatSheet.rst +++ b/Documentation/Localization.fr_FR/07-CheatSheets/PhpCheatSheet.rst @@ -287,59 +287,3 @@ La fonction :php:`getFormWithErrors` permet de récupérer un formulaire soumis } } } - -Rediriger une action si l'argument est manquant -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Avec Extbase, il est parfois possible qu'un utilisateur essaie d'accéder à une action de soumission de formulaire, sans pour autant avoir soumis le formulaire. Par exemple, il soumet le formulaire, puis rentre l'URL obtenue dans un nouvel onglet : Extbase croira que l'utilisateur a soumis un formulaire, pour autant ce dernier n'existe pas. En temps normal, cela renvoie une erreur fatale. - -FormZ met à disposition la fonction :php:`onRequiredArgumentIsMissing`, qui permet de vérifier qu'un argument requis est manquant, et de lancer certaines actions si c'est le cas. - -**Exemple :** - -.. code-block:: php - :emphasize-lines: 22 - - arguments, - $this->request, - function() { - // If the argument `$exForm` is missing, we redirect to the - // action "showForm". - $this->redirect('showForm'); - } - ); - } - - /** - * Action called when the Example form is submitted. - * - * @param ExampleForm $exForm - * @validate $exForm MyVendor.MyExtension:Form\ExampleFormValidator - */ - public function submitFormAction(ExampleForm $exForm) - { - // The form is valid: should you save it? Process it? Your call! - } - } From 2c4763fa3f8d5eada45ddd99ed7bb204166e8eec Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Fri, 11 Aug 2017 13:22:29 +0200 Subject: [PATCH 02/27] [BUGFIX] Instantiate fields validators with object manager in Ajax Following the commit 2f23f8088e8c7db58021eccde04a6f38de094c1f, a field validator should be instantiated with the object manager, to allow more Extbase feature. The instantiation type was not modified in the Ajax controller, though. --- Classes/Controller/AjaxValidationController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Classes/Controller/AjaxValidationController.php b/Classes/Controller/AjaxValidationController.php index f8af88b..4fce7e7 100644 --- a/Classes/Controller/AjaxValidationController.php +++ b/Classes/Controller/AjaxValidationController.php @@ -30,7 +30,6 @@ use Romm\Formz\Service\ExtensionService; use Romm\Formz\Service\MessageService; use Romm\Formz\Validation\DataObject\ValidatorDataObject; -use TYPO3\CMS\Core\Utility\GeneralUtility; use TYPO3\CMS\Extbase\Error\Error; use TYPO3\CMS\Extbase\Mvc\Controller\ActionController; use TYPO3\CMS\Extbase\Mvc\RequestInterface; @@ -201,7 +200,7 @@ public function runAction($name, $className, $fieldName, $validatorName) $validatorDataObject = new ValidatorDataObject($this->formObject, $this->validation); /** @var ValidatorInterface $validator */ - $validator = GeneralUtility::makeInstance( + $validator = Core::instantiate( $this->validation->getClassName(), $this->validation->getOptions(), $validatorDataObject From 0be97eb34fe8ff4651da20d57f97a0bbd92efa98 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Fri, 11 Aug 2017 13:30:12 +0200 Subject: [PATCH 03/27] [TASK] Initialize validator messages before validation This allows more flexibility for the messages initialization; for instance you can dynamically build the messages list in the method `initializeObject()` of your validator, then they will be processed just before the validation actually runs. --- .../Validator/AbstractValidator.php | 21 +++++++++++++++++ .../Validation/Validator/DummyValidator.php | 11 +++++++++ .../Validator/AbstractValidatorTest.php | 23 +++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/Classes/Validation/Validator/AbstractValidator.php b/Classes/Validation/Validator/AbstractValidator.php index 5865aee..c6b72f7 100644 --- a/Classes/Validation/Validator/AbstractValidator.php +++ b/Classes/Validation/Validator/AbstractValidator.php @@ -20,6 +20,7 @@ use Romm\Formz\Form\FormInterface; use Romm\Formz\Service\MessageService; use Romm\Formz\Validation\DataObject\ValidatorDataObject; +use TYPO3\CMS\Extbase\Error\Result; abstract class AbstractValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator { @@ -96,11 +97,31 @@ final public function __construct(array $options = [], ValidatorDataObject $data $this->dataObject = $dataObject; $this->form = $dataObject->getFormObject()->getForm(); + } + + /** + * @param mixed $value + * @return Result + */ + public function validate($value) + { + /* + * Messages are initialized just before the validation actually runs, + * and not in the constructor. + * + * This allows more flexibility for the messages initialization; for + * instance you can dynamically build the messages list in the method + * `initializeObject()` of your validator (and not only in the class + * variable declaration), then the messages will be processed just + * below. + */ $this->messages = MessageService::get()->filterMessages( $this->dataObject->getValidation()->getMessages(), $this->supportedMessages, (bool)$this->supportsAllMessages ); + + return parent::validate($value); } /** diff --git a/Tests/Fixture/Validation/Validator/DummyValidator.php b/Tests/Fixture/Validation/Validator/DummyValidator.php index 9a36544..b61975e 100644 --- a/Tests/Fixture/Validation/Validator/DummyValidator.php +++ b/Tests/Fixture/Validation/Validator/DummyValidator.php @@ -6,6 +6,7 @@ class DummyValidator extends AbstractValidator { const MESSAGE_1 = 'message1'; + const DYNAMIC_MESSAGE = 'dynamic_message'; /** * @var array @@ -26,6 +27,16 @@ class DummyValidator extends AbstractValidator */ protected $callback; + /** + * Dynamically assigns a message to the list of supported messages. + * + * @see \Romm\Formz\Tests\Unit\Validation\Validator\AbstractValidatorTest::runValidatorDataProvider #9 + */ + public function initializeObject() + { + $this->supportedMessages[self::DYNAMIC_MESSAGE]['value'] = 'dynamic message'; + } + /** * @param mixed $value */ diff --git a/Tests/Unit/Validation/Validator/AbstractValidatorTest.php b/Tests/Unit/Validation/Validator/AbstractValidatorTest.php index cb71966..f712a40 100644 --- a/Tests/Unit/Validation/Validator/AbstractValidatorTest.php +++ b/Tests/Unit/Validation/Validator/AbstractValidatorTest.php @@ -32,6 +32,8 @@ public function runValidator($value, array $messages, $callback, $finalCallback, $validatorDataObject = new ValidatorDataObject($this->getDefaultFormObject(), $validation); $validator = new DummyValidator([], $validatorDataObject); + $validator->initializeObject(); + if (is_callable($callback)) { $validator->setCallBack($callback); } @@ -205,6 +207,27 @@ public function runValidatorDataProvider() $this->assertEquals(42, $message->getCode()); $this->assertEquals('baz', $message->getTitle()); } + ], + /* + * #9 + * + * Messages can be assigned dynamically (outside of the variable + * declaration scope). The code below will test that a dynamic + * message can be retrieved after the validation process. + */ + [ + 'value' => 'foo', + 'messages' => [], + 'callback' => function (DummyValidator $validator) { + $validator->addNewError(DummyValidator::DYNAMIC_MESSAGE, 42, [], 'foo'); + }, + 'final' => function (Result $result) { + $this->assertTrue($result->hasErrors()); + $message = $result->getFirstError(); + $this->assertEquals('dynamic message', $message->getMessage()); + $this->assertEquals(42, $message->getCode()); + $this->assertEquals('foo', $message->getTitle()); + } ] ]; } From f22182270b2f33c9e04f78fd480df70656fcd397 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Tue, 5 Sep 2017 14:34:04 +0200 Subject: [PATCH 04/27] [BUGFIX] Fix variables not passed to field layout view in TYPO3 8.6 Fluid variables were not passed correctly to the layout standalone view of a field. This commit fixes this issue, as well as a refactor for the older versions behaviour to make the code more readable. --- Classes/ViewHelpers/FieldViewHelper.php | 150 ++++++++++-------- ...LocalizationJavaScriptAssetHandlerTest.php | 2 +- .../Unit/ViewHelpers/FieldViewHelperTest.php | 16 +- 3 files changed, 95 insertions(+), 73 deletions(-) diff --git a/Classes/ViewHelpers/FieldViewHelper.php b/Classes/ViewHelpers/FieldViewHelper.php index dd9a680..57c1373 100644 --- a/Classes/ViewHelpers/FieldViewHelper.php +++ b/Classes/ViewHelpers/FieldViewHelper.php @@ -68,11 +68,6 @@ class FieldViewHelper extends AbstractViewHelper */ protected $slotService; - /** - * @var array - */ - protected $originalArguments = []; - /** * @inheritdoc */ @@ -88,8 +83,6 @@ public function initializeArguments() */ public function render() { - $viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer(); - /* * First, we check if this view helper is called from within the * `FormViewHelper`, because it would not make sense anywhere else. @@ -116,21 +109,11 @@ public function render() */ $this->renderChildren(); - /* - * We need to store original arguments declared for the current view - * context, because we may override them during the rendering of this - * view helper. - */ - $this->storeOriginalArguments(); - - /* - * We merge the arguments with the ones registered with the - * `OptionViewHelper`. - */ - $templateArguments = $this->arguments['arguments'] ?: []; - ArrayUtility::mergeRecursiveWithOverrule($templateArguments, $this->fieldService->getFieldOptions()); + if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { + $restoreCallback = $this->storeViewDataLegacy(); + } - $currentView = $viewHelperVariableContainer->getView(); + $templateArguments = $this->getTemplateArguments(); $result = $this->renderLayoutView($templateArguments); @@ -140,14 +123,16 @@ public function render() $this->fieldService->removeCurrentField(); $this->slotService->resetState(); - $viewHelperVariableContainer->setView($currentView); - $this->restoreOriginalArguments($templateArguments); + if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { + /** @noinspection PhpUndefinedVariableInspection */ + $restoreCallback($templateArguments); + } return $result; } /** - * Will render the associated Fluid view for this field (configured with the + * Will return the associated Fluid view for this field (configured with the * `layout` argument). * * @param array $templateArguments @@ -168,23 +153,34 @@ protected function renderLayoutView(array $templateArguments) $view = $this->fieldService->getView($layout); + /* + * Warning: we need to store the layouts/partials paths before + * manipulating the rendering context! + */ $layoutPaths = $this->getPaths('layout'); $partialPaths = $this->getPaths('partial'); if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { $view->setRenderingContext($this->renderingContext); } else { - $view->setControllerContext($this->controllerContext); + $renderingContext = $view->getRenderingContext(); - $variableProvider = $this->getVariableProvider(); + // Removing all variables previously added to the provider. + $provider = $renderingContext->getVariableProvider(); - foreach ($templateArguments as $key => $value) { - if ($variableProvider->exists($key)) { - $variableProvider->remove($key); - } - - $variableProvider->add($key, $value); + foreach ($provider->getAllIdentifiers() as $key) { + $provider->remove($key); } + + /* + * Updating the view dependencies: the variable container as well as + * the controller context must be injected in the view. + */ + $renderingContext->setViewHelperVariableContainer($this->viewHelperVariableContainer); + + $view->setControllerContext($this->controllerContext); + + $this->viewHelperVariableContainer->setView($view); } $view->setLayoutRootPaths($layoutPaths); @@ -194,6 +190,56 @@ protected function renderLayoutView(array $templateArguments) return $view->render(); } + /** + * Temporary solution for TYPO3 6.2 to 7.6 that will store the current view + * variables, to be able to restore them later. + * + * A callback function is returned; it will be called once the field layout + * view was processed, and will restore all the view data. + * + * @return \Closure + * + * @deprecated Will be deleted when TYPO3 7.6 is not supported anymore. + */ + protected function storeViewDataLegacy() + { + $originalArguments = []; + + $variableProvider = $this->getVariableProvider(); + + foreach (self::$reservedVariablesNames as $key) { + if ($variableProvider->exists($key)) { + $originalArguments[$key] = $variableProvider->get($key); + } + } + + $viewHelperVariableContainer = $this->renderingContext->getViewHelperVariableContainer(); + $currentView = $viewHelperVariableContainer->getView(); + + return function (array $templateArguments) use ($originalArguments, $variableProvider, $viewHelperVariableContainer, $currentView) { + $viewHelperVariableContainer->setView($currentView); + + /* + * Cleaning up the variables in the provider: the original + * values are restored to make the provider like it was before + * the field rendering started. + */ + foreach ($variableProvider->getAllIdentifiers() as $identifier) { + if (array_key_exists($identifier, $templateArguments)) { + $variableProvider->remove($identifier); + } + } + + foreach ($originalArguments as $key => $value) { + if ($variableProvider->exists($key)) { + $variableProvider->remove($key); + } + + $variableProvider->add($key, $value); + } + }; + } + /** * Will check that the given field exists in the current form definition and * inject it in the `FieldService` as `currentField`. @@ -257,43 +303,17 @@ protected function getLayout(View $viewConfiguration) } /** - * Stores some arguments which may already have been initialized, and could - * be overridden in the local scope. - */ - protected function storeOriginalArguments() - { - $this->originalArguments = []; - $variableProvider = $this->getVariableProvider(); - - foreach (self::$reservedVariablesNames as $key) { - if ($variableProvider->exists($key)) { - $this->originalArguments[$key] = $variableProvider->get($key); - } - } - } - - /** - * Will restore original arguments in the template variable container. + * Merging the arguments with the ones registered with the + * `OptionViewHelper`. * - * @param array $templateArguments + * @return array */ - protected function restoreOriginalArguments(array $templateArguments) + protected function getTemplateArguments() { - $variableProvider = $this->getVariableProvider(); - - foreach ($variableProvider->getAllIdentifiers() as $identifier) { - if (array_key_exists($identifier, $templateArguments)) { - $variableProvider->remove($identifier); - } - } - - foreach ($this->originalArguments as $key => $value) { - if ($variableProvider->exists($key)) { - $variableProvider->remove($key); - } + $templateArguments = $this->arguments['arguments'] ?: []; + ArrayUtility::mergeRecursiveWithOverrule($templateArguments, $this->fieldService->getFieldOptions()); - $variableProvider->add($key, $value); - } + return $templateArguments; } /** diff --git a/Tests/Unit/AssetHandler/JavaScript/FormzLocalizationJavaScriptAssetHandlerTest.php b/Tests/Unit/AssetHandler/JavaScript/FormzLocalizationJavaScriptAssetHandlerTest.php index eec8510..c647a86 100644 --- a/Tests/Unit/AssetHandler/JavaScript/FormzLocalizationJavaScriptAssetHandlerTest.php +++ b/Tests/Unit/AssetHandler/JavaScript/FormzLocalizationJavaScriptAssetHandlerTest.php @@ -30,7 +30,7 @@ public function checkLocalizationIsInitializedCorrectly() $validation->setClassName(RequiredValidator::class); $field->addValidation($validation); - /** @var FormzLocalizationJavaScriptAssetHandler|\PHPUnit_Framework_MockObject_MockObject $assetHandler */ + /** @var FormzLocalizationJavaScriptAssetHandler|\PHPUnit_Framework_MockObject_MockObject $assetHandler */ $assetHandler = $this->getMockBuilder(FormzLocalizationJavaScriptAssetHandler::class) ->setMethods(['handleRealTranslations', 'handleTranslationsBinding']) ->setConstructorArgs([$assetHandlerFactory]) diff --git a/Tests/Unit/ViewHelpers/FieldViewHelperTest.php b/Tests/Unit/ViewHelpers/FieldViewHelperTest.php index 02f73c1..a62f96a 100644 --- a/Tests/Unit/ViewHelpers/FieldViewHelperTest.php +++ b/Tests/Unit/ViewHelpers/FieldViewHelperTest.php @@ -338,13 +338,15 @@ public function originalArgumentsAreRestoredAfterViewHelperIsRendered() ->will(function ($arguments) use ($templateVariableContainerProphecy, &$templateVariables) { $templateVariables[$arguments[0]] = $arguments[1]; - if (in_array($arguments[0], FieldViewHelper::$reservedVariablesNames)) { - $templateVariableContainerProphecy - ->remove($arguments[0]) - ->shouldBeCalled() - ->will(function () use ($arguments, &$templateVariables) { - unset($templateVariables[$arguments[0]]); - }); + if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { + if (in_array($arguments[0], FieldViewHelper::$reservedVariablesNames)) { + $templateVariableContainerProphecy + ->remove($arguments[0]) + ->shouldBeCalled() + ->will(function () use ($arguments, &$templateVariables) { + unset($templateVariables[$arguments[0]]); + }); + } } $templateVariableContainerProphecy From a97b7a301623a043961d842fc61d4894b9855eb9 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Mon, 23 Oct 2017 16:24:46 +0200 Subject: [PATCH 05/27] [TASK] Fix unit tests --- Tests/Fixture/Configuration/FormzConfiguration.php | 5 +++-- Tests/Unit/Condition/ConditionFactoryTest.php | 6 +++--- .../Unit/Condition/Items/AbstractConditionItemTest.php | 4 ++-- Tests/Unit/Configuration/ConfigurationFactoryTest.php | 2 ++ Tests/Unit/Controller/AjaxValidationControllerTest.php | 10 +++++----- Tests/Unit/Form/FormObjectConfigurationTest.php | 2 ++ Tests/Unit/ViewHelpers/FieldViewHelperTest.php | 4 ++-- Tests/Unit/ViewHelpers/FormViewHelperTest.php | 6 +++--- 8 files changed, 22 insertions(+), 17 deletions(-) diff --git a/Tests/Fixture/Configuration/FormzConfiguration.php b/Tests/Fixture/Configuration/FormzConfiguration.php index bf6fecf..6cc7277 100644 --- a/Tests/Fixture/Configuration/FormzConfiguration.php +++ b/Tests/Fixture/Configuration/FormzConfiguration.php @@ -20,8 +20,9 @@ class FormzConfiguration */ public static function getDefaultConfiguration() { - $typoScriptConfiguration = file_get_contents(realpath(dirname(__FILE__)) . '/../../../Configuration/TypoScript/Configuration/Settings.ts'); - + $configurationPath = realpath(dirname(__FILE__)) . '/../../../Configuration/'; + $typoScriptConfiguration = file_get_contents($configurationPath . 'TypoScript/Configuration/Settings.ts'); + $typoScriptConfiguration .= file_get_contents($configurationPath . 'TypoScript/View/ViewConfiguration.ts'); $typoScriptParser = new TypoScriptParser; $typoScriptParser->parse($typoScriptConfiguration); diff --git a/Tests/Unit/Condition/ConditionFactoryTest.php b/Tests/Unit/Condition/ConditionFactoryTest.php index 0de430e..32e0b33 100644 --- a/Tests/Unit/Condition/ConditionFactoryTest.php +++ b/Tests/Unit/Condition/ConditionFactoryTest.php @@ -16,7 +16,7 @@ class ConditionFactoryTest extends AbstractUnitTest */ public function registerConditionMustBeAString() { - $this->setExpectedException(InvalidArgumentTypeException::class, '', 1466588489); + $this->setExpectedException(InvalidArgumentTypeException::class, null, 1466588489); $conditionFactory = new ConditionFactory; $conditionFactory->registerCondition(true, 'foo'); @@ -27,7 +27,7 @@ public function registerConditionMustBeAString() */ public function registerConditionWithClassNameNotFoundThrowsException() { - $this->setExpectedException(ClassNotFoundException::class, '', 1489602455); + $this->setExpectedException(ClassNotFoundException::class, null, 1489602455); $conditionFactory = new ConditionFactory; $conditionFactory->registerCondition('foo', 'nope!'); @@ -38,7 +38,7 @@ public function registerConditionWithClassNameNotFoundThrowsException() */ public function registerConditionWithInvalidClassNameThrowsException() { - $this->setExpectedException(InvalidArgumentTypeException::class, '', 1466588495); + $this->setExpectedException(InvalidArgumentTypeException::class, null, 1466588495); $conditionFactory = new ConditionFactory; $conditionFactory->registerCondition('foo', \stdClass::class); diff --git a/Tests/Unit/Condition/Items/AbstractConditionItemTest.php b/Tests/Unit/Condition/Items/AbstractConditionItemTest.php index be7521c..b6b45a4 100644 --- a/Tests/Unit/Condition/Items/AbstractConditionItemTest.php +++ b/Tests/Unit/Condition/Items/AbstractConditionItemTest.php @@ -18,7 +18,7 @@ class AbstractConditionItemTest extends AbstractUnitTest */ public function validatingFieldConditionConfigurationThrowsException() { - $this->setExpectedException(InvalidConditionException::class, '', 1488653398); + $this->setExpectedException(InvalidConditionException::class, null, 1488653398); /** @var AbstractConditionItem|\PHPUnit_Framework_MockObject_MockObject $conditionItem */ $conditionItem = $this->getMockBuilder(AbstractConditionItem::class) @@ -53,7 +53,7 @@ public function validatingFieldConditionConfigurationThrowsException() */ public function validatingValidationConditionConfigurationThrowsException() { - $this->setExpectedException(InvalidConditionException::class, '', 1488653713); + $this->setExpectedException(InvalidConditionException::class, null, 1488653713); /** @var AbstractConditionItem|\PHPUnit_Framework_MockObject_MockObject $conditionItem */ $conditionItem = $this->getMockBuilder(AbstractConditionItem::class) diff --git a/Tests/Unit/Configuration/ConfigurationFactoryTest.php b/Tests/Unit/Configuration/ConfigurationFactoryTest.php index 4d17aa2..c2aa10f 100644 --- a/Tests/Unit/Configuration/ConfigurationFactoryTest.php +++ b/Tests/Unit/Configuration/ConfigurationFactoryTest.php @@ -126,6 +126,8 @@ public function builtFormzConfigurationIsStoredInSystemCacheWhenItDoesNotHaveErr $configurationInstance = new Configuration; $configurationResult = new Result; + $configurationInstance->getView()->setPartialRootPath('foo', 'bar'); + $configurationInstance->getView()->setLayoutRootPath('foo', 'bar'); $configurationObjectInstance = new ConfigurationObjectInstance($configurationInstance, $configurationResult); $configurationObjectInstance->refreshValidationResult(); diff --git a/Tests/Unit/Controller/AjaxValidationControllerTest.php b/Tests/Unit/Controller/AjaxValidationControllerTest.php index 5498670..ece32f9 100644 --- a/Tests/Unit/Controller/AjaxValidationControllerTest.php +++ b/Tests/Unit/Controller/AjaxValidationControllerTest.php @@ -145,7 +145,7 @@ public function exceptionCatchInProtectedRequestWithDebugModeShouldCustomizeResu public function requestArgumentMissingThrowsException(RequestInterface $request, $exceptionType = null, $exceptionCode = null) { if ($exceptionType) { - $this->setExpectedException($exceptionType, '', $exceptionCode); + $this->setExpectedException($exceptionType, null, $exceptionCode); } /** @var AjaxValidationController|\PHPUnit_Framework_MockObject_MockObject $controller */ @@ -241,7 +241,7 @@ public function requestArgumentMissingThrowsExceptionDataProvider() */ public function incorrectFormConfigurationThrowsException() { - $this->setExpectedException(InvalidConfigurationException::class, '', 1487671395); + $this->setExpectedException(InvalidConfigurationException::class, null, 1487671395); $formObject = $this->getDefaultFormObject(); $formObject->getConfigurationValidationResult()->addError(new Error('foo', 42)); @@ -260,7 +260,7 @@ public function incorrectFormConfigurationThrowsException() */ public function validatingUnknownFieldThrowsException() { - $this->setExpectedException(EntryNotFoundException::class, '', 1487671603); + $this->setExpectedException(EntryNotFoundException::class, null, 1487671603); $ajaxValidationController = $this->getAjaxValidationControllerMock(); @@ -276,7 +276,7 @@ public function validatingUnknownFieldThrowsException() */ public function validatingFieldWithUnknownValidationThrowsException() { - $this->setExpectedException(EntryNotFoundException::class, '', 1487672956); + $this->setExpectedException(EntryNotFoundException::class, null, 1487672956); $ajaxValidationController = $this->getAjaxValidationControllerMock(); $ajaxValidationController->setProtectedRequestMode(false); @@ -291,7 +291,7 @@ public function validatingFieldWithUnknownValidationThrowsException() */ public function validationFieldValidationWithAjaxDeactivatedThrowsException() { - $this->setExpectedException(InvalidConfigurationException::class, '', 1487673434); + $this->setExpectedException(InvalidConfigurationException::class, null, 1487673434); $formObject = $this->getDefaultFormObject(); $validation = new Validation; diff --git a/Tests/Unit/Form/FormObjectConfigurationTest.php b/Tests/Unit/Form/FormObjectConfigurationTest.php index c2f82e7..be73236 100644 --- a/Tests/Unit/Form/FormObjectConfigurationTest.php +++ b/Tests/Unit/Form/FormObjectConfigurationTest.php @@ -199,6 +199,8 @@ public function validationResultIsRebuiltWhenFormObjectHashChanges() protected function getConfigurationObjectInstance() { $formzConfiguration = new Configuration; + $formzConfiguration->getView()->setPartialRootPath('foo', 'bar'); + $formzConfiguration->getView()->setLayoutRootPath('foo', 'bar'); $result = new Result; return new ConfigurationObjectInstance($formzConfiguration, $result); diff --git a/Tests/Unit/ViewHelpers/FieldViewHelperTest.php b/Tests/Unit/ViewHelpers/FieldViewHelperTest.php index a62f96a..c370aeb 100644 --- a/Tests/Unit/ViewHelpers/FieldViewHelperTest.php +++ b/Tests/Unit/ViewHelpers/FieldViewHelperTest.php @@ -229,7 +229,7 @@ public function renderFieldWithEmptyLayoutNameThrowsException() */ public function renderFieldWithNotExistingLayoutNameThrowsException() { - $this->setExpectedException(EntryNotFoundException::class, '', 1465243586); + $this->setExpectedException(EntryNotFoundException::class, null, 1465243586); $this->addFooLayoutToTypoScriptConfiguration(); /** @var FormObjectFactory $formObjectFactory */ @@ -262,7 +262,7 @@ public function renderFieldWithNotExistingLayoutNameThrowsException() */ public function renderFieldWithNotExistingLayoutItemNameThrowsException() { - $this->setExpectedException(EntryNotFoundException::class, '', 1485867803); + $this->setExpectedException(EntryNotFoundException::class, null, 1485867803); $this->addFooLayoutToTypoScriptConfiguration(); /** @var FormObjectFactory $formObjectFactory */ diff --git a/Tests/Unit/ViewHelpers/FormViewHelperTest.php b/Tests/Unit/ViewHelpers/FormViewHelperTest.php index b5338ec..9af883e 100644 --- a/Tests/Unit/ViewHelpers/FormViewHelperTest.php +++ b/Tests/Unit/ViewHelpers/FormViewHelperTest.php @@ -75,7 +75,7 @@ public function typoScriptNotIncludedInDebugModeWillReturnMessage() */ public function formArgumentIsNotAnObjectThrowsException() { - $this->setExpectedException(InvalidOptionValueException::class, '', 1490713939); + $this->setExpectedException(InvalidOptionValueException::class, null, 1490713939); $viewHelper = $this->getFormViewHelperMock(['getFormClassName']); @@ -92,7 +92,7 @@ public function formArgumentIsNotAnObjectThrowsException() */ public function formArgumentIsNotFormThrowsException() { - $this->setExpectedException(InvalidOptionValueException::class, '', 1490714346); + $this->setExpectedException(InvalidOptionValueException::class, null, 1490714346); $viewHelper = $this->getFormViewHelperMock(['getFormClassName']); @@ -110,7 +110,7 @@ public function formArgumentIsNotFormThrowsException() */ public function formArgumentIsNotExpectedClassThrowsException() { - $this->setExpectedException(InvalidOptionValueException::class, '', 1490714534); + $this->setExpectedException(InvalidOptionValueException::class, null, 1490714534); $viewHelper = $this->getFormViewHelperMock(['getFormClassName']); From 193ba2fd453a0e841fc2cc33d3320895a296caa5 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Mon, 23 Oct 2017 17:12:37 +0200 Subject: [PATCH 06/27] [BUGFIX] Use more accurate identifier for assets hash A collision could occur on the name of the asset files for two forms with the same definition but with different names. --- .../AssetHandlerConnectorManager.php | 2 +- .../AssetHandlerConnectorManagerTest.php | 33 ++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/Classes/AssetHandler/Connector/AssetHandlerConnectorManager.php b/Classes/AssetHandler/Connector/AssetHandlerConnectorManager.php index 028ce5f..a2f1998 100644 --- a/Classes/AssetHandler/Connector/AssetHandlerConnectorManager.php +++ b/Classes/AssetHandler/Connector/AssetHandlerConnectorManager.php @@ -122,7 +122,7 @@ public function getFormzGeneratedFilePath($prefix = '') 0, 22 ); - $identifier .= '-' . md5($formObject->getHash()); + $identifier .= '-' . md5($formObject->getHash() . $formObject->getName()); return CacheService::GENERATED_FILES_PATH . $identifier; } diff --git a/Tests/Unit/AssetHandler/Connector/AssetHandlerConnectorManagerTest.php b/Tests/Unit/AssetHandler/Connector/AssetHandlerConnectorManagerTest.php index d402f9a..3a4deb7 100644 --- a/Tests/Unit/AssetHandler/Connector/AssetHandlerConnectorManagerTest.php +++ b/Tests/Unit/AssetHandler/Connector/AssetHandlerConnectorManagerTest.php @@ -6,6 +6,8 @@ use Romm\Formz\AssetHandler\Connector\AssetHandlerConnectorStates; use Romm\Formz\AssetHandler\Connector\CssAssetHandlerConnector; use Romm\Formz\AssetHandler\Connector\JavaScriptAssetHandlerConnector; +use Romm\Formz\Form\FormObject; +use Romm\Formz\Tests\Fixture\Form\DefaultForm; use Romm\Formz\Tests\Unit\AbstractUnitTest; use TYPO3\CMS\Core\Page\PageRenderer; use TYPO3\CMS\Extbase\Mvc\Controller\ControllerContext; @@ -101,7 +103,7 @@ public function includingDefaultAssetsIncludesThemOnce() * * @test */ - public function GeneratedFilePathDependsOnPrefix() + public function generatedFilePathDependsOnPrefix() { $formObject = $this->getDefaultFormObject(); $controllerContext = new ControllerContext; @@ -120,4 +122,33 @@ public function GeneratedFilePathDependsOnPrefix() $this->assertEquals($path3, $path4); $this->assertNotEquals($path1, $path3); } + + /** + * The assets file names must be different for two forms with the same + * definition but different names. + * + * @test + */ + public function sameFormsWithDifferentNamesUseDifferentGeneratedFileNames() + { + $pageRenderer = new PageRenderer; + + $formObject = new FormObject(DefaultForm::class, 'foo', []); + $controllerContext = new ControllerContext; + + $assetHandlerFactory = AssetHandlerFactory::get($formObject, $controllerContext); + + $assetHandlerConnectorManager1 = new AssetHandlerConnectorManager($pageRenderer, $assetHandlerFactory); + $path1 = $assetHandlerConnectorManager1->getFormzGeneratedFilePath('foo'); + + $formObject = new FormObject(DefaultForm::class, 'bar', []); + $controllerContext = new ControllerContext; + + $assetHandlerFactory = AssetHandlerFactory::get($formObject, $controllerContext); + + $assetHandlerConnectorManager2 = new AssetHandlerConnectorManager($pageRenderer, $assetHandlerFactory); + $path2 = $assetHandlerConnectorManager2->getFormzGeneratedFilePath('foo'); + + $this->assertNotEquals($path1, $path2); + } } From 0c91fd79a4e26f71bbe8c88e3f5a04b0dac776d3 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Tue, 17 Oct 2017 11:09:27 +0200 Subject: [PATCH 07/27] [BUGFIX] Handle multiple occurrences of messages markers in JavaScript The message template of a field can be filled with the same marker several times: every occurrence should be replaced. Example: ``` messageTemplate = #FIELD# ``` --- Resources/Public/JavaScript/Field/Formz.Field.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Resources/Public/JavaScript/Field/Formz.Field.js b/Resources/Public/JavaScript/Field/Formz.Field.js index 6fc86d4..f9c805b 100644 --- a/Resources/Public/JavaScript/Field/Formz.Field.js +++ b/Resources/Public/JavaScript/Field/Formz.Field.js @@ -97,12 +97,12 @@ Fz.Field = (function () { for (var name in messages[validationRuleName]) { if (messages[validationRuleName].hasOwnProperty(name)) { messageListContainerElement.innerHTML += messageTemplate - .replace('#FIELD#', this.getName()) - .replace('#FIELD_ID#', Fz.camelCaseToDashed('fz-' + this.getForm().getName() + '-' + this.getName())) - .replace('#VALIDATOR#', Fz.camelCaseToDashed(validationRuleName)) - .replace('#TYPE#', type) - .replace('#KEY#', name) - .replace('#MESSAGE#', messages[validationRuleName][name]); + .split('#FIELD#').join(this.getName()) + .split('#FIELD_ID#').join(Fz.camelCaseToDashed('fz-' + this.getForm().getName() + '-' + this.getName())) + .split('#VALIDATOR#').join(Fz.camelCaseToDashed(validationRuleName)) + .split('#TYPE#').join(type) + .split('#KEY#').join(name) + .split('#MESSAGE#').join(messages[validationRuleName][name]); } } } From a3ee50a3469b7fb9d82b7445e0886b866e632994 Mon Sep 17 00:00:00 2001 From: AzOoDev Date: Tue, 24 Oct 2017 19:55:35 +0200 Subject: [PATCH 08/27] [BUGFIX] Prevent getting unformatted input (#70) On an ajax call, FormZ will get all the input in the form tag and will create the query. But if there is an input unformatted (without `[]`) FormZ will send a bad data. --- Resources/Public/JavaScript/Validators/Formz.Validator.Ajax.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Public/JavaScript/Validators/Formz.Validator.Ajax.js b/Resources/Public/JavaScript/Validators/Formz.Validator.Ajax.js index e896088..51d7010 100644 --- a/Resources/Public/JavaScript/Validators/Formz.Validator.Ajax.js +++ b/Resources/Public/JavaScript/Validators/Formz.Validator.Ajax.js @@ -121,7 +121,7 @@ && null === key.match(/\[__referrer\]/) ) { var value = getElementValue(form.elements[i]); - if (value) { + if (value && key.indexOf("[") !== -1) { if ('' !== query) { query += '&'; } From e5b4370e320e1936bfeb498daa65b21b31021c69 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Wed, 25 Oct 2017 11:27:51 +0200 Subject: [PATCH 09/27] [BUGFIX] Use a different view form the field layout rendering It allows for instance nesting several fields located in multiple partial files. --- .../Field/FieldViewHelperService.php | 21 +++++++++++----- Classes/ViewHelpers/FieldViewHelper.php | 7 ------ .../Field/FieldViewHelperServiceTest.php | 25 ++++++++++++------- 3 files changed, 31 insertions(+), 22 deletions(-) diff --git a/Classes/Service/ViewHelper/Field/FieldViewHelperService.php b/Classes/Service/ViewHelper/Field/FieldViewHelperService.php index 5543760..b40420c 100644 --- a/Classes/Service/ViewHelper/Field/FieldViewHelperService.php +++ b/Classes/Service/ViewHelper/Field/FieldViewHelperService.php @@ -108,14 +108,23 @@ public function getView(Layout $layout) $identifier = $layout->getTemplateFile(); if (null === $this->view[$identifier]) { - /** @var StandaloneView $view */ - $view = Core::instantiate(StandaloneView::class); - $view->setTemplatePathAndFilename($layout->getTemplateFile()); - - $this->view[$identifier] = $view; + $this->view[$identifier] = $this->getViewInstance($layout); } - return $this->view[$identifier]; + return clone $this->view[$identifier]; + } + + /** + * @param Layout $layout + * @return StandaloneView + */ + protected function getViewInstance(Layout $layout) + { + /** @var StandaloneView $view */ + $view = Core::instantiate(StandaloneView::class); + $view->setTemplatePathAndFilename($layout->getTemplateFile()); + + return $view; } /** diff --git a/Classes/ViewHelpers/FieldViewHelper.php b/Classes/ViewHelpers/FieldViewHelper.php index 57c1373..fd3f6bc 100644 --- a/Classes/ViewHelpers/FieldViewHelper.php +++ b/Classes/ViewHelpers/FieldViewHelper.php @@ -165,13 +165,6 @@ protected function renderLayoutView(array $templateArguments) } else { $renderingContext = $view->getRenderingContext(); - // Removing all variables previously added to the provider. - $provider = $renderingContext->getVariableProvider(); - - foreach ($provider->getAllIdentifiers() as $key) { - $provider->remove($key); - } - /* * Updating the view dependencies: the variable container as well as * the controller context must be injected in the view. diff --git a/Tests/Unit/Service/ViewHelper/Field/FieldViewHelperServiceTest.php b/Tests/Unit/Service/ViewHelper/Field/FieldViewHelperServiceTest.php index b8ff9b6..75d0235 100644 --- a/Tests/Unit/Service/ViewHelper/Field/FieldViewHelperServiceTest.php +++ b/Tests/Unit/Service/ViewHelper/Field/FieldViewHelperServiceTest.php @@ -5,6 +5,7 @@ use Romm\Formz\Configuration\View\Layouts\Layout; use Romm\Formz\Service\ViewHelper\Field\FieldViewHelperService; use Romm\Formz\Tests\Unit\AbstractUnitTest; +use TYPO3\CMS\Fluid\View\StandaloneView; class FieldViewHelperServiceTest extends AbstractUnitTest { @@ -61,7 +62,10 @@ public function nestingFieldsWorks() */ public function viewIsInstantiatedOncePerLayout() { - $fieldService = new FieldViewHelperService; + /** @var FieldViewHelperService|\PHPUnit_Framework_MockObject_MockObject $fieldService */ + $fieldService = $this->getMockBuilder(FieldViewHelperService::class) + ->setMethods(['getViewInstance']) + ->getMock(); $layout1 = new Layout; $layout1->setTemplateFile('foo/bar'); @@ -69,13 +73,16 @@ public function viewIsInstantiatedOncePerLayout() $layout2 = new Layout; $layout2->setTemplateFile('bar/baz'); - $view1 = $fieldService->getView($layout1); - $view2 = $fieldService->getView($layout1); - - $this->assertSame($view1, $view2); - - $view3 = $fieldService->getView($layout2); - - $this->assertNotSame($view1, $view3); + $fieldService->expects($this->exactly(2)) + ->method('getViewInstance') + ->withConsecutive($layout1, $layout2) + ->willReturnCallback(function () { + return $this->prophesize(StandaloneView::class)->reveal(); + }); + + $fieldService->getView($layout1); + $fieldService->getView($layout1); + $fieldService->getView($layout2); + $fieldService->getView($layout2); } } From 3834e324b1d40122ee2fa64f8c92ed43d54bd017 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Wed, 25 Oct 2017 14:46:56 +0200 Subject: [PATCH 10/27] [BUGFIX] Add current template variables to slot Allows the inner part of a slot to have access to the current view variables. --- Classes/ViewHelpers/Slot/RenderViewHelper.php | 10 ++++++- .../ViewHelpers/Slot/RenderViewHelperTest.php | 26 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Classes/ViewHelpers/Slot/RenderViewHelper.php b/Classes/ViewHelpers/Slot/RenderViewHelper.php index a11c610..a7b69ef 100644 --- a/Classes/ViewHelpers/Slot/RenderViewHelper.php +++ b/Classes/ViewHelpers/Slot/RenderViewHelper.php @@ -19,6 +19,8 @@ use Romm\Formz\Service\ViewHelper\Field\FieldViewHelperService; use Romm\Formz\Service\ViewHelper\Slot\SlotViewHelperService; use Romm\Formz\ViewHelpers\AbstractViewHelper; +use TYPO3\CMS\Core\Utility\ArrayUtility; +use TYPO3\CMS\Core\Utility\VersionNumberUtility; use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextInterface; use TYPO3\CMS\Fluid\Core\ViewHelper\Facets\CompilableInterface; @@ -71,7 +73,13 @@ public static function renderStatic(array $arguments, Closure $renderChildrenClo $result = ''; if ($slotService->hasSlot($slotName)) { - $slotService->addTemplateVariables($slotName, $arguments['arguments']); + $currentVariables = version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<') + ? $renderingContext->getTemplateVariableContainer()->getAll() + : $renderingContext->getVariableProvider()->getAll(); + + ArrayUtility::mergeRecursiveWithOverrule($currentVariables, $arguments['arguments']); + + $slotService->addTemplateVariables($slotName, $currentVariables); $slotClosure = $slotService->getSlotClosure($slotName); $result = $slotClosure(); diff --git a/Tests/Unit/ViewHelpers/Slot/RenderViewHelperTest.php b/Tests/Unit/ViewHelpers/Slot/RenderViewHelperTest.php index e8faec8..cf0b70e 100644 --- a/Tests/Unit/ViewHelpers/Slot/RenderViewHelperTest.php +++ b/Tests/Unit/ViewHelpers/Slot/RenderViewHelperTest.php @@ -8,7 +8,9 @@ use Romm\Formz\Tests\Unit\UnitTestContainer; use Romm\Formz\Tests\Unit\ViewHelpers\AbstractViewHelperUnitTest; use Romm\Formz\ViewHelpers\Slot\RenderViewHelper; +use TYPO3\CMS\Core\Utility\VersionNumberUtility; use TYPO3\CMS\Fluid\Core\Rendering\RenderingContext; +use TYPO3\CMS\Fluid\Core\ViewHelper\TemplateVariableContainer; class RenderViewHelperTest extends AbstractViewHelperUnitTest { @@ -54,7 +56,10 @@ public function renderViewHelper() UnitTestContainer::get()->registerMockedInstance(SlotViewHelperService::class, $slotService); $viewHelper = new RenderViewHelper; + $this->injectDependenciesIntoViewHelper($viewHelper); + $this->injectVariableProviderMock(); + $viewHelper->setArguments([ 'slot' => $slotName, 'arguments' => [] @@ -93,7 +98,10 @@ public function argumentsAreAddedThenRemoved() UnitTestContainer::get()->registerMockedInstance(SlotViewHelperService::class, $slotService); $viewHelper = new RenderViewHelper; + $this->injectDependenciesIntoViewHelper($viewHelper); + $this->injectVariableProviderMock(); + $viewHelper->setArguments([ 'slot' => 'foo', 'arguments' => [] @@ -113,9 +121,27 @@ public function renderViewHelperWithoutFieldThrowsException() $this->setExpectedException(ContextNotFoundException::class); $viewHelper = new RenderViewHelper; + $this->injectDependenciesIntoViewHelper($viewHelper); + $this->injectVariableProviderMock(); + $viewHelper->initializeArguments(); $viewHelper->render(); } + + protected function injectVariableProviderMock() + { + $templateVariableContainer = $this->getMockBuilder(TemplateVariableContainer::class) + ->setMethods(['getAll']) + ->getMock(); + $templateVariableContainer->method('getAll') + ->willReturn(['foo' => 'bar']); + + if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { + $this->renderingContext->injectTemplateVariableContainer($templateVariableContainer); + } else { + $this->renderingContext->setVariableProvider($templateVariableContainer); + } + } } From a0a4d2c70ad72f4875568b9dc10a59a3029fd309 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Wed, 25 Oct 2017 14:47:22 +0200 Subject: [PATCH 11/27] [BUGFIX] Add current layout/partial root paths to field template Fixes an issue in TYPO3 v8 where rendering a partial or using a layout was impossible due to the inheritance of layout/partial root paths not properly set. --- Classes/ViewHelpers/FieldViewHelper.php | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Classes/ViewHelpers/FieldViewHelper.php b/Classes/ViewHelpers/FieldViewHelper.php index fd3f6bc..d486ce6 100644 --- a/Classes/ViewHelpers/FieldViewHelper.php +++ b/Classes/ViewHelpers/FieldViewHelper.php @@ -336,7 +336,11 @@ protected function getPaths($type) : $viewConfiguration->getAbsoluteLayoutRootPaths(); if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '>=')) { - return $paths; + $templatePaths = $this->renderingContext->getTemplatePaths(); + + $currentPaths = $type === 'partial' + ? $templatePaths->getPartialRootPaths() + : $templatePaths->getLayoutRootPaths(); } else { $currentView = $this->renderingContext->getViewHelperVariableContainer()->getView(); $propertyName = $type === 'partial' @@ -347,10 +351,10 @@ protected function getPaths($type) $method = $reflectionClass->getMethod($propertyName); $method->setAccessible(true); - $value = $method->invoke($currentView); - - return array_unique(array_merge($paths, $value)); + $currentPaths = $method->invoke($currentView); } + + return array_unique(array_merge($paths, $currentPaths)); } /** From 3b5ac06eb63653ddc9a2270c6aff001200c5bfd2 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Wed, 25 Oct 2017 15:10:52 +0200 Subject: [PATCH 12/27] [BUGFIX] Handle nested fields views behaviour with Fluid standalone Couple of fixes that will fix certain issues when working with nested fields in TYPO3 v8. --- Classes/ViewHelpers/FieldViewHelper.php | 42 ++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/Classes/ViewHelpers/FieldViewHelper.php b/Classes/ViewHelpers/FieldViewHelper.php index d486ce6..4a09dcc 100644 --- a/Classes/ViewHelpers/FieldViewHelper.php +++ b/Classes/ViewHelpers/FieldViewHelper.php @@ -151,6 +151,9 @@ protected function renderLayoutView(array $templateArguments) $templateArguments['fieldName'] = $fieldName; $templateArguments['fieldId'] = ($templateArguments['fieldId']) ?: StringService::get()->sanitizeString('formz-' . $formObject->getName() . '-' . $fieldName); + $currentView = $this->viewHelperVariableContainer->getView(); + $currentVariables = []; + $view = $this->fieldService->getView($layout); /* @@ -163,24 +166,55 @@ protected function renderLayoutView(array $templateArguments) if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '<')) { $view->setRenderingContext($this->renderingContext); } else { - $renderingContext = $view->getRenderingContext(); + $currentVariables = $this->renderingContext->getVariableProvider()->getAll(); /* * Updating the view dependencies: the variable container as well as * the controller context must be injected in the view. */ - $renderingContext->setViewHelperVariableContainer($this->viewHelperVariableContainer); + $this->viewHelperVariableContainer->setView($view); + + $view->getRenderingContext()->setViewHelperVariableContainer($this->viewHelperVariableContainer); $view->setControllerContext($this->controllerContext); - $this->viewHelperVariableContainer->setView($view); + /* + * Adding current variables to the field view variables. + */ + $tmpVariables = $currentVariables; + ArrayUtility::mergeRecursiveWithOverrule($tmpVariables, $templateArguments); + $templateArguments = $tmpVariables; } $view->setLayoutRootPaths($layoutPaths); $view->setPartialRootPaths($partialPaths); $view->assignMultiple($templateArguments); - return $view->render(); + $result = $view->render(); + + if (version_compare(VersionNumberUtility::getCurrentTypo3Version(), '8.0.0', '>=')) { + /* + * Because the view can be used several times in nested fields, we + * need to restore the variables after the view was rendered. + */ + $viewVariableProvider = $view->getRenderingContext()->getVariableProvider(); + + foreach ($viewVariableProvider->getAllIdentifiers() as $identifier) { + $viewVariableProvider->remove($identifier); + } + + foreach ($currentVariables as $key => $value) { + $viewVariableProvider->add($key, $value); + } + + /* + * Resetting the view of the variable container with the original + * view. + */ + $this->viewHelperVariableContainer->setView($currentView); + } + + return $result; } /** From 3cf3f5fc2c1e706301ea0a02b36ef765abeda29e Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Wed, 25 Oct 2017 16:16:33 +0200 Subject: [PATCH 13/27] [BUGFIX] Call view helpers parent arguments initialization --- Classes/ViewHelpers/ClassViewHelper.php | 2 ++ Classes/ViewHelpers/FieldViewHelper.php | 2 ++ Classes/ViewHelpers/FormatMessageViewHelper.php | 2 ++ Classes/ViewHelpers/OptionViewHelper.php | 2 ++ Classes/ViewHelpers/Slot/HasViewHelper.php | 2 ++ Classes/ViewHelpers/Slot/RenderViewHelper.php | 2 ++ Classes/ViewHelpers/SlotViewHelper.php | 2 ++ 7 files changed, 14 insertions(+) diff --git a/Classes/ViewHelpers/ClassViewHelper.php b/Classes/ViewHelpers/ClassViewHelper.php index ef4ea62..a775702 100644 --- a/Classes/ViewHelpers/ClassViewHelper.php +++ b/Classes/ViewHelpers/ClassViewHelper.php @@ -89,6 +89,8 @@ class ClassViewHelper extends AbstractViewHelper */ public function initializeArguments() { + parent::initializeArguments(); + $this->registerArgument('name', 'string', 'Name of the class which will be managed.', true); $this->registerArgument('field', 'string', 'Name of the field which will be managed. By default, it is the field from the current `FieldViewHelper`.'); } diff --git a/Classes/ViewHelpers/FieldViewHelper.php b/Classes/ViewHelpers/FieldViewHelper.php index 4a09dcc..d47385a 100644 --- a/Classes/ViewHelpers/FieldViewHelper.php +++ b/Classes/ViewHelpers/FieldViewHelper.php @@ -73,6 +73,8 @@ class FieldViewHelper extends AbstractViewHelper */ public function initializeArguments() { + parent::initializeArguments(); + $this->registerArgument('name', 'string', 'Name of the field which should be rendered.', true); $this->registerArgument('layout', 'string', 'Path of the TypoScript layout which will be used.', true); $this->registerArgument('arguments', 'array', 'Arbitrary arguments which will be sent to the field template.', false, []); diff --git a/Classes/ViewHelpers/FormatMessageViewHelper.php b/Classes/ViewHelpers/FormatMessageViewHelper.php index a48baca..30d7aa1 100644 --- a/Classes/ViewHelpers/FormatMessageViewHelper.php +++ b/Classes/ViewHelpers/FormatMessageViewHelper.php @@ -60,6 +60,8 @@ class FormatMessageViewHelper extends AbstractViewHelper */ public function initializeArguments() { + parent::initializeArguments(); + $this->registerArgument('message', 'object', 'The message which will be formatted.', true); $this->registerArgument('field', 'string', 'Name of the field which will be managed. By default, it is the field from the current `FieldViewHelper`.'); } diff --git a/Classes/ViewHelpers/OptionViewHelper.php b/Classes/ViewHelpers/OptionViewHelper.php index dec4c4d..6f0be5a 100644 --- a/Classes/ViewHelpers/OptionViewHelper.php +++ b/Classes/ViewHelpers/OptionViewHelper.php @@ -60,6 +60,8 @@ class OptionViewHelper extends AbstractViewHelper implements CompilableInterface */ public function initializeArguments() { + parent::initializeArguments(); + $this->registerArgument('name', 'string', 'Name of the option.', true); $this->registerArgument('value', 'string', 'Value of the option.', true); } diff --git a/Classes/ViewHelpers/Slot/HasViewHelper.php b/Classes/ViewHelpers/Slot/HasViewHelper.php index 0569bd7..8f67f75 100644 --- a/Classes/ViewHelpers/Slot/HasViewHelper.php +++ b/Classes/ViewHelpers/Slot/HasViewHelper.php @@ -32,6 +32,8 @@ class HasViewHelper extends AbstractConditionViewHelper implements CompilableInt */ public function initializeArguments() { + parent::initializeArguments(); + $this->registerArgument('slot', 'string', 'Name of the slot.', true); } diff --git a/Classes/ViewHelpers/Slot/RenderViewHelper.php b/Classes/ViewHelpers/Slot/RenderViewHelper.php index a7b69ef..1a57fe8 100644 --- a/Classes/ViewHelpers/Slot/RenderViewHelper.php +++ b/Classes/ViewHelpers/Slot/RenderViewHelper.php @@ -50,6 +50,8 @@ public function initializeArguments() */ public function render() { + parent::initializeArguments(); + return self::renderStatic($this->arguments, $this->buildRenderChildrenClosure(), $this->renderingContext); } diff --git a/Classes/ViewHelpers/SlotViewHelper.php b/Classes/ViewHelpers/SlotViewHelper.php index 1de0540..46678a3 100644 --- a/Classes/ViewHelpers/SlotViewHelper.php +++ b/Classes/ViewHelpers/SlotViewHelper.php @@ -35,6 +35,8 @@ class SlotViewHelper extends AbstractViewHelper implements CompilableInterface */ public function initializeArguments() { + parent::initializeArguments(); + $this->registerArgument('name', 'string', 'Name of the slot.', true); $this->registerArgument('arguments', 'array', 'Arguments sent to the slot.', false, []); } From 4626b02249dcb6a03e581c3c8d0cad73b6f7e4ed Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Thu, 9 Nov 2017 09:37:42 +0100 Subject: [PATCH 14/27] [TASK] Use `self` statement in method parameter After StyleCI inspection failed. --- Classes/Condition/Parser/Node/NodeInterface.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/Condition/Parser/Node/NodeInterface.php b/Classes/Condition/Parser/Node/NodeInterface.php index bfb830d..f06666c 100644 --- a/Classes/Condition/Parser/Node/NodeInterface.php +++ b/Classes/Condition/Parser/Node/NodeInterface.php @@ -27,9 +27,9 @@ interface NodeInterface public function getParent(); /** - * @param NodeInterface $parent + * @param self $parent */ - public function setParent(NodeInterface $parent); + public function setParent(self $parent); /** * @return ConditionTree From 79eddae9fea5f6ca63493f5fb9014fe27d4bb4d4 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Thu, 9 Nov 2017 09:37:53 +0100 Subject: [PATCH 15/27] [BUGFIX] Handle boolean value `false` in `RequiredValidator` --- Classes/Validation/Validator/RequiredValidator.php | 1 + 1 file changed, 1 insertion(+) diff --git a/Classes/Validation/Validator/RequiredValidator.php b/Classes/Validation/Validator/RequiredValidator.php index 9b9d4f8..b509454 100644 --- a/Classes/Validation/Validator/RequiredValidator.php +++ b/Classes/Validation/Validator/RequiredValidator.php @@ -45,6 +45,7 @@ class RequiredValidator extends AbstractValidator public function isValid($value) { if (null === $value + || false === $value || '' === $value || (is_array($value) && empty($value)) || (is_object($value) && $value instanceof \Countable && $value->count() === 0) From ea395e8a62e0114e4848a5e0ac09f838877de589 Mon Sep 17 00:00:00 2001 From: Haythem Labbassi Date: Fri, 10 Nov 2017 15:26:56 +0100 Subject: [PATCH 16/27] [TASK] Add CSS condition for `FieldIsEmptyCondition` (#73) --- Classes/Condition/Items/FieldIsEmptyCondition.php | 5 ++++- Tests/Unit/Condition/Items/FieldIsEmptyConditionTest.php | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Classes/Condition/Items/FieldIsEmptyCondition.php b/Classes/Condition/Items/FieldIsEmptyCondition.php index 4ed9179..113f9c1 100644 --- a/Classes/Condition/Items/FieldIsEmptyCondition.php +++ b/Classes/Condition/Items/FieldIsEmptyCondition.php @@ -45,7 +45,10 @@ class FieldIsEmptyCondition extends AbstractConditionItem */ public function getCssResult() { - return '[' . DataAttributesAssetHandler::getFieldDataValueKey($this->fieldName) . '=""]'; + return [ + '[' . DataAttributesAssetHandler::getFieldDataValueKey($this->fieldName) . '=""]', + ':not([' . DataAttributesAssetHandler::getFieldDataValueKey($this->fieldName) . '])' + ]; } /** diff --git a/Tests/Unit/Condition/Items/FieldIsEmptyConditionTest.php b/Tests/Unit/Condition/Items/FieldIsEmptyConditionTest.php index 3d626a0..da6c5b4 100644 --- a/Tests/Unit/Condition/Items/FieldIsEmptyConditionTest.php +++ b/Tests/Unit/Condition/Items/FieldIsEmptyConditionTest.php @@ -94,7 +94,7 @@ public function getCssResultEmpty() $conditionItem = new FieldIsEmptyCondition; $conditionItem->setFieldName('foo'); - $this->assertEquals('[fz-value-foo=""]', $conditionItem->getCssResult()); + $this->assertEquals(['[fz-value-foo=""]', ':not([fz-value-foo])'], $conditionItem->getCssResult()); } /** From 7474dafb7b90815505b7afb3e4e4495fc4498c98 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Thu, 11 Jan 2018 17:03:13 +0100 Subject: [PATCH 17/27] [DOC] Fix several typos Fixes #75 --- Classes/Configuration/ConfigurationFactory.php | 2 +- Classes/Validation/Validator/Form/AbstractFormValidator.php | 2 +- Documentation/01-Introduction/FieldsActivation.rst | 2 +- Documentation/01-Introduction/Index.rst | 2 +- Documentation/03-Tutorial/Index.rst | 2 +- Documentation/04-DeveloperManual/JavaScript/Field.rst | 2 +- Documentation/04-DeveloperManual/JavaScript/Form.rst | 2 +- Documentation/04-DeveloperManual/PHP/FormValidator.rst | 6 +++--- Documentation/04-DeveloperManual/PHP/Validator.rst | 2 +- .../ConfigurationActivation/FieldIsEmptyCondition.rst | 2 +- .../05-UsersManual/TypoScript/ConfigurationFields.rst | 2 +- .../05-UsersManual/TypoScript/ConfigurationValidators.rst | 4 ++-- .../06-IntegratorManual/ViewHelpers/SlotViewHelper.rst | 2 +- Documentation/11-Changelog/Legacy/v0.3.2/Notes.md | 2 +- Documentation/11-Changelog/Legacy/v1.0.0/Notes.md | 2 +- .../Connector/AssetHandlerConnectorManagerTest.php | 2 +- Tests/Unit/Validation/Validator/EmailValidatorTest.php | 2 +- 17 files changed, 20 insertions(+), 20 deletions(-) diff --git a/Classes/Configuration/ConfigurationFactory.php b/Classes/Configuration/ConfigurationFactory.php index 44ecc19..46076d9 100644 --- a/Classes/Configuration/ConfigurationFactory.php +++ b/Classes/Configuration/ConfigurationFactory.php @@ -69,7 +69,7 @@ public function getFormzConfiguration() /** * Will fetch the configuration from cache, and build it if not found. It - * wont be stored in cache if any error is found. This is done this way to + * won't be stored in cache if any error is found. This is done this way to * avoid the integrator to be forced to flush caches when errors are found. * * @param string $cacheIdentifier diff --git a/Classes/Validation/Validator/Form/AbstractFormValidator.php b/Classes/Validation/Validator/Form/AbstractFormValidator.php index 70d74d3..0dfba09 100644 --- a/Classes/Validation/Validator/Form/AbstractFormValidator.php +++ b/Classes/Validation/Validator/Form/AbstractFormValidator.php @@ -131,7 +131,7 @@ final public function validate($form) } /** - * Validates the form, but wont save form data in the form object. + * Validates the form, but won't save form data in the form object. * * @param FormInterface $form * @param bool $initialize diff --git a/Documentation/01-Introduction/FieldsActivation.rst b/Documentation/01-Introduction/FieldsActivation.rst index a7a8071..f97dec6 100644 --- a/Documentation/01-Introduction/FieldsActivation.rst +++ b/Documentation/01-Introduction/FieldsActivation.rst @@ -51,7 +51,7 @@ You can find the different existing conditions and their configuration at the ch The expression ^^^^^^^^^^^^^^ -The boolean expression allows to use several condition thanks to logical operators: the logical “and”, the logical “or”. It also allows to gather expressions thanks to parenthesis. +The boolean expression allows to use several conditions thanks to logical operators: the logical “and”, the logical “or”. It also allows to gather expressions thanks to parenthesis. **Example:** diff --git a/Documentation/01-Introduction/Index.rst b/Documentation/01-Introduction/Index.rst index 0105c28..f543e23 100644 --- a/Documentation/01-Introduction/Index.rst +++ b/Documentation/01-Introduction/Index.rst @@ -23,7 +23,7 @@ FormZ helps with the following topics: What is this for? ----------------- -The goal of FormZ it to accelerate forms development, from a simple contact form to a complex subscription form. The extension provides a set of tools: developers, integrators and administrators will have access to ready-to-run and simple features. +The goal of FormZ is to accelerate forms development, from a simple contact form to a complex subscription form. The extension provides a set of tools: developers, integrators and administrators will have access to ready-to-run and simple features. A form manipulation can be divided into three principal axes: its **composition**, its **validation on submission**, and its **data exploitation** when it is validated. The last part is specific to each form, while composition and validation will always have similarities between forms: identical fields with same validation rules, same display, etc. diff --git a/Documentation/03-Tutorial/Index.rst b/Documentation/03-Tutorial/Index.rst index 4401c01..d9b893e 100644 --- a/Documentation/03-Tutorial/Index.rst +++ b/Documentation/03-Tutorial/Index.rst @@ -91,7 +91,7 @@ TypoScript configuration The handling of the validation rules is done with TypoScript. -You must follow the explanations of the chapter “:ref:`usersManual`” to configure correctly you validation rules. +You must follow the explanations of the chapter “:ref:`usersManual`” to configure correctly your validation rules. **Configuration example:** diff --git a/Documentation/04-DeveloperManual/JavaScript/Field.rst b/Documentation/04-DeveloperManual/JavaScript/Field.rst index 7914272..233c663 100644 --- a/Documentation/04-DeveloperManual/JavaScript/Field.rst +++ b/Documentation/04-DeveloperManual/JavaScript/Field.rst @@ -11,7 +11,7 @@ Field ===== -You can find below the list of available function for a **field instance**: +You can find below the list of available functions for a **field instance**: =========================================================================================================================== ==================================================================== Function Description diff --git a/Documentation/04-DeveloperManual/JavaScript/Form.rst b/Documentation/04-DeveloperManual/JavaScript/Form.rst index d188cab..cd7ab2b 100644 --- a/Documentation/04-DeveloperManual/JavaScript/Form.rst +++ b/Documentation/04-DeveloperManual/JavaScript/Form.rst @@ -154,7 +154,7 @@ Bind a function on the form submission Parameters - ``callback``: function called when the form is submitted. If it returns false, the form submission is cancelled. Description - Binds a function on the form submission. Note that the function wont be called if the form submission is blocked (for instance because of an invalid field). + Binds a function on the form submission. Note that the function won't be called if the form submission is blocked (for instance because of an invalid field). The function can return ``false`` if the submission must be blocked for any reason. diff --git a/Documentation/04-DeveloperManual/PHP/FormValidator.rst b/Documentation/04-DeveloperManual/PHP/FormValidator.rst index e86aa2b..630f4f8 100644 --- a/Documentation/04-DeveloperManual/PHP/FormValidator.rst +++ b/Documentation/04-DeveloperManual/PHP/FormValidator.rst @@ -67,7 +67,7 @@ Pre-validation process .. container:: table-row - Fonction + Function .. code-block:: php protected function beforeValidationProcess() @@ -86,7 +86,7 @@ During-validation process .. container:: table-row - Fonction + Function .. code-block:: php protected function *field*Validated() @@ -107,7 +107,7 @@ Post-validation process .. container:: table-row - Fonction + Function .. code-block:: php protected function afterValidationProcess() diff --git a/Documentation/04-DeveloperManual/PHP/Validator.rst b/Documentation/04-DeveloperManual/PHP/Validator.rst index 23c2c29..5b3e0f5 100644 --- a/Documentation/04-DeveloperManual/PHP/Validator.rst +++ b/Documentation/04-DeveloperManual/PHP/Validator.rst @@ -98,7 +98,7 @@ Supported messages list ], 'test' => [ // If you fill "value", the value will be directly used and - // the process wont try to fetch a translation. + // the process won't try to fetch a translation. 'value' => 'Test message!' ] ]; diff --git a/Documentation/05-UsersManual/TypoScript/ConfigurationActivation/FieldIsEmptyCondition.rst b/Documentation/05-UsersManual/TypoScript/ConfigurationActivation/FieldIsEmptyCondition.rst index b94800d..c81ba70 100644 --- a/Documentation/05-UsersManual/TypoScript/ConfigurationActivation/FieldIsEmptyCondition.rst +++ b/Documentation/05-UsersManual/TypoScript/ConfigurationActivation/FieldIsEmptyCondition.rst @@ -5,7 +5,7 @@ « FieldIsEmpty» ================ -This condition is verified when a given field was not filled. Works witch multiple checkboxes. +This condition is verified when a given field was not filled. Works with multiple checkboxes. Properties ---------- diff --git a/Documentation/05-UsersManual/TypoScript/ConfigurationFields.rst b/Documentation/05-UsersManual/TypoScript/ConfigurationFields.rst index e35e885..b6e677f 100644 --- a/Documentation/05-UsersManual/TypoScript/ConfigurationFields.rst +++ b/Documentation/05-UsersManual/TypoScript/ConfigurationFields.rst @@ -249,7 +249,7 @@ Message list selector Required? No Description - Contains the CSS selector which will be used to fetch the block containing the field messages. It's a second selection layout for the message container (``settings.messageContainerSelector``): it allows adding static HTML contents which wont be cleaned up by JavaScript during the message refreshing. + Contains the CSS selector which will be used to fetch the block containing the field messages. It's a second selection layout for the message container (``settings.messageContainerSelector``): it allows adding static HTML contents which won't be cleaned up by JavaScript during the message refreshing. Note that the marker ``#FIELD#`` is dynamically replaced by the name of the field. diff --git a/Documentation/05-UsersManual/TypoScript/ConfigurationValidators.rst b/Documentation/05-UsersManual/TypoScript/ConfigurationValidators.rst index fbc07d4..985c277 100644 --- a/Documentation/05-UsersManual/TypoScript/ConfigurationValidators.rst +++ b/Documentation/05-UsersManual/TypoScript/ConfigurationValidators.rst @@ -114,7 +114,7 @@ Validator messages } test { # If you fill `value`, the value will be directly used - # and the system wont try to fetch a translation. + # and the system won't try to fetch a translation. value = Message test! } } @@ -168,7 +168,7 @@ Use Ajax validation Description If this property is defined, an Ajax request is sent by JavaScript when it needs to test this validator. - Note that if a JavaScript version of this validator exists (see “:ref:`developerManual-javaScript-validation-registerValidator`”), then filling this property wont have any effect and the JavaScript validator will be used instead of Ajax. + Note that if a JavaScript version of this validator exists (see “:ref:`developerManual-javaScript-validation-registerValidator`”), then filling this property won't have any effect and the JavaScript validator will be used instead of Ajax. .. code-block:: typoscript diff --git a/Documentation/06-IntegratorManual/ViewHelpers/SlotViewHelper.rst b/Documentation/06-IntegratorManual/ViewHelpers/SlotViewHelper.rst index fa791dd..b0775d1 100644 --- a/Documentation/06-IntegratorManual/ViewHelpers/SlotViewHelper.rst +++ b/Documentation/06-IntegratorManual/ViewHelpers/SlotViewHelper.rst @@ -21,7 +21,7 @@ Argument Description ======================= ================================================================================================================ \* ``name`` Name of the slot. - Note that if you use the name of a slot which is not used in the field layout, this slot wont be rendered. + Note that if you use the name of a slot which is not used in the field layout, this slot won't be rendered. ``arguments`` Array of arbitrary arguments that will be passed to the slot and can be used within it as Fluid variables. ======================= ================================================================================================================ diff --git a/Documentation/11-Changelog/Legacy/v0.3.2/Notes.md b/Documentation/11-Changelog/Legacy/v0.3.2/Notes.md index 0c6fcb4..50c8d11 100644 --- a/Documentation/11-Changelog/Legacy/v0.3.2/Notes.md +++ b/Documentation/11-Changelog/Legacy/v0.3.2/Notes.md @@ -3,7 +3,7 @@ This release introduces partial backend support for FormZ, meaning you can use FormZ in any backend module. -The last remaining known issue is ajax calls, which wont work for now. +The last remaining known issue is ajax calls, which won't work for now. ---- diff --git a/Documentation/11-Changelog/Legacy/v1.0.0/Notes.md b/Documentation/11-Changelog/Legacy/v1.0.0/Notes.md index 91aa1c6..2a14bce 100644 --- a/Documentation/11-Changelog/Legacy/v1.0.0/Notes.md +++ b/Documentation/11-Changelog/Legacy/v1.0.0/Notes.md @@ -11,7 +11,7 @@ After months of work on making the extension as reliable as possible (core refac This commit adds a more reliable handling of the warning and notice message types in validation rules. - These messages wont block a validation (an error will), but can be used to deliver more information to the final user about actions done during the validation. + These messages won't block a validation (an error will), but can be used to deliver more information to the final user about actions done during the validation. Ajax requests are supported. diff --git a/Tests/Unit/AssetHandler/Connector/AssetHandlerConnectorManagerTest.php b/Tests/Unit/AssetHandler/Connector/AssetHandlerConnectorManagerTest.php index 3a4deb7..863d9b1 100644 --- a/Tests/Unit/AssetHandler/Connector/AssetHandlerConnectorManagerTest.php +++ b/Tests/Unit/AssetHandler/Connector/AssetHandlerConnectorManagerTest.php @@ -17,7 +17,7 @@ class AssetHandlerConnectorManagerTest extends AbstractUnitTest /** * Checks that the manager getter method returns correct instances, and that - * there wont be duplicated instances for the same constructor arguments. + * there won't be duplicated instances for the same constructor arguments. * * @test */ diff --git a/Tests/Unit/Validation/Validator/EmailValidatorTest.php b/Tests/Unit/Validation/Validator/EmailValidatorTest.php index 5ba6398..044df34 100644 --- a/Tests/Unit/Validation/Validator/EmailValidatorTest.php +++ b/Tests/Unit/Validation/Validator/EmailValidatorTest.php @@ -30,7 +30,7 @@ public function validatorWorks($value, array $options = [], array $errors = []) public function validatorWorksDataProvider() { /* - * We wont test too much different email adresses because the email + * We won't test too much different email adresses because the email * validator relies and TYPO3 core function for email checking. * * We're just going to see if both invalid and valid email examples From e316fe7e73db4b973f746b8c12d70a0fce6a2475 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Tue, 16 Jan 2018 14:01:33 +0100 Subject: [PATCH 18/27] [TASK] Improve pattern for `word` validation rules Allow some more characters, mostly for German language. Fixes #76 --- .../TypoScript/Validators/ValidatorsConfiguration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Configuration/TypoScript/Validators/ValidatorsConfiguration.ts b/Configuration/TypoScript/Validators/ValidatorsConfiguration.ts index a5e390a..9ac6265 100644 --- a/Configuration/TypoScript/Validators/ValidatorsConfiguration.ts +++ b/Configuration/TypoScript/Validators/ValidatorsConfiguration.ts @@ -150,7 +150,7 @@ config.tx_formz { className = Romm\Formz\Validation\Validator\RegexValidator options { # Pattern used by the regex (you don't need separators). - pattern = ^[\w-\' àáâãäåçèéêëìíîïðòóôõöùúûüýÿ]*$ + pattern = ^[\w-\' àáâãäåçèéêëìíîïðòóôõöùúûüýÿßÄÖÜ]*$ # Options for the regex, e.g. "i" for insensitive case. options = i } @@ -164,7 +164,7 @@ config.tx_formz { className = Romm\Formz\Validation\Validator\RegexValidator options { # Pattern used by the regex (you don't need separators). - pattern = ^[A-Za-z0-9àáâãäåçèéêëìíîïðòóôõöùúûüýÿ]*$ + pattern = ^[A-Za-z0-9àáâãäåçèéêëìíîïðòóôõöùúûüýÿßÄÖÜ]*$ # Options for the regex, e.g. "i" for insensitive case. options = i } From 92597e572fbe6956dc25293a87f9c3a3125c1b96 Mon Sep 17 00:00:00 2001 From: Nathan Boiron Date: Tue, 16 Jan 2018 15:23:13 +0100 Subject: [PATCH 19/27] [FEATURE] Add `fz-loading` to `form` tag when field is validating (#78) When any field starts its validation, the data attribute `fz-loading` is added to the `
` tag. When the validation is done (successful or not) the data attribute is removed. --- Resources/Public/JavaScript/Field/Formz.Field.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Resources/Public/JavaScript/Field/Formz.Field.js b/Resources/Public/JavaScript/Field/Formz.Field.js index f9c805b..3cfe499 100644 --- a/Resources/Public/JavaScript/Field/Formz.Field.js +++ b/Resources/Public/JavaScript/Field/Formz.Field.js @@ -192,10 +192,13 @@ Fz.Field = (function () { handleLoadingBehaviour: function (run) { var element = this.getFieldContainer(); if (null !== element) { + var formElement = this.getForm().getElement(); if (true === run) { element.setAttribute('fz-loading', '1'); + formElement.setAttribute('fz-loading', '1'); } else { element.removeAttribute('fz-loading'); + formElement.removeAttribute('fz-loading'); } } }, From 55ec7e258710e66a31114aad117e8fc6a6db1577 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Mon, 22 Jan 2018 13:29:50 +0100 Subject: [PATCH 20/27] [TASK] Rename TCA file to `tt_content` Fixes #79 --- Configuration/TCA/Overrides/{sys_template.php => tt_content.php} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Configuration/TCA/Overrides/{sys_template.php => tt_content.php} (100%) diff --git a/Configuration/TCA/Overrides/sys_template.php b/Configuration/TCA/Overrides/tt_content.php similarity index 100% rename from Configuration/TCA/Overrides/sys_template.php rename to Configuration/TCA/Overrides/tt_content.php From a349528e15ff8e815da0bbb0ee8d860887bf0248 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Mon, 22 Jan 2018 13:37:07 +0100 Subject: [PATCH 21/27] [DOC] Fix English translation --- Documentation/Index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Index.rst b/Documentation/Index.rst index 5839e29..bce90c8 100644 --- a/Documentation/Index.rst +++ b/Documentation/Index.rst @@ -43,7 +43,7 @@ FormZ • Modern form handler The content of this document is related to TYPO3, a GNU/GPL CMS/Framework available from `www.typo3.org `_. - **Sommaire** + **Table of contents** .. toctree:: :maxdepth: 5 From c99d9f93f059175829a930f063d12be67c54c24c Mon Sep 17 00:00:00 2001 From: Franz Holzinger Date: Sat, 27 Jan 2018 14:57:55 +0100 Subject: [PATCH 22/27] [TASK] Show the error code when definition is invalid (#82) --- Resources/Private/Templates/Error/ConfigurationErrorBlock.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Resources/Private/Templates/Error/ConfigurationErrorBlock.html b/Resources/Private/Templates/Error/ConfigurationErrorBlock.html index 8303b6e..13f3d9f 100644 --- a/Resources/Private/Templates/Error/ConfigurationErrorBlock.html +++ b/Resources/Private/Templates/Error/ConfigurationErrorBlock.html @@ -12,7 +12,7 @@ {property}
    -
  • {error}
  • +
  • {error} (code: {error.code})
From a6ab84ddf24f598f6801f09c6097721fdb139d44 Mon Sep 17 00:00:00 2001 From: Romain Canon Date: Sat, 27 Jan 2018 15:04:49 +0100 Subject: [PATCH 23/27] [TASK] Update Composer licence identifier to new format --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index a8fe25a..1315860 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "flexibility" ], "homepage": "http://typo3-formz.com/", - "license": "GPL-3.0+", + "license": "GPL-3.0-or-later", "authors": [ { "name": "Romain Canon", From b44b0ec3f9844e62e1058eaaf1797a25ee81fa24 Mon Sep 17 00:00:00 2001 From: Philippe Court Date: Sat, 27 Jan 2018 15:09:04 +0100 Subject: [PATCH 24/27] [TASK] Trim value on input fields (#83) Removing beginning/ending whitespaces on input fields. --- Resources/Public/JavaScript/Field/Formz.Field.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Resources/Public/JavaScript/Field/Formz.Field.js b/Resources/Public/JavaScript/Field/Formz.Field.js index 3cfe499..4b69aeb 100644 --- a/Resources/Public/JavaScript/Field/Formz.Field.js +++ b/Resources/Public/JavaScript/Field/Formz.Field.js @@ -343,6 +343,10 @@ Fz.Field = (function () { * triggered when the value is changed. */ (function () { + var trimField = function () { + this.value = this.value.replace(/^\s+|\s+$/g, ''); + }; + var validateCallback = function () { states.field.validate(); }; @@ -358,6 +362,9 @@ Fz.Field = (function () { element.addEventListener('change', validateCallback); } else if (element.type.substr(0, 6) === 'select') { element.addEventListener('change', validateCallback); + } else if (element.type === 'text') { + element.addEventListener('blur', trimField); + element.addEventListener('blur', validateCallback); } else { element.addEventListener('blur', validateCallback); } From e4a9bdb8a45a8decd9699cf519a2802590509d47 Mon Sep 17 00:00:00 2001 From: saiid abdeljabbar Date: Mon, 12 Feb 2018 15:09:17 +0100 Subject: [PATCH 25/27] [FEATURE] Introduce new condition `FieldIsFilled` (#84) Can be used to check whether a field does contain any value (it is not empty). --- Classes/Condition/ConditionFactory.php | 4 + .../Exceptions/InvalidConditionException.php | 17 ++++ .../Items/FieldIsFilledCondition.php | 95 +++++++++++++++++++ .../Formz.Condition.FieldIsFilled.js | 26 +++++ 4 files changed, 142 insertions(+) create mode 100644 Classes/Condition/Items/FieldIsFilledCondition.php create mode 100644 Resources/Public/JavaScript/Conditions/Formz.Condition.FieldIsFilled.js diff --git a/Classes/Condition/ConditionFactory.php b/Classes/Condition/ConditionFactory.php index 8bcec3f..2856d9c 100644 --- a/Classes/Condition/ConditionFactory.php +++ b/Classes/Condition/ConditionFactory.php @@ -17,6 +17,7 @@ use Romm\Formz\Condition\Items\FieldHasErrorCondition; use Romm\Formz\Condition\Items\FieldHasValueCondition; use Romm\Formz\Condition\Items\FieldIsEmptyCondition; +use Romm\Formz\Condition\Items\FieldIsFilledCondition; use Romm\Formz\Condition\Items\FieldIsValidCondition; use Romm\Formz\Exceptions\ClassNotFoundException; use Romm\Formz\Exceptions\EntryNotFoundException; @@ -134,6 +135,9 @@ public function registerDefaultConditions() )->registerCondition( FieldIsEmptyCondition::CONDITION_NAME, FieldIsEmptyCondition::class + )->registerCondition( + FieldIsFilledCondition::CONDITION_IDENTIFIER, + FieldIsFilledCondition::class ); } } diff --git a/Classes/Condition/Exceptions/InvalidConditionException.php b/Classes/Condition/Exceptions/InvalidConditionException.php index 09000a2..7f42ffa 100644 --- a/Classes/Condition/Exceptions/InvalidConditionException.php +++ b/Classes/Condition/Exceptions/InvalidConditionException.php @@ -149,6 +149,23 @@ final public static function conditionFieldIsEmptyFieldNotFound($fieldName) return $exception; } + /** + * @code 1518016343128 + * + * @param string $fieldName + * @return InvalidConditionException + */ + final public static function conditionFieldIsFilledFieldNotFound($fieldName) + { + /** @var self $exception */ + $exception = self::getNewExceptionInstance( + self::FIELD_DOES_NOT_EXIST, + [$fieldName] + ); + + return $exception; + } + /** * @code 1488183577 * diff --git a/Classes/Condition/Items/FieldIsFilledCondition.php b/Classes/Condition/Items/FieldIsFilledCondition.php new file mode 100644 index 0000000..14d4981 --- /dev/null +++ b/Classes/Condition/Items/FieldIsFilledCondition.php @@ -0,0 +1,95 @@ + + * + * This file is part of the TYPO3 FormZ project. + * It is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, either + * version 3 of the License, or any later version. + * + * For the full copyright and license information, see: + * http://www.gnu.org/licenses/gpl-3.0.html + */ + +namespace Romm\Formz\Condition\Items; + +use Romm\Formz\AssetHandler\Html\DataAttributesAssetHandler; +use Romm\Formz\Condition\Exceptions\InvalidConditionException; +use Romm\Formz\Condition\Processor\DataObject\PhpConditionDataObject; +use TYPO3\CMS\Extbase\Reflection\ObjectAccess; + +/** + * This condition will match when a field is filled with any value. + */ +class FieldIsFilledCondition extends AbstractConditionItem +{ + const CONDITION_IDENTIFIER = 'fieldIsFilled'; + + /** + * @inheritdoc + * @var array + */ + protected static $javaScriptFiles = [ + 'EXT:formz/Resources/Public/JavaScript/Conditions/Formz.Condition.FieldIsFilled.js' + ]; + + /** + * @var string + * @validate NotEmpty + */ + protected $fieldName; + + /** + * @param string $fieldName + */ + public function __construct($fieldName) + { + $this->fieldName = $fieldName; + } + + /** + * @inheritdoc + */ + public function getCssResult() + { + $valueKey = DataAttributesAssetHandler::getFieldDataValueKey($this->fieldName); + + return '[' . $valueKey . ']:not([' . $valueKey . '=""])'; + } + + /** + * @inheritdoc + */ + public function getJavaScriptResult() + { + return $this->getDefaultJavaScriptCall(['fieldName' => $this->fieldName]); + } + + /** + * @inheritdoc + */ + public function getPhpResult(PhpConditionDataObject $dataObject) + { + $value = ObjectAccess::getProperty($dataObject->getForm(), $this->fieldName); + + return !empty($value); + } + + /** + * Checks the condition configuration/options. + * + * If any syntax/configuration error is found, an exception of type + * `InvalidConditionException` must be thrown. + * + * @throws InvalidConditionException + */ + protected function checkConditionConfiguration() + { + $configuration = $this->formObject->getConfiguration(); + + if (false === $configuration->hasField($this->fieldName)) { + throw InvalidConditionException::conditionFieldIsFilledFieldNotFound($this->fieldName); + } + } +} diff --git a/Resources/Public/JavaScript/Conditions/Formz.Condition.FieldIsFilled.js b/Resources/Public/JavaScript/Conditions/Formz.Condition.FieldIsFilled.js new file mode 100644 index 0000000..769feee --- /dev/null +++ b/Resources/Public/JavaScript/Conditions/Formz.Condition.FieldIsFilled.js @@ -0,0 +1,26 @@ +Fz.Condition.registerCondition( + 'Romm\\Formz\\Condition\\Items\\FieldIsFilledCondition', + /** + * @param {Formz.FormInstance} form + * @param {Object} data + * @param {String} data.fieldName + */ + function (form, data) { + var flag = false; + var field = form.getFieldByName(data['fieldName']); + + if (null !== field) { + var value = field.getValue(); + + if (typeof value === 'object') { + flag = (value.length !== 0); + } else { + flag = (value !== ''); + } + } else { + flag = false; + } + + return flag; + } +); From b28e82403c80d58c427c7a4d1d0259f679188e17 Mon Sep 17 00:00:00 2001 From: Philippe Court Date: Fri, 2 Mar 2018 09:41:06 +0100 Subject: [PATCH 26/27] [FEATURE] add condition activation fieldCountValues --- Classes/Condition/ConditionFactory.php | 4 + .../Exceptions/InvalidConditionException.php | 17 +++ .../Items/FieldCountValuesCondition.php | 108 ++++++++++++++++++ .../Formz.Condition.FieldCountValues.js | 31 +++++ 4 files changed, 160 insertions(+) create mode 100644 Classes/Condition/Items/FieldCountValuesCondition.php create mode 100644 Resources/Public/JavaScript/Conditions/Formz.Condition.FieldCountValues.js diff --git a/Classes/Condition/ConditionFactory.php b/Classes/Condition/ConditionFactory.php index 2856d9c..545d8ec 100644 --- a/Classes/Condition/ConditionFactory.php +++ b/Classes/Condition/ConditionFactory.php @@ -14,6 +14,7 @@ namespace Romm\Formz\Condition; use Romm\Formz\Condition\Items\ConditionItemInterface; +use Romm\Formz\Condition\Items\FieldCountValuesCondition; use Romm\Formz\Condition\Items\FieldHasErrorCondition; use Romm\Formz\Condition\Items\FieldHasValueCondition; use Romm\Formz\Condition\Items\FieldIsEmptyCondition; @@ -138,6 +139,9 @@ public function registerDefaultConditions() )->registerCondition( FieldIsFilledCondition::CONDITION_IDENTIFIER, FieldIsFilledCondition::class + )->registerCondition( + FieldCountValuesCondition::CONDITION_IDENTIFIER, + FieldCountValuesCondition::class ); } } diff --git a/Classes/Condition/Exceptions/InvalidConditionException.php b/Classes/Condition/Exceptions/InvalidConditionException.php index 7f42ffa..55660f8 100644 --- a/Classes/Condition/Exceptions/InvalidConditionException.php +++ b/Classes/Condition/Exceptions/InvalidConditionException.php @@ -182,4 +182,21 @@ final public static function conditionFieldIsValidFieldNotFound($fieldName) return $exception; } + + /** + * @code 1519909297 + * + * @param string $fieldName + * @return InvalidConditionException + */ + final public static function conditionFieldCountValuesFieldNotFound($fieldName) + { + /** @var self $exception */ + $exception = self::getNewExceptionInstance( + self::FIELD_DOES_NOT_EXIST, + [$fieldName] + ); + + return $exception; + } } diff --git a/Classes/Condition/Items/FieldCountValuesCondition.php b/Classes/Condition/Items/FieldCountValuesCondition.php new file mode 100644 index 0000000..c0b82d2 --- /dev/null +++ b/Classes/Condition/Items/FieldCountValuesCondition.php @@ -0,0 +1,108 @@ + + * + * This file is part of the TYPO3 FormZ project. + * It is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License, either + * version 3 of the License, or any later version. + * + * For the full copyright and license information, see: + * http://www.gnu.org/licenses/gpl-3.0.html + */ + +namespace Romm\Formz\Condition\Items; + +use Romm\Formz\Condition\Exceptions\InvalidConditionException; +use Romm\Formz\Condition\Processor\DataObject\PhpConditionDataObject; +use TYPO3\CMS\Extbase\Reflection\ObjectAccess; + +/** + * This condition will match when a field has a number of selected items between + * a given minimum and maximum. + */ +class FieldCountValuesCondition extends AbstractConditionItem +{ + const CONDITION_IDENTIFIER = 'fieldCountValues'; + + /** + * @inheritdoc + * @var array + */ + protected static $javaScriptFiles = [ + 'EXT:formz/Resources/Public/JavaScript/Conditions/Formz.Condition.FieldCountValues.js' + ]; + + /** + * @var string + * @validate NotEmpty + */ + protected $fieldName; + + /** + * @var int + */ + protected $minimum; + + /** + * @var int + */ + protected $maximum; + + /** + * @param string $fieldName + * @param int $minimum + * @param int $maximum + */ + public function __construct($fieldName, $minimum = null, $maximum = null) + { + $this->fieldName = $fieldName; + $this->minimum = $minimum; + $this->maximum = $maximum; + } + + /** + * @inheritdoc + */ + public function getJavaScriptResult() + { + return $this->getDefaultJavaScriptCall([ + 'fieldName' => $this->fieldName, + 'minimum' => $this->minimum, + 'maximum' => $this->maximum + ]); + } + + /** + * @inheritdoc + */ + public function getPhpResult(PhpConditionDataObject $dataObject) + { + $value = ObjectAccess::getProperty($dataObject->getForm(), $this->fieldName); + + return !($this->minimum && count($value) < (int)$this->minimum) + && !($this->maximum && count($value) > (int)$this->maximum); + } + + /** + * @inheritdoc + * + * @throws InvalidConditionException + */ + protected function checkConditionConfiguration() + { + $configuration = $this->formObject->getConfiguration(); + + if (false === $configuration->hasField($this->fieldName)) { + throw InvalidConditionException::conditionFieldCountValuesFieldNotFound($this->fieldName); + } + } + + /** + * @return string + */ + public function getCssResult() + { + return ''; + } +} diff --git a/Resources/Public/JavaScript/Conditions/Formz.Condition.FieldCountValues.js b/Resources/Public/JavaScript/Conditions/Formz.Condition.FieldCountValues.js new file mode 100644 index 0000000..309ecde --- /dev/null +++ b/Resources/Public/JavaScript/Conditions/Formz.Condition.FieldCountValues.js @@ -0,0 +1,31 @@ +Fz.Condition.registerCondition( + 'Romm\\Formz\\Condition\\Items\\FieldCountValuesCondition', + /** + * @param {Formz.FormInstance} form + * @param {Object} data + * @param {String} data.fieldName + * @param {String} data.minimum + * @param {String} data.maximum + */ + function (form, data) { + var field = form.getFieldByName(data['fieldName']); + var minimum = data['minimum']; + var maximum = data['maximum']; + + if (null === field) { + return false; + } + + var fieldValue = field.getValue(); + var values = []; + + if (fieldValue !== '' + && fieldValue.length !== 0 + ) { + values = Fz.commaSeparatedValues(fieldValue).split(','); + } + + return !(maximum && values.length > maximum) + && !(minimum && values.length < minimum); + } +); From 64c3e8b7cb9e98a8da86ebc613fbc5ec8f65b0d8 Mon Sep 17 00:00:00 2001 From: Philippe Court Date: Fri, 16 Mar 2018 09:09:42 +0100 Subject: [PATCH 27/27] [FEATURE] add getter getInstances() in FormObjectFactory --- Classes/Form/FormObjectFactory.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Classes/Form/FormObjectFactory.php b/Classes/Form/FormObjectFactory.php index cfae228..fe8aa6a 100644 --- a/Classes/Form/FormObjectFactory.php +++ b/Classes/Form/FormObjectFactory.php @@ -177,6 +177,14 @@ protected function getCacheIdentifier($className, $name) ] ); } + + /** + * @return FormObject[] + */ + public function getInstances() + { + return $this->instances; + } /** * @param ConfigurationFactory $configurationFactory