diff --git a/CHANGELOG.md b/CHANGELOG.md index 36edd062ed0..7c13e13972d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,9 @@ ## Unreleased +- Asset indexes now remember their previously-selected source path. ([#13147](https://github.com/craftcms/cms/issues/13147)) - Added the `enabledForSite` field for entries queried via GraphQL. ([#13214](https://github.com/craftcms/cms/pull/13214)) +- Added `craft\base\ElementInterface::sourcePath()`. - Improved `craft\helpers\FileHelper::getExtensionByMimeType()` for some ambiguous, web-friendly MIME types. - Fixed a bug where reverting an entry’s content from a revision could omit some Matrix blocks. - Fixed an error that could occur when adding a new site to an entry which contained Matrix blocks, if the same site had been added and removed previously. diff --git a/src/base/Element.php b/src/base/Element.php index 82df5508240..3c553c6a819 100644 --- a/src/base/Element.php +++ b/src/base/Element.php @@ -904,6 +904,14 @@ public static function findSource(string $sourceKey, ?string $context = null): ? return null; } + /** + * @inheritdoc + */ + public static function sourcePath(string $sourceKey, string $stepKey, ?string $context): ?array + { + return null; + } + /** * @inheritdoc * @since 3.5.0 diff --git a/src/base/ElementInterface.php b/src/base/ElementInterface.php index b9d1bb8d8f7..ae707c3dc8f 100644 --- a/src/base/ElementInterface.php +++ b/src/base/ElementInterface.php @@ -308,6 +308,17 @@ public static function sources(string $context): array; */ public static function findSource(string $sourceKey, ?string $context = null): ?array; + /** + * Returns the source path for a given source key, step key, and context. + * + * @param string $sourceKey + * @param string $stepKey + * @param string|null $context + * @return array[]|null + * @since 4.4.12 + */ + public static function sourcePath(string $sourceKey, string $stepKey, ?string $context): ?array; + /** * Returns all of the field layouts associated with elements from the given source. * diff --git a/src/controllers/ElementIndexesController.php b/src/controllers/ElementIndexesController.php index 69e1ec8f310..6047cfc39f5 100644 --- a/src/controllers/ElementIndexesController.php +++ b/src/controllers/ElementIndexesController.php @@ -136,6 +136,24 @@ public function getElementQuery(): ElementQueryInterface return $this->elementQuery; } + /** + * Returns the source path for the given source key, step key, and context. + * + * @since 4.4.12 + */ + public function actionSourcePath(): Response + { + /** @var string|ElementInterface $elementType */ + $elementType = $this->elementType; + $stepKey = $this->request->getRequiredBodyParam('stepKey'); + $sourcePath = $elementType::sourcePath($this->sourceKey, $stepKey, $this->context); + + return $this->asJson([ + 'sourcePath' => $sourcePath, + ]); + } + + /** * Renders and returns an element index container, plus its first batch of elements. * diff --git a/src/elements/Asset.php b/src/elements/Asset.php index d3b8def9035..70b9f52529b 100644 --- a/src/elements/Asset.php +++ b/src/elements/Asset.php @@ -412,6 +412,28 @@ public static function findSource(string $sourceKey, ?string $context = null): ? return null; } + public static function sourcePath(string $sourceKey, string $stepKey, ?string $context): ?array + { + if (!preg_match('/^folder:([\w\-]+)$/', $stepKey, $match)) { + return null; + } + + $folder = Craft::$app->getAssets()->getFolderByUid($match[1]); + + if (!$folder) { + return null; + } + + $path = [$folder->getSourcePathInfo()]; + + while ($parent = $folder->getParent()) { + array_unshift($path, $parent->getSourcePathInfo()); + $folder = $parent; + } + + return $path; + } + /** * @inheritdoc * @since 3.5.0 diff --git a/src/fields/Assets.php b/src/fields/Assets.php index 5ba51596e7f..72a81bdd4ff 100644 --- a/src/fields/Assets.php +++ b/src/fields/Assets.php @@ -728,6 +728,7 @@ protected function inputTemplateVariables(array|ElementQueryInterface $value = n $variables['defaultSourcePath'] = array_map(function(VolumeFolder $folder) { return $folder->getSourcePathInfo(); }, $folders); + $variables['preferStoredSource'] = true; } } diff --git a/src/models/VolumeFolder.php b/src/models/VolumeFolder.php index 59bf0def72b..4eaf8cbd1c6 100644 --- a/src/models/VolumeFolder.php +++ b/src/models/VolumeFolder.php @@ -131,6 +131,7 @@ public function getSourcePathInfo(): ?array // Is this a root folder? if (!$this->parentId) { $info += [ + 'key' => "volume:$volume->uid", 'icon' => 'home', 'label' => Craft::t('app', '{volume} root', [ 'volume' => Html::encode(Craft::t('site', $volume->name)), @@ -141,6 +142,7 @@ public function getSourcePathInfo(): ?array $canRename = $canCreate & $userSession->checkPermission("deleteAssets:$volume->uid"); $info += [ + 'key' => "folder:$this->uid", 'label' => Html::encode($this->name), 'criteria' => [ 'folderId' => $this->id, diff --git a/src/templates/_components/fieldtypes/Assets/input.twig b/src/templates/_components/fieldtypes/Assets/input.twig index c2c4719c0aa..128dae86d8e 100644 --- a/src/templates/_components/fieldtypes/Assets/input.twig +++ b/src/templates/_components/fieldtypes/Assets/input.twig @@ -66,6 +66,7 @@ hideSidebar: hideSidebar ?? false, defaultSource: defaultSource ?? null, defaultSourcePath: defaultSourcePath ?? null, + preferStoredSource: preferStoredSource ?? false, showSourcePath: showSourcePath ?? true, indexSettings: { showFolders: showFolders ?? true, diff --git a/src/web/assets/cp/dist/cp.js b/src/web/assets/cp/dist/cp.js index 775517a03d2..5e5104f9f9a 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__={463:function(){Craft.Accordion=Garnish.Base.extend({$trigger:null,targetSelector:null,_$target:null,init:function(t){var e=this;this.$trigger=$(t),this.$trigger.data("accordion")&&(console.warn("Double-instantiating an accordion trigger on an element"),this.$trigger.data("accordion").destroy()),this.$trigger.data("accordion",this),this.targetSelector=this.$trigger.attr("aria-controls")?"#".concat(this.$trigger.attr("aria-controls")):null,this.targetSelector&&(this._$target=$(this.targetSelector)),this.addListener(this.$trigger,"click","onTriggerClick"),this.addListener(this.$trigger,"keypress",(function(t){var i=t.keyCode;i!==Garnish.SPACE_KEY&&i!==Garnish.RETURN_KEY||(t.preventDefault(),e.onTriggerClick())}))},onTriggerClick:function(){"true"===this.$trigger.attr("aria-expanded")?this.hideTarget(this._$target):this.showTarget(this._$target)},showTarget:function(t){var e=this;if(t&&t.length){this.showTarget._currentHeight=t.height(),t.removeClass("hidden"),this.$trigger.removeClass("collapsed").addClass("expanded").attr("aria-expanded","true");for(var i=0;i .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=$('