Synchro Data Sources Implementation on going...

SVN:trunk[1107]
This commit is contained in:
Denis Flaven
2011-03-02 17:06:56 +00:00
parent cf8174d54e
commit 3d4e76d8b0
6 changed files with 206 additions and 11 deletions

View File

@@ -91,9 +91,58 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$oSingletonFilter->AddCondition('id', $this->GetKey(), '=');
$oBlock = new MenuBlock($oSingletonFilter, 'popup', false);
$oBlock->Display($oPage, -1);
// Master data sources
$oReplicaSet = $this->GetMasterReplica();
$bSynchronized = false;
$bCreated = false;
$bCanBeDeleted = false;
$aMasterSources = array();
if ($oReplicaSet->Count() > 0)
{
$bSynchronized = true;
$sTip = "<p>The object is synchronized with an external data source</p>";
while($aData = $oReplicaSet->FetchAssoc())
{
$sApplicationURL = $aData['datasource']->GetApplicationUrl($this, $aData['replica']);
$sLink = "<a href=\"$sApplicationURL\" target=\"_blank\">".$aData['datasource']->GetName()."</a>";
if ($aData['replica']->Get('status_dest_creator') == 1)
{
$sTip .= "<p>The object was <b>created</b> by the external data source $sLink</p>";
$bCreated = true;
}
if ($bCreated)
{
$sDeletePolicy = $aData['datasource']->Get('delete_policy');
if (($sDeletePolicy == 'delete') || ($sDeletePolicy == 'update_then_delete'))
{
$bCanBeDeleted = true;
$sTip .= "<p>The object <b>can be deleted</b> by the external data source $sLink</p>";
}
}
$aMasterSources[$aData['datasource']->GetKey()]['datasource'] = $aData['datasource'];
$aMasterSources[$aData['datasource']->GetKey()]['url'] = $sLink;
}
}
$sSynchroIcon = '';
if ($bSynchronized)
{
$sTip .= "<p><b>List of data sources:</b></p>";
foreach($aMasterSources as $aStruct)
{
$oDataSource = $aStruct['datasource'];
$sLink = $aStruct['url'];
$sTip .= "<p style=\"white-space:nowrap\">".$oDataSource->GetIcon(true, 'style="vertical-align:middle"')."&nbsp;$sLink</p>";
}
$sSynchroIcon = '&nbsp;<img style="vertical-align:middle;" id="synchro_icon" src="../images/locked.png"/>';
$oPage->add_ready_script("$('#synchro_icon').qtip( { content: '$sTip', show: 'mouseover', hide: 'unfocus', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
}
$oPage->add("<div class=\"page_header\"><h1>".$this->GetIcon()."&nbsp;\n");
$oPage->add(MetaModel::GetName(get_class($this)).": <span class=\"hilite\">".$this->GetName()."</span></h1>\n");
$oPage->add(MetaModel::GetName(get_class($this)).": <span class=\"hilite\">".$this->GetName()."</span>$sSynchroIcon</h1>\n");
$oPage->add("</div>\n");
}
function DisplayBareHistory(WebPage $oPage, $bEditMode = false)
@@ -267,6 +316,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
// all the remaining attributes that are not external fields
$sHtml = '';
$aDetails = array();
$iInputId = 0;
foreach($aDetailsStruct as $sTab => $aCols )
{
$aDetails[$sTab] = array();
@@ -310,8 +360,25 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
$val = $this->GetFieldAsHtml($sClass, $sAttCode, $sStateAttCode);
if ($val != null)
{
// Check if the attribute is not mastered by a synchro...
$aReasons = array();
$iSynchroFlags = $this->GetSynchroReplicaFlags($sAttCode, $aReasons);
$sSynchroIcon = '';
if ($iSynchroFlags & OPT_ATT_READONLY)
{
$sSynchroIcon = "&nbsp;<img id=\"synchro_$iInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
$sTip = '';
foreach($aReasons as $aRow)
{
$sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>";
}
$oPage->add_ready_script("$('#synchro_$iInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
}
$val['value'] .= $sSynchroIcon;
// The field is visible, add it to the current column
$aDetails[$sTab][$sColIndex][] = $val;
$iInputId++;
}
}
}
@@ -1305,8 +1372,7 @@ EOF
$aVal = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue);
}
else
{
$iFlags = $this->GetAttributeFlags($sAttCode);
{
$sInputId = $this->m_iFormId.'_'.$sAttCode;
if ($iFlags & OPT_ATT_HIDDEN)
{
@@ -1318,8 +1384,24 @@ EOF
{
if ($iFlags & OPT_ATT_READONLY)
{
// Check if the attribute is not read-only becuase of a synchro...
$aReasons = array();
$iSynchroFlags = $this->GetSynchroReplicaFlags($sAttCode, $aReasons);
$sSynchroIcon = '';
if ($iSynchroFlags & OPT_ATT_READONLY)
{
$sSynchroIcon = "&nbsp;<img id=\"synchro_$sInputId\" src=\"../images/transp-lock.png\" style=\"vertical-align:middle\"/>";
$sTip = '';
foreach($aReasons as $aRow)
{
$sTip .= "<p>Synchronized with {$aRow['name']} - {$aRow['description']}</p>";
}
$oPage->add_ready_script("$('#synchro_$sInputId').qtip( { content: '$sTip', show: 'mouseover', hide: 'mouseout', style: { name: 'dark', tip: 'leftTop' }, position: { corner: { target: 'rightMiddle', tooltip: 'leftTop' }} } );");
}
// Attribute is read-only
$sHTMLValue = $this->GetAsHTML($sAttCode);
$sHTMLValue = $this->GetAsHTML($sAttCode).$sSynchroIcon;
$sHTMLValue .= '<input type="hidden" id="'.$sInputId.'" name="attr_'.$sPrefix.$sAttCode.'" value="'.htmlentities($this->Get($sAttCode), ENT_QUOTES, 'UTF-8').'"/>';
$aFieldsMap[$sAttCode] = $sInputId;
}

View File

@@ -69,6 +69,7 @@ class iTopWebPage extends NiceWebPage
$this->add_linked_script("../js/swfobject.js");
$this->add_linked_script("../js/ckeditor/ckeditor.js");
$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
$this->add_linked_script("../js/jquery.qtip-1.0.min.js");
$this->add_ready_script(
<<<EOF
try

View File

@@ -50,6 +50,7 @@ abstract class DBObject
private $m_bFullyLoaded = false; // Compound objects can be partially loaded
private $m_aLoadedAtt = array(); // Compound objects can be partially loaded, array of sAttCode
protected $m_oMasterReplicaSet = null; // Set of SynchroReplica related to this object
// Use the MetaModel::NewObject to build an object (do we have to force it?)
public function __construct($aRow = null, $sClassAlias = '', $aExtendedDataSpec = null)
@@ -645,7 +646,9 @@ abstract class DBObject
{
$iFlags = MetaModel::GetAttributeFlags(get_class($this), $this->Get($sStateAttCode), $sAttCode);
}
return $iFlags;
$aReasons = array();
$iSynchroFlags = $this->GetSynchroReplicaFlags($sAttCode, $aReasons);
return $iFlags | $iSynchroFlags; // Combine both sets of flags
}
// check if the given (or current) value is suitable for the attribute
@@ -1402,6 +1405,50 @@ abstract class DBObject
}
}
}
/**
* Get all the synchro replica related to this object
* @param none
* @return DBObjectSet Set with two columns: R=SynchroReplica S=SynchroDataSource
*/
public function GetMasterReplica()
{
if ($this->m_oMasterReplicaSet == null)
{
$aParentClasses = MetaModel::EnumParentClasses(get_class($this), ENUM_PARENT_CLASSES_ALL);
$sClassesList = "'".implode("','", $aParentClasses)."'";
$sOQL = "SELECT replica,datasource FROM SynchroReplica AS replica JOIN SynchroDataSource AS datasource ON replica.sync_source_id=datasource.id WHERE datasource.scope_class IN ($sClassesList) AND replica.dest_id = :dest_id";
$oReplicaSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array() /* order by*/, array('dest_id' => $this->GetKey()));
$this->m_oMasterReplicaSet = $oReplicaSet;
}
else
{
$this->m_oMasterReplicaSet->Rewind();
}
return $this->m_oMasterReplicaSet;
}
public function GetSynchroReplicaFlags($sAttCode, &$aReason)
{
$iFlags = OPT_ATT_NORMAL;
$oSet = $this->GetMasterReplica();
while($aData = $oSet->FetchAssoc())
{
$oReplica = $aData['replica'];
$oSource = $aData['datasource'];
$oAttrSet = $oSource->Get('attribute_list');
while($oSyncAttr = $oAttrSet->Fetch())
{
if (($oSyncAttr->Get('attcode') == $sAttCode) && ($oSyncAttr->Get('update') == 1) && ($oSyncAttr->Get('update_policy') == 'master_locked'))
{
$iFlags |= OPT_ATT_READONLY;
$sUrl = $oSource->GetApplicationUrl($this, $oReplica);
$aReason[] = array('name' => $oSource->GetName(), 'description' => $oSource->Get('description'), 'url_application' => $sUrl);
}
}
}
return $iFlags;
}
}

BIN
images/transp-lock.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 390 B

16
js/jquery.qtip-1.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -75,9 +75,15 @@ class SynchroDataSource extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeDuration("delete_policy_retention", array("allowed_values"=>null, "sql"=>"delete_policy_retention", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeLinkedSet("attribute_list", array("linked_class"=>"SynchroAttribute", "ext_key_to_me"=>"sync_source_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
// Not used yet !
MetaModel::Init_AddAttribute(new AttributeEnum("user_delete_policy", array("allowed_values"=>new ValueSetEnum('never,depends,always'), "sql"=>"user_delete_policy", "default_value"=>"always", "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeURL("url_icon", array("allowed_values"=>null, "sql"=>"url_icon", "default_value"=>null, "is_null_allowed"=>true, "target"=> '_top', "depends_on"=>array())));
// The field below is not a real URL since it can contain placeholders like $replica->primary_key$ which are not syntactically allowed in a real URL
MetaModel::Init_AddAttribute(new AttributeString("url_application", array("allowed_values"=>null, "sql"=>"url_application", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Display lists
MetaModel::Init_SetZListItems('details', array('name', 'description', 'scope_class', /*'scope_restriction', */'status', 'user_id', 'full_load_periodicity', 'reconciliation_policy', 'action_on_zero', 'action_on_one', 'action_on_multiple', 'delete_policy', 'delete_policy_update', 'delete_policy_retention' /*'attribute_list'*/)); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('details', array('name', 'description', 'url_icon', 'url_application', 'scope_class', /*'scope_restriction', */'status', 'user_id', 'full_load_periodicity', 'reconciliation_policy', 'action_on_zero', 'action_on_one', 'action_on_multiple', 'delete_policy', 'delete_policy_update', 'delete_policy_retention' /*'attribute_list'*/)); // Attributes to be displayed for the complete details
MetaModel::Init_SetZListItems('list', array('scope_class', 'status', 'user_id', 'full_load_periodicity')); // Attributes to be displayed for a list
// Search criteria
MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'scope_class', 'user_id')); // Criteria of the std search form
@@ -168,6 +174,7 @@ class SynchroDataSource extends cmdbAbstractObject
$aValues[] = $aRow;
}
}
$oPage->p(Dict::Format('Class:SynchroDataSource:DataTable', $this->GetDataTable()));
$oPage->Table($aAttribs, $aValues);
$this->DisplayStatusTab($oPage);
}
@@ -208,7 +215,7 @@ class SynchroDataSource extends cmdbAbstractObject
$oPage->add('<table class="synoptics"><tr><td style="color:#333;vertical-align:top">');
// List all the log entries for the user to select
$oPage->add('<h2>'.Dict::S('Core:Synchro:History').'</h2>');
$oPage->add('<h2 style="line-height:55px;">'.Dict::S('Core:Synchro:History').'</h2>');
$oSetSynchroLog->Rewind();
$oPage->add('<select size="25" onChange="UpdateSynoptics(this.value);">');
$sSelected = ' selected'; // First log is selected by default
@@ -362,6 +369,43 @@ EOF
return $aData;
}
public function GetIcon($bImgTag = true, $sMoreStyles = '')
{
if ($this->Get('url_icon') == '') return MetaModel::GetClassIcon(get_class($this), $bImgTag);
if ($bImgTag)
{
return "<img src=\"".$this->Get('url_icon')."\" style=\"vertical-align:middle;$sMoreStyles\"/>";
}
return $this->Get('url_icon');
}
/**
* Get the actual hyperlink to the remote application for the given replica and dest object
*/
public function GetApplicationUrl(DBObject $oDestObj, SynchroReplica $oReplica)
{
if ($this->Get('url_application') == '') return '';
$aSearches = array();
$aReplacements = array();
foreach(MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode=>$oAttDef)
{
if ($oAttDef->IsScalar())
{
$aSearches[] = '$this->'.$sAttCode.'$';
$aReplacements[] = $oDestObj->Get($sAttCode);
}
}
$aData = $oReplica->LoadExtendedDataFromTable($this->GetDataTable());
foreach($aData as $sColumn => $value)
{
$aSearches[] = '$replica->'.$sColumn.'$';
$aReplacements[] = $value;
}
return str_replace($aSearches, $aReplacements, $this->Get('url_application'));
}
public function GetAttributeFlags($sAttCode)
{
if (($sAttCode == 'scope_class') && (!$this->IsNew()))
@@ -1643,10 +1687,7 @@ class SynchroReplica extends DBObject implements iDisplay
$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);
$aData = $this->LoadExtendedDataFromTable($sSQLTable);
$aHeaders = array('attcode' => array('label' => 'Attribute Code', 'description' => ''),
'data' => array('label' => 'Value', 'description' => ''));
@@ -1660,6 +1701,14 @@ class SynchroReplica extends DBObject implements iDisplay
$oPage->add('</td></tr></table>');
}
public function LoadExtendedDataFromTable($sSQLTable)
{
$sSQL = "SELECT * FROM $sSQLTable WHERE id=".$this->GetKey();
$rQuery = CMDBSource::Query($sSQL);
return CMDBSource::FetchArray($rQuery);
}
}
// TO DO: finalize.... admins only ? which options ? troubleshoot WebPageMenuNode::__construct(.... sEnableClass...) ?