diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php
index 0b2f0d93f..2b0d9e97a 100644
--- a/lib/composer/autoload_classmap.php
+++ b/lib/composer/autoload_classmap.php
@@ -491,6 +491,7 @@ return array(
'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\\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\\FormBlockException' => $baseDir . '/sources/Forms/Block/FormBlockException.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockIOException' => $baseDir . '/sources/Forms/Block/IO/FormBlockIOException.php',
'Combodo\\iTop\\Forms\\Block\\FormType\\ChoiceFormType' => $baseDir . '/sources/Forms/Block/FormType/ChoiceFormType.php',
@@ -523,6 +524,7 @@ return array(
'Combodo\\iTop\\Forms\\FormsException' => $baseDir . '/sources/Forms/FormsException.php',
'Combodo\\iTop\\Forms\\IFormBlock' => $baseDir . '/sources/Forms/IFormBlock.php',
'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => $baseDir . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php',
+ 'Combodo\\iTop\\ItopSdkFormDemonstrator\\Form\\Block\\ExpressionFormBlock' => $baseDir . '/sources/Forms/Block/Base/ExpressionFormBlock.php',
'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php',
'Combodo\\iTop\\Renderer\\BlockRenderer' => $baseDir . '/sources/Renderer/BlockRenderer.php',
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => $baseDir . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php',
diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php
index ef80b7de1..f450c1e58 100644
--- a/lib/composer/autoload_static.php
+++ b/lib/composer/autoload_static.php
@@ -872,6 +872,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'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\\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\\FormBlockException' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockException.php',
'Combodo\\iTop\\Forms\\Block\\FormBlockIOException' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/FormBlockIOException.php',
'Combodo\\iTop\\Forms\\Block\\FormType\\ChoiceFormType' => __DIR__ . '/../..' . '/sources/Forms/Block/FormType/ChoiceFormType.php',
@@ -904,6 +905,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Forms\\FormsException' => __DIR__ . '/../..' . '/sources/Forms/FormsException.php',
'Combodo\\iTop\\Forms\\IFormBlock' => __DIR__ . '/../..' . '/sources/Forms/IFormBlock.php',
'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => __DIR__ . '/../..' . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php',
+ 'Combodo\\iTop\\ItopSdkFormDemonstrator\\Form\\Block\\ExpressionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/ExpressionFormBlock.php',
'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php',
'Combodo\\iTop\\Renderer\\BlockRenderer' => __DIR__ . '/../..' . '/sources/Renderer/BlockRenderer.php',
'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => __DIR__ . '/../..' . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php',
diff --git a/sources/Forms/Block/Base/CheckboxFormBlock.php b/sources/Forms/Block/Base/CheckboxFormBlock.php
index 3417ad9d1..a64a00f40 100644
--- a/sources/Forms/Block/Base/CheckboxFormBlock.php
+++ b/sources/Forms/Block/Base/CheckboxFormBlock.php
@@ -26,6 +26,7 @@ class CheckboxFormBlock extends AbstractTypeFormBlock
return CheckboxType::class;
}
+ /** @inheritdoc */
function InitBlockOptions(array &$aUserOptions): void
{
parent::InitBlockOptions($aUserOptions);
diff --git a/sources/Forms/Block/Base/ExpressionFormBlock.php b/sources/Forms/Block/Base/ExpressionFormBlock.php
new file mode 100644
index 000000000..1401caf68
--- /dev/null
+++ b/sources/Forms/Block/Base/ExpressionFormBlock.php
@@ -0,0 +1,96 @@
+AddOutput(self::OUTPUT_RESULT, BooleanIOFormat::class);
+// $this->AddOutput(self::OUTPUT_RAW, RawFormat::class);
+ }
+
+// public function InputHasChanged()
+// {
+// if (!$this->IsInputsDataReady()) {
+// return;
+// }
+// $sExpression = $this->GetOptions()['expression'];
+// $sValue = preg_replace_callback(
+// "/\[\[(?[^\]]+)]]/",
+// function(array $aMatches): string {
+// return $this->GetInput($aMatches['input'])->GetValue();
+// },
+// $sExpression);
+//
+// foreach ($this->GetInputs() as $oFormInput) {
+// IssueLog::Info($oFormInput->GetName().' = '.$oFormInput->GetValue());
+// }
+// IssueLog::Info("Result of [$sExpression] is [$sValue]");
+//
+// $result = '';
+// eval('$result = '.$sValue.';');
+// IssueLog::Info("Result of [$sExpression] is eval to [$result]");
+//
+// $this->GetOutput(self::OUTPUT_RESULT)->SetValue(FormEvents::POST_SUBMIT, new BooleanIOFormat($result));
+// $this->GetOutput(self::OUTPUT_VALUE)->SetValue(FormEvents::POST_SUBMIT, new RawFormat($result));
+// }
+
+
+
+ public function InputHasChanged()
+ {
+ if (!$this->IsInputsDataReady()) {
+ return;
+ }
+ $sExpression = $this->GetOptions()['expression'];
+
+ $this->Compute($sExpression, FormEvents::POST_SET_DATA);
+ $this->Compute($sExpression, FormEvents::POST_SUBMIT);
+ }
+
+ public function Compute(string $sExpression, string $sEventType): void
+ {
+ try{
+ $sValue = preg_replace_callback(
+ "/\[\[(?[^\]]+)]]/",
+ function(array $aMatches) use ($sEventType): ?string {
+ $oInput = $this->GetInput($aMatches['input']);
+ if(!$oInput->HasEventValue($sEventType)){
+ throw new FormsException('Unable to compute expression: input '.$aMatches['input'].' has no value for event '.$sEventType.'.');
+ }
+ return $oInput->GetValue($sEventType);
+ },
+ $sExpression);
+
+ $result = '';
+ eval('$result = '.$sValue.';');
+
+ $this->GetOutput(self::OUTPUT_RESULT)->SetValue($sEventType, new BooleanIOFormat($result));
+ $this->GetOutput(self::OUTPUT_VALUE)->SetValue($sEventType, new RawFormat($result));
+ }
+ catch(\Exception $e){
+ IssueLog::Exception('Compute expression block issue', $e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sources/Forms/Block/Expression/ExpressionFormBlock.php b/sources/Forms/Block/Expression/ExpressionFormBlock.php
new file mode 100644
index 000000000..8eea4961b
--- /dev/null
+++ b/sources/Forms/Block/Expression/ExpressionFormBlock.php
@@ -0,0 +1,96 @@
+AddOutput(self::OUTPUT_RESULT, BooleanIOFormat::class);
+// $this->AddOutput(self::OUTPUT_RAW, RawFormat::class);
+ }
+
+// public function InputHasChanged()
+// {
+// if (!$this->IsInputsDataReady()) {
+// return;
+// }
+// $sExpression = $this->GetOptions()['expression'];
+// $sValue = preg_replace_callback(
+// "/\[\[(?[^\]]+)]]/",
+// function(array $aMatches): string {
+// return $this->GetInput($aMatches['input'])->GetValue();
+// },
+// $sExpression);
+//
+// foreach ($this->GetInputs() as $oFormInput) {
+// IssueLog::Info($oFormInput->GetName().' = '.$oFormInput->GetValue());
+// }
+// IssueLog::Info("Result of [$sExpression] is [$sValue]");
+//
+// $result = '';
+// eval('$result = '.$sValue.';');
+// IssueLog::Info("Result of [$sExpression] is eval to [$result]");
+//
+// $this->GetOutput(self::OUTPUT_RESULT)->SetValue(FormEvents::POST_SUBMIT, new BooleanIOFormat($result));
+// $this->GetOutput(self::OUTPUT_VALUE)->SetValue(FormEvents::POST_SUBMIT, new RawFormat($result));
+// }
+
+
+
+ public function InputHasChanged()
+ {
+ if (!$this->IsInputsDataReady()) {
+ return;
+ }
+ $sExpression = $this->GetOptions()['expression'];
+
+ $this->Compute($sExpression, FormEvents::POST_SET_DATA);
+ $this->Compute($sExpression, FormEvents::POST_SUBMIT);
+ }
+
+ public function Compute(string $sExpression, string $sEventType): void
+ {
+ try{
+ $sValue = preg_replace_callback(
+ "/\[\[(?[^\]]+)]]/",
+ function(array $aMatches) use ($sEventType): ?string {
+ $oInput = $this->GetInput($aMatches['input']);
+ if(!$oInput->HasEventValue($sEventType)){
+ throw new FormsException('Unable to compute expression: input '.$aMatches['input'].' has no value for event '.$sEventType.'.');
+ }
+ return $oInput->GetValue($sEventType);
+ },
+ $sExpression);
+
+ $result = '';
+ eval('$result = '.$sValue.';');
+
+ $this->GetOutput(self::OUTPUT_RESULT)->SetValue($sEventType, new BooleanIOFormat($result));
+ $this->GetOutput(self::OUTPUT_VALUE)->SetValue($sEventType, new RawFormat($result));
+ }
+ catch(\Exception $e){
+ IssueLog::Exception('Compute expression block issue', $e);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sources/Forms/Block/IO/AbstractFormIO.php b/sources/Forms/Block/IO/AbstractFormIO.php
index 1a82b3c55..f2e928876 100644
--- a/sources/Forms/Block/IO/AbstractFormIO.php
+++ b/sources/Forms/Block/IO/AbstractFormIO.php
@@ -245,5 +245,14 @@ class AbstractFormIO
return $this->HasValue();
}
+ public function HasBindingOut(): bool
+ {
+ return count($this->aBindingsToInputs) > 0;
+ }
+
+ public function GetBindingsToInputs(): array
+ {
+ return $this->aBindingsToInputs;
+ }
}
\ No newline at end of file
diff --git a/sources/Forms/FormBuilder/DependencyHandler.php b/sources/Forms/FormBuilder/DependencyHandler.php
index 499afd7b7..782387a33 100644
--- a/sources/Forms/FormBuilder/DependencyHandler.php
+++ b/sources/Forms/FormBuilder/DependencyHandler.php
@@ -26,8 +26,8 @@ class DependencyHandler
/** @var DependencyMap dependencies map */
private DependencyMap $oDependenciesMap;
- /** @var array Debug data */
- private array $aDebugData = [];
+ /** @var array events */
+ private array $aEvents = [];
private readonly string $sName;
private readonly AbstractFormBlock $oFormBlock;
private readonly FormBuilder $oFormBuilder;
@@ -94,12 +94,8 @@ class DependencyHandler
continue;
}
- $this->aDebugData[] = [
- 'builder' => $this->oFormBuilder->getName(),
- 'event' => 'form.listen',
- 'form' => $sOutputBlockName,
- 'value' => 'NA',
- ];
+ // Add event
+ $this->AddEvent('form.listen', $sOutputBlockName);
// Listen the output block POST_SET_DATA & POST_SUBMIT
$this->oFormBuilder->get($sOutputBlockName)->addEventListener(FormEvents::POST_SET_DATA, $this->GetEventListeningCallback());
@@ -120,12 +116,8 @@ class DependencyHandler
// Get the event type
$sEventType = FormHelper::GetEventType($oEvent);
- $this->aDebugData[] = [
- 'builder' => $this->oFormBuilder->getName(),
- 'event' => $sEventType,
- 'form' => $oEvent->getForm()->getName(),
- 'value' => $oEvent->getData(),
- ];
+ // Add event
+ $this->AddEvent($sEventType, $oEvent->getForm()->getName(), $oEvent->getData());
// Get the form
$oForm = $oEvent->getForm();
@@ -176,9 +168,13 @@ class DependencyHandler
if ($oDependentBlock->IsVisible($sEventType) && $oDependentBlock->IsInputsDataReady($sEventType)) {
// Get the dependent field options
+ $aBefore = $oDependentBlock->GetOptionsMergedWithDynamic();
$oDependentBlock->UpdateDynamicOptions($sEventType);
$aOptions = $oDependentBlock->GetOptionsMergedWithDynamic($sEventType);
+ // Options changed flag
+ $bOptionsChanged = FormHelper::CompareArrayValues($aBefore, $aOptions);
+
// Add the listener callback to the dependent field if it is also a dependency for another field
if ($this->oDependenciesMap->IsTheBlockInDependencies($oDependentBlock->getName())) {
@@ -188,22 +184,12 @@ class DependencyHandler
]);
}
- if ($oDependentBlock->AllowAdd($sEventType)) {
-
- $this->aDebugData[] = [
- 'builder' => $this->oFormBuilder->getName(),
- 'event' => 'form.add',
- 'form' => $oDependentBlock->getName(),
- 'value' => 'NA',
- ];
+ if ( (!$oDependentBlock->IsAdded() || $bOptionsChanged) && $oDependentBlock->AllowAdd($sEventType)) {
+ // Add events
+ $this->AddEvent('form.add', $oDependentBlock->getName());
if (array_key_exists('builder_listener', $aOptions)) {
- $this->aDebugData[] = [
- 'builder' => $this->oFormBuilder->getName(),
- 'event' => 'form.listen.after',
- 'form' => $oDependentBlock->getName(),
- 'value' => 'NA',
- ];
+ $this->AddEvent('form.listen.after', $oDependentBlock->getName());
}
// Mark the dependency as added
@@ -220,12 +206,8 @@ class DependencyHandler
$oForm->remove($oDependentBlock->GetName());
$oDependentBlock->SetAdded(false);
- $this->aDebugData[] = [
- 'builder' => $this->oFormBuilder->getName(),
- 'event' => 'form.remove',
- 'form' => $oDependentBlock->getName(),
- 'value' => 'NA'
- ];
+ // Add event
+ $this->AddEvent('form.remove', $oDependentBlock->getName());
}
}
@@ -239,7 +221,7 @@ class DependencyHandler
*/
public function GetDebugData(): array
{
- return $this->aDebugData;
+ return $this->aEvents;
}
public function GetMap(): DependencyMap
@@ -247,13 +229,20 @@ class DependencyHandler
return $this->oDependenciesMap;
}
- public function GetSubBlocks(): array
- {
- return $this->aSubBlocks;
- }
public function GetName(): string
{
return $this->sName;
}
+
+ private function AddEvent(string $sEvent, string $sForm, mixed $oValue = 'NA'): void
+ {
+
+ $this->aEvents[] = [
+ 'builder' => $this->oFormBuilder->getName(),
+ 'event' => $sEvent,
+ 'form' => $sForm,
+ 'value' => $oValue,
+ ];
+ }
}
\ No newline at end of file
diff --git a/sources/Forms/FormBuilder/DependencyMap.php b/sources/Forms/FormBuilder/DependencyMap.php
index c3bb37eec..f88d218d7 100644
--- a/sources/Forms/FormBuilder/DependencyMap.php
+++ b/sources/Forms/FormBuilder/DependencyMap.php
@@ -23,6 +23,9 @@ class DependencyMap
/** @var array array of blocks with dependencies group by dependence */
private array $aBlocksWithDependenciesGroupByDependence = [];
+ /** @var array array of binding */
+ private array $aBindings = [];
+
/** @var array array of binding (OUT > OUT) grouped by block and output name */
private array $aBindingsOutputToInput = [];
@@ -148,6 +151,7 @@ class DependencyMap
// add to map
$map[$sBlockName][$sIOName][] = $oBinding;
+ $this->aBindings[] = $oBinding;
}
/**
@@ -240,4 +244,9 @@ class DependencyMap
{
return $this->aBindingsOutputToOutputs;
}
+
+ public function GetBindings()
+ {
+ return $this->aBindings;
+ }
}
\ No newline at end of file
diff --git a/sources/Forms/FormBuilder/FormHelper.php b/sources/Forms/FormBuilder/FormHelper.php
index 276da8e10..863b23820 100644
--- a/sources/Forms/FormBuilder/FormHelper.php
+++ b/sources/Forms/FormBuilder/FormHelper.php
@@ -30,4 +30,22 @@ class FormHelper
throw new FormBuilderException(sprintf('Unknown event type %s', get_class($event)));
}
+
+ public static function CompareArrayValues($mValue1, $mValue2): int
+ {
+ if (is_array($mValue1) && is_array($mValue2)) {
+ if (count($mValue1) !== count($mValue2)) {
+ return 1;
+ }
+ $aDiff = array_udiff_assoc($mValue1, $mValue2, [FormHelper::class, 'CompareArrayValues']);
+
+ return count($aDiff);
+ }
+
+ if ($mValue1 === $mValue2) {
+ return 0;
+ }
+
+ return 1;
+ }
}
\ No newline at end of file