From f33c82130689f68873fa6ade9577f2787835e1a1 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 29 Oct 2012 15:36:55 +0000 Subject: [PATCH] Merged, the implementation of Track #582: "stable name" for synchro_data_xxx tables SVN:1.2[2404] --- core/bulkchange.class.inc.php | 8 ++- dictionaries/de.dictionary.itop.core.php | 3 ++ dictionaries/dictionary.itop.core.php | 3 ++ dictionaries/fr.dictionary.itop.core.php | 3 ++ synchro/synchrodatasource.class.inc.php | 64 +++++++++++++++++++++--- 5 files changed, 73 insertions(+), 8 deletions(-) diff --git a/core/bulkchange.class.inc.php b/core/bulkchange.class.inc.php index ccbd2e912..c0e747d5e 100644 --- a/core/bulkchange.class.inc.php +++ b/core/bulkchange.class.inc.php @@ -401,7 +401,13 @@ class BulkChange if ($sAttCode == 'id') continue; $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); - if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect()) + $aReasons = array(); + $iFlags = $oTargetObj->GetAttributeFlags($sAttCode, $aReasons); + if ( (($iFlags & OPT_ATT_READONLY) == OPT_ATT_READONLY) && ( $oTargetObj->Get($sAttCode) != $aRowData[$iCol]) ) + { + $aErrors[$sAttCode] = "the attribute '$sAttCode' is read-only and cannot be modified (current value: ".$oTargetObj->Get($sAttCode).", proposed value: {$aRowData[$iCol]})."; + } + else if ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect()) { try { diff --git a/dictionaries/de.dictionary.itop.core.php b/dictionaries/de.dictionary.itop.core.php index abedb4827..56aa9eced 100644 --- a/dictionaries/de.dictionary.itop.core.php +++ b/dictionaries/de.dictionary.itop.core.php @@ -284,6 +284,9 @@ Dict::Add('DE DE', 'German', 'Deutsch', array( 'Class:SynchroReplica/Attribute:status_last_warning' => 'Letzte Warnung', 'Class:SynchroReplica/Attribute:info_creation_date' => 'Erzeugungs-Datum', 'Class:SynchroReplica/Attribute:info_last_modified' => 'Datum der letzten Modifikation', + 'Class:SynchroDataSource/Attribute:database_table_name' => 'Datenbanktabelle', + 'Class:SynchroDataSource/Attribute:database_table_name+' => 'Name der Tabelle, die Speicherung der Daten aus dieser Datenquelle. Ein Default-Name wird automatisch berechnet, wenn dieses Feld leer gelassen wird.', + 'Class:SynchroDataSource/Error:DataTableAlreadyExists' => 'Tabelle %1$s existiert bereits in der Datenbank. Bitte benutzen Sie einen anderen Namen für die Datenbanktabelle aus dieser Datenquelle.', 'Class:appUserPreferences' => 'Benutzer-Voreinstellungen', 'Class:appUserPreferences/Attribute:userid' => 'Benutzer', 'Class:appUserPreferences/Attribute:preferences' => 'Voreinstellungen', diff --git a/dictionaries/dictionary.itop.core.php b/dictionaries/dictionary.itop.core.php index e68c0f51d..deba9ac78 100644 --- a/dictionaries/dictionary.itop.core.php +++ b/dictionaries/dictionary.itop.core.php @@ -570,6 +570,8 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:SynchroDataSource/Attribute:delete_policy_update+' => 'Syntax: field_name:value; ...', 'Class:SynchroDataSource/Attribute:delete_policy_retention' => 'Retention Duration', 'Class:SynchroDataSource/Attribute:delete_policy_retention+' => 'How much time an obsolete object is kept before being deleted', + 'Class:SynchroDataSource/Attribute:database_table_name' => 'Data table', + 'Class:SynchroDataSource/Attribute:database_table_name+' => 'Name of the table to store the synchronization data. If left empty, a default name will be computed.', 'SynchroDataSource:Description' => 'Description', 'SynchroDataSource:Reconciliation' => 'Search & reconciliation', 'SynchroDataSource:Deletion' => 'Deletion rules', @@ -618,6 +620,7 @@ Dict::Add('EN US', 'English', 'English', array( 'Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified' => 'At Least one reconciliation key must be specified, or the reconciliation policy must be to use the primary key.', '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:DataTableAlreadyExists' => 'The table %1$s already exists in the database. Please use another name for the synchro data table.', 'Core:SynchroReplica:PublicData' => 'Public Data', 'Core:SynchroReplica:PrivateDetails' => 'Private Details', 'Core:SynchroReplica:BackToDataSource' => 'Go Back to the Synchro Data Source: %1$s', diff --git a/dictionaries/fr.dictionary.itop.core.php b/dictionaries/fr.dictionary.itop.core.php index 1ec62c57a..c09d16c40 100644 --- a/dictionaries/fr.dictionary.itop.core.php +++ b/dictionaries/fr.dictionary.itop.core.php @@ -280,6 +280,8 @@ Dict::Add('FR FR', 'French', 'Français', array( 'Class:SynchroDataSource/Attribute:url_icon+' => 'Hyperlien vers une icône représentant l\'application source des données', 'Class:SynchroDataSource/Attribute:url_application' => 'Application (hyperlien)', 'Class:SynchroDataSource/Attribute:url_application+' => 'Un hyperlien vers l\'application source des données. Paramètres possibles: $this->nom_de_champ$ et $replica->primary_key$', + 'Class:SynchroDataSource/Attribute:database_table_name' => 'Table de données', + 'Class:SynchroDataSource/Attribute:database_table_name+' => 'Nom de la table stockant les données de cette source. Un nom par défaut est calculé automatiquement si ce champ est laissé vide.', 'Class:SynchroAttribute' => 'Champs de synchronisation', 'Class:SynchroAttribute+' => '', 'Class:SynchroAttribute/Attribute:sync_source_id' => 'Source de données', @@ -587,6 +589,7 @@ Opérateurs :
'Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified' => 'Si la politique de réconciliation n\'est pas la clé primaire, au moins une clé de recherche doit être spécifiée', 'Class:SynchroDataSource/Error:DeleteRetentionDurationMustBeSpecified' => 'Pour que les objets soient effacés après avoir été obsoletés, il faut spécifier une durée de rétention', 'Class:SynchroDataSource/Error:DeletePolicyUpdateMustBeSpecified' => 'Les objets obsolètes doivent être mis à jour, mais aucune information de mise à jour n\'est spécifiée', + 'Class:SynchroDataSource/Error:DataTableAlreadyExists' => 'La table %1$s existe déjà dans la base de données. Veuillez utiliser un autre nom pour la table des données de cette source.', 'Core:SynchroReplica:PublicData' => 'Données synchronisées', 'Core:SynchroReplica:PrivateDetails' => 'Informations internes', 'Core:SynchroReplica:BackToDataSource' => 'Retourner aux détails de la source de données: %1$s', diff --git a/synchro/synchrodatasource.class.inc.php b/synchro/synchrodatasource.class.inc.php index 88022d3df..d1203ca24 100644 --- a/synchro/synchrodatasource.class.inc.php +++ b/synchro/synchrodatasource.class.inc.php @@ -54,7 +54,8 @@ class SynchroDataSource extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("targetclass"=>"User", "jointype"=>null, "allowed_values"=>null, "sql"=>"user_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("notify_contact_id", array("targetclass"=>"Contact", "jointype"=>null, "allowed_values"=>null, "sql"=>"notify_contact_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeClass("scope_class", array("class_category"=>"bizmodel,addon/authentication", "more_values"=>"", "sql"=>"scope_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - + MetaModel::Init_AddAttribute(new AttributeString("database_table_name", array("allowed_values"=>null, "sql"=>"database_table_name", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array(), "validation_pattern" => "^[A-Za-z0-9_]*$"))); + // Declared here for a future usage, but ignored so far MetaModel::Init_AddAttribute(new AttributeString("scope_restriction", array("allowed_values"=>null, "sql"=>"scope_restriction", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); @@ -86,7 +87,7 @@ class SynchroDataSource extends cmdbAbstractObject // Display lists MetaModel::Init_SetZListItems('details', array( 'col:0'=> array( - 'fieldset:SynchroDataSource:Description' => array('name','description','status','scope_class','user_id','notify_contact_id','url_icon','url_application')), + 'fieldset:SynchroDataSource:Description' => array('name','description','status','scope_class','user_id','notify_contact_id','url_icon','url_application', 'database_table_name')), 'col:1'=> array( 'fieldset:SynchroDataSource:Reconciliation' => array('reconciliation_policy','action_on_zero','action_on_one','action_on_multiple'), 'fieldset:SynchroDataSource:Deletion' => array('user_delete_policy','full_load_periodicity','delete_policy','delete_policy_update','delete_policy_retention')) @@ -98,6 +99,15 @@ class SynchroDataSource extends cmdbAbstractObject // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } + public function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array()) + { + if (!$this->IsNew()) + { + $this->Set('database_table_name', $this->GetDataTable()); + } + parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams); + } + public function DisplayBareRelations(WebPage $oPage, $bEditMode = false) { if (!$this->IsNew()) @@ -476,7 +486,7 @@ EOF public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '') { - if (($sAttCode == 'scope_class') && (!$this->IsNew())) + if ( (($sAttCode == 'scope_class') || ($sAttCode == 'database_table_name')) && (!$this->IsNew())) { return OPT_ATT_READONLY; } @@ -574,6 +584,13 @@ EOF if ($this->IsNew()) { + // Compute the database_table_name + $sDataTable = $this->Get('database_table_name'); + if (!empty($sDataTable)) + { + $this->Set('database_table_name', $this->ComputeDataTableName()); + } + // When inserting a new datasource object, also create the SynchroAttribute objects // for each field of the target class // Create all the SynchroAttribute records @@ -659,6 +676,17 @@ EOF { $this->m_aCheckIssues[] = Dict::Format('Class:SynchroDataSource/Error:DeletePolicyUpdateMustBeSpecified'); } + + // When creating the data source with a specified database_table_name, this table must NOT exist + if ($this->IsNew()) + { + $sDataTable = $this->GetDataTable(); + if (!empty($sDataTable) && CMDBSource::IsTable($this->GetDataTable())) + { + // Hmm, the synchro_data_xxx table already exists !! + $this->m_aCheckIssues[] = Dict::Format('Class:SynchroDataSource/Error:DataTableAlreadyExists', $this->GetDataTable()); + } + } } public function GetTargetClass() @@ -668,12 +696,34 @@ EOF public function GetDataTable() { - $sName = strtolower($this->GetTargetClass()); - $sName = str_replace('\'"&@|\\/ ', '_', $sName); // Remove forbidden characters from the table name - $sName .= '_'.$this->GetKey(); // Add a suffix for unicity - $sTable = MetaModel::GetConfig()->GetDBSubName()."synchro_data_$sName"; // Add the prefix if any + $sTable = $this->Get('database_table_name'); + if (empty($sTable)) + { + $sTable = $this->ComputeDataTableName(); + } return $sTable; } + + protected function ComputeDataTableName() + { + $sDBTableName = $this->Get('database_table_name'); + if (empty($sDBTableName)) + { + $sDBTableName = strtolower($this->GetTargetClass()); + $sDBTableName = preg_replace('/[^A-za-z0-9_]/', '_', $sDBTableName); // Remove forbidden characters from the table name + $sDBTableName .= '_'.$this->GetKey(); // Add a suffix for unicity + } + else + { + $sDBTableName = preg_replace('/[^A-za-z0-9_]/', '_', $sDBTableName); // Remove forbidden characters from the table name + } + $sPrefix = MetaModel::GetConfig()->GetDBSubName()."synchro_data_"; + if (strpos($sDBTableName, $sPrefix) !== 0) + { + $sDBTableName = $sPrefix.$sDBTableName; + } + return $sDBTableName; + } /** * When the new datasource has been created, let's create the synchro_data table