public function Process(CMDBChange $oChange = null) { // Note: $oChange can be null, in which case the aim is to check what would be done // Debug... // if (false) { echo "<pre>\n"; echo "Attributes:\n"; print_r($this->m_aAttList); echo "ExtKeys:\n"; print_r($this->m_aExtKeys); echo "Reconciliation:\n"; print_r($this->m_aReconcilKeys); echo "Synchro scope:\n"; print_r($this->m_sSynchroScope); echo "Synchro changes:\n"; print_r($this->m_aOnDisappear); //echo "Data:\n"; //print_r($this->m_aData); echo "</pre>\n"; exit; } $aResult = array(); if (!is_null($this->m_sDateFormat) && strlen($this->m_sDateFormat) > 0) { // Translate dates from the source data // foreach ($this->m_aAttList as $sAttCode => $iCol) { $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); if ($oAttDef instanceof AttributeDateTime) { foreach ($this->m_aData as $iRow => $aRowData) { $sNewDate = utils::StringToTime($this->m_aData[$iRow][$iCol], $this->m_sDateFormat); if ($sNewDate !== false) { // Todo - improve the reporting $this->m_aData[$iRow][$iCol] = $sNewDate; } else { // Leave the cell unchanged $aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-DateFormat')); $aResult[$iRow][$sAttCode] = new CellStatus_Issue(null, $this->m_aData[$iRow][$iCol], Dict::S('UI:CSVReport-Row-Issue-DateFormat')); } } } } } // Compute the results // if (!is_null($this->m_sSynchroScope)) { $aVisited = array(); } $iPreviousTimeLimit = ini_get('max_execution_time'); $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); foreach ($this->m_aData as $iRow => $aRowData) { set_time_limit($iLoopTimeLimit); if (isset($aResult[$iRow]["__STATUS__"])) { // An issue at the earlier steps - skip the rest continue; } try { $oReconciliationFilter = new DBObjectSearch($this->m_sClass); $bSkipQuery = false; foreach ($this->m_aReconcilKeys as $sAttCode) { $valuecondition = null; if (array_key_exists($sAttCode, $this->m_aExtKeys)) { if ($this->IsNullExternalKeySpec($aRowData, $sAttCode)) { $oExtKey = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); if ($oExtKey->IsNullAllowed()) { $valuecondition = $oExtKey->GetNullValue(); $aResult[$iRow][$sAttCode] = new CellStatus_Void($oExtKey->GetNullValue()); } else { $aResult[$iRow][$sAttCode] = new CellStatus_NullIssue(); } } else { // The value has to be found or verified list($sQuery, $aMatches) = $this->ResolveExternalKey($aRowData, $sAttCode, $aResult[$iRow]); if (count($aMatches) == 1) { $oRemoteObj = reset($aMatches); // first item $valuecondition = $oRemoteObj->GetKey(); $aResult[$iRow][$sAttCode] = new CellStatus_Void($oRemoteObj->GetKey()); } elseif (count($aMatches) == 0) { $aResult[$iRow][$sAttCode] = new CellStatus_SearchIssue(); } else { $aResult[$iRow][$sAttCode] = new CellStatus_Ambiguous(null, count($aMatches), $sQuery); } } } else { // The value is given in the data row $iCol = $this->m_aAttList[$sAttCode]; if ($sAttCode == 'id') { $valuecondition = $aRowData[$iCol]; } else { $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); $valuecondition = $oAttDef->MakeValueFromString($aRowData[$iCol], $this->m_bLocalizedValues); } } if (is_null($valuecondition)) { $bSkipQuery = true; } else { $oReconciliationFilter->AddCondition($sAttCode, $valuecondition, '='); } } if ($bSkipQuery) { $aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Reconciliation')); } else { $oReconciliationSet = new CMDBObjectSet($oReconciliationFilter); switch ($oReconciliationSet->Count()) { case 0: $oTargetObj = $this->CreateObject($aResult, $iRow, $aRowData, $oChange); // $aResult[$iRow]["__STATUS__"]=> set in CreateObject $aVisited[] = $oTargetObj->GetKey(); break; case 1: $oTargetObj = $oReconciliationSet->Fetch(); $this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange); // $aResult[$iRow]["__STATUS__"]=> set in UpdateObject if (!is_null($this->m_sSynchroScope)) { $aVisited[] = $oTargetObj->GetKey(); } break; default: // Found several matches, ambiguous $aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::S('UI:CSVReport-Row-Issue-Ambiguous')); $aResult[$iRow]["id"] = new CellStatus_Ambiguous(0, $oReconciliationSet->Count(), $oReconciliationFilter->ToOql()); $aResult[$iRow]["finalclass"] = 'n/a'; } } } catch (Exception $e) { $aResult[$iRow]["__STATUS__"] = new RowStatus_Issue(Dict::Format('UI:CSVReport-Row-Issue-Internal', get_class($e), $e->getMessage())); } } if (!is_null($this->m_sSynchroScope)) { // Compute the delta between the scope and visited objects $oScopeSearch = DBObjectSearch::FromOQL($this->m_sSynchroScope); $oScopeSet = new DBObjectSet($oScopeSearch); while ($oObj = $oScopeSet->Fetch()) { $iObj = $oObj->GetKey(); if (!in_array($iObj, $aVisited)) { set_time_limit($iLoopTimeLimit); $iRow++; $this->UpdateMissingObject($aResult, $iRow, $oObj, $oChange); } } } set_time_limit($iPreviousTimeLimit); // Fill in the blanks - the result matrix is expected to be 100% complete // foreach ($this->m_aData as $iRow => $aRowData) { foreach ($this->m_aAttList as $iCol) { if (!array_key_exists($iCol, $aResult[$iRow])) { $aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]); } } foreach ($this->m_aExtKeys as $sAttCode => $aForeignAtts) { if (!array_key_exists($sAttCode, $aResult[$iRow])) { $aResult[$iRow][$sAttCode] = new CellStatus_Void('n/a'); } foreach ($aForeignAtts as $sForeignAttCode => $iCol) { if (!array_key_exists($iCol, $aResult[$iRow])) { // The foreign attribute is one of our reconciliation key $aResult[$iRow][$iCol] = new CellStatus_Void($aRowData[$iCol]); } } } } return $aResult; }