ソースを参照

#149 Implemented friendly names

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@1057 a333f486-631f-4898-b8df-5754b55c2be0
romainq 14 年 前
コミット
8fee154865

+ 2 - 28
application/cmdbabstract.class.inc.php

@@ -64,39 +64,14 @@ abstract class cmdbAbstractObject extends CMDBObject
 		return $sPage;
 	}
 
-	protected static function MakeHyperLink($sObjClass, $sObjKey, $aAvailableFields)
+	protected static function MakeHyperLink($sObjClass, $sObjKey, $sLabel = '')
 	{
 		if ($sObjKey <= 0) return '<em>'.Dict::S('UI:UndefinedObject').'</em>'; // Objects built in memory have negative IDs
 
 		$oAppContext = new ApplicationContext();	
-		$sExtClassNameAtt = MetaModel::GetNameAttributeCode($sObjClass);
 		$sPage = self::ComputeUIPage($sObjClass);
 		$sAbsoluteUrl = utils::GetAbsoluteUrlPath();
 
-		// Use the "name" of the target class as the label of the hyperlink
-		// unless it's not available in the external attributes...
-		if (isset($aAvailableFields[$sExtClassNameAtt]))
-		{
-			$sLabel = $aAvailableFields[$sExtClassNameAtt];
-		}
-		else
-		{
-			$sLabel = implode(' / ', $aAvailableFields);
-		}
-		// Safety belt
-		//
-		if (empty($sLabel))
-		{
-			// Developer's note:
-			// This is doing the job for you, but that is just there in case
-			// the external fields associated to the external key are blanks
-			// The ultimate solution will be to query the name automatically
-			// and independantly from the data model (automatic external field)
-			// AND make the name be a mandatory field
-			//
-			$sObject = MetaModel::GetObject($sObjClass, $sObjKey);
-			$sLabel = $sObject->GetName();
-		}
 		// Safety net
 		//
 		if (empty($sLabel))
@@ -365,10 +340,9 @@ abstract class cmdbAbstractObject extends CMDBObject
 		if (!empty($sTemplate))
 		{
 			$oTemplate = new DisplayTemplate($sTemplate);
-			$sNameAttCode = MetaModel::GetNameAttributeCode(get_class($this));
 			// Note: to preserve backward compatibility with home-made templates, the placeholder '$pkey$' has been preserved
 			//       but the preferred method is to use '$id$'
-			$oTemplate->Render($oPage, array('class_name'=> MetaModel::GetName(get_class($this)),'class'=> get_class($this), 'pkey'=> $this->GetKey(), 'id'=> $this->GetKey(), 'name' => $this->Get($sNameAttCode)));
+			$oTemplate->Render($oPage, array('class_name'=> MetaModel::GetName(get_class($this)),'class'=> get_class($this), 'pkey'=> $this->GetKey(), 'id'=> $this->GetKey(), 'name' => $this->Get('friendlyname')));
 		}
 		else
 		{

+ 2 - 89
application/ui.linkswidget.class.inc.php

@@ -66,7 +66,7 @@ class UILinksWidget
 			{
 				// State attribute is always hidden from the UI
 			}
-			else if (!$oAttDef->IsExternalField() && ($sAttCode != $sExtKeyToMe) && ($sAttCode != $this->m_sExtKeyToRemote) && ($sAttCode != 'finalclass'))
+			else if ($oAttDef->IsWritable() && ($sAttCode != $sExtKeyToMe) && ($sAttCode != $this->m_sExtKeyToRemote) && ($sAttCode != 'finalclass'))
 			{
 				$iFlags = MetaModel::GetAttributeFlags($this->m_sLinkedClass, $sDefaultState, $sAttCode);				
 				if ( !($iFlags & OPT_ATT_HIDDEN) && !($iFlags & OPT_ATT_READONLY) )
@@ -238,94 +238,7 @@ EOF
 		$oPage->add_at_the_end($this->GetObjectPickerDialog($oPage)); // To prevent adding forms inside the main form
 		return $sHtmlValue;
 	}
-	
-	/**
-	 * This static function is called by the Ajax Page when there is a need to fill an autocomplete combo
-	 * @param $oPage WebPage The ajax page used for the output (sent back to the browser)
-	 * @param $sClass string The name of the class of the current object being edited
-	 * @param $sAttCode string The name of the attribute being edited
-	 * @param $sName string The partial name that was typed by the user
-	 * @param $iMaxCount integer The maximum number of items to return
-	 * @return void
-	 */	 	 	  	 	 	 	
-	static public function Autocomplete(WebPage $oPage, $sClass, $sAttCode, $sName, $iMaxCount)
-	{
-		// #@# todo - add context information, otherwise any value will be authorized for external keys
-		$aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array() /* $aArgs */, $sName);
-		if ($aAllowedValues != null)
-		{
-			$iCount = $iMaxCount;
-			foreach($aAllowedValues as $key => $value)
-			{
-				$oPage->add($value."|".$key."\n");
-				$iCount--;
-				if ($iCount == 0) break;
-			}
-		}
-		else // No limitation to the allowed values
-		{
-			// Search for all the object of the linked class
-			$oAttDef = 	$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
-			$sLinkedClass = $oAttDef->GetLinkedClass();
-			$sSearchClass = self::GetTargetClass($sClass, $sAttCode);
-			$oFilter = new DBObjectSearch($sSearchClass);
-			$sSearchAttCode = MetaModel::GetNameAttributeCode($sSearchClass);
-			$oFilter->AddCondition($sSearchAttCode, $sName, 'Begins with');
-			$oSet = new CMDBObjectSet($oFilter, array($sSearchAttCode => true));
-			$iCount = 0;
-			while( ($iCount < $iMaxCount) && ($oObj = $oSet->fetch()) )
-			{
-				$oPage->add($oObj->GetName()."|".$oObj->GetKey()."\n");
-				$iCount++;
-			}
-		}
-	}
-
-	/**
-	 * This static function is called by the Ajax Page display a set of objects being linked
-	 * to the object being created	 
-	 * @param $oPage WebPage The ajax page used for the put^put (sent back to the browser
-	 * @param $sClass string The name of the 'linking class' which is the class of the objects to display
-	 * @param $sSet JSON serialized set of objects
-	 * @param $sExtKeyToMe Name of the attribute in sClass that is pointing to a given object
-	 * @param $iObjectId The id of the object $sExtKeyToMe is pointing to
-	 * @return void
-	 */	 	 	  	 	 	 	
-	static public function RenderSet($oPage, $sClass, $sJSONSet, $sExtKeyToMe, $sExtKeyToRemote, $iObjectId)
-	{
-		$aSet = json_decode($sJSONSet, true); // true means hash array instead of object
-		$oSet = CMDBObjectSet::FromScratch($sClass);
-		foreach($aSet as $aObject)
-		{
-			$oObj = MetaModel::NewObject($sClass);
-			foreach($aObject as $sAttCode => $value)
-			{
-				$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);
-				if ($oAttDef->IsExternalKey() && ($value != 0))
-				{
-					$oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value); // @@ optimization, don't do & query per object in the set !
-					$oObj->Set($sAttCode, $oTargetObj);
-				}
-				else
-				{
-					$oObj->Set($sAttCode, $value);
-				}
-
-			}
-			$oSet->AddObject($oObj);
-		}
-		$aExtraParams = array();
-		$aExtraParams['link_attr'] = $sExtKeyToMe;
-		$aExtraParams['object_id'] = $iObjectId;
-		$aExtraParams['target_attr'] = $sExtKeyToRemote;
-		$aExtraParams['menu'] = false;
-		$aExtraParams['select'] = false;
-		$aExtraParams['view_link'] = false;
-		
-		cmdbAbstractObject::DisplaySet($oPage, $oSet, $aExtraParams);
-	}
-
-	
+	         
 	protected static function GetTargetClass($sClass, $sAttCode)
 	{
 		$oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode);

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

@@ -417,7 +417,7 @@ class AttributeDBFieldVoid extends AttributeDefinition
 	public function GetSQLExpressions()
 	{
 		$aColumns = array();
-		// Note: to optimize things, the existence of the attribute is determine by the existence of one column with an empty suffix
+		// Note: to optimize things, the existence of the attribute is determined by the existence of one column with an empty suffix
 		$aColumns[''] = $this->Get("sql");
 		return $aColumns;
 	}
@@ -934,6 +934,7 @@ class AttributeFinalClass extends AttributeString
 	}
 }
 
+
 /**
  * Map a varchar column (size < ?) to an attribute that must never be shown to the user 
  *
@@ -1753,7 +1754,6 @@ class AttributeExternalKey extends AttributeDBFieldVoid
 	{
 		return $this->GetOptional('allow_target_creation', MetaModel::GetConfig()->Get('allow_target_creation'));
 	}
-	
 }
 
 /**
@@ -1776,6 +1776,11 @@ class AttributeExternalField extends AttributeDefinition
 		return $oExtAttDef->GetSQLCol(); 
 	}
 
+	public function GetSQLExpressions()
+	{
+		return array('' => $this->GetCode()); 
+	}
+
 	public function GetLabel()
 	{
 		$oRemoteAtt = $this->GetExtAttDef();
@@ -2377,4 +2382,137 @@ class AttributePropertySet extends AttributeTable
 	}
 }
 
+/**
+ * The attribute dedicated to the friendly name automatic attribute (not written) 
+ *
+ * @package     iTopORM
+ */
+class AttributeComputedFieldVoid extends AttributeDefinition
+{	
+	static protected function ListExpectedParams()
+	{
+		return array_merge(parent::ListExpectedParams(), array());
+	}
+
+	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;} 
+	public function IsWritable() {return false;} 
+	public function GetSQLExpr()    {return null;}
+	public function GetDefaultValue() {return $this->MakeRealValue("");}
+	public function IsNullAllowed() {return false;}
+
+	// 
+//	protected function ScalarToSQL($value) {return $value;} // format value as a valuable SQL literal (quoted outside)
+
+	public function GetSQLExpressions()
+	{
+		return array('' => $this->GetCode()); 
+	}
+
+	public function FromSQLToValue($aCols, $sPrefix = '')
+	{
+		return null;
+	}
+	public function GetSQLValues($value)
+	{
+		return array();
+	}
+
+	public function GetSQLColumns()
+	{
+		return array();
+	}
+
+	public function GetFilterDefinitions()
+	{
+		return array($this->GetCode() => new FilterFromAttribute($this));
+	}
+
+	public function GetBasicFilterOperators()
+	{
+		return array();
+	}
+	public function GetBasicFilterLooseOperator()
+	{
+		return "=";
+	}
+
+	public function GetBasicFilterSQLExpr($sOpCode, $value)
+	{
+		$sQValue = CMDBSource::Quote($value);
+		switch ($sOpCode)
+		{
+		case '!=':
+			return $this->GetSQLExpr()." != $sQValue";
+			break;
+		case '=':
+		default:
+			return $this->GetSQLExpr()." = $sQValue";
+		}
+	} 
+}
+
+/**
+ * The attribute dedicated to the friendly name automatic attribute (not written) 
+ *
+ * @package     iTopORM
+ */
+class AttributeFriendlyName extends AttributeComputedFieldVoid
+{
+	public function __construct($sCode, $sExtKeyAttCode)
+	{
+		$this->m_sCode = $sCode;
+		$aParams = array();
+//		$aParams["is_null_allowed"] = false,
+		$aParams["default_value"] = '';
+		$aParams["extkey_attcode"] = $sExtKeyAttCode;
+		parent::__construct($sCode, $aParams);
+
+		$this->m_sValue = $this->Get("default_value");
+	}
+
+	public function GetKeyAttCode() {return $this->Get("extkey_attcode");} 
+
+	// n/a, the friendly name is made of a complex expression (see GetNameSpec)
+	protected function GetSQLCol() {return "";}	
+
+	public function FromSQLToValue($aCols, $sPrefix = '')
+	{
+ 		$sValue = $aCols[$sPrefix];
+		return $sValue;
+	}
+
+	/**
+	 * Encrypt the value before storing it in the database
+	 */
+	public function GetSQLValues($value)
+	{
+		return array();
+	}
+
+	public function IsWritable()
+	{
+		return false;
+	}
+
+	public function SetFixedValue($sValue)
+	{
+		$this->m_sValue = $sValue;
+	}
+	public function GetDefaultValue()
+	{
+		return $this->m_sValue;
+	}
+
+	public function GetAsHTML($sValue)
+	{
+		return Str::pure2html((string)$sValue);
+	}
+}
+
 ?>

+ 25 - 42
core/dbobject.class.php

@@ -230,6 +230,7 @@ abstract class DBObject
 			// Take care: the function isset will return false in case the value is null,
 			// which is something that could happen on open joins
 			$sAttRef = $sClassAlias.$sAttCode;
+
 			if (array_key_exists($sAttRef, $aRow))
 			{
 				$value = $oAttDef->FromSQLToValue($aRow, $sAttRef);
@@ -390,22 +391,13 @@ abstract class DBObject
 		$sClass = get_class($this);
 		$oAtt = MetaModel::GetAttributeDef($sClass, $sAttCode);
 
-		$aExtKeyFriends = MetaModel::GetExtKeyFriends($sClass, $sAttCode);
-		if (count($aExtKeyFriends) > 0)
+		if ($oAtt->IsExternalKey(EXTKEY_ABSOLUTE))
 		{
-			// This attribute is an ext key (in this class or in another class)
-			// The corresponding value is an id of the remote object
-			// Let's try to use the corresponding external fields for a sexy display
-
-			$aAvailableFields = array();
-			foreach ($aExtKeyFriends as $sDispAttCode => $oExtField)
-			{
-//				$aAvailableFields[$oExtField->GetExtAttCode()] = $oExtField->GetAsHTML($this->Get($oExtField->GetCode()));
-				$aAvailableFields[$oExtField->GetExtAttCode()] = $this->Get($oExtField->GetCode());
-			}
-
+			//return $this->Get($sAttCode.'_friendlyname');
 			$sTargetClass = $oAtt->GetTargetClass(EXTKEY_ABSOLUTE);
-			return $this->MakeHyperLink($sTargetClass, $this->Get($sAttCode), $aAvailableFields);
+			$iTargetKey = $this->Get($sAttCode);
+			$sLabel = $this->Get($sAttCode.'_friendlyname');
+			return $this->MakeHyperLink($sTargetClass, $iTargetKey, $sLabel);
 		}
 
 		// That's a standard attribute (might be an ext field or a direct field, etc.)
@@ -437,23 +429,7 @@ abstract class DBObject
 			}
 			else
 			{
-				$aAvailableFields = array();
-				// retrieve the "external fields" linked to this external key
-				foreach (MetaModel::GetExternalFields(get_class($this), $sAttCode) as $oExtField)
-				{
-					$aAvailableFields[$oExtField->GetExtAttCode()] = $oExtField->GetAsHTML($this->Get($oExtField->GetCode()));
-				}
-				// Use the "name" of the target class as the label of the hyperlink
-				// unless it's not available in the external fields...
-				$sExtClassNameAtt = MetaModel::GetNameAttributeCode($sTargetClass);
-				if (isset($aAvailableFields[$sExtClassNameAtt]))
-				{
-					$sEditValue = $aAvailableFields[$sExtClassNameAtt];
-				}
-				else
-				{
-					$sEditValue = implode(' / ', $aAvailableFields);
-				}
+				$sEditValue = $this->Get($sAttCode.'_friendlyname');
 			}
 		}
 		else
@@ -475,7 +451,7 @@ abstract class DBObject
 		return $oAtt->GetAsCSV($this->Get($sAttCode), $sSeparator, $sTextQualifier);
 	}
 
-	protected static function MakeHyperLink($sObjClass, $sObjKey, $aAvailableFields)
+	protected static function MakeHyperLink($sObjClass, $sObjKey, $sLabel = '')
 	{
 		if ($sObjKey == 0) return '<em>undefined</em>';
 
@@ -484,8 +460,7 @@ abstract class DBObject
 
 	public function GetHyperlink()
 	{
-		$aAvailableFields[MetaModel::GetNameAttributeCode(get_class($this))] = $this->GetName();
-		return $this->MakeHyperLink(get_class($this), $this->GetKey(), $aAvailableFields);
+		return $this->MakeHyperLink(get_class($this), $this->GetKey(), $this->GetName());
 	}
 
 
@@ -524,15 +499,23 @@ abstract class DBObject
 
 	public function GetName()
 	{
-		$sNameAttCode = MetaModel::GetNameAttributeCode(get_class($this));
-		if (empty($sNameAttCode))
-		{
-			return $this->m_iKey;
-		}
-		else
-		{
-			return $this->Get($sNameAttCode);
+		$aNameSpec = MetaModel::GetNameSpec(get_class($this));
+		$sFormat = $aNameSpec[0];
+		$aAttributes = $aNameSpec[1];                                                     
+
+		$aValues = array();
+      foreach ($aAttributes as $sAttCode)
+      {
+      	if (empty($sAttCode))
+      	{
+      		$aValues[] = $this->m_iKey;
+			}
+			else
+			{
+				$aValues[] = $this->Get($sAttCode);
+			}
 		}
+		return vsprintf($sFormat, $aValues);
 	}
 
 	public function GetState()

+ 8 - 0
core/dbobjectsearch.class.php

@@ -226,6 +226,14 @@ class DBObjectSearch
 		$this->m_oSearchCondition = $this->m_oSearchCondition->LogAnd($oExpression); 
 	}
 
+  	public function AddNameCondition($sName)
+	{
+		$oValueExpr = new ScalarExpression($sName);
+		$oNameExpr = new FieldExpression('friendlyname', $this->GetClassAlias());
+		$oNewCondition = new BinaryExpression($oNameExpr, '=', $oValueExpr);
+		$this->AddConditionExpression($oNewCondition);
+	}
+
 	public function AddCondition($sFilterCode, $value, $sOpCode = null)
 	{
 		MyHelpers::CheckKeyInArray('filter code', $sFilterCode, MetaModel::GetClassFilterDefs($this->GetClass()));

+ 180 - 6
core/expression.class.inc.php

@@ -30,6 +30,7 @@ class MissingQueryArgument extends CoreException
 abstract class Expression
 {
 	// recursive translation of identifiers
+	abstract public function GetUnresolvedFields($sAlias, &$aUnresolved);
 	abstract public function Translate($aTranslationData, $bMatchAll = true);
 
 	// recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True
@@ -66,6 +67,12 @@ abstract class Expression
 		return $oExpression;
 	}
 
+	static public function FromSQL($sSQL)
+	{
+		$oSql = new SQLExpression($sSQL);
+		return $oSql;
+	}
+
 	public function LogAnd($oExpr)
 	{
 		if ($this->IsTrue()) return clone $oExpr;
@@ -79,6 +86,42 @@ abstract class Expression
 	}
 }
 
+class SQLExpression extends Expression
+{
+	protected $m_sSQL;
+
+	public function __construct($sSQL)
+	{
+		$this->m_sSQL  = $sSQL;
+	}
+
+	public function IsTrue()
+	{
+		return false;
+	}
+
+	// recursive rendering
+	public function Render(&$aArgs = null, $bRetrofitParams = false)
+	{
+		return $this->m_sSQL;
+	}
+
+	public function GetUnresolvedFields($sAlias, &$aUnresolved)
+	{
+	}
+
+	public function Translate($aTranslationData, $bMatchAll = true)
+	{
+		return clone $this;
+	}
+
+	public function ListRequiredFields()
+	{
+		return array();
+	}
+}
+
+
 
 class BinaryExpression extends Expression
 {
@@ -143,6 +186,12 @@ class BinaryExpression extends Expression
 		return "($sLeft $sOperator $sRight)";
 	}
 
+	public function GetUnresolvedFields($sAlias, &$aUnresolved)
+	{
+		$this->GetLeftExpr()->GetUnresolvedFields($sAlias, $aUnresolved);
+		$this->GetRightExpr()->GetUnresolvedFields($sAlias, $aUnresolved);
+	}
+
 	public function Translate($aTranslationData, $bMatchAll = true)
 	{
 		$oLeft = $this->GetLeftExpr()->Translate($aTranslationData, $bMatchAll);
@@ -194,6 +243,10 @@ class UnaryExpression extends Expression
 		}
 	}
 
+	public function GetUnresolvedFields($sAlias, &$aUnresolved)
+	{
+	}
+
 	public function Translate($aTranslationData, $bMatchAll = true)
 	{
 		return clone $this;
@@ -275,6 +328,20 @@ class FieldExpression extends UnaryExpression
 		return "`{$this->m_sParent}`.`{$this->m_sName}`";
 	}
 
+	public function ListRequiredFields()
+	{
+		return array($this->m_sParent.'.'.$this->m_sName);
+	}
+
+	public function GetUnresolvedFields($sAlias, &$aUnresolved)
+	{
+		if ($this->m_sParent == $sAlias)
+		{
+			// Add a reference to the field
+			$aUnresolved[$this->m_sName] = $this;
+		}
+	}
+
 	public function Translate($aTranslationData, $bMatchAll = true)
 	{
 		if (!array_key_exists($this->m_sParent, $aTranslationData))
@@ -292,21 +359,28 @@ class FieldExpression extends UnaryExpression
 			}
 			$sNewParent = $aTranslationData[$this->m_sParent]['*'];
 			$sNewName = $this->m_sName;
+			$oRet = new FieldExpressionResolved($sNewName, $sNewParent);
 		}
 		else
 		{
-			$sNewParent = $aTranslationData[$this->m_sParent][$this->m_sName][0];
-			$sNewName = $aTranslationData[$this->m_sParent][$this->m_sName][1];
+			$oRet = $aTranslationData[$this->m_sParent][$this->m_sName];
 		}
-		return new FieldExpression($sNewName, $sNewParent);
+		return $oRet;
 	}
+}
 
-	public function ListRequiredFields()
+// Has been resolved into an SQL expression
+class FieldExpressionResolved extends FieldExpression
+{
+	public function GetUnresolvedFields($sAlias, &$aUnresolved)
 	{
-		return array($this->m_sParent.'.'.$this->m_sName);
 	}
-}
 
+	public function Translate($aTranslationData, $bMatchAll = true)
+	{
+		return clone $this;
+	}
+}
 
 class VariableExpression extends UnaryExpression
 {
@@ -393,6 +467,14 @@ class ListExpression extends Expression
 		return '('.implode(', ', $aRes).')';
 	}
 
+	public function GetUnresolvedFields($sAlias, &$aUnresolved)
+	{
+		foreach ($this->m_aExpressions as $oExpr)
+		{
+			$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
+		}
+	}
+
 	public function Translate($aTranslationData, $bMatchAll = true)
 	{
 		$aRes = array();
@@ -453,6 +535,14 @@ class FunctionExpression extends Expression
 		return $this->m_sVerb.'('.implode(', ', $aRes).')';
 	}
 
+	public function GetUnresolvedFields($sAlias, &$aUnresolved)
+	{
+		foreach ($this->m_aArgs as $oExpr)
+		{
+			$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
+		}
+	}
+
 	public function Translate($aTranslationData, $bMatchAll = true)
 	{
 		$aRes = array();
@@ -507,6 +597,11 @@ class IntervalExpression extends Expression
 		return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit;
 	}
 
+	public function GetUnresolvedFields($sAlias, &$aUnresolved)
+	{
+		$this->m_oValue->GetUnresolvedFields($sAlias, $aUnresolved);
+	}
+
 	public function Translate($aTranslationData, $bMatchAll = true)
 	{
 		return new IntervalExpression($this->m_oValue->Translate($aTranslationData, $bMatchAll), $this->m_sUnit);
@@ -551,6 +646,14 @@ class CharConcatExpression extends Expression
 		return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)";
 	}
 
+	public function GetUnresolvedFields($sAlias, &$aUnresolved)
+	{
+		foreach ($this->m_aExpressions as $oExpr)
+		{
+			$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
+		}
+	}
+
 	public function Translate($aTranslationData, $bMatchAll = true)
 	{
 		$aRes = array();
@@ -572,4 +675,75 @@ class CharConcatExpression extends Expression
 	}
 }
 
+class QueryBuilderExpressions
+{
+	protected $m_oConditionExpr;
+	protected $m_aSelectExpr;
+	protected $m_aJoinFields;
+
+	public function __construct($aSelect, $oCondition)
+	{
+		$this->m_oConditionExpr = $oCondition;
+		$this->m_aSelectExpr = $aSelect;
+		$this->m_aJoinFields = array();
+	}
+
+	public function GetSelect()
+	{
+		return $this->m_aSelectExpr;
+	}
+
+	public function GetCondition()
+	{
+		return $this->m_oConditionExpr;
+	}
+
+	public function PopJoinField()
+	{
+		return array_pop($this->m_aJoinFields);
+	}
+
+	public function AddSelect($sAttAlias, $oExpression)
+	{
+		$this->m_aSelectExpr[$sAttAlias] = $oExpression;
+	}
+
+			//$oConditionTree = $oConditionTree->LogAnd($oFinalClassRestriction);
+	public function AddCondition($oExpression)
+	{
+		$this->m_oConditionExpr = $this->m_oConditionExpr->LogAnd($oExpression);
+	}
+
+	public function PushJoinField($oExpression)
+	{
+		array_push($this->m_aJoinFields, $oExpression);
+	}
+
+	public function GetUnresolvedFields($sAlias, &$aUnresolved)
+	{
+		$this->m_oConditionExpr->GetUnresolvedFields($sAlias, $aUnresolved);
+		foreach($this->m_aSelectExpr as $sColAlias => $oExpr)
+		{
+			$oExpr->GetUnresolvedFields($sAlias, $aUnresolved);
+		}
+		foreach($this->m_aJoinFields as $oExpression)
+		{
+			$oExpression->GetUnresolvedFields($sAlias, $aUnresolved);
+		}
+	}
+
+	public function Translate($aTranslationData, $bMatchAll = true)
+	{
+		$this->m_oConditionExpr = $this->m_oConditionExpr->Translate($aTranslationData, $bMatchAll);
+		foreach($this->m_aSelectExpr as $sColAlias => $oExpr)
+		{
+			$this->m_aSelectExpr[$sColAlias] = $oExpr->Translate($aTranslationData, $bMatchAll);
+		}
+		foreach($this->m_aJoinFields as $index => $oExpression)
+		{
+			$this->m_aJoinFields[$index] = $oExpression->Translate($aTranslationData, $bMatchAll);
+		}
+	}
+}
+
 ?>

+ 261 - 113
core/metamodel.class.php

@@ -345,11 +345,84 @@ abstract class MetaModel
 		self::_check_subclass($sClass);	
 		return (self::$m_aClassParams[$sClass]["key_type"] == "autoincrement");
 	}
-	final static public function GetNameAttributeCode($sClass)
+	final static public function GetNameSpec($sClass)
 	{
-		self::_check_subclass($sClass);	
-		return self::$m_aClassParams[$sClass]["name_attcode"];
+		self::_check_subclass($sClass);
+		$nameRawSpec = self::$m_aClassParams[$sClass]["name_attcode"];
+		if (is_array($nameRawSpec))
+		{
+			$sFormat = Dict::S("Class:$sClass/Name", '');
+			if (strlen($sFormat) == 0)
+			{
+				// Default to "%1$s %2$s..."
+				for($i = 1 ; $i <= count($nameRawSpec) ; $i++)
+				{
+					if (empty($sFormat))
+					{					
+						$sFormat .= '%'.$i.'$s';
+					}
+					else
+					{
+						$sFormat .= ' %'.$i.'$s';
+					}
+				}
+			}
+			return array($sFormat, $nameRawSpec);
+		}
+		elseif (empty($nameRawSpec))
+		{
+			//return array($sClass.' %s', array('id'));
+			return array($sClass, array());
+		}
+		else
+		{
+			// string -> attcode
+			return array('%1$s', array($nameRawSpec));
+		}
 	}
+	final static public function GetNameExpression($sClass, $sClassAlias)
+	{
+		$aNameSpec = self::GetNameSpec($sClass);
+		$sFormat = $aNameSpec[0];
+		$aAttributes = $aNameSpec[1];                                                     
+
+		$aPieces = preg_split('/%([0-9])\\$s/', $sFormat, -1, PREG_SPLIT_DELIM_CAPTURE);
+		//echo "<pre>\n";
+		//print_r($aPieces);
+		//echo "</pre>\n";
+		$aExpressions = array();
+		foreach($aPieces as $i => $sPiece)
+		{
+			if ($i & 1)
+			{
+				// $i is ODD - sPiece is a delimiter
+				//
+				$iReplacement = (int)$sPiece - 1;
+
+		      if (isset($aAttributes[$iReplacement]))
+		      {
+		      	$sAtt = $aAttributes[$iReplacement];
+					$aExpressions[] = new FieldExpression($sAtt, $sClassAlias);
+				}
+			}
+			else
+			{
+				// $i is EVEN - sPiece is a literal
+				//
+				if (strlen($sPiece) > 0)
+				{
+	    			$aExpressions[] = new ScalarExpression($sPiece);
+				}
+			}
+		}
+		//echo "<pre>\n";
+		//print_r($aExpressions);
+		//echo "</pre>\n";
+
+		$oNameExpr = new CharConcatExpression($aExpressions);
+		return $oNameExpr;
+	}
+
 	final static public function GetStateAttributeCode($sClass)
 	{
 		self::_check_subclass($sClass);	
@@ -1023,6 +1096,16 @@ abstract class MetaModel
 		//
 		foreach (self::GetClasses() as $sClass)
 		{
+			// Create the friendly name attribute
+			$sFriendlyNameAttCode = 'friendlyname'; 
+			$oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode, 'id');
+			$oFriendlyName->SetHostClass($sClass);
+			self::$m_aAttribDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyName;
+			self::$m_aAttribOrigins[$sClass][$sFriendlyNameAttCode] = $sClass;
+			$oFriendlyNameFlt = new FilterFromAttribute($oFriendlyName);
+			self::$m_aFilterDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyNameFlt;
+			self::$m_aFilterOrigins[$sClass][$sFriendlyNameAttCode] = $sClass;
+
 			self::$m_aExtKeyFriends[$sClass] = array();
 			foreach (self::$m_aAttribDefs[$sClass] as $sAttCode => $oAttDef)
 			{
@@ -1052,10 +1135,36 @@ abstract class MetaModel
 					// - an external KEY / FIELD (direct),
 					// - an external field pointing to an external KEY / FIELD
 					// - an external field pointing to an external field pointing to....
+					$sRemoteClass = $oAttDef->GetTargetClass();
 
-					if ($oAttDef->IsExternalKey())
+					if ($oAttDef->IsExternalField())
 					{
-						$sRemoteClass = $oAttDef->GetTargetClass();
+						// This is a key, but the value comes from elsewhere
+						// Create an external field pointing to the remote friendly name attribute
+						$sKeyAttCode = $oAttDef->GetKeyAttCode();
+						$sRemoteAttCode = $oAttDef->GetExtAttCode()."_friendlyname";
+						$sFriendlyNameAttCode = $sAttCode.'_friendlyname';
+						// propagate "is_null_allowed" ? 
+						$oFriendlyName = new AttributeExternalField($sFriendlyNameAttCode, array("allowed_values"=>null, "extkey_attcode"=>$sKeyAttCode, "target_attcode"=>$sRemoteAttCode, "is_null_allowed"=>true, "depends_on"=>array()));
+						$oFriendlyName->SetHostClass($sClass);
+						self::$m_aAttribDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyName;
+						self::$m_aAttribOrigins[$sClass][$sFriendlyNameAttCode] = $sRemoteClass;
+						$oFriendlyNameFlt = new FilterFromAttribute($oFriendlyName);
+						self::$m_aFilterDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyNameFlt;
+						self::$m_aFilterOrigins[$sClass][$sFriendlyNameAttCode] = $sRemoteClass;
+					}
+					else
+					{
+						// Create the friendly name attribute
+						$sFriendlyNameAttCode = $sAttCode.'_friendlyname'; 
+						$oFriendlyName = new AttributeFriendlyName($sFriendlyNameAttCode, $sAttCode);
+						$oFriendlyName->SetHostClass($sClass);
+						self::$m_aAttribDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyName;
+						self::$m_aAttribOrigins[$sClass][$sFriendlyNameAttCode] = $sRemoteClass;
+						$oFriendlyNameFlt = new FilterFromAttribute($oFriendlyName);
+						self::$m_aFilterDefs[$sClass][$sFriendlyNameAttCode] = $oFriendlyNameFlt;
+						self::$m_aFilterOrigins[$sClass][$sFriendlyNameAttCode] = $sRemoteClass;
+
 						if (self::HasChildrenClasses($sRemoteClass))
 						{
 							// First, create an external field attribute, that gets the final class
@@ -1168,7 +1277,7 @@ abstract class MetaModel
 		$aMandatParams = array(
 			"category" => "group classes by modules defining their visibility in the UI",
 			"key_type" => "autoincrement | string",
-			"name_attcode" => "define wich attribute is the class name, may be an inherited attribute",
+			"name_attcode" => "define wich attribute is the class name, may be an array of attributes (format specified in the dictionary as 'Class:myclass/Name' => '%1\$s %2\$s...'",
 			"state_attcode" => "define wich attribute is representing the state (object lifecycle)",
 			"reconc_keys" => "define the attributes that will 'almost uniquely' identify an object in batch processes",
 			"db_table" => "database table",
@@ -1295,6 +1404,10 @@ abstract class MetaModel
 
 	public static function Init_AddAttribute(AttributeDefinition $oAtt)
 	{
+		$sAttCode = $oAtt->GetCode();
+		if ($sAttCode == 'finalclass') throw new Exception('Using a reserved keyword in metamodel declaration: '.$sAttCode);
+		if ($sAttCode == 'friendlyname') throw new Exception('Using a reserved keyword in metamodel declaration: '.$sAttCode);
+	
 		$sTargetClass = self::GetCallersPHPClass("Init");		
 
 		// Some attributes could refer to a class
@@ -1642,13 +1755,12 @@ abstract class MetaModel
 
 		if (!isset($oSelect))
 		{
-			$aTranslation = array();
 			$aClassAliases = array();
 			$aTableAliases = array();
-			$oConditionTree = $oFilter->GetCriteria();
+			$oQBExpr = new QueryBuilderExpressions(array(), $oFilter->GetCriteria());
 
 			$oKPI = new ExecutionKPI();
-			$oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter, array(), array(), true /* main query */);
+			$oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, array(), true /* main query */);
 			$oKPI->ComputeStats('MakeQuery (select)', $sOqlQuery);
 
 			self::$m_aQueryStructCache[$sOqlId] = clone $oSelect;
@@ -1672,12 +1784,8 @@ abstract class MetaModel
 		{
 			foreach ($oFilter->GetSelectedClasses() as $sSelectedAlias => $sSelectedClass)
 			{
-				$sNameAttCode = self::GetNameAttributeCode($sSelectedClass);
-				if (!empty($sNameAttCode))
-				{
-					// By default, simply order on the "name" attribute, ascending
-					$aOrderSpec[$sSelectedAlias.$sNameAttCode] = true;
-				}
+				// By default, simply order on the "friendlyname" attribute, ascending
+				$aOrderSpec[$sSelectedAlias."friendlyname"] = true;
 			}
 		}
 		
@@ -1759,11 +1867,10 @@ abstract class MetaModel
 
 	public static function MakeDeleteQuery(DBObjectSearch $oFilter, $aArgs = array())
 	{
-		$aTranslation = array();
 		$aClassAliases = array();
 		$aTableAliases = array();
-		$oConditionTree = $oFilter->GetCriteria();
-		$oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter, array(), array(), true /* main query */);
+		$oQBExpr = new QueryBuilderExpressions(array(), $oFilter->GetCriteria());
+		$oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, array(), true /* main query */);
 		$aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams());
 		return $oSelect->RenderDelete($aScalarArgs);
 	}
@@ -1771,20 +1878,18 @@ abstract class MetaModel
 	public static function MakeUpdateQuery(DBObjectSearch $oFilter, $aValues, $aArgs = array())
 	{
 		// $aValues is an array of $sAttCode => $value
-		$aTranslation = array();
 		$aClassAliases = array();
 		$aTableAliases = array();
-		$oConditionTree = $oFilter->GetCriteria();
-		$oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter, array(), $aValues, true /* main query */);
+		$oQBExpr = new QueryBuilderExpressions(array(), $oFilter->GetCriteria());
+		$oSelect = self::MakeQuery($oFilter->GetSelectedClasses(), $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, $aValues, true /* main query */);
 		$aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams());
 		return $oSelect->RenderUpdate($aScalarArgs);
 	}
 
-	private static function MakeQuery($aSelectedClasses, &$oConditionTree, &$aClassAliases, &$aTableAliases, &$aTranslation, DBObjectSearch $oFilter, $aExpectedAtts = array(), $aValues = array(), $bIsMainQuery = false)
+	private static function MakeQuery($aSelectedClasses, &$oQBExpr, &$aClassAliases, &$aTableAliases, DBObjectSearch $oFilter, $aValues = array(), $bIsMainQuery = false)
 	{
 		// Note: query class might be different than the class of the filter
 		// -> this occurs when we are linking our class to an external class (referenced by, or pointing to)
-		// $aExpectedAtts is an array of sAttCode=>array of columns
 		$sClass = $oFilter->GetFirstJoinedClass();
 		$sClassAlias = $oFilter->GetFirstJoinedClassAlias();
 
@@ -1794,7 +1899,7 @@ abstract class MetaModel
 			$aClassAliases = array_merge($aClassAliases, $oFilter->GetJoinedClasses());
 		}
 
-		self::DbgTrace("Entering: ".$oFilter->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY").", expectedatts=".count($aExpectedAtts).": ".implode(",", array_keys($aExpectedAtts)));
+		self::DbgTrace("Entering: ".$oFilter->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
 
 		$sRootClass = self::GetRootClass($sClass);
 		$sKeyField = self::DBGetKey($sClass);
@@ -1802,23 +1907,32 @@ abstract class MetaModel
 		if ($bIsOnQueriedClass)
 		{
 			// default to the whole list of attributes + the very std id/finalclass
-			$aExpectedAtts['id'][] = $sClassAlias.'id';
-			foreach (self::GetAttributesList($sClass) as $sAttCode)
+			$oQBExpr->AddSelect($sClassAlias.'id', new FieldExpression('id', $sClassAlias));
+
+			foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef)
 			{
-				$aExpectedAtts[$sAttCode][] = $sClassAlias.$sAttCode; // alias == class and attcode
+				if (!$oAttDef->IsScalar()) continue;
+				
+				foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
+				{
+					$oQBExpr->AddSelect($sClassAlias.$sAttCode.$sColId, new FieldExpression($sAttCode.$sColId, $sClassAlias));
+				}
 			}
 		}
 
-		// Compute a clear view of external keys, and external attributes
+		$aExpectedAtts = array(); // array of (attcode => fieldexpression)
+		$oQBExpr->GetUnresolvedFields($sClassAlias, $aExpectedAtts);
+
+		// Compute a clear view of required joins (from the current class)
 		// Build the list of external keys:
-		// -> ext keys required by a closed join ???
+		// -> ext keys required by an explicit join
 		// -> ext keys mentionned in a 'pointing to' condition
 		// -> ext keys required for an external field
+		// -> ext keys required for a friendly name
 		//
 		$aExtKeys = array(); // array of sTableClass => array of (sAttCode (keys) => array of (sAttCode (fields)=> oAttDef))
 		//
-		// Optimization: could be computed once for all (cached)
-		// Could be done in MakeQuerySingleTable ???
+		// Optimization: could be partially computed once for all (cached) ?
 		//  
 
 		if ($bIsOnQueriedClass)
@@ -1836,27 +1950,54 @@ abstract class MetaModel
 			$sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode];
 			$aExtKeys[$sKeyTableClass][$sKeyAttCode] = array();
 		}
-		// Add the ext fields used in the select (eventually adds an external key)
-		foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
+
+		if (array_key_exists('friendlyname', $aExpectedAtts))
 		{
-			if ($oAttDef->IsExternalField())
+			$aTranslateNow = array();
+			$aTranslateNow[$sClassAlias]['friendlyname'] = self::GetNameExpression($sClass, $sClassAlias);
+			$oQBExpr->Translate($aTranslateNow, false);
+
+			$aNameSpec = self::GetNameSpec($sClass);
+			foreach($aNameSpec[1] as $i => $sAttCode)
 			{
-				$sKeyAttCode = $oAttDef->GetKeyAttCode();
-				if (array_key_exists($sAttCode, $aExpectedAtts) || $oConditionTree->RequiresField($sClassAlias, $sAttCode))
+				$oAttDef = self::GetAttributeDef($sClass, $sAttCode);
+				if ($oAttDef->IsExternalKey())
 				{
-					// Add the external attribute
+					$sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sAttCode];
+					$aExtKeys[$sKeyTableClass][$sAttCode] = array();
+				}				
+				elseif ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
+				{
+					$sKeyAttCode = $oAttDef->GetKeyAttCode();
 					$sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode];
 					$aExtKeys[$sKeyTableClass][$sKeyAttCode][$sAttCode] = $oAttDef;
 				}
 			}
 		}
+		// Add the ext fields used in the select (eventually adds an external key)
+		foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef)
+		{
+			if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
+			{
+				if (array_key_exists($sAttCode, $aExpectedAtts))
+				{
+					$sKeyAttCode = $oAttDef->GetKeyAttCode();
+					if ($sKeyAttCode != 'id')
+					{
+						// Add the external attribute
+						$sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode];
+						$aExtKeys[$sKeyTableClass][$sKeyAttCode][$sAttCode] = $oAttDef;
+					}
+				}
+			}
+		}
 
 		// First query built upon on the leaf (ie current) class
 		//
 		self::DbgTrace("Main (=leaf) class, call MakeQuerySingleTable()");
 		if (self::HasTable($sClass))
 		{
-			$oSelectBase = self::MakeQuerySingleTable($aSelectedClasses, $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter, $sClass, $aExpectedAtts, $aExtKeys, $aValues);
+			$oSelectBase = self::MakeQuerySingleTable($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, $sClass, $aExtKeys, $aValues);
 		}
 		else
 		{
@@ -1865,7 +2006,7 @@ abstract class MetaModel
 			// As the join will not filter on the expected classes, we have to specify it explicitely
 			$sExpectedClasses = implode("', '", self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL));
 			$oFinalClassRestriction = Expression::FromOQL("`$sClassAlias`.finalclass IN ('$sExpectedClasses')");
-			$oConditionTree = $oConditionTree->LogAnd($oFinalClassRestriction);
+			$oQBExpr->AddCondition($oFinalClassRestriction);
 		}
 
 		// Then we join the queries of the eventual parent classes (compound model)
@@ -1873,7 +2014,7 @@ abstract class MetaModel
 		{
 			if (!self::HasTable($sParentClass)) continue;
 			self::DbgTrace("Parent class: $sParentClass... let's call MakeQuerySingleTable()");
-			$oSelectParentTable = self::MakeQuerySingleTable($aSelectedClasses, $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter, $sParentClass, $aExpectedAtts, $aExtKeys, $aValues);
+			$oSelectParentTable = self::MakeQuerySingleTable($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oFilter, $sParentClass, $aExtKeys, $aValues);
 			if (is_null($oSelectBase))
 			{
 				$oSelectBase = $oSelectParentTable;
@@ -1891,19 +2032,20 @@ abstract class MetaModel
 			{
 				$oForeignKeyAttDef = self::GetAttributeDef($sForeignClass, $sForeignKeyAttCode);
 	
-				// We don't want any attribute from the foreign class, just filter on an inner join
-				$aExpAtts = array();
-	
 				self::DbgTrace("Referenced by foreign key: $sForeignKeyAttCode... let's call MakeQuery()");
 				//self::DbgTrace($oForeignFilter);
 				//self::DbgTrace($oForeignFilter->ToOQL());
 				//self::DbgTrace($oSelectForeign);
 				//self::DbgTrace($oSelectForeign->RenderSelect(array()));
-				$oSelectForeign = self::MakeQuery($aSelectedClasses, $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oForeignFilter, $aExpAtts);
 
 				$sForeignClassAlias = $oForeignFilter->GetFirstJoinedClassAlias();
-				$sForeignKeyTable = $aTranslation[$sForeignClassAlias][$sForeignKeyAttCode][0];
-				$sForeignKeyColumn = $aTranslation[$sForeignClassAlias][$sForeignKeyAttCode][1];
+				$oQBExpr->PushJoinField(new FieldExpression($sForeignKeyAttCode, $sForeignClassAlias));
+
+				$oSelectForeign = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oForeignFilter);
+
+				$oJoinExpr = $oQBExpr->PopJoinField();
+				$sForeignKeyTable = $oJoinExpr->GetParent();
+				$sForeignKeyColumn = $oJoinExpr->GetName();
 				$oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable);
 			}
 		}
@@ -1940,8 +2082,8 @@ abstract class MetaModel
 		//
 		if ($bIsMainQuery)
 		{
-			$oConditionTranslated = $oConditionTree->Translate($aTranslation);
-			$oSelectBase->SetCondition($oConditionTranslated);
+			$oSelectBase->SetCondition($oQBExpr->GetCondition());
+			$oSelectBase->SetSelect($oQBExpr->GetSelect());
 		}
 
 		// That's all... cross fingers and we'll get some working query
@@ -1952,9 +2094,8 @@ abstract class MetaModel
 		return $oSelectBase;
 	}
 
-	protected static function MakeQuerySingleTable($aSelectedClasses, &$oConditionTree, &$aClassAliases, &$aTableAliases, &$aTranslation, $oFilter, $sTableClass, $aExpectedAtts, $aExtKeys, $aValues)
+	protected static function MakeQuerySingleTable($aSelectedClasses, &$oQBExpr, &$aClassAliases, &$aTableAliases, $oFilter, $sTableClass, $aExtKeys, $aValues)
 	{
-		// $aExpectedAtts is an array of sAttCode=>sAlias
 		// $aExtKeys is an array of sTableClass => array of (sAttCode (keys) => array of sAttCode (fields))
 
 		// Prepare the query for a single table (compound objects)
@@ -1968,31 +2109,30 @@ abstract class MetaModel
 		$sTable = self::DBGetTable($sTableClass);
 		$sTableAlias = self::GenerateUniqueAlias($aTableAliases, $sTargetAlias.'_'.$sTable, $sTable);
 
+		$aTranslation = array();
+		$aExpectedAtts = array();
+		$oQBExpr->GetUnresolvedFields($sTargetAlias, $aExpectedAtts);
+		
 		$bIsOnQueriedClass = array_key_exists($sTargetAlias, $aSelectedClasses);
 		
-		self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$oFilter->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY").", expectedatts=".count($aExpectedAtts).": ".implode(",", array_keys($aExpectedAtts)));
+		self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$oFilter->ToOQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
 
 		// 1 - SELECT and UPDATE
 		//
 		// Note: no need for any values nor fields for foreign Classes (ie not the queried Class)
 		//
-		$aSelect = array();
 		$aUpdateValues = array();
 
-		// 1/a - Get the key
+		// 1/a - Get the key and friendly name
 		//
-		if ($bIsOnQueriedClass)
-		{
-			$aSelect[$aExpectedAtts['id'][0]] = new FieldExpression(self::DBGetKey($sTableClass), $sTableAlias);
-		}
 		// We need one pkey to be the key, let's take the one corresponding to the root class
 		// (used to be based on the leaf, but it may happen that this one has no table defined)
 		$sRootClass = self::GetRootClass($sTargetClass);
 		if ($sTableClass == $sRootClass)
 		{
-			$aTranslation[$sTargetAlias]['id'] = array($sTableAlias, self::DBGetKey($sTableClass));
+			$aTranslation[$sTargetAlias]['id'] = new FieldExpressionResolved(self::DBGetKey($sTableClass), $sTableAlias);
 		}
-	
+
 		// 1/b - Get the other attributes
 		// 
 		foreach(self::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef)
@@ -2000,7 +2140,7 @@ abstract class MetaModel
 			// Skip this attribute if not defined in this table
 			if (self::$m_aAttribOrigins[$sTargetClass][$sAttCode] != $sTableClass) continue;
 
-			// Skip this attribute if not writable (means that it does not correspond 
+			// Skip this attribute if not made of SQL columns 
 			if (count($oAttDef->GetSQLExpressions()) == 0) continue;
 
 			// Update...
@@ -2016,12 +2156,9 @@ abstract class MetaModel
 
 			// Select...
 			//
-			// Skip, if a list of fields has been specified and it is not there
-			if (!array_key_exists($sAttCode, $aExpectedAtts)) continue;
-
 			if ($oAttDef->IsExternalField())
 			{
-				// skip, this will be handled in the joined tables
+				// skip, this will be handled in the joined tables (done hereabove)
 			}
 			else
 			{
@@ -2029,32 +2166,16 @@ abstract class MetaModel
 				// add it to the output
 				foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr)
 				{
-					foreach ($aExpectedAtts[$sAttCode] as $sAttAlias)
+					if (array_key_exists($sAttCode, $aExpectedAtts))
 					{
-						$aSelect[$sAttAlias.$sColId] = new FieldExpression($sSQLExpr, $sTableAlias);
-					} 
+						$aTranslation[$sTargetAlias][$sAttCode.$sColId] = new FieldExpressionResolved($sSQLExpr, $sTableAlias);
+					}
 				}
 			}
 		}
 
-		// 2 - WHERE
-		//
-		foreach(self::$m_aFilterDefs[$sTargetClass] as $sFltCode => $oFltAtt)
-		{
-			// Skip this filter if not defined in this table
-			if (self::$m_aFilterOrigins[$sTargetClass][$sFltCode] != $sTableClass) continue;
-
-			// #@# todo - aller plus loin... a savoir que la table de translation doit contenir une "Expression"
-			foreach($oFltAtt->GetSQLExpressions() as $sColID => $sFltExpr)
-			{
-				// Note: I did not test it with filters relying on several expressions...
-				//       as long as sColdID is empty, this is working, otherwise... ?
-				$aTranslation[$sTargetAlias][$sFltCode.$sColID] = array($sTableAlias, $sFltExpr);
-			}
-		}
-
 		// #@# todo - See what a full text search condition should be
-		// 2' - WHERE / Full text search condition
+		// 2 - WHERE / Full text search condition
 		//
 		if ($bIsOnQueriedClass)
 		{
@@ -2068,7 +2189,7 @@ abstract class MetaModel
 
 		// 3 - The whole stuff, for this table only
 		//
-		$oSelectBase = new SQLQuery($sTable, $sTableAlias, $aSelect, null, $aFullText, $bIsOnQueriedClass, $aUpdateValues);
+		$oSelectBase = new SQLQuery($sTable, $sTableAlias, array(), $aFullText, $bIsOnQueriedClass, $aUpdateValues);
 
 		// 4 - The external keys -> joins...
 		//
@@ -2099,45 +2220,59 @@ abstract class MetaModel
 
 				// Specify expected attributes for the target class query
 				// ... and use the current alias !
-				$aExpAtts = array();
-				$aIntermediateTranslation = array();
+				$aTranslateNow = array(); // Translation for external fields - must be performed before the join is done (recursion...)
 				foreach($aExtFields as $sAttCode => $oAtt)
 				{
-
-					$sExtAttCode = $oAtt->GetExtAttCode();
-					if (array_key_exists($sAttCode, $aExpectedAtts))
+					if ($oAtt instanceof AttributeFriendlyName)
 					{
-						// Request this attribute... transmit the alias !
-						$aExpAtts[$sExtAttCode] = $aExpectedAtts[$sAttCode];
+						// Note: for a given ext key, there is one single attribute "friendly name"
+						$aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression('friendlyname', $sKeyClassAlias);
 					}
-					// Translate mainclass.extfield => remoteclassalias.remotefieldcode
-					$oRemoteAttDef = self::GetAttributeDef($sKeyClass, $sExtAttCode);
-					foreach ($oRemoteAttDef->GetSQLExpressions() as $sColID => $sRemoteAttExpr)
+					else
 					{
-						$aIntermediateTranslation[$sTargetAlias.$sColID][$sAttCode] = array($sKeyClassAlias, $sRemoteAttExpr);
+						$sExtAttCode = $oAtt->GetExtAttCode();
+						// Translate mainclass.extfield => remoteclassalias.remotefieldcode
+						$oRemoteAttDef = self::GetAttributeDef($sKeyClass, $sExtAttCode);
+						foreach ($oRemoteAttDef->GetSQLExpressions() as $sColID => $sRemoteAttExpr)
+						{
+							$aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias);
+						}
+						//#@# debug - echo "<p>$sTargetAlias.$sAttCode to $sKeyClassAlias.$sRemoteAttExpr (class: $sKeyClass)</p>\n";
 					}
-					//#@# debug - echo "<p>$sTargetAlias.$sAttCode to $sKeyClassAlias.$sRemoteAttExpr (class: $sKeyClass)</p>\n";
 				}
-				$oConditionTree = $oConditionTree->Translate($aIntermediateTranslation, false);
+				// Translate prior to recursing
+				//
+				$oQBExpr->Translate($aTranslateNow, false);
 
 				self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()");
-				$oSelectExtKey = self::MakeQuery($aSelectedClasses, $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oExtFilter, $aExpAtts);
+
+				$oQBExpr->PushJoinField(new FieldExpression('id', $sKeyClassAlias));
+
+				$oSelectExtKey = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oExtFilter);
+
+				$oJoinExpr = $oQBExpr->PopJoinField();
+				$sExternalKeyTable = $oJoinExpr->GetParent();
+				$sExternalKeyField = $oJoinExpr->GetName();
 
 				$aCols = $oKeyAttDef->GetSQLExpressions(); // Workaround a PHP bug: sometimes issuing a Notice if invoking current(somefunc())
 				$sLocalKeyField = current($aCols); // get the first column for an external key
-				$sExternalKeyField = self::DBGetKey($sKeyClass);
+
 				self::DbgTrace("External key $sKeyAttCode, Join on $sLocalKeyField = $sExternalKeyField");
 				if ($oKeyAttDef->IsNullAllowed())
 				{
-					$oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField);
+					$oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable);
 				}
 				else
 				{
-					$oSelectBase->AddInnerJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField);
+					$oSelectBase->AddInnerJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable);
 				}
 			}
 		}
 
+		// Translate the selected columns
+		//
+		$oQBExpr->Translate($aTranslation, false);
+
 		//MyHelpers::var_dump_html($oSelectBase->RenderSelect());
 		return $oSelectBase;
 	}
@@ -2174,18 +2309,15 @@ abstract class MetaModel
 		$aSugFix = array();
 		foreach (self::GetClasses() as $sClass)
 		{
-			$sNameAttCode = self::GetNameAttributeCode($sClass);
-			if (empty($sNameAttCode))
+			$aNameSpec = self::GetNameSpec($sClass);
+			foreach($aNameSpec[1] as $i => $sAttCode)
 			{
-			//  let's try this !!!
-				// $aErrors[$sClass][] = "Missing value for name definition: the framework will (should...) replace it by the id";
-				// $aSugFix[$sClass][] = "Expecting a value in ".implode(", ", self::GetAttributesList($sClass));
-			}
-			else if(!self::IsValidAttCode($sClass, $sNameAttCode))
-			{
-				$aErrors[$sClass][] = "Unkown attribute code '".$sNameAttCode."' for the name definition";
-				$aSugFix[$sClass][] = "Expecting a value in ".implode(", ", self::GetAttributesList($sClass));
-			}
+				if(!self::IsValidAttCode($sClass, $sAttCode))
+				{
+					$aErrors[$sClass][] = "Unkown attribute code '".$sAttCode."' for the name definition";
+					$aSugFix[$sClass][] = "Expecting a value in ".implode(", ", self::GetAttributesList($sClass));
+				}
+			}				
 
 			foreach(self::GetReconcKeys($sClass) as $sReconcKeyAttCode)
 			{
@@ -3555,6 +3687,22 @@ abstract class MetaModel
 		return self::GetObjectByRow($sClass, $aRow);
 	}
 
+	public static function GetObjectByName($sClass, $sName, $bMustBeFound = true)
+	{
+		self::_check_subclass($sClass);	
+
+		$oObjSearch = new DBObjectSearch($sClass);
+		$oObjSearch->AddNameCondition($sName);
+		$oSet = new DBObjectSet($oObjSearch);
+		if ($oSet->Count() != 1)
+		{
+			if ($bMustBeFound) throw new CoreException('Failed to get an object by its name', array('class'=>$sClass, 'name'=>$sName));
+			return null;
+		}
+		$oObj = $oSet->fetch();
+		return $oObj;
+	}
+
 	public static function GetObjectFromOQL($sQuery, $aParams = null, $bAllowAllData = false)
 	{
 		$oFilter = DBObjectSearch::FromOQL($sQuery, $aParams);

+ 43 - 46
core/sqlquery.class.inc.php

@@ -35,24 +35,6 @@
 require_once('cmdbsource.class.inc.php');
 
 
-class SQLExpression extends BinaryExpression
-{
-}
-class ScalarSQLExpression extends ScalarExpression
-{
-}
-class TrueSQLExpression extends TrueExpression
-{
-}
-class FieldSQLExpression extends FieldExpression
-{
-}
-class VariableSQLExpression extends VariableExpression
-{
-}
-
-
-
 class SQLQuery
 {
 	private $m_sTable = '';
@@ -64,7 +46,7 @@ class SQLQuery
 	private $m_aValues = array(); // Values to set in case of an update query
 	private $m_aJoinSelects = array();
 
-	public function __construct($sTable, $sTableAlias, $aFields, $oConditionExpr, $aFullTextNeedles, $bToDelete = true, $aValues = array())
+	public function __construct($sTable, $sTableAlias, $aFields, $aFullTextNeedles, $bToDelete = true, $aValues = array())
 	{
 		// This check is not needed but for developping purposes
 		//if (!CMDBSource::IsTable($sTable))
@@ -79,15 +61,7 @@ class SQLQuery
 		$this->m_sTable = $sTable;
 		$this->m_sTableAlias = $sTableAlias;
 		$this->m_aFields = $aFields;
-		$this->m_oConditionExpr = $oConditionExpr;
-		if (is_null($oConditionExpr))
-		{
-			$this->m_oConditionExpr = new TrueExpression;
-		}
-		else if (!$oConditionExpr instanceof Expression)
-		{
-			throw new CoreException('Invalid type for condition, expecting an Expression', array('class' => get_class($oConditionExpr)));
-		}
+		$this->m_oConditionExpr = null;
 		$this->m_aFullTextNeedles = $aFullTextNeedles;
 		$this->m_bToDelete = $bToDelete;
 		$this->m_aValues = $aValues;
@@ -146,6 +120,11 @@ class SQLQuery
 		echo "</pre>";
 	}
 
+	public function SetSelect($aExpressions)
+	{
+		$this->m_aFields = $aExpressions;
+	}
+
 	public function SetCondition($oConditionExpr)
 	{
 		$this->m_oConditionExpr = $oConditionExpr;
@@ -153,7 +132,14 @@ class SQLQuery
 
 	public function AddCondition($oConditionExpr)
 	{
-		$this->m_oConditionExpr->LogAnd($oConditionExpr);
+		if (is_null($this->m_oConditionExpr))
+		{
+			$this->m_oConditionExpr = $oConditionExpr;
+		}
+		else
+		{
+			$this->m_oConditionExpr->LogAnd($oConditionExpr);
+		}
 	}
 
 	private function AddJoin($sJoinType, $oSQLQuery, $sLeftField, $sRightField, $sRightTableAlias = '')
@@ -216,8 +202,15 @@ class SQLQuery
 			throw new CoreException("Building a request wich will delete every object of a given table -looks suspicious- please use truncate instead...");
 		}
 		*/
-		$sWhere  = self::ClauseWhere($oCondition, $aArgs);
-		return "DELETE $sDelete FROM $sFrom WHERE $sWhere";
+		if (is_null($oCondition))
+		{
+			// Delete all !!!
+		}
+		else
+		{
+			$sWhere  = self::ClauseWhere($oCondition, $aArgs);
+			return "DELETE $sDelete FROM $sFrom WHERE $sWhere";
+		}
 	}
 
 	// Interface, build the SQL query
@@ -338,7 +331,14 @@ class SQLQuery
 
 	private static function ClauseWhere($oConditionExpr, $aArgs = array())
 	{
-		return $oConditionExpr->Render($aArgs);
+		if (is_null($oConditionExpr))
+		{
+			return '1';
+		}
+		else
+		{
+			return $oConditionExpr->Render($aArgs);
+		}
 	}
 
 	private static function ClauseOrderBy($aOrderBy)
@@ -365,24 +365,23 @@ class SQLQuery
 		$oCondition = $this->m_oConditionExpr;
 		if ((count($aFields) > 0) && (count($this->m_aFullTextNeedles) > 0))
 		{
-			$aFieldExp = array();
-			foreach ($aFields as $sField)
-			{
-				// This is TEMPORARY (that's why it is weird, actually)
-				// Full text match will be done as an expression in the filter condition
-
-				// $sField is already a string `table`.`column`
-				// Let's make an expression out of it (again !)
-				$aFieldExp[] = Expression::FromOQL($sField);
-			}
-			$oFullTextExpr = new CharConcatExpression($aFieldExp);
+			$sFields = implode(', ', $aFields);
+			$oFullTextExpr = Expression::FromSQL("CONCAT_WS(' ', $sFields)");
+			
 			// The cast is necessary because the CONCAT result in a binary string:
 			// if any of the field is a binary string => case sensitive comparison
 			//
 			foreach($this->m_aFullTextNeedles as $sFTNeedle)
 			{
 				$oNewCond = new BinaryExpression($oFullTextExpr, 'LIKE', new ScalarExpression("%$sFTNeedle%"));
-				$oCondition = $oCondition->LogAnd($oNewCond);
+				if (is_null($oCondition))
+				{
+					$oCondition = $oNewCond;
+				}
+				else
+				{
+					$oCondition = $oCondition->LogAnd($oNewCond);
+				}
 			}
 		}
 
@@ -419,8 +418,6 @@ class SQLQuery
 		//
 		foreach($this->m_aFields as $sAlias => $oExpression)
 		{
-			$sTable = $oExpression->GetParent();
-			$sColumn = $oExpression->GetName();
 			$aFields["`$sAlias`"] = $oExpression->Render();
 		}
 		if ($this->m_bToDelete)

+ 6 - 0
modules/itop-config-mgmt-1.0.0/de.dict.itop-config-mgmt.php

@@ -391,6 +391,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
 Dict::Add('DE DE', 'German', 'Deutsch', array(
 	'Class:Subnet' => 'Subnetz',
 	'Class:Subnet+' => '',
+	'Class:Subnet/Name' => '%1$s / %2$s',
 	//'Class:Subnet/Attribute:name' => 'Name',
 	//'Class:Subnet/Attribute:name+' => '',
 	'Class:Subnet/Attribute:org_id' => 'Organisation',
@@ -544,6 +545,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
 Dict::Add('DE DE', 'German', 'Deutsch', array(
 	'Class:SoftwareInstance' => 'Software-Instanz',
 	'Class:SoftwareInstance+' => '',
+	'Class:SoftwareInstance/Name' => '%1$s - %2$s',
 	'Class:SoftwareInstance/Attribute:device_id' => 'Gerät',
 	'Class:SoftwareInstance/Attribute:device_id+' => '',
 	'Class:SoftwareInstance/Attribute:device_name' => 'Gerät',
@@ -569,6 +571,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
 Dict::Add('DE DE', 'German', 'Deutsch', array(
 	'Class:ApplicationInstance' => 'Anwendungsinstanz',
 	'Class:ApplicationInstance+' => '',
+	'Class:ApplicationInstance/Name' => '%1$s - %2$s',
 	'Class:ApplicationInstance/Attribute:software_id' => 'Software',
 	'Class:ApplicationInstance/Attribute:software_id+' => '',
 	'Class:ApplicationInstance/Attribute:software_name' => 'Name',
@@ -583,6 +586,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
 Dict::Add('DE DE', 'German', 'Deutsch', array(
 	'Class:DBServerInstance' => 'Datenbank-Server-Instanz',
 	'Class:DBServerInstance+' => '',
+	'Class:DBServerInstance/Name' => '%1$s - %2$s',
 	'Class:DBServerInstance/Attribute:software_id' => 'Software',
 	'Class:DBServerInstance/Attribute:software_id+' => '',
 	'Class:DBServerInstance/Attribute:software_name' => 'Software Name',
@@ -598,6 +602,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
 Dict::Add('DE DE', 'German', 'Deutsch', array(
 	'Class:DatabaseInstance' => 'Datenbank-Instanz',
 	'Class:DatabaseInstance+' => '',
+	'Class:DatabaseInstance/Name' => '%1$s - %2$s',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id' => 'Datenbank-Server',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id+' => '',
 	'Class:DatabaseInstance/Attribute:db_server_instance_version' => 'Datenbank-Version',
@@ -658,6 +663,7 @@ Dict::Add('DE DE', 'German', 'Deutsch', array(
 Dict::Add('DE DE', 'German', 'Deutsch', array(
 	'Class:NetworkInterface' => 'Netzwerk-Interface',
 	'Class:NetworkInterface+' => '',
+	'Class:NetworkInterface/Name' => '%1$s - %2$s',
 	'Class:NetworkInterface/Attribute:device_id' => 'Gerät',
 	'Class:NetworkInterface/Attribute:device_id+' => '',
 	'Class:NetworkInterface/Attribute:device_name' => 'Gerät',

+ 180 - 174
modules/itop-config-mgmt-1.0.0/en.dict.itop-config-mgmt.php

@@ -131,60 +131,60 @@ Dict::Add('EN US', 'English', 'English', array(
 	'Class:Location/Attribute:infra_list' => 'Infrastructure',
 	'Class:Location/Attribute:infra_list+' => 'CIs located on this site',
 ));
-//
-// Class: Group
-//
-
-Dict::Add('EN US', 'English', 'English', array(
-	'Class:Group' => 'Group',
-	'Class:Group+' => '',
-	'Class:Group/Attribute:name' => 'Name',
-	'Class:Group/Attribute:name+' => '',
-	'Class:Group/Attribute:status' => 'Status',
-	'Class:Group/Attribute:status+' => '',
-	'Class:Group/Attribute:status/Value:implementation' => 'Implementation',
-	'Class:Group/Attribute:status/Value:implementation+' => 'Implementation',
-	'Class:Group/Attribute:status/Value:obsolete' => 'Obsolete',
-	'Class:Group/Attribute:status/Value:obsolete+' => 'Obsolete',
-	'Class:Group/Attribute:status/Value:production' => 'Production',
-	'Class:Group/Attribute:status/Value:production+' => 'Production',
-	'Class:Group/Attribute:org_id' => 'Organization',
-	'Class:Group/Attribute:org_id+' => '',
-	'Class:Group/Attribute:owner_name' => 'Name',
-	'Class:Group/Attribute:owner_name+' => 'Common name',
-	'Class:Group/Attribute:description' => 'Description',
-	'Class:Group/Attribute:description+' => '',
-	'Class:Group/Attribute:type' => 'Type',
-	'Class:Group/Attribute:type+' => '',
-	'Class:Group/Attribute:parent_id' => 'Parent Group',
-	'Class:Group/Attribute:parent_id+' => '',
-	'Class:Group/Attribute:parent_name' => 'Name',
-	'Class:Group/Attribute:parent_name+' => '',
-	'Class:Group/Attribute:ci_list' => 'Linked CIs',
-	'Class:Group/Attribute:ci_list+' => '',
-));
-
-//
-// Class: lnkGroupToCI
-//
-
-Dict::Add('EN US', 'English', 'English', array(
-	'Class:lnkGroupToCI' => 'Group / CI',
-	'Class:lnkGroupToCI+' => '',
-	'Class:lnkGroupToCI/Attribute:group_id' => 'Group',
-	'Class:lnkGroupToCI/Attribute:group_id+' => '',
-	'Class:lnkGroupToCI/Attribute:group_name' => 'Name',
-	'Class:lnkGroupToCI/Attribute:group_name+' => '',
-	'Class:lnkGroupToCI/Attribute:ci_id' => 'CI',
-	'Class:lnkGroupToCI/Attribute:ci_id+' => '',
-	'Class:lnkGroupToCI/Attribute:ci_name' => 'Name',
-	'Class:lnkGroupToCI/Attribute:ci_name+' => '',
-	'Class:lnkGroupToCI/Attribute:ci_status' => 'CI Status',
-	'Class:lnkGroupToCI/Attribute:ci_status+' => '',
-	'Class:lnkGroupToCI/Attribute:reason' => 'Reason',
-	'Class:lnkGroupToCI/Attribute:reason+' => '',
-));
-
+//
+// Class: Group
+//
+
+Dict::Add('EN US', 'English', 'English', array(
+	'Class:Group' => 'Group',
+	'Class:Group+' => '',
+	'Class:Group/Attribute:name' => 'Name',
+	'Class:Group/Attribute:name+' => '',
+	'Class:Group/Attribute:status' => 'Status',
+	'Class:Group/Attribute:status+' => '',
+	'Class:Group/Attribute:status/Value:implementation' => 'Implementation',
+	'Class:Group/Attribute:status/Value:implementation+' => 'Implementation',
+	'Class:Group/Attribute:status/Value:obsolete' => 'Obsolete',
+	'Class:Group/Attribute:status/Value:obsolete+' => 'Obsolete',
+	'Class:Group/Attribute:status/Value:production' => 'Production',
+	'Class:Group/Attribute:status/Value:production+' => 'Production',
+	'Class:Group/Attribute:org_id' => 'Organization',
+	'Class:Group/Attribute:org_id+' => '',
+	'Class:Group/Attribute:owner_name' => 'Name',
+	'Class:Group/Attribute:owner_name+' => 'Common name',
+	'Class:Group/Attribute:description' => 'Description',
+	'Class:Group/Attribute:description+' => '',
+	'Class:Group/Attribute:type' => 'Type',
+	'Class:Group/Attribute:type+' => '',
+	'Class:Group/Attribute:parent_id' => 'Parent Group',
+	'Class:Group/Attribute:parent_id+' => '',
+	'Class:Group/Attribute:parent_name' => 'Name',
+	'Class:Group/Attribute:parent_name+' => '',
+	'Class:Group/Attribute:ci_list' => 'Linked CIs',
+	'Class:Group/Attribute:ci_list+' => '',
+));
+
+//
+// Class: lnkGroupToCI
+//
+
+Dict::Add('EN US', 'English', 'English', array(
+	'Class:lnkGroupToCI' => 'Group / CI',
+	'Class:lnkGroupToCI+' => '',
+	'Class:lnkGroupToCI/Attribute:group_id' => 'Group',
+	'Class:lnkGroupToCI/Attribute:group_id+' => '',
+	'Class:lnkGroupToCI/Attribute:group_name' => 'Name',
+	'Class:lnkGroupToCI/Attribute:group_name+' => '',
+	'Class:lnkGroupToCI/Attribute:ci_id' => 'CI',
+	'Class:lnkGroupToCI/Attribute:ci_id+' => '',
+	'Class:lnkGroupToCI/Attribute:ci_name' => 'Name',
+	'Class:lnkGroupToCI/Attribute:ci_name+' => '',
+	'Class:lnkGroupToCI/Attribute:ci_status' => 'CI Status',
+	'Class:lnkGroupToCI/Attribute:ci_status+' => '',
+	'Class:lnkGroupToCI/Attribute:reason' => 'Reason',
+	'Class:lnkGroupToCI/Attribute:reason+' => '',
+));
+
 
 //
 // Class: Contact
@@ -358,33 +358,33 @@ Dict::Add('EN US', 'English', 'English', array(
 // Class: Licence
 //
 
-Dict::Add('EN US', 'English', 'English', array(
-	'Class:Licence' => 'Licence',
-	'Class:Licence+' => '',
-	'Class:Licence/Attribute:provider' => 'Provider',
-	'Class:Licence/Attribute:provider+' => '',
-	'Class:Licence/Attribute:org_id' => 'Owner',
-	'Class:Licence/Attribute:org_id+' => '',
-	'Class:Licence/Attribute:org_name' => 'Name',
-	'Class:Licence/Attribute:org_name+' => 'Common name',
-	'Class:Licence/Attribute:product' => 'Product',
-	'Class:Licence/Attribute:product+' => '',
-	'Class:Licence/Attribute:name' => 'Name',
-	'Class:Licence/Attribute:name+' => '',
-	'Class:Licence/Attribute:start' => 'Start date',
-	'Class:Licence/Attribute:start+' => '',
-	'Class:Licence/Attribute:end' => 'End date',
-	'Class:Licence/Attribute:end+' => '',
-	'Class:Licence/Attribute:licence_key' => 'Key',
-	'Class:Licence/Attribute:licence_key+' => '',
-	'Class:Licence/Attribute:scope' => 'Scope',
-	'Class:Licence/Attribute:scope+' => '',
-	'Class:Licence/Attribute:usage_limit' => 'Usage limit',
-	'Class:Licence/Attribute:usage_limit+' => '',
-	'Class:Licence/Attribute:usage_list' => 'Usage',
-	'Class:Licence/Attribute:usage_list+' => 'Application instances using this licence',
-));
-
+Dict::Add('EN US', 'English', 'English', array(
+	'Class:Licence' => 'Licence',
+	'Class:Licence+' => '',
+	'Class:Licence/Attribute:provider' => 'Provider',
+	'Class:Licence/Attribute:provider+' => '',
+	'Class:Licence/Attribute:org_id' => 'Owner',
+	'Class:Licence/Attribute:org_id+' => '',
+	'Class:Licence/Attribute:org_name' => 'Name',
+	'Class:Licence/Attribute:org_name+' => 'Common name',
+	'Class:Licence/Attribute:product' => 'Product',
+	'Class:Licence/Attribute:product+' => '',
+	'Class:Licence/Attribute:name' => 'Name',
+	'Class:Licence/Attribute:name+' => '',
+	'Class:Licence/Attribute:start' => 'Start date',
+	'Class:Licence/Attribute:start+' => '',
+	'Class:Licence/Attribute:end' => 'End date',
+	'Class:Licence/Attribute:end+' => '',
+	'Class:Licence/Attribute:licence_key' => 'Key',
+	'Class:Licence/Attribute:licence_key+' => '',
+	'Class:Licence/Attribute:scope' => 'Scope',
+	'Class:Licence/Attribute:scope+' => '',
+	'Class:Licence/Attribute:usage_limit' => 'Usage limit',
+	'Class:Licence/Attribute:usage_limit+' => '',
+	'Class:Licence/Attribute:usage_list' => 'Usage',
+	'Class:Licence/Attribute:usage_list+' => 'Application instances using this licence',
+));
+
 
 //
 // Class: Subnet
@@ -393,6 +393,7 @@ Dict::Add('EN US', 'English', 'English', array(
 Dict::Add('EN US', 'English', 'English', array(
 	'Class:Subnet' => 'Subnet',
 	'Class:Subnet+' => '',
+	'Class:Subnet/Name' => '%1$s / %2$s',
 	//'Class:Subnet/Attribute:name' => 'Name',
 	//'Class:Subnet/Attribute:name+' => '',
 	'Class:Subnet/Attribute:org_id' => 'Owner organization',
@@ -546,6 +547,7 @@ Dict::Add('EN US', 'English', 'English', array(
 Dict::Add('EN US', 'English', 'English', array(
 	'Class:SoftwareInstance' => 'Software Instance',
 	'Class:SoftwareInstance+' => '',
+	'Class:SoftwareInstance/Name' => '%1$s - %2$s',
 	'Class:SoftwareInstance/Attribute:device_id' => 'Device',
 	'Class:SoftwareInstance/Attribute:device_id+' => '',
 	'Class:SoftwareInstance/Attribute:device_name' => 'Device',
@@ -566,31 +568,33 @@ Dict::Add('EN US', 'English', 'English', array(
 // Class: ApplicationInstance
 //
 
-Dict::Add('EN US', 'English', 'English', array(
-	'Class:ApplicationInstance' => 'Application Instance',
-	'Class:ApplicationInstance+' => '',
-	'Class:ApplicationInstance/Attribute:software_id' => 'Software',
-	'Class:ApplicationInstance/Attribute:software_id+' => '',
-	'Class:ApplicationInstance/Attribute:software_name' => 'Name',
-	'Class:ApplicationInstance/Attribute:software_name+' => '',
-));
-
+Dict::Add('EN US', 'English', 'English', array(
+	'Class:ApplicationInstance' => 'Application Instance',
+	'Class:ApplicationInstance+' => '',
+	'Class:ApplicationInstance/Name' => '%1$s - %2$s',
+	'Class:ApplicationInstance/Attribute:software_id' => 'Software',
+	'Class:ApplicationInstance/Attribute:software_id+' => '',
+	'Class:ApplicationInstance/Attribute:software_name' => 'Name',
+	'Class:ApplicationInstance/Attribute:software_name+' => '',
+));
+
 
 //
 // Class: DBServerInstance
 //
 
-Dict::Add('EN US', 'English', 'English', array(
-	'Class:DBServerInstance' => 'DB Server Instance',
-	'Class:DBServerInstance+' => '',
-	'Class:DBServerInstance/Attribute:software_id' => 'Software',
-	'Class:DBServerInstance/Attribute:software_id+' => '',
+Dict::Add('EN US', 'English', 'English', array(
+	'Class:DBServerInstance' => 'DB Server Instance',
+	'Class:DBServerInstance+' => '',
+	'Class:DBServerInstance/Name' => '%1$s - %2$s',
+	'Class:DBServerInstance/Attribute:software_id' => 'Software',
+	'Class:DBServerInstance/Attribute:software_id+' => '',
 	'Class:DBServerInstance/Attribute:software_name' => 'Software Name',
-	'Class:DBServerInstance/Attribute:software_name+' => '',
-	'Class:DBServerInstance/Attribute:dbinstance_list' => 'Databases',
-	'Class:DBServerInstance/Attribute:dbinstance_list+' => 'Database sources',
-));
-
+	'Class:DBServerInstance/Attribute:software_name+' => '',
+	'Class:DBServerInstance/Attribute:dbinstance_list' => 'Databases',
+	'Class:DBServerInstance/Attribute:dbinstance_list+' => 'Database sources',
+));
+
 
 //
 // Class: DatabaseInstance
@@ -599,6 +603,7 @@ Dict::Add('EN US', 'English', 'English', array(
 Dict::Add('EN US', 'English', 'English', array(
 	'Class:DatabaseInstance' => 'Database Instance',
 	'Class:DatabaseInstance+' => '',
+	'Class:DatabaseInstance/Name' => '%1$s - %2$s',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id' => 'Database server',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id+' => '',
 	'Class:DatabaseInstance/Attribute:db_server_instance_version' => 'Database version',
@@ -656,70 +661,71 @@ Dict::Add('EN US', 'English', 'English', array(
 // Class: NetworkInterface
 //
 
-Dict::Add('EN US', 'English', 'English', array(
-	'Class:NetworkInterface' => 'Network Interface',
-	'Class:NetworkInterface+' => '',
-	'Class:NetworkInterface/Attribute:device_id' => 'Device',
-	'Class:NetworkInterface/Attribute:device_id+' => '',
-	'Class:NetworkInterface/Attribute:device_name' => 'Device',
-	'Class:NetworkInterface/Attribute:device_name+' => '',
-	'Class:NetworkInterface/Attribute:logical_type' => 'Logical Type',
-	'Class:NetworkInterface/Attribute:logical_type+' => '',
-	'Class:NetworkInterface/Attribute:logical_type/Value:backup' => 'Backup',
-	'Class:NetworkInterface/Attribute:logical_type/Value:backup+' => '',
-	'Class:NetworkInterface/Attribute:logical_type/Value:logical' => 'Logical',
-	'Class:NetworkInterface/Attribute:logical_type/Value:logical+' => '',
-	'Class:NetworkInterface/Attribute:logical_type/Value:port' => 'Port',
-	'Class:NetworkInterface/Attribute:logical_type/Value:port+' => '',
-	'Class:NetworkInterface/Attribute:logical_type/Value:primary' => 'Primary',
-	'Class:NetworkInterface/Attribute:logical_type/Value:primary+' => '',
-	'Class:NetworkInterface/Attribute:logical_type/Value:secondary' => 'Secondary',
-	'Class:NetworkInterface/Attribute:logical_type/Value:secondary+' => '',
-	'Class:NetworkInterface/Attribute:physical_type' => 'Physical Type',
-	'Class:NetworkInterface/Attribute:physical_type+' => '',
-	'Class:NetworkInterface/Attribute:physical_type/Value:atm' => 'ATM',
-	'Class:NetworkInterface/Attribute:physical_type/Value:atm+' => '',
-	'Class:NetworkInterface/Attribute:physical_type/Value:ethernet' => 'Ethernet',
-	'Class:NetworkInterface/Attribute:physical_type/Value:ethernet+' => '',
-	'Class:NetworkInterface/Attribute:physical_type/Value:framerelay' => 'Frame Relay',
-	'Class:NetworkInterface/Attribute:physical_type/Value:framerelay+' => '',
-	'Class:NetworkInterface/Attribute:physical_type/Value:vlan' => 'VLAN',
-	'Class:NetworkInterface/Attribute:physical_type/Value:vlan+' => '',
-	'Class:NetworkInterface/Attribute:ip_address' => 'IP Address',
-	'Class:NetworkInterface/Attribute:ip_address+' => '',
-	'Class:NetworkInterface/Attribute:ip_mask' => 'IP Mask',
-	'Class:NetworkInterface/Attribute:ip_mask+' => '',
-	'Class:NetworkInterface/Attribute:mac_address' => 'MAC Address',
-	'Class:NetworkInterface/Attribute:mac_address+' => '',
-	'Class:NetworkInterface/Attribute:speed' => 'Speed',
-	'Class:NetworkInterface/Attribute:speed+' => '',
-	'Class:NetworkInterface/Attribute:duplex' => 'Duplex',
-	'Class:NetworkInterface/Attribute:duplex+' => '',
-	'Class:NetworkInterface/Attribute:duplex/Value:auto' => 'Auto',
-	'Class:NetworkInterface/Attribute:duplex/Value:auto+' => 'Auto',
-	'Class:NetworkInterface/Attribute:duplex/Value:full' => 'Full',
-	'Class:NetworkInterface/Attribute:duplex/Value:full+' => '',
-	'Class:NetworkInterface/Attribute:duplex/Value:half' => 'Half',
-	'Class:NetworkInterface/Attribute:duplex/Value:half+' => '',
-	'Class:NetworkInterface/Attribute:duplex/Value:unknown' => 'Unknown',
-	'Class:NetworkInterface/Attribute:duplex/Value:unknown+' => '',
-	'Class:NetworkInterface/Attribute:connected_if' => 'Connected to',
-	'Class:NetworkInterface/Attribute:connected_if+' => 'Connected interface',
-	'Class:NetworkInterface/Attribute:connected_name' => 'Connected to',
-	'Class:NetworkInterface/Attribute:connected_name+' => '',
-	'Class:NetworkInterface/Attribute:connected_if_device_id' => 'Connected device',
-	'Class:NetworkInterface/Attribute:connected_if_device_id+' => '',
-	'Class:NetworkInterface/Attribute:connected_if_device_id_name' => 'Device',
-	'Class:NetworkInterface/Attribute:connected_if_device_id_name+' => '',
-	'Class:NetworkInterface/Attribute:link_type' => 'Link type',
-	'Class:NetworkInterface/Attribute:link_type+' => '',
-	'Class:NetworkInterface/Attribute:link_type/Value:downlink' => 'Down link',
-	'Class:NetworkInterface/Attribute:link_type/Value:downlink+' => '',
-	'Class:NetworkInterface/Attribute:link_type/Value:uplink' => 'Up link',
-	'Class:NetworkInterface/Attribute:link_type/Value:uplink+' => '',
-));
-
-
+Dict::Add('EN US', 'English', 'English', array(
+	'Class:NetworkInterface' => 'Network Interface',
+	'Class:NetworkInterface+' => '',
+	'Class:NetworkInterface/Name' => '%1$s - %2$s',
+	'Class:NetworkInterface/Attribute:device_id' => 'Device',
+	'Class:NetworkInterface/Attribute:device_id+' => '',
+	'Class:NetworkInterface/Attribute:device_name' => 'Device',
+	'Class:NetworkInterface/Attribute:device_name+' => '',
+	'Class:NetworkInterface/Attribute:logical_type' => 'Logical Type',
+	'Class:NetworkInterface/Attribute:logical_type+' => '',
+	'Class:NetworkInterface/Attribute:logical_type/Value:backup' => 'Backup',
+	'Class:NetworkInterface/Attribute:logical_type/Value:backup+' => '',
+	'Class:NetworkInterface/Attribute:logical_type/Value:logical' => 'Logical',
+	'Class:NetworkInterface/Attribute:logical_type/Value:logical+' => '',
+	'Class:NetworkInterface/Attribute:logical_type/Value:port' => 'Port',
+	'Class:NetworkInterface/Attribute:logical_type/Value:port+' => '',
+	'Class:NetworkInterface/Attribute:logical_type/Value:primary' => 'Primary',
+	'Class:NetworkInterface/Attribute:logical_type/Value:primary+' => '',
+	'Class:NetworkInterface/Attribute:logical_type/Value:secondary' => 'Secondary',
+	'Class:NetworkInterface/Attribute:logical_type/Value:secondary+' => '',
+	'Class:NetworkInterface/Attribute:physical_type' => 'Physical Type',
+	'Class:NetworkInterface/Attribute:physical_type+' => '',
+	'Class:NetworkInterface/Attribute:physical_type/Value:atm' => 'ATM',
+	'Class:NetworkInterface/Attribute:physical_type/Value:atm+' => '',
+	'Class:NetworkInterface/Attribute:physical_type/Value:ethernet' => 'Ethernet',
+	'Class:NetworkInterface/Attribute:physical_type/Value:ethernet+' => '',
+	'Class:NetworkInterface/Attribute:physical_type/Value:framerelay' => 'Frame Relay',
+	'Class:NetworkInterface/Attribute:physical_type/Value:framerelay+' => '',
+	'Class:NetworkInterface/Attribute:physical_type/Value:vlan' => 'VLAN',
+	'Class:NetworkInterface/Attribute:physical_type/Value:vlan+' => '',
+	'Class:NetworkInterface/Attribute:ip_address' => 'IP Address',
+	'Class:NetworkInterface/Attribute:ip_address+' => '',
+	'Class:NetworkInterface/Attribute:ip_mask' => 'IP Mask',
+	'Class:NetworkInterface/Attribute:ip_mask+' => '',
+	'Class:NetworkInterface/Attribute:mac_address' => 'MAC Address',
+	'Class:NetworkInterface/Attribute:mac_address+' => '',
+	'Class:NetworkInterface/Attribute:speed' => 'Speed',
+	'Class:NetworkInterface/Attribute:speed+' => '',
+	'Class:NetworkInterface/Attribute:duplex' => 'Duplex',
+	'Class:NetworkInterface/Attribute:duplex+' => '',
+	'Class:NetworkInterface/Attribute:duplex/Value:auto' => 'Auto',
+	'Class:NetworkInterface/Attribute:duplex/Value:auto+' => 'Auto',
+	'Class:NetworkInterface/Attribute:duplex/Value:full' => 'Full',
+	'Class:NetworkInterface/Attribute:duplex/Value:full+' => '',
+	'Class:NetworkInterface/Attribute:duplex/Value:half' => 'Half',
+	'Class:NetworkInterface/Attribute:duplex/Value:half+' => '',
+	'Class:NetworkInterface/Attribute:duplex/Value:unknown' => 'Unknown',
+	'Class:NetworkInterface/Attribute:duplex/Value:unknown+' => '',
+	'Class:NetworkInterface/Attribute:connected_if' => 'Connected to',
+	'Class:NetworkInterface/Attribute:connected_if+' => 'Connected interface',
+	'Class:NetworkInterface/Attribute:connected_name' => 'Connected to',
+	'Class:NetworkInterface/Attribute:connected_name+' => '',
+	'Class:NetworkInterface/Attribute:connected_if_device_id' => 'Connected device',
+	'Class:NetworkInterface/Attribute:connected_if_device_id+' => '',
+	'Class:NetworkInterface/Attribute:connected_if_device_id_name' => 'Device',
+	'Class:NetworkInterface/Attribute:connected_if_device_id_name+' => '',
+	'Class:NetworkInterface/Attribute:link_type' => 'Link type',
+	'Class:NetworkInterface/Attribute:link_type+' => '',
+	'Class:NetworkInterface/Attribute:link_type/Value:downlink' => 'Down link',
+	'Class:NetworkInterface/Attribute:link_type/Value:downlink+' => '',
+	'Class:NetworkInterface/Attribute:link_type/Value:uplink' => 'Up link',
+	'Class:NetworkInterface/Attribute:link_type/Value:uplink+' => '',
+));
+
+
 
 //
 // Class: Device
@@ -1033,21 +1039,21 @@ Dict::Add('EN US', 'English', 'English', array(
 'Menu:MobilePhone+' => 'All Mobile Phones',
 'Menu:PC' => 'Personal Computers',
 'Menu:PC+' => 'All Personal Computers',
-'Menu:NewContact' => 'New Contact',
-'Menu:NewContact+' => 'New Contact',
-'Menu:SearchContacts' => 'Search for contacts',
-'Menu:SearchContacts+' => 'Search for contacts',
-'Menu:NewCI' => 'New CI',
-'Menu:NewCI+' => 'New CI',
-'Menu:SearchCIs' => 'Search for CIs',
+'Menu:NewContact' => 'New Contact',
+'Menu:NewContact+' => 'New Contact',
+'Menu:SearchContacts' => 'Search for contacts',
+'Menu:SearchContacts+' => 'Search for contacts',
+'Menu:NewCI' => 'New CI',
+'Menu:NewCI+' => 'New CI',
+'Menu:SearchCIs' => 'Search for CIs',
 'Menu:SearchCIs+' => 'Search for CIs',
 'Menu:ConfigManagement:Devices' => 'Devices',
 'Menu:ConfigManagement:AllDevices' => 'Number of devices: %1$d',
 'Menu:ConfigManagement:SWAndApps' => 'Software and Applications',
-'Menu:ConfigManagement:Misc' => 'Miscellaneous',
-'Menu:Group' => 'Groups of CIs',
+'Menu:ConfigManagement:Misc' => 'Miscellaneous',
+'Menu:Group' => 'Groups of CIs',
 'Menu:Group+' => 'Groups of CIs',
-'Menu:ConfigManagement:Shortcuts' => 'Shortcuts',
+'Menu:ConfigManagement:Shortcuts' => 'Shortcuts',
 'Menu:ConfigManagement:AllContacts' => 'All contacts: %1$d',
 ));
 ?>

+ 6 - 0
modules/itop-config-mgmt-1.0.0/es_cr.dict.itop-config-mgmt.php

@@ -387,6 +387,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 	'Class:Subnet' => 'Sub-Red',
 	'Class:Subnet+' => '',
+	'Class:Subnet/Name' => '%1$s / %2$s',
 	//'Class:Subnet/Attribute:name' => 'Nombre',
 	//'Class:Subnet/Attribute:name+' => '',
 	'Class:Subnet/Attribute:org_id' => 'Organización propietaria',
@@ -540,6 +541,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 	'Class:SoftwareInstance' => 'Instancia de Software',
 	'Class:SoftwareInstance+' => '',
+	'Class:SoftwareInstance/Name' => '%1$s - %2$s',
 	'Class:SoftwareInstance/Attribute:device_id' => 'Dispositivo',
 	'Class:SoftwareInstance/Attribute:device_id+' => '',
 	'Class:SoftwareInstance/Attribute:device_name' => 'Dispositivo',
@@ -565,6 +567,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 	'Class:ApplicationInstance' => 'Instancia de aplicación',
 	'Class:ApplicationInstance+' => '',
+	'Class:ApplicationInstance/Name' => '%1$s - %2$s',
 ));
 
 //
@@ -574,6 +577,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 	'Class:DBServerInstance' => 'Instancia de Servidor de BD',
 	'Class:DBServerInstance+' => '',
+	'Class:DBServerInstance/Name' => '%1$s - %2$s',
 	'Class:DBServerInstance/Attribute:dbinstance_list' => 'Bases de Datos',
 	'Class:DBServerInstance/Attribute:dbinstance_list+' => 'Fuentes de Bases de Datos',
 ));
@@ -585,6 +589,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 	'Class:DatabaseInstance' => 'Instancia de Base de Datos',
 	'Class:DatabaseInstance+' => '',
+	'Class:DatabaseInstance/Name' => '%1$s - %2$s',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id' => 'Servidor de Base de Datos',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id+' => '',
 	'Class:DatabaseInstance/Attribute:db_server_instance_version' => 'Versión de Base de Datos',
@@ -645,6 +650,7 @@ Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 Dict::Add('ES CR', 'Spanish', 'Español, Castellano', array(
 	'Class:NetworkInterface' => 'Interfase de Red',
 	'Class:NetworkInterface+' => '',
+	'Class:NetworkInterface/Name' => '%1$s - %2$s',
 	'Class:NetworkInterface/Attribute:device_id' => 'Dispositivo',
 	'Class:NetworkInterface/Attribute:device_id+' => '',
 	'Class:NetworkInterface/Attribute:device_name' => 'Dispositivo',

+ 61 - 55
modules/itop-config-mgmt-1.0.0/fr.dict.itop-config-mgmt.php

@@ -130,59 +130,59 @@ Dict::Add('FR FR', 'French', 'Français', array(
 	'Class:Location/Attribute:infra_list' => 'Infrastructure',
 	'Class:Location/Attribute:infra_list+' => 'Eléments d\'infrastructure situés sur ce lieu',
 ));
-//
-// Class: Group
-//
-
-Dict::Add('FR FR', 'French', 'Français', array(
-        'Class:Group' => 'Groupe',
-        'Class:Group+' => '',
-        'Class:Group/Attribute:name' => 'Nom',
-        'Class:Group/Attribute:name+' => '',
-        'Class:Group/Attribute:status' => 'Etat',
-        'Class:Group/Attribute:status+' => '',
-        'Class:Group/Attribute:status/Value:implementation' => 'Implémentation',
-        'Class:Group/Attribute:status/Value:implementation+' => 'Implémentation',
-        'Class:Group/Attribute:status/Value:obsolete' => 'Obsolète',
-        'Class:Group/Attribute:status/Value:obsolete+' => 'Obsolète',
-        'Class:Group/Attribute:status/Value:production' => 'Production',
-        'Class:Group/Attribute:status/Value:production+' => 'Production',
-        'Class:Group/Attribute:org_id' => 'Organization',
-        'Class:Group/Attribute:org_id+' => '',
-        'Class:Group/Attribute:owner_name' => 'Nom',
-        'Class:Group/Attribute:owner_name+' => 'Nom commun',
-        'Class:Group/Attribute:description' => 'Description',
-        'Class:Group/Attribute:description+' => '',
-        'Class:Group/Attribute:type' => 'Type',
-        'Class:Group/Attribute:type+' => '',
-        'Class:Group/Attribute:parent_id' => 'Group parent',
-        'Class:Group/Attribute:parent_id+' => '',
-        'Class:Group/Attribute:parent_name' => 'Nom',
-        'Class:Group/Attribute:parent_name+' => '',
-        'Class:Group/Attribute:ci_list' => 'CIs lié',
-        'Class:Group/Attribute:ci_list+' => '',
-));
-//
-// Class: lnkGroupToCI
-//
-
-Dict::Add('FR FR', 'French', 'Français', array(
-        'Class:lnkGroupToCI' => 'Groupe / CI',
-        'Class:lnkGroupToCI+' => '',
-        'Class:lnkGroupToCI/Attribute:group_id' => 'Groupe',
-        'Class:lnkGroupToCI/Attribute:group_id+' => '',
-        'Class:lnkGroupToCI/Attribute:group_name' => 'Nom',
-        'Class:lnkGroupToCI/Attribute:group_name+' => '',
-        'Class:lnkGroupToCI/Attribute:ci_id' => 'CI',
-        'Class:lnkGroupToCI/Attribute:ci_id+' => '',
-        'Class:lnkGroupToCI/Attribute:ci_name' => 'Nom',
-        'Class:lnkGroupToCI/Attribute:ci_name+' => '',
-        'Class:lnkGroupToCI/Attribute:ci_status' => 'Etat du CI',
-        'Class:lnkGroupToCI/Attribute:ci_status+' => '',
-        'Class:lnkGroupToCI/Attribute:reason' => 'Raison',
-        'Class:lnkGroupToCI/Attribute:reason+' => '',
-));
-
+//
+// Class: Group
+//
+
+Dict::Add('FR FR', 'French', 'Français', array(
+        'Class:Group' => 'Groupe',
+        'Class:Group+' => '',
+        'Class:Group/Attribute:name' => 'Nom',
+        'Class:Group/Attribute:name+' => '',
+        'Class:Group/Attribute:status' => 'Etat',
+        'Class:Group/Attribute:status+' => '',
+        'Class:Group/Attribute:status/Value:implementation' => 'Implémentation',
+        'Class:Group/Attribute:status/Value:implementation+' => 'Implémentation',
+        'Class:Group/Attribute:status/Value:obsolete' => 'Obsolète',
+        'Class:Group/Attribute:status/Value:obsolete+' => 'Obsolète',
+        'Class:Group/Attribute:status/Value:production' => 'Production',
+        'Class:Group/Attribute:status/Value:production+' => 'Production',
+        'Class:Group/Attribute:org_id' => 'Organization',
+        'Class:Group/Attribute:org_id+' => '',
+        'Class:Group/Attribute:owner_name' => 'Nom',
+        'Class:Group/Attribute:owner_name+' => 'Nom commun',
+        'Class:Group/Attribute:description' => 'Description',
+        'Class:Group/Attribute:description+' => '',
+        'Class:Group/Attribute:type' => 'Type',
+        'Class:Group/Attribute:type+' => '',
+        'Class:Group/Attribute:parent_id' => 'Group parent',
+        'Class:Group/Attribute:parent_id+' => '',
+        'Class:Group/Attribute:parent_name' => 'Nom',
+        'Class:Group/Attribute:parent_name+' => '',
+        'Class:Group/Attribute:ci_list' => 'CIs lié',
+        'Class:Group/Attribute:ci_list+' => '',
+));
+//
+// Class: lnkGroupToCI
+//
+
+Dict::Add('FR FR', 'French', 'Français', array(
+        'Class:lnkGroupToCI' => 'Groupe / CI',
+        'Class:lnkGroupToCI+' => '',
+        'Class:lnkGroupToCI/Attribute:group_id' => 'Groupe',
+        'Class:lnkGroupToCI/Attribute:group_id+' => '',
+        'Class:lnkGroupToCI/Attribute:group_name' => 'Nom',
+        'Class:lnkGroupToCI/Attribute:group_name+' => '',
+        'Class:lnkGroupToCI/Attribute:ci_id' => 'CI',
+        'Class:lnkGroupToCI/Attribute:ci_id+' => '',
+        'Class:lnkGroupToCI/Attribute:ci_name' => 'Nom',
+        'Class:lnkGroupToCI/Attribute:ci_name+' => '',
+        'Class:lnkGroupToCI/Attribute:ci_status' => 'Etat du CI',
+        'Class:lnkGroupToCI/Attribute:ci_status+' => '',
+        'Class:lnkGroupToCI/Attribute:reason' => 'Raison',
+        'Class:lnkGroupToCI/Attribute:reason+' => '',
+));
+
 
 //
 // Class: Contact
@@ -385,6 +385,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
 Dict::Add('FR FR', 'French', 'Français', array(
 	'Class:Subnet' => 'Sous-réseau',
 	'Class:Subnet+' => '',
+	'Class:Subnet/Name' => '%1$s / %2$s',
 	//'Class:Subnet/Attribute:name' => 'Name',
 	//'Class:Subnet/Attribute:name+' => '',
 	'Class:Subnet/Attribute:org_id' => 'Organisation',
@@ -538,6 +539,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
 Dict::Add('FR FR', 'French', 'Français', array(
 	'Class:SoftwareInstance' => 'Instance de logiciel',
 	'Class:SoftwareInstance+' => '',
+	'Class:SoftwareInstance/Name' => '%1$s - %2$s',
 	'Class:SoftwareInstance/Attribute:device_id' => 'Equipement',
 	'Class:SoftwareInstance/Attribute:device_id+' => '',
 	'Class:SoftwareInstance/Attribute:device_name' => 'Equipement',
@@ -563,6 +565,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
 Dict::Add('FR FR', 'French', 'Français', array(
 	'Class:ApplicationInstance' => 'Instance d\'application',
 	'Class:ApplicationInstance+' => '',
+	'Class:ApplicationInstance/Name' => '%1$s - %2$s',
 ));
 
 //
@@ -572,6 +575,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
 Dict::Add('FR FR', 'French', 'Français', array(
 	'Class:DBServerInstance' => 'Instance de serveur de base de données',
 	'Class:DBServerInstance+' => '',
+	'Class:DBServerInstance/Name' => '%1$s - %2$s',
 	'Class:DBServerInstance/Attribute:software_id' => 'Logiciel',
 	'Class:DBServerInstance/Attribute:software_name' => 'Logiciel Serveur',
 	'Class:DBServerInstance/Attribute:dbinstance_list' => 'Bases',
@@ -585,6 +589,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
 Dict::Add('FR FR', 'French', 'Français', array(
 	'Class:DatabaseInstance' => 'Base de données',
 	'Class:DatabaseInstance+' => '',
+	'Class:DatabaseInstance/Name' => '%1$s - %2$s',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id' => 'Serveur de données',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id+' => '',
 	'Class:DatabaseInstance/Attribute:db_server_instance_version' => 'Version',
@@ -645,6 +650,7 @@ Dict::Add('FR FR', 'French', 'Français', array(
 Dict::Add('FR FR', 'French', 'Français', array(
 	'Class:NetworkInterface' => 'Interface réseau',
 	'Class:NetworkInterface+' => '',
+	'Class:NetworkInterface/Name' => '%1$s - %2$s',
 	'Class:NetworkInterface/Attribute:device_id' => 'Equipement',
 	'Class:NetworkInterface/Attribute:device_id+' => '',
 	'Class:NetworkInterface/Attribute:device_name' => 'Equipement',
@@ -1019,8 +1025,8 @@ Dict::Add('FR FR', 'French', 'Français', array(
 'Menu:ConfigManagement:AllDevices' => 'Nombre d\'équipements: %1$d',
 'Menu:ConfigManagement:SWAndApps' => 'Logiciels et Applications',
 'Menu:ConfigManagement:Misc' => 'Divers',
-'Menu:Group' => 'Groupes de CIs',
-'Menu:Group+' => 'Groupes de CIs',
+'Menu:Group' => 'Groupes de CIs',
+'Menu:Group+' => 'Groupes de CIs',
 'Menu:ConfigManagement:Shortcuts' => 'Raccourcis',
 'Menu:ConfigManagement:AllContacts' => 'Tous les contacts: %1$d',
 ));

+ 7 - 33
modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php

@@ -148,7 +148,7 @@ class Person extends Contact
 		(
 			"category" => "bizmodel,searchable,structure",
 			"key_type" => "autoincrement",
-			"name_attcode" => "name",
+			"name_attcode" => array('first_name', 'name'),
 			"state_attcode" => "",
 			"reconc_keys" => array("name","first_name","org_id","email"),
 			"db_table" => "person",
@@ -167,11 +167,6 @@ class Person extends Contact
 		MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'org_id', 'email', 'phone', 'location_id', 'first_name', 'employee_id'));
 		MetaModel::Init_SetZListItems('list', array('first_name','status', 'org_id', 'email', 'phone', 'location_id'));
 	}
-	
-	public function GetName()
-	{
-		return $this->Get('first_name').' '.$this->Get('name');
-	}
 }
 class Team extends Contact
 {
@@ -421,7 +416,7 @@ class Subnet extends cmdbAbstractObject
 		(
 			"category" => "bizmodel,searchable,configmgmt",
 			"key_type" => "autoincrement",
-			"name_attcode" => "ip",
+			"name_attcode" => array('ip', 'ip_mask'),
 			"state_attcode" => "",
 			"reconc_keys" => array("ip", "ip_mask","org_id", "org_name"),
 			"db_table" => "subnet",
@@ -445,11 +440,6 @@ class Subnet extends cmdbAbstractObject
 		MetaModel::Init_SetZListItems('list', array('ip', 'ip_mask', 'org_id'));
 	}
 
-	public function GetName()
-	{
-		return $this->Get('ip').' / '.$this->Get('ip_mask');
-	}
-
 	function DisplayBareRelations(WebPage $oPage, $bEditMode = false)
 	{
 		parent::DisplayBareRelations($oPage, $bEditMode);
@@ -708,7 +698,7 @@ abstract class SoftwareInstance extends FunctionalCI
 		(
 			"category" => "bizmodel,searchable,configmgmt",
 			"key_type" => "autoincrement",
-			"name_attcode" => "name",
+			"name_attcode" => array('name', 'device_id_friendlyname'),
 			"state_attcode" => "",
 			"reconc_keys" => array("name", "device_id", "device_name", "org_id", "owner_name"),
 			"db_table" => "softwareinstance",
@@ -732,11 +722,6 @@ abstract class SoftwareInstance extends FunctionalCI
 		MetaModel::Init_SetZListItems('list', array('finalclass', 'status', 'org_id', 'importance', 'device_id', 'version'));
 	}
 
-	public function GetName()
-	{
-		return $this->Get('name').' - '.$this->Get('device_name');
-	}
-
 	public function ComputeValues()
 	{
 	}
@@ -773,7 +758,7 @@ class DBServerInstance extends SoftwareInstance
 		(
 			"category" => "bizmodel,searchable,configmgmt",
 			"key_type" => "autoincrement",
-			"name_attcode" => "software_name",
+			"name_attcode" => array('name', 'device_id_friendlyname'),
 			"state_attcode" => "",
 			"reconc_keys" => array("name","software_id","software_name","device_id","device_name","org_id","owner_name"),
 			"db_table" => "softwareinstance_dbserver",
@@ -802,7 +787,7 @@ class ApplicationInstance extends SoftwareInstance
 		(
 			"category" => "bizmodel,searchable,configmgmt",
 			"key_type" => "autoincrement",
-			"name_attcode" => "software_name",
+			"name_attcode" => array('name', 'device_id_friendlyname'),
 			"state_attcode" => "",
 			"reconc_keys" => array("name","software_id","software_name","device_id","device_name","org_id","owner_name"),
 			"db_table" => "softwareinstance_application",
@@ -832,7 +817,7 @@ class DatabaseInstance extends FunctionalCI
 		(
 			"category" => "bizmodel,searchable,configmgmt",
 			"key_type" => "autoincrement",
-			"name_attcode" => "name",
+			"name_attcode" => array('name', 'db_server_instance_name'),
 			"state_attcode" => "",
 			"reconc_keys" => array("name","org_id","owner_name","db_server_instance_id","db_server_instance_name"),
 			"db_table" => "databaseinstance",
@@ -854,10 +839,6 @@ class DatabaseInstance extends FunctionalCI
 		MetaModel::Init_SetZListItems('list', array('status', 'org_id', 'importance', 'db_server_instance_id', 'db_server_instance_version'));
 	}
 
-	public function GetName()
-	{
-		return $this->Get('name').' - '.$this->Get('db_server_instance_name');
-	}
 	public static function GetRelationQueries($sRelCode)
 	{
 		switch ($sRelCode)
@@ -1081,7 +1062,7 @@ class NetworkInterface extends ConnectableCI
 		(
 			"category" => "bizmodel,searchable,configmgmt",
 			"key_type" => "autoincrement",
-			"name_attcode" => "name",
+			"name_attcode" => array('device_id_friendlyname', 'name'),
 			"state_attcode" => "",
 			"reconc_keys" => array("name","device_id","device_name","org_id"),
 			"db_table" => "networkinterface",
@@ -1113,13 +1094,6 @@ class NetworkInterface extends ConnectableCI
 		MetaModel::Init_SetZListItems('list', array('status', 'ip_address', 'importance', 'device_id', 'logical_type', 'physical_type', 'link_type', 'connected_if_device_id'));
 	}
 
-	public function GetName()
-	{
-		return $this->Get('device_name').' - '.$this->Get('name');
-	}
-
-
-
 	public static function GetRelationQueries($sRelCode)
 	{
 		switch ($sRelCode)

+ 6 - 0
modules/itop-config-mgmt-1.0.0/pt_br.dict.itop-config-mgmt.php

@@ -329,6 +329,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 	'Class:Subnet' => 'Sub-rede',
 	'Class:Subnet+' => '',
+	'Class:Subnet/Name' => '%1$s / %2$s',
 	//'Class:Subnet/Attribute:name' => 'Nome',
 	//'Class:Subnet/Attribute:name+' => '',
 	'Class:Subnet/Attribute:org_id' => 'Organização',
@@ -482,6 +483,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 	'Class:SoftwareInstance' => 'Software Instance',
 	'Class:SoftwareInstance+' => '',
+	'Class:SoftwareInstance/Name' => '%1$s - %2$s',
 	'Class:SoftwareInstance/Attribute:device_id' => 'Dispositivo',
 	'Class:SoftwareInstance/Attribute:device_id+' => '',
 	'Class:SoftwareInstance/Attribute:device_name' => 'Dispositivo',
@@ -507,6 +509,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 	'Class:ApplicationInstance' => 'Inst&acirc;ncia Aplica&ccedil;&atilde;o',
 	'Class:ApplicationInstance+' => '',
+	'Class:ApplicationInstance/Name' => '%1$s - %2$s',
 ));
 
 //
@@ -516,6 +519,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 	'Class:DBServerInstance' => 'Inst&acirc;ncias DB Server',
 	'Class:DBServerInstance+' => '',
+	'Class:DBServerInstance/Name' => '%1$s - %2$s',
 	'Class:DBServerInstance/Attribute:dbinstance_list' => 'Base de Dados',
 	'Class:DBServerInstance/Attribute:dbinstance_list+' => 'Origem Base de dados',
 ));
@@ -527,6 +531,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 	'Class:DatabaseInstance' => 'Inst&acirc;ncia Base de Dados',
 	'Class:DatabaseInstance+' => '',
+	'Class:DatabaseInstance/Name' => '%1$s - %2$s',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id' => 'Servidor Base de Dados',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id+' => '',
 	'Class:DatabaseInstance/Attribute:db_server_instance_version' => 'Vers&atilde;o Base de Dados',
@@ -587,6 +592,7 @@ Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 Dict::Add('PT BR', 'Brazilian', 'Brazilian', array(
 	'Class:NetworkInterface' => 'Interface de rede',
 	'Class:NetworkInterface+' => '',
+	'Class:NetworkInterface/Name' => '%1$s - %2$s',
 	'Class:NetworkInterface/Attribute:device_id' => 'Dispositivo',
 	'Class:NetworkInterface/Attribute:device_id+' => '',
 	'Class:NetworkInterface/Attribute:device_name' => 'Dispositivo',

+ 6 - 0
modules/itop-config-mgmt-1.0.0/ru.dict.itop-config-mgmt.php

@@ -391,6 +391,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
 Dict::Add('RU RU', 'Russian', 'Русский', array(
 	'Class:Subnet' => 'Подсеть',
 	'Class:Subnet+' => '',
+	'Class:Subnet/Name' => '%1$s / %2$s',
 	//'Class:Subnet/Attribute:name' => 'Name',
 	//'Class:Subnet/Attribute:name+' => '',
 	'Class:Subnet/Attribute:org_id' => 'Организация-владелец',
@@ -544,6 +545,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
 Dict::Add('RU RU', 'Russian', 'Русский', array(
 	'Class:SoftwareInstance' => 'Экземпляры ПО',
 	'Class:SoftwareInstance+' => '',
+	'Class:SoftwareInstance/Name' => '%1$s - %2$s',
 	'Class:SoftwareInstance/Attribute:device_id' => 'Устройство',
 	'Class:SoftwareInstance/Attribute:device_id+' => '',
 	'Class:SoftwareInstance/Attribute:device_name' => 'Устройство',
@@ -567,6 +569,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
 Dict::Add('RU RU', 'Russian', 'Русский', array(
 	'Class:ApplicationInstance' => 'Экземпляры приложений',
 	'Class:ApplicationInstance+' => '',
+	'Class:ApplicationInstance/Name' => '%1$s - %2$s',
 	'Class:ApplicationInstance/Attribute:software_id' => 'ПО',
 	'Class:ApplicationInstance/Attribute:software_id+' => '',
 	'Class:ApplicationInstance/Attribute:software_name' => 'Название',
@@ -581,6 +584,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
 Dict::Add('RU RU', 'Russian', 'Русский', array(
 	'Class:DBServerInstance' => 'Экземпляры серверов баз данных',
 	'Class:DBServerInstance+' => '',
+	'Class:DBServerInstance/Name' => '%1$s - %2$s',
 	'Class:DBServerInstance/Attribute:software_id' => 'ПО',
 	'Class:DBServerInstance/Attribute:software_id+' => '',
 	'Class:DBServerInstance/Attribute:software_name' => 'Название',
@@ -597,6 +601,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
 Dict::Add('RU RU', 'Russian', 'Русский', array(
 	'Class:DatabaseInstance' => 'Экземпляры баз данных',
 	'Class:DatabaseInstance+' => '',
+	'Class:DatabaseInstance/Name' => '%1$s - %2$s',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id' => 'Сервер базы данных',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id+' => '',
 	'Class:DatabaseInstance/Attribute:db_server_instance_version' => 'Версия базы данных',
@@ -657,6 +662,7 @@ Dict::Add('RU RU', 'Russian', 'Русский', array(
 Dict::Add('RU RU', 'Russian', 'Русский', array(
 	'Class:NetworkInterface' => 'Сетевой интерфейс',
 	'Class:NetworkInterface+' => '',
+	'Class:NetworkInterface/Name' => '%1$s - %2$s',
 	'Class:NetworkInterface/Attribute:device_id' => 'Устройство',
 	'Class:NetworkInterface/Attribute:device_id+' => '',
 	'Class:NetworkInterface/Attribute:device_name' => 'Устройство',

+ 182 - 176
modules/itop-config-mgmt-1.0.0/tr.dict.itop-config-mgmt.php

@@ -129,60 +129,60 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 	'Class:Location/Attribute:infra_list' => 'Altyapı',
 	'Class:Location/Attribute:infra_list+' => 'Yerleşkedeki Konfigürasyon Kalemleri (KK)',
 ));
-//
-// Class: Group
-//
-
-Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
-	'Class:Group' => 'Grup',
-	'Class:Group+' => '',
-	'Class:Group/Attribute:name' => 'Adı',
-	'Class:Group/Attribute:name+' => '',
-	'Class:Group/Attribute:status' => 'Surumu',
-	'Class:Group/Attribute:status+' => '',
-	'Class:Group/Attribute:status/Value:implementation' => 'Uygulama',
-	'Class:Group/Attribute:status/Value:implementation+' => 'Uygulama',
-	'Class:Group/Attribute:status/Value:obsolete' => 'Üretimden kalkan',
-	'Class:Group/Attribute:status/Value:obsolete+' => 'Üretimden kalkan',
-	'Class:Group/Attribute:status/Value:production' => 'Kullanımda',
-	'Class:Group/Attribute:status/Value:production+' => 'Kullanımda',
-	'Class:Group/Attribute:org_id' => 'Kurum',
-	'Class:Group/Attribute:org_id+' => '',
-	'Class:Group/Attribute:owner_name' => 'Adı',
-	'Class:Group/Attribute:owner_name+' => 'Kullanılan Adı',
-	'Class:Group/Attribute:description' => 'Tanımlama',
-	'Class:Group/Attribute:description+' => '',
-	'Class:Group/Attribute:type' => 'Tip',
-	'Class:Group/Attribute:type+' => '',
-	'Class:Group/Attribute:parent_id' => 'Bağlı olduğu grup',
-	'Class:Group/Attribute:parent_id+' => '',
-	'Class:Group/Attribute:parent_name' => 'Adı',
-	'Class:Group/Attribute:parent_name+' => '',
-	'Class:Group/Attribute:ci_list' => 'Bağlantılı Konfigürasyon Kalemleri (KK)',
-	'Class:Group/Attribute:ci_list+' => '',
-));
-
-//
-// Class: lnkGroupToCI
-//
-
-Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
-	'Class:lnkGroupToCI' => 'Grup / KK',
-	'Class:lnkGroupToCI+' => '',
-	'Class:lnkGroupToCI/Attribute:group_id' => 'Grup',
-	'Class:lnkGroupToCI/Attribute:group_id+' => '',
-	'Class:lnkGroupToCI/Attribute:group_name' => 'Adı',
-	'Class:lnkGroupToCI/Attribute:group_name+' => '',
-	'Class:lnkGroupToCI/Attribute:ci_id' => 'KK',
-	'Class:lnkGroupToCI/Attribute:ci_id+' => '',
-	'Class:lnkGroupToCI/Attribute:ci_name' => 'Adı',
-	'Class:lnkGroupToCI/Attribute:ci_name+' => '',
-	'Class:lnkGroupToCI/Attribute:ci_status' => 'KK durumu',
-	'Class:lnkGroupToCI/Attribute:ci_status+' => '',
-	'Class:lnkGroupToCI/Attribute:reason' => 'Sebep',
-	'Class:lnkGroupToCI/Attribute:reason+' => '',
-));
-
+//
+// Class: Group
+//
+
+Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
+	'Class:Group' => 'Grup',
+	'Class:Group+' => '',
+	'Class:Group/Attribute:name' => 'Adı',
+	'Class:Group/Attribute:name+' => '',
+	'Class:Group/Attribute:status' => 'Surumu',
+	'Class:Group/Attribute:status+' => '',
+	'Class:Group/Attribute:status/Value:implementation' => 'Uygulama',
+	'Class:Group/Attribute:status/Value:implementation+' => 'Uygulama',
+	'Class:Group/Attribute:status/Value:obsolete' => 'Üretimden kalkan',
+	'Class:Group/Attribute:status/Value:obsolete+' => 'Üretimden kalkan',
+	'Class:Group/Attribute:status/Value:production' => 'Kullanımda',
+	'Class:Group/Attribute:status/Value:production+' => 'Kullanımda',
+	'Class:Group/Attribute:org_id' => 'Kurum',
+	'Class:Group/Attribute:org_id+' => '',
+	'Class:Group/Attribute:owner_name' => 'Adı',
+	'Class:Group/Attribute:owner_name+' => 'Kullanılan Adı',
+	'Class:Group/Attribute:description' => 'Tanımlama',
+	'Class:Group/Attribute:description+' => '',
+	'Class:Group/Attribute:type' => 'Tip',
+	'Class:Group/Attribute:type+' => '',
+	'Class:Group/Attribute:parent_id' => 'Bağlı olduğu grup',
+	'Class:Group/Attribute:parent_id+' => '',
+	'Class:Group/Attribute:parent_name' => 'Adı',
+	'Class:Group/Attribute:parent_name+' => '',
+	'Class:Group/Attribute:ci_list' => 'Bağlantılı Konfigürasyon Kalemleri (KK)',
+	'Class:Group/Attribute:ci_list+' => '',
+));
+
+//
+// Class: lnkGroupToCI
+//
+
+Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
+	'Class:lnkGroupToCI' => 'Grup / KK',
+	'Class:lnkGroupToCI+' => '',
+	'Class:lnkGroupToCI/Attribute:group_id' => 'Grup',
+	'Class:lnkGroupToCI/Attribute:group_id+' => '',
+	'Class:lnkGroupToCI/Attribute:group_name' => 'Adı',
+	'Class:lnkGroupToCI/Attribute:group_name+' => '',
+	'Class:lnkGroupToCI/Attribute:ci_id' => 'KK',
+	'Class:lnkGroupToCI/Attribute:ci_id+' => '',
+	'Class:lnkGroupToCI/Attribute:ci_name' => 'Adı',
+	'Class:lnkGroupToCI/Attribute:ci_name+' => '',
+	'Class:lnkGroupToCI/Attribute:ci_status' => 'KK durumu',
+	'Class:lnkGroupToCI/Attribute:ci_status+' => '',
+	'Class:lnkGroupToCI/Attribute:reason' => 'Sebep',
+	'Class:lnkGroupToCI/Attribute:reason+' => '',
+));
+
 
 //
 // Class: Contact
@@ -356,33 +356,33 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 // Class: Licence
 //
 
-Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
-	'Class:Licence' => 'Lisans',
-	'Class:Licence+' => '',
-	'Class:Licence/Attribute:provider' => 'Lisansı veren',
-	'Class:Licence/Attribute:provider+' => '',
-	'Class:Licence/Attribute:org_id' => 'Sahibi',
-	'Class:Licence/Attribute:org_id+' => '',
-	'Class:Licence/Attribute:org_name' => 'Adı',
-	'Class:Licence/Attribute:org_name+' => 'Adı',
-	'Class:Licence/Attribute:product' => 'Ürün',
-	'Class:Licence/Attribute:product+' => '',
-	'Class:Licence/Attribute:name' => 'Adı',
-	'Class:Licence/Attribute:name+' => '',
-	'Class:Licence/Attribute:start' => 'Başlangıç tarihi',
-	'Class:Licence/Attribute:start+' => '',
-	'Class:Licence/Attribute:end' => 'Bitiş tarihi',
-	'Class:Licence/Attribute:end+' => '',
-	'Class:Licence/Attribute:licence_key' => 'Lisans',
-	'Class:Licence/Attribute:licence_key+' => '',
-	'Class:Licence/Attribute:scope' => 'Kapsam',
-	'Class:Licence/Attribute:scope+' => '',
-	'Class:Licence/Attribute:usage_limit' => 'Kullanım limit',
-	'Class:Licence/Attribute:usage_limit+' => '',
-	'Class:Licence/Attribute:usage_list' => 'Kullanım',
-	'Class:Licence/Attribute:usage_list+' => 'Lisansı kullanan uygulamalar',
-));
-
+Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
+	'Class:Licence' => 'Lisans',
+	'Class:Licence+' => '',
+	'Class:Licence/Attribute:provider' => 'Lisansı veren',
+	'Class:Licence/Attribute:provider+' => '',
+	'Class:Licence/Attribute:org_id' => 'Sahibi',
+	'Class:Licence/Attribute:org_id+' => '',
+	'Class:Licence/Attribute:org_name' => 'Adı',
+	'Class:Licence/Attribute:org_name+' => 'Adı',
+	'Class:Licence/Attribute:product' => 'Ürün',
+	'Class:Licence/Attribute:product+' => '',
+	'Class:Licence/Attribute:name' => 'Adı',
+	'Class:Licence/Attribute:name+' => '',
+	'Class:Licence/Attribute:start' => 'Başlangıç tarihi',
+	'Class:Licence/Attribute:start+' => '',
+	'Class:Licence/Attribute:end' => 'Bitiş tarihi',
+	'Class:Licence/Attribute:end+' => '',
+	'Class:Licence/Attribute:licence_key' => 'Lisans',
+	'Class:Licence/Attribute:licence_key+' => '',
+	'Class:Licence/Attribute:scope' => 'Kapsam',
+	'Class:Licence/Attribute:scope+' => '',
+	'Class:Licence/Attribute:usage_limit' => 'Kullanım limit',
+	'Class:Licence/Attribute:usage_limit+' => '',
+	'Class:Licence/Attribute:usage_list' => 'Kullanım',
+	'Class:Licence/Attribute:usage_list+' => 'Lisansı kullanan uygulamalar',
+));
+
 
 //
 // Class: Subnet
@@ -391,6 +391,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 	'Class:Subnet' => 'Subnet',
 	'Class:Subnet+' => '',
+	'Class:Subnet/Name' => '%1$s / %2$s',
 	//'Class:Subnet/Attribute:name' => 'Adı',
 	//'Class:Subnet/Attribute:name+' => '',
 	'Class:Subnet/Attribute:org_id' => 'Kurum',
@@ -544,6 +545,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 	'Class:SoftwareInstance' => 'Yazılım Kurulumu',
 	'Class:SoftwareInstance+' => '',
+	'Class:SoftwareInstance/Name' => '%1$s - %2$s',
 	'Class:SoftwareInstance/Attribute:device_id' => 'Cihaz',
 	'Class:SoftwareInstance/Attribute:device_id+' => '',
 	'Class:SoftwareInstance/Attribute:device_name' => 'Cihaz',
@@ -564,31 +566,33 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 // Class: ApplicationInstance
 //
 
-Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
-	'Class:ApplicationInstance' => 'Uygulama Kurulumu',
-	'Class:ApplicationInstance+' => '',
-	'Class:ApplicationInstance/Attribute:software_id' => 'Yazılım',
-	'Class:ApplicationInstance/Attribute:software_id+' => '',
-	'Class:ApplicationInstance/Attribute:software_name' => 'Adı',
-	'Class:ApplicationInstance/Attribute:software_name+' => '',
-));
-
+Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
+	'Class:ApplicationInstance' => 'Uygulama Kurulumu',
+	'Class:ApplicationInstance+' => '',
+	'Class:ApplicationInstance/Name' => '%1$s - %2$s',
+	'Class:ApplicationInstance/Attribute:software_id' => 'Yazılım',
+	'Class:ApplicationInstance/Attribute:software_id+' => '',
+	'Class:ApplicationInstance/Attribute:software_name' => 'Adı',
+	'Class:ApplicationInstance/Attribute:software_name+' => '',
+));
+
 
 //
 // Class: DBServerInstance
 //
 
-Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
-	'Class:DBServerInstance' => 'Veritabanı Sunucusu',
-	'Class:DBServerInstance+' => '',
-	'Class:DBServerInstance/Attribute:software_id' => 'Yazılım',
-	'Class:DBServerInstance/Attribute:software_id+' => '',
-	'Class:DBServerInstance/Attribute:software_name' => 'Adı',
-	'Class:DBServerInstance/Attribute:software_name+' => '',
-	'Class:DBServerInstance/Attribute:dbinstance_list' => 'Veritabanları',
-	'Class:DBServerInstance/Attribute:dbinstance_list+' => 'Veritabanları',
-));
-
+Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
+	'Class:DBServerInstance' => 'Veritabanı Sunucusu',
+	'Class:DBServerInstance+' => '',
+	'Class:DBServerInstance/Name' => '%1$s - %2$s',
+	'Class:DBServerInstance/Attribute:software_id' => 'Yazılım',
+	'Class:DBServerInstance/Attribute:software_id+' => '',
+	'Class:DBServerInstance/Attribute:software_name' => 'Adı',
+	'Class:DBServerInstance/Attribute:software_name+' => '',
+	'Class:DBServerInstance/Attribute:dbinstance_list' => 'Veritabanları',
+	'Class:DBServerInstance/Attribute:dbinstance_list+' => 'Veritabanları',
+));
+
 
 //
 // Class: DatabaseInstance
@@ -597,6 +601,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 	'Class:DatabaseInstance' => 'Veritabanı',
 	'Class:DatabaseInstance+' => '',
+	'Class:DatabaseInstance/Name' => '%1$s - %2$s',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id' => 'Veritabanı sunucusu',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id+' => '',
 	'Class:DatabaseInstance/Attribute:db_server_instance_version' => 'Veritabanı versiyonu',
@@ -654,70 +659,71 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 // Class: NetworkInterface
 //
 
-Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
-	'Class:NetworkInterface' => 'Network arayüzü',
-	'Class:NetworkInterface+' => '',
-	'Class:NetworkInterface/Attribute:device_id' => 'Cihaz',
-	'Class:NetworkInterface/Attribute:device_id+' => '',
-	'Class:NetworkInterface/Attribute:device_name' => 'Cihaz',
-	'Class:NetworkInterface/Attribute:device_name+' => '',
-	'Class:NetworkInterface/Attribute:logical_type' => 'Mantıksal tipi',
-	'Class:NetworkInterface/Attribute:logical_type+' => '',
-	'Class:NetworkInterface/Attribute:logical_type/Value:backup' => 'Yedek',
-	'Class:NetworkInterface/Attribute:logical_type/Value:backup+' => '',
-	'Class:NetworkInterface/Attribute:logical_type/Value:logical' => 'Mantıksal',
-	'Class:NetworkInterface/Attribute:logical_type/Value:logical+' => '',
-	'Class:NetworkInterface/Attribute:logical_type/Value:port' => 'Port',
-	'Class:NetworkInterface/Attribute:logical_type/Value:port+' => '',
-	'Class:NetworkInterface/Attribute:logical_type/Value:primary' => 'Birincil',
-	'Class:NetworkInterface/Attribute:logical_type/Value:primary+' => '',
-	'Class:NetworkInterface/Attribute:logical_type/Value:secondary' => 'İkincil',
-	'Class:NetworkInterface/Attribute:logical_type/Value:secondary+' => '',
-	'Class:NetworkInterface/Attribute:physical_type' => 'Fiziksel tip',
-	'Class:NetworkInterface/Attribute:physical_type+' => '',
-	'Class:NetworkInterface/Attribute:physical_type/Value:atm' => 'ATM',
-	'Class:NetworkInterface/Attribute:physical_type/Value:atm+' => '',
-	'Class:NetworkInterface/Attribute:physical_type/Value:ethernet' => 'Ethernet',
-	'Class:NetworkInterface/Attribute:physical_type/Value:ethernet+' => '',
-	'Class:NetworkInterface/Attribute:physical_type/Value:framerelay' => 'Frame Relay',
-	'Class:NetworkInterface/Attribute:physical_type/Value:framerelay+' => '',
-	'Class:NetworkInterface/Attribute:physical_type/Value:vlan' => 'VLAN',
-	'Class:NetworkInterface/Attribute:physical_type/Value:vlan+' => '',
-	'Class:NetworkInterface/Attribute:ip_address' => 'IP Adresi',
-	'Class:NetworkInterface/Attribute:ip_address+' => '',
-	'Class:NetworkInterface/Attribute:ip_mask' => 'IP maskesi',
-	'Class:NetworkInterface/Attribute:ip_mask+' => '',
-	'Class:NetworkInterface/Attribute:mac_address' => 'MAC Adresi',
-	'Class:NetworkInterface/Attribute:mac_address+' => '',
-	'Class:NetworkInterface/Attribute:speed' => 'Hızı',
-	'Class:NetworkInterface/Attribute:speed+' => '',
-	'Class:NetworkInterface/Attribute:duplex' => 'Çift yönlü',
-	'Class:NetworkInterface/Attribute:duplex+' => '',
-	'Class:NetworkInterface/Attribute:duplex/Value:auto' => 'Otomatik',
-	'Class:NetworkInterface/Attribute:duplex/Value:auto+' => 'Otomatik',
-	'Class:NetworkInterface/Attribute:duplex/Value:full' => 'Full',
-	'Class:NetworkInterface/Attribute:duplex/Value:full+' => '',
-	'Class:NetworkInterface/Attribute:duplex/Value:half' => 'Half',
-	'Class:NetworkInterface/Attribute:duplex/Value:half+' => '',
-	'Class:NetworkInterface/Attribute:duplex/Value:unknown' => 'Unknown',
-	'Class:NetworkInterface/Attribute:duplex/Value:unknown+' => '',
-	'Class:NetworkInterface/Attribute:connected_if' => 'Bağlantıda',
-	'Class:NetworkInterface/Attribute:connected_if+' => 'Bağlantıdaki arayüz',
-	'Class:NetworkInterface/Attribute:connected_name' => 'Bağlantıda',
-	'Class:NetworkInterface/Attribute:connected_name+' => '',
-	'Class:NetworkInterface/Attribute:connected_if_device_id' => 'Bağlı cihazlar',
-	'Class:NetworkInterface/Attribute:connected_if_device_id+' => '',
-	'Class:NetworkInterface/Attribute:connected_if_device_id_name' => 'Cihaz',
-	'Class:NetworkInterface/Attribute:connected_if_device_id_name+' => '',
-	'Class:NetworkInterface/Attribute:link_type' => 'Hat tipi',
-	'Class:NetworkInterface/Attribute:link_type+' => '',
-	'Class:NetworkInterface/Attribute:link_type/Value:downlink' => 'Hat kapalı',
-	'Class:NetworkInterface/Attribute:link_type/Value:downlink+' => '',
-	'Class:NetworkInterface/Attribute:link_type/Value:uplink' => 'Hat açık',
-	'Class:NetworkInterface/Attribute:link_type/Value:uplink+' => '',
-));
-
-
+Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
+	'Class:NetworkInterface' => 'Network arayüzü',
+	'Class:NetworkInterface+' => '',
+	'Class:NetworkInterface/Name' => '%1$s - %2$s',
+	'Class:NetworkInterface/Attribute:device_id' => 'Cihaz',
+	'Class:NetworkInterface/Attribute:device_id+' => '',
+	'Class:NetworkInterface/Attribute:device_name' => 'Cihaz',
+	'Class:NetworkInterface/Attribute:device_name+' => '',
+	'Class:NetworkInterface/Attribute:logical_type' => 'Mantıksal tipi',
+	'Class:NetworkInterface/Attribute:logical_type+' => '',
+	'Class:NetworkInterface/Attribute:logical_type/Value:backup' => 'Yedek',
+	'Class:NetworkInterface/Attribute:logical_type/Value:backup+' => '',
+	'Class:NetworkInterface/Attribute:logical_type/Value:logical' => 'Mantıksal',
+	'Class:NetworkInterface/Attribute:logical_type/Value:logical+' => '',
+	'Class:NetworkInterface/Attribute:logical_type/Value:port' => 'Port',
+	'Class:NetworkInterface/Attribute:logical_type/Value:port+' => '',
+	'Class:NetworkInterface/Attribute:logical_type/Value:primary' => 'Birincil',
+	'Class:NetworkInterface/Attribute:logical_type/Value:primary+' => '',
+	'Class:NetworkInterface/Attribute:logical_type/Value:secondary' => 'İkincil',
+	'Class:NetworkInterface/Attribute:logical_type/Value:secondary+' => '',
+	'Class:NetworkInterface/Attribute:physical_type' => 'Fiziksel tip',
+	'Class:NetworkInterface/Attribute:physical_type+' => '',
+	'Class:NetworkInterface/Attribute:physical_type/Value:atm' => 'ATM',
+	'Class:NetworkInterface/Attribute:physical_type/Value:atm+' => '',
+	'Class:NetworkInterface/Attribute:physical_type/Value:ethernet' => 'Ethernet',
+	'Class:NetworkInterface/Attribute:physical_type/Value:ethernet+' => '',
+	'Class:NetworkInterface/Attribute:physical_type/Value:framerelay' => 'Frame Relay',
+	'Class:NetworkInterface/Attribute:physical_type/Value:framerelay+' => '',
+	'Class:NetworkInterface/Attribute:physical_type/Value:vlan' => 'VLAN',
+	'Class:NetworkInterface/Attribute:physical_type/Value:vlan+' => '',
+	'Class:NetworkInterface/Attribute:ip_address' => 'IP Adresi',
+	'Class:NetworkInterface/Attribute:ip_address+' => '',
+	'Class:NetworkInterface/Attribute:ip_mask' => 'IP maskesi',
+	'Class:NetworkInterface/Attribute:ip_mask+' => '',
+	'Class:NetworkInterface/Attribute:mac_address' => 'MAC Adresi',
+	'Class:NetworkInterface/Attribute:mac_address+' => '',
+	'Class:NetworkInterface/Attribute:speed' => 'Hızı',
+	'Class:NetworkInterface/Attribute:speed+' => '',
+	'Class:NetworkInterface/Attribute:duplex' => 'Çift yönlü',
+	'Class:NetworkInterface/Attribute:duplex+' => '',
+	'Class:NetworkInterface/Attribute:duplex/Value:auto' => 'Otomatik',
+	'Class:NetworkInterface/Attribute:duplex/Value:auto+' => 'Otomatik',
+	'Class:NetworkInterface/Attribute:duplex/Value:full' => 'Full',
+	'Class:NetworkInterface/Attribute:duplex/Value:full+' => '',
+	'Class:NetworkInterface/Attribute:duplex/Value:half' => 'Half',
+	'Class:NetworkInterface/Attribute:duplex/Value:half+' => '',
+	'Class:NetworkInterface/Attribute:duplex/Value:unknown' => 'Unknown',
+	'Class:NetworkInterface/Attribute:duplex/Value:unknown+' => '',
+	'Class:NetworkInterface/Attribute:connected_if' => 'Bağlantıda',
+	'Class:NetworkInterface/Attribute:connected_if+' => 'Bağlantıdaki arayüz',
+	'Class:NetworkInterface/Attribute:connected_name' => 'Bağlantıda',
+	'Class:NetworkInterface/Attribute:connected_name+' => '',
+	'Class:NetworkInterface/Attribute:connected_if_device_id' => 'Bağlı cihazlar',
+	'Class:NetworkInterface/Attribute:connected_if_device_id+' => '',
+	'Class:NetworkInterface/Attribute:connected_if_device_id_name' => 'Cihaz',
+	'Class:NetworkInterface/Attribute:connected_if_device_id_name+' => '',
+	'Class:NetworkInterface/Attribute:link_type' => 'Hat tipi',
+	'Class:NetworkInterface/Attribute:link_type+' => '',
+	'Class:NetworkInterface/Attribute:link_type/Value:downlink' => 'Hat kapalı',
+	'Class:NetworkInterface/Attribute:link_type/Value:downlink+' => '',
+	'Class:NetworkInterface/Attribute:link_type/Value:uplink' => 'Hat açık',
+	'Class:NetworkInterface/Attribute:link_type/Value:uplink+' => '',
+));
+
+
 
 //
 // Class: Device
@@ -1031,22 +1037,22 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
 'Menu:MobilePhone+' => 'Tüm Cep Telefonları',
 'Menu:PC' => 'Kişisel Bilgisayarlar',
 'Menu:PC+' => 'Tüm Kişisel Bilgisayarlar',
-'Menu:NewContact' => 'Yeni İrtibat',
-'Menu:NewContact+' => 'Yeni İrtibat',
-'Menu:SearchContacts' => 'İrtibat ara',
-'Menu:SearchContacts+' => 'İrtibat ara',
-'Menu:NewCI' => 'Yeni KK',
-'Menu:NewCI+' => 'Yeni KK',
-'Menu:SearchCIs' => 'KK ara',
+'Menu:NewContact' => 'Yeni İrtibat',
+'Menu:NewContact+' => 'Yeni İrtibat',
+'Menu:SearchContacts' => 'İrtibat ara',
+'Menu:SearchContacts+' => 'İrtibat ara',
+'Menu:NewCI' => 'Yeni KK',
+'Menu:NewCI+' => 'Yeni KK',
+'Menu:SearchCIs' => 'KK ara',
 'Menu:SearchCIs+' => 'KK ara',
 'Menu:ConfigManagement:Devices' => 'Cihazlar',
 'Menu:ConfigManagement:AllDevices' => 'Cihaz sayısı: %1$d',
 'Menu:ConfigManagement:SWAndApps' => 'Yazılım ve uygulamalar',
-'Menu:ConfigManagement:Misc' => 'Diğer',
-'Menu:Group' => 'KK Grupları',
+'Menu:ConfigManagement:Misc' => 'Diğer',
+'Menu:Group' => 'KK Grupları',
 'Menu:Group+' => 'KK Grupları',
-'Menu:ConfigManagement:Shortcuts' => 'Kısalyollar',
+'Menu:ConfigManagement:Shortcuts' => 'Kısalyollar',
 'Menu:ConfigManagement:AllContacts' => 'Tüm irtibatlar: %1$d',
-
+
 ));
 ?>

+ 6 - 0
modules/itop-config-mgmt-1.0.0/zh.dict.itop-config-mgmt.php

@@ -393,6 +393,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 	'Class:Subnet' => '子网',
 	'Class:Subnet+' => '',
+	'Class:Subnet/Name' => '%1$s / %2$s',
 	//'Class:Subnet/Attribute:name' => 'Name',
 	//'Class:Subnet/Attribute:name+' => '',
 	'Class:Subnet/Attribute:org_id' => '拥有者组织',
@@ -546,6 +547,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 	'Class:SoftwareInstance' => '软件实例',
 	'Class:SoftwareInstance+' => '',
+	'Class:SoftwareInstance/Name' => '%1$s - %2$s',
 	'Class:SoftwareInstance/Attribute:device_id' => '设备',
 	'Class:SoftwareInstance/Attribute:device_id+' => '',
 	'Class:SoftwareInstance/Attribute:device_name' => '设备',
@@ -569,6 +571,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 	'Class:ApplicationInstance' => '应用实例',
 	'Class:ApplicationInstance+' => '',
+	'Class:ApplicationInstance/Name' => '%1$s - %2$s',
 	'Class:ApplicationInstance/Attribute:software_id' => '软件',
 	'Class:ApplicationInstance/Attribute:software_id+' => '',
 	'Class:ApplicationInstance/Attribute:software_name' => '名称',
@@ -583,6 +586,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 	'Class:DBServerInstance' => 'DB Server 实例',
 	'Class:DBServerInstance+' => '',
+	'Class:DBServerInstance/Name' => '%1$s - %2$s',
 	'Class:DBServerInstance/Attribute:software_id' => '软件',
 	'Class:DBServerInstance/Attribute:software_id+' => '',
 	'Class:DBServerInstance/Attribute:software_name' => '名称',
@@ -599,6 +603,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 	'Class:DatabaseInstance' => 'Database 实例',
 	'Class:DatabaseInstance+' => '',
+	'Class:DatabaseInstance/Name' => '%1$s - %2$s',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id' => '数据库服务器',
 	'Class:DatabaseInstance/Attribute:db_server_instance_id+' => '',
 	'Class:DatabaseInstance/Attribute:db_server_instance_version' => '数据库版本',
@@ -659,6 +664,7 @@ Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 Dict::Add('ZH CN', 'Chinese', '简体中文', array(
 	'Class:NetworkInterface' => '网络接口',
 	'Class:NetworkInterface+' => '',
+	'Class:NetworkInterface/Name' => '%1$s - %2$s',
 	'Class:NetworkInterface/Attribute:device_id' => '设备',
 	'Class:NetworkInterface/Attribute:device_id+' => '',
 	'Class:NetworkInterface/Attribute:device_name' => '设备',

+ 8 - 1
pages/UI.php

@@ -521,7 +521,14 @@ try
 				throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'id'));
 			}
 
-			$oObj = MetaModel::GetObject($sClass, $id, false /* MustBeFound */);
+			if (is_numeric($id))
+			{
+				$oObj = MetaModel::GetObject($sClass, $id, false /* MustBeFound */);
+			}
+			else
+			{
+				$oObj = MetaModel::GetObjectByName($sClass, $id, false /* MustBeFound */);
+			}
 			if (is_null($oObj))
 			{
 				$oP->set_title(Dict::S('UI:ErrorPageTitle'));

+ 0 - 24
pages/ajax.render.php

@@ -274,30 +274,6 @@ try
 		$oPage->Add("<input type=\"button\" class=\"jqmClose\" value=\" Close \" />\n");
 		break;
 			
-		case 'ui.linkswidget':
-		/*
-		$sClass = utils::ReadParam('sclass', 'bizContact');
-		$sAttCode = utils::ReadParam('attCode', 'name');
-		$sOrg = utils::ReadParam('org_id', '');
-		$sName = utils::ReadParam('q', '');
-		$iMaxCount = utils::ReadParam('max', 30);
-		UILinksWidget::Autocomplete($oPage, $sClass, $sAttCode, $sName, $iMaxCount);
-		*/
-		break;
-		
-		case 'ui.linkswidget.linkedset':
-		/*
-		$sClass = utils::ReadParam('sclass', 'bizContact');
-		$sJSONSet = stripslashes(utils::ReadParam('sset', ''));
-		$sExtKeyToMe = utils::ReadParam('sextkeytome', '');
-		$sExtKeyToRemote = utils::ReadParam('sextkeytoremote', '');
-		$iObjectId = utils::ReadParam('id', -1);
-		UILinksWidget::RenderSet($oPage, $sClass, $sJSONSet, $sExtKeyToMe, $sExtKeyToRemote, $iObjectId);
-		$iFieldId = utils::ReadParam('myid', '-1');
-		$oPage->add_ready_script("$('#{$iFieldId}').trigger('validate');");
-		*/
-		break;
-		
 		case 'autocomplete':
 		$key = utils::ReadParam('id', 0);
 		$sClass = utils::ReadParam('sclass', 'bizContact');

+ 113 - 4
test/testlist.inc.php

@@ -50,7 +50,6 @@ class TestSQLQuery extends TestScenarioOnDB
 			$sTable = 'myTable',
 			$sTableAlias = 'myTableAlias',
 			$aFields = array('column1'=>new FieldExpression('column1', 'myTableAlias'), 'column2'=>new FieldExpression('column2', 'myTableAlias')),
-			$oCondition = new BinaryExpression(new FieldExpression('column1', 'myTableAlias'), 'LIKE', new ScalarExpression('trash')),
 			$aFullTextNeedles = array('column1'),
 			$bToDelete = false,
 			$aValues = array()
@@ -61,7 +60,6 @@ class TestSQLQuery extends TestScenarioOnDB
 			$sTable = 'myTable1',
 			$sTableAlias = 'myTable1Alias',
 			$aFields = array('column1_1'=>new FieldExpression('column1', 'myTableAlias'), 'column1_2'=>new FieldExpression('column1', 'myTableAlias')),
-			$oCondition = new TrueSQLExpression,
 			$aFullTextNeedles = array(),
 			$bToDelete = false,
 			$aValues = array()
@@ -71,7 +69,6 @@ class TestSQLQuery extends TestScenarioOnDB
 			$sTable = 'myTable2',
 			$sTableAlias = 'myTable2Alias',
 			$aFields = array('column2_1'=>new FieldExpression('column2', 'myTableAlias'), 'column2_2'=>new FieldExpression('column2', 'myTableAlias')),
-			$oCondition = new TrueSQLExpression,
 			$aFullTextNeedles = array(),
 			$bToDelete = false,
 			$aValues = array()
@@ -1069,7 +1066,7 @@ class TestFullTextSearchOnFarm extends MyFarm
 
 
 ///////////////////////////////////////////////////////////////////////////
-// Benchmark queries
+// Test queries
 ///////////////////////////////////////////////////////////////////////////
 
 class TestItopEfficiency extends TestBizModel
@@ -1194,6 +1191,118 @@ class TestItopEfficiency extends TestBizModel
 }
 
 ///////////////////////////////////////////////////////////////////////////
+// Benchmark queries
+///////////////////////////////////////////////////////////////////////////
+
+class TestQueries extends TestBizModel
+{
+	static public function GetName()
+	{
+		return 'Itop - queries';
+	}
+
+	static public function GetDescription()
+	{
+		return 'Try as many queries as possible';
+	}
+
+	static public function GetConfigFile() {return '/config-itop.php';}
+
+	protected function DoBenchmark($sOqlQuery)
+	{
+		echo "<h5>Testing query: $sOqlQuery</h5>";
+
+		$fStart = MyHelpers::getmicrotime();
+		$oFilter = DBObjectSearch::FromOQL($sOqlQuery);
+		$fParsingDuration = MyHelpers::getmicrotime() - $fStart;
+
+		$fStart = MyHelpers::getmicrotime();
+		$sSQL = MetaModel::MakeSelectQuery($oFilter);
+		$fBuildDuration = MyHelpers::getmicrotime() - $fStart;
+
+		$fStart = MyHelpers::getmicrotime();
+		$res = CMDBSource::Query($sSQL);
+		$fQueryDuration = MyHelpers::getmicrotime() - $fStart;
+
+		// The fetch could not be repeated with the same results
+		// But we've seen so far that is was very very quick to exec
+		// So it makes sense to benchmark it a single time
+		$fStart = MyHelpers::getmicrotime();
+		$aRow = CMDBSource::FetchArray($res);
+		$fDuration = MyHelpers::getmicrotime() - $fStart;
+		$fFetchDuration = $fDuration;
+
+		$fStart = MyHelpers::getmicrotime();
+		$sOql = $oFilter->ToOQL();
+		$fToOqlDuration = MyHelpers::getmicrotime() - $fStart;
+
+      if (false)
+      {
+		echo "<ul style=\"font-size:smaller;\">\n";
+		echo "<li>Parsing: $fParsingDuration</li>\n";
+		echo "<li>Build: $fBuildDuration</li>\n";
+		echo "<li>Query: $fQueryDuration</li>\n";
+		echo "<li>Fetch: $fFetchDuration</li>\n";
+		echo "<li>ToOql: $fToOqlDuration</li>\n";
+		echo "</ul>\n";
+		}
+
+		// Everything but the ToOQL (wich is interesting, anyhow)
+		$fTotal = $fParsingDuration + $fBuildDuration + $fQueryDuration + $fFetchDuration; 
+
+		return array(
+			'rows' => CMDBSource::NbRows($res),
+			'duration (s)' => round($fTotal, 4),
+			'parsing (%)' => round(100 * $fParsingDuration / $fTotal, 1),
+			'build SQL (%)' => round(100 * $fBuildDuration / $fTotal, 1),
+			'query exec (%)' => round(100 * $fQueryDuration / $fTotal, 1),
+			'fetch (%)' => round(100 * $fFetchDuration / $fTotal, 1),
+			'to OQL (%)' => round(100 * $fToOqlDuration / $fTotal, 1),
+			'parsing+build (%)' => round(100 * ($fParsingDuration + $fBuildDuration) / $fTotal, 1),
+		);
+	}
+	
+	protected function DoExecute()
+	{
+		$aQueries = array(
+			'SELECT Person AS PP WHERE PP.friendlyname LIKE "%dali"',
+			'SELECT Person AS PP WHERE PP.location_id_friendlyname LIKE "%ce ch%"',
+			'SELECT Organization AS OO JOIN Person AS PP ON PP.org_id = OO.id',
+			'SELECT lnkTeamToContact AS lnk JOIN Team AS T ON lnk.team_id = T.id',
+			'SELECT lnkTeamToContact AS lnk JOIN Team AS T ON lnk.team_id = T.id JOIN Contact AS C ON lnk.contact_id = C.id',
+			'SELECT Incident JOIN Person ON Incident.agent_id = Person.id WHERE Person.id = 5',
+			// this one is failing...
+			//'SELECT L, P FROM Person AS P JOIN Location AS L ON P.location_id = L.id',
+		);
+		foreach (MetaModel::GetClasses() as $sClass)
+		{
+			$aQueries[] = 'SELECT '.$sClass;
+			$aQueries[] = 'SELECT '.$sClass.' AS zz';
+			$aQueries[] = 'SELECT '.$sClass.' AS zz WHERE id = 1';
+		}	
+		$aStats  = array();
+		foreach ($aQueries as $sOQL)
+		{
+			$aStats[$sOQL] = $this->DoBenchmark($sOQL);
+		}
+
+		$aData = array();
+		foreach ($aStats as $sOQL => $aResults)
+		{
+			$aValues = array();
+			$aValues['OQL'] = htmlentities($sOQL);
+
+			foreach($aResults as $sDesc => $sInfo)
+			{
+				$aValues[$sDesc] = htmlentities($sInfo);
+			}
+			$aData[] = $aValues;
+		}
+		echo MyHelpers::make_table_from_assoc_array($aData);
+	}
+}
+
+///////////////////////////////////////////////////////////////////////////
 // Test bulk load API
 ///////////////////////////////////////////////////////////////////////////