diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php
index b6ef2dafe..09b33f65f 100644
--- a/core/attributedef.class.inc.php
+++ b/core/attributedef.class.inc.php
@@ -7076,12 +7076,8 @@ class AttributeExternalKey extends AttributeDBFieldVoid
*
* @since 3.1.0
*/
- public function GetDeletionOptionForTargetObject()
+ public function GetDeletionOptionForTargetObject(): int
{
- if ($this->IsParam('on_host_delete')) {
- return $this->Get('on_host_delete');
- }
-
return $this->GetOptional('on_host_delete', DEL_MANUAL);
}
diff --git a/core/datamodel.core.xml b/core/datamodel.core.xml
index b24417e75..3a9750c96 100644
--- a/core/datamodel.core.xml
+++ b/core/datamodel.core.xml
@@ -269,5 +269,242 @@
+
+
+
+ sql
+ true
+ string
+
+
+ linked_class
+ true
+ string
+
+
+ ext_key_to_me
+ true
+ string
+
+
+ ext_key_to_remote
+ true
+ string
+
+
+ sql
+ true
+ string
+
+
+ class_attcode
+ true
+ string
+
+
+ extkey_attcode
+ true
+ string
+
+
+ target_attcode
+ true
+ string
+
+
+ item_code
+ true
+ string
+
+
+ relation_code
+ true
+ string
+
+
+ from_class
+ true
+ string
+
+
+ neighbour_id
+ true
+ string
+
+
+ enabled_mode
+ true
+ string
+
+
+ min_up_mode
+ true
+ string
+
+
+ min_up_type
+ true
+ string
+
+
+ handler_class
+ true
+ string
+
+
+ class_field
+ true
+ string
+
+
+ query_field
+ true
+ string
+
+
+ display_style
+ false
+ string
+
+
+
+ target
+ false
+ string
+ ''
+
+
+ default_value
+ false
+ string
+ ''
+
+
+ attribute_definition_list
+ false
+ string
+ ''
+
+
+ attribute_definition_exclusion_list
+ false
+ string
+ ''
+
+
+ min_up
+ true
+ number
+
+
+ count_min
+ false
+ number
+ 0
+
+
+ count_max
+ false
+ number
+ 0
+
+
+ max_combo_length
+ false
+ number
+
+
+ min_autocomplete_chars
+ false
+ number
+
+
+ display_max_width
+ false
+ number
+ 128
+
+
+ display_max_height
+ false
+ number
+ 128
+
+
+ storage_max_width
+ false
+ number
+ 256
+
+
+ storage_max_height
+ false
+ number
+ 256
+
+
+ max_items
+ false
+ number
+ 12
+
+
+ tag_code_max_len
+ false
+ number
+ 20
+
+
+ enabled
+ true
+ boolean
+
+
+ duplicates
+ false
+ boolean
+ false
+
+
+ is_null_allowed
+ false
+ boolean
+ false
+
+
+ allow_target_creation
+ false
+ boolean
+
+
+ is_user_editable
+ false
+ boolean
+ true
+
+
+ on_target_delete
+ false
+ php
+
+
+ targetclass
+ true
+ string
+
+
+ goal_computing
+ false
+ string
+ 'DefaultMetricComputer'
+
+
+ working_time_computing
+ false
+ string
+ ''
+
+
+
diff --git a/core/dbobject.class.php b/core/dbobject.class.php
index c32687dfb..b9c62d608 100644
--- a/core/dbobject.class.php
+++ b/core/dbobject.class.php
@@ -4894,7 +4894,7 @@ abstract class DBObject implements iDisplay
foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
if ($oAttDef instanceof AttributeExternalKey && $oAttDef->IsExternalKey(EXTKEY_ABSOLUTE)) {
$iOption = $oAttDef->GetDeletionOptionForTargetObject();
- if ($iOption == DEL_SILENT || $iOption == DEL_AUTO) {
+ if ($iOption === DEL_SILENT || $iOption === DEL_AUTO) {
// Delete target object
$oTargetObject = MetaModel::GetObject($oAttDef->GetTargetClass(), $this->Get($sAttCode));
$oTargetObject->MakeDeletionPlan($oDeletionPlan, $aVisited, $iOption);
diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php
index 887c0b849..696f26f4b 100644
--- a/setup/compiler.class.inc.php
+++ b/setup/compiler.class.inc.php
@@ -112,10 +112,23 @@ class MFCompiler
protected $sEnvironment;
protected $sCompilationTimeStamp;
- /** @var array dynamic attributes definition
+ /** @var array dynamic attributes definition
* @since 3.1.0
*/
protected array $aDynamicAttributeDefinitions;
+ /** @var array dynamic attribute property definition */
+ protected array $aDynamicPropertyDefinitions;
public function __construct($oModelFactory, $sEnvironment)
{
@@ -144,6 +157,66 @@ class MFCompiler
$this->aLog[] = $sText;
}
+ /**
+ * @param $sPropertyName
+ * @param null $aProperty
+ * @param \DOMElement $oField
+ * @param array $aParameters
+ *
+ * @return array
+ * @throws \DOMFormatException
+ */
+ public function CompileDynamicProperty($sPropertyName, $aProperty, DOMElement $oField, array $aParameters): array
+ {
+ $sPHPParam = $aProperty['php_param'] ?? $sPropertyName;
+ $bMandatory = $aProperty['mandatory'] ?? false;
+ $sType = $aProperty['type'] ?? 'string';
+ $sDefault = $aProperty['default'] ?? null;
+ switch ($sType) {
+ case 'string':
+ if ($bMandatory) {
+ $aParameters[$sPHPParam] = $this->GetMandatoryPropString($oField, $sPropertyName);
+ } else {
+ $aParameters[$sPHPParam] = $this->GetPropString($oField, $sPropertyName, $sDefault);
+ }
+ break;
+ case 'boolean':
+ if ($bMandatory) {
+ $aParameters[$sPHPParam] = $this->GetMandatoryPropBoolean($oField, $sPropertyName);
+ } else {
+ $aParameters[$sPHPParam] = $this->GetPropBoolean($oField, $sPropertyName, is_null($sDefault) ? null : $sDefault === 'true');
+ }
+ break;
+ case 'number':
+ if ($bMandatory) {
+ $aParameters[$sPHPParam] = $this->GetMandatoryPropNumber($oField, $sPropertyName);
+ } else {
+ $aParameters[$sPHPParam] = $this->GetPropNumber($oField, $sPropertyName, is_null($sDefault) ? null : (int)$sDefault);
+ }
+ break;
+ case 'php':
+ $sValue = $oField->GetChildText($sPropertyName);
+ if ($bMandatory && is_null($sValue)) {
+ throw new DOMFormatException("missing (or empty) mandatory tag '$sPropertyName' under the tag '".$oField->nodeName."'");
+ }
+ $aParameters[$sPHPParam] = $sValue ?? 'null';
+ break;
+ case 'oql':
+ if ($sOql = $oField->GetChildText($sPropertyName)) {
+ $sEscapedOql = self::QuoteForPHP($sOql);
+ $aParameters[$sPHPParam] = "$sEscapedOql";
+ } else {
+ $aParameters[$sPHPParam] = 'null';
+ }
+ break;
+ case 'null':
+ $aParameters[$sPHPParam] = 'null';
+ break;
+ }
+
+ return $aParameters;
+ }
+
protected function DumpLog($oPage)
{
foreach ($this->aLog as $sText) {
@@ -2022,7 +2095,7 @@ EOF
// Check dynamic attribute definition first
if ($this->HasDynamicAttributeDefinition($sAttType)) {
- $this->CompileDynamicAttribute($sAttType, $oField, $aParameters, $sModuleRelativeDir, $sClass, $sCss, $sDependencies);
+ $this->CompileDynamicAttribute($sAttType, $oField, $aParameters, $sModuleRelativeDir, $sDependencies);
} elseif ($sAttType == 'AttributeLinkedSetIndirect') {
$this->CompileCommonProperty('linked_class', $oField, $aParameters, $sModuleRelativeDir);
$this->CompileCommonProperty('ext_key_to_me', $oField, $aParameters, $sModuleRelativeDir);
@@ -2044,7 +2117,7 @@ EOF
$this->CompileCommonProperty('filter', $oField, $aParameters, $sModuleRelativeDir);
$aParameters['depends_on'] = $sDependencies;
} elseif ($sAttType == 'AttributeExternalKey') {
- $this->CompileCommonProperty('target_class', $oField, $aParameters, $sModuleRelativeDir, '');
+ $this->CompileCommonProperty('target_class', $oField, $aParameters, $sModuleRelativeDir);
$this->CompileCommonProperty('filter', $oField, $aParameters, $sModuleRelativeDir);
$this->CompileCommonProperty('sql', $oField, $aParameters, $sModuleRelativeDir);
$this->CompileCommonProperty('is_null_allowed', $oField, $aParameters, $sModuleRelativeDir, false);
@@ -2283,6 +2356,8 @@ EOF
* @param string $sPropertyName
* @param \DOMElement $oField
* @param array $aParameters
+ * @param string $sModuleRelativeDir
+ *
* @param mixed $default
*
* @return bool true if the property was found and compiled
@@ -2290,169 +2365,115 @@ EOF
*/
protected function CompileCommonProperty(string $sPropertyName, DOMElement $oField, array &$aParameters, string $sModuleRelativeDir, $default = null): bool
{
- switch ($sPropertyName) {
- case 'linked_class':
- case 'ext_key_to_me':
- case 'ext_key_to_remote':
- case 'sql':
- case 'class_attcode':
- case 'extkey_attcode':
- case 'target_attcode':
- case 'item_code':
- case 'relation_code':
- case 'from_class':
- case 'neighbour_id':
- case 'enabled_mode':
- case 'min_up_mode':
- case 'min_up_type':
- case 'handler_class':
- case 'class_field':
- case 'query_field':
- $aParameters[$sPropertyName] = $this->GetMandatoryPropString($oField, $sPropertyName);
- break;
- case 'display_style':
- case 'target':
- case 'default_value':
- case 'attribute_definition_list':
- case 'attribute_definition_exclusion_list':
- $aParameters[$sPropertyName] = $this->GetPropString($oField, $sPropertyName, $default);
- break;
- case 'min_up':
- $aParameters[$sPropertyName] = $this->GetMandatoryPropNumber($oField, $sPropertyName);
- break;
- case 'count_min':
- case 'count_max':
- case 'max_combo_length':
- case 'min_autocomplete_chars':
- case 'display_max_width':
- case 'display_max_height':
- case 'storage_max_width':
- case 'storage_max_height':
- case 'max_items':
- case 'tag_code_max_len':
- $aParameters[$sPropertyName] = $this->GetPropNumber($oField, $sPropertyName, $default);
- break;
- case 'enabled':
- $aParameters[$sPropertyName] = $this->GetMandatoryPropBoolean($oField, $sPropertyName);
- break;
- case 'duplicates':
- case 'is_null_allowed':
- case 'allow_target_creation':
- case 'is_user_editable':
- $aParameters[$sPropertyName] = $this->GetPropBoolean($oField, $sPropertyName, $default);
- break;
- case 'on_target_delete':
- $aParameters[$sPropertyName] = $oField->GetChildText($sPropertyName);
- break;
- case 'allowed_values':
- $aParameters[$sPropertyName] = 'null';
- break;
- case 'filter':
- if ($sOql = $oField->GetChildText('filter')) {
- $sEscapedOql = self::QuoteForPHP($sOql);
- $aParameters['allowed_values'] = "new ValueSetObjects($sEscapedOql)"; // or "new ValueSetObjects('SELECT xxxx')"
- } else {
- $aParameters['allowed_values'] = 'null';
- }
- break;
- case 'edit_mode':
- $sEditMode = $oField->GetChildText('edit_mode');
- if (!is_null($sEditMode)) {
- $aParameters['edit_mode'] = $this->EditModeToPHP($sEditMode);
- }
- break;
- case 'target_class':
- $aParameters['targetclass'] = $this->GetPropString($oField, 'target_class', $default);
- break;
- case 'mappings':
- $oMappings = $oField->GetUniqueElement('mappings');
- $oMappingNodes = $oMappings->getElementsByTagName('mapping');
- $aMapping = array();
- foreach ($oMappingNodes as $oMapping) {
- $sMappingId = $oMapping->getAttribute('id');
- $sMappingAttCode = $oMapping->GetChildText('attcode');
- $aMapping[$sMappingId]['attcode'] = $sMappingAttCode;
- $aMapping[$sMappingId]['values'] = array();
- $oMetaValues = $oMapping->GetUniqueElement('metavalues');
- foreach ($oMetaValues->getElementsByTagName('metavalue') as $oMetaValue) {
- $sMetaValue = $oMetaValue->getAttribute('id');
- $oValues = $oMetaValue->GetUniqueElement('values');
- foreach ($oValues->getElementsByTagName('value') as $oValue) {
- $sValue = $oValue->getAttribute('id');
- $aMapping[$sMappingId]['values'][$sValue] = $sMetaValue;
- }
-
+ if ($this->HasDynamicPropertyDefinition($sPropertyName)) {
+ $aProperty = $this->aDynamicPropertyDefinitions[$sPropertyName];
+ if (!is_null($default)) {
+ $aProperty['default'] = $default;
+ }
+ $aParameters = $this->CompileDynamicProperty($sPropertyName, $aProperty, $oField, $aParameters);
+ } else {
+ /* Properties too specific to be defined in XML */
+ switch ($sPropertyName) {
+ case 'allowed_values':
+ $aParameters[$sPropertyName] = 'null';
+ break;
+ case 'filter':
+ if ($sOql = $oField->GetChildText('filter')) {
+ $sEscapedOql = self::QuoteForPHP($sOql);
+ $aParameters['allowed_values'] = "new ValueSetObjects($sEscapedOql)"; // or "new ValueSetObjects('SELECT xxxx')"
+ } else {
+ $aParameters['allowed_values'] = 'null';
}
- }
- $aParameters['mapping'] = var_export($aMapping, true);
- break;
- case 'default_image':
- if (($sDefault = $oField->GetChildText('default_image')) && (strlen($sDefault) > 0)) {
- $aParameters['default_image'] = "utils::GetAbsoluteUrlModulesRoot().'$sModuleRelativeDir/$sDefault'";
- } else {
- $aParameters['default_image'] = 'null';
- }
- break;
- case 'states':
- $oStates = $oField->GetUniqueElement('states');
- $oStateNodes = $oStates->getElementsByTagName('state');
- $aStates = array();
- foreach ($oStateNodes as $oState) {
- $aStates[] = '"'.$oState->GetAttribute('id').'"';
- }
- $aParameters['states'] = 'array('.implode(', ', $aStates).')';
- break;
- case 'goal':
- $aParameters['goal_computing'] = $this->GetPropString($oField, 'goal', $default); // Optional, no deadline by default
- break;
- case 'working_time':
- $aParameters['working_time_computing'] = $this->GetPropString($oField, 'working_time', $default); // Blank (different than DefaultWorkingTimeComputer)
- break;
- case 'thresholds':
- $oThresholds = $oField->GetUniqueElement('thresholds');
- $oThresholdNodes = $oThresholds->getElementsByTagName('threshold');
- $aThresholds = array();
- foreach ($oThresholdNodes as $oThreshold) {
- $iPercent = (int)$oThreshold->getAttribute('id');
-
- $oHighlight = $oThreshold->GetUniqueElement('highlight', false);
- $sHighlight = '';
- if ($oHighlight) {
- $sCode = $oHighlight->GetChildText('code');
- $sPersistent = $this->GetPropBoolean($oHighlight, 'persistent', false);
- $sHighlight = "'highlight' => array('code' => '$sCode', 'persistent' => $sPersistent), ";
+ break;
+ case 'edit_mode':
+ $sEditMode = $oField->GetChildText('edit_mode');
+ if (!is_null($sEditMode)) {
+ $aParameters['edit_mode'] = $this->EditModeToPHP($sEditMode);
}
-
- $oActions = $oThreshold->GetUniqueElement('actions');
- $oActionNodes = $oActions->getElementsByTagName('action');
- $aActions = array();
- foreach ($oActionNodes as $oAction) {
- $oParams = $oAction->GetOptionalElement('params');
- $aActionParams = array();
- if ($oParams) {
- $oParamNodes = $oParams->getElementsByTagName('param');
- foreach ($oParamNodes as $oParam) {
- $sParamType = $oParam->getAttribute('xsi:type');
- if ($sParamType == '') {
- $sParamType = 'string';
- }
- $aActionParams[] = "array('type' => '$sParamType', 'value' => ".self::QuoteForPHP($oParam->textContent).')';
+ break;
+ case 'mappings':
+ $oMappings = $oField->GetUniqueElement('mappings');
+ $oMappingNodes = $oMappings->getElementsByTagName('mapping');
+ $aMapping = array();
+ foreach ($oMappingNodes as $oMapping) {
+ $sMappingId = $oMapping->getAttribute('id');
+ $sMappingAttCode = $oMapping->GetChildText('attcode');
+ $aMapping[$sMappingId]['attcode'] = $sMappingAttCode;
+ $aMapping[$sMappingId]['values'] = array();
+ $oMetaValues = $oMapping->GetUniqueElement('metavalues');
+ foreach ($oMetaValues->getElementsByTagName('metavalue') as $oMetaValue) {
+ $sMetaValue = $oMetaValue->getAttribute('id');
+ $oValues = $oMetaValue->GetUniqueElement('values');
+ foreach ($oValues->getElementsByTagName('value') as $oValue) {
+ $sValue = $oValue->getAttribute('id');
+ $aMapping[$sMappingId]['values'][$sValue] = $sMetaValue;
}
+
}
- $sActionParams = 'array('.implode(', ', $aActionParams).')';
- $sVerb = $this->GetPropString($oAction, 'verb');
- $aActions[] = "array('verb' => $sVerb, 'params' => $sActionParams)";
}
- $sActions = 'array('.implode(', ', $aActions).')';
- $aThresholds[] = $iPercent." => array('percent' => $iPercent, $sHighlight 'actions' => $sActions)";
- }
- $aParameters['thresholds'] = 'array('.implode(', ', $aThresholds).')';
- break;
+ $aParameters['mapping'] = var_export($aMapping, true);
+ break;
+ case 'default_image':
+ if (($sDefault = $oField->GetChildText('default_image')) && (strlen($sDefault) > 0)) {
+ $aParameters['default_image'] = "utils::GetAbsoluteUrlModulesRoot().'$sModuleRelativeDir/$sDefault'";
+ } else {
+ $aParameters['default_image'] = 'null';
+ }
+ break;
+ case 'states':
+ $oStates = $oField->GetUniqueElement('states');
+ $oStateNodes = $oStates->getElementsByTagName('state');
+ $aStates = array();
+ foreach ($oStateNodes as $oState) {
+ $aStates[] = '"'.$oState->GetAttribute('id').'"';
+ }
+ $aParameters['states'] = 'array('.implode(', ', $aStates).')';
+ break;
+ case 'thresholds':
+ $oThresholds = $oField->GetUniqueElement('thresholds');
+ $oThresholdNodes = $oThresholds->getElementsByTagName('threshold');
+ $aThresholds = array();
+ foreach ($oThresholdNodes as $oThreshold) {
+ $iPercent = (int)$oThreshold->getAttribute('id');
- default:
- return false;
+ $oHighlight = $oThreshold->GetUniqueElement('highlight', false);
+ $sHighlight = '';
+ if ($oHighlight) {
+ $sCode = $oHighlight->GetChildText('code');
+ $sPersistent = $this->GetPropBoolean($oHighlight, 'persistent', false);
+ $sHighlight = "'highlight' => array('code' => '$sCode', 'persistent' => $sPersistent), ";
+ }
+
+ $oActions = $oThreshold->GetUniqueElement('actions');
+ $oActionNodes = $oActions->getElementsByTagName('action');
+ $aActions = array();
+ foreach ($oActionNodes as $oAction) {
+ $oParams = $oAction->GetOptionalElement('params');
+ $aActionParams = array();
+ if ($oParams) {
+ $oParamNodes = $oParams->getElementsByTagName('param');
+ foreach ($oParamNodes as $oParam) {
+ $sParamType = $oParam->getAttribute('xsi:type');
+ if ($sParamType == '') {
+ $sParamType = 'string';
+ }
+ $aActionParams[] = "array('type' => '$sParamType', 'value' => ".self::QuoteForPHP($oParam->textContent).')';
+ }
+ }
+ $sActionParams = 'array('.implode(', ', $aActionParams).')';
+ $sVerb = $this->GetPropString($oAction, 'verb');
+ $aActions[] = "array('verb' => $sVerb, 'params' => $sActionParams)";
+ }
+ $sActions = 'array('.implode(', ', $aActions).')';
+ $aThresholds[] = $iPercent." => array('percent' => $iPercent, $sHighlight 'actions' => $sActions)";
+ }
+ $aParameters['thresholds'] = 'array('.implode(', ', $aThresholds).')';
+ break;
+
+ default:
+ return false;
+ }
}
-
return true;
}
@@ -2460,56 +2481,23 @@ EOF
* @param string $sAttType
* @param \DOMElement $oField
* @param array $aParameters
+ * @param string $sModuleRelativeDir
* @param string $sDependencies
*
* @throws \DOMFormatException
* @since 3.1.0
*/
- protected function CompileDynamicAttribute(string $sAttType, DOMElement $oField, array &$aParameters, string $sModuleRelativeDir, string $sClass, string &$sCss, string $sDependencies): void
+ protected function CompileDynamicAttribute(string $sAttType, DOMElement $oField, array &$aParameters, string $sModuleRelativeDir, string $sDependencies): void
{
foreach ($this->GetPropertiesForDynamicAttributeDefinition($sAttType) as $sPropertyName => $aProperty) {
- if (!$this->CompileCommonProperty($sPropertyName, $oField, $aParameters, $sModuleRelativeDir)) {
- $sPHPParam = $aProperty['php_param'];
- $bMandatory = $aProperty['mandatory'];
- $sType = $aProperty['type'];
- $sDefault = $aProperty['default'];
- switch ($sType) {
- case 'string':
- if ($bMandatory) {
- $aParameters[$sPHPParam] = $this->GetMandatoryPropString($oField, $sPropertyName);
- } else {
- $aParameters[$sPHPParam] = $this->GetPropString($oField, $sPropertyName, $sDefault);
- }
- break;
- case 'boolean':
- if ($bMandatory) {
- $aParameters[$sPHPParam] = $this->GetMandatoryPropBoolean($oField, $sPropertyName);
- } else {
- $aParameters[$sPHPParam] = $this->GetPropBoolean($oField, $sPropertyName, $sDefault === 'true');
- }
- break;
- case 'number':
- if ($bMandatory) {
- $aParameters[$sPHPParam] = $this->GetMandatoryPropNumber($oField, $sPropertyName);
- } else {
- $aParameters[$sPHPParam] = $this->GetPropNumber($oField, $sPropertyName, (int)$sDefault);
- }
- break;
- case 'php':
- $sValue = $oField->GetChildText($sPropertyName);
- if ($bMandatory && is_null($sValue)) {
- throw new DOMFormatException("missing (or empty) mandatory tag '$sPropertyName' under the tag '".$oField->nodeName."'");
- }
- $aParameters[$sPHPParam] = $sValue ?? 'null';
- break;
- case 'oql':
- if ($sOql = $oField->GetChildText($sPropertyName)) {
- $sEscapedOql = self::QuoteForPHP($sOql);
- $aParameters[$sPHPParam] = "$sEscapedOql";
- } else {
- $aParameters[$sPHPParam] = 'null';
- }
- break;
+ if ($this->HasDynamicPropertyDefinition($sPropertyName)) {
+ // Attribute can rewrite common properties definition
+ $aProperty = array_merge($this->aDynamicPropertyDefinitions[$sPropertyName], $aProperty);
+ $aParameters = $this->CompileDynamicProperty($sPropertyName, $aProperty, $oField, $aParameters);
+ } else {
+ if (!$this->CompileCommonProperty($sPropertyName, $oField, $aParameters, $sModuleRelativeDir)) {
+ /* new property specific to that attribute */
+ $aParameters = $this->CompileDynamicProperty($sPropertyName, $aProperty, $oField, $aParameters);
}
}
}
@@ -3903,6 +3891,33 @@ PHP;
}
}
+ /**
+ * @param string $sPropertyName
+ * @param \Combodo\iTop\DesignElement $oProperty
+ *
+ * @return array{php_param: string, mandatory: bool, type: string, default: string}
+ * @throws \DOMFormatException
+ */
+ private function LoadDynamicPropertyDefinition(DesignElement $oProperty): array
+ {
+ $aDefinition = [];
+
+ if ($oNode = $oProperty->GetOptionalElement('php_param')) {
+ $aDefinition['php_param'] = $oNode->GetText();
+ }
+ if ($oNode = $oProperty->GetOptionalElement('mandatory')) {
+ $aDefinition['mandatory'] = $oNode->GetText('false') === 'true';
+ }
+ if ($oNode = $oProperty->GetOptionalElement('type')) {
+ $aDefinition['type'] = $oNode->GetText();
+ }
+ if ($oNode = $oProperty->GetOptionalElement('default')) {
+ $aDefinition['default'] = $oNode->GetText();
+ }
+
+ return $aDefinition;
+ }
+
/**
* @throws \DOMFormatException
@@ -3910,6 +3925,13 @@ PHP;
*/
protected function LoadDynamicAttributeDefinitions(): void
{
+ $oNodes = $this->oFactory->GetNodes('meta/attribute_properties_definition/properties/property');
+ foreach ($oNodes as $oProperty) {
+ /** @var \Combodo\iTop\DesignElement $oProperty */
+ $sPropertyName = $oProperty->getAttribute('id');
+ $this->aDynamicPropertyDefinitions[$sPropertyName] = $this->LoadDynamicPropertyDefinition($oProperty);
+ }
+
/* Load dynamic attribute definitions */
$oNodes = $this->oFactory->GetNodes('meta/attribute_definitions/attribute_definition');
foreach ($oNodes as $oNode) {
@@ -3920,16 +3942,7 @@ PHP;
foreach ($oProperties as $oProperty) {
/** @var \Combodo\iTop\DesignElement $oProperty */
$sPropertyName = $oProperty->getAttribute('id');
- $sPHPParamName = $oProperty->GetChildText('php_param', $sPropertyName);
- $bMandatory = $oProperty->GetChildText('mandatory', 'false') === 'true';
- $sType = $oProperty->GetChildText('type', 'string');
- $sDefault = $oProperty->GetChildText('default');
- $aAttributeDefinition[$sPropertyName] = [
- 'php_param' => $sPHPParamName,
- 'mandatory' => $bMandatory,
- 'type' => $sType,
- 'default' => $sDefault,
- ];
+ $aAttributeDefinition[$sPropertyName] = $this->LoadDynamicPropertyDefinition($oProperty);
}
$this->aDynamicAttributeDefinitions[$sAttributeDefinitionName]['properties'] = $aAttributeDefinition;
}
@@ -3946,6 +3959,17 @@ PHP;
return array_key_exists($sAttributeName, $this->aDynamicAttributeDefinitions);
}
+ /**
+ * @param string $sPropertyName
+ *
+ * @return bool
+ * @since 3.1.0
+ */
+ protected function HasDynamicPropertyDefinition(string $sPropertyName): bool
+ {
+ return array_key_exists($sPropertyName, $this->aDynamicPropertyDefinitions);
+ }
+
/**
* @param string $sAttributeName
*