/** * Helper to determine the supported types of tickets */ function GetTicketClasses() { $aClasses = array(); foreach (explode(',', MetaModel::GetConfig()->Get('portal_tickets')) as $sRawClass) { $sRawClass = trim($sRawClass); if (!MetaModel::IsValidClass($sRawClass)) { throw new Exception("Class '{$sRawClass}' is not a valid class, please review your configuration (portal_tickets)"); } if (!MetaModel::IsParentClass('Ticket', $sRawClass)) { throw new Exception("Class '{$sRawClass}' does not inherit from Ticket, please review your configuration (portal_tickets)"); } $aClasses[] = $sRawClass; } return $aClasses; }
/** * Find the object of the specified Class/ID. * @param WebPage $oP The current page * @return DBObject The found object, or throws an exception in case of failure */ public function FindObjectFromArgs($aAllowedClasses = null) { $sClass = utils::ReadParam('class', '', true, 'class'); $iId = utils::ReadParam('id', 0, true, 'integer'); if (empty($sClass)) { throw new Exception("Missing argument 'class'"); } if (!MetaModel::IsValidClass($sClass)) { throw new Exception("Wrong value for argument 'class': {$sClass}"); } if ($iId == 0) { throw new Exception("Missing argument 'id'"); } if (!is_null($aAllowedClasses)) { $bAllowed = false; foreach ($aAllowedClasses as $sParentClass) { if (MetaModel::IsParentClass($sParentClass, $sClass)) { $bAllowed = true; } } if (!$bAllowed) { throw new Exception("Class '{$sClass} not allowed in this implementation'"); } } $oObj = MetaModel::GetObject($sClass, $iId, false); if (!is_object($oObj)) { throw new Exception("Could not find the object {$sClass}/{$iId}"); } return $oObj; }
/** * 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 CMDBSearchFilter($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; }
public function DoCheckToWrite() { parent::DoCheckToWrite(); $sFilter = trim($this->Get('filter')); if (strlen($sFilter) > 0) { try { $oSearch = DBObjectSearch::FromOQL($sFilter); if (!MetaModel::IsParentClass($this->Get('target_class'), $oSearch->GetClass())) { $this->m_aCheckIssues[] = Dict::Format('TriggerOnObject:WrongFilterClass', $this->Get('target_class')); } } catch (OqlException $e) { $this->m_aCheckIssues[] = Dict::Format('TriggerOnObject:WrongFilterQuery', $e->getMessage()); } } }
/** * Read and interpret object search criteria from a Rest/Json structure * * @param string $sClass Name of the class * @param StdClass $oCriteria Hash of attribute code => value (can be a substructure or a scalar, depending on the nature of the attriute) * @return object The object found * @throws Exception If the input structure is not valid or it could not find exactly one object */ protected static function FindObjectFromCriteria($sClass, $oCriteria) { $aCriteriaReport = array(); if (isset($oCriteria->finalclass)) { if (!MetaModel::IsValidClass($oCriteria->finalclass)) { throw new Exception("finalclass: Unknown class '" . $oCriteria->finalclass . "'"); } if (!MetaModel::IsParentClass($sClass, $oCriteria->finalclass)) { throw new Exception("finalclass: '" . $oCriteria->finalclass . "' is not a child class of '{$sClass}'"); } $sClass = $oCriteria->finalclass; } $oSearch = new DBObjectSearch($sClass); foreach ($oCriteria as $sAttCode => $value) { $realValue = static::MakeValue($sClass, $sAttCode, $value); $oSearch->AddCondition($sAttCode, $realValue, '='); if (is_object($value) || is_array($value)) { $value = json_encode($value); } $aCriteriaReport[] = "{$sAttCode}: {$value} ({$realValue})"; } $oSet = new DBObjectSet($oSearch); $iCount = $oSet->Count(); if ($iCount == 0) { throw new Exception("No item found with criteria: " . implode(', ', $aCriteriaReport)); } elseif ($iCount > 1) { throw new Exception("Several items found ({$iCount}) with criteria: " . implode(', ', $aCriteriaReport)); } $res = $oSet->Fetch(); return $res; }
protected function MakeSQLObjectQuery(&$oBuild, $aAttToLoad = null, $aValues = array()) { // 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 = $this->GetFirstJoinedClass(); $sClassAlias = $this->GetFirstJoinedClassAlias(); $bIsOnQueriedClass = array_key_exists($sClassAlias, $oBuild->GetRootFilter()->GetSelectedClasses()); self::DbgTrace("Entering: " . $this->ToOQL() . ", " . ($bIsOnQueriedClass ? "MAIN" : "SECONDARY")); $sRootClass = MetaModel::GetRootClass($sClass); $sKeyField = MetaModel::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)) { $sSelectedClass = $oBuild->GetSelectedClass($sClassAlias); $aAttList = MetaModel::ListAttributeDefs($sSelectedClass); } 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 = $this->GetCriteria_FullText(); if (count($aFullText) > 0) { $aFullTextFields = array(); foreach (MetaModel::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 (MetaModel::GetKeysList($sClass) as $sKeyAttCode) { $sKeyTableClass = MetaModel::GetAttributeOrigin($sClass, $sKeyAttCode); $aExtKeys[$sKeyTableClass][$sKeyAttCode] = array(); } } // Get all Ext keys used by the filter foreach ($this->GetCriteria_PointingTo() as $sKeyAttCode => $aPointingTo) { if (array_key_exists(TREE_OPERATOR_EQUALS, $aPointingTo)) { $sKeyTableClass = MetaModel::GetAttributeOrigin($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 = MetaModel::GetAttributeDef($sSubClass, $sAttCode); if ($oAttDef->IsExternalKey()) { $sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode); $aExtKeys[$sClassOfAttribute][$sAttCode] = array(); } elseif ($oAttDef->IsExternalField() || $oAttDef instanceof AttributeFriendlyName) { $sKeyAttCode = $oAttDef->GetKeyAttCode(); $sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sKeyAttCode); $aExtKeys[$sClassOfAttribute][$sKeyAttCode][$sAttCode] = $oAttDef; } else { $sClassOfAttribute = MetaModel::GetAttributeOrigin($sSubClass, $sAttCode); } if (MetaModel::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 (MetaModel::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 = MetaModel::GetAttributeOrigin($sClass, $sKeyAttCode); $aExtKeys[$sKeyTableClass][$sKeyAttCode][$sAttCode] = $oAttDef; } } } } // First query built upon on the leaf (ie current) class // self::DbgTrace("Main (=leaf) class, call MakeSQLObjectQuerySingleTable()"); if (MetaModel::HasTable($sClass)) { $oSelectBase = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sClass, $aExtKeys, $aValues); } else { $oSelectBase = null; // As the join will not filter on the expected classes, we have to specify it explicitely $sExpectedClasses = implode("', '", MetaModel::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 (MetaModel::EnumParentClasses($sClass) as $sParentClass) { if (!MetaModel::HasTable($sParentClass)) { continue; } self::DbgTrace("Parent class: {$sParentClass}... let's call MakeSQLObjectQuerySingleTable()"); $oSelectParentTable = $this->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sParentClass, $aExtKeys, $aValues); if (is_null($oSelectBase)) { $oSelectBase = $oSelectParentTable; } else { $oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, MetaModel::DBGetKey($sParentClass)); } } // Filter on objects referencing me foreach ($this->GetCriteria_ReferencedBy() as $sForeignClass => $aKeysAndFilters) { foreach ($aKeysAndFilters as $sForeignKeyAttCode => $oForeignFilter) { $oForeignKeyAttDef = MetaModel::GetAttributeDef($sForeignClass, $sForeignKeyAttCode); self::DbgTrace("Referenced by foreign key: {$sForeignKeyAttCode}... let's call MakeSQLObjectQuery()"); //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)); if ($oForeignKeyAttDef instanceof AttributeObjectKey) { $sClassAttCode = $oForeignKeyAttDef->Get('class_attcode'); // Add the condition: `$sForeignClassAlias`.$sClassAttCode IN (subclasses of $sClass') $oClassListExpr = ListExpression::FromScalars(MetaModel::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL)); $oClassExpr = new FieldExpression($sClassAttCode, $sForeignClassAlias); $oClassRestriction = new BinaryExpression($oClassExpr, 'IN', $oClassListExpr); $oBuild->m_oQBExpressions->AddCondition($oClassRestriction); } $oSelectForeign = $oForeignFilter->MakeSQLObjectQuery($oBuild, $aAttToLoad); $oJoinExpr = $oBuild->m_oQBExpressions->PopJoinField(); $sForeignKeyTable = $oJoinExpr->GetParent(); $sForeignKeyColumn = $oJoinExpr->GetName(); $oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable); } } // Additional JOINS for Friendly names // foreach ($aFNJoinAlias as $sSubClass => $sSubClassAlias) { $oSubClassFilter = new DBObjectSearch($sSubClass, $sSubClassAlias); $oSelectFN = $oSubClassFilter->MakeSQLObjectQuerySingleTable($oBuild, $aAttToLoad, $sSubClass, $aExtKeys, array()); $oSelectBase->AddLeftJoin($oSelectFN, $sKeyField, MetaModel::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; }
protected function LoadValues($aArgs) { $this->m_aValues = array(); if (!array_key_exists('this', $aArgs)) { throw new CoreException("Missing 'this' in arguments", array('args' => $aArgs)); } $oTarget = $aArgs['this->object()']; // Nodes from which we will start the search for neighbourhood $oNodes = DBObjectSet::FromLinkSet($oTarget, $this->m_sLinkSetAttCode, $this->m_sExtKeyToRemote); // Neighbours, whatever their class $aRelated = $oNodes->GetRelatedObjects($this->m_sRelationCode, $this->m_iMaxDepth); $sRootClass = MetaModel::GetRootClass($this->m_sTargetClass); if (array_key_exists($sRootClass, $aRelated)) { $aLinksToCreate = array(); foreach ($aRelated[$sRootClass] as $iKey => $oObject) { if (MetaModel::IsParentClass($this->m_sTargetClass, get_class($oObject))) { $oNewLink = MetaModel::NewObject($this->m_sTargetLinkClass); $oNewLink->Set($this->m_sTargetExtKey, $iKey); //$oNewLink->Set('role', 'concerned by an impacted CI'); $aLinksToCreate[] = $oNewLink; } } // #@# or AddObjectArray($aObjects) ? $oSetToCreate = DBObjectSet::FromArray($this->m_sTargetLinkClass, $aLinksToCreate); $this->m_aValues[$oObject->GetKey()] = $oObject->GetName(); } return true; }
/** * Lifecycle action: Set the current logged in CONTACT for the given attribute */ public function SetCurrentPerson($sAttCode) { $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); if ($oAttDef instanceof AttributeString) { $iPerson = UserRights::GetContactId(); if ($iPerson == 0) { $this->Set($sAttCode, ''); } else { $oPerson = MetaModel::GetObject('Person', $iPerson); $this->Set($sAttCode, $oPerson->Get('friendlyname')); } } else { if ($oAttDef->IsExternalKey()) { if (!MetaModel::IsParentClass($oAttDef->GetTargetClass(), 'Person')) { throw new Exception("SetCurrentContact: the attribute {$sAttCode} must be an external key to 'Person' or any other class above 'Person', found '" . $oAttDef->GetTargetClass() . "'"); } } $this->Set($sAttCode, UserRights::GetContactId()); } return true; }
/** * Change the class (only subclasses are supported as of now, because the conditions must fit the new class) * Defaults to the first selected class (most of the time it is also the first joined class */ public function ChangeClass($sNewClass, $sAlias = null) { if (is_null($sAlias)) { $sAlias = $this->GetClassAlias(); } else { if (!array_key_exists($sAlias, $this->m_aClasses)) { // discard silently - necessary when recursing on the related nodes (see code below) return; } } $sCurrClass = $this->GetClassName($sAlias); if (!MetaModel::IsParentClass($sCurrClass, $sNewClass)) { throw new Exception("Could not change the search class from '{$sCurrClass}' to '{$sNewClass}'. Only child classes are permitted."); } // Change for this node // $this->m_aSelectedClasses[$sAlias] = $sNewClass; $this->m_aClasses[$sAlias] = $sNewClass; // Change for all the related node (yes, this was necessary with some queries - strange effects otherwise) // foreach ($this->m_aRelatedTo as $aRelatedTo) { $aRelatedTo['flt']->ChangeClass($sNewClass, $sAlias); } foreach ($this->m_aPointingTo as $sExtKeyAttCode => $aPointingTo) { foreach ($aPointingTo as $iOperatorCode => $aFilter) { foreach ($aFilter as $oExtFilter) { $oExtFilter->ChangeClass($sNewClass, $sAlias); } } } foreach ($this->m_aReferencedBy as $sForeignClass => $aReferences) { foreach ($aReferences as $sForeignExtKeyAttCode => $oForeignFilter) { $oForeignFilter->ChangeClass($sNewClass, $sAlias); } } }