// Second part: advanced search form:
        if (!empty($sFilter)) {
            $oFilter = DBSearch::unserialize($sFilter);
        } else {
            if (!empty($sClass)) {
                $oFilter = new DBObjectSearch($sClass);
            }
        }
    }
} catch (CoreException $e) {
    $oFilter = new DBObjectSearch($sClass);
    $oP->P("<b>" . Dict::Format('UI:UniversalSearch:Error', $e->getHtmlDesc()) . "</b>");
}
if ($oFilter != null) {
    $oSet = new CMDBObjectSet($oFilter);
    $oBlock = new DisplayBlock($oFilter, 'search', false);
    $aExtraParams = $oAppContext->GetAsHash();
    $aExtraParams['open'] = true;
    $aExtraParams['baseClass'] = $sBaseClass;
    $aExtraParams['action'] = utils::GetAbsoluteUrlAppRoot() . 'pages/UniversalSearch.php';
    //$aExtraParams['class'] = $sClassName;
    $oBlock->Display($oP, 0, $aExtraParams);
    // Search results
    $oResultBlock = new DisplayBlock($oFilter, 'list', false);
    $oResultBlock->Display($oP, 1);
    // Menu node
    $sFilter = $oFilter->ToOQL();
    $oP->add("\n<!-- {$sFilter} -->\n");
}
$oP->add("</div>\n");
$oP->output();
Beispiel #2
0
 protected function DoExecute()
 {
     foreach (MetaModel::GetClasses() as $sClassName) {
         if (MetaModel::HasTable($sClassName)) {
             continue;
         }
         $oNobody = MetaModel::GetObject($sClassName, 123);
         $oBaby = new $sClassName();
         $oFilter = new DBObjectSearch($sClassName);
         // Challenge reversibility of OQL / filter object
         //
         $sExpr1 = $oFilter->ToOQL();
         $oNewFilter = DBObjectSearch::FromOQL($sExpr1);
         $sExpr2 = $oNewFilter->ToOQL();
         if ($sExpr1 != $sExpr2) {
             $this->ReportError("Found two different OQL expression out of the (same?) filter: <em>{$sExpr1}</em> != <em>{$sExpr2}</em>");
         }
         // Use the filter (perform the query)
         //
         $oSet = new CMDBObjectSet($oFilter);
         $this->ReportSuccess('Found ' . $oSet->Count() . " objects of class {$sClassName}");
     }
     return true;
 }
 /**
  * Helper to link objects
  *
  * @param string sLinkAttCode
  * @param string sLinkedClass
  * @param array $aLinkList
  * @param DBObject oTargetObj
  * @param WebServiceResult oRes
  *
  * @return array List of objects that could not be found
  */
 protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $aLinkList, &$oTargetObj, &$oRes)
 {
     $oLinkAtt = MetaModel::GetAttributeDef(get_class($oTargetObj), $sLinkAttCode);
     $sLinkClass = $oLinkAtt->GetLinkedClass();
     $sExtKeyToItem = $oLinkAtt->GetExtKeyToRemote();
     $aItemsFound = array();
     $aItemsNotFound = array();
     if (is_null($aLinkList)) {
         return $aItemsNotFound;
     }
     foreach ($aLinkList as $aItemData) {
         if (!array_key_exists('class', $aItemData)) {
             $oRes->LogWarning("Parameter {$sParamName}: missing 'class' specification");
             continue;
             // skip
         }
         $sTargetClass = $aItemData['class'];
         if (!MetaModel::IsValidClass($sTargetClass)) {
             $oRes->LogError("Parameter {$sParamName}: invalid class '{$sTargetClass}'");
             continue;
             // skip
         }
         if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass)) {
             $oRes->LogError("Parameter {$sParamName}: '{$sTargetClass}' is not a child class of '{$sLinkedClass}'");
             continue;
             // skip
         }
         $oReconFilter = new DBObjectSearch($sTargetClass);
         $aCIStringDesc = array();
         foreach ($aItemData['search'] as $sAttCode => $value) {
             if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) {
                 $aCodes = array_keys(MetaModel::GetClassFilterDefs($sTargetClass));
                 $oRes->LogError("Parameter {$sParamName}: '{$sAttCode}' is not a valid filter code for class '{$sTargetClass}', expecting a value in {" . implode(', ', $aCodes) . "}");
                 continue 2;
                 // skip the entire item
             }
             $aCIStringDesc[] = "{$sAttCode}: {$value}";
             // The attribute is one of our reconciliation key
             $oReconFilter->AddCondition($sAttCode, $value, '=');
         }
         if (count($aCIStringDesc) == 1) {
             // take the last and unique value to describe the object
             $sItemDesc = $value;
         } else {
             // describe the object by the given keys
             $sItemDesc = $sTargetClass . '(' . implode('/', $aCIStringDesc) . ')';
         }
         $oExtObjects = new CMDBObjectSet($oReconFilter);
         switch ($oExtObjects->Count()) {
             case 0:
                 $oRes->LogWarning("Parameter {$sParamName}: object to link {$sLinkedClass} / {$sItemDesc} could not be found (searched: '" . $oReconFilter->ToOQL(true) . "')");
                 $aItemsNotFound[] = $sItemDesc;
                 break;
             case 1:
                 $aItemsFound[] = array('object' => $oExtObjects->Fetch(), 'link_values' => @$aItemData['link_values'], 'desc' => $sItemDesc);
                 break;
             default:
                 $oRes->LogWarning("Parameter {$sParamName}: Found " . $oExtObjects->Count() . " matches for item '{$sItemDesc}' (searched: '" . $oReconFilter->ToOQL(true) . "')");
                 $aItemsNotFound[] = $sItemDesc;
         }
     }
     if (count($aItemsFound) > 0) {
         $aLinks = array();
         foreach ($aItemsFound as $aItemData) {
             $oLink = MetaModel::NewObject($sLinkClass);
             $oLink->Set($sExtKeyToItem, $aItemData['object']->GetKey());
             foreach ($aItemData['link_values'] as $sKey => $value) {
                 if (!MetaModel::IsValidAttCode($sLinkClass, $sKey)) {
                     $oRes->LogWarning("Parameter {$sParamName}: Attaching item '" . $aItemData['desc'] . "', the attribute code '{$sKey}' is not valid ; check the class '{$sLinkClass}'");
                 } else {
                     $oLink->Set($sKey, $value);
                 }
             }
             $aLinks[] = $oLink;
         }
         $oImpactedInfraSet = DBObjectSet::FromArray($sLinkClass, $aLinks);
         $oTargetObj->Set($sLinkAttCode, $oImpactedInfraSet);
     }
     return $aItemsNotFound;
 }
 private static function MakeQuery(&$oBuild, DBObjectSearch $oFilter, $aAttToLoad = null, $aValues = array(), $bIsMainQueryUNUSED = 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)
     $sClass = $oFilter->GetFirstJoinedClass();
     $sClassAlias = $oFilter->GetFirstJoinedClassAlias();
     $bIsOnQueriedClass = array_key_exists($sClassAlias, $oBuild->GetRootFilter()->GetSelectedClasses());
     self::DbgTrace("Entering: " . $oFilter->ToOQL() . ", " . ($bIsOnQueriedClass ? "MAIN" : "SECONDARY"));
     $sRootClass = self::GetRootClass($sClass);
     $sKeyField = self::DBGetKey($sClass);
     if ($bIsOnQueriedClass) {
         // default to the whole list of attributes + the very std id/finalclass
         $oBuild->m_oQBExpressions->AddSelect($sClassAlias . 'id', new FieldExpression('id', $sClassAlias));
         if (is_null($aAttToLoad) || !array_key_exists($sClassAlias, $aAttToLoad)) {
             $aAttList = self::ListAttributeDefs($sClass);
         } else {
             $aAttList = $aAttToLoad[$sClassAlias];
         }
         foreach ($aAttList as $sAttCode => $oAttDef) {
             if (!$oAttDef->IsScalar()) {
                 continue;
             }
             // keep because it can be used for sorting - if (!$oAttDef->LoadInObject()) continue;
             foreach ($oAttDef->GetSQLExpressions() as $sColId => $sSQLExpr) {
                 $oBuild->m_oQBExpressions->AddSelect($sClassAlias . $sAttCode . $sColId, new FieldExpression($sAttCode . $sColId, $sClassAlias));
             }
         }
         // Transform the full text condition into additional condition expression
         $aFullText = $oFilter->GetCriteria_FullText();
         if (count($aFullText) > 0) {
             $aFullTextFields = array();
             foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
                 if (!$oAttDef->IsScalar()) {
                     continue;
                 }
                 if ($oAttDef->IsExternalKey()) {
                     continue;
                 }
                 $aFullTextFields[] = new FieldExpression($sAttCode, $sClassAlias);
             }
             $oTextFields = new CharConcatWSExpression(' ', $aFullTextFields);
             foreach ($aFullText as $sFTNeedle) {
                 $oNewCond = new BinaryExpression($oTextFields, 'LIKE', new ScalarExpression("%{$sFTNeedle}%"));
                 $oBuild->m_oQBExpressions->AddCondition($oNewCond);
             }
         }
     }
     //echo "<p>oQBExpr ".__LINE__.": <pre>\n".print_r($oBuild->m_oQBExpressions, true)."</pre></p>\n";
     $aExpectedAtts = array();
     // array of (attcode => fieldexpression)
     //echo "<p>".__LINE__.": GetUnresolvedFields($sClassAlias, ...)</p>\n";
     $oBuild->m_oQBExpressions->GetUnresolvedFields($sClassAlias, $aExpectedAtts);
     // Compute a clear view of required joins (from the current class)
     // Build the list of external keys:
     // -> 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 partially computed once for all (cached) ?
     //
     if ($bIsOnQueriedClass) {
         // Get all Ext keys for the queried class (??)
         foreach (self::GetKeysList($sClass) as $sKeyAttCode) {
             $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode];
             $aExtKeys[$sKeyTableClass][$sKeyAttCode] = array();
         }
     }
     // Get all Ext keys used by the filter
     foreach ($oFilter->GetCriteria_PointingTo() as $sKeyAttCode => $aPointingTo) {
         if (array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo)) {
             $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode];
             $aExtKeys[$sKeyTableClass][$sKeyAttCode] = array();
         }
     }
     $aFNJoinAlias = array();
     // array of (subclass => alias)
     if (array_key_exists('friendlyname', $aExpectedAtts)) {
         // To optimize: detect a restriction on child classes in the condition expression
         //    e.g. SELECT FunctionalCI WHERE finalclass IN ('Server', 'VirtualMachine')
         $oNameExpression = self::GetExtendedNameExpression($sClass);
         $aNameFields = array();
         $oNameExpression->GetUnresolvedFields('', $aNameFields);
         $aTranslateNameFields = array();
         foreach ($aNameFields as $sSubClass => $aFields) {
             foreach ($aFields as $sAttCode => $oField) {
                 $oAttDef = self::GetAttributeDef($sSubClass, $sAttCode);
                 if ($oAttDef->IsExternalKey()) {
                     $sClassOfAttribute = self::$m_aAttribOrigins[$sSubClass][$sAttCode];
                     $aExtKeys[$sClassOfAttribute][$sAttCode] = array();
                 } elseif ($oAttDef->IsExternalField() || $oAttDef instanceof AttributeFriendlyName) {
                     $sKeyAttCode = $oAttDef->GetKeyAttCode();
                     $sClassOfAttribute = self::$m_aAttribOrigins[$sSubClass][$sKeyAttCode];
                     $aExtKeys[$sClassOfAttribute][$sKeyAttCode][$sAttCode] = $oAttDef;
                 } else {
                     $sClassOfAttribute = self::GetAttributeOrigin($sSubClass, $sAttCode);
                 }
                 if (self::IsParentClass($sClassOfAttribute, $sClass)) {
                     // The attribute is part of the standard query
                     //
                     $sAliasForAttribute = $sClassAlias;
                 } else {
                     // The attribute will be available from an additional outer join
                     // For each subclass (table) one single join is enough
                     //
                     if (!array_key_exists($sClassOfAttribute, $aFNJoinAlias)) {
                         $sAliasForAttribute = $oBuild->GenerateClassAlias($sClassAlias . '_fn_' . $sClassOfAttribute, $sClassOfAttribute);
                         $aFNJoinAlias[$sClassOfAttribute] = $sAliasForAttribute;
                     } else {
                         $sAliasForAttribute = $aFNJoinAlias[$sClassOfAttribute];
                     }
                 }
                 $aTranslateNameFields[$sSubClass][$sAttCode] = new FieldExpression($sAttCode, $sAliasForAttribute);
             }
         }
         $oNameExpression = $oNameExpression->Translate($aTranslateNameFields, false);
         $aTranslateNow = array();
         $aTranslateNow[$sClassAlias]['friendlyname'] = $oNameExpression;
         $oBuild->m_oQBExpressions->Translate($aTranslateNow, false);
     }
     // Add the ext fields used in the select (eventually adds an external key)
     foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) {
         if ($oAttDef->IsExternalField() || $oAttDef instanceof AttributeFriendlyName) {
             if (array_key_exists($sAttCode, $aExpectedAtts)) {
                 $sKeyAttCode = $oAttDef->GetKeyAttCode();
                 if ($sKeyAttCode != 'id') {
                     // Add the external attribute
                     $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode];
                     $aExtKeys[$sKeyTableClass][$sKeyAttCode][$sAttCode] = $oAttDef;
                 }
             }
         }
     }
     // First query built upon on the leaf (ie current) class
     //
     self::DbgTrace("Main (=leaf) class, call MakeQuerySingleTable()");
     if (self::HasTable($sClass)) {
         $oSelectBase = self::MakeQuerySingleTable($oBuild, $oFilter, $sClass, $aExtKeys, $aValues);
     } else {
         $oSelectBase = null;
         // 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}')");
         $oBuild->m_oQBExpressions->AddCondition($oFinalClassRestriction);
     }
     // Then we join the queries of the eventual parent classes (compound model)
     foreach (self::EnumParentClasses($sClass) as $sParentClass) {
         if (!self::HasTable($sParentClass)) {
             continue;
         }
         self::DbgTrace("Parent class: {$sParentClass}... let's call MakeQuerySingleTable()");
         $oSelectParentTable = self::MakeQuerySingleTable($oBuild, $oFilter, $sParentClass, $aExtKeys, $aValues);
         if (is_null($oSelectBase)) {
             $oSelectBase = $oSelectParentTable;
         } else {
             $oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, self::DBGetKey($sParentClass));
         }
     }
     // Filter on objects referencing me
     foreach ($oFilter->GetCriteria_ReferencedBy() as $sForeignClass => $aKeysAndFilters) {
         foreach ($aKeysAndFilters as $sForeignKeyAttCode => $oForeignFilter) {
             $oForeignKeyAttDef = self::GetAttributeDef($sForeignClass, $sForeignKeyAttCode);
             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()));
             $sForeignClassAlias = $oForeignFilter->GetFirstJoinedClassAlias();
             $oBuild->m_oQBExpressions->PushJoinField(new FieldExpression($sForeignKeyAttCode, $sForeignClassAlias));
             $oSelectForeign = self::MakeQuery($oBuild, $oForeignFilter, $aAttToLoad);
             $oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField();
             $sForeignKeyTable = $oJoinExpr->GetParent();
             $sForeignKeyColumn = $oJoinExpr->GetName();
             $oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable);
         }
     }
     // Filter on related objects
     //
     foreach ($oFilter->GetCriteria_RelatedTo() as $aCritInfo) {
         $oSubFilter = $aCritInfo['flt'];
         $sRelCode = $aCritInfo['relcode'];
         $iMaxDepth = $aCritInfo['maxdepth'];
         // Get the starting point objects
         $oStartSet = new CMDBObjectSet($oSubFilter);
         // Get the objects related to those objects... recursively...
         $aRelatedObjs = $oStartSet->GetRelatedObjects($sRelCode, $iMaxDepth);
         $aRestriction = array_key_exists($sRootClass, $aRelatedObjs) ? $aRelatedObjs[$sRootClass] : array();
         // #@# todo - related objects and expressions...
         // Create condition
         if (count($aRestriction) > 0) {
             $oSelectBase->AddCondition($sKeyField . ' IN (' . implode(', ', CMDBSource::Quote(array_keys($aRestriction), true)) . ')');
         } else {
             // Quick N'dirty -> generate an empty set
             $oSelectBase->AddCondition('false');
         }
     }
     // Additional JOINS for Friendly names
     //
     foreach ($aFNJoinAlias as $sSubClass => $sSubClassAlias) {
         $oSubClassFilter = new DBObjectSearch($sSubClass, $sSubClassAlias);
         $oSelectFN = self::MakeQuerySingleTable($oBuild, $oSubClassFilter, $sSubClass, $aExtKeys, array());
         $oSelectBase->AddLeftJoin($oSelectFN, $sKeyField, self::DBGetKey($sSubClass));
     }
     // That's all... cross fingers and we'll get some working query
     //MyHelpers::var_dump_html($oSelectBase, true);
     //MyHelpers::var_dump_html($oSelectBase->RenderSelect(), true);
     if (self::$m_bDebugQuery) {
         $oSelectBase->DisplayHtml();
     }
     return $oSelectBase;
 }