Skip to content

Commit dd4c2f2

Browse files
committed
Add operation on replica
1 parent 4d88ef6 commit dd4c2f2

File tree

4 files changed

+264
-37
lines changed

4 files changed

+264
-37
lines changed

application/cmdbabstract.class.inc.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,11 @@ public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
533533
$sLabel = Dict::S('Tag:Synchronized');
534534
$sSynchroTagId = 'synchro_icon-'.$this->GetKey();
535535
$aTags[$sSynchroTagId] = ['title' => $sTip, 'css_classes' => 'ibo-object-details--tag--synchronized', 'decoration_classes' => 'fas fa-lock', 'label' => $sLabel];
536+
if (UserRights::IsActionAllowed('SynchroReplica', UR_ACTION_READ)) {
537+
$sFilter = 'SELECT SynchroReplica WHERE dest_class=\''.get_class($this).'\' AND dest_id='.$this->GetKey();
538+
$sUrlSearchReplica = 'UI.php?operation=search&filter='.urlencode(json_encode([$sFilter, [], []]));
539+
$oPage->add_ready_script("$('#$sSynchroTagId').on('click',function() {window.location = '$sUrlSearchReplica' });");
540+
}
536541
}
537542
}
538543

core/attributedef.class.inc.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3082,13 +3082,17 @@ public function MakeRealValue($proposedValue, $oHostObj)
30823082
{
30833083
return 0;
30843084
}
3085-
if (MetaModel::IsValidObject($proposedValue))
3086-
{
3085+
if (MetaModel::IsValidObject($proposedValue)) {
30873086
return $proposedValue->GetKey();
30883087
}
30893088

30903089
return (int)$proposedValue;
30913090
}
3091+
3092+
public function GetTargetClass($iType = EXTKEY_RELATIVE)
3093+
{
3094+
return '';
3095+
}
30923096
}
30933097

30943098
/**

synchro/replica.php

Lines changed: 152 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818
*/
1919

2020
use Combodo\iTop\Application\WebPage\iTopWebPage;
21+
use Combodo\iTop\Core\CMDBChange\CMDBChangeOrigin;
2122

2223
require_once('../approot.inc.php');
2324
require_once(APPROOT.'/application/application.inc.php');
2425

2526
require_once(APPROOT.'/application/startup.inc.php');
2627
require_once(APPROOT.'/application/loginwebpage.class.inc.php');
27-
LoginWebPage::DoLogin();
28+
LoginWebPage::DoLogin();
2829

2930
$sOperation = utils::ReadParam('operation', 'menu');
3031
$oAppContext = new ApplicationContext();
@@ -33,46 +34,163 @@
3334

3435
// Main program
3536
$sOperation = utils::ReadParam('operation', 'details');
36-
try
37+
38+
/**
39+
* @param \DBObject|null $oReplica
40+
* @param $this
41+
*
42+
* @return \SynchroLog
43+
* @throws \ArchivedObjectException
44+
* @throws \CoreCannotSaveObjectException
45+
* @throws \CoreException
46+
* @throws \CoreUnexpectedValue
47+
* @throws \CoreWarning
48+
* @throws \MySQLException
49+
* @throws \OQLException
50+
* @throws \SynchroExceptionNotStarted
51+
*/
52+
function Synchro($oReplica): SynchroLog
3753
{
38-
switch($sOperation)
39-
{
40-
case 'details':
41-
$iId = utils::ReadParam('id', null);
42-
if ($iId == null)
43-
{
44-
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
54+
$oDataSource = MetaModel::GetObject('SynchroDataSource', $oReplica->Get('sync_source_id'));
55+
56+
$oStatLog = new SynchroLog();
57+
$oStatLog->Set('sync_source_id', $oDataSource->GetKey());
58+
$oStatLog->Set('start_date', time());
59+
$oStatLog->Set('status', 'running');
60+
$oStatLog->AddTrace('Manual synchro');
61+
62+
// Get the list of SQL columns
63+
$aAttCodesExpected = array();
64+
$aAttCodesToReconcile = array();
65+
$aAttCodesToUpdate = array();
66+
$sSelectAtt = 'SELECT SynchroAttribute WHERE sync_source_id = :source_id AND (update = 1 OR reconcile = 1)';
67+
$oSetAtt = new DBObjectSet(DBObjectSearch::FromOQL($sSelectAtt), array() /* order by*/, array('source_id' => $oDataSource->GetKey()) /* aArgs */);
68+
while ($oSyncAtt = $oSetAtt->Fetch()) {
69+
if ($oSyncAtt->Get('update')) {
70+
$aAttCodesToUpdate[$oSyncAtt->Get('attcode')] = $oSyncAtt;
4571
}
46-
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
47-
$oReplica->DisplayDetails($oP);
48-
break;
49-
50-
case 'oql':
51-
$sOQL = utils::ReadParam('oql', null, false, 'raw_data');
52-
if ($sOQL == null)
53-
{
54-
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'oql'));
72+
if ($oSyncAtt->Get('reconcile')) {
73+
$aAttCodesToReconcile[$oSyncAtt->Get('attcode')] = $oSyncAtt;
5574
}
56-
$oFilter = DBObjectSearch::FromOQL($sOQL);
57-
$oBlock1 = new DisplayBlock($oFilter, 'search', false, array('menu' => false, 'table_id' => '1'));
58-
$oBlock1->Display($oP, 0);
59-
$oP->add('<p class="page-header">'.MetaModel::GetClassIcon('SynchroReplica').Dict::S('Core:SynchroReplica:ListOfReplicas').'</p>');
60-
$iSourceId = utils::ReadParam('datasource', null);
61-
if ($iSourceId != null)
62-
{
63-
$oSource = MetaModel::GetObject('SynchroDataSource', $iSourceId);
64-
$oP->p(Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetHyperlink()).'</a>');
75+
$aAttCodesExpected[$oSyncAtt->Get('attcode')] = $oSyncAtt;
76+
}
77+
78+
// Get the list of attributes, determine reconciliation keys and update targets
79+
//
80+
if ($oDataSource->Get('reconciliation_policy') == 'use_attributes') {
81+
$aReconciliationKeys = $aAttCodesToReconcile;
82+
} elseif ($oDataSource->Get('reconciliation_policy') == 'use_primary_key') {
83+
// Override the settings made at the attribute level !
84+
$aReconciliationKeys = array('primary_key' => null);
85+
}
86+
87+
if (count($aAttCodesToUpdate) == 0) {
88+
$oStatLog->AddTrace('No attribute to update');
89+
throw new SynchroExceptionNotStarted('There is no attribute to update');
90+
}
91+
if (count($aReconciliationKeys) == 0) {
92+
$oStatLog->AddTrace('No attribute for reconciliation');
93+
throw new SynchroExceptionNotStarted('No attribute for reconciliation');
94+
}
95+
96+
97+
$aAttributesToUpdate = array();
98+
foreach ($aAttCodesToUpdate as $sAttCode => $oSyncAtt) {
99+
$oAttDef = MetaModel::GetAttributeDef($oDataSource->GetTargetClass(), $sAttCode);
100+
if ($oAttDef->IsWritable()) {
101+
$aAttributesToUpdate[$sAttCode] = $oSyncAtt;
65102
}
66-
$oBlock = new DisplayBlock($oFilter, 'list', false, array('menu'=>false));
67-
$oBlock->Display($oP, 1);
68-
break;
103+
}
104+
// Create a change used for logging all the modifications/creations happening during the synchro
105+
$oChange = MetaModel::NewObject('CMDBChange');
106+
$oChange->Set('date', time());
107+
$sUserString = CMDBChange::GetCurrentUserName();
108+
$oChange->Set('userinfo', $sUserString.' '.Dict::S('Core:SyncDataExchangeComment'));
109+
$oChange->Set('origin', CMDBChangeOrigin::SYNCHRO_DATA_SOURCE);
110+
$oChange->DBInsert();
111+
CMDBObject::SetCurrentChange($oChange);
112+
113+
$oReplica->InitExtendedData($oDataSource);
114+
115+
$oReplica->Synchro($oDataSource, $aReconciliationKeys, $aAttributesToUpdate, $oChange, $oStatLog);
116+
$oReplica->DBUpdate();
117+
118+
return $oStatLog;
119+
}
120+
121+
try {
122+
switch ($sOperation) {
123+
case 'details':
124+
$iId = utils::ReadParam('id', null);
125+
if ($iId == null) {
126+
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
127+
}
128+
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
129+
$oReplica->DisplayDetails($oP);
130+
break;
131+
132+
case 'oql':
133+
$sOQL = utils::ReadParam('oql', null, false, 'raw_data');
134+
if ($sOQL == null) {
135+
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'oql'));
136+
}
137+
$oFilter = DBObjectSearch::FromOQL($sOQL);
138+
$oBlock1 = new DisplayBlock($oFilter, 'search', false, array('menu' => false, 'table_id' => '1'));
139+
$oBlock1->Display($oP, 0);
140+
$oP->add('<p class="page-header">'.MetaModel::GetClassIcon('SynchroReplica').Dict::S('Core:SynchroReplica:ListOfReplicas').'</p>');
141+
$iSourceId = utils::ReadParam('datasource', null);
142+
if ($iSourceId != null) {
143+
$oSource = MetaModel::GetObject('SynchroDataSource', $iSourceId);
144+
$oP->p(Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetHyperlink()).'</a>');
145+
}
146+
$oBlock = new DisplayBlock($oFilter, 'list', false, array('menu' => false));
147+
$oBlock->Display($oP, 1);
148+
break;
69149

70150
case 'delete':
71151
case 'select_for_deletion':
72-
// Redirect to the page that implements bulk delete
73-
$sDelete = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?'.$_SERVER['QUERY_STRING'];
74-
header("Location: $sDelete");
75-
break;
152+
// Redirect to the page that implements bulk delete
153+
$sDelete = utils::GetAbsoluteUrlAppRoot().'pages/UI.php?'.$_SERVER['QUERY_STRING'];
154+
header("Location: $sDelete");
155+
break;
156+
157+
case 'unlinksynchro':
158+
$iId = utils::ReadParam('id', null);
159+
if ($iId == null) {
160+
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
161+
}
162+
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
163+
$oReplica->Set('dest_id', '');
164+
$oReplica->Set('status', 'new');
165+
$oReplica->DBWrite();
166+
167+
$oStatLog = Synchro($oReplica);
168+
$oP->add(implode('<br>', $oStatLog->GetTraces()));
169+
170+
$oReplica->DisplayDetails($oP);
171+
break;
172+
173+
case 'unlink':
174+
$iId = utils::ReadParam('id', null);
175+
if ($iId == null) {
176+
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
177+
}
178+
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
179+
$oReplica->Set('dest_id', '');
180+
$oReplica->Set('status', 'new');
181+
$oReplica->DBWrite();
182+
183+
$oReplica->DisplayDetails($oP);
184+
break;
185+
186+
case 'synchro':
187+
$iId = utils::ReadParam('id', null);
188+
if ($iId == null) {
189+
throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'id'));
190+
}
191+
$oReplica = MetaModel::GetObject('SynchroReplica', $iId);
192+
$oStatLog = Synchro($oReplica);
193+
break;
76194
}
77195
}
78196
catch(CoreException $e)

synchro/synchrodatasource.class.inc.php

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
*/
66

77
use Combodo\iTop\Application\WebPage\WebPage;
8+
use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory;
9+
use Combodo\iTop\Application\UI\Base\Component\PopoverMenu\PopoverMenu;
10+
use Combodo\iTop\Application\UI\Base\Component\Title\TitleUIBlockFactory;
11+
use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory;
12+
use Combodo\iTop\Application\UI\Base\Layout\UIContentBlockUIBlockFactory;
813

914
class SynchroDataSource extends cmdbAbstractObject
1015
{
@@ -2117,6 +2122,12 @@ public static function Init()
21172122
// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form
21182123
}
21192124

2125+
public function InitExtendedData($oSource)
2126+
{
2127+
$sSQLTable = $oSource->GetDataTable();
2128+
$this->m_aExtendedData = $this->LoadExtendedDataFromTable($sSQLTable);
2129+
}
2130+
21202131
public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null)
21212132
{
21222133
parent::__construct($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec);
@@ -2759,13 +2770,102 @@ public static function GetUIPage()
27592770
function DisplayDetails(WebPage $oPage, $bEditMode = false)
27602771
{
27612772
// Object's details
2762-
//$this->DisplayBareHeader($oPage, $bEditMode);
2773+
$this->DisplayBareHeader($oPage, $bEditMode);
2774+
27632775
$oPage->AddTabContainer(OBJECT_PROPERTIES_TAB);
27642776
$oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB);
27652777
$oPage->SetCurrentTab('UI:PropertiesTab');
27662778
$this->DisplayBareProperties($oPage, $bEditMode);
27672779
}
27682780

2781+
public function DisplayBareHeader(WebPage $oPage, $bEditMode = false)
2782+
{
2783+
$oBlock = UIContentBlockUIBlockFactory::MakeStandard('title-for-replica', ['ibo-page-header--replica-title']);
2784+
$oPage->AddSubBlock($oBlock);
2785+
$oPage->add_style('.ibo-page-header--replica-title{ display: table; width: 100%;}');
2786+
$oPage->add_style('.ibo-page-header--replica-title>.ibo-toolbar--button{ display: table-cell; vertical-align:middle;}');
2787+
2788+
$sId = $this->GetKey();
2789+
$oTitle = TitleUIBlockFactory::MakeNeutral(Dict::S('Class:SynchroReplica'));
2790+
$oBlock->AddSubBlock($oTitle);
2791+
$oActionsToolbar = ToolbarUIBlockFactory::MakeForButton(MenuBlock::ACTIONS_TOOLBAR_ID_PREFIX.$sId);
2792+
$oActionsToolbar->AddCSSClass('ibo-panel--toolbar');
2793+
$oBlock->AddSubBlock($oActionsToolbar);
2794+
2795+
$sClass = get_class($this);
2796+
$sRootUrl = utils::GetAbsoluteUrlAppRoot();
2797+
$sUIPage = cmdbAbstractObject::ComputeStandardUIPage($sClass);
2798+
$oAppContext = new ApplicationContext();
2799+
$sContext = $oAppContext->GetForLink();
2800+
if (utils::IsNotNullOrEmptyString($sContext)) {
2801+
$sContext = '&'.$sContext;
2802+
}
2803+
2804+
$aActions = [];
2805+
//Delete
2806+
if (UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE)) {
2807+
$aActions['UI:Menu:Delete'] = array(
2808+
'label' => Dict::S('UI:Menu:Delete'),
2809+
'url' => "{$sRootUrl}pages/$sUIPage?operation=delete&class=$sClass&id=$sId{$sContext}",
2810+
);
2811+
}
2812+
2813+
if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY)) {
2814+
if (count($aActions) > 0) {
2815+
$sSeparator = '<hr class="menu-separator"/>';
2816+
$aActions['sep_0'] = array('label' => $sSeparator, 'url' => '');
2817+
}
2818+
$sUrl = "{$sRootUrl}synchro/replica.php?operation=unlink&class=$sClass&id=$sId{$sContext}";
2819+
$aActions['Class:SynchroReplica/Action:unlink'] = [
2820+
'label' => Dict::S('Class:SynchroReplica/Action:unlink'),
2821+
'url' => $sUrl,
2822+
'tooltip' => Dict::S('Class:SynchroReplica/Action:unlink+'),
2823+
];
2824+
2825+
$sUrl = "{$sRootUrl}synchro/replica.php?operation=unlinksynchro&class=$sClass&id=$sId{$sContext}";
2826+
$aActions['Class:SynchroReplica/Action:unlinksynchro'] = [
2827+
'label' => Dict::S('Class:SynchroReplica/Action:unlinksynchro'),
2828+
'url' => $sUrl,
2829+
'tooltip' => Dict::S('Class:SynchroReplica/Action:unlinksynchro+'),
2830+
];
2831+
2832+
$sUrl = "{$sRootUrl}synchro/replica.php?operation=synchro&class=$sClass&id=$sId{$sContext}";
2833+
$aActions['Class:SynchroReplica/Action:synchro'] = [
2834+
'label' => Dict::S('Class:SynchroReplica/Action:synchro'),
2835+
'url' => $sUrl,
2836+
'tooltip' => Dict::S('Class:SynchroReplica/Action:synchro+'),
2837+
];
2838+
}
2839+
if (count($aActions) > 0) {
2840+
$sRegularActionsMenuTogglerId = "ibo-regular-actions-menu-toggler-{$sId}";
2841+
$sRegularActionsPopoverMenuId = "ibo-regular-actions-popover-{$sId}";
2842+
2843+
$oActionButton = ButtonUIBlockFactory::MakeIconAction('fas fa-ellipsis-v', Dict::S('UI:Menu:Actions'), 'UI:Menu:Actions', '', false, $sRegularActionsMenuTogglerId)
2844+
->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
2845+
2846+
$oRegularActionsMenu = $oPage->GetPopoverMenu($sRegularActionsPopoverMenuId, $aActions)
2847+
->SetTogglerJSSelector("#$sRegularActionsMenuTogglerId")
2848+
->SetContainer(PopoverMenu::ENUM_CONTAINER_BODY);
2849+
2850+
$oActionsToolbar->AddSubBlock($oActionButton)
2851+
->AddSubBlock($oRegularActionsMenu);
2852+
2853+
$oActionButton = ButtonUIBlockFactory::MakeIconLink('fas fa-search', Dict::Format('UI:SearchFor_Class', MetaModel::GetName($sClass)), "{$sRootUrl}pages/UI.php?operation=search_form&do_search=0&class=$sClass{$sContext}", '', 'UI:SearchFor_Class');
2854+
$oActionButton->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
2855+
$oActionsToolbar->AddSubBlock($oActionButton);
2856+
}
2857+
2858+
$sUrl = "{$sRootUrl}pages/$sUIPage?operation=display&class=$sClass&id=$sId{$sContext}";
2859+
$oActionButton = ButtonUIBlockFactory::MakeAlternativeNeutral('', 'UI:Button:Refresh');
2860+
$oActionButton->SetIconClass('fas fa-sync-alt')
2861+
->SetOnClickJsCode('window.location.href=\''.$sUrl.'\'')
2862+
->SetTooltip(Dict::S('UI:Button:Refresh'))
2863+
->AddCSSClasses(['ibo-action-button', 'ibo-regular-action-button']);
2864+
$oActionsToolbar->AddSubBlock($oActionButton);
2865+
2866+
return $oBlock;
2867+
}
2868+
27692869
function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array())
27702870
{
27712871
if ($bEditMode) {

0 commit comments

Comments
 (0)