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
+
+
+