diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 49d10c506..f2178c641 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -1,5 +1,5 @@ $trash) { - $aDeps[$sAttCode] = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode); + $aDeps[$sAttCode] = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode); } $aList = $this->OrderDependentFields($aDeps); @@ -3489,7 +3489,7 @@ EOF $sFormPrefix = '2_'; foreach($aList as $sAttCode => $oAttDef) { - $aPrerequisites = MetaModel::GetPrequisiteAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one + $aPrerequisites = MetaModel::GetPrerequisiteAttributes($sClass, $sAttCode); // List of attributes that are needed for the current one if (count($aPrerequisites) > 0) { // When 'enabling' a field, all its prerequisites must be enabled too diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 7f53dea14..529c11c11 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -1,5 +1,5 @@ Get("allowed_values");} - public function GetPrerequisiteAttributes() {return $this->Get("depends_on");} + public function GetPrerequisiteAttributes($sClass = null) {return $this->Get("depends_on");} public function GetDefaultValue($aArgs = array()) { // Note: so far, this feature is a prototype, @@ -1274,7 +1274,7 @@ class AttributeDBFieldVoid extends AttributeDefinition public function GetEditClass() {return "String";} public function GetValuesDef() {return $this->Get("allowed_values");} - public function GetPrerequisiteAttributes() {return $this->Get("depends_on");} + public function GetPrerequisiteAttributes($sClass = null) {return $this->Get("depends_on");} public function IsDirectField() {return true;} public function IsScalar() {return true;} @@ -2992,6 +2992,120 @@ class AttributeEnum extends AttributeString } } +/** + * A meta enum is an aggregation of enum from subclasses into an enum of a base class + * It has been designed is to cope with the fact that statuses must be defined in leaf classes, while it makes sense to + * have a superstatus available on the root classe(s) + * + * @package iTopORM + */ +class AttributeMetaEnum extends AttributeEnum +{ + static public function ListExpectedParams() + { + return array('allowed_values', 'sql', 'default_value', 'mapping'); + } + + public function IsWritable() + { + return false; + } + + public function RequiresIndex() + { + return true; + } + + public function GetPrerequisiteAttributes($sClass = null) + { + if (is_null($sClass)) + { + $sClass = $this->GetHostClass(); + } + $aMappingData = $this->GetMapRule($sClass); + if ($aMappingData == null) + { + $aRet = array(); + } + else + { + $aRet = array($aMappingData['attcode']); + } + return $aRet; + } + + /** + * Overload the standard so as to leave the data unsorted + * + * @param array $aArgs + * @param string $sContains + * @return array|null + */ + public function GetAllowedValues($aArgs = array(), $sContains = '') + { + $oValSetDef = $this->GetValuesDef(); + if (!$oValSetDef) return null; + $aRawValues = $oValSetDef->GetValueList(); + + if (is_null($aRawValues)) return null; + $aLocalizedValues = array(); + foreach ($aRawValues as $sKey => $sValue) + { + $aLocalizedValues[$sKey] = Str::pure2html($this->GetValueLabel($sKey)); + } + return $aLocalizedValues; + } + + /** + * Returns the meta value for the given object. + * See also MetaModel::RebuildMetaEnums() that must be maintained when MapValue changes + * + * @param $oObject + * @return mixed + * @throws Exception + */ + public function MapValue($oObject) + { + $aMappingData = $this->GetMapRule(get_class($oObject)); + if ($aMappingData == null) + { + $sRet = $this->GetDefaultValue(); + } + else + { + $sAttCode = $aMappingData['attcode']; + $value = $oObject->Get($sAttCode); + if (array_key_exists($value, $aMappingData['values'])) + { + $sRet = $aMappingData['values'][$value]; + } + elseif ($this->GetDefaultValue() != '') + { + $sRet = $this->GetDefaultValue(); + } + else + { + throw new Exception('AttributeMetaEnum::MapValue(): mapping not found for value "'.$value.'" in '.get_class($oObject).', on attribute '.MetaModel::GetAttributeOrigin($this->GetHostClass(), $this->GetCode()).'::'.$this->GetCode()); + } + } + return $sRet; + } + + public function GetMapRule($sClass) + { + $aMappings = $this->Get('mapping'); + if (array_key_exists($sClass, $aMappings)) + { + $aMappingData = $aMappings[$sClass]; + } + else + { + $sParent = MetaModel::GetParentClass($sClass); + $aMappingData = $this->GetMapRule($sParent); + } + return $aMappingData; + } +} /** * Map a date+time column to an attribute * @@ -3794,7 +3908,7 @@ class AttributeExternalField extends AttributeDefinition } } - public function GetPrerequisiteAttributes() + public function GetPrerequisiteAttributes($sClass = null) { return array($this->Get("extkey_attcode")); } @@ -4833,7 +4947,6 @@ class AttributeSubItem extends AttributeDefinition public function GetEditClass() {return "";} public function GetValuesDef() {return null;} - //public function GetPrerequisiteAttributes() {return $this->Get("depends_on");} public function IsDirectField() {return true;} public function IsScalar() {return true;} @@ -5322,7 +5435,7 @@ class AttributeComputedFieldVoid extends AttributeDefinition public function GetEditClass() {return "";} public function GetValuesDef() {return null;} - public function GetPrerequisiteAttributes() {return $this->GetOptional("depends_on", array());} + public function GetPrerequisiteAttributes($sClass = null) {return $this->GetOptional("depends_on", array());} public function IsDirectField() {return true;} public function IsScalar() {return true;} @@ -5546,7 +5659,7 @@ class AttributeRedundancySettings extends AttributeDBField } public function GetValuesDef() {return null;} - public function GetPrerequisiteAttributes() {return array();} + public function GetPrerequisiteAttributes($sClass = null) {return array();} public function GetEditClass() {return "RedundancySetting";} protected function GetSQLCol($bFullSpec = false) diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 50df3469c..1125ac131 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -1,5 +1,5 @@ m_aCurrValues[$sAttCode] = $realvalue; $this->m_aTouchedAtt[$sAttCode] = true; unset($this->m_aModifiedAtt[$sAttCode]); - + + foreach (MetaModel::ListMetaAttributes(get_class($this), $sAttCode) as $sMetaAttCode => $oMetaAttDef) + { + $this->Set($sMetaAttCode, $oMetaAttDef->MapValue($this)); + } + // The object has changed, reset caches $this->m_bCheckStatus = null; diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 8aa9131d4..5567bd4a9 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -1,5 +1,5 @@ GetPrerequisiteAttributes(); } /** - * Find all attributes that depend on the specified one (reverse of GetPrequisiteAttributes) + * Find all attributes that depend on the specified one (reverse of GetPrerequisiteAttributes) * @param string $sClass Name of the class * @param string $sAttCode Code of the attributes * @return Array List of attribute codes that depend on the given attribute, empty array if none. @@ -510,7 +510,7 @@ abstract class MetaModel self::_check_subclass($sClass); foreach (self::ListAttributeDefs($sClass) as $sDependentAttCode=>$void) { - $aPrerequisites = self::GetPrequisiteAttributes($sClass, $sDependentAttCode); + $aPrerequisites = self::GetPrerequisiteAttributes($sClass, $sDependentAttCode); if (in_array($sAttCode, $aPrerequisites)) { $aResults[] = $sDependentAttCode; @@ -633,7 +633,8 @@ abstract class MetaModel private static $m_aAttribDefs = array(); // array of ("classname" => array of attributes) private static $m_aAttribOrigins = array(); // array of ("classname" => array of ("attcode"=>"sourceclass")) private static $m_aExtKeyFriends = array(); // array of ("classname" => array of ("indirect ext key attcode"=> array of ("relative ext field"))) - private static $m_aIgnoredAttributes = array(); //array of ("classname" => array of ("attcode") + private static $m_aIgnoredAttributes = array(); //array of ("classname" => array of ("attcode")) + private static $m_aEnumToMeta = array(); // array of ("classname" => array of ("attcode" => array of ("metaattcode" => oMetaAttDef)) final static public function ListAttributeDefs($sClass) { @@ -848,6 +849,18 @@ abstract class MetaModel return self::$m_aTrackForwardCache[$sClass]; } + final static public function ListMetaAttributes($sClass, $sAttCode) + { + if (isset(self::$m_aEnumToMeta[$sClass][$sAttCode])) + { + $aRet = self::$m_aEnumToMeta[$sClass][$sAttCode]; + } + else + { + $aRet = array(); + } + return $aRet; + } /** * Get the attribute label @@ -1847,6 +1860,15 @@ abstract class MetaModel } } } + if ($oAttDef instanceof AttributeMetaEnum) + { + $aMappingData = $oAttDef->GetMapRule($sClass); + if ($aMappingData != null) + { + $sEnumAttCode = $aMappingData['attcode']; + self::$m_aEnumToMeta[$sClass][$sEnumAttCode][$sAttCode] = $oAttDef; + } + } } // Add a 'id' filter @@ -2685,7 +2707,77 @@ abstract class MetaModel CMDBSource::Query($sSQL); } } - + + /** + * Update the meta enums + * See Also AttributeMetaEnum::MapValue that must be aligned with the above implementation + * + * @param $bVerbose boolean Displays some information about what is done/what needs to be done + */ + public static function RebuildMetaEnums($bVerbose = false) + { + foreach (self::GetClasses() as $sClass) + { + if (!self::HasTable($sClass)) continue; + + foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) + { + // Check (once) all the attributes that are hierarchical keys + if((self::GetAttributeOrigin($sClass, $sAttCode) == $sClass) && $oAttDef instanceof AttributeEnum) + { + if (isset(self::$m_aEnumToMeta[$sClass][$sAttCode])) + { + foreach (self::$m_aEnumToMeta[$sClass][$sAttCode] as $sMetaAttCode => $oMetaAttDef) + { + $aMetaValues = array(); // array of (metavalue => array of values) + foreach ($oAttDef->GetAllowedValues() as $sCode => $sLabel) + { + $aMappingData = $oMetaAttDef->GetMapRule($sClass); + if ($aMappingData == null) + { + $sMetaValue = $oMetaAttDef->GetDefaultValue(); + } + else + { + if (array_key_exists($sCode, $aMappingData['values'])) + { + $sMetaValue = $aMappingData['values'][$sCode]; + } + elseif ($oMetaAttDef->GetDefaultValue() != '') + { + $sMetaValue = $oMetaAttDef->GetDefaultValue(); + } + else + { + throw new Exception('MetaModel::RebuildMetaEnums(): mapping not found for value "'.$sCode.'"" in '.$sClass.', on attribute '.self::GetAttributeOrigin($sClass, $oMetaAttDef->GetCode()).'::'.$oMetaAttDef->GetCode()); + } + } + $aMetaValues[$sMetaValue][] = $sCode; + } + foreach ($aMetaValues as $sMetaValue => $aEnumValues) + { + $sMetaTable = self::DBGetTable($sClass, $sMetaAttCode); + $sEnumTable = self::DBGetTable($sClass); + $aColumns = array_keys($oMetaAttDef->GetSQLColumns()); + $sMetaColumn = reset($aColumns); + $aColumns = array_keys($oAttDef->GetSQLColumns()); + $sEnumColumn = reset($aColumns); + $sValueList = implode(', ', CMDBSource::Quote($aEnumValues)); + $sSql = "UPDATE `$sMetaTable` JOIN `$sEnumTable` ON `$sEnumTable`.id = `$sMetaTable`.id SET `$sMetaTable`.`$sMetaColumn` = '$sMetaValue' WHERE `$sEnumTable`.`$sEnumColumn` IN ($sValueList) AND `$sMetaTable`.`$sMetaColumn` != '$sMetaValue'"; + if ($bVerbose) + { + echo "Executing query: $sSql\n"; + } + CMDBSource::Query($sSql); + } + } + } + } + } + } + } + + public static function CheckDataSources($bDiagnostics, $bVerbose) { $sOQL = 'SELECT SynchroDataSource'; @@ -4190,6 +4282,7 @@ abstract class MetaModel self::$m_aStimuli = $result['m_aStimuli']; self::$m_aTransitions = $result['m_aTransitions']; self::$m_aHighlightScales = $result['m_aHighlightScales']; + self::$m_aEnumToMeta = $result['m_aEnumToMeta']; } $oKPI->ComputeAndReport('Metamodel APC (fetch + read)'); } @@ -4227,6 +4320,7 @@ abstract class MetaModel $aCache['m_aStimuli'] = self::$m_aStimuli; // array of ("classname" => array of ("stimuluscode"=>array('label'=>...))) $aCache['m_aTransitions'] = self::$m_aTransitions; // array of ("classname" => array of ("statcode_from"=>array of ("stimuluscode" => array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD))) $aCache['m_aHighlightScales'] = self::$m_aHighlightScales; // array of ("classname" => array of higlightcodes))) + $aCache['m_aEnumToMeta'] = self::$m_aEnumToMeta; apc_store($sOqlAPCCacheId, $aCache); $oKPI->ComputeAndReport('Metamodel APC (store)'); } 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 54ed7d3ac..8d225e7fa 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 @@ -1103,6 +1103,9 @@ 30 + + 35 + 40 @@ -1179,6 +1182,9 @@ 60 + + 65 + 70 @@ -1893,6 +1899,9 @@ 30 + + 35 + 40 @@ -1969,6 +1978,9 @@ 60 + + 65 + 70 @@ -2884,6 +2896,9 @@ 30 + + 35 + 40 @@ -2963,6 +2978,9 @@ 60 + + 65 + 70 @@ -3570,6 +3588,9 @@ 30 + + 35 + 40 @@ -3652,6 +3673,9 @@ 60 + + 65 + 70 @@ -4323,6 +4347,9 @@ 30 + + 35 + 40 @@ -4402,6 +4429,9 @@ 60 + + 65 + 70 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 4f6830f9d..302870fe0 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 @@ -664,6 +664,9 @@ 40 + + 45 + 50 @@ -707,6 +710,9 @@ 50 + + 55 + 60 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 518c8167d..6e8448c0a 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 @@ -1553,6 +1553,9 @@ 90 + + 95 + 100 @@ -1614,6 +1617,9 @@ 50 + + 55 + 60 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 daa76b917..293187964 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 @@ -607,6 +607,9 @@ 50 + + 55 + 60 @@ -653,6 +656,9 @@ 40 + + 45 + 50 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 e542f87c8..6fb81e76d 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 @@ -1615,6 +1615,9 @@ 90 + + 95 + 100 @@ -1679,6 +1682,9 @@ 50 + + 55 + 60 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 9916b3c6d..76aa59c32 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 @@ -1615,6 +1615,9 @@ 90 + + 95 + 100 @@ -1679,6 +1682,9 @@ 50 + + 55 + 60 diff --git a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml index 4456ca5fa..277591027 100755 --- a/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml +++ b/datamodels/2.x/itop-tickets/datamodel.itop-tickets.xml @@ -1,5 +1,5 @@ - + service_id AND sc.org_id = :this->org_id AND slt.request_type = :request_type AND slt.priority = :this->priority]]> @@ -45,6 +45,28 @@ + + + active + inactive + + operational_status + active + + + status + + + + + + + + + + + + ref @@ -238,6 +260,9 @@ 70 + + 75 + 80 @@ -275,6 +300,9 @@ 30 + + 35 + 40 @@ -309,6 +337,9 @@ 60 + + 65 + 70 diff --git a/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php b/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php index 6888d212c..169e79153 100755 --- a/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php +++ b/datamodels/2.x/itop-tickets/en.dict.itop-tickets.php @@ -82,6 +82,12 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:Ticket/Attribute:workorders_list+' => 'All the work orders for this ticket', 'Class:Ticket/Attribute:finalclass' => 'Type', 'Class:Ticket/Attribute:finalclass+' => '', + 'Class:Ticket/Attribute:operational_status' => 'Operational status', + 'Class:Ticket/Attribute:operational_status+' => 'Computed after the detailed status', + 'Class:Ticket/Attribute:operational_status/Value:active' => 'Active', + 'Class:Ticket/Attribute:operational_status/Value:active+' => 'Work in progress', + 'Class:Ticket/Attribute:operational_status/Value:inactive' => 'Inactive', + 'Class:Ticket/Attribute:operational_status/Value:inactive+' => 'Done', 'Ticket:ImpactAnalysis' => 'Impact Analysis', )); diff --git a/datamodels/2.x/itop-tickets/fr.dict.itop-tickets.php b/datamodels/2.x/itop-tickets/fr.dict.itop-tickets.php index 893d8ab32..23c5b68f3 100755 --- a/datamodels/2.x/itop-tickets/fr.dict.itop-tickets.php +++ b/datamodels/2.x/itop-tickets/fr.dict.itop-tickets.php @@ -69,6 +69,12 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:Ticket/Attribute:workorders_list+' => '', 'Class:Ticket/Attribute:finalclass' => 'Type', 'Class:Ticket/Attribute:finalclass+' => '', + 'Class:Ticket/Attribute:operational_status' => 'Statut opérationnel', + 'Class:Ticket/Attribute:operational_status+' => 'Calculé à partir du statut détaillé', + 'Class:Ticket/Attribute:operational_status/Value:active' => 'Actif', + 'Class:Ticket/Attribute:operational_status/Value:active+' => 'Traitement en cours', + 'Class:Ticket/Attribute:operational_status/Value:inactive' => 'Inactif', + 'Class:Ticket/Attribute:operational_status/Value:inactive+' => 'Ticket définitivement fermé', 'Ticket:ImpactAnalysis' => 'Analyse d\'Impact', )); diff --git a/pages/UI.php b/pages/UI.php index 1821b9aa3..e0e42b6ef 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -1,5 +1,5 @@ 0) { // When 'enabling' a field, all its prerequisites must be enabled too diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index 869b46672..b75700d54 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -1,5 +1,5 @@ GetPropString($oField, 'display_style', 'list'); $aParameters['sql'] = $this->GetMandatoryPropString($oField, 'sql', ''); $aParameters['default_value'] = $this->GetPropString($oField, 'default_value', ''); - $aParameters['is_null_allowed'] = $this->GetPropBoolean($oField, 'is_null_allowed', false); - $aParameters['depends_on'] = $sDependencies; $oMappings = $oField->GetUniqueElement('mappings'); $oMappingNodes = $oMappings->getElementsByTagName('mapping'); diff --git a/setup/itopdesignformat.class.inc.php b/setup/itopdesignformat.class.inc.php index b1b824e3c..4e0fb002e 100644 --- a/setup/itopdesignformat.class.inc.php +++ b/setup/itopdesignformat.class.inc.php @@ -1,5 +1,5 @@ LogWarning('The module design defined in '.self::GetItopNodePath($oNode).' will be lost.'); $this->DeleteNode($oNode); } + + // Remove MetaEnum attributes + // + $oNodeList = $oXPath->query("/itop_design/classes//class/fields/field[@xsi:type='AttributeMetaEnum']"); + foreach ($oNodeList as $oNode) + { + $this->LogWarning('The attribute '.self::GetItopNodePath($oNode).' is irrelevant and must be removed.'); + $this->DeleteNode($oNode); + } } /**