diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index eac184e4d..0e551c230 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -39,6 +39,7 @@ class DBObjectSearch private $m_aPointingTo; private $m_aReferencedBy; private $m_aRelatedTo; + private $m_bDataFiltered; // By default, some information may be hidden to the current user // But it may happen that we need to disable that feature @@ -59,10 +60,13 @@ class DBObjectSearch $this->m_aPointingTo = array(); $this->m_aReferencedBy = array(); $this->m_aRelatedTo = array(); + $this->m_bDataFiltered = false; } public function AllowAllData() {$this->m_bAllowAllData = true;} public function IsAllDataAllowed() {return $this->m_bAllowAllData;} + public function IsDataFiltered() {return $this->m_bDataFiltered; } + public function SetDataFiltered() {$this->m_bDataFiltered = true;} public function GetClassName($sAlias) {return $this->m_aClasses[$sAlias];} public function GetJoinedClasses() {return $this->m_aClasses;} @@ -247,10 +251,11 @@ class DBObjectSearch protected function TransferConditionExpression($oFilter, $aTranslation) { - $oTranslated = $oFilter->GetCriteria()->Translate($aTranslation, false, false /* leave unresolved fields */); //echo "

TransferConditionExpression:
"; -//echo "Adding Conditions:

".print_r($oTranslated, true)."
\n"; +//echo "Adding Conditions:
oFilter:\n".print_r($oFilter, true)."\naTranslation:\n".print_r($aTranslation, true)."
\n"; //echo "

"; + $oTranslated = $oFilter->GetCriteria()->Translate($aTranslation, false, false /* leave unresolved fields */); +//echo "Adding Conditions (translated):
".print_r($oTranslated, true)."
\n"; $this->AddConditionExpression($oTranslated); // #@# what about collisions in parameter names ??? $this->m_aParams = array_merge($this->m_aParams, $oFilter->m_aParams); @@ -441,32 +446,38 @@ class DBObjectSearch ); } - protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation) + protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation, $bTranslateMainAlias = true) { - $sOrigAlias = $this->GetClassAlias(); - if (array_key_exists($sOrigAlias, $aClassAliases)) + if ($bTranslateMainAlias) { - $sNewAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $this->GetClass()); -//echo "

Generating a new alias for $sOrigAlias (already used). It is now: $sNewAlias

\n"; - $this->m_aSelectedClasses[$sNewAlias] = $this->GetClass(); - unset($this->m_aSelectedClasses[$sOrigAlias]); - - $this->m_aClasses[$sNewAlias] = $this->GetClass(); - unset($this->m_aClasses[$sOrigAlias]); - - // Translate the condition expression with the new alias - $aAliasTranslation[$sOrigAlias]['*'] = $sNewAlias; + $sOrigAlias = $this->GetClassAlias(); + if (array_key_exists($sOrigAlias, $aClassAliases)) + { + $sNewAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $this->GetClass()); +//echo "

Generating a new alias for $sOrigAlias (already used). It is now: $sNewAlias

\n"; + $this->m_aSelectedClasses[$sNewAlias] = $this->GetClass(); + unset($this->m_aSelectedClasses[$sOrigAlias]); + + $this->m_aClasses[$sNewAlias] = $this->GetClass(); + unset($this->m_aClasses[$sOrigAlias]); + + // Translate the condition expression with the new alias + $aAliasTranslation[$sOrigAlias]['*'] = $sNewAlias; + } + +//echo "

Adding the alias ".$this->GetClass()." as ".$this->GetClassAlias()."

\n"; + // add the alias into the filter aliases list + $aClassAliases[$this->GetClassAlias()] = $this->GetClass(); } - -//echo "

Adding the alias for ".$this->GetClass().". as ".$this->GetClassAlias()."

\n"; - // add the alias into the filter aliases list - $aClassAliases[$this->GetClassAlias()] = $this->GetClass(); foreach($this->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo) { - foreach($aPointingTo as $iOperatorCode => $oFilter) + foreach($aPointingTo as $iOperatorCode => $aFilter) { - $oFilter->AddToNameSpace($aClassAliases, $aAliasTranslation); + foreach($aFilter as $sAlias => $oFilter) + { + $oFilter->AddToNameSpace($aClassAliases, $aAliasTranslation); + } } } @@ -489,7 +500,7 @@ class DBObjectSearch protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation, $iOperatorCode) { -//echo "

Calling: AddCondition_PointingTo_InNameSpace([".implode(',', $aClassAliases)."], [".implode(',', $aAliasTranslation)."]);

"; +//echo "

Calling: AddCondition_PointingTo_InNameSpace([

".print_r($aClassAliases, true)."

], [
".print_r($aAliasTranslation, true)."
]);

"; if (!MetaModel::IsValidKeyAttCode($this->GetClass(), $sExtKeyAttCode)) { throw new CoreWarning("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}' - the condition will be ignored"); @@ -504,31 +515,36 @@ class DBObjectSearch throw new CoreException("The specified tree operator $isOperatorCode is not applicable to the key '{$this->GetClass()}::$sExtKeyAttCode', which is not a HierarchicalKey"); } + $bSamePointingTo = false; if (array_key_exists($sExtKeyAttCode, $this->m_aPointingTo)) { if (array_key_exists($iOperatorCode, $this->m_aPointingTo[$sExtKeyAttCode])) { - // Same ext key and same operator, merge the filters together - $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation); - } - else - { -//echo "

Calling: AddToNameSpace([".implode(',', $aClassAliases)."], [".implode(',', $aAliasTranslation)."]);

"; - $oFilter->AddToNamespace($aClassAliases, $aAliasTranslation); - - $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode] = $oFilter; + if (array_key_exists($oFilter->GetClassAlias(), $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode])) + { +//echo "

[".__LINE__."]this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode][".$oFilter->GetFirstJoinedClassAlias()."]:

\n".print_r($this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode], true)."
;

"; + $bSamePointingTo = true; + } } } + +//echo "

[".__LINE__."]Calling: AddToNameSpace([".implode(',', $aClassAliases)."], [".implode(',', $aAliasTranslation)."]);

"; + if ($bSamePointingTo) + { +//echo "

[".__LINE__."]AddPointingTo: Merging filters for [$sExtKeyAttCode][$iOperatorCode][".$oFilter->GetClassAlias()."]

"; + // Same ext key, alias and same operator, merge the filters together +// $sAlias = $oFilter->GetClassAlias(); +//echo "

[".__LINE__."]before: AddToNameSpace(aClassAliases[

\n".print_r($aClassAliases, true)."
], aAliasTranslation[
\n".print_r($aAliasTranslation, true)."
]);

"; + $oFilter->AddToNamespace($aClassAliases, $aAliasTranslation, true /* Don't translate the main alias */); +//echo "

[".__LINE__."]after: AddToNameSpace(aClassAliases[

\n".print_r($aClassAliases, true)."
], aAliasTranslation[
\n".print_r($aAliasTranslation, true)."
]);

"; +// $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode][$sAlias]->MergeWith($oFilter, $aClassAliases, $aAliasTranslation); + $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode][$oFilter->GetClassAlias()] = $oFilter; + } else { -//echo "

Calling: AddToNameSpace([".implode(',', $aClassAliases)."], [".implode(',', $aAliasTranslation)."]);

"; +//echo "

[".__LINE__."]AddPointingTo: Adding a new PointingTo filter for [$sExtKeyAttCode][$iOperatorCode][".$oFilter->GetClassAlias()."]

"; $oFilter->AddToNamespace($aClassAliases, $aAliasTranslation); - - // #@# The condition expression found in that filter should not be used - could be another kind of structure like a join spec tree !!!! - // $oNewFilter = clone $oFilter; - // $oNewFilter->ResetCondition(); - - $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode] = $oFilter; + $this->m_aPointingTo[$sExtKeyAttCode][$iOperatorCode][$oFilter->GetClassAlias()] = $oFilter; } } @@ -607,9 +623,12 @@ class DBObjectSearch foreach($oFilter->m_aPointingTo as $sExtKeyAttCode=>$aPointingTo) { - foreach($aPointingTo as $iOperatorCode => $oExtFilter) + foreach($aPointingTo as $iOperatorCode => $aFilter) { - $this->AddCondition_PointingTo_InNamespace($oExtFilter, $sExtKeyAttCode, $aClassAliases, $aAliasTranslation, $iOperatorCode); + foreach($aFilter as $sAlias => $oExtFilter) + { + $this->AddCondition_PointingTo_InNamespace($oExtFilter, $sExtKeyAttCode, $aClassAliases, $aAliasTranslation, $iOperatorCode); + } } } foreach($oFilter->m_aReferencedBy as $sForeignClass => $aReferences) @@ -797,33 +816,36 @@ class DBObjectSearch $sRes = ''; foreach($this->m_aPointingTo as $sExtKey => $aPointingTo) { - foreach($aPointingTo as $iOperatorCode => $oFilter) + foreach($aPointingTo as $iOperatorCode => $aFilter) { - switch($iOperatorCode) + foreach($aFilter as $sAlias => $oFilter) { - case TREE_OPERATOR_EQUALS: - $sOperator = ' = '; - break; - - case TREE_OPERATOR_BELOW: - $sOperator = ' BELOW '; - break; - - case TREE_OPERATOR_BELOW_STRICT: - $sOperator = ' BELOW STRICT '; - break; - - case TREE_OPERATOR_NOT_BELOW: - $sOperator = ' NOT BELOW '; - break; - - case TREE_OPERATOR_NOT_BELOW_STRICT: - $sOperator = ' NOT BELOW STRICT '; - break; - + switch($iOperatorCode) + { + case TREE_OPERATOR_EQUALS: + $sOperator = ' = '; + break; + + case TREE_OPERATOR_BELOW: + $sOperator = ' BELOW '; + break; + + case TREE_OPERATOR_BELOW_STRICT: + $sOperator = ' BELOW STRICT '; + break; + + case TREE_OPERATOR_NOT_BELOW: + $sOperator = ' NOT BELOW '; + break; + + case TREE_OPERATOR_NOT_BELOW_STRICT: + $sOperator = ' NOT BELOW STRICT '; + break; + + } + $sRes .= ' JOIN '.$oFilter->GetClass().' AS '.$oFilter->GetClassAlias().' ON '.$this->GetClassAlias().'.'.$sExtKey.$sOperator.$oFilter->GetClassAlias().'.id'; + $sRes .= $oFilter->ToOQL_Joins(); } - $sRes .= ' JOIN '.$oFilter->GetClass().' AS '.$oFilter->GetClassAlias().' ON '.$this->GetClassAlias().'.'.$sExtKey.$sOperator.$oFilter->GetClassAlias().'.id'; - $sRes .= $oFilter->ToOQL_Joins(); } } foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences) diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 336883f58..ec9901ab1 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -1811,7 +1811,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) { // Hide objects that are not visible to the current user // - if (!$oFilter->IsAllDataAllowed()) + if (!$oFilter->IsAllDataAllowed() && !$oFilter->IsDataFiltered()) { $oVisibleObjects = UserRights::GetSelectFilter($oFilter->GetClass()); if ($oVisibleObjects === false) @@ -1822,6 +1822,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) if (is_object($oVisibleObjects)) { $oFilter->MergeWith($oVisibleObjects); + $oFilter->SetDataFiltered(); } else { @@ -2423,7 +2424,7 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) $sKeyClassAlias = self::GenerateUniqueAlias($aClassAliases, $sKeyClass.'_'.$sKeyAttCode, $sKeyClass); $oExtFilter = new DBObjectSearch($sKeyClass, $sKeyClassAlias); - $aAllPointingTo[$sKeyAttCode][TREE_OPERATOR_EQUALS] = $oExtFilter; + $aAllPointingTo[$sKeyAttCode][TREE_OPERATOR_EQUALS][$sKeyClassAlias] = $oExtFilter; } } } @@ -2431,94 +2432,97 @@ if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) foreach ($aAllPointingTo as $sKeyAttCode => $aPointingTo) { - foreach($aPointingTo as $iOperatorCode => $oExtFilter) + foreach($aPointingTo as $iOperatorCode => $aFilter) { - if (!MetaModel::IsValidAttCode($sTableClass, $sKeyAttCode)) continue; // Not defined in the class, skip it - // The aliases should not conflict because normalization occured while building the filter - $oKeyAttDef = self::GetAttributeDef($sTableClass, $sKeyAttCode); - $sKeyClass = $oExtFilter->GetFirstJoinedClass(); - $sKeyClassAlias = $oExtFilter->GetFirstJoinedClassAlias(); + foreach($aFilter as $sAlias => $oExtFilter) + { + if (!MetaModel::IsValidAttCode($sTableClass, $sKeyAttCode)) continue; // Not defined in the class, skip it + // The aliases should not conflict because normalization occured while building the filter + $oKeyAttDef = self::GetAttributeDef($sTableClass, $sKeyAttCode); + $sKeyClass = $oExtFilter->GetFirstJoinedClass(); + $sKeyClassAlias = $oExtFilter->GetFirstJoinedClassAlias(); //echo "MAKEQUERY-$sTableClass::$sKeyAttCode Foreach PointingTo($iOperatorCode) $sKeyClass (alias:$sKeyClassAlias)
\n"; - // Note: there is no search condition in $oExtFilter, because normalization did merge the condition onto the top of the filter tree + // Note: there is no search condition in $oExtFilter, because normalization did merge the condition onto the top of the filter tree - if ($iOperatorCode == TREE_OPERATOR_EQUALS) - { - // Specify expected attributes for the target class query - // ... and use the current alias ! - $aTranslateNow = array(); // Translation for external fields - must be performed before the join is done (recursion...) -//echo "MAKEQUERY-array_key_exists($sTableClass, \$aExtKeys)
\n"; - if (array_key_exists($sTableClass, $aExtKeys) && array_key_exists($sKeyAttCode, $aExtKeys[$sTableClass])) + if ($iOperatorCode == TREE_OPERATOR_EQUALS) { - foreach($aExtKeys[$sTableClass][$sKeyAttCode] as $sAttCode => $oAtt) + // Specify expected attributes for the target class query + // ... and use the current alias ! + $aTranslateNow = array(); // Translation for external fields - must be performed before the join is done (recursion...) +//echo "MAKEQUERY-array_key_exists($sTableClass, \$aExtKeys)
\n"; + if (array_key_exists($sTableClass, $aExtKeys) && array_key_exists($sKeyAttCode, $aExtKeys[$sTableClass])) { + foreach($aExtKeys[$sTableClass][$sKeyAttCode] as $sAttCode => $oAtt) + { //echo "MAKEQUERY aExtKeys[$sTableClass][$sKeyAttCode] => $sAttCode-oAtt:
".print_r($oAtt, true)."

\n"; - if ($oAtt instanceof AttributeFriendlyName) - { - // Note: for a given ext key, there is one single attribute "friendly name" - $aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression('friendlyname', $sKeyClassAlias); -//echo "

aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression('friendlyname', $sKeyClassAlias);

\n"; - } - else - { - $sExtAttCode = $oAtt->GetExtAttCode(); - // Translate mainclass.extfield => remoteclassalias.remotefieldcode - $oRemoteAttDef = self::GetAttributeDef($sKeyClass, $sExtAttCode); - foreach ($oRemoteAttDef->GetSQLExpressions() as $sColID => $sRemoteAttExpr) + if ($oAtt instanceof AttributeFriendlyName) { - $aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias); -//echo "

aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias);

\n"; + // Note: for a given ext key, there is one single attribute "friendly name" + $aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression('friendlyname', $sKeyClassAlias); +//echo "

aTranslateNow[$sTargetAlias][$sAttCode] = new FieldExpression('friendlyname', $sKeyClassAlias);

\n"; } + else + { + $sExtAttCode = $oAtt->GetExtAttCode(); + // Translate mainclass.extfield => remoteclassalias.remotefieldcode + $oRemoteAttDef = self::GetAttributeDef($sKeyClass, $sExtAttCode); + foreach ($oRemoteAttDef->GetSQLExpressions() as $sColID => $sRemoteAttExpr) + { + $aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias); +//echo "

aTranslateNow[$sTargetAlias][$sAttCode.$sColId] = new FieldExpression($sExtAttCode, $sKeyClassAlias);

\n"; + } //echo "

ExtAttr2: $sTargetAlias.$sAttCode to $sKeyClassAlias.$sRemoteAttExpr (class: $sKeyClass)

\n"; + } } - } - // Translate prior to recursing - // + // Translate prior to recursing + // //echo "

oQBExpr ".__LINE__.":

\n".print_r($oQBExpr, true)."\n".print_r($aTranslateNow, true)."

\n"; - $oQBExpr->Translate($aTranslateNow, false); + $oQBExpr->Translate($aTranslateNow, false); //echo "

oQBExpr ".__LINE__.":

\n".print_r($oQBExpr, true)."

\n"; //echo "

External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()/p>\n"; - self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()"); - $oQBExpr->PushJoinField(new FieldExpression('id', $sKeyClassAlias)); - + self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()"); + $oQBExpr->PushJoinField(new FieldExpression('id', $sKeyClassAlias)); + //echo "

Recursive MakeQuery ".__LINE__.":

\n".print_r($aSelectedClasses, true)."

\n"; - $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 - - self::DbgTrace("External key $sKeyAttCode, Join on $sLocalKeyField = $sExternalKeyField"); - if ($oKeyAttDef->IsNullAllowed()) - { - $oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable); - } - else - { - $oSelectBase->AddInnerJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable); + $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 + + self::DbgTrace("External key $sKeyAttCode, Join on $sLocalKeyField = $sExternalKeyField"); + if ($oKeyAttDef->IsNullAllowed()) + { + $oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable); + } + else + { + $oSelectBase->AddInnerJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField, $sExternalKeyTable); + } } } - } - else - { - $oQBExpr->PushJoinField(new FieldExpression($sKeyAttCode, $sKeyClassAlias)); - $oSelectExtKey = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oExtFilter); - $oJoinExpr = $oQBExpr->PopJoinField(); + else + { + $oQBExpr->PushJoinField(new FieldExpression($sKeyAttCode, $sKeyClassAlias)); + $oSelectExtKey = self::MakeQuery($aSelectedClasses, $oQBExpr, $aClassAliases, $aTableAliases, $oExtFilter); + $oJoinExpr = $oQBExpr->PopJoinField(); //echo "MAKEQUERY-PopJoinField pour $sKeyAttCode, $sKeyClassAlias:
".print_r($oJoinExpr, true)."

\n"; - $sExternalKeyTable = $oJoinExpr->GetParent(); - $sExternalKeyField = $oJoinExpr->GetName(); - $sLeftIndex = $sExternalKeyField.'_left'; // TODO use GetSQLLeft() - $sRightIndex = $sExternalKeyField.'_right'; // TODO use GetSQLRight() - - $LocalKeyLeft = $oKeyAttDef->GetSQLLeft(); + $sExternalKeyTable = $oJoinExpr->GetParent(); + $sExternalKeyField = $oJoinExpr->GetName(); + $sLeftIndex = $sExternalKeyField.'_left'; // TODO use GetSQLLeft() + $sRightIndex = $sExternalKeyField.'_right'; // TODO use GetSQLRight() + + $LocalKeyLeft = $oKeyAttDef->GetSQLLeft(); //echo "MAKEQUERY-LocalKeyLeft pour $sKeyAttCode => $LocalKeyLeft
\n"; - - $oSelectBase->AddInnerJoinTree($oSelectExtKey, $LocalKeyLeft, $sLeftIndex, $sRightIndex, $sExternalKeyTable, $iOperatorCode); + + $oSelectBase->AddInnerJoinTree($oSelectExtKey, $LocalKeyLeft, $sLeftIndex, $sRightIndex, $sExternalKeyTable, $iOperatorCode); + } } } }