diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index 69b70fff7..e2d6b938a 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -780,8 +780,6 @@ exit; // All of them are visible return true; } - $oExpression = new FieldExpression($sAttCode, $sClass); - // Position the user // @$aUserOrgs = $this->m_aUserOrgs[$oUser->GetKey()]; @@ -791,12 +789,52 @@ exit; return true; } + $oExpression = new FieldExpression($sAttCode, $sClass); + $oFilter = new DBObjectSearch($sClass); $aIds = array_keys($aUserOrgs); $oListExpr = ListExpression::FromScalars($aIds); - $oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); - - $oFilter = new DBObjectSearch($sClass); - $oFilter->AddConditionExpression($oCondition); + + // Check if the condition points to a hierarchical key + if ($sAttCode == 'id') + { + // Filtering on the objects themselves + $sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($sClass); + + if ($sHierarchicalKeyCode !== false) + { + $oRootFilter = new DBObjectSearch($sClass); + $oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); + $oRootFilter->AddConditionExpression($oCondition); + $oFilter->AddCondition_PointingTo($oRootFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW); // Use the 'below' operator by default + $bConditionAdded = true; + } + } + else + { + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + $bConditionAdded = false; + if ($oAttDef->IsExternalKey()) + { + $sHierarchicalKeyCode = MetaModel::IsHierarchicalClass($oAttDef->GetTargetClass()); + + if ($sHierarchicalKeyCode !== false) + { + $oRootFilter = new DBObjectSearch($oAttDef->GetTargetClass()); + $oExpression = new FieldExpression('id', $oAttDef->GetTargetClass()); + $oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); + $oRootFilter->AddConditionExpression($oCondition); + $oHKFilter = new DBObjectSearch($oAttDef->GetTargetClass()); + $oHKFilter->AddCondition_PointingTo($oRootFilter, $sHierarchicalKeyCode, TREE_OPERATOR_BELOW); // Use the 'below' operator by default + $oFilter->AddCondition_PointingTo($oHKFilter, $sAttCode); + $bConditionAdded = true; + } + } + } + if (!$bConditionAdded) + { + $oCondition = new BinaryExpression($oExpression, 'IN', $oListExpr); + $oFilter->AddConditionExpression($oCondition); + } return $oFilter; } diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 459c1b164..99d0c9411 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -2431,9 +2431,10 @@ class AttributeHierarchicalKey extends AttributeExternalKey static protected function ListExpectedParams() { $aParams = parent::ListExpectedParams(); - //unset($aParams[array_search('targetclass', $aParams)]); - - //print_r($aParams); + $idx = array_search('targetclass', $aParams); + unset($aParams[$idx]); + $idx = array_search('jointype', $aParams); + unset($aParams[$idx]); return $aParams; // TODO: mettre les bons parametres ici !! } @@ -2444,9 +2445,9 @@ class AttributeHierarchicalKey extends AttributeExternalKey } public function IsHierarchicalKey() {return true;} + public function GetTargetClass($iType = EXTKEY_RELATIVE) {return $this->GetHostClass();} public function GetKeyAttDef($iType = EXTKEY_RELATIVE){return $this;} - public function GetKeyAttCode() {return $this->GetCode();} - + public function GetKeyAttCode() {return $this->GetCode();} public function GetBasicFilterOperators() { @@ -2637,8 +2638,8 @@ class AttributeExternalField extends AttributeDefinition public function GetExtAttDef() { $oKeyAttDef = $this->GetKeyAttDef(); - $oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->Get("targetclass"), $this->Get("target_attcode")); - if (!is_object($oExtAttDef)) throw new CoreException("Invalid external field ".$this->GetCode()." in class ".$this->GetHostClass().". The class ".$oKeyAttDef->Get("targetclass")." has no attribute ".$this->Get("target_attcode")); + $oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->GetTargetClass(), $this->Get("target_attcode")); + if (!is_object($oExtAttDef)) throw new CoreException("Invalid external field ".$this->GetCode()." in class ".$this->GetHostClass().". The class ".$oKeyAttDef->GetTargetClass()." has no attribute ".$this->Get("target_attcode")); return $oExtAttDef; } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 87c4a7971..336883f58 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -1487,7 +1487,10 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) 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"); + $sTargetClass = self::GetCallersPHPClass("Init"); + // Set the "host class" as soon as possible, since HierarchicalKeys use it for their 'target class' as well + // and this needs to be know early (for Init_IsKnowClass 19 lines below) + $oAtt->SetHostClass($sTargetClass); // Some attributes could refer to a class // declared in a module which is currently not installed/active @@ -1527,12 +1530,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) self::$m_aAttribDefs[$sTargetClass][$oAtt->GetCode()] = $oAtt; self::$m_aAttribOrigins[$sTargetClass][$oAtt->GetCode()] = $sTargetClass; - // Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used - - // Specific case of external fields: - // I wanted to simplify the syntax of the declaration of objects in the biz model - // Therefore, the reference to the host class is set there - $oAtt->SetHostClass($sTargetClass); + // Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used } public static function Init_SetZListItems($sListCode, $aItems) diff --git a/dictionaries/dictionary.itop.ui.php b/dictionaries/dictionary.itop.ui.php index 6a70fa326..7cb24e742 100644 --- a/dictionaries/dictionary.itop.ui.php +++ b/dictionaries/dictionary.itop.ui.php @@ -920,5 +920,6 @@ When associated with a trigger, each action is given an "order" number, specifyi 'UI:Pagination:PageSize' => '%1$s objects per page', 'UI:Pagination:PagesLabel' => 'Pages:', 'UI:Pagination:All' => 'All', + 'UI:HierarchyOf_Class' => 'Hierarchy of %1$s', )); ?> diff --git a/dictionaries/fr.dictionary.itop.ui.php b/dictionaries/fr.dictionary.itop.ui.php index 277a182c8..cfb8fa4f0 100644 --- a/dictionaries/fr.dictionary.itop.ui.php +++ b/dictionaries/fr.dictionary.itop.ui.php @@ -770,5 +770,6 @@ Lors de l\'association à un déclencheur, on attribue à chaque action un numé 'UI:Pagination:PageSize' => '%1$s éléments par page', 'UI:Pagination:PagesLabel' => 'Pages:', 'UI:Pagination:All' => 'Tous', + 'UI:HierarchyOf_Class' => 'Hiérarchie de type %1$s', )); ?> diff --git a/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php b/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php index 86319b3c1..57a238c7d 100644 --- a/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php +++ b/modules/itop-config-mgmt-1.0.0/model.itop-config-mgmt.php @@ -50,7 +50,7 @@ class Organization extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("code", array("allowed_values"=>null, "sql"=>"code", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('active,inactive'), "sql"=>"status", "default_value"=>"active", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("parent_id", array("targetclass"=>"Organization", "jointype"=>null, "allowed_values"=>null, "sql"=>"parent_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeHierarchicalKey("parent_id", array("allowed_values"=>null, "sql"=>"parent_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("parent_name", array("allowed_values"=>null, "extkey_attcode"=>"parent_id", "target_attcode"=>"name", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_SetZListItems('details', array('name', 'code', 'status', 'parent_id')); @@ -87,7 +87,7 @@ class Location extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeString("postal_code", array("allowed_values"=>null, "sql"=>"postal_code", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("city", array("allowed_values"=>null, "sql"=>"city", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("country", array("allowed_values"=>null, "sql"=>"country", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("parent_id", array("targetclass"=>"Location", "jointype"=>null, "allowed_values"=>new ValueSetObjects('SELECT Location AS L WHERE L.org_id = :this->org_id'), "sql"=>"parent_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); + MetaModel::Init_AddAttribute(new AttributeHierarchicalKey("parent_id", array("allowed_values"=>new ValueSetObjects('SELECT Location AS L WHERE L.org_id = :this->org_id'), "sql"=>"parent_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("parent_name", array("allowed_values"=>null, "extkey_attcode"=>"parent_id", "target_attcode"=>"name", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeLinkedSet("contact_list", array("linked_class"=>"Contact", "ext_key_to_me"=>"location_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array()))); diff --git a/pages/run_query.php b/pages/run_query.php index 1769ea15e..ef6a64c67 100644 --- a/pages/run_query.php +++ b/pages/run_query.php @@ -42,7 +42,8 @@ function ShowExamples($oP, $sExpression) "Person having an 'A' in their name" => "SELECT Person AS B WHERE B.name LIKE '%A%'", "Servers having a name like dbserver1.demo.com or dbserver023.foo.fr" => "SELECT Server WHERE name REGEXP '^dbserver[0-9]+\\\\..+\\\\.[a-z]{2,3}$'", "Changes planned on new year's day" => "SELECT Change AS ch WHERE ch.start_date >= '2009-12-31' AND ch.end_date <= '2010-01-01'", - "IPs in a range" => "SELECT InfrastructureCI AS dev WHERE INET_ATON(dev.management_ip) > INET_ATON('10.22.32.224') AND INET_ATON(dev.management_ip) < INET_ATON('10.22.32.255')" + "IPs in a range" => "SELECT InfrastructureCI AS dev WHERE INET_ATON(dev.management_ip) > INET_ATON('10.22.32.224') AND INET_ATON(dev.management_ip) < INET_ATON('10.22.32.255')", + "Persons below a given root organization" => "SELECT Person AS P JOIN Organization AS Node ON P.org_id = Node.id JOIN Organization AS Root ON Node.parent_id BELOW Root.id WHERE Root.id=1", ), 'Usefull examples' => array( "NW interfaces of equipment in production for customer 'Demo'" => "SELECT NetworkInterface AS if JOIN InfrastructureCI AS dev ON if.device_id = dev.id WHERE if.status = 'production' AND dev.status = 'production' AND dev.owner_name = 'Demo' AND if.physical_type = 'ethernet'",