diff --git a/.doc/README.md b/.doc/README.md index 2bc851cd7..50a0769df 100644 --- a/.doc/README.md +++ b/.doc/README.md @@ -1,52 +1,103 @@ -# phpdoc dokuwiki template +# Phpdoc dokuwiki template This directory contains a template rendering iTop phpdoc as wiki pages. +conventional tag that you should use: + * `@internal` : exclude from the documentation. + * `@api` : it means that a method is an api, thus it may be interacted with. + * `@see` : it points to another documented method + * `@link` : external url + * if you point to another page of the wiki, please use relative links. + * `@example` : let you provide example of code + * `@param`, `@return`, `@throws`, ... -## special instructions + +## Special instructions some tags where added : * `@api-advanced`: it means that a method is an `@api` but mark it also as "complex" to use * `@overwritable-hook`: used to mark a method as "designed to be extended" * `@extension-hook`: not used for now - * `@phpdoc-tunning-exclude-inherited`: tunning tag that inform this template to ignere inherited methods in the rendrering. + * `@phpdoc-tuning-exclude-inherited`: once this tag is present on a class, it's inherited methods won't be showed. -some behaviours where added : - * only public `@api` and `@api-advanced` are showed - * class properties are hidden (subject to change, but will require to white list them using `@api` tag) -known limitations: - * inline tag do not work properly - * links are hardcoded instead of using phpdoc routes (adding our own routes was too complex) - * `@see` tags must be very specific: - * always prefix class members with `Class::` (ie: `DBObject::Get()`) +### known limitations: +#### `@see` tags must be very specific: + * always prefix class members with `ClassName::` * for methods always suffix them with `()`, - * for variables, always prefix them with `$`. - * do not use inline @see - * as spaces are used to mark code, the templates have very few indentation, thus they are awful to read (sorry) - * `@example` - * the sentence in the first line (next to the tag) is the title, it must be enclose by double quotes - * the following lines are the sample code. - * šŸ’” since we simply hack the official tag, this syntax must be respected carefully šŸ’” - + * do not reference variables since they are not documented. If you have to, always prefix them with `$` +examples: +``` +/** + * @see DBObject + * @see DBObject::Get() + * @see DBObject::$foo + */ +``` -## dokuwiki requirements - * the templates uses the [wrap plugin](https://www.dokuwiki.org/plugin:wrap). - * the generated files have to be placed under an arbitrary directory of `[/path/to/dokuwiki]/data/pages`. - * the html as to be activated [config:htmlok](https://www.dokuwiki.org/config:htmlok) - * the generated files have to be in lowercase +#### Do not use inline tags, they do not work properly, example: +``` +/** + * This is a texts with ans inline tag {@see [FQSEN] []} it must never be used + */ +``` + +#### The `@example` tag must respect this very precise syntax + * the sentence in the first line (next to the tag) is the title, it must be enclose by double quotes + * the following lines are the sample code. + * šŸ’” since we simply hack the official tag, this syntax must be respected carefully šŸ’” +example: +``` +/** +* @example "This is the title of the multiline example" +* $foo = DBObject::Get('foo'); +* DBObject::Set('foo', ++$foo); +*/ +``` + +## How content is included into the documentation -## installation +**For a class** those requirements have to be respected: + - the file containing the class must be listed in `/phpdoc/files/file[]` of `.doc/phpdoc-objects-manipulation.dist.xml` + - the class **must not** have the tag `@internal` + - the class **must** have at least one of: `@api`, `@api-advanced`, `@overwritable-hook`, `@extension-hook` + +Then, **for a method** of an eligible class: + - **public** methods **must** have at least one of: `@api`, `@api-advanced`, `@overwritable-hook`, `@extension-hook` + - **protected** methods **must** have at least one of: `@overwritable-hook`, `@extension-hook` + - **private** methods are **always excluded** + +**Class properties** and **constants** are never documented (this is subject to change). + + + + +## A note about the rendering engine + +:notebook: as spaces are used to mark code, the templates (`.doc/phpdoc-templates/combodo-wiki/*`) have very few indentation, thus they are awful to read (sorry). + + + + +## Installation ``` composer require phpdocumentor/phpdocumentor:~2 --dev ``` -## bin +## Generation `.doc/bin/build-doc-object-manipulation` and `.doc/bin/build-doc-extensions` contains examples of doc. generation, beware: they have to be called from iTop root directory: ```shell cd /path/to/itop/ ./.doc/bin/build-doc-object-manipulation ``` -the resulting documentation is written into `data/phpdocumentor/output` \ No newline at end of file +the resulting documentation is written into `data/phpdocumentor/output` + + +## Dokuwiki requirements + * the template uses the [wrap plugin](https://www.dokuwiki.org/plugin:wrap). + * the generated files have to be placed under an arbitrary directory of `[/path/to/dokuwiki]/data/pages`. + * the html has to be activated [config:htmlok](https://www.dokuwiki.org/config:htmlok) + * the generated files have to be in lowercase + diff --git a/.doc/phpdoc-templates/combodo-wiki/class.txt.twig b/.doc/phpdoc-templates/combodo-wiki/class.txt.twig index 94e805173..5959eff9f 100644 --- a/.doc/phpdoc-templates/combodo-wiki/class.txt.twig +++ b/.doc/phpdoc-templates/combodo-wiki/class.txt.twig @@ -64,7 +64,7 @@ {% include 'includes/see-also.txt.twig' with {structure:node, title_level: '==='} %} -{% include 'includes/tags.txt.twig' with {structure:node, title_level: '=====', blacklist: ['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'phpdoc-tunning-exclude-inherited', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'copyright', 'license', 'code-example']} %} +{% include 'includes/tags.txt.twig' with {structure:node, title_level: '=====', blacklist: ['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'phpdoc-tuning-exclude-inherited', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'copyright', 'license', 'code-example']} %} {% set methods = node.inheritedMethods.merge(node.methods.merge(node.magicMethods)) %} {% include 'includes/tag-synthesys.txt.twig' with {methods:methods, tag:'api'} %} @@ -86,7 +86,7 @@ or method.tags['extension-hook'] is defined ) and ( - node.tags['phpdoc-tunning-exclude-inherited'] is not defined + node.tags['phpdoc-tuning-exclude-inherited'] is not defined or method.parent.name == node.name ) %} diff --git a/.doc/phpdoc-templates/combodo-wiki/includes/tag-synthesys.txt.twig b/.doc/phpdoc-templates/combodo-wiki/includes/tag-synthesys.txt.twig index 05b26087a..1c68b90b1 100644 --- a/.doc/phpdoc-templates/combodo-wiki/includes/tag-synthesys.txt.twig +++ b/.doc/phpdoc-templates/combodo-wiki/includes/tag-synthesys.txt.twig @@ -47,7 +47,7 @@ you can extend the behaviour of iTop by implementing: {% endif %} {% set sanitizedMethod = method|trim('\\', 'left')|replace({(node.name~'::'): ''}) %} {% if '::' in sanitizedMethod -%} -{%- if node.tags['phpdoc-tunning-exclude-inherited'] is not defined %} +{%- if node.tags['phpdoc-tuning-exclude-inherited'] is not defined %} * [[{{sanitizedMethod|replace({'::': '#'})|lower}}|↪{{sanitizedMethod}}]] — {{ method.summary|replace({"\n":""})|raw }} {% endif %} {%- else %} diff --git a/.doc/phpdoc-templates/combodo-wiki/interface.txt.twig b/.doc/phpdoc-templates/combodo-wiki/interface.txt.twig index 5bf639994..2f8d0698c 100644 --- a/.doc/phpdoc-templates/combodo-wiki/interface.txt.twig +++ b/.doc/phpdoc-templates/combodo-wiki/interface.txt.twig @@ -64,7 +64,7 @@ {% include 'includes/see-also.txt.twig' with {structure:node, title_level: '==='} %} -{% include 'includes/tags.txt.twig' with {structure:node, title_level: '=====', blacklist: ['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'phpdoc-tunning-exclude-inherited', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'copyright', 'license', 'code-example']} %} +{% include 'includes/tags.txt.twig' with {structure:node, title_level: '=====', blacklist: ['link', 'see', 'abstract', 'example', 'method', 'property', 'property-read', 'property-write', 'package', 'subpackage', 'phpdoc-tuning-exclude-inherited', 'api', 'api-advanced', 'overwritable-hook', 'extension-hook', 'copyright', 'license', 'code-example']} %} {% set methods = node.inheritedMethods.merge(node.methods) %} diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 3cd1efd64..385b98924 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -385,6 +385,7 @@ EOF if (!isset($aExtraParams['disable_plugins']) || !$aExtraParams['disable_plugins']) { + /** @var iApplicationUIExtension $oExtensionInstance */ foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oExtensionInstance->OnDisplayProperties($this, $oPage, $bEditMode); @@ -648,6 +649,7 @@ EOF } $oPage->SetCurrentTab(''); + /** @var \iApplicationUIExtension $oExtensionInstance */ foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oExtensionInstance->OnDisplayRelations($this, $oPage, $bEditMode); @@ -3033,6 +3035,7 @@ EOF $current = parent::GetHilightClass(); // Default computation // Invoke extensions before the deletion (the deletion will do some cleanup and we might loose some information + /** @var \iApplicationUIExtension $oExtensionInstance */ foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $new = $oExtensionInstance->GetHilightClass($this); @@ -3454,6 +3457,7 @@ EOF } // Invoke extensions after the update of the object from the form + /** @var \iApplicationUIExtension $oExtensionInstance */ foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oExtensionInstance->OnFormSubmit($this, $sFormPrefix); diff --git a/application/datatable.class.inc.php b/application/datatable.class.inc.php index 05c15af85..00d97d9ee 100644 --- a/application/datatable.class.inc.php +++ b/application/datatable.class.inc.php @@ -184,6 +184,10 @@ class DataTable */ public function GetAsHTMLTableRows(WebPage $oPage, $iPageSize, $aColumns, $sSelectMode, $bViewLink, $aExtraParams) { + if ($iPageSize < 1) + { + $iPageSize = -1; // convention: no pagination + } $aAttribs = $this->GetHTMLTableConfig($aColumns, $sSelectMode, $bViewLink); $aValues = $this->GetHTMLTableValues($aColumns, $sSelectMode, $iPageSize, $bViewLink, $aExtraParams); @@ -222,14 +226,21 @@ class DataTable } $sCombo = ''; $sPages = Dict::S('UI:Pagination:PagesLabel'); @@ -577,8 +588,8 @@ EOF public function UpdatePager(WebPage $oPage, $iDefaultPageSize, $iStart) { - $iPageSize = ($iDefaultPageSize < 1) ? 1 : $iDefaultPageSize; - $iPageIndex = 1 + floor($iStart / $iPageSize); + $iPageSize = $iDefaultPageSize; + $iPageIndex = 0; $sHtml = $this->GetPager($oPage, $iPageSize, $iDefaultPageSize, $iPageIndex); $oPage->add_ready_script("$('#pager{$this->iListId}').html('".json_encode($sHtml)."');"); if ($iDefaultPageSize < 1) @@ -936,4 +947,4 @@ class DataTableSettings implements Serializable } return $ret; } -} \ No newline at end of file +} diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index b6886169b..b4ae94e56 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -500,7 +500,10 @@ class DisplayBlock } $aAttribs =array( 'group' => array('label' => $sGroupByLabel, 'description' => ''), - 'value' => array('label'=> Dict::S('UI:GroupBy:'.$sAggregationFunction), 'description' => Dict::Format('UI:GroupBy:'.$sAggregationFunction.'+', $sAggregationAttr)) + 'value' => array( + 'label' => Dict::S('UI:GroupBy:'.$sAggregationFunction), + 'description' => Dict::Format('UI:GroupBy:'.$sAggregationFunction.'+', $sAggregationAttr), + ), ); $sFormat = isset($aExtraParams['format']) ? $aExtraParams['format'] : 'UI:Pagination:HeaderNoSelection'; $sHtml .= $oPage->GetP(Dict::Format($sFormat, $iTotalCount)); @@ -699,7 +702,7 @@ class DisplayBlock 'breadcrumb_label' => MetaModel::GetName($this->m_oSet->GetClass()), 'breadcrumb_max_count' => utils::GetConfig()->Get('breadcrumb.max_count'), 'breadcrumb_instance_id' => MetaModel::GetConfig()->GetItopInstanceid(), - 'breadcrumb_icon' => utils::GetAbsoluteUrlAppRoot().'images/breadcrumb-search.png' + 'breadcrumb_icon' => utils::GetAbsoluteUrlAppRoot().'images/breadcrumb-search.png', )); $oPage->add_ready_script("$('body').trigger('update_history.itop', [$seventAttachedData])"); @@ -1729,6 +1732,7 @@ class MenuBlock extends DisplayBlock */ } $this->AddMenuSeparator($aActions); + /** @var \iApplicationUIExtension $oExtensionInstance */ foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oSet->Rewind(); @@ -1824,6 +1828,7 @@ class MenuBlock extends DisplayBlock } $this->AddMenuSeparator($aActions); + /** @var \iApplicationUIExtension $oExtensionInstance */ foreach (MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oSet->Rewind(); diff --git a/application/template.class.inc.php b/application/template.class.inc.php index 99544644b..e5fd7e1bd 100644 --- a/application/template.class.inc.php +++ b/application/template.class.inc.php @@ -399,6 +399,7 @@ class ObjectDetailsTemplate extends DisplayTemplate $aPlugInProperties = $aMatches[1]; foreach($aPlugInProperties as $sPlugInClass) { + /** @var \iApplicationUIExtension $oInstance */ $oInstance = MetaModel::GetPlugins('iApplicationUIExtension', $sPlugInClass); if ($oInstance != null) // Safety check... { diff --git a/application/ui.linksdirectwidget.class.inc.php b/application/ui.linksdirectwidget.class.inc.php index db2459869..6152af8c9 100644 --- a/application/ui.linksdirectwidget.class.inc.php +++ b/application/ui.linksdirectwidget.class.inc.php @@ -286,11 +286,13 @@ class UILinksWidgetDirect * @param DBObject $oCurrentObj * @param $aAlreadyLinked * + * @param array $aPrefillFormParam + * * @throws \CoreException - * @throws \Exception + * @throws \MissingQueryArgument * @throws \OQLException */ - public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked) + public function GetObjectsSelectionDlg($oPage, $oCurrentObj, $aAlreadyLinked, $aPrefillFormParam = array()) { $sHtml = "
\n"; @@ -333,6 +335,7 @@ class UILinksWidgetDirect $aArgs = array_merge($oCurrentObj->ToArgs('this'), $oFilter->GetInternalParams()); $oFilter->SetInternalParams($aArgs); + $oCurrentObj->PrefillForm('search', $aPrefillFormParam); } $oBlock = new DisplayBlock($oFilter, 'search', false); $sHtml .= $oBlock->GetDisplay($oPage, "SearchFormToAdd_{$this->sInputid}", diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 001adac21..2da7b78e2 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -28,7 +28,7 @@ define('ENABLE_OPT', true); * Please refer to DBSearch's documentation * * @package iTopORM - * @phpdoc-tunning-exclude-inherited this tag prevent PHPdoc from displaying inherited methods. This is done in order to force the API doc. location into DBSearch only. + * @phpdoc-tuning-exclude-inherited this tag prevent PHPdoc from displaying inherited methods. This is done in order to force the API doc. location into DBSearch only. * @api * @see DBSearch * @see DBUnionSearch diff --git a/core/dbunionsearch.class.php b/core/dbunionsearch.class.php index dada399a9..01a0e2a8b 100644 --- a/core/dbunionsearch.class.php +++ b/core/dbunionsearch.class.php @@ -29,7 +29,7 @@ * * * @package iTopORM - * @phpdoc-tunning-exclude-inherited this tag prevent PHPdoc from displaying inherited methods. This is done in order to force the API doc. location into DBSearch only. + * @phpdoc-tuning-exclude-inherited this tag prevent PHPdoc from displaying inherited methods. This is done in order to force the API doc. location into DBSearch only. * @api * @see DBSearch * @see DBObjectSearch diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 935f3bf0c..bfdb61da4 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -6665,20 +6665,21 @@ abstract class MetaModel * Instantiate an object already persisted to the Database. * * @api + * @see MetaModel::GetObjectWithArchive to get object even if it's archived + * @see utils::PushArchiveMode() to enable search on archived objects + * * @param string $sClass * @param int $iKey id value of the object to retrieve * @param bool $bMustBeFound see throws ArchivedObjectException * @param bool $bAllowAllData if true then user rights will be bypassed - use with care! * @param null $aModifierProperties * - * @return DBObject|null null if : (the object is not found) or (archive mode disabled and object is archived and + * @return \cmdbAbstractObject null if : (the object is not found) or (archive mode disabled and object is archived and * $bMustBeFound=false) * @throws CoreException if no result found and $bMustBeFound=true * @throws ArchivedObjectException if archive mode disabled and result is archived and $bMustBeFound=true * @throws \Exception * - * @see MetaModel::GetObjectWithArchive to get object even if it's archived - * @see utils::PushArchiveMode() to enable search on archived objects */ public static function GetObject($sClass, $iKey, $bMustBeFound = true, $bAllowAllData = false, $aModifierProperties = null) { diff --git a/js/jquery.autocomplete.js b/js/jquery.autocomplete.js index ad65f5d16..af07d4232 100644 --- a/js/jquery.autocomplete.js +++ b/js/jquery.autocomplete.js @@ -349,7 +349,10 @@ $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") ); } else { - $input.val( "" ); + // N°532 + // do NOT clear the typed text when the value does not match one of the possible values, but clear the + // actual underlying value so that the input field gets marked as "invalid" if it is mandatory. + // $input.val(""); $input.trigger("result", null); } } @@ -491,7 +494,7 @@ autoFill: false, width: 0, multiple: false, - multipleSeparator: " ", + multipleSeparator: ", ", inputFocus: true, clickFire: false, highlight: function(value, term) { @@ -775,6 +778,8 @@ var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term); if ( formatted === false ) continue; + // Escape dangerous characters to prevent XSS vulnerabilities + formatted = formatted.replace('&', '&').replace('"', '"').replace('>', '>').replace('<', '<'); var li = $("
  • ").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0]; $.data(li, "ac_data", data[i]); } @@ -829,7 +834,7 @@ show: function() { var offset = $(input).offset(); element.css({ - width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(), + 'min-width': typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(), top: offset.top + input.offsetHeight, left: offset.left }).show(); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 3ca298ffc..38a532ca9 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -382,8 +382,16 @@ try $iInputId = utils::ReadParam('iInputId', ''); $iCurrObjectId = utils::ReadParam('iObjId', 0); $oPage->SetContentType('text/html'); + $oAppContext = new ApplicationContext(); + $aPrefillFormParam = array( 'user' => $_SESSION["auth_user"], + 'context' => $oAppContext->GetAsHash(), + 'att_code' => $sAttCode, + 'origin' => 'console', + 'source_obj' => $oObj, + ); + $aPrefillFormParam['dest_class'] = ($oObj === null ? '' : $oObj->Get($sAttCode)->GetClass()); $oWidget = new UILinksWidgetDirect($sClass, $sAttCode, $iInputId); - $oWidget->GetObjectsSelectionDlg($oPage, $oObj, $aAlreadyLinked); + $oWidget->GetObjectsSelectionDlg($oPage, $oObj, $aAlreadyLinked, $aPrefillFormParam); break; // ui.linksdirectwidget @@ -915,6 +923,7 @@ try $iTransactionId = utils::ReadParam('transaction_id', 0, false, 'transaction_id'); $sTempId = utils::GetUploadTempId($iTransactionId); InlineImage::OnFormCancel($sTempId); + /** @var \iApplicationUIExtension $oExtensionInstance */ foreach(MetaModel::EnumPlugins('iApplicationUIExtension') as $oExtensionInstance) { $oExtensionInstance->OnFormCancel($sTempId); diff --git a/setup/runtimeenv.class.inc.php b/setup/runtimeenv.class.inc.php index 8bf2f97bd..3196c358d 100644 --- a/setup/runtimeenv.class.inc.php +++ b/setup/runtimeenv.class.inc.php @@ -944,7 +944,7 @@ class RunTimeEnvironment { // Make a "previous" file copy( - APPROOT.'data/'.$this->sTargetEnv.'.delta.xml', + APPROOT.'data/'.$this->sFinalEnv.'.delta.xml', APPROOT.'data/'.$this->sFinalEnv.'.delta.prev.xml' ); } diff --git a/synchro/synchro_import.php b/synchro/synchro_import.php index 97e7401a2..26f936e99 100644 --- a/synchro/synchro_import.php +++ b/synchro/synchro_import.php @@ -17,7 +17,7 @@ // along with iTop. If not, see /** - * Data Exchange web service + * Data Exchange web service * * @copyright Copyright (C) 2010-2017 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 @@ -28,17 +28,14 @@ // - reconciliation is made on the column primary_key // -if (!defined('__DIR__')) -{ - /** @noinspection DirectoryConstantCanBeUsedInspection */ - define('__DIR__', dirname(__FILE__)); -} -require_once __DIR__.'/../approot.inc.php'; -require_once APPROOT.'/application/application.inc.php'; -require_once APPROOT.'/application/webpage.class.inc.php'; -require_once APPROOT.'/application/csvpage.class.inc.php'; -require_once APPROOT.'/application/clipage.class.inc.php'; -require_once APPROOT.'/application/startup.inc.php'; +if (!defined('__DIR__')) define('__DIR__', dirname(__FILE__)); +require_once(__DIR__.'/../approot.inc.php'); +require_once(APPROOT.'/application/application.inc.php'); +require_once(APPROOT.'/application/webpage.class.inc.php'); +require_once(APPROOT.'/application/csvpage.class.inc.php'); +require_once(APPROOT.'/application/clipage.class.inc.php'); + +require_once(APPROOT.'/application/startup.inc.php'); class ExchangeException extends Exception { @@ -130,15 +127,15 @@ $aPageParams = array 'default' => '0', 'description' => 'Limit on the count of records that can be loaded at once while performing the synchronization', ), - /* - 'reportlevel' => array - ( - 'mandatory' => false, - 'modes' => 'http,cli', - 'default' => 'errors|warnings|created|changed|unchanged', - 'description' => 'combination of flags to limit the detailed output', - ), - */ +/* + 'reportlevel' => array + ( + 'mandatory' => false, + 'modes' => 'http,cli', + 'default' => 'errors|warnings|created|changed|unchanged', + 'description' => 'combination of flags to limit the detailed output', + ), +*/ 'simulate' => array ( 'mandatory' => false, @@ -168,12 +165,12 @@ function UsageAndExit($oP) $bModeCLI = utils::IsModeCLI(); $oP->p("USAGE:\n"); - foreach ($aPageParams as $sParam => $aParamData) + foreach($aPageParams as $sParam => $aParamData) { $aModes = explode(',', $aParamData['modes']); if ($bModeCLI) { - if (in_array('cli', $aModes, false)) + if (in_array('cli', $aModes)) { $sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']'); $oP->p("$sParam = $sDesc"); @@ -181,7 +178,7 @@ function UsageAndExit($oP) } else { - if (in_array('http', $aModes, false)) + if (in_array('http', $aModes)) { $sDesc = $aParamData['description'].', '.($aParamData['mandatory'] ? 'mandatory' : 'optional, defaults to ['.$aParamData['default'].']'); $oP->p("$sParam = $sDesc"); @@ -199,7 +196,6 @@ function ReadParam($oP, $sParam, $sSanitizationFilter = 'parameter') assert(isset($aPageParams[$sParam])); assert(!$aPageParams[$sParam]['mandatory']); $sValue = utils::ReadParam($sParam, $aPageParams[$sParam]['default'], true /* Allow CLI */, $sSanitizationFilter); - return trim($sValue); } @@ -210,18 +206,17 @@ function ReadMandatoryParam($oP, $sParam, $sSanitizationFilter) assert($aPageParams[$sParam]['mandatory']); $sValue = utils::ReadParam($sParam, null, true /* Allow CLI */, $sSanitizationFilter); - if ($sValue === null) + if (is_null($sValue)) { $oP->p("ERROR: Missing argument '$sParam'\n"); UsageAndExit($oP); } - return trim($sValue); } function ChangeDateFormat($sProposedDate, $sFormat, $bDateOnly) { - if ($sProposedDate === '') + if ($sProposedDate == '') { // An empty string means 'reset' return ''; @@ -231,19 +226,22 @@ function ChangeDateFormat($sProposedDate, $sFormat, $bDateOnly) $sRegExpr = $oFormat->ToRegExpr('/'); if (!preg_match($sRegExpr, $sProposedDate)) { - return false; + return false; } - - $oDate = $oFormat->Parse($sProposedDate); - if ($oDate !== null) + else { - $oTargetFormat = $bDateOnly ? AttributeDate::GetInternalFormat() : AttributeDateTime::GetInternalFormat(); - $sDate = $oDate->format($oTargetFormat); - - return $sDate; + $oDate = $oFormat->Parse($sProposedDate); + if ($oDate !== null) + { + $oTargetFormat = $bDateOnly ? AttributeDate::GetInternalFormat() : AttributeDateTime::GetInternalFormat(); + $sDate = $oDate->format($oTargetFormat); + return $sDate; + } + else + { + return false; + } } - - return false; } @@ -260,18 +258,18 @@ class CLILikeWebPage extends WebPage if (utils::IsModeCLI()) { - $oP = new CLIPage(Dict::S('TitleSynchroExecution')); + $oP = new CLIPage(Dict::S("TitleSynchroExecution")); } else { - $oP = new CLILikeWebPage(Dict::S('TitleSynchroExecution')); + $oP = new CLILikeWebPage(Dict::S("TitleSynchroExecution")); } try { utils::UseParamFile(); } -catch (Exception $e) +catch(Exception $e) { $oP->p("Error: ".$e->GetMessage()); $oP->output(); @@ -309,10 +307,10 @@ if (utils::IsModeCLI()) else { $_SESSION['login_mode'] = 'basic'; - require_once APPROOT.'/application/loginwebpage.class.inc.php'; + require_once(APPROOT.'/application/loginwebpage.class.inc.php'); LoginWebPage::DoLogin(); // Check user rights and prompt if needed - $sCSVData = utils::ReadPostedParam('csvdata', '', 'raw_data'); + $sCSVData = utils::ReadPostedParam('csvdata', '', false, 'raw_data'); } @@ -328,7 +326,7 @@ try $sQualifier = ReadParam($oP, 'qualifier', 'raw_data'); $sCharSet = ReadParam($oP, 'charset', 'raw_data'); $sDateTimeFormat = ReadParam($oP, 'date_format', 'raw_data'); - if ($sDateTimeFormat === '') + if ($sDateTimeFormat == '') { $sDateTimeFormat = 'Y-m-d H:i:s'; // By default use the SQL date & time format } @@ -344,7 +342,7 @@ try $sComment = ReadParam($oP, 'comment', 'raw_data'); $sNoStopOnImportError = ReadParam($oP, 'no_stop_on_import_error'); - if (strtolower(trim($sSep)) === 'tab') + if (strtolower(trim($sSep)) == 'tab') { $sSep = "\t"; } @@ -353,10 +351,10 @@ try // then exec phase will need this ! $oLoadStartDate = SynchroExecution::GetDataBaseCurrentDateTime(); - // Note about date formatting: These MySQL settings are read-only... and in fact unused :-( + // Note about date formatting: These MySQL settings are read-only... and in fact unused :-( // SET SESSION date_format = '%d/%m/%Y'; - // SET SESSION datetime_format = '%d/%m/%Y %H:%i:%s'; - // Therefore, we have to allow users to transform the format according to a given specification: date_format + // SET SESSION datetime_format = '%d/%m/%Y %H:%i:%s'; + // Therefore, we have to allow users to transform the format according to a given specification: date_format ////////////////////////////////////////////////// @@ -371,14 +369,14 @@ try // // Check parameters format/consistency // - if (strlen($sCSVData) === 0) + if (strlen($sCSVData) == 0) { - throw new ExchangeException('Missing data - at least one line is expected'); + throw new ExchangeException("Missing data - at least one line is expected"); } /** @var \SynchroDataSource $oDataSource */ $oDataSource = MetaModel::GetObject('SynchroDataSource', $iDataSourceId, false); - if ($oDataSource === null) + if (is_null($oDataSource)) { throw new ExchangeException("Unknown data source id: '$iDataSourceId'"); } @@ -399,18 +397,18 @@ try throw new ExchangeException("Unknown output format: '$sOutput'"); } - /* - $aReportLevels = explode('|', $sReportLevel); - foreach($aReportLevels as $sLevel) +/* + $aReportLevels = explode('|', $sReportLevel); + foreach($aReportLevels as $sLevel) + { + if (!in_array($sLevel, explode('|', 'errors|warnings|created|changed|unchanged'))) { - if (!in_array($sLevel, explode('|', 'errors|warnings|created|changed|unchanged'))) - { - throw new ExchangeException("Unknown level in reporting level: '$sLevel'"); - } + throw new ExchangeException("Unknown level in reporting level: '$sLevel'"); } - */ + } +*/ - if ($sSimulate === '1') + if ($sSimulate == '1') { $bSimulate = true; } @@ -419,7 +417,7 @@ try $bSimulate = false; } - if ($sSynchronize === '1') + if ($sSynchronize == '1') { $bSynchronize = true; } @@ -432,15 +430,15 @@ try // // Parse first line, check attributes, analyse the request // - if ($sCharSet === 'UTF-8') + if ($sCharSet == 'UTF-8') { - $sUTF8Data = $sCSVData; + $sUTF8Data = $sCSVData; } else { $sUTF8Data = iconv($sCharSet, 'UTF-8//IGNORE//TRANSLIT', $sCSVData); } - $oCSVParser = new CSVParser($sUTF8Data, $sSep, $sQualifier, MetaModel::GetConfig()->Get('max_execution_time_per_loop')); + $oCSVParser = new CSVParser($sUTF8Data, $sSep, $sQualifier, MetaModel::GetConfig()->Get('max_execution_time_per_loop')); $aInputColumns = $oCSVParser->ListFields(); $iColCount = count($aInputColumns); @@ -449,7 +447,7 @@ try $aColumns = $oDataSource->GetSQLColumns(); $aDateColumns = array(); - foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($oAttDef instanceof AttributeDate) { @@ -463,7 +461,7 @@ try $aIsDateToTransform = array(); $aDateToTransformReport = array(); - foreach ($aInputColumns as $iFieldId => $sInputColumn) + foreach($aInputColumns as $iFieldId => $sInputColumn) { if (array_key_exists($sInputColumn, $aDateColumns)) { @@ -475,7 +473,7 @@ try $aIsDateToTransform[$iFieldId] = false; } - if ($sInputColumn === 'primary_key') + if ($sInputColumn == 'primary_key') { $iPrimaryKeyCol = $iFieldId; continue; @@ -496,51 +494,51 @@ try // try { - if ($sOutput === 'details') + if ($sOutput == 'details') { $oP->add_comment('------------------------------------------------------------'); $oP->add_comment(' Import phase'); $oP->add_comment('------------------------------------------------------------'); } - + if ($bSimulate) { CMDBSource::Query('START TRANSACTION'); } $aData = $oCSVParser->ToArray(); $iLineCount = count($aData); - + $sTable = $oDataSource->GetDataTable(); - - // Prepare insert columns + + // Prepare insert columns $sInsertColumns = '`'.implode('`, `', $aInputColumns).'`'; - + $iPreviousTimeLimit = ini_get('max_execution_time'); $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); $oMutex = new iTopMutex('synchro_import_'.$oDataSource->GetKey()); $oMutex->Lock(); - set_time_limit($iLoopTimeLimit); - foreach ($aData as $iRow => $aRow) - { - $sReconciliationCondition = '`primary_key` = '.CMDBSource::Quote($aRow[$iPrimaryKeyCol]); - $sSelect = "SELECT COUNT(*) FROM `$sTable` WHERE $sReconciliationCondition"; + foreach($aData as $iRow => $aRow) + { + set_time_limit($iLoopTimeLimit); + $sReconciliationCondition = "`primary_key` = ".CMDBSource::Quote($aRow[$iPrimaryKeyCol]); + $sSelect = "SELECT COUNT(*) FROM `$sTable` WHERE $sReconciliationCondition"; $aRes = CMDBSource::QueryToArray($sSelect); $iCount = $aRes[0]['COUNT(*)']; - - if ($iCount === 0) + + if ($iCount == 0) { // No record... create it // $iCountCreations++; - if ($sOutput === 'details') + if ($sOutput == 'details') { $oP->add("$iRow: New entry, reconciliation: '$sReconciliationCondition'\n"); } - + $aValues = array(); // Used to build the insert query foreach ($aRow as $iCol => $value) { - if ($value === null) + if (is_null($value)) { $aValues[] = 'NULL'; } @@ -548,7 +546,7 @@ try { $bDateOnly = false; $sFormat = $sDateTimeFormat; - if ($aIsDateToTransform[$iCol] === 'DATE') + if ($aIsDateToTransform[$iCol] == 'DATE') { $bDateOnly = true; $sFormat = $sDateFormat; @@ -557,7 +555,7 @@ try if ($sDate === false) { $aValues[] = CMDBSource::Quote(''); - if ($sOutput === 'details') + if ($sOutput == 'details') { $oP->add("$iRow: Wrong format for {$aIsDateToTransform[$iCol]} column $iCol: '$value' does not match the expected format: '$sFormat' (column skipped)\n"); } @@ -578,9 +576,9 @@ try { CMDBSource::Query($sInsert); } - catch (Exception $e) + catch(Exception $e) { - if ($sNoStopOnImportError === '1') + if ($sNoStopOnImportError == '1') { $iCountErrors++; $oP->add("$iRow: Import error '".$e->getMessage()."' (continuing)...\n"); @@ -591,30 +589,28 @@ try } } } - elseif ($iCount === 1) + elseif ($iCount == 1) { // Found a match... update it // $iCountUpdates++; - if ($sOutput === 'details') + if ($sOutput == 'details') { $oP->add("$iRow: Update entry, reconciliation: '$sReconciliationCondition'\n"); } + $aValuePairs = array(); // Used to build the update query foreach ($aRow as $iCol => $value) { // Skip reconciliation column - if ($iCol === $iPrimaryKeyCol) - { - continue; - } - + if ($iCol == $iPrimaryKeyCol) continue; + $sCol = $aInputColumns[$iCol]; if ($aIsDateToTransform[$iCol] !== false) { $bDateOnly = false; $sFormat = $sDateTimeFormat; - if ($aIsDateToTransform[$iCol] === 'DATE') + if ($aIsDateToTransform[$iCol] == 'DATE') { $bDateOnly = true; $sFormat = $sDateFormat; @@ -622,7 +618,7 @@ try $sDate = ChangeDateFormat($value, $sFormat, $bDateOnly); if ($sDate === false) { - if ($sOutput === 'details') + if ($sOutput == 'details') { $oP->add("$iRow: Wrong format for {$aIsDateToTransform[$iCol]} column $iCol: '$value' does not match the expected format: '$sFormat' (column skipped)\n"); } @@ -643,9 +639,9 @@ try { CMDBSource::Query($sUpdateQuery); } - catch (Exception $e) + catch(Exception $e) { - if ($sNoStopOnImportError === '1') + if ($sNoStopOnImportError == '1') { $iCountErrors++; $oP->add("$iRow: Import error '".$e->getMessage()."' (continuing)...\n"); @@ -666,29 +662,29 @@ try } $oMutex->Unlock(); set_time_limit($iPreviousTimeLimit); - - if (($sOutput === 'summary') || ($sOutput === 'details')) + + if (($sOutput == "summary") || ($sOutput == 'details')) { $oP->add_comment('------------------------------------------------------------'); $oP->add_comment(' Import phase summary'); $oP->add_comment('------------------------------------------------------------'); - $oP->add_comment('Data Source: '.$iDataSourceId); - $oP->add_comment('Synchronize: '.($bSynchronize ? '1' : '0')); - $oP->add_comment('Class: '.$sClass); - $oP->add_comment('Separator: '.$sSep); - $oP->add_comment('Qualifier: '.$sQualifier); - $oP->add_comment('Charset Encoding:'.$sCharSet); + $oP->add_comment("Data Source: ".$iDataSourceId); + $oP->add_comment("Synchronize: ".($bSynchronize ? '1' : '0')); + $oP->add_comment("Class: ".$sClass); + $oP->add_comment("Separator: ".$sSep); + $oP->add_comment("Qualifier: ".$sQualifier); + $oP->add_comment("Charset Encoding:".$sCharSet); if (strlen($sDateTimeFormat) > 0) { $aDateTimeColumns = array(); $aDateColumns = array(); - foreach ($aIsDateToTransform as $iCol => $sType) + foreach($aIsDateToTransform as $iCol => $sType) { if ($sType !== false) { $sCol = $aInputColumns[$iCol]; - if ($sType === 'DATE') + if ($sType == 'DATE') { $aDateColumns[] = $sCol; } @@ -698,8 +694,7 @@ try } } } - $sFormatedDateTimeColumns = (count($aDateTimeColumns) > 0) ? ', applied to columns ['.implode(', ', - $aDateTimeColumns).']' : ''; + $sFormatedDateTimeColumns = (count($aDateTimeColumns) > 0) ? ', applied to columns ['.implode(', ', $aDateTimeColumns).']' : ''; $sFormatedDateColumns = (count($aDateColumns) > 0) ? ', applied to columns ['.implode(', ', $aDateColumns).']' : ''; $oP->add_comment("Date and time format: '$sDateTimeFormat' $sFormatedDateTimeColumns"); $oP->add_comment("Date only format: '$sDateFormat' $sFormatedDateColumns"); @@ -707,19 +702,19 @@ try else { // shall never get there - $oP->add_comment('Date format: '); + $oP->add_comment("Date format: "); } - $oP->add_comment('Data Size: '.strlen($sCSVData)); - $oP->add_comment('Data Lines: '.$iLineCount); - $oP->add_comment('Columns: '.implode(', ', $aInputColumns)); - $oP->add_comment('Output format: '.$sOutput); - // $oP->add_comment("Report level: ".$sReportLevel); - $oP->add_comment('Simulate: '.($bSimulate ? '1' : '0')); - $oP->add_comment('Change tracking comment: '.$sComment); - $oP->add_comment('Issues (before synchro): '.$iCountErrors); - // $oP->add_comment("Warnings: ".$iCountWarnings); - $oP->add_comment('Created (before synchro): '.$iCountCreations); - $oP->add_comment('Updated (before synchro): '.$iCountUpdates); + $oP->add_comment("Data Size: ".strlen($sCSVData)); + $oP->add_comment("Data Lines: ".$iLineCount); + $oP->add_comment("Columns: ".implode(', ', $aInputColumns)); + $oP->add_comment("Output format: ".$sOutput); + // $oP->add_comment("Report level: ".$sReportLevel); + $oP->add_comment("Simulate: ".($bSimulate ? '1' : '0')); + $oP->add_comment("Change tracking comment: ".$sComment); + $oP->add_comment("Issues (before synchro): ".$iCountErrors); + // $oP->add_comment("Warnings: ".$iCountWarnings); + $oP->add_comment("Created (before synchro): ".$iCountCreations); + $oP->add_comment("Updated (before synchro): ".$iCountUpdates); } ////////////////////////////////////////////////// @@ -730,7 +725,7 @@ try { $oSynchroExec = new SynchroExecution($oDataSource, $oLoadStartDate); $oStatLog = $oSynchroExec->Process(); - if ($sOutput === 'details') + if ($sOutput == 'details') { $oP->add_comment('------------------------------------------------------------'); $oP->add_comment(' Synchronization phase'); @@ -741,35 +736,35 @@ try $iCount++; $oP->add_comment($sMessage); } - if ($iCount === 0) + if ($iCount == 0) { $oP->add_comment(' No traces. (To activate the traces set "synchro_trace" => true in the configuration file)'); } } - if ($oStatLog->Get('status') === 'error') + if ($oStatLog->Get('status') == 'error') { - $oP->p('ERROR: '.$oStatLog->Get('last_error')); + $oP->p("ERROR: ".$oStatLog->Get('last_error')); } $oP->add_comment('------------------------------------------------------------'); $oP->add_comment(' Synchronization phase summary'); $oP->add_comment('------------------------------------------------------------'); - $oP->add_comment('Replicas: '.$oStatLog->Get('stats_nb_replica_total')); - $oP->add_comment('Replicas touched since last synchro: '.$oStatLog->Get('stats_nb_replica_seen')); - $oP->add_comment('Objects deleted: '.$oStatLog->Get('stats_nb_obj_deleted')); - $oP->add_comment('Objects deletion errors: '.$oStatLog->Get('stats_nb_obj_deleted_errors')); - $oP->add_comment('Objects obsoleted: '.$oStatLog->Get('stats_nb_obj_obsoleted')); - $oP->add_comment('Objects obsolescence errors: '.$oStatLog->Get('stats_nb_obj_obsoleted_errors')); - $oP->add_comment('Objects created: '.$oStatLog->Get('stats_nb_obj_created').' ('.$oStatLog->Get('stats_nb_obj_created_warnings').' warnings)'); - $oP->add_comment('Objects creation errors: '.$oStatLog->Get('stats_nb_obj_created_errors')); - $oP->add_comment('Objects updated: '.$oStatLog->Get('stats_nb_obj_updated').' ('.$oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)"); - $oP->add_comment('Objects update errors: '.$oStatLog->Get('stats_nb_obj_updated_errors')); - $oP->add_comment('Objects reconciled (updated): '.$oStatLog->Get('stats_nb_obj_new_updated').' ('.$oStatLog->Get('stats_nb_obj_new_updated_warnings').' warnings)'); - $oP->add_comment('Objects reconciled (unchanged): '.$oStatLog->Get('stats_nb_obj_new_unchanged').' ('.$oStatLog->Get('stats_nb_obj_new_updated_warnings').' warnings)'); - $oP->add_comment('Objects reconciliation errors: '.$oStatLog->Get('stats_nb_replica_reconciled_errors')); - $oP->add_comment('Replica disappeared, no action taken: '.$oStatLog->Get('stats_nb_replica_disappeared_no_action')); + $oP->add_comment("Replicas: ".$oStatLog->Get('stats_nb_replica_total')); + $oP->add_comment("Replicas touched since last synchro: ".$oStatLog->Get('stats_nb_replica_seen')); + $oP->add_comment("Objects deleted: ".$oStatLog->Get('stats_nb_obj_deleted')); + $oP->add_comment("Objects deletion errors: ".$oStatLog->Get('stats_nb_obj_deleted_errors')); + $oP->add_comment("Objects obsoleted: ".$oStatLog->Get('stats_nb_obj_obsoleted')); + $oP->add_comment("Objects obsolescence errors: ".$oStatLog->Get('stats_nb_obj_obsoleted_errors')); + $oP->add_comment("Objects created: ".$oStatLog->Get('stats_nb_obj_created')." (".$oStatLog->Get('stats_nb_obj_created_warnings')." warnings)"); + $oP->add_comment("Objects creation errors: ".$oStatLog->Get('stats_nb_obj_created_errors')); + $oP->add_comment("Objects updated: ".$oStatLog->Get('stats_nb_obj_updated')." (".$oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)"); + $oP->add_comment("Objects update errors: ".$oStatLog->Get('stats_nb_obj_updated_errors')); + $oP->add_comment("Objects reconciled (updated): ".$oStatLog->Get('stats_nb_obj_new_updated')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)"); + $oP->add_comment("Objects reconciled (unchanged): ".$oStatLog->Get('stats_nb_obj_new_unchanged')." (".$oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)"); + $oP->add_comment("Objects reconciliation errors: ".$oStatLog->Get('stats_nb_replica_reconciled_errors')); + $oP->add_comment("Replica disappeared, no action taken: ".$oStatLog->Get('stats_nb_replica_disappeared_no_action')); } } - catch (Exception $e) + catch(Exception $e) { if ($bSimulate) { @@ -786,18 +781,23 @@ try // // Summary of settings and results // - if ($sOutput === 'retcode') + if ($sOutput == 'retcode') { $oP->add($iCountErrors); } } -catch (ExchangeException $e) +catch(ExchangeException $e) { - $oP->add_comment($e->getMessage()); + $oP->add_comment($e->getMessage()); } -catch (Exception $e) +catch(SecurityException $e) { - $oP->add_comment((string)$e); + $oP->add_comment($e->getMessage()); +} +catch(Exception $e) +{ + $oP->add_comment((string)$e); } $oP->output(); +?> diff --git a/synchro/synchrodatasource.class.inc.php b/synchro/synchrodatasource.class.inc.php index 324bec7ff..3ce1d008a 100644 --- a/synchro/synchrodatasource.class.inc.php +++ b/synchro/synchrodatasource.class.inc.php @@ -23,231 +23,79 @@ * @copyright Copyright (C) 2010-2018 Combodo SARL * @license http://opensource.org/licenses/AGPL-3.0 */ + + class SynchroExceptionNotStarted extends CoreException { } class SynchroDataSource extends cmdbAbstractObject -{ +{ public static function Init() { $aParams = array ( - 'category' => 'core/cmdb,view_in_gui,grant_by_profile', - 'key_type' => 'autoincrement', - 'name_attcode' => array('name'), - 'state_attcode' => '', - 'reconc_keys' => array(), - 'db_table' => 'priv_sync_datasource', - 'db_key_field' => 'id', - 'db_finalclass_field' => 'realclass', - 'display_template' => '', - 'icon' => '../images/synchro.png', + "category" => "core/cmdb,view_in_gui,grant_by_profile", + "key_type" => "autoincrement", + "name_attcode" => array('name'), + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_sync_datasource", + "db_key_field" => "id", + "db_finalclass_field" => "realclass", + "display_template" => "", + "icon" => "../images/synchro.png", ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeString('name', array( - 'allowed_values' => null, - 'sql' => 'name', - 'default_value' => null, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeText('description', array( - 'allowed_values' => null, - 'sql' => 'description', - 'default_value' => null, - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeEnum('status', array( - 'allowed_values' => new ValueSetEnum('implementation,production,obsolete'), - 'sql' => 'status', - 'default_value' => 'implementation', - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - 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,application', - '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_]*$', - ))); - + MetaModel::Init_AddAttribute(new AttributeString("name", array("allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("description", array("allowed_values" => null, "sql" => "description", "default_value" => null, "is_null_allowed" => true, "depends_on" => array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('implementation,production,obsolete'), "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); + 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,application", "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(), - ))); - + MetaModel::Init_AddAttribute(new AttributeString("scope_restriction", array("allowed_values"=>null, "sql"=>"scope_restriction", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + //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: seconds (int) - MetaModel::Init_AddAttribute(new AttributeDuration('full_load_periodicity', array( - 'allowed_values' => null, - 'sql' => 'full_load_periodicity', - 'default_value' => 0, - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - + MetaModel::Init_AddAttribute(new AttributeDuration("full_load_periodicity", array("allowed_values"=>null, "sql"=>"full_load_periodicity", "default_value"=>0, "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(), - ))); - MetaModel::Init_AddAttribute(new AttributeEnum('action_on_zero', array( - 'allowed_values' => new ValueSetEnum('create,error'), - 'sql' => 'action_on_zero', - 'default_value' => 'create', - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeEnum('action_on_one', array( - 'allowed_values' => new ValueSetEnum('update,error'), - 'sql' => 'action_on_one', - 'default_value' => 'update', - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeEnum('action_on_multiple', array( - 'allowed_values' => new ValueSetEnum('take_first,create,error'), - 'sql' => 'action_on_multiple', - 'default_value' => 'error', - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - - 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(), - ))); + 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()))); + MetaModel::Init_AddAttribute(new AttributeEnum("action_on_zero", array("allowed_values"=>new ValueSetEnum('create,error'), "sql"=>"action_on_zero", "default_value"=>"create", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("action_on_one", array("allowed_values"=>new ValueSetEnum('update,error'), "sql"=>"action_on_one", "default_value"=>"update", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("action_on_multiple", array("allowed_values"=>new ValueSetEnum('take_first,create,error'), "sql"=>"action_on_multiple", "default_value"=>"error", "is_null_allowed"=>false, "depends_on"=>array()))); + + 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: 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 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(), - 'tracking_level' => LINKSET_TRACKING_DETAILS, - ))); + 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(), 'tracking_level' => LINKSET_TRACKING_DETAILS))); // Not used yet ! - MetaModel::Init_AddAttribute(new AttributeEnum('user_delete_policy', array( - 'allowed_values' => new ValueSetEnum('everybody,administrators,nobody'), - 'sql' => 'user_delete_policy', - 'default_value' => 'nobody', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); + MetaModel::Init_AddAttribute(new AttributeEnum("user_delete_policy", array("allowed_values"=>new ValueSetEnum('everybody,administrators,nobody'), "sql"=>"user_delete_policy", "default_value"=>"nobody", "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(), - ))); + 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(), - ))); + 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( - 'col:0' => array( - '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', - ), - ), + 'col:0'=> array( + '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')) ) - ); - MetaModel::Init_SetZListItems('list', - array('scope_class', 'status', 'user_id', 'full_load_periodicity')); // Attributes to be displayed for a list + ); + 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 + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'scope_class', 'user_id')); // Criteria of the std search form MetaModel::Init_SetZListItems('default_search', array('name', 'status', 'scope_class')); // Criteria of the defaut search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } @@ -258,10 +106,9 @@ class SynchroDataSource extends cmdbAbstractObject { $this->Set('database_table_name', $this->GetDataTable()); } - return parent::DisplayBareProperties($oPage, $bEditMode, $sPrefix, $aExtraParams); } - + public function DisplayBareRelations(WebPage $oPage, $bEditMode = false) { if (!$this->IsNew()) @@ -270,19 +117,19 @@ class SynchroDataSource extends cmdbAbstractObject $oAttributeSet = $this->Get('attribute_list'); $aAttributes = array(); - while ($oAttribute = $oAttributeSet->Fetch()) + while($oAttribute = $oAttributeSet->Fetch()) { $aAttributes[$oAttribute->Get('attcode')] = $oAttribute; } // Columns of the form $aAttribs = array(); - foreach (array('attcode', 'reconciliation', 'update', 'update_policy', 'reconciliation_attcode') as $s) + foreach(array('attcode', 'reconciliation', 'update', 'update_policy', 'reconciliation_attcode') as $s ) { - $aAttribs[$s] = array('label' => Dict::S("Core:SynchroAtt:$s"), 'description' => Dict::S("Core:SynchroAtt:$s+")); + $aAttribs[$s] = array( 'label' => Dict::S("Core:SynchroAtt:$s"), "description" => Dict::S("Core:SynchroAtt:$s+")); } // Rows of the form $aValues = array(); - foreach (MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode => $oAttDef) + foreach(MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode=>$oAttDef) { if ($oAttDef->IsWritable()) { @@ -297,7 +144,7 @@ class SynchroDataSource extends cmdbAbstractObject $oAttribute = new SynchroAttExtKey(); $oAttribute->Set('reconciliation_attcode', ''); // Blank means by pkey } - elseif ($oAttDef::IsLinkSet() && $oAttDef->IsIndirect()) + elseif ($oAttDef->IsLinkSet() && $oAttDef->IsIndirect()) { $oAttribute = new SynchroAttLinkSet(); // Todo - add these settings into the form @@ -306,7 +153,7 @@ class SynchroDataSource extends cmdbAbstractObject $oAttribute->Set('value_separator', MetaModel::GetConfig()->Get('link_set_value_separator')); $oAttribute->Set('attribute_qualifier', MetaModel::GetConfig()->Get('link_set_attribute_qualifier')); } - elseif ($oAttDef::IsScalar()) + elseif ($oAttDef->IsScalar()) { $oAttribute = new SynchroAttribute(); } @@ -315,7 +162,7 @@ class SynchroDataSource extends cmdbAbstractObject $oAttribute = null; } - if ($oAttribute !== null) + if (!is_null($oAttribute)) { $oAttribute->Set('sync_source_id', $this->GetKey()); $oAttribute->Set('attcode', $sAttCode); @@ -324,28 +171,26 @@ class SynchroDataSource extends cmdbAbstractObject $oAttribute->Set('update_policy', 'master_locked'); } } - if ($oAttribute !== null) + if (!is_null($oAttribute)) { if (!$bEditMode) { // Read-only mode - $aRow['reconciliation'] = $oAttribute->Get('reconcile') == 1 ? Dict::S('Core:SynchroReconcile:Yes') : Dict::S('Core:SynchroReconcile:No'); - $aRow['update'] = $oAttribute->Get('update') == 1 ? Dict::S('Core:SynchroUpdate:Yes') : Dict::S('Core:SynchroUpdate:No'); - $aRow['attcode'] = MetaModel::GetLabel($this->GetTargetClass(), - $oAttribute->Get('attcode')).' ('.$oAttribute->Get('attcode').')'; + $aRow['reconciliation'] = $oAttribute->Get('reconcile') == 1 ? Dict::S('Core:SynchroReconcile:Yes') : Dict::S('Core:SynchroReconcile:No'); + $aRow['update'] = $oAttribute->Get('update') == 1 ? Dict::S('Core:SynchroUpdate:Yes') : Dict::S('Core:SynchroUpdate:No'); + $aRow['attcode'] = MetaModel::GetLabel($this->GetTargetClass(), $oAttribute->Get('attcode')).' ('.$oAttribute->Get('attcode').')'; $aRow['update_policy'] = $oAttribute->GetAsHTML('update_policy'); if ($oAttDef->IsExternalKey()) { $sReconciliationAttCode = $oAttribute->Get('reconciliation_attcode'); - switch ($sReconciliationAttCode) + switch($sReconciliationAttCode) { case '': - $sDisplayReconciliationAttCode = Dict::S('Core:SynchroAttExtKey:ReconciliationById'); - break; - + $sDisplayReconciliationAttCode = Dict::S('Core:SynchroAttExtKey:ReconciliationById'); + break; + default: - $sDisplayReconciliationAttCode = MetaModel::GetLabel($oAttDef->GetTargetClass(), - $sReconciliationAttCode); + $sDisplayReconciliationAttCode = MetaModel::GetLabel($oAttDef->GetTargetClass(), $sReconciliationAttCode); } $aRow['reconciliation_attcode'] = $sDisplayReconciliationAttCode; } @@ -359,19 +204,15 @@ class SynchroDataSource extends cmdbAbstractObject // Edit mode $sAttCode = $oAttribute->Get('attcode'); $sChecked = $oAttribute->Get('reconcile') == 1 ? 'checked' : ''; - $aRow['reconciliation'] = ""; + $aRow['reconciliation'] = ""; $sChecked = $oAttribute->Get('update') == 1 ? 'checked' : ''; - $aRow['update'] = ""; - $aRow['attcode'] = MetaModel::GetLabel($this->GetTargetClass(), - $oAttribute->Get('attcode')).' ('.$oAttribute->Get('attcode').')'; - $oUpdateAttDef = MetaModel::GetAttributeDef(get_class($oAttribute), 'update_policy'); - $aRow['update_policy'] = cmdbAbstractObject::GetFormElementForField($oPage, get_class($oAttribute), - 'update_policy', $oUpdateAttDef, $oAttribute->Get('update_policy'), '', 'update_policy_'.$sAttCode, - "[$sAttCode]"); + $aRow['update'] = ""; + $aRow['attcode'] = MetaModel::GetLabel($this->GetTargetClass(), $oAttribute->Get('attcode')).' ('.$oAttribute->Get('attcode').')'; + $oUpdateAttDef = MetaModel::GetAttributeDef(get_class($oAttribute), 'update_policy'); + $aRow['update_policy'] = cmdbAbstractObject::GetFormElementForField($oPage, get_class($oAttribute), 'update_policy', $oUpdateAttDef, $oAttribute->Get('update_policy'), '', 'update_policy_'.$sAttCode, "[$sAttCode]"); if ($oAttDef->IsExternalKey()) { - $aRow['reconciliation_attcode'] = $oAttribute->GetReconciliationFormElement($oAttDef->GetTargetClass(), - "attr_reconciliation_attcode[$sAttCode]"); + $aRow['reconciliation_attcode'] = $oAttribute->GetReconciliationFormElement($oAttDef->GetTargetClass(), "attr_reconciliation_attcode[$sAttCode]"); } else { @@ -388,29 +229,20 @@ class SynchroDataSource extends cmdbAbstractObject } parent::DisplayBareRelations($oPage, $bEditMode); } - + /** * Displays the status (SynchroLog) of the datasource in a graphical manner - * * @param $oPage WebPage - * * @return void - * @throws \CoreException - * @throws \CoreUnexpectedValue - * @throws \MissingQueryArgument - * @throws \MySQLException - * @throws \MySQLHasGoneAwayException - * @throws \OQLException */ protected function DisplayStatusTab(WebPage $oPage) { $oPage->SetCurrentTab(Dict::S('Core:SynchroStatus')); - + $sSelectSynchroLog = 'SELECT SynchroLog WHERE sync_source_id = :source_id'; - $oSetSynchroLog = new CMDBObjectSet(DBObjectSearch::FromOQL($sSelectSynchroLog), array('start_date' => false) /* order by*/, - array('source_id' => $this->GetKey())); + $oSetSynchroLog = new CMDBObjectSet(DBObjectSearch::FromOQL($sSelectSynchroLog), array('start_date' => false) /* order by*/, array('source_id' => $this->GetKey())); $oSetSynchroLog->SetLimit(100); // Display only the 100 latest runs - + if ($oSetSynchroLog->Count() > 0) { $oLastLog = $oSetSynchroLog->Fetch(); @@ -418,7 +250,7 @@ class SynchroDataSource extends cmdbAbstractObject $oLastLog->Get('stats_nb_replica_seen'); $iLastLog = 0; $iDSid = $this->GetKey(); - if ($oLastLog->Get('status') === 'running') + if ($oLastLog->Get('status') == 'running') { // Still running ! $oPage->p('

    '.Dict::Format('Core:Synchro:SynchroRunningStartedOn_Date', $sStartDate).'

    '); @@ -440,8 +272,7 @@ class SynchroDataSource extends cmdbAbstractObject $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL)); $iCountAllWarnings = $oSet->Count(); $sAllWarnings = "$iCountAllWarnings"; - $oPage->p('

    '.Dict::Format('Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings', $sAllReplicas, $sAllErrors, - $sAllWarnings).'

    '); + $oPage->p('

    '.Dict::Format('Core:Synchro:ListReplicas_AllReplicas_Errors_Warnings', $sAllReplicas, $sAllErrors, $sAllWarnings).'

    '); } $oPage->add(' EOF - ); - $sBaseOQL = 'SELECT SynchroReplica WHERE sync_source_id='.$this->GetKey()." AND status_last_error!=''"; +); + $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')); + $oPage->add($this->HtmlBox('repl_disappeared', $aData, '#630', 'rowspan="4"').''.$this->HtmlBox('obj_disappeared_no_action', $aData, '#333')); $oPage->add("\n"); $oPage->add($this->HtmlBox('obj_deleted', $aData, '#000')); $oPage->add("\n"); $oPage->add($this->HtmlBox('obj_obsoleted', $aData, '#630')); $oPage->add("\n"); $sOQL = urlencode($sBaseOQL." AND status='obsolete'"); - $oPage->add($this->HtmlBox('obj_disappeared_errors', $aData, '#C00', '', - " Show")); + $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($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"); $sOQL = urlencode($sBaseOQL." AND status='modified'"); - $oPage->add($this->HtmlBox('obj_updated_errors', $aData, '#C00', '', - " Show")); + $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($this->HtmlBox('repl_new', $aData, '#339', 'rowspan="4"').''.$this->HtmlBox('obj_new_unchanged', $aData, '#393')); $oPage->add("\n"); $oPage->add($this->HtmlBox('obj_new_updated', $aData, '#3C3')); $oPage->add("\n"); $oPage->add($this->HtmlBox('obj_created', $aData, '#339')); $oPage->add("\n"); $sOQL = urlencode($sBaseOQL." AND status='new'"); - $oPage->add($this->HtmlBox('obj_new_errors', $aData, '#C00', '', - " Show")); + $oPage->add($this->HtmlBox('obj_new_errors', $aData, '#C00', '', " Show")); $oPage->add("\n
    '); @@ -452,7 +283,7 @@ class SynchroDataSource extends cmdbAbstractObject $oPage->add(''); - + $oPage->add(''); - + // Now build the big "synoptics" view $aData = $this->ProcessLog($oLastLog); - $sNbReplica = $this->GetIcon().' '.Dict::Format('Core:Synchro:Nb_Replica', - "{$aData['nb_replica_total']}"); - $sNbObjects = MetaModel::GetClassIcon($this->GetTargetClass()).' '.Dict::Format('Core:Synchro:Nb_Class:Objects', - $this->GetTargetClass(), "{$aData['nb_obj_total']}"); + $sNbReplica = $this->GetIcon()." ".Dict::Format('Core:Synchro:Nb_Replica', "{$aData['nb_replica_total']}"); + $sNbObjects = MetaModel::GetClassIcon($this->GetTargetClass())." ".Dict::Format('Core:Synchro:Nb_Class:Objects', $this->GetTargetClass(), "{$aData['nb_obj_total']}"); $oPage->add( - <<
    $sNbReplica $sNbObjects
     
    =>=>
    =>=>
    =>=>
    \n"); $oPage->add(''); $oPage->add('
    '); @@ -587,21 +411,21 @@ EOF $oPage->p('

    '.Dict::S('Core:Synchro:NeverRun').'

    '); } } - + 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;' : ''; + $sOpacity = ($iCount==0) ? "opacity:0.3;" : ""; if (isset($aData[$sId.'_warnings'])) { - $sLabel .= " (".$aData[$sId.'_warnings'].')'; + $sLabel .= " (".$aData[$sId.'_warnings'].")"; } return "{$sLabel}{$sErrorLink}"; } - + protected function ProcessLog($oLastLog) { $aData = array( @@ -634,31 +458,25 @@ EOF $aData['repl_ignored'] = $iIgnored; $aData['nb_obj_total'] = $iNew + $iExisting + $iDisappeared; $aData['nb_replica_total'] = $aData['nb_obj_total'] + $iIgnored; - if (strlen($oLastLog->Get('traces')) > 0) + if(strlen($oLastLog->Get('traces')) > 0) { - $aData['traces'] = '
    Debug traces
    '.htmlentities($oLastLog->Get('traces'), ENT_QUOTES,
    -					'UTF-8').'
    '; + $aData['traces'] = '
    Debug traces
    '.htmlentities($oLastLog->Get('traces'), ENT_QUOTES, 'UTF-8').'
    '; } else { $aData['traces'] = ''; } - return $aData; } - + public function GetIcon($bImgTag = true, $sMoreStyles = '') { - if ($this->Get('url_icon') == '') - { - return MetaModel::GetClassIcon(get_class($this), $bImgTag); - } + if ($this->Get('url_icon') == '') return MetaModel::GetClassIcon(get_class($this), $bImgTag); if ($bImgTag) { - return '"; - + return "Get('url_icon')."\" style=\"vertical-align:middle;$sMoreStyles\"/>"; + } - return $this->Get('url_icon'); } @@ -672,13 +490,10 @@ EOF */ public function GetApplicationUrl(DBObject $oDestObj, SynchroReplica $oReplica) { - if ($this->Get('url_application') == '') - { - return ''; - } + if ($this->Get('url_application') == '') return ''; $aSearches = array(); $aReplacements = array(); - foreach (MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode => $oAttDef) + foreach(MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode=>$oAttDef) { if ($oAttDef->IsScalar()) { @@ -688,32 +503,30 @@ EOF } $aData = $oReplica->LoadExtendedDataFromTable($this->GetDataTable()); - foreach ($aData as $sColumn => $value) + foreach($aData as $sColumn => $value) { $aSearches[] = '$replica->'.$sColumn.'$'; $aReplacements[] = $value; } - return str_replace($aSearches, $aReplacements, $this->Get('url_application')); } - + public function GetAttributeFlags($sAttCode, &$aReasons = array(), $sTargetState = '') { - if ((($sAttCode == 'scope_class') || ($sAttCode == 'database_table_name')) && (!$this->IsNew())) + if ( (($sAttCode == 'scope_class') || ($sAttCode == 'database_table_name')) && (!$this->IsNew())) { return OPT_ATT_READONLY; } - return parent::GetAttributeFlags($sAttCode, $aReasons, $sTargetState); } - + public function UpdateObjectFromPostedForm($sFormPrefix = '', $sAttList = null, $aAttFlags = array()) { parent::UpdateObjectFromPostedForm($sFormPrefix, $sAttList, $aAttFlags); // And now read the other post parameters... $oAttributeSet = $this->Get('attribute_list'); $aAttributes = array(); - while ($oAttribute = $oAttributeSet->Fetch()) + while($oAttribute = $oAttributeSet->Fetch()) { $aAttributes[$oAttribute->Get('attcode')] = $oAttribute; } @@ -723,9 +536,9 @@ EOF $aReconciliation = utils::ReadPostedParam('attr_reconciliation_attcode', array()); // update_policy cannot be empty, so there is one entry per attribute, use this to iterate // through all the writable attributes - foreach ($aUpdatePolicy as $sAttCode => $sValue) + foreach($aUpdatePolicy as $sAttCode => $sValue) { - if (!isset($aAttributes[$sAttCode])) + if(!isset($aAttributes[$sAttCode])) { $oAttribute = $this->CreateSynchroAtt($sAttCode); } @@ -738,7 +551,7 @@ EOF { $bReconcile = $aReconcile[$sAttCode] == 'on' ? 1 : 0; } - $bUpdate = 0; // Default / initial value + $bUpdate = 0 ; // Default / initial value if (isset($aUpdate[$sAttCode])) { $bUpdate = $aUpdate[$sAttCode] == 'on' ? 1 : 0; @@ -791,13 +604,11 @@ EOF $oAttribute->Set('reconcile', 0); $oAttribute->Set('update', 0); $oAttribute->Set('update_policy', 'master_locked'); - return $oAttribute; } - /** * Overload the standard behavior - */ + */ public function ComputeValues() { parent::ComputeValues(); @@ -810,14 +621,14 @@ EOF { $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 $oAttributeSet = $this->Get('attribute_list'); if ($oAttributeSet->Count() == 0) { - foreach (MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode => $oAttDef) + foreach(MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode=>$oAttDef) { if ($oAttDef->IsWritable()) { @@ -867,7 +678,6 @@ EOF } } } - public function DoCheckToWrite() { parent::DoCheckToWrite(); @@ -877,7 +687,7 @@ EOF { $oSet = $this->Get('attribute_list'); $bReconciliationKey = false; - foreach ($oSet as $oSynchroAttribute) + foreach($oSet as $oSynchroAttribute) { if ($oSynchroAttribute->Get('reconcile') == 1) { @@ -887,23 +697,23 @@ EOF } if (!$bReconciliationKey) { - $this->m_aCheckIssues[] = Dict::Format('Class:SynchroDataSource/Error:AtLeastOneReconciliationKeyMustBeSpecified'); + $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'); + $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') == '')) + 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'); + $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()) { @@ -911,11 +721,11 @@ EOF 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()); + $this->m_aCheckIssues[] = Dict::Format('Class:SynchroDataSource/Error:DataTableAlreadyExists', $this->GetDataTable()); } } } - + public function GetTargetClass() { return $this->Get('scope_class'); @@ -928,10 +738,9 @@ EOF { $sTable = $this->ComputeDataTableName(); } - return $sTable; } - + protected function ComputeDataTableName() { $sDBTableName = $this->Get('database_table_name'); @@ -945,13 +754,12 @@ EOF { $sDBTableName = preg_replace('/[^A-za-z0-9_]/', '_', $sDBTableName); // Remove forbidden characters from the table name } - $sPrefix = MetaModel::GetConfig()->Get('db_subname').'synchro_data_'; + $sPrefix = MetaModel::GetConfig()->Get('db_subname')."synchro_data_"; if (strpos($sDBTableName, $sPrefix) !== 0) { $sDBTableName = $sPrefix.$sDBTableName; } - - return $sDBTableName; + return $sDBTableName; } /** @@ -966,43 +774,42 @@ EOF $sTable = $this->GetDataTable(); $aColumns = $this->GetSQLColumns(); - + $aFieldDefs = array(); // Allow '0', otherwise mysql will render an error when the id is not given // (the trigger is expected to set the value, but it is not executed soon enough) - $aFieldDefs[] = 'id INTEGER(11) NOT NULL DEFAULT 0 '; - $aFieldDefs[] = '`primary_key` VARCHAR(255) NULL DEFAULT NULL'; - foreach ($aColumns as $sColumn => $ColSpec) + $aFieldDefs[] = "id INTEGER(11) NOT NULL DEFAULT 0 "; + $aFieldDefs[] = "`primary_key` VARCHAR(255) NULL DEFAULT NULL"; + foreach($aColumns as $sColumn => $ColSpec) { $aFieldDefs[] = "`$sColumn` $ColSpec NULL DEFAULT NULL"; } - $aFieldDefs[] = 'INDEX (id)'; - $aFieldDefs[] = 'INDEX (primary_key)'; + $aFieldDefs[] = "INDEX (id)"; + $aFieldDefs[] = "INDEX (primary_key)"; $sFieldDefs = implode(', ', $aFieldDefs); $sDbCharset = DEFAULT_CHARACTER_SET; $sDbCollation = DEFAULT_COLLATION; - $sCreateTable = "CREATE TABLE `$sTable` ($sFieldDefs) ENGINE = ".MYSQL_ENGINE.' CHARACTER SET '.$sDbCharset.' COLLATE '.$sDbCollation.';'; + $sCreateTable = "CREATE TABLE `$sTable` ($sFieldDefs) ENGINE = ".MYSQL_ENGINE." CHARACTER SET ".$sDbCharset." COLLATE ".$sDbCollation.";"; CMDBSource::Query($sCreateTable); $aTriggers = $this->GetTriggersDefinition(); - foreach ($aTriggers as $key => $sTriggerSQL) + foreach($aTriggers as $key => $sTriggerSQL) { CMDBSource::Query($sTriggerSQL); } - + $sDataTable = $this->Get('database_table_name'); if (empty($sDataTable)) { $this->Set('database_table_name', $this->ComputeDataTableName()); $this->DBUpdate(); } - + } /** * Gets the definitions of the 3 triggers: before insert, before update and after delete - * * @return array An array with 3 entries, one for each of the SQL queries */ protected function GetTriggersDefinition() @@ -1013,15 +820,15 @@ EOF $aResult = array(); $sTriggerInsert = "CREATE TRIGGER `{$sTable}_bi` BEFORE INSERT ON `$sTable`"; - $sTriggerInsert .= ' FOR EACH ROW'; - $sTriggerInsert .= ' BEGIN'; + $sTriggerInsert .= " FOR EACH ROW"; + $sTriggerInsert .= " BEGIN"; $sTriggerInsert .= " INSERT INTO `{$sReplicaTable}` (`sync_source_id`, `status_last_seen`, `status`) VALUES ({$this->GetKey()}, NOW(), 'new');"; - $sTriggerInsert .= ' SET NEW.id = LAST_INSERT_ID();'; - $sTriggerInsert .= ' END;'; + $sTriggerInsert .= " SET NEW.id = LAST_INSERT_ID();"; + $sTriggerInsert .= " END;"; $aResult['bi'] = $sTriggerInsert; $aModified = array(); - foreach ($aColumns as $sColumn => $ColSpec) + foreach($aColumns as $sColumn => $ColSpec) { // <=> is a null-safe 'EQUALS' operator (there is no equivalent for "DIFFERS FROM") $aModified[] = "NOT(NEW.`$sColumn` <=> OLD.`$sColumn`)"; @@ -1034,25 +841,24 @@ EOF // otherwise, if status was either 'obsolete' or 'synchronized' it is turned into 'modified' or 'synchronized' depending on the changes // otherwise, the status is left as is $sTriggerUpdate = "CREATE TRIGGER `{$sTable}_bu` BEFORE UPDATE ON `$sTable`"; - $sTriggerUpdate .= ' FOR EACH ROW'; - $sTriggerUpdate .= ' BEGIN'; - $sTriggerUpdate .= ' IF @itopuser is null THEN'; + $sTriggerUpdate .= " FOR EACH ROW"; + $sTriggerUpdate .= " BEGIN"; + $sTriggerUpdate .= " IF @itopuser is null THEN"; $sTriggerUpdate .= " UPDATE `{$sReplicaTable}` SET status_last_seen = NOW(), `status` = IF(`status` = 'obsolete', IF(`dest_id` IS NULL, 'new', 'modified'), IF(`status` IN ('synchronized') AND ($sIsModified), 'modified', `status`)) WHERE sync_source_id = {$this->GetKey()} AND id = OLD.id;"; - $sTriggerUpdate .= ' SET NEW.id = OLD.id;'; // make sure this id won't change - $sTriggerUpdate .= ' END IF;'; - $sTriggerUpdate .= ' END;'; + $sTriggerUpdate .= " SET NEW.id = OLD.id;"; // make sure this id won't change + $sTriggerUpdate .= " END IF;"; + $sTriggerUpdate .= " END;"; $aResult['bu'] = $sTriggerUpdate; $sTriggerDelete = "CREATE TRIGGER `{$sTable}_ad` AFTER DELETE ON `$sTable`"; - $sTriggerDelete .= ' FOR EACH ROW'; - $sTriggerDelete .= ' BEGIN'; + $sTriggerDelete .= " FOR EACH ROW"; + $sTriggerDelete .= " BEGIN"; $sTriggerDelete .= " DELETE FROM `{$sReplicaTable}` WHERE id = OLD.id;"; - $sTriggerDelete .= ' END;'; + $sTriggerDelete .= " END;"; $aResult['ad'] = $sTriggerDelete; - return $aResult; } - + protected function AfterDelete() { parent::AfterDelete(); @@ -1081,11 +887,11 @@ EOF $oAttributeSet = $this->Get('attribute_list'); $aAttributes = array(); - while ($oAttribute = $oAttributeSet->Fetch()) + while($oAttribute = $oAttributeSet->Fetch()) { $sAttCode = $oAttribute->Get('attcode'); if (MetaModel::IsValidAttCode($this->GetTargetClass(), $sAttCode)) - { + { $aAttributes[$sAttCode] = $oAttribute; } else @@ -1094,7 +900,7 @@ EOF $bTriggerRebuildNeeded = true; if ($bVerbose) { - echo "Irrelevant field description for the field '$sAttCode', for the data synchro task ".$this->GetName().' ('.$this->GetKey()."), will be removed.\n"; + echo "Irrelevant field description for the field '$sAttCode', for the data synchro task ".$this->GetName()." (".$this->GetKey()."), will be removed.\n"; } $bFixNeeded = true; if (!$bDiagnostics) @@ -1106,7 +912,7 @@ EOF } $sTable = $this->GetDataTable(); - foreach ($this->ListTargetAttributes() as $sAttCode => $oAttDef) + foreach($this->ListTargetAttributes() as $sAttCode=>$oAttDef) { if (!isset($aAttributes[$sAttCode])) { @@ -1116,7 +922,7 @@ EOF // New field missing... if ($bVerbose) { - echo "Missing field description for the field '$sAttCode', for the data synchro task ".$this->GetName().' ('.$this->GetKey()."), will be created with default values.\n"; + echo "Missing field description for the field '$sAttCode', for the data synchro task ".$this->GetName()." (".$this->GetKey()."), will be created with default values.\n"; } if (!$bDiagnostics) { @@ -1128,7 +934,7 @@ EOF else { $aColumns = $this->GetSQLColumns(array($sAttCode)); - foreach ($aColumns as $sColName => $sColumnDef) + foreach($aColumns as $sColName => $sColumnDef) { $bOneColIsMissing = false; if (!CMDBSource::IsField($sTable, $sColName)) @@ -1139,29 +945,25 @@ EOF { if (count($aColumns) > 1) { - echo "Missing column '$sColName', in the table '$sTable' for the data synchro task ".$this->GetName().' ('.$this->GetKey()."). The columns '".implode("', '", - $aColumns)." will be re-created.'.\n"; + echo "Missing column '$sColName', in the table '$sTable' for the data synchro task ".$this->GetName()." (".$this->GetKey()."). The columns '".implode("', '", $aColumns )." will be re-created.'.\n"; } else { - echo "Missing column '$sColName', in the table '$sTable' for the data synchro task ".$this->GetName().' ('.$this->GetKey()."). The column '$sColName' will be added.\n"; + echo "Missing column '$sColName', in the table '$sTable' for the data synchro task ".$this->GetName()." (".$this->GetKey()."). The column '$sColName' will be added.\n"; } } } - elseif (strcasecmp(CMDBSource::GetFieldType($sTable, $sColName), $sColumnDef) != 0) + else if (strcasecmp(CMDBSource::GetFieldType($sTable, $sColName), $sColumnDef) != 0) { $bFixNeeded = true; $bOneColIsMissing = true; if (count($aColumns) > 1) { - echo "Incorrect column '$sColName' (".CMDBSource::GetFieldType($sTable, - $sColName).' instead of '.$sColumnDef."), in the table '$sTable' for the data synchro task ".$this->GetName().' ('.$this->GetKey()."). The columns '".implode("', '", - $aColumns)." will be re-created.'.\n"; + echo "Incorrect column '$sColName' (".CMDBSource::GetFieldType($sTable, $sColName)." instead of ".$sColumnDef."), in the table '$sTable' for the data synchro task ".$this->GetName()." (".$this->GetKey()."). The columns '".implode("', '", $aColumns )." will be re-created.'.\n"; } else { - echo "Incorrect column '$sColName' (".CMDBSource::GetFieldType($sTable, - $sColName).' instead of '.$sColumnDef."), in the table '$sTable' for the data synchro task ".$this->GetName().' ('.$this->GetKey()."). The column '$sColName' will be added.\n"; + echo "Incorrect column '$sColName' (".CMDBSource::GetFieldType($sTable, $sColName)." instead of ".$sColumnDef."), in the table '$sTable' for the data synchro task ".$this->GetName()." (".$this->GetKey()."). The column '$sColName' will be added.\n"; } } if ($bOneColIsMissing) @@ -1184,7 +986,7 @@ EOF { if ($bVerbose) { - echo 'Failed to investigate on the synchro triggers (skipping the check): '.$e->getMessage().".\n"; + echo "Failed to investigate on the synchro triggers (skipping the check): ".$e->getMessage().".\n"; } // Ignore this error: consider that the trigger are there $iTriggerCount = 3; @@ -1195,7 +997,7 @@ EOF $bTriggerRebuildNeeded = true; if ($bVerbose) { - echo 'Missing trigger(s) for the data synchro task '.$this->GetName()." (table {$sTable}).\n"; + echo "Missing trigger(s) for the data synchro task ".$this->GetName()." (table {$sTable}).\n"; } } @@ -1206,7 +1008,7 @@ EOF // The structure of the table needs adjusting $aColumns = $this->GetSQLColumns($aMissingFields); $aFieldDefs = array(); - foreach ($aColumns as $sAttCode => $sColumnDef) + foreach($aColumns as $sAttCode => $sColumnDef) { if (CMDBSource::IsField($sTable, $sAttCode)) { @@ -1220,15 +1022,14 @@ EOF } if (count($aFieldDefs) > 0) { - $aRepairQueries[] = "ALTER TABLE `$sTable` ADD (".implode(',', $aFieldDefs).');'; + $aRepairQueries[] = "ALTER TABLE `$sTable` ADD (".implode(',', $aFieldDefs).");"; } if ($bDiagnostics) { if ($bVerbose) { - echo "The structure of the table $sTable for the data synchro task ".$this->GetName().' ('.$this->GetKey().') must be altered (missing or incorrect fields: '.implode(',', - $aMissingFields).").\n"; + echo "The structure of the table $sTable for the data synchro task ".$this->GetName()." (".$this->GetKey().") must be altered (missing or incorrect fields: ".implode(',', $aMissingFields).").\n"; } } } @@ -1246,12 +1047,12 @@ EOF $aTriggerRepair[] = $aTriggersDefs['bu']; $aTriggerRepair[] = "DROP TRIGGER IF EXISTS `{$sTable}_ad`;"; $aTriggerRepair[] = $aTriggersDefs['ad']; - + if ($bDiagnostics) { if ($bVerbose) { - echo "The triggers {$sTable}_bi, {$sTable}_bu, {$sTable}_ad for the data synchro task ".$this->GetName().' ('.$this->GetKey().") must be re-created.\n"; + echo "The triggers {$sTable}_bi, {$sTable}_bu, {$sTable}_ad for the data synchro task ".$this->GetName()." (".$this->GetKey().") must be re-created.\n"; echo implode("\n", $aTriggerRepair)."\n"; } } @@ -1263,7 +1064,7 @@ EOF if (!$bDiagnostics && (count($aRepairQueries) > 0)) { // Fix the issue - foreach ($aRepairQueries as $sSQL) + foreach($aRepairQueries as $sSQL) { CMDBSource::Query($sSQL); if ($bVerbose) @@ -1272,7 +1073,6 @@ EOF } } } - return $bFixNeeded; } @@ -1319,29 +1119,19 @@ EOF } /** - * Get the list of attributes eligible to the synchronization + * Get the list of attributes eligible to the synchronization */ public function ListTargetAttributes() { $aRet = array(); - foreach (MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode => $oAttDef) + foreach(MetaModel::ListAttributeDefs($this->GetTargetClass()) as $sAttCode => $oAttDef) { - if ($sAttCode == 'finalclass') - { - continue; - } - if (!$oAttDef->IsWritable()) - { - continue; - } - if ($oAttDef->IsLinkSet() && !$oAttDef->IsIndirect()) - { - continue; - } + if ($sAttCode == 'finalclass') continue; + if (!$oAttDef->IsWritable()) continue; + if ($oAttDef->IsLinkSet() && !$oAttDef->IsIndirect()) continue; $aRet[$sAttCode] = $oAttDef; } - return $aRet; } @@ -1359,16 +1149,16 @@ EOF if (is_null($aAttributeCodes)) { $aAttributeCodes = array(); - foreach ($this->ListTargetAttributes() as $sAttCode => $oAttDef) + foreach($this->ListTargetAttributes() as $sAttCode => $oAttDef) { $aAttributeCodes[] = $sAttCode; } } - foreach ($aAttributeCodes as $sAttCode) + foreach($aAttributeCodes as $sAttCode) { $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); - + if ($oAttDef->IsExternalKey()) { // The pkey might be used as well as any other key column @@ -1376,40 +1166,37 @@ EOF } else { - foreach ($oAttDef->GetImportColumns() as $sField => $sDBFieldType) + foreach($oAttDef->GetImportColumns() as $sField => $sDBFieldType) { $aColumns[$sField] = $sDBFieldType; } } } - return $aColumns; } - + /** * DEPRECATED - Get the list of Date and Datetime SQL columns */ public function GetDateSQLColumns() { $aDateAttributes = array(); - + $sClass = $this->GetTargetClass(); - foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) { if ($oAttDef instanceof AttributeDateTime) { $aDateAttributes[] = $sAttCode; } } - return $this->GetSQLColumns($aDateAttributes); } public function IsRunning() { $sOQL = "SELECT SynchroLog WHERE sync_source_id = :source_id AND status='running'"; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array('start_date' => false) /* order by*/, - array('source_id' => $this->GetKey()) /* aArgs */, array(), 1 /* limitCount */, 0 /* limitStart */); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array('start_date' => false) /* order by*/, array('source_id' => $this->GetKey()) /* aArgs */, array(), 1 /* limitCount */, 0 /* limitStart */); if ($oSet->Count() < 1) { $bRet = false; @@ -1418,44 +1205,37 @@ EOF { $bRet = true; } - return $bRet; } - + public function GetLatestLog() { $oLog = null; - - $sOQL = 'SELECT SynchroLog WHERE sync_source_id = :source_id'; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array('start_date' => false) /* order by*/, - array('source_id' => $this->GetKey()) /* aArgs */, array(), 1 /* limitCount */, 0 /* limitStart */); + + $sOQL = "SELECT SynchroLog WHERE sync_source_id = :source_id"; + $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array('start_date' => false) /* order by*/, array('source_id' => $this->GetKey()) /* aArgs */, array(), 1 /* limitCount */, 0 /* limitStart */); if ($oSet->Count() >= 1) { $oLog = $oSet->Fetch(); } - return $oLog; } - + // TO DO: remove if still unused - /** * Retrieve from the log, the date of the last completed import - * * @return DateTime */ public function GetLastCompletedImportDate() { $date = null; $sOQL = "SELECT SynchroLog WHERE sync_source_id = :source_id AND status='completed'"; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array('end_date' => false) /* order by*/, - array('source_id' => $this->GetKey()) /* aArgs */, array(), 0 /* limitCount */, 0 /* limitStart */); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL($sOQL), array('end_date' => false) /* order by*/, array('source_id' => $this->GetKey()) /* aArgs */, array(), 0 /* limitCount */, 0 /* limitStart */); if ($oSet->Count() >= 1) { $oLog = $oSet->Fetch(); $date = $oLog->Get('end_date'); } - return $date; } } @@ -1466,68 +1246,28 @@ class SynchroAttribute extends cmdbAbstractObject { $aParams = array ( - 'category' => 'core/cmdb,view_in_gui,grant_by_profile', - 'key_type' => 'autoincrement', - 'name_attcode' => 'attcode', - 'state_attcode' => '', - 'reconc_keys' => array(), - 'db_table' => 'priv_sync_att', - 'db_key_field' => 'id', - 'db_finalclass_field' => '', - 'display_template' => '', + "category" => "core/cmdb,view_in_gui,grant_by_profile", + "key_type" => "autoincrement", + "name_attcode" => "attcode", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_sync_att", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - 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_SILENT, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeExternalField('sync_source_name', - array('allowed_values' => null, 'extkey_attcode' => 'sync_source_id', 'target_attcode' => 'name'))); - MetaModel::Init_AddAttribute(new AttributeString('attcode', array( - 'allowed_values' => null, - 'sql' => 'attcode', - 'default_value' => null, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeBoolean('update', array( - 'allowed_values' => null, - 'sql' => 'update', - 'default_value' => true, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeBoolean('reconcile', array( - 'allowed_values' => null, - 'sql' => 'reconcile', - 'default_value' => false, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeEnum('update_policy', array( - 'allowed_values' => new ValueSetEnum('master_locked,master_unlocked,write_if_empty'), - 'sql' => 'update_policy', - 'default_value' => 'master_locked', - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); + 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_SILENT, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("sync_source_name", array("allowed_values"=>null, "extkey_attcode"=> 'sync_source_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("attcode", array("allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeBoolean("update", array("allowed_values"=>null, "sql"=>"update", "default_value"=>true, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeBoolean("reconcile", array("allowed_values"=>null, "sql"=>"reconcile", "default_value"=>false, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("update_policy", array("allowed_values"=>new ValueSetEnum('master_locked,master_unlocked,write_if_empty'), "sql"=>"update_policy", "default_value"=>"master_locked", "is_null_allowed"=>false, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array( - 'sync_source_id', - 'attcode', - 'update', - 'reconcile', - 'update_policy', - )); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', - array('sync_source_id', 'attcode', 'update', 'reconcile', 'update_policy')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('sync_source_id', 'attcode', 'update', 'reconcile', 'update_policy')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('sync_source_id', 'attcode', 'update', 'reconcile', 'update_policy')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form @@ -1540,76 +1280,60 @@ class SynchroAttExtKey extends SynchroAttribute { $aParams = array ( - 'category' => 'core/cmdb,view_in_gui,grant_by_profile', - 'key_type' => 'autoincrement', - 'name_attcode' => 'attcode', - 'state_attcode' => '', - 'reconc_keys' => array(), - 'db_table' => 'priv_sync_att_extkey', - 'db_key_field' => 'id', - 'db_finalclass_field' => '', - 'display_template' => '', + "category" => "core/cmdb,view_in_gui,grant_by_profile", + "key_type" => "autoincrement", + "name_attcode" => "attcode", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_sync_att_extkey", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeString('reconciliation_attcode', array( - 'allowed_values' => null, - 'sql' => 'reconciliation_attcode', - 'default_value' => null, - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); + MetaModel::Init_AddAttribute(new AttributeString("reconciliation_attcode", array("allowed_values"=>null, "sql"=>"reconciliation_attcode", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array( - 'sync_source_id', - 'attcode', - 'update', - 'reconcile', - 'update_policy', - 'reconciliation_attcode', - )); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', - array('sync_source_id', 'attcode', 'update', 'reconcile', 'update_policy')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('sync_source_id', 'attcode', 'update', 'reconcile', 'update_policy', 'reconciliation_attcode')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('sync_source_id', 'attcode', 'update', 'reconcile', 'update_policy')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } - + public function GetReconciliationFormElement($sTargetClass, $sFieldName) { $sHtml = "\n"; - return $sHtml; } @@ -1621,61 +1345,26 @@ class SynchroAttLinkSet extends SynchroAttribute { $aParams = array ( - 'category' => 'core/cmdb,view_in_gui,grant_by_profile', - 'key_type' => 'autoincrement', - 'name_attcode' => 'attcode', - 'state_attcode' => '', - 'reconc_keys' => array(), - 'db_table' => 'priv_sync_att_linkset', - 'db_key_field' => 'id', - 'db_finalclass_field' => '', - 'display_template' => '', + "category" => "core/cmdb,view_in_gui,grant_by_profile", + "key_type" => "autoincrement", + "name_attcode" => "attcode", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_sync_att_linkset", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeString('row_separator', array( - 'allowed_values' => null, - 'sql' => 'row_separator', - 'default_value' => '|', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeString('attribute_separator', array( - 'allowed_values' => null, - 'sql' => 'attribute_separator', - 'default_value' => ';', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeString('value_separator', array( - 'allowed_values' => null, - 'sql' => 'value_separator', - 'default_value' => ':', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeString('attribute_qualifier', array( - 'allowed_values' => null, - 'sql' => 'attribute_qualifier', - 'default_value' => '\'', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); + MetaModel::Init_AddAttribute(new AttributeString("row_separator", array("allowed_values"=>null, "sql"=>"row_separator", "default_value"=>'|', "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("attribute_separator", array("allowed_values"=>null, "sql"=>"attribute_separator", "default_value"=>';', "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("value_separator", array("allowed_values"=>null, "sql"=>"value_separator", "default_value"=>':', "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("attribute_qualifier", array("allowed_values"=>null, "sql"=>"attribute_qualifier", "default_value"=>'\'', "is_null_allowed"=>true, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array( - 'sync_source_id', - 'attcode', - 'update', - 'reconcile', - 'update_policy', - 'row_separator', - 'attribute_separator', - 'value_separator', - 'attribute_qualifier', - )); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', - array('sync_source_id', 'attcode', 'update', 'reconcile', 'update_policy')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('sync_source_id', 'attcode', 'update', 'reconcile', 'update_policy', 'row_separator', 'attribute_separator', 'value_separator', 'attribute_qualifier')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('sync_source_id', 'attcode', 'update', 'reconcile', 'update_policy')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form @@ -1691,257 +1380,73 @@ class SynchroLog extends DBObject { $aParams = array ( - 'category' => 'core/cmdb,view_in_gui', - 'key_type' => 'autoincrement', - 'name_attcode' => '', - 'state_attcode' => '', - 'reconc_keys' => array(), - 'db_table' => 'priv_sync_log', - 'db_key_field' => 'id', - 'db_finalclass_field' => '', - 'display_template' => '', + "category" => "core/cmdb,view_in_gui", + "key_type" => "autoincrement", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_sync_log", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); // MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); - 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_SILENT, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeDateTime('start_date', array( - 'allowed_values' => null, - 'sql' => 'start_date', - 'default_value' => '', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeDateTime('end_date', array( - 'allowed_values' => null, - 'sql' => 'end_date', - 'default_value' => '', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeEnum('status', array( - 'allowed_values' => new ValueSetEnum('running,completed,error'), - 'sql' => 'status', - 'default_value' => 'running', - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('status_curr_job', array( - 'allowed_values' => null, - 'sql' => 'status_curr_job', - 'default_value' => 0, - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('status_curr_pos', array( - 'allowed_values' => null, - 'sql' => 'status_curr_pos', - 'default_value' => 0, - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); + 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_SILENT, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDateTime("start_date", array("allowed_values"=>null, "sql"=>"start_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDateTime("end_date", array("allowed_values"=>null, "sql"=>"end_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("allowed_values"=>new ValueSetEnum('running,completed,error'), "sql"=>"status", "default_value"=>"running", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("status_curr_job", array("allowed_values"=>null, "sql"=>"status_curr_job", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("status_curr_pos", array("allowed_values"=>null, "sql"=>"status_curr_pos", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_replica_seen', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_replica_seen', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_replica_total', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_replica_total', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_deleted', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_deleted', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_deleted_errors', array( - 'allowed_values' => null, - 'sql' => 'stats_deleted_errors', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_obsoleted', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_obsoleted', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_obsoleted_errors', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_obsoleted_errors', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_created', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_created', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_created_errors', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_created_errors', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_created_warnings', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_created_warnings', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_updated', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_updated', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_updated_errors', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_updated_errors', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_updated_warnings', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_updated_warnings', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_unchanged_warnings', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_unchanged_warnings', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_seen", array("allowed_values"=>null, "sql"=>"stats_nb_replica_seen", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_total", array("allowed_values"=>null, "sql"=>"stats_nb_replica_total", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_deleted", array("allowed_values"=>null, "sql"=>"stats_nb_obj_deleted", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_deleted_errors", array("allowed_values"=>null, "sql"=>"stats_deleted_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_obsoleted", array("allowed_values"=>null, "sql"=>"stats_nb_obj_obsoleted", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_obsoleted_errors", array("allowed_values"=>null, "sql"=>"stats_nb_obj_obsoleted_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_created", array("allowed_values"=>null, "sql"=>"stats_nb_obj_created", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_created_errors", array("allowed_values"=>null, "sql"=>"stats_nb_obj_created_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_created_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_created_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_updated", array("allowed_values"=>null, "sql"=>"stats_nb_obj_updated", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_updated_errors", array("allowed_values"=>null, "sql"=>"stats_nb_obj_updated_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_updated_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_updated_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_unchanged_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_unchanged_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); // MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_reconciled", array("allowed_values"=>null, "sql"=>"stats_nb_replica_reconciled", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_replica_reconciled_errors', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_replica_reconciled_errors', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_replica_disappeared_no_action', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_replica_disappeared_no_action', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_reconciled_errors", array("allowed_values"=>null, "sql"=>"stats_nb_replica_reconciled_errors", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_replica_disappeared_no_action", array("allowed_values"=>null, "sql"=>"stats_nb_replica_disappeared_no_action", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_new_updated', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_new_updated', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_new_updated_warnings', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_new_updated_warnings', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_new_unchanged', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_new_unchanged', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeInteger('stats_nb_obj_new_unchanged_warnings', array( - 'allowed_values' => null, - 'sql' => 'stats_nb_obj_new_unchanged_warnings', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_updated", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_updated", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_updated_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_updated_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_unchanged", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_unchanged", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("stats_nb_obj_new_unchanged_warnings", array("allowed_values"=>null, "sql"=>"stats_nb_obj_new_unchanged_warnings", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeText("last_error", array("allowed_values"=>null, "sql"=>"last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeLongText("traces", array("allowed_values"=>null, "sql"=>"traces", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeText('last_error', array( - 'allowed_values' => null, - 'sql' => 'last_error', - 'default_value' => '', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeLongText('traces', - array('allowed_values' => null, 'sql' => 'traces', 'default_value' => '', 'is_null_allowed' => true, 'depends_on' => array()))); - - MetaModel::Init_AddAttribute(new AttributeInteger('memory_usage_peak', array( - 'allowed_values' => null, - 'sql' => 'memory_usage_peak', - 'default_value' => 0, - 'is_null_allowed' => false, - 'depends_on' => array(), - ))); + MetaModel::Init_AddAttribute(new AttributeInteger("memory_usage_peak", array("allowed_values"=>null, "sql"=>"memory_usage_peak", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array( - 'sync_source_id', - 'start_date', - 'end_date', - 'status', - 'stats_nb_replica_total', - 'stats_nb_replica_seen', - 'stats_nb_obj_created', /*'stats_nb_replica_reconciled',*/ 'stats_nb_obj_updated', - 'stats_nb_obj_obsoleted', - 'stats_nb_obj_deleted', - 'stats_nb_obj_created_errors', - 'stats_nb_replica_reconciled_errors', - 'stats_nb_replica_disappeared_no_action', - 'stats_nb_obj_updated_errors', - 'stats_nb_obj_obsoleted_errors', - 'stats_nb_obj_deleted_errors', - 'stats_nb_obj_new_unchanged', - 'stats_nb_obj_new_updated', - 'traces', - )); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', - array('sync_source_id', 'start_date', 'end_date', 'status', 'stats_nb_replica_seen')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('sync_source_id', 'start_date', 'end_date', 'status', 'stats_nb_replica_total', 'stats_nb_replica_seen', 'stats_nb_obj_created', /*'stats_nb_replica_reconciled',*/ 'stats_nb_obj_updated', 'stats_nb_obj_obsoleted', 'stats_nb_obj_deleted', + 'stats_nb_obj_created_errors', 'stats_nb_replica_reconciled_errors', 'stats_nb_replica_disappeared_no_action', 'stats_nb_obj_updated_errors', 'stats_nb_obj_obsoleted_errors', 'stats_nb_obj_deleted_errors', 'stats_nb_obj_new_unchanged', 'stats_nb_obj_new_updated', 'traces')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('sync_source_id', 'start_date', 'end_date', 'status', 'stats_nb_replica_seen')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } - + /** * Helper */ - function GetErrorCount() - { - return $this->Get('stats_nb_obj_deleted_errors') - + $this->Get('stats_nb_obj_obsoleted_errors') - + $this->Get('stats_nb_obj_created_errors') - + $this->Get('stats_nb_obj_updated_errors') - + $this->Get('stats_nb_replica_reconciled_errors'); - } + function GetErrorCount() + { + return $this->Get('stats_nb_obj_deleted_errors') + + $this->Get('stats_nb_obj_obsoleted_errors') + + $this->Get('stats_nb_obj_created_errors') + + $this->Get('stats_nb_obj_updated_errors') + + $this->Get('stats_nb_replica_reconciled_errors'); + } /** * Increments a statistics counter @@ -1950,7 +1455,7 @@ class SynchroLog extends DBObject */ function Inc($sCode) { - $this->Set($sCode, 1 + $this->Get($sCode)); + $this->Set($sCode, 1+$this->Get($sCode)); } @@ -1958,7 +1463,6 @@ class SynchroLog extends DBObject * Implement traces management */ protected $m_aTraces = array(); - public function AddTrace($sMsg, $oReplica = null) { if (MetaModel::GetConfig()->Get('synchro_trace') == 'none') @@ -2040,156 +1544,66 @@ class SynchroReplica extends DBObject implements iDisplay { static $aSearches = array(); // Cache of OQL queries used for reconciliation (per data source) protected $aWarnings; - + public static function Init() { $aParams = array ( - 'category' => 'core/cmdb,view_in_gui', - 'key_type' => 'autoincrement', - 'name_attcode' => '', - 'state_attcode' => '', - 'reconc_keys' => array(), - 'db_table' => 'priv_sync_replica', - 'db_key_field' => 'id', - 'db_finalclass_field' => '', - 'display_template' => '', - 'indexes' => array(array('dest_class', 'dest_id'),), + "category" => "core/cmdb,view_in_gui", + "key_type" => "autoincrement", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_sync_replica", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", + "indexes" => array( array ('dest_class', 'dest_id'), ), ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - 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_SILENT, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeExternalField('base_class', - array('allowed_values' => null, 'extkey_attcode' => 'sync_source_id', 'target_attcode' => 'scope_class'))); + 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_SILENT, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("base_class", array("allowed_values"=>null, "extkey_attcode"=> 'sync_source_id', "target_attcode"=>"scope_class"))); - MetaModel::Init_AddAttribute(new AttributeObjectKey('dest_id', array( - 'allowed_values' => null, - 'class_attcode' => 'dest_class', - 'sql' => 'dest_id', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeClass('dest_class', array( - 'class_category' => '', - 'more_values' => '', - 'sql' => 'dest_class', - 'default_value' => 'Organization', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); + MetaModel::Init_AddAttribute(new AttributeObjectKey("dest_id", array("allowed_values"=>null, "class_attcode"=>"dest_class", "sql"=>"dest_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeClass("dest_class", array("class_category"=>"", "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(), - ))); - MetaModel::Init_AddAttribute(new AttributeBoolean('status_dest_creator', array( - 'allowed_values' => null, - 'sql' => 'status_dest_creator', - 'default_value' => 0, - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeString('status_last_error', array( - 'allowed_values' => null, - 'sql' => 'status_last_error', - 'default_value' => '', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeString('status_last_warning', array( - 'allowed_values' => null, - 'sql' => 'status_last_warning', - 'default_value' => '', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - - MetaModel::Init_AddAttribute(new AttributeDateTime('info_creation_date', array( - 'allowed_values' => null, - 'sql' => 'info_creation_date', - 'default_value' => '', - 'is_null_allowed' => true, - 'depends_on' => array(), - ))); - MetaModel::Init_AddAttribute(new AttributeDateTime('info_last_modified', array( - 'allowed_values' => null, - 'sql' => 'info_last_modified', - 'default_value' => '', - '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()))); + MetaModel::Init_AddAttribute(new AttributeBoolean("status_dest_creator", array("allowed_values"=>null, "sql"=>"status_dest_creator", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("status_last_error", array("allowed_values"=>null, "sql"=>"status_last_error", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("status_last_warning", array("allowed_values"=>null, "sql"=>"status_last_warning", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeDateTime("info_creation_date", array("allowed_values"=>null, "sql"=>"info_creation_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDateTime("info_last_modified", array("allowed_values"=>null, "sql"=>"info_last_modified", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); // Display lists - MetaModel::Init_SetZListItems('details', array( - ''. - 'col:0' => array( - 'fieldset:SynchroDataSource:Definition' => array('sync_source_id', 'dest_id', 'dest_class'), - 'fieldset:SynchroDataSource:Status' => array( - 'status', - 'status_last_seen', - 'status_dest_creator', - 'status_last_error', - 'status_last_warning', - ), - 'fieldset:SynchroDataSource:Information' => array('info_creation_date', 'info_last_modified'), - ), + MetaModel::Init_SetZListItems('details', array('' . + 'col:0'=> array( + 'fieldset:SynchroDataSource:Definition' => array('sync_source_id','dest_id','dest_class'), + 'fieldset:SynchroDataSource:Status' => array('status','status_last_seen','status_dest_creator','status_last_error','status_last_warning'), + 'fieldset:SynchroDataSource:Information' => array('info_creation_date','info_last_modified')) ) ); - MetaModel::Init_SetZListItems('list', array( - 'sync_source_id', - 'dest_id', - 'dest_class', - 'status_last_seen', - 'status', - 'status_dest_creator', - 'status_last_error', - 'status_last_warning', - )); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('sync_source_id', 'dest_id', 'dest_class', 'status_last_seen', 'status', 'status_dest_creator', 'status_last_error', 'status_last_warning')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array( - 'sync_source_id', - 'status_last_seen', - 'status', - 'status_dest_creator', - 'dest_class', - 'dest_id', - 'status_last_error', - 'status_last_warning', - )); // Criteria of the std search form + MetaModel::Init_SetZListItems('standard_search', array('sync_source_id', 'status_last_seen', 'status', 'status_dest_creator', 'dest_class', 'dest_id', 'status_last_error', 'status_last_warning')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } - + public function __construct($aRow = null, $sClassAlias = '', $aAttToLoad = null, $aExtendedDataSpec = null) { parent::__construct($aRow, $sClassAlias, $aAttToLoad, $aExtendedDataSpec); $this->aWarnings = array(); } - protected function AddWarning($sWarningMessage) + protected function AddWarning($sWarningMessage) { $this->aWarnings[] = $sWarningMessage; } - protected function ResetWarnings() + protected function ResetWarnings() { $this->aWarnings = array(); } @@ -2202,26 +1616,26 @@ class SynchroReplica extends DBObject implements iDisplay protected function RecordWarnings() { $MAX_WARNING_LENGTH = 255; - switch (count($this->aWarnings)) + switch(count($this->aWarnings)) { case 0: - $sWarningMessage = ''; - break; - + $sWarningMessage = ''; + break; + case 1: - $sWarningMessage = $this->aWarnings[0]; - break; - + $sWarningMessage = $this->aWarnings[0]; + break; + default: - $sWarningMessage = count($this->aWarnings).' warnings: '.implode(' ', $this->aWarnings); - break; + $sWarningMessage = count($this->aWarnings)." warnings: ".implode(' ', $this->aWarnings); + break; } if (strlen($sWarningMessage) > $MAX_WARNING_LENGTH) { $sWarningMessage = substr($sWarningMessage, 0, $MAX_WARNING_LENGTH - 3).'...'; } - + $this->Set('status_last_warning', $sWarningMessage); } @@ -2242,7 +1656,7 @@ class SynchroReplica extends DBObject implements iDisplay if ($oDataSource) { $sTable = $oDataSource->GetDataTable(); - + $sSQL = "DELETE FROM `$sTable` WHERE id = '{$this->GetKey()}'"; CMDBSource::Query($sSQL); } @@ -2272,202 +1686,196 @@ class SynchroReplica extends DBObject implements iDisplay $this->Set('status_last_error', $sText); } - + public function Synchro($oDataSource, $aReconciliationKeys, $aAttributes, $oChange, &$oStatLog) { $oStatLog->AddTrace(">>> Beginning of SynchroReplica::Synchro, replica status is '".$this->Get('status')."'.", $this); $this->ResetWarnings(); - switch ($this->Get('status')) + switch($this->Get('status')) { case 'new': - $this->Set('status_dest_creator', false); - // If needed, construct the query used for the reconciliation - if (!isset(self::$aSearches[$oDataSource->GetKey()])) + $this->Set('status_dest_creator', false); + // If needed, construct the query used for the reconciliation + if (!isset(self::$aSearches[$oDataSource->GetKey()])) + { + $aCriterias = array(); + foreach($aReconciliationKeys as $sFilterCode => $oSyncAtt) { - $aCriterias = array(); - foreach ($aReconciliationKeys as $sFilterCode => $oSyncAtt) - { - $aCriterias[] = ($sFilterCode == 'primary_key' ? 'id' : $sFilterCode).' = :'.$sFilterCode; - } - $sOQL = 'SELECT '.$oDataSource->GetTargetClass().' WHERE '.implode(' AND ', $aCriterias); - self::$aSearches[$oDataSource->GetKey()] = DBObjectSearch::FromOQL($sOQL); + $aCriterias[] = ($sFilterCode == 'primary_key' ? 'id' : $sFilterCode).' = :'.$sFilterCode; } - // Get the criterias for the search - $aFilterValues = array(); - foreach ($aReconciliationKeys as $sFilterCode => $oSyncAtt) + $sOQL = "SELECT ".$oDataSource->GetTargetClass()." WHERE ".implode(' AND ', $aCriterias); + self::$aSearches[$oDataSource->GetKey()] = DBObjectSearch::FromOQL($sOQL); + } + // Get the criterias for the search + $aFilterValues = array(); + foreach($aReconciliationKeys as $sFilterCode => $oSyncAtt) + { + $value = $this->GetValueFromExtData($sFilterCode, $oSyncAtt, $oStatLog); + if (!is_null($value)) { - $value = $this->GetValueFromExtData($sFilterCode, $oSyncAtt, $oStatLog); - if (!is_null($value)) - { - $aFilterValues[$sFilterCode] = $value; - } - else - { - // TO DO: can we retry this ?? - // Reconciliation could not be performed - log and EXIT - $oStatLog->AddTrace("Could not reconcile on null value for attribute '$sFilterCode'", $this); - $this->SetLastError("Could not reconcile on null value for attribute '$sFilterCode'"); - $oStatLog->Inc('stats_nb_replica_reconciled_errors'); - $oStatLog->AddTrace("<<< End of SyncroReplica::Synchro (error could not reconcile on null value for attribute '$sFilterCode').", - $this); - - return; - } - } - $oDestSet = new DBObjectSet(self::$aSearches[$oDataSource->GetKey()], array(), $aFilterValues); - $iCount = $oDestSet->Count(); - $sDebugOQL = $oDestSet->GetFilter()->ToOQL(true); - $oStatLog->AddTrace("Reconciliation query: '$sDebugOQL' returned $iCount object(s).", $this); - $aConditions = array(); - foreach ($aFilterValues as $sCode => $sValue) - { - $aConditions[] = $sCode.'='.$sValue; - } - $sConditionDesc = implode(' AND ', $aConditions); - // How many objects match the reconciliation criterias - switch ($iCount) - { - case 0: - $oStatLog->AddTrace("Nothing found on: $sConditionDesc", $this); - if ($oDataSource->Get('action_on_zero') == 'create') - { - $oStatLog->AddTrace('Calling CreateObjectFromReplica', $this); - $bCreated = $this->CreateObjectFromReplica($oDataSource->GetTargetClass(), $aAttributes, $oChange, $oStatLog); - if ($bCreated) - { - if ($this->HasWarnings()) - { - $oStatLog->Inc('stats_nb_obj_created_warnings'); - } - } - else - { - // Creation error has precedence over any warning - $this->ResetWarnings(); - } - } - else // assumed to be 'error' - { - $oStatLog->AddTrace('Failed to reconcile (no match)', $this); - // Recoverable error - $this->SetLastError('Could not find a match for reconciliation'); - $oStatLog->Inc('stats_nb_replica_reconciled_errors'); - } - break; - - case 1: - $oStatLog->AddTrace("Found 1 object on: $sConditionDesc", $this); - if ($oDataSource->Get('action_on_one') == 'update') - { - $oDestObj = $oDestSet->Fetch(); - $oStatLog->AddTrace('Calling UpdateObjectFromReplica('.(get_class($oDestObj).'::'.$oDestObj->GetKey()).')', - $this); - $bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', - 'stats_nb_replica_reconciled_errors'); - $this->Set('dest_id', $oDestObj->GetKey()); - $this->Set('dest_class', get_class($oDestObj)); - if ($this->HasWarnings()) - { - if ($bModified) - { - $oStatLog->Inc('stats_nb_obj_new_updated_warnings'); - } - else - { - $oStatLog->Inc('stats_nb_obj_new_unchanged_warnings'); - } - } - } - else - { - // assumed to be 'error' - $oStatLog->AddTrace('Failed to reconcile (1 match)', $this); - // Recoverable error - $this->SetLastError('Found a match while expecting several'); - $oStatLog->Inc('stats_nb_replica_reconciled_errors'); - } - break; - - default: - $oStatLog->AddTrace("Found $iCount objects on: $sConditionDesc", $this); - if ($oDataSource->Get('action_on_multiple') == 'error') - { - $oStatLog->AddTrace('Failed to reconcile (N>1 matches)', $this); - // Recoverable error - $this->SetLastError($iCount.' destination objects match the reconciliation criterias: '.$sConditionDesc); - $oStatLog->Inc('stats_nb_replica_reconciled_errors'); - } - elseif ($oDataSource->Get('action_on_multiple') == 'create') - { - $bCreated = $this->CreateObjectFromReplica($oDataSource->GetTargetClass(), $aAttributes, $oChange, $oStatLog); - if ($bCreated) - { - if ($this->HasWarnings()) - { - $oStatLog->Inc('stats_nb_obj_created_warnings'); - } - } - else - { - // Creation error has precedence over any warning - $this->ResetWarnings(); - } - } - else - { - // assumed to be 'take_first' - $oDestObj = $oDestSet->Fetch(); - $bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', - 'stats_nb_replica_reconciled_errors'); - $this->Set('dest_id', $oDestObj->GetKey()); - $this->Set('dest_class', get_class($oDestObj)); - if ($this->HasWarnings()) - { - if ($bModified) - { - $oStatLog->Inc('stats_nb_obj_new_updated_warnings'); - } - else - { - $oStatLog->Inc('stats_nb_obj_new_unchanged_warnings'); - } - } - } - } - $this->RecordWarnings(); - break; - - case 'synchronized': // try to recover synchronized replicas with warnings - case 'modified': - $oDestObj = MetaModel::GetObject($oDataSource->GetTargetClass(), $this->Get('dest_id')); - if ($oDestObj == null) - { - $this->Set('status', 'orphan'); // The destination object has been deleted ! - $this->SetLastError('Destination object deleted unexpectedly'); - $oStatLog->Inc('stats_nb_obj_updated_errors'); + $aFilterValues[$sFilterCode] = $value; } else { - $bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj', - 'stats_nb_obj_updated_errors'); + // TO DO: can we retry this ?? + // Reconciliation could not be performed - log and EXIT + $oStatLog->AddTrace("Could not reconcile on null value for attribute '$sFilterCode'", $this); + $this->SetLastError("Could not reconcile on null value for attribute '$sFilterCode'"); + $oStatLog->Inc('stats_nb_replica_reconciled_errors'); + $oStatLog->AddTrace("<<< End of SyncroReplica::Synchro (error could not reconcile on null value for attribute '$sFilterCode').", $this); + return; + } + } + $oDestSet = new DBObjectSet(self::$aSearches[$oDataSource->GetKey()], array(), $aFilterValues); + $iCount = $oDestSet->Count(); + $sDebugOQL = $oDestSet->GetFilter()->ToOQL(true); + $oStatLog->AddTrace("Reconciliation query: '$sDebugOQL' returned $iCount object(s).", $this); + $aConditions = array(); + foreach($aFilterValues as $sCode => $sValue) + { + $aConditions[] = $sCode.'='.$sValue; + } + $sConditionDesc = implode(' AND ', $aConditions); + // How many objects match the reconciliation criterias + switch($iCount) + { + case 0: + $oStatLog->AddTrace("Nothing found on: $sConditionDesc", $this); + if ($oDataSource->Get('action_on_zero') == 'create') + { + $oStatLog->AddTrace("Calling CreateObjectFromReplica", $this); + $bCreated = $this->CreateObjectFromReplica($oDataSource->GetTargetClass(), $aAttributes, $oChange, $oStatLog); + if ($bCreated) + { + if ($this->HasWarnings()) + { + $oStatLog->Inc('stats_nb_obj_created_warnings'); + } + } + else + { + // Creation error has precedence over any warning + $this->ResetWarnings(); + } + } + else // assumed to be 'error' + { + $oStatLog->AddTrace("Failed to reconcile (no match)", $this); + // Recoverable error + $this->SetLastError('Could not find a match for reconciliation'); + $oStatLog->Inc('stats_nb_replica_reconciled_errors'); + } + break; + + case 1: + $oStatLog->AddTrace("Found 1 object on: $sConditionDesc", $this); + if ($oDataSource->Get('action_on_one') == 'update') + { + $oDestObj = $oDestSet->Fetch(); + $oStatLog->AddTrace("Calling UpdateObjectFromReplica(".(get_class($oDestObj).'::'.$oDestObj->GetKey()).")", $this); + $bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors'); + $this->Set('dest_id', $oDestObj->GetKey()); + $this->Set('dest_class', get_class($oDestObj)); if ($this->HasWarnings()) { if ($bModified) { - $oStatLog->Inc('stats_nb_obj_updated_warnings'); + $oStatLog->Inc('stats_nb_obj_new_updated_warnings'); } else { - $oStatLog->Inc('stats_nb_obj_unchanged_warnings'); + $oStatLog->Inc('stats_nb_obj_new_unchanged_warnings'); } } } - $this->RecordWarnings(); + else + { + // assumed to be 'error' + $oStatLog->AddTrace("Failed to reconcile (1 match)", $this); + // Recoverable error + $this->SetLastError('Found a match while expecting several'); + $oStatLog->Inc('stats_nb_replica_reconciled_errors'); + } break; - + + default: + $oStatLog->AddTrace("Found $iCount objects on: $sConditionDesc", $this); + if ($oDataSource->Get('action_on_multiple') == 'error') + { + $oStatLog->AddTrace("Failed to reconcile (N>1 matches)", $this); + // Recoverable error + $this->SetLastError($iCount.' destination objects match the reconciliation criterias: '.$sConditionDesc); + $oStatLog->Inc('stats_nb_replica_reconciled_errors'); + } + elseif ($oDataSource->Get('action_on_multiple') == 'create') + { + $bCreated = $this->CreateObjectFromReplica($oDataSource->GetTargetClass(), $aAttributes, $oChange, $oStatLog); + if ($bCreated) + { + if ($this->HasWarnings()) + { + $oStatLog->Inc('stats_nb_obj_created_warnings'); + } + } + else + { + // Creation error has precedence over any warning + $this->ResetWarnings(); + } + } + else + { + // assumed to be 'take_first' + $oDestObj = $oDestSet->Fetch(); + $bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj_new', 'stats_nb_replica_reconciled_errors'); + $this->Set('dest_id', $oDestObj->GetKey()); + $this->Set('dest_class', get_class($oDestObj)); + if ($this->HasWarnings()) + { + if ($bModified) + { + $oStatLog->Inc('stats_nb_obj_new_updated_warnings'); + } + else + { + $oStatLog->Inc('stats_nb_obj_new_unchanged_warnings'); + } + } + } + } + $this->RecordWarnings(); + break; + + case 'synchronized': // try to recover synchronized replicas with warnings + case 'modified': + $oDestObj = MetaModel::GetObject($oDataSource->GetTargetClass(), $this->Get('dest_id')); + if ($oDestObj == null) + { + $this->Set('status', 'orphan'); // The destination object has been deleted ! + $this->SetLastError('Destination object deleted unexpectedly'); + $oStatLog->Inc('stats_nb_obj_updated_errors'); + } + else + { + $bModified = $this->UpdateObjectFromReplica($oDestObj, $aAttributes, $oChange, $oStatLog, 'stats_nb_obj', 'stats_nb_obj_updated_errors'); + if ($this->HasWarnings()) + { + if ($bModified) + { + $oStatLog->Inc('stats_nb_obj_updated_warnings'); + } + else + { + $oStatLog->Inc('stats_nb_obj_unchanged_warnings'); + } + } + } + $this->RecordWarnings(); + break; + default: // Do nothing in all other cases } - $oStatLog->AddTrace('<<< End of SynchroReplica::Synchro.', $this); + $oStatLog->AddTrace("<<< End of SynchroReplica::Synchro.", $this); } /** @@ -2488,7 +1896,7 @@ class SynchroReplica extends DBObject implements iDisplay $bModified = false; try { - foreach ($aAttributes as $sAttCode => $oSyncAtt) + foreach($aAttributes as $sAttCode => $oSyncAtt) { $value = $this->GetValueFromExtData($sAttCode, $oSyncAtt, $oStatLog); if (!is_null($value)) @@ -2516,7 +1924,7 @@ class SynchroReplica extends DBObject implements iDisplay $oDestObj->DBUpdateTracked($oChange); $bModified = true; $oStatLog->AddTrace('Updated object - Values: {'.implode(', ', $aValueTrace).'}', $this); - if (($sStatsCode != '') && (MetaModel::IsValidAttCode(get_class($oStatLog), $sStatsCode.'_updated'))) + if (($sStatsCode != '') &&(MetaModel::IsValidAttCode(get_class($oStatLog), $sStatsCode.'_updated'))) { $oStatLog->Inc($sStatsCode.'_updated'); } @@ -2525,7 +1933,7 @@ class SynchroReplica extends DBObject implements iDisplay else { $oStatLog->AddTrace('Unchanged object', $this); - if (($sStatsCode != '') && (MetaModel::IsValidAttCode(get_class($oStatLog), $sStatsCode.'_unchanged'))) + if (($sStatsCode != '') &&(MetaModel::IsValidAttCode(get_class($oStatLog), $sStatsCode.'_unchanged'))) { $oStatLog->Inc($sStatsCode.'_unchanged'); } @@ -2534,13 +1942,12 @@ class SynchroReplica extends DBObject implements iDisplay $this->Set('status_last_error', ''); $this->Set('status', 'synchronized'); } - catch (Exception $e) + catch(Exception $e) { $oStatLog->AddTrace("Failed to update destination object: {$e->getMessage()}", $this); $this->SetLastError('Unable to update destination object: ', $e); $oStatLog->Inc($sStatsCodeError); } - return $bModified; } @@ -2561,7 +1968,7 @@ class SynchroReplica extends DBObject implements iDisplay try { $aValueTrace = array(); - foreach ($aAttributes as $sAttCode => $oSyncAtt) + foreach($aAttributes as $sAttCode => $oSyncAtt) { $value = $this->GetValueFromExtData($sAttCode, $oSyncAtt, $oStatLog); if (!is_null($value)) @@ -2580,31 +1987,26 @@ class SynchroReplica extends DBObject implements iDisplay $this->Set('info_creation_date', date(AttributeDateTime::GetSQLFormat())); $bCreated = true; - $oStatLog->AddTrace('Created ('.implode(', ', $aValueTrace).')', $this); + $oStatLog->AddTrace("Created (".implode(', ', $aValueTrace).")", $this); $oStatLog->Inc('stats_nb_obj_created'); } - catch (Exception $e) + catch(Exception $e) { $oStatLog->AddTrace("Failed to create $sClass ({$e->getMessage()})", $this); $this->SetLastError('Unable to create destination object: ', $e); $oStatLog->Inc('stats_nb_obj_created_errors'); } - return $bCreated; } - + /** * Update the destination object with given values - * - * @param array $aValues - * @param CMDBChange $oChange - * @param SynchroLog $oStatLog - */ + */ public function UpdateDestObject($aValues, $oChange, &$oStatLog) { try { - if ($this->Get('dest_class') === '') + if ($this->Get('dest_class') == '') { $this->SetLastError('No destination object to update'); $oStatLog->Inc('stats_nb_obj_obsoleted_errors'); @@ -2612,21 +2014,21 @@ class SynchroReplica extends DBObject implements iDisplay else { $oDestObj = MetaModel::GetObject($this->Get('dest_class'), $this->Get('dest_id')); - foreach ($aValues as $sAttCode => $value) + foreach($aValues as $sAttCode => $value) { if (!MetaModel::IsValidAttCode(get_class($oDestObj), $sAttCode)) { throw new Exception("Unknown attribute code '$sAttCode'"); - } + } $oDestObj->Set($sAttCode, $value); } $this->Set('info_last_modified', date(AttributeDateTime::GetSQLFormat())); $oDestObj->DBUpdateTracked($oChange); - $oStatLog->AddTrace('Replica marked as obsolete', $this); + $oStatLog->AddTrace("Replica marked as obsolete", $this); $oStatLog->Inc('stats_nb_obj_obsoleted'); } } - catch (Exception $e) + catch(Exception $e) { $this->SetLastError('Unable to update the destination object: ', $e); $oStatLog->Inc('stats_nb_obj_obsoleted_errors'); @@ -2635,13 +2037,10 @@ class SynchroReplica extends DBObject implements iDisplay /** * Delete the destination object - * - * @param CMDBChange $oChange - * @param SynchroLog $oStatLog - */ + */ public function DeleteDestObject($oChange, &$oStatLog) { - if ($this->Get('status_dest_creator')) + if($this->Get('status_dest_creator')) { try { @@ -2660,7 +2059,7 @@ class SynchroReplica extends DBObject implements iDisplay throw(new Exception($sIssues)); } } - catch (Exception $e) + catch(Exception $e) { $this->SetLastError('Unable to delete the destination object: ', $e); $this->Set('status', 'obsolete'); @@ -2716,15 +2115,14 @@ class SynchroReplica extends DBObject implements iDisplay $oObj = MetaModel::GetObjectByColumn($sRemoteClass, $sReconcAttCode, $rawValue, false); if ($oObj) { - $retValue = $oObj->GetKey(); + $retValue = $oObj->GetKey(); } else { if ($rawValue != '') { // Note: differs from null (in which case the value would be left unchanged) - $oStatLog->AddTrace("Could not find [unique] object for '$sExtAttCode': searched on $sReconcAttCode = '$rawValue'", - $this); + $oStatLog->AddTrace("Could not find [unique] object for '$sExtAttCode': searched on $sReconcAttCode = '$rawValue'", $this); $this->AddWarning("Could not find [unique] object for '$sExtAttCode': searched on $sReconcAttCode = '$rawValue'"); } $retValue = 0; @@ -2745,13 +2143,12 @@ class SynchroReplica extends DBObject implements iDisplay } // MakeValueFromString() throws an exception in case of failure $bLocalizedValue = false; - $retValue = $oAttDef->MakeValueFromString($rawValue, $bLocalizedValue, $oSyncAtt->Get('row_separator'), - $oSyncAtt->Get('attribute_separator'), $oSyncAtt->Get('value_separator'), $oSyncAtt->Get('attribute_qualifier')); + $retValue = $oAttDef->MakeValueFromString($rawValue, $bLocalizedValue, $oSyncAtt->Get('row_separator'), $oSyncAtt->Get('attribute_separator'), $oSyncAtt->Get('value_separator'), $oSyncAtt->Get('attribute_qualifier')); } else { $aColumns = $oAttDef->GetImportColumns(); - foreach ($aColumns as $sColumn => $sFormat) + foreach($aColumns as $sColumn => $sFormat) { // In any case, a null column means "ignore this attribute" // @@ -2771,12 +2168,10 @@ class SynchroReplica extends DBObject implements iDisplay return $retValue; } - + /** * 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) @@ -2796,9 +2191,7 @@ class SynchroReplica extends DBObject implements iDisplay * 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() @@ -2822,28 +2215,21 @@ class SynchroReplica extends DBObject implements iDisplay $oPage->SetCurrentTab(Dict::S('UI:PropertiesTab')); $this->DisplayBareProperties($oPage, $bEditMode); } - + function DisplayBareProperties(WebPage $oPage, $bEditMode = false, $sPrefix = '', $aExtraParams = array()) { - if ($bEditMode) - { - return; - } // Not editable - + if ($bEditMode) return; // Not editable + $oPage->add('
    '); $aDetails = array(); $sClass = get_class($this); $oPage->add('
    '); $oPage->add(''.Dict::S('Core:SynchroReplica:PrivateDetails').''); $aZList = MetaModel::FlattenZlist(MetaModel::GetZListItems($sClass, 'details')); - foreach ($aZList as $sAttCode) + foreach( $aZList as $sAttCode) { - $sDisplayValue = $this->GetAsHTML($sAttCode); - $aDetails[] = array( - 'label' => ''.MetaModel::GetLabel($sClass, - $sAttCode).'', - 'value' => $sDisplayValue, - ); + $sDisplayValue = $this->GetAsHTML($sAttCode); + $aDetails[] = array('label' => ''.MetaModel::GetLabel($sClass, $sAttCode).'', 'value' => $sDisplayValue); } $oPage->Details($aDetails); $oPage->add('
    '); @@ -2854,7 +2240,7 @@ class SynchroReplica extends DBObject implements iDisplay { $oPage->add('
    '); $oPage->add(''.Dict::Format('Core:SynchroReplica:TargetObject', $oDestObj->GetHyperlink()).''); - $oDestObj->DisplayBareProperties($oPage, false, $sPrefix, $aExtraParams); + $oDestObj->DisplayBareProperties($oPage, false, $sPrefix, $aExtraParams); $oPage->add('
    '); } } @@ -2862,60 +2248,61 @@ class SynchroReplica extends DBObject implements iDisplay $oPage->add('
    '); $oPage->add(''.Dict::S('Core:SynchroReplica:PublicData').''); $oSource = MetaModel::GetObject('SynchroDataSource', $this->Get('sync_source_id')); - + $sSQLTable = $oSource->GetDataTable(); $aData = $this->LoadExtendedDataFromTable($sSQLTable); - $aHeaders = array( - 'attcode' => array('label' => 'Attribute Code', 'description' => ''), - 'data' => array('label' => 'Value', 'description' => ''), - ); + $aHeaders = array('attcode' => array('label' => 'Attribute Code', 'description' => ''), + 'data' => array('label' => 'Value', 'description' => '')); $aRows = array(); - foreach ($aData as $sKey => $value) + foreach($aData as $sKey => $value) { $aRows[] = array('attcode' => $sKey, 'data' => $value); } $oPage->Table($aHeaders, $aRows); $oPage->add('
    '); $oPage->add('
    '); - + } - + public function LoadExtendedDataFromTable($sSQLTable) { $sSQL = "SELECT * FROM $sSQLTable WHERE id=".$this->GetKey(); $rQuery = CMDBSource::Query($sSQL); - return CMDBSource::FetchArray($rQuery); } } /** - * Handles the execution phase of datasynchro (updates iTop objects from replicas) + * Handles the execution phase of datasynchro (create, update, delete iTop objects from replicas) * - * Two usages: + * Usage: * - * 1. Public usage: execute the synchronization + * Execute the synchronization * ```php * $oSynchroExec = new SynchroExecution($oDataSource[, $iLastFullLoad]); * $oSynchroExec->Process($iMaxChunkSize); * ``` - * 2. Internal usage: continue the synchronization (split into chunks, each performed in a separate process) - * This is implemented in the page priv_sync_chunk.php - * ```php - * $oSynchroExec = SynchroExecution::Resume($oDataSource, $iLastFullLoad, $iSynchroLog, $iChange, $iMaxToProcess, $iJob, $iNextInJob); - * $oSynchroExec->Process() - * ``` */ class SynchroExecution { /** @var \SynchroDataSource */ protected $m_oDataSource = null; /** @var \DateTime */ + protected $m_oImportPhaseStartDate = null; + /** + * @var \DateTime The reference that will be used to query replicas.
    + * Value is either : + *
      + *
    • specified by the caller (if it is doing both import and exec phases) + *
    • computed using current date minus the 'full load interval' datasource field + *
    • a dumb date if full load interval is 0 and doing only exec phase + *
    + */ protected $m_oLastFullLoadStartDate = null; - /** @var bool true if the caller script did instantiate using a date (the datetime before import phase was launched) */ - protected $m_bIsDoingImportAndExecInSameScript; + /** @var bool true if the caller script gave the datetime before import phase was launched */ + protected $m_bIsImportPhaseDateKnown; /** @var \CMDBChange */ protected $m_oChange = null; @@ -2932,22 +2319,17 @@ class SynchroExecution /** * @param SynchroDataSource $oDataSource Synchronization task - * @param DateTime $oLastFullLoadStartDate when doing both import and exec phases in the same script, this parameter should contain - * the current datetime when the script was launched (before import phase).
    - * This is used by `synchro_import.php --synchronize=1` + * @param DateTime $oImportPhaseStartDate Start of the import when doing both import and exec phases in the same script. (This is used by `synchro_import.php --synchronize=1`) + * null when exec phase is used alone and the import start date is unknown. * * @throws \CoreException */ - public function __construct($oDataSource, $oLastFullLoadStartDate = null) + public function __construct($oDataSource, $oImportPhaseStartDate = null) { $this->m_oDataSource = $oDataSource; - $this->m_bIsDoingImportAndExecInSameScript = ($this->m_oLastFullLoadStartDate != null); - if (!$this->m_bIsDoingImportAndExecInSameScript) - { - $oLastFullLoadStartDate = self::GetDataBaseCurrentDateTime(); - } - $this->m_oLastFullLoadStartDate = $oLastFullLoadStartDate; + $this->m_bIsImportPhaseDateKnown = ($oImportPhaseStartDate != null); + $this->m_oImportPhaseStartDate = $oImportPhaseStartDate; $this->m_oCtx = new ContextTag('Synchro'); $this->m_oCtx1 = new ContextTag('Synchro:'.$oDataSource->GetRawName()); // More precise context information @@ -2981,11 +2363,11 @@ class SynchroExecution } // Create a change used for logging all the modifications/creations happening during the synchro - $this->m_oChange = MetaModel::NewObject('CMDBChange'); - $this->m_oChange->Set('date', time()); + $this->m_oChange = MetaModel::NewObject("CMDBChange"); + $this->m_oChange->Set("date", time()); $sUserString = CMDBChange::GetCurrentUserName(); - $this->m_oChange->Set('userinfo', $sUserString.' '.Dict::S('Core:SyncDataExchangeComment')); - $this->m_oChange->Set('origin', 'synchro-data-source'); + $this->m_oChange->Set("userinfo", $sUserString.' '.Dict::S('Core:SyncDataExchangeComment')); + $this->m_oChange->Set("origin", 'synchro-data-source'); $this->m_oChange->DBInsert(); // Start logging this execution (stats + protection against reentrance) @@ -3012,17 +2394,16 @@ class SynchroExecution $this->m_oStatLog->Set('stats_nb_replica_disappeared_no_action', 0); $this->m_oStatLog->Set('stats_nb_obj_new_updated', 0); $this->m_oStatLog->Set('stats_nb_obj_new_updated_warnings', 0); - $this->m_oStatLog->Set('stats_nb_obj_new_unchanged', 0); - $this->m_oStatLog->Set('stats_nb_obj_new_unchanged_warnings', 0); + $this->m_oStatLog->Set('stats_nb_obj_new_unchanged',0); + $this->m_oStatLog->Set('stats_nb_obj_new_unchanged_warnings',0); - $sSelectTotal = 'SELECT SynchroReplica WHERE sync_source_id = :source_id'; - $oSetTotal = new DBObjectSet(DBObjectSearch::FromOQL($sSelectTotal), array() /* order by*/, - array('source_id' => $this->m_oDataSource->GetKey())); + $sSelectTotal = "SELECT SynchroReplica WHERE sync_source_id = :source_id"; + $oSetTotal = new DBObjectSet(DBObjectSearch::FromOQL($sSelectTotal), array() /* order by*/, array('source_id' => $this->m_oDataSource->GetKey())); $this->m_iCountAllReplicas = $oSetTotal->Count(); $this->m_oStatLog->Set('stats_nb_replica_total', $this->m_iCountAllReplicas); $this->m_oStatLog->DBInsertTracked($this->m_oChange); - $sLastFullLoad = ($this->m_bIsDoingImportAndExecInSameScript) ? $this->m_oLastFullLoadStartDate->format('Y-m-d H:i:s') : 'not specified'; + $sLastFullLoad = ($this->m_bIsImportPhaseDateKnown) ? $this->m_oImportPhaseStartDate->format('Y-m-d H:i:s') : 'not specified'; $this->m_oStatLog->AddTrace("###### STARTING SYNCHRONIZATION ##### Total: {$this->m_iCountAllReplicas} replica(s). Last full load: '$sLastFullLoad' "); $sSql = 'SELECT NOW();'; $sDBNow = CMDBSource::QueryToScalar($sSql); @@ -3030,10 +2411,9 @@ class SynchroExecution } /** - * Prevent against the reentrance... or allow the current task to do things forbidden by the others ! - */ + * Prevent against the reentrance... or allow the current task to do things forbidden by the others ! + */ public static $m_oCurrentTask = null; - public static function GetCurrentTaskId() { if (is_object(self::$m_oCurrentTask)) @@ -3072,9 +2452,8 @@ class SynchroExecution $aAttCodesExpected = array(); $aAttCodesToReconcile = array(); $aAttCodesToUpdate = array(); - $sSelectAtt = 'SELECT SynchroAttribute WHERE sync_source_id = :source_id AND (update = 1 OR reconcile = 1)'; - $oSetAtt = new DBObjectSet(DBObjectSearch::FromOQL($sSelectAtt), array() /* order by*/, - array('source_id' => $this->m_oDataSource->GetKey()) /* aArgs */); + $sSelectAtt = "SELECT SynchroAttribute WHERE sync_source_id = :source_id AND (update = 1 OR reconcile = 1)"; + $oSetAtt = new DBObjectSet(DBObjectSearch::FromOQL($sSelectAtt), array() /* order by*/, array('source_id' => $this->m_oDataSource->GetKey()) /* aArgs */); while ($oSyncAtt = $oSetAtt->Fetch()) { if ($oSyncAtt->Get('update')) @@ -3094,7 +2473,7 @@ class SynchroExecution $this->m_aExtDataSpec = array( 'table' => $this->m_oDataSource->GetDataTable(), 'join_key' => 'id', - 'fields' => $aExtDataFields, + 'fields' => $aExtDataFields ); // Get the list of attributes, determine reconciliation keys and update targets @@ -3106,28 +2485,28 @@ class SynchroExecution elseif ($this->m_oDataSource->Get('reconciliation_policy') == 'use_primary_key') { // Override the settings made at the attribute level ! - $this->m_aReconciliationKeys = array('primary_key' => null); + $this->m_aReconciliationKeys = array("primary_key" => null); } if ($bFirstPass) { - $this->m_oStatLog->AddTrace('Update of: {'.implode(', ', array_keys($aAttCodesToUpdate)).'}'); - $this->m_oStatLog->AddTrace('Reconciliation on: {'.implode(', ', array_keys($this->m_aReconciliationKeys)).'}'); + $this->m_oStatLog->AddTrace("Update of: {".implode(', ', array_keys($aAttCodesToUpdate))."}"); + $this->m_oStatLog->AddTrace("Reconciliation on: {".implode(', ', array_keys($this->m_aReconciliationKeys))."}"); } if (count($aAttCodesToUpdate) == 0) { - $this->m_oStatLog->AddTrace('No attribute to update'); + $this->m_oStatLog->AddTrace("No attribute to update"); throw new SynchroExceptionNotStarted('There is no attribute to update'); } if (count($this->m_aReconciliationKeys) == 0) { - $this->m_oStatLog->AddTrace('No attribute for reconciliation'); + $this->m_oStatLog->AddTrace("No attribute for reconciliation"); throw new SynchroExceptionNotStarted('No attribute for reconciliation'); } - + $this->m_aAttributes = array(); - foreach ($aAttCodesToUpdate as $sAttCode => $oSyncAtt) + foreach($aAttCodesToUpdate as $sAttCode => $oSyncAtt) { $oAttDef = MetaModel::GetAttributeDef($this->m_oDataSource->GetTargetClass(), $sAttCode); if ($oAttDef->IsWritable()) @@ -3136,11 +2515,34 @@ class SynchroExecution } } - // keep track of the limit date taken into account for obsoleting replicas + // Compute and keep track of the limit date taken into account for obsoleting replicas // + $iFullLoadInterval = $this->m_oDataSource->Get('full_load_periodicity'); // Duration in seconds + if ($this->m_bIsImportPhaseDateKnown) + { + $oLimitDate = clone $this->m_oImportPhaseStartDate; + $sInterval = "-$iFullLoadInterval seconds"; + $oLimitDate->Modify($sInterval); + } + else + { + if ($iFullLoadInterval <= 0) + { + // we are doing exec phase alone, and the full load interval is set to 0 => we should not update/delete replicas !! + // This will prevent actions in DoJob1() method + $oLimitDate = new DateTime('1970-01-01'); + } + else + { + $oLimitDate = self::GetDataBaseCurrentDateTime(); + $sInterval = "-$iFullLoadInterval seconds"; + $oLimitDate->Modify($sInterval); + } + } + $this->m_oLastFullLoadStartDate = $oLimitDate; if ($bFirstPass) { - $this->m_oStatLog->AddTrace('Limit Date: '.$this->m_oLastFullLoadStartDate->Format('Y-m-d H:i:s')); + $this->m_oStatLog->AddTrace("Limit Date: ".$this->m_oLastFullLoadStartDate->Format('Y-m-d H:i:s')); } } @@ -3164,7 +2566,7 @@ class SynchroExecution $oMutex->Lock(); $this->DoSynchronize(); $oMutex->Unlock(); - + $this->m_oStatLog->Set('end_date', time()); $this->m_oStatLog->Set('status', 'completed'); $this->m_oStatLog->DBUpdateTracked($this->m_oChange); @@ -3172,32 +2574,31 @@ class SynchroExecution $iErrors = $this->m_oStatLog->GetErrorCount(); if ($iErrors > 0) { - $sIssuesOQL = 'SELECT SynchroReplica WHERE sync_source_id='.$this->m_oDataSource->GetKey()." AND status_last_error!=''"; + $sIssuesOQL = "SELECT SynchroReplica WHERE sync_source_id=".$this->m_oDataSource->GetKey()." AND status_last_error!=''"; $sAbsoluteUrl = utils::GetAbsoluteUrlAppRoot(); - $sIssuesURL = "{$sAbsoluteUrl}synchro/replica.php?operation=oql&datasource=".$this->m_oDataSource->GetKey().'&oql='.urlencode($sIssuesOQL); + $sIssuesURL = "{$sAbsoluteUrl}synchro/replica.php?operation=oql&datasource=".$this->m_oDataSource->GetKey()."&oql=".urlencode($sIssuesOQL); $sStatistics = "

    Statistics

    \n"; $sStatistics .= "
      \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('start_date').': '.$this->m_oStatLog->Get('start_date')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('end_date').': '.$this->m_oStatLog->Get('end_date')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_replica_seen').': '.$this->m_oStatLog->Get('stats_nb_replica_seen')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_replica_total').': '.$this->m_oStatLog->Get('stats_nb_replica_total')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_obj_deleted').': '.$this->m_oStatLog->Get('stats_nb_obj_deleted')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_obj_deleted_errors').': '.$this->m_oStatLog->Get('stats_nb_obj_deleted_errors')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_obj_obsoleted').': '.$this->m_oStatLog->Get('stats_nb_obj_obsoleted')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_obj_obsoleted_errors').': '.$this->m_oStatLog->Get('stats_nb_obj_obsoleted_errors')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_obj_created').': '.$this->m_oStatLog->Get('stats_nb_obj_created').' ('.$this->m_oStatLog->Get('stats_nb_obj_created_warnings').' warnings)'."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_obj_created_errors').': '.$this->m_oStatLog->Get('stats_nb_obj_created_errors')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_obj_updated').': '.$this->m_oStatLog->Get('stats_nb_obj_updated').' ('.$this->m_oStatLog->Get('stats_nb_obj_updated_warnings').' warnings)'."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_obj_updated_errors').': '.$this->m_oStatLog->Get('stats_nb_obj_updated_errors')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_replica_reconciled_errors').': '.$this->m_oStatLog->Get('stats_nb_replica_reconciled_errors')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_replica_disappeared_no_action').': '.$this->m_oStatLog->Get('stats_nb_replica_disappeared_no_action')."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_obj_new_updated').': '.$this->m_oStatLog->Get('stats_nb_obj_new_updated').' ('.$this->m_oStatLog->Get('stats_nb_obj_new_updated_warnings').' warnings)'."
    • \n"; - $sStatistics .= '
    • '.$this->m_oStatLog->GetLabel('stats_nb_obj_new_unchanged').': '.$this->m_oStatLog->Get('stats_nb_obj_new_unchanged').' ('.$this->m_oStatLog->Get('stats_nb_obj_new_unchanged_warnings').' warnings)'."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('start_date').": ".$this->m_oStatLog->Get('start_date')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('end_date').": ".$this->m_oStatLog->Get('end_date')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_replica_seen').": ".$this->m_oStatLog->Get('stats_nb_replica_seen')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_replica_total').": ".$this->m_oStatLog->Get('stats_nb_replica_total')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_obj_deleted').": ".$this->m_oStatLog->Get('stats_nb_obj_deleted')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_obj_deleted_errors').": ".$this->m_oStatLog->Get('stats_nb_obj_deleted_errors')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_obj_obsoleted').": ".$this->m_oStatLog->Get('stats_nb_obj_obsoleted')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_obj_obsoleted_errors').": ".$this->m_oStatLog->Get('stats_nb_obj_obsoleted_errors')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_obj_created').": ".$this->m_oStatLog->Get('stats_nb_obj_created')." (".$this->m_oStatLog->Get('stats_nb_obj_created_warnings')." warnings)"."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_obj_created_errors').": ".$this->m_oStatLog->Get('stats_nb_obj_created_errors')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_obj_updated').": ".$this->m_oStatLog->Get('stats_nb_obj_updated')." (".$this->m_oStatLog->Get('stats_nb_obj_updated_warnings')." warnings)"."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_obj_updated_errors').": ".$this->m_oStatLog->Get('stats_nb_obj_updated_errors')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_replica_reconciled_errors').": ".$this->m_oStatLog->Get('stats_nb_replica_reconciled_errors')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_replica_disappeared_no_action').": ".$this->m_oStatLog->Get('stats_nb_replica_disappeared_no_action')."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_obj_new_updated').": ".$this->m_oStatLog->Get('stats_nb_obj_new_updated')." (".$this->m_oStatLog->Get('stats_nb_obj_new_updated_warnings')." warnings)"."
    • \n"; + $sStatistics .= "
    • ".$this->m_oStatLog->GetLabel('stats_nb_obj_new_unchanged').": ".$this->m_oStatLog->Get('stats_nb_obj_new_unchanged')." (".$this->m_oStatLog->Get('stats_nb_obj_new_unchanged_warnings')." warnings)"."
    • \n"; $sStatistics .= "
    \n"; - $this->m_oDataSource->SendNotification("errors ($iErrors)", - "

    The synchronization has been executed, $iErrors errors have been encountered. Click here to see the records being currently in error.

    ".$sStatistics); + $this->m_oDataSource->SendNotification("errors ($iErrors)", "

    The synchronization has been executed, $iErrors errors have been encountered. Click here to see the records being currently in error.

    ".$sStatistics); } else { @@ -3212,8 +2613,7 @@ class SynchroExecution $this->m_oStatLog->Set('status', 'error'); $this->m_oStatLog->Set('last_error', $e->getMessage()); $this->m_oStatLog->DBDeleteTracked($this->m_oChange); - $this->m_oDataSource->SendNotification('fatal error', - '

    The synchronization could not start: \''.$e->getMessage().'\'

    Please check its configuration

    '); + $this->m_oDataSource->SendNotification('fatal error', '

    The synchronization could not start: \''.$e->getMessage().'\'

    Please check its configuration

    '); } catch (Exception $e) { @@ -3222,8 +2622,7 @@ class SynchroExecution $this->m_oStatLog->Set('status', 'error'); $this->m_oStatLog->Set('last_error', $e->getMessage()); $this->m_oStatLog->DBUpdateTracked($this->m_oChange); - $this->m_oDataSource->SendNotification('exception', - '

    The synchronization has been interrupted: \''.$e->getMessage().'\'

    Please contact the application support team

    '); + $this->m_oDataSource->SendNotification('exception', '

    The synchronization has been interrupted: \''.$e->getMessage().'\'

    Please contact the application support team

    '); } self::$m_oCurrentTask = null; @@ -3262,16 +2661,16 @@ class SynchroExecution $aArguments['log'] = $this->m_oStatLog->GetKey(); $aArguments['change'] = $this->m_oChange->GetKey(); $aArguments['chunk'] = $iMaxChunkSize; - if ($this->m_bIsDoingImportAndExecInSameScript) + if ($this->m_bIsImportPhaseDateKnown) { - $aArguments['last_full_load'] = $this->m_oLastFullLoadStartDate->Format('Y-m-d H:i:s'); + $aArguments['last_full_load'] = $this->m_oImportPhaseStartDate->Format('Y-m-d H:i:s'); } else { $aArguments['last_full_load'] = ''; } - $this->m_oStatLog->DBUpdate(); + $this->m_oStatLog->DBUpdate($this->m_oChange); $iStepCount = 0; do @@ -3286,27 +2685,28 @@ class SynchroExecution $this->m_oStatLog->Reload(); $sLastRes = strtolower(trim(end($aOut))); - switch ($sLastRes) + switch($sLastRes) { - case 'continue': - $bContinue = true; - break; + case 'continue': + $bContinue = true; + break; - case 'finished': - $bContinue = false; - break; + case 'finished': + $bContinue = false; + break; - default: - $this->m_oStatLog->AddTrace('The script did not reply with the expected keywords:'); - $aIndentedOut = array(); - foreach ($aOut as $sOut) - { - $aIndentedOut[] = "-> $sOut"; - $this->m_oStatLog->AddTrace(">>> $sOut"); - } - throw new Exception("Encountered an error in an underspinned process:\n".implode("\n", $aIndentedOut)); + default: + $this->m_oStatLog->AddTrace("The script did not reply with the expected keywords:"); + $aIndentedOut = array(); + foreach ($aOut as $sOut) + { + $aIndentedOut[] = "-> $sOut"; + $this->m_oStatLog->AddTrace(">>> $sOut"); + } + throw new Exception("Encountered an error in an underspinned process:\n".implode("\n", $aIndentedOut)); } - } while ($bContinue); + } + while ($bContinue); } else { @@ -3368,12 +2768,13 @@ class SynchroExecution } $this->m_oStatLog->DBUpdate($this->m_oChange); self::$m_oCurrentTask = null; - return $bContinue; } /** - * Do the synchronization job #1: Obsolete replica "untouched" for some time + * Do the synchronization job #1: Execute action on replicas "untouched" for some time + * - Obsolete entries if delete_policy is 'update' or 'update_then_delete' + * - Delete entries if delete_policy is 'delete' * * @param integer $iMaxReplica Limit the number of replicas to process * @param integer $iCurrPos Current position where to resume the processing @@ -3390,53 +2791,36 @@ class SynchroExecution protected function DoJob1UpdateReplicas($iMaxReplica = null, $iCurrPos = -1) { $this->m_oStatLog->AddTrace(">>> Beginning of DoJob1(\$iMaxReplica = $iMaxReplica, \$iCurrPos = $iCurrPos)"); - if ($this->m_bIsDoingImportAndExecInSameScript) - { - $sLastFullLoadStartDate = $this->m_oLastFullLoadStartDate->Format('Y-m-d H:i:s'); - } - else - { - $sLastFullLoadStartDate = ''; - } - + $sLastFullLoadStartDate = $this->m_oLastFullLoadStartDate->Format('Y-m-d H:i:s'); $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); // Get all the replicas that were not seen in the last import and mark them as obsolete $sDeletePolicy = $this->m_oDataSource->Get('delete_policy'); if ($sDeletePolicy !== 'ignore') { - $iLoadPeriodicity = $this->m_oDataSource->Get('full_load_periodicity'); // Duration in seconds - if ($this->m_bIsDoingImportAndExecInSameScript && ($iLoadPeriodicity <= 0)) - { - // we are doing exec phase alone, and the full load interval is set to 0 => we should not update !! - $oLimitDate = new DateTime('1970-01-01'); - } - else - { - $oLimitDate = clone $this->m_oLastFullLoadStartDate; - $sInterval = "-$iLoadPeriodicity seconds"; - $oLimitDate->Modify($sInterval); - } - $sLimitDate = $oLimitDate->Format('Y-m-d H:i:s'); - $sLastFullLoadStartDate = $sLimitDate; - - $sSelectToObsolete = "SELECT SynchroReplica WHERE id > :curr_pos AND sync_source_id = :source_id AND status IN ('new', 'synchronized', 'modified', 'orphan') AND status_last_seen < :last_import"; - $oSetScope = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToObsolete), array() /* order by*/, - array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sLimitDate, 'curr_pos' => $iCurrPos)); + $sSelectToObsolete = "SELECT SynchroReplica WHERE id > :curr_pos AND sync_source_id = :source_id AND status IN ('new', 'synchronized', 'modified', 'orphan') AND status_last_seen < :last_import"; + $oSetScope = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToObsolete), array() /* order by*/, array( + 'source_id' => $this->m_oDataSource->GetKey(), + 'last_import' => $sLastFullLoadStartDate, + 'curr_pos' => $iCurrPos, + )); $iCountScope = $oSetScope->Count(); $sDebugOql = $oSetScope->GetFilter()->ToOQL(true); $this->m_oStatLog->AddTrace("Searching for replicas to mark as obsolete using query: '$sDebugOql', returned $iCountScope replica(s)."); if (($this->m_iCountAllReplicas > 10) && ($this->m_iCountAllReplicas == $iCountScope) && MetaModel::GetConfig()->Get('synchro_prevent_delete_all')) { throw new SynchroExceptionNotStarted(Dict::S('Core:SyncTooManyMissingReplicas')); - } + } if ($iMaxReplica) { // Consider a given subset, starting from replica iCurrPos, limited to the count of iMaxReplica // The replica have to be ordered by id - $oSetToProcess = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToObsolete), array('id' => true) /* order by*/, - array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sLimitDate, 'curr_pos' => $iCurrPos)); + $oSetToProcess = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToObsolete), array('id' => true) /* order by*/, array( + 'source_id' => $this->m_oDataSource->GetKey(), + 'last_import' => $sLastFullLoadStartDate, + 'curr_pos' => $iCurrPos, + )); $oSetToProcess->SetLimit($iMaxReplica); } else @@ -3445,50 +2829,54 @@ class SynchroExecution } $iLastReplicaProcessed = -1; - while ($oReplica = $oSetToProcess->Fetch()) + while($oReplica = $oSetToProcess->Fetch()) { set_time_limit($iLoopTimeLimit); $iLastReplicaProcessed = $oReplica->GetKey(); switch ($sDeletePolicy) { - case 'update': - case 'update_then_delete': - $this->m_oStatLog->AddTrace('Destination object to be updated', $oReplica); - $aToUpdate = array(); - $aToUpdateSpec = explode(';', - $this->m_oDataSource->Get('delete_policy_update')); //ex: 'status:obsolete;description:stopped', - foreach ($aToUpdateSpec as $sUpdateSpec) + case 'update': + case 'update_then_delete': + $this->m_oStatLog->AddTrace("Destination object to be updated", $oReplica); + $aToUpdate = array(); + $aToUpdateSpec = explode(';', $this->m_oDataSource->Get('delete_policy_update')); //ex: 'status:obsolete;description:stopped', + foreach($aToUpdateSpec as $sUpdateSpec) + { + $aUpdateSpec = explode(':', $sUpdateSpec); + if (count($aUpdateSpec) == 2) { - $aUpdateSpec = explode(':', $sUpdateSpec); - if (count($aUpdateSpec) == 2) - { - $sAttCode = $aUpdateSpec[0]; - $sValue = $aUpdateSpec[1]; - $aToUpdate[$sAttCode] = $sValue; - } + $sAttCode = $aUpdateSpec[0]; + $sValue = $aUpdateSpec[1]; + $aToUpdate[$sAttCode] = $sValue; } - $oReplica->Set('status_last_error', ''); - if ($oReplica->Get('dest_id') == '') + } + $oReplica->Set('status_last_error', ''); + if ($oReplica->Get('dest_id') == '') + { + $oReplica->Set('status', 'obsolete'); + $this->m_oStatLog->Inc('stats_nb_replica_disappeared_no_action'); + } + else + { + $oReplica->UpdateDestObject($aToUpdate, $this->m_oChange, $this->m_oStatLog); + if ($oReplica->Get('status_last_error') == '') { + // Change the status of the replica IIF $oReplica->Set('status', 'obsolete'); - $this->m_oStatLog->Inc('stats_nb_replica_disappeared_no_action'); } - else - { - $oReplica->UpdateDestObject($aToUpdate, $this->m_oChange, $this->m_oStatLog); - if ($oReplica->Get('status_last_error') == '') - { - // Change the status of the replica IIF - $oReplica->Set('status', 'obsolete'); - } - } - $oReplica->DBUpdateTracked($this->m_oChange); - break; + } + $oReplica->DBUpdateTracked($this->m_oChange); + break; + + case 'delete': + // delete_policy_retention is not used here only full_load_interval + $this->m_oStatLog->AddTrace("Destination object to be DELETED", $oReplica); + $oReplica->DeleteDestObject($this->m_oChange, $this->m_oStatLog); + break; - case 'delete': - default: - $this->m_oStatLog->AddTrace('Destination object to be DELETED', $oReplica); - $oReplica->DeleteDestObject($this->m_oChange, $this->m_oStatLog); + default: + $this->m_oStatLog->AddTrace("Unknown delete policy : $sDeletePolicy"); + break; } } if ($iMaxReplica) @@ -3498,16 +2886,14 @@ class SynchroExecution // Continue with this job! $this->m_oStatLog->Set('status_curr_pos', $iLastReplicaProcessed); $this->m_oStatLog->AddTrace("<<< End of DoJob1(\$iMaxReplica = $iMaxReplica, \$iCurrPos = $iCurrPos) (returning true => more replicas to process)"); - return true; } } } // if ($sDeletePolicy != 'ignore' //Count "seen" objects - $sSelectSeen = "SELECT SynchroReplica WHERE sync_source_id = :source_id AND status IN ('new', 'synchronized', 'modified', 'orphan') AND status_last_seen >= :last_import"; - $oSetSeen = new DBObjectSet(DBObjectSearch::FromOQL($sSelectSeen), array() /* order by*/, - array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sLastFullLoadStartDate)); + $sSelectSeen = "SELECT SynchroReplica WHERE sync_source_id = :source_id AND status IN ('new', 'synchronized', 'modified', 'orphan') AND status_last_seen >= :last_import"; + $oSetSeen = new DBObjectSet(DBObjectSearch::FromOQL($sSelectSeen), array() /* order by*/, array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sLastFullLoadStartDate)); $this->m_oStatLog->Set('stats_nb_replica_seen', $oSetSeen->Count()); @@ -3515,7 +2901,6 @@ class SynchroExecution $this->m_oStatLog->Set('status_curr_job', 2); $this->m_oStatLog->Set('status_curr_pos', -1); $this->m_oStatLog->AddTrace("<<< End of DoJob1(\$iMaxReplica = $iMaxReplica, \$iCurrPos = $iCurrPos) (completed)"); - return false; } @@ -3543,10 +2928,8 @@ class SynchroExecution // Get all the replicas that are 'new' or modified or synchronized with a warning // - $sSelectToSync = "SELECT SynchroReplica WHERE id > :curr_pos AND (status = 'new' OR status = 'modified' OR (status = 'synchronized' AND status_last_warning != '')) AND sync_source_id = :source_id AND status_last_seen >= :last_import"; - $oSetScope = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToSync), array() /* order by*/, - array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sLimitDate, 'curr_pos' => $iCurrPos), - $this->m_aExtDataSpec); + $sSelectToSync = "SELECT SynchroReplica WHERE id > :curr_pos AND (status = 'new' OR status = 'modified' OR (status = 'synchronized' AND status_last_warning != '')) AND sync_source_id = :source_id AND status_last_seen >= :last_import"; + $oSetScope = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToSync), array() /* order by*/, array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sLimitDate, 'curr_pos' => $iCurrPos), $this->m_aExtDataSpec); $iCountScope = $oSetScope->Count(); $sDebugOQL = $oSetScope->GetFilter()->ToOQL(true); $this->m_oStatLog->AddTrace("Looking for - new, modified or synchonized with a warning - replicas using the OQL query: '$sDebugOQL', returned $iCountScope replicas."); @@ -3555,9 +2938,7 @@ class SynchroExecution { // Consider a given subset, starting from replica iCurrPos, limited to the count of iMaxReplica // The replica have to be ordered by id - $oSetToProcess = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToSync), array('id' => true) /* order by*/, - array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sLimitDate, 'curr_pos' => $iCurrPos), - $this->m_aExtDataSpec); + $oSetToProcess = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToSync), array('id'=>true) /* order by*/, array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sLimitDate, 'curr_pos' => $iCurrPos), $this->m_aExtDataSpec); $oSetToProcess->SetLimit($iMaxReplica); } else @@ -3566,17 +2947,17 @@ class SynchroExecution } $iLastReplicaProcessed = -1; - while ($oReplica = $oSetToProcess->Fetch()) + /** @var \SynchroReplica $oReplica */ + while($oReplica = $oSetToProcess->Fetch()) { set_time_limit($iLoopTimeLimit); $iLastReplicaProcessed = $oReplica->GetKey(); $this->m_oStatLog->AddTrace("Synchronizing replica id=$iLastReplicaProcessed."); - $oReplica->Synchro($this->m_oDataSource, $this->m_aReconciliationKeys, $this->m_aAttributes, $this->m_oChange, - $this->m_oStatLog); + $oReplica->Synchro($this->m_oDataSource, $this->m_aReconciliationKeys, $this->m_aAttributes, $this->m_oChange, $this->m_oStatLog); $this->m_oStatLog->AddTrace("Updating replica id=$iLastReplicaProcessed."); - $oReplica->DBUpdateTracked($this->m_oChange); + $oReplica->DBUpdateTracked($this->m_oChange); } - + if ($iMaxReplica) { if ($iMaxReplica < $iCountScope) @@ -3584,7 +2965,6 @@ class SynchroExecution // Continue with this job! $this->m_oStatLog->Set('status_curr_pos', $iLastReplicaProcessed); $this->m_oStatLog->AddTrace("<<< End of DoJob2(\$iMaxReplica = $iMaxReplica, \$iCurrPos = $iCurrPos) (returning true => more replicas to process)"); - return true; } } @@ -3593,12 +2973,11 @@ class SynchroExecution $this->m_oStatLog->Set('status_curr_job', 3); $this->m_oStatLog->Set('status_curr_pos', -1); $this->m_oStatLog->AddTrace("<<< End of DoJob2(\$iMaxReplica = $iMaxReplica, \$iCurrPos = $iCurrPos) (completed)"); - return false; } /** - * Do the synchronization job #3: Delete replica depending on the obsolescence scheme + * Do the synchronization job #3: Delete replica depending on the obsolescence scheme if delete_policy is 'update_then_delete' * * @param integer $iMaxReplica Limit the number of replicas to process * @param integer $iCurrPos Current position where to resume the processing @@ -3616,7 +2995,7 @@ class SynchroExecution { $this->m_oStatLog->AddTrace(">>> Beginning of DoJob3(\$iMaxReplica = $iMaxReplica, \$iCurrPos = $iCurrPos)"); $iLoopTimeLimit = MetaModel::GetConfig()->Get('max_execution_time_per_loop'); - + $sDeletePolicy = $this->m_oDataSource->Get('delete_policy'); if ($sDeletePolicy !== 'update_then_delete') { @@ -3625,7 +3004,6 @@ class SynchroExecution $this->m_oStatLog->Set('status_curr_job', 0); $this->m_oStatLog->Set('status_curr_pos', -1); $this->m_oStatLog->AddTrace("<<< End of DoJob3(\$iMaxReplica = $iMaxReplica, \$iCurrPos = $iCurrPos) (completed)"); - return false; } @@ -3640,22 +3018,20 @@ class SynchroExecution $sInterval = "-$iDeleteRetention seconds"; $oDeletionDate->Modify($sInterval); } - $sDeletionDate = $oDeletionDate->Format('Y-m-d H:i:s'); + $sDeletionDate = $oDeletionDate->Format('Y-m-d H:i:s'); if ($bFirstPass) { $this->m_oStatLog->AddTrace("Deletion date: $sDeletionDate"); } $sSelectToDelete = "SELECT SynchroReplica WHERE id > :curr_pos AND sync_source_id = :source_id AND status IN ('obsolete') AND status_last_seen < :last_import"; - $oSetScope = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToDelete), array() /* order by*/, - array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sDeletionDate, 'curr_pos' => $iCurrPos)); + $oSetScope = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToDelete), array() /* order by*/, array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sDeletionDate, 'curr_pos' => $iCurrPos)); $iCountScope = $oSetScope->Count(); if ($iMaxReplica) { // Consider a given subset, starting from replica iCurrPos, limited to the count of iMaxReplica // The replica have to be ordered by id - $oSetToProcess = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToDelete), array('id' => true) /* order by*/, - array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sDeletionDate, 'curr_pos' => $iCurrPos)); + $oSetToProcess = new DBObjectSet(DBObjectSearch::FromOQL($sSelectToDelete), array('id'=>true) /* order by*/, array('source_id' => $this->m_oDataSource->GetKey(), 'last_import' => $sDeletionDate, 'curr_pos' => $iCurrPos)); $oSetToProcess->SetLimit($iMaxReplica); } else @@ -3664,11 +3040,11 @@ class SynchroExecution } $iLastReplicaProcessed = -1; - while ($oReplica = $oSetToProcess->Fetch()) + while($oReplica = $oSetToProcess->Fetch()) { set_time_limit($iLoopTimeLimit); $iLastReplicaProcessed = $oReplica->GetKey(); - $this->m_oStatLog->AddTrace('Destination object to be DELETED', $oReplica); + $this->m_oStatLog->AddTrace("Destination object to be DELETED", $oReplica); $oReplica->DeleteDestObject($this->m_oChange, $this->m_oStatLog); } @@ -3679,7 +3055,6 @@ class SynchroExecution // Continue with this job! $this->m_oStatLog->Set('status_curr_pos', $iLastReplicaProcessed); $this->m_oStatLog->AddTrace("<<< End of DoJob3\$iMaxReplica = $iMaxReplica, \$iCurrPos = $iCurrPos) (returning true => more replicas to process)"); - return true; } } @@ -3687,7 +3062,6 @@ class SynchroExecution $this->m_oStatLog->Set('status_curr_job', 0); $this->m_oStatLog->Set('status_curr_pos', -1); $this->m_oStatLog->AddTrace("<<< End of DoJob3(\$iMaxReplica = $iMaxReplica, \$iCurrPos = $iCurrPos) (completed)"); - return false; } }