From 2947da192efe8a78bd617565df0239baba46f931 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Wed, 14 Dec 2022 16:59:11 -0800 Subject: [PATCH] Fixed #12459 --- CHANGELOG.md | 4 ++++ src/models/FieldLayout.php | 23 +++++++++++++++++++ src/web/assets/cp/dist/cp.js | 2 +- src/web/assets/cp/dist/cp.js.map | 2 +- .../assets/cp/src/js/FieldLayoutDesigner.js | 19 +++++++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fabb966b04c..46d8982ebf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Release Notes for Craft CMS 4 +## Unreleased + +- Fixed a bug where deleting a field layout tab could result in duplicated tabs. ([#12459](https://github.com/craftcms/cms/issues/12459)) + ## 4.3.5 - 2022-12-13 - Fixed a bug where entry tab contents could remain visible when switching to other tabs, after changing the entry type. diff --git a/src/models/FieldLayout.php b/src/models/FieldLayout.php index fac49b9fb48..40721f234df 100644 --- a/src/models/FieldLayout.php +++ b/src/models/FieldLayout.php @@ -306,6 +306,7 @@ public function setTabs(array $tabs): void $this->_tabs = []; $index = 0; + $elementUids = []; foreach ($tabs as $tab) { if (is_array($tab)) { @@ -315,6 +316,28 @@ public function setTabs(array $tabs): void } else { $tab->setLayout($this); } + + // Ensure there aren't any layout element UUID conflicts + $layoutElements = $tab->getElements(); + $filteredLayoutElements = array_filter($layoutElements, function(FieldLayoutElement $layoutElement) use (&$elementUids) { + if (!isset($layoutElement->uid)) { + return true; + } + if (isset($elementUids[$layoutElement->uid])) { + return false; + } + $elementUids[$layoutElement->uid] = true; + return true; + }); + + if (empty($filteredLayoutElements)) { + continue; + } + + if (count($filteredLayoutElements) !== count($layoutElements)) { + $tab->setElements($filteredLayoutElements); + } + $tab->sortOrder = ++$index; $this->_tabs[] = $tab; } diff --git a/src/web/assets/cp/dist/cp.js b/src/web/assets/cp/dist/cp.js index 826df385f56..2eea6c2e0e6 100644 --- a/src/web/assets/cp/dist/cp.js +++ b/src/web/assets/cp/dist/cp.js @@ -1,2 +1,2 @@ -(function(){var __webpack_modules__={3839:function(){Craft.AddressesInput=Garnish.Base.extend({$container:null,$addBtn:null,$addBtnItem:null,$cards:null,init:function(t,e){var i=this;this.$container=$(t),this.setSettings(e,Craft.AddressesInput.defaults),this.$container.data("addresses")&&(console.warn("Double-instantiating an address input on an element"),this.$container.data("addresses").destroy()),this.$container.data("addresses",this),this.$addBtn=this.$container.find(".address-cards__add-btn"),this.$addBtnItem=this.$addBtn.closest("li"),this.$cards=this.$container.find("> .address-card");for(var s=0;s=this.settings.maxItems)){var e=$(t).appendTo(this.$tbody),i=e.find(".delete");this.settings.sortable&&this.sorter.addItems(e),this.$deleteBtns=this.$deleteBtns.add(i),this.addListener(i,"click","handleDeleteBtnClick"),this.totalItems++,this.updateUI()}},reorderItems:function(){var t=this;if(this.settings.sortable){for(var e=[],i=0;i=this.settings.maxItems?$(this.settings.newItemBtnSelector).addClass("hidden"):$(this.settings.newItemBtnSelector).removeClass("hidden"))}},{defaults:{tableSelector:null,noItemsSelector:null,newItemBtnSelector:null,idAttribute:"data-id",nameAttribute:"data-name",sortable:!1,allowDeleteAll:!0,minItems:0,maxItems:null,reorderAction:null,deleteAction:null,reorderSuccessMessage:Craft.t("app","New order saved."),reorderFailMessage:Craft.t("app","Couldn’t save new order."),confirmDeleteMessage:Craft.t("app","Are you sure you want to delete “{name}”?"),deleteSuccessMessage:Craft.t("app","“{name}” deleted."),deleteFailMessage:Craft.t("app","Couldn’t delete “{name}”."),onReorderItems:$.noop,onDeleteItem:$.noop}})},6872:function(){Craft.AssetImageEditor=Garnish.Modal.extend({$body:null,$footer:null,$imageTools:null,$buttons:null,$cancelBtn:null,$replaceBtn:null,$saveBtn:null,$focalPointBtn:null,$editorContainer:null,$straighten:null,$croppingCanvas:null,$spinner:null,$constraintContainer:null,$constraintRadioInputs:null,$customConstraints:null,canvas:null,image:null,viewport:null,focalPoint:null,grid:null,croppingCanvas:null,clipper:null,croppingRectangle:null,cropperHandles:null,cropperGrid:null,croppingShade:null,imageStraightenAngle:0,viewportRotation:0,originalWidth:0,originalHeight:0,imageVerticeCoords:null,zoomRatio:1,animationInProgress:!1,currentView:"",assetId:null,cacheBust:null,draggingCropper:!1,scalingCropper:!1,draggingFocal:!1,previousMouseX:0,previousMouseY:0,shiftKeyHeld:!1,editorHeight:0,editorWidth:0,cropperState:!1,scaleFactor:1,flipData:{},focalPointState:!1,maxImageSize:null,lastLoadedDimensions:null,imageIsLoading:!1,mouseMoveEvent:null,croppingConstraint:!1,constraintOrientation:"landscape",showingCustomConstraint:!1,saving:!1,renderImage:null,renderCropper:null,_queue:null,init:function(t,e){var i=this;this._queue=new Craft.Queue,this.cacheBust=Date.now(),this.setSettings(e,Craft.AssetImageEditor.defaults),null===this.settings.allowDegreeFractions&&(this.settings.allowDegreeFractions=Craft.isImagick),Garnish.prefersReducedMotion()&&(this.settings.animationDuration=1),this.assetId=t,this.flipData={x:0,y:0},this.$container=$('').appendTo(Garnish.$bod),this.$body=$('
').appendTo(this.$container),this.$footer=$('