mirror of
https://github.com/strapi/strapi.git
synced 2025-11-03 11:25:17 +00:00
Merge pull request #13083 from strapi/ML-folder/folder-tree
Ml folder/folder tree
This commit is contained in:
commit
b4cd89646b
@ -178,7 +178,7 @@ Object {
|
||||
class=" css-1hb7zxy-IndicatorsContainer"
|
||||
>
|
||||
<button
|
||||
class="sc-hGnimi sc-bPVzhA sc-evQXBP iaYaJy cDFemq cOhtSo"
|
||||
class="sc-iuqRDJ sc-hqyYpk sc-imVSVl cuYSdT kzYvLH iYRxVH"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
|
||||
@ -2,7 +2,6 @@ import { useQuery } from 'react-query';
|
||||
import { useNotifyAT } from '@strapi/design-system/LiveRegions';
|
||||
import { useNotification, useQueryParams } from '@strapi/helper-plugin';
|
||||
import { useIntl } from 'react-intl';
|
||||
|
||||
import { axiosInstance, getRequestUrl } from '../utils';
|
||||
|
||||
export const useFolders = ({ enabled = true }) => {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
module.exports = {
|
||||
collectionName: 'folders',
|
||||
collectionName: 'upload_folders',
|
||||
info: {
|
||||
singularName: 'folder',
|
||||
pluralName: 'folders',
|
||||
|
||||
@ -73,4 +73,14 @@ module.exports = {
|
||||
data: deletedFolders,
|
||||
};
|
||||
},
|
||||
|
||||
async getStructure(ctx) {
|
||||
const { getStructure } = getService('folder');
|
||||
|
||||
const structure = await getStructure();
|
||||
|
||||
ctx.body = {
|
||||
data: structure,
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
@ -17,13 +17,13 @@ const validateCreateFolderSchema = yup
|
||||
.required(),
|
||||
parent: yup.strapiID().nullable(),
|
||||
})
|
||||
.noUnknown()
|
||||
.required()
|
||||
.test('is-folder-unique', 'name already taken', async folder => {
|
||||
const { exists } = getService('folder');
|
||||
const doesExist = await exists({ parent: folder.parent || null, name: folder.name });
|
||||
return !doesExist;
|
||||
})
|
||||
.noUnknown()
|
||||
.required();
|
||||
});
|
||||
|
||||
const validateDeleteManyFoldersSchema = yup
|
||||
.object()
|
||||
|
||||
@ -16,7 +16,9 @@ const fileInfoSchema = yup.object({
|
||||
return true;
|
||||
}
|
||||
|
||||
return getService('folder').exists({ id: folderId });
|
||||
const exists = await getService('folder').exists({ id: folderId });
|
||||
|
||||
return exists;
|
||||
}),
|
||||
});
|
||||
|
||||
|
||||
@ -139,5 +139,21 @@ module.exports = {
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: '/folder-structure',
|
||||
handler: 'admin-folder.getStructure',
|
||||
config: {
|
||||
policies: [
|
||||
'admin::isAuthenticatedAdmin',
|
||||
{
|
||||
name: 'admin::hasPermissions',
|
||||
config: {
|
||||
actions: ['plugin::upload.read'],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const uuid = require('uuid').v4;
|
||||
const { keys, sortBy, omit } = require('lodash/fp');
|
||||
const { joinBy } = require('@strapi/utils');
|
||||
|
||||
const folderModel = 'plugin::upload.folder';
|
||||
@ -42,8 +43,41 @@ const exists = async (params = {}) => {
|
||||
return count > 0;
|
||||
};
|
||||
|
||||
const getStructure = async () => {
|
||||
const joinTable = strapi.db.metadata.get('plugin::upload.folder').attributes.parent.joinTable;
|
||||
const qb = strapi.db.queryBuilder('plugin::upload.folder');
|
||||
const alias = qb.getAlias();
|
||||
const folders = await qb
|
||||
.select(['id', 'name', `${alias}.${joinTable.inverseJoinColumn.name} as parent`])
|
||||
.join({
|
||||
alias,
|
||||
referencedTable: joinTable.name,
|
||||
referencedColumn: joinTable.joinColumn.name,
|
||||
rootColumn: joinTable.joinColumn.referencedColumn,
|
||||
rootTable: qb.alias,
|
||||
})
|
||||
.execute({ mapResults: false });
|
||||
|
||||
const folderMap = folders.reduce((map, f) => {
|
||||
f.children = [];
|
||||
map[f.id] = f;
|
||||
return map;
|
||||
}, {});
|
||||
folderMap.null = { children: [] };
|
||||
|
||||
for (const id of keys(omit('null', folderMap))) {
|
||||
const parentId = folderMap[id].parent;
|
||||
folderMap[parentId].children.push(folderMap[id]);
|
||||
folderMap[parentId].children = sortBy('name', folderMap[parentId].children);
|
||||
delete folderMap[id].parent;
|
||||
}
|
||||
|
||||
return folderMap.null.children;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
exists,
|
||||
deleteByIds,
|
||||
setPathAndUID,
|
||||
getStructure,
|
||||
};
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
'use strict';
|
||||
|
||||
const { createTestBuilder } = require('../../../../../test/helpers/builder');
|
||||
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
|
||||
const { createAuthRequest } = require('../../../../../test/helpers/request');
|
||||
|
||||
let strapi;
|
||||
let rq;
|
||||
let data = {
|
||||
folders: [],
|
||||
};
|
||||
|
||||
const createFolder = async (name, parent = null) => {
|
||||
const res = await rq({
|
||||
method: 'POST',
|
||||
url: '/upload/folders',
|
||||
body: { name, parent },
|
||||
});
|
||||
return res.body.data;
|
||||
};
|
||||
|
||||
describe('Folder structure', () => {
|
||||
const builder = createTestBuilder();
|
||||
|
||||
beforeAll(async () => {
|
||||
strapi = await createStrapiInstance();
|
||||
rq = await createAuthRequest({ strapi });
|
||||
|
||||
const rootFolder1 = await createFolder('folder1');
|
||||
const rootFolder2 = await createFolder('folder2');
|
||||
const nestedFolder1a = await createFolder('folder1A', rootFolder1.id);
|
||||
const nestedFolder1b = await createFolder('folder1B', rootFolder1.id);
|
||||
const nestedFolder1a1 = await createFolder('folder1A1', nestedFolder1a.id);
|
||||
data.folders.push(rootFolder1, rootFolder2, nestedFolder1a, nestedFolder1b, nestedFolder1a1);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await rq({
|
||||
method: 'POST',
|
||||
url: '/upload/folders/batch-delete',
|
||||
body: {
|
||||
ids: data.folders.map(f => f.id),
|
||||
},
|
||||
});
|
||||
|
||||
await strapi.destroy();
|
||||
await builder.cleanup();
|
||||
});
|
||||
|
||||
describe('Read', () => {
|
||||
test('get structure', async () => {
|
||||
const res = await rq({
|
||||
method: 'GET',
|
||||
url: '/upload/folder-structure',
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.body).toMatchObject({
|
||||
data: [
|
||||
{
|
||||
children: [
|
||||
{
|
||||
children: [{ children: [], id: expect.anything(), name: 'folder1A1' }],
|
||||
id: expect.anything(),
|
||||
name: 'folder1A',
|
||||
},
|
||||
{ children: [], id: expect.anything(), name: 'folder1B' },
|
||||
],
|
||||
id: expect.anything(),
|
||||
name: 'folder1',
|
||||
},
|
||||
{ children: [], id: expect.anything(), name: 'folder2' },
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -31,6 +31,14 @@ describe('Folder', () => {
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await rq({
|
||||
method: 'POST',
|
||||
url: '/upload/folders/batch-delete',
|
||||
body: {
|
||||
ids: data.folders.map(f => f.id),
|
||||
},
|
||||
});
|
||||
|
||||
await strapi.destroy();
|
||||
await builder.cleanup();
|
||||
});
|
||||
|
||||
@ -762,7 +762,7 @@ describe('Admin | containers | RoleCreatePage', () => {
|
||||
border: 1px solid #4945ff;
|
||||
}
|
||||
|
||||
.c44:hover:not([aria-disabled='true']) .sc-hrJiTw {
|
||||
.c44:hover:not([aria-disabled='true']) .sc-jcneYm {
|
||||
color: #271fe0;
|
||||
}
|
||||
|
||||
|
||||
@ -831,7 +831,7 @@ describe('Admin | containers | RoleEditPage', () => {
|
||||
border: 1px solid #4945ff;
|
||||
}
|
||||
|
||||
.c49:hover:not([aria-disabled='true']) .sc-gtPNqn {
|
||||
.c49:hover:not([aria-disabled='true']) .sc-kGrBqp {
|
||||
color: #271fe0;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user