mirror of
https://github.com/Combodo/iTop.git
synced 2026-05-19 07:12:26 +02:00
WIP
- Add access rights management - Manage mass updates - Add tooltips to buttons
This commit is contained in:
@@ -1947,7 +1947,7 @@ class MenuBlock extends DisplayBlock
|
|||||||
$sSelectedClassName = MetaModel::GetName($sSelectedClass);
|
$sSelectedClassName = MetaModel::GetName($sSelectedClass);
|
||||||
|
|
||||||
// Check rights on class
|
// 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);
|
$bIsBulkDeleteAllowed = (bool) UserRights::IsActionAllowed($sSelectedClass, UR_ACTION_BULK_DELETE);
|
||||||
|
|
||||||
// Refine filter on selected class so bullk actions occur on the right class
|
// Refine filter on selected class so bullk actions occur on the right class
|
||||||
@@ -1958,8 +1958,14 @@ class MenuBlock extends DisplayBlock
|
|||||||
// Action label dict code has a specific suffix for "Link" / "Remote" aliases to allow dedicated labels in linksets.
|
// 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';
|
$sActionLabelCodeSuffix = in_array($sSelectedAlias, ['Link', 'Remote']) ? $sSelectedAlias : 'Class';
|
||||||
if ($bIsBulkModifyAllowed) {
|
if ($bIsBulkModifyAllowed) {
|
||||||
|
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));
|
$this->AddBulkModifyObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:ModifyAll:'.$sSelectedAlias, Dict::Format('UI:Menu:ModifyAll_'.$sActionLabelCodeSuffix, $sSelectedClassName));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if ($bIsBulkDeleteAllowed) {
|
if ($bIsBulkDeleteAllowed) {
|
||||||
$this->AddBulkDeleteObjectsMenuAction($aRegularActions, $sSelectedClass, $oSelectedClassFilter->serialize(), 'UI:Menu:BulkDelete:'.$sSelectedAlias, Dict::Format('UI:Menu:BulkDelete_'.$sActionLabelCodeSuffix, $sSelectedClassName));
|
$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
|
* @since 3.1.0
|
||||||
* @internal
|
* @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] = [
|
$aActions[$sActionIdentifier] = [
|
||||||
'label' => Dict::S($sActionLabel),
|
'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();
|
] + $this->GetDefaultParamsForMenuAction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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:status_last_warning' => 'Warnings',
|
||||||
'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date',
|
'Class:SynchroReplica/Attribute:info_creation_date' => 'Creation Date',
|
||||||
'Class:SynchroReplica/Attribute:info_last_modified' => 'Last Modified 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',
|
||||||
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object',
|
'Class:SynchroReplica/Action:unlink+' => 'Unlink replica with destination object',
|
||||||
'Class:SynchroReplica/Action:unlinksynchro' => 'Unlink & Synchro',
|
'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' => 'Synchro',
|
||||||
'Class:SynchroReplica/Action:synchro+' => 'Execute synchronization with this replica',
|
'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' => 'User Preferences',
|
||||||
'Class:appUserPreferences/Attribute:userid' => 'User',
|
'Class:appUserPreferences/Attribute:userid' => 'User',
|
||||||
|
|||||||
14
pages/UI.php
14
pages/UI.php
@@ -10,7 +10,6 @@ use Combodo\iTop\Application\Helper\Session;
|
|||||||
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
|
use Combodo\iTop\Application\TwigBase\Twig\TwigHelper;
|
||||||
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
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\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\GlobalSearch\GlobalSearchHelper;
|
||||||
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||||
use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory;
|
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\WebPage\WebPage;
|
||||||
use Combodo\iTop\Application\WelcomePopup\WelcomePopupService;
|
use Combodo\iTop\Application\WelcomePopup\WelcomePopupService;
|
||||||
use Combodo\iTop\Controller\Base\Layout\ObjectController;
|
use Combodo\iTop\Controller\Base\Layout\ObjectController;
|
||||||
|
use Combodo\iTop\Controller\Links\SynchroReplicaController;
|
||||||
use Combodo\iTop\Controller\WelcomePopupController;
|
use Combodo\iTop\Controller\WelcomePopupController;
|
||||||
use Combodo\iTop\Service\Router\Router;
|
use Combodo\iTop\Service\Router\Router;
|
||||||
|
|
||||||
@@ -585,6 +585,16 @@ try {
|
|||||||
UI::OperationFormForModifyAll($oP, $oAppContext);
|
UI::OperationFormForModifyAll($oP, $oAppContext);
|
||||||
break;
|
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
|
case 'preview_or_modify_all': // Preview or apply bulk modify
|
||||||
@@ -627,7 +637,7 @@ try {
|
|||||||
'title' => Dict::S('UI:BulkDeleteTitle'),
|
'title' => Dict::S('UI:BulkDeleteTitle'),
|
||||||
];
|
];
|
||||||
$oChecker = new ActionChecker($oFilter, UR_ACTION_BULK_DELETE);
|
$oChecker = new ActionChecker($oFilter, UR_ACTION_BULK_DELETE);
|
||||||
DisplayMultipleSelectionForm($oP, $oFilter, 'bulk_delete', $oChecker, [], $aDisplayParams);
|
UI::DisplayMultipleSelectionForm($oP, $oFilter, 'bulk_delete', $oChecker, [], $aDisplayParams);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
132
sources/Controller/Links/SynchroReplicaController.php
Normal file
132
sources/Controller/Links/SynchroReplicaController.php
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Combodo\iTop\Controller\Links;
|
||||||
|
|
||||||
|
use ApplicationContext;
|
||||||
|
use ApplicationException;
|
||||||
|
use Combodo\iTop\Application\TwigBase\Controller\Controller;
|
||||||
|
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\Panel\PanelUIBlockFactory;
|
||||||
|
use DBObjectSearch;
|
||||||
|
use Dict;
|
||||||
|
use Exception;
|
||||||
|
use iTopWebPage;
|
||||||
|
use MetaModel;
|
||||||
|
use utils;
|
||||||
|
|
||||||
|
class SynchroReplicaController extends Controller
|
||||||
|
{
|
||||||
|
public const ROUTE_NAMESPACE = 'synchroreplica';
|
||||||
|
|
||||||
|
public function __construct($sViewPath = '', $sModuleName = 'core', $aAdditionalPaths = [])
|
||||||
|
{
|
||||||
|
$sViewPath = APPROOT.'synchro';
|
||||||
|
parent::__construct($sViewPath, $sModuleName, $aAdditionalPaths);
|
||||||
|
|
||||||
|
// Previously in index.php
|
||||||
|
$this->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' => '<p>'.($bResult ? '' : implode('</p><p>', $aErrorsToDisplay)).'</p>',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
170
sources/Controller/UI.php
Normal file
170
sources/Controller/UI.php
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
|
||||||
|
use Combodo\iTop\Application\UI\Base\Component\Form\Form;
|
||||||
|
use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory;
|
||||||
|
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||||
|
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||||
|
use Combodo\iTop\Application\WebPage\WebPage;
|
||||||
|
|
||||||
|
class UI
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Operation select_for_modify_all
|
||||||
|
*
|
||||||
|
* @param iTopWebPage $oP
|
||||||
|
*
|
||||||
|
* @throws \ApplicationException
|
||||||
|
* @throws \ArchivedObjectException
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \OQLException
|
||||||
|
*/
|
||||||
|
public static function OperationSelectForModifyAll(iTopWebPage $oP, $sTitleTab = 'UI:ModifyAllPageTitle', $sTitleCode = 'UI:Modify_ObjectsOf_Class', $sNextOperation = 'form_for_modify_all'): void
|
||||||
|
{
|
||||||
|
$oP->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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
use Combodo\iTop\Application\WebPage\iTopWebPage;
|
||||||
use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin;
|
use UI;
|
||||||
|
|
||||||
require_once('../approot.inc.php');
|
require_once('../approot.inc.php');
|
||||||
require_once(APPROOT.'/application/application.inc.php');
|
require_once(APPROOT.'/application/application.inc.php');
|
||||||
@@ -36,89 +36,6 @@ $oP = new iTopWebPage("iTop - Synchro Replicas");
|
|||||||
// Main program
|
// Main program
|
||||||
$sOperation = utils::ReadParam('operation', 'details');
|
$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 {
|
try {
|
||||||
switch ($sOperation) {
|
switch ($sOperation) {
|
||||||
case 'details':
|
case 'details':
|
||||||
@@ -161,11 +78,9 @@ try {
|
|||||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
||||||
}
|
}
|
||||||
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
|
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
|
||||||
$oReplica->Set('dest_id', '');
|
$oReplica->UnLink();
|
||||||
$oReplica->Set('status', 'new');
|
|
||||||
$oReplica->DBWrite();
|
|
||||||
|
|
||||||
$oStatLog = Synchro($oReplica);
|
$oStatLog = $oReplica->ReSynchro();
|
||||||
$oP->add(implode('<br>', $oStatLog->GetTraces()));
|
$oP->add(implode('<br>', $oStatLog->GetTraces()));
|
||||||
|
|
||||||
$oReplica->DisplayDetails($oP);
|
$oReplica->DisplayDetails($oP);
|
||||||
@@ -177,9 +92,7 @@ try {
|
|||||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
||||||
}
|
}
|
||||||
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
|
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
|
||||||
$oReplica->Set('dest_id', '');
|
$oReplica->UnLink();
|
||||||
$oReplica->Set('status', 'new');
|
|
||||||
$oReplica->DBWrite();
|
|
||||||
|
|
||||||
$oReplica->DisplayDetails($oP);
|
$oReplica->DisplayDetails($oP);
|
||||||
break;
|
break;
|
||||||
@@ -190,7 +103,19 @@ try {
|
|||||||
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
|
||||||
}
|
}
|
||||||
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
|
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
|
||||||
$oStatLog = Synchro($oReplica);
|
$oStatLog = $oReplica->ReSynchro();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'select_for_unlink_all': // Select the list of objects to be modified (bulk modify)
|
||||||
|
UI::OperationSelectForModifyAll($oP,'UI:UnlinkAllTabTitle', 'UI:UnlinkAllPageTitle', 'form_for_unlink_all');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'select_for_unlinksynchro_all': // Select the list of objects to be modified (bulk modify)
|
||||||
|
UI::OperationSelectForModifyAll($oP,'UI:UnlinkSynchroAllTabTitle', 'UI:UnlinkSynchroAllPageTitle', 'form_for_unlinksynchro_all');
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'select_for_synchro_all': // Select the list of objects to be modified (bulk modify)
|
||||||
|
UI::OperationSelectForModifyAll($oP,'UI:SynchroAllTabTitle', 'UI:SynchroAllPageTitle','form_for_synchro_all');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} catch (CoreException $e) {
|
} catch (CoreException $e) {
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
|
|||||||
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
|
||||||
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
|
||||||
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
|
||||||
|
use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin;
|
||||||
|
|
||||||
class SynchroDataSource extends cmdbAbstractObject
|
class SynchroDataSource extends cmdbAbstractObject
|
||||||
{
|
{
|
||||||
@@ -2200,6 +2201,16 @@ class SynchroReplica extends DBObject implements iDisplay
|
|||||||
$this->Set('status_last_error', $sText);
|
$this->Set('status_last_error', $sText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disassociate the replica from the destination object and set the status to "new" to be synchronized with the next operation
|
||||||
|
*/
|
||||||
|
public function UnLink(){
|
||||||
|
$this->Set('dest_id', '');
|
||||||
|
$this->Set('dest_class', '');
|
||||||
|
$this->Set('status', 'new');
|
||||||
|
$this->DBWrite();
|
||||||
|
}
|
||||||
|
|
||||||
public function Synchro($oDataSource, $aReconciliationKeys, $aAttributes, $oChange, &$oStatLog)
|
public function Synchro($oDataSource, $aReconciliationKeys, $aAttributes, $oChange, &$oStatLog)
|
||||||
{
|
{
|
||||||
$oStatLog->AddTrace(">>> Beginning of SynchroReplica::Synchro, replica status is '".$this->Get('status')."'.", $this);
|
$oStatLog->AddTrace(">>> Beginning of SynchroReplica::Synchro, replica status is '".$this->Get('status')."'.", $this);
|
||||||
@@ -2376,6 +2387,89 @@ class SynchroReplica extends DBObject implements iDisplay
|
|||||||
$oStatLog->AddTrace('<<< End of SynchroReplica::Synchro.', $this);
|
$oStatLog->AddTrace('<<< End of SynchroReplica::Synchro.', $this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return \SynchroLog
|
||||||
|
* @throws \ArchivedObjectException
|
||||||
|
* @throws \CoreCannotSaveObjectException
|
||||||
|
* @throws \CoreException
|
||||||
|
* @throws \CoreUnexpectedValue
|
||||||
|
* @throws \CoreWarning
|
||||||
|
* @throws \MySQLException
|
||||||
|
* @throws \OQLException
|
||||||
|
* @throws \SynchroExceptionNotStarted
|
||||||
|
*/
|
||||||
|
public function ReSynchro(): SynchroLog
|
||||||
|
{
|
||||||
|
$oDataSource = MetaModel::GetObject('SynchroDataSource', $this->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);
|
||||||
|
|
||||||
|
$this->InitExtendedData($oDataSource);
|
||||||
|
|
||||||
|
$this->Synchro($oDataSource, $aReconciliationKeys, $aAttributesToUpdate, $oChange, $oStatLog);
|
||||||
|
$this->DBUpdate();
|
||||||
|
|
||||||
|
return $oStatLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the destination object with the Extended data found in the synchro_data_XXXX table
|
* Updates the destination object with the Extended data found in the synchro_data_XXXX table
|
||||||
*
|
*
|
||||||
@@ -2728,6 +2822,7 @@ class SynchroReplica extends DBObject implements iDisplay
|
|||||||
$aActions['UI:Menu:Delete'] = array(
|
$aActions['UI:Menu:Delete'] = array(
|
||||||
'label' => Dict::S('UI:Menu:Delete'),
|
'label' => Dict::S('UI:Menu:Delete'),
|
||||||
'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$sId{$sContext}",
|
'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$sId{$sContext}",
|
||||||
|
'tooltip' => Dict::S('Class:SynchroReplica/Action:delete+'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user