#149 Implemented friendly names

SVN:trunk[1057]
This commit is contained in:
Romain Quetiez
2011-01-26 09:43:35 +00:00
parent cb0610bf0b
commit 1a52f4de72
20 changed files with 1244 additions and 795 deletions

View File

@@ -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
{

View File

@@ -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) )
@@ -239,93 +239,6 @@ EOF
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);

View File

@@ -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);
}
}
?>

View File

@@ -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,16 +499,24 @@ abstract class DBObject
public function GetName()
{
$sNameAttCode = MetaModel::GetNameAttributeCode(get_class($this));
if (empty($sNameAttCode))
$aNameSpec = MetaModel::GetNameSpec(get_class($this));
$sFormat = $aNameSpec[0];
$aAttributes = $aNameSpec[1];
$aValues = array();
foreach ($aAttributes as $sAttCode)
{
return $this->m_iKey;
if (empty($sAttCode))
{
$aValues[] = $this->m_iKey;
}
else
{
return $this->Get($sNameAttCode);
$aValues[] = $this->Get($sAttCode);
}
}
return vsprintf($sFormat, $aValues);
}
public function GetState()
{

View File

@@ -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()));

View File

@@ -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);
}
public function ListRequiredFields()
{
return array($this->m_sParent.'.'.$this->m_sName);
return $oRet;
}
}
// Has been resolved into an SQL expression
class FieldExpressionResolved extends FieldExpression
{
public function GetUnresolvedFields($sAlias, &$aUnresolved)
{
}
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);
}
}
}
?>

View File

@@ -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"];
$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....
if ($oAttDef->IsExternalKey())
{
$sRemoteClass = $oAttDef->GetTargetClass();
if ($oAttDef->IsExternalField())
{
// 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,13 +1950,39 @@ abstract class MetaModel
$sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode];
$aExtKeys[$sKeyTableClass][$sKeyAttCode] = array();
}
if (array_key_exists('friendlyname', $aExpectedAtts))
{
$aTranslateNow = array();
$aTranslateNow[$sClassAlias]['friendlyname'] = self::GetNameExpression($sClass, $sClassAlias);
$oQBExpr->Translate($aTranslateNow, false);
$aNameSpec = self::GetNameSpec($sClass);
foreach($aNameSpec[1] as $i => $sAttCode)
{
$oAttDef = self::GetAttributeDef($sClass, $sAttCode);
if ($oAttDef->IsExternalKey())
{
$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())
if ($oAttDef->IsExternalField() || ($oAttDef instanceof AttributeFriendlyName))
{
if (array_key_exists($sAttCode, $aExpectedAtts))
{
$sKeyAttCode = $oAttDef->GetKeyAttCode();
if (array_key_exists($sAttCode, $aExpectedAtts) || $oConditionTree->RequiresField($sClassAlias, $sAttCode))
if ($sKeyAttCode != 'id')
{
// Add the external attribute
$sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode];
@@ -1850,13 +1990,14 @@ abstract class MetaModel
}
}
}
}
// 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,29 +2109,28 @@ 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
@@ -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);
}
else
{
$sExtAttCode = $oAtt->GetExtAttCode();
// Translate mainclass.extfield => remoteclassalias.remotefieldcode
$oRemoteAttDef = self::GetAttributeDef($sKeyClass, $sExtAttCode);
foreach ($oRemoteAttDef->GetSQLExpressions() as $sColID => $sRemoteAttExpr)
{
$aIntermediateTranslation[$sTargetAlias.$sColID][$sAttCode] = array($sKeyClassAlias, $sRemoteAttExpr);
$aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias);
}
//#@# 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))
if(!self::IsValidAttCode($sClass, $sAttCode))
{
$aErrors[$sClass][] = "Unkown attribute code '".$sNameAttCode."' for the name definition";
$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);

View File

@@ -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,15 +120,27 @@ class SQLQuery
echo "</pre>";
}
public function SetSelect($aExpressions)
{
$this->m_aFields = $aExpressions;
}
public function SetCondition($oConditionExpr)
{
$this->m_oConditionExpr = $oConditionExpr;
}
public function AddCondition($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,9 +202,16 @@ class SQLQuery
throw new CoreException("Building a request wich will delete every object of a given table -looks suspicious- please use truncate instead...");
}
*/
if (is_null($oCondition))
{
// Delete all !!!
}
else
{
$sWhere = self::ClauseWhere($oCondition, $aArgs);
return "DELETE $sDelete FROM $sFrom WHERE $sWhere";
}
}
// Interface, build the SQL query
public function RenderUpdate($aArgs = array())
@@ -337,9 +330,16 @@ class SQLQuery
}
private static function ClauseWhere($oConditionExpr, $aArgs = array())
{
if (is_null($oConditionExpr))
{
return '1';
}
else
{
return $oConditionExpr->Render($aArgs);
}
}
private static function ClauseOrderBy($aOrderBy)
{
@@ -365,26 +365,25 @@ 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
$sFields = implode(', ', $aFields);
$oFullTextExpr = Expression::FromSQL("CONCAT_WS(' ', $sFields)");
// $sField is already a string `table`.`column`
// Let's make an expression out of it (again !)
$aFieldExp[] = Expression::FromOQL($sField);
}
$oFullTextExpr = new CharConcatExpression($aFieldExp);
// 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%"));
if (is_null($oCondition))
{
$oCondition = $oNewCond;
}
else
{
$oCondition = $oCondition->LogAnd($oNewCond);
}
}
}
return $sTableAlias;
}
@@ -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)

View File

@@ -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',

View File

@@ -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',
@@ -569,6 +571,7 @@ Dict::Add('EN US', 'English', 'English', array(
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',
@@ -583,6 +586,7 @@ Dict::Add('EN US', 'English', 'English', array(
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',
@@ -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',
@@ -659,6 +664,7 @@ Dict::Add('EN US', 'English', 'English', array(
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',

View File

@@ -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',

View File

@@ -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',

View File

@@ -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)

View File

@@ -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',

View File

@@ -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' => 'Устройство',

View File

@@ -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',
@@ -567,6 +569,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
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ı',
@@ -581,6 +584,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
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ı',
@@ -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',
@@ -657,6 +662,7 @@ Dict::Add('TR TR', 'Turkish', 'Türkçe', array(
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',

View File

@@ -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' => '设备',

View File

@@ -521,7 +521,14 @@ try
throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'id'));
}
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'));

View File

@@ -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');

View File

@@ -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
@@ -1193,6 +1190,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
///////////////////////////////////////////////////////////////////////////