Synchro Data Sources Implementation on going...

SVN:trunk[1100]
This commit is contained in:
Denis Flaven
2011-03-01 09:01:44 +00:00
parent a1b9bdaf66
commit f3f2eb5c79
7 changed files with 201 additions and 39 deletions

View File

@@ -1068,6 +1068,22 @@ EOF
$aEventsList[] ='change';
$sHTMLValue = "<input title=\"$sHelpText\" class=\"date-pick\" type=\"text\" size=\"20\" name=\"attr_{$sFieldPrefix}{$sAttCode}{$sNameSuffix}\" value=\"$value\" id=\"$iId\"/>&nbsp;{$sValidationField}";
break;
case 'Duration':
$aEventsList[] ='validate';
$aEventsList[] ='change';
$oPage->add_ready_script("$('#{$iId}_d').bind('keyup change', function(evt, sFormId) { return UpdateDuration('$iId'); });");
$oPage->add_ready_script("$('#{$iId}_h').bind('keyup change', function(evt, sFormId) { return UpdateDuration('$iId'); });");
$oPage->add_ready_script("$('#{$iId}_m').bind('keyup change', function(evt, sFormId) { return UpdateDuration('$iId'); });");
$oPage->add_ready_script("$('#{$iId}_s').bind('keyup change', function(evt, sFormId) { return UpdateDuration('$iId'); });");
$aVal = AttributeDuration::SplitDuration($value);
$sDays = "<input title=\"$sHelpText\" type=\"text\" size=\"3\" name=\"attr_{$sFieldPrefix}{$sAttCode}[d]{$sNameSuffix}\" value=\"{$aVal['days']}\" id=\"{$iId}_d\"/>";
$sHours = "<input title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[h]{$sNameSuffix}\" value=\"{$aVal['hours']}\" id=\"{$iId}_h\"/>";
$sMinutes = "<input title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[m]{$sNameSuffix}\" value=\"{$aVal['minutes']}\" id=\"{$iId}_m\"/>";
$sSeconds = "<input title=\"$sHelpText\" type=\"text\" size=\"2\" name=\"attr_{$sFieldPrefix}{$sAttCode}[s]{$sNameSuffix}\" value=\"{$aVal['seconds']}\" id=\"{$iId}_s\"/>";
$sHidden = "<input type=\"hidden\" id=\"{$iId}\" value=\"$value\"/>";
$sHTMLValue = Dict::Format('UI:DurationForm_Days_Hours_Minutes_Seconds', $sDays, $sHours, $sMinutes, $sSeconds).$sHidden."&nbsp;".$sValidationField;
break;
case 'Password':
$aEventsList[] ='validate';
@@ -1170,7 +1186,7 @@ EOF
}
break;
}
$sPattern = addslashes($oAttDef->GetValidationPattern()); //'^([0-9]+)$';
$sPattern = addslashes($oAttDef->GetValidationPattern()); //'^([0-9]+)$';
if (!empty($aEventsList))
{
$sNullValue = $oAttDef->GetNullValue();
@@ -1768,6 +1784,24 @@ EOF
$this->Set($sAttCode, $rawValue);
}
}
elseif ($oAttDef->GetEditClass() == 'Duration')
{
$rawValue = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null);
if (!is_array($rawValue))
{
$iValue = null;
}
else
{
$iValue = (((24*$rawValue['d'])+$rawValue['h'])*60 +$rawValue['m'])*60 + $rawValue['s'];
}
$this->Set($sAttCode, $iValue);
$previousValue = $this->Get($sAttCode);
if ($previousValue !== $iValue)
{
$this->Set($sAttCode, $iValue);
}
}
else
{
$rawValue = utils::ReadPostedParam("attr_{$sFormPrefix}{$sAttCode}", null);

View File

@@ -1619,6 +1619,82 @@ class AttributeDateTime extends AttributeDBField
}
}
/**
* Store a duration as a number of seconds
*
* @package iTopORM
*/
class AttributeDuration extends AttributeInteger
{
public function GetEditClass() {return "Duration";}
protected function GetSQLCol() {return "INT(11) UNSIGNED";}
public function GetNullValue() {return '0';}
public function GetDefaultValue()
{
return 0;
}
public function MakeRealValue($proposedValue)
{
if (is_null($proposedValue)) return null;
if (!is_numeric($proposedValue)) return null;
if ( ((int)$proposedValue) < 0) return null;
return (int)$proposedValue;
}
public function ScalarToSQL($value)
{
if (is_null($value))
{
return null;
}
return $value;
}
public function GetAsHTML($value)
{
return Str::pure2html(self::FormatDuration($value));
}
static function FormatDuration($duration)
{
$aDuration = self::SplitDuration($duration);
$sResult = '';
if ($duration < 60)
{
// Less than 1 min
$sResult = Dict::Format('Core:Duration_Seconds', $aDuration['seconds']);
}
else if ($duration < 3600)
{
// less than 1 hour, display it in minutes/seconds
$sResult = Dict::Format('Core:Duration_Minutes_Seconds', $aDuration['minutes'], $aDuration['seconds']);
}
else if ($duration < 86400)
{
// Less than 1 day, display it in hours/minutes/seconds
$sResult = Dict::Format('Core:Duration_Hours_Minutes_Seconds', $aDuration['hours'], $aDuration['minutes'], $aDuration['seconds']);
}
else
{
// more than 1 day, display it in days/hours/minutes/seconds
$sResult = Dict::Format('Core:Duration_Days_Hours_Minutes_Seconds', $aDuration['days'], $aDuration['hours'], $aDuration['minutes'], $aDuration['seconds']);
}
return $sResult;
}
static function SplitDuration($duration)
{
$days = floor($duration / 86400);
$hours = floor(($duration - (86400*$days)) / 3600);
$minutes = floor(($duration - (86400*$days + 3600*$hours)) / 60);
$seconds = ($duration % 60); // modulo
return array( 'days' => $days, 'hours' => $hours, 'minutes' => $minutes, 'seconds' => $seconds );
}
}
/**
* Map a date+time column to an attribute
*

View File

@@ -349,7 +349,12 @@ abstract class DBObject
// #@# non-scalar attributes.... handle that differently
$this->Reload();
}
return $this->m_aCurrValues[$sAttCode];
$value = $this->m_aCurrValues[$sAttCode];
if ($value instanceof DBObjectSet)
{
$value->Rewind();
}
return $value;
}
public function GetOriginal($sAttCode)

View File

@@ -537,5 +537,19 @@ Dict::Add('EN US', 'English', 'English', array(
'Core:SynchroLogTitle' => '%1$s - %2$s',
'Core:Synchro:Nb_Replica' => 'Replica processed: %1$s',
'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.',
));
//
// Attribute Duration
//
Dict::Add('EN US', 'English', 'English', array(
'Core:Duration_Seconds' => '%1$ds',
'Core:Duration_Minutes_Seconds' =>'%1$dmin %2$ds',
'Core:Duration_Hours_Minutes_Seconds' => '%1$dh %2$dmin %3$ds',
'Core:Duration_Days_Hours_Minutes_Seconds' => '%1$sd %2$dh %3$dmin %4$ds',
));
?>

View File

@@ -400,7 +400,7 @@ Dict::Add('EN US', 'English', 'English', array(
'UI:HistoryTab' => 'History',
'UI:NotificationsTab' => 'Notifications',
'UI:History:BulkImports' => 'History',
'UI:History:BulkImports+' => 'List of CSV imports (last first)',
'UI:History:BulkImports+' => 'List of CSV imports (latest import first)',
'UI:History:BulkImportDetails' => 'Changes resulting from the CSV import performed on %1$s (by %2$s)',
'UI:History:Date' => 'Date',
'UI:History:Date+' => 'Date of the change',
@@ -879,9 +879,7 @@ When associated with a trigger, each action is given an "order" number, specifyi
'Portal:AddAttachment' => ' Add Attachment ',
'Portal:RemoveAttachment' => ' Remove Attachment ',
'Portal:Attachment_No_To_Ticket_Name' => 'Attachment #%1$d to %2$s (%3$s)',
'Enum:Undefined' => 'Undefined',
'Enum:Undefined' => 'Undefined',
'UI:DurationForm_Days_Hours_Minutes_Seconds' => '%1$s Days %2$s Hours %3$s Minutes %4$s Seconds',
));
?>

View File

@@ -292,6 +292,20 @@ function ValidatePasswordField(id, sFormId)
return true;
}
// Manage a 'duration' field
function UpdateDuration(iId)
{
var iDays = parseInt($('#'+iId+'_d').val(), 10);
var iHours = parseInt($('#'+iId+'_h').val(), 10);
var iMinutes = parseInt($('#'+iId+'_m').val(), 10);
var iSeconds = parseInt($('#'+iId+'_s').val(), 10);
var iDuration = (((iDays*24)+ iHours)*60+ iMinutes)*60 + iSeconds;
$('#'+iId).val(iDuration);
$('#'+iId).trigger('change');
return true;
}
// Called when filling an autocomplete field
function OnAutoComplete(id, event, data, formatted)
{

View File

@@ -57,8 +57,8 @@ class SynchroDataSource extends cmdbAbstractObject
//MetaModel::Init_AddAttribute(new AttributeDateTime("last_synchro_date", array("allowed_values"=>null, "sql"=>"last_synchro_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
// Format: '1 hour', '2 weeks', '3 hoursABCDEF'... Cf DateTime->Modify()
MetaModel::Init_AddAttribute(new AttributeString("full_load_periodicity", array("allowed_values"=>null, "sql"=>"full_load_periodicity", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
// Format: seconds (int)
MetaModel::Init_AddAttribute(new AttributeDuration("full_load_periodicity", array("allowed_values"=>null, "sql"=>"full_load_periodicity", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array())));
// MetaModel::Init_AddAttribute(new AttributeString("reconciliation_list", array("allowed_values"=>null, "sql"=>"reconciliation_list", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("reconciliation_policy", array("allowed_values"=>new ValueSetEnum('use_primary_key,use_attributes'), "sql"=>"reconciliation_policy", "default_value"=>"use_attributes", "is_null_allowed"=>false, "depends_on"=>array())));
@@ -69,8 +69,8 @@ class SynchroDataSource extends cmdbAbstractObject
MetaModel::Init_AddAttribute(new AttributeEnum("delete_policy", array("allowed_values"=>new ValueSetEnum('ignore,delete,update,update_then_delete'), "sql"=>"delete_policy", "default_value"=>"ignore", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeString("delete_policy_update", array("allowed_values"=>null, "sql"=>"delete_policy_update", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Format: '1 hour', '2 weeks', '3 hoursABCDEF'... Cf DateTime->Modify()
MetaModel::Init_AddAttribute(new AttributeString("delete_policy_retention", array("allowed_values"=>null, "sql"=>"delete_policy_retention", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
// Format: seconds (unsigned int)
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())));
MetaModel::Init_AddAttribute(new AttributeLinkedSet("status_list", array("linked_class"=>"SynchroLog", "ext_key_to_me"=>"sync_source_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array())));
@@ -90,6 +90,7 @@ class SynchroDataSource extends cmdbAbstractObject
$oPage->SetCurrentTab(Dict::S('Core:SynchroAttributes'));
$oAttributeSet = $this->Get('attribute_list');
$aAttributes = array();
while($oAttribute = $oAttributeSet->Fetch())
{
$aAttributes[$oAttribute->Get('attcode')] = $oAttribute;
@@ -351,6 +352,47 @@ EOF
}
$this->Set('attribute_list', $oAttributeSet);
}
/*
* Overload the standard behavior
*/
public function DoCheckToWrite()
{
parent::DoCheckToWrite();
// Check that there is at least one reconciliation key defined
if ($this->Get('reconciliation_policy') == 'use_attributes')
{
$oSet = $this->Get('attribute_list');
$oSynchroAttributeList = $oSet->ToArray();
$bReconciliationKey = false;
foreach($oSynchroAttributeList as $oSynchroAttribute)
{
if ($oSynchroAttribute->Get('reconcile') == 1)
{
$bReconciliationKey = true; // At least one key is defined
}
}
if (!$bReconciliationKey)
{
$this->m_aCheckIssues[] = Dict::Format('Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified');
}
}
// If 'update_then_delete' is specified there must be a delete_retention_period
if (($this->Get('delete_policy') == 'update_then_delete') && ($this->Get('delete_policy_retention') == 0))
{
$this->m_aCheckIssues[] = Dict::Format('Class:SynchroDataSource/Error:DeleteRetentionDurationMustBeSpecified');
}
// If update is specified, then something to update must be defined
if ((($this->Get('delete_policy') == 'update_then_delete') || ($this->Get('delete_policy') == 'update'))
&& ($this->Get('delete_policy_update') == ''))
{
$this->m_aCheckIssues[] = Dict::Format('Class:SynchroDataSource/Error:DeletePolicyUpdateMustBeSpecified');
}
}
public function GetTargetClass()
{
return $this->Get('scope_class');
@@ -562,22 +604,11 @@ EOF
{
// No previous import known, use the full_load_periodicity value... and the current date
$oLastFullLoadStartDate = new DateTime(); // Now
// TO DO: how do we support localization here ??
$sLoadPeriodicity = trim($this->Get('full_load_periodicity'));
if (strlen($sLoadPeriodicity) > 0)
$iLoadPeriodicity = $this->Get('full_load_periodicity'); // Duration in seconds
if ($iLoadPeriodicity > 0)
{
$sInterval = '-'.$sLoadPeriodicity;
// Note: the PHP doc states that Modify return FALSE in case of error
// but, this is actually NOT the case
// Therefore, I do compare before and after, considering that the
// format is incorrect when the datetime remains unchanged
$sBefore = $oLastFullLoadStartDate->Format('Y-m-d H:i:s');
$sInterval = "-$iLoadPeriodicity seconds";
$oLastFullLoadStartDate->Modify($sInterval);
$sAfter = $oLastFullLoadStartDate->Format('Y-m-d H:i:s');
if ($sBefore == $sAfter)
{
throw new SynchroExceptionNotStarted("Data exchange: Wrong interval specification", array('interval' => $sInterval, 'source_id' => $this->GetKey()));
}
}
}
$sLimitDate = $oLastFullLoadStartDate->Format('Y-m-d H:i:s');
@@ -689,21 +720,11 @@ EOF
if ($sDeletePolicy == 'update_then_delete')
{
$oDeletionDate = $oLastFullLoadStartDate;
$sDeleteRetention = trim($this->Get('delete_policy_retention')); // MUST NOT BE NULL
if (strlen($sDeleteRetention) > 0)
$iDeleteRetention = $this->Get('delete_policy_retention'); // Duration in seconds
if ($iDeleteRetention > 0)
{
$sInterval = '-'.$sDeleteRetention;
// Note: the PHP doc states that Modify return FALSE in case of error
// but, this is actually NOT the case
// Therefore, I do compare before and after, considering that the
// format is incorrect when the datetime remains unchanged
$sBefore = $oDeletionDate->Format('Y-m-d H:i:s');
$sInterval = "-$iDeleteRetention seconds";
$oDeletionDate->Modify($sInterval);
$sAfter = $oDeletionDate->Format('Y-m-d H:i:s');
if ($sBefore == $sAfter)
{
throw new SynchroExceptionNotStarted("Data exchange: Wrong interval specification", array('interval' => $sInterval, 'source_id' => $this->GetKey()));
}
}
$sDeletionDate = $oDeletionDate->Format('Y-m-d H:i:s');
$aTraces[] = "Deletion date: $sDeletionDate";
@@ -982,7 +1003,7 @@ class SynchroReplica extends DBObject
MetaModel::Init_AddAttribute(new AttributeExternalKey("sync_source_id", array("targetclass"=>"SynchroDataSource", "jointype"=> "", "allowed_values"=>null, "sql"=>"sync_source_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeInteger("dest_id", array("allowed_values"=>null, "sql"=>"dest_id", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeClass("dest_class", array("class_category"=>"bizmodel", "more_values"=>"", "sql"=>"dest_class", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeClass("dest_class", array("class_category"=>"bizmodel", "more_values"=>"", "sql"=>"dest_class", "default_value"=>'Organization', "is_null_allowed"=>true, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeDateTime("status_last_seen", array("allowed_values"=>null, "sql"=>"status_last_seen", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array())));
MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('new,synchronized,modified,orphan,obsolete'), "sql"=>"status", "default_value"=>"new", "is_null_allowed"=>false, "depends_on"=>array())));