mirror of
https://github.com/Combodo/iTop.git
synced 2026-04-19 00:28:47 +02:00
N°8772 - dynamic form
This commit is contained in:
@@ -501,6 +501,7 @@ return array(
|
||||
'Combodo\\iTop\\Forms\\Block\\FormType\\OqlFormType' => $baseDir . '/sources/Forms/Block/FormType/OqlFormType.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\\ChoiceValueToLabelConverter' => $baseDir . '/sources/Forms/Block/IO/Converter/ChoiceValueToLabelConverter.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\\Converter\\StringToBooleanConverter' => $baseDir . '/sources/Forms/Block/IO/Converter/StringToBooleanConverter.php',
|
||||
|
||||
@@ -882,6 +882,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
|
||||
'Combodo\\iTop\\Forms\\Block\\FormType\\OqlFormType' => __DIR__ . '/../..' . '/sources/Forms/Block/FormType/OqlFormType.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\\ChoiceValueToLabelConverter' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/Converter/ChoiceValueToLabelConverter.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\\Converter\\StringToBooleanConverter' => __DIR__ . '/../..' . '/sources/Forms/Block/IO/Converter/StringToBooleanConverter.php',
|
||||
|
||||
@@ -7,18 +7,19 @@
|
||||
namespace Combodo\iTop\Forms\Block;
|
||||
|
||||
use Combodo\iTop\Forms\Block\Base\FormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\AbstractFormIO;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\AbstractConverter;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\RawFormat;
|
||||
use Combodo\iTop\Forms\Block\IO\FormInput;
|
||||
use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
use Combodo\iTop\Forms\IFormBlock;
|
||||
use Forms\BlockIO;
|
||||
|
||||
/**
|
||||
* Abstract form block.
|
||||
*
|
||||
* A form block describe a form (complex or simple type).
|
||||
* A complex form have sub blocks.
|
||||
* It defines its inputs and outputs.
|
||||
* Inputs / Outputs.
|
||||
* Options.
|
||||
*
|
||||
*/
|
||||
abstract class AbstractFormBlock implements IFormBlock
|
||||
@@ -96,7 +97,12 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
return $this->oParent;
|
||||
}
|
||||
|
||||
public function HasParent(): bool
|
||||
/**
|
||||
* Return true if this block is root.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function IsRootBlock(): bool
|
||||
{
|
||||
return $this->oParent !== null;
|
||||
}
|
||||
@@ -111,28 +117,6 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
return $this->sName;
|
||||
}
|
||||
|
||||
public function GetIdentifier(): string
|
||||
{
|
||||
$sParentName = $this->GetParent()?->GetIdentifier();
|
||||
if (is_null($sParentName)) {
|
||||
return $this->GetName();
|
||||
}
|
||||
return $sParentName.'_'.$this->sName;
|
||||
}
|
||||
|
||||
public function GetPath(): array
|
||||
{
|
||||
$aPath = [];
|
||||
$oCurrent = $this;
|
||||
|
||||
do {
|
||||
$aPath[] = $oCurrent->GetName();
|
||||
$oCurrent = $oCurrent->getParent();
|
||||
} while ($oCurrent->HasParent());
|
||||
|
||||
return array_reverse($aPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the form block options.
|
||||
* Options will be passed to FormType for building.
|
||||
@@ -170,7 +154,7 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
* @param string $sName the input name
|
||||
* @param string $sType the type of the input
|
||||
*
|
||||
* @return void
|
||||
* @return AbstractFormBlock
|
||||
*/
|
||||
public function AddInput(string $sName, string $sType): AbstractFormBlock
|
||||
{
|
||||
@@ -180,6 +164,27 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an input connected to another block.
|
||||
*
|
||||
* @param string $sName the input name
|
||||
* @param string $sOutputBlockName
|
||||
* @param string $sOutputName
|
||||
*
|
||||
* @return AbstractFormBlock
|
||||
* @throws FormBlockException
|
||||
*/
|
||||
public function AddInputDependsOn(string $sName, string $sOutputBlockName, string $sOutputName): AbstractFormBlock
|
||||
{
|
||||
$oOutputBlock = $this->GetParent()->Get($sOutputBlockName);
|
||||
$oBlockOutput = $oOutputBlock->GetOutput($sOutputName);
|
||||
|
||||
$this->AddInput($sName, $oBlockOutput->GetDataType());
|
||||
$this->DependsOn($sName, $sOutputBlockName, $sOutputName);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an input.
|
||||
*
|
||||
@@ -204,7 +209,7 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
* @param string $sType
|
||||
* @param AbstractConverter|null $oConverter
|
||||
*
|
||||
* @return void
|
||||
* @return AbstractFormBlock
|
||||
*/
|
||||
public function AddOutput(string $sName, string $sType, AbstractConverter $oConverter = null): AbstractFormBlock
|
||||
{
|
||||
@@ -342,11 +347,11 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bindings on inputs.
|
||||
* Get bound inputs bindings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetInputsBindings(): array
|
||||
public function GetBoundInputsBindings(): array
|
||||
{
|
||||
$aBindings = [];
|
||||
|
||||
@@ -361,11 +366,11 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bindings on outputs.
|
||||
* Get bound outputs bindings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetOutputBindings(): array
|
||||
public function GetBoundOutputBindings(): array
|
||||
{
|
||||
$aBindings = [];
|
||||
|
||||
@@ -379,26 +384,6 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
return $aBindings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inputs data ready.
|
||||
*
|
||||
* @param string|null $sType
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function IsInputsDataReady(string $sType = null): bool
|
||||
{
|
||||
foreach ($this->aFormInputs as $oFormInput) {
|
||||
if ($oFormInput->IsBound()) {
|
||||
if (!$oFormInput->IsEventDataReady($sType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The block has been added to its parent.
|
||||
*
|
||||
@@ -421,6 +406,26 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
$this->bIsAddedToForm = $bIsAdded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inputs data ready.
|
||||
*
|
||||
* @param string|null $sType
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function IsInputsDataReady(string $sType = null): bool
|
||||
{
|
||||
foreach ($this->aFormInputs as $oFormInput) {
|
||||
if ($oFormInput->IsBound()) {
|
||||
if (!$oFormInput->IsEventDataReady($sType)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute outputs values.
|
||||
*
|
||||
@@ -438,19 +443,9 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
$oFormOutput->ComputeValue($sEventType, $oData);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Propagate inputs values.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function PropagateInputsValues(): void
|
||||
{
|
||||
foreach ($this->aFormInputs as $oFormInput) {
|
||||
$oFormInput->PropagateBindingsValues();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize inputs.
|
||||
@@ -471,7 +466,27 @@ abstract class AbstractFormBlock implements IFormBlock
|
||||
$this->AddOutput(self::OUTPUT_VALUE, RawFormat::class);
|
||||
}
|
||||
|
||||
public function InputHasChanged()
|
||||
/**
|
||||
* Called when a binding value has been transmitted.
|
||||
*
|
||||
* @param AbstractFormIO $oBlockIO
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function BindingReceivedEvent(AbstractFormIO $oBlockIO): void
|
||||
{
|
||||
if ($this->IsInputsDataReady()) {
|
||||
$this->AllInputsReadyEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when all inputs are ready.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function AllInputsReadyEvent(): void
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -35,12 +35,11 @@ abstract class AbstractTypeFormBlock extends AbstractFormBlock
|
||||
* @param string|null $sEventType
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Combodo\iTop\Forms\Block\FormBlockException
|
||||
* @throws FormBlockException
|
||||
*/
|
||||
public function IsVisible(string $sEventType = null): bool
|
||||
{
|
||||
$oInput = $this->GetInput(self::INPUT_VISIBLE);
|
||||
|
||||
if(!$oInput->IsBound()){
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ namespace Combodo\iTop\Forms\Block\Base;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractTypeFormBlock;
|
||||
use Combodo\iTop\Forms\Block\FormType\ChoiceFormType;
|
||||
use Combodo\iTop\Forms\Block\IO\Converter\ChoiceValueToLabelConverter;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\RawFormat;
|
||||
|
||||
/**
|
||||
* Form block for choices.
|
||||
@@ -15,10 +17,18 @@ use Combodo\iTop\Forms\Block\FormType\ChoiceFormType;
|
||||
*/
|
||||
class ChoiceFormBlock extends AbstractTypeFormBlock
|
||||
{
|
||||
// Outputs
|
||||
public const OUTPUT_LABEL = 'label';
|
||||
|
||||
/** @inheritdoc */
|
||||
public function GetFormType(): string
|
||||
{
|
||||
return ChoiceFormType::class;
|
||||
}
|
||||
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
parent::InitOutputs();
|
||||
$this->AddOutput(self::OUTPUT_LABEL, RawFormat::class, new ChoiceValueToLabelConverter($this));
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,7 @@ 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 CoreException;
|
||||
use MetaModel;
|
||||
|
||||
@@ -52,10 +53,17 @@ class AttributeChoiceFormBlock extends ChoiceFormBlock
|
||||
/** @inheritdoc */
|
||||
public function UpdateDynamicOptions(string $sEventType = null): void
|
||||
{
|
||||
$oValue = $this->GetInput(self::INPUT_CLASS_NAME)->GetValue($sEventType);
|
||||
$oClass = $this->GetInput(self::INPUT_CLASS_NAME)->GetValue($sEventType);
|
||||
|
||||
$aAttributeCodes = MetaModel::GetAttributesList($oValue);
|
||||
$this->aDynamicOptions['choices'] = array_combine($aAttributeCodes, $aAttributeCodes);
|
||||
$aAttributeCodes = MetaModel::GetAttributesList($oClass);
|
||||
|
||||
$aAttributes = [];
|
||||
foreach ($aAttributeCodes as $sAttributeCode){
|
||||
$oAttribute = MetaModel::GetAttributeDef(strval($oClass), $sAttributeCode);
|
||||
$aAttributes[$oAttribute->GetLabel()] = $sAttributeCode;
|
||||
}
|
||||
|
||||
$this->aDynamicOptions['choices'] = $aAttributes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -13,66 +13,47 @@ use Combodo\iTop\Forms\FormsException;
|
||||
use IssueLog;
|
||||
use Symfony\Component\Form\FormEvents;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ExpressionFormBlock extends AbstractFormBlock
|
||||
{
|
||||
public const EXPRESSION_PATTERN = "/\[\[(?<input>[^\]]+)]]/";
|
||||
|
||||
// Outputs
|
||||
const OUTPUT_RESULT = "result";
|
||||
const OUTPUT_RESULT_INVERT = "result_invert";
|
||||
|
||||
public function InitBlockOptions(array &$aUserOptions): void
|
||||
{
|
||||
parent::InitBlockOptions($aUserOptions);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function InitOutputs(): void
|
||||
{
|
||||
parent::InitOutputs();
|
||||
$this->AddOutput(self::OUTPUT_RESULT, BooleanIOFormat::class);
|
||||
// $this->AddOutput(self::OUTPUT_RAW, RawFormat::class);
|
||||
$this->AddOutput(self::OUTPUT_RESULT_INVERT, BooleanIOFormat::class);
|
||||
}
|
||||
|
||||
// public function InputHasChanged()
|
||||
// {
|
||||
// if (!$this->IsInputsDataReady()) {
|
||||
// return;
|
||||
// }
|
||||
// $sExpression = $this->GetOptions()['expression'];
|
||||
// $sValue = preg_replace_callback(
|
||||
// "/\[\[(?<input>[^\]]+)]]/",
|
||||
// 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()
|
||||
/** @inheritdoc */
|
||||
public function AllInputsReadyEvent(): void
|
||||
{
|
||||
if (!$this->IsInputsDataReady()) {
|
||||
return;
|
||||
}
|
||||
$sExpression = $this->GetOptions()['expression'];
|
||||
|
||||
$this->Compute($sExpression, FormEvents::POST_SET_DATA);
|
||||
$this->Compute($sExpression, FormEvents::POST_SUBMIT);
|
||||
$this->ComputeExpression(FormEvents::POST_SET_DATA);
|
||||
$this->ComputeExpression(FormEvents::POST_SUBMIT);
|
||||
}
|
||||
|
||||
public function Compute(string $sExpression, string $sEventType): void
|
||||
/**
|
||||
* Compute the expression and set the output values.
|
||||
*
|
||||
* @param string $sEventType
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function ComputeExpression(string $sEventType): void
|
||||
{
|
||||
try{
|
||||
$sExpression = $this->GetOptions()['expression'];
|
||||
|
||||
$sValue = preg_replace_callback(
|
||||
"/\[\[(?<input>[^\]]+)]]/",
|
||||
self::EXPRESSION_PATTERN,
|
||||
function(array $aMatches) use ($sEventType): ?string {
|
||||
$oInput = $this->GetInput($aMatches['input']);
|
||||
if(!$oInput->HasEventValue($sEventType)){
|
||||
@@ -86,6 +67,7 @@ class ExpressionFormBlock extends AbstractFormBlock
|
||||
eval('$result = '.$sValue.';');
|
||||
|
||||
$this->GetOutput(self::OUTPUT_RESULT)->SetValue($sEventType, new BooleanIOFormat($result));
|
||||
$this->GetOutput(self::OUTPUT_RESULT_INVERT)->SetValue($sEventType, new BooleanIOFormat(!$result));
|
||||
$this->GetOutput(self::OUTPUT_VALUE)->SetValue($sEventType, new RawFormat($result));
|
||||
}
|
||||
catch(\Exception $e){
|
||||
|
||||
@@ -33,13 +33,16 @@ class ChoiceFormType extends AbstractType
|
||||
{
|
||||
parent::configureOptions($resolver);
|
||||
|
||||
// options to control the inline display of choices
|
||||
$resolver->setDefault('inline_display', true);
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function buildView(FormView $view, FormInterface $form, array $options): void
|
||||
{
|
||||
parent::buildView($view, $form, $options);
|
||||
|
||||
// pass options to the view
|
||||
$view->vars['inline_display'] = $options['inline_display'];
|
||||
}
|
||||
|
||||
@@ -47,46 +50,51 @@ class ChoiceFormType extends AbstractType
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
// on preset data
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $event) use ($options) {
|
||||
|
||||
if ($options['multiple'] === false && $options['required'] === true) {
|
||||
if ($event->getData() === null) {
|
||||
$FirstElement = array_shift($options['choices']);
|
||||
if ($FirstElement !== null) {
|
||||
$event->setData($FirstElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (PreSetDataEvent $oEvent) use ($options) {
|
||||
$this->InitializeValue($oEvent, $options);
|
||||
});
|
||||
|
||||
// on pre submit
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event) use ($options) {
|
||||
|
||||
if($options['multiple'] === false && $options['required'] === true) {
|
||||
if ($event->getData() === null) {
|
||||
$FirstElement = array_shift($options['choices']);
|
||||
if($FirstElement !== null){
|
||||
$event->setData($FirstElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// on pre submit
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $event) use ($options){
|
||||
// on pre submit (prior)
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $oEvent) 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);
|
||||
if (!empty($oEvent->getData()) && !$this->CheckValue($oEvent->getData(), $options)) {
|
||||
$oEvent->getForm()->addError(new FormError("The value has been reset because it is not part of the available choices anymore."));
|
||||
$oEvent->setData(null);
|
||||
}
|
||||
|
||||
}, 1);
|
||||
}, 1); // priority 1 to be executed before the default validation (priority 0)
|
||||
|
||||
// on pre submit
|
||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (PreSubmitEvent $oEvent) use ($options) {
|
||||
$this->InitializeValue($oEvent, $options);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the value of the choice field.
|
||||
*
|
||||
* @param PreSetDataEvent|PreSubmitEvent $oEvent
|
||||
* @param array $options
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function InitializeValue(PreSetDataEvent|PreSubmitEvent $oEvent, array $options): void
|
||||
{
|
||||
if ($options['multiple'] === false && $options['required'] === true) {
|
||||
if ($oEvent->getData() === null) {
|
||||
$oFirstElement = array_shift($options['choices']);
|
||||
if ($oFirstElement !== null) {
|
||||
$oEvent->setData($oFirstElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the value(s) are part of the available choices.
|
||||
*
|
||||
* @param $oValue
|
||||
* @param $options
|
||||
*
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\FormType;
|
||||
|
||||
@@ -8,14 +12,18 @@ use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
use Symfony\Component\Form\FormView;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class FormType extends AbstractType
|
||||
{
|
||||
/** @inheritdoc */
|
||||
public function getParent(): string
|
||||
{
|
||||
return \Symfony\Component\Form\Extension\Core\Type\FormType::class;
|
||||
}
|
||||
|
||||
|
||||
/** @inheritdoc */
|
||||
public function buildView(FormView $view, FormInterface $form, array $options)
|
||||
{
|
||||
parent::buildView($view, $form, $options);
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
<?php
|
||||
/*
|
||||
* @copyright Copyright (C) 2010-2025 Combodo SARL
|
||||
* @license http://opensource.org/licenses/AGPL-3.0
|
||||
*/
|
||||
|
||||
namespace Combodo\iTop\Forms\Block\FormType;
|
||||
|
||||
use Combodo\iTop\Forms\Block\Base\FormBlock;
|
||||
use Symfony\Component\Form\FormInterface;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class FormTypeHelper
|
||||
{
|
||||
|
||||
@@ -51,7 +58,7 @@ class FormTypeHelper
|
||||
$aBlocksToRedraw[$sBlockTurboTriggerId] = $oFormTurboTrigger->createView();
|
||||
|
||||
// Add impacted blocks
|
||||
$aImpacted = $oMap->GetImpacted($oBlockTurboTrigger->GetName());
|
||||
$aImpacted = $oMap->GetBlocksImpactedBy($oBlockTurboTrigger->GetName());
|
||||
foreach ($aImpacted as $oImpactedBlock) {
|
||||
$sName = $sParentName.'_'.$oImpactedBlock->GetName();
|
||||
if($oParent->has($oImpactedBlock->GetName())){
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
<?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\Base\ChoiceFormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\Format\RawFormat;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ChoiceValueToLabelConverter extends AbstractConverter
|
||||
{
|
||||
private ChoiceFormBlock $oChoiceBlock;
|
||||
|
||||
public function __construct(ChoiceFormBlock $oChoiceBlock)
|
||||
{
|
||||
$this->oChoiceBlock = $oChoiceBlock;
|
||||
}
|
||||
|
||||
/** @inheritdoc */
|
||||
public function Convert(mixed $oData): ?RawFormat
|
||||
{
|
||||
if(is_null($oData) || is_array($oData)){
|
||||
return null;
|
||||
}
|
||||
|
||||
$aOptions = array_flip($this->oChoiceBlock->GetOptionsMergedWithDynamic()['choices']);
|
||||
|
||||
return new RawFormat($aOptions[$oData]);
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ class FormBinding
|
||||
{
|
||||
$this->oDestinationIO = $oDestinationIO;
|
||||
$this->oSourceIO = $oSourceIO;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -33,6 +32,6 @@ class FormBinding
|
||||
public function PropagateValues(): void
|
||||
{
|
||||
$this->oDestinationIO->SetValues($this->oSourceIO->GetValues());
|
||||
$this->oDestinationIO->GetOwnerBlock()->InputHasChanged();
|
||||
$this->oDestinationIO->GetOwnerBlock()->BindingReceivedEvent($this->oDestinationIO);
|
||||
}
|
||||
}
|
||||
@@ -4,16 +4,16 @@ namespace Combodo\iTop\Forms\Block\IO\Format;
|
||||
|
||||
class RawFormat
|
||||
{
|
||||
public string $oValue;
|
||||
public string $sValue;
|
||||
|
||||
public function __construct(string $oValue)
|
||||
public function __construct(string $sValue)
|
||||
{
|
||||
$this->oValue = $oValue;
|
||||
$this->sValue = $sValue;
|
||||
// validation du format sinon exception
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return strval($this->oValue);
|
||||
return strval($this->sValue);
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\Block\AbstractTypeFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Base\FormBlock;
|
||||
use Exception;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\Form\FormError;
|
||||
@@ -29,27 +30,24 @@ class DependencyHandler
|
||||
/** @var array events */
|
||||
private array $aEvents = [];
|
||||
private readonly string $sName;
|
||||
private readonly AbstractFormBlock $oFormBlock;
|
||||
private readonly FormBuilder $oFormBuilder;
|
||||
private readonly array $aSubBlocks;
|
||||
private readonly FormBlock $oFormBlock;
|
||||
private readonly array $aDependentBlocks;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $sName The name
|
||||
* @param AbstractFormBlock $oFormBlock The attached form block
|
||||
* @param FormBuilder $oFormBuilder The form builder
|
||||
* @param array $aSubBlocks Sub blocks
|
||||
* @param FormBlock $oFormBlock The block attached to the builder
|
||||
* @param array $aDependentBlocks Dependants blocks
|
||||
*/
|
||||
public function __construct(string $sName, AbstractFormBlock $oFormBlock, FormBuilder $oFormBuilder, array $aSubBlocks, array $aDependentBlocks)
|
||||
public function __construct(FormBuilder $oFormBuilder, FormBlock $oFormBlock, array $aDependentBlocks)
|
||||
{
|
||||
$this->sName = $oFormBuilder->getName();
|
||||
$this->aDependentBlocks = $aDependentBlocks;
|
||||
$this->aSubBlocks = $aSubBlocks;
|
||||
$this->oFormBuilder = $oFormBuilder;
|
||||
$this->oFormBlock = $oFormBlock;
|
||||
$this->sName = $sName;
|
||||
|
||||
// dependencies map
|
||||
$this->oDependenciesMap = new DependencyMap($aDependentBlocks);
|
||||
|
||||
@@ -63,16 +61,6 @@ class DependencyHandler
|
||||
self::$aDependencyHandlers[] = $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the form block.
|
||||
*
|
||||
* @return AbstractFormBlock
|
||||
*/
|
||||
public function GetFormBlock(): AbstractFormBlock
|
||||
{
|
||||
return $this->oFormBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add form ready listener.
|
||||
*
|
||||
@@ -86,13 +74,8 @@ class DependencyHandler
|
||||
// Initialize the dependencies listeners once the form is built
|
||||
$this->oFormBuilder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
|
||||
|
||||
/** Iterate throw listened blocks */
|
||||
foreach ($this->oDependenciesMap->GetInitialBoundOutputBlockNames() as $sOutputBlockName) {
|
||||
|
||||
// inner binding
|
||||
if ($sOutputBlockName === $this->oFormBlock->getName()) {
|
||||
continue;
|
||||
}
|
||||
/** Iterate throw blocks impacting other but without dependencies */
|
||||
foreach ($this->oDependenciesMap->GetImpactingBlocksWithoutDependencies() as $sOutputBlockName) {
|
||||
|
||||
// Add event
|
||||
$this->AddEvent('form.listen', $sOutputBlockName);
|
||||
@@ -123,7 +106,7 @@ class DependencyHandler
|
||||
$oForm = $oEvent->getForm();
|
||||
|
||||
// Get the form block
|
||||
$oFormBlock = $this->aSubBlocks[$oForm->getName()];
|
||||
$oFormBlock = $this->oFormBlock->Get($oForm->getName());
|
||||
|
||||
// Compute the block outputs with the data
|
||||
try{
|
||||
@@ -151,11 +134,9 @@ class DependencyHandler
|
||||
{
|
||||
$aImpactedBlocks = $this->aDependentBlocks;
|
||||
if($sOutputBlock !== null){
|
||||
$aImpactedBlocks = $this->oDependenciesMap->GetBlocksDependingOn($sOutputBlock);
|
||||
}
|
||||
|
||||
if($aImpactedBlocks === null){
|
||||
return;
|
||||
$aImpactedBlocks = $this->oDependenciesMap->GetBlocksImpactedBy($sOutputBlock, function(AbstractFormBlock $oBlock) use ($sEventType) {
|
||||
return $oBlock instanceof AbstractTypeFormBlock;
|
||||
});
|
||||
}
|
||||
|
||||
/** Iterate throw dependencies... @var AbstractFormBlock $oDependentBlock */
|
||||
@@ -172,7 +153,7 @@ class DependencyHandler
|
||||
$aOptions = $oDependentBlock->GetOptionsMergedWithDynamic($sEventType);
|
||||
|
||||
// Add the listener callback to the dependent field if it is also a dependency for another field
|
||||
if ($this->oDependenciesMap->IsTheBlockInDependencies($oDependentBlock->getName())) {
|
||||
if ($this->oDependenciesMap->HasBlocksImpactedBy($oDependentBlock->getName())) {
|
||||
|
||||
// Pass the listener call back to be registered by the dependency form builder
|
||||
$aOptions = array_merge($aOptions, [
|
||||
|
||||
@@ -7,7 +7,6 @@
|
||||
namespace Combodo\iTop\Forms\FormBuilder;
|
||||
|
||||
use Combodo\iTop\Forms\Block\AbstractFormBlock;
|
||||
use Combodo\iTop\Forms\Block\AbstractTypeFormBlock;
|
||||
use Combodo\iTop\Forms\Block\Expression\ExpressionFormBlock;
|
||||
use Combodo\iTop\Forms\Block\FormBlock;
|
||||
use Combodo\iTop\Forms\Block\IO\FormBinding;
|
||||
@@ -20,8 +19,8 @@ use Combodo\iTop\Forms\Block\IO\FormOutput;
|
||||
*/
|
||||
class DependencyMap
|
||||
{
|
||||
/** @var array array of blocks with dependencies group by dependence */
|
||||
private array $aBlocksWithDependenciesGroupByDependence = [];
|
||||
/** @var array array of blocks impacted by dependence */
|
||||
private array $aBlocksImpactedBy = [];
|
||||
|
||||
/** @var array array of binding */
|
||||
private array $aBindings = [];
|
||||
@@ -57,13 +56,13 @@ class DependencyMap
|
||||
foreach ($this->aBlocksWithDependencies as $oDependentBlock) {
|
||||
|
||||
/** Iterate throw the block inputs bindings... @var FormBinding $oBinding**/
|
||||
foreach ($oDependentBlock->GetInputsBindings() as $oBinding) {
|
||||
foreach ($oDependentBlock->GetBoundInputsBindings() as $oBinding) {
|
||||
|
||||
// OUT > IN
|
||||
if($oBinding->oSourceIO instanceof FormOutput
|
||||
&& $oBinding->oDestinationIO instanceof FormInput){
|
||||
$this->AddBindingToMap($this->aBindingsOutputToInput, $oBinding);
|
||||
$this->AddToBlockWithDependenciesMap($oBinding->oSourceIO->GetOwnerBlock()->GetName(), $oDependentBlock);
|
||||
$this->AddToBlockImpactedBy($oBinding->oSourceIO->GetOwnerBlock()->GetName(), $oDependentBlock);
|
||||
}
|
||||
|
||||
// IN > IN
|
||||
@@ -75,7 +74,7 @@ class DependencyMap
|
||||
}
|
||||
|
||||
/** Iterate throw the block inputs connections... @var FormBinding $oBinding**/
|
||||
foreach ($oDependentBlock->GetOutputBindings() as $oBinding) {
|
||||
foreach ($oDependentBlock->GetBoundOutputBindings() as $oBinding) {
|
||||
|
||||
// OUT > OUT
|
||||
if($oBinding->oSourceIO instanceof FormOutput
|
||||
@@ -88,45 +87,6 @@ class DependencyMap
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDependenceBlockName
|
||||
* @param AbstractFormBlock $oBlockWithDependencies
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function AddToBlockWithDependenciesMap(string $sDependenceBlockName, AbstractFormBlock $oBlockWithDependencies): void
|
||||
{
|
||||
// Initialize array for this dependence
|
||||
if(!array_key_exists($sDependenceBlockName, $this->aBlocksWithDependenciesGroupByDependence)){
|
||||
$this->aBlocksWithDependenciesGroupByDependence[$sDependenceBlockName] = [];
|
||||
}
|
||||
|
||||
// Add the block
|
||||
$this->aBlocksWithDependenciesGroupByDependence[$sDependenceBlockName][$oBlockWithDependencies->GetName()] = $oBlockWithDependencies;
|
||||
|
||||
// TODO
|
||||
if($oBlockWithDependencies instanceof ExpressionFormBlock){
|
||||
foreach($oBlockWithDependencies->GetOutputs() as $oOutput){
|
||||
foreach($oOutput->GetBindings() as $oBinding){
|
||||
$this->AddToBlockWithDependenciesMap($sDependenceBlockName, $oBinding->oDestinationIO->GetOwnerBlock());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sBlockName
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function GetBlocksDependingOn(string $sBlockName): ?array
|
||||
{
|
||||
if(!array_key_exists($sBlockName,$this->aBlocksWithDependenciesGroupByDependence)){
|
||||
return null;
|
||||
}
|
||||
return $this->aBlocksWithDependenciesGroupByDependence[$sBlockName] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a binding to a map.
|
||||
*
|
||||
@@ -154,10 +114,36 @@ class DependencyMap
|
||||
$this->aBindings[] = $oBinding;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sDependsOnName
|
||||
* @param AbstractFormBlock $oImpactedBlock
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function AddToBlockImpactedBy(string $sDependsOnName, AbstractFormBlock $oImpactedBlock): void
|
||||
{
|
||||
// Initialize array for this dependence
|
||||
if(!array_key_exists($sDependsOnName, $this->aBlocksImpactedBy)){
|
||||
$this->aBlocksImpactedBy[$sDependsOnName] = [];
|
||||
}
|
||||
|
||||
// Add the block
|
||||
$this->aBlocksImpactedBy[$sDependsOnName][$oImpactedBlock->GetName()] = $oImpactedBlock;
|
||||
|
||||
// TODO
|
||||
if($oImpactedBlock instanceof ExpressionFormBlock){
|
||||
foreach($oImpactedBlock->GetOutputs() as $oOutput){
|
||||
foreach($oOutput->GetBindings() as $oBinding){
|
||||
$this->AddToBlockImpactedBy($sDependsOnName, $oBinding->oDestinationIO->GetOwnerBlock());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function GetInitialBoundOutputBlockNames(): array
|
||||
public function GetImpactingBlocksWithoutDependencies(): array
|
||||
{
|
||||
$aResult = [];
|
||||
|
||||
@@ -173,79 +159,78 @@ class DependencyMap
|
||||
return $aResult;
|
||||
}
|
||||
|
||||
public function GetImpacted(string $sBlockName): array
|
||||
/**
|
||||
* Get block impacted by a given block.
|
||||
* The blocks can be filtered using a callable.
|
||||
*
|
||||
* @param string $sBlockName
|
||||
* @param callable|null $oFilter
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
public function GetBlocksImpactedBy(string $sBlockName, callable $oFilter = null): ?array
|
||||
{
|
||||
$aImpacted = [];
|
||||
if (array_key_exists($sBlockName, $this->aBindingsOutputToInput)) {
|
||||
foreach ($this->aBindingsOutputToInput[$sBlockName] as $aBindings) {
|
||||
foreach ($aBindings as $oBinding) {
|
||||
$oDestBlock = $oBinding->oDestinationIO->GetOwnerBlock();
|
||||
if ($oDestBlock instanceof AbstractTypeFormBlock) {
|
||||
$aImpacted[] = $oDestBlock;
|
||||
} else {
|
||||
$aImpacted = array_merge($aImpacted, $this->GetImpacted($oDestBlock->GetName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!array_key_exists($sBlockName, $this->aBlocksImpactedBy)){
|
||||
return null;
|
||||
}
|
||||
$aBlocks = $this->aBlocksImpactedBy[$sBlockName];
|
||||
|
||||
// Filtering
|
||||
if($oFilter !== null){
|
||||
$aBlocks = array_filter($aBlocks, $oFilter);
|
||||
}
|
||||
|
||||
return $aImpacted;
|
||||
return $aBlocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a block impacts other blocks.
|
||||
*
|
||||
* @param string $sBlockName
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function IsBlockHasOutputs(string $sBlockName): bool
|
||||
public function HasBlocksImpactedBy(string $sBlockName): bool
|
||||
{
|
||||
return array_key_exists($sBlockName, $this->aBindingsOutputToInput);
|
||||
return $this->GetBlocksImpactedBy($sBlockName) !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sBlockName
|
||||
* Get bindings OUT > IN.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetOutputsForBlock(string $sBlockName): array
|
||||
{
|
||||
return array_keys($this->aBindingsOutputToInput[$sBlockName]);
|
||||
}
|
||||
|
||||
public function GetOutputsDependenciesForBlock(string $sOutputBlockName): array
|
||||
{
|
||||
return $this->aBindingsOutputToInput[$sOutputBlockName];
|
||||
}
|
||||
|
||||
public function IsTheBlockInDependencies(string $sBlockName): bool
|
||||
{
|
||||
// foreach ($this->aDependentBlocks as $oDependentBlock)
|
||||
// {
|
||||
// if($oDependentBlock->getName() === $sBlockName) {
|
||||
// return true;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return false;
|
||||
return $this->GetBlocksDependingOn($sBlockName) !== null;
|
||||
}
|
||||
|
||||
public function GetOutputToInputs(): array
|
||||
{
|
||||
return $this->aBindingsOutputToInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bindings IN > IN.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetInputToInputs(): array
|
||||
{
|
||||
return $this->aBindingsInputToInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bindings OUT > OUT.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetOutputToOutputs(): array
|
||||
{
|
||||
return $this->aBindingsOutputToOutputs;
|
||||
}
|
||||
|
||||
public function GetBindings()
|
||||
/**
|
||||
* Get all bindings.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function GetAllBindings()
|
||||
{
|
||||
return $this->aBindings;
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ class FormBuilder implements FormBuilderInterface, IteratorAggregate
|
||||
/** @var AbstractFormBlock */
|
||||
private AbstractFormBlock $oFormBlock;
|
||||
|
||||
/** @var array sub blocks */
|
||||
private array $aChildren = [];
|
||||
/** @var FormBuilderInterface */
|
||||
private readonly FormBuilderInterface $builder;
|
||||
|
||||
/**
|
||||
@@ -63,7 +62,7 @@ class FormBuilder implements FormBuilderInterface, IteratorAggregate
|
||||
*/
|
||||
private function BuildForm(FormBlock $oFormBlock): void
|
||||
{
|
||||
// Hidden (ignore)
|
||||
// Prevent form build option
|
||||
$aOptions = $this->builder->getOptions();
|
||||
if (array_key_exists('prevent_form_build', $aOptions) && $aOptions['prevent_form_build']) {
|
||||
return;
|
||||
@@ -73,11 +72,8 @@ class FormBuilder implements FormBuilderInterface, IteratorAggregate
|
||||
/** Iterate throw the form sub blocks... @var FormBlock $oSubFormBlock */
|
||||
foreach ($oFormBlock->GetChildren() as $sBlockName => $oChildBlock) {
|
||||
|
||||
// Add to the children
|
||||
$this->aChildren[$sBlockName] = $oChildBlock;
|
||||
|
||||
// Handle block
|
||||
$bHasDependency = $this->HandleSubBlock($oChildBlock);
|
||||
// Handle child block
|
||||
$bHasDependency = $this->HandleChildBlock($oChildBlock);
|
||||
|
||||
// Add to the array of blocks with dependencies
|
||||
if ($bHasDependency) {
|
||||
@@ -88,7 +84,7 @@ class FormBuilder implements FormBuilderInterface, IteratorAggregate
|
||||
|
||||
// Create a dependency handler if needed
|
||||
if (count($aBlocksWithDependencies) > 0) {
|
||||
$this->oDependencyHandler = new DependencyHandler($this->builder->getName(), $oFormBlock, $this, $this->aChildren, $aBlocksWithDependencies);
|
||||
$this->oDependencyHandler = new DependencyHandler($this, $oFormBlock, $aBlocksWithDependencies);
|
||||
$oFormBlock->oDependencyMap = $this->oDependencyHandler->GetMap();
|
||||
}
|
||||
|
||||
@@ -105,7 +101,7 @@ class FormBuilder implements FormBuilderInterface, IteratorAggregate
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function HandleSubBlock(AbstractFormBlock $oSubFormBlock): bool
|
||||
private function HandleChildBlock(AbstractFormBlock $oSubFormBlock): bool
|
||||
{
|
||||
|
||||
// Has dependencies blocks
|
||||
@@ -122,18 +118,6 @@ class FormBuilder implements FormBuilderInterface, IteratorAggregate
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a child block.
|
||||
*
|
||||
* @param string $sName
|
||||
*
|
||||
* @return AbstractFormBlock|null
|
||||
*/
|
||||
public function GetChild(string $sName): ?AbstractFormBlock
|
||||
{
|
||||
return $this->aChildren[$sName] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the dependency handler attached to this builder.
|
||||
*
|
||||
|
||||
@@ -51,8 +51,9 @@
|
||||
<div id="turbo_{{ block.id }}" class="ibo-field ibo-content-block ibo-block ibo-field-small">
|
||||
{% if block.added == 1 %}
|
||||
{{ form_row(form[block.name]) }}
|
||||
{% else %}
|
||||
<div style="background-color: #ecd9eb;border-radius: 6px;padding: 2px 5px;">Reserved place for <b>{{ block.name }}</b></div>
|
||||
{# {% else %}#}
|
||||
{# {% else %}#}
|
||||
{# <div style="background-color: #ecd9eb;border-radius: 6px;padding: 2px 5px;">Reserved place for <b>{{ block.name }}</b></div>#}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
{% UITurboUpdate Standard { sTarget: "turbo_" ~ sBlockIdentifier} %}
|
||||
{% if oBlockToRedraw is not null %}
|
||||
{{ form_row(oBlockToRedraw) }}
|
||||
{% else %}
|
||||
<div style="background-color: #ecd9eb;border-radius: 6px;padding: 2px 5px;">Reserved place for <b>{{ sBlockIdentifier }}</b></div>
|
||||
{# {% else %}#}
|
||||
{# <div style="background-color: #ecd9eb;border-radius: 6px;padding: 2px 5px;">Reserved place for <b>{{ sBlockIdentifier }}</b></div>#}
|
||||
{% endif %}
|
||||
{% EndUITurboUpdate %}
|
||||
{% endfor %}
|
||||
Reference in New Issue
Block a user