N°8772 - "generic" form controller wip

This commit is contained in:
Eric Espie
2025-12-04 17:06:41 +01:00
parent 1fe6103d4f
commit b315b97e9e
38 changed files with 511 additions and 53 deletions

View File

@@ -488,12 +488,15 @@ return array(
'Combodo\\iTop\\Forms\\Block\\Base\\DateTimeFormBlock' => $baseDir . '/sources/Forms/Block/Base/DateTimeFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\FormBlock' => $baseDir . '/sources/Forms/Block/Base/FormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\HiddenFormBlock' => $baseDir . '/sources/Forms/Block/Base/HiddenFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\IntegerFormBlock' => $baseDir . '/sources/Forms/Block/Base/IntegerFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\NumberFormBlock' => $baseDir . '/sources/Forms/Block/Base/NumberFormBlock.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\\AttributeTypeChoiceFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/AttributeTypeChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeValueChoiceFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\Dashlet\\AggregateFunctionFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/Dashlet/AggregateFunctionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\Dashlet\\ClassAttributeGroupByFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/Dashlet/ClassAttributeGroupByFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\LabelFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/LabelFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\OqlFormBlock' => $baseDir . '/sources/Forms/Block/DataModel/OqlFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Expression\\AbstractExpressionFormBlock' => $baseDir . '/sources/Forms/Block/Expression/AbstractExpressionFormBlock.php',
@@ -535,6 +538,7 @@ return array(
'Combodo\\iTop\\Forms\\IO\\Format\\AttributeTypeIOFormat' => $baseDir . '/sources/Forms/IO/Format/AttributeTypeIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\BooleanIOFormat' => $baseDir . '/sources/Forms/IO/Format/BooleanIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\ClassIOFormat' => $baseDir . '/sources/Forms/IO/Format/ClassIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\IntegerIOFormat' => $baseDir . '/sources/Forms/IO/Format/IntegerIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\NumberIOFormat' => $baseDir . '/sources/Forms/IO/Format/NumberIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\StringIOFormat' => $baseDir . '/sources/Forms/IO/Format/StringIOFormat.php',
'Combodo\\iTop\\Forms\\Register\\IORegister' => $baseDir . '/sources/Forms/Register/IORegister.php',
@@ -551,6 +555,7 @@ return array(
'Combodo\\iTop\\PropertyTree\\Property' => $baseDir . '/sources/PropertyTree/Property.php',
'Combodo\\iTop\\PropertyTree\\PropertyTree' => $baseDir . '/sources/PropertyTree/PropertyTree.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\AbstractValueType' => $baseDir . '/sources/PropertyTree/ValueType/AbstractValueType.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeAggregateFunction' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeAggregateFunction.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoice' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeChoice.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttribute' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttribute.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeGroupBy' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeGroupBy.php',

View File

@@ -874,12 +874,15 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Forms\\Block\\Base\\DateTimeFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/DateTimeFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\FormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/FormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\HiddenFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/HiddenFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\IntegerFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/IntegerFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Base\\NumberFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Base/NumberFormBlock.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\\AttributeTypeChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/AttributeTypeChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\AttributeValueChoiceFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\Dashlet\\AggregateFunctionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/Dashlet/AggregateFunctionFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\Dashlet\\ClassAttributeGroupByFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/Dashlet/ClassAttributeGroupByFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\LabelFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/LabelFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\DataModel\\OqlFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/DataModel/OqlFormBlock.php',
'Combodo\\iTop\\Forms\\Block\\Expression\\AbstractExpressionFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/Expression/AbstractExpressionFormBlock.php',
@@ -921,6 +924,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\Forms\\IO\\Format\\AttributeTypeIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/AttributeTypeIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\BooleanIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/BooleanIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\ClassIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/ClassIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\IntegerIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/IntegerIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\NumberIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/NumberIOFormat.php',
'Combodo\\iTop\\Forms\\IO\\Format\\StringIOFormat' => __DIR__ . '/../..' . '/sources/Forms/IO/Format/StringIOFormat.php',
'Combodo\\iTop\\Forms\\Register\\IORegister' => __DIR__ . '/../..' . '/sources/Forms/Register/IORegister.php',
@@ -937,6 +941,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f
'Combodo\\iTop\\PropertyTree\\Property' => __DIR__ . '/../..' . '/sources/PropertyTree/Property.php',
'Combodo\\iTop\\PropertyTree\\PropertyTree' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTree.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\AbstractValueType' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/AbstractValueType.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeAggregateFunction' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeAggregateFunction.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoice' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeChoice.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttribute' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttribute.php',
'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeGroupBy' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeGroupBy.php',

View File

@@ -28,7 +28,9 @@ use Combodo\iTop\Application\WebPage\iTopWebPage;
use Combodo\iTop\Application\WebPage\WebPage;
use Combodo\iTop\Controller\AbstractController;
use Combodo\iTop\Forms\Block\AbstractFormBlock;
use Combodo\iTop\Forms\Block\Base\FormBlock;
use Combodo\iTop\Forms\Forms;
use Combodo\iTop\Forms\FormType\FormTypeHelper;
use Combodo\iTop\Service\InterfaceDiscovery\InterfaceDiscovery;
use Dict;
use Exception;
@@ -45,6 +47,7 @@ use Symfony\Component\Form\Extension\Csrf\CsrfExtension;
use Symfony\Component\Form\Extension\HttpFoundation\HttpFoundationExtension;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormFactoryBuilderInterface;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormRenderer;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Csrf\CsrfTokenManager;
@@ -1094,4 +1097,24 @@ abstract class Controller extends AbstractController
$this->bDebugAllowed = $bDebugAllowed;
}
protected function HandleFormSubmitted(FormBlock $oFormBlock, FormInterface $oForm): bool
{
$sTrigger = $this->GetRequest()->get($oFormBlock->GetName())['_turbo_trigger'];
if (!empty($sTrigger)) {
// Compute blocks to redraw
$aBlocksToRedraw = FormTypeHelper::ComputeBlocksToRedraw($oFormBlock, $oForm, $sTrigger);
// Display turbo response
$this->DisplayTurboAjaxPage($aBlocksToRedraw);
} else {
$this->DisplayTurboAjaxPage(['current_form' => $oForm->createView()]);
}
return true;
}
}

View File

@@ -48,13 +48,17 @@ class TurboFormUIBlockFactory extends AbstractUIBlockFactory
}
/**
* For dashlet configuration forms
*
* @api
*
* @param string $sDashletId
* @param string|null $sAction
* @param string|null $sId
*
* @return \Combodo\iTop\Application\UI\Base\Component\TurboForm\TurboForm
* @throws \Combodo\iTop\Forms\Block\FormBlockException
*/
public static function MakeForDashlet(string $sDashletId, string $sId = null): TurboForm
public static function MakeForDashletConfiguration(string $sDashletId, string $sId = null): TurboForm
{
$oBlockForm = FormBlockService::GetInstance()->GetFormBlockById($sDashletId);
$oController = new FormsController();

View File

@@ -40,6 +40,8 @@ abstract class AbstractFormBlock implements IFormBlock
*
* @param string $sName
* @param array $aOptions
*
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function __construct(private readonly string $sName, array $aOptions = [])
{
@@ -200,7 +202,9 @@ abstract class AbstractFormBlock implements IFormBlock
* @param string $sOutputName
*
* @return AbstractFormBlock
* @throws FormBlockException
* @throws \Combodo\iTop\Forms\Block\FormBlockException
* @throws \Combodo\iTop\Forms\IO\FormBlockIOException
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function AddInputDependsOn(string $sName, string $sOutputBlockName, string $sOutputName): AbstractFormBlock
{
@@ -214,7 +218,7 @@ abstract class AbstractFormBlock implements IFormBlock
* @param string $sName
*
* @return FormInput
* @throws FormBlockException
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function GetInput(string $sName): FormInput
{
@@ -227,7 +231,7 @@ abstract class AbstractFormBlock implements IFormBlock
* @param string $sName
*
* @return mixed
* @throws FormBlockException
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function GetInputValue(string $sName): mixed
{
@@ -242,6 +246,7 @@ abstract class AbstractFormBlock implements IFormBlock
* @param AbstractConverter|null $oConverter
*
* @return AbstractFormBlock
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function AddOutput(string $sName, string $sType, AbstractConverter $oConverter = null): AbstractFormBlock
{
@@ -254,8 +259,8 @@ abstract class AbstractFormBlock implements IFormBlock
*
* @param string $sName output name
*
* @return FormOutput
* @throws FormBlockException
* @return \Combodo\iTop\Forms\IO\FormOutput
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function GetOutput(string $sName): FormOutput
{
@@ -306,7 +311,9 @@ abstract class AbstractFormBlock implements IFormBlock
* @param string $sOutputName the dependency output name
*
* @return $this
* @throws FormBlockException
* @throws \Combodo\iTop\Forms\Block\FormBlockException
* @throws \Combodo\iTop\Forms\IO\FormBlockIOException
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function InputDependsOn(string $sInputName, string $sOutputBlockName, string $sOutputName): AbstractFormBlock
{
@@ -337,7 +344,9 @@ abstract class AbstractFormBlock implements IFormBlock
* @param string $sParentInputName parent input name
*
* @return $this
* @throws FormBlockException
* @throws \Combodo\iTop\Forms\Block\FormBlockException
* @throws \Combodo\iTop\Forms\IO\FormBlockIOException
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function InputDependsOnParent(string $sInputName, string $sParentInputName): AbstractFormBlock
{
@@ -352,7 +361,9 @@ abstract class AbstractFormBlock implements IFormBlock
* @param string $sParentOutputName parent output name
*
* @return $this
* @throws FormBlockException
* @throws \Combodo\iTop\Forms\Block\FormBlockException
* @throws \Combodo\iTop\Forms\IO\FormBlockIOException
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function OutputImpactParent(string $sOutputName, string $sParentOutputName): AbstractFormBlock
{

View File

@@ -22,8 +22,8 @@ use Combodo\iTop\Forms\Register\RegisterException;
abstract class AbstractTypeFormBlock extends AbstractFormBlock
{
// Inputs
public const INPUT_VISIBLE = 'input_visible';
public const INPUT_ENABLE = 'input_enable';
public const INPUT_VISIBLE = 'visible';
public const INPUT_ENABLE = 'enable';
/** @var bool flag indicating the form insertion */
private bool $bIsAddedToForm = false;

View File

@@ -23,7 +23,7 @@ use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
class CheckboxFormBlock extends AbstractTypeFormBlock
{
// outputs
public const OUTPUT_CHECKED = 'output_checked';
public const OUTPUT_CHECKED = 'checked';
/** @inheritdoc */
public function GetFormType(): string

View File

@@ -23,8 +23,8 @@ use Combodo\iTop\Forms\Register\IORegister;
class ChoiceFormBlock extends AbstractTypeFormBlock
{
// Outputs
public const OUTPUT_LABEL = 'output_label';
public const OUTPUT_VALUE = 'output_value';
public const OUTPUT_LABEL = 'label';
public const OUTPUT_VALUE = 'value';
/** @inheritdoc */
public function GetFormType(): string

View File

@@ -24,7 +24,7 @@ use Combodo\iTop\Forms\Register\RegisterException;
class CollectionBlock extends AbstractTypeFormBlock
{
// Inputs
public const INPUT_CLASS_NAME = 'input_class_name';
public const INPUT_CLASS_NAME = 'class';
private AbstractTypeFormBlock $oPrototypeBlock;

View File

@@ -10,6 +10,12 @@ namespace Combodo\iTop\Forms\Block\Base;
use Combodo\iTop\Forms\Block\AbstractTypeFormBlock;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
/**
* A block to manage a hidden field
*
* @package Combodo\iTop\Forms\Block\Base
* @since 3.3.0
*/
class HiddenFormBlock extends AbstractTypeFormBlock
{
/**

View File

@@ -0,0 +1,37 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Forms\Block\Base;
use Combodo\iTop\Forms\Block\AbstractTypeFormBlock;
use Combodo\iTop\Forms\IO\Format\NumberIOFormat;
use Combodo\iTop\Forms\Register\IORegister;
use Symfony\Component\Form\Extension\Core\Type\IntegerType;
/**
* A block to manage an integer
* This block exposes a single output: the integer value.
*
* @package Combodo\iTop\Forms\Block\Base
* @since 3.3.0
*/
class IntegerFormBlock extends AbstractTypeFormBlock
{
public const OUTPUT_INTEGER = 'integer';
public function GetFormType(): string
{
return IntegerType::class;
}
/** @inheritdoc */
protected function RegisterIO(IORegister $oIORegister): void
{
parent::RegisterIO($oIORegister);
$oIORegister->AddOutput(self::OUTPUT_INTEGER, IntegerIOFormat::class);
}
}

View File

@@ -22,7 +22,7 @@ use Symfony\Component\Form\Extension\Core\Type\NumberType;
class NumberFormBlock extends AbstractTypeFormBlock
{
// Outputs
public const OUTPUT_NUMBER = "output_number";
public const OUTPUT_NUMBER = "number";
/** @inheritdoc */
public function GetFormType(): string

View File

@@ -22,7 +22,7 @@ use Symfony\Component\Form\Extension\Core\Type\TextType;
class TextFormBlock extends AbstractTypeFormBlock
{
// Outputs
public const OUTPUT_TEXT = "output_text";
public const OUTPUT_TEXT = "text";
/** @inheritdoc */
public function GetFormType(): string

View File

@@ -7,10 +7,8 @@
namespace Combodo\iTop\Forms\Block\DataModel;
use Combodo\iTop\Core\AttributeDefinition\AttributeEnum;
use Combodo\iTop\Forms\Block\FormBlockException;
use Combodo\iTop\Forms\IO\Format\AttributeTypeArrayIOFormat;
use Combodo\iTop\Forms\IO\Format\AttributeTypeIOFormat;
use Combodo\iTop\Forms\IO\Format\StringIOFormat;
use Combodo\iTop\Forms\Register\RegisterException;
use Combodo\iTop\Service\DependencyInjection\DIException;
use Combodo\iTop\Service\DependencyInjection\DIService;
@@ -31,26 +29,29 @@ use utils;
class AttributeChoiceFormBlock extends ChoiceFormBlock
{
// inputs
public const INPUT_CLASS_NAME = 'input_class_name';
public const INPUT_ATTRIBUTE_TYPE = 'input_attribute_type';
public const INPUT_CLASS_NAME = 'class';
public const INPUT_CATEGORY = 'category';
// outputs
public const OUTPUT_ATTRIBUTE = 'output_attribute';
public const OUTPUT_ATTRIBUTE = 'attribute';
/** @inheritdoc */
protected function RegisterOptions(OptionsRegister $oOptionsRegister): void
{
parent::RegisterOptions($oOptionsRegister);
$oOptionsRegister->SetOption('placeholder', 'Select an attribute...');
$oOptionsRegister->SetOption('category', '', false);
}
/** @inheritdoc */
/** @inheritdoc
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
protected function RegisterIO(IORegister $oIORegister): void
{
parent::RegisterIO($oIORegister);
$oIORegister->AddInput(self::INPUT_CLASS_NAME, ClassIOFormat::class);
$oIORegister->AddInput(self::INPUT_ATTRIBUTE_TYPE, AttributeTypeIOFormat::class);
$oIORegister->AddInput(self::INPUT_CATEGORY, StringIOFormat::class);
// Default value
$this->SetInputValue(self::INPUT_CATEGORY, '');
$oIORegister->AddOutput(self::OUTPUT_ATTRIBUTE, AttributeIOFormat::class);
}
@@ -67,7 +68,7 @@ class AttributeChoiceFormBlock extends ChoiceFormBlock
// Get class name
$sClass = strval($this->GetInputValue(self::INPUT_CLASS_NAME));
$sAttType = $this->GetInputValue(self::INPUT_ATTRIBUTE_TYPE)->sAttributeType;
$sCategory = strval($this->GetInputValue(self::INPUT_CATEGORY));
// Empty class => no choices
if (utils::IsNullOrEmptyString($sClass)) {
@@ -75,14 +76,77 @@ class AttributeChoiceFormBlock extends ChoiceFormBlock
return;
}
/** List attributes @var ModelReflection $oModelReflection */
$aAttributeCodes = self::ListAttributeCodesByCategory($sClass, $sCategory);
$oOptionsRegister->SetOption('choices', $aAttributeCodes);
}
/**
* @param string $sClass
* @param string|null $sCategory
*
* @return array
* @throws \Combodo\iTop\Service\DependencyInjection\DIException
*/
public static function ListAttributeCodesByCategory(string $sClass, string $sCategory = ''): array
{
$oModelReflection = DIService::GetInstance()->GetService('ModelReflection');
$aAttributeCodes = array_keys($oModelReflection->ListAttributes($sClass, $sAttType));
$aAttributes = [];
foreach ($aAttributeCodes as $sAttCode) {
$sLabel = $oModelReflection->GetLabel($sClass, $sAttCode);
$aAttributes[$sLabel] = $sAttCode;
$aAttributeCodes = [];
switch ($sCategory) {
case 'numeric':
foreach ($oModelReflection->ListAttributes($sClass, 'AttributeDecimal,AttributeDuration,AttributeInteger,AttributePercentage,AttributeSubItem') as $sAttCode => $sAttType) {
$sLabel = $oModelReflection->GetLabel($sClass, $sAttCode);
$aAttributeCodes[$sLabel] = $sAttCode;
}
break;
case 'group_by':
$aForbiddenAttType = [
'AttributeLinkedSet',
'AttributeFriendlyName',
'iAttributeNoGroupBy', //we cannot only use iAttributeNoGroupBy since this method is also used by the designer who do not have access to the classes' PHP reflection API. So the known classes has to be listed altogether
'AttributeOneWayPassword',
'AttributeEncryptedString',
'AttributePassword',
];
foreach ($oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) {
foreach ($aForbiddenAttType as $sForbiddenAttType) {
if (is_a($sAttType, $sForbiddenAttType, true)) {
continue 2;
}
}
$sLabel = $oModelReflection->GetLabel($sClass, $sAttCode);
$aAttributeCodes[$sLabel] = $sAttCode;
}
break;
case 'enum':
foreach ($oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) {
if (is_a($sAttType, 'AttributeEnum', true)) {
$sLabel = $oModelReflection->GetLabel($sClass, $sAttCode);
$aAttributeCodes[$sLabel] = $sAttCode;
}
}
break;
case 'date':
foreach ($oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) {
if (is_a($sAttType, 'AttributeDateTime', true)) {
$sLabel = $oModelReflection->GetLabel($sClass, $sAttCode);
$aAttributeCodes[$sLabel] = $sAttCode;
}
}
break;
case '':
foreach ($oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) {
$sLabel = $oModelReflection->GetLabel($sClass, $sAttCode);
$aAttributeCodes[$sLabel] = $sAttCode;
}
break;
}
$oOptionsRegister->SetOption('choices', $aAttributes);
return $aAttributeCodes;
}
}

View File

@@ -25,8 +25,8 @@ use Exception;
class AttributeValueChoiceFormBlock extends ChoiceFormBlock
{
// inputs
public const INPUT_CLASS_NAME = 'input_class_name';
public const INPUT_ATTRIBUTE = 'input_attribute';
public const INPUT_CLASS_NAME = 'class';
public const INPUT_ATTRIBUTE = 'attribute';
/** @inheritdoc */
protected function RegisterOptions(OptionsRegister $oOptionsRegister): void

View File

@@ -0,0 +1,73 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Forms\Block\DataModel\Dashlet;
use Combodo\iTop\Forms\Block\Base\ChoiceFormBlock;
use Combodo\iTop\Forms\Block\DataModel\AttributeChoiceFormBlock;
use Combodo\iTop\Forms\IO\Format\ClassIOFormat;
use Combodo\iTop\Forms\Register\IORegister;
use Combodo\iTop\Forms\Register\OptionsRegister;
use Dict;
/**
* A block to manage an aggregation function list
*
* @package Combodo\iTop\Forms\Block\DataModel\Dashlet
* @since 3.3.0
*/
class AggregateFunctionFormBlock extends ChoiceFormBlock
{
public const INPUT_CLASS_NAME = 'class';
/** @inheritdoc */
protected function RegisterIO(IORegister $oIORegister): void
{
parent::RegisterIO($oIORegister);
$oIORegister->AddInput(self::INPUT_CLASS_NAME, ClassIOFormat::class);
}
/**
* @param \Combodo\iTop\Forms\Register\OptionsRegister $oOptionsRegister
*
* @throws \Combodo\iTop\Forms\Block\FormBlockException
* @throws \Combodo\iTop\Forms\Register\RegisterException
* @throws \Combodo\iTop\Service\DependencyInjection\DIException
*/
public function UpdateOptions(OptionsRegister $oOptionsRegister): void
{
parent::UpdateOptions($oOptionsRegister);
$sClass = strval($this->GetInputValue(self::INPUT_CLASS_NAME));
$aFunctionAttributes = AttributeChoiceFormBlock::ListAttributeCodesByCategory($sClass, 'numeric');
$aFunctions = $this->GetAllowedFunctions($aFunctionAttributes);
$oOptionsRegister->SetOption('choices', $aFunctions);
}
/**
* @param array $aFunctionAttributes
*
* @return array
*/
protected function GetAllowedFunctions(array $aFunctionAttributes): array
{
$aFunctions = [];
$aFunctions[Dict::S('UI:GroupBy:count')] = 'count';
if (!empty($aFunctionAttributes)) {
$aFunctions[Dict::S('UI:GroupBy:sum')] = 'sum';
$aFunctions[Dict::S('UI:GroupBy:avg')] = 'avg';
$aFunctions[Dict::S('UI:GroupBy:min')] = 'min';
$aFunctions[Dict::S('UI:GroupBy:max')] = 'max';
}
return $aFunctions;
}
}

View File

@@ -0,0 +1,73 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Forms\Block\DataModel\Dashlet;
use Combodo\iTop\Forms\Block\DataModel\AttributeChoiceFormBlock;
use Combodo\iTop\Forms\Block\FormBlockException;
use Combodo\iTop\Forms\Register\OptionsRegister;
use Combodo\iTop\Service\DependencyInjection\DIService;
use Dict;
use Exception;
/**
* A block to manage an attribute of a data model class for grouping purpose
*
* @package Combodo\iTop\Forms\Block\DataModel\Dashlet
* @since 3.3.0
*/
class ClassAttributeGroupByFormBlock extends AttributeChoiceFormBlock
{
/**
* @param \Combodo\iTop\Forms\Register\OptionsRegister $oOptionsRegister
*
* @throws \Combodo\iTop\Forms\Block\FormBlockException
* @throws \Combodo\iTop\Forms\Register\RegisterException
* @throws \Combodo\iTop\Service\DependencyInjection\DIException
*/
public function UpdateOptions(OptionsRegister $oOptionsRegister): void
{
parent::UpdateOptions($oOptionsRegister);
$oModelReflection = DIService::GetInstance()->GetService('ModelReflection');
$aGroupBy = [];
try {
$sClass = strval($this->GetInputValue(self::INPUT_CLASS_NAME));
foreach ($oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) {
// For external fields, find the real type of the target
$sExtFieldAttCode = $sAttCode;
$sTargetClass = $sClass;
while (is_a($sAttType, 'AttributeExternalField', true)) {
$sExtKeyAttCode = $oModelReflection->GetAttributeProperty($sTargetClass, $sExtFieldAttCode, 'extkey_attcode');
$sTargetAttCode = $oModelReflection->GetAttributeProperty($sTargetClass, $sExtFieldAttCode, 'target_attcode');
$sTargetClass = $oModelReflection->GetAttributeProperty($sTargetClass, $sExtKeyAttCode, 'targetclass');
$aTargetAttCodes = AttributeChoiceFormBlock::ListAttributeCodesByCategory($sTargetClass, 'group_by');
$sAttType = $aTargetAttCodes[$sTargetAttCode];
$sExtFieldAttCode = $sTargetAttCode;
}
$sLabel = $oModelReflection->GetLabel($sClass, $sAttCode);
if (!in_array($sLabel, $aGroupBy)) {
$aGroupBy[$sLabel] = $sAttCode;
if (is_a($sAttType, 'AttributeDateTime', true)) {
$aGroupBy[Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Hour', $sLabel)] = $sAttCode.':hour';
$aGroupBy[Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Month', $sLabel)] = $sAttCode.':month';
$aGroupBy[Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-Year', $sLabel)] = $sAttCode.':year';
$aGroupBy[Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfWeek', $sLabel)] = $sAttCode.':day_of_week';
$aGroupBy[Dict::Format('UI:DashletGroupBy:Prop-GroupBy:Select-DayOfMonth', $sLabel)] = $sAttCode.':day_of_month';
}
}
}
ksort($aGroupBy);
} catch (Exception $e) {
throw new FormBlockException(__METHOD__.': block issue', 0, $e);
}
$oOptionsRegister->SetOption('choices', $aGroupBy);
}
}

View File

@@ -24,7 +24,7 @@ use Combodo\iTop\Forms\Register\OptionsRegister;
class OqlFormBlock extends TextAreaFormBlock
{
// outputs
public const OUTPUT_SELECTED_CLASS = 'output_selected_class';
public const OUTPUT_SELECTED_CLASS = 'selected_class';
/** @inheritdoc */
public function GetFormType(): string

View File

@@ -21,8 +21,8 @@ use Combodo\iTop\Forms\Register\IORegister;
class BooleanExpressionFormBlock extends AbstractExpressionFormBlock
{
// Outputs
public const OUTPUT_RESULT = "output_result";
public const OUTPUT_NOT_RESULT = "output_not_result";
public const OUTPUT_RESULT = "result";
public const OUTPUT_NOT_RESULT = "not_result";
/** @inheritdoc */
protected function RegisterIO(IORegister $oIORegister): void
@@ -38,7 +38,8 @@ class BooleanExpressionFormBlock extends AbstractExpressionFormBlock
* @param string $sEventType
*
* @return mixed
* @throws FormBlockException
* @throws \Combodo\iTop\Forms\Block\FormBlockException
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function ComputeExpression(string $sEventType): mixed
{

View File

@@ -21,7 +21,7 @@ use Combodo\iTop\Forms\Register\IORegister;
class NumberExpressionFormBlock extends AbstractExpressionFormBlock
{
// Outputs
public const OUTPUT_RESULT = "output_result";
public const OUTPUT_RESULT = "result";
/** @inheritdoc */
protected function RegisterIO(IORegister $oIORegister): void
@@ -36,7 +36,8 @@ class NumberExpressionFormBlock extends AbstractExpressionFormBlock
* @param string $sEventType
*
* @return mixed
* @throws FormBlockException
* @throws \Combodo\iTop\Forms\Block\FormBlockException
* @throws \Combodo\iTop\Forms\Register\RegisterException
*/
public function ComputeExpression(string $sEventType): mixed
{

View File

@@ -22,7 +22,7 @@ class FormsController extends Controller
public function __construct($sViewPath = '', $sModuleName = 'core', $aAdditionalPaths = [])
{
$sModuleName = 'core';
$sViewPath = APPROOT.'templates';
$sViewPath = APPROOT.'templates/application/forms';
parent::__construct($sViewPath, $sModuleName, $aAdditionalPaths);
}
@@ -43,14 +43,8 @@ class FormsController extends Controller
IssueLog::Info('form is valid');
}
// Retrieve form data
$aData = $oRequest->request->all($sDashletId);
// Compute blocks to redraw
$aBlocksToRedraw = FormTypeHelper::ComputeBlocksToRedraw($oFormBlock, $oForm, $aData['_turbo_trigger']);
// Display turbo response
$this->DisplayTurboAjaxPage($aBlocksToRedraw);
$this->HandleFormSubmitted($oFormBlock, $oForm);
return;
}
@@ -58,13 +52,13 @@ class FormsController extends Controller
$this->DisplayPage([
'form' => $oForm->createView(),
'sAction' => utils::GetAbsoluteUrlAppRoot().'pages/UI.php?route=forms.dashlet_configuration&dashlet_code='.urlencode($sDashletId),
], 'BasicForm');
], 'itop_form');
} catch (Exception $e) {
ItopSdkFormDemonstratorLog::Exception($e->getMessage(), $e);
$this->DisplayPage([
'sError' => $e->getMessage(),
], 'BasicForm');
], 'itop_error');
return;
}

View File

@@ -0,0 +1,30 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\Forms\IO\Format;
use Combodo\iTop\Forms\IO\Format\AbstractIOFormat;
class IntegerIOFormat extends AbstractIOFormat
{
public int $oValue;
public function __construct(string $oValue)
{
$this->oValue = intval($oValue);
}
public function __toString(): string
{
return strval($this->oValue);
}
public function jsonSerialize(): mixed
{
return strval($this->oValue);
}
}

View File

@@ -9,6 +9,9 @@ namespace Combodo\iTop\PropertyTree;
use Combodo\iTop\PropertyTree\ValueType\AbstractValueType;
/**
* @since 3.3.0
*/
abstract class AbstractProperty
{
/** @var array<AbstractProperty> */

View File

@@ -7,6 +7,9 @@
namespace Combodo\iTop\PropertyTree;
/**
* @since 3.3.0
*/
class CollectionOfTrees extends AbstractProperty
{
}

View File

@@ -7,6 +7,9 @@
namespace Combodo\iTop\PropertyTree;
/**
* @since 3.3.0
*/
class CollectionOfValues extends AbstractProperty
{
}

View File

@@ -7,6 +7,9 @@
namespace Combodo\iTop\PropertyTree;
/**
* @since 3.3.0
*/
class Property extends AbstractProperty
{
}

View File

@@ -7,6 +7,9 @@
namespace Combodo\iTop\PropertyTree;
/**
* @since 3.3.0
*/
class PropertyTree extends AbstractProperty
{
}

View File

@@ -7,6 +7,10 @@
namespace Combodo\iTop\PropertyTree\ValueType;
/**
* @since 3.3.0
*/
abstract class AbstractValueType
{
abstract public function getFormBlockClass(): string;
}

View File

@@ -0,0 +1,21 @@
<?php
/*
* @copyright Copyright (C) 2010-2025 Combodo SAS
* @license http://opensource.org/licenses/AGPL-3.0
*/
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\Forms\Block\DataModel\Dashlet\AggregateFunctionFormBlock;
/**
* @since 3.3.0
*/
class ValueTypeAggregateFunction extends AbstractValueType
{
public function getFormBlockClass(): string
{
return AggregateFunctionFormBlock::class;
}
}

View File

@@ -7,6 +7,16 @@
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\Forms\Block\Base\ChoiceFormBlock;
use Combodo\iTop\Forms\Block\Base\FormBlock;
/**
* @since 3.3.0
*/
class ValueTypeChoice extends AbstractValueType
{
public function getFormBlockClass(): string
{
return ChoiceFormBlock::class;
}
}

View File

@@ -7,6 +7,15 @@
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\Forms\Block\DataModel\AttributeChoiceFormBlock;
/**
* @since 3.3.0
*/
class ValueTypeClassAttribute extends AbstractValueType
{
public function getFormBlockClass(): string
{
return AttributeChoiceFormBlock::class;
}
}

View File

@@ -7,6 +7,15 @@
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\Forms\Block\DataModel\Dashlet\ClassAttributeGroupByFormBlock;
/**
* @since 3.3.0
*/
class ValueTypeClassAttributeGroupBy extends AbstractValueType
{
public function getFormBlockClass(): string
{
return ClassAttributeGroupByFormBlock::class;
}
}

View File

@@ -7,6 +7,15 @@
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\Forms\Block\DataModel\AttributeValueChoiceFormBlock;
/**
* @since 3.3.0
*/
class ValueTypeClassAttributeValue extends AbstractValueType
{
public function getFormBlockClass(): string
{
return AttributeValueChoiceFormBlock::class;
}
}

View File

@@ -7,6 +7,15 @@
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\Forms\Block\Base\IntegerFormBlock;
/**
* @since 3.3.0
*/
class ValueTypeInteger extends AbstractValueType
{
public function getFormBlockClass(): string
{
return IntegerFormBlock::class;
}
}

View File

@@ -7,6 +7,15 @@
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\Forms\Block\DataModel\LabelFormBlock;
/**
* @since 3.3.0
*/
class ValueTypeLabel extends AbstractValueType
{
public function getFormBlockClass(): string
{
return LabelFormBlock::class;
}
}

View File

@@ -7,6 +7,15 @@
namespace Combodo\iTop\PropertyTree\ValueType;
use Combodo\iTop\Forms\Block\DataModel\OqlFormBlock;
/**
* @since 3.3.0
*/
class ValueTypeOQL extends AbstractValueType
{
public function getFormBlockClass(): string
{
return OqlFormBlock::class;
}
}

View File

@@ -7,6 +7,13 @@
namespace Combodo\iTop\PropertyTree\ValueType;
/**
* @since 3.3.0
*/
class ValueTypeProfileName extends AbstractValueType
{
public function getFormBlockClass(): string
{
return '';
}
}

View File

@@ -0,0 +1,20 @@
{# @copyright Copyright (C) 2010-2025 Combodo SARL #}
{# @license http://opensource.org/licenses/AGPL-3.0 #}
{% if form and not bFormInModal %}
{% if sAction %}
{% UITurboForm Standard { oFormView: form, sAction: sAction } %}
<div class="form-buttons">
<button type="submit" name="form-action" value="submit" class="ibo-button ibo-button ibo-is-regular ibo-is-primary">{{ 'Form:Confirm'|dict_s }}</button>
<button type="submit" name="form-action" value="reset" class="ibo-button ibo-button ibo-is-regular">{{ 'Form:Reset'|dict_s }}</button>
</div>
{% EndUITurboForm %}
{% else %}
{% UITurboForm Standard { oFormView: form } %}
<div class="form-buttons">
<button type="submit" name="form-action" value="submit" class="ibo-button ibo-button ibo-is-regular ibo-is-primary">{{ 'Form:Confirm'|dict_s }}</button>
<button type="submit" name="form-action" value="reset" class="ibo-button ibo-button ibo-is-regular">{{ 'Form:Reset'|dict_s }}</button>
</div>
{% EndUITurboForm %}
{% endif %}
{% endif %}