From 753d0acce4fba5ef4e941b27816d2a5355bfa72f Mon Sep 17 00:00:00 2001 From: Benjamin Dalsass Date: Mon, 17 Nov 2025 14:51:11 +0100 Subject: [PATCH] =?UTF-8?q?N=C2=B08772=20-=20dynamic=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/composer/autoload_classmap.php | 4 ++ lib/composer/autoload_static.php | 4 ++ sources/Forms/Block/AbstractTypeFormBlock.php | 11 ++++ sources/Forms/Block/Base/FormBlock.php | 43 +++++++++++---- .../Forms/FormBuilder/DependencyHandler.php | 3 +- sources/Forms/Register/OptionsRegister.php | 16 ++++++ sources/Forms/Register/RegisterException.php | 18 +++++++ .../Forms/{Block.php => Block/BlockTest.php} | 10 ++-- .../Forms/Register/OptionsRegisterTest.php | 53 +++++++++++++++++++ 9 files changed, 146 insertions(+), 16 deletions(-) create mode 100644 sources/Forms/Register/RegisterException.php rename tests/php-unit-tests/unitary-tests/sources/Forms/{Block.php => Block/BlockTest.php} (92%) create mode 100644 tests/php-unit-tests/unitary-tests/sources/Forms/Register/OptionsRegisterTest.php diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index fd3a8a7b4..1755cbed6 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -491,9 +491,12 @@ return array( 'Combodo\\iTop\\Forms\\Block\\Base\\TextFormBlock' => $baseDir . '/sources/Forms/Block/Base/TextFormBlock.php', 'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeChoiceFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/AttributeChoiceFormBlock.php', 'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeValueChoiceFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php', + 'Combodo\\iTop\\Forms\\Block\\DataModel\\IDataModelBlock' => $baseDir . '/sources/Forms/Block/DataModel/IDataModelBlock.php', 'Combodo\\iTop\\Forms\\Block\\DataModel\\OqlFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/OqlFormBlock.php', 'Combodo\\iTop\\Forms\\Block\\Expression\\ExpressionFormBlock' => $baseDir . '/sources/Forms/Block/Expression/ExpressionFormBlock.php', + 'Combodo\\iTop\\Forms\\Block\\FormBlockDescription' => $baseDir . '/sources/Forms/Block/FormBlockDescription.php', 'Combodo\\iTop\\Forms\\Block\\FormBlockException' => $baseDir . '/sources/Forms/Block/FormBlockException.php', + 'Combodo\\iTop\\Forms\\Block\\FormBlockFactory' => $baseDir . '/sources/Forms/Block/FormBlockFactory.php', 'Combodo\\iTop\\Forms\\FormBuilder\\DependencyHandler' => $baseDir . '/sources/Forms/FormBuilder/DependencyHandler.php', 'Combodo\\iTop\\Forms\\FormBuilder\\DependencyMap' => $baseDir . '/sources/Forms/FormBuilder/DependencyMap.php', 'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilder' => $baseDir . '/sources/Forms/FormBuilder/FormBuilder.php', @@ -528,6 +531,7 @@ return array( 'Combodo\\iTop\\Forms\\Register\\IORegister' => $baseDir . '/sources/Forms/Register/IORegister.php', 'Combodo\\iTop\\Forms\\Register\\Option' => $baseDir . '/sources/Forms/Register/Option.php', 'Combodo\\iTop\\Forms\\Register\\OptionsRegister' => $baseDir . '/sources/Forms/Register/OptionsRegister.php', + 'Combodo\\iTop\\Forms\\Register\\RegisterException' => $baseDir . '/sources/Forms/Register/RegisterException.php', 'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => $baseDir . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php', 'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php', 'Combodo\\iTop\\Renderer\\BlockRenderer' => $baseDir . '/sources/Renderer/BlockRenderer.php', diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index a503b8c3a..943e26dca 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -872,9 +872,12 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Forms\\Block\\Base\\TextFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/TextFormBlock.php', 'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/AttributeChoiceFormBlock.php', 'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeValueChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php', + 'Combodo\\iTop\\Forms\\Block\\DataModel\\IDataModelBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/IDataModelBlock.php', 'Combodo\\iTop\\Forms\\Block\\DataModel\\OqlFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/OqlFormBlock.php', 'Combodo\\iTop\\Forms\\Block\\Expression\\ExpressionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Expression/ExpressionFormBlock.php', + 'Combodo\\iTop\\Forms\\Block\\FormBlockDescription' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockDescription.php', 'Combodo\\iTop\\Forms\\Block\\FormBlockException' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockException.php', + 'Combodo\\iTop\\Forms\\Block\\FormBlockFactory' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockFactory.php', 'Combodo\\iTop\\Forms\\FormBuilder\\DependencyHandler' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/DependencyHandler.php', 'Combodo\\iTop\\Forms\\FormBuilder\\DependencyMap' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/DependencyMap.php', 'Combodo\\iTop\\Forms\\FormBuilder\\FormBuilder' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/FormBuilder.php', @@ -909,6 +912,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Forms\\Register\\IORegister' => __DIR__ . '/../..' . '/sources/Forms/Register/IORegister.php', 'Combodo\\iTop\\Forms\\Register\\Option' => __DIR__ . '/../..' . '/sources/Forms/Register/Option.php', 'Combodo\\iTop\\Forms\\Register\\OptionsRegister' => __DIR__ . '/../..' . '/sources/Forms/Register/OptionsRegister.php', + 'Combodo\\iTop\\Forms\\Register\\RegisterException' => __DIR__ . '/../..' . '/sources/Forms/Register/RegisterException.php', 'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => __DIR__ . '/../..' . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php', 'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php', 'Combodo\\iTop\\Renderer\\BlockRenderer' => __DIR__ . '/../..' . '/sources/Renderer/BlockRenderer.php', diff --git a/sources/Forms/Block/AbstractTypeFormBlock.php b/sources/Forms/Block/AbstractTypeFormBlock.php index 8f91c3f3b..f8c73f5a6 100644 --- a/sources/Forms/Block/AbstractTypeFormBlock.php +++ b/sources/Forms/Block/AbstractTypeFormBlock.php @@ -8,6 +8,7 @@ namespace Combodo\iTop\Forms\Block; use Combodo\iTop\Forms\IO\Format\BooleanIOFormat; use Combodo\iTop\Forms\Register\IORegister; +use Combodo\iTop\Forms\Register\OptionsRegister; abstract class AbstractTypeFormBlock extends AbstractFormBlock { @@ -80,4 +81,14 @@ abstract class AbstractTypeFormBlock extends AbstractFormBlock { $this->bIsAddedToForm = $bIsAdded; } + + /** @inheritdoc */ + public function UpdateOptions(OptionsRegister $oOptionsRegister): void + { + parent::UpdateOptions($oOptionsRegister); + + if($this->GetInput(self::INPUT_ENABLE)->IsBound()){ + $oOptionsRegister->SetOption('disabled', !$this->GetInputValue(self::INPUT_ENABLE)); + } + } } \ No newline at end of file diff --git a/sources/Forms/Block/Base/FormBlock.php b/sources/Forms/Block/Base/FormBlock.php index 3a8e6eda4..86871d9c8 100644 --- a/sources/Forms/Block/Base/FormBlock.php +++ b/sources/Forms/Block/Base/FormBlock.php @@ -66,29 +66,54 @@ class FormBlock extends AbstractTypeFormBlock * Add a child form. * * @param string $sName block name - * @param string $sType block class name - * @param array $aSymfonyOptions options + * @param string $sBlockClass block class name * @param array $aOptions options * * @return $this * @throws ReflectionException - * @throws \Combodo\iTop\Forms\Block\FormBlockException + * @throws FormBlockException */ - public function Add(string $sName, string $sType, array $aSymfonyOptions, array $aOptions = []): AbstractFormBlock + public function Add(string $sName, string $sBlockClass,array $aOptions = []): AbstractFormBlock { - $oRef = new ReflectionClass($sType); - if($oRef->isSubclassOf(AbstractFormBlock::class) === false){ - throw new FormBlockException("The block type '$sType' is not a subclass of AbstractFormBlock."); - } + $this->VerifyBlockName($sName); + $this->VerifyBlockClassName($sBlockClass); $aOptions['priority'] = -count($this->aChildrenBlocks); - $oSubFormBlock = new ($sType)($sName, $aSymfonyOptions, $aOptions); + $oSubFormBlock = new ($sBlockClass)($sName, $aOptions); $this->aChildrenBlocks[$sName] = $oSubFormBlock; $oSubFormBlock->SetParent($this); return $oSubFormBlock; } + /** + * @param string $sBlockName + * + * @return void + * @throws FormBlockException + */ + private function VerifyBlockName(string $sBlockName): void + { + if(!ctype_alnum(str_replace(array('-', '_'), '', $sBlockName))) { + throw new FormBlockException("Block name '$sBlockName' is not valid. Only alphanumeric characters, hyphens and underscores are allowed."); + } + } + + /** + * @param string $sBlockClass + * + * @return void + * @throws FormBlockException + * @throws ReflectionException + */ + private function VerifyBlockClassName(string $sBlockClass): void + { + $oRef = new ReflectionClass($sBlockClass); + if($oRef->isSubclassOf(AbstractFormBlock::class) === false){ + throw new FormBlockException("The block type '$sBlockClass' is not a subclass of AbstractFormBlock."); + } + } + /** * Get the children forms. * diff --git a/sources/Forms/FormBuilder/DependencyHandler.php b/sources/Forms/FormBuilder/DependencyHandler.php index 1aeb86a3c..bca47c4a7 100644 --- a/sources/Forms/FormBuilder/DependencyHandler.php +++ b/sources/Forms/FormBuilder/DependencyHandler.php @@ -9,6 +9,7 @@ namespace Combodo\iTop\Forms\FormBuilder; use Combodo\iTop\Forms\Block\AbstractFormBlock; use Combodo\iTop\Forms\Block\AbstractTypeFormBlock; use Combodo\iTop\Forms\Block\Base\FormBlock; +use Combodo\iTop\Forms\Block\FormBlockException; use Exception; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormError; @@ -156,7 +157,7 @@ class DependencyHandler * @param string|null $sEventType * * @return void - * @throws \Combodo\iTop\Forms\Block\FormBlockException + * @throws FormBlockException */ private function CheckDependencies(FormInterface|FormBuilderInterface $oForm, string $sOutputBlock = null, string $sEventType = null): void { diff --git a/sources/Forms/Register/OptionsRegister.php b/sources/Forms/Register/OptionsRegister.php index 922b51b2a..a67f8c97c 100644 --- a/sources/Forms/Register/OptionsRegister.php +++ b/sources/Forms/Register/OptionsRegister.php @@ -22,9 +22,12 @@ class OptionsRegister * @param bool $bTypeOption * * @return void + * @throws RegisterException */ public function SetOption(string $sOptionName, mixed $mDefaultValue = null, bool $bTypeOption = true): void { + $this->VerifyOptionName($sOptionName); + if(isset($this->aOptions[$sOptionName])){ $this->aOptions[$sOptionName]->oValue = $mDefaultValue; } @@ -33,6 +36,19 @@ class OptionsRegister } } + /** + * @param string $sOptionName + * + * @return void + * @throws RegisterException + */ + private function VerifyOptionName(string $sOptionName): void + { + if(!ctype_alnum(str_replace(array('-', '_'), '', $sOptionName))) { + throw new RegisterException("Option name '$sOptionName' is not valid. Only alphanumeric characters, hyphens and underscores are allowed."); + } + } + /** * Set an option array value. * diff --git a/sources/Forms/Register/RegisterException.php b/sources/Forms/Register/RegisterException.php new file mode 100644 index 000000000..db8fb837d --- /dev/null +++ b/sources/Forms/Register/RegisterException.php @@ -0,0 +1,18 @@ +FindItopClasses(iFormBlock::class); foreach ($aFormBlocks as $sFormBlock) { @@ -51,7 +49,7 @@ class Block extends ItopDataTestCase * * @throws ReflectionException */ - public function testAddBlockFromSymfonyType(): void + public function testAddChildBlockClass(): void { $oFormBlock = new FormBlock('formBlock'); $this->expectException(FormBlockException::class); diff --git a/tests/php-unit-tests/unitary-tests/sources/Forms/Register/OptionsRegisterTest.php b/tests/php-unit-tests/unitary-tests/sources/Forms/Register/OptionsRegisterTest.php new file mode 100644 index 000000000..45471e9f2 --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/sources/Forms/Register/OptionsRegisterTest.php @@ -0,0 +1,53 @@ +oOptionsRegister = new OptionsRegister(); + } + + public function testSetOptionWithInvalidName(): void + { + $this->oOptionsRegister->SetOption('valid_option_name', 'value'); + + $this->expectException(RegisterException::class); + $this->oOptionsRegister->SetOption('not valid option name', 'value'); + } + + public function testSetNonTypeOption(): void + { + $this->oOptionsRegister->SetOption('not_a_type_option', 'value', false); + + $this->assertArrayNotHasKey('not_a_type_option', $this->oOptionsRegister->GetOptions()); + } + + public function testSetOptionArrayValue(): void + { + $this->oOptionsRegister->SetOptionArrayValue('att', 'class', 'ibo-class'); + + $this->assertEquals('ibo-class', $this->oOptionsRegister->GetOption('att')['class']); + } + + public function testHasOption(): void + { + $this->oOptionsRegister->SetOption('option', true); + + $this->assertTrue($this->oOptionsRegister->HasOption('option')); + } + +} \ No newline at end of file