Skip to content

Commit

Permalink
Merge pull request #13238 from craftcms/feature/source-path-keys--v4
Browse files Browse the repository at this point in the history
Remember the selected source path (Craft 4)
  • Loading branch information
brandonkelly authored May 24, 2023
2 parents 4f55d43 + 6a188a5 commit 8f8f78c
Show file tree
Hide file tree
Showing 12 changed files with 154 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
8 changes: 8 additions & 0 deletions src/base/Element.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions src/base/ElementInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
18 changes: 18 additions & 0 deletions src/controllers/ElementIndexesController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down
22 changes: 22 additions & 0 deletions src/elements/Asset.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/fields/Assets.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/models/VolumeFolder.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)),
Expand All @@ -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,
Expand Down
1 change: 1 addition & 0 deletions src/templates/_components/fieldtypes/Assets/input.twig
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
hideSidebar: hideSidebar ?? false,
defaultSource: defaultSource ?? null,
defaultSourcePath: defaultSourcePath ?? null,
preferStoredSource: preferStoredSource ?? false,
showSourcePath: showSourcePath ?? true,
indexSettings: {
showFolders: showFolders ?? true,
Expand Down
2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/web/assets/cp/dist/cp.js.map

Large diffs are not rendered by default.

95 changes: 85 additions & 10 deletions src/web/assets/cp/src/js/BaseElementIndex.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ Craft.BaseElementIndex = Garnish.Base.extend(
siteMenu: null,
siteId: null,

_sourcePath: null,
sourcePaths: null,
$sourcePathOuterContainer: null,
$sourcePathInnerContainer: null,
$sourcePathOverflowBtnContainer: null,
Expand Down Expand Up @@ -114,6 +114,8 @@ Craft.BaseElementIndex = Garnish.Base.extend(
this.$container = $container;
this.setSettings(settings, Craft.BaseElementIndex.defaults);

this.sourcePaths = {};

// Define an ID prefix that can be used for dynamically created elements
// ---------------------------------------------------------------------

Expand Down Expand Up @@ -346,15 +348,68 @@ Craft.BaseElementIndex = Garnish.Base.extend(
// Select the initial source + source path
// ---------------------------------------------------------------------

// Grab the localStorage step key up front, so we don's lose track of it when the default source's default
// source path is selected
const stepKey = this.getSelectedSourceState('sourcePathStep');

this.selectDefaultSource();

const sourcePath = this.getDefaultSourcePath();
if (sourcePath !== null) {

// If no default source path was explicitly configured, use the localStorage key
if (!sourcePath && stepKey) {
this.loadSourcePathByKey(stepKey).then((sourcePath) => {
if (sourcePath) {
// Filter out any source path steps that are above the source's root
const lastSourceKey = this.sourceKey.split('/').slice(-1)[0];
const sourceRootIndex = sourcePath.findIndex(
(p) => p.key === lastSourceKey
);
if (sourceRootIndex !== -1) {
this.sourcePath = sourcePath.slice(sourceRootIndex);
}
}
this.afterSetInitialSource(queryParams);
});
} else {
this.sourcePath = sourcePath;
this.afterSetInitialSource(queryParams);
}
},

afterInit: function () {
this.onAfterInit();
},

loadSourcePathByKey: function (stepKey) {
return new Promise((resolve, reject) => {
// If the step key is equal to the current source key, then it represents the root. No source path needed.
if (stepKey === this.sourceKey) {
resolve([]);
return;
}

const params = this.getViewParams();
params.stepKey = stepKey;

Craft.sendActionRequest('POST', 'element-indexes/source-path', {
data: params,
})
.then(({data}) => {
resolve(data.sourcePath);
})
.catch(reject);
});
},

afterSetInitialSource: function (queryParams) {
// Resize handler
// ---------------------------------------------------------------------

if (this.settings.context === 'index') {
this.addListener(Garnish.$win, 'resize', 'handleResize');
}

this.handleResize();

// Respect initial search
Expand Down Expand Up @@ -388,10 +443,6 @@ Craft.BaseElementIndex = Garnish.Base.extend(
this.updateElements(true);
},

afterInit: function () {
this.onAfterInit();
},

handleResize: function () {
if (this.sourcePath.length && this.settings.showSourcePath) {
this._updateSourcePathVisibility();
Expand Down Expand Up @@ -561,6 +612,16 @@ Craft.BaseElementIndex = Garnish.Base.extend(
},

getDefaultSourceKey: function () {
if (
this.settings.preferStoredSource &&
this.instanceState.selectedSource
) {
// Discard the defaults and go with localStorage
this.settings.defaultSource = null;
this.settings.defaultSourcePath = null;
return this.instanceState.selectedSource;
}

let sourceKey = null;

if (this.settings.defaultSource) {
Expand Down Expand Up @@ -629,14 +690,15 @@ Craft.BaseElementIndex = Garnish.Base.extend(
* @returns {Object[]}
*/
get sourcePath() {
return this._sourcePath || [];
return this.sourcePaths[this.sourceKey] || [];
},

/**
* @param {Object[]|null} sourcePath
*/
set sourcePath(sourcePath) {
this._sourcePath = sourcePath && sourcePath.length ? sourcePath : null;
this.sourcePaths[this.sourceKey] =
sourcePath && sourcePath.length ? sourcePath : null;

if (this.$sourcePathOuterContainer) {
this.$sourcePathOuterContainer.remove();
Expand All @@ -646,7 +708,7 @@ Craft.BaseElementIndex = Garnish.Base.extend(
this.$sourcePathActionsBtn = null;
}

if (this._sourcePath && this.settings.showSourcePath) {
if (this.sourcePaths[this.sourceKey] && this.settings.showSourcePath) {
const actions = this.getSourcePathActions();

this.$sourcePathOuterContainer = $('<div/>', {
Expand Down Expand Up @@ -827,6 +889,16 @@ Craft.BaseElementIndex = Garnish.Base.extend(
}
}

// Store the source path
this.setSelecetedSourceState(
'sourcePathStep',
(this.sourcePaths[this.sourceKey]
? this.sourcePaths[this.sourceKey][
this.sourcePaths[this.sourceKey].length - 1
].key
: null) || null
);

this.onSourcePathChange();
},

Expand Down Expand Up @@ -1887,7 +1959,9 @@ Craft.BaseElementIndex = Garnish.Base.extend(
this.updateViewMenu();
this.updateFilterBtn();

this.sourcePath = this.$source.data('default-source-path');
this.sourcePath =
this.sourcePaths[this.sourceKey] ||
this.$source.data('default-source-path');

this.onSelectSource();

Expand Down Expand Up @@ -3205,6 +3279,7 @@ Craft.BaseElementIndex = Garnish.Base.extend(
defaultSiteId: null,
defaultSource: null,
defaultSourcePath: null,
preferStoredSource: false,
showSourcePath: true,
canHaveDrafts: false,

Expand Down
2 changes: 2 additions & 0 deletions src/web/assets/cp/src/js/BaseElementSelectorModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,7 @@ Craft.BaseElementSelectorModal = Garnish.Modal.extend(
defaultSiteId: this.settings.defaultSiteId,
defaultSource: this.settings.defaultSource,
defaultSourcePath: this.settings.defaultSourcePath,
preferStoredSource: this.settings.preferStoredSource,
showSourcePath: this.settings.showSourcePath,
},
this.settings.indexSettings
Expand Down Expand Up @@ -498,6 +499,7 @@ Craft.BaseElementSelectorModal = Garnish.Modal.extend(
defaultSiteId: null,
defaultSource: null,
defaultSourcePath: null,
preferStoredSource: false,
showSourcePath: true,
bodyAction: 'element-selector-modals/body',
indexSettings: {},
Expand Down

0 comments on commit 8f8f78c

Please sign in to comment.