diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index ca2fe6857a..206dd2ce7f 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -1947,7 +1947,7 @@ class MenuBlock extends DisplayBlock $sSelectedClassName = MetaModel::GetName($sSelectedClass); // Check rights on class - $bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY) && ($oReflectionClass->IsSubclassOf('cmdbAbstractObject')); + $bIsBulkModifyAllowed = (!MetaModel::IsAbstract($sSelectedClass)) && UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_MODIFY) && (($oReflectionClass->IsSubclassOf('cmdbAbstractObject') || $sSelectedClass === 'SynchroReplica')); $bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE); // Refine filter on selected class so bullk actions occur on the right class @@ -1958,7 +1958,13 @@ class MenuBlock extends DisplayBlock // Action label dict code has a specific suffix for "Link" / "Remote" aliases to allow dedicated labels in linksets. $sActionLabelCodeSuffix = in_array($sSelectedAlias, ['Link', 'Remote']) ? $sSelectedAlias : 'Class'; if ($bIsBulkModifyAllowed) { - $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:ModifyAll:'.$sSelectedAlias, Dict::Format('UI:Menu:ModifyAll_'.$sActionLabelCodeSuffix, $sSelectedClassName)); + if($sSelectedClass === 'SynchroReplica'){ + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:UnlinkAll:', Dict::S('Class:SynchroReplica/Action:unlink_all'),'unlink'); + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:UnLinkSynchroAll:', Dict::S('Class:SynchroReplica/Action:unlinksynchro_all'),'unlinksynchro'); + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:SynchroAll:', Dict::S('Class:SynchroReplica/Action:synchro_all'),'synchro'); + } else { + $this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:ModifyAll:'.$sSelectedAlias, Dict::Format('UI:Menu:ModifyAll_'.$sActionLabelCodeSuffix, $sSelectedClassName)); + } } if ($bIsBulkDeleteAllowed) { $this->AddBulkDeleteObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:BulkDelete:'.$sSelectedAlias, Dict::Format('UI:Menu:BulkDelete_'.$sActionLabelCodeSuffix, $sSelectedClassName)); @@ -2478,11 +2484,11 @@ class MenuBlock extends DisplayBlock * @since 3.1.0 * @internal */ - protected function AddBulkModifyObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:ModifyAll', $sActionLabel = 'UI:Menu:ModifyAll'): void + protected function AddBulkModifyObjectsMenuAction(array &$aActions, string $sClass, string $sFilter, string $sActionIdentifier = 'UI:Menu:ModifyAll', $sActionLabel = 'UI:Menu:ModifyAll', $sOperationName = 'modify'): void { $aActions[$sActionIdentifier] = [ 'label' => Dict::S($sActionLabel), - 'url' => $this->PrepareUrlForStandardMenuAction($sClass, "operation=select_for_modify_all&class=$sClass&filter=".urlencode($sFilter)), + 'url' => $this->PrepareUrlForStandardMenuAction($sClass, 'operation=select_for_'.$sOperationName.'_all&class='.$sClass.'&filter='.urlencode($sFilter)), ] + $this->GetDefaultParamsForMenuAction(); } diff --git a/dictionaries/en.dictionary.itop.core.php b/dictionaries/en.dictionary.itop.core.php index e2675e36b2..1f59b9518a 100644 --- a/dictionaries/en.dictionary.itop.core.php +++ b/dictionaries/en.dictionary.itop.core.php @@ -1075,6 +1075,7 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of 'Class:SynchroReplica/Attribute:status_last_warning' => 'Warnings', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified Date', + 'Class:SynchroReplica/Action:delete+' => 'delete replica', 'Class:SynchroReplica/Action:unlink' => 'Unlink', 'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object', 'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro', @@ -1082,6 +1083,19 @@ The hyperlink is displayed in the tooltip appearing on the “Lock” symbol of 'Class:SynchroReplica/Action:synchro' => 'Synchro', 'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica', + 'Class:SynchroReplica/Action:unlink_all' => 'Unlink Synchro Replica objects', + 'Class:SynchroReplica/Action:unlink_all+' => 'Unlink replica with destination object', + 'Class:SynchroReplica/Action:unlinksynchro_all' => 'Unlink & Synchronize Synchro Replica objects', + 'Class:SynchroReplica/Action:unlinksynchro_all+' => 'Unlink replica with destination object and execute synchronization with this replica', + 'Class:SynchroReplica/Action:synchro_all' => 'Synchronize Synchro Replica objects', + 'Class:SynchroReplica/Action:synchro_all+' => 'Execute synchronization with this replica', + + 'UI:UnlinkAllTabTitle' => 'Unlink Synchro Replica objects', + 'UI:UnlinkAllPageTitle' => 'Unlink Synchro Replica objects', + 'UI:UnlinkSynchroAllTabTitle' => 'Unlink & Synchronize Synchro Replica objects', + 'UI:UnlinkSynchroAllPageTitle' => ' Unlink & Synchronize Synchro Replica objects', + 'UI:SynchroAllTabTitle' => 'Synchronize Synchro Replica objects', + 'UI:SynchroAllPageTitle' => 'Synchronize Synchro Replica objects', 'Class:appUserPreferences' => 'User Preferences', 'Class:appUserPreferences/Attribute:userid' => 'User', diff --git a/pages/UI.php b/pages/UI.php index 2761915084..80cd805db9 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -10,7 +10,6 @@ use Combodo\iTop\Application\Helper\Session; use Combodo\iTop\Application\TwigBase\Twig\TwigHelper; use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory; -use Combodo\iTop\Application\UI\Base\Component\Form\Form; use Combodo\iTop\Application\UI\Base\Component\GlobalSearch\GlobalSearchHelper; use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory; @@ -24,6 +23,7 @@ use Combodo\iTop\Application\WebPage\iTopWebPage; use Combodo\iTop\Application\WebPage\WebPage; use Combodo\iTop\Application\WelcomePopup\WelcomePopupService; use Combodo\iTop\Controller\Base\Layout\ObjectController; +use Combodo\iTop\Controller\Links\SynchroReplicaController; use Combodo\iTop\Controller\WelcomePopupController; use Combodo\iTop\Service\Router\Router; @@ -585,6 +585,16 @@ try { UI::OperationFormForModifyAll($oP, $oAppContext); break; + case 'form_for_unlink_all': // Form to modify multiple objects (bulk modify) + SynchroReplicaController::OperationUnlinkAll($oP, $oAppContext,'unlink'); + break; + case 'form_for_unlinksynchro_all': // Form to modify multiple objects (bulk modify) + SynchroReplicaController::OperationUnlinkAll($oP, $oAppContext,'unlinksynchro'); + break; + case 'form_for_synchro_all': // Form to modify multiple objects (bulk modify) + SynchroReplicaController::OperationUnlinkAll($oP, $oAppContext,'synchro'); + break; + /////////////////////////////////////////////////////////////////////////////////////////// case 'preview_or_modify_all': // Preview or apply bulk modify @@ -627,7 +637,7 @@ try { 'title' => Dict::S('UI:BulkDeleteTitle'), ]; $oChecker = new ActionChecker($oFilter, UR_ACTION_BULK_DELETE); - DisplayMultipleSelectionForm($oP, $oFilter, 'bulk_delete', $oChecker, [], $aDisplayParams); + UI::DisplayMultipleSelectionForm($oP, $oFilter, 'bulk_delete', $oChecker, [], $aDisplayParams); break; /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/sources/Controller/Links/SynchroReplicaController.php b/sources/Controller/Links/SynchroReplicaController.php new file mode 100644 index 0000000000..71a59a0e98 --- /dev/null +++ b/sources/Controller/Links/SynchroReplicaController.php @@ -0,0 +1,132 @@ +DisableInDemoMode(); + $this->AllowOnlyAdmin(); + $this->CheckAccess(); + } + + public static function OperationUnlinkAll(iTopWebPage $oP, ApplicationContext $oAppContext, $sOperation = 'unlink'): void + { + $oP->DisableBreadCrumb(); + $sClass = utils::ReadParam('class', '', false, 'class'); + $sFilter = utils::ReadPostedParam('filter', '', 'raw_data'); + $oFullSetFilter = DBObjectSearch::unserialize($sFilter); + // Add user filter + $oFullSetFilter->UpdateContextFromUser(); + $aSelectObject = utils::ReadMultipleSelection($oFullSetFilter); + if ( empty($sClass) || empty($aSelectObject)) // TO DO: check that the class name is valid ! + { + throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObject[]')); + } + $sCancelUrl = "./UI.php?operation=search&filter=".urlencode($sFilter)."&".$oAppContext->GetForLink(); + $aContext = array( + 'filter' => utils::EscapeHtml($sFilter), + 'selectObj' => $aSelectObject, + ); + + $aHeaders = array( + 'object' => array('label' => MetaModel::GetName($sClass), 'description' => Dict::S('UI:ModifiedObject')), + 'status' => array( + 'label' => Dict::S('UI:BulkModifyStatus'), + 'description' => Dict::S('UI:BulkModifyStatus+'), + ), + 'errors' => array( + 'label' => Dict::S('UI:BulkModifyErrors'), + 'description' => Dict::S('UI:BulkModifyErrors+'), + ), + ); + $aRows = array(); + + + $sHeaderTitle = Dict::Format('UI:Modify_N_ObjectsOf_Class', count($aSelectObject), MetaModel::GetName($sClass)); + $sClassIcon = MetaModel::GetClassIcon($sClass, false); + + // Not in preview mode, do the update for real + $sTransactionId = utils::ReadPostedParam('transaction_id', '', 'transaction_id'); + if (!utils::IsTransactionValid($sTransactionId, false)) { + throw new Exception(Dict::S('UI:Error:ObjectAlreadyUpdated')); + } + utils::RemoveTransaction($sTransactionId); + + // Avoid too many events + $iPreviousTimeLimit = ini_get('max_execution_time'); + $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); + $aErrors = []; + foreach ($aSelectObject as $iId) { + set_time_limit(intval($iLoopTimeLimit)); + /** @var \cmdbAbstractObject $oObj */ + $oReplica = MetaModel::GetObject('SynchroReplica', $iId); + $bResult = true; + try { + if (in_array($sOperation, ['unlink', 'unlinksynchro'])) { + \IssueLog::Error('unlinking replica '.$oReplica->GetKey()); + $oReplica->UnLink(); + } + if (in_array($sOperation, ['synchro', 'unlinksynchro'])) { + \IssueLog::Error('synchro replica '.$oReplica->GetKey()); + $oStatLog = $oReplica->ReSynchro(); + $aErrors = $oStatLog->GetTraces(); + } + } + catch (Exception $e) { + $bResult = false; + $aErrors[] = $e->getMessage(); + } + catch (Error $e) { + $bResult = false; + $aErrors[] = $e->getMessage(); + } + + $sStatus = $bResult ? Dict::S('UI:BulkModifyStatusModified') : Dict::S('UI:BulkModifyStatusSkipped'); + + $aErrorsToDisplay = array_map(function ($sError) { + return utils::HtmlEntities($sError); + }, $aErrors); + $aRows[] = array( + 'object' => $oReplica->GetHyperlink(), + 'status' => $sStatus, + 'errors' => '
'.($bResult ? '' : implode('
', $aErrorsToDisplay)).'
', + ); + } + + set_time_limit(intval($iPreviousTimeLimit)); + $oTable = DataTableUIBlockFactory::MakeForForm('BulkModify', $aHeaders, $aRows); + $oTable->AddOption("bFullscreen", true); + + $oPanel = PanelUIBlockFactory::MakeForClass($sClass, ''); + $oPanel->SetIcon($sClassIcon); + $oPanel->SetTitle($sHeaderTitle); + $oPanel->AddCSSClass('ibo-datatable-panel'); + $oPanel->AddSubBlock($oTable); + + $oP->AddUiBlock($oPanel); + $oP->AddSubBlock(ButtonUIBlockFactory::MakeForSecondaryAction(Dict::S('UI:Button:Done')))->SetOnClickJsCode("window.location.href='$sCancelUrl'")->AddCSSClass('mt-5'); + + + } +} \ No newline at end of file diff --git a/sources/Controller/UI.php b/sources/Controller/UI.php new file mode 100644 index 0000000000..e8eb7cf033 --- /dev/null +++ b/sources/Controller/UI.php @@ -0,0 +1,170 @@ +DisableBreadCrumb(); + IssueLog::Error('OperationSelectForModifyAll'.$sTitleCode); + $oP->set_title(Dict::S($sTitleTab)); + $sFilter = utils::ReadParam('filter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); + if (empty($sFilter)) { + throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'filter')); + } + $oFilter = DBObjectSearch::unserialize($sFilter); //TODO : check that the filter is valid + // Add user filter + $oFilter->UpdateContextFromUser(); + $oChecker = new ActionChecker($oFilter, UR_ACTION_BULK_MODIFY); + $sClass = $oFilter->GetClass(); + $sClassName = MetaModel::GetName($sClass); + + $aDisplayParams = [ + 'icon' => MetaModel::GetClassIcon($sClass, false), + 'title' => Dict::Format('UI:Modify_ObjectsOf_Class', $sClassName), + ]; + IssueLog::Error('OperationSelectForModifyAll'); + self::DisplayMultipleSelectionForm($oP, $oFilter, $sNextOperation, $oChecker, [], $aDisplayParams); + } + + /** + * Operation form_for_modify_all + * + * @param iTopWebPage $oP + * @param \ApplicationContext $oAppContext + * + * @throws \ArchivedObjectException + * @throws \CoreException + * @throws \CoreUnexpectedValue + * @throws \MySQLException + * @throws \OQLException + */ + public static function OperationFormForModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void + { + $oP->DisableBreadCrumb(); + $sFilter = utils::ReadParam('filter', '', false, utils::ENUM_SANITIZATION_FILTER_RAW_DATA); + $sClass = utils::ReadParam('class', '', false, utils::ENUM_SANITIZATION_FILTER_CLASS); + $oFullSetFilter = DBObjectSearch::unserialize($sFilter); + // Add user filter + $oFullSetFilter->UpdateContextFromUser(); + $aSelectedObj = utils::ReadMultipleSelection($oFullSetFilter); + $sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter=' . urlencode($sFilter) . '&' . $oAppContext->GetForLink(); + $aContext = array('filter' => utils::EscapeHtml($sFilter)); + cmdbAbstractObject::DisplayBulkModifyForm($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $sCancelUrl, array(), $aContext); + } + + /** + * Operation preview_or_modify_all + * + * @param iTopWebPage $oP + * @param \ApplicationContext $oAppContext + * + * @throws \ApplicationException + * @throws \ArchivedObjectException + * @throws \CoreCannotSaveObjectException + * @throws \CoreException + * @throws \DictExceptionMissingString + * @throws \OQLException + */ + public static function OperationPreviewOrModifyAll(iTopWebPage $oP, ApplicationContext $oAppContext): void + { + $oP->DisableBreadCrumb(); + $sFilter = utils::ReadParam('filter', '', false, 'raw_data'); + $oFilter = DBObjectSearch::unserialize($sFilter); // TO DO : check that the filter is valid + // Add user filter + $oFilter->UpdateContextFromUser(); + + $sClass = utils::ReadParam('class', '', false, 'class'); + $bPreview = utils::ReadParam('preview_mode', ''); + $sSelectedObj = utils::ReadParam('selectObj', '', false, 'raw_data'); + if (empty($sClass) || empty($sSelectedObj)) // TO DO: check that the class name is valid ! + { + throw new ApplicationException(Dict::Format('UI:Error:2ParametersMissing', 'class', 'selectObj')); + } + $aSelectedObj = explode(',', $sSelectedObj); + $sCancelUrl = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?operation=search&filter=' . urlencode($sFilter) . '&' . $oAppContext->GetForLink(); + $aContext = array( + 'filter' => utils::EscapeHtml($sFilter), + 'selectObj' => $sSelectedObj, + ); + cmdbAbstractObject::DoBulkModify($oP, $sClass, $aSelectedObj, 'preview_or_modify_all', $bPreview, $sCancelUrl, $aContext); + }/** + * Displays a form (checkboxes) to select the objects for which to apply a given action + * Only the objects for which the action is valid can be checked. By default all valid objects are checked + * + * @param WebPage $oP WebPage The page for output + * @param \DBSearch $oFilter DBSearch The filter that defines the list of objects + * @param string $sNextOperation string The next operation (code) to be executed when the form is submitted + * @param ActionChecker $oChecker ActionChecker The helper class/instance used to check for which object the action is valid + * @param array $aExtraFormParams + * @param array $aDisplayParams + * + * @throws \ApplicationException + * @throws \ArchivedObjectException + * @throws \CoreException + *@since 3.0.0 $aDisplayParams parameter + * + */ + public static function DisplayMultipleSelectionForm(WebPage $oP, DBSearch $oFilter, string $sNextOperation, ActionChecker $oChecker, array $aExtraFormParams = [], array $aDisplayParams = []) +{ + $oAppContext = new ApplicationContext(); + $iBulkActionAllowed = $oChecker->IsAllowed(); + $aExtraParams = array('selection_type' => 'multiple', 'selection_mode' => true, 'display_limit' => false, 'menu' => false); + if ($iBulkActionAllowed == UR_ALLOWED_DEPENDS) { + $aExtraParams['selection_enabled'] = $oChecker->GetAllowedIDs(); + } else { + if (UR_ALLOWED_NO) { + throw new ApplicationException(Dict::Format('UI:ActionNotAllowed')); + } + } + + $oForm = new Form(); + $oForm->SetAction( utils::GetAbsoluteUrlAppRoot().'pages/UI.php'); + $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('operation', $sNextOperation)); + $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('class', $oFilter->GetClass())); + $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('filter', utils::HtmlEntities($oFilter->Serialize()))); + $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden('transaction_id', utils::GetNewTransactionId())); + foreach ($aExtraFormParams as $sName => $sValue) { + $oForm->AddSubBlock(InputUIBlockFactory::MakeForHidden($sName, $sValue)); + } + IssueLog::Error('DisplayMultipleSelectionForm'); + $oForm->AddSubBlock($oAppContext->GetForFormBlock()); + $oDisplayBlock = new DisplayBlock($oFilter, 'list', false); + //by default all the elements are selected + $aExtraParams['selectionMode'] = 'negative'; + if (array_key_exists('icon', $aDisplayParams) || array_key_exists('title', $aDisplayParams)) { + $aExtraParams['surround_with_panel'] = true; + if (array_key_exists('icon', $aDisplayParams)) { + $aExtraParams['panel_icon'] = $aDisplayParams['icon']; + } + if (array_key_exists('title', $aDisplayParams)) { + $aExtraParams['panel_title'] = $aDisplayParams['title']; + } + } + $oForm->AddSubBlock($oDisplayBlock->GetDisplay($oP, 1, $aExtraParams)); + $oToolbarButtons = ToolbarUIBlockFactory::MakeStandard(null); + $oToolbarButtons->AddCSSClass('ibo-toolbar--button'); + $oForm->AddSubBlock($oToolbarButtons); + $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForCancel(Dict::S('UI:Button:Cancel'), 'cancel')->SetOnClickJsCode('window.history.back()')); + $oToolbarButtons->AddSubBlock(ButtonUIBlockFactory::MakeForPrimaryAction(Dict::S('UI:Button:Next'), 'next', Dict::S('UI:Button:Next'), true)); + + $oP->AddUiBlock($oForm); +} +} \ No newline at end of file diff --git a/synchro/replica.php b/synchro/replica.php index 26aedfa6fc..2f746a9a70 100644 --- a/synchro/replica.php +++ b/synchro/replica.php @@ -19,7 +19,7 @@ */ use Combodo\iTop\Application\WebPage\iTopWebPage; -use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin; +use UI; require_once('../approot.inc.php'); require_once(APPROOT.'/application/application.inc.php'); @@ -36,89 +36,6 @@ $oP = new iTopWebPage("iTop - Synchro Replicas"); // Main program $sOperation = utils::ReadParam('operation', 'details'); -/** - * @param \DBObject|null $oReplica - * @param $this - * - * @return \SynchroLog - * @throws \ArchivedObjectException - * @throws \CoreCannotSaveObjectException - * @throws \CoreException - * @throws \CoreUnexpectedValue - * @throws \CoreWarning - * @throws \MySQLException - * @throws \OQLException - * @throws \SynchroExceptionNotStarted - */ -function Synchro($oReplica): SynchroLog -{ - $oDataSource = MetaModel::GetObject('SynchroDataSource', $oReplica->Get('sync_source_id')); - - $oStatLog = new SynchroLog(); - $oStatLog->Set('sync_source_id', $oDataSource->GetKey()); - $oStatLog->Set('start_date', time()); - $oStatLog->Set('status', 'running'); - $oStatLog->AddTrace('Manual synchro'); - - // Get the list of SQL columns - $aAttCodesExpected = array(); - $aAttCodesToReconcile = array(); - $aAttCodesToUpdate = array(); - $sSelectAtt = 'SELECT SynchroAttribute WHERE sync_source_id = :source_id AND (update = 1 OR reconcile = 1)'; - $oSetAtt = new DBObjectSet(DBObjectSearch::FromOQL($sSelectAtt), array() /* order by*/, array('source_id' => $oDataSource->GetKey()) /* aArgs */); - while ($oSyncAtt = $oSetAtt->Fetch()) { - if ($oSyncAtt->Get('update')) { - $aAttCodesToUpdate[$oSyncAtt->Get('attcode')] = $oSyncAtt; - } - if ($oSyncAtt->Get('reconcile')) { - $aAttCodesToReconcile[$oSyncAtt->Get('attcode')] = $oSyncAtt; - } - $aAttCodesExpected[$oSyncAtt->Get('attcode')] = $oSyncAtt; - } - - // Get the list of attributes, determine reconciliation keys and update targets - // - if ($oDataSource->Get('reconciliation_policy') == 'use_attributes') { - $aReconciliationKeys = $aAttCodesToReconcile; - } elseif ($oDataSource->Get('reconciliation_policy') == 'use_primary_key') { - // Override the settings made at the attribute level ! - $aReconciliationKeys = array('primary_key' => null); - } - - if (count($aAttCodesToUpdate) == 0) { - $oStatLog->AddTrace('No attribute to update'); - throw new SynchroExceptionNotStarted('There is no attribute to update'); - } - if (count($aReconciliationKeys) == 0) { - $oStatLog->AddTrace('No attribute for reconciliation'); - throw new SynchroExceptionNotStarted('No attribute for reconciliation'); - } - - - $aAttributesToUpdate = array(); - foreach ($aAttCodesToUpdate as $sAttCode => $oSyncAtt) { - $oAttDef = MetaModel::GetAttributeDef($oDataSource->GetTargetClass(), $sAttCode); - if ($oAttDef->IsWritable()) { - $aAttributesToUpdate[$sAttCode] = $oSyncAtt; - } - } - // Create a change used for logging all the modifications/creations happening during the synchro - $oChange = MetaModel::NewObject('CMDBChange'); - $oChange->Set('date', time()); - $sUserString = CMDBChange::GetCurrentUserName(); - $oChange->Set('userinfo', $sUserString.' '.Dict::S('Core:SyncDataExchangeComment')); - $oChange->Set('origin', CMDBChangeOrigin::SYNCHRO_DATA_SOURCE); - $oChange->DBInsert(); - CMDBObject::SetCurrentChange($oChange); - - $oReplica->InitExtendedData($oDataSource); - - $oReplica->Synchro($oDataSource, $aReconciliationKeys, $aAttributesToUpdate, $oChange, $oStatLog); - $oReplica->DBUpdate(); - - return $oStatLog; -} - try { switch ($sOperation) { case 'details': @@ -161,11 +78,9 @@ try { throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id')); } $oReplica = MetaModel::GetObject('SynchroReplica', $iId); - $oReplica->Set('dest_id', ''); - $oReplica->Set('status', 'new'); - $oReplica->DBWrite(); + $oReplica->UnLink(); - $oStatLog = Synchro($oReplica); + $oStatLog = $oReplica->ReSynchro(); $oP->add(implode('