diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 97f88356d..3a1af6b66 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -39,7 +39,40 @@ require_once(APPROOT.'/application/ui.passwordwidget.class.inc.php'); require_once(APPROOT.'/application/ui.extkeywidget.class.inc.php'); require_once(APPROOT.'/application/ui.htmleditorwidget.class.inc.php'); -abstract class cmdbAbstractObject extends CMDBObject +/** + * All objects to be displayed in the application (either as a list or as details) + * must implement this interface. + */ +interface iDisplay +{ + + /** + * Maps the given context parameter name to the appropriate filter/search code for this class + * @param string $sContextParam Name of the context parameter, i.e. 'org_id' + * @return string Filter code, i.e. 'customer_id' + */ + public static function MapContextParam($sContextParam); + /** + * This function returns a 'hilight' CSS class, used to hilight a given row in a table + * There are currently (i.e defined in the CSS) 4 possible values HILIGHT_CLASS_CRITICAL, + * HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE + * To Be overridden by derived classes + * @param void + * @return String The desired higlight class for the object/row + */ + public function GetHilightClass(); + /** + * Returns the relative path to the page that handles the display of the object + * @return string + */ + public static function GetUIPage(); + /** + * Displays the details of the object + */ + public function DisplayDetails(WebPage $oPage, $bEditMode = false); +} + +abstract class cmdbAbstractObject extends CMDBObject implements iDisplay { protected $m_iFormId; // The ID of the form used to edit the object (when in edition mode !) @@ -48,40 +81,6 @@ abstract class cmdbAbstractObject extends CMDBObject return '../pages/UI.php'; } - public static function ComputeUIPage($sClass) - { - static $aUIPagesCache = array(); // Cache to store the php page used to display each class of object - if (!isset($aUIPagesCache[$sClass])) - { - $UIPage = false; - if (is_callable("$sClass::GetUIPage")) - { - $UIPage = eval("return $sClass::GetUIPage();"); // May return false in case of error - } - $aUIPagesCache[$sClass] = $UIPage === false ? './UI.php' : $UIPage; - } - $sPage = $aUIPagesCache[$sClass]; - return $sPage; - } - - protected static function MakeHyperLink($sObjClass, $sObjKey, $sLabel = '') - { - if ($sObjKey <= 0) return ''.Dict::S('UI:UndefinedObject').''; // Objects built in memory have negative IDs - - $oAppContext = new ApplicationContext(); - $sPage = self::ComputeUIPage($sObjClass); - $sAbsoluteUrl = utils::GetAbsoluteUrlPath(); - - // Safety net - // - if (empty($sLabel)) - { - $sLabel = MetaModel::GetName($sObjClass)." #$sObjKey"; - } - $sHint = MetaModel::GetName($sObjClass)."::$sObjKey"; - return "GetForLink()."\" title=\"$sHint\">$sLabel"; - } - function DisplayBareHeader(WebPage $oPage, $bEditMode = false) { // Standard Header with name, actions menu and history block diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 97f696d2d..fbaa96d06 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -482,14 +482,56 @@ abstract class DBObject protected static function MakeHyperLink($sObjClass, $sObjKey, $sLabel = '') { - if ($sObjKey == 0) return 'undefined'; + if ($sObjKey <= 0) return ''.Dict::S('UI:UndefinedObject').''; // Objects built in memory have negative IDs - return MetaModel::GetName($sObjClass)."::$sObjKey"; + $oAppContext = new ApplicationContext(); + $sPage = self::ComputeUIPage($sObjClass); + $sAbsoluteUrl = utils::GetAbsoluteUrlPath(); + + // Safety net + // + if (empty($sLabel)) + { + $sLabel = MetaModel::GetName($sObjClass)." #$sObjKey"; + } + $sHint = MetaModel::GetName($sObjClass)."::$sObjKey"; + return "GetForLink()."\" title=\"$sHint\">$sLabel"; } public function GetHyperlink() { - return $this->MakeHyperLink(get_class($this), $this->GetKey(), $this->GetName()); + if ($this->IsNew()) return ''.Dict::S('UI:UndefinedObject').''; // Objects built in memory have negative IDs + + $oAppContext = new ApplicationContext(); + $sPage = $this->GetUIPage(); + $sAbsoluteUrl = utils::GetAbsoluteUrlPath(); + $sObjClass = get_class($this); + $sObjKey = $this->GetKey(); + + $sLabel = $this->GetName(); + $sHint = MetaModel::GetName($sObjClass)."::$sObjKey"; + return "GetForLink()."\" title=\"$sHint\">$sLabel"; + } + + public static function ComputeUIPage($sClass) + { + static $aUIPagesCache = array(); // Cache to store the php page used to display each class of object + if (!isset($aUIPagesCache[$sClass])) + { + $UIPage = false; + if (is_callable("$sClass::GetUIPage")) + { + $UIPage = eval("return $sClass::GetUIPage();"); // May return false in case of error + } + $aUIPagesCache[$sClass] = $UIPage === false ? './UI.php' : $UIPage; + } + $sPage = $aUIPagesCache[$sClass]; + return $sPage; + } + + public static function GetUIPage() + { + return 'UI.php'; } diff --git a/dictionaries/dictionary.itop.core.php b/dictionaries/dictionary.itop.core.php index 5e9d7a2a3..b121db8a5 100644 --- a/dictionaries/dictionary.itop.core.php +++ b/dictionaries/dictionary.itop.core.php @@ -539,7 +539,12 @@ Dict::Add('EN US', 'English', 'English', array( 'Core:Synchro:Nb_Class:Objects' => '%1$s: %2$s', 'Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified' => 'At Least one reconciliation key must be specified.', 'Class:SynchroDataSource/Error:DeleteRetentionDurationMustBeSpecified' => 'A delete retention period must be specified, since objects are to be deleted after being marked as obsolete', - 'Class:SynchroDataSource/Error:DeletePolicyUpdateMustBeSpecified' => 'Obsolete objects are to be updated, but no update is specified.', + 'Class:SynchroDataSource/Error:DeletePolicyUpdateMustBeSpecified' => 'Obsolete objects are to be updated, but no update is specified.', + 'Core:SynchroReplica:PublicData' => 'Public Data', + 'Core:SynchroReplica:PrivateDetails' => 'Private Details', + 'Core:SynchroReplica:BackToDataSource' => 'Go Back to the Synchro Data Source: %1$s', + 'Core:SynchroReplica:ListOfReplicas' => 'List of Replica', + )); // diff --git a/synchro/replica.php b/synchro/replica.php new file mode 100644 index 000000000..d02838a46 --- /dev/null +++ b/synchro/replica.php @@ -0,0 +1,83 @@ +DisplayDetails($oP); + break; + + case 'oql': + $sOQL = utils::ReadParam('oql', null); + if ($sOQL == null) + { + throw new ApplicationException(Dict::Format('UI:Error:1ParametersMissing', 'oql')); + } + $oP->add(''); + $iSourceId = utils::ReadParam('datasource', null); + if ($iSourceId != null) + { + $oSource = MetaModel::GetObject('SynchroDataSource', $iSourceId); + $oP->p(Dict::Format('Core:SynchroReplica:BackToDataSource', $oSource->GetHyperlink()).''); + } + $oFilter = DBObjectSearch::FromOQL($sOQL); + $oBlock = new DisplayBlock($oFilter, 'list', false, array('menu'=>false)); + $oBlock->Display($oP, 1); + break; + } +} +catch(CoreException $e) +{ + $oP->p('An error occured while running the query:'); + $oP->p($e->getHtmlDesc()); +} +catch(Exception $e) +{ + $oP->p('An error occured while running the query:'); + $oP->p($e->getMessage()); +} + +$oP->output(); +?> + +?> diff --git a/synchro/synchro_exec.php b/synchro/synchro_exec.php index 314230eaa..f87d7abad 100644 --- a/synchro/synchro_exec.php +++ b/synchro/synchro_exec.php @@ -102,7 +102,6 @@ else $oP = new WebPage(Dict::S("TitleSynchroExecution")); $sDataSourcesList = utils::ReadParam('data_sources', null, true); - $bSimulate = (utils::ReadParam('simulate', '0', true) == '1'); if ($sDataSourcesList == null) { @@ -110,6 +109,8 @@ else } } +$bSimulate = (utils::ReadParam('simulate', '0', true) == '1'); + foreach(explode(',', $sDataSourcesList) as $iSDS) { diff --git a/synchro/synchrodatasource.class.inc.php b/synchro/synchrodatasource.class.inc.php index 0bc4d77a8..621c74d9f 100644 --- a/synchro/synchrodatasource.class.inc.php +++ b/synchro/synchrodatasource.class.inc.php @@ -174,6 +174,8 @@ class SynchroDataSource extends cmdbAbstractObject $oLastLog = $oSetSynchroLog->Fetch(); $sStartDate = $oLastLog->Get('start_date'); $oLastLog->Get('stats_nb_replica_seen'); + $iLastLog = 0; + $iDSid = $this->GetKey(); if ($oLastLog->Get('status') == 'running') { // Still running ! @@ -182,6 +184,7 @@ class SynchroDataSource extends cmdbAbstractObject else { $sEndDate = $oLastLog->Get('end_date'); + $iLastLog = $oLastLog->GetKey(); $oPage->p('

'.Dict::Format('Core:Synchro:SynchroEndedOn_Date', $sEndDate).'

'); } @@ -204,6 +207,7 @@ class SynchroDataSource extends cmdbAbstractObject $sScript .= "end: 'Done'"; $sScript .= "};\n"; $sScript .= << 0) ) + { + $('#new_errors_link').show(); + } + else + { + $('#new_errors_link').hide(); + } + + if ( (id == sLastLog) && (aValues['obj_updated_errors'] > 0) ) + { + $('#updated_errors_link').show(); + } + else + { + $('#updated_errors_link').hide(); + } + + if ( (id == sLastLog) && (aValues['obj_disappeared_errors'] > 0) ) + { + $('#disappeared_errors_link').show(); + } + else + { + $('#disappeared_errors_link').hide(); + } } EOF ; @@ -236,6 +267,7 @@ EOF EOF ); + $sBaseOQL = "SELECT SynchroReplica WHERE sync_source_id=".$this->GetKey()." AND status_last_error!=''"; $oPage->add($this->HtmlBox('repl_ignored', $aData, '#999').' '); $oPage->add("\n"); $oPage->add($this->HtmlBox('repl_disappeared', $aData, '#630', 'rowspan="4"').'=>'.$this->HtmlBox('obj_disappeared_no_action', $aData, '#333')); @@ -244,13 +276,15 @@ EOF $oPage->add("\n"); $oPage->add($this->HtmlBox('obj_obsoleted', $aData, '#630')); $oPage->add("\n"); - $oPage->add($this->HtmlBox('obj_disappeared_errors', $aData, '#C00')); + $sOQL = urlencode($sBaseOQL." AND status='obsolete'"); + $oPage->add($this->HtmlBox('obj_disappeared_errors', $aData, '#C00', '', " Show")); $oPage->add("\n"); $oPage->add($this->HtmlBox('repl_existing', $aData, '#093', 'rowspan="3"').'=>'.$this->HtmlBox('obj_unchanged', $aData, '#393')); $oPage->add("\n"); $oPage->add($this->HtmlBox('obj_updated', $aData, '#3C3')); $oPage->add("\n"); - $oPage->add($this->HtmlBox('obj_updated_errors', $aData, '#C00')); + $sOQL = urlencode($sBaseOQL." AND status='modified'"); + $oPage->add($this->HtmlBox('obj_updated_errors', $aData, '#C00', '', " Show")); $oPage->add("\n"); $oPage->add($this->HtmlBox('repl_new', $aData, '#339', 'rowspan="4"').'=>'.$this->HtmlBox('obj_new_unchanged', $aData, '#393')); $oPage->add("\n"); @@ -258,9 +292,11 @@ EOF $oPage->add("\n"); $oPage->add($this->HtmlBox('obj_created', $aData, '#339')); $oPage->add("\n"); - $oPage->add($this->HtmlBox('obj_new_errors', $aData, '#C00')); + $sOQL = urlencode($sBaseOQL." AND status='new'"); + $oPage->add($this->HtmlBox('obj_new_errors', $aData, '#C00', '', " Show")); $oPage->add("\n\n"); $oPage->add(''); + $oPage->add_ready_script("UpdateSynoptics('$iLastLog')"); } else { @@ -268,13 +304,14 @@ EOF } } - protected function HtmlBox($sId, $aData, $sColor, $sHTMLAttribs = '') + protected function HtmlBox($sId, $aData, $sColor, $sHTMLAttribs = '', $sErrorLink = '') { $iCount = $aData[$sId]; $sCount = "$iCount"; $sLabel = Dict::Format('Core:Synchro:label_'.$sId, $sCount); $sOpacity = ($iCount==0) ? "opacity:0.3;" : ""; - return "$sLabel"; + + return "{$sLabel}{$sErrorLink}"; } protected function ProcessLog($oLastLog) @@ -378,6 +415,37 @@ EOF { parent::DoCheckToWrite(); + if ($this->IsNew()) + { + // When inserting a new datasource object, also create the SynchroAttribute objects + // for each field of the target class + // Create all the SynchroAttribute records + $oAttributeSet = $this->Get('attribute_list'); + foreach(MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode=>$oAttDef) + { + if ($oAttDef->IsScalar() && $oAttDef->IsWritable()) + { + $oAttDef = MetaModel::GetAttributeDef($this->GetTargetClass(), $sAttCode); + if ($oAttDef->IsExternalKey()) + { + $oAttribute = new SynchroAttExtKey(); + $oAttribute->Set('reconciliation_attcode', ''); // Blank means by pkey + } + else + { + $oAttribute = new SynchroAttribute(); + } + $oAttribute->Set('sync_source_id', $this->GetKey()); + $oAttribute->Set('attcode', $sAttCode); + $oAttribute->Set('reconcile', MetaModel::IsReconcKey($this->GetTargetClass(), $sAttCode) ? 1 : 0); + $oAttribute->Set('update', 1); + $oAttribute->Set('update_policy', 'master_locked'); + $oAttributeSet->AddObject($oAttribute); + } + } + $this->Set('attribute_list', $oAttributeSet); + } + // Check that there is at least one reconciliation key defined if ($this->Get('reconciliation_policy') == 'use_attributes') { @@ -425,38 +493,6 @@ EOF return $sTable; } - /** - * When inserting a new datasource object, also create the SynchroAttribute objects - * for each field of the target class - */ - protected function OnInsert() - { - // Create all the SynchroAttribute records - $oAttributeSet = $this->Get('attribute_list'); - foreach(MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode=>$oAttDef) - { - if ($oAttDef->IsScalar() && $oAttDef->IsWritable()) - { - $oAttDef = MetaModel::GetAttributeDef($this->GetTargetClass(), $sAttCode); - if ($oAttDef->IsExternalKey()) - { - $oAttribute = new SynchroAttExtKey(); - $oAttribute->Set('reconciliation_attcode', ''); // Blank means by pkey - } - else - { - $oAttribute = new SynchroAttribute(); - } - $oAttribute->Set('sync_source_id', $this->GetKey()); - $oAttribute->Set('attcode', $sAttCode); - $oAttribute->Set('reconcile', MetaModel::IsReconcKey($this->GetTargetClass(), $sAttCode) ? 1 : 0); - $oAttribute->Set('update', 1); - $oAttribute->Set('update_policy', 'master_locked'); - $oAttributeSet->AddObject($oAttribute); - } - } - $this->Set('attribute_list', $oAttributeSet); - } /** * When the new datasource has been created, let's create the synchro_data table * that will hold the data records and the correspoding triggers which will maintain @@ -1084,7 +1120,7 @@ class SynchroLog extends DBObject } -class SynchroReplica extends DBObject +class SynchroReplica extends DBObject implements iDisplay { static $aSearches = array(); // Cache of OQL queries used for reconciliation (per data source) @@ -1460,6 +1496,94 @@ class SynchroReplica extends DBObject return $aData[$sColumnName]; } + + /** + * Maps the given context parameter name to the appropriate filter/search code for this class + * @param string $sContextParam Name of the context parameter, i.e. 'org_id' + * @return string Filter code, i.e. 'customer_id' + */ + public static function MapContextParam($sContextParam) + { + if ($sContextParam == 'menu') + { + return null; + } + else + { + return $sContextParam; + } + } + + /** + * This function returns a 'hilight' CSS class, used to hilight a given row in a table + * There are currently (i.e defined in the CSS) 4 possible values HILIGHT_CLASS_CRITICAL, + * HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE + * To Be overridden by derived classes + * @param void + * @return String The desired higlight class for the object/row + */ + public function GetHilightClass() + { + // Possible return values are: + // HILIGHT_CLASS_CRITICAL, HILIGHT_CLASS_WARNING, HILIGHT_CLASS_OK, HILIGHT_CLASS_NONE + return HILIGHT_CLASS_NONE; // Not hilighted by default + } + + public static function GetUIPage() + { + return '../synchro/replica.php'; + } + + function DisplayDetails(WebPage $oPage, $bEditMode = false) + { + // Object's details + //$this->DisplayBareHeader($oPage, $bEditMode); + $oPage->AddTabContainer(OBJECT_PROPERTIES_TAB); + $oPage->SetCurrentTabContainer(OBJECT_PROPERTIES_TAB); + $oPage->SetCurrentTab(Dict::S('UI:PropertiesTab')); + $this->DisplayBareProperties($oPage, $bEditMode); + } + + function DisplayBareProperties(WebPage $oPage, $bEditMode = false) + { + if ($bEditMode) return; // Not editable + + $oPage->add('
'); + $aDetails = array(); + $sClass = get_class($this); + $oPage->add('
'); + $oPage->add(''.Dict::S('Core:SynchroReplica:PrivateDetails').''); + $aZList = MetaModel::GetZListItems($sClass, 'details'); + foreach( $aZList as $sAttCode) + { + $sDisplayValue = $this->GetAsHTML($sAttCode); + $aDetails[] = array('label' => ''.MetaModel::GetLabel($sClass, $sAttCode).'', 'value' => $sDisplayValue); + } + $oPage->Details($aDetails); + $oPage->add('
'); + $oPage->add('
'); + $oPage->add('
'); + $oPage->add(''.Dict::S('Core:SynchroReplica:PublicData').''); + $oSource = MetaModel::GetObject('SynchroDataSource', $this->Get('sync_source_id')); + + $sSQLTable = $oSource->GetDataTable(); + $sSQL = "SELECT * FROM $sSQLTable WHERE id=".$this->GetKey(); + + $rQuery = CMDBSource::Query($sSQL); + $aData = CMDBSource::FetchArray($rQuery); + + $aHeaders = array('attcode' => array('label' => 'Attribute Code', 'description' => ''), + 'data' => array('label' => 'Value', 'description' => '')); + $aRows = array(); + foreach($aData as $sKey => $value) + { + $aRows[] = array('attcode' => $sKey, 'data' => $value); + } + $oPage->Table($aHeaders, $aRows); + $oPage->add('
'); + $oPage->add('
'); + + } } // TO DO: finalize.... admins only ? which options ? troubleshoot WebPageMenuNode::__construct(.... sEnableClass...) ? @@ -1467,6 +1591,7 @@ class SynchroReplica extends DBObject { $oAdminMenu = new MenuGroup('AdminTools', 80 /* fRank */); new OQLMenuNode('DataSources', 'SELECT SynchroDataSource', $oAdminMenu->GetIndex(), 12 /* fRank */, true, 'SynchroDataSource', UR_ACTION_MODIFY, UR_ALLOWED_YES); + new OQLMenuNode('Replicas', 'SELECT SynchroReplica', $oAdminMenu->GetIndex(), 12 /* fRank */, true, 'SynchroReplica', UR_ACTION_MODIFY, UR_ALLOWED_YES); new WebPageMenuNode('Test:RunSynchro', '../synchro/synchro_exec.php', $oAdminMenu->GetIndex(), 13 /* fRank */, 'SynchroDataSource'); } ?> \ No newline at end of file