From 3cad4da23fd71e1b14a9693c821e5acc2a8d17f8 Mon Sep 17 00:00:00 2001 From: brandonkelly Date: Mon, 4 Apr 2022 22:29:01 -0700 Subject: [PATCH] Make it possible to disable native sources Resolves #10676 --- CHANGELOG-WIP.md | 1 + CHANGELOG.md | 1 + .../ElementIndexSettingsController.php | 15 ++- src/services/ElementSources.php | 18 ++- src/templates/_elements/sources.twig | 9 +- src/templates/_layouts/elementindex.twig | 2 +- src/web/assets/cp/dist/cp.js | 2 +- src/web/assets/cp/dist/cp.js.map | 2 +- src/web/assets/cp/src/js/BaseElementIndex.js | 59 +++++---- .../assets/cp/src/js/CustomizeSourcesModal.js | 122 +++++++++++------- 10 files changed, 147 insertions(+), 84 deletions(-) diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index 2705139c0df..eca91726155 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -8,6 +8,7 @@ - Element slideouts now support provisional drafts and autosaving, for element types that support them. ([#10467](https://github.com/craftcms/cms/pull/10467)) - Element indexes can now be filtered by element attributes and custom field values. ([#9192](https://github.com/craftcms/cms/discussions/9192), [#9450](https://github.com/craftcms/cms/discussions/9450), [#9462](https://github.com/craftcms/cms/discussions/9462), [#9483](https://github.com/craftcms/cms/discussions/9483)) - Admins can now create custom element sources from the Customize Sources modal. ([#8423](https://github.com/craftcms/cms/discussions/8423)) +- It’s now possible to disable native element sources from the Customize Sources modal. ([#10676](https://github.com/craftcms/cms/discussions/10676)) - Field layout tabs, fields, and UI elements can now be conditionally shown based on properties of the current user and/or element being edited. ([#8099](https://github.com/craftcms/cms/discussions/8099), [#8154](https://github.com/craftcms/cms/discussions/8154)) - Assets, Entries, and Users fields have new condition settings that can be used to further limit which elements should be relatable, beyond the existing field settings. ([#10393](https://github.com/craftcms/cms/pull/10393)) - Assets, Entries, and Users fields have new “Min Relations” settings, and their former “Limit” settings have been renamed to “Max Relations”. ([#8621](https://github.com/craftcms/cms/discussions/8621)) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2bc25cacbf0..45f6bbc864e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased ### Added +- It’s now possible to disable native element sources from the Customize Sources modal. ([#10676](https://github.com/craftcms/cms/discussions/10676)) - GraphQL schemas now include settings that determine which sites elements can be queried from. ([#10610](https://github.com/craftcms/cms/issues/10610)) - Added `craft\base\FsInterface::read()`. - Added `craft\base\FsInterface::write()`. diff --git a/src/controllers/ElementIndexSettingsController.php b/src/controllers/ElementIndexSettingsController.php index 6b1f0b986ae..6ddc4bd72c2 100644 --- a/src/controllers/ElementIndexSettingsController.php +++ b/src/controllers/ElementIndexSettingsController.php @@ -55,7 +55,7 @@ public function actionGetCustomizeSourcesModalData(): Response // Get the source info $sourcesService = Craft::$app->getElementSources(); - $sources = $sourcesService->getSources($elementType, ElementSources::CONTEXT_INDEX); + $sources = $sourcesService->getSources($elementType, ElementSources::CONTEXT_INDEX, true); foreach ($sources as &$source) { if ($source['type'] === ElementSources::TYPE_HEADING) { @@ -153,6 +153,7 @@ public function actionSaveCustomizeSourcesModalSettings(): Response $sourceOrder = $this->request->getBodyParam('sourceOrder', []); $sourceSettings = $this->request->getBodyParam('sources', []); $newSourceConfigs = []; + $disabledSourceKeys = []; // Normalize to the way it's stored in the DB foreach ($sourceOrder as $source) { @@ -182,9 +183,17 @@ public function actionSaveCustomizeSourcesModalSettings(): Response if (isset($postedSettings['userGroups']) && $postedSettings['userGroups'] !== '*') { $sourceConfig['userGroups'] = is_array($postedSettings['userGroups']) ? $postedSettings['userGroups'] : false; } + } elseif (isset($postedSettings['enabled'])) { + $sourceConfig['disabled'] = !$postedSettings['enabled']; + if ($sourceConfig['disabled']) { + $disabledSourceKeys[] = $source['key']; + } } } elseif (isset($oldSourceConfigs[$source['key']])) { $sourceConfig += $oldSourceConfigs[$source['key']]; + if (!empty($sourceConfig['disabled'])) { + $disabledSourceKeys[] = $source['key']; + } } elseif ($isCustom) { // Ignore it continue; @@ -195,6 +204,8 @@ public function actionSaveCustomizeSourcesModalSettings(): Response } $projectConfig->set(ProjectConfig::PATH_ELEMENT_SOURCES . ".$elementType", $newSourceConfigs); - return $this->asSuccess(); + return $this->asSuccess(data: [ + 'disabledSourceKeys' => $disabledSourceKeys, + ]); } } diff --git a/src/services/ElementSources.php b/src/services/ElementSources.php index 3812c27d9c6..4f45ee26f3b 100644 --- a/src/services/ElementSources.php +++ b/src/services/ElementSources.php @@ -68,9 +68,10 @@ public static function filterExtraHeadings(array $sources): array * @param string $elementType The element type class * @phpstan-param class-string $elementType * @param string $context The context + * @param bool $withDisabled Whether disabled sources should be included * @return array[] */ - public function getSources(string $elementType, string $context = self::CONTEXT_INDEX): array + public function getSources(string $elementType, string $context = self::CONTEXT_INDEX, bool $withDisabled = false): array { $nativeSources = $this->_nativeSources($elementType, $context); $sourceConfigs = $this->_sourceConfigs($elementType); @@ -83,8 +84,12 @@ public function getSources(string $elementType, string $context = self::CONTEXT_ foreach ($sourceConfigs as $source) { if ($source['type'] === self::TYPE_NATIVE) { if (isset($indexedNativeSources[$source['key']])) { - $sources[] = $source + $indexedNativeSources[$source['key']]; - $nativeSourceKeys[$source['key']] = true; + if ($withDisabled || !($source['disabled'] ?? false)) { + $sources[] = $source + $indexedNativeSources[$source['key']]; + $nativeSourceKeys[$source['key']] = true; + } else { + unset($indexedNativeSources[$source['key']]); + } } } else { if ($source['type'] === self::TYPE_CUSTOM && !$this->_showCustomSource($source)) { @@ -95,7 +100,12 @@ public function getSources(string $elementType, string $context = self::CONTEXT_ } // Make sure all native sources are accounted for - $missingSources = array_filter($nativeSources, fn($s) => $s['type'] === self::TYPE_NATIVE && !isset($nativeSourceKeys[$s['key']])); + $missingSources = array_filter($nativeSources, fn($s) => ( + $s['type'] === self::TYPE_NATIVE && + isset($indexedNativeSources[$s['key']]) && + !isset($nativeSourceKeys[$s['key']]) + )); + if (!empty($missingSources)) { if (!empty($sources)) { $sources[] = [ diff --git a/src/templates/_elements/sources.twig b/src/templates/_elements/sources.twig index a548f765c78..4327fe1520f 100644 --- a/src/templates/_elements/sources.twig +++ b/src/templates/_elements/sources.twig @@ -18,6 +18,7 @@ : false, sites: (source.sites ?? false) ? source.sites|join(',') : false, 'override-status': (source.criteria.status ?? false) ? true : false, + disabled: source.disabled ?? false, }|merge(source.data ?? {}), html: sourceInnerHtml(source) }) }} @@ -49,7 +50,11 @@
  • {{ source.heading|t('site') }}
  • {% else %} {% set key = source.keyPath ?? (keyPrefix ~ source.key) %} -
  • + {% tag 'li' with { + class: [ + (source.disabled ?? false) ? 'hidden' : null, + ]|filter, + } %} {{ sourceLink(key, source, not keyPrefix, elementType) }} {% if source.nested is defined and source.nested is not empty %} @@ -58,7 +63,7 @@ sources: source.nested } %} {% endif %} -
  • + {% endtag %} {% endif %} {% endfor %} diff --git a/src/templates/_layouts/elementindex.twig b/src/templates/_layouts/elementindex.twig index a89f26701da..030628c6c60 100644 --- a/src/templates/_layouts/elementindex.twig +++ b/src/templates/_layouts/elementindex.twig @@ -7,7 +7,7 @@ {% exit 404 %} {% endif %} -{% set sources = craft.app.elementSources.getSources(elementType) %} +{% set sources = craft.app.elementSources.getSources(elementType, 'index', true) %} {% set showSiteMenu = (craft.app.getIsMultiSite() ? (showSiteMenu ?? 'auto') : false) %} {% if showSiteMenu == 'auto' %} diff --git a/src/web/assets/cp/dist/cp.js b/src/web/assets/cp/dist/cp.js index d0ebe22f06f..5560e42e1d6 100644 --- a/src/web/assets/cp/dist/cp.js +++ b/src/web/assets/cp/dist/cp.js @@ -1,3 +1,3 @@ /*! For license information please see cp.js.LICENSE.txt */ -(function(){var __webpack_modules__={3839:function(){Craft.AddressesInput=Garnish.Base.extend({$container:null,$addBtn: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("> .btn.add"),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,$editorContainer:null,$straighten:null,$croppingCanvas:null,$spinner:null,canvas:null,image:null,viewport:null,focalPoint:null,grid:null,croppingCanvas:null,clipper:null,croppingRectangle:null,cropperHandles:null,cropperGrid:null,croppingShade:null,croppingAreaText: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,init:function(t,e){var i=this;this.cacheBust=Date.now(),this.setSettings(e,Craft.AssetImageEditor.defaults),null===this.settings.allowDegreeFractions&&(this.settings.allowDegreeFractions=Craft.isImagick),this.assetId=t,this.flipData={x:0,y:0},this.$container=$('').appendTo(Garnish.$bod),this.$body=$('
    ').appendTo(this.$container),this.$footer=$('