diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 71b3e2040..ddb035dad 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -834,7 +834,7 @@ EOF if ((!$oAttDef->IsLinkSet()) && (($iFlags & OPT_ATT_HIDDEN) == 0) && !($oAttDef instanceof AttributeDashboard)) { $sInputId = $this->m_iFormId.'_'.$sAttCode; if ($oAttDef->IsWritable()) { - if ($sStateAttCode == $sAttCode) { + if (($sStateAttCode === $sAttCode) && (MetaModel::HasLifecycle($sClass))) { // State attribute is always read-only from the UI $sHTMLValue = $this->GetStateLabel(); $val = array( @@ -4565,7 +4565,7 @@ HTML //echo "

current value for $sAttCode : $currValue

"; $oDummyObj->Set($sAttCode, $currValue); $aComments[$sAttCode] = ''; - if ($sAttCode != MetaModel::GetStateAttributeCode($sClass)) + if ($sAttCode != MetaModel::GetStateAttributeCode($sClass) || !MetaModel::HasLifecycle($sClass)) { $aComments[$sAttCode] .= ''; } @@ -4625,7 +4625,7 @@ HTML $oDummyObj->Set($sAttCode, null); } $aComments[$sAttCode] = ''; - if ($sAttCode != MetaModel::GetStateAttributeCode($sClass)) + if ($sAttCode != MetaModel::GetStateAttributeCode($sClass) || !MetaModel::HasLifecycle($sClass)) { $aComments[$sAttCode] .= ''; } @@ -4636,11 +4636,11 @@ HTML } } - $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); - if (($sStateAttCode != '') && ($oDummyObj->GetState() == '')) + if (MetaModel::HasLifecycle($sClass) && ($oDummyObj->GetState() == '')) { // Hmmm, it's not gonna work like this ! Set a default value for the "state" // Maybe we should use the "state" that is the most common among the objects... + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); $aMultiValues = $aValues[$sStateAttCode]; uasort($aMultiValues, 'MyComparison'); foreach($aMultiValues as $sCurrValue => $aVal) diff --git a/core/dbobject.class.php b/core/dbobject.class.php index f6eb94566..4dc9eac96 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -1673,6 +1673,7 @@ abstract class DBObject implements iDisplay public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '') { $iFlags = 0; // By default (if no life cycle) no flag at all + $sClass = get_class($this); $aReadOnlyAtts = $this->GetReadOnlyAttributes(); if (($aReadOnlyAtts != null) && (in_array($sAttCode, $aReadOnlyAtts))) @@ -1680,16 +1681,16 @@ abstract class DBObject implements iDisplay return OPT_ATT_READONLY; } - $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); - if (!empty($sStateAttCode)) + if (MetaModel::HasLifecycle($sClass)) { if ($sTargetState != '') { - $iFlags = MetaModel::GetAttributeFlags(get_class($this), $sTargetState, $sAttCode); + $iFlags = MetaModel::GetAttributeFlags($sClass, $sTargetState, $sAttCode); } else { - $iFlags = MetaModel::GetAttributeFlags(get_class($this), $this->Get($sStateAttCode), $sAttCode); + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); + $iFlags = MetaModel::GetAttributeFlags($sClass, $this->Get($sStateAttCode), $sAttCode); } } $aReasons = array(); @@ -1742,10 +1743,10 @@ abstract class DBObject implements iDisplay public function GetTransitionFlags($sAttCode, $sStimulus, &$aReasons = array(), $sOriginState = '') { $iFlags = 0; // By default (if no lifecycle) no flag at all + $sClass = get_class($this); - $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); // If no state attribute, there is no lifecycle - if (empty($sStateAttCode)) + if (!MetaModel::HasLifecycle($sClass)) { return $iFlags; } @@ -1753,6 +1754,7 @@ abstract class DBObject implements iDisplay // Retrieving current state if necessary if ($sOriginState === '') { + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); $sOriginState = $this->Get($sStateAttCode); } @@ -1760,7 +1762,7 @@ abstract class DBObject implements iDisplay $iAttributeFlags = $this->GetAttributeFlags($sAttCode, $aReasons, $sOriginState); // Retrieving transition flags - $iTransitionFlags = MetaModel::GetTransitionFlags(get_class($this), $sOriginState, $sStimulus, $sAttCode); + $iTransitionFlags = MetaModel::GetTransitionFlags($sClass, $sOriginState, $sStimulus, $sAttCode); // Merging transition flags with attribute flags $iFlags = $iTransitionFlags | $iAttributeFlags; @@ -1811,10 +1813,12 @@ abstract class DBObject implements iDisplay public function GetInitialStateAttributeFlags($sAttCode, &$aReasons = array()) { $iFlags = 0; - $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); - if (!empty($sStateAttCode)) + $sClass = get_class($this); + + if (MetaModel::HasLifecycle($sClass)) { - $iFlags = MetaModel::GetInitialStateAttributeFlags(get_class($this), $this->Get($sStateAttCode), $sAttCode); + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); + $iFlags = MetaModel::GetInitialStateAttributeFlags($sClass, $this->Get($sStateAttCode), $sAttCode); } return $iFlags; // No need to care about the synchro flags since we'll be creating a new object anyway } @@ -3631,11 +3635,12 @@ abstract class DBObject implements iDisplay */ public function EnumTransitions() { - $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); - if (empty($sStateAttCode)) return array(); + $sClass = get_class($this); + if (!MetaModel::HasLifecycle($sClass)) return array(); + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); $sState = $this->Get($sStateAttCode); - return MetaModel::EnumTransitions(get_class($this), $sState); + return MetaModel::EnumTransitions($sClass, $sState); } /** @@ -3681,14 +3686,14 @@ abstract class DBObject implements iDisplay public function ApplyStimulus($sStimulusCode, $bDoNotWrite = false) { $sClass = get_class($this); - $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); - if (empty($sStateAttCode)) + if (!MetaModel::HasLifecycle($sClass)) { throw new CoreException('No lifecycle for the class '.$sClass); } MyHelpers::CheckKeyInArray('object lifecycle stimulus', $sStimulusCode, MetaModel::EnumStimuli($sClass)); + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); $aStateTransitions = $this->EnumTransitions(); if (!array_key_exists($sStimulusCode, $aStateTransitions)) { @@ -5338,10 +5343,10 @@ abstract class DBObject implements iDisplay */ public static function MakeDefaultInstance($sClass) { - $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); $oObj = MetaModel::NewObject($sClass); - if (!empty($sStateAttCode)) + if (MetaModel::HasLifecycle($sClass)) { + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); $sTargetState = MetaModel::GetDefaultState($sClass); $oObj->Set($sStateAttCode, $sTargetState); } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 7a6600c74..d940b9f5e 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -853,8 +853,13 @@ abstract class MetaModel } /** - * Return true if the $sClass has a state attribute defined + * Return true if the $sClass has a state attribute defined. * + * Note that having a state attribute does NOT mean having a lifecycle! + * - A Person with active/inactive state won't have transitions and therefore no lifecycle + * - A UserRequest will have transitions between its states and so a lifecycle + * + * @see self::HasLifecycle($sClass) * @param string $sClass Datamodel class to check * * @return bool @@ -2372,6 +2377,22 @@ abstract class MetaModel { return self::$m_aStates[$sClass]; } + elseif (self::HasStateAttributeCode($sClass)) + { + $sStateAttCode = self::GetStateAttributeCode($sClass); + $oAttDef = self::GetAttributeDef($sClass, $sStateAttCode); + + $aStates = []; + foreach($oAttDef->GetAllowedValues() as $sStateCode => $sStateLabel) + { + $aStates[$sStateCode] = [ + 'attribute_inherit' => '', + 'attribute_list' => [], + ]; + } + + return $aStates; + } else { return array(); @@ -2429,6 +2450,21 @@ abstract class MetaModel } } + /** + * Return true if $sClass has a lifecycle, which means that it has a state attribute AND stimuli + * + * @param string $sClass + * + * @return bool + * @throws \CoreException + * @since 3.0.0 + * @see self::HasStateAttributeCode($sClass) + */ + public static function HasLifecycle(string $sClass) + { + return self::HasStateAttributeCode($sClass) && !empty(self::EnumStimuli($sClass)); + } + /** * @param string $sClass * @param string $sStateValue @@ -2517,17 +2553,13 @@ abstract class MetaModel public static function GetAttributeFlags($sClass, $sState, $sAttCode) { $iFlags = 0; // By default (if no life cycle) no flag at all - $sStateAttCode = self::GetStateAttributeCode($sClass); - if (!empty($sStateAttCode)) - { + if (self::HasLifecycle($sClass)) { $aStates = MetaModel::EnumStates($sClass); - if (!array_key_exists($sState, $aStates)) - { + if (!array_key_exists($sState, $aStates)) { throw new CoreException("Invalid state '$sState' for class '$sClass', expecting a value in {".implode(', ', array_keys($aStates))."}"); } $aCurrentState = $aStates[$sState]; - if ((array_key_exists('attribute_list', $aCurrentState)) && (array_key_exists($sAttCode, $aCurrentState['attribute_list']))) - { + if ((array_key_exists('attribute_list', $aCurrentState)) && (array_key_exists($sAttCode, $aCurrentState['attribute_list']))) { $iFlags = $aCurrentState['attribute_list'][$sAttCode]; } } @@ -2547,18 +2579,14 @@ abstract class MetaModel public static function GetTransitionFlags($sClass, $sState, $sStimulus, $sAttCode) { $iFlags = 0; // By default (if no lifecycle) no flag at all - $sStateAttCode = self::GetStateAttributeCode($sClass); - if (!empty($sStateAttCode)) - { + if (self::HasLifecycle($sClass)) { $aTransitions = MetaModel::EnumTransitions($sClass, $sState); - if (!array_key_exists($sStimulus, $aTransitions)) - { + if (!array_key_exists($sStimulus, $aTransitions)) { throw new CoreException("Invalid transition '$sStimulus' for class '$sClass', expecting a value in {".implode(', ', array_keys($aTransitions))."}"); } $aCurrentTransition = $aTransitions[$sStimulus]; - if ((array_key_exists('attribute_list', $aCurrentTransition)) && (array_key_exists($sAttCode, $aCurrentTransition['attribute_list']))) - { + if ((array_key_exists('attribute_list', $aCurrentTransition)) && (array_key_exists($sAttCode, $aCurrentTransition['attribute_list']))) { $iFlags = $aCurrentTransition['attribute_list'][$sAttCode]; } } @@ -2637,37 +2665,30 @@ abstract class MetaModel public static function GetInitialStateAttributeFlags($sClass, $sState, $sAttCode) { $iFlags = self::GetAttributeFlags($sClass, $sState, $sAttCode); // Be default set the same flags as the 'target' state - $sStateAttCode = self::GetStateAttributeCode($sClass); - if (!empty($sStateAttCode)) - { + if (self::HasLifecycle($sClass)) { $aStates = MetaModel::EnumInitialStates($sClass); - if (array_key_exists($sState, $aStates)) - { + if (array_key_exists($sState, $aStates)) { $bReadOnly = (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY); $bHidden = (($iFlags & OPT_ATT_HIDDEN) == OPT_ATT_HIDDEN); - foreach($aStates[$sState] as $sPrevState) - { + foreach($aStates[$sState] as $sPrevState) { $iPrevFlags = self::GetAttributeFlags($sClass, $sPrevState, $sAttCode); - if (($iPrevFlags & OPT_ATT_HIDDEN) != OPT_ATT_HIDDEN) - { + if (($iPrevFlags & OPT_ATT_HIDDEN) != OPT_ATT_HIDDEN) { $bReadOnly = $bReadOnly && (($iPrevFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY); // if it is/was not readonly => then it's not } $bHidden = $bHidden && (($iPrevFlags & OPT_ATT_HIDDEN) == OPT_ATT_HIDDEN); // if it is/was not hidden => then it's not } - if ($bReadOnly) - { + + if ($bReadOnly) { $iFlags = $iFlags | OPT_ATT_READONLY; } - else - { + else { $iFlags = $iFlags & ~OPT_ATT_READONLY; } - if ($bHidden) - { + + if ($bHidden) { $iFlags = $iFlags | OPT_ATT_HIDDEN; } - else - { + else { $iFlags = $iFlags & ~OPT_ATT_HIDDEN; } } @@ -2877,6 +2898,25 @@ abstract class MetaModel } self::$m_aClassParams[$sPHPClass]['obsolescence_expression'] = $sObsolescence; + // Inherit fields semantic + // - State attribute + $bParentHasStateAttribute = (isset(self::$m_aClassParams[$sParent]['state_attcode']) && !empty(self::$m_aClassParams[$sParent]['state_attcode'])); + $bHasStateAttribute = (isset(self::$m_aClassParams[$sPHPClass]['state_attcode']) && !empty(self::$m_aClassParams[$sPHPClass]['state_attcode'])); + if($bParentHasStateAttribute && !$bHasStateAttribute) { + // Set attribute code + self::$m_aClassParams[$sPHPClass]['state_attcode'] = self::$m_aClassParams[$sParent]['state_attcode']; + + // Set states + self::$m_aStates[$sPHPClass] = self::$m_aStates[$sParent]; + } + // - Image attribute + $bParentHasImageAttribute = (isset(self::$m_aClassParams[$sParent]['image_attcode']) && !empty(self::$m_aClassParams[$sParent]['image_attcode'])); + $bHasImageAttribute = (isset(self::$m_aClassParams[$sPHPClass]['image_attcode']) && !empty(self::$m_aClassParams[$sPHPClass]['image_attcode'])); + if($bParentHasImageAttribute && !$bHasImageAttribute) { + // Set attribute code + self::$m_aClassParams[$sPHPClass]['image_attcode'] = self::$m_aClassParams[$sParent]['image_attcode']; + } + foreach(MetaModel::EnumPlugins('iOnClassInitialization') as $sPluginClass => $oClassInit) { $oClassInit->OnAfterClassInitialization($sPHPClass); diff --git a/datamodels/2.x/itop-change-mgmt-itil/datamodel.itop-change-mgmt-itil.xml b/datamodels/2.x/itop-change-mgmt-itil/datamodel.itop-change-mgmt-itil.xml index b8eb1cb31..21ffdf457 100755 --- a/datamodels/2.x/itop-change-mgmt-itil/datamodel.itop-change-mgmt-itil.xml +++ b/datamodels/2.x/itop-change-mgmt-itil/datamodel.itop-change-mgmt-itil.xml @@ -21,6 +21,9 @@ + + status + images/change.png @@ -199,7 +202,6 @@ - status 1 diff --git a/datamodels/2.x/itop-change-mgmt/datamodel.itop-change-mgmt.xml b/datamodels/2.x/itop-change-mgmt/datamodel.itop-change-mgmt.xml index ad764ce5b..7c14296af 100755 --- a/datamodels/2.x/itop-change-mgmt/datamodel.itop-change-mgmt.xml +++ b/datamodels/2.x/itop-change-mgmt/datamodel.itop-change-mgmt.xml @@ -21,6 +21,9 @@ + + status + images/change.png @@ -160,7 +163,6 @@ images/change-closed.png - status diff --git a/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml b/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml index e601833c0..98b8cdd9d 100755 --- a/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml +++ b/datamodels/2.x/itop-config-mgmt/datamodel.itop-config-mgmt.xml @@ -256,6 +256,9 @@ + + status + images/server.png @@ -1516,6 +1519,9 @@ + + status + images/solution.png @@ -1657,6 +1663,9 @@ + + status + images/business-process.png @@ -1768,6 +1777,9 @@ + + status + images/application.png @@ -5972,6 +5984,9 @@ + + status + images/group.png diff --git a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml index 6b42eb376..365b48d79 100755 --- a/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml +++ b/datamodels/2.x/itop-incident-mgmt-itil/datamodel.itop-incident-mgmt-itil.xml @@ -21,6 +21,9 @@ + + status + images/incident.png @@ -376,7 +379,6 @@ images/incident-closed.png - status diff --git a/datamodels/2.x/itop-problem-mgmt/datamodel.itop-problem-mgmt.xml b/datamodels/2.x/itop-problem-mgmt/datamodel.itop-problem-mgmt.xml index d6eb544cc..0c0dfb17a 100755 --- a/datamodels/2.x/itop-problem-mgmt/datamodel.itop-problem-mgmt.xml +++ b/datamodels/2.x/itop-problem-mgmt/datamodel.itop-problem-mgmt.xml @@ -21,6 +21,9 @@ + + status + images/problem.png @@ -160,7 +163,6 @@ - status diff --git a/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml b/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml index 79333aa66..109c51340 100755 --- a/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml +++ b/datamodels/2.x/itop-request-mgmt-itil/datamodel.itop-request-mgmt-itil.xml @@ -21,6 +21,9 @@ + + status + images/user-request.png @@ -408,7 +411,6 @@ images/user-request-closed.png - status diff --git a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml index 7fbe02200..1052c0329 100755 --- a/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml +++ b/datamodels/2.x/itop-request-mgmt/datamodel.itop-request-mgmt.xml @@ -21,6 +21,9 @@ + + status + images/user-request.png @@ -412,7 +415,6 @@ images/user-request-closed.png - status diff --git a/datamodels/2.x/itop-service-mgmt-provider/datamodel.itop-service-mgmt-provider.xml b/datamodels/2.x/itop-service-mgmt-provider/datamodel.itop-service-mgmt-provider.xml index 10dff74ed..4ac9d07d7 100755 --- a/datamodels/2.x/itop-service-mgmt-provider/datamodel.itop-service-mgmt-provider.xml +++ b/datamodels/2.x/itop-service-mgmt-provider/datamodel.itop-service-mgmt-provider.xml @@ -60,6 +60,9 @@ + + status + images/contract.png @@ -984,9 +987,9 @@ public function PrefillSearchForm(&$aContextParam) - + icon - + images/service.png @@ -1069,9 +1072,10 @@ public function PrefillSearchForm(&$aContextParam) - + icon - + status + images/service.png @@ -1421,6 +1425,9 @@ public function PrefillSearchForm(&$aContextParam) + + status + images/service.png diff --git a/datamodels/2.x/itop-service-mgmt/datamodel.itop-service-mgmt.xml b/datamodels/2.x/itop-service-mgmt/datamodel.itop-service-mgmt.xml index 5c9e8f9d4..36f427f5d 100755 --- a/datamodels/2.x/itop-service-mgmt/datamodel.itop-service-mgmt.xml +++ b/datamodels/2.x/itop-service-mgmt/datamodel.itop-service-mgmt.xml @@ -60,6 +60,9 @@ + + status + images/contract.png @@ -876,9 +879,9 @@ public function PrefillSearchForm(&$aContextParam) - + icon - + images/service.png @@ -961,9 +964,10 @@ public function PrefillSearchForm(&$aContextParam) - + icon - + status + images/service.png @@ -1335,6 +1339,9 @@ public function PrefillSearchForm(&$aContextParam) + + status + images/service.png diff --git a/datamodels/2.x/itop-structure/datamodel.itop-structure.xml b/datamodels/2.x/itop-structure/datamodel.itop-structure.xml index dbe174baf..5a105a9c2 100644 --- a/datamodels/2.x/itop-structure/datamodel.itop-structure.xml +++ b/datamodels/2.x/itop-structure/datamodel.itop-structure.xml @@ -21,6 +21,9 @@ + + status + images/building.png @@ -181,6 +184,9 @@ + + status + images/location.png @@ -347,6 +353,9 @@ + + status + images/team.png @@ -514,9 +523,9 @@ - + picture - + images/person.png @@ -1099,6 +1108,9 @@ + + status + images/document.png diff --git a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml index a3d155370..809239f5e 100755 --- a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml +++ b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml @@ -475,6 +475,9 @@ + + status + images/workorder.png @@ -557,7 +560,6 @@ - status diff --git a/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml b/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml index fdafbc515..05730fe37 100644 --- a/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml +++ b/datamodels/2.x/itop-virtualization-mgmt/datamodel.itop-virtualization-mgmt.xml @@ -15,6 +15,9 @@ + + status + diff --git a/pages/UI.php b/pages/UI.php index 856f2b0ed..ffcc8ba71 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -1225,9 +1225,9 @@ HTML { /** @var \cmdbAbstractObject $oObj */ $oObj = MetaModel::NewObject($sClass); - $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); - if (!empty($sStateAttCode)) + if (MetaModel::HasLifecycle($sClass)) { + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); $sTargetState = utils::ReadPostedParam('obj_state', ''); if ($sTargetState != '') { diff --git a/pages/graphviz.php b/pages/graphviz.php index b5d0e420c..1f56354de 100644 --- a/pages/graphviz.php +++ b/pages/graphviz.php @@ -45,8 +45,7 @@ function GraphvizEscape($s) function GraphvizLifecycle($sClass) { $sDotFileContent = ""; - $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); - if (empty($sStateAttCode)) + if (!MetaModel::HasLifecycle($sClass)) { //$oPage->p("no lifecycle for this class"); } diff --git a/pages/schema.php b/pages/schema.php index bd5c3fe4e..99a1e2ba7 100644 --- a/pages/schema.php +++ b/pages/schema.php @@ -84,8 +84,7 @@ function DisplaySubclasses($oPage, $sClass, $sContext) */ function DisplayLifecycle($oPage, $sClass) { - $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); - if (empty($sStateAttCode)) + if (!MetaModel::HasLifecycle($sClass)) { $oPage->p(Dict::S('UI:Schema:NoLifeCyle')); } diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index 7035daf20..aac6846b0 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -1029,31 +1029,24 @@ EOF // - Default attributes code $sImageAttCode = ""; $sStateAttCode = ""; - // - Parse optional semantic node - $oSemantic = $oProperties->GetOptionalElement('semantic'); - if ($oSemantic) { + // - Parse optional fields semantic node + $oFieldsSemantic = $oProperties->GetOptionalElement('fields_semantic'); + if ($oFieldsSemantic) { // Image attribute - $oImageAttribute = $oSemantic->GetOptionalElement('image_attribute'); + $oImageAttribute = $oFieldsSemantic->GetOptionalElement('image_attribute'); if ($oImageAttribute) { $sImageAttCode = $oImageAttribute->GetText(); } - // State attribute, only if not already found from lifecycle -// $oStateAttribute = $oSemantic->GetOptionalElement('state_attribute'); -// if(empty($sStateAttCode) && $oStateAttribute) { -// $sStateAttCode = $oStateAttribute->GetText(); -// } + // State attribute (for XML v1.7- the lifecycle/attribute node should have been migrated in this one) + $oStateAttribute = $oFieldsSemantic->GetOptionalElement('state_attribute'); + if($oStateAttribute) { + $sStateAttCode = $oStateAttribute->GetText(); + } } $aClassParams['image_attcode'] = "'$sImageAttCode'"; $aClassParams['state_attcode'] = "'$sStateAttCode'"; - // Lifecycle (overload any state attribute defined in the semantic node) - $oLifecycle = $oClass->GetOptionalElement('lifecycle'); - if ($oLifecycle) { - $sStateAttCode = $oLifecycle->GetChildText('attribute'); - $aClassParams['state_attcode'] = "'$sStateAttCode'"; - } - // Reconcialiation if ($oReconciliation = $oProperties->GetOptionalElement('reconciliation')) { @@ -1641,8 +1634,8 @@ EOF // $sLifecycle = ''; $sHighlightScale = ''; - if ($oLifecycle) - { + $oLifecycle = $oClass->GetOptionalElement('lifecycle'); + if ($oLifecycle) { $sLifecycle .= "\t\t// Lifecycle (status attribute: $sStateAttCode)\n"; $sLifecycle .= "\t\t//\n"; @@ -1869,6 +1862,26 @@ EOF } } } + // No "real" lifecycle with stimuli and such but still a state attribute, we need to define states from the enum. values + elseif ($oFieldsSemantic && $oStateAttribute) { + $sLifecycle .= "\t\t// States but no lifecycle declared in XML (status attribute: $sStateAttCode)\n"; + $sLifecycle .= "\t\t//\n"; + + // Note: We can't use ModelFactory::GetField() as the current clas doesn't seem to be loaded yet. + $oField = $this->oFactory->GetNodes('field[@id="'.$sStateAttCode.'"]', $oFields)->item(0); + $oValues = $oField->GetUniqueElement('values'); + $oValueNodes = $oValues->getElementsByTagName('value'); + foreach($oValueNodes as $oValue) + { + $sLifecycle .= " MetaModel::Init_DefineState(\n"; + $sLifecycle .= " \"".$oValue->GetText()."\",\n"; + $sLifecycle .= " array(\n"; + $sLifecycle .= " \"attribute_inherit\" => '',\n"; + $sLifecycle .= " \"attribute_list\" => array()\n"; + $sLifecycle .= " )\n"; + $sLifecycle .= " );\n"; + } + } // ZLists // diff --git a/setup/itopdesignformat.class.inc.php b/setup/itopdesignformat.class.inc.php index 3dcf60ae8..e605fa69c 100644 --- a/setup/itopdesignformat.class.inc.php +++ b/setup/itopdesignformat.class.inc.php @@ -95,7 +95,7 @@ class iTopDesignFormat 'go_to_previous' => 'From18To17', 'next' => null, 'go_to_next' => null, - ) + ), ); /** @@ -768,9 +768,35 @@ class iTopDesignFormat */ protected function From17To18($oFactory) { + $oXPath = new DOMXPath($this->oDocument); + // N°3233 - Remove "display template" feature from MetaModel $sPath = "/itop_design//class/properties/display_template"; $this->RemoveNodeFromXPath($sPath); + + // N°3203 - Datamodel: Add semantic for image & state attributes + // - Move lifecycle attribute declaration to the semantic node + $oNodeList = $oXPath->query("/itop_design//class/lifecycle/attribute"); + /** @var \DOMElement $oNode */ + foreach ($oNodeList as $oNode) { + // Find semantic node or create it + $oPropertiesNode = $oXPath->query("../../properties", $oNode)->item(0); + $oFieldsSemanticNodeList = $oXPath->query("fields_semantic", $oPropertiesNode); + if ($oFieldsSemanticNodeList->length > 0) { + $oSemanticNode = $oFieldsSemanticNodeList->item(0); + } + else { + $oSemanticNode = $oPropertiesNode->ownerDocument->createElement("fields_semantic"); + $oPropertiesNode->appendChild($oSemanticNode); + } + + // Create state_attribute node + $oStateNode = $oSemanticNode->ownerDocument->createElement("state_attribute", $oNode->nodeValue); + $oSemanticNode->appendChild($oStateNode); + + // Remove current node from lifecycle + $this->DeleteNode($oNode); + } } /** @@ -780,6 +806,8 @@ class iTopDesignFormat */ protected function From18To17($oFactory) { + $oXPath = new DOMXPath($this->oDocument); + // N°3182 - Remove style node from MenuGroup $sPath = "/itop_design/menus/menu[@xsi:type='MenuGroup']/style"; $this->RemoveNodeFromXPath($sPath); @@ -791,6 +819,27 @@ class iTopDesignFormat // N°2982 - Speed up SCSS themes compilation during setup $sPath = "/itop_design/branding/themes/theme/precompiled_stylesheet"; $this->RemoveNodeFromXPath($sPath); + + // N°3203 - Datamodel: Add semantic for image & state attributes + // - Move state_attribute back to the lifecycle node if it has one + $oNodeList = $oXPath->query("/itop_design//class/properties/fields_semantic/state_attribute"); + /** @var \DOMElement $oNode */ + foreach ($oNodeList as $oNode) { + // Move node under lifecycle only if there is such a node + $oLifecycleNode = $oXPath->query("../../../lifecycle", $oNode)->item(0); + if($oLifecycleNode !== null) + { + // Create attribute node + $oAttributeNode = $oLifecycleNode->ownerDocument->createElement("attribute", $oNode->nodeValue); + $oLifecycleNode->appendChild($oAttributeNode); + } + + // Remove current node from semantic in all cases + $this->DeleteNode($oNode); + } + // - Remove semantic node + $sPath = "/itop_design//class/properties/fields_semantic"; + $this->RemoveNodeFromXPath($sPath); } /** diff --git a/sources/application/UI/Component/Title/TitleFactory.php b/sources/application/UI/Component/Title/TitleFactory.php index 37123bca1..d888d1054 100644 --- a/sources/application/UI/Component/Title/TitleFactory.php +++ b/sources/application/UI/Component/Title/TitleFactory.php @@ -52,8 +52,8 @@ class TitleFactory $oTitle->SetIcon($sObjIconUrl, $sIconCoverMethod); } - $sStatusAttCode = MetaModel::GetStateAttributeCode($sObjClass); - if (!empty($sStatusAttCode)) { + if (MetaModel::HasStateAttributeCode($sObjClass)) { + $sStatusAttCode = MetaModel::GetStateAttributeCode($sObjClass); $sStateCode = $oObject->GetState(); $sStatusLabel = $oObject->GetStateLabel(); $sStatusColor = UIHelper::GetColorFromStatus(get_class($oObject), $sStateCode); diff --git a/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpSetAttributeScalarFactory.php b/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpSetAttributeScalarFactory.php index ba3f4bf6f..e823a2986 100644 --- a/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpSetAttributeScalarFactory.php +++ b/sources/application/UI/Layout/ActivityPanel/ActivityEntry/CMDBChangeOp/CMDBChangeOpSetAttributeScalarFactory.php @@ -45,7 +45,7 @@ class CMDBChangeOpSetAttributeScalarFactory extends CMDBChangeOpSetAttributeFact $sAttCode = $oChangeOp->Get('attcode'); // Specific ActivityEntry for transition, otherwise just a regular EditsEntry - if($sAttCode === MetaModel::GetStateAttributeCode($sHostObjectClass)) + if(MetaModel::HasLifecycle($sHostObjectClass) && ($sAttCode === MetaModel::GetStateAttributeCode($sHostObjectClass))) { $oDateTime = DateTime::createFromFormat(AttributeDateTime::GetInternalFormat(), $oChangeOp->Get('date')); diff --git a/sources/application/UI/Layout/Object/ObjectDetails.php b/sources/application/UI/Layout/Object/ObjectDetails.php index b0226bdc1..ad4f91fba 100644 --- a/sources/application/UI/Layout/Object/ObjectDetails.php +++ b/sources/application/UI/Layout/Object/ObjectDetails.php @@ -48,8 +48,7 @@ class ObjectDetails extends Panel $this->sName = $oObject->GetRawName(); $this->sIconUrl = $oObject->GetIcon(false); - $sStatusAttCode = MetaModel::GetStateAttributeCode($this->sClassName); - if(!empty($sStatusAttCode)) { + if(MetaModel::HasStateAttributeCode($this->sClassName)) { $this->sStatusCode = $oObject->GetState(); $this->sStatusLabel = $oObject->GetStateLabel(); $this->sStatusColor = UIHelper::GetColorFromStatus($this->sClassName, $this->sStatusCode); diff --git a/test/setup/iTopDesignFormat/1.7_to_1.8.expected.xml b/test/setup/iTopDesignFormat/1.7_to_1.8.expected.xml new file mode 100644 index 000000000..f75dbee54 --- /dev/null +++ b/test/setup/iTopDesignFormat/1.7_to_1.8.expected.xml @@ -0,0 +1,14 @@ + + + + + + images/class-with-lifecycle.png + + foo + + + + + + diff --git a/test/setup/iTopDesignFormat/1.7_to_1.8.input.xml b/test/setup/iTopDesignFormat/1.7_to_1.8.input.xml new file mode 100644 index 000000000..6f2b86a67 --- /dev/null +++ b/test/setup/iTopDesignFormat/1.7_to_1.8.input.xml @@ -0,0 +1,13 @@ + + + + + + images/class-with-lifecycle.png + + + foo + + + + diff --git a/test/setup/iTopDesignFormat/1.8_to_1.7.expected.xml b/test/setup/iTopDesignFormat/1.8_to_1.7.expected.xml index d169b121f..7841cbdd3 100644 --- a/test/setup/iTopDesignFormat/1.8_to_1.7.expected.xml +++ b/test/setup/iTopDesignFormat/1.8_to_1.7.expected.xml @@ -1,5 +1,30 @@ + + + + images/class-with-lifecycle.png + + + + + images/class-with-lifecycle.png + + + foo + + + + + images/class-with-lifecycle.png + + + + + images/class-with-lifecycle.png + + + 100 diff --git a/test/setup/iTopDesignFormat/1.8_to_1.7.input.xml b/test/setup/iTopDesignFormat/1.8_to_1.7.input.xml index 2ef953c22..17b5a4fd2 100644 --- a/test/setup/iTopDesignFormat/1.8_to_1.7.input.xml +++ b/test/setup/iTopDesignFormat/1.8_to_1.7.input.xml @@ -1,22 +1,58 @@ - - - 100 - WelcomeMenu - $$http://fr.wikipedia.org/ - true - - - 30 - - - - - - images/itop-logo.png - images/itop-logo-square.png - + + + + images/class-with-lifecycle.png + + foo + + + + + + images/class-with-lifecycle.png + + foo + + + + + + + images/class-with-lifecycle.png + + foo + bar + + + + + + images/class-with-lifecycle.png + + bar + + + + + + + 100 + WelcomeMenu + $$http://fr.wikipedia.org/ + true + + + 30 + + + + + + images/itop-logo.png + images/itop-logo-square.png + diff --git a/test/setup/iTopDesignFormat/iTopDesignFormatTest.php b/test/setup/iTopDesignFormat/iTopDesignFormatTest.php index 004541520..ec76ddd5c 100644 --- a/test/setup/iTopDesignFormat/iTopDesignFormatTest.php +++ b/test/setup/iTopDesignFormat/iTopDesignFormatTest.php @@ -68,6 +68,7 @@ class TestForITopDesignFormatClass extends ItopTestCase { return array( '1.7 to 1.6' => array('1.6', '1.7_to_1.6.input', '1.7_to_1.6.expected'), + '1.7 to 1.8' => array('1.8', '1.7_to_1.8.input', '1.7_to_1.8.expected'), '1.8 to 1.7' => array('1.7', '1.8_to_1.7.input', '1.8_to_1.7.expected'), ); }