mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-21 09:38:48 +02:00
N°8771 - Add Symfony form component to iTop core
- IO debug
This commit is contained in:
@@ -50,3 +50,7 @@
|
||||
form[aria-busy="true"] {
|
||||
opacity: .5;
|
||||
}
|
||||
|
||||
.ibo-field legend{
|
||||
margin-top: 24px;
|
||||
}
|
||||
0
data/.compilation-symlinks
Normal file
0
data/.compilation-symlinks
Normal file
@@ -475,22 +475,24 @@ return array(
|
||||
'Combodo\\iTop\\Form\\Validator\\SelectObjectValidator' => $baseDir . '/sources/Form/Validator/SelectObjectValidator.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\AbstractFormBlock' => $baseDir . '/sources/Forms/Block/AbstractFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\Base\\ChoiceFormBlock' => $baseDir . '/sources/Forms/Block/Base/ChoiceFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\Base\\StringFormBlock' => $baseDir . '/sources/Forms/Block/Base/StringFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\Base\\FormBlock' => $baseDir . '/sources/Forms/Block/Base/FormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\Base\\TextAreaFormBlock' => $baseDir . '/sources/Forms/Block/Base/TextAreaFormBlock.php',
|
||||
'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\\OqlFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/OqlFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormBlock' => $baseDir . '/sources/Forms/Block/FormBlock.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\\IO\\AbstractFormIO' => $baseDir . '/sources/Forms/Block/IO/AbstractFormIO.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Converter\\AbstractConverter' => $baseDir . '/sources/Forms/Block/IO/Converter/AbstractConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Converter\\OqlToClassConverter' => $baseDir . '/sources/Forms/Block/IO/Converter/OqlToClassConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Converter\\StringToAttributeConverter' => $baseDir . '/sources/Forms/Block/IO/Converter/StringToAttributeConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\FormBinding' => $baseDir . '/sources/Forms/Block/IO/FormBinding.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\FormInput' => $baseDir . '/sources/Forms/Block/IO/FormInput.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\FormOutput' => $baseDir . '/sources/Forms/Block/IO/FormOutput.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Format\\AttributeIOFormat' => $baseDir . '/sources/Forms/Block/IO/Format/AttributeIOFormat.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Format\\ClassIOFormat' => $baseDir . '/sources/Forms/Block/IO/Format/ClassIOFormat.php',
|
||||
'Combodo\\iTop\\Forms\\Converter\\AbstractOutputConverter' => $baseDir . '/sources/Forms/Converter/AbstractOutputConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Converter\\OqlToClassConverter' => $baseDir . '/sources/Forms/Converter/OqlToClassConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Converter\\StringToAttributeConverter' => $baseDir . '/sources/Forms/Converter/StringToAttributeConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Format\\RawFormat' => $baseDir . '/sources/Forms/Block/IO/Format/RawFormat.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',
|
||||
@@ -499,9 +501,10 @@ return array(
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\FormTypeExtension' => $baseDir . '/sources/Forms/FormBuilder/FormTypeExtension.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormType' => $baseDir . '/sources/Forms/FormBuilder/ResolvedFormType.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormTypeFactory' => $baseDir . '/sources/Forms/FormBuilder/ResolvedFormTypeFactory.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\AttributeChoiceType' => $baseDir . '/sources/Forms/FormType/AttributeChoiceType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\AttributeValueChoiceType' => $baseDir . '/sources/Forms/FormType/AttributeValueChoiceType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\OqlType' => $baseDir . '/sources/Forms/FormType/OqlType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\AttributeFormType' => $baseDir . '/sources/Forms/FormType/AttributeFormType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\AttributeValueFormType' => $baseDir . '/sources/Forms/FormType/AttributeValueFormType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\ChoiceFormType' => $baseDir . '/sources/Forms/FormType/ChoiceFormType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\OqlFormType' => $baseDir . '/sources/Forms/FormType/OqlFormType.php',
|
||||
'Combodo\\iTop\\Forms\\Forms' => $baseDir . '/sources/Forms/Forms.php',
|
||||
'Combodo\\iTop\\Forms\\FormsException' => $baseDir . '/sources/Forms/FormsException.php',
|
||||
'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => $baseDir . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php',
|
||||
|
||||
@@ -856,22 +856,24 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Form\\Validator\\SelectObjectValidator' => __DIR__ . '/../..' . '/sources/Form/Validator/SelectObjectValidator.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\AbstractFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/AbstractFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\Base\\ChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/ChoiceFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\Base\\StringFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/StringFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\Base\\FormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/FormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\Base\\TextAreaFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/TextAreaFormBlock.php',
|
||||
'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\\OqlFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/OqlFormBlock.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\FormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlock.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\\IO\\AbstractFormIO' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/AbstractFormIO.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Converter\\AbstractConverter' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/Converter/AbstractConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Converter\\OqlToClassConverter' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/Converter/OqlToClassConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Converter\\StringToAttributeConverter' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/Converter/StringToAttributeConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\FormBinding' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/FormBinding.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\FormInput' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/FormInput.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\FormOutput' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/FormOutput.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Format\\AttributeIOFormat' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/Format/AttributeIOFormat.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Format\\ClassIOFormat' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/Format/ClassIOFormat.php',
|
||||
'Combodo\\iTop\\Forms\\Converter\\AbstractOutputConverter' => __DIR__ . '/../..' . '/sources/Forms/Converter/AbstractOutputConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Converter\\OqlToClassConverter' => __DIR__ . '/../..' . '/sources/Forms/Converter/OqlToClassConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Converter\\StringToAttributeConverter' => __DIR__ . '/../..' . '/sources/Forms/Converter/StringToAttributeConverter.php',
|
||||
'Combodo\\iTop\\Forms\\Block\\IO\\Format\\RawFormat' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/Format/RawFormat.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',
|
||||
@@ -880,9 +882,10 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\FormTypeExtension' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/FormTypeExtension.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormType' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/ResolvedFormType.php',
|
||||
'Combodo\\iTop\\Forms\\FormBuilder\\ResolvedFormTypeFactory' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/ResolvedFormTypeFactory.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\AttributeChoiceType' => __DIR__ . '/../..' . '/sources/Forms/FormType/AttributeChoiceType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\AttributeValueChoiceType' => __DIR__ . '/../..' . '/sources/Forms/FormType/AttributeValueChoiceType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\OqlType' => __DIR__ . '/../..' . '/sources/Forms/FormType/OqlType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\AttributeFormType' => __DIR__ . '/../..' . '/sources/Forms/FormType/AttributeFormType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\AttributeValueFormType' => __DIR__ . '/../..' . '/sources/Forms/FormType/AttributeValueFormType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\ChoiceFormType' => __DIR__ . '/../..' . '/sources/Forms/FormType/ChoiceFormType.php',
|
||||
'Combodo\\iTop\\Forms\\FormType\\OqlFormType' => __DIR__ . '/../..' . '/sources/Forms/FormType/OqlFormType.php',
|
||||
'Combodo\\iTop\\Forms\\Forms' => __DIR__ . '/../..' . '/sources/Forms/Forms.php',
|
||||
'Combodo\\iTop\\Forms\\FormsException' => __DIR__ . '/../..' . '/sources/Forms/FormsException.php',
|
||||
'Combodo\\iTop\\Forms\\Twig\\Extension\\FormCompatibilityExtension' => __DIR__ . '/../..' . '/sources/Forms/Twig/Extension/FormCompatibilityExtension.php',
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Combodo\iTop\Forms\Block;
|
||||
|
||||
use Combodo\iTop\Forms\Block\IO\FormInput;
|
||||
use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
use IssueLog;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
|
||||
/**
|
||||
* Abstract form block.
|
||||
@@ -19,15 +21,6 @@ use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
*/
|
||||
abstract class AbstractFormBlock
|
||||
{
|
||||
/** @var string form block name */
|
||||
private string $sName;
|
||||
|
||||
/** @var array form block options */
|
||||
private array $aOptions = [];
|
||||
|
||||
/** @var array form sub blocks */
|
||||
private array $aSubFormBlocks = [];
|
||||
|
||||
/** @var array form block inputs */
|
||||
private array $aFormInputs = [];
|
||||
|
||||
@@ -37,26 +30,40 @@ abstract class AbstractFormBlock
|
||||
/** @var bool flag */
|
||||
private bool $bIsAdded = false;
|
||||
|
||||
/**
|
||||
* Return the form type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
abstract public function GetFormType(): string;
|
||||
|
||||
/**
|
||||
* Initialize options.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
abstract public function InitOptions(): array;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $sName
|
||||
* @param array $aOptions
|
||||
*/
|
||||
public function __construct(string $sName, array $aOptions = [])
|
||||
public function __construct(private readonly string $sName, private array $aOptions = [])
|
||||
{
|
||||
$this->sName = $sName;
|
||||
$this->aOptions = $aOptions;
|
||||
|
||||
// Attach the form block
|
||||
$this->aOptions['form_block'] = $this;
|
||||
|
||||
$this->InitInputs();
|
||||
$this->InitOutputs();
|
||||
// Compute options
|
||||
$this->aOptions = array_merge($this->aOptions, $this->InitOptions());
|
||||
$this->BuildForm();
|
||||
}
|
||||
|
||||
abstract protected function BuildForm();
|
||||
// Initialize block inputs
|
||||
$this->InitInputs();
|
||||
|
||||
// Initialize block outputs
|
||||
$this->InitOutputs();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the form block name.
|
||||
@@ -91,30 +98,7 @@ abstract class AbstractFormBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a sub form.
|
||||
*
|
||||
* @param AbstractFormBlock $oSubFormBlock
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function AddSubFormBlock(AbstractFormBlock $oSubFormBlock): AbstractFormBlock
|
||||
{
|
||||
$this->aSubFormBlocks[] = $oSubFormBlock;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sub forms.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetSubFormBlocks(): array
|
||||
{
|
||||
return $this->aSubFormBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an input declaration.
|
||||
* Add an input.
|
||||
*
|
||||
* @param FormInput $oFormInput
|
||||
*
|
||||
@@ -127,7 +111,7 @@ abstract class AbstractFormBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an input declaration.
|
||||
* Get an input.
|
||||
*
|
||||
* @param string $sName
|
||||
*
|
||||
@@ -144,7 +128,7 @@ abstract class AbstractFormBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an output declaration.
|
||||
* Add an output.
|
||||
*
|
||||
* @param FormOutput $oFormOutput
|
||||
*
|
||||
@@ -157,7 +141,7 @@ abstract class AbstractFormBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an output declaration.
|
||||
* Get an output.
|
||||
*
|
||||
* @param string $sName
|
||||
*
|
||||
@@ -173,6 +157,21 @@ abstract class AbstractFormBlock
|
||||
return $this->aFormOutputs[$sName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the inputs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetInputs(): array
|
||||
{
|
||||
return $this->aFormInputs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the outputs.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetOutputs(): array
|
||||
{
|
||||
return $this->aFormOutputs;
|
||||
@@ -182,31 +181,66 @@ abstract class AbstractFormBlock
|
||||
* Attach an input to a block output.
|
||||
*
|
||||
* @param string $sInputName
|
||||
* @param FormBlock $oOutputBlock
|
||||
* @param AbstractFormBlock $oOutputBlock
|
||||
* @param string $sOutputName
|
||||
*
|
||||
* @return $this
|
||||
* @throws FormBlockException
|
||||
* @throws FormBlockIOException
|
||||
*/
|
||||
public function DependsOn(string $sInputName, FormBlock $oOutputBlock, string $sOutputName): AbstractFormBlock
|
||||
public function DependsOnBlockOutput(string $sInputName, AbstractFormBlock $oOutputBlock, string $sOutputName): AbstractFormBlock
|
||||
{
|
||||
$oFormInput = $this->GetInput($sInputName);
|
||||
$oFormOutput = $oOutputBlock->GetOutput($sOutputName);
|
||||
$oFormInput->Bind($oFormOutput);
|
||||
$oFormInput->BindFromOutput($oFormOutput);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function DependsOnParent(string $sInputName, FormBlock $oParentBlock, string $sParentInputName): AbstractFormBlock
|
||||
/**
|
||||
* Attach an input to a parent block input.
|
||||
*
|
||||
* @param string $sInputName
|
||||
* @param AbstractFormBlock $oParentBlock
|
||||
* @param string $sParentInputName
|
||||
*
|
||||
* @return $this
|
||||
* @throws FormBlockException
|
||||
* @throws FormBlockIOException
|
||||
*/
|
||||
public function DependsOnParentBlockInput(string $sInputName, AbstractFormBlock $oParentBlock, string $sParentInputName): AbstractFormBlock
|
||||
{
|
||||
$oFormInput = $this->GetInput($sInputName);
|
||||
$oParentFormInput = $oParentBlock->GetInput($sParentInputName);
|
||||
$oFormInput->Bind($oParentFormInput);
|
||||
$oParentFormInput = $oParentBlock->GetInput($sInputName);
|
||||
$oFormInput->BindFromInput($oParentFormInput);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function HasConnections(): bool
|
||||
/**
|
||||
* Attach an output to a parent block outpu.
|
||||
*
|
||||
* @param string $sOutputName
|
||||
* @param AbstractFormBlock $oParentBlock
|
||||
* @param string $sParentInputName
|
||||
*
|
||||
* @return $this
|
||||
* @throws FormBlockException
|
||||
* @throws FormBlockIOException
|
||||
*/
|
||||
public function BindOutputToParentBlockOutput(string $sOutputName, AbstractFormBlock $oParentBlock, string $sParentOutputName): AbstractFormBlock
|
||||
{
|
||||
$oFormOutput = $this->GetOutput($sOutputName);
|
||||
$oParentFormOutput = $oParentBlock->GetOutput($sParentOutputName);
|
||||
$oParentFormOutput->BindFromOutput($oFormOutput);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function HasAtLeastOneBoundInput(): bool
|
||||
{
|
||||
foreach ($this->aFormInputs as $oFormInput) {
|
||||
if ($oFormInput->IsBound()) {
|
||||
@@ -216,6 +250,23 @@ abstract class AbstractFormBlock
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function HasAtLeastOneBoundOutput(): bool
|
||||
{
|
||||
/** @var FormOutput $oFormOutput */
|
||||
foreach ($this->aFormOutputs as $oFormOutput) {
|
||||
if (count($oFormOutput->GetBindings()) > 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function GetInputsBindings(): array
|
||||
{
|
||||
$aBindings = [];
|
||||
@@ -229,11 +280,30 @@ abstract class AbstractFormBlock
|
||||
return $aBindings;
|
||||
}
|
||||
|
||||
public function IsInputsReady(string $sEventType): bool
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function GetOutputBindings(): array
|
||||
{
|
||||
$aBindings = [];
|
||||
|
||||
/** @var FormInput $oFormInput */
|
||||
foreach ($this->aFormOutputs as $oFormOutput) {
|
||||
if ($oFormOutput->IsBound()) {
|
||||
$aBindings[$oFormOutput->GetName()] = $oFormOutput->GetBinding();
|
||||
}
|
||||
}
|
||||
return $aBindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function IsInputsReady(): bool
|
||||
{
|
||||
foreach ($this->aFormInputs as $oFormInput) {
|
||||
if ($oFormInput->IsBound()) {
|
||||
if(!$oFormInput->IsDataReady($sEventType)) {
|
||||
if(!$oFormInput->IsDataReady()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -242,41 +312,84 @@ abstract class AbstractFormBlock
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function IsAdded(): bool
|
||||
{
|
||||
return $this->bIsAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $bIsAdded
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function SetAdded(bool $bIsAdded): void
|
||||
{
|
||||
$this->bIsAdded = $bIsAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the form type.
|
||||
* @param string $sEventType
|
||||
* @param mixed $oData
|
||||
*
|
||||
* @return string
|
||||
* @return void
|
||||
*/
|
||||
abstract public function GetFormType(): string;
|
||||
public function ComputeOutputs(string $sEventType, mixed $oData): void
|
||||
{
|
||||
/** Iterate throw output @var FormOutput $oFormOutput */
|
||||
foreach ($this->aFormOutputs as $oFormOutput) {
|
||||
|
||||
// Compute the output value
|
||||
try{
|
||||
$oFormOutput->ComputeValue($sEventType, $oData);
|
||||
}
|
||||
catch(IOException $oException){
|
||||
IssueLog::Exception(sprintf('Unable to compute values for output %s of block %s', $oFormOutput->GetName(), $this->GetName()), $oException);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate inputs values.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function PropagateInputsValues(): void
|
||||
{
|
||||
foreach ($this->aFormInputs as $oFormInput) {
|
||||
$oFormInput->PropagateBindingsValues();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize inputs.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function InitInputs(): void;
|
||||
public function InitInputs(): void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize outputs.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
abstract public function InitOutputs(): void;
|
||||
public function InitOutputs()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initialize options.
|
||||
*
|
||||
* @return array
|
||||
* @return true
|
||||
*/
|
||||
abstract public function InitOptions(): array;
|
||||
public function AllowAdd(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -6,18 +6,28 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\Base;
|
||||
|
||||
use Combodo\iTop\Forms\Block\FormBlock;
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
|
||||
/**
|
||||
* Form block for choices.
|
||||
*
|
||||
*/
|
||||
class ChoiceFormBlock extends FormBlock
|
||||
class ChoiceFormBlock extends AbstractFormBlock
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
return [
|
||||
// debug purpose
|
||||
'required' => false,
|
||||
'placeholder' => 'Select an element...',
|
||||
];
|
||||
}
|
||||
}
|
||||
82
sources/Forms/Block/Base/FormBlock.php
Normal file
82
sources/Forms/Block/Base/FormBlock.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\Base;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
|
||||
/**
|
||||
* Complex form type.
|
||||
*
|
||||
*/
|
||||
class FormBlock extends AbstractFormBlock
|
||||
{
|
||||
/** @var array form sub blocks */
|
||||
private array $aSubFormBlocks = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $sName
|
||||
* @param array $aOptions
|
||||
*/
|
||||
public function __construct(string $sName, array $aOptions = [])
|
||||
{
|
||||
parent::__construct($sName, $aOptions);
|
||||
|
||||
// Build the form
|
||||
$this->BuildForm();
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return FormType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
return [
|
||||
'compound' => true
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a sub form.
|
||||
*
|
||||
* @param AbstractFormBlock $oSubFormBlock
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function AddSubFormBlock(AbstractFormBlock $oSubFormBlock): AbstractFormBlock
|
||||
{
|
||||
$this->aSubFormBlocks[] = $oSubFormBlock;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the sub forms.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetSubFormBlocks(): array
|
||||
{
|
||||
return $this->aSubFormBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the form.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
29
sources/Forms/Block/Base/TextAreaFormBlock.php
Normal file
29
sources/Forms/Block/Base/TextAreaFormBlock.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\Base;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
|
||||
/**
|
||||
* Form block for text area.
|
||||
*
|
||||
*/
|
||||
class TextAreaFormBlock extends AbstractFormBlock
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return TextareaType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -6,18 +6,24 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\Base;
|
||||
|
||||
use Combodo\iTop\Forms\Block\FormBlock;
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
|
||||
/**
|
||||
* Form block for string.
|
||||
*
|
||||
*/
|
||||
class StringFormBlock extends FormBlock
|
||||
class TextFormBlock extends AbstractFormBlock
|
||||
{
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,15 @@
|
||||
namespace Combodo\iTop\Forms\Block\DataModel;
|
||||
|
||||
use Combodo\iTop\Forms\Block\Base\ChoiceFormBlock;
|
||||
use Combodo\iTop\Forms\Block\FormBlockException;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\AttributeIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\ClassIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\FormInput;
|
||||
use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
use Combodo\iTop\Forms\Converter\StringToAttributeConverter;
|
||||
use Combodo\iTop\Forms\FormType\AttributeChoiceType;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\StringToAttributeConverter;
|
||||
use Combodo\iTop\Forms\FormType\AttributeFormType;
|
||||
use CoreException;
|
||||
use MetaModel;
|
||||
|
||||
/**
|
||||
* Form block for choice of class attributes.
|
||||
@@ -27,6 +30,14 @@ class AttributeChoiceFormBlock extends ChoiceFormBlock
|
||||
// outputs
|
||||
public const OUTPUT_ATTRIBUTE = 'attribute';
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
$aOptions = parent::InitOptions();
|
||||
$aOptions['placeholder'] = 'Select an attribute...';
|
||||
return $aOptions;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitInputs(): void
|
||||
{
|
||||
@@ -41,15 +52,27 @@ class AttributeChoiceFormBlock extends ChoiceFormBlock
|
||||
$this->AddOutput(new FormOutput(self::OUTPUT_ATTRIBUTE, AttributeIOFormat::class, new StringToAttributeConverter()));
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
/** @inheritdoc
|
||||
* @throws FormBlockException
|
||||
*/
|
||||
public function AllowAdd(): bool
|
||||
{
|
||||
return $this->GetInput(self::INPUT_CLASS_NAME)->Value() != '';
|
||||
}
|
||||
|
||||
/** @inheritdoc
|
||||
* @throws FormBlockException
|
||||
* @throws CoreException
|
||||
*/
|
||||
public function UpdateOptions(): array
|
||||
{
|
||||
$aOptions = parent::GetOptions();
|
||||
|
||||
$oBinding = $this->GetInput(self::INPUT_CLASS_NAME)->GetBinding();
|
||||
$oConnectionValue = $oBinding->oSourceIO->Value();
|
||||
$oValue = $this->GetInput(self::INPUT_CLASS_NAME)->Value();
|
||||
if($oValue == '')
|
||||
return $aOptions;
|
||||
|
||||
$aAttributeCodes = \MetaModel::GetAttributesList($oConnectionValue);
|
||||
$aAttributeCodes = MetaModel::GetAttributesList($oValue);
|
||||
$aAttributeCodes = array_combine($aAttributeCodes, $aAttributeCodes) ;
|
||||
$aOptions['choices'] = $aAttributeCodes;
|
||||
|
||||
@@ -59,6 +82,6 @@ class AttributeChoiceFormBlock extends ChoiceFormBlock
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return AttributeChoiceType::class;
|
||||
return AttributeFormType::class;
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,16 @@
|
||||
namespace Combodo\iTop\Forms\Block\DataModel;
|
||||
|
||||
use Combodo\iTop\Forms\Block\Base\ChoiceFormBlock;
|
||||
use Combodo\iTop\Forms\Block\FormBlockException;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\StringToAttributeConverter;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\AttributeIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\ClassIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\RawFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\FormInput;
|
||||
use Combodo\iTop\Forms\FormType\AttributeValueChoiceType;
|
||||
use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
use Combodo\iTop\Forms\FormType\AttributeValueFormType;
|
||||
use Exception;
|
||||
use MetaModel;
|
||||
|
||||
/**
|
||||
* Form block for choice of class attribute values.
|
||||
@@ -23,13 +29,15 @@ class AttributeValueChoiceFormBlock extends ChoiceFormBlock
|
||||
public const INPUT_CLASS_NAME = 'class_name';
|
||||
public const INPUT_ATTRIBUTE = 'attribute';
|
||||
|
||||
// Outputs
|
||||
public const OUTPUT_VALUE = 'value';
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(array &$aOptions = []): array
|
||||
{
|
||||
$aOptions['multiple'] = true;
|
||||
$aOptions['required'] = false;
|
||||
$aOptions['attr'] = [
|
||||
'size' => 10,
|
||||
'size' => 5,
|
||||
'style' => 'height: auto;'
|
||||
];
|
||||
|
||||
@@ -44,23 +52,52 @@ class AttributeValueChoiceFormBlock extends ChoiceFormBlock
|
||||
$this->AddInput(new FormInput(self::INPUT_ATTRIBUTE, AttributeIOFormat::class));
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
parent::InitOutputs();
|
||||
$this->AddOutput(new FormOutput(self::OUTPUT_VALUE, RawFormat::class));
|
||||
}
|
||||
|
||||
/** @inheritdoc
|
||||
* @throws FormBlockException
|
||||
* @throws Exception
|
||||
*/
|
||||
public function AllowAdd(): bool
|
||||
{
|
||||
$bDependentOk = $this->GetInput(self::INPUT_CLASS_NAME)->Value() != '' && $this->GetInput(self::INPUT_ATTRIBUTE)->Value() != '';
|
||||
|
||||
if($bDependentOk) {
|
||||
$oAttDef = MetaModel::GetAttributeDef($this->GetInput(self::INPUT_CLASS_NAME)->Value()->__toString(), $this->GetInput(self::INPUT_ATTRIBUTE)->Value()->__toString());
|
||||
$aValues = $oAttDef->GetAllowedValues();
|
||||
return $aValues != null;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** @inheritdoc
|
||||
* @throws Exception
|
||||
*/
|
||||
public function UpdateOptions(): array
|
||||
{
|
||||
$aOptions = parent::GetOptions();
|
||||
|
||||
$oBindingClassName = $this->GetInput(self::INPUT_CLASS_NAME)->GetBinding();
|
||||
if($oBindingClassName->oSourceIO->Value() === null || $oBindingClassName->oSourceIO->Value() == "")
|
||||
$oClassName = $this->GetInput(self::INPUT_CLASS_NAME)->Value();
|
||||
if($oClassName == '')
|
||||
return $aOptions;
|
||||
$oClassName = $oBindingClassName->oSourceIO->Value();
|
||||
|
||||
$oBindingAttribute = $this->GetInput(self::INPUT_ATTRIBUTE)->GetBinding();
|
||||
if($oBindingAttribute->oSourceIO->Value() === null || $oBindingAttribute->oSourceIO->Value() == "")
|
||||
$oAttribute = $this->GetInput(self::INPUT_ATTRIBUTE)->Value();
|
||||
if($oAttribute == '')
|
||||
return $aOptions;
|
||||
$oAttribute = $oBindingAttribute->oSourceIO->Value();
|
||||
|
||||
$oAttDef = \MetaModel::GetAttributeDef(strval($oClassName), strval($oAttribute));
|
||||
$oAttDef = MetaModel::GetAttributeDef(strval($oClassName), strval($oAttribute));
|
||||
$aValues = $oAttDef->GetAllowedValues();
|
||||
|
||||
if($aValues === null)
|
||||
return $aOptions;
|
||||
|
||||
$aOptions['choices'] = array_flip($aValues);
|
||||
|
||||
return $aOptions;
|
||||
@@ -69,7 +106,7 @@ class AttributeValueChoiceFormBlock extends ChoiceFormBlock
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return AttributeValueChoiceType::class;
|
||||
return AttributeValueFormType::class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,21 +6,28 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\DataModel;
|
||||
|
||||
use Combodo\iTop\Forms\Block\Base\StringFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\TextAreaFormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\ClassIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
use Combodo\iTop\Forms\Converter\OqlToClassConverter;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\OqlToClassConverter;
|
||||
use Combodo\iTop\Forms\FormType\OqlFormType;
|
||||
|
||||
/**
|
||||
* Form block for oql expression.
|
||||
*
|
||||
* @package DataModel
|
||||
*/
|
||||
class OqlFormBlock extends StringFormBlock
|
||||
class OqlFormBlock extends TextAreaFormBlock
|
||||
{
|
||||
// outputs
|
||||
public const OUTPUT_SELECTED_CLASS = 'selected_class';
|
||||
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return OqlFormType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
@@ -28,4 +35,14 @@ class OqlFormBlock extends StringFormBlock
|
||||
$this->AddOutput(new FormOutput(self::OUTPUT_SELECTED_CLASS, ClassIOFormat::class, new OqlToClassConverter()));
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
$aOptions = parent::InitOptions();
|
||||
$aOptions['with_ai_button'] = true;
|
||||
return $aOptions;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Block;
|
||||
|
||||
use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
|
||||
/**
|
||||
* Complex form type.
|
||||
*
|
||||
*/
|
||||
class FormBlock extends AbstractFormBlock
|
||||
{
|
||||
public const OUTPUT_VALUE = 'value';
|
||||
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return FormType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitInputs(): void
|
||||
{
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
$this->AddOutput(new FormOutput(self::OUTPUT_VALUE, 'string'));
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOptions(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
protected function BuildForm(): void
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -7,48 +7,168 @@
|
||||
namespace Combodo\iTop\Forms\Block\IO;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
class AbstractFormIO
|
||||
{
|
||||
|
||||
/** @var AbstractFormBlock The owner block */
|
||||
private AbstractFormBlock $oOwnerBlock;
|
||||
|
||||
/** @var string Name of the IO */
|
||||
private string $sName;
|
||||
|
||||
/** @var string Type of the IO data */
|
||||
private string $sType;
|
||||
|
||||
/** @var array Stored values */
|
||||
private array $aValues = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $sName
|
||||
* @param string $sType
|
||||
*/
|
||||
public function __construct(string $sName, string $sType)
|
||||
{
|
||||
$this->sName = $sName;
|
||||
$this->sType = $sType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IO name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetName(): string
|
||||
{
|
||||
return $this->sName;
|
||||
}
|
||||
|
||||
public function SetName(string $sName): void
|
||||
/**
|
||||
* Set the IO name.
|
||||
*
|
||||
* @param string $sName
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function SetName(string $sName): self
|
||||
{
|
||||
$this->sName = $sName;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function GetType(): string
|
||||
/**
|
||||
* Get the IO data type.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function GetDataType(): string
|
||||
{
|
||||
return $this->sType;
|
||||
}
|
||||
|
||||
public function SetType(string $sType): void
|
||||
{
|
||||
$this->sType = $sType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the owner block.
|
||||
*
|
||||
* @return AbstractFormBlock
|
||||
*/
|
||||
public function GetOwnerBlock(): AbstractFormBlock
|
||||
{
|
||||
return $this->oOwnerBlock;
|
||||
}
|
||||
|
||||
public function SetOwnerBlock(AbstractFormBlock $oOwnerBlock): void
|
||||
/**
|
||||
* Set the owner block.
|
||||
*
|
||||
* @param AbstractFormBlock $oOwnerBlock
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function SetOwnerBlock(AbstractFormBlock $oOwnerBlock): self
|
||||
{
|
||||
$this->oOwnerBlock = $oOwnerBlock;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IO value.
|
||||
*
|
||||
* @param string $sEventType
|
||||
* @param mixed $oValue
|
||||
*
|
||||
* @return self
|
||||
*/
|
||||
public function SetValue(string $sEventType, mixed $oValue): self
|
||||
{
|
||||
$this->aValues[$sEventType] = $oValue;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the IO value.
|
||||
*
|
||||
* @param string $sEventType
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function GetValue(string $sEventType): mixed
|
||||
{
|
||||
return $this->aValues[$sEventType] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if value exist.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function HasValue(): bool
|
||||
{
|
||||
$PostSetDataExist = array_key_exists(FormEvents::POST_SET_DATA, $this->aValues) && $this->aValues[FormEvents::POST_SET_DATA] !== null;
|
||||
$PostSubmitExist = array_key_exists(FormEvents::POST_SUBMIT, $this->aValues) && $this->aValues[FormEvents::POST_SUBMIT] !== null;
|
||||
return $PostSetDataExist || $PostSubmitExist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all values.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetValues(): array
|
||||
{
|
||||
return $this->aValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IO values.
|
||||
*
|
||||
* @param array $aValues
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function SetValues(array $aValues): self
|
||||
{
|
||||
$this->aValues = $aValues;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the most relevant value.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function Value(): mixed
|
||||
{
|
||||
if(array_key_exists(FormEvents::POST_SUBMIT, $this->aValues) ){
|
||||
return $this->aValues[FormEvents::POST_SUBMIT];
|
||||
}
|
||||
if(array_key_exists(FormEvents::POST_SET_DATA, $this->aValues) ){
|
||||
return $this->aValues[FormEvents::POST_SET_DATA];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -4,12 +4,12 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Converter;
|
||||
namespace Combodo\iTop\Forms\Block\IO\Converter;
|
||||
|
||||
/**
|
||||
* Output converter.
|
||||
*/
|
||||
abstract class AbstractOutputConverter
|
||||
abstract class AbstractConverter
|
||||
{
|
||||
/**
|
||||
* Convert the date to output format.
|
||||
41
sources/Forms/Block/IO/Converter/OqlToClassConverter.php
Normal file
41
sources/Forms/Block/IO/Converter/OqlToClassConverter.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\IO\Converter;
|
||||
|
||||
use Combodo\iTop\Forms\Block\IO\Format\ClassIOFormat;
|
||||
use MetaModel;
|
||||
use Symfony\Component\Filesystem\Exception\IOException;
|
||||
|
||||
/**
|
||||
* OQL expression to class converter.
|
||||
*/
|
||||
class OqlToClassConverter extends AbstractConverter
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function Convert(mixed $oData): ?ClassIOFormat
|
||||
{
|
||||
if($oData === null)
|
||||
return null;
|
||||
|
||||
// Extract OQL information
|
||||
preg_match('/SELECT\s+(\w+)/', $oData, $aMatches);
|
||||
|
||||
// Selected class
|
||||
if(isset($aMatches[1])){
|
||||
$sSelectedClass = $aMatches[1];
|
||||
if(!MetaModel::IsValidClass($sSelectedClass)){
|
||||
throw new IOException('Incorrect OQL select class name ' . $sSelectedClass);
|
||||
}
|
||||
return new ClassIOFormat($aMatches[1]);
|
||||
}
|
||||
else{
|
||||
throw new IOException('Incorrect OQL sentence');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -4,15 +4,14 @@
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Converter;
|
||||
namespace Combodo\iTop\Forms\Block\IO\Converter;
|
||||
|
||||
use Combodo\iTop\Forms\Block\IO\Format\AttributeIOFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\ClassIOFormat;
|
||||
|
||||
/**
|
||||
* String to attribute converter.
|
||||
*/
|
||||
class StringToAttributeConverter extends AbstractOutputConverter
|
||||
class StringToAttributeConverter extends AbstractConverter
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function Convert(mixed $oData): ?AttributeIOFormat
|
||||
@@ -6,11 +6,15 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\IO;
|
||||
|
||||
|
||||
class FormBinding
|
||||
{
|
||||
public function __construct(public readonly FormInput $oDestinationIO, public readonly AbstractFormIO $oSourceIO)
|
||||
public function __construct(public readonly AbstractFormIO $oSourceIO, public readonly AbstractFormIO $oDestinationIO)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function PropagateValues(): void
|
||||
{
|
||||
$this->oDestinationIO->SetValues($this->oSourceIO->GetValues());
|
||||
}
|
||||
}
|
||||
@@ -10,27 +10,59 @@ use Combodo\iTop\Forms\Block\FormBlockIOException;
|
||||
|
||||
class FormInput extends AbstractFormIO
|
||||
{
|
||||
|
||||
private FormBinding|null $oBinding = null;
|
||||
|
||||
private array $aBindingsToInputs = [];
|
||||
|
||||
public function Bind(AbstractFormIO $oSourceIO): void
|
||||
/**
|
||||
* @throws FormBlockIOException
|
||||
*/
|
||||
public function BindFromOutput(FormOutput $oSourceIO): void
|
||||
{
|
||||
if($this->GetType() !== $oSourceIO->GetType()){
|
||||
if($this->GetDataType() !== $oSourceIO->GetDataType()){
|
||||
throw new FormBlockIOException('Cannot connect input types incompatibles ' . $this->GetName() . ' from ' . $oSourceIO->GetOwnerBlock()->GetName() . ' ' . $oSourceIO->GetName());
|
||||
}
|
||||
|
||||
$this->oBinding = new FormBinding($this, $oSourceIO);
|
||||
$this->oBinding = $oSourceIO->BindToInput($this);
|
||||
}
|
||||
|
||||
public function GetBinding(): FormBinding
|
||||
/**
|
||||
* @throws FormBlockIOException
|
||||
*/
|
||||
public function BindFromInput(FormInput $oSourceIO): void
|
||||
{
|
||||
if($this->GetDataType() !== $oSourceIO->GetDataType()){
|
||||
throw new FormBlockIOException('Cannot connect input types incompatibles ' . $this->GetName() . ' from ' . $oSourceIO->GetOwnerBlock()->GetName() . ' ' . $oSourceIO->GetName());
|
||||
}
|
||||
|
||||
$this->oBinding = $oSourceIO->BindToInput($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FormBlockIOException
|
||||
*/
|
||||
public function BindToInput(FormInput $oDestinationIO): FormBinding
|
||||
{
|
||||
if($this->GetDataType() !== $oDestinationIO->GetDataType()){
|
||||
throw new FormBlockIOException('Cannot connect input types incompatibles ' . $this->GetName() . ' from ' . $oDestinationIO->GetOwnerBlock()->GetName() . ' ' . $oDestinationIO->GetName());
|
||||
}
|
||||
|
||||
$oBinding = new FormBinding($this, $oDestinationIO);
|
||||
|
||||
$this->aBindingsToInputs[] = $oBinding;
|
||||
|
||||
return $oBinding;
|
||||
}
|
||||
|
||||
|
||||
public function GetBinding(): ?FormBinding
|
||||
{
|
||||
return $this->oBinding;
|
||||
}
|
||||
|
||||
public function IsDataReady(string $sEventType): bool
|
||||
public function IsDataReady(): bool
|
||||
{
|
||||
return $this->oBinding->oSourceIO->HasValue($sEventType);
|
||||
return $this->HasValue();
|
||||
}
|
||||
|
||||
public function IsBound(): bool
|
||||
@@ -38,4 +70,25 @@ class FormInput extends AbstractFormIO
|
||||
return $this->oBinding !== null;
|
||||
}
|
||||
|
||||
public function SetValues(array $aValues): AbstractFormIO
|
||||
{
|
||||
parent::SetValues($aValues);
|
||||
|
||||
$this->PropagateBindingsValues();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate the bindings values.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function PropagateBindingsValues(): void
|
||||
{
|
||||
// propagate the value
|
||||
foreach ($this->aBindingsToInputs as $oBinding) {
|
||||
$oBinding->PropagateValues();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,20 +6,42 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\IO;
|
||||
|
||||
use Combodo\iTop\Forms\Converter\AbstractOutputConverter;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Combodo\iTop\Forms\Block\FormBlockIOException;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\AbstractConverter;
|
||||
|
||||
class FormOutput extends AbstractFormIO
|
||||
{
|
||||
private null|AbstractOutputConverter $oConverter;
|
||||
private array $aValues = [];
|
||||
/** @var AbstractConverter|null */
|
||||
private null|AbstractConverter $oConverter;
|
||||
|
||||
public function __construct(string $sName, string $sType, AbstractOutputConverter $oConverter = null)
|
||||
private FormBinding|null $oBinding = null;
|
||||
|
||||
/** @var array */
|
||||
private array $aBindingsToInputs = [];
|
||||
|
||||
/** @var array */
|
||||
private array $aBindingsToOutputs = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $sName
|
||||
* @param string $sType
|
||||
* @param AbstractConverter|null $oConverter
|
||||
*/
|
||||
public function __construct(string $sName, string $sType, AbstractConverter $oConverter = null)
|
||||
{
|
||||
parent::__construct($sName, $sType);
|
||||
$this->oConverter = $oConverter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the value.
|
||||
*
|
||||
* @param mixed $oData
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function ConvertValue(mixed $oData): mixed
|
||||
{
|
||||
if (is_null($this->oConverter)) {
|
||||
@@ -28,39 +50,101 @@ class FormOutput extends AbstractFormIO
|
||||
return $this->oConverter->Convert($oData);
|
||||
}
|
||||
|
||||
public function UpdateOutputValue(mixed $oData, string $sEventType): void
|
||||
/**
|
||||
* Compute the value.
|
||||
*
|
||||
* @param string $sEventType
|
||||
* @param mixed $oData
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ComputeValue(string $sEventType, mixed $oData): void
|
||||
{
|
||||
$this->aValues[$sEventType] = $this->ConvertValue($oData);
|
||||
$this->SetValue($sEventType, $this->ConvertValue($oData));
|
||||
|
||||
// propagate the bindings values
|
||||
$this->PropagateBindingsValues();
|
||||
}
|
||||
|
||||
public function GetValue(string $sEventType): mixed
|
||||
/**
|
||||
* Propagate the bindings values.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function PropagateBindingsValues(): void
|
||||
{
|
||||
return $this->aValues[$sEventType] ?? null;
|
||||
}
|
||||
|
||||
public function Value(): mixed
|
||||
{
|
||||
if(array_key_exists(FormEvents::POST_SUBMIT, $this->aValues) ){
|
||||
return $this->aValues[FormEvents::POST_SUBMIT];
|
||||
}
|
||||
if(array_key_exists(FormEvents::POST_SET_DATA, $this->aValues) ){
|
||||
return $this->aValues[FormEvents::POST_SET_DATA];
|
||||
// propagate the value
|
||||
foreach ($this->aBindingsToInputs as $oBinding) {
|
||||
$oBinding->PropagateValues();
|
||||
}
|
||||
|
||||
// propagate the value
|
||||
foreach ($this->aBindingsToOutputs as $oBinding) {
|
||||
$oBinding->PropagateValues();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public function HasValue(string $sEventType): bool
|
||||
/**
|
||||
* Bind to input.
|
||||
*
|
||||
* @param FormInput $oDestinationIO
|
||||
*
|
||||
* @return FormBinding
|
||||
*/
|
||||
public function BindToInput(FormInput $oDestinationIO): FormBinding
|
||||
{
|
||||
return array_key_exists($sEventType, $this->aValues) && $this->aValues[$sEventType] !== null;
|
||||
$oBinding = new FormBinding($this, $oDestinationIO);
|
||||
|
||||
$this->aBindingsToInputs[] = $oBinding;
|
||||
|
||||
return $oBinding;
|
||||
}
|
||||
|
||||
public function HasValues(): bool
|
||||
/**
|
||||
* Bind to output.
|
||||
*
|
||||
* @param FormOutput $oDestinationIO
|
||||
*
|
||||
* @return FormBinding
|
||||
*/
|
||||
public function BindToOutput(FormOutput $oDestinationIO): FormBinding
|
||||
{
|
||||
return count($this->aValues) > 0;
|
||||
$oBinding = new FormBinding($this, $oDestinationIO);
|
||||
|
||||
$this->aBindingsToOutputs[] = $oBinding;
|
||||
|
||||
return $oBinding;
|
||||
}
|
||||
|
||||
public function GetValues(): array
|
||||
public function GetBinding(): ?FormBinding
|
||||
{
|
||||
return $this->aValues;
|
||||
return $this->oBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws FormBlockIOException
|
||||
*/
|
||||
public function BindFromOutput(FormOutput $oSourceIO): void
|
||||
{
|
||||
if($this->GetDataType() !== $oSourceIO->GetDataType()){
|
||||
throw new FormBlockIOException('Cannot connect input types incompatibles ' . $this->GetName() . ' from ' . $oSourceIO->GetOwnerBlock()->GetName() . ' ' . $oSourceIO->GetName());
|
||||
}
|
||||
|
||||
$this->oBinding = $oSourceIO->BindToOutput($this);
|
||||
}
|
||||
|
||||
public function IsBound(): bool
|
||||
{
|
||||
return $this->oBinding !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bindings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetBindings(): array
|
||||
{
|
||||
return $this->aBindingsToInputs;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\IO\Format;
|
||||
|
||||
class AttributeIOFormat
|
||||
use JsonSerializable;
|
||||
|
||||
class AttributeIOFormat implements JsonSerializable
|
||||
{
|
||||
public function __construct(public string $sAttributeName)
|
||||
{
|
||||
@@ -13,4 +15,9 @@ class AttributeIOFormat
|
||||
{
|
||||
return $this->sAttributeName;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return $this->sAttributeName;
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\IO\Format;
|
||||
|
||||
class ClassIOFormat
|
||||
use JsonSerializable;
|
||||
|
||||
class ClassIOFormat implements JsonSerializable
|
||||
{
|
||||
public function __construct(public string $sClassName)
|
||||
{
|
||||
@@ -13,4 +15,9 @@ class ClassIOFormat
|
||||
{
|
||||
return $this->sClassName;
|
||||
}
|
||||
|
||||
public function jsonSerialize(): mixed
|
||||
{
|
||||
return $this->sClassName;
|
||||
}
|
||||
}
|
||||
16
sources/Forms/Block/IO/Format/RawFormat.php
Normal file
16
sources/Forms/Block/IO/Format/RawFormat.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\IO\Format;
|
||||
|
||||
class RawFormat
|
||||
{
|
||||
public function __construct(public string $oValue)
|
||||
{
|
||||
// validation du format sinon exception
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return strval($this->oValue);
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Converter;
|
||||
|
||||
use Combodo\iTop\Forms\Block\IO\Format\ClassIOFormat;
|
||||
|
||||
/**
|
||||
* OQL expression to class converter.
|
||||
*/
|
||||
class OqlToClassConverter extends AbstractOutputConverter
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function Convert(mixed $oData): ?ClassIOFormat
|
||||
{
|
||||
if($oData === null)
|
||||
return null;
|
||||
// extract selected class
|
||||
preg_match('/SELECT\s+(\w+)/', $oData, $aMatches);
|
||||
return new ClassIOFormat($aMatches[1]) ?? null;
|
||||
}
|
||||
}
|
||||
@@ -6,8 +6,13 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\FormBlock;
|
||||
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
/**
|
||||
* Dependencies handler.
|
||||
@@ -15,28 +20,45 @@ use Symfony\Component\Form\FormEvents;
|
||||
*/
|
||||
class DependencyHandler
|
||||
{
|
||||
/** @var FormBuilder form builder */
|
||||
private FormBuilder $oFormBuilder;
|
||||
|
||||
/** @var array dependant blocks */
|
||||
private array $aDependentBlocks;
|
||||
public static array $aDependencyHandlers = [];
|
||||
|
||||
/** @var DependencyMap dependencies map */
|
||||
private DependencyMap $aDependenciesMap;
|
||||
private DependencyMap $oDependenciesMap;
|
||||
|
||||
/** @var array Debug data */
|
||||
private array $aDebugData = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param FormBuilder $oFormBuilder
|
||||
* @param array $aDependentBlocks
|
||||
* @param string $sName The name
|
||||
* @param AbstractFormBlock $oFormBlock The attached form block
|
||||
* @param FormBuilder $oFormBuilder The form builder
|
||||
* @param array $aSubBlocks Sub blocks
|
||||
* @param array $aDependentBlocks Dependants blocks
|
||||
*/
|
||||
public function __construct(FormBuilder $oFormBuilder, array $aDependentBlocks)
|
||||
public function __construct(private readonly string $sName, private readonly AbstractFormBlock $oFormBlock, private readonly FormBuilder $oFormBuilder, private readonly array $aSubBlocks, private readonly array $aDependentBlocks)
|
||||
{
|
||||
$this->aDependentBlocks = $aDependentBlocks;
|
||||
$this->oFormBuilder = $oFormBuilder;
|
||||
// dependencies map
|
||||
$this->oDependenciesMap = new DependencyMap($this->aDependentBlocks);
|
||||
|
||||
// add form ready listener
|
||||
// Add form ready listener
|
||||
$this->AddFormReadyListener();
|
||||
|
||||
// Check the dependencies
|
||||
$this->CheckDependencies($this->oFormBuilder);
|
||||
|
||||
self::$aDependencyHandlers[] = $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the form block.
|
||||
*
|
||||
* @return AbstractFormBlock
|
||||
*/
|
||||
public function GetFormBlock(): AbstractFormBlock
|
||||
{
|
||||
return $this->oFormBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,11 +74,20 @@ class DependencyHandler
|
||||
// Initialize the dependencies listeners once the form is built
|
||||
$this->oFormBuilder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
|
||||
|
||||
// dependencies map
|
||||
$this->aDependenciesMap = new DependencyMap($this->aDependentBlocks);
|
||||
|
||||
/** Iterate throw listened blocks */
|
||||
foreach ($this->aDependenciesMap->GetListenedBlockNames() as $sOutputBlockName) {
|
||||
foreach ($this->oDependenciesMap->GetListenedOutputBlockNames() as $sOutputBlockName) {
|
||||
|
||||
// inner binding
|
||||
if($sOutputBlockName === $this->oFormBlock->getName()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => 'form.listen',
|
||||
'form' => $sOutputBlockName,
|
||||
'value' => null
|
||||
];
|
||||
|
||||
// Listen the output block POST_SET_DATA & POST_SUBMIT
|
||||
$this->oFormBuilder->get($sOutputBlockName)->addEventListener(FormEvents::POST_SET_DATA, $this->GetEventListeningCallback());
|
||||
@@ -65,7 +96,6 @@ class DependencyHandler
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the listening callback.
|
||||
*
|
||||
@@ -78,52 +108,108 @@ 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()
|
||||
];
|
||||
|
||||
// Get the form
|
||||
$oForm = $oEvent->getForm();
|
||||
|
||||
/** Iterate throw dependencies map... */
|
||||
$sOutputBlockName = $oForm->getName();
|
||||
if($this->aDependenciesMap->IsBlockHasOutputs($sOutputBlockName)){
|
||||
$oOutputBlock = $this->oFormBuilder->GetFormBlock($sOutputBlockName);
|
||||
foreach ($this->aDependenciesMap->GetOutputsForBlock($sOutputBlockName) as $sOutputName) {
|
||||
$oOutput = $oOutputBlock->GetOutput($sOutputName);
|
||||
$oOutput->UpdateOutputValue($oEvent->getData(), $sEventType);
|
||||
}
|
||||
// Get the form block
|
||||
$oFormBlock = $this->aSubBlocks[$oForm->getName()];
|
||||
|
||||
// Compute the block outputs with the data
|
||||
if(!$oFormBlock instanceof FormBlock) {
|
||||
$oFormBlock->ComputeOutputs($sEventType, $oEvent->getData());
|
||||
}
|
||||
|
||||
|
||||
|
||||
foreach ($this->aDependentBlocks as $oDependentBlock)
|
||||
{
|
||||
|
||||
// When dependencies met, add the dependent field if not already done
|
||||
if(!$oDependentBlock->IsAdded() && $oDependentBlock->IsInputsReady($sEventType)) {
|
||||
|
||||
// Get the dependent field options
|
||||
$aOptions = $oDependentBlock->UpdateOptions();
|
||||
|
||||
// Add the listener callback to the dependent field if it is also a dependency for another field
|
||||
if($this->aDependenciesMap->IsTheBlockInDependencies($oDependentBlock->getName())) {
|
||||
$aOptions = array_merge($aOptions, [
|
||||
'listener_callback' => $this->GetEventListeningCallback(),
|
||||
]);
|
||||
}
|
||||
|
||||
// Mark the dependency as added
|
||||
$oDependentBlock->SetAdded(true);
|
||||
|
||||
// Add the dependent field to the form
|
||||
$oForm->getParent()->add($oDependentBlock->GetName(), $oDependentBlock->GetFormType(), $aOptions);
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Check dependencies
|
||||
$this->CheckDependencies($oForm->getParent());
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param FormInterface|FormBuilderInterface $oForm
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function CheckDependencies(FormInterface|FormBuilderInterface $oForm): void
|
||||
{
|
||||
/** Iterate throw dependencies... @var AbstractFormBlock $oDependentBlock */
|
||||
foreach ($this->aDependentBlocks as $qBlockName => $oDependentBlock)
|
||||
{
|
||||
// When dependencies met, add the dependent field if not already done
|
||||
if(!$oDependentBlock->IsAdded() && $oDependentBlock->IsInputsReady()) {
|
||||
|
||||
// Get the dependent field options
|
||||
$aOptions = $oDependentBlock->UpdateOptions();
|
||||
|
||||
// Add the listener callback to the dependent field if it is also a dependency for another field
|
||||
if($this->oDependenciesMap->IsTheBlockInDependencies($oDependentBlock->getName())) {
|
||||
|
||||
// Pass the listener call back to be registered by the dependency form builder
|
||||
$aOptions = array_merge($aOptions, [
|
||||
'builder_listener' => $this->GetEventListeningCallback(),
|
||||
]);
|
||||
}
|
||||
|
||||
if($oDependentBlock->AllowAdd()) {
|
||||
|
||||
$this->aDebugData[] = [
|
||||
'builder' => $this->oFormBuilder->getName(),
|
||||
'event' => 'form.add',
|
||||
'form' => $oDependentBlock->getName(),
|
||||
'value' => null
|
||||
];
|
||||
|
||||
// Mark the dependency as added
|
||||
$oDependentBlock->SetAdded(true);
|
||||
|
||||
// Add the dependent field to the form
|
||||
$oForm->add($oDependentBlock->GetName(), $oDependentBlock->GetFormType(), $aOptions);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if($oDependentBlock->IsAdded() && !$oDependentBlock->IsInputsReady()) {
|
||||
$oForm->add($oDependentBlock->GetName(), HiddenType::class, [
|
||||
'form_block' => $oDependentBlock,
|
||||
'prevent_form_build' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the debug data.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetDebugData(): array
|
||||
{
|
||||
return $this->aDebugData;
|
||||
}
|
||||
|
||||
public function GetMap(): DependencyMap
|
||||
{
|
||||
return $this->oDependenciesMap;
|
||||
}
|
||||
|
||||
public function GetSubBlocks(): array
|
||||
{
|
||||
return $this->aSubBlocks;
|
||||
}
|
||||
|
||||
public function GetName(): string
|
||||
{
|
||||
return $this->sName;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@ namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\Block\FormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\FormBinding;
|
||||
use Combodo\iTop\Forms\Block\IO\FormInput;
|
||||
use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
|
||||
/**
|
||||
* Dependencies handler.
|
||||
@@ -15,8 +17,14 @@ use Combodo\iTop\Forms\Block\IO\FormBinding;
|
||||
*/
|
||||
class DependencyMap
|
||||
{
|
||||
/** @var array dependencies map */
|
||||
private array $aDependenciesMap;
|
||||
/** @var array output to outputs map map */
|
||||
private array $aOutputToInputsMap = [];
|
||||
|
||||
/** @var array input to inputs map map */
|
||||
private array $aInputToInputsMap = [];
|
||||
|
||||
/** @var array output to outputs */
|
||||
private array $aOutputToOutputsMap = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@@ -36,38 +44,85 @@ class DependencyMap
|
||||
*/
|
||||
private function Init(): void
|
||||
{
|
||||
/** iterate throw dependents blocks... @var FormBlock $oDependentBlock */
|
||||
foreach ($this->aDependentBlocks as $oDependentBlock) {
|
||||
/** Iterate throw blocks with dependencies... @var FormBlock $oDependentBlock */
|
||||
foreach ($this->aDependentBlocks as $sBlockName => $oDependentBlock) {
|
||||
|
||||
/** iterate throw the block inputs connections... @var FormBinding $oBinding**/
|
||||
foreach ($oDependentBlock->GetInputsBindings() as $sInputName => $oBinding) {
|
||||
/** Iterate throw the block inputs connections... @var FormBinding $oBinding**/
|
||||
foreach ($oDependentBlock->GetInputsBindings() as $oBinding) {
|
||||
|
||||
// connection information
|
||||
$sOutputBlockName = $oBinding->oSourceIO->GetOwnerBlock()->GetName();
|
||||
$sOutputName = $oBinding->oSourceIO->GetName();
|
||||
|
||||
// initialize map
|
||||
if (!isset($this->aDependenciesMap[$sOutputBlockName])) {
|
||||
$this->aDependenciesMap[$sOutputBlockName] = [];
|
||||
// Output to inputs map
|
||||
if($oBinding->oSourceIO instanceof FormOutput
|
||||
&& $oBinding->oDestinationIO instanceof FormInput){
|
||||
$this->AddBindingToMap($this->aOutputToInputsMap, $oBinding);
|
||||
}
|
||||
if (!isset($this->aDependenciesMap[$sOutputBlockName][$sOutputName])) {
|
||||
$this->aDependenciesMap[$sOutputBlockName][$sOutputName] = [];
|
||||
// Input to inputs map
|
||||
if($oBinding->oSourceIO instanceof FormInput
|
||||
&& $oBinding->oDestinationIO instanceof FormInput){
|
||||
$this->AddBindingToMap($this->aInputToInputsMap, $oBinding);
|
||||
}
|
||||
// Output to outputs map
|
||||
if($oBinding->oSourceIO instanceof FormOutput
|
||||
&& $oBinding->oDestinationIO instanceof FormOutput){
|
||||
$this->AddBindingToMap($this->aOutputToOutputsMap, $oBinding);
|
||||
}
|
||||
|
||||
// add to map
|
||||
$this->aDependenciesMap[$sOutputBlockName][$sOutputName][] = $oBinding;
|
||||
}
|
||||
|
||||
/** Iterate throw the block inputs connections... @var FormBinding $oBinding**/
|
||||
foreach ($oDependentBlock->GetOutputBindings() as $oBinding) {
|
||||
|
||||
// Output to outputs map
|
||||
if($oBinding->oSourceIO instanceof FormOutput
|
||||
&& $oBinding->oDestinationIO instanceof FormOutput){
|
||||
$this->AddBindingToMap($this->aOutputToOutputsMap, $oBinding);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a binding to a map.
|
||||
*
|
||||
* @param array $map
|
||||
* @param FormBinding $oBinding
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function AddBindingToMap(array &$map, FormBinding $oBinding): void
|
||||
{
|
||||
// Binding information
|
||||
$sBlockName = $oBinding->oSourceIO->GetOwnerBlock()->GetName();
|
||||
$sIOName = $oBinding->oSourceIO->GetName();
|
||||
|
||||
// initialize map
|
||||
if (!isset($map[$sBlockName])) {
|
||||
$map[$sBlockName] = [];
|
||||
}
|
||||
if (!isset($map[$sBlockName][$sIOName])) {
|
||||
$map[$sBlockName][$sIOName] = [];
|
||||
}
|
||||
|
||||
// add to map
|
||||
$map[$sBlockName][$sIOName][] = $oBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function GetListenedBlockNames(): array
|
||||
public function GetListenedOutputBlockNames(): array
|
||||
{
|
||||
return array_keys($this->aDependenciesMap);
|
||||
$aResult = [];
|
||||
|
||||
foreach(array_keys($this->aOutputToInputsMap) as $sOutputBlockName) {
|
||||
if(!array_key_exists($sOutputBlockName, $this->aDependentBlocks)){
|
||||
$aResult[] = $sOutputBlockName;
|
||||
}
|
||||
}
|
||||
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -77,7 +132,7 @@ class DependencyMap
|
||||
*/
|
||||
public function IsBlockHasOutputs(string $sBlockName): bool
|
||||
{
|
||||
return array_key_exists($sBlockName, $this->aDependenciesMap);
|
||||
return array_key_exists($sBlockName, $this->aOutputToInputsMap);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,12 +142,12 @@ class DependencyMap
|
||||
*/
|
||||
public function GetOutputsForBlock(string $sBlockName): array
|
||||
{
|
||||
return array_keys($this->aDependenciesMap[$sBlockName]);
|
||||
return array_keys($this->aOutputToInputsMap[$sBlockName]);
|
||||
}
|
||||
|
||||
public function GetOutputsDependenciesForBlock(string $sOutputBlockName): array
|
||||
{
|
||||
return $this->aDependenciesMap[$sOutputBlockName];
|
||||
return $this->aOutputToInputsMap[$sOutputBlockName];
|
||||
}
|
||||
|
||||
public function IsTheBlockInDependencies(string $sBlockName): bool
|
||||
@@ -106,4 +161,19 @@ class DependencyMap
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function GetOutputToInputs(): array
|
||||
{
|
||||
return $this->aOutputToInputsMap;
|
||||
}
|
||||
|
||||
public function GetInputToInputs(): array
|
||||
{
|
||||
return $this->aInputToInputsMap;
|
||||
}
|
||||
|
||||
public function GetOutputToOutputs(): array
|
||||
{
|
||||
return $this->aOutputToOutputsMap;
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\FormBlock;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\Form\DataMapperInterface;
|
||||
@@ -23,57 +24,135 @@ use Traversable;
|
||||
|
||||
class FormBuilder implements FormBuilderInterface, \IteratorAggregate
|
||||
{
|
||||
/** @var DependencyHandler|null */
|
||||
private ?DependencyHandler $oDependencyHandler = null;
|
||||
private array $aFormBlocks = [];
|
||||
|
||||
/** @var AbstractFormBlock */
|
||||
private AbstractFormBlock $oFormBlock;
|
||||
|
||||
/** @var array sub blocks */
|
||||
private array $aSubFormBlocks = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param FormBuilderInterface $builder
|
||||
*
|
||||
*/
|
||||
public function __construct(private FormBuilderInterface $builder)
|
||||
public function __construct(private readonly FormBuilderInterface $builder)
|
||||
{
|
||||
$this->InitFormBlocks();
|
||||
/** Get the corresponding form block @var AbstractFormBlock $oFormBlock */
|
||||
$oFormBlock = $this->builder->getOption('form_block');
|
||||
|
||||
// Build the form
|
||||
if($oFormBlock instanceof FormBlock) {
|
||||
$this->BuildForm($oFormBlock);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the form.
|
||||
*
|
||||
* @param FormBlock $oFormBlock
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function BuildForm(FormBlock $oFormBlock): void
|
||||
{
|
||||
// Hidden (ignore)
|
||||
$aOptions = $this->builder->getOptions();
|
||||
if(array_key_exists('prevent_form_build', $aOptions) && $aOptions['prevent_form_build']) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aDependentBlocks = [];
|
||||
/** Iterate throw the form sub blocks... @var FormBlock $oSubFormBlock */
|
||||
foreach ($oFormBlock->GetSubFormBlocks() as $oSubFormBlock) {
|
||||
|
||||
// Add to the sub blocks array
|
||||
$this->aSubFormBlocks[$oSubFormBlock->getName()] = $oSubFormBlock;
|
||||
|
||||
// Handle sub block
|
||||
$bHasDependency = $this->HandleSubBlock($oSubFormBlock);
|
||||
|
||||
// Add to the dependencies array
|
||||
if($bHasDependency){
|
||||
$aDependentBlocks[$oSubFormBlock->GetName()] = $oSubFormBlock;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Create a dependency handler if needed
|
||||
if (count($aDependentBlocks) > 0) {
|
||||
$this->oDependencyHandler = new DependencyHandler($this->builder->getName(), $oFormBlock, $this, $this->aSubFormBlocks, $aDependentBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a sub block.
|
||||
*
|
||||
* @param AbstractFormBlock $oSubFormBlock
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function HandleSubBlock(AbstractFormBlock $oSubFormBlock): bool
|
||||
{
|
||||
|
||||
// Has at least one bounded input ?
|
||||
if ($oSubFormBlock->HasAtLeastOneBoundInput()) {
|
||||
|
||||
// Insert a hidden type to save the place
|
||||
$this->builder->add($oSubFormBlock->GetName(), HiddenType::class, [
|
||||
'form_block' => $oSubFormBlock,
|
||||
'prevent_form_build' => true,
|
||||
// 'mapped' => false,
|
||||
// 'disabled' => true,
|
||||
]);
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
// Directly insert the block corresponding form type
|
||||
$this->add($oSubFormBlock->GetName(), $oSubFormBlock->GetFormType(), $oSubFormBlock->UpdateOptions());
|
||||
$oSubFormBlock->SetAdded(true);
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a sub form block.
|
||||
*
|
||||
* @param string $sName
|
||||
*
|
||||
* @return AbstractFormBlock|null
|
||||
*/
|
||||
public function GetSubFormBlock(string $sName): ?AbstractFormBlock
|
||||
{
|
||||
return $this->aSubFormBlocks[$sName] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dependency handler attached to this builder.
|
||||
*
|
||||
* @return DependencyHandler|null
|
||||
*/
|
||||
protected function GetDependencyHandler(): ?DependencyHandler
|
||||
{
|
||||
return $this->oDependencyHandler;
|
||||
}
|
||||
|
||||
// pure decoration of FormBuilderInterface
|
||||
|
||||
public function add(string|FormBuilderInterface $child, ?string $type = null, array $options = []): static
|
||||
{
|
||||
$this->builder->add($child, $type, $options);
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function InitFormBlocks()
|
||||
{
|
||||
$oFormBlock = $this->builder->getOption('form_block');
|
||||
if (is_null($oFormBlock)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$aDependentBlocks = [];
|
||||
/** @var \Combodo\iTop\Forms\Block\FormBlock $oSubFormBlock */
|
||||
foreach ($oFormBlock->GetSubFormBlocks() as $oSubFormBlock) {
|
||||
$this->aFormBlocks[$oSubFormBlock->getName()] = $oSubFormBlock;
|
||||
if ($oSubFormBlock->HasConnections()) {
|
||||
$this->builder->add($oSubFormBlock->GetName(), HiddenType::class);
|
||||
$aDependentBlocks[] = $oSubFormBlock;
|
||||
} else {
|
||||
$this->add($oSubFormBlock->GetName(), $oSubFormBlock->GetFormType(), $oSubFormBlock->UpdateOptions());
|
||||
$oSubFormBlock->SetAdded(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($aDependentBlocks) > 0) {
|
||||
$this->oDependencyHandler = new DependencyHandler($this, $aDependentBlocks);
|
||||
}
|
||||
}
|
||||
|
||||
public function GetFormBlock(string $sName): ?AbstractFormBlock
|
||||
{
|
||||
return $this->aFormBlocks[$sName] ?? null;
|
||||
}
|
||||
|
||||
// pure decoration of FormBuilderInterface
|
||||
|
||||
public function getIterator(): Traversable
|
||||
{
|
||||
return $this->builder->getIterator();
|
||||
|
||||
@@ -4,6 +4,7 @@ namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Symfony\Component\Form\Event\PostSetDataEvent;
|
||||
use Symfony\Component\Form\Event\PostSubmitEvent;
|
||||
use Symfony\Component\Form\Event\PreSubmitEvent;
|
||||
use Symfony\Component\Form\FormEvent;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
@@ -24,6 +25,9 @@ class FormHelper
|
||||
} else if ($event instanceof PostSubmitEvent) {
|
||||
return FormEvents::POST_SUBMIT;
|
||||
}
|
||||
else if ($event instanceof PreSubmitEvent) {
|
||||
return FormEvents::PRE_SUBMIT;
|
||||
}
|
||||
|
||||
throw new FormBuilderException(sprintf('Unknown event type %s', get_class($event)));
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\Block\FormBlock;
|
||||
use Symfony\Component\Form\AbstractTypeExtension;
|
||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
@@ -14,9 +15,14 @@ use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
/**
|
||||
* Extension for form types.
|
||||
*
|
||||
*/
|
||||
class FormTypeExtension extends AbstractTypeExtension
|
||||
{
|
||||
|
||||
/** @inheritdoc */
|
||||
public static function getExtendedTypes(): iterable
|
||||
{
|
||||
return [
|
||||
@@ -24,27 +30,34 @@ class FormTypeExtension extends AbstractTypeExtension
|
||||
];
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefined([
|
||||
'form_block',
|
||||
'listener_callback',
|
||||
'builder_listener',
|
||||
'prevent_form_build'
|
||||
]);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
if(array_key_exists('listener_callback', $options)) {
|
||||
$builder->addEventListener(FormEvents::POST_SET_DATA, $options['listener_callback']);
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, $options['listener_callback']);
|
||||
if(array_key_exists('builder_listener', $options)) {
|
||||
$builder->addEventListener(FormEvents::POST_SET_DATA, $options['builder_listener']);
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, $options['builder_listener']);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||
{
|
||||
if(array_key_exists('form_block', $options)) {
|
||||
$view->vars['form_block'] = $options['form_block'];
|
||||
|
||||
$oFormBlock = $options['form_block'];
|
||||
$view->vars['trigger_form_submit_on_modify'] = $oFormBlock->HasAtLeastOneBoundOutput();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
18
sources/Forms/FormType/AttributeFormType.php
Normal file
18
sources/Forms/FormType/AttributeFormType.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\FormType;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
|
||||
class AttributeFormType extends AbstractType
|
||||
{
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChoiceFormType::class;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\FormType;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Event\PreSubmitEvent;
|
||||
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class AttributeValueChoiceType extends AbstractType
|
||||
{
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
// on pre submit
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event) use ($options){
|
||||
|
||||
// reset value if not in available choices
|
||||
if(!empty($event->getData()) && !$this->CheckValue($event->getData(), $options)){
|
||||
$event->getForm()->addError(new FormError("The value has been reset because it is not part of the available choices anymore."));
|
||||
$event->setData(null);
|
||||
}
|
||||
|
||||
}, 1);
|
||||
}
|
||||
|
||||
private function CheckValue($oValue, $options): bool
|
||||
{
|
||||
if(!is_array($oValue)){
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($oValue as $v){
|
||||
if(!in_array($v, $options['choices'])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static function GetOptionsFromInputs(array $inputs): array
|
||||
{
|
||||
$aValues = [];
|
||||
|
||||
if(!empty($inputs['attribute'])){
|
||||
$oAttDef = \MetaModel::GetAttributeDef($inputs['object_class'], $inputs['attribute']);
|
||||
$aValues = $oAttDef->GetAllowedValues();
|
||||
$aValues = $aValues !== null ? array_combine($aValues, $aValues) : [];
|
||||
}
|
||||
|
||||
return [
|
||||
'choices' => $aValues
|
||||
];
|
||||
}
|
||||
}
|
||||
32
sources/Forms/FormType/AttributeValueFormType.php
Normal file
32
sources/Forms/FormType/AttributeValueFormType.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\FormType;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
|
||||
class AttributeValueFormType extends AbstractType
|
||||
{
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChoiceFormType::class;
|
||||
}
|
||||
|
||||
public static function GetOptionsFromInputs(array $inputs): array
|
||||
{
|
||||
$aValues = [];
|
||||
|
||||
if(!empty($inputs['attribute'])){
|
||||
$oAttDef = \MetaModel::GetAttributeDef($inputs['object_class'], $inputs['attribute']);
|
||||
$aValues = $oAttDef->GetAllowedValues();
|
||||
$aValues = $aValues !== null ? array_combine($aValues, $aValues) : [];
|
||||
}
|
||||
|
||||
return [
|
||||
'choices' => $aValues
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,19 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class AttributeChoiceType extends AbstractType
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ChoiceFormType extends AbstractType
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function getParent(): string
|
||||
{
|
||||
return ChoiceType::class;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
// on pre submit
|
||||
@@ -35,13 +39,28 @@ class AttributeChoiceType extends AbstractType
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $oValue
|
||||
* @param $options
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function CheckValue($oValue, $options): bool
|
||||
{
|
||||
|
||||
if(!in_array($oValue, $options['choices'])){
|
||||
return false;
|
||||
// Check multi selection values
|
||||
if(is_array($oValue)){
|
||||
foreach ($oValue as $v){
|
||||
if(!in_array($v, $options['choices'])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check single selection values
|
||||
else{
|
||||
if(!in_array($oValue, $options['choices'])){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
75
sources/Forms/FormType/OqlFormType.php
Normal file
75
sources/Forms/FormType/OqlFormType.php
Normal file
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\FormType;
|
||||
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\OqlToClassConverter;
|
||||
use Exception;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Event\PostSubmitEvent;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class OqlFormType extends AbstractType
|
||||
{
|
||||
public function getParent(): string
|
||||
{
|
||||
return TextareaType::class;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefault('help', 'An OQL query expression');
|
||||
|
||||
$resolver->setDefault('attr', [
|
||||
'placeholder' => 'SELECT Contact',
|
||||
]);
|
||||
|
||||
$resolver->setDefault('outputs', array(
|
||||
'selected_class' => function($oData) {
|
||||
if($oData === null)
|
||||
return null;
|
||||
// extract selected class
|
||||
preg_match('/SELECT\s+(\w+)/', $oData, $aMatches);
|
||||
return $aMatches[1] ?? null;
|
||||
}
|
||||
));
|
||||
|
||||
$resolver->setDefined('with_ai_button');
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
parent::buildForm($builder, $options);
|
||||
|
||||
// on pre submit
|
||||
$builder->addEventListener(FormEvents::POST_SUBMIT, function (PostSubmitEvent $event) use ($options){
|
||||
|
||||
try{
|
||||
$oClassConverter = new OqlToClassConverter();
|
||||
$oClassConverter->Convert($event->getData());
|
||||
}
|
||||
catch(Exception $e){
|
||||
$event->getForm()->addError(new FormError($e->getMessage()));
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||
{
|
||||
parent::buildView($view, $form, $options);
|
||||
|
||||
$view->vars['with_ai_button'] = $options['with_ai_button'];
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\FormType;
|
||||
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class OqlType extends AbstractType
|
||||
{
|
||||
public function getParent(): string
|
||||
{
|
||||
return TextType::class;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefault('outputs', array(
|
||||
'selected_class' => function($oData) {
|
||||
if($oData === null)
|
||||
return null;
|
||||
// extract selected class
|
||||
preg_match('/SELECT\s+(\w+)/', $oData, $aMatches);
|
||||
return $aMatches[1] ?? null;
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,7 +6,9 @@
|
||||
{% if type == 'text' %}{% set ibo_class='ibo-input-string' %}{% else %}{% set ibo_class='ibo-input-' ~ type %}{% endif %}
|
||||
{% set attr = attr|merge({class: (attr.class|default('') ~ ' ibo-input ' ~ ibo_class)|trim}) %}
|
||||
{{- parent() -}}
|
||||
onChange="this.form.requestSubmit();console.log('Auto submitting form due to change in field {{ full_name }}');"
|
||||
{% if trigger_form_submit_on_modify %}
|
||||
onChange="this.form.requestSubmit();console.log('Auto submitting form due to change in field {{ full_name }}');"
|
||||
{% endif %}
|
||||
{%- endblock widget_attributes -%}
|
||||
|
||||
{%- block form_label -%}
|
||||
@@ -18,28 +20,23 @@
|
||||
{{- parent() -}}
|
||||
{%- endblock form_label -%}
|
||||
|
||||
{%- block form_label_content -%}
|
||||
{{- parent() -}}
|
||||
{% if with_ai_button is defined and with_ai_button %}
|
||||
<div style="float: right;display: flex;flex-direction: column;">
|
||||
<button class="ibo-button ibo-button ibo-block ibo-is-alternative ibo-is-neutral " data-role="ibo-button" type="button" name="AI" value="" data-tooltip-content="AI" aria-label="AI">
|
||||
<span class="ibo-button--icon fas fa-magic"></span>
|
||||
</button>
|
||||
<button class="ibo-button ibo-button ibo-block ibo-is-alternative ibo-is-neutral " data-role="ibo-button" type="button" name="Book" value="" data-tooltip-content="Query Book" aria-label="AI">
|
||||
<span class="ibo-button--icon fas fa-book"></span>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{%-endblock form_label_content -%}
|
||||
|
||||
{%- block form_row -%}
|
||||
{% set row_attr = row_attr|merge({class: (row_attr.class|default('') ~ ' ibo-field ibo-content-block ibo-block ibo-field-small')|trim}) %}
|
||||
{{- parent() -}}
|
||||
|
||||
{% if form_block is defined %}
|
||||
{% for output in form_block.GetOutputs() %}
|
||||
<ul>
|
||||
{% if output.HasValues %}
|
||||
<li style="background-color:#dac2e3;border-radius: 8px;padding: 1px 4px;font-size: 1rem;margin: 5px 0;">
|
||||
<div>
|
||||
<div><b>{{ output.name }}</b></div>
|
||||
{% for t,v in output.GetValues %}
|
||||
<div>- {{ t }}: {{ v }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
{% else %}
|
||||
Aucun output
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{%- endblock form_row -%}
|
||||
|
||||
{%- block form_errors -%}
|
||||
|
||||
Reference in New Issue
Block a user