From 4f6e74a3ee28e83ac1d9583ce210d0dfa7945619 Mon Sep 17 00:00:00 2001 From: Eric Espie Date: Wed, 3 May 2023 14:39:14 +0200 Subject: [PATCH] =?UTF-8?q?N=C2=B02250=20-=20DisplayObject=20with=20ormLin?= =?UTF-8?q?kSet=20ignore=20Removed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- application/cmdbabstract.class.inc.php | 2 +- core/ormlinkset.class.inc.php | 13 ++++ .../plugin_combodo_update_operations.js | 1 - .../UI/Base/Component/Input/Set/Set.php | 21 ++++++ .../BlockIndirectLinkSetEditTable.php | 67 ++++++++++++------- .../Set/BlockLinksSetDisplayAsProperty.php | 4 +- .../UI/Links/Set/LinksSetUIBlockFactory.php | 10 ++- .../Service/Links/LinkSetDataTransformer.php | 1 + sources/Service/Links/LinkSetRepository.php | 16 +++-- .../components/input/set/layout.ready.js.twig | 5 +- 10 files changed, 101 insertions(+), 39 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index b2b63dc9c..501ab5520 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -2367,6 +2367,7 @@ EOF $sHTMLValue = ConsoleBlockRenderer::RenderBlockTemplateInPage($oPage, $oTagSetBlock); } else { $sInputType = self::ENUM_INPUT_TYPE_LINKEDSET; + $oObj = $aArgs['this'] ?? null; if ($oAttDef->IsIndirect()) { $oWidget = new UILinksWidget($sClass, $sAttCode, $iId, $sNameSuffix, $oAttDef->DuplicatesAllowed()); @@ -2375,7 +2376,6 @@ EOF } $aEventsList[] = 'validate'; $aEventsList[] = 'change'; - $oObj = $aArgs['this'] ?? null; $sHTMLValue = $oWidget->Display($oPage, $value, array(), $sFormPrefix, $oObj); } break; diff --git a/core/ormlinkset.class.inc.php b/core/ormlinkset.class.inc.php index aa52720f8..3343605f2 100644 --- a/core/ormlinkset.class.inc.php +++ b/core/ormlinkset.class.inc.php @@ -845,6 +845,11 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator } $oLinkSearch->SetSelectedClasses([self::LINK_ALIAS, self::REMOTE_ALIAS]); } + if (count($this->aRemoved) !== 0) { + $sConditionExpr = '`'.self::LINK_ALIAS.'`.id NOT IN ('.implode(',', $this->aRemoved).')'; + $oRemovedExpression = Expression::FromOQL($sConditionExpr); + $oLinkSearch->AddConditionExpression($oRemovedExpression); + } $oLinkSet = new DBObjectSet($oLinkSearch); $oLinkSet->SetShowObsoleteData($bShowObsolete); if ($this->HasDelta()) { @@ -873,4 +878,12 @@ class ormLinkSet implements iDBObjectSetIterator, Iterator, SeekableIterator return $aValues; } + + /** + * @return \DBObjectSet|null + */ + public function GetOriginalSet(): ?DBObjectSet + { + return $this->oOriginalSet; + } } diff --git a/js/selectize/plugin_combodo_update_operations.js b/js/selectize/plugin_combodo_update_operations.js index a51a06b23..f8bd73811 100644 --- a/js/selectize/plugin_combodo_update_operations.js +++ b/js/selectize/plugin_combodo_update_operations.js @@ -112,7 +112,6 @@ Selectize.define("combodo_update_operations", function (aOptions) { } } }); - }; })(); diff --git a/sources/Application/UI/Base/Component/Input/Set/Set.php b/sources/Application/UI/Base/Component/Input/Set/Set.php index 5d3845560..c36b98b70 100644 --- a/sources/Application/UI/Base/Component/Input/Set/Set.php +++ b/sources/Application/UI/Base/Component/Input/Set/Set.php @@ -81,6 +81,8 @@ class Set extends AbstractInput /** @var bool $bHasError Error flag */ private bool $bHasError; + private ?string $sInitialValue = null; + /** * Constructor. * @@ -424,4 +426,23 @@ class Set extends AbstractInput { return $this->bHasError; } + + /** + * @return string + */ + public function GetInitialValue(): string + { + if (is_null($this->sInitialValue)) { + return $this->GetValue(); + } + return $this->sInitialValue; + } + + /** + * @param string $sInitialValue + */ + public function SetInitialValue(string $sInitialValue): void + { + $this->sInitialValue = $sInitialValue; + } } \ No newline at end of file diff --git a/sources/Application/UI/Links/Indirect/BlockIndirectLinkSetEditTable.php b/sources/Application/UI/Links/Indirect/BlockIndirectLinkSetEditTable.php index a89eba502..664d0a023 100644 --- a/sources/Application/UI/Links/Indirect/BlockIndirectLinkSetEditTable.php +++ b/sources/Application/UI/Links/Indirect/BlockIndirectLinkSetEditTable.php @@ -8,19 +8,17 @@ namespace Combodo\iTop\Application\UI\Links\Indirect; use AttributeLinkedSetIndirect; use cmdbAbstractObject; -use Combodo\iTop\Application\UI\Base\Component\Alert\AlertUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Button\ButtonUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\DataTable\DataTableUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Input\InputUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Panel\PanelUIBlockFactory; use Combodo\iTop\Application\UI\Base\Component\Toolbar\ToolbarUIBlockFactory; use Combodo\iTop\Application\UI\Base\Layout\UIContentBlock; -use Combodo\iTop\Service\Links\LinkSetModel; use ConfigException; use CoreException; use DBObject; -use Exception; use Dict; +use Exception; use MetaModel; use UILinksWidget; use utils; @@ -60,7 +58,7 @@ class BlockIndirectLinkSetEditTable extends UIContentBlock /** @var int */ public int $iMaxAddedId = 0; - /** @var array */ + /** @var array List of removed links id used by twig template */ public array $aRemoved = []; /** @var string */ @@ -131,7 +129,7 @@ class BlockIndirectLinkSetEditTable extends UIContentBlock * @throws \CoreException * @throws \CoreUnexpectedValue */ - public function InitTable(\WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aTableConfig) + public function InitTable(WebPage $oPage, $oValue, $aArgs, $sFormPrefix, $oCurrentObj, $aTableConfig) { $this->sWizHelper = 'oWizardHelper'.$sFormPrefix; $oValue->Rewind(); @@ -139,29 +137,48 @@ class BlockIndirectLinkSetEditTable extends UIContentBlock $aForm = array(); $iMaxAddedId = 0; $iAddedId = -1; // Unique id for new links - $this->aRemoved = json_decode(\utils::ReadPostedParam("attr_{$sFormPrefix}{$this->oUILinksWidget->GetAttCode()}_tbd", '[]', 'raw_data')); + $this->aRemoved = json_decode(\utils::ReadPostedParam("attr_{$sFormPrefix}{$this->oUILinksWidget->GetAttCode()}_tbd", '[]', 'raw_data'), true); + $aModified = json_decode(\utils::ReadPostedParam("attr_{$sFormPrefix}{$this->oUILinksWidget->GetAttCode()}_tbm", '[]', 'raw_data'), true); while ($oCurrentLink = $oValue->Fetch()) { // We try to retrieve the remote object as usual - if (!in_array($oCurrentLink->GetKey(), $this->aRemoved)) { - $oLinkedObj = MetaModel::GetObject($this->oUILinksWidget->GetRemoteClass(), $oCurrentLink->Get($this->oUILinksWidget->GetExternalKeyToRemote()), false /* Must not be found */); - // If successful, it means that we can edit its link - if ($oLinkedObj !== null) { - $bReadOnly = false; - } // Else we retrieve it without restrictions (silos) and will display its link as readonly - else { - $bReadOnly = true; - $oLinkedObj = MetaModel::GetObject($this->oUILinksWidget->GetRemoteClass(), $oCurrentLink->Get($this->oUILinksWidget->GetExternalKeyToRemote()), false /* Must not be found */, true); - } - - if ($oCurrentLink->IsNew()) { - $key = $iAddedId--; - } else { - $key = $oCurrentLink->GetKey(); - } - - $iMaxAddedId = max($iMaxAddedId, $key); - $aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly, $bAllowRemoteExtKeyEdit); + $sCurrentLinkId = $oCurrentLink->GetKey(); + if ($oCurrentLink->IsNew()) { + $key = $iAddedId--; + } else { + $key = $oCurrentLink->GetKey(); } + + if (isset($aModified[$sCurrentLinkId])) { + // Apply the modifications to the current link + $aModifications = $aModified[$sCurrentLinkId]; + $sPrefix = 'attr_'.$aModifications['formPrefix']; + foreach ($aModifications as $sName => $sValue) { + if (!utils::StartsWith($sName, $sPrefix)) { + continue; + } + $sAttCode = substr($sName, strlen($sPrefix)); + $oCurrentLink->Set($sAttCode, $sValue); + $sEscapedValue = addslashes($sValue); + $oPage->add_ready_script(<<oUILinksWidget->GetInputId()}.OnValueChange($sCurrentLinkId, $iAddedId, "$sAttCode", "$sEscapedValue"); +EOF + ); + } + } + $oLinkedObj = MetaModel::GetObject($this->oUILinksWidget->GetRemoteClass(), $oCurrentLink->Get($this->oUILinksWidget->GetExternalKeyToRemote()), false /* Must not be found */); + // If successful, it means that we can edit its link + if ($oLinkedObj !== null) { + $bReadOnly = false; + } // Else we retrieve it without restrictions (silos) and will display its link as readonly + else { + $bReadOnly = true; + $oLinkedObj = MetaModel::GetObject($this->oUILinksWidget->GetRemoteClass(), $oCurrentLink->Get($this->oUILinksWidget->GetExternalKeyToRemote()), false /* Must not be found */, true); + } + + + + $iMaxAddedId = max($iMaxAddedId, $key); + $aForm[$key] = $this->GetFormRow($oPage, $oLinkedObj, $oCurrentLink, $aArgs, $oCurrentObj, $key, $bReadOnly, $bAllowRemoteExtKeyEdit); } $this->iMaxAddedId = (int)$iMaxAddedId; diff --git a/sources/Application/UI/Links/Set/BlockLinksSetDisplayAsProperty.php b/sources/Application/UI/Links/Set/BlockLinksSetDisplayAsProperty.php index e16c2ccee..62cbe32e4 100644 --- a/sources/Application/UI/Links/Set/BlockLinksSetDisplayAsProperty.php +++ b/sources/Application/UI/Links/Set/BlockLinksSetDisplayAsProperty.php @@ -89,7 +89,9 @@ class BlockLinkSetDisplayAsProperty extends UIContentBlock $sTargetField = LinkSetModel::GetTargetField($this->oAttribute); // Get objects from linked data - $this->aObjectsData = LinkSetRepository::LinksDbSetToTargetObjectArray($this->oValue, $this->sTargetClass, $sTargetField); + $aObjectsData = []; + LinkSetRepository::LinksDbSetToTargetObjectArray($this->oValue, false, $aObjectsData, $this->sTargetClass, $sTargetField); + $this->aObjectsData = array_values($aObjectsData); // Twig environment $this->oTwigEnv = TwigHelper::GetTwigEnvironment(TwigHelper::ENUM_TEMPLATES_BASE_PATH_BACKOFFICE); diff --git a/sources/Application/UI/Links/Set/LinksSetUIBlockFactory.php b/sources/Application/UI/Links/Set/LinksSetUIBlockFactory.php index 0dacfe999..96137563b 100644 --- a/sources/Application/UI/Links/Set/LinksSetUIBlockFactory.php +++ b/sources/Application/UI/Links/Set/LinksSetUIBlockFactory.php @@ -69,13 +69,19 @@ class LinkSetUIBlockFactory extends SetUIBlockFactory // Current value $aCurrentValues = LinkSetDataTransformer::Decode($oDbObjectSet, $sTargetClass, $sTargetField); + // Some operations can have been done in case of reload after an error + $aInitialValues = LinkSetDataTransformer::Decode($oDbObjectSet->GetOriginalSet(), $sTargetClass, $sTargetField); // Initial options data - $aInitialOptions = LinkSetRepository::LinksDbSetToTargetObjectArray($oDbObjectSet, $sTargetClass, $sTargetField); + $aInitialOptions = []; + LinkSetRepository::LinksDbSetToTargetObjectArray($oDbObjectSet, false, $aInitialOptions, $sTargetClass, $sTargetField); + // Register also original values in case of reload after an error. In order to remember the operations, use the "bForce" flag + LinkSetRepository::LinksDbSetToTargetObjectArray($oDbObjectSet->GetOriginalSet(), true, $aInitialOptions, $sTargetClass, $sTargetField); if ($aInitialOptions !== null) { - $oSetUIBlock->GetDataProvider()->SetOptions($aInitialOptions); + $oSetUIBlock->GetDataProvider()->SetOptions(array_values($aInitialOptions)); // Set value $oSetUIBlock->SetValue(json_encode($aCurrentValues)); + $oSetUIBlock->SetInitialValue(json_encode($aInitialValues)); } else { $oSetUIBlock->SetHasError(true); } diff --git a/sources/Service/Links/LinkSetDataTransformer.php b/sources/Service/Links/LinkSetDataTransformer.php index 93b4c6f8e..2a83ade2e 100644 --- a/sources/Service/Links/LinkSetDataTransformer.php +++ b/sources/Service/Links/LinkSetDataTransformer.php @@ -78,6 +78,7 @@ class LinkSetDataTransformer catch (Exception $e) { ExceptionLog::LogException($e); + return []; } } diff --git a/sources/Service/Links/LinkSetRepository.php b/sources/Service/Links/LinkSetRepository.php index 9a0d4a010..82e52d0cb 100644 --- a/sources/Service/Links/LinkSetRepository.php +++ b/sources/Service/Links/LinkSetRepository.php @@ -26,15 +26,17 @@ class LinkSetRepository { /** - * LinksDbSetToTargetObjectArray. + * Get list of remote objects information based on a linkSet * * @param iDBObjectSetIterator $oDbObjectSet Db object set + * @param bool $bForce options with force flag will be kept event if they don't be part of the current value of set + * @param array $aInitialOptions * @param string $sTargetClass Target class name * @param string|null $sTargetField Target field * * @return array|null */ - static public function LinksDbSetToTargetObjectArray(iDBObjectSetIterator $oDbObjectSet, string $sTargetClass, string $sTargetField = null): ?array + static public function LinksDbSetToTargetObjectArray(iDBObjectSetIterator $oDbObjectSet, bool $bForce, array &$aInitialOptions, string $sTargetClass, string $sTargetField = null): ?array { try { @@ -52,9 +54,6 @@ class LinkSetRepository $sTargetClass => $aFieldsToLoad, ]); - // Prepare result - $aResult = []; - // Iterate throw objects... $oDbObjectSet->Rewind(); while ($oObject = $oDbObjectSet->Fetch()) { @@ -78,16 +77,19 @@ class LinkSetRepository // Remote key $aObjectData['key'] = $oObject->GetKey(); + // force option + $aObjectData['force'] = $bForce; + // Fill loaded columns... foreach ($aFieldsToLoad as $sField) { $aObjectData[$sField] = $oObject->Get($sField); } // Compute others data - $aResult[] = ObjectRepository::ComputeOthersData($oObject, $sTargetClass, $aObjectData, $aComplementAttributeSpec, $sObjectImageAttCode); + $aInitialOptions[$oObject->GetKey()] = ObjectRepository::ComputeOthersData($oObject, $sTargetClass, $aObjectData, $aComplementAttributeSpec, $sObjectImageAttCode); } - return $aResult; + return $aInitialOptions; } catch (Exception $e) { diff --git a/templates/base/components/input/set/layout.ready.js.twig b/templates/base/components/input/set/layout.ready.js.twig index c040d803e..dd19ed2b1 100644 --- a/templates/base/components/input/set/layout.ready.js.twig +++ b/templates/base/components/input/set/layout.ready.js.twig @@ -19,7 +19,7 @@ let oWidget{{ oUIBlock.GetId() }} = $('#{{ oUIBlock.GetId() }}').selectize({ plugins: { {# PLUGIN update operations #} 'combodo_update_operations' : { - initial: {{ oUIBlock.GetValue()|raw }}, + initial: {{ oUIBlock.GetInitialValue()|raw }}, }, {# PLUGIN combodo auto position #} 'combodo_auto_position' : { @@ -106,8 +106,9 @@ let oWidget{{ oUIBlock.GetId() }} = $('#{{ oUIBlock.GetId() }}').selectize({ // Retrieve current input value let aSelectedItems = me.getValue(); // Filter old options data to keep selected values + // (options with force flag will be kept event if they doesn't be part of the current value) let options = Object.values(me.options); - options = options.filter(item => aSelectedItems.includes(item['{{ oDataProvider.GetDataValueField() }}'])); + options = options.filter(item => (typeof(item.force) !== "undefined" && item.force === true) || aSelectedItems.includes(item['{{ oDataProvider.GetDataValueField() }}'])); // Merge kept and new values options = $.merge(options, res.data.search_data); // Compute groups