/** * Safety net: in case the change is not given, let's guarantee that it will * be set to the current ongoing change (or create a new one) */ protected function OnInsert() { if ($this->Get('change') <= 0) { $this->Set('change', CMDBObject::GetCurrentChange()); } parent::OnInsert(); }
/** * Apply the 'next-action' to the given object or redirect to the page that prompts for additional information if needed * @param $oP WebPage The page for the output * @param $oObj CMDBObject The object to process * @param $sNextAction string The code of the stimulus for the 'action' (i.e. Transition) to apply */ function ApplyNextAction(Webpage $oP, CMDBObject $oObj, $sNextAction) { // Here handle the apply stimulus $aTransitions = $oObj->EnumTransitions(); $aStimuli = MetaModel::EnumStimuli(get_class($oObj)); if (!isset($aTransitions[$sNextAction])) { // Invalid stimulus throw new ApplicationException(Dict::Format('UI:Error:Invalid_Stimulus_On_Object_In_State', $sNextAction, $oObj->GetName(), $oObj->GetStateLabel())); } // Get the list of missing mandatory fields for the target state, considering only the changes from the previous form (i.e don't prompt twice) $aExpectedAttributes = $oObj->GetExpectedAttributes($oObj->GetState(), $sNextAction, true); if (count($aExpectedAttributes) == 0) { // If all the mandatory fields are already present, just apply the transition silently... if ($oObj->ApplyStimulus($sNextAction)) { $oObj->DBUpdate(); } ReloadAndDisplay($oP, $oObj); } else { // redirect to the 'stimulus' action $oAppContext = new ApplicationContext(); //echo "<p>Missing Attributes <pre>".print_r($aExpectedAttributes, true)."</pre></p>\n"; $oP->add_header('Location: ' . utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?operation=stimulus&class=' . get_class($oObj) . '&stimulus=' . $sNextAction . '&id=' . $oObj->getKey() . '&' . $oAppContext->GetForLink()); } }
public function Process($iTimeLimit) { $aList = array(); foreach (MetaModel::GetClasses() as $sClass) { foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($oAttDef instanceof AttributeStopWatch) { foreach ($oAttDef->ListThresholds() as $iThreshold => $aThresholdData) { $iPercent = $aThresholdData['percent']; // could be different than the index ! $sNow = date('Y-m-d H:i:s'); $sExpression = "SELECT {$sClass} WHERE {$sAttCode}_laststart AND {$sAttCode}_{$iThreshold}_triggered = 0 AND {$sAttCode}_{$iThreshold}_deadline < '{$sNow}'"; $oFilter = DBObjectSearch::FromOQL($sExpression); $oSet = new DBObjectSet($oFilter); while (time() < $iTimeLimit && ($oObj = $oSet->Fetch())) { $sClass = get_class($oObj); $aList[] = $sClass . '::' . $oObj->GetKey() . ' ' . $sAttCode . ' ' . $iThreshold; // Execute planned actions // foreach ($aThresholdData['actions'] as $aActionData) { $sVerb = $aActionData['verb']; $aParams = $aActionData['params']; $aValues = array(); foreach ($aParams as $def) { if (is_string($def)) { // Old method (pre-2.1.0) non typed parameters $aValues[] = $def; } else { $sParamType = array_key_exists('type', $def) ? $def['type'] : 'string'; switch ($sParamType) { case 'int': $value = (int) $def['value']; break; case 'float': $value = (double) $def['value']; break; case 'bool': $value = (bool) $def['value']; break; case 'reference': $value = ${$def['value']}; break; case 'string': default: $value = (string) $def['value']; } $aValues[] = $value; } } $aCallSpec = array($oObj, $sVerb); call_user_func_array($aCallSpec, $aValues); } // Mark the threshold as "triggered" // $oSW = $oObj->Get($sAttCode); $oSW->MarkThresholdAsTriggered($iThreshold); $oObj->Set($sAttCode, $oSW); if ($oObj->IsModified()) { CMDBObject::SetTrackInfo("Automatic - threshold triggered"); $oMyChange = CMDBObject::GetCurrentChange(); $oObj->DBUpdateTracked($oMyChange, true); } // Activate any existing trigger // $sClassList = implode("', '", MetaModel::EnumParentClasses($sClass, ENUM_PARENT_CLASSES_ALL)); $oTriggerSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnThresholdReached AS t WHERE t.target_class IN ('{$sClassList}') AND stop_watch_code=:stop_watch_code AND threshold_index = :threshold_index"), array(), array('stop_watch_code' => $sAttCode, 'threshold_index' => $iThreshold)); while ($oTrigger = $oTriggerSet->Fetch()) { $oTrigger->DoActivate($oObj->ToArgs('this')); } } } } } } $iProcessed = count($aList); return "Triggered {$iProcessed} threshold(s):" . implode(", ", $aList); }
/** * Perform all the needed checks to delete one (or more) objects */ public static function DeleteObjects(WebPage $oP, $sClass, $aObjects, $bPreview, $sCustomOperation, $aContextData = array()) { $oDeletionPlan = new DeletionPlan(); foreach ($aObjects as $oObj) { if ($bPreview) { $oObj->CheckToDelete($oDeletionPlan); } else { $oObj->DBDeleteTracked(CMDBObject::GetCurrentChange(), null, $oDeletionPlan); } } if ($bPreview) { if (count($aObjects) == 1) { $oObj = $aObjects[0]; $oP->add("<h1>" . Dict::Format('UI:Delete:ConfirmDeletionOf_Name', $oObj->GetName()) . "</h1>\n"); } else { $oP->add("<h1>" . Dict::Format('UI:Delete:ConfirmDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)) . "</h1>\n"); } // Explain what should be done // $aDisplayData = array(); foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes) { foreach ($aDeletes as $iId => $aData) { $oToDelete = $aData['to_delete']; $bAutoDel = $aData['mode'] == DEL_SILENT || $aData['mode'] == DEL_AUTO; if (array_key_exists('issue', $aData)) { if ($bAutoDel) { if (isset($aData['requested_explicitely'])) { $sConsequence = Dict::Format('UI:Delete:CannotDeleteBecause', $aData['issue']); } else { $sConsequence = Dict::Format('UI:Delete:ShouldBeDeletedAtomaticallyButNotPossible', $aData['issue']); } } else { $sConsequence = Dict::Format('UI:Delete:MustBeDeletedManuallyButNotPossible', $aData['issue']); } } else { if ($bAutoDel) { if (isset($aData['requested_explicitely'])) { $sConsequence = ''; // not applicable } else { $sConsequence = Dict::S('UI:Delete:WillBeDeletedAutomatically'); } } else { $sConsequence = Dict::S('UI:Delete:MustBeDeletedManually'); } } $aDisplayData[] = array('class' => MetaModel::GetName(get_class($oToDelete)), 'object' => $oToDelete->GetHyperLink(), 'consequence' => $sConsequence); } } foreach ($oDeletionPlan->ListUpdates() as $sRemoteClass => $aToUpdate) { foreach ($aToUpdate as $iId => $aData) { $oToUpdate = $aData['to_reset']; if (array_key_exists('issue', $aData)) { $sConsequence = Dict::Format('UI:Delete:CannotUpdateBecause_Issue', $aData['issue']); } else { $sConsequence = Dict::Format('UI:Delete:WillAutomaticallyUpdate_Fields', $aData['attributes_list']); } $aDisplayData[] = array('class' => MetaModel::GetName(get_class($oToUpdate)), 'object' => $oToUpdate->GetHyperLink(), 'consequence' => $sConsequence); } } $iImpactedIndirectly = $oDeletionPlan->GetTargetCount() - count($aObjects); if ($iImpactedIndirectly > 0) { if (count($aObjects) == 1) { $oObj = $aObjects[0]; $oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencing_Object', $iImpactedIndirectly, $oObj->GetName())); } else { $oP->p(Dict::Format('UI:Delete:Count_Objects/LinksReferencingTheObjects', $iImpactedIndirectly)); } $oP->p(Dict::S('UI:Delete:ReferencesMustBeDeletedToEnsureIntegrity')); } if ($iImpactedIndirectly > 0 || $oDeletionPlan->FoundStopper()) { $aDisplayConfig = array(); $aDisplayConfig['class'] = array('label' => 'Class', 'description' => ''); $aDisplayConfig['object'] = array('label' => 'Object', 'description' => ''); $aDisplayConfig['consequence'] = array('label' => 'Consequence', 'description' => Dict::S('UI:Delete:Consequence+')); $oP->table($aDisplayConfig, $aDisplayData); } if ($oDeletionPlan->FoundStopper()) { if ($oDeletionPlan->FoundSecurityIssue()) { $oP->p(Dict::S('UI:Delete:SorryDeletionNotAllowed')); } elseif ($oDeletionPlan->FoundManualOperation()) { $oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations')); } else { $oP->p(Dict::S('UI:Delete:PleaseDoTheManualOperations')); } $oAppContext = new ApplicationContext(); $oP->add("<form method=\"post\">\n"); $oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"" . utils::ReadParam('transaction_id') . "\">\n"); $oP->add("<input type=\"button\" onclick=\"window.history.back();\" value=\"" . Dict::S('UI:Button:Back') . "\">\n"); $oP->add("<input DISABLED type=\"submit\" name=\"\" value=\"" . Dict::S('UI:Button:Delete') . "\">\n"); $oP->add($oAppContext->GetForForm()); $oP->add("</form>\n"); } else { if (count($aObjects) == 1) { $oObj = $aObjects[0]; $id = $oObj->GetKey(); $oP->p('<h1>' . Dict::Format('UI:Delect:Confirm_Object', $oObj->GetHyperLink()) . '</h1>'); } else { $oP->p('<h1>' . Dict::Format('UI:Delect:Confirm_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)) . '</h1>'); } foreach ($aObjects as $oObj) { $aKeys[] = $oObj->GetKey(); } $oFilter = new DBObjectSearch($sClass); $oFilter->AddCondition('id', $aKeys, 'IN'); $oSet = new CMDBobjectSet($oFilter); $oP->add('<div id="0">'); CMDBAbstractObject::DisplaySet($oP, $oSet, array('display_limit' => false, 'menu' => false)); $oP->add("</div>\n"); $oP->add("<form method=\"post\">\n"); foreach ($aContextData as $sKey => $value) { $oP->add("<input type=\"hidden\" name=\"{$sKey}\" value=\"{$value}\">\n"); } $oP->add("<input type=\"hidden\" name=\"transaction_id\" value=\"" . utils::GetNewTransactionId() . "\">\n"); $oP->add("<input type=\"hidden\" name=\"operation\" value=\"{$sCustomOperation}\">\n"); $oP->add("<input type=\"hidden\" name=\"filter\" value=\"" . $oFilter->Serialize() . "\">\n"); $oP->add("<input type=\"hidden\" name=\"class\" value=\"{$sClass}\">\n"); foreach ($aObjects as $oObj) { $oP->add("<input type=\"hidden\" name=\"selectObject[]\" value=\"" . $oObj->GetKey() . "\">\n"); } $oP->add("<input type=\"button\" onclick=\"window.history.back();\" value=\"" . Dict::S('UI:Button:Back') . "\">\n"); $oP->add("<input type=\"submit\" name=\"\" value=\"" . Dict::S('UI:Button:Delete') . "\">\n"); $oAppContext = new ApplicationContext(); $oP->add($oAppContext->GetForForm()); $oP->add("</form>\n"); } } else { // Execute the deletion // if (count($aObjects) == 1) { $oObj = $aObjects[0]; $oP->add("<h1>" . Dict::Format('UI:Title:DeletionOf_Object', $oObj->GetName()) . "</h1>\n"); } else { $oP->add("<h1>" . Dict::Format('UI:Title:BulkDeletionOf_Count_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass)) . "</h1>\n"); } // Security - do not allow the user to force a forbidden delete by the mean of page arguments... if ($oDeletionPlan->FoundSecurityIssue()) { throw new CoreException(Dict::S('UI:Error:NotEnoughRightsToDelete')); } if ($oDeletionPlan->FoundManualOperation()) { throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseManualOpNeeded')); } if ($oDeletionPlan->FoundManualDelete()) { throw new CoreException(Dict::S('UI:Error:CannotDeleteBecauseOfDepencies')); } // Report deletions // $aDisplayData = array(); foreach ($oDeletionPlan->ListDeletes() as $sTargetClass => $aDeletes) { foreach ($aDeletes as $iId => $aData) { $oToDelete = $aData['to_delete']; if (isset($aData['requested_explicitely'])) { $sMessage = Dict::S('UI:Delete:Deleted'); } else { $sMessage = Dict::S('UI:Delete:AutomaticallyDeleted'); } $aDisplayData[] = array('class' => MetaModel::GetName(get_class($oToDelete)), 'object' => $oToDelete->GetName(), 'consequence' => $sMessage); } } // Report updates // foreach ($oDeletionPlan->ListUpdates() as $sTargetClass => $aToUpdate) { foreach ($aToUpdate as $iId => $aData) { $oToUpdate = $aData['to_reset']; $aDisplayData[] = array('class' => MetaModel::GetName(get_class($oToUpdate)), 'object' => $oToUpdate->GetHyperLink(), 'consequence' => Dict::Format('UI:Delete:AutomaticResetOf_Fields', $aData['attributes_list'])); } } // Report automatic jobs // if ($oDeletionPlan->GetTargetCount() > 0) { if (count($aObjects) == 1) { $oObj = $aObjects[0]; $oP->p(Dict::Format('UI:Delete:CleaningUpRefencesTo_Object', $oObj->GetName())); } else { $oP->p(Dict::Format('UI:Delete:CleaningUpRefencesTo_Several_ObjectsOf_Class', count($aObjects), MetaModel::GetName($sClass))); } $aDisplayConfig = array(); $aDisplayConfig['class'] = array('label' => 'Class', 'description' => ''); $aDisplayConfig['object'] = array('label' => 'Object', 'description' => ''); $aDisplayConfig['consequence'] = array('label' => 'Done', 'description' => Dict::S('UI:Delete:Done+')); $oP->table($aDisplayConfig, $aDisplayData); } } }
protected function ForgotPwdGo() { $sAuthUser = utils::ReadParam('auth_user', '', true, 'raw_data'); try { UserRights::Login($sAuthUser); // Set the user's language (if possible!) $oUser = UserRights::GetUserObject(); if ($oUser == null) { throw new Exception(Dict::Format('UI:ResetPwd-Error-WrongLogin', $sAuthUser)); } if (!MetaModel::IsValidAttCode(get_class($oUser), 'reset_pwd_token')) { throw new Exception(Dict::S('UI:ResetPwd-Error-NotPossible')); } if (!$oUser->CanChangePassword()) { throw new Exception(Dict::S('UI:ResetPwd-Error-FixedPwd')); } $sTo = $oUser->GetResetPasswordEmail(); // throws Exceptions if not allowed if ($sTo == '') { throw new Exception(Dict::S('UI:ResetPwd-Error-NoEmail')); } // This token allows the user to change the password without knowing the previous one $sToken = substr(md5(APPROOT . uniqid()), 0, 16); $oUser->Set('reset_pwd_token', $sToken); CMDBObject::SetTrackInfo('Reset password'); $oUser->DBUpdate(); $oEmail = new Email(); $oEmail->SetRecipientTO($sTo); $sFrom = MetaModel::GetConfig()->Get('forgot_password_from'); if ($sFrom == '') { $sFrom = $sTo; } $oEmail->SetRecipientFrom($sFrom); $oEmail->SetSubject(Dict::S('UI:ResetPwd-EmailSubject')); $sResetUrl = utils::GetAbsoluteUrlAppRoot() . 'pages/UI.php?loginop=reset_pwd&auth_user='******'login')) . '&token=' . urlencode($sToken); $oEmail->SetBody(Dict::Format('UI:ResetPwd-EmailBody', $sResetUrl)); $iRes = $oEmail->Send($aIssues, true); switch ($iRes) { //case EMAIL_SEND_PENDING: case EMAIL_SEND_OK: break; case EMAIL_SEND_ERROR: default: IssueLog::Error('Failed to send the email with the NEW password for ' . $oUser->Get('friendlyname') . ': ' . implode(', ', $aIssues)); throw new Exception(Dict::S('UI:ResetPwd-Error-Send')); } $this->DisplayLoginHeader(); $this->add("<div id=\"login\">\n"); $this->add("<h1>" . Dict::S('UI:Login:ForgotPwdForm') . "</h1>\n"); $this->add("<p>" . Dict::S('UI:ResetPwd-EmailSent') . "</p>"); $this->add("<form method=\"post\">\n"); $this->add("<table>\n"); $this->add("<tr><td colspan=\"2\" class=\"center v-spacer\"><input type=\"button\" onClick=\"window.close();\" value=\"" . Dict::S('UI:Button:Done') . "\" /></td></tr>\n"); $this->add("</table>\n"); $this->add("</form>\n"); $this->add("</div\n"); } catch (Exception $e) { $this->DisplayForgotPwdForm(true, $e->getMessage()); } }
if (isset($_REQUEST['service_category']) && !empty($_REQUEST['service_category'])) { $sServiceClass = $_REQUEST['service_category']; if (!class_exists($sServiceClass)) { // not a valid class name (not a PHP class at all) throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '{$sServiceClass}' is not a PHP class"); } elseif (!is_subclass_of($sServiceClass, 'WebServicesBase')) { // not a valid class name (not deriving from WebServicesBase) throw new SoapFault("iTop SOAP server", "Invalid argument service_category: '{$sServiceClass}' is not derived from WebServicesBase"); } else { $oSoapServer->setClass($sServiceClass, null); } } else { $oSoapServer->setClass('BasicServices', null); } if ($_SERVER["REQUEST_METHOD"] == "POST") { CMDBObject::SetTrackOrigin('webservice-soap'); $oSoapServer->handle(); } else { echo "This SOAP server can handle the following functions: "; $aFunctions = $oSoapServer->getFunctions(); echo "<ul>\n"; foreach ($aFunctions as $sFunc) { if ($sFunc == 'GetWSDLContents') { continue; } echo "<li>{$sFunc}</li>\n"; } echo "</ul>\n"; echo "<p>Here the <a href=\"{$sWsdlUri}\">WSDL file</a><p>"; echo "You may also want to try the following service categories: "; echo "<ul>\n";
/** * Registering tracking information. Any further object modification be associated with the given comment, when the modification gets recorded into the DB * * @param StdClass $oData Structured input data. Must contain 'comment'. * @return void * @throws Exception * @api */ public static function InitTrackingComment($oData) { $sComment = self::GetMandatoryParam($oData, 'comment'); CMDBObject::SetTrackInfo($sComment); }
public function CreateAdministrator($sAdminUser, $sAdminPwd, $sLanguage = 'EN US') { CMDBObject::SetTrackInfo('Initialization'); $oChange = CMDBObject::GetCurrentChange(); $iContactId = 0; // Support drastic data model changes: no organization class (or not writable)! if (MetaModel::IsValidClass('Organization') && !MetaModel::IsAbstract('Organization')) { $oOrg = new Organization(); $oOrg->Set('name', 'My Company/Department'); $oOrg->Set('code', 'SOMECODE'); $iOrgId = $oOrg->DBInsertTrackedNoReload($oChange, true); // Support drastic data model changes: no Person class (or not writable)! if (MetaModel::IsValidClass('Person') && !MetaModel::IsAbstract('Person')) { $oContact = new Person(); $oContact->Set('name', 'My last name'); $oContact->Set('first_name', 'My first name'); if (MetaModel::IsValidAttCode('Person', 'org_id')) { $oContact->Set('org_id', $iOrgId); } if (MetaModel::IsValidAttCode('Person', 'phone')) { $oContact->Set('phone', '+00 000 000 000'); } $oContact->Set('email', '*****@*****.**'); $iContactId = $oContact->DBInsertTrackedNoReload($oChange, true); } } $oUser = new UserLocal(); $oUser->Set('login', $sAdminUser); $oUser->Set('password', $sAdminPwd); if (MetaModel::IsValidAttCode('UserLocal', 'contactid') && $iContactId != 0) { $oUser->Set('contactid', $iContactId); } $oUser->Set('language', $sLanguage); // Language was chosen during the installation // Add this user to the very specific 'admin' profile $oAdminProfile = MetaModel::GetObjectFromOQL("SELECT URP_Profiles WHERE name = :name", array('name' => ADMIN_PROFILE_NAME), true); if (is_object($oAdminProfile)) { $oUserProfile = new URP_UserProfile(); //$oUserProfile->Set('userid', $iUserId); $oUserProfile->Set('profileid', $oAdminProfile->GetKey()); $oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile'); //$oUserProfile->DBInsertTrackedNoReload($oChange, true /* skip security */); $oSet = DBObjectSet::FromObject($oUserProfile); $oUser->Set('profile_list', $oSet); } $iUserId = $oUser->DBInsertTrackedNoReload($oChange, true); return true; }
public function DBDeleteTracked(CMDBChange $oChange, $bSkipStrongSecurity = null, &$oDeletionPlan = null) { CMDBObject::SetCurrentChange($oChange); $this->DBDelete($oDeletionPlan); }
} $sOperation = RestUtils::GetMandatoryParam($aJsonData, 'operation'); if ($sOperation == 'list_operations') { $oResult = new RestResultListOperations(); $oResult->message = "Operations: " . count($aOpToRestService); $oResult->version = $sVersion; foreach ($aOpToRestService as $sVerb => $aOpData) { $oResult->AddOperation($sVerb, $aOpData['description'], get_class($aOpData['service_provider'])); } } else { if (!array_key_exists($sOperation, $aOpToRestService)) { throw new Exception("Unknown verb '{$sOperation}' in version '{$sVersion}'", RestResult::UNKNOWN_OPERATION); } $oRS = $aOpToRestService[$sOperation]['service_provider']; $sProvider = get_class($oRS); CMDBObject::SetTrackOrigin('webservice-rest'); $oResult = $oRS->ExecOperation($sVersion, $sOperation, $aJsonData); } } catch (Exception $e) { $oResult = new RestResult(); if ($e->GetCode() == 0) { $oResult->code = RestResult::INTERNAL_ERROR; } else { $oResult->code = $e->GetCode(); } $oResult->message = "Error: " . $e->GetMessage(); } // Output the results // $sResponse = json_encode($oResult); $oP->add_header('Access-Control-Allow-Origin: *');
/** * Process the CSV data, for real or as a simulation * @param WebPage $oPage The page used to display the wizard * @param bool $bSimulate Whether or not to simulate the data load * @return array The CSV lines in error that were rejected from the load (with the header line - if any) or null */ function ProcessCSVData(WebPage $oPage, $bSimulate = true) { $aResult = array(); $sCSVData = utils::ReadParam('csvdata', '', false, 'raw_data'); $sCSVDataTruncated = utils::ReadParam('csvdata_truncated', '', false, 'raw_data'); $sSeparator = utils::ReadParam('separator', ',', false, 'raw_data'); $sTextQualifier = utils::ReadParam('text_qualifier', '"', false, 'raw_data'); $bHeaderLine = utils::ReadParam('header_line', '0') == 1; $iSkippedLines = 0; if (utils::ReadParam('box_skiplines', '0') == 1) { $iSkippedLines = utils::ReadParam('nb_skipped_lines', '0'); } $sClassName = utils::ReadParam('class_name', '', false, 'class'); $aFieldsMapping = utils::ReadParam('field', array(), false, 'raw_data'); $aSearchFields = utils::ReadParam('search_field', array(), false, 'field_name'); $iCurrentStep = $bSimulate ? 4 : 5; $bAdvanced = utils::ReadParam('advanced', 0); $sEncoding = utils::ReadParam('encoding', 'UTF-8'); $sSynchroScope = utils::ReadParam('synchro_scope', '', false, 'raw_data'); if (!empty($sSynchroScope)) { $oSearch = DBObjectSearch::FromOQL($sSynchroScope); $sClassName = $oSearch->GetClass(); // If a synchronization scope is set, then the class is fixed ! $oSet = new DBObjectSet($oSearch); $iCount = $oSet->Count(); DisplaySynchroBanner($oPage, $sClassName, $iCount); $sClassesSelect = "<select id=\"select_class_name\" name=\"class_name\"><option value=\"{$sClassName}\" selected>" . MetaModel::GetName($sClassName) . "</option>"; $aSynchroUpdate = utils::ReadParam('synchro_update', array()); } else { $sSynchroScope = ''; $aSynchroUpdate = null; } // Parse the data set $oCSVParser = new CSVParser($sCSVData, $sSeparator, $sTextQualifier); $aData = $oCSVParser->ToArray($iSkippedLines); $iRealSkippedLines = $iSkippedLines; if ($bHeaderLine) { $aResult[] = $sTextQualifier . implode($sTextQualifier . $sSeparator . $sTextQualifier, array_shift($aData)) . $sTextQualifier; // Remove the first line and store it in case of error $iRealSkippedLines++; } // Format for the line numbers $sMaxLen = strlen('' . count($aData)) < 3 ? 3 : strlen('' . count($aData)); // Pad line numbers to the appropriate number of chars, but at least 3 // Compute the list of search/reconciliation criteria $aSearchKeys = array(); foreach ($aSearchFields as $index => $sDummy) { $sSearchField = $aFieldsMapping[$index]; $aMatches = array(); if (preg_match('/(.+)->(.+)/', $sSearchField, $aMatches) > 0) { $sSearchField = $aMatches[1]; $aSearchKeys[$aMatches[1]] = ''; } else { $aSearchKeys[$sSearchField] = ''; } if (!MetaModel::IsValidFilterCode($sClassName, $sSearchField)) { // Remove invalid or unmapped search fields $aSearchFields[$index] = null; unset($aSearchKeys[$sSearchField]); } } // Compute the list of fields and external keys to process $aExtKeys = array(); $aAttributes = array(); $aExternalKeysByColumn = array(); foreach ($aFieldsMapping as $iNumber => $sAttCode) { $iIndex = $iNumber - 1; if (!empty($sAttCode) && $sAttCode != ':none:' && $sAttCode != 'finalclass') { if (preg_match('/(.+)->(.+)/', $sAttCode, $aMatches) > 0) { $sAttribute = $aMatches[1]; $sField = $aMatches[2]; $aExtKeys[$sAttribute][$sField] = $iIndex; $aExternalKeysByColumn[$iIndex] = $sAttribute; } else { if ($sAttCode == 'id') { $aAttributes['id'] = $iIndex; } else { $oAttDef = MetaModel::GetAttributeDef($sClassName, $sAttCode); if ($oAttDef->IsExternalKey()) { $aExtKeys[$sAttCode]['id'] = $iIndex; $aExternalKeysByColumn[$iIndex] = $sAttCode; } else { $aAttributes[$sAttCode] = $iIndex; } } } } } $oMyChange = null; if (!$bSimulate) { // We're doing it for real, let's create a change $sUserString = CMDBChange::GetCurrentUserName() . ' (CSV)'; CMDBObject::SetTrackInfo($sUserString); CMDBObject::SetTrackOrigin('csv-interactive'); $oMyChange = CMDBObject::GetCurrentChange(); } $oBulk = new BulkChange($sClassName, $aData, $aAttributes, $aExtKeys, array_keys($aSearchKeys), empty($sSynchroScope) ? null : $sSynchroScope, $aSynchroUpdate, null, true); $oBulk->SetReportHtml(); $oPage->add('<input type="hidden" name="csvdata_truncated" id="csvdata_truncated" value="' . htmlentities($sCSVDataTruncated, ENT_QUOTES, 'UTF-8') . '"/>'); $aRes = $oBulk->Process($oMyChange); $sHtml = '<table id="bulk_preview" style="border-collapse: collapse;">'; $sHtml .= '<tr><th style="padding:2px;border-right: 2px #fff solid;">Line</th>'; $sHtml .= '<th style="padding:2px;border-right: 2px #fff solid;">Status</th>'; $sHtml .= '<th style="padding:2px;border-right: 2px #fff solid;">Object</th>'; foreach ($aFieldsMapping as $iNumber => $sAttCode) { if (!empty($sAttCode) && $sAttCode != ':none:' && $sAttCode != 'finalclass') { $sHtml .= "<th style=\"padding:2px;border-right: 2px #fff solid;\">" . MetaModel::GetLabel($sClassName, $sAttCode) . "</th>"; } } $sHtml .= '<th>Message</th>'; $sHtml .= '</tr>'; $iErrors = 0; $iCreated = 0; $iModified = 0; $iUnchanged = 0; foreach ($aRes as $iLine => $aResRow) { $oStatus = $aResRow['__STATUS__']; $sUrl = ''; $sMessage = ''; $sCSSRowClass = ''; $sCSSMessageClass = 'cell_ok'; switch (get_class($oStatus)) { case 'RowStatus_NoChange': $iUnchanged++; $sFinalClass = $aResRow['finalclass']; $oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue()); $sUrl = $oObj->GetHyperlink(); $sStatus = '<img src="../images/unchanged.png" title="' . Dict::S('UI:CSVReport-Icon-Unchanged') . '">'; $sCSSRowClass = 'row_unchanged'; break; case 'RowStatus_Modify': $iModified++; $sFinalClass = $aResRow['finalclass']; $oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue()); $sUrl = $oObj->GetHyperlink(); $sStatus = '<img src="../images/modified.png" title="' . Dict::S('UI:CSVReport-Icon-Modified') . '">'; $sCSSRowClass = 'row_modified'; break; case 'RowStatus_Disappeared': $iModified++; $sFinalClass = $aResRow['finalclass']; $oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue()); $sUrl = $oObj->GetHyperlink(); $sStatus = '<img src="../images/delete.png" title="' . Dict::S('UI:CSVReport-Icon-Missing') . '">'; $sCSSRowClass = 'row_modified'; if ($bSimulate) { $sMessage = Dict::S('UI:CSVReport-Object-MissingToUpdate'); } else { $sMessage = Dict::S('UI:CSVReport-Object-MissingUpdated'); } break; case 'RowStatus_NewObj': $iCreated++; $sFinalClass = $aResRow['finalclass']; $sStatus = '<img src="../images/added.png" title="' . Dict::S('UI:CSVReport-Icon-Created') . '">'; $sCSSRowClass = 'row_added'; if ($bSimulate) { $sMessage = Dict::S('UI:CSVReport-Object-ToCreate'); } else { $sFinalClass = $aResRow['finalclass']; $oObj = MetaModel::GetObject($sFinalClass, $aResRow['id']->GetPureValue()); $sUrl = $oObj->GetHyperlink(); $sMessage = Dict::S('UI:CSVReport-Object-Created'); } break; case 'RowStatus_Issue': $iErrors++; $sMessage .= $oPage->GetP($oStatus->GetDescription()); $sStatus = '<img src="../images/error.png" title="' . Dict::S('UI:CSVReport-Icon-Error') . '">'; //translate $sCSSMessageClass = 'cell_error'; $sCSSRowClass = 'row_error'; if (array_key_exists($iLine, $aData)) { $aRow = $aData[$iLine]; $aResult[] = $sTextQualifier . implode($sTextQualifier . $sSeparator . $sTextQualifier, $aRow) . $sTextQualifier; // Remove the first line and store it in case of error } break; } $sHtml .= '<tr class="' . $sCSSRowClass . '">'; $sHtml .= "<td style=\"background-color:#f1f1f1;border-right:2px #fff solid;\">" . sprintf("%0{$sMaxLen}d", 1 + $iLine + $iRealSkippedLines) . "</td>"; $sHtml .= "<td style=\"text-align:center;background-color:#f1f1f1;border-right:2px #fff solid;\">{$sStatus}</td>"; $sHtml .= "<td style=\"text-align:center;background-color:#f1f1f1;\">{$sUrl}</td>"; foreach ($aFieldsMapping as $iNumber => $sAttCode) { if (!empty($sAttCode) && $sAttCode != ':none:' && $sAttCode != 'finalclass') { $oCellStatus = $aResRow[$iNumber - 1]; $sCellMessage = ''; if (isset($aExternalKeysByColumn[$iNumber - 1])) { $sExtKeyName = $aExternalKeysByColumn[$iNumber - 1]; $oExtKeyCellStatus = $aResRow[$sExtKeyName]; switch (get_class($oExtKeyCellStatus)) { case 'CellStatus_Issue': case 'CellStatus_SearchIssue': case 'CellStatus_NullIssue': $sCellMessage .= $oPage->GetP($oExtKeyCellStatus->GetDescription()); break; case 'CellStatus_Ambiguous': $sCellMessage .= $oPage->GetP($oExtKeyCellStatus->GetDescription()); break; default: // Do nothing } } $sHtmlValue = $oCellStatus->GetDisplayableValue(); switch (get_class($oCellStatus)) { case 'CellStatus_Issue': $sCellMessage .= $oPage->GetP($oCellStatus->GetDescription()); $sHtml .= '<td class="cell_error" style="border-right:1px #eee solid;">' . Dict::Format('UI:CSVReport-Object-Error', $sHtmlValue) . $sCellMessage . '</td>'; break; case 'CellStatus_SearchIssue': $sCellMessage .= $oPage->GetP($oCellStatus->GetDescription()); $sHtml .= '<td class="cell_error">ERROR: ' . $sHtmlValue . $sCellMessage . '</td>'; break; case 'CellStatus_Ambiguous': $sCellMessage .= $oPage->GetP($oCellStatus->GetDescription()); $sHtml .= '<td class="cell_error" style="border-right:1px #eee solid;">' . Dict::Format('UI:CSVReport-Object-Ambiguous', $sHtmlValue) . $sCellMessage . '</td>'; break; case 'CellStatus_Modify': $sHtml .= '<td class="cell_modified" style="border-right:1px #eee solid;"><b>' . $sHtmlValue . '</b></td>'; break; default: $sHtml .= '<td class="cell_ok" style="border-right:1px #eee solid;">' . $sHtmlValue . $sCellMessage . '</td>'; } } } $sHtml .= "<td class=\"{$sCSSMessageClass}\" style=\"background-color:#f1f1f1;\">{$sMessage}</td>"; $sHtml .= '</tr>'; } $iUnchanged = count($aRes) - $iErrors - $iModified - $iCreated; $sHtml .= '</table>'; $oPage->add('<div class="wizContainer" style="width:auto;display:inline-block;">'); $oPage->add('<form enctype="multipart/form-data" id="wizForm" method="post">'); $oPage->add('<input type="hidden" name="step" value="' . ($iCurrentStep + 1) . '"/>'); $oPage->add('<input type="hidden" name="separator" value="' . htmlentities($sSeparator, ENT_QUOTES, 'UTF-8') . '"/>'); $oPage->add('<input type="hidden" name="text_qualifier" value="' . htmlentities($sTextQualifier, ENT_QUOTES, 'UTF-8') . '"/>'); $oPage->add('<input type="hidden" name="header_line" value="' . $bHeaderLine . '"/>'); $oPage->add('<input type="hidden" name="nb_skipped_lines" value="' . utils::ReadParam('nb_skipped_lines', '0') . '"/>'); $oPage->add('<input type="hidden" name="box_skiplines" value="' . utils::ReadParam('box_skiplines', '0') . '"/>'); $oPage->add('<input type="hidden" name="csvdata" value="' . htmlentities($sCSVData, ENT_QUOTES, 'UTF-8') . '"/>'); $oPage->add('<input type="hidden" name="csvdata_truncated" value="' . htmlentities($sCSVDataTruncated, ENT_QUOTES, 'UTF-8') . '"/>'); $oPage->add('<input type="hidden" name="class_name" value="' . $sClassName . '"/>'); $oPage->add('<input type="hidden" name="advanced" value="' . $bAdvanced . '"/>'); $oPage->add('<input type="hidden" name="encoding" value="' . $sEncoding . '"/>'); $oPage->add('<input type="hidden" name="synchro_scope" value="' . $sSynchroScope . '"/>'); if (!empty($sSynchroScope)) { foreach ($aSynchroUpdate as $sKey => $value) { $oPage->add('<input type="hidden" name="synchro_update[' . $sKey . ']" value="' . $value . '"/>'); } } foreach ($aFieldsMapping as $iNumber => $sAttCode) { $oPage->add('<input type="hidden" name="field[' . $iNumber . ']" value="' . $sAttCode . '"/>'); } foreach ($aSearchFields as $index => $sDummy) { $oPage->add('<input type="hidden" name="search_field[' . $index . ']" value="1"/>'); } $aDisplayFilters = array(); if ($bSimulate) { $aDisplayFilters['unchanged'] = Dict::S('UI:CSVImport:ObjectsWillStayUnchanged'); $aDisplayFilters['modified'] = Dict::S('UI:CSVImport:ObjectsWillBeModified'); $aDisplayFilters['added'] = Dict::S('UI:CSVImport:ObjectsWillBeAdded'); $aDisplayFilters['errors'] = Dict::S('UI:CSVImport:ObjectsWillHaveErrors'); } else { $aDisplayFilters['unchanged'] = Dict::S('UI:CSVImport:ObjectsRemainedUnchanged'); $aDisplayFilters['modified'] = Dict::S('UI:CSVImport:ObjectsWereModified'); $aDisplayFilters['added'] = Dict::S('UI:CSVImport:ObjectsWereAdded'); $aDisplayFilters['errors'] = Dict::S('UI:CSVImport:ObjectsHadErrors'); } $oPage->add('<p><input type="checkbox" checked id="show_unchanged" onClick="ToggleRows(\'row_unchanged\')"/> <img src="../images/unchanged.png"> ' . sprintf($aDisplayFilters['unchanged'], $iUnchanged) . '  '); $oPage->add('<input type="checkbox" checked id="show_modified" onClick="ToggleRows(\'row_modified\')"/> <img src="../images/modified.png"> ' . sprintf($aDisplayFilters['modified'], $iModified) . '  '); $oPage->add('<input type="checkbox" checked id="show_created" onClick="ToggleRows(\'row_added\')"/> <img src="../images/added.png"> ' . sprintf($aDisplayFilters['added'], $iCreated) . '  '); $oPage->add('<input type="checkbox" checked id="show_errors" onClick="ToggleRows(\'row_error\')"/> <img src="../images/error.png"> ' . sprintf($aDisplayFilters['errors'], $iErrors) . '</p>'); $oPage->add('<div class="white" style="display:inline-block">'); $oPage->add($sHtml); $oPage->add('</div> <!-- end of preview -->'); $oPage->add('<p>'); if ($bSimulate) { $oPage->add('<input type="button" value="' . Dict::S('UI:Button:Restart') . '" onClick="CSVRestart()"/> '); } $oPage->add('<input type="button" value="' . Dict::S('UI:Button:Back') . '" onClick="CSVGoBack()"/> '); $bShouldConfirm = false; if ($bSimulate) { // if there are *too many* changes, we should ask the user for a confirmation if (count($aRes) >= MetaModel::GetConfig()->Get('csv_import_min_object_confirmation')) { $fErrorsPercentage = 100.0 * $iErrors / count($aRes); if ($fErrorsPercentage >= MetaModel::GetConfig()->Get('csv_import_errors_percentage')) { $sMessage = Dict::Format('UI:CSVReport-Stats-Errors', $fErrorsPercentage); $bShouldConfirm = true; } $fCreatedPercentage = 100.0 * $iCreated / count($aRes); if ($fCreatedPercentage >= MetaModel::GetConfig()->Get('csv_import_creations_percentage')) { $sMessage = Dict::Format('UI:CSVReport-Stats-Created', $fCreatedPercentage); $bShouldConfirm = true; } $fModifiedPercentage = 100.0 * $iModified / count($aRes); if ($fModifiedPercentage >= MetaModel::GetConfig()->Get('csv_import_modifications_percentage')) { $sMessage = Dict::Format('UI:CSVReport-Stats-Modified', $fModifiedPercentage); $bShouldConfirm = true; } } $iCount = count($aRes); //$oPage->add('<input type="submit" value="'.Dict::S('UI:Button:DoImport').'" onClick="$(\'#wizForm\').block();"/></p>'); $sConfirm = $bShouldConfirm ? 'true' : 'false'; $oPage->add('<input type="button" value="' . Dict::S('UI:Button:DoImport') . "\" onClick=\"return DoSubmit({$sConfirm});\"/></p>"); } else { $oPage->add('<input type="submit" value="' . Dict::S('UI:Button:Done') . '"/></p>'); } $oPage->add('</form>'); $oPage->add('</div> <!-- end of wizForm -->'); if ($bShouldConfirm) { $sYesButton = Dict::S('UI:Button:Ok'); $sNoButton = Dict::S('UI:Button:Cancel'); $oPage->add('<div id="dlg_confirmation" title="' . htmlentities(Dict::S('UI:CSVImportConfirmTitle'), ENT_QUOTES, 'UTF-8') . '">'); $oPage->add('<p style="text-align:center"><b>' . $sMessage . '</b></p>'); $oPage->add('<p style="text-align:center">' . htmlentities(Dict::S('UI:CSVImportConfirmMessage'), ENT_QUOTES, 'UTF-8') . '</p>'); $oPage->add('<div id="confirmation_chart"></div>'); $oPage->add('</div> <!-- end of dlg_confirmation -->'); $oPage->add_ready_script(<<<EOF \t\$('#dlg_confirmation').dialog( \t\t{ \t\t\theight: 'auto', \t\t\twidth: 500, \t\t\tmodal:true, \t\t\tautoOpen: false, \t\t\tbuttons: \t\t\t{ \t\t\t\t'{$sYesButton}': RunImport, \t\t\t\t'{$sNoButton}': CancelImport \t\t\t} \t\t}); \t\tswfobject.embedSWF(\t"../images/open-flash-chart.swf", \t\t\t\t\t\t\t"confirmation_chart", \t\t\t\t\t\t\t"100%", "300","9.0.0", \t\t\t\t\t\t\t"expressInstall.swf", \t\t\t\t\t\t\t{}, \t\t\t\t\t\t\t{'wmode': 'transparent'} \t\t\t\t\t\t); EOF ); } $sErrors = addslashes(Dict::Format('UI:CSVImportError_items', $iErrors)); $sCreated = addslashes(Dict::Format('UI:CSVImportCreated_items', $iCreated)); $sModified = addslashes(Dict::Format('UI:CSVImportModified_items', $iModified)); $sUnchanged = addslashes(Dict::Format('UI:CSVImportUnchanged_items', $iUnchanged)); $oPage->add_script(<<<EOF function CSVGoBack() { \t\$('input[name=step]').val({$iCurrentStep}-1); \t\$('#wizForm').submit(); \t } function CSVRestart() { \t\$('input[name=step]').val(1); \t\$('#wizForm').submit(); \t } function ToggleRows(sCSSClass) { \t\$('.'+sCSSClass).toggle(); } function DoSubmit(bConfirm) { \tif (bConfirm) //Ask for a confirmation \t{ \t\t\$('#dlg_confirmation').dialog('open'); \t} \telse \t{ \t\t// Submit the form \t\t\$('#wizForm').block(); \t\t\$('#wizForm').submit(); \t} \treturn false; } function CancelImport() { \t\$('#dlg_confirmation').dialog('close'); } function RunImport() { \t\$('#dlg_confirmation').dialog('close'); \t// Submit the form \t\$('#wizForm').block(); \t\$('#wizForm').submit(); } function open_flash_chart_data() { \tvar iErrors = {$iErrors}; \tvar iModified = {$iModified}; \tvar iCreated = {$iCreated}; \tvar iUnchanged = {$iUnchanged}; \tvar fAlpha = 0.9; \t \tvar oResult = { \t\t"elements": [ \t\t\t{ \t\t\t\t"type": "pie", \t\t\t\t"tip": "#label# (#percent#)", \t\t\t\t"gradient-fill": true, \t\t\t\t"font-size": 14, \t\t\t\t"colours":[], \t\t\t\t"values": [], \t\t\t\t"animate":[ \t\t\t { \t\t\t "type": "fade" \t\t\t } \t\t ] \t\t\t} \t\t], \t\t"x_axis": null, \t\t"font-size": 14, \t\t"bg_colour": "#EEEEEE" \t}; \tif (iErrors > 0) \t{ \t\tvar oErrors = \t\t{ \t\t\t"value": iErrors, \t\t\t"label": "{$sErrors}", \t\t\t"alpha": fAlpha, \t\t\t"label-colour": "#CC3333", \t\t}; \t\toResult.elements[0].values.push(oErrors); \t\toResult.elements[0].colours.push('#FF6666'); \t} \tif (iModified > 0) \t{ \t\tvar oModified = \t\t{ \t\t\t"value": iModified, \t\t\t"label": "{$sModified}", \t\t\t"alpha": fAlpha, \t\t\t"label-colour": "#3333CC", \t\t}; \t\toResult.elements[0].values.push(oModified); \t\toResult.elements[0].colours.push('#6666FF'); \t} \tif (iCreated > 0) \t{ \t\tvar oCreated = \t\t{ \t\t\t"value": iCreated, \t\t\t"label": "{$sCreated}", \t\t\t"alpha": fAlpha, \t\t\t"label-colour": "#33CC33", \t\t\t \t\t}; \t\toResult.elements[0].values.push(oCreated); \t\toResult.elements[0].colours.push('#66FF66'); \t} \tif (iUnchanged > 0) \t{ \t\tvar oUnchanged = \t\t{ \t\t\t"value": iUnchanged, \t\t\t"label": "{$sUnchanged}", \t\t\t"alpha": fAlpha, \t\t\t"label-colour": "#333333", \t\t\t \t\t}; \t\toResult.elements[0].values.push(oUnchanged); \t\toResult.elements[0].colours.push('#666666'); \t} \treturn JSON.stringify(oResult); } EOF ); if ($iErrors > 0) { return $aResult; } else { return null; } }
/** * Create a standard change record (done here 99% of the time, and nearly once per page) */ protected static function CreateChange() { self::$m_oCurrChange = MetaModel::NewObject("CMDBChange"); self::$m_oCurrChange->Set("date", time()); self::$m_oCurrChange->Set("userinfo", self::GetTrackInfo()); self::$m_oCurrChange->Set("origin", self::GetTrackOrigin()); self::$m_oCurrChange->DBInsert(); }
foreach ($aWarnings as $sWarning) { $oP->add_comment("Warning: " . $sWarning); } } $oBulk = new BulkChange($sClass, $aData, $aAttList, $aExtKeys, $aFinalReconcilKeys, null, null, $sDateFormat, $bLocalize); if ($bSimulate) { $oMyChange = null; } else { if (strlen($sComment) > 0) { $sMoreInfo = CMDBChange::GetCurrentUserName() . ', Web Service (CSV) - ' . $sComment; } else { $sMoreInfo = CMDBChange::GetCurrentUserName() . ', Web Service (CSV)'; } CMDBObject::SetTrackInfo($sMoreInfo); CMDBObject::SetTrackOrigin('csv-import.php'); $oMyChange = CMDBObject::GetCurrentChange(); } $aRes = $oBulk->Process($oMyChange); ////////////////////////////////////////////////// // // Compute statistics // $iCountErrors = 0; $iCountWarnings = 0; $iCountCreations = 0; $iCountUpdates = 0; $iCountUnchanged = 0; foreach ($aRes as $iRow => $aRowData) { $bWritten = false; $oStatus = $aRowData["__STATUS__"]; switch (get_class($oStatus)) {
protected function DoExecute() { CMDBSource::Query('START TRANSACTION'); //CMDBSource::Query('ROLLBACK'); automatique ! //////////////////////////////////////////////////////////////////////////////// // Set the stage // $oProvider = new Organization(); $oProvider->Set('name', 'Test-Provider1'); $oProvider->DBInsert(); $iProvider = $oProvider->GetKey(); $oDM1 = new DeliveryModel(); $oDM1->Set('name', 'Test-DM-1'); $oDM1->Set('org_id', $iProvider); $oDM1->DBInsert(); $iDM1 = $oDM1->GetKey(); $oDM2 = new DeliveryModel(); $oDM2->Set('name', 'Test-DM-2'); $oDM2->Set('org_id', $iProvider); $oDM2->DBInsert(); $iDM2 = $oDM2->GetKey(); //////////////////////////////////////////////////////////////////////////////// // Scenarii // $aScenarii = array(array('description' => 'Add the first customer', 'organizations' => array(array('deliverymodel_id' => $iDM1, 'name' => 'Test-Customer-1')), 'expected-res' => array("Test-Customer-1, , active, 0, , {$iDM1}, Test-DM-1, , Test-DM-1"), 'history_added' => 0, 'history_removed' => 0, 'history_modified' => 0), array('description' => 'Remove the customer by loading an empty set', 'organizations' => array(), 'expected-res' => array(), 'history_added' => 0, 'history_removed' => 0, 'history_modified' => 0), array('description' => 'Create two customers at once', 'organizations' => array(array('deliverymodel_id' => $iDM1, 'name' => 'Test-Customer-1'), array('deliverymodel_id' => $iDM1, 'name' => 'Test-Customer-2')), 'expected-res' => array("Test-Customer-1, , active, 0, , {$iDM1}, Test-DM-1, , Test-DM-1", "Test-Customer-2, , active, 0, , {$iDM1}, Test-DM-1, , Test-DM-1"), 'history_added' => 0, 'history_removed' => 0, 'history_modified' => 0), array('description' => 'Move Customer-1 to the second Delivery Model', 'organizations' => array(array('id' => "SELECT Organization WHERE name='Test-Customer-1'", 'deliverymodel_id' => $iDM2, 'name' => 'Test-Customer-1'), array('deliverymodel_id' => $iDM1, 'name' => 'Test-Customer-2')), 'expected-res' => array("Test-Customer-2, , active, 0, , {$iDM1}, Test-DM-1, , Test-DM-1"), 'history_added' => 0, 'history_removed' => 0, 'history_modified' => 0), array('description' => 'Move Customer-1 back to the first Delivery Model and reset Customer-2 (no Delivery Model)', 'organizations' => array(array('id' => "SELECT Organization WHERE name='Test-Customer-1'", 'deliverymodel_id' => $iDM1, 'name' => 'Test-Customer-1'), array('id' => "SELECT Organization WHERE name='Test-Customer-2'", 'deliverymodel_id' => 0, 'name' => 'Test-Customer-2')), 'expected-res' => array("Test-Customer-1, , active, 0, , {$iDM1}, Test-DM-1, , Test-DM-1"), 'history_added' => 0, 'history_removed' => 0, 'history_modified' => 0)); foreach ($aScenarii as $aScenario) { echo "<h4>" . $aScenario['description'] . "</h4>\n"; $oChange = MetaModel::NewObject("CMDBChange"); $oChange->Set("date", time()); $oChange->Set("userinfo", CMDBChange::GetCurrentUserName()); $oChange->Set("origin", 'custom-extension'); $oChange->DBInsert(); CMDBObject::SetCurrentChange($oChange); $iChange = $oChange->GetKey(); // Prepare set $oLinkset = DBObjectSet::FromScratch('Organization'); foreach ($aScenario['organizations'] as $aOrgData) { if (array_key_exists('id', $aOrgData)) { $sOQL = $aOrgData['id']; $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL)); $oOrg = $oSet->Fetch(); if (!is_object($oOrg)) { throw new Exception('Failed to find the Organization: ' . $sOQL); } } else { $oOrg = MetaModel::NewObject('Organization'); } foreach ($aOrgData as $sAttCode => $value) { if ($sAttCode == 'id') { continue; } $oOrg->Set($sAttCode, $value); } $oLinkset->AddObject($oOrg); } // Write $oDM = MetaModel::GetObject('DeliveryModel', $iDM1); $oDM->Set('customers_list', $oLinkset); $oDM->DBWrite(); // Check Results $bFoundIssue = false; $oDM = MetaModel::GetObject('DeliveryModel', $iDM1); $oLinkset = $oDM->Get('customers_list'); $aRes = $this->StandardizedDump($oLinkset, 'zzz'); $sRes = var_export($aRes, true); echo "Found: <pre>" . $sRes . "</pre>\n"; $sExpectedRes = var_export($aScenario['expected-res'], true); if ($sRes != $sExpectedRes) { $bFoundIssue = true; echo "NOT COMPLIANT!!! Expecting: <pre>" . $sExpectedRes . "</pre>\n"; } // Check History $aQueryParams = array('change' => $iChange, 'objclass' => get_class($oDM), 'objkey' => $oDM->GetKey()); $oAdded = new DBObjectSet(DBSearch::FromOQL("SELECT CMDBChangeOpSetAttributeLinksAddRemove WHERE objclass = :objclass AND objkey = :objkey AND change = :change AND type = 'added'"), array(), $aQueryParams); echo "added: " . $oAdded->Count() . "<br/>\n"; if ($aScenario['history_added'] != $oAdded->Count()) { $bFoundIssue = true; echo "NOT COMPLIANT!!! Expecting: " . $aScenario['history_added'] . "<br/>\n"; } $oRemoved = new DBObjectSet(DBSearch::FromOQL("SELECT CMDBChangeOpSetAttributeLinksAddRemove WHERE objclass = :objclass AND objkey = :objkey AND change = :change AND type = 'removed'"), array(), $aQueryParams); echo "removed: " . $oRemoved->Count() . "<br/>\n"; if ($aScenario['history_removed'] != $oRemoved->Count()) { $bFoundIssue = true; echo "NOT COMPLIANT!!! Expecting: " . $aScenario['history_removed'] . "<br/>\n"; } $oModified = new DBObjectSet(DBSearch::FromOQL("SELECT CMDBChangeOpSetAttributeLinksTune WHERE objclass = :objclass AND objkey = :objkey AND change = :change"), array(), $aQueryParams); echo "modified: " . $oModified->Count() . "<br/>\n"; if ($aScenario['history_modified'] != $oModified->Count()) { $bFoundIssue = true; echo "NOT COMPLIANT!!! Expecting: " . $aScenario['history_modified'] . "<br/>\n"; } if ($bFoundIssue) { throw new Exception('Stopping on failed scenario'); } } }
protected static function DoLoadFiles($aSelectedModules, $sModulesDir, $sDBServer, $sDBUser, $sDBPwd, $sDBName, $sDBPrefix, $sTargetEnvironment = '', $bOldAddon = false, $bSampleData = false) { $aParamValues = array('db_server' => $sDBServer, 'db_user' => $sDBUser, 'db_pwd' => $sDBPwd, 'db_name' => $sDBName, 'new_db_name' => $sDBName, 'db_prefix' => $sDBPrefix); $oConfig = new Config(); $oConfig->UpdateFromParams($aParamValues, $sModulesDir); if ($bOldAddon) { // Old version of the add-on for backward compatibility with pre-2.0 data models $oConfig->SetAddons(array('user rights' => 'addons/userrights/userrightsprofile.db.class.inc.php')); } //Load the MetaModel if needed (asynchronous mode) if (!self::$bMetaModelStarted) { $oProductionEnv = new RunTimeEnvironment($sTargetEnvironment); $oProductionEnv->InitDataModel($oConfig, false); // load data model and connect to the database self::$bMetaModelStarted = true; // No need to reload the final MetaModel in case the installer runs synchronously } $oDataLoader = new XMLDataLoader(); CMDBObject::SetTrackInfo("Initialization"); $oMyChange = CMDBObject::GetCurrentChange(); SetupPage::log_info("starting data load session"); $oDataLoader->StartSession($oMyChange); $aFiles = array(); $aPreviouslyLoadedFiles = array(); $oProductionEnv = new RunTimeEnvironment(); $aAvailableModules = $oProductionEnv->AnalyzeInstallation($oConfig, APPROOT . $sModulesDir); foreach ($aAvailableModules as $sModuleId => $aModule) { if ($sModuleId != ROOT_MODULE) { // Load data only for selected AND newly installed modules if (in_array($sModuleId, $aSelectedModules)) { if ($aModule['version_db'] != '') { // Simulate the load of the previously loaded XML files to get the mapping of the keys if ($bSampleData) { $aPreviouslyLoadedFiles = array_merge($aPreviouslyLoadedFiles, $aAvailableModules[$sModuleId]['data.struct'], $aAvailableModules[$sModuleId]['data.sample']); } else { // Load only structural data $aPreviouslyLoadedFiles = array_merge($aPreviouslyLoadedFiles, $aAvailableModules[$sModuleId]['data.struct']); } } else { if ($bSampleData) { $aFiles = array_merge($aFiles, $aAvailableModules[$sModuleId]['data.struct'], $aAvailableModules[$sModuleId]['data.sample']); } else { // Load only structural data $aFiles = array_merge($aFiles, $aAvailableModules[$sModuleId]['data.struct']); } } } } } // Simulate the load of the previously loaded files, in order to initialize // the mapping between the identifiers in the XML and the actual identifiers // in the current database foreach ($aPreviouslyLoadedFiles as $sFileRelativePath) { $sFileName = APPROOT . $sFileRelativePath; SetupPage::log_info("Loading file: {$sFileName} (just to get the keys mapping)"); if (empty($sFileName) || !file_exists($sFileName)) { throw new Exception("File {$sFileName} does not exist"); } $oDataLoader->LoadFile($sFileName, true); $sResult = sprintf("loading of %s done.", basename($sFileName)); SetupPage::log_info($sResult); } foreach ($aFiles as $sFileRelativePath) { $sFileName = APPROOT . $sFileRelativePath; SetupPage::log_info("Loading file: {$sFileName}"); if (empty($sFileName) || !file_exists($sFileName)) { throw new Exception("File {$sFileName} does not exist"); } $oDataLoader->LoadFile($sFileName); $sResult = sprintf("loading of %s done.", basename($sFileName)); SetupPage::log_info($sResult); } $oDataLoader->EndSession(); SetupPage::log_info("ending data load session"); }