diff --git a/application/datamodel.application.xml b/application/datamodel.application.xml index 0cca2b0aa..a294dbe2a 100644 --- a/application/datamodel.application.xml +++ b/application/datamodel.application.xml @@ -1,10 +1,12 @@ - + cmdbAbstractObject - /* Resource access control abstraction. Can be herited by abstract resource access control classes. Generaly controlled using UR_ACTION_MODIFY access right. */ + /* Resource access control abstraction. Can be herited by abstract resource access control classes. Generally controlled using UR_ACTION_MODIFY access right. */ true @@ -552,7 +554,7 @@ Call $this->AddInitialAttributeFlags($sAttCode, $iFlags) for all the initial att - The login step result code (LoginWebPage::EXIT_CODE_...) + The login step result code (LoginWebPage::EXIT_CODE_...) integer @@ -849,34 +851,31 @@ Call $this->AddInitialAttributeFlags($sAttCode, $iFlags) for all the initial att - - + + + Dashlet - - - - - - - - + + + + + + + + - - - - - - {{query.selected_class}} + + + - - - - - + {{query.selected_class}} + + + bars @@ -891,31 +890,25 @@ Call $this->AddInitialAttributeFlags($sAttCode, $iFlags) for all the initial att - - - - - + + + {{query.selected_class}} - - - - - {{aggregation_function.value != 'count'}} - + + + + {{aggregation_function.value != 'count'}} {{query.selected_class}} numeric - - - - - + + + @@ -924,16 +917,13 @@ Call $this->AddInitialAttributeFlags($sAttCode, $iFlags) for all the initial att - - - - - {{order_by.value = 'function'}} - - - - - + + + + {{order_by.value = 'function'}} + + + @@ -945,98 +935,87 @@ Call $this->AddInitialAttributeFlags($sAttCode, $iFlags) for all the initial att - - desc - - - - + + + + + Dashlet - - - - + + + + bizmodel - - - - - + + + + + Dashlet - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + - - - - - + + + {{query.selected_class}} enum - - - - - + + + + {{query.selected_class}} {{group_by.attribute}} - - - - - + + + + + + Dashlet - - - - - - - - - - - - + + + + + + + + + + + + Dashlet - - - - - - - - - - - - - + + + + + + + + + + @@ -1046,22 +1025,20 @@ Call $this->AddInitialAttributeFlags($sAttCode, $iFlags) for all the initial att false - - - - - + + + + + Dashlet - - - - - - - - - - - + + + + + + + + + diff --git a/lib/autoload.php b/lib/autoload.php index db10dc867..9ee03077e 100644 --- a/lib/autoload.php +++ b/lib/autoload.php @@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) { echo $err; } } - trigger_error( - $err, - E_USER_ERROR - ); + throw new RuntimeException($err); } require_once __DIR__ . '/composer/autoload_real.php'; diff --git a/lib/composer/autoload_classmap.php b/lib/composer/autoload_classmap.php index 4cea94364..95caade5c 100644 --- a/lib/composer/autoload_classmap.php +++ b/lib/composer/autoload_classmap.php @@ -507,8 +507,6 @@ return array( 'Combodo\\iTop\\Forms\\Block\\FormBlockHelper' => $baseDir . '/sources/Forms/Block/FormBlockHelper.php', 'Combodo\\iTop\\Forms\\Block\\FormBlockService' => $baseDir . '/sources/Forms/Block/FormBlockService.php', 'Combodo\\iTop\\Forms\\Block\\IFormBlock' => $baseDir . '/sources/Forms/Block/IFormBlock.php', - 'Combodo\\iTop\\Forms\\Compiler\\FormsCompiler' => $baseDir . '/sources/Forms/Compiler/FormsCompiler.php', - 'Combodo\\iTop\\Forms\\Compiler\\FormsCompilerException' => $baseDir . '/sources/Forms/Compiler/FormsCompilerException.php', 'Combodo\\iTop\\Forms\\Controller\\FormsController' => $baseDir . '/sources/Forms/Controller/FormsController.php', 'Combodo\\iTop\\Forms\\FormBuilder\\DependencyHandler' => $baseDir . '/sources/Forms/FormBuilder/DependencyHandler.php', 'Combodo\\iTop\\Forms\\FormBuilder\\DependencyMap' => $baseDir . '/sources/Forms/FormBuilder/DependencyMap.php', @@ -551,30 +549,35 @@ return array( 'Combodo\\iTop\\Forms\\Validator\\AttributeExist' => $baseDir . '/sources/Forms/Validator/AttributeExist.php', 'Combodo\\iTop\\Forms\\Validator\\AttributeExistValidator' => $baseDir . '/sources/Forms/Validator/AttributeExistValidator.php', 'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => $baseDir . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php', - 'Combodo\\iTop\\PropertyTree\\AbstractProperty' => $baseDir . '/sources/PropertyTree/AbstractProperty.php', - 'Combodo\\iTop\\PropertyTree\\Property' => $baseDir . '/sources/PropertyTree/Property.php', - 'Combodo\\iTop\\PropertyTree\\PropertyTree' => $baseDir . '/sources/PropertyTree/PropertyTree.php', - 'Combodo\\iTop\\PropertyTree\\PropertyTreeDesign' => $baseDir . '/sources/PropertyTree/PropertyTreeDesign.php', - 'Combodo\\iTop\\PropertyTree\\PropertyTreeException' => $baseDir . '/sources/PropertyTree/PropertyTreeException.php', - 'Combodo\\iTop\\PropertyTree\\PropertyTreeFactory' => $baseDir . '/sources/PropertyTree/PropertyTreeFactory.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\\ValueTypeBoolean' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeBoolean.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoice' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeChoice.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoiceFromInput' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeChoiceFromInput.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClass' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClass.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttribute' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttribute.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeGroupBy' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeGroupBy.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeValue' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeValue.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeCollection' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeCollection.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeFactory' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeFactory.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeIcon' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeIcon.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeInteger' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeInteger.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeLabel' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeLabel.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeOQL' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeOQL.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeProfileName' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeProfileName.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeString' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeString.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeText' => $baseDir . '/sources/PropertyTree/ValueType/ValueTypeText.php', + 'Combodo\\iTop\\PropertyType\\Compiler\\PropertyTypeCompiler' => $baseDir . '/sources/PropertyType/Compiler/PropertyTypeCompiler.php', + 'Combodo\\iTop\\PropertyType\\Compiler\\PropertyTypeCompilerException' => $baseDir . '/sources/PropertyType/Compiler/PropertyTypeCompilerException.php', + 'Combodo\\iTop\\PropertyType\\PropertyType' => $baseDir . '/sources/PropertyType/PropertyType.php', + 'Combodo\\iTop\\PropertyType\\PropertyTypeDesign' => $baseDir . '/sources/PropertyType/PropertyTypeDesign.php', + 'Combodo\\iTop\\PropertyType\\PropertyTypeException' => $baseDir . '/sources/PropertyType/PropertyTypeException.php', + 'Combodo\\iTop\\PropertyType\\PropertyTypeFactory' => $baseDir . '/sources/PropertyType/PropertyTypeFactory.php', + 'Combodo\\iTop\\PropertyType\\PropertyTypeService' => $baseDir . '/sources/PropertyType/PropertyTypeService.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\AbstractValueType' => $baseDir . '/sources/PropertyType/ValueType/AbstractValueType.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Branch\\AbstractBranchValueType' => $baseDir . '/sources/PropertyType/ValueType/Branch/AbstractBranchValueType.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Branch\\ValueTypeCollection' => $baseDir . '/sources/PropertyType/ValueType/Branch/ValueTypeCollection.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Branch\\ValueTypePropertyTree' => $baseDir . '/sources/PropertyType/ValueType/Branch/ValueTypePropertyTree.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\AbstractLeafValueType' => $baseDir . '/sources/PropertyType/ValueType/Leaf/AbstractLeafValueType.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeAggregateFunction' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeAggregateFunction.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeBoolean' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeBoolean.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeChoice' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeChoice.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeChoiceFromInput' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeChoiceFromInput.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeClass' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeClass.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeClassAttribute' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeClassAttribute.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeClassAttributeGroupBy' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeClassAttributeGroupBy.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeClassAttributeValue' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeClassAttributeValue.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeCollectionOfValues' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeCollectionOfValues.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeIcon' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeIcon.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeInteger' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeInteger.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeLabel' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeLabel.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeOQL' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeOQL.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeProfileName' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeProfileName.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeString' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeString.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeText' => $baseDir . '/sources/PropertyType/ValueType/Leaf/ValueTypeText.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\ValueTypeFactory' => $baseDir . '/sources/PropertyType/ValueType/ValueTypeFactory.php', 'Combodo\\iTop\\Renderer\\BlockRenderer' => $baseDir . '/sources/Renderer/BlockRenderer.php', 'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => $baseDir . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php', 'Combodo\\iTop\\Renderer\\Bootstrap\\BsFormRenderer' => $baseDir . '/sources/Renderer/Bootstrap/BsFormRenderer.php', @@ -3932,5 +3935,5 @@ return array( 'privUITransactionFile' => $baseDir . '/application/transaction.class.inc.php', 'privUITransactionSession' => $baseDir . '/application/transaction.class.inc.php', 'utils' => $baseDir . '/application/utils.inc.php', - '�' => $vendorDir . '/symfony/cache/Traits/ValueWrapper.php', + '©' => $vendorDir . '/symfony/cache/Traits/ValueWrapper.php', ); diff --git a/lib/composer/autoload_psr4.php b/lib/composer/autoload_psr4.php index 15036b1a5..d71f96f11 100644 --- a/lib/composer/autoload_psr4.php +++ b/lib/composer/autoload_psr4.php @@ -62,7 +62,7 @@ return array( 'Psr\\Cache\\' => array($vendorDir . '/psr/cache/src'), 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), 'Pelago\\Emogrifier\\' => array($vendorDir . '/pelago/emogrifier/src'), - 'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-client/src', $vendorDir . '/league/oauth2-google/src'), + 'League\\OAuth2\\Client\\' => array($vendorDir . '/league/oauth2-google/src', $vendorDir . '/league/oauth2-client/src'), 'GuzzleHttp\\Psr7\\' => array($vendorDir . '/guzzlehttp/psr7/src'), 'GuzzleHttp\\Promise\\' => array($vendorDir . '/guzzlehttp/promises/src'), 'GuzzleHttp\\' => array($vendorDir . '/guzzlehttp/guzzle/src'), diff --git a/lib/composer/autoload_static.php b/lib/composer/autoload_static.php index 92a502981..63fcc0a7f 100644 --- a/lib/composer/autoload_static.php +++ b/lib/composer/autoload_static.php @@ -345,8 +345,8 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f ), 'League\\OAuth2\\Client\\' => array ( - 0 => __DIR__ . '/..' . '/league/oauth2-client/src', - 1 => __DIR__ . '/..' . '/league/oauth2-google/src', + 0 => __DIR__ . '/..' . '/league/oauth2-google/src', + 1 => __DIR__ . '/..' . '/league/oauth2-client/src', ), 'GuzzleHttp\\Psr7\\' => array ( @@ -893,8 +893,6 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Forms\\Block\\FormBlockHelper' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockHelper.php', 'Combodo\\iTop\\Forms\\Block\\FormBlockService' => __DIR__ . '/../..' . '/sources/Forms/Block/FormBlockService.php', 'Combodo\\iTop\\Forms\\Block\\IFormBlock' => __DIR__ . '/../..' . '/sources/Forms/Block/IFormBlock.php', - 'Combodo\\iTop\\Forms\\Compiler\\FormsCompiler' => __DIR__ . '/../..' . '/sources/Forms/Compiler/FormsCompiler.php', - 'Combodo\\iTop\\Forms\\Compiler\\FormsCompilerException' => __DIR__ . '/../..' . '/sources/Forms/Compiler/FormsCompilerException.php', 'Combodo\\iTop\\Forms\\Controller\\FormsController' => __DIR__ . '/../..' . '/sources/Forms/Controller/FormsController.php', 'Combodo\\iTop\\Forms\\FormBuilder\\DependencyHandler' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/DependencyHandler.php', 'Combodo\\iTop\\Forms\\FormBuilder\\DependencyMap' => __DIR__ . '/../..' . '/sources/Forms/FormBuilder/DependencyMap.php', @@ -937,30 +935,35 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'Combodo\\iTop\\Forms\\Validator\\AttributeExist' => __DIR__ . '/../..' . '/sources/Forms/Validator/AttributeExist.php', 'Combodo\\iTop\\Forms\\Validator\\AttributeExistValidator' => __DIR__ . '/../..' . '/sources/Forms/Validator/AttributeExistValidator.php', 'Combodo\\iTop\\PhpParser\\Evaluation\\PhpExpressionEvaluator' => __DIR__ . '/../..' . '/sources/PhpParser/Evaluation/PhpExpressionEvaluator.php', - 'Combodo\\iTop\\PropertyTree\\AbstractProperty' => __DIR__ . '/../..' . '/sources/PropertyTree/AbstractProperty.php', - 'Combodo\\iTop\\PropertyTree\\Property' => __DIR__ . '/../..' . '/sources/PropertyTree/Property.php', - 'Combodo\\iTop\\PropertyTree\\PropertyTree' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTree.php', - 'Combodo\\iTop\\PropertyTree\\PropertyTreeDesign' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTreeDesign.php', - 'Combodo\\iTop\\PropertyTree\\PropertyTreeException' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTreeException.php', - 'Combodo\\iTop\\PropertyTree\\PropertyTreeFactory' => __DIR__ . '/../..' . '/sources/PropertyTree/PropertyTreeFactory.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\\ValueTypeBoolean' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeBoolean.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoice' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeChoice.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeChoiceFromInput' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeChoiceFromInput.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClass' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClass.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttribute' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttribute.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeGroupBy' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeGroupBy.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeClassAttributeValue' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeClassAttributeValue.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeCollection' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeCollection.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeFactory' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeFactory.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeIcon' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeIcon.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeInteger' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeInteger.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeLabel' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeLabel.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeOQL' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeOQL.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeProfileName' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeProfileName.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeString' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeString.php', - 'Combodo\\iTop\\PropertyTree\\ValueType\\ValueTypeText' => __DIR__ . '/../..' . '/sources/PropertyTree/ValueType/ValueTypeText.php', + 'Combodo\\iTop\\PropertyType\\Compiler\\PropertyTypeCompiler' => __DIR__ . '/../..' . '/sources/PropertyType/Compiler/PropertyTypeCompiler.php', + 'Combodo\\iTop\\PropertyType\\Compiler\\PropertyTypeCompilerException' => __DIR__ . '/../..' . '/sources/PropertyType/Compiler/PropertyTypeCompilerException.php', + 'Combodo\\iTop\\PropertyType\\PropertyType' => __DIR__ . '/../..' . '/sources/PropertyType/PropertyType.php', + 'Combodo\\iTop\\PropertyType\\PropertyTypeDesign' => __DIR__ . '/../..' . '/sources/PropertyType/PropertyTypeDesign.php', + 'Combodo\\iTop\\PropertyType\\PropertyTypeException' => __DIR__ . '/../..' . '/sources/PropertyType/PropertyTypeException.php', + 'Combodo\\iTop\\PropertyType\\PropertyTypeFactory' => __DIR__ . '/../..' . '/sources/PropertyType/PropertyTypeFactory.php', + 'Combodo\\iTop\\PropertyType\\PropertyTypeService' => __DIR__ . '/../..' . '/sources/PropertyType/PropertyTypeService.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\AbstractValueType' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/AbstractValueType.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Branch\\AbstractBranchValueType' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Branch/AbstractBranchValueType.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Branch\\ValueTypeCollection' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Branch/ValueTypeCollection.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Branch\\ValueTypePropertyTree' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Branch/ValueTypePropertyTree.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\AbstractLeafValueType' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/AbstractLeafValueType.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeAggregateFunction' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeAggregateFunction.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeBoolean' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeBoolean.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeChoice' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeChoice.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeChoiceFromInput' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeChoiceFromInput.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeClass' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeClass.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeClassAttribute' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeClassAttribute.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeClassAttributeGroupBy' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeClassAttributeGroupBy.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeClassAttributeValue' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeClassAttributeValue.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeCollectionOfValues' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeCollectionOfValues.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeIcon' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeIcon.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeInteger' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeInteger.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeLabel' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeLabel.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeOQL' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeOQL.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeProfileName' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeProfileName.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeString' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeString.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\Leaf\\ValueTypeText' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/Leaf/ValueTypeText.php', + 'Combodo\\iTop\\PropertyType\\ValueType\\ValueTypeFactory' => __DIR__ . '/../..' . '/sources/PropertyType/ValueType/ValueTypeFactory.php', 'Combodo\\iTop\\Renderer\\BlockRenderer' => __DIR__ . '/../..' . '/sources/Renderer/BlockRenderer.php', 'Combodo\\iTop\\Renderer\\Bootstrap\\BsFieldRendererMappings' => __DIR__ . '/../..' . '/sources/Renderer/Bootstrap/BsFieldRendererMappings.php', 'Combodo\\iTop\\Renderer\\Bootstrap\\BsFormRenderer' => __DIR__ . '/../..' . '/sources/Renderer/Bootstrap/BsFormRenderer.php', @@ -4318,7 +4321,7 @@ class ComposerStaticInit7f81b4a2a468a061c306af5e447a9a9f 'privUITransactionFile' => __DIR__ . '/../..' . '/application/transaction.class.inc.php', 'privUITransactionSession' => __DIR__ . '/../..' . '/application/transaction.class.inc.php', 'utils' => __DIR__ . '/../..' . '/application/utils.inc.php', - '�' => __DIR__ . '/..' . '/symfony/cache/Traits/ValueWrapper.php', + '©' => __DIR__ . '/..' . '/symfony/cache/Traits/ValueWrapper.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/lib/composer/platform_check.php b/lib/composer/platform_check.php index dee74e173..72145773d 100644 --- a/lib/composer/platform_check.php +++ b/lib/composer/platform_check.php @@ -36,8 +36,7 @@ if ($issues) { echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; } } - trigger_error( - 'Composer detected issues in your platform: ' . implode(' ', $issues), - E_USER_ERROR + throw new \RuntimeException( + 'Composer detected issues in your platform: ' . implode(' ', $issues) ); } diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index 5ff67e236..a9366a663 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -23,7 +23,7 @@ use Combodo\iTop\Application\WebPage\iTopWebPage; use Combodo\iTop\Application\WebPage\Page; use Combodo\iTop\DesignElement; use Combodo\iTop\DesignDocument; -use Combodo\iTop\PropertyTree\PropertyTreeDesign; +use Combodo\iTop\PropertyType\PropertyTypeDesign; require_once(APPROOT.'setup/setuputils.class.inc.php'); require_once(APPROOT.'setup/modelfactory.class.inc.php'); @@ -700,9 +700,9 @@ PHP; $oModuleDesignsNode = $this->oFactory->GetNodes('/itop_design/module_designs')->item(0); $this->CompileModuleDesigns($oModuleDesignsNode, $sTempTargetDir, $sFinalTargetDir); - // Create property trees XML files - $oPropertyTreesNode = $this->oFactory->GetNodes('/itop_design/meta/property_trees')->item(0); - $this->CompilePropertyTrees($oPropertyTreesNode, $sTempTargetDir, $sFinalTargetDir); + // Create property types XML files + $oPropertyTypesNode = $this->oFactory->GetNodes('/itop_design/meta/property_types')->item(0); + $this->CompilePropertyTypes($oPropertyTypesNode, $sTempTargetDir, $sFinalTargetDir); // Compile the XML parameters /** @var \MFElement $oParametersNode */ @@ -3577,17 +3577,17 @@ EOF; } } - protected function CompilePropertyTrees(?DOMNode $oPropertyTrees, string $sTempTargetDir, string $sFinalTargetDir): void + protected function CompilePropertyTypes(?DOMNode $oPropertyTypes, string $sTempTargetDir, string $sFinalTargetDir): void { - if ($oPropertyTrees) { - foreach ($oPropertyTrees->GetNodes('property_tree') as $oPropertyTree) { - $oDoc = new PropertyTreeDesign(); - $oClone = $oDoc->importNode($oPropertyTree->cloneNode(true), true); + if ($oPropertyTypes) { + foreach ($oPropertyTypes->GetNodes('property_type') as $oPropertyType) { + $oDoc = new PropertyTypeDesign(); + $oClone = $oDoc->importNode($oPropertyType->cloneNode(true), true); $oDoc->appendChild($oClone); - /** @var DesignElement $oPropertyTree */ - $sExtends = $oPropertyTree->GetChildText('extends', 'Default'); - SetupUtils::builddir($sTempTargetDir.'/core/property_trees/'.$sExtends); - $oDoc->save($sTempTargetDir.'/core/property_trees/'.$sExtends.'/'.$oPropertyTree->getAttribute('id').'.xml'); + /** @var DesignElement $oPropertyType */ + $sExtends = $oPropertyType->GetChildText('extends', 'Default'); + SetupUtils::builddir($sTempTargetDir.'/core/property_types/'.$sExtends); + $oDoc->save($sTempTargetDir.'/core/property_types/'.$sExtends.'/'.$oPropertyType->getAttribute('id').'.xml'); } } } diff --git a/sources/Forms/Block/DataModel/AttributeChoiceFormBlock.php b/sources/Forms/Block/DataModel/AttributeChoiceFormBlock.php index 3e64fce2d..cbc4abf44 100644 --- a/sources/Forms/Block/DataModel/AttributeChoiceFormBlock.php +++ b/sources/Forms/Block/DataModel/AttributeChoiceFormBlock.php @@ -94,60 +94,83 @@ class AttributeChoiceFormBlock extends ChoiceFormBlock public static function ListAttributeCodesByCategory(string $sClass, string $sCategory = ''): array { $oModelReflection = ServiceLocator::GetInstance()->get('ModelReflection'); + $aNonGroupableAttributes = [ + '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', + ]; $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; + foreach ($oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) { + $sLabel = $oModelReflection->GetLabel($sClass, $sAttCode); - case 'groupable': - $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; + // 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 = $oModelReflection->ListAttributes($sTargetClass); + $sAttType = $aTargetAttCodes[$sTargetAttCode]; + $sExtFieldAttCode = $sTargetAttCode; + } + + switch ($sCategory) { + case 'numeric': + if (is_a($sAttType, 'AttributeDecimal', true) || + is_a($sAttType, 'AttributeDuration', true) || + is_a($sAttType, 'AttributeInteger', true) || + is_a($sAttType, 'AttributePercentage', true) || + is_a($sAttType, 'AttributeSubItem', true)) { + $aAttributeCodes[$sLabel] = $sAttCode; + } + break; + + case 'groupable': + foreach ($aNonGroupableAttributes as $sNonGroupableAttribute) { + if (is_a($sAttType, $sNonGroupableAttribute, true)) { + break; } } - $sLabel = $oModelReflection->GetLabel($sClass, $sAttCode); $aAttributeCodes[$sLabel] = $sAttCode; - } - break; + break; - case 'enum': - foreach ($oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) { + case 'enum': if (is_a($sAttType, 'AttributeEnum', true)) { - $sLabel = $oModelReflection->GetLabel($sClass, $sAttCode); $aAttributeCodes[$sLabel] = $sAttCode; } - } - break; + break; - case 'date': - foreach ($oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) { + case 'date': if (is_a($sAttType, 'AttributeDateTime', true)) { - $sLabel = $oModelReflection->GetLabel($sClass, $sAttCode); $aAttributeCodes[$sLabel] = $sAttCode; } - } - break; + break; - case '': - foreach ($oModelReflection->ListAttributes($sClass) as $sAttCode => $sAttType) { - $sLabel = $oModelReflection->GetLabel($sClass, $sAttCode); + case 'link': + if (is_a($sAttType, 'AttributeLinkedSet', true) || + is_a($sAttType, 'AttributeLinkedSetIndirect', true) || + is_a($sAttType, 'AttributeExternalKey', true) || + is_a($sAttType, 'AttributeHierarchicalKey', true)) { + $aAttributeCodes[$sLabel] = $sAttCode; + } + break; + + case 'string': + if (is_a($sAttType, 'AttributeString', true)) { + $aAttributeCodes[$sLabel] = $sAttCode; + } + break; + + case 'all': + case '': $aAttributeCodes[$sLabel] = $sAttCode; - } - break; + break; + } } return $aAttributeCodes; diff --git a/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php b/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php index 7f47e5eab..78d4ba59c 100644 --- a/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php +++ b/sources/Forms/Block/DataModel/AttributeValueChoiceFormBlock.php @@ -32,7 +32,6 @@ class AttributeValueChoiceFormBlock extends ChoiceFormBlock protected function RegisterOptions(OptionsRegister $oOptionsRegister): void { parent::RegisterOptions($oOptionsRegister); - $oOptionsRegister->SetOption('multiple', true); $oOptionsRegister->SetOptionArrayValue('attr', 'size', 5); $oOptionsRegister->SetOptionArrayValue('attr', 'style', 'height: auto;'); } diff --git a/sources/Forms/Block/FormBlockService.php b/sources/Forms/Block/FormBlockService.php index ca3e38019..0e7430b7b 100644 --- a/sources/Forms/Block/FormBlockService.php +++ b/sources/Forms/Block/FormBlockService.php @@ -8,7 +8,8 @@ namespace Combodo\iTop\Forms\Block; use Combodo\iTop\Forms\Block\Base\FormBlock; -use Combodo\iTop\Forms\Compiler\FormsCompiler; +use Combodo\iTop\PropertyType\Compiler\PropertyTypeCompiler; +use Combodo\iTop\PropertyType\PropertyTypeService; use Combodo\iTop\Service\Cache\DataModelDependantCache; use Combodo\iTop\Service\DependencyInjection\ServiceLocator; use ModelReflection; @@ -17,14 +18,11 @@ use utils; class FormBlockService { - public const CACHE_POOL = 'Forms'; private static FormBlockService $oInstance; - private DataModelDependantCache $oCacheService; protected function __construct(ModelReflection $oModelReflection = null) { ServiceLocator::GetInstance()->RegisterService('ModelReflection', $oModelReflection ?? new ModelReflectionRuntime()); - $this->oCacheService = DataModelDependantCache::GetInstance(); } final public static function GetInstance(ModelReflection $oModelReflection = null): FormBlockService @@ -42,26 +40,13 @@ class FormBlockService * * @return \Combodo\iTop\Forms\Block\Base\FormBlock * @throws \Combodo\iTop\Forms\Block\FormBlockException - * @throws \Combodo\iTop\Forms\Compiler\FormsCompilerException - * @throws \Combodo\iTop\PropertyTree\PropertyTreeException + * @throws \Combodo\iTop\PropertyType\Compiler\PropertyTypeCompilerException + * @throws \Combodo\iTop\PropertyType\PropertyTypeException * @throws \DOMFormatException */ public function GetFormBlockById(string $sId, string $sType): FormBlock { - $sFilteredId = preg_replace('/[^0-9a-zA-Z_]/', '', $sId); - if (strlen($sFilteredId) === 0 || $sFilteredId !== $sId) { - throw new FormBlockException('Malformed name for block: '.json_encode($sId)); - } - $sCacheKey = $sType.'/'.$sFilteredId; - if (!$this->oCacheService->HasEntry(self::CACHE_POOL, $sCacheKey) || utils::IsDevelopmentEnvironment()) { - // Cache not found, compile the form - $sPHPContent = FormsCompiler::GetInstance()->CompileForm($sFilteredId, $sType); - $this->oCacheService->StorePhpContent(FormBlockService::CACHE_POOL, $sCacheKey, "oCacheService->FetchPHP(self::CACHE_POOL, $sCacheKey); - $sFormBlockClass = 'FormFor__'.$sFilteredId; - - return new $sFormBlockClass($sFilteredId); + return PropertyTypeService::GetInstance()->GetFormBlockById($sId, $sType); } } diff --git a/sources/Forms/Compiler/FormsCompiler.php b/sources/Forms/Compiler/FormsCompiler.php deleted file mode 100644 index c4bb60aae..000000000 --- a/sources/Forms/Compiler/FormsCompiler.php +++ /dev/null @@ -1,90 +0,0 @@ -oCacheService = DataModelDependantCache::GetInstance(); - } - - final public static function GetInstance(): FormsCompiler - { - if (!isset(static::$oInstance)) { - static::$oInstance = new FormsCompiler(); - } - - return static::$oInstance; - } - - /** - * Compile XML property tree into PHP to create the configuration form - * - * @param string $sXMLContent property tree structure in xml - * - * @return string Generated PHP - * @throws \Combodo\iTop\Forms\Compiler\FormsCompilerException - * @throws \Combodo\iTop\PropertyTree\PropertyTreeException - * @throws \DOMFormatException - */ - public function CompileFormFromXML(string $sXMLContent): string - { - $oDoc = new DesignDocument(); - libxml_clear_errors(); - $oDoc->loadXML($sXMLContent); - $aErrors = libxml_get_errors(); - if (count($aErrors) > 0) { - throw new FormsCompilerException('Dashlet properties definition not correctly formatted!'); - } - - /** @var \Combodo\iTop\DesignElement $oRoot */ - $oRoot = $oDoc->firstChild; - $oPropertyTree = PropertyTreeFactory::GetInstance()->CreateTreeFromDom($oRoot); - - return $oPropertyTree->ToPHPFormBlock(); - } - - /** - * @param string $sId - * @param string $sType - * - * @return string Generated PHP - * @throws \Combodo\iTop\Forms\Compiler\FormsCompilerException - * @throws \Combodo\iTop\PropertyTree\PropertyTreeException - * @throws \DOMFormatException - */ - public function CompileForm(string $sId, string $sType): string - { - $sPath = utils::GetAbsoluteModulePath('core')."property_trees/$sType/$sId.xml"; - if (!file_exists($sPath)) { - throw new FormsCompilerException("Properties definition $sType/$sId not present"); - } - - $sXMLContent = file_get_contents($sPath); - - return $this->CompileFormFromXML($sXMLContent); - } -} diff --git a/sources/Forms/Compiler/FormsCompilerException.php b/sources/Forms/Compiler/FormsCompilerException.php deleted file mode 100644 index 9f7d6c101..000000000 --- a/sources/Forms/Compiler/FormsCompilerException.php +++ /dev/null @@ -1,14 +0,0 @@ -aBindingsToInputs; } + /** + * Compatibility rules + * + * @param \Combodo\iTop\Forms\IO\AbstractFormIO $oFormIO + * + * @return bool + */ + public function IsCompatibleWith(AbstractFormIO $oFormIO): bool + { + if ($this->GetDataType() === StringIOFormat::class) { + switch ($oFormIO->GetDataType()) { + case StringIOFormat::class: + case AttributeIOFormat::class: + case ClassIOFormat::class: + return true; + default: + return false; + } + } + return ($oFormIO->GetDataType() === $this->GetDataType()); + } + } diff --git a/sources/Forms/IO/FormBinding.php b/sources/Forms/IO/FormBinding.php index 1dc57438c..d6603bea1 100644 --- a/sources/Forms/IO/FormBinding.php +++ b/sources/Forms/IO/FormBinding.php @@ -29,7 +29,7 @@ class FormBinding // Check IOFormat validity $sSourceDataType = $oSourceIO->GetDataType(); $sDestinationDataType = $oDestinationIO->GetDataType(); - if ($sSourceDataType !== $sDestinationDataType) { + if (!$oDestinationIO->IsCompatibleWith($oSourceIO)) { throw new FormBlockIOException('binding '.json_encode($sSourceDataType).' to '.json_encode($sDestinationDataType).' is not supported'); } $this->oDestinationIO = $oDestinationIO; diff --git a/sources/PropertyTree/AbstractProperty.php b/sources/PropertyTree/AbstractProperty.php deleted file mode 100644 index 87e69487e..000000000 --- a/sources/PropertyTree/AbstractProperty.php +++ /dev/null @@ -1,86 +0,0 @@ - */ - protected array $aChildren = []; - protected ?AbstractValueType $oValueType; - protected ?string $sIdWithPath; - - /** - * Init property tree node from xml dom node - * - * @param \Combodo\iTop\DesignElement $oDomNode - * @param string $sParentId - * - * @return void - * @throws \DOMFormatException - * @throws \Combodo\iTop\PropertyTree\PropertyTreeException - */ - public function InitFromDomNode(DesignElement $oDomNode, ?AbstractProperty $oParent = null): void - { - $this->oParent = $oParent; - $this->sId = $oDomNode->getAttribute('id'); - if (is_null($oParent)) { - $this->sIdWithPath = $this->sId; - } else { - $this->sIdWithPath = $oParent->sIdWithPath.'__'.$this->sId; - } - $this->sLabel = $oDomNode->GetChildText('label'); - } - - abstract public function ToPHPFormBlock(array &$aPHPFragments = []): string; - - public function GetValueType(): ?AbstractValueType - { - return $this->oValueType; - } - - public function AddChild(AbstractProperty $oValueType): void - { - $this->aChildren[] = $oValueType; - } - - public function GetChildren(): array - { - return $this->aChildren; - } - - public function GetSibling(string $sId): ?AbstractProperty - { - if (is_null($this->oParent)) { - return null; - } - - foreach ($this->oParent->GetChildren() as $oSibling) { - if ($oSibling->sId == $sId) { - return $oSibling; - } - } - - return null; - } - - public function GetIdWithPath(): ?string - { - return $this->sIdWithPath; - } - -} diff --git a/sources/PropertyTree/Property.php b/sources/PropertyTree/Property.php deleted file mode 100644 index a7e40bb66..000000000 --- a/sources/PropertyTree/Property.php +++ /dev/null @@ -1,155 +0,0 @@ -GetOptionalElement('value-type'); - if ($oValueTypeNode) { - $this->oValueType = ValueTypeFactory::GetInstance()->CreateValueTypeFromDomNode($oValueTypeNode, $this); - } else { - throw new PropertyTreeException("Node: {$this->sId}, missing value-type in node specification"); - } - - $this->sRelevanceCondition = $oDomNode->GetChildText('relevance-condition'); - } - - /** - * @param $aPHPFragments - * - * @return string - * @throws \Combodo\iTop\PropertyTree\PropertyTreeException - */ - public function ToPHPFormBlock(array &$aPHPFragments = []): string - { - $sFormBlockClass = $this->oValueType->GetFormBlockClass(); - - $sInputs = ''; - $sPrerequisiteExpressions = ''; - if (!is_null($this->sRelevanceCondition)) { - $this->GenerateInputs('visible', $this->sRelevanceCondition, $sPrerequisiteExpressions, $sInputs); - } - - foreach ($this->oValueType->GetInputValues() as $sInputName => $sValue) { - $this->GenerateInputs($sInputName, $sValue, $sPrerequisiteExpressions, $sInputs); - } - - foreach ($this->oValueType->GetDynamicInputValues() as $sInputName => $sValue) { - $this->GenerateInputs($sInputName, $sValue, $sPrerequisiteExpressions, $sInputs, true); - } - - $sLabel = utils::QuoteForPHP($this->sLabel); - $aOptions = [ - 'label' => $sLabel, - ]; - $aOptions += $this->oValueType->GetFormBlockOptions(); - $sOptions = ''; - foreach ($aOptions as $sOption => $sValue) { - $sOptions .= "\t\t\t".utils::QuoteForPHP($sOption)." => $sValue,\n"; - } - $this->oValueType->UpdatePHPFragmentsList($aPHPFragments); - return <<Add('$this->sId', '$sFormBlockClass', [ -$sOptions\t\t]){$sInputs}; - -PHP; - } - - private function GenerateInputs(string $sInputName, string $sValue, string &$sPrerequisiteExpressions, string &$sInputs, bool $bIsDynamic = false): void - { - if (preg_match('/^{{(?\w+)\.(?\w+)}}$/', $sValue, $aMatches) === 1) { - $sVerb = $bIsDynamic ? 'AddInputDependsOn' : 'InputDependsOn'; - $sInputs .= "\n ->$sVerb('$sInputName', '{$aMatches['node']}', '{$aMatches['output']}')"; - } elseif (preg_match('/^{{(?.*)}}$/', $sValue, $aMatches) === 1) { - $sExpression = $aMatches['expression']; - $sBindings = ''; - try { - $oExpression = Expression::FromOQL($sExpression); - } catch (Exception $e) { - throw new PropertyTreeException("Node: {$this->sId}, invalid syntax in condition: ".$e->getMessage()); - } - $aFieldsToResolve = array_unique($oExpression->ListRequiredFields()); - foreach ($aFieldsToResolve as $sFieldToResolve) { - if (preg_match('/(?\w+)\.(?\w+)/', $sFieldToResolve, $aMatches) === 1) { - $sNode = $aMatches['node']; - $oSibling = $this->GetSibling($sNode); - if (is_null($oSibling)) { - // Search in collection - if (is_a($this->oParent?->oValueType ?? null, 'Combodo-ValueType-Collection')) { - $bSourceNodeFound = false; - $aSiblings = $this->oParent->oValueType->GetChildren(); - foreach ($aSiblings as $oSibling) { - if ($oSibling->sId == $sNode) { - $bSourceNodeFound = true; - break; - } - } - if (!$bSourceNodeFound) { - throw new PropertyTreeException("node: {$this->sId}, source: $sNode not found in collection: {$this->oParent->sId}"); - } - } else { - throw new PropertyTreeException("Node: {$this->sId}, invalid source in condition: $sNode"); - } - } - $sOutput = $aMatches['output']; - if (!in_array($sOutput, $oSibling->oValueType->GetOutputs())) { - throw new PropertyTreeException("Node: {$this->sId}, invalid output in condition: $sFieldToResolve"); - } - $sBindings .= "\n ->AddInputDependsOn('{$sNode}.$sOutput', '$sNode', '$sOutput')"; - } else { - throw new PropertyTreeException("Node: {$this->sId}, missing output or source in condition: $sFieldToResolve"); - } - } - - $sExpressionClass = match ($this->oValueType->GetInputType($sInputName)) { - BooleanIOFormat::class => BooleanExpressionFormBlock::class, - StringIOFormat::class, ClassIOFormat::class => StringExpressionFormBlock::class, - NumberIOFormat::class => NumberExpressionFormBlock::class, - default => throw new PropertyTreeException("Node: {$this->sId}, unsupported expression for input type: $sInputName"), - }; - - $sExpression = utils::QuoteForPHP($sExpression); - $sPrerequisiteExpressions = <<Add('{$this->sId}_{$sInputName}_expression', '$sExpressionClass', [ - 'expression' => $sExpression, - ]){$sBindings}; - - -PHP; - $sVerb = $bIsDynamic ? 'AddInputDependsOn' : 'InputDependsOn'; - $sInputs .= "\n ->$sVerb('$sInputName', '{$this->sId}_{$sInputName}_expression', 'result')"; - } else { - $sInputs .= "\n ->SetInputValue('$sInputName', ".utils::QuoteForPHP($sValue).")"; - } - } -} diff --git a/sources/PropertyTree/PropertyTree.php b/sources/PropertyTree/PropertyTree.php deleted file mode 100644 index 39a4c3a3f..000000000 --- a/sources/PropertyTree/PropertyTree.php +++ /dev/null @@ -1,65 +0,0 @@ -GetUniqueElement('nodes')->childNodes as $oNode) { - if ($oNode instanceof DesignElement) { - $this->AddChild($oPropertyTreeFactory->CreateNodeFromDom($oNode, $this)); - } - } - } - - public function ToPHPFormBlock(array &$aPHPFragments = []): string - { - $bIsRoot = (count($aPHPFragments) === 0); - $sLocalPHP = <<sId extends Combodo\iTop\Forms\Block\Base\FormBlock -{ - protected function BuildForm(): void - { -PHP; - - foreach ($this->aChildren as $oProperty) { - $sLocalPHP .= "\n".$oProperty->ToPHPFormBlock($aPHPFragments); - } - - $sLocalPHP .= <<InitFromDomNode($oDomNode, $oParent); - - return $oNode; - } - - /** - * Create a property node from a design element - * - * @param \Combodo\iTop\DesignElement $oDomNode - * @param \Combodo\iTop\PropertyTree\AbstractProperty|null $oParent - * - * @return \Combodo\iTop\PropertyTree\AbstractProperty - * @throws \Combodo\iTop\PropertyTree\PropertyTreeException - * @throws \DOMFormatException - */ - public function CreateNodeFromDom(DesignElement $oDomNode, ?AbstractProperty $oParent = null): AbstractProperty - { - // The class of the property tree node is given by the xsi:type attribute - $oNode = new Property(); - $oNode->InitFromDomNode($oDomNode, $oParent); - - return $oNode; - } -} diff --git a/sources/PropertyTree/ValueType/AbstractValueType.php b/sources/PropertyTree/ValueType/AbstractValueType.php deleted file mode 100644 index aa7ae9cb2..000000000 --- a/sources/PropertyTree/ValueType/AbstractValueType.php +++ /dev/null @@ -1,81 +0,0 @@ -GetFormBlockClass(); - $oBlockNode = new $sBlockNodeClass('foo'); - foreach ($oBlockNode->GetInputs() as $oInput) { - $sInputName = $oInput->GetName(); - $this->aInputs[$sInputName] = $oInput; - $sInputValue = $oDomNode->GetChildText($sInputName); - if (utils::IsNotNullOrEmptyString($sInputValue)) { - $this->aInputValues[$sInputName] = $sInputValue; - } - } - foreach ($oBlockNode->GetOutputs() as $oOutput) { - $this->aOutputs[] = $oOutput->GetName(); - } - } - - public function GetFormBlockOptions(): array - { - return $this->aFormBlockOptionsForPHP; - } - - public function GetInputValues(): array - { - return $this->aInputValues; - } - - public function GetInputType(string $sInputName): string - { - return $this->aInputs[$sInputName]->GetDataType(); - } - - public function GetDynamicInputValues(): array - { - return $this->aDynamicInputValues; - } - - public function GetOutputs(): array - { - return $this->aOutputs; - } - - public function UpdatePHPFragmentsList(array &$aPHPFragments): void - { - } -} diff --git a/sources/PropertyTree/ValueType/ValueTypeCollection.php b/sources/PropertyTree/ValueType/ValueTypeCollection.php deleted file mode 100644 index 54186b7da..000000000 --- a/sources/PropertyTree/ValueType/ValueTypeCollection.php +++ /dev/null @@ -1,75 +0,0 @@ -aFormBlockOptionsForPHP['button_label'] = utils::QuoteForPHP('UI:AddSubTree'); - $this->sSubTreeClass = 'SubFormFor__'.$oParent->GetIdWithPath(); - $this->aFormBlockOptionsForPHP['block_entry_type'] = utils::QuoteForPHP($this->sSubTreeClass); - - // read child properties - foreach ($oDomNode->GetUniqueElement('prototype')->childNodes as $oNode) { - if ($oNode instanceof DesignElement) { - $this->AddChild($oPropertyTreeFactory->CreateNodeFromDom($oNode, $oParent)); - } - } - } - - public function GetFormBlockClass(): string - { - return CollectionBlock::class; - } - public function AddChild(AbstractProperty $oValueType): void - { - $this->aChildren[] = $oValueType; - } - - /** - * @return AbstractProperty[] - */ - public function GetChildren(): array - { - return $this->aChildren; - } - - public function UpdatePHPFragmentsList(array &$aPHPFragments): void - { - $sSubClassPHP = <<sSubTreeClass extends Combodo\iTop\Forms\Block\Base\FormBlock - { - protected function BuildForm(): void - { - PHP; - - foreach ($this->GetChildren() as $oProperty) { - $sSubClassPHP .= "\n".$oProperty->ToPHPFormBlock($aPHPFragments); - } - - $sSubClassPHP .= <<loadXML($sXMLContent); + $aErrors = libxml_get_errors(); + if (count($aErrors) > 0) { + throw new PropertyTypeCompilerException('Property types definition not correctly formatted!'); + } + + /** @var \Combodo\iTop\DesignElement $oRoot */ + $oRoot = $oDoc->firstChild; + + return PropertyTypeFactory::GetInstance()->CreatePropertyTypeFromDom($oRoot); + } + + /** + * @param string $sId + * @param string $sType + * + * @return string + * @throws \Combodo\iTop\PropertyType\Compiler\PropertyTypeCompilerException + */ + protected function GetXMLContent(string $sId, string $sType): string + { + $sPath = utils::GetAbsoluteModulePath('core')."property_types/$sType/$sId.xml"; + if (!file_exists($sPath)) { + throw new PropertyTypeCompilerException("Properties definition $sType/$sId not present"); + } + + return file_get_contents($sPath); + } + + /** + * Compile XML property tree into PHP to create the configuration form + * + * @param string $sXMLContent property tree structure in xml + * + * @return string Generated PHP + * @throws \Combodo\iTop\PropertyType\Compiler\PropertyTypeCompilerException + * @throws \Combodo\iTop\PropertyType\PropertyTypeException + * @throws \DOMFormatException + */ + public function CompileFormFromXML(string $sXMLContent): string + { + $oPropertyType = $this->CompilePropertyTypeFromXML($sXMLContent); + + return $oPropertyType->ToPHPFormBlock(); + } + + /** + * @param string $sXMLContent + * + * @return string + * @throws \Combodo\iTop\PropertyType\Compiler\PropertyTypeCompilerException + * @throws \Combodo\iTop\PropertyType\PropertyTypeException + * @throws \DOMFormatException + */ + public function CompileEntityFromXML(string $sXMLContent): string + { + $oPropertyType = $this->CompilePropertyTypeFromXML($sXMLContent); + + return $oPropertyType->ToPHPEntity(); + } + + /** + * @param string $sId + * @param string $sType + * + * @return string Generated PHP + * @throws \Combodo\iTop\PropertyType\Compiler\PropertyTypeCompilerException + * @throws \Combodo\iTop\PropertyType\PropertyTypeException + * @throws \DOMFormatException + */ + public function CompileForm(string $sId, string $sType): string + { + $sXMLContent = $this->GetXMLContent($sId, $sType); + + return $this->CompileFormFromXML($sXMLContent); + } + + public function CompileEntity(string $sId, string $sType): string + { + $sXMLContent = $this->GetXMLContent($sId, $sType); + + return $this->CompileEntityFromXML($sXMLContent); + } +} diff --git a/sources/PropertyType/Compiler/PropertyTypeCompilerException.php b/sources/PropertyType/Compiler/PropertyTypeCompilerException.php new file mode 100644 index 000000000..ff59a9946 --- /dev/null +++ b/sources/PropertyType/Compiler/PropertyTypeCompilerException.php @@ -0,0 +1,14 @@ +sId = $oDomNode->getAttribute('id'); + $this->sParentType = $oDomNode->GetChildText('extends', ''); + + $oDefinitionNode = $oDomNode->GetUniqueElement('definition'); + $sDefinitionNodeType = $oDefinitionNode->getAttribute('xsi:type'); + + if (!is_a($sDefinitionNodeType, AbstractValueType::class, true)) { + throw new PropertyTypeException('Unsupported type '.json_encode($sDefinitionNodeType).' in '); + } + + $this->oValueType = new $sDefinitionNodeType(); + $this->oValueType->SetRootId($this->sId); + $this->oValueType->InitFromDomNode($oDefinitionNode); + } + + public function ToPHPFormBlock(): string + { + $aPHPFragments = []; + + if ($this->oValueType->IsLeaf()) { + + $sLocalPHP = <<sId extends Combodo\iTop\Forms\Block\Base\FormBlock +{ + protected function BuildForm(): void + { +PHP; + + $sLocalPHP .= "\n".$this->oValueType->ToPHPFormBlock($aPHPFragments); + + $sLocalPHP .= <<oValueType->ToPHPFormBlock($aPHPFragments); + } + + return implode("\n\n", $aPHPFragments); + } + + public function GetParentType(): string + { + return $this->sParentType; + } +} diff --git a/sources/PropertyTree/PropertyTreeDesign.php b/sources/PropertyType/PropertyTypeDesign.php similarity index 93% rename from sources/PropertyTree/PropertyTreeDesign.php rename to sources/PropertyType/PropertyTypeDesign.php index 3e8cc917d..1b67b88a3 100644 --- a/sources/PropertyTree/PropertyTreeDesign.php +++ b/sources/PropertyType/PropertyTypeDesign.php @@ -5,14 +5,18 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ -namespace Combodo\iTop\PropertyTree; +namespace Combodo\iTop\PropertyType; use Combodo\iTop\DesignDocument; use Exception; use ReturnTypeWillChange; use utils; -class PropertyTreeDesign extends DesignDocument +/** + * Load and save XML definitions of property types + * @since 3.3.0 + */ +class PropertyTypeDesign extends DesignDocument { public function __construct(string $sDesignSourceId = null, string $sType = 'Default') { diff --git a/sources/PropertyTree/PropertyTreeException.php b/sources/PropertyType/PropertyTypeException.php similarity index 57% rename from sources/PropertyTree/PropertyTreeException.php rename to sources/PropertyType/PropertyTypeException.php index 2f6c164d0..2f433c0ac 100644 --- a/sources/PropertyTree/PropertyTreeException.php +++ b/sources/PropertyType/PropertyTypeException.php @@ -5,10 +5,13 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ -namespace Combodo\iTop\PropertyTree; +namespace Combodo\iTop\PropertyType; use Exception; -class PropertyTreeException extends Exception +/** + * @since 3.3.0 + */ +class PropertyTypeException extends Exception { } diff --git a/sources/PropertyType/PropertyTypeFactory.php b/sources/PropertyType/PropertyTypeFactory.php new file mode 100644 index 000000000..0e48b1fff --- /dev/null +++ b/sources/PropertyType/PropertyTypeFactory.php @@ -0,0 +1,51 @@ +InitFromDomNode($oDomNode); + + return $oNode; + } +} diff --git a/sources/PropertyType/PropertyTypeService.php b/sources/PropertyType/PropertyTypeService.php new file mode 100644 index 000000000..a29139dd8 --- /dev/null +++ b/sources/PropertyType/PropertyTypeService.php @@ -0,0 +1,91 @@ +oCacheService = DataModelDependantCache::GetInstance(); + } + + final public static function GetInstance(): PropertyTypeService + { + if (!isset(static::$oInstance)) { + static::$oInstance = new PropertyTypeService(); + } + + return static::$oInstance; + } + + /** + * @param string $sId name of the form to retrieve + * @param string $sType + * + * @return \Combodo\iTop\Forms\Block\Base\FormBlock + * @throws \Combodo\iTop\Forms\Block\FormBlockException + * @throws \Combodo\iTop\PropertyType\Compiler\PropertyTypeCompilerException + * @throws \Combodo\iTop\PropertyType\PropertyTypeException + * @throws \DOMFormatException + */ + public function GetFormBlockById(string $sId, string $sType): FormBlock + { + $sFilteredId = $this->SanitizeId($sId); + $sCacheKey = $this->GetCacheKey($sType, $sFilteredId); + + if (!$this->oCacheService->HasEntry(self::FORM_CACHE_POOL, $sCacheKey) || utils::IsDevelopmentEnvironment()) { + // Cache not found, compile the form + $sPHPContent = PropertyTypeCompiler::GetInstance()->CompileForm($sFilteredId, $sType); + $this->oCacheService->StorePhpContent(self::FORM_CACHE_POOL, $sCacheKey, "oCacheService->FetchPHP(self::FORM_CACHE_POOL, $sCacheKey); + $sFormBlockClass = 'FormFor__'.$sFilteredId; + + return new $sFormBlockClass($sFilteredId); + } + + /** + * @param string $sId + * + * @return string + * @throws \Combodo\iTop\Forms\Block\FormBlockException + */ + private function SanitizeId(string $sId): string + { + $sFilteredId = preg_replace('/[^0-9a-zA-Z_]/', '', $sId); + if (strlen($sFilteredId) === 0 || $sFilteredId !== $sId) { + throw new FormBlockException('Malformed name for block: '.json_encode($sId)); + } + + return $sFilteredId; + } + + /** + * @param string $sType + * @param string $sFilteredId + * + * @return string + */ + private function GetCacheKey(string $sType, string $sFilteredId): string + { + return $sType.'/'.$sFilteredId; + } +} diff --git a/sources/PropertyType/ValueType/AbstractValueType.php b/sources/PropertyType/ValueType/AbstractValueType.php new file mode 100644 index 000000000..effdd22c2 --- /dev/null +++ b/sources/PropertyType/ValueType/AbstractValueType.php @@ -0,0 +1,231 @@ +oParent = $oParent; + // id can already be set for the definition root node + $this->sId = $this->sId ?? $oDomNode->getAttribute('id'); + if (is_null($oParent)) { + $this->sIdWithPath = $this->sId; + } else { + $this->sIdWithPath = $oParent->sIdWithPath.'__'.$this->sId; + } + $this->sLabel = $oDomNode->GetChildText('label'); + + $this->sRelevanceCondition = $oDomNode->GetChildText('relevance-condition'); + $sBlockNodeClass = $this->GetFormBlockClass(); + $oBlockNode = new $sBlockNodeClass('foo'); + foreach ($oBlockNode->GetInputs() as $oInput) { + $sInputName = $oInput->GetName(); + $this->aInputs[$sInputName] = $oInput; + $sInputValue = $oDomNode->GetChildText($sInputName); + if (utils::IsNotNullOrEmptyString($sInputValue)) { + $this->aInputValues[$sInputName] = $sInputValue; + } + } + foreach ($oBlockNode->GetOutputs() as $oOutput) { + $this->aOutputs[] = $oOutput->GetName(); + } + } + + public function GetFormBlockOptions(): array + { + return $this->aFormBlockOptionsForPHP; + } + + public function GetInputValues(): array + { + return $this->aInputValues; + } + + public function GetInputType(string $sInputName): string + { + return $this->aInputs[$sInputName]->GetDataType(); + } + + public function GetDynamicInputValues(): array + { + return $this->aDynamicInputValues; + } + + public function GetOutputs(): array + { + return $this->aOutputs; + } + + public function SetRootId(string $sId): void + { + $this->sId = $sId; + $this->sIdWithPath = $sId; + } + + abstract public function IsLeaf(): bool; + + abstract public function ToPHPFormBlock(array &$aPHPFragments = []): string; + + /** + * @param array $aPHPFragments + * + * @return string + * @throws \Combodo\iTop\PropertyType\PropertyTypeException + */ + protected function GetLocalPHPForValueType(?string $sFormBlockClass = null): string + { + $sFormBlockClass = $sFormBlockClass ?? $this->GetFormBlockClass(); + $sInputs = ''; + $sPrerequisiteExpressions = ''; + if (!is_null($this->sRelevanceCondition)) { + $this->GenerateInputs('visible', $this->sRelevanceCondition, $sPrerequisiteExpressions, $sInputs); + } + + foreach ($this->GetInputValues() as $sInputName => $sValue) { + $this->GenerateInputs($sInputName, $sValue, $sPrerequisiteExpressions, $sInputs); + } + + foreach ($this->GetDynamicInputValues() as $sInputName => $sValue) { + $this->GenerateInputs($sInputName, $sValue, $sPrerequisiteExpressions, $sInputs, true); + } + + $sLabel = utils::QuoteForPHP($this->sLabel ?? ''); + $aOptions = [ + 'label' => $sLabel, + ]; + $aOptions += $this->GetFormBlockOptions(); + $sOptions = ''; + foreach ($aOptions as $sOption => $sValue) { + $sOptions .= "\t\t\t".utils::QuoteForPHP($sOption)." => $sValue,\n"; + } + + return <<Add('$this->sId', '$sFormBlockClass', [ +$sOptions\t\t]){$sInputs};\n +PHP; + } + + private function GenerateInputs(string $sInputName, string $sValue, string &$sPrerequisiteExpressions, string &$sInputs, bool $bIsDynamic = false): void + { + if (preg_match('/^{{(?\w+)\.(?\w+)}}$/', $sValue, $aMatches) === 1) { + $sVerb = $bIsDynamic ? 'AddInputDependsOn' : 'InputDependsOn'; + $sInputs .= "\n ->$sVerb('$sInputName', '{$aMatches['node']}', '{$aMatches['output']}')"; + } elseif (preg_match('/^{{(?.*)}}$/', $sValue, $aMatches) === 1) { + $sExpression = $aMatches['expression']; + $sBindings = ''; + try { + $oExpression = Expression::FromOQL($sExpression); + } catch (Exception $e) { + throw new PropertyTypeException("Node: {$this->sId}, invalid syntax in condition: ".$e->getMessage()); + } + $aFieldsToResolve = array_unique($oExpression->ListRequiredFields()); + foreach ($aFieldsToResolve as $sFieldToResolve) { + if (preg_match('/(?\w+)\.(?\w+)/', $sFieldToResolve, $aMatches) === 1) { + $sNode = $aMatches['node']; + $oSibling = $this->GetSibling($sNode); + if (is_null($oSibling)) { + // Search in collection + if (is_a($this->oParent ?? null, 'Combodo-ValueType-Collection')) { + $bSourceNodeFound = false; + $aSiblings = $this->oParent->GetChildren(); + foreach ($aSiblings as $oSibling) { + if ($oSibling->sId == $sNode) { + $bSourceNodeFound = true; + break; + } + } + if (!$bSourceNodeFound) { + throw new PropertyTypeException("node: {$this->sId}, source: $sNode not found in collection: {$this->oParent->sId}"); + } + } else { + throw new PropertyTypeException("Node: {$this->sId}, invalid source in condition: $sNode"); + } + } + $sOutput = $aMatches['output']; + if (!in_array($sOutput, $oSibling->GetOutputs())) { + throw new PropertyTypeException("Node: {$this->sId}, invalid output in condition: $sFieldToResolve"); + } + $sBindings .= "\n\t\t\t->AddInputDependsOn('{$sNode}.$sOutput', '$sNode', '$sOutput')"; + } else { + throw new PropertyTypeException("Node: {$this->sId}, missing output or source in condition: $sFieldToResolve"); + } + } + + $sExpressionClass = match ($this->GetInputType($sInputName)) { + BooleanIOFormat::class => BooleanExpressionFormBlock::class, + StringIOFormat::class, ClassIOFormat::class => StringExpressionFormBlock::class, + NumberIOFormat::class => NumberExpressionFormBlock::class, + default => throw new PropertyTypeException("Node: {$this->sId}, unsupported expression for input type: $sInputName"), + }; + + $sExpression = utils::QuoteForPHP($sExpression); + $sPrerequisiteExpressions = <<Add('{$this->sId}_{$sInputName}_expression', '$sExpressionClass', [ + 'expression' => $sExpression, + ]){$sBindings};\n\n\t\t +PHP; + $sVerb = $bIsDynamic ? 'AddInputDependsOn' : 'InputDependsOn'; + $sInputs .= "\n\t\t\t->$sVerb('$sInputName', '{$this->sId}_{$sInputName}_expression', 'result')"; + } else { + $sInputs .= "\n\t\t\t->SetInputValue('$sInputName', ".utils::QuoteForPHP($sValue).')'; + } + } + + protected function GetSibling(string $sId): ?AbstractValueType + { + if (is_null($this->oParent)) { + return null; + } + + return $this->oParent->GetChild($sId); + } + +} diff --git a/sources/PropertyType/ValueType/Branch/AbstractBranchValueType.php b/sources/PropertyType/ValueType/Branch/AbstractBranchValueType.php new file mode 100644 index 000000000..25b82f5de --- /dev/null +++ b/sources/PropertyType/ValueType/Branch/AbstractBranchValueType.php @@ -0,0 +1,63 @@ +oParent); + } + + /** + * @param \Combodo\iTop\PropertyType\ValueType\AbstractValueType $oChild + * + * @return void + */ + public function AddChild(AbstractValueType $oChild): void + { + $this->aChildren[$oChild->sId] = $oChild; + } + + /** + * @param string $sId + * + * @return \Combodo\iTop\PropertyType\ValueType\AbstractValueType|null + */ + public function GetChild(string $sId): ?AbstractValueType + { + return $this->aChildren[$sId] ?? null; + } + + /** + * @return AbstractValueType[] + */ + public function GetChildren(): array + { + return $this->aChildren; + } +} diff --git a/sources/PropertyType/ValueType/Branch/ValueTypeCollection.php b/sources/PropertyType/ValueType/Branch/ValueTypeCollection.php new file mode 100644 index 000000000..e1d735d3b --- /dev/null +++ b/sources/PropertyType/ValueType/Branch/ValueTypeCollection.php @@ -0,0 +1,77 @@ +aFormBlockOptionsForPHP['button_label'] = utils::QuoteForPHP('UI:AddSubTree'); + $this->sSubTreeClass = 'SubFormFor__'.$this->sIdWithPath; + $this->aFormBlockOptionsForPHP['block_entry_type'] = utils::QuoteForPHP($this->sSubTreeClass); + + // read child properties + foreach ($oDomNode->GetUniqueElement('prototype')->childNodes as $oNode) { + if ($oNode instanceof DesignElement) { + $this->AddChild(ValueTypeFactory::GetInstance()->CreateValueTypeFromDomNode($oNode, $this)); + } + } + } + + public function GetFormBlockClass(): string + { + return CollectionBlock::class; + } + + /** + * @param array $aPHPFragments + * + * @return string + * @throws \Combodo\iTop\PropertyType\PropertyTypeException + */ + public function ToPHPFormBlock(array &$aPHPFragments = []): string + { + $sSubClassPHP = <<sSubTreeClass extends Combodo\iTop\Forms\Block\Base\FormBlock + { + protected function BuildForm(): void + { + PHP; + + foreach ($this->GetChildren() as $oProperty) { + $sSubClassPHP .= "\n".$oProperty->ToPHPFormBlock($aPHPFragments); + } + + $sSubClassPHP .= <<GetLocalPHPForValueType(); + } +} diff --git a/sources/PropertyType/ValueType/Branch/ValueTypePropertyTree.php b/sources/PropertyType/ValueType/Branch/ValueTypePropertyTree.php new file mode 100644 index 000000000..599926b8f --- /dev/null +++ b/sources/PropertyType/ValueType/Branch/ValueTypePropertyTree.php @@ -0,0 +1,89 @@ +GetOptionalElement('nodes'); + if (!is_null($oNodes)) { + foreach ($oNodes->childNodes as $oNode) { + if ($oNode instanceof DesignElement) { + $this->AddChild(ValueTypeFactory::GetInstance()->CreateValueTypeFromDomNode($oNode, $this)); + } + } + } + } + + public function ToPHPFormBlock(array &$aPHPFragments = []): string + { + if ($this->IsRoot()) { + $this->sSubTreeClass = 'FormFor__'.$this->sId; + } else { + $this->sSubTreeClass = 'SubFormFor__'.$this->sIdWithPath; + } + + $sLocalPHP = <<sSubTreeClass extends Combodo\iTop\Forms\Block\Base\FormBlock +{ + protected function BuildForm(): void + { +PHP; + + foreach ($this->aChildren as $oChild) { + $sLocalPHP .= "\n".$oChild->ToPHPFormBlock($aPHPFragments); + } + + $sLocalPHP .= <<GetLocalPHPForValueType($this->sSubTreeClass); + } +} diff --git a/sources/PropertyType/ValueType/Leaf/AbstractLeafValueType.php b/sources/PropertyType/ValueType/Leaf/AbstractLeafValueType.php new file mode 100644 index 000000000..ae31530bc --- /dev/null +++ b/sources/PropertyType/ValueType/Leaf/AbstractLeafValueType.php @@ -0,0 +1,43 @@ +GetLocalPHPForValueType(); + } +} diff --git a/sources/PropertyTree/ValueType/ValueTypeAggregateFunction.php b/sources/PropertyType/ValueType/Leaf/ValueTypeAggregateFunction.php similarity index 61% rename from sources/PropertyTree/ValueType/ValueTypeAggregateFunction.php rename to sources/PropertyType/ValueType/Leaf/ValueTypeAggregateFunction.php index c99e39255..236f6d4f9 100644 --- a/sources/PropertyTree/ValueType/ValueTypeAggregateFunction.php +++ b/sources/PropertyType/ValueType/Leaf/ValueTypeAggregateFunction.php @@ -1,18 +1,18 @@ sFormBlockClass; + } + + public function InitFromDomNode(DesignElement $oDomNode, ?AbstractBranchValueType $oParent = null): void + { + $oNode = $oDomNode->GetUniqueElement('value-type'); + $oRealValueType = ValueTypeFactory::GetInstance()->CreateValueTypeFromDomNode($oNode, $oParent); + $this->sFormBlockClass = $oRealValueType->getFormBlockClass(); + + if (is_a($this->sFormBlockClass, ChoiceFormBlock::class, true)) { + $this->aFormBlockOptionsForPHP['multiple'] = 'true'; + } + + parent::InitFromDomNode($oDomNode, $oParent); + } +} diff --git a/sources/PropertyTree/ValueType/ValueTypeIcon.php b/sources/PropertyType/ValueType/Leaf/ValueTypeIcon.php similarity index 54% rename from sources/PropertyTree/ValueType/ValueTypeIcon.php rename to sources/PropertyType/ValueType/Leaf/ValueTypeIcon.php index 4b3ed1cc9..670df0917 100644 --- a/sources/PropertyTree/ValueType/ValueTypeIcon.php +++ b/sources/PropertyType/ValueType/Leaf/ValueTypeIcon.php @@ -1,15 +1,18 @@ getAttribute('xsi:type'); + if (utils::IsNullOrEmptyString($sNodeType)) { + $sId = $oDomNode->getAttribute('id'); + throw new PropertyTypeException("Node: $sId, missing value-type in node specification"); + } + if (is_a($sNodeType, AbstractValueType::class, true)) { $oNode = new $sNodeType(); $oNode->InitFromDomNode($oDomNode, $oParent); @@ -48,6 +56,7 @@ class ValueTypeFactory return $oNode; } - throw new PropertyTreeException('Unknown value-type node class: '.json_encode($sNodeType)); + $sId = $oDomNode->getAttribute('id'); + throw new PropertyTypeException("Node: $sId, unknown type node class: ".json_encode($sNodeType)); } } diff --git a/sources/alias.php b/sources/alias.php index 8ba0e4041..b3183cfe4 100644 --- a/sources/alias.php +++ b/sources/alias.php @@ -97,21 +97,23 @@ class_alias(\Combodo\iTop\Core\AttributeDefinition\AttributeURL::class, 'Attribu class_alias(\Combodo\iTop\Core\AttributeDefinition\iAttributeNoGroupBy::class, 'iAttributeNoGroupBy'); class_alias(\Combodo\iTop\Core\AttributeDefinition\MissingColumnException::class, 'MissingColumnException'); -class_alias(\Combodo\iTop\PropertyTree\PropertyTree::class, 'Combodo-PropertyTree'); -class_alias(\Combodo\iTop\PropertyTree\Property::class, 'Combodo-Property'); +class_alias(\Combodo\iTop\PropertyType\PropertyType::class, 'Combodo-PropertyType'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeAggregateFunction::class, 'Combodo-ValueType-AggregateFunction'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeBoolean::class, 'Combodo-ValueType-Boolean'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeChoice::class, 'Combodo-ValueType-Choice'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeChoiceFromInput::class, 'Combodo-ValueType-ChoiceFromInput'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeClass::class, 'Combodo-ValueType-Class'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeClassAttribute::class, 'Combodo-ValueType-ClassAttribute'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeClassAttributeGroupBy::class, 'Combodo-ValueType-ClassAttributeGroupBy'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeClassAttributeValue::class, 'Combodo-ValueType-ClassAttributeValue'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeCollection::class, 'Combodo-ValueType-Collection'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeIcon::class, 'Combodo-ValueType-Icon'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeInteger::class, 'Combodo-ValueType-Integer'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeLabel::class, 'Combodo-ValueType-Label'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeOQL::class, 'Combodo-ValueType-OQL'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeString::class, 'Combodo-ValueType-String'); -class_alias(\Combodo\iTop\PropertyTree\ValueType\ValueTypeText::class, 'Combodo-ValueType-Text'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Branch\ValueTypeCollection::class, 'Combodo-ValueType-Collection'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Branch\ValueTypePropertyTree::class, 'Combodo-ValueType-PropertyTree'); + +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeAggregateFunction::class, 'Combodo-ValueType-AggregateFunction'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeBoolean::class, 'Combodo-ValueType-Boolean'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeChoice::class, 'Combodo-ValueType-Choice'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeChoiceFromInput::class, 'Combodo-ValueType-ChoiceFromInput'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeClass::class, 'Combodo-ValueType-Class'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeClassAttribute::class, 'Combodo-ValueType-ClassAttribute'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeClassAttributeGroupBy::class, 'Combodo-ValueType-ClassAttributeGroupBy'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeClassAttributeValue::class, 'Combodo-ValueType-ClassAttributeValue'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeCollectionOfValues::class, 'Combodo-ValueType-CollectionOfValues'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeIcon::class, 'Combodo-ValueType-Icon'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeInteger::class, 'Combodo-ValueType-Integer'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeLabel::class, 'Combodo-ValueType-Label'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeOQL::class, 'Combodo-ValueType-OQL'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeString::class, 'Combodo-ValueType-String'); +class_alias(\Combodo\iTop\PropertyType\ValueType\Leaf\ValueTypeText::class, 'Combodo-ValueType-Text'); diff --git a/tests/php-unit-tests/unitary-tests/sources/Forms/IO/FormBindingTest.php b/tests/php-unit-tests/unitary-tests/sources/Forms/IO/FormBindingTest.php index 0edc3215b..85e1ac5e0 100644 --- a/tests/php-unit-tests/unitary-tests/sources/Forms/IO/FormBindingTest.php +++ b/tests/php-unit-tests/unitary-tests/sources/Forms/IO/FormBindingTest.php @@ -182,7 +182,6 @@ class FormBindingTest extends AbstractFormsTest 'Attribute -> Boolean' => [AttributeIOFormat::class, BooleanIOFormat::class], 'Attribute -> Class' => [AttributeIOFormat::class, ClassIOFormat::class], 'Attribute -> Number' => [AttributeIOFormat::class, NumberIOFormat::class], - 'Attribute -> String' => [AttributeIOFormat::class, StringIOFormat::class], 'Boolean => Attribute' => [BooleanIOFormat::class, AttributeIOFormat::class], 'Boolean => Class' => [BooleanIOFormat::class, ClassIOFormat::class], @@ -192,7 +191,6 @@ class FormBindingTest extends AbstractFormsTest 'Class => Attribute' => [ClassIOFormat::class, AttributeIOFormat::class], 'Class => Boolean' => [ClassIOFormat::class, BooleanIOFormat::class], 'Class => Number' => [ClassIOFormat::class, NumberIOFormat::class], - 'Class -> String' => [ClassIOFormat::class, StringIOFormat::class], 'Number => Attribute' => [NumberIOFormat::class, AttributeIOFormat::class], 'Number => Class' => [NumberIOFormat::class, ClassIOFormat::class], @@ -227,8 +225,10 @@ class FormBindingTest extends AbstractFormsTest { return [ 'Attribute -> Attribute' => [AttributeIOFormat::class, AttributeIOFormat::class], + 'Attribute -> String' => [AttributeIOFormat::class, StringIOFormat::class], 'Boolean => Boolean' => [BooleanIOFormat::class, BooleanIOFormat::class], 'Class => Class' => [ClassIOFormat::class, ClassIOFormat::class], + 'Class -> String' => [ClassIOFormat::class, StringIOFormat::class], 'Number => Number' => [NumberIOFormat::class, NumberIOFormat::class], 'String => String' => [StringIOFormat::class, StringIOFormat::class], ]; diff --git a/tests/php-unit-tests/unitary-tests/sources/Forms/Compiler/FormsCompilerTest.php b/tests/php-unit-tests/unitary-tests/sources/PropertyType/Compiler/FormsCompilerTest.php similarity index 51% rename from tests/php-unit-tests/unitary-tests/sources/Forms/Compiler/FormsCompilerTest.php rename to tests/php-unit-tests/unitary-tests/sources/PropertyType/Compiler/FormsCompilerTest.php index 0810b0ba3..e6542d163 100644 --- a/tests/php-unit-tests/unitary-tests/sources/Forms/Compiler/FormsCompilerTest.php +++ b/tests/php-unit-tests/unitary-tests/sources/PropertyType/Compiler/FormsCompilerTest.php @@ -5,7 +5,7 @@ * @license http://opensource.org/licenses/AGPL-3.0 */ -use Combodo\iTop\Forms\Compiler\FormsCompiler; +use Combodo\iTop\PropertyType\Compiler\PropertyTypeCompiler; use Combodo\iTop\Service\DependencyInjection\ServiceLocator; use Combodo\iTop\Test\UnitTest\ItopDataTestCase; @@ -18,15 +18,15 @@ class FormsCompilerTest extends ItopDataTestCase * @param string $sExpectedPHP * * @return void - * @throws \Combodo\iTop\Forms\Compiler\FormsCompilerException - * @throws \Combodo\iTop\PropertyTree\PropertyTreeException + * @throws \Combodo\iTop\PropertyType\Compiler\PropertyTypeCompilerException + * @throws \Combodo\iTop\PropertyType\PropertyTypeException * @throws \DOMFormatException */ public function testCompileFormFromXML(string $sXMLContent, string $sExpectedPHP) { ServiceLocator::GetInstance()->RegisterService('ModelReflection', new ModelReflectionRuntime()); - $sProducedPHP = FormsCompiler::GetInstance()->CompileFormFromXML($sXMLContent); + $sProducedPHP = PropertyTypeCompiler::GetInstance()->CompileFormFromXML($sXMLContent); $this->AssertPHPCodeIsValid($sProducedPHP); $sMessage = $this->dataName(); @@ -39,21 +39,20 @@ class FormsCompilerTest extends ItopDataTestCase 'Basic scalar properties should generate PHP' => [ 'sXMLContent' => << - - - - - - - - - - - test - - - - + + Dashlet + + + + + + + + test + + + + XML, 'sExpectedPHP' => << [ 'sXMLContent' => << - - - - + + Dashlet + + + + + XML, 'sExpectedPHP' => << [ 'sXMLContent' => << - - - - + + Dashlet + + + + + XML, 'sExpectedPHP' => << [ 'sXMLContent' => << - - - - - - - - - - - - - - - - - - - - - - - - test - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + Dashlet + + + + + + + + + + + + + + + + + + + test + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + XML, 'sExpectedPHP' => << [ 'sXMLContent' => << - + + Dashlet + - + - - - - - - - - - - {{string_property.text != 'no-display'}} - - - - - + + + + + + + {{string_property.text != 'no-display'}} + + - + + XML, 'sExpectedPHP' => << [ 'sXMLContent' => << - - - - - + + Dashlet + + + + Contact Test - - - - + + + + XML, 'sExpectedPHP' => << [ 'sXMLContent' => << - - - - - + + Dashlet + + + + {{CONCAT("'", '"')}} 'Class' and "Attribute" - - - - + + + + XML, 'sExpectedPHP' => << [ 'sXMLContent' => << - - - - - - test - - - - - + + Dashlet + + + + + test + + + {{class_property.text}} - - - - + + + + XML, 'sExpectedPHP' => << [ 'sXMLContent' => << - + + Dashlet + - + - test - - + - {{IF(class_property.value = '', 'Person', class_property.value)}} - - + + XML, 'sExpectedPHP' => << [ 'sXMLContent' => << - + + Dashlet + - + - - - + {{source_property.text != 'count'}} - - - + + XML, 'sExpectedPHP' => << [ 'sXMLContent' => << - + + Dashlet + - + - - - + - - - + {{IF(source_a_property.text != '', source_a_property.text, source_b_property.text)}} - - - + + XML, 'sExpectedPHP' => << [ + 'Sub form generate a sub-form' => [ 'sXMLContent' => << - - - - - - addon/authentication,grant_by_profile,silo - - - - + + Dashlet + + + + + + + + + + + + + XML, 'sExpectedPHP' => <<Add('class_property', 'Combodo\iTop\Forms\Block\Base\ChoiceFormBlock', [ - 'label' => 'UI:Class', - 'choices' => [ - \Dict::S('Class:ActionEmail') => 'ActionEmail', - \Dict::S('Class:ActionNewsroom') => 'ActionNewsroom', - \Dict::S('Class:AuditCategory') => 'AuditCategory', - \Dict::S('Class:AuditDomain') => 'AuditDomain', - \Dict::S('Class:AuditRule') => 'AuditRule', - \Dict::S('Class:OAuthClientAzure') => 'OAuthClientAzure', - \Dict::S('Class:OAuthClientGoogle') => 'OAuthClientGoogle', - \Dict::S('Class:QueryOQL') => 'QueryOQL', - \Dict::S('Class:SynchroAttExtKey') => 'SynchroAttExtKey', - \Dict::S('Class:SynchroAttLinkSet') => 'SynchroAttLinkSet', - \Dict::S('Class:SynchroAttribute') => 'SynchroAttribute', - \Dict::S('Class:SynchroDataSource') => 'SynchroDataSource', - \Dict::S('Class:SynchroLog') => 'SynchroLog', - \Dict::S('Class:SynchroReplica') => 'SynchroReplica', - \Dict::S('Class:TriggerOnAttachmentCreate') => 'TriggerOnAttachmentCreate', - \Dict::S('Class:TriggerOnAttachmentDelete') => 'TriggerOnAttachmentDelete', - \Dict::S('Class:TriggerOnAttachmentDownload') => 'TriggerOnAttachmentDownload', - \Dict::S('Class:TriggerOnAttributeBlobDownload') => 'TriggerOnAttributeBlobDownload', - \Dict::S('Class:TriggerOnObjectCreate') => 'TriggerOnObjectCreate', - \Dict::S('Class:TriggerOnObjectDelete') => 'TriggerOnObjectDelete', - \Dict::S('Class:TriggerOnObjectMention') => 'TriggerOnObjectMention', - \Dict::S('Class:TriggerOnObjectUpdate') => 'TriggerOnObjectUpdate', - \Dict::S('Class:TriggerOnPortalUpdate') => 'TriggerOnPortalUpdate', - \Dict::S('Class:TriggerOnStateEnter') => 'TriggerOnStateEnter', - \Dict::S('Class:TriggerOnStateLeave') => 'TriggerOnStateLeave', - \Dict::S('Class:TriggerOnThresholdReached') => 'TriggerOnThresholdReached', - \Dict::S('Class:URP_Profiles') => 'URP_Profiles', - \Dict::S('Class:URP_UserOrg') => 'URP_UserOrg', - \Dict::S('Class:UserExternal') => 'UserExternal', - \Dict::S('Class:UserLDAP') => 'UserLDAP', - \Dict::S('Class:UserLocal') => 'UserLocal', + \$this->Add('string_property', 'Combodo\iTop\Forms\Block\Base\TextFormBlock', [ + 'label' => 'UI:String', + ]); + } +} + +class FormFor__SubFormTest extends Combodo\iTop\Forms\Block\Base\FormBlock +{ + protected function BuildForm(): void + { + \$this->Add('sub_form_property', 'SubFormFor__SubFormTest__sub_form_property', [ + 'label' => 'UI:SubForm:Title', + ]); + } +} +PHP, ], + 'Collection of values' => [ + 'sXMLContent' => << + + Dashlet + + + + + + Contact + status + + + + + +XML, + 'sExpectedPHP' => <<Add('coll', 'Combodo\iTop\Forms\Block\DataModel\AttributeValueChoiceFormBlock', [ + 'label' => 'UI:ClassAttributeValue', + 'multiple' => true, ]); } } @@ -613,10 +605,13 @@ PHP, 'test' => [ 'sXMLContent' => << - - - - + + Dashlet + + + + + XML, 'sExpectedPHP' => <<expectException($sExpectedClass); $this->expectExceptionMessage($sExpectedMessage); - FormsCompiler::GetInstance()->CompileFormFromXML($sXMLContent); + PropertyTypeCompiler::GetInstance()->CompileFormFromXML($sXMLContent); } public function CompileFormFromInvalidXMLProvider() { return [ 'Invalid OQL expression in condition' => [ - 'sXMLContent' => << << - + + Dashlet + - + {{source_property.text == 'count'}} - - - + + XML, - 'sExpectedClass' => 'Combodo\iTop\PropertyTree\PropertyTreeException', + 'sExpectedClass' => 'Combodo\iTop\PropertyType\PropertyTypeException', 'sExpectedMessage' => 'Node: dependant_property, invalid syntax in condition: Unexpected token EQ - found \'=\' at 22 in \'source_property.text == \'count\'\'', ], 'Unknown source in relevance condition' => [ - 'sXMLContent' => << << - + + Dashlet + - + {{source_property.text = 'count'}} - - - + + XML, - 'sExpectedClass' => 'Combodo\iTop\PropertyTree\PropertyTreeException', + 'sExpectedClass' => 'Combodo\iTop\PropertyType\PropertyTypeException', 'sExpectedMessage' => 'Node: dependant_property, invalid source in condition: source_property', ], 'Unknown output in relevance condition' => [ - 'sXMLContent' => << << - + + Dashlet + - + - - - + {{source_property.text_output != 'count'}} - - - + + XML, - 'sExpectedClass' => 'Combodo\iTop\PropertyTree\PropertyTreeException', + 'sExpectedClass' => 'Combodo\iTop\PropertyType\PropertyTypeException', 'sExpectedMessage' => 'Node: dependant_property, invalid output in condition: source_property.text_output', ], 'Missing output or source in relevance condition' => [ - 'sXMLContent' => << << - + + Dashlet + - + - - - + {{source_property != 'count'}} - - - + + XML, - 'sExpectedClass' => 'Combodo\iTop\PropertyTree\PropertyTreeException', + 'sExpectedClass' => 'Combodo\iTop\PropertyType\PropertyTypeException', 'sExpectedMessage' => 'Node: dependant_property, missing output or source in condition: source_property', ], 'Missing value-type in node specification' => [ - 'sXMLContent' => << << - + + Dashlet + - + + XML, - 'sExpectedClass' => 'Combodo\iTop\PropertyTree\PropertyTreeException', + 'sExpectedClass' => 'Combodo\iTop\PropertyType\PropertyTypeException', 'sExpectedMessage' => 'Node: source_property, missing value-type in node specification', ], + + 'Wrong class for value-type in node specification' => [ + 'sXMLContent' => << + + Dashlet + + + + + + + + +XML, + 'sExpectedClass' => 'Combodo\iTop\PropertyType\PropertyTypeException', + 'sExpectedMessage' => 'Node: source_property, unknown type node class: "Test-Combodo"', + ], ]; } } diff --git a/tests/php-unit-tests/unitary-tests/sources/PropertyType/Serializer/XMLSerializerTest.php b/tests/php-unit-tests/unitary-tests/sources/PropertyType/Serializer/XMLSerializerTest.php new file mode 100644 index 000000000..e3281f3ea --- /dev/null +++ b/tests/php-unit-tests/unitary-tests/sources/PropertyType/Serializer/XMLSerializerTest.php @@ -0,0 +1,331 @@ +RegisterService('ModelReflection', new ModelReflectionRuntime()); + + $oDOMDocument = new PropertyTypeDesign(); + $oDOMDocument->preserveWhiteSpace = false; + $oDOMDocument->formatOutput = true; + + /** @var \Combodo\iTop\DesignElement $oRootNode */ + $oRootNode = $oDOMDocument->createElement('root'); + $oDOMDocument->appendChild($oRootNode); + + Combodo\iTop\PropertyType\Serializer\XMLSerializer::GetInstance()->SerializeForPropertyType($inputContent, $oRootNode, $sPropertyTypeXML); + + $sActualXML = $oDOMDocument->saveXML(); + + $this->AssertEqualiTopXML($sExpectedXMLContent, $sActualXML); + } + + public function XMLSerializerProvider() + { + return [ + 'Basic test should serialize to XML' => [ + 'inputContent' => 'text', + 'sPropertyTypeXML' => << + + Dashlet + + + +XML, + 'sExpectedXMLContent' => << +text +XML, + ], + 'Collection of values as CSV' => [ + 'inputContent' => ['Contact', 'Organization'], + 'sPropertyTypeXML' => << + + Dashlet + + + + + + +XML, + 'sExpectedXMLContent' => << +Contact,Organization +XML, + ], + 'Collection of values as id attribute' => [ + 'inputContent' => ['Contact', 'Organization'], + 'sPropertyTypeXML' => << + + Dashlet + + + item + + + + + +XML, + 'sExpectedXMLContent' => << + + + + +XML, + ], + 'Collection of tree as flat array' => [ + 'inputContent' => [ + [ + 'title_property' => 'title_a', + 'class_property' => 'class_a', + ], + [ + 'title_property' => 'title_b', + 'class_property' => 'class_b', + ], + ], + 'sPropertyTypeXML' => << + + Dashlet + + + item_count + item_\$rank\$_\$id\$ + + + + + + + + test + + + + +XML, + 'sExpectedXMLContent' => << + + 2 + title_a + class_a + title_b + class_b + +XML, + ], + 'Property tree' => [ + 'inputContent' => ['title_property' => 'title', 'class_property' => 'class'], + 'sPropertyTypeXML' => << + + Dashlet + + + + + + + + test + + + + +XML, + 'sExpectedXMLContent' => << + + title + class + +XML, + ], + ]; + } + + /** + * @dataProvider XMLUnserializerProvider + * + * @param $sInputXMLContent + * @param string $sPropertyTypeXML + * @param $expectedValue + * + * @return void + */ + public function testUnserializeXML($sInputXMLContent, string $sPropertyTypeXML, $expectedValue) + { + ServiceLocator::GetInstance()->RegisterService('ModelReflection', new ModelReflectionRuntime()); + + $oDoc = new PropertyTypeDesign(); + $oDoc->loadXML($sInputXMLContent); + /** @var \Combodo\iTop\DesignElement $oRoot */ + $oRoot = $oDoc->firstChild; + + $aActualValue = Combodo\iTop\PropertyType\Serializer\XMLSerializer::GetInstance()->UnserializeForPropertyType($oRoot, $sPropertyTypeXML); + + $this->assertEquals($expectedValue, $aActualValue); + } + + public function XMLUnserializerProvider() + { + return [ + 'Basic test should unserialize from XML' => [ + 'sInputXMLContent' => << +text +XML, + 'sPropertyTypeXML' => << + + Dashlet + + + +XML, + 'expectedValue' => 'text', + ], + 'Collection of values as CSV' => [ + 'sInputXMLContent' => << +Contact,Organization +XML, + 'sPropertyTypeXML' => << + + Dashlet + + + + + + +XML, + 'expectedValue' => ['Contact', 'Organization'], + ], + 'Collection of values as id attribute' => [ + 'sInputXMLContent' => << + + + + +XML, + 'sPropertyTypeXML' => << + + Dashlet + + + item + + + + + +XML, + 'expectedValue' => ['Contact', 'Organization'], + ], + 'Collection of tree as flat array' => [ + 'sInputXMLContent' => << + + 2 + title_a + class_a + title_b + class_b + +XML, + 'sPropertyTypeXML' => << + + Dashlet + + + item_count + item_\$rank\$_\$id\$ + + + + + + + + test + + + + +XML, + 'expectedValue' => [ + [ + 'title_property' => 'title_a', + 'class_property' => 'class_a', + ], + [ + 'title_property' => 'title_b', + 'class_property' => 'class_b', + ], + ], + ], + 'Property tree' => [ + 'sInputXMLContent' => << + + title + class + +XML, + 'sPropertyTypeXML' => << + + Dashlet + + + + + + + + test + + + + +XML, + 'expectedValue' => ['title_property' => 'title', 'class_property' => 'class'], + ], + ]; + } + +} diff --git a/validation/itop-delta.xsd b/validation/itop-delta.xsd new file mode 100644 index 000000000..68aab6943 --- /dev/null +++ b/validation/itop-delta.xsd @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/validation/itop_design.xsd b/validation/itop_design.xsd new file mode 100644 index 000000000..352055aa5 --- /dev/null +++ b/validation/itop_design.xsd @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/validation/property_types.xsd b/validation/property_types.xsd new file mode 100644 index 000000000..7f360abb3 --- /dev/null +++ b/validation/property_types.xsd @@ -0,0 +1,400 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Name of the tag in the target XML delta. Should be lower case, separated by underscores + + + + + + + Dictionary entry or string + + + + + + + Example: "{{query1.class}}" + Reference to the class to + + + + + + + + + + + + + + + + + + + + + When to use this tag (always, never, optional...) + + + + + + + + + + + OQL Expression, with each sibling value as a parameter like in "{{query.selected_class = 'Ticket'}}" + + + + + + + + + + + + + Format of the property in the source XML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Value representing "true" + + + + + Value representing "false" + + + + + + + + + + + + + + + + + + + + + Example: "bizmodel,structure" + Categories of classes to propose (union) + + + + + + + + + + + + + + Example: "groupable" + Category of attributes to propose + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Format of the property in the source XML + + + + + + + + + + + + + + + + + + + + + + Format of the property in the source XML + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Tag to hold the number of items in the collection + + + + + Template to generate tag name for the sub-nodes. Available placeholders: $id$ and $rank$ + + + + + + + + \ No newline at end of file