diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 9892bad2c..23e00827d 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -1381,15 +1381,12 @@ EOF $rawValue = $oObj->Get($sAttCodeEx); // Due to custom formatting rules, empty friendlynames may be rendered as non-empty strings // let's fix this and make sure we render an empty string if the key == 0 - if ($oAttDef instanceof AttributeFriendlyName) + if ($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName()) { $sKeyAttCode = $oAttDef->GetKeyAttCode(); - if ($sKeyAttCode != 'id') + if ($oObj->Get($sKeyAttCode) == 0) { - if ($oObj->Get($sKeyAttCode) == 0) - { - $rawValue = ''; - } + $rawValue = ''; } } if ($bLocalize) diff --git a/application/datatable.class.inc.php b/application/datatable.class.inc.php index 0e4e552db..d83d132b7 100644 --- a/application/datatable.class.inc.php +++ b/application/datatable.class.inc.php @@ -928,8 +928,15 @@ class DataTableSettings implements Serializable } else if ($oAttDef->IsExternalField()) { - $oExtAttDef = $oAttDef->GetExtAttDef(); - $sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel()); + if ($oAttDef->IsFriendlyName()) + { + $sLabel = Dict::Format('UI:ExtKey_AsFriendlyName', $oAttDef->GetLabel()); + } + else + { + $oExtAttDef = $oAttDef->GetExtAttDef(); + $sLabel = Dict::Format('UI:ExtField_AsRemoteField', $oAttDef->GetLabel(), $oExtAttDef->GetLabel()); + } } elseif ($oAttDef instanceof AttributeFriendlyName) { diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 6476062bd..409554646 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -239,7 +239,7 @@ abstract class AttributeDefinition */ static public function IsBasedOnDBColumns() {return false;} /** - * Returns true if the attribute value is built after other attributes by the mean of an expression + * Returns true if the attribute value is built after other attributes by the mean of an expression (obtained via GetOQLExpression) * @return bool */ static public function IsBasedOnOQLExpression() {return false;} @@ -926,9 +926,12 @@ class AttributeLinkedSet extends AttributeDefinition } } if ($sAttCode == $this->GetExtKeyToMe()) continue; - if ($oAttDef->IsExternalField() && ($oAttDef->GetKeyAttCode() == $this->GetExtKeyToMe())) continue; - if (($oAttDef instanceof AttributeFriendlyName) && ($oAttDef->GetKeyAttCode() == $this->GetExtKeyToMe())) continue; - if (($oAttDef instanceof AttributeFriendlyName) && ($oAttDef->GetKeyAttCode() == 'id')) continue; + if ($oAttDef->IsExternalField()) + { + if ($oAttDef->GetKeyAttCode() == $this->GetExtKeyToMe()) continue; + if ($oAttDef->IsFriendlyName()) continue; + } + if ($oAttDef instanceof AttributeFriendlyName) continue; if (!$oAttDef->IsScalar()) continue; $sAttValue = $oObj->GetAsXML($sAttCode, $bLocalize); $sRes .= "<$sAttCode>$sAttValue$sAttCode>\n"; @@ -4826,11 +4829,20 @@ class AttributeExternalField extends AttributeDefinition public function GetLabel($sDefault = null) { - $sLabel = parent::GetLabel(''); - if (strlen($sLabel) == 0) + if ($this->IsFriendlyName()) { - $oRemoteAtt = $this->GetExtAttDef(); - $sLabel = $oRemoteAtt->GetLabel($this->m_sCode); + $sKeyAttCode = $this->Get("extkey_attcode"); + $oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode); + $sLabel = $oExtKeyAttDef->GetLabel($this->m_sCode); + } + else + { + $sLabel = parent::GetLabel(''); + if (strlen($sLabel) == 0) + { + $oRemoteAtt = $this->GetExtAttDef(); + $sLabel = $oRemoteAtt->GetLabel($this->m_sCode); + } } return $sLabel; } @@ -4872,6 +4884,27 @@ class AttributeExternalField extends AttributeDefinition } } + /** + * @return bool + */ + public function IsFriendlyName() + { + $oRemoteAtt = $this->GetExtAttDef(); + if ($oRemoteAtt instanceof AttributeExternalField) + { + $bRet = $oRemoteAtt->IsFriendlyName(); + } + elseif ($oRemoteAtt instanceof AttributeFriendlyName) + { + $bRet = true; + } + else + { + $bRet = false; + } + return $bRet; + } + public function GetTargetClass($iType = EXTKEY_RELATIVE) { return $this->GetKeyAttDef($iType)->GetTargetClass(); @@ -6700,12 +6733,11 @@ class AttributePropertySet extends AttributeTable */ class AttributeFriendlyName extends AttributeDefinition { - public function __construct($sCode, $sExtKeyAttCode) + public function __construct($sCode) { $this->m_sCode = $sCode; $aParams = array(); $aParams["default_value"] = ''; - $aParams["extkey_attcode"] = $sExtKeyAttCode; parent::__construct($sCode, $aParams); $this->m_sValue = $this->Get("default_value"); @@ -6732,84 +6764,15 @@ class AttributeFriendlyName extends AttributeDefinition static public function IsBasedOnOQLExpression() {return true;} public function GetOQLExpression() { - return static::GetExtendedNameExpression($this->GetHostClass()); + return MetaModel::GetNameExpression($this->GetHostClass()); } - /** - * Get the friendly name for the class and its subclasses (if finalclass = 'subclass' ...) - * Simplifies the final expression by grouping classes having the same name expression - * Used when querying a parent class - */ - static protected function GetExtendedNameExpression($sClass) - { - // 1st step - get all of the required expressions (instantiable classes) - // and group them using their OQL representation - // - $aFNExpressions = array(); // signature => array('expression' => oExp, 'classes' => array of classes) - foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sSubClass) - { - if (($sSubClass != $sClass) && MetaModel::IsAbstract($sSubClass)) continue; - - $oSubClassName = MetaModel::GetNameExpression($sSubClass); - $sSignature = $oSubClassName->Render(); - if (!array_key_exists($sSignature, $aFNExpressions)) - { - $aFNExpressions[$sSignature] = array( - 'expression' => $oSubClassName, - 'classes' => array(), - ); - } - $aFNExpressions[$sSignature]['classes'][] = $sSubClass; - } - - // 2nd step - build the final name expression depending on the finalclass - // - if (count($aFNExpressions) == 1) - { - $aExpData = reset($aFNExpressions); - $oNameExpression = $aExpData['expression']; - } - else - { - $oNameExpression = null; - foreach ($aFNExpressions as $sSignature => $aExpData) - { - $oClassListExpr = ListExpression::FromScalars($aExpData['classes']); - $oClassExpr = new FieldExpression('finalclass', $sClass); - $oClassInList = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr); - - if (is_null($oNameExpression)) - { - $oNameExpression = $aExpData['expression']; - } - else - { - $oNameExpression = new FunctionExpression('IF', array($oClassInList, $aExpData['expression'], $oNameExpression)); - } - } - } - return $oNameExpression; - } - - - public function GetKeyAttCode() {return $this->Get("extkey_attcode");} - - public function GetExtAttCode() {return 'friendlyname';} public function GetLabel($sDefault = null) { $sLabel = parent::GetLabel(''); if (strlen($sLabel) == 0) { - $sKeyAttCode = $this->Get("extkey_attcode"); - if ($sKeyAttCode == 'id') - { - return Dict::S('Core:FriendlyName-Label'); - } - else - { - $oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode); - $sLabel = $oExtKeyAttDef->GetLabel($this->m_sCode); - } + $sLabel = Dict::S('Core:FriendlyName-Label'); } return $sLabel; } @@ -6818,16 +6781,7 @@ class AttributeFriendlyName extends AttributeDefinition $sLabel = parent::GetDescription(''); if (strlen($sLabel) == 0) { - $sKeyAttCode = $this->Get("extkey_attcode"); - if ($sKeyAttCode == 'id') - { - return Dict::S('Core:FriendlyName-Description'); - } - else - { - $oExtKeyAttDef = MetaModel::GetAttributeDef($this->GetHostClass(), $sKeyAttCode); - $sLabel = $oExtKeyAttDef->GetDescription(''); - } + $sLabel = Dict::S('Core:FriendlyName-Description'); } return $sLabel; } @@ -7712,3 +7666,58 @@ class AttributeArchiveDate extends AttributeDate return parent::GetDescription($sDefault); } } + +class AttributeObsolescenceFlag extends AttributeBoolean +{ + public function __construct($sCode) + { + parent::__construct($sCode, array("allowed_values"=>null, "sql"=>$sCode, "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())); + } + public function IsWritable() + { + return false; + } + public function IsMagic() + { + return true; + } + + static public function IsBasedOnDBColumns() {return false;} + /** + * Returns true if the attribute value is built after other attributes by the mean of an expression (obtained via GetOQLExpression) + * @return bool + */ + static public function IsBasedOnOQLExpression() {return true;} + public function GetOQLExpression() + { + return MetaModel::GetObsolescenceExpression($this->GetHostClass()); + } + + public function GetSQLExpressions($sPrefix = '') + { + return array(); + if ($sPrefix == '') + { + $sPrefix = $this->GetCode(); // Warning AttributeComputedFieldVoid does not have any sql property + } + return array('' => $sPrefix); + } + public function GetSQLColumns($bFullSpec = false) {return array();} // returns column/spec pairs (1 in most of the cases), for STRUCTURING (DB creation) + public function GetSQLValues($value) {return array();} // returns column/value pairs (1 in most of the cases), for WRITING (Insert, Update) + + public function GetEditClass() {return "";} + + public function GetValuesDef() {return null;} + public function GetPrerequisiteAttributes($sClass = null) {return $this->GetOptional("depends_on", array());} + + public function IsDirectField() {return true;} + static public function IsScalar() {return true;} + public function GetSQLExpr() + { + return null; + } + + public function GetDefaultValue(DBObject $oHostObject = null) {return $this->MakeRealValue("", $oHostObject);} + public function IsNullAllowed() {return false;} +} + diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 5ab61e3e3..335e1378d 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -410,7 +410,7 @@ abstract class DBObject implements iDisplay foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef) { - if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sAttCode)) + if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode)) { $this->m_aCurrValues[$sCode] = $value->Get($oDef->GetExtAttCode()); $this->m_aLoadedAtt[$sCode] = true; @@ -423,7 +423,7 @@ abstract class DBObject implements iDisplay // Invalidate the corresponding fields so that they get reloaded in case they are needed (See Get()) foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef) { - if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sAttCode)) + if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode)) { $this->m_aCurrValues[$sCode] = $oDef->GetDefaultValue($this); unset($this->m_aLoadedAtt[$sCode]); @@ -566,7 +566,7 @@ abstract class DBObject implements iDisplay else { // Not loaded... is it related to an external key? - if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName)) + if ($oAttDef->IsExternalField()) { // Let's get the object and compute all of the corresponding attributes // (i.e not only the requested attribute) @@ -588,7 +588,7 @@ abstract class DBObject implements iDisplay foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef) { - if (($oDef->IsExternalField() || ($oDef instanceof AttributeFriendlyName)) && ($oDef->GetKeyAttCode() == $sExtKeyAttCode)) + if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sExtKeyAttCode)) { if ($oRemote) { @@ -3614,6 +3614,16 @@ abstract class DBObject implements iDisplay return $bRet; } + public function IsObsolete() + { + $bRet = false; + if (MetaModel::IsValidAttCode(get_class($this), 'obsolescence_flag') && $this->Get('obsolescence_flag')) + { + $bRet = true; + } + return $bRet; + } + /** * @param $bArchive * @throws Exception diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index e74a4ee64..3e0ae57ea 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -1677,15 +1677,15 @@ class DBObjectSearch extends DBSearch } $aFNJoinAlias = array(); // array of (subclass => alias) - foreach ($aExpectedAtts as $sAttCode => $oExpression) + foreach ($aExpectedAtts as $sExpectedAttCode => $oExpression) { - if (!MetaModel::IsValidAttCode($sClass, $sAttCode)) continue; - $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + if (!MetaModel::IsValidAttCode($sClass, $sExpectedAttCode)) continue; + $oAttDef = MetaModel::GetAttributeDef($sClass, $sExpectedAttCode); if ($oAttDef->IsBasedOnOQLExpression()) { // To optimize: detect a restriction on child classes in the condition expression // e.g. SELECT FunctionalCI WHERE finalclass IN ('Server', 'VirtualMachine') - $oExpression = $oAttDef->GetOQLExpression($sClass); + $oExpression = static::GetPolymorphicExpression($sClass, $sExpectedAttCode); $aRequiredFields = array(); $oExpression->GetUnresolvedFields('', $aRequiredFields); @@ -1700,7 +1700,7 @@ class DBObjectSearch extends DBSearch $sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode); $aExtKeys[$sClassOfAttribute][$sAttCode] = array(); } - elseif ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName)) + elseif ($oAttDef->IsExternalField()) { $sKeyAttCode = $oAttDef->GetKeyAttCode(); $sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sKeyAttCode); @@ -1739,7 +1739,7 @@ class DBObjectSearch extends DBSearch $oExpression = $oExpression->Translate($aTranslateFields, false); $aTranslateNow = array(); - $aTranslateNow[$sClassAlias]['friendlyname'] = $oExpression; + $aTranslateNow[$sClassAlias][$sExpectedAttCode] = $oExpression; $oBuild->m_oQBExpressions->Translate($aTranslateNow, false); } } @@ -1747,17 +1747,14 @@ class DBObjectSearch extends DBSearch // Add the ext fields used in the select (eventually adds an external key) foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) { - if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName)) + if ($oAttDef->IsExternalField()) { if (array_key_exists($sAttCode, $aExpectedAtts)) { + // Add the external attribute $sKeyAttCode = $oAttDef->GetKeyAttCode(); - if ($sKeyAttCode != 'id') - { - // Add the external attribute - $sKeyTableClass = MetaModel::GetAttributeOrigin($sClass, $sKeyAttCode); - $aExtKeys[$sKeyTableClass][$sKeyAttCode][$sAttCode] = $oAttDef; - } + $sKeyTableClass = MetaModel::GetAttributeOrigin($sClass, $sKeyAttCode); + $aExtKeys[$sKeyTableClass][$sKeyAttCode][$sAttCode] = $oAttDef; } } } @@ -1871,7 +1868,6 @@ class DBObjectSearch extends DBSearch protected function MakeSQLObjectQuerySingleTable(&$oBuild, $aAttToLoad, $sTableClass, $aExtKeys, $aValues) { // $aExtKeys is an array of sTableClass => array of (sAttCode (keys) => array of sAttCode (fields)) -//echo "MakeSQLObjectQuery($sTableClass)-liste des clefs externes($sTableClass):
".print_r($aExtKeys, true)."
MakeSQLObjectQuerySingleTable: Field $sAttCode is part of the table $sTable (named: $sTableAlias)
"; // standard field, or external key // add it to the output foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr) @@ -1971,7 +1966,6 @@ class DBObjectSearch extends DBSearch } } -//echo "MakeSQLObjectQuery- Classe $sTableClass".print_r(array_keys($aAllPointingTo), true)."
".print_r($oAtt, true)."
aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression('friendlyname', $sKeyClassAlias);
\n"; } else { @@ -2040,9 +2026,7 @@ class DBObjectSearch extends DBSearch foreach ($oRemoteAttDef->GetSQLExpressions() as $sColId => $sRemoteAttExpr) { $aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias); -//echo "aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias);
\n"; } -//echo "ExtAttr2: $sTargetAlias.$sAttCode to $sKeyClassAlias.$sRemoteAttExpr (class: $sKeyClass)
\n"; } } @@ -2064,15 +2048,11 @@ class DBObjectSearch extends DBSearch // Translate prior to recursing // -//echo "oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."\n".print_r($aTranslateNow, true)."\n"; $oBuild->m_oQBExpressions->Translate($aTranslateNow, false); -//echo "
oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."\n"; - -//echo "
External key $sKeyAttCode (class: $sKeyClass), call MakeSQLObjectQuery()/p>\n"; + self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeSQLObjectQuery()"); $oBuild->m_oQBExpressions->PushJoinField(new FieldExpression('id', $sKeyClassAlias)); -//echo "
Recursive MakeSQLObjectQuery ".__LINE__.":
\n".print_r($oBuild->GetRootFilter()->GetSelectedClasses(), true)."\n"; $oSelectExtKey = $oExtFilter->MakeSQLObjectQuery($oBuild, $aAttToLoad); $oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField(); @@ -2114,9 +2094,7 @@ class DBObjectSearch extends DBSearch // Translate the selected columns // -//echo "
oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."\n"; $oBuild->m_oQBExpressions->Translate($aTranslation, false); -//echo "
oQBExpr ".__LINE__.":
\n".print_r($oBuild->m_oQBExpressions, true)."\n"; // Filter out archived records // @@ -2125,7 +2103,6 @@ class DBObjectSearch extends DBSearch if (!$oBuild->GetRootFilter()->GetArchiveMode()) { $bIsOnJoinedClass = array_key_exists($sTargetAlias, $oBuild->GetRootFilter()->GetJoinedClasses()); - //$bIsOnJoinedClass = true; if ($bIsOnJoinedClass) { if (MetaModel::IsParentClass($sTableClass, $sTargetClass)) @@ -2136,7 +2113,84 @@ class DBObjectSearch extends DBSearch } } } - //MyHelpers::var_dump_html($oSelectBase->RenderSelect()); return $oSelectBase; } + + /** + * Get the expression for the class and its subclasses (if finalclass = 'subclass' ...) + * Simplifies the final expression by grouping classes having the same expression + */ + static protected function GetPolymorphicExpression($sClass, $sAttCode) + { + // 1st step - get all of the required expressions (instantiable classes) + // and group them using their OQL representation + // + $aExpressions = array(); // signature => array('expression' => oExp, 'classes' => array of classes) + foreach (MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL) as $sSubClass) + { + if (($sSubClass != $sClass) && MetaModel::IsAbstract($sSubClass)) continue; + + $oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode); + $oSubClassExp = $oAttDef->GetOQLExpression($sSubClass); + + // 3rd step - position the attributes in the hierarchy of classes + // + $oSubClassExp->Browse(function($oNode) use ($sSubClass) { + if ($oNode instanceof FieldExpression) + { + $sAttCode = $oNode->GetName(); + $oAttDef = MetaModel::GetAttributeDef($sSubClass, $sAttCode); + if ($oAttDef->IsExternalField()) + { + $sKeyAttCode = $oAttDef->GetKeyAttCode(); + $sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sKeyAttCode); + } + else + { + $sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode); + } + $sParent = MetaModel::GetAttributeOrigin($sClassOfAttribute, $oNode->GetName()); + $oNode->SetParent($sParent); + } + }); + + $sSignature = $oSubClassExp->Render(); + if (!array_key_exists($sSignature, $aExpressions)) + { + $aExpressions[$sSignature] = array( + 'expression' => $oSubClassExp, + 'classes' => array(), + ); + } + $aExpressions[$sSignature]['classes'][] = $sSubClass; + } + + // 2nd step - build the final name expression depending on the finalclass + // + if (count($aExpressions) == 1) + { + $aExpData = reset($aExpressions); + $oExpression = $aExpData['expression']; + } + else + { + $oExpression = null; + foreach ($aExpressions as $sSignature => $aExpData) + { + $oClassListExpr = ListExpression::FromScalars($aExpData['classes']); + $oClassExpr = new FieldExpression('finalclass', $sClass); + $oClassInList = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr); + + if (is_null($oExpression)) + { + $oExpression = $aExpData['expression']; + } + else + { + $oExpression = new FunctionExpression('IF', array($oClassInList, $aExpData['expression'], $oExpression)); + } + } + } + return $oExpression; + } } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index cab82b393..d9a9a56a0 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -324,6 +324,18 @@ abstract class MetaModel self::_check_subclass($sClass); return self::$m_aClassParams[$sClass]["archive"]; } + final static public function IsObsoletable($sClass) + { + self::_check_subclass($sClass); + return (!is_null(self::$m_aClassParams[$sClass]['obsolescence_expression'])); + } + final static public function GetObsolescenceExpression($sClass) + { + self::_check_subclass($sClass); + $sOql = self::$m_aClassParams[$sClass]['obsolescence_expression']; + $oRet = Expression::FromOQL($sOql); + return $oRet; + } final static public function GetNameSpec($sClass) { self::_check_subclass($sClass); @@ -379,20 +391,10 @@ abstract class MetaModel // $iReplacement = (int)$sPiece - 1; - if (isset($aAttributes[$iReplacement])) - { - $sAttCode = $aAttributes[$iReplacement]; - $oAttDef = self::GetAttributeDef($sClass, $sAttCode); - if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName)) - { - $sKeyAttCode = $oAttDef->GetKeyAttCode(); - $sClassOfAttribute = self::GetAttributeOrigin($sClass, $sKeyAttCode); - } - else - { - $sClassOfAttribute = self::GetAttributeOrigin($sClass, $sAttCode); - } - $aExpressions[] = new FieldExpression($sAttCode, $sClassOfAttribute); + if (isset($aAttributes[$iReplacement])) + { + $sAttCode = $aAttributes[$iReplacement]; + $aExpressions[] = new FieldExpression($sAttCode); } } else @@ -1758,6 +1760,31 @@ abstract class MetaModel self::$m_aClassParams[$sPHPClass]['archive_root_class'] = $bArchiveRoot ? $sPHPClass : self::$m_aClassParams[$sParent]['archive_root_class']; } + // Inherit obsolescence expression + $sObsolescence = null; + if (isset(self::$m_aClassParams[$sPHPClass]['obsolescence_expression'])) + { + // Defined or overloaded + $sObsolescence = self::$m_aClassParams[$sPHPClass]['obsolescence_expression']; + } + elseif (@self::$m_aClassParams[$sParent]['obsolescence_expression']) + { + // Inherited + $sObsolescence = self::$m_aClassParams[$sParent]['obsolescence_expression']; + } + self::$m_aClassParams[$sPHPClass]['obsolescence_expression'] = $sObsolescence; + + if (@self::$m_aClassParams[$sParent]['obsolescence_expression']) + { + // Inherited or overloaded + self::$m_aClassParams[$sPHPClass]['obsolescence_root_class'] = self::$m_aClassParams[$sParent]['obsolescence_root_class']; + } + elseif ($sObsolescence) + { + // Defined + self::$m_aClassParams[$sPHPClass]['obsolescence_root_class'] = $sPHPClass; + } + foreach (MetaModel::EnumPlugins('iOnClassInitialization') as $sPluginClass => $oClassInit) { $oClassInit->OnAfterClassInitialization($sPHPClass); @@ -1814,7 +1841,7 @@ abstract class MetaModel { // Create the friendly name attribute $sFriendlyNameAttCode = 'friendlyname'; - $oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode, 'id'); + $oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode); self::AddMagicAttribute($oFriendlyName, $sClass); if (self::$m_aClassParams[$sClass]["archive_root"]) @@ -1831,14 +1858,33 @@ abstract class MetaModel $sArchiveRoot = self::$m_aClassParams[$sClass]['archive_root_class']; // Inherit archive attributes $oArchiveFlag = clone self::$m_aAttribDefs[$sArchiveRoot]['archive_flag']; - $oArchiveFlag->SetHostClass($sArchiveRoot); + $oArchiveFlag->SetHostClass($sClass); self::$m_aAttribDefs[$sClass]['archive_flag'] = $oArchiveFlag; self::$m_aAttribOrigins[$sClass]['archive_flag'] = $sArchiveRoot; $oArchiveDate = clone self::$m_aAttribDefs[$sArchiveRoot]['archive_date']; - $oArchiveDate->SetHostClass($sArchiveRoot); + $oArchiveDate->SetHostClass($sClass); self::$m_aAttribDefs[$sClass]['archive_date'] = $oArchiveDate; self::$m_aAttribOrigins[$sClass]['archive_date'] = $sArchiveRoot; } + if (!is_null(self::$m_aClassParams[$sClass]['obsolescence_expression'])) + { + $oObsolescenceFlag = new AttributeObsolescenceFlag('obsolescence_flag'); + self::AddMagicAttribute($oObsolescenceFlag, $sClass); + + $sObsolescenceRoot = self::$m_aClassParams[$sClass]['obsolescence_root_class']; + if ($sClass == $sObsolescenceRoot) + { + $oObsolescenceDate = new AttributeDate('obsolescence_date', array('magic' => true, "allowed_values" => null, "sql" => 'obsolescence_date', "default_value" => '', "is_null_allowed" => true, "depends_on" => array())); + self::AddMagicAttribute($oObsolescenceDate, $sClass); + } + else + { + $oObsolescenceDate = clone self::$m_aAttribDefs[$sObsolescenceRoot]['archive_date']; + $oObsolescenceDate->SetHostClass($sClass); + self::$m_aAttribDefs[$sClass]['obsolescence_date'] = $oObsolescenceDate; + self::$m_aAttribOrigins[$sClass]['obsolescence_date'] = $sObsolescenceRoot; + } + } foreach (self::$m_aAttribDefs[$sClass] as $sAttCode => $oAttDef) { // Compute the filter codes @@ -1882,8 +1928,8 @@ abstract class MetaModel else { // Create the friendly name attribute - $sFriendlyNameAttCode = $sAttCode.'_friendlyname'; - $oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode, $sAttCode); + $sFriendlyNameAttCode = $sAttCode.'_friendlyname'; + $oFriendlyName = new AttributeExternalField($sFriendlyNameAttCode, array('magic' => true, 'allowed_values'=>null, 'extkey_attcode'=>$sAttCode, "target_attcode"=>'friendlyname', 'depends_on'=>array())); self::AddMagicAttribute($oFriendlyName, $sClass, self::$m_aAttribOrigins[$sClass][$sAttCode]); if (self::HasChildrenClasses($sRemoteClass)) @@ -1936,6 +1982,12 @@ abstract class MetaModel $oArchiveRemote = new AttributeExternalField($sArchiveRemote, array("allowed_values"=>null, "extkey_attcode"=>$sAttCode, "target_attcode"=>'archive_flag', "depends_on"=>array())); self::AddMagicAttribute($oArchiveRemote, $sClass, self::$m_aAttribOrigins[$sClass][$sAttCode]); } + if (self::IsObsoletable($sRemoteClass)) + { + $sObsoleteRemote = $sAttCode.'_obsolescence_flag'; + $oObsoleteRemote = new AttributeExternalField($sObsoleteRemote, array("allowed_values"=>null, "extkey_attcode"=>$sAttCode, "target_attcode"=>'archive_flag', "depends_on"=>array())); + self::AddMagicAttribute($oObsoleteRemote, $sClass, self::$m_aAttribOrigins[$sClass][$sAttCode]); + } } if ($oAttDef instanceof AttributeMetaEnum) { @@ -2490,6 +2542,18 @@ abstract class MetaModel } return $aRes; } + public static function EnumObsoletableClasses() + { + $aRes = array(); + foreach (self::GetClasses() as $sClass) + { + if (self::IsObsoletable($sClass)) + { + $aRes[] = $sClass; + } + } + return $aRes; + } public static function HasChildrenClasses($sClass) { @@ -5157,26 +5221,29 @@ abstract class MetaModel else { $oAttDef = static::GetAttributeDef($sClass, $sField); - if ($oAttDef instanceof AttributeFriendlyName) + if ($oAttDef->IsExternalField()) { - $oKeyAttDef = MetaModel::GetAttributeDef($sClass, $oAttDef->GetKeyAttCode()); - $sRemoteClass = $oKeyAttDef->GetTargetClass(); - $sFriendlyNameAttCode = static::GetFriendlyNameAttributeCode($sRemoteClass); - if (is_null($sFriendlyNameAttCode)) + if ($oAttDef->IsFriendlyName()) { - // The friendly name is made of several attributes - $sRet = $oAttDef->GetKeyAttCode().'->friendlyname'; + $oKeyAttDef = MetaModel::GetAttributeDef($sClass, $oAttDef->GetKeyAttCode()); + $sRemoteClass = $oKeyAttDef->GetTargetClass(); + $sFriendlyNameAttCode = static::GetFriendlyNameAttributeCode($sRemoteClass); + if (is_null($sFriendlyNameAttCode)) + { + // The friendly name is made of several attributes + $sRet = $oAttDef->GetKeyAttCode().'->friendlyname'; + } + else + { + // The friendly name is made of a single attribute + $sRet = $oAttDef->GetKeyAttCode().'->'.$sFriendlyNameAttCode; + } } else { - // The friendly name is made of a single attribute - $sRet = $oAttDef->GetKeyAttCode().'->'.$sFriendlyNameAttCode; + $sRet = $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode(); } } - elseif ($oAttDef->IsExternalField()) - { - $sRet = $oAttDef->GetKeyAttCode().'->'.$oAttDef->GetExtAttCode(); - } } return $sRet; } diff --git a/core/oql/expression.class.inc.php b/core/oql/expression.class.inc.php index b9615181a..7ae9913a3 100644 --- a/core/oql/expression.class.inc.php +++ b/core/oql/expression.class.inc.php @@ -45,6 +45,13 @@ abstract class Expression // recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True abstract public function Render(&$aArgs = null, $bRetrofitParams = false); + /** + * Recursively browse the expression tree + * @param Closure $callback + * @return mixed + */ + abstract public function Browse(Closure $callback); + abstract public function ApplyParameters($aArgs); // recursively builds an array of class => fieldname @@ -76,6 +83,10 @@ abstract class Expression return self::FromOQL(base64_decode($sValue)); } + /** + * @param $sConditionExpr + * @return Expression + */ static public function FromOQL($sConditionExpr) { $oOql = new OqlInterpreter($sConditionExpr); @@ -139,6 +150,11 @@ class SQLExpression extends Expression return $this->m_sSQL; } + public function Browse(Closure $callback) + { + $callback($this); + } + public function ApplyParameters($aArgs) { } @@ -245,7 +261,14 @@ class BinaryExpression extends Expression $sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams); return "($sLeft $sOperator $sRight)"; } - + + public function Browse(Closure $callback) + { + $callback($this); + $this->m_oLeftExpr->Browse($callback); + $this->m_oRightExpr->Browse($callback); + } + public function ApplyParameters($aArgs) { if ($this->m_oLeftExpr instanceof VariableExpression) @@ -358,7 +381,7 @@ class UnaryExpression extends Expression public function GetValue() { return $this->m_value; - } + } // recursive rendering public function Render(&$aArgs = null, $bRetrofitParams = false) @@ -366,6 +389,11 @@ class UnaryExpression extends Expression return CMDBSource::Quote($this->m_value); } + public function Browse(Closure $callback) + { + $callback($this); + } + public function ApplyParameters($aArgs) { } @@ -484,6 +512,12 @@ class FieldExpression extends UnaryExpression public function GetParent() {return $this->m_sParent;} public function GetName() {return $this->m_sName;} + public function SetParent($sParent) + { + $this->m_sParent = $sParent; + $this->m_value = $sParent.'.'.$this->m_sName; + } + // recursive rendering public function Render(&$aArgs = null, $bRetrofitParams = false) { @@ -680,7 +714,7 @@ class VariableExpression extends UnaryExpression throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>array_keys($aArgs))); } } - + public function RenameParam($sOldName, $sNewName) { if ($this->m_sName == $sOldName) @@ -768,6 +802,15 @@ class ListExpression extends Expression return '('.implode(', ', $aRes).')'; } + public function Browse(Closure $callback) + { + $callback($this); + foreach ($this->m_aExpressions as $oExpr) + { + $oExpr->Browse($callback); + } + } + public function ApplyParameters($aArgs) { $aRes = array(); @@ -888,6 +931,15 @@ class FunctionExpression extends Expression return $this->m_sVerb.'('.implode(', ', $aRes).')'; } + public function Browse(Closure $callback) + { + $callback($this); + foreach ($this->m_aArgs as $iPos => $oExpr) + { + $oExpr->Browse($callback); + } + } + public function ApplyParameters($aArgs) { $aRes = array(); @@ -1071,6 +1123,12 @@ class IntervalExpression extends Expression return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit; } + public function Browse(Closure $callback) + { + $callback($this); + $this->m_oValue->Browse($callback); + } + public function ApplyParameters($aArgs) { if ($this->m_oValue instanceof VariableExpression) @@ -1151,6 +1209,15 @@ class CharConcatExpression extends Expression return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)"; } + public function Browse(Closure $callback) + { + $callback($this); + foreach ($this->m_aExpressions as $oExpr) + { + $oExpr->Browse($callback); + } + } + public function ApplyParameters($aArgs) { $aRes = array(); @@ -1255,6 +1322,15 @@ class CharConcatWSExpression extends CharConcatExpression return "CAST(CONCAT_WS($sSep, ".implode(', ', $aRes).") AS CHAR)"; } + public function Browse(Closure $callback) + { + $callback($this); + foreach ($this->m_aExpressions as $oExpr) + { + $oExpr->Browse($callback); + } + } + public function Translate($aTranslationData, $bMatchAll = true, $bMarkFieldsAsResolved = true) { $aRes = array(); diff --git a/core/oql/oqlinterpreter.class.inc.php b/core/oql/oqlinterpreter.class.inc.php index 02abc3de4..14e38a858 100644 --- a/core/oql/oqlinterpreter.class.inc.php +++ b/core/oql/oqlinterpreter.class.inc.php @@ -1,5 +1,5 @@ Parse(); @@ -93,6 +96,9 @@ class OqlInterpreter return $oRes; } + /** + * @return Expression + */ public function ParseExpression() { $oRes = $this->Parse(); @@ -103,5 +109,3 @@ class OqlInterpreter return $oRes; } } - -?> diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index fb248c35a..d347bf9ef 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/dictionaries/dictionary.itop.ui.php @@ -775,6 +775,7 @@ Dict::Add('EN US', 'English', 'English', array( 'Tag:Archived+' => 'Can be accessed only in archive mode', 'Tag:Synchronized' => 'Synchronized', 'ObjectRef:Archived' => 'Archived', + 'ObjectRef:Obsolete' => 'Obsolete', 'UI:SearchResultsPageTitle' => 'iTop - Search Results', 'UI:SearchResultsTitle' => 'Search Results', 'UI:SearchResultsTitle+' => 'Full-text search results', diff --git a/setup/compiler.class.inc.php b/setup/compiler.class.inc.php index b58e38bb5..e5ac77be7 100644 --- a/setup/compiler.class.inc.php +++ b/setup/compiler.class.inc.php @@ -994,6 +994,14 @@ EOF $bEnabled = $this->GetPropBoolean($oArchive, 'enabled', false); $aClassParams['archive'] = $bEnabled; } + if ($oObsolescence = $oProperties->GetOptionalElement('obsolescence')) + { + $sCondition = trim($this->GetPropString($oObsolescence, 'condition', '')); + if ($sCondition != "''") + { + $aClassParams['obsolescence_expression'] = $sCondition; + } + } // Finalize class params declaration // @@ -1687,7 +1695,6 @@ EOF $sMethods .= "\t}\n"; } - // Let's make the whole class declaration // $sPHP = "\n\n$sCodeComment\n"; diff --git a/webservices/export.php b/webservices/export.php index 996038623..495522166 100644 --- a/webservices/export.php +++ b/webservices/export.php @@ -162,13 +162,9 @@ if (!empty($sExpression)) { $aAliasToFields[$sClassAlias][] = $oAttDef->GetParentAttCode(); } - else if($oAttDef instanceof AttributeFriendlyname) + else if($oAttDef instanceof AttributeExternalField && $oAttDef->IsFriendlyName()) { - $sKeyAttCode = $oAttDef->GetKeyAttCode(); - if ($sKeyAttCode != 'id') - { - $aAliasToFields[$sClassAlias][] = $sKeyAttCode; - } + $aAliasToFields[$sClassAlias][] = $sKeyAttCode; } }