Prechádzať zdrojové kódy

Clarified the meaning of the attribute 'finalclass'

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@380 a333f486-631f-4898-b8df-5754b55c2be0
romainq 15 rokov pred
rodič
commit
06275ac868

+ 1 - 1
application/cmdbabstract.class.inc.php

@@ -589,7 +589,7 @@ abstract class cmdbAbstractObject extends CMDBObject
 				$oPage->add("<$sClassName alias=\"$sAlias\" id=\"".$oObj->GetKey()."\">\n");
 				foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode=>$oAttDef)
 				{
-					if (($oAttDef->IsWritable()) && ($oAttDef->IsScalar()) && ($sAttCode != 'finalclass') )
+					if (($oAttDef->IsWritable()) && ($oAttDef->IsScalar()))
 					{
 						$sValue = $oObj->GetAsXML($sAttCode);
 						$oPage->add("<$sAttCode>$sValue</$sAttCode>\n");

+ 32 - 2
core/attributedef.class.inc.php

@@ -59,7 +59,7 @@ abstract class AttributeDefinition
 
 	protected $m_sCode;
 	private $m_aParams = array();
-	private $m_sHostClass = array();
+	private $m_sHostClass = '!undefined!';
 	protected function Get($sParamName) {return $this->m_aParams[$sParamName];}
 	protected function IsParam($sParamName) {return (array_key_exists($sParamName, $this->m_aParams));}
 	
@@ -132,7 +132,7 @@ abstract class AttributeDefinition
 	public function IsNullAllowed() {return true;} 
 	public function GetNullValue() {return null;} 
 	public function GetCode() {return $this->m_sCode;} 
-	public function GetLabel() {return Dict::S('Class:'.$this->m_sHostClass.'/Attribute:'.$this->m_sCode, $this->m_sCode);} 
+	public function GetLabel() {return Dict::S('Class:'.$this->m_sHostClass.'/Attribute:'.$this->m_sCode, $this->m_sCode);}
 	public function GetLabel_Obsolete()
 	{
 		// Written for compatibility with a data model written prior to version 0.9.1
@@ -669,6 +669,36 @@ class AttributeClass extends AttributeString
 }
 
 /**
+ * The attribute dedicated to the finalclass automatic attribute 
+ *
+ * @package     iTopORM
+ * @author      Romain Quetiez <romainquetiez@yahoo.fr>
+ * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
+ * @link        www.itop.com
+ * @since       1.0
+ * @version     $itopversion$
+ */
+class AttributeFinalClass extends AttributeString
+{
+	public function __construct($sCode, $aParams)
+	{
+		$this->m_sCode = $sCode;
+		$aParams["allowed_values"] = null;
+		parent::__construct($sCode, $aParams);
+	}
+
+	public function IsWritable()
+	{
+		return false;
+	}
+
+	public function GetAsHTML($sValue)
+	{
+		return MetaModel::GetName($sValue);
+	}
+}
+
+/**
  * Map a varchar column (size < ?) to an attribute that must never be shown to the user 
  *
  * @package     iTopORM

+ 96 - 80
core/metamodel.class.php

@@ -354,18 +354,18 @@ abstract class MetaModel
 		self::_check_subclass($sClass);	
 		return self::$m_aClassParams[$sClass]["db_finalclass_field"];
 	}
-	final static public function HasFinalClassField($sClass)
-	{
-		self::_check_subclass($sClass);	
-		if (!array_key_exists("db_finalclass_field", self::$m_aClassParams[$sClass])) return false;
-		return (self::$m_aClassParams[$sClass]["db_finalclass_field"]);
-	}
 	final static public function IsStandaloneClass($sClass)
 	{
 		self::_check_subclass($sClass);
 
-		$sRootClass = self::GetRootClass($sClass);
-		return (!self::HasFinalClassField($sRootClass));
+		if (count(self::$m_aChildClasses[$sClass]) == 0)
+		{
+			if (count(self::$m_aParentClasses[$sClass]) == 0)
+			{
+				return true;
+			}
+		}
+		return false;
 	}
 	final static public function IsParentClass($sParentClass, $sChildClass)
 	{
@@ -877,37 +877,6 @@ abstract class MetaModel
 			self::$m_aFilterDefs[$sClass]['id'] = $oFilter;
 			self::$m_aFilterOrigins[$sClass]['id'] = $sClass;
 
-			// Add a 'class' attribute/filter to the root classes and their children
-			//
-			if (!self::IsStandaloneClass($sClass))
-			{
-				if (array_key_exists('finalclass', self::$m_aAttribDefs[$sClass]))
-				{
-					throw new CoreException("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as an attribute code");
-				}
-				if (array_key_exists('finalclass', self::$m_aFilterDefs[$sClass]))
-				{
-					throw new CoreException("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as a filter code");
-				}
-				$sClassAttCode = 'finalclass';
-				$sRootClass = self::GetRootClass($sClass);
-				$sDbFinalClassField = self::DBGetClassField($sRootClass);
-				$oClassAtt = new AttributeClass($sClassAttCode, array(
-						"class_category"=>null,
-						"more_values"=>'',
-						"sql"=>$sDbFinalClassField,
-						"default_value"=>$sClass,
-						"is_null_allowed"=>false,
-						"depends_on"=>array()
-				));
-				self::$m_aAttribDefs[$sClass][$sClassAttCode] = $oClassAtt;
-				self::$m_aAttribOrigins[$sClass][$sClassAttCode] = $sRootClass;
-
-				$oClassFlt = new FilterFromAttribute($oClassAtt);
-				self::$m_aFilterDefs[$sClass][$sClassAttCode] = $oClassFlt;
-				self::$m_aFilterOrigins[$sClass][$sClassAttCode] = self::GetRootClass($sClass);
-			}
-
 			// Define defaults values for the standard ZLists
 			//
 			//foreach (self::$m_aListInfos as $sListCode => $aListConfig)
@@ -921,6 +890,50 @@ abstract class MetaModel
 			//}
 		}
 
+		// Add a 'class' attribute/filter to the root classes and their children
+		//
+		foreach(self::EnumRootClasses() as $sRootClass)
+		{
+			if (self::IsStandaloneClass($sRootClass)) continue;
+			
+			$sDbFinalClassField = self::DBGetClassField($sRootClass);
+			if (strlen($sDbFinalClassField) == 0)
+			{
+				$sDbFinalClassField = 'finalclass';
+				self::$m_aClassParams[$sClass]["db_finalclass_field"] = 'finalclass';
+			}
+			$oClassAtt = new AttributeFinalClass('finalclass', array(
+					"sql"=>$sDbFinalClassField,
+					"default_value"=>$sRootClass,
+					"is_null_allowed"=>false,
+					"depends_on"=>array()
+			));
+			$oClassAtt->SetHostClass($sRootClass);
+			self::$m_aAttribDefs[$sRootClass]['finalclass'] = $oClassAtt;
+			self::$m_aAttribOrigins[$sRootClass]['finalclass'] = $sRootClass;
+
+			$oClassFlt = new FilterFromAttribute($oClassAtt);
+			self::$m_aFilterDefs[$sRootClass]['finalclass'] = $oClassFlt;
+			self::$m_aFilterOrigins[$sRootClass]['finalclass'] = $sRootClass;
+
+			foreach(self::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_EXCLUDETOP) as $sChildClass)
+			{
+				if (array_key_exists('finalclass', self::$m_aAttribDefs[$sChildClass]))
+				{
+					throw new CoreException("Class $sChildClass, 'finalclass' is a reserved keyword, it cannot be used as an attribute code");
+				}
+				if (array_key_exists('finalclass', self::$m_aFilterDefs[$sChildClass]))
+				{
+					throw new CoreException("Class $sChildClass, 'finalclass' is a reserved keyword, it cannot be used as a filter code");
+				}
+				self::$m_aAttribDefs[$sChildClass]['finalclass'] = clone $oClassAtt;
+				self::$m_aAttribOrigins[$sChildClass]['finalclass'] = $sRootClass;
+
+				$oClassFlt = new FilterFromAttribute($oClassAtt);
+				self::$m_aFilterDefs[$sChildClass]['finalclass'] = $oClassFlt;
+				self::$m_aFilterOrigins[$sChildClass]['finalclass'] = self::GetRootClass($sChildClass);
+			}
+		}
 	}
 
 	// To be overriden, must be called for any object class (optimization)
@@ -1014,18 +1027,17 @@ abstract class MetaModel
 			self::$m_aAttribDefs[$sTargetClass] = self::object_array_mergeclone(self::$m_aAttribDefs[$sTargetClass], self::$m_aAttribDefs[$sSourceClass]);
 			self::$m_aAttribOrigins[$sTargetClass] = array_merge(self::$m_aAttribOrigins[$sTargetClass], self::$m_aAttribOrigins[$sSourceClass]);
 		}
-		// later on, we might consider inheritance in different ways !!!
-		//if (strlen(self::DBGetTable($sSourceClass)) != 0)
-		if (self::HasFinalClassField(self::$m_aRootClasses[$sSourceClass]))
+		// Build root class information
+		if (array_key_exists($sSourceClass, self::$m_aRootClasses))
 		{
-			// Inherit the root class
+			// Inherit...
 			self::$m_aRootClasses[$sTargetClass] = self::$m_aRootClasses[$sSourceClass];
 		}
 		else
 		{
-			// I am a root class, standalone as well !
-			// ????
-			//self::$m_aRootClasses[$sTargetClass] = $sTargetClass;
+			// This class will be the root class
+			self::$m_aRootClasses[$sSourceClass] = $sSourceClass;
+			self::$m_aRootClasses[$sTargetClass] = $sSourceClass;
 		}
 		self::$m_aParentClasses[$sTargetClass] += self::$m_aParentClasses[$sSourceClass];
 		self::$m_aParentClasses[$sTargetClass][] = $sSourceClass;
@@ -1212,6 +1224,10 @@ abstract class MetaModel
 		self::_check_subclass($sClass);
 		return (self::GetRootClass($sClass) == $sClass);
 	}
+	public static function EnumRootClasses()
+	{
+		return array_unique(self::$m_aRootClasses);
+	}
 	public static function EnumParentClasses($sClass)
 	{
 		self::_check_subclass($sClass);	
@@ -2540,41 +2556,41 @@ abstract class MetaModel
 			$sTable = self::DBGetTable($sClass);
 			$sKeyField = self::DBGetKey($sClass);
 
-			// Check that the final class field contains the name of a class which inherited from the current class
-			//
-			if (self::HasFinalClassField($sClass))
-			{
-				$sFinalClassField = self::DBGetClassField($sClass);
-
-				$aAllowedValues = self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL);
-				$sAllowedValues = implode(",", CMDBSource::Quote($aAllowedValues, true));
-
-				$sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS id FROM `$sTable` AS maintable WHERE `$sFinalClassField` NOT IN ($sAllowedValues)";
-				self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "final class (field `<em>$sFinalClassField</em>`) is wrong (expected a value in {".$sAllowedValues."})", $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel);
-			}
-
-			// Compound objects model - node/leaf classes (not the root itself)
-			//
-			if (!self::IsStandaloneClass($sClass) && !self::HasFinalClassField($sClass))
+			if (!self::IsStandaloneClass($sClass))
 			{
-				$sRootTable = self::DBGetTable($sRootClass);
-				$sRootKey = self::DBGetKey($sRootClass);
-				$sFinalClassField = self::DBGetClassField($sRootClass);
-
-				$aExpectedClasses = self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL);
-				$sExpectedClasses = implode(",", CMDBSource::Quote($aExpectedClasses, true));
-
-				// Check that any record found here has its counterpart in the root table
-				// and which refers to a child class
-				//
-				$sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS id FROM `$sTable` as maintable LEFT JOIN `$sRootTable` ON maintable.`$sKeyField` = `$sRootTable`.`$sRootKey` AND `$sRootTable`.`$sFinalClassField` IN ($sExpectedClasses) WHERE `$sRootTable`.`$sRootKey` IS NULL";
-				self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "Found a record in `<em>$sTable</em>`, but no counterpart in root table `<em>$sRootTable</em>` (inc. records pointing to a class in {".$sExpectedClasses."})", $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel);
-
-				// Check that any record found in the root table and referring to a child class
-				// has its counterpart here (detect orphan nodes -root or in the middle of the hierarchy)
-				//
-				$sSelWrongRecs = "SELECT DISTINCT maintable.`$sRootKey` AS id FROM `$sRootTable` AS maintable LEFT JOIN `$sTable` ON maintable.`$sRootKey` = `$sTable`.`$sKeyField` WHERE `$sTable`.`$sKeyField` IS NULL AND maintable.`$sFinalClassField` IN ($sExpectedClasses)";
-				self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "Found a record in root table `<em>$sRootTable</em>`, but no counterpart in table `<em>$sTable</em>` (root records pointing to a class in {".$sExpectedClasses."})", $sRootClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel);
+				if (self::IsRootClass($sClass))
+				{
+					// Check that the final class field contains the name of a class which inherited from the current class
+					//
+					$sFinalClassField = self::DBGetClassField($sClass);
+	
+					$aAllowedValues = self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL);
+					$sAllowedValues = implode(",", CMDBSource::Quote($aAllowedValues, true));
+	
+					$sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS id FROM `$sTable` AS maintable WHERE `$sFinalClassField` NOT IN ($sAllowedValues)";
+					self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "final class (field `<em>$sFinalClassField</em>`) is wrong (expected a value in {".$sAllowedValues."})", $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel);
+				}
+				else
+				{
+					$sRootTable = self::DBGetTable($sRootClass);
+					$sRootKey = self::DBGetKey($sRootClass);
+					$sFinalClassField = self::DBGetClassField($sRootClass);
+	
+					$aExpectedClasses = self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL);
+					$sExpectedClasses = implode(",", CMDBSource::Quote($aExpectedClasses, true));
+	
+					// Check that any record found here has its counterpart in the root table
+					// and which refers to a child class
+					//
+					$sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS id FROM `$sTable` as maintable LEFT JOIN `$sRootTable` ON maintable.`$sKeyField` = `$sRootTable`.`$sRootKey` AND `$sRootTable`.`$sFinalClassField` IN ($sExpectedClasses) WHERE `$sRootTable`.`$sRootKey` IS NULL";
+					self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "Found a record in `<em>$sTable</em>`, but no counterpart in root table `<em>$sRootTable</em>` (inc. records pointing to a class in {".$sExpectedClasses."})", $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel);
+	
+					// Check that any record found in the root table and referring to a child class
+					// has its counterpart here (detect orphan nodes -root or in the middle of the hierarchy)
+					//
+					$sSelWrongRecs = "SELECT DISTINCT maintable.`$sRootKey` AS id FROM `$sRootTable` AS maintable LEFT JOIN `$sTable` ON maintable.`$sRootKey` = `$sTable`.`$sKeyField` WHERE `$sTable`.`$sKeyField` IS NULL AND maintable.`$sFinalClassField` IN ($sExpectedClasses)";
+					self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "Found a record in root table `<em>$sRootTable</em>`, but no counterpart in table `<em>$sTable</em>` (root records pointing to a class in {".$sExpectedClasses."})", $sRootClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel);
+				}
 			}
 
 			foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)

+ 4 - 4
dictionaries/dictionary.itop.core.php

@@ -35,7 +35,7 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Class:CMDBChangeOp/Attribute:objclass+' => 'object class',
 	'Class:CMDBChangeOp/Attribute:objkey' => 'object id',
 	'Class:CMDBChangeOp/Attribute:objkey+' => 'object id',
-	'Class:CMDBChangeOp/Attribute:finalclass' => 'finalclass',
+	'Class:CMDBChangeOp/Attribute:finalclass' => 'type',
 	'Class:CMDBChangeOp/Attribute:finalclass+' => '',
 ));
 
@@ -116,7 +116,7 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Class:Event/Attribute:date+' => 'date and time at which the changes have been recorded',
 	'Class:Event/Attribute:userinfo' => 'user info',
 	'Class:Event/Attribute:userinfo+' => 'identification of the user that was doing the action that triggered this event',
-	'Class:Event/Attribute:finalclass' => 'finalclass',
+	'Class:Event/Attribute:finalclass' => 'type',
 	'Class:Event/Attribute:finalclass+' => '',
 ));
 
@@ -221,7 +221,7 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Class:Action/Attribute:status/Value:disabled+' => 'Inactive',
 	'Class:Action/Attribute:related_triggers' => 'Related Triggers',
 	'Class:Action/Attribute:related_triggers+' => 'Triggers linked to this action',
-	'Class:Action/Attribute:finalclass' => 'finalclass',
+	'Class:Action/Attribute:finalclass' => 'Type',
 	'Class:Action/Attribute:finalclass+' => '',
 ));
 
@@ -278,7 +278,7 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Class:Trigger/Attribute:description+' => 'one line description',
 	'Class:Trigger/Attribute:linked_actions' => 'Triggered actions',
 	'Class:Trigger/Attribute:linked_actions+' => 'Actions performed when the trigger is activated',
-	'Class:Trigger/Attribute:finalclass' => 'finalclass',
+	'Class:Trigger/Attribute:finalclass' => 'Type',
 	'Class:Trigger/Attribute:finalclass+' => '',
 ));
 

+ 1 - 1
dictionaries/dictionary.itop.model.php

@@ -67,7 +67,7 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Class:logRealObject/Attribute:org_id+' => 'ID of the object owner organization',
 	'Class:logRealObject/Attribute:org_name' => 'Organization',
 	'Class:logRealObject/Attribute:org_name+' => 'Company / Department owning this object',
-	'Class:logRealObject/Attribute:finalclass' => 'finalclass',
+	'Class:logRealObject/Attribute:finalclass' => 'Type',
 	'Class:logRealObject/Attribute:finalclass+' => '',
 ));
 

+ 1 - 1
pages/ajax.csvimport.php

@@ -29,7 +29,7 @@ function GetMappingForField($sClassName, $sFieldName, $iFieldIndex)
 				}
 			}
 		}
-		else if ($oAttDef->IsWritable() && ($sAttCode != 'finalclass')) // finalclass should not be considered as 'writable' isn't it ?
+		else if ($oAttDef->IsWritable())
 		{
 			$aChoices[$sAttCode] = $oAttDef->GetLabel();
 		}

+ 1 - 1
setup/xmldataloader.class.inc.php

@@ -156,7 +156,7 @@ class XMLDataLoader
 			$oTargetObj = MetaModel::NewObject($sClass);
 			foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
 			{
-				if (($oAttDef->IsWritable()) && ($oAttDef->IsScalar()) && ($sAttCode != 'finalclass') )
+				if (($oAttDef->IsWritable()) && ($oAttDef->IsScalar()))
 				{
 					if ($oAttDef->IsExternalKey())
 					{