From e0e2084422e306aa2eab4051383ad3aee2cdecce Mon Sep 17 00:00:00 2001
From: Alexandre Bodin
Date: Mon, 6 Mar 2023 21:46:45 +0100
Subject: [PATCH 1/8] Move RBAC into CE
---
.../core/admin/ee/server/controllers/index.js | 6 +-
.../admin/ee/server/controllers/permission.js | 21 -
.../core/admin/ee/server/controllers/role.js | 39 -
.../core/admin/ee/server/controllers/user.js | 36 +-
packages/core/admin/ee/server/routes/index.js | 96 +-
.../admin/ee/server/validation/permission.js | 8 -
.../core/admin/ee/server/validation/role.js | 48 +-
packages/core/admin/server/bootstrap.js | 1 -
.../admin/server/controllers/permission.js | 6 +-
.../core/admin/server/controllers/role.js | 93 +-
packages/core/admin/server/domain/role.js | 29 -
packages/core/admin/server/routes/roles.js | 48 +
.../server/services/permission/queries.js | 75 +-
.../admin/server/tests/admin-auth.test.api.js | 18 +-
.../server/tests/admin-permission.test.api.js | 2120 +++++++----------
.../admin-permissions-conditions.test.api.js | 312 ++-
.../admin/server/tests/admin-role.test.api.js | 1102 ++++-----
.../admin/server/tests/admin-user.test.api.js | 13 +-
.../admin/server/validation/permission.js | 84 +-
packages/core/admin/server/validation/role.js | 44 +
20 files changed, 1723 insertions(+), 2476 deletions(-)
delete mode 100644 packages/core/admin/ee/server/controllers/permission.js
delete mode 100644 packages/core/admin/ee/server/validation/permission.js
delete mode 100644 packages/core/admin/server/domain/role.js
diff --git a/packages/core/admin/ee/server/controllers/index.js b/packages/core/admin/ee/server/controllers/index.js
index 26a0bb79ad0..fd8d5d1e22e 100644
--- a/packages/core/admin/ee/server/controllers/index.js
+++ b/packages/core/admin/ee/server/controllers/index.js
@@ -2,9 +2,9 @@
module.exports = {
authentication: require('./authentication'),
- permission: require('./permission'),
- role: require('./role'),
- user: require('./user'),
+ // permission: require('./permission'),
+ // role: require('./role'),
+ // user: require('./user'),
auditLogs: require('./audit-logs'),
admin: require('./admin'),
};
diff --git a/packages/core/admin/ee/server/controllers/permission.js b/packages/core/admin/ee/server/controllers/permission.js
deleted file mode 100644
index f000e1d53e3..00000000000
--- a/packages/core/admin/ee/server/controllers/permission.js
+++ /dev/null
@@ -1,21 +0,0 @@
-'use strict';
-
-const { getService } = require('../../../server/utils');
-const { formatConditions } = require('../../../server/controllers/formatters');
-
-module.exports = {
- async getAll(ctx) {
- const { sectionsBuilder, actionProvider, conditionProvider } = getService('permission');
-
- const actions = actionProvider.values();
- const conditions = conditionProvider.values();
- const sections = await sectionsBuilder.build(actions);
-
- ctx.body = {
- data: {
- conditions: formatConditions(conditions),
- sections,
- },
- };
- },
-};
diff --git a/packages/core/admin/ee/server/controllers/role.js b/packages/core/admin/ee/server/controllers/role.js
index b025914dd02..67ace666ed2 100644
--- a/packages/core/admin/ee/server/controllers/role.js
+++ b/packages/core/admin/ee/server/controllers/role.js
@@ -1,14 +1,11 @@
'use strict';
-const { ApplicationError } = require('@strapi/utils').errors;
const {
validateRoleCreateInput,
validateRoleDeleteInput,
validateRolesDeleteInput,
} = require('../validation/role');
const { getService } = require('../../../server/utils');
-const { validatedUpdatePermissionsInput } = require('../validation/permission');
-const { SUPER_ADMIN_CODE } = require('../../../server/services/constants');
module.exports = {
/**
@@ -64,40 +61,4 @@ module.exports = {
data: sanitizedRoles,
});
},
-
- /**
- * Updates the permissions assigned to a role
- * @param {KoaContext} ctx - koa context
- */
- async updatePermissions(ctx) {
- const { id } = ctx.params;
- const { body: input } = ctx.request;
-
- const roleService = getService('role');
- const permissionService = getService('permission');
-
- const role = await roleService.findOne({ id });
-
- if (!role) {
- return ctx.notFound('role.notFound');
- }
-
- if (role.code === SUPER_ADMIN_CODE) {
- throw new ApplicationError("Super admin permissions can't be edited.");
- }
-
- await validatedUpdatePermissionsInput(input);
-
- if (!role) {
- return ctx.notFound('role.notFound');
- }
-
- const permissions = await roleService.assignPermissions(role.id, input.permissions);
-
- const sanitizedPermissions = permissions.map(permissionService.sanitizePermission);
-
- return ctx.send({
- data: sanitizedPermissions,
- });
- },
};
diff --git a/packages/core/admin/ee/server/controllers/user.js b/packages/core/admin/ee/server/controllers/user.js
index 902ff4cb42f..4bd2014951b 100644
--- a/packages/core/admin/ee/server/controllers/user.js
+++ b/packages/core/admin/ee/server/controllers/user.js
@@ -6,10 +6,7 @@ const _ = require('lodash');
const { pick, isNil } = require('lodash/fp');
const { ApplicationError, ForbiddenError } = require('@strapi/utils').errors;
const { validateUserCreationInput } = require('../validation/user');
-const {
- validateUserUpdateInput,
- validateUsersDeleteInput,
-} = require('../../../server/validation/user');
+const { validateUserUpdateInput } = require('../../../server/validation/user');
const { getService } = require('../../../server/utils');
const pickUserCreationAttributes = pick(['firstname', 'lastname', 'email', 'roles']);
@@ -98,35 +95,4 @@ module.exports = {
data: getService('user').sanitizeUser(updatedUser),
};
},
-
- async deleteOne(ctx) {
- const { id } = ctx.params;
-
- const deletedUser = await getService('user').deleteById(id);
-
- if (!deletedUser) {
- return ctx.notFound('User not found');
- }
-
- return ctx.deleted({
- data: getService('user').sanitizeUser(deletedUser),
- });
- },
-
- /**
- * Delete several users
- * @param {KoaContext} ctx - koa context
- */
- async deleteMany(ctx) {
- const { body } = ctx.request;
- await validateUsersDeleteInput(body);
-
- const users = await getService('user').deleteByIds(body.ids);
-
- const sanitizedUsers = users.map(getService('user').sanitizeUser);
-
- return ctx.deleted({
- data: sanitizedUsers,
- });
- },
};
diff --git a/packages/core/admin/ee/server/routes/index.js b/packages/core/admin/ee/server/routes/index.js
index a684354b459..857423dd89b 100644
--- a/packages/core/admin/ee/server/routes/index.js
+++ b/packages/core/admin/ee/server/routes/index.js
@@ -11,54 +11,54 @@ const enableFeatureMiddleware = (featureName) => (ctx, next) => {
};
module.exports = [
- {
- method: 'POST',
- path: '/roles',
- handler: 'role.create',
- config: {
- policies: [
- 'admin::isAuthenticatedAdmin',
- {
- name: 'admin::hasPermissions',
- config: {
- actions: ['admin::roles.create'],
- },
- },
- ],
- },
- },
- {
- method: 'DELETE',
- path: '/roles/:id',
- handler: 'role.deleteOne',
- config: {
- policies: [
- 'admin::isAuthenticatedAdmin',
- {
- name: 'admin::hasPermissions',
- config: {
- actions: ['admin::roles.delete'],
- },
- },
- ],
- },
- },
- {
- method: 'POST',
- path: '/roles/batch-delete',
- handler: 'role.deleteMany',
- config: {
- policies: [
- 'admin::isAuthenticatedAdmin',
- {
- name: 'admin::hasPermissions',
- config: {
- actions: ['admin::roles.delete'],
- },
- },
- ],
- },
- },
+ // {
+ // method: 'POST',
+ // path: '/roles',
+ // handler: 'role.create',
+ // config: {
+ // policies: [
+ // 'admin::isAuthenticatedAdmin',
+ // {
+ // name: 'admin::hasPermissions',
+ // config: {
+ // actions: ['admin::roles.create'],
+ // },
+ // },
+ // ],
+ // },
+ // },
+ // {
+ // method: 'DELETE',
+ // path: '/roles/:id',
+ // handler: 'role.deleteOne',
+ // config: {
+ // policies: [
+ // 'admin::isAuthenticatedAdmin',
+ // {
+ // name: 'admin::hasPermissions',
+ // config: {
+ // actions: ['admin::roles.delete'],
+ // },
+ // },
+ // ],
+ // },
+ // },
+ // {
+ // method: 'POST',
+ // path: '/roles/batch-delete',
+ // handler: 'role.deleteMany',
+ // config: {
+ // policies: [
+ // 'admin::isAuthenticatedAdmin',
+ // {
+ // name: 'admin::hasPermissions',
+ // config: {
+ // actions: ['admin::roles.delete'],
+ // },
+ // },
+ // ],
+ // },
+ // },
// SSO
{
diff --git a/packages/core/admin/ee/server/validation/permission.js b/packages/core/admin/ee/server/validation/permission.js
deleted file mode 100644
index 4712a1ade29..00000000000
--- a/packages/core/admin/ee/server/validation/permission.js
+++ /dev/null
@@ -1,8 +0,0 @@
-'use strict';
-
-const { validateYupSchema } = require('@strapi/utils');
-const validators = require('../../../server/validation/common-validators');
-
-module.exports = {
- validatedUpdatePermissionsInput: validateYupSchema(validators.updatePermissions),
-};
diff --git a/packages/core/admin/ee/server/validation/role.js b/packages/core/admin/ee/server/validation/role.js
index fa8a3f40185..4538c170b97 100644
--- a/packages/core/admin/ee/server/validation/role.js
+++ b/packages/core/admin/ee/server/validation/role.js
@@ -20,38 +20,46 @@ const rolesDeleteSchema = yup
.of(yup.strapiID())
.min(1)
.required()
- .test('roles-deletion-checks', 'Roles deletion checks have failed', async function (ids) {
- try {
- await strapi.admin.services.role.checkRolesIdForDeletion(ids);
+ .test(
+ 'roles-deletion-checks',
+ 'Roles deletion checks have failed',
+ async function rolesDeletionChecks(ids) {
+ try {
+ await strapi.admin.services.role.checkRolesIdForDeletion(ids);
- if (features.isEnabled('sso')) {
- await strapi.admin.services.role.ssoCheckRolesIdForDeletion(ids);
+ if (features.isEnabled('sso')) {
+ await strapi.admin.services.role.ssoCheckRolesIdForDeletion(ids);
+ }
+ } catch (e) {
+ return this.createError({ path: 'ids', message: e.message });
}
- } catch (e) {
- return this.createError({ path: 'ids', message: e.message });
- }
- return true;
- }),
+ return true;
+ }
+ ),
})
.noUnknown();
const roleDeleteSchema = yup
.strapiID()
.required()
- .test('no-admin-single-delete', 'Role deletion checks have failed', async function (id) {
- try {
- await strapi.admin.services.role.checkRolesIdForDeletion([id]);
+ .test(
+ 'no-admin-single-delete',
+ 'Role deletion checks have failed',
+ async function noAdminSingleDelete(id) {
+ try {
+ await strapi.admin.services.role.checkRolesIdForDeletion([id]);
- if (features.isEnabled('sso')) {
- await strapi.admin.services.role.ssoCheckRolesIdForDeletion([id]);
+ if (features.isEnabled('sso')) {
+ await strapi.admin.services.role.ssoCheckRolesIdForDeletion([id]);
+ }
+ } catch (e) {
+ return this.createError({ path: 'id', message: e.message });
}
- } catch (e) {
- return this.createError({ path: 'id', message: e.message });
- }
- return true;
- });
+ return true;
+ }
+ );
module.exports = {
validateRoleCreateInput: validateYupSchema(roleCreateSchema),
diff --git a/packages/core/admin/server/bootstrap.js b/packages/core/admin/server/bootstrap.js
index e04c989bb0a..48f1f1ae304 100644
--- a/packages/core/admin/server/bootstrap.js
+++ b/packages/core/admin/server/bootstrap.js
@@ -85,7 +85,6 @@ module.exports = async () => {
await roleService.resetSuperAdminPermissions();
await roleService.displayWarningIfNoSuperAdmin();
- await permissionService.ensureBoundPermissionsInDatabase();
await permissionService.cleanPermissionsInDatabase();
await userService.displayWarningIfUsersDontHaveRole();
diff --git a/packages/core/admin/server/controllers/permission.js b/packages/core/admin/server/controllers/permission.js
index 481f5adb415..84b3c803798 100644
--- a/packages/core/admin/server/controllers/permission.js
+++ b/packages/core/admin/server/controllers/permission.js
@@ -29,11 +29,9 @@ module.exports = {
* @param {KoaContext} ctx - koa context
*/
async getAll(ctx) {
- const { role: roleId } = ctx.query;
+ const { sectionsBuilder, actionProvider, conditionProvider } = getService('permission');
- const { sectionsBuilder, conditionProvider } = getService('permission');
-
- const actions = await getService('action').getAllowedActionsForRole(roleId);
+ const actions = actionProvider.values();
const conditions = conditionProvider.values();
const sections = await sectionsBuilder.build(actions);
diff --git a/packages/core/admin/server/controllers/role.js b/packages/core/admin/server/controllers/role.js
index 013c91cf47d..03c4b44644a 100644
--- a/packages/core/admin/server/controllers/role.js
+++ b/packages/core/admin/server/controllers/role.js
@@ -1,12 +1,32 @@
'use strict';
const { ApplicationError } = require('@strapi/utils').errors;
-const { validateRoleUpdateInput } = require('../validation/role');
+const {
+ validateRoleUpdateInput,
+ validateRoleCreateInput,
+ validateRoleDeleteInput,
+ validateRolesDeleteInput,
+} = require('../validation/role');
const { validatedUpdatePermissionsInput } = require('../validation/permission');
-const { EDITOR_CODE, AUTHOR_CODE, SUPER_ADMIN_CODE } = require('../services/constants');
+const { SUPER_ADMIN_CODE } = require('../services/constants');
const { getService } = require('../utils');
module.exports = {
+ /**
+ * Create a new role
+ * @param {KoaContext} ctx - koa context
+ */
+ async create(ctx) {
+ await validateRoleCreateInput(ctx.request.body);
+
+ const roleService = getService('role');
+
+ const role = await roleService.create(ctx.request.body);
+ const sanitizedRole = roleService.sanitizeRole(role);
+
+ ctx.created({ data: sanitizedRole });
+ },
+
/**
* Returns on role by id
* @param {KoaContext} ctx - koa context
@@ -99,10 +119,10 @@ module.exports = {
const { id } = ctx.params;
const { body: input } = ctx.request;
- const { findOne, assignPermissions } = getService('role');
- const { sanitizePermission, actionProvider } = getService('permission');
+ const roleService = getService('role');
+ const permissionService = getService('permission');
- const role = await findOne({ id });
+ const role = await roleService.findOne({ id });
if (!role) {
return ctx.notFound('role.notFound');
@@ -112,30 +132,57 @@ module.exports = {
throw new ApplicationError("Super admin permissions can't be edited.");
}
- await validatedUpdatePermissionsInput(input, role);
+ await validatedUpdatePermissionsInput(input);
- let permissionsToAssign;
+ if (!role) {
+ return ctx.notFound('role.notFound');
+ }
- if ([EDITOR_CODE, AUTHOR_CODE].includes(role.code)) {
- permissionsToAssign = input.permissions.map((permission) => {
- const action = actionProvider.get(permission.action);
+ const permissions = await roleService.assignPermissions(role.id, input.permissions);
- if (action.section !== 'contentTypes') {
- return permission;
- }
+ const sanitizedPermissions = permissions.map(permissionService.sanitizePermission);
- const conditions = role.code === AUTHOR_CODE ? ['admin::is-creator'] : [];
+ return ctx.send({
+ data: sanitizedPermissions,
+ });
+ },
- return { ...permission, conditions };
- });
- } else {
- permissionsToAssign = input.permissions;
- }
+ /**
+ * Delete a role
+ * @param {KoaContext} ctx - koa context
+ */
+ async deleteOne(ctx) {
+ const { id } = ctx.params;
- const permissions = await assignPermissions(role.id, permissionsToAssign);
+ await validateRoleDeleteInput(id);
- ctx.body = {
- data: permissions.map(sanitizePermission),
- };
+ const roleService = getService('role');
+
+ const roles = await roleService.deleteByIds([id]);
+
+ const sanitizedRole = roles.map((role) => roleService.sanitizeRole(role))[0] || null;
+
+ return ctx.deleted({
+ data: sanitizedRole,
+ });
+ },
+
+ /**
+ * delete several roles
+ * @param {KoaContext} ctx - koa context
+ */
+ async deleteMany(ctx) {
+ const { body } = ctx.request;
+
+ await validateRolesDeleteInput(body);
+
+ const roleService = getService('role');
+
+ const roles = await roleService.deleteByIds(body.ids);
+ const sanitizedRoles = roles.map(roleService.sanitizeRole);
+
+ return ctx.deleted({
+ data: sanitizedRoles,
+ });
},
};
diff --git a/packages/core/admin/server/domain/role.js b/packages/core/admin/server/domain/role.js
deleted file mode 100644
index 773558aac82..00000000000
--- a/packages/core/admin/server/domain/role.js
+++ /dev/null
@@ -1,29 +0,0 @@
-'use strict';
-
-const {
- contentTypes: { hasDraftAndPublish },
-} = require('@strapi/utils');
-const {
- AUTHOR_CODE,
- PUBLISH_ACTION,
- DELETE_ACTION,
- UPDATE_ACTION,
- CREATE_ACTION,
- READ_ACTION,
-} = require('../services/constants');
-
-const BOUND_ACTIONS = [READ_ACTION, CREATE_ACTION, UPDATE_ACTION, DELETE_ACTION, PUBLISH_ACTION];
-
-const BOUND_ACTIONS_FOR_FIELDS = [READ_ACTION, CREATE_ACTION, UPDATE_ACTION];
-
-const getBoundActionsBySubject = (role, subject) => {
- const model = strapi.contentTypes[subject];
-
- if (role.code === AUTHOR_CODE || !hasDraftAndPublish(model)) {
- return [READ_ACTION, UPDATE_ACTION, CREATE_ACTION, DELETE_ACTION];
- }
-
- return BOUND_ACTIONS;
-};
-
-module.exports = { getBoundActionsBySubject, BOUND_ACTIONS, BOUND_ACTIONS_FOR_FIELDS };
diff --git a/packages/core/admin/server/routes/roles.js b/packages/core/admin/server/routes/roles.js
index 991e0b77f06..3ff5b9b5932 100644
--- a/packages/core/admin/server/routes/roles.js
+++ b/packages/core/admin/server/routes/roles.js
@@ -45,6 +45,22 @@ module.exports = [
],
},
},
+ {
+ method: 'POST',
+ path: '/roles',
+ handler: 'role.create',
+ config: {
+ policies: [
+ 'admin::isAuthenticatedAdmin',
+ {
+ name: 'admin::hasPermissions',
+ config: {
+ actions: ['admin::roles.create'],
+ },
+ },
+ ],
+ },
+ },
{
method: 'PUT',
path: '/roles/:id',
@@ -56,4 +72,36 @@ module.exports = [
],
},
},
+ {
+ method: 'DELETE',
+ path: '/roles/:id',
+ handler: 'role.deleteOne',
+ config: {
+ policies: [
+ 'admin::isAuthenticatedAdmin',
+ {
+ name: 'admin::hasPermissions',
+ config: {
+ actions: ['admin::roles.delete'],
+ },
+ },
+ ],
+ },
+ },
+ {
+ method: 'POST',
+ path: '/roles/batch-delete',
+ handler: 'role.deleteMany',
+ config: {
+ policies: [
+ 'admin::isAuthenticatedAdmin',
+ {
+ name: 'admin::hasPermissions',
+ config: {
+ actions: ['admin::roles.delete'],
+ },
+ },
+ ],
+ },
+ },
];
diff --git a/packages/core/admin/server/services/permission/queries.js b/packages/core/admin/server/services/permission/queries.js
index 6b64367eb5a..58930a0541e 100644
--- a/packages/core/admin/server/services/permission/queries.js
+++ b/packages/core/admin/server/services/permission/queries.js
@@ -1,22 +1,7 @@
'use strict';
-const {
- flatMap,
- reject,
- isNil,
- isArray,
- prop,
- xor,
- eq,
- uniq,
- map,
- difference,
- differenceWith,
- pipe,
-} = require('lodash/fp');
+const { isNil, isArray, prop, xor, eq, map, differenceWith } = require('lodash/fp');
const pmap = require('p-map');
-const { EDITOR_CODE } = require('../constants');
-const { getBoundActionsBySubject, BOUND_ACTIONS_FOR_FIELDS } = require('../../domain/role');
const { getService } = require('../../utils');
const permissionDomain = require('../../domain/permission/index');
@@ -195,63 +180,6 @@ const cleanPermissionsInDatabase = async () => {
}
};
-const ensureBoundPermissionsInDatabase = async () => {
- if (strapi.EE) {
- return;
- }
-
- const contentTypes = Object.values(strapi.contentTypes);
- const editorRole = await strapi.query('admin::role').findOne({
- where: { code: EDITOR_CODE },
- });
-
- if (isNil(editorRole)) {
- return;
- }
-
- for (const contentType of contentTypes) {
- const boundActions = getBoundActionsBySubject(editorRole, contentType.uid);
-
- const permissions = await findMany({
- where: {
- subject: contentType.uid,
- action: boundActions,
- role: { id: editorRole.id },
- },
- });
-
- if (permissions.length === 0) {
- return;
- }
-
- const fields = pipe(
- flatMap(permissionDomain.getProperty('fields')),
- reject(isNil),
- uniq
- )(permissions);
-
- // Handle the scenario where permissions are missing
- const missingActions = difference(map('action', permissions), boundActions);
-
- if (missingActions.length > 0) {
- const permissions = pipe(
- // Create a permission skeleton from the action id
- map((action) => ({ action, subject: contentType.uid, role: editorRole.id })),
- // Use the permission domain to create a clean permission from the given object
- map(permissionDomain.create),
- // Adds the fields property if the permission action is eligible
- map((permission) =>
- BOUND_ACTIONS_FOR_FIELDS.includes(permission.action)
- ? permissionDomain.setProperty('fields', fields, permission)
- : permission
- )
- )(missingActions);
-
- await createMany(permissions);
- }
- }
-};
-
module.exports = {
createMany,
findMany,
@@ -259,5 +187,4 @@ module.exports = {
deleteByIds,
findUserPermissions,
cleanPermissionsInDatabase,
- ensureBoundPermissionsInDatabase,
};
diff --git a/packages/core/admin/server/tests/admin-auth.test.api.js b/packages/core/admin/server/tests/admin-auth.test.api.js
index 7a38beb353b..8855f02b262 100644
--- a/packages/core/admin/server/tests/admin-auth.test.api.js
+++ b/packages/core/admin/server/tests/admin-auth.test.api.js
@@ -5,8 +5,6 @@ const { createAuthRequest } = require('../../../../../test/helpers/request');
const { createStrapiInstance, superAdmin } = require('../../../../../test/helpers/strapi');
const { createUtils } = require('../../../../../test/helpers/utils');
-const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE';
-
const internals = {
role: null,
};
@@ -21,20 +19,14 @@ describe('Admin Auth End to End', () => {
rq = await createAuthRequest({ strapi });
utils = createUtils(strapi);
- if (edition === 'EE') {
- internals.role = await utils.createRole({
- name: 'auth_test_role',
- description: 'Only used for auth crud test (api)',
- });
- } else {
- internals.role = await utils.getSuperAdminRole();
- }
+ internals.role = await utils.createRole({
+ name: 'auth_test_role',
+ description: 'Only used for auth crud test (api)',
+ });
});
afterAll(async () => {
- if (edition === 'EE') {
- await utils.deleteRolesById([internals.role.id]);
- }
+ await utils.deleteRolesById([internals.role.id]);
await strapi.destroy();
});
diff --git a/packages/core/admin/server/tests/admin-permission.test.api.js b/packages/core/admin/server/tests/admin-permission.test.api.js
index 8e2b03d7b7f..f41dcf2c412 100644
--- a/packages/core/admin/server/tests/admin-permission.test.api.js
+++ b/packages/core/admin/server/tests/admin-permission.test.api.js
@@ -5,8 +5,6 @@ const _ = require('lodash');
const { createAuthRequest } = require('../../../../../test/helpers/request');
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
-const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE';
-
describe('Role CRUD End to End', () => {
let rq;
let strapi;
@@ -35,1244 +33,169 @@ describe('Role CRUD End to End', () => {
});
sortedData.conditions = sortedData.conditions.sort();
- if (edition === 'CE') {
+ // eslint-disable-next-line node/no-extraneous-require
+ const { features } = require('@strapi/strapi/lib/utils/ee');
+ const hasSSO = features.isEnabled('sso');
+
+ if (hasSSO) {
expect(sortedData).toMatchInlineSnapshot(`
- {
- "conditions": [
- {
- "category": "default",
- "displayName": "Is creator",
- "id": "admin::is-creator",
- },
- {
- "category": "default",
- "displayName": "Has same role as creator",
- "id": "admin::has-same-role-as-creator",
- },
- ],
- "sections": {
- "collectionTypes": [
- [
+ {
+ "conditions": [
+ {
+ "category": "default",
+ "displayName": "Is creator",
+ "id": "admin::is-creator",
+ },
+ {
+ "category": "default",
+ "displayName": "Has same role as creator",
+ "id": "admin::has-same-role-as-creator",
+ },
+ ],
+ "sections": {
+ "collectionTypes": [
+ [
+ {
+ "actionId": "plugin::content-manager.explorer.create",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Create",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.read",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Read",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.update",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Update",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.delete",
+ "applyToProperties": [
+ "locales",
+ ],
+ "label": "Delete",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.publish",
+ "applyToProperties": [
+ "locales",
+ ],
+ "label": "Publish",
+ "subjects": [],
+ },
+ ],
+ [
+ {
+ "label": "user",
+ "properties": [
+ {
+ "children": [
+ {
+ "label": "username",
+ "required": true,
+ "value": "username",
+ },
+ {
+ "label": "email",
+ "required": true,
+ "value": "email",
+ },
+ {
+ "label": "provider",
+ "value": "provider",
+ },
+ {
+ "label": "password",
+ "value": "password",
+ },
+ {
+ "label": "resetPasswordToken",
+ "value": "resetPasswordToken",
+ },
+ {
+ "label": "confirmationToken",
+ "value": "confirmationToken",
+ },
+ {
+ "label": "confirmed",
+ "value": "confirmed",
+ },
+ {
+ "label": "blocked",
+ "value": "blocked",
+ },
+ {
+ "label": "role",
+ "value": "role",
+ },
+ ],
+ "label": "Fields",
+ "value": "fields",
+ },
+ ],
+ "uid": "plugin::users-permissions.user",
+ },
+ ],
+ ],
+ "plugins": [
{
- "actionId": "plugin::content-manager.explorer.create",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Create",
- "subjects": [
- "plugin::users-permissions.user",
- ],
+ "action": "plugin::content-manager.collection-types.configure-view",
+ "displayName": "Configure view",
+ "plugin": "content-manager",
+ "subCategory": "collection types",
},
{
- "actionId": "plugin::content-manager.explorer.read",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Read",
- "subjects": [
- "plugin::users-permissions.user",
- ],
+ "action": "plugin::content-manager.components.configure-layout",
+ "displayName": "Configure Layout",
+ "plugin": "content-manager",
+ "subCategory": "components",
},
{
- "actionId": "plugin::content-manager.explorer.update",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Update",
- "subjects": [
- "plugin::users-permissions.user",
- ],
+ "action": "plugin::content-manager.single-types.configure-view",
+ "displayName": "Configure view",
+ "plugin": "content-manager",
+ "subCategory": "single types",
},
{
- "actionId": "plugin::content-manager.explorer.delete",
- "applyToProperties": [
- "locales",
- ],
- "label": "Delete",
- "subjects": [
- "plugin::users-permissions.user",
- ],
+ "action": "plugin::content-type-builder.read",
+ "displayName": "Read",
+ "plugin": "content-type-builder",
+ "subCategory": "general",
},
{
- "actionId": "plugin::content-manager.explorer.publish",
- "applyToProperties": [
- "locales",
- ],
- "label": "Publish",
- "subjects": [],
+ "action": "plugin::documentation.read",
+ "displayName": "Access the Documentation",
+ "plugin": "documentation",
+ "subCategory": "general",
},
- ],
- [
{
- "label": "user",
- "properties": [
- {
- "children": [
- {
- "label": "username",
- "required": true,
- "value": "username",
- },
- {
- "label": "email",
- "required": true,
- "value": "email",
- },
- {
- "label": "provider",
- "value": "provider",
- },
- {
- "label": "password",
- "value": "password",
- },
- {
- "label": "resetPasswordToken",
- "value": "resetPasswordToken",
- },
- {
- "label": "confirmationToken",
- "value": "confirmationToken",
- },
- {
- "label": "confirmed",
- "value": "confirmed",
- },
- {
- "label": "blocked",
- "value": "blocked",
- },
- {
- "label": "role",
- "value": "role",
- },
- ],
- "label": "Fields",
- "value": "fields",
- },
- ],
- "uid": "plugin::users-permissions.user",
- },
- ],
- ],
- "plugins": [
- {
- "action": "plugin::content-manager.collection-types.configure-view",
- "displayName": "Configure view",
- "plugin": "content-manager",
- "subCategory": "collection types",
- },
- {
- "action": "plugin::content-manager.components.configure-layout",
- "displayName": "Configure Layout",
- "plugin": "content-manager",
- "subCategory": "components",
- },
- {
- "action": "plugin::content-manager.single-types.configure-view",
- "displayName": "Configure view",
- "plugin": "content-manager",
- "subCategory": "single types",
- },
- {
- "action": "plugin::content-type-builder.read",
- "displayName": "Read",
- "plugin": "content-type-builder",
- "subCategory": "general",
- },
- {
- "action": "plugin::documentation.read",
- "displayName": "Access the Documentation",
- "plugin": "documentation",
- "subCategory": "general",
- },
- {
- "action": "plugin::documentation.settings.regenerate",
- "displayName": "Regenerate",
- "plugin": "documentation",
- "subCategory": "general",
- },
- {
- "action": "plugin::documentation.settings.update",
- "displayName": "Update and delete",
- "plugin": "documentation",
- "subCategory": "general",
- },
- {
- "action": "plugin::upload.assets.copy-link",
- "displayName": "Copy link",
- "plugin": "upload",
- "subCategory": "assets",
- },
- {
- "action": "plugin::upload.assets.create",
- "displayName": "Create (upload)",
- "plugin": "upload",
- "subCategory": "assets",
- },
- {
- "action": "plugin::upload.assets.download",
- "displayName": "Download",
- "plugin": "upload",
- "subCategory": "assets",
- },
- {
- "action": "plugin::upload.assets.update",
- "displayName": "Update (crop, details, replace) + delete",
- "plugin": "upload",
- "subCategory": "assets",
- },
- {
- "action": "plugin::upload.configure-view",
- "displayName": "Configure view",
- "plugin": "upload",
- "subCategory": "general",
- },
- {
- "action": "plugin::upload.read",
- "displayName": "Access the Media Library",
- "plugin": "upload",
- "subCategory": "general",
- },
- {
- "action": "plugin::users-permissions.advanced-settings.read",
- "displayName": "Read",
- "plugin": "users-permissions",
- "subCategory": "advancedSettings",
- },
- {
- "action": "plugin::users-permissions.advanced-settings.update",
- "displayName": "Edit",
- "plugin": "users-permissions",
- "subCategory": "advancedSettings",
- },
- {
- "action": "plugin::users-permissions.email-templates.read",
- "displayName": "Read",
- "plugin": "users-permissions",
- "subCategory": "emailTemplates",
- },
- {
- "action": "plugin::users-permissions.email-templates.update",
- "displayName": "Edit",
- "plugin": "users-permissions",
- "subCategory": "emailTemplates",
- },
- {
- "action": "plugin::users-permissions.providers.read",
- "displayName": "Read",
- "plugin": "users-permissions",
- "subCategory": "providers",
- },
- {
- "action": "plugin::users-permissions.providers.update",
- "displayName": "Edit",
- "plugin": "users-permissions",
- "subCategory": "providers",
- },
- {
- "action": "plugin::users-permissions.roles.create",
- "displayName": "Create",
- "plugin": "users-permissions",
- "subCategory": "roles",
- },
- {
- "action": "plugin::users-permissions.roles.delete",
- "displayName": "Delete",
- "plugin": "users-permissions",
- "subCategory": "roles",
- },
- {
- "action": "plugin::users-permissions.roles.read",
- "displayName": "Read",
- "plugin": "users-permissions",
- "subCategory": "roles",
- },
- {
- "action": "plugin::users-permissions.roles.update",
- "displayName": "Update",
- "plugin": "users-permissions",
- "subCategory": "roles",
- },
- ],
- "settings": [
- {
- "action": "admin::api-tokens.access",
- "category": "api tokens",
- "displayName": "Access the API tokens settings page",
- "subCategory": "api Tokens",
- },
- {
- "action": "admin::api-tokens.create",
- "category": "api tokens",
- "displayName": "Create (generate)",
- "subCategory": "general",
- },
- {
- "action": "admin::api-tokens.delete",
- "category": "api tokens",
- "displayName": "Delete (revoke)",
- "subCategory": "general",
- },
- {
- "action": "admin::api-tokens.read",
- "category": "api tokens",
- "displayName": "Read",
- "subCategory": "general",
- },
- {
- "action": "admin::api-tokens.regenerate",
- "category": "api tokens",
- "displayName": "Regenerate",
- "subCategory": "general",
- },
- {
- "action": "admin::api-tokens.update",
- "category": "api tokens",
- "displayName": "Update",
- "subCategory": "general",
- },
- {
- "action": "admin::marketplace.read",
- "category": "plugins and marketplace",
- "displayName": "Access the marketplace",
- "subCategory": "marketplace",
- },
- {
- "action": "admin::project-settings.read",
- "category": "project",
- "displayName": "Read the project level settings",
- "subCategory": "general",
- },
- {
- "action": "admin::project-settings.update",
- "category": "project",
- "displayName": "Update the project level settings",
- "subCategory": "general",
- },
- {
- "action": "admin::roles.create",
- "category": "users and roles",
- "displayName": "Create",
- "subCategory": "roles",
- },
- {
- "action": "admin::roles.delete",
- "category": "users and roles",
- "displayName": "Delete",
- "subCategory": "roles",
- },
- {
- "action": "admin::roles.read",
- "category": "users and roles",
- "displayName": "Read",
- "subCategory": "roles",
- },
- {
- "action": "admin::roles.update",
- "category": "users and roles",
- "displayName": "Update",
- "subCategory": "roles",
- },
- {
- "action": "admin::transfer.tokens.access",
- "category": "transfer tokens",
- "displayName": "Access the transfer tokens settings page",
- "subCategory": "transfer tokens",
- },
- {
- "action": "admin::transfer.tokens.create",
- "category": "transfer tokens",
- "displayName": "Create (generate)",
- "subCategory": "general",
- },
- {
- "action": "admin::transfer.tokens.delete",
- "category": "transfer tokens",
- "displayName": "Delete (revoke)",
- "subCategory": "general",
- },
- {
- "action": "admin::transfer.tokens.read",
- "category": "transfer tokens",
- "displayName": "Read",
- "subCategory": "general",
- },
- {
- "action": "admin::transfer.tokens.regenerate",
- "category": "transfer tokens",
- "displayName": "Regenerate",
- "subCategory": "general",
- },
- {
- "action": "admin::transfer.tokens.update",
- "category": "transfer tokens",
- "displayName": "Update",
- "subCategory": "general",
- },
- {
- "action": "admin::users.create",
- "category": "users and roles",
- "displayName": "Create (invite)",
- "subCategory": "users",
- },
- {
- "action": "admin::users.delete",
- "category": "users and roles",
- "displayName": "Delete",
- "subCategory": "users",
- },
- {
- "action": "admin::users.read",
- "category": "users and roles",
- "displayName": "Read",
- "subCategory": "users",
- },
- {
- "action": "admin::users.update",
- "category": "users and roles",
- "displayName": "Update",
- "subCategory": "users",
- },
- {
- "action": "admin::webhooks.create",
- "category": "webhooks",
- "displayName": "Create",
- "subCategory": "general",
- },
- {
- "action": "admin::webhooks.delete",
- "category": "webhooks",
- "displayName": "Delete",
- "subCategory": "general",
- },
- {
- "action": "admin::webhooks.read",
- "category": "webhooks",
- "displayName": "Read",
- "subCategory": "general",
- },
- {
- "action": "admin::webhooks.update",
- "category": "webhooks",
- "displayName": "Update",
- "subCategory": "general",
- },
- {
- "action": "plugin::documentation.settings.read",
- "category": "documentation",
- "displayName": "Access the documentation settings page",
- "subCategory": "general",
- },
- {
- "action": "plugin::email.settings.read",
- "category": "email",
- "displayName": "Access the Email Settings page",
- "subCategory": "general",
- },
- {
- "action": "plugin::i18n.locale.create",
- "category": "Internationalization",
- "displayName": "Create",
- "subCategory": "Locales",
- },
- {
- "action": "plugin::i18n.locale.delete",
- "category": "Internationalization",
- "displayName": "Delete",
- "subCategory": "Locales",
- },
- {
- "action": "plugin::i18n.locale.read",
- "category": "Internationalization",
- "displayName": "Read",
- "subCategory": "Locales",
- },
- {
- "action": "plugin::i18n.locale.update",
- "category": "Internationalization",
- "displayName": "Update",
- "subCategory": "Locales",
- },
- {
- "action": "plugin::upload.settings.read",
- "category": "media library",
- "displayName": "Access the Media Library settings page",
- "subCategory": "general",
- },
- ],
- "singleTypes": [
- [
- {
- "actionId": "plugin::content-manager.explorer.create",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Create",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.read",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Read",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.update",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Update",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.delete",
- "applyToProperties": [
- "locales",
- ],
- "label": "Delete",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.publish",
- "applyToProperties": [
- "locales",
- ],
- "label": "Publish",
- "subjects": [],
- },
- ],
- [],
- ],
- },
- }
- `);
- } else {
- // eslint-disable-next-line node/no-extraneous-require
- const { features } = require('@strapi/strapi/lib/utils/ee');
- const hasSSO = features.isEnabled('sso');
-
- if (hasSSO) {
- expect(sortedData).toMatchInlineSnapshot(`
- {
- "conditions": [
- {
- "category": "default",
- "displayName": "Is creator",
- "id": "admin::is-creator",
- },
- {
- "category": "default",
- "displayName": "Has same role as creator",
- "id": "admin::has-same-role-as-creator",
- },
- ],
- "sections": {
- "collectionTypes": [
- [
- {
- "actionId": "plugin::content-manager.explorer.create",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Create",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.read",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Read",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.update",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Update",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.delete",
- "applyToProperties": [
- "locales",
- ],
- "label": "Delete",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.publish",
- "applyToProperties": [
- "locales",
- ],
- "label": "Publish",
- "subjects": [],
- },
- ],
- [
- {
- "label": "user",
- "properties": [
- {
- "children": [
- {
- "label": "username",
- "required": true,
- "value": "username",
- },
- {
- "label": "email",
- "required": true,
- "value": "email",
- },
- {
- "label": "provider",
- "value": "provider",
- },
- {
- "label": "password",
- "value": "password",
- },
- {
- "label": "resetPasswordToken",
- "value": "resetPasswordToken",
- },
- {
- "label": "confirmationToken",
- "value": "confirmationToken",
- },
- {
- "label": "confirmed",
- "value": "confirmed",
- },
- {
- "label": "blocked",
- "value": "blocked",
- },
- {
- "label": "role",
- "value": "role",
- },
- ],
- "label": "Fields",
- "value": "fields",
- },
- ],
- "uid": "plugin::users-permissions.user",
- },
- ],
- ],
- "plugins": [
- {
- "action": "plugin::content-manager.collection-types.configure-view",
- "displayName": "Configure view",
- "plugin": "content-manager",
- "subCategory": "collection types",
- },
- {
- "action": "plugin::content-manager.components.configure-layout",
- "displayName": "Configure Layout",
- "plugin": "content-manager",
- "subCategory": "components",
- },
- {
- "action": "plugin::content-manager.single-types.configure-view",
- "displayName": "Configure view",
- "plugin": "content-manager",
- "subCategory": "single types",
- },
- {
- "action": "plugin::content-type-builder.read",
- "displayName": "Read",
- "plugin": "content-type-builder",
- "subCategory": "general",
- },
- {
- "action": "plugin::documentation.read",
- "displayName": "Access the Documentation",
- "plugin": "documentation",
- "subCategory": "general",
- },
- {
- "action": "plugin::documentation.settings.regenerate",
- "displayName": "Regenerate",
- "plugin": "documentation",
- "subCategory": "general",
- },
- {
- "action": "plugin::documentation.settings.update",
- "displayName": "Update and delete",
- "plugin": "documentation",
- "subCategory": "general",
- },
- {
- "action": "plugin::upload.assets.copy-link",
- "displayName": "Copy link",
- "plugin": "upload",
- "subCategory": "assets",
- },
- {
- "action": "plugin::upload.assets.create",
- "displayName": "Create (upload)",
- "plugin": "upload",
- "subCategory": "assets",
- },
- {
- "action": "plugin::upload.assets.download",
- "displayName": "Download",
- "plugin": "upload",
- "subCategory": "assets",
- },
- {
- "action": "plugin::upload.assets.update",
- "displayName": "Update (crop, details, replace) + delete",
- "plugin": "upload",
- "subCategory": "assets",
- },
- {
- "action": "plugin::upload.configure-view",
- "displayName": "Configure view",
- "plugin": "upload",
- "subCategory": "general",
- },
- {
- "action": "plugin::upload.read",
- "displayName": "Access the Media Library",
- "plugin": "upload",
- "subCategory": "general",
- },
- {
- "action": "plugin::users-permissions.advanced-settings.read",
- "displayName": "Read",
- "plugin": "users-permissions",
- "subCategory": "advancedSettings",
- },
- {
- "action": "plugin::users-permissions.advanced-settings.update",
- "displayName": "Edit",
- "plugin": "users-permissions",
- "subCategory": "advancedSettings",
- },
- {
- "action": "plugin::users-permissions.email-templates.read",
- "displayName": "Read",
- "plugin": "users-permissions",
- "subCategory": "emailTemplates",
- },
- {
- "action": "plugin::users-permissions.email-templates.update",
- "displayName": "Edit",
- "plugin": "users-permissions",
- "subCategory": "emailTemplates",
- },
- {
- "action": "plugin::users-permissions.providers.read",
- "displayName": "Read",
- "plugin": "users-permissions",
- "subCategory": "providers",
- },
- {
- "action": "plugin::users-permissions.providers.update",
- "displayName": "Edit",
- "plugin": "users-permissions",
- "subCategory": "providers",
- },
- {
- "action": "plugin::users-permissions.roles.create",
- "displayName": "Create",
- "plugin": "users-permissions",
- "subCategory": "roles",
- },
- {
- "action": "plugin::users-permissions.roles.delete",
- "displayName": "Delete",
- "plugin": "users-permissions",
- "subCategory": "roles",
- },
- {
- "action": "plugin::users-permissions.roles.read",
- "displayName": "Read",
- "plugin": "users-permissions",
- "subCategory": "roles",
- },
- {
- "action": "plugin::users-permissions.roles.update",
- "displayName": "Update",
- "plugin": "users-permissions",
- "subCategory": "roles",
- },
- ],
- "settings": [
- {
- "action": "admin::api-tokens.access",
- "category": "api tokens",
- "displayName": "Access the API tokens settings page",
- "subCategory": "api Tokens",
- },
- {
- "action": "admin::api-tokens.create",
- "category": "api tokens",
- "displayName": "Create (generate)",
- "subCategory": "general",
- },
- {
- "action": "admin::api-tokens.delete",
- "category": "api tokens",
- "displayName": "Delete (revoke)",
- "subCategory": "general",
- },
- {
- "action": "admin::api-tokens.read",
- "category": "api tokens",
- "displayName": "Read",
- "subCategory": "general",
- },
- {
- "action": "admin::api-tokens.regenerate",
- "category": "api tokens",
- "displayName": "Regenerate",
- "subCategory": "general",
- },
- {
- "action": "admin::api-tokens.update",
- "category": "api tokens",
- "displayName": "Update",
- "subCategory": "general",
- },
- {
- "action": "admin::audit-logs.read",
- "category": "audit logs",
- "displayName": "Read",
- "subCategory": "options",
- },
- {
- "action": "admin::marketplace.read",
- "category": "plugins and marketplace",
- "displayName": "Access the marketplace",
- "subCategory": "marketplace",
- },
- {
- "action": "admin::project-settings.read",
- "category": "project",
- "displayName": "Read the project level settings",
- "subCategory": "general",
- },
- {
- "action": "admin::project-settings.update",
- "category": "project",
- "displayName": "Update the project level settings",
- "subCategory": "general",
- },
- {
- "action": "admin::provider-login.read",
- "category": "single sign on",
- "displayName": "Read",
- "subCategory": "options",
- },
- {
- "action": "admin::provider-login.update",
- "category": "single sign on",
- "displayName": "Update",
- "subCategory": "options",
- },
- {
- "action": "admin::roles.create",
- "category": "users and roles",
- "displayName": "Create",
- "subCategory": "roles",
- },
- {
- "action": "admin::roles.delete",
- "category": "users and roles",
- "displayName": "Delete",
- "subCategory": "roles",
- },
- {
- "action": "admin::roles.read",
- "category": "users and roles",
- "displayName": "Read",
- "subCategory": "roles",
- },
- {
- "action": "admin::roles.update",
- "category": "users and roles",
- "displayName": "Update",
- "subCategory": "roles",
- },
- {
- "action": "admin::transfer.tokens.access",
- "category": "transfer tokens",
- "displayName": "Access the transfer tokens settings page",
- "subCategory": "transfer tokens",
- },
- {
- "action": "admin::transfer.tokens.create",
- "category": "transfer tokens",
- "displayName": "Create (generate)",
- "subCategory": "general",
- },
- {
- "action": "admin::transfer.tokens.delete",
- "category": "transfer tokens",
- "displayName": "Delete (revoke)",
- "subCategory": "general",
- },
- {
- "action": "admin::transfer.tokens.read",
- "category": "transfer tokens",
- "displayName": "Read",
- "subCategory": "general",
- },
- {
- "action": "admin::transfer.tokens.regenerate",
- "category": "transfer tokens",
- "displayName": "Regenerate",
- "subCategory": "general",
- },
- {
- "action": "admin::transfer.tokens.update",
- "category": "transfer tokens",
- "displayName": "Update",
- "subCategory": "general",
- },
- {
- "action": "admin::users.create",
- "category": "users and roles",
- "displayName": "Create (invite)",
- "subCategory": "users",
- },
- {
- "action": "admin::users.delete",
- "category": "users and roles",
- "displayName": "Delete",
- "subCategory": "users",
- },
- {
- "action": "admin::users.read",
- "category": "users and roles",
- "displayName": "Read",
- "subCategory": "users",
- },
- {
- "action": "admin::users.update",
- "category": "users and roles",
- "displayName": "Update",
- "subCategory": "users",
- },
- {
- "action": "admin::webhooks.create",
- "category": "webhooks",
- "displayName": "Create",
- "subCategory": "general",
- },
- {
- "action": "admin::webhooks.delete",
- "category": "webhooks",
- "displayName": "Delete",
- "subCategory": "general",
- },
- {
- "action": "admin::webhooks.read",
- "category": "webhooks",
- "displayName": "Read",
- "subCategory": "general",
- },
- {
- "action": "admin::webhooks.update",
- "category": "webhooks",
- "displayName": "Update",
- "subCategory": "general",
- },
- {
- "action": "plugin::documentation.settings.read",
- "category": "documentation",
- "displayName": "Access the documentation settings page",
- "subCategory": "general",
- },
- {
- "action": "plugin::email.settings.read",
- "category": "email",
- "displayName": "Access the Email Settings page",
- "subCategory": "general",
- },
- {
- "action": "plugin::i18n.locale.create",
- "category": "Internationalization",
- "displayName": "Create",
- "subCategory": "Locales",
- },
- {
- "action": "plugin::i18n.locale.delete",
- "category": "Internationalization",
- "displayName": "Delete",
- "subCategory": "Locales",
- },
- {
- "action": "plugin::i18n.locale.read",
- "category": "Internationalization",
- "displayName": "Read",
- "subCategory": "Locales",
- },
- {
- "action": "plugin::i18n.locale.update",
- "category": "Internationalization",
- "displayName": "Update",
- "subCategory": "Locales",
- },
- {
- "action": "plugin::upload.settings.read",
- "category": "media library",
- "displayName": "Access the Media Library settings page",
- "subCategory": "general",
- },
- ],
- "singleTypes": [
- [
- {
- "actionId": "plugin::content-manager.explorer.create",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Create",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.read",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Read",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.update",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Update",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.delete",
- "applyToProperties": [
- "locales",
- ],
- "label": "Delete",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.publish",
- "applyToProperties": [
- "locales",
- ],
- "label": "Publish",
- "subjects": [],
- },
- ],
- [],
- ],
- },
- }
- `);
- } else {
- expect(sortedData).toMatchInlineSnapshot(`
- {
- "conditions": [
- {
- "category": "default",
- "displayName": "Is creator",
- "id": "admin::is-creator",
- },
- {
- "category": "default",
- "displayName": "Has same role as creator",
- "id": "admin::has-same-role-as-creator",
- },
- ],
- "sections": {
- "collectionTypes": [
- [
- {
- "actionId": "plugin::content-manager.explorer.create",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Create",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.read",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Read",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.update",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Update",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.delete",
- "applyToProperties": [
- "locales",
- ],
- "label": "Delete",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.publish",
- "applyToProperties": [
- "locales",
- ],
- "label": "Publish",
- "subjects": [],
- },
- ],
- [
- {
- "label": "user",
- "properties": [
- {
- "children": [
- {
- "label": "username",
- "required": true,
- "value": "username",
- },
- {
- "label": "email",
- "required": true,
- "value": "email",
- },
- {
- "label": "provider",
- "value": "provider",
- },
- {
- "label": "password",
- "value": "password",
- },
- {
- "label": "resetPasswordToken",
- "value": "resetPasswordToken",
- },
- {
- "label": "confirmationToken",
- "value": "confirmationToken",
- },
- {
- "label": "confirmed",
- "value": "confirmed",
- },
- {
- "label": "blocked",
- "value": "blocked",
- },
- {
- "label": "role",
- "value": "role",
- },
- ],
- "label": "Fields",
- "value": "fields",
- },
- ],
- "uid": "plugin::users-permissions.user",
- },
- ],
- ],
- "plugins": [
- {
- "action": "plugin::content-manager.collection-types.configure-view",
- "displayName": "Configure view",
- "plugin": "content-manager",
- "subCategory": "collection types",
- },
- {
- "action": "plugin::content-manager.components.configure-layout",
- "displayName": "Configure Layout",
- "plugin": "content-manager",
- "subCategory": "components",
- },
- {
- "action": "plugin::content-manager.single-types.configure-view",
- "displayName": "Configure view",
- "plugin": "content-manager",
- "subCategory": "single types",
- },
- {
- "action": "plugin::content-type-builder.read",
- "displayName": "Read",
- "plugin": "content-type-builder",
- "subCategory": "general",
- },
- {
- "action": "plugin::documentation.read",
- "displayName": "Access the Documentation",
- "plugin": "documentation",
- "subCategory": "general",
- },
- {
- "action": "plugin::documentation.settings.regenerate",
- "displayName": "Regenerate",
- "plugin": "documentation",
- "subCategory": "general",
+ "action": "plugin::documentation.settings.regenerate",
+ "displayName": "Regenerate",
+ "plugin": "documentation",
+ "subCategory": "general",
},
{
"action": "plugin::documentation.settings.update",
@@ -1414,6 +337,12 @@ describe('Role CRUD End to End', () => {
"displayName": "Update",
"subCategory": "general",
},
+ {
+ "action": "admin::audit-logs.read",
+ "category": "audit logs",
+ "displayName": "Read",
+ "subCategory": "options",
+ },
{
"action": "admin::marketplace.read",
"category": "plugins and marketplace",
@@ -1432,6 +361,18 @@ describe('Role CRUD End to End', () => {
"displayName": "Update the project level settings",
"subCategory": "general",
},
+ {
+ "action": "admin::provider-login.read",
+ "category": "single sign on",
+ "displayName": "Read",
+ "subCategory": "options",
+ },
+ {
+ "action": "admin::provider-login.update",
+ "category": "single sign on",
+ "displayName": "Update",
+ "subCategory": "options",
+ },
{
"action": "admin::roles.create",
"category": "users and roles",
@@ -1456,6 +397,42 @@ describe('Role CRUD End to End', () => {
"displayName": "Update",
"subCategory": "roles",
},
+ {
+ "action": "admin::transfer.tokens.access",
+ "category": "transfer tokens",
+ "displayName": "Access the transfer tokens settings page",
+ "subCategory": "transfer tokens",
+ },
+ {
+ "action": "admin::transfer.tokens.create",
+ "category": "transfer tokens",
+ "displayName": "Create (generate)",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::transfer.tokens.delete",
+ "category": "transfer tokens",
+ "displayName": "Delete (revoke)",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::transfer.tokens.read",
+ "category": "transfer tokens",
+ "displayName": "Read",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::transfer.tokens.regenerate",
+ "category": "transfer tokens",
+ "displayName": "Regenerate",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::transfer.tokens.update",
+ "category": "transfer tokens",
+ "displayName": "Update",
+ "subCategory": "general",
+ },
{
"action": "admin::users.create",
"category": "users and roles",
@@ -1517,96 +494,623 @@ describe('Role CRUD End to End', () => {
"subCategory": "general",
},
{
- "action": "plugin::i18n.locale.create",
- "category": "Internationalization",
- "displayName": "Create",
- "subCategory": "Locales",
+ "action": "plugin::i18n.locale.create",
+ "category": "Internationalization",
+ "displayName": "Create",
+ "subCategory": "Locales",
+ },
+ {
+ "action": "plugin::i18n.locale.delete",
+ "category": "Internationalization",
+ "displayName": "Delete",
+ "subCategory": "Locales",
+ },
+ {
+ "action": "plugin::i18n.locale.read",
+ "category": "Internationalization",
+ "displayName": "Read",
+ "subCategory": "Locales",
+ },
+ {
+ "action": "plugin::i18n.locale.update",
+ "category": "Internationalization",
+ "displayName": "Update",
+ "subCategory": "Locales",
+ },
+ {
+ "action": "plugin::upload.settings.read",
+ "category": "media library",
+ "displayName": "Access the Media Library settings page",
+ "subCategory": "general",
+ },
+ ],
+ "singleTypes": [
+ [
+ {
+ "actionId": "plugin::content-manager.explorer.create",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Create",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.read",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Read",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.update",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Update",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.delete",
+ "applyToProperties": [
+ "locales",
+ ],
+ "label": "Delete",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.publish",
+ "applyToProperties": [
+ "locales",
+ ],
+ "label": "Publish",
+ "subjects": [],
+ },
+ ],
+ [],
+ ],
+ },
+ }
+ `);
+ } else {
+ expect(sortedData).toMatchInlineSnapshot(`
+ {
+ "conditions": [
+ {
+ "category": "default",
+ "displayName": "Is creator",
+ "id": "admin::is-creator",
+ },
+ {
+ "category": "default",
+ "displayName": "Has same role as creator",
+ "id": "admin::has-same-role-as-creator",
+ },
+ ],
+ "sections": {
+ "collectionTypes": [
+ [
+ {
+ "actionId": "plugin::content-manager.explorer.create",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Create",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.read",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Read",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.update",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Update",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.delete",
+ "applyToProperties": [
+ "locales",
+ ],
+ "label": "Delete",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
+ },
+ {
+ "actionId": "plugin::content-manager.explorer.publish",
+ "applyToProperties": [
+ "locales",
+ ],
+ "label": "Publish",
+ "subjects": [],
+ },
+ ],
+ [
+ {
+ "label": "user",
+ "properties": [
+ {
+ "children": [
+ {
+ "label": "username",
+ "required": true,
+ "value": "username",
+ },
+ {
+ "label": "email",
+ "required": true,
+ "value": "email",
+ },
+ {
+ "label": "provider",
+ "value": "provider",
+ },
+ {
+ "label": "password",
+ "value": "password",
+ },
+ {
+ "label": "resetPasswordToken",
+ "value": "resetPasswordToken",
+ },
+ {
+ "label": "confirmationToken",
+ "value": "confirmationToken",
+ },
+ {
+ "label": "confirmed",
+ "value": "confirmed",
+ },
+ {
+ "label": "blocked",
+ "value": "blocked",
+ },
+ {
+ "label": "role",
+ "value": "role",
+ },
+ ],
+ "label": "Fields",
+ "value": "fields",
+ },
+ ],
+ "uid": "plugin::users-permissions.user",
+ },
+ ],
+ ],
+ "plugins": [
+ {
+ "action": "plugin::content-manager.collection-types.configure-view",
+ "displayName": "Configure view",
+ "plugin": "content-manager",
+ "subCategory": "collection types",
+ },
+ {
+ "action": "plugin::content-manager.components.configure-layout",
+ "displayName": "Configure Layout",
+ "plugin": "content-manager",
+ "subCategory": "components",
+ },
+ {
+ "action": "plugin::content-manager.single-types.configure-view",
+ "displayName": "Configure view",
+ "plugin": "content-manager",
+ "subCategory": "single types",
+ },
+ {
+ "action": "plugin::content-type-builder.read",
+ "displayName": "Read",
+ "plugin": "content-type-builder",
+ "subCategory": "general",
+ },
+ {
+ "action": "plugin::documentation.read",
+ "displayName": "Access the Documentation",
+ "plugin": "documentation",
+ "subCategory": "general",
+ },
+ {
+ "action": "plugin::documentation.settings.regenerate",
+ "displayName": "Regenerate",
+ "plugin": "documentation",
+ "subCategory": "general",
+ },
+ {
+ "action": "plugin::documentation.settings.update",
+ "displayName": "Update and delete",
+ "plugin": "documentation",
+ "subCategory": "general",
+ },
+ {
+ "action": "plugin::upload.assets.copy-link",
+ "displayName": "Copy link",
+ "plugin": "upload",
+ "subCategory": "assets",
+ },
+ {
+ "action": "plugin::upload.assets.create",
+ "displayName": "Create (upload)",
+ "plugin": "upload",
+ "subCategory": "assets",
+ },
+ {
+ "action": "plugin::upload.assets.download",
+ "displayName": "Download",
+ "plugin": "upload",
+ "subCategory": "assets",
+ },
+ {
+ "action": "plugin::upload.assets.update",
+ "displayName": "Update (crop, details, replace) + delete",
+ "plugin": "upload",
+ "subCategory": "assets",
+ },
+ {
+ "action": "plugin::upload.configure-view",
+ "displayName": "Configure view",
+ "plugin": "upload",
+ "subCategory": "general",
+ },
+ {
+ "action": "plugin::upload.read",
+ "displayName": "Access the Media Library",
+ "plugin": "upload",
+ "subCategory": "general",
+ },
+ {
+ "action": "plugin::users-permissions.advanced-settings.read",
+ "displayName": "Read",
+ "plugin": "users-permissions",
+ "subCategory": "advancedSettings",
+ },
+ {
+ "action": "plugin::users-permissions.advanced-settings.update",
+ "displayName": "Edit",
+ "plugin": "users-permissions",
+ "subCategory": "advancedSettings",
+ },
+ {
+ "action": "plugin::users-permissions.email-templates.read",
+ "displayName": "Read",
+ "plugin": "users-permissions",
+ "subCategory": "emailTemplates",
+ },
+ {
+ "action": "plugin::users-permissions.email-templates.update",
+ "displayName": "Edit",
+ "plugin": "users-permissions",
+ "subCategory": "emailTemplates",
+ },
+ {
+ "action": "plugin::users-permissions.providers.read",
+ "displayName": "Read",
+ "plugin": "users-permissions",
+ "subCategory": "providers",
+ },
+ {
+ "action": "plugin::users-permissions.providers.update",
+ "displayName": "Edit",
+ "plugin": "users-permissions",
+ "subCategory": "providers",
+ },
+ {
+ "action": "plugin::users-permissions.roles.create",
+ "displayName": "Create",
+ "plugin": "users-permissions",
+ "subCategory": "roles",
+ },
+ {
+ "action": "plugin::users-permissions.roles.delete",
+ "displayName": "Delete",
+ "plugin": "users-permissions",
+ "subCategory": "roles",
+ },
+ {
+ "action": "plugin::users-permissions.roles.read",
+ "displayName": "Read",
+ "plugin": "users-permissions",
+ "subCategory": "roles",
+ },
+ {
+ "action": "plugin::users-permissions.roles.update",
+ "displayName": "Update",
+ "plugin": "users-permissions",
+ "subCategory": "roles",
+ },
+ ],
+ "settings": [
+ {
+ "action": "admin::api-tokens.access",
+ "category": "api tokens",
+ "displayName": "Access the API tokens settings page",
+ "subCategory": "api Tokens",
+ },
+ {
+ "action": "admin::api-tokens.create",
+ "category": "api tokens",
+ "displayName": "Create (generate)",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::api-tokens.delete",
+ "category": "api tokens",
+ "displayName": "Delete (revoke)",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::api-tokens.read",
+ "category": "api tokens",
+ "displayName": "Read",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::api-tokens.regenerate",
+ "category": "api tokens",
+ "displayName": "Regenerate",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::api-tokens.update",
+ "category": "api tokens",
+ "displayName": "Update",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::marketplace.read",
+ "category": "plugins and marketplace",
+ "displayName": "Access the marketplace",
+ "subCategory": "marketplace",
+ },
+ {
+ "action": "admin::project-settings.read",
+ "category": "project",
+ "displayName": "Read the project level settings",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::project-settings.update",
+ "category": "project",
+ "displayName": "Update the project level settings",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::roles.create",
+ "category": "users and roles",
+ "displayName": "Create",
+ "subCategory": "roles",
+ },
+ {
+ "action": "admin::roles.delete",
+ "category": "users and roles",
+ "displayName": "Delete",
+ "subCategory": "roles",
+ },
+ {
+ "action": "admin::roles.read",
+ "category": "users and roles",
+ "displayName": "Read",
+ "subCategory": "roles",
+ },
+ {
+ "action": "admin::roles.update",
+ "category": "users and roles",
+ "displayName": "Update",
+ "subCategory": "roles",
+ },
+ {
+ "action": "admin::transfer.tokens.access",
+ "category": "transfer tokens",
+ "displayName": "Access the transfer tokens settings page",
+ "subCategory": "transfer tokens",
+ },
+ {
+ "action": "admin::transfer.tokens.create",
+ "category": "transfer tokens",
+ "displayName": "Create (generate)",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::transfer.tokens.delete",
+ "category": "transfer tokens",
+ "displayName": "Delete (revoke)",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::transfer.tokens.read",
+ "category": "transfer tokens",
+ "displayName": "Read",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::transfer.tokens.regenerate",
+ "category": "transfer tokens",
+ "displayName": "Regenerate",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::transfer.tokens.update",
+ "category": "transfer tokens",
+ "displayName": "Update",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::users.create",
+ "category": "users and roles",
+ "displayName": "Create (invite)",
+ "subCategory": "users",
+ },
+ {
+ "action": "admin::users.delete",
+ "category": "users and roles",
+ "displayName": "Delete",
+ "subCategory": "users",
+ },
+ {
+ "action": "admin::users.read",
+ "category": "users and roles",
+ "displayName": "Read",
+ "subCategory": "users",
+ },
+ {
+ "action": "admin::users.update",
+ "category": "users and roles",
+ "displayName": "Update",
+ "subCategory": "users",
+ },
+ {
+ "action": "admin::webhooks.create",
+ "category": "webhooks",
+ "displayName": "Create",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::webhooks.delete",
+ "category": "webhooks",
+ "displayName": "Delete",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::webhooks.read",
+ "category": "webhooks",
+ "displayName": "Read",
+ "subCategory": "general",
+ },
+ {
+ "action": "admin::webhooks.update",
+ "category": "webhooks",
+ "displayName": "Update",
+ "subCategory": "general",
+ },
+ {
+ "action": "plugin::documentation.settings.read",
+ "category": "documentation",
+ "displayName": "Access the documentation settings page",
+ "subCategory": "general",
+ },
+ {
+ "action": "plugin::email.settings.read",
+ "category": "email",
+ "displayName": "Access the Email Settings page",
+ "subCategory": "general",
+ },
+ {
+ "action": "plugin::i18n.locale.create",
+ "category": "Internationalization",
+ "displayName": "Create",
+ "subCategory": "Locales",
+ },
+ {
+ "action": "plugin::i18n.locale.delete",
+ "category": "Internationalization",
+ "displayName": "Delete",
+ "subCategory": "Locales",
+ },
+ {
+ "action": "plugin::i18n.locale.read",
+ "category": "Internationalization",
+ "displayName": "Read",
+ "subCategory": "Locales",
+ },
+ {
+ "action": "plugin::i18n.locale.update",
+ "category": "Internationalization",
+ "displayName": "Update",
+ "subCategory": "Locales",
+ },
+ {
+ "action": "plugin::upload.settings.read",
+ "category": "media library",
+ "displayName": "Access the Media Library settings page",
+ "subCategory": "general",
+ },
+ ],
+ "singleTypes": [
+ [
+ {
+ "actionId": "plugin::content-manager.explorer.create",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Create",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
},
{
- "action": "plugin::i18n.locale.delete",
- "category": "Internationalization",
- "displayName": "Delete",
- "subCategory": "Locales",
+ "actionId": "plugin::content-manager.explorer.read",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Read",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
},
{
- "action": "plugin::i18n.locale.read",
- "category": "Internationalization",
- "displayName": "Read",
- "subCategory": "Locales",
+ "actionId": "plugin::content-manager.explorer.update",
+ "applyToProperties": [
+ "fields",
+ "locales",
+ ],
+ "label": "Update",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
},
{
- "action": "plugin::i18n.locale.update",
- "category": "Internationalization",
- "displayName": "Update",
- "subCategory": "Locales",
+ "actionId": "plugin::content-manager.explorer.delete",
+ "applyToProperties": [
+ "locales",
+ ],
+ "label": "Delete",
+ "subjects": [
+ "plugin::users-permissions.user",
+ ],
},
{
- "action": "plugin::upload.settings.read",
- "category": "media library",
- "displayName": "Access the Media Library settings page",
- "subCategory": "general",
+ "actionId": "plugin::content-manager.explorer.publish",
+ "applyToProperties": [
+ "locales",
+ ],
+ "label": "Publish",
+ "subjects": [],
},
],
- "singleTypes": [
- [
- {
- "actionId": "plugin::content-manager.explorer.create",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Create",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.read",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Read",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.update",
- "applyToProperties": [
- "fields",
- "locales",
- ],
- "label": "Update",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.delete",
- "applyToProperties": [
- "locales",
- ],
- "label": "Delete",
- "subjects": [
- "plugin::users-permissions.user",
- ],
- },
- {
- "actionId": "plugin::content-manager.explorer.publish",
- "applyToProperties": [
- "locales",
- ],
- "label": "Publish",
- "subjects": [],
- },
- ],
- [],
- ],
- },
- }
- `);
- }
+ [],
+ ],
+ },
+ }
+ `);
}
});
});
diff --git a/packages/core/admin/server/tests/admin-permissions-conditions.test.api.js b/packages/core/admin/server/tests/admin-permissions-conditions.test.api.js
index fcdfd125602..6da010f7214 100644
--- a/packages/core/admin/server/tests/admin-permissions-conditions.test.api.js
+++ b/packages/core/admin/server/tests/admin-permissions-conditions.test.api.js
@@ -6,197 +6,189 @@ const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
const { createRequest, createAuthRequest } = require('../../../../../test/helpers/request');
const { createUtils } = require('../../../../../test/helpers/utils');
-const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE';
-
-if (edition === 'EE') {
- describe('Admin Permissions - Conditions', () => {
- let strapi;
- let utils;
- const builder = createTestBuilder();
- const requests = {
- public: null,
- admin: null,
- };
-
- const localTestData = {
- models: {
- article: {
- singularName: 'article',
- pluralName: 'articles',
- displayName: 'Article',
- attributes: {
- title: {
- type: 'string',
- },
- price: {
- type: 'integer',
- },
+describe('Admin Permissions - Conditions', () => {
+ let strapi;
+ let utils;
+ const builder = createTestBuilder();
+ const requests = {
+ public: null,
+ admin: null,
+ };
+
+ const localTestData = {
+ models: {
+ article: {
+ singularName: 'article',
+ pluralName: 'articles',
+ displayName: 'Article',
+ attributes: {
+ title: {
+ type: 'string',
+ },
+ price: {
+ type: 'integer',
},
},
},
- entry: {
- name: 'Test Article',
- price: 999,
+ },
+ entry: {
+ name: 'Test Article',
+ price: 999,
+ },
+ role: {
+ name: 'foobar',
+ description: 'A dummy test role',
+ },
+ permissions: [
+ {
+ action: 'plugin::content-manager.explorer.create',
+ subject: 'api::article.article',
+ fields: null,
+ conditions: [],
},
- role: {
- name: 'foobar',
- description: 'A dummy test role',
+ {
+ action: 'plugin::content-manager.explorer.read',
+ subject: 'api::article.article',
+ fields: null,
+ conditions: ['admin::has-same-role-as-creator'],
},
- permissions: [
- {
- action: 'plugin::content-manager.explorer.create',
- subject: 'api::article.article',
- fields: null,
- conditions: [],
- },
- {
- action: 'plugin::content-manager.explorer.read',
- subject: 'api::article.article',
- fields: null,
- conditions: ['admin::has-same-role-as-creator'],
- },
- {
- action: 'plugin::content-manager.explorer.delete',
- subject: 'api::article.article',
- fields: null,
- conditions: ['admin::is-creator'],
- },
- ],
- userPassword: 'fooBar42',
- users: [
- { firstname: 'Alice', lastname: 'Foo', email: 'alice.foo@test.com' },
- { firstname: 'Bob', lastname: 'Bar', email: 'bob.bar@test.com' },
- ],
- };
+ {
+ action: 'plugin::content-manager.explorer.delete',
+ subject: 'api::article.article',
+ fields: null,
+ conditions: ['admin::is-creator'],
+ },
+ ],
+ userPassword: 'fooBar42',
+ users: [
+ { firstname: 'Alice', lastname: 'Foo', email: 'alice.foo@test.com' },
+ { firstname: 'Bob', lastname: 'Bar', email: 'bob.bar@test.com' },
+ ],
+ };
- const createFixtures = async () => {
- // Login with admin and init admin tools
- requests.admin = await createAuthRequest({ strapi });
- requests.public = createRequest({ strapi });
+ const createFixtures = async () => {
+ // Login with admin and init admin tools
+ requests.admin = await createAuthRequest({ strapi });
+ requests.public = createRequest({ strapi });
- // Create the foobar role
- const role = await utils.createRole(localTestData.role);
+ // Create the foobar role
+ const role = await utils.createRole(localTestData.role);
- // Assign permissions to the foobar role
- const permissions = await utils.assignPermissionsToRole(role.id, localTestData.permissions);
- Object.assign(role, { permissions });
+ // Assign permissions to the foobar role
+ const permissions = await utils.assignPermissionsToRole(role.id, localTestData.permissions);
+ Object.assign(role, { permissions });
- // Create users with the new role & create associated auth requests
- const users = [];
+ // Create users with the new role & create associated auth requests
+ const users = [];
- for (let i = 0; i < localTestData.users.length; i += 1) {
- const userFixture = localTestData.users[i];
- const userAttributes = {
- ...userFixture,
- password: localTestData.userPassword,
- roles: [role.id],
- };
+ for (let i = 0; i < localTestData.users.length; i += 1) {
+ const userFixture = localTestData.users[i];
+ const userAttributes = {
+ ...userFixture,
+ password: localTestData.userPassword,
+ roles: [role.id],
+ };
- const createdUser = await utils.createUser(userAttributes);
+ const createdUser = await utils.createUser(userAttributes);
- requests[createdUser.id] = await createAuthRequest({ strapi, userInfo: createdUser });
+ requests[createdUser.id] = await createAuthRequest({ strapi, userInfo: createdUser });
- users.push(createdUser);
- }
+ users.push(createdUser);
+ }
- // Update the local data store
- Object.assign(localTestData, { role, permissions, users });
- };
+ // Update the local data store
+ Object.assign(localTestData, { role, permissions, users });
+ };
- const getUserRequest = (idx) => requests[localTestData.users[idx].id];
- const getModelName = () => localTestData.models.article.singularName;
+ const getUserRequest = (idx) => requests[localTestData.users[idx].id];
+ const getModelName = () => localTestData.models.article.singularName;
- const deleteFixtures = async () => {
- // Delete users
- const usersId = localTestData.users.map(prop('id'));
- await utils.deleteUsersById(usersId);
+ const deleteFixtures = async () => {
+ // Delete users
+ const usersId = localTestData.users.map(prop('id'));
+ await utils.deleteUsersById(usersId);
- // Delete the foobar role
- await utils.deleteRolesById([localTestData.role.id]);
- };
+ // Delete the foobar role
+ await utils.deleteRolesById([localTestData.role.id]);
+ };
- beforeAll(async () => {
- await builder.addContentType(localTestData.models.article).build();
+ beforeAll(async () => {
+ await builder.addContentType(localTestData.models.article).build();
- strapi = await createStrapiInstance();
- utils = createUtils(strapi);
+ strapi = await createStrapiInstance();
+ utils = createUtils(strapi);
- await createFixtures();
- });
+ await createFixtures();
+ });
- afterAll(async () => {
- await deleteFixtures();
+ afterAll(async () => {
+ await deleteFixtures();
- await strapi.destroy();
- await builder.cleanup();
- });
+ await strapi.destroy();
+ await builder.cleanup();
+ });
- test('User A can create an entry', async () => {
- const rq = getUserRequest(0);
- const modelName = getModelName();
- const res = await rq({
- method: 'POST',
- url: `/content-manager/collection-types/api::${modelName}.${modelName}`,
- body: localTestData.entry,
- });
-
- expect(res.statusCode).toBe(200);
- localTestData.entry = res.body;
+ test('User A can create an entry', async () => {
+ const rq = getUserRequest(0);
+ const modelName = getModelName();
+ const res = await rq({
+ method: 'POST',
+ url: `/content-manager/collection-types/api::${modelName}.${modelName}`,
+ body: localTestData.entry,
});
- test('User A can read its entry', async () => {
- const { id } = localTestData.entry;
- const modelName = getModelName();
- const rq = getUserRequest(0);
- const res = await rq({
- method: 'GET',
- url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`,
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.body).toMatchObject(localTestData.entry);
- });
+ expect(res.statusCode).toBe(200);
+ localTestData.entry = res.body;
+ });
- test('User B can read the entry created by user A', async () => {
- const { id } = localTestData.entry;
- const modelName = getModelName();
- const rq = getUserRequest(1);
- const res = await rq({
- method: 'GET',
- url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`,
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.body).toMatchObject(localTestData.entry);
+ test('User A can read its entry', async () => {
+ const { id } = localTestData.entry;
+ const modelName = getModelName();
+ const rq = getUserRequest(0);
+ const res = await rq({
+ method: 'GET',
+ url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`,
});
- test('User B cannot delete the entry created by user A', async () => {
- const { id } = localTestData.entry;
- const modelName = getModelName();
- const rq = getUserRequest(1);
- const res = await rq({
- method: 'DELETE',
- url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`,
- });
+ expect(res.statusCode).toBe(200);
+ expect(res.body).toMatchObject(localTestData.entry);
+ });
- expect(res.statusCode).toBe(403);
+ test('User B can read the entry created by user A', async () => {
+ const { id } = localTestData.entry;
+ const modelName = getModelName();
+ const rq = getUserRequest(1);
+ const res = await rq({
+ method: 'GET',
+ url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`,
});
- test('User A can delete its entry', async () => {
- const { id } = localTestData.entry;
- const modelName = getModelName();
- const rq = getUserRequest(0);
- const res = await rq({
- method: 'DELETE',
- url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`,
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.body).toMatchObject(localTestData.entry);
+ expect(res.statusCode).toBe(200);
+ expect(res.body).toMatchObject(localTestData.entry);
+ });
+
+ test('User B cannot delete the entry created by user A', async () => {
+ const { id } = localTestData.entry;
+ const modelName = getModelName();
+ const rq = getUserRequest(1);
+ const res = await rq({
+ method: 'DELETE',
+ url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`,
});
+
+ expect(res.statusCode).toBe(403);
});
-} else {
- describe('Admin Permissions - Conditions ', () => {
- test.skip('Only in EE', () => {});
+
+ test('User A can delete its entry', async () => {
+ const { id } = localTestData.entry;
+ const modelName = getModelName();
+ const rq = getUserRequest(0);
+ const res = await rq({
+ method: 'DELETE',
+ url: `/content-manager/collection-types/api::${modelName}.${modelName}/${id}`,
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.body).toMatchObject(localTestData.entry);
});
-}
+});
diff --git a/packages/core/admin/server/tests/admin-role.test.api.js b/packages/core/admin/server/tests/admin-role.test.api.js
index e8ea1e564df..97870746670 100644
--- a/packages/core/admin/server/tests/admin-role.test.api.js
+++ b/packages/core/admin/server/tests/admin-role.test.api.js
@@ -5,8 +5,6 @@ const _ = require('lodash');
const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
const { createAuthRequest } = require('../../../../../test/helpers/request');
-const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE';
-
const data = {
rolesWithUsers: [],
rolesWithoutUsers: [],
@@ -106,644 +104,404 @@ describe('Role CRUD End to End', () => {
});
});
- if (edition === 'EE') {
- const newPermissions = [
- {
- action: 'plugin::users-permissions.roles.update',
- },
- {
- action: 'plugin::content-manager.explorer.create',
- subject: 'plugin::users-permissions.user',
- properties: { fields: ['username'], locales: [] },
- conditions: ['admin::is-creator'],
- },
- ];
-
- test('Conditions of editors and author can be modified', async () => {
- let res = await rq({
- url: `/admin/roles/${data.editorRole.id}/permissions`,
- method: 'PUT',
- body: { permissions: newPermissions },
- });
+ const newPermissions = [
+ {
+ action: 'plugin::users-permissions.roles.update',
+ },
+ {
+ action: 'plugin::content-manager.explorer.create',
+ subject: 'plugin::users-permissions.user',
+ properties: { fields: ['username'], locales: [] },
+ conditions: ['admin::is-creator'],
+ },
+ ];
+
+ test('Conditions of editors and author can be modified', async () => {
+ let res = await rq({
+ url: `/admin/roles/${data.editorRole.id}/permissions`,
+ method: 'PUT',
+ body: { permissions: newPermissions },
+ });
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toHaveLength(2);
- expect(res.body).toEqual({
- data: expect.arrayContaining([
- expect.objectContaining({
- action: 'plugin::users-permissions.roles.update',
- properties: {},
- conditions: [],
- subject: null,
- }),
- expect.objectContaining({
- action: 'plugin::content-manager.explorer.create',
- subject: 'plugin::users-permissions.user',
- properties: { fields: ['username'], locales: [] },
- conditions: ['admin::is-creator'],
- }),
- ]),
- });
+ expect(res.statusCode).toBe(200);
+ expect(res.body.data).toHaveLength(2);
+ expect(res.body).toEqual({
+ data: expect.arrayContaining([
+ expect.objectContaining({
+ action: 'plugin::users-permissions.roles.update',
+ properties: {},
+ conditions: [],
+ subject: null,
+ }),
+ expect.objectContaining({
+ action: 'plugin::content-manager.explorer.create',
+ subject: 'plugin::users-permissions.user',
+ properties: { fields: ['username'], locales: [] },
+ conditions: ['admin::is-creator'],
+ }),
+ ]),
+ });
- res = await rq({
- url: `/admin/roles/${data.authorRole.id}/permissions`,
- method: 'PUT',
- body: { permissions: newPermissions },
- });
+ res = await rq({
+ url: `/admin/roles/${data.authorRole.id}/permissions`,
+ method: 'PUT',
+ body: { permissions: newPermissions },
+ });
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toHaveLength(2);
- expect(res.body).toEqual({
- data: expect.arrayContaining([
- expect.objectContaining({
- action: 'plugin::users-permissions.roles.update',
- properties: {},
- conditions: [],
- subject: null,
- }),
- expect.objectContaining({
- action: 'plugin::content-manager.explorer.create',
- subject: 'plugin::users-permissions.user',
- properties: { fields: ['username'], locales: [] },
- conditions: ['admin::is-creator'],
- }),
- ]),
- });
+ expect(res.statusCode).toBe(200);
+ expect(res.body.data).toHaveLength(2);
+ expect(res.body).toEqual({
+ data: expect.arrayContaining([
+ expect.objectContaining({
+ action: 'plugin::users-permissions.roles.update',
+ properties: {},
+ conditions: [],
+ subject: null,
+ }),
+ expect.objectContaining({
+ action: 'plugin::content-manager.explorer.create',
+ subject: 'plugin::users-permissions.user',
+ properties: { fields: ['username'], locales: [] },
+ conditions: ['admin::is-creator'],
+ }),
+ ]),
});
- } else if (edition === 'CE') {
- const newPermissions = [
- {
- action: 'plugin::users-permissions.roles.update',
- },
- {
- action: 'plugin::users-permissions.roles.read',
- conditions: ['admin::is-creator'],
- },
- {
- action: 'plugin::content-manager.explorer.create',
- subject: 'plugin::users-permissions.user',
- properties: { fields: ['username'], locales: [] },
- conditions: ['admin::is-creator'],
- },
- {
- action: 'plugin::content-manager.explorer.update',
- subject: 'plugin::users-permissions.user',
- properties: { fields: ['username'], locales: [] },
- conditions: ['admin::is-creator'],
- },
- {
- action: 'plugin::content-manager.explorer.delete',
- subject: 'plugin::users-permissions.user',
- properties: { locales: [] },
- conditions: ['admin::is-creator'],
- },
- {
- action: 'plugin::content-manager.explorer.read',
- subject: 'plugin::users-permissions.user',
- properties: { fields: ['username'], locales: [] },
- conditions: ['admin::is-creator'],
- },
- ];
+ });
+ });
- test("Conditions of editors and author can't be modified", async () => {
- let res = await rq({
- url: `/admin/roles/${data.editorRole.id}/permissions`,
- method: 'PUT',
- body: { permissions: newPermissions },
- });
+ describe('Create some roles', () => {
+ const rolesToCreate = [
+ [{ name: 'new role 0', description: 'description' }],
+ [{ name: 'new role 1', description: 'description' }],
+ [{ name: 'new role 2', description: 'description' }],
+ [{ name: 'new role 3', description: 'description' }],
+ [{ name: 'new role 4', description: 'description' }],
+ [{ name: 'new role 5', description: 'description' }],
+ ];
+
+ test.each(rolesToCreate)('can create %p', async (role) => {
+ const res = await rq({
+ url: '/admin/roles',
+ method: 'POST',
+ body: role,
+ });
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toHaveLength(6);
- expect(res.body).toEqual({
- data: expect.arrayContaining(
- newPermissions
- .slice(3, 6)
- .map((p) => ({ ...p, conditions: [] }))
- .map((perm) => expect.objectContaining(perm))
- ),
- });
+ expect(res.statusCode).toBe(201);
+ expect(res.body.data).toMatchObject({
+ id: expect.anything(),
+ name: role.name,
+ description: role.description,
+ });
+ data.rolesWithoutUsers.push(res.body.data);
+ });
+ test('Cannot create a role already existing', async () => {
+ const role = _.pick(data.rolesWithoutUsers[0], ['name', 'description']);
+ const res = await rq({
+ url: '/admin/roles',
+ method: 'POST',
+ body: role,
+ });
- res = await rq({
- url: `/admin/roles/${data.authorRole.id}/permissions`,
- method: 'PUT',
- body: { permissions: newPermissions },
- });
+ expect(res.statusCode).toBe(400);
+ expect(res.body).toMatchObject({
+ data: null,
+ error: {
+ details: {},
+ message: 'The name must be unique and a role with name `new role 0` already exists.',
+ name: 'ApplicationError',
+ status: 400,
+ },
+ });
+ });
+ test('Can create a user with a role', async () => {
+ const user = {
+ email: 'new-user@strapi.io',
+ firstname: 'New',
+ lastname: 'User',
+ roles: [data.rolesWithoutUsers[5].id],
+ };
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toHaveLength(6);
- expect(res.body).toEqual({
- data: expect.arrayContaining(
- newPermissions
- .slice(3, 6)
- .map((p) => ({ ...p, conditions: ['admin::is-creator'] }))
- .map(expect.objectContaining)
- ),
- });
+ const res = await rq({
+ url: '/admin/users',
+ method: 'POST',
+ body: user,
});
- }
- });
- if (edition === 'EE') {
- describe('Create some roles', () => {
- const rolesToCreate = [
- [{ name: 'new role 0', description: 'description' }],
- [{ name: 'new role 1', description: 'description' }],
- [{ name: 'new role 2', description: 'description' }],
- [{ name: 'new role 3', description: 'description' }],
- [{ name: 'new role 4', description: 'description' }],
- [{ name: 'new role 5', description: 'description' }],
- ];
+ expect(res.statusCode).toBe(201);
- test.each(rolesToCreate)('can create %p', async (role) => {
- const res = await rq({
- url: '/admin/roles',
- method: 'POST',
- body: role,
- });
+ data.users.push(res.body.data);
+ data.rolesWithUsers.push(data.rolesWithoutUsers[5]);
+ data.rolesWithoutUsers.splice(5, 1);
+ });
+ });
- expect(res.statusCode).toBe(201);
- expect(res.body.data).toMatchObject({
- id: expect.anything(),
- name: role.name,
- description: role.description,
- });
- data.rolesWithoutUsers.push(res.body.data);
+ describe('Find a role', () => {
+ test('Can find a role successfully', async () => {
+ const res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
+ method: 'GET',
});
- test('Cannot create a role already existing', async () => {
- const role = _.pick(data.rolesWithoutUsers[0], ['name', 'description']);
- const res = await rq({
- url: '/admin/roles',
- method: 'POST',
- body: role,
- });
- expect(res.statusCode).toBe(400);
- expect(res.body).toMatchObject({
- data: null,
- error: {
- details: {},
- message: 'The name must be unique and a role with name `new role 0` already exists.',
- name: 'ApplicationError',
- status: 400,
- },
- });
+ expect(res.statusCode).toBe(200);
+ expect(res.body.data).toMatchObject({
+ id: data.rolesWithoutUsers[0].id,
+ name: data.rolesWithoutUsers[0].name,
+ description: data.rolesWithoutUsers[0].description,
+ usersCount: 0,
+ code: expect.anything(),
});
- test('Can create a user with a role', async () => {
- const user = {
- email: 'new-user@strapi.io',
- firstname: 'New',
- lastname: 'User',
- roles: [data.rolesWithoutUsers[5].id],
- };
+ expect(res.body.data.code.startsWith('new-role-0')).toBe(true);
+ });
+ });
- const res = await rq({
- url: '/admin/users',
- method: 'POST',
- body: user,
- });
+ describe('Find all roles', () => {
+ test('Can find all roles successfully', async () => {
+ const expectedRolesWithoutUser = data.rolesWithoutUsers.map((r) => ({
+ ...r,
+ usersCount: 0,
+ }));
+ const expectedRolesWithUser = data.rolesWithUsers.map((r) => ({ ...r, usersCount: 1 }));
+ const expectedRoles = expectedRolesWithoutUser.concat(expectedRolesWithUser);
- expect(res.statusCode).toBe(201);
+ const res = await rq({
+ url: '/admin/roles',
+ method: 'GET',
+ });
- data.users.push(res.body.data);
- data.rolesWithUsers.push(data.rolesWithoutUsers[5]);
- data.rolesWithoutUsers.splice(5, 1);
+ expect(res.statusCode).toBe(200);
+ expectedRoles.forEach((role) => {
+ expect(res.body.data).toEqual(
+ expect.arrayContaining([
+ expect.objectContaining({
+ id: role.id,
+ name: role.name,
+ description: role.description,
+ usersCount: role.usersCount,
+ code: expect.anything(),
+ }),
+ ])
+ );
});
});
+ });
- describe('Find a role', () => {
- test('Can find a role successfully', async () => {
- const res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
- method: 'GET',
- });
+ describe('Update a role', () => {
+ test('Can update name and description of a role successfully', async () => {
+ const updates = {
+ name: 'new name - Cannot update the name of a role',
+ description: 'new description - Can update a role successfully',
+ };
+ const res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
+ method: 'PUT',
+ body: updates,
+ });
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toMatchObject({
- id: data.rolesWithoutUsers[0].id,
- name: data.rolesWithoutUsers[0].name,
- description: data.rolesWithoutUsers[0].description,
- usersCount: 0,
- code: expect.anything(),
- });
- expect(res.body.data.code.startsWith('new-role-0')).toBe(true);
+ expect(res.statusCode).toBe(200);
+ expect(omitTimestamps(res.body.data)).toMatchObject({
+ ...omitTimestamps(data.rolesWithoutUsers[0]),
+ ...updates,
});
+ data.rolesWithoutUsers[0] = res.body.data;
});
- describe('Find all roles', () => {
- test('Can find all roles successfully', async () => {
- const expectedRolesWithoutUser = data.rolesWithoutUsers.map((r) => ({
- ...r,
- usersCount: 0,
- }));
- const expectedRolesWithUser = data.rolesWithUsers.map((r) => ({ ...r, usersCount: 1 }));
- const expectedRoles = expectedRolesWithoutUser.concat(expectedRolesWithUser);
-
- const res = await rq({
- url: '/admin/roles',
- method: 'GET',
- });
+ test('Can update description of a role successfully', async () => {
+ const updates = {
+ name: 'new name - Cannot update the name of a role',
+ description: 'new description - Can update description of a role successfully',
+ };
+ const res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
+ method: 'PUT',
+ body: updates,
+ });
- expect(res.statusCode).toBe(200);
- expectedRoles.forEach((role) => {
- expect(res.body.data).toEqual(
- expect.arrayContaining([
- expect.objectContaining({
- id: role.id,
- name: role.name,
- description: role.description,
- usersCount: role.usersCount,
- code: expect.anything(),
- }),
- ])
- );
- });
+ expect(res.statusCode).toBe(200);
+ expect(omitTimestamps(res.body.data)).toMatchObject({
+ ...omitTimestamps(data.rolesWithoutUsers[0]),
+ ...updates,
});
+ data.rolesWithoutUsers[0] = res.body.data;
});
- describe('Update a role', () => {
- test('Can update name and description of a role successfully', async () => {
- const updates = {
- name: 'new name - Cannot update the name of a role',
- description: 'new description - Can update a role successfully',
- };
- const res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
- method: 'PUT',
- body: updates,
- });
+ test('Cannot update the name of a role if already exists', async () => {
+ const updates = {
+ name: data.rolesWithoutUsers[0].name,
+ description: 'new description - Cannot update the name of a role if already exists',
+ };
+ const res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[1].id}`,
+ method: 'PUT',
+ body: updates,
+ });
- expect(res.statusCode).toBe(200);
- expect(omitTimestamps(res.body.data)).toMatchObject({
- ...omitTimestamps(data.rolesWithoutUsers[0]),
- ...updates,
- });
- data.rolesWithoutUsers[0] = res.body.data;
+ expect(res.statusCode).toBe(400);
+ expect(res.body).toMatchObject({
+ data: null,
+ error: {
+ details: {},
+ message:
+ 'The name must be unique and a role with name `new name - Cannot update the name of a role` already exists.',
+ name: 'ApplicationError',
+ status: 400,
+ },
});
+ });
- test('Can update description of a role successfully', async () => {
- const updates = {
- name: 'new name - Cannot update the name of a role',
- description: 'new description - Can update description of a role successfully',
- };
- const res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
- method: 'PUT',
- body: updates,
- });
+ test('Cannot update super admin role', async () => {
+ const updates = {
+ name: 'new name - Cannot update the name of a role',
+ description: 'new description - Can update a role successfully',
+ };
+ const res = await rq({
+ url: `/admin/roles/${data.superAdminRole.id}`,
+ method: 'PUT',
+ body: updates,
+ });
- expect(res.statusCode).toBe(200);
- expect(omitTimestamps(res.body.data)).toMatchObject({
- ...omitTimestamps(data.rolesWithoutUsers[0]),
- ...updates,
- });
- data.rolesWithoutUsers[0] = res.body.data;
- });
-
- test('Cannot update the name of a role if already exists', async () => {
- const updates = {
- name: data.rolesWithoutUsers[0].name,
- description: 'new description - Cannot update the name of a role if already exists',
- };
- const res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[1].id}`,
- method: 'PUT',
- body: updates,
+ expect(res.statusCode).toBe(400);
+ });
+ });
+
+ describe('Delete roles', () => {
+ describe('batch-delete', () => {
+ test("Don't delete the roles if some still have assigned users", async () => {
+ const roles = [data.rolesWithUsers[0], data.rolesWithUsers[0]];
+ const rolesIds = roles.map((r) => r.id);
+ let res = await rq({
+ url: '/admin/roles/batch-delete',
+ method: 'POST',
+ body: { ids: rolesIds },
});
expect(res.statusCode).toBe(400);
expect(res.body).toMatchObject({
data: null,
error: {
- details: {},
- message:
- 'The name must be unique and a role with name `new name - Cannot update the name of a role` already exists.',
- name: 'ApplicationError',
+ details: {
+ errors: [
+ {
+ message: 'Some roles are still assigned to some users',
+ name: 'ValidationError',
+ path: ['ids'],
+ },
+ ],
+ },
+ message: 'Some roles are still assigned to some users',
+ name: 'ValidationError',
status: 400,
},
});
- });
-
- test('Cannot update super admin role', async () => {
- const updates = {
- name: 'new name - Cannot update the name of a role',
- description: 'new description - Can update a role successfully',
- };
- const res = await rq({
- url: `/admin/roles/${data.superAdminRole.id}`,
- method: 'PUT',
- body: updates,
- });
-
- expect(res.statusCode).toBe(400);
- });
- });
-
- describe('Delete roles', () => {
- describe('batch-delete', () => {
- test("Don't delete the roles if some still have assigned users", async () => {
- const roles = [data.rolesWithUsers[0], data.rolesWithUsers[0]];
- const rolesIds = roles.map((r) => r.id);
- let res = await rq({
- url: '/admin/roles/batch-delete',
- method: 'POST',
- body: { ids: rolesIds },
- });
-
- expect(res.statusCode).toBe(400);
- expect(res.body).toMatchObject({
- data: null,
- error: {
- details: {
- errors: [
- {
- message: 'Some roles are still assigned to some users',
- name: 'ValidationError',
- path: ['ids'],
- },
- ],
- },
- message: 'Some roles are still assigned to some users',
- name: 'ValidationError',
- status: 400,
- },
- });
-
- for (const role of roles) {
- res = await rq({
- url: `/admin/roles/${role.id}`,
- method: 'GET',
- });
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toMatchObject(role);
- }
- });
-
- test('Can delete a role', async () => {
- let res = await rq({
- url: '/admin/roles/batch-delete',
- method: 'POST',
- body: { ids: [data.rolesWithoutUsers[0].id] },
- });
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toMatchObject([data.rolesWithoutUsers[0]]);
+ for (const role of roles) {
res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
+ url: `/admin/roles/${role.id}`,
method: 'GET',
});
- expect(res.statusCode).toBe(404);
-
- data.deleteRolesIds.push(data.rolesWithoutUsers[0].id);
- data.rolesWithoutUsers.shift();
- });
-
- test('Can delete two roles', async () => {
- const roles = data.rolesWithoutUsers.slice(0, 2);
- const rolesIds = roles.map((r) => r.id);
-
- let res = await rq({
- url: '/admin/roles/batch-delete',
- method: 'POST',
- body: { ids: rolesIds },
- });
expect(res.statusCode).toBe(200);
- expect(res.body.data).toMatchObject(roles);
-
- for (const roleId of rolesIds) {
- res = await rq({
- url: `/admin/roles/${roleId}`,
- method: 'GET',
- });
- expect(res.statusCode).toBe(404);
- data.deleteRolesIds.push(data.rolesWithoutUsers[0].id);
- data.rolesWithoutUsers.shift();
- }
- });
+ expect(res.body.data).toMatchObject(role);
+ }
});
- describe('simple delete', () => {
- test('Can delete a role', async () => {
- let res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
- method: 'DELETE',
- });
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toMatchObject(data.rolesWithoutUsers[0]);
-
- res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
- method: 'GET',
- });
- expect(res.statusCode).toBe(404);
+ test('Can delete a role', async () => {
+ let res = await rq({
+ url: '/admin/roles/batch-delete',
+ method: 'POST',
+ body: { ids: [data.rolesWithoutUsers[0].id] },
+ });
+ expect(res.statusCode).toBe(200);
+ expect(res.body.data).toMatchObject([data.rolesWithoutUsers[0]]);
- data.deleteRolesIds.push(data.rolesWithoutUsers[0].id);
- data.rolesWithoutUsers.shift();
+ res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
+ method: 'GET',
});
+ expect(res.statusCode).toBe(404);
- test("Don't delete a role if it still has assigned users", async () => {
- let res = await rq({
- url: `/admin/roles/${data.rolesWithUsers[0].id}`,
- method: 'DELETE',
- });
+ data.deleteRolesIds.push(data.rolesWithoutUsers[0].id);
+ data.rolesWithoutUsers.shift();
+ });
- expect(res.statusCode).toBe(400);
- expect(res.body).toMatchObject({
- data: null,
- error: {
- details: {
- errors: [
- {
- message: 'Some roles are still assigned to some users',
- name: 'ValidationError',
- path: ['id'],
- },
- ],
- },
- message: 'Some roles are still assigned to some users',
- name: 'ValidationError',
- status: 400,
- },
- });
+ test('Can delete two roles', async () => {
+ const roles = data.rolesWithoutUsers.slice(0, 2);
+ const rolesIds = roles.map((r) => r.id);
- res = await rq({
- url: `/admin/roles/${data.rolesWithUsers[0].id}`,
- method: 'GET',
- });
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toMatchObject(data.rolesWithUsers[0]);
+ let res = await rq({
+ url: '/admin/roles/batch-delete',
+ method: 'POST',
+ body: { ids: rolesIds },
});
+ expect(res.statusCode).toBe(200);
+ expect(res.body.data).toMatchObject(roles);
- test("Can't delete super admin role", async () => {
- let res = await rq({
- url: `/admin/roles/${data.superAdminRole.id}`,
- method: 'DELETE',
- });
-
- expect(res.statusCode).toBe(400);
- expect(res.body).toMatchObject({
- data: null,
- error: {
- details: {
- errors: [
- {
- message: 'You cannot delete the super admin role',
- name: 'ValidationError',
- path: ['id'],
- },
- ],
- },
- message: 'You cannot delete the super admin role',
- name: 'ValidationError',
- status: 400,
- },
- });
-
+ for (const roleId of rolesIds) {
res = await rq({
- url: `/admin/roles/${data.rolesWithUsers[0].id}`,
+ url: `/admin/roles/${roleId}`,
method: 'GET',
});
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toMatchObject(data.rolesWithUsers[0]);
- });
+ expect(res.statusCode).toBe(404);
+ data.deleteRolesIds.push(data.rolesWithoutUsers[0].id);
+ data.rolesWithoutUsers.shift();
+ }
});
});
- describe("Roles don't exist", () => {
- test("Cannot update a role if it doesn't exist", async () => {
- const updates = {
- name: "new name - Cannot update a role if it doesn't exist",
- description: "new description - Cannot update a role if it doesn't exist",
- };
- const res = await rq({
- url: `/admin/roles/${data.deleteRolesIds[0]}`,
- method: 'PUT',
- body: updates,
- });
-
- expect(res.statusCode).toBe(404);
- expect(res.body).toMatchObject({
- error: {
- details: {},
- message: 'role.notFound',
- name: 'NotFoundError',
- status: 404,
- },
- });
- });
-
- test("Simple delete - No error if deleting a role that doesn't exist", async () => {
- const res = await rq({
- url: `/admin/roles/${data.deleteRolesIds[0]}`,
+ describe('simple delete', () => {
+ test('Can delete a role', async () => {
+ let res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
method: 'DELETE',
});
-
expect(res.statusCode).toBe(200);
- expect(res.body.data).toEqual(null);
- });
- test("Batch Delete - No error if deleting a role that doesn't exist", async () => {
- const res = await rq({
- url: '/admin/roles/batch-delete',
- method: 'POST',
- body: { ids: [data.deleteRolesIds[0]] },
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.body.data).toEqual([]);
- });
- });
+ expect(res.body.data).toMatchObject(data.rolesWithoutUsers[0]);
- describe('get & update Permissions', () => {
- test('get permissions on empty role', async () => {
- const res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`,
+ res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}`,
method: 'GET',
});
+ expect(res.statusCode).toBe(404);
- expect(res.statusCode).toBe(200);
- expect(res.body).toEqual({
- data: [],
- });
+ data.deleteRolesIds.push(data.rolesWithoutUsers[0].id);
+ data.rolesWithoutUsers.shift();
});
- test('assign permissions on role', async () => {
- const res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`,
- method: 'PUT',
- body: {
- permissions: [
- {
- action: 'plugin::users-permissions.roles.update',
- },
- {
- action: 'plugin::content-manager.explorer.create',
- subject: 'plugin::users-permissions.user',
- properties: { fields: ['username'], locales: [] },
- conditions: ['admin::is-creator'],
- },
- ],
- },
- });
-
- expect(res.statusCode).toBe(200);
- expect(res.body.data.length > 0).toBe(true);
- res.body.data.forEach((permission) => {
- expect(permission).toMatchObject({
- id: expect.anything(),
- action: expect.any(String),
- subject: expect.stringOrNull(),
- });
-
- if (permission.conditions.length > 0) {
- expect(permission.conditions).toEqual(expect.arrayContaining([expect.any(String)]));
- }
- if (permission.fields && permission.fields.length > 0) {
- expect(permission.fields).toEqual(expect.arrayContaining([expect.any(String)]));
- }
+ test("Don't delete a role if it still has assigned users", async () => {
+ let res = await rq({
+ url: `/admin/roles/${data.rolesWithUsers[0].id}`,
+ method: 'DELETE',
});
- });
- test('assign permissions on role with an unknown condition', async () => {
- const permissions = [
- {
- action: 'plugin::users-permissions.roles.update',
- },
- {
- action: 'plugin::content-manager.explorer.create',
- subject: 'plugin::users-permissions.user',
- properties: { fields: ['username'], locales: [] },
- conditions: ['admin::is-creator'],
- },
- ];
- const res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`,
- method: 'PUT',
- body: {
- permissions: [
- permissions[0],
- {
- ...permissions[1],
- conditions: [...permissions[1].conditions, 'unknown-condition'],
- },
- ],
+ expect(res.statusCode).toBe(400);
+ expect(res.body).toMatchObject({
+ data: null,
+ error: {
+ details: {
+ errors: [
+ {
+ message: 'Some roles are still assigned to some users',
+ name: 'ValidationError',
+ path: ['id'],
+ },
+ ],
+ },
+ message: 'Some roles are still assigned to some users',
+ name: 'ValidationError',
+ status: 400,
},
});
+ res = await rq({
+ url: `/admin/roles/${data.rolesWithUsers[0].id}`,
+ method: 'GET',
+ });
expect(res.statusCode).toBe(200);
- expect(res.body.data).toHaveLength(1);
- expect(res.body.data[0]).toMatchObject(permissions[1]);
+ expect(res.body.data).toMatchObject(data.rolesWithUsers[0]);
});
- test("can't assign non-existing permissions on role", async () => {
- const res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`,
- method: 'PUT',
- body: {
- permissions: [
- {
- action: 'non.existing.action',
- },
- ],
- },
+ test("Can't delete super admin role", async () => {
+ let res = await rq({
+ url: `/admin/roles/${data.superAdminRole.id}`,
+ method: 'DELETE',
});
expect(res.statusCode).toBe(400);
@@ -753,61 +511,209 @@ describe('Role CRUD End to End', () => {
details: {
errors: [
{
- message: 'action is not an existing permission action',
+ message: 'You cannot delete the super admin role',
name: 'ValidationError',
- path: ['permissions', '0', 'action'],
+ path: ['id'],
},
],
},
- message: 'action is not an existing permission action',
+ message: 'You cannot delete the super admin role',
name: 'ValidationError',
status: 400,
},
});
- });
- test('get permissions role', async () => {
- const res = await rq({
- url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`,
+ res = await rq({
+ url: `/admin/roles/${data.rolesWithUsers[0].id}`,
method: 'GET',
});
-
expect(res.statusCode).toBe(200);
- expect(res.body.data.length > 0).toBe(true);
- res.body.data.forEach((permission) => {
- expect(permission).toMatchObject({
- id: expect.anything(),
- action: expect.any(String),
- subject: expect.stringOrNull(),
- });
+ expect(res.body.data).toMatchObject(data.rolesWithUsers[0]);
+ });
+ });
+ });
+
+ describe("Roles don't exist", () => {
+ test("Cannot update a role if it doesn't exist", async () => {
+ const updates = {
+ name: "new name - Cannot update a role if it doesn't exist",
+ description: "new description - Cannot update a role if it doesn't exist",
+ };
+ const res = await rq({
+ url: `/admin/roles/${data.deleteRolesIds[0]}`,
+ method: 'PUT',
+ body: updates,
+ });
+
+ expect(res.statusCode).toBe(404);
+ expect(res.body).toMatchObject({
+ error: {
+ details: {},
+ message: 'role.notFound',
+ name: 'NotFoundError',
+ status: 404,
+ },
+ });
+ });
+
+ test("Simple delete - No error if deleting a role that doesn't exist", async () => {
+ const res = await rq({
+ url: `/admin/roles/${data.deleteRolesIds[0]}`,
+ method: 'DELETE',
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.body.data).toEqual(null);
+ });
+ test("Batch Delete - No error if deleting a role that doesn't exist", async () => {
+ const res = await rq({
+ url: '/admin/roles/batch-delete',
+ method: 'POST',
+ body: { ids: [data.deleteRolesIds[0]] },
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.body.data).toEqual([]);
+ });
+ });
- if (permission.conditions.length > 0) {
- expect(permission.conditions).toEqual(expect.arrayContaining([expect.any(String)]));
- }
- if (permission.fields && permission.fields.length > 0) {
- expect(permission.fields).toEqual(expect.arrayContaining([expect.any(String)]));
- }
+ describe('get & update Permissions', () => {
+ test('get permissions on empty role', async () => {
+ const res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`,
+ method: 'GET',
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.body).toEqual({
+ data: [],
+ });
+ });
+
+ test('assign permissions on role', async () => {
+ const res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`,
+ method: 'PUT',
+ body: {
+ permissions: [
+ {
+ action: 'plugin::users-permissions.roles.update',
+ },
+ {
+ action: 'plugin::content-manager.explorer.create',
+ subject: 'plugin::users-permissions.user',
+ properties: { fields: ['username'], locales: [] },
+ conditions: ['admin::is-creator'],
+ },
+ ],
+ },
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.body.data.length > 0).toBe(true);
+ res.body.data.forEach((permission) => {
+ expect(permission).toMatchObject({
+ id: expect.anything(),
+ action: expect.any(String),
+ subject: expect.stringOrNull(),
});
+
+ if (permission.conditions.length > 0) {
+ expect(permission.conditions).toEqual(expect.arrayContaining([expect.any(String)]));
+ }
+ if (permission.fields && permission.fields.length > 0) {
+ expect(permission.fields).toEqual(expect.arrayContaining([expect.any(String)]));
+ }
});
});
- }
-
- if (edition === 'CE') {
- describe('Cannot create a new role', () => {
- test('Cannot create a role successfully', async () => {
- const role = {
- name: 'new role',
- description: 'Description of new role',
- };
-
- const res = await rq({
- url: '/admin/roles',
- method: 'POST',
- body: role,
+
+ test('assign permissions on role with an unknown condition', async () => {
+ const permissions = [
+ {
+ action: 'plugin::users-permissions.roles.update',
+ },
+ {
+ action: 'plugin::content-manager.explorer.create',
+ subject: 'plugin::users-permissions.user',
+ properties: { fields: ['username'], locales: [] },
+ conditions: ['admin::is-creator'],
+ },
+ ];
+ const res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`,
+ method: 'PUT',
+ body: {
+ permissions: [
+ permissions[0],
+ {
+ ...permissions[1],
+ conditions: [...permissions[1].conditions, 'unknown-condition'],
+ },
+ ],
+ },
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.body.data).toHaveLength(1);
+ expect(res.body.data[0]).toMatchObject(permissions[1]);
+ });
+
+ test("can't assign non-existing permissions on role", async () => {
+ const res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`,
+ method: 'PUT',
+ body: {
+ permissions: [
+ {
+ action: 'non.existing.action',
+ },
+ ],
+ },
+ });
+
+ expect(res.statusCode).toBe(400);
+ expect(res.body).toMatchObject({
+ data: null,
+ error: {
+ details: {
+ errors: [
+ {
+ message: 'action is not an existing permission action',
+ name: 'ValidationError',
+ path: ['permissions', '0', 'action'],
+ },
+ ],
+ },
+ message: 'action is not an existing permission action',
+ name: 'ValidationError',
+ status: 400,
+ },
+ });
+ });
+
+ test('get permissions role', async () => {
+ const res = await rq({
+ url: `/admin/roles/${data.rolesWithoutUsers[0].id}/permissions`,
+ method: 'GET',
+ });
+
+ expect(res.statusCode).toBe(200);
+ expect(res.body.data.length > 0).toBe(true);
+
+ res.body.data.forEach((permission) => {
+ expect(permission).toMatchObject({
+ id: expect.anything(),
+ action: expect.any(String),
+ subject: expect.stringOrNull(),
});
- expect(res.statusCode).toBe(405);
+ if (permission.conditions.length > 0) {
+ expect(permission.conditions).toEqual(expect.arrayContaining([expect.any(String)]));
+ }
+ if (permission.fields && permission.fields.length > 0) {
+ expect(permission.fields).toEqual(expect.arrayContaining([expect.any(String)]));
+ }
});
});
- }
+ });
});
diff --git a/packages/core/admin/server/tests/admin-user.test.api.js b/packages/core/admin/server/tests/admin-user.test.api.js
index 7a04853aa21..2cf6de4d876 100644
--- a/packages/core/admin/server/tests/admin-user.test.api.js
+++ b/packages/core/admin/server/tests/admin-user.test.api.js
@@ -5,8 +5,6 @@ const { createStrapiInstance } = require('../../../../../test/helpers/strapi');
const { createAuthRequest } = require('../../../../../test/helpers/request');
const { createUtils } = require('../../../../../test/helpers/utils');
-const edition = process.env.STRAPI_DISABLE_EE === 'true' ? 'CE' : 'EE';
-
const omitTimestamps = omit(['updatedAt', 'createdAt']);
const omitRegistrationToken = omit(['registrationToken']);
@@ -59,11 +57,7 @@ describe('Admin User CRUD (api)', () => {
rq = await createAuthRequest({ strapi });
utils = createUtils(strapi);
- if (edition === 'EE') {
- testData.role = await createUserRole();
- } else {
- testData.role = await utils.getSuperAdminRole();
- }
+ testData.role = await createUserRole();
testData.firstSuperAdminUser = rq.getLoggedUser();
testData.superAdminRole = await utils.getSuperAdminRole();
@@ -71,9 +65,8 @@ describe('Admin User CRUD (api)', () => {
// Cleanup actions
afterAll(async () => {
- if (edition === 'EE') {
- await utils.deleteRolesById([testData.role.id]);
- }
+ await utils.deleteRolesById([testData.role.id]);
+
await strapi.destroy();
});
diff --git a/packages/core/admin/server/validation/permission.js b/packages/core/admin/server/validation/permission.js
index 7fea301f86c..11cf98640b3 100644
--- a/packages/core/admin/server/validation/permission.js
+++ b/packages/core/admin/server/validation/permission.js
@@ -1,83 +1,9 @@
'use strict';
-const _ = require('lodash');
const { yup, validateYupSchema } = require('@strapi/utils');
const { getService } = require('../utils');
-const { AUTHOR_CODE, PUBLISH_ACTION } = require('../services/constants');
-const {
- BOUND_ACTIONS_FOR_FIELDS,
- BOUND_ACTIONS,
- getBoundActionsBySubject,
-} = require('../domain/role');
const validators = require('./common-validators');
-// validatedUpdatePermissionsInput
-
-const actionFieldsAreEqual = (a, b) => {
- const aFields = a.properties.fields || [];
- const bFields = b.properties.fields || [];
-
- return _.isEqual(aFields.sort(), bFields.sort());
-};
-
-const haveSameFieldsAsOtherActions = (a, i, allActions) =>
- allActions.slice(i + 1).every((b) => actionFieldsAreEqual(a, b));
-
-const checkPermissionsAreBound = (role) =>
- function (permissions) {
- const permsBySubject = _.groupBy(
- permissions.filter((perm) => BOUND_ACTIONS.includes(perm.action)),
- 'subject'
- );
-
- for (const [subject, perms] of Object.entries(permsBySubject)) {
- const boundActions = getBoundActionsBySubject(role, subject);
- const missingActions =
- _.xor(
- perms.map((p) => p.action),
- boundActions
- ).length !== 0;
- if (missingActions) return false;
-
- const permsBoundByFields = perms.filter((p) => BOUND_ACTIONS_FOR_FIELDS.includes(p.action));
- const everyActionsHaveSameFields = _.every(permsBoundByFields, haveSameFieldsAsOtherActions);
- if (!everyActionsHaveSameFields) return false;
- }
-
- return true;
- };
-
-const noPublishPermissionForAuthorRole = (role) =>
- function (permissions) {
- const isAuthor = role.code === AUTHOR_CODE;
- const hasPublishPermission = permissions.some((perm) => perm.action === PUBLISH_ACTION);
-
- return !(isAuthor && hasPublishPermission);
- };
-
-const getUpdatePermissionsSchemas = (role) => [
- validators.updatePermissions,
- yup.object().shape({ permissions: actionsExistSchema.clone() }),
- yup.object().shape({
- permissions: yup
- .array()
- .test(
- 'author-no-publish',
- 'The author role cannot have the publish permission.',
- noPublishPermissionForAuthorRole(role)
- ),
- }),
- yup.object().shape({
- permissions: yup
- .array()
- .test(
- 'are-bond',
- 'Permissions have to be defined all together for a subject field or not at all',
- checkPermissionsAreBound(role)
- ),
- }),
-];
-
const checkPermissionsSchema = yup.object().shape({
permissions: yup.array().of(
yup
@@ -91,13 +17,6 @@ const checkPermissionsSchema = yup.object().shape({
),
});
-const validatedUpdatePermissionsInput = async (permissions, role) => {
- const schemas = getUpdatePermissionsSchemas(role);
- for (const schema of schemas) {
- await validateYupSchema(schema)(permissions);
- }
-};
-
// validatePermissionsExist
const checkPermissionsExist = function (permissions) {
@@ -131,7 +50,8 @@ const actionsExistSchema = yup
// exports
module.exports = {
- validatedUpdatePermissionsInput,
+ // validatedUpdatePermissionsInput,
+ validatedUpdatePermissionsInput: validateYupSchema(validators.updatePermissions),
validatePermissionsExist: validateYupSchema(actionsExistSchema),
validateCheckPermissionsInput: validateYupSchema(checkPermissionsSchema),
};
diff --git a/packages/core/admin/server/validation/role.js b/packages/core/admin/server/validation/role.js
index 63fc5c27192..5e943ed1773 100644
--- a/packages/core/admin/server/validation/role.js
+++ b/packages/core/admin/server/validation/role.js
@@ -2,6 +2,47 @@
const { yup, validateYupSchema } = require('@strapi/utils');
+const roleCreateSchema = yup
+ .object()
+ .shape({
+ name: yup.string().min(1).required(),
+ description: yup.string().nullable(),
+ })
+ .noUnknown();
+
+const rolesDeleteSchema = yup
+ .object()
+ .shape({
+ ids: yup
+ .array()
+ .of(yup.strapiID())
+ .min(1)
+ .required()
+ .test('roles-deletion-checks', 'Roles deletion checks have failed', async function (ids) {
+ try {
+ await strapi.admin.services.role.checkRolesIdForDeletion(ids);
+ } catch (e) {
+ return this.createError({ path: 'ids', message: e.message });
+ }
+
+ return true;
+ }),
+ })
+ .noUnknown();
+
+const roleDeleteSchema = yup
+ .strapiID()
+ .required()
+ .test('no-admin-single-delete', 'Role deletion checks have failed', async function (id) {
+ try {
+ await strapi.admin.services.role.checkRolesIdForDeletion([id]);
+ } catch (e) {
+ return this.createError({ path: 'id', message: e.message });
+ }
+
+ return true;
+ });
+
const roleUpdateSchema = yup
.object()
.shape({
@@ -12,4 +53,7 @@ const roleUpdateSchema = yup
module.exports = {
validateRoleUpdateInput: validateYupSchema(roleUpdateSchema),
+ validateRoleCreateInput: validateYupSchema(roleCreateSchema),
+ validateRolesDeleteInput: validateYupSchema(rolesDeleteSchema),
+ validateRoleDeleteInput: validateYupSchema(roleDeleteSchema),
};
From 6a003d738c2af89ed0c56dffdec5371fc756d271 Mon Sep 17 00:00:00 2001
From: Alexandre Bodin
Date: Mon, 6 Mar 2023 22:15:57 +0100
Subject: [PATCH 2/8] Move frontend
---
.../src/components/UpgradePlanModal/index.js | 123 -----
.../UpgradePlanModal/tests/index.test.js | 66 ---
.../pages/Roles/CreatePage/index.js | 270 ++++++++++-
.../tests/__snapshots__/index.test.js.snap | 0
.../Roles/CreatePage/tests/index.test.js | 4 +-
.../pages/Roles/CreatePage/utils/schema.js | 0
.../ConditionsModal/ActionRow/index.js | 3 +-
.../ActionRow/utils/constants.js | 3 -
.../ContentTypeCollapse/Collapse/index.js | 5 +-
.../Collapse/utils/constants.js | 3 -
.../CollapsePropertyMatrix/ActionRow/index.js | 5 +-
.../ActionRow/utils/constants.js | 3 -
.../SubActionRow/index.js | 5 +-
.../SubActionRow/utils/constants.js | 3 -
.../components/GlobalActions/index.js | 3 +-
.../GlobalActions/utils/constants.js | 3 -
.../PluginsAndSettings/SubCategory/index.js | 10 +-
.../SubCategory/utils/constants.js | 3 -
.../pages/Roles/ListPage/index.js | 427 +++++++++++++-----
.../pages/Roles/ListPage/reducer.js | 0
.../Roles/ListPage/tests/reducer.test.js | 0
.../pages/SettingsPage/utils/defaultRoutes.js | 28 +-
.../core/admin/admin/src/translations/ca.json | 20 +-
.../core/admin/admin/src/translations/de.json | 6 -
.../core/admin/admin/src/translations/dk.json | 6 -
.../core/admin/admin/src/translations/en.json | 6 -
.../core/admin/admin/src/translations/es.json | 6 -
.../core/admin/admin/src/translations/eu.json | 6 -
.../core/admin/admin/src/translations/fr.json | 6 -
.../core/admin/admin/src/translations/gu.json | 6 -
.../core/admin/admin/src/translations/he.json | 6 -
.../core/admin/admin/src/translations/hi.json | 6 -
.../core/admin/admin/src/translations/hu.json | 6 -
.../core/admin/admin/src/translations/id.json | 6 -
.../core/admin/admin/src/translations/it.json | 6 -
.../core/admin/admin/src/translations/ja.json | 6 -
.../core/admin/admin/src/translations/ko.json | 6 -
.../core/admin/admin/src/translations/ml.json | 6 -
.../core/admin/admin/src/translations/nl.json | 6 -
.../core/admin/admin/src/translations/no.json | 6 -
.../core/admin/admin/src/translations/pl.json | 6 -
.../admin/admin/src/translations/pt-BR.json | 6 -
.../core/admin/admin/src/translations/ru.json | 6 -
.../core/admin/admin/src/translations/sa.json | 6 -
.../core/admin/admin/src/translations/sk.json | 6 -
.../core/admin/admin/src/translations/sv.json | 6 -
.../core/admin/admin/src/translations/th.json | 6 -
.../core/admin/admin/src/translations/tr.json | 6 -
.../admin/admin/src/translations/zh-Hans.json | 6 -
.../core/admin/admin/src/translations/zh.json | 6 -
.../pages/Roles/CreatePage/index.js | 270 -----------
.../ActionRow/utils/constants.js | 3 -
.../MenuList/utils/constants.js | 3 -
.../Collapse/utils/constants.js | 3 -
.../ActionRow/utils/constants.js | 3 -
.../SubActionRow/utils/constants.js | 3 -
.../GlobalActions/utils/constants.js | 3 -
.../SubCategory/utils/constants.js | 3 -
.../pages/Roles/ListPage/index.js | 376 ---------------
.../pages/Roles/ListPage/tests/index.test.js | 165 -------
.../pages/Roles/ProtectedListPage/index.js | 12 -
61 files changed, 607 insertions(+), 1386 deletions(-)
delete mode 100644 packages/core/admin/admin/src/components/UpgradePlanModal/index.js
delete mode 100644 packages/core/admin/admin/src/components/UpgradePlanModal/tests/index.test.js
rename packages/core/admin/{ee/admin => admin/src}/pages/SettingsPage/pages/Roles/CreatePage/tests/__snapshots__/index.test.js.snap (100%)
rename packages/core/admin/{ee/admin => admin/src}/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js (90%)
rename packages/core/admin/{ee/admin => admin/src}/pages/SettingsPage/pages/Roles/CreatePage/utils/schema.js (100%)
delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js
delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/utils/constants.js
delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js
delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/utils/constants.js
delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js
delete mode 100644 packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js
rename packages/core/admin/{ee/admin => admin/src}/pages/SettingsPage/pages/Roles/ListPage/reducer.js (100%)
rename packages/core/admin/{ee/admin => admin/src}/pages/SettingsPage/pages/Roles/ListPage/tests/reducer.test.js (100%)
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/index.js
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ConditionsSelect/MenuList/utils/constants.js
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/utils/constants.js
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/utils/constants.js
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/tests/index.test.js
delete mode 100644 packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ProtectedListPage/index.js
diff --git a/packages/core/admin/admin/src/components/UpgradePlanModal/index.js b/packages/core/admin/admin/src/components/UpgradePlanModal/index.js
deleted file mode 100644
index 0338d53492a..00000000000
--- a/packages/core/admin/admin/src/components/UpgradePlanModal/index.js
+++ /dev/null
@@ -1,123 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import styled from 'styled-components';
-import { useIntl } from 'react-intl';
-import { Portal, FocusTrap, IconButton, Box, Flex, Typography } from '@strapi/design-system';
-import { LinkButton } from '@strapi/design-system/v2';
-import { ExternalLink, Cross } from '@strapi/icons';
-import { setHexOpacity, useLockScroll } from '@strapi/helper-plugin';
-import AirBalloon from '../../assets/images/hot-air-balloon.png';
-import BigArrow from '../../assets/images/upgrade-details.png';
-
-const UpgradeWrapper = styled.div`
- position: absolute;
- z-index: 3;
- inset: 0;
- background: ${({ theme }) => setHexOpacity(theme.colors.neutral800, 0.2)};
- padding: 0 ${({ theme }) => theme.spaces[8]};
-`;
-
-const UpgradeContainer = styled(Flex)`
- position: relative;
- max-width: ${830 / 16}rem;
- height: ${415 / 16}rem;
- margin: 0 auto;
- overflow: hidden;
- margin-top: 10%;
- padding-left: ${64 / 16}rem;
-
- img:first-of-type {
- position: absolute;
- right: 0;
- top: 0;
- max-width: ${360 / 16}rem;
- }
-
- img:not(:first-of-type) {
- width: ${130 / 16}rem;
- margin-left: 12%;
- margin-right: ${20 / 16}rem;
- z-index: 0;
- }
-`;
-
-const FlexStart = styled(Flex)`
- max-width: ${400 / 16}rem;
- z-index: 0;
-`;
-
-const CloseButtonContainer = styled(Box)`
- position: absolute;
- right: ${({ theme }) => theme.spaces[4]};
- top: ${({ theme }) => theme.spaces[4]};
-`;
-
-const UpgradePlanModal = ({ onClose, isOpen }) => {
- useLockScroll(isOpen);
- const { formatMessage } = useIntl();
-
- if (!isOpen) {
- return null;
- }
-
- return (
-
-
-
- e.stopPropagation()}
- aria-labelledby="upgrade-plan"
- background="neutral0"
- hasRadius
- >
-
-
- } />
-
-
-
- {formatMessage({
- id: 'app.components.UpgradePlanModal.text-ce',
- defaultMessage: 'COMMUNITY EDITION',
- })}
-
-
-
- {formatMessage({
- id: 'app.components.UpgradePlanModal.limit-reached',
- defaultMessage: 'You have reached the limit',
- })}
-
-
- {formatMessage({
- id: 'app.components.UpgradePlanModal.text-power',
- defaultMessage:
- 'Unlock the full power of Strapi by upgrading your plan to the Enterprise Edition',
- })}
-
-
- }
- >
- {formatMessage({
- id: 'app.components.UpgradePlanModal.button',
- defaultMessage: 'Learn more',
- })}
-
-
-
-
-
-
-
- );
-};
-
-UpgradePlanModal.propTypes = {
- onClose: PropTypes.func.isRequired,
- isOpen: PropTypes.bool.isRequired,
-};
-
-export default UpgradePlanModal;
diff --git a/packages/core/admin/admin/src/components/UpgradePlanModal/tests/index.test.js b/packages/core/admin/admin/src/components/UpgradePlanModal/tests/index.test.js
deleted file mode 100644
index f77343b998c..00000000000
--- a/packages/core/admin/admin/src/components/UpgradePlanModal/tests/index.test.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import React from 'react';
-import { render, screen, waitFor } from '@testing-library/react';
-import { IntlProvider } from 'react-intl';
-import { ThemeProvider, lightTheme } from '@strapi/design-system';
-import UpgradePlanModal from '../index';
-
-const App = (
-
-
-
-
-
-);
-
-describe('UpgradePlanModal', () => {
- it('renders and matches the snapshot', async () => {
- const {
- container: { firstChild },
- } = render(App);
-
- expect(firstChild).toMatchInlineSnapshot(`
- .c0 {
- border: 0;
- -webkit-clip: rect(0 0 0 0);
- clip: rect(0 0 0 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- width: 1px;
- }
-
-
- `);
- });
-
- it('renders and matches the snapshot', async () => {
- render(App);
-
- await waitFor(() => {
- expect(screen.getByText('You have reached the limit')).toBeInTheDocument();
- });
- });
-});
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/index.js
index 01f1a7a2e71..3f3db74d443 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/index.js
@@ -1,8 +1,270 @@
-import React from 'react';
-import { Redirect } from 'react-router-dom';
+import React, { useRef, useState } from 'react';
+import { format } from 'date-fns';
+import {
+ CheckPagePermissions,
+ Form,
+ LoadingIndicatorPage,
+ SettingsPageTitle,
+ request,
+ useNotification,
+ useOverlayBlocker,
+ useTracking,
+ Link,
+} from '@strapi/helper-plugin';
+import {
+ Box,
+ Button,
+ ContentLayout,
+ HeaderLayout,
+ Grid,
+ GridItem,
+ Main,
+ Flex,
+ Typography,
+ TextInput,
+ Textarea,
+} from '@strapi/design-system';
+import { ArrowLeft } from '@strapi/icons';
+import { Formik } from 'formik';
+import get from 'lodash/get';
+import isEmpty from 'lodash/isEmpty';
+import { useIntl } from 'react-intl';
+import { useHistory, useRouteMatch } from 'react-router-dom';
+import styled from 'styled-components';
+import Permissions from '../EditPage/components/Permissions';
+import { useFetchPermissionsLayout, useFetchRole } from '../../../../../hooks';
+import adminPermissions from '../../../../../permissions';
+import schema from './utils/schema';
+
+const UsersRoleNumber = styled.div`
+ border: 1px solid ${({ theme }) => theme.colors.primary200};
+ background: ${({ theme }) => theme.colors.primary100};
+ padding: ${({ theme }) => `${theme.spaces[2]} ${theme.spaces[4]}`};
+ color: ${({ theme }) => theme.colors.primary600};
+ border-radius: ${({ theme }) => theme.borderRadius};
+ font-size: ${12 / 16}rem;
+ font-weight: bold;
+`;
const CreatePage = () => {
- return ;
+ const toggleNotification = useNotification();
+ const { lockApp, unlockApp } = useOverlayBlocker();
+ const { formatMessage } = useIntl();
+ const [isSubmitting, setIsSubmiting] = useState(false);
+ const { replace } = useHistory();
+ const permissionsRef = useRef();
+ const { trackUsage } = useTracking();
+ const params = useRouteMatch('/settings/roles/duplicate/:id');
+ const id = get(params, 'params.id', null);
+ const { isLoading: isLayoutLoading, data: permissionsLayout } = useFetchPermissionsLayout();
+ const { permissions: rolePermissions, isLoading: isRoleLoading } = useFetchRole(id);
+
+ const handleCreateRoleSubmit = (data) => {
+ lockApp();
+ setIsSubmiting(true);
+
+ if (id) {
+ trackUsage('willDuplicateRole');
+ } else {
+ trackUsage('willCreateNewRole');
+ }
+
+ Promise.resolve(
+ request('/admin/roles', {
+ method: 'POST',
+ body: data,
+ })
+ )
+ .then(async (res) => {
+ const { permissionsToSend } = permissionsRef.current.getPermissions();
+
+ if (id) {
+ trackUsage('didDuplicateRole');
+ } else {
+ trackUsage('didCreateNewRole');
+ }
+
+ if (res.data.id && !isEmpty(permissionsToSend)) {
+ await request(`/admin/roles/${res.data.id}/permissions`, {
+ method: 'PUT',
+ body: { permissions: permissionsToSend },
+ });
+ }
+
+ return res;
+ })
+ .then((res) => {
+ setIsSubmiting(false);
+ toggleNotification({
+ type: 'success',
+ message: { id: 'Settings.roles.created', defaultMessage: 'created' },
+ });
+ replace(`/settings/roles/${res.data.id}`);
+ })
+ .catch((err) => {
+ console.error(err);
+ setIsSubmiting(false);
+ toggleNotification({
+ type: 'warning',
+ message: { id: 'notification.error' },
+ });
+ })
+ .finally(() => {
+ unlockApp();
+ });
+ };
+
+ const defaultDescription = `${formatMessage({
+ id: 'Settings.roles.form.created',
+ defaultMessage: 'Created',
+ })} ${format(new Date(), 'PPP')}`;
+
+ return (
+
+
+
+ {({ handleSubmit, values, errors, handleReset, handleChange }) => (
+
+ )}
+
+
+ );
};
-export default CreatePage;
+export default function () {
+ return (
+
+
+
+ );
+}
+
+export { CreatePage };
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/tests/__snapshots__/index.test.js.snap b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/tests/__snapshots__/index.test.js.snap
similarity index 100%
rename from packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/tests/__snapshots__/index.test.js.snap
rename to packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/tests/__snapshots__/index.test.js.snap
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js
similarity index 90%
rename from packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js
rename to packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js
index 4f194a00923..d3a718a2016 100644
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/tests/index.test.js
@@ -10,8 +10,8 @@ import { IntlProvider } from 'react-intl';
import { Router, Switch, Route } from 'react-router-dom';
import { createMemoryHistory } from 'history';
import { lightTheme, darkTheme } from '@strapi/design-system';
-import Theme from '../../../../../../../../admin/src/components/Theme';
-import ThemeToggleProvider from '../../../../../../../../admin/src/components/ThemeToggleProvider';
+import Theme from '../../../../../../components/Theme';
+import ThemeToggleProvider from '../../../../../../components/ThemeToggleProvider';
import { CreatePage } from '../index';
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/utils/schema.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/utils/schema.js
similarity index 100%
rename from packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/utils/schema.js
rename to packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/CreatePage/utils/schema.js
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js
index 41a8f7a184c..697304e51d6 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/index.js
@@ -1,6 +1,5 @@
import React from 'react';
import PropTypes from 'prop-types';
-import IS_DISABLED from 'ee_else_ce/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants';
import { Box, Flex, Typography, MultiSelectNested } from '@strapi/design-system';
import { useIntl } from 'react-intl';
@@ -52,7 +51,7 @@ const ActionRow = ({
onChange={handleChange}
value={getSelectedValues(value)}
options={getNestedOptions(arrayOfOptionsGroupedByCategory)}
- disabled={isFormDisabled || IS_DISABLED}
+ disabled={isFormDisabled}
/>
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js
deleted file mode 100644
index 7cb5eb2dac0..00000000000
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = true;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js
index 447d20f41ad..93db5b5a3c1 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/index.js
@@ -6,7 +6,6 @@ import get from 'lodash/get';
import omit from 'lodash/omit';
import { useIntl } from 'react-intl';
import styled from 'styled-components';
-import IS_DISABLED from 'ee_else_ce/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/utils/constants';
import { usePermissionsDataManager } from '../../../../../../../../hooks';
import ConditionsButton from '../../ConditionsButton';
import ConditionsModal from '../../ConditionsModal';
@@ -179,7 +178,7 @@ const Collapse = ({
{hasConditions && }
{hasConditions && }
{
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js
deleted file mode 100644
index 7cb5eb2dac0..00000000000
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = true;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/index.js
index dfe24614d22..bc7e456d120 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/index.js
@@ -5,7 +5,6 @@ import upperFirst from 'lodash/upperFirst';
import { useIntl } from 'react-intl';
import styled from 'styled-components';
import { BaseCheckbox, Box, Flex, Typography } from '@strapi/design-system';
-import IS_DISABLED from 'ee_else_ce/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/utils/constants';
import { usePermissionsDataManager } from '../../../../../../../../../hooks';
import CollapseLabel from '../../../CollapseLabel';
import Curve from '../../../Curve';
@@ -150,7 +149,7 @@ const SubActionRow = ({
return (
{
})}
{
onChangeCollectionTypeGlobalActionCheckbox(kind, actionId, value);
}}
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js
deleted file mode 100644
index 7cb5eb2dac0..00000000000
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = true;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/index.js
index 91527ff0827..9a81fff6d15 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/index.js
@@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
import { Grid, GridItem, Box, Checkbox, Flex, Typography } from '@strapi/design-system';
import { useIntl } from 'react-intl';
import get from 'lodash/get';
-import IS_DISABLED from 'ee_else_ce/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants';
import { usePermissionsDataManager } from '../../../../../../../../hooks';
import { getCheckboxState, removeConditionKeyFromData } from '../../utils';
import ConditionsButton from '../../ConditionsButton';
@@ -77,7 +76,7 @@ const SubCategory = ({ categoryName, isFormDisabled, subCategoryName, actions, p
{
onChangeParentCheckbox({
@@ -99,13 +98,10 @@ const SubCategory = ({ categoryName, isFormDisabled, subCategoryName, actions, p
{formattedActions.map(({ checkboxName, value, action, displayName, hasConditions }) => {
return (
-
+
{
onChangeSimpleCheckbox({
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js
deleted file mode 100644
index 7cb5eb2dac0..00000000000
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = true;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js
index 98769f28203..ee1f34eb17f 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/index.js
@@ -1,49 +1,138 @@
-import React, { useCallback, useState } from 'react';
-import matchSorter from 'match-sorter';
+import React, { useCallback, useEffect, useReducer, useState } from 'react';
import {
+ ConfirmDialog,
+ LoadingIndicatorPage,
+ SearchURLQuery,
SettingsPageTitle,
- useQuery,
- useTracking,
+ getFetchClient,
+ useNotification,
+ useQueryParams,
+ useRBAC,
useFocusWhenNavigate,
} from '@strapi/helper-plugin';
-import { Plus, Trash, Pencil, Duplicate } from '@strapi/icons';
+import { Plus, Trash, Duplicate, Pencil } from '@strapi/icons';
import {
Button,
+ ActionLayout,
ContentLayout,
HeaderLayout,
+ Main,
Table,
Tbody,
- Th,
+ TFooter,
Thead,
+ Th,
Tr,
- TFooter,
Typography,
- Main,
VisuallyHidden,
} from '@strapi/design-system';
-
+import { get } from 'lodash';
+import matchSorter from 'match-sorter';
import { useIntl } from 'react-intl';
import { useHistory } from 'react-router-dom';
-import RoleRow from './components/RoleRow';
-import EmptyRole from './components/EmptyRole';
-import UpgradePlanModal from '../../../../../components/UpgradePlanModal';
import { useRolesList } from '../../../../../hooks';
+import adminPermissions from '../../../../../permissions';
+import EmptyRole from './components/EmptyRole';
+import BaseRoleRow from './components/RoleRow';
+import reducer, { initialState } from './reducer';
const useSortedRoles = () => {
- const { roles, isLoading } = useRolesList();
+ useFocusWhenNavigate();
+ const {
+ isLoading: isLoadingForPermissions,
+ allowedActions: { canCreate, canDelete, canRead, canUpdate },
+ } = useRBAC(adminPermissions.settings.roles);
- const query = useQuery();
- const _q = decodeURIComponent(query.get('_q') || '');
+ const { getData, roles, isLoading } = useRolesList(false);
+ const [{ query }] = useQueryParams();
+ const _q = query?._q || '';
const sortedRoles = matchSorter(roles, _q, { keys: ['name', 'description'] });
- return { isLoading, sortedRoles };
+ useEffect(() => {
+ if (!isLoadingForPermissions && canRead) {
+ getData();
+ }
+ }, [isLoadingForPermissions, canRead, getData]);
+
+ return {
+ isLoadingForPermissions,
+ canCreate,
+ canDelete,
+ canRead,
+ canUpdate,
+ isLoading,
+ getData,
+ sortedRoles,
+ roles,
+ };
};
-const useRoleActions = () => {
+const useRoleActions = ({ getData, canCreate, canDelete, canUpdate }) => {
const { formatMessage } = useIntl();
- const [isModalOpen, setIsModalOpen] = useState(false);
- const { trackUsage } = useTracking();
+
+ const toggleNotification = useNotification();
+ const [isWarningDeleteAllOpened, setIsWarningDeleteAllOpenend] = useState(false);
const { push } = useHistory();
+ const [{ selectedRoles, showModalConfirmButtonLoading, roleToDelete }, dispatch] = useReducer(
+ reducer,
+ initialState
+ );
+
+ const { post } = getFetchClient();
+
+ const handleDeleteData = async () => {
+ try {
+ dispatch({
+ type: 'ON_REMOVE_ROLES',
+ });
+
+ await post('/admin/roles/batch-delete', {
+ ids: [roleToDelete],
+ });
+
+ await getData();
+
+ dispatch({
+ type: 'RESET_DATA_TO_DELETE',
+ });
+ } catch (err) {
+ const errorIds = get(err, ['response', 'payload', 'data', 'ids'], null);
+
+ if (errorIds && Array.isArray(errorIds)) {
+ const errorsMsg = errorIds.join('\n');
+ toggleNotification({
+ type: 'warning',
+ message: errorsMsg,
+ });
+ } else {
+ toggleNotification({
+ type: 'warning',
+ message: { id: 'notification.error' },
+ });
+ }
+ }
+ handleToggleModal();
+ };
+
+ const onRoleDuplicate = useCallback(
+ (id) => {
+ push(`/settings/roles/duplicate/${id}`);
+ },
+ [push]
+ );
+
+ const handleNewRoleClick = () => push('/settings/roles/new');
+
+ const onRoleRemove = useCallback((roleId) => {
+ dispatch({
+ type: 'SET_ROLE_TO_DELETE',
+ id: roleId,
+ });
+
+ handleToggleModal();
+ }, []);
+
+ const handleToggleModal = () => setIsWarningDeleteAllOpenend((prev) => !prev);
const handleGoTo = useCallback(
(id) => {
@@ -52,144 +141,234 @@ const useRoleActions = () => {
[push]
);
- const handleToggle = useCallback(() => {
- setIsModalOpen((prev) => !prev);
- }, []);
+ const handleClickDelete = useCallback(
+ (e, role) => {
+ e.preventDefault();
+ e.stopPropagation();
- const handleToggleModalForCreatingRole = useCallback(() => {
- trackUsage('didShowRBACUpgradeModal');
- setIsModalOpen(true);
- }, [trackUsage]);
+ if (role.usersCount) {
+ toggleNotification({
+ type: 'info',
+ message: { id: 'Roles.ListPage.notification.delete-not-allowed' },
+ });
+ } else {
+ onRoleRemove(role.id);
+ }
+ },
+ [toggleNotification, onRoleRemove]
+ );
+
+ const handleClickDuplicate = useCallback(
+ (e, role) => {
+ e.preventDefault();
+ e.stopPropagation();
+ onRoleDuplicate(role.id);
+ },
+ [onRoleDuplicate]
+ );
const getIcons = useCallback(
(role) => [
- {
- onClick: handleToggle,
- label: formatMessage({ id: 'app.utils.duplicate', defaultMessage: 'Duplicate' }),
- icon: ,
- },
- {
- onClick: () => handleGoTo(role.id),
- label: formatMessage({ id: 'app.utils.edit', defaultMessage: 'Edit' }),
- icon: ,
- },
- {
- onClick: handleToggle,
- label: formatMessage({ id: 'global.delete', defaultMessage: 'Delete' }),
- icon: ,
- },
+ ...(canCreate
+ ? [
+ {
+ onClick: (e) => handleClickDuplicate(e, role),
+ label: formatMessage({ id: 'app.utils.duplicate', defaultMessage: 'Duplicate' }),
+ icon: ,
+ },
+ ]
+ : []),
+ ...(canUpdate
+ ? [
+ {
+ onClick: () => handleGoTo(role.id),
+ label: formatMessage({ id: 'app.utils.edit', defaultMessage: 'Edit' }),
+ icon: ,
+ },
+ ]
+ : []),
+ ...(canDelete
+ ? [
+ {
+ onClick: (e) => handleClickDelete(e, role),
+ label: formatMessage({ id: 'global.delete', defaultMessage: 'Delete' }),
+ icon: ,
+ },
+ ]
+ : []),
],
- [formatMessage, handleToggle, handleGoTo]
+ [
+ formatMessage,
+ handleClickDelete,
+ handleClickDuplicate,
+ handleGoTo,
+ canCreate,
+ canUpdate,
+ canDelete,
+ ]
);
return {
- isModalOpen,
- handleToggleModalForCreatingRole,
- handleToggle,
+ handleNewRoleClick,
getIcons,
+ selectedRoles,
+ isWarningDeleteAllOpened,
+ showModalConfirmButtonLoading,
+ handleToggleModal,
+ handleDeleteData,
};
};
const RoleListPage = () => {
const { formatMessage } = useIntl();
- useFocusWhenNavigate();
- const { sortedRoles, isLoading } = useSortedRoles();
- const { isModalOpen, handleToggle, handleToggleModalForCreatingRole, getIcons } =
- useRoleActions();
+ const {
+ isLoadingForPermissions,
+ canCreate,
+ canRead,
+ canDelete,
+ canUpdate,
+ isLoading,
+ getData,
+ sortedRoles,
+ } = useSortedRoles();
+
+ const {
+ handleNewRoleClick,
+ getIcons,
+ isWarningDeleteAllOpened,
+ showModalConfirmButtonLoading,
+ handleToggleModal,
+ handleDeleteData,
+ } = useRoleActions({ getData, canCreate, canDelete, canUpdate });
+
+ // ! TODO - Show the search bar only if the user is allowed to read - add the search input
+ // canRead
const rowCount = sortedRoles.length + 1;
- const colCount = 5;
+ const colCount = 6;
+
+ if (isLoadingForPermissions) {
+ return (
+
+
+
+ );
+ }
- // ! TODO - Add the search input
+ const title = formatMessage({
+ id: 'global.roles',
+ defaultMessage: 'roles',
+ });
return (
} size="S">
- {formatMessage({
- id: 'Settings.roles.list.button.add',
- defaultMessage: 'Add new role',
- })}
-
+ canCreate ? (
+ } size="S">
+ {formatMessage({
+ id: 'Settings.roles.list.button.add',
+ defaultMessage: 'Add new role',
+ })}
+
+ ) : null
}
- title={formatMessage({
- id: 'global.roles',
- defaultMessage: 'roles',
- })}
+ title={title}
subtitle={formatMessage({
id: 'Settings.roles.list.description',
defaultMessage: 'List of roles',
})}
+ as="h2"
/>
-
- }>
- {formatMessage({
- id: 'Settings.roles.list.button.add',
- defaultMessage: 'Add new role',
- })}
-
+ {canRead && (
+
}
- >
-
-
-
-
+ />
+ )}
+ {canRead && (
+
+ }>
{formatMessage({
- id: 'global.name',
- defaultMessage: 'Name',
+ id: 'Settings.roles.list.button.add',
+ defaultMessage: 'Add new role',
})}
-
-
-
-
- {formatMessage({
- id: 'global.description',
- defaultMessage: 'Description',
- })}
-
-
-
-
- {formatMessage({
- id: 'global.users',
- defaultMessage: 'Users',
- })}
-
-
-
-
- {formatMessage({
- id: 'global.actions',
- defaultMessage: 'Actions',
- })}
-
-
-
-
-
- {sortedRoles?.map((role, rowIndex) => (
-
- ))}
-
-
- {!rowCount && !isLoading && }
-
-
+
+ ) : null
+ }
+ >
+
+
+
+
+ {formatMessage({
+ id: 'global.name',
+ defaultMessage: 'Name',
+ })}
+
+
+
+
+ {formatMessage({
+ id: 'global.description',
+ defaultMessage: 'Description',
+ })}
+
+
+
+
+ {formatMessage({
+ id: 'global.users',
+ defaultMessage: 'Users',
+ })}
+
+
+
+
+ {formatMessage({
+ id: 'global.actions',
+ defaultMessage: 'Actions',
+ })}
+
+
+
+
+
+ {sortedRoles?.map((role, index) => (
+
+ ))}
+
+
+ {!rowCount && !isLoading && }
+
+ )}
+
);
};
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/reducer.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/reducer.js
similarity index 100%
rename from packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/reducer.js
rename to packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/reducer.js
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/tests/reducer.test.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/tests/reducer.test.js
similarity index 100%
rename from packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/tests/reducer.test.js
rename to packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/tests/reducer.test.js
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js b/packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js
index 266e7e43a16..97dd3cc4b35 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/utils/defaultRoutes.js
@@ -1,25 +1,33 @@
-import RolesCreatePage from 'ee_else_ce/pages/SettingsPage/pages/Roles/CreatePage';
-import ProtectedRolesListPage from 'ee_else_ce/pages/SettingsPage/pages/Roles/ProtectedListPage';
-
const defaultRoutes = [
{
- Component() {
- return { default: ProtectedRolesListPage };
- },
+ async Component() {
+ const component = await import(
+ /* webpackChunkName: "admin-edit-roles-page" */ '../pages/Roles/ProtectedListPage'
+ );
+ return component;
+ },
to: '/settings/roles',
exact: true,
},
{
- Component() {
- return { default: RolesCreatePage };
+ async Component() {
+ const component = await import(
+ /* webpackChunkName: "admin-edit-roles-page" */ '../pages/Roles/CreatePage'
+ );
+
+ return component;
},
to: '/settings/roles/duplicate/:id',
exact: true,
},
{
- Component() {
- return { default: RolesCreatePage };
+ async Component() {
+ const component = await import(
+ /* webpackChunkName: "admin-edit-roles-page" */ '../pages/Roles/CreatePage'
+ );
+
+ return component;
},
to: '/settings/roles/new',
exact: true,
diff --git a/packages/core/admin/admin/src/translations/ca.json b/packages/core/admin/admin/src/translations/ca.json
index f1c0510a4f0..f1322d95bdb 100644
--- a/packages/core/admin/admin/src/translations/ca.json
+++ b/packages/core/admin/admin/src/translations/ca.json
@@ -162,7 +162,7 @@
"Settings.webhooks.create": "Crea un webhook",
"Settings.webhooks.create.header": "Crea una nova capçalera",
"Settings.webhooks.created": "Webhook creat",
- "Settings.webhooks.event.publish-tooltip": "Aquest esdeveniment només existeix per a continguts amb el sistema Esborrany\/Publicació habilitat",
+ "Settings.webhooks.event.publish-tooltip": "Aquest esdeveniment només existeix per a continguts amb el sistema Esborrany/Publicació habilitat",
"Settings.webhooks.events.create": "Crear",
"Settings.webhooks.events.update": "Actualitzar",
"Settings.webhooks.form.events": "Esdeveniments",
@@ -245,20 +245,20 @@
"app.components.EmptyAttributes.title": "Encara no hi ha camps",
"app.components.EmptyStateLayout.content-document": "No s'ha trobat contingut",
"app.components.EmptyStateLayout.content-permissions": "No tens els permisos per accedir a aquest contingut.",
- "app.components.GuidedTour.CM.create.content": "Creeu i gestioneu tot el contingut aquí al Gestor de continguts.<\/p> Ex.: Si fem l'exemple del lloc web del bloc més enllà, es pot escriure un article, desar-lo i publicar-lo com vulgui.<\/p>< p>💡 Consell ràpid: no us oblideu de fer clic a Publicar al contingut que creeu.<\/p>",
+ "app.components.GuidedTour.CM.create.content": " Creeu i gestioneu tot el contingut aquí al Gestor de continguts. Ex.: Si fem l'exemple del lloc web del bloc més enllà, es pot escriure un article, desar-lo i publicar-lo com vulgui. < p>💡 Consell ràpid: no us oblideu de fer clic a Publicar al contingut que creeu. | |
",
"app.components.GuidedTour.CM.create.title": "⚡️ Crea contingut",
- "app.components.GuidedTour.CM.success.content": "Increïble, un últim pas per fer!<\/p>🚀 Veure contingut en acció<\/b>",
+ "app.components.GuidedTour.CM.success.content": "
Increïble, un últim pas per fer!
🚀 Veure contingut en acció ",
"app.components.GuidedTour.CM.success.cta.title": "Prova l'API",
"app.components.GuidedTour.CM.success.title": "Pas 2: completat ✅",
- "app.components.GuidedTour.CTB.create.content": "Els tipus de col·lecció us ajuden a gestionar diverses entrades, els tipus únics són adequats per gestionar només una entrada.<\/p>
Ex: per a un lloc web de bloc, els articles serien un tipus de col·lecció mentre que una pàgina d'inici seria un tipus únic. <\/p>",
+ "app.components.GuidedTour.CTB.create.content": "
Els tipus de col·lecció us ajuden a gestionar diverses entrades, els tipus únics són adequats per gestionar només una entrada.
Ex: per a un lloc web de bloc, els articles serien un tipus de col·lecció mentre que una pàgina d'inici seria un tipus únic.
",
"app.components.GuidedTour.CTB.create.cta.title": "Creeu un tipus de col·lecció",
"app.components.GuidedTour.CTB.create.title": "🧠 Crea un primer tipus de col·lecció",
- "app.components.GuidedTour.CTB.success.content": "Que vagi bé!<\/p>⚡️ Què t'agradaria compartir amb el món?<\/b>",
+ "app.components.GuidedTour.CTB.success.content": "
Que vagi bé!
⚡️ Què t'agradaria compartir amb el món? ",
"app.components.GuidedTour.CTB.success.title": "Pas 1: Completat ✅",
- "app.components.GuidedTour.apiTokens.create.content": "Genereu aquí un testimoni d'autenticació i recupereu el contingut que acabeu de crear.<\/p>",
+ "app.components.GuidedTour.apiTokens.create.content": "
Genereu aquí un testimoni d'autenticació i recupereu el contingut que acabeu de crear.
",
"app.components.GuidedTour.apiTokens.create.cta.title": "Genereu un testimoni API",
"app.components.GuidedTour.apiTokens.create.title": "🚀 Veure contingut en acció",
- "app.components.GuidedTour.apiTokens.success.content": "Consulteu el contingut en acció fent una sol·licitud HTTP:<\/p>
A aquest URL: https:\/\/'<'YOUR_DOMAIN'>'\/api\/'<' YOUR_CT'>'<\/light><\/p><\/li>Amb la capçalera: Autorització: portador '<'YOUR_API_TOKEN'>'<\/light><\/p><\/li ><\/ul>Per obtenir més maneres d'interaccionar amb el contingut, consulteu la documentació<\/documentationLink>.<\/p>",
+ "app.components.GuidedTour.apiTokens.success.content": "Consulteu el contingut en acció fent una sol·licitud HTTP:
Per obtenir més maneres d'interaccionar amb el contingut, consulteu la documentació .
",
"app.components.GuidedTour.apiTokens.success.cta.title": "Torna a la pàgina d'inici",
"app.components.GuidedTour.apiTokens.success.title": "Pas 3: Completat ✅",
"app.components.GuidedTour.create-content": "Crea contingut",
@@ -328,12 +328,6 @@
"app.components.PluginCard.more-details": "Més detalls",
"app.components.ToggleCheckbox.off-label": "Apagat",
"app.components.ToggleCheckbox.on-label": "Encès",
- "app.components.UpgradePlanModal.button": "SABER MÉS",
- "app.components.UpgradePlanModal.limit-reached": "Has assolit el límit máxim",
- "app.components.UpgradePlanModal.text-Strapi": "de Strapi actualitzant el seu pla al",
- "app.components.UpgradePlanModal.text-ce": "Edició de la Comunitat",
- "app.components.UpgradePlanModal.text-ee": "Edició Empresarial",
- "app.components.UpgradePlanModal.text-power": "Desbloqueja tot el poder",
"app.components.Users.MagicLink.connect": "Envieu aquest enllaç a l'usuari perquè es connecti.",
"app.components.Users.MagicLink.connect.sso": "Envieu aquest enllaç a l'usuari, el primer inici de sessió es pot fer a través d'un proveïdor de SSO",
"app.components.Users.ModalCreateBody.block-title.details": "Detalls",
diff --git a/packages/core/admin/admin/src/translations/de.json b/packages/core/admin/admin/src/translations/de.json
index 9d6be6a5669..1191bb4093d 100644
--- a/packages/core/admin/admin/src/translations/de.json
+++ b/packages/core/admin/admin/src/translations/de.json
@@ -324,12 +324,6 @@
"app.components.PluginCard.more-details": "Mehr Details",
"app.components.ToggleCheckbox.off-label": "Nein",
"app.components.ToggleCheckbox.on-label": "Ja",
- "app.components.UpgradePlanModal.button": "Mehr erfahren",
- "app.components.UpgradePlanModal.limit-reached": "Limit erreicht",
- "app.components.UpgradePlanModal.text-ce": "Community Edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
- "app.components.UpgradePlanModal.text-power": "Schalte die ganze Power frei",
- "app.components.UpgradePlanModal.text-strapi": "in dem du Strapi upgradest auf",
"app.components.Users.MagicLink.connect": "Diesen Link dem Benutzer zum Registrieren schicken.",
"app.components.Users.MagicLink.connect.sso": "Sende dem Benutzer diesen Link, das erste Login kann über einen SSO-Anbeiter gemacht werden",
"app.components.Users.ModalCreateBody.block-title.details": "Details",
diff --git a/packages/core/admin/admin/src/translations/dk.json b/packages/core/admin/admin/src/translations/dk.json
index bf8a6475f50..8ac461aab23 100644
--- a/packages/core/admin/admin/src/translations/dk.json
+++ b/packages/core/admin/admin/src/translations/dk.json
@@ -274,12 +274,6 @@
"app.components.PluginCard.more-details": "Flere detaljer",
"app.components.ToggleCheckbox.off-label": "Fra",
"app.components.ToggleCheckbox.on-label": "Til",
- "app.components.UpgradePlanModal.button": "LÆR MERE",
- "app.components.UpgradePlanModal.limit-reached": "Du har nået grænsen",
- "app.components.UpgradePlanModal.text-ce": "Community Edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
- "app.components.UpgradePlanModal.text-power": "Lås op for det fulle potentiale",
- "app.components.UpgradePlanModal.text-strapi": "af Strapi ved at opgradere til",
"app.components.Users.MagicLink.connect": "Send dette link til brugeren for at de kan connecte.",
"app.components.Users.MagicLink.connect.sso": "Send dette link til brugeren. Det første log ind kan klares med en SSO provider",
"app.components.Users.ModalCreateBody.block-title.details": "Detaljer",
diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json
index 26b5e42c76f..6db257f2158 100644
--- a/packages/core/admin/admin/src/translations/en.json
+++ b/packages/core/admin/admin/src/translations/en.json
@@ -477,12 +477,6 @@
"app.components.PluginCard.more-details": "More details",
"app.components.ToggleCheckbox.off-label": "False",
"app.components.ToggleCheckbox.on-label": "True",
- "app.components.UpgradePlanModal.button": "Learn more",
- "app.components.UpgradePlanModal.limit-reached": "You have reached the limit",
- "app.components.UpgradePlanModal.text-ce": "Community Edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
- "app.components.UpgradePlanModal.text-power": "Unlock the full power of Strapi by upgrading your plan to the Enterprise Edition",
- "app.components.UpgradePlanModal.text-strapi": "of Strapi by upgrading your plan to the",
"app.components.Users.MagicLink.connect": "Copy and share this link to give access to this user",
"app.components.Users.MagicLink.connect.sso": "Send this link to the user, the first login can be made via a SSO provider",
"app.components.Users.ModalCreateBody.block-title.details": "User details",
diff --git a/packages/core/admin/admin/src/translations/es.json b/packages/core/admin/admin/src/translations/es.json
index 639d4d76da8..95cc5b0d88a 100644
--- a/packages/core/admin/admin/src/translations/es.json
+++ b/packages/core/admin/admin/src/translations/es.json
@@ -274,12 +274,6 @@
"app.components.PluginCard.more-details": "Más detalles",
"app.components.ToggleCheckbox.off-label": "Apagado",
"app.components.ToggleCheckbox.on-label": "Encendido",
- "app.components.UpgradePlanModal.button": "SABER MÁS",
- "app.components.UpgradePlanModal.limit-reached": "Has alcanzado el límite",
- "app.components.UpgradePlanModal.text-ce": "Edición de la Comunidad",
- "app.components.UpgradePlanModal.text-ee": "Edición Empresarial",
- "app.components.UpgradePlanModal.text-power": "Desbloquea todo el poder",
- "app.components.UpgradePlanModal.text-strapi": "de Strapi actualizando su plan al",
"app.components.Users.MagicLink.connect": "Envíe este enlace al usuario para que se conecte.",
"app.components.Users.MagicLink.connect.sso": "Envíe este enlace al usuario, el primer inicio de sesión se puede realizar a través de un proveedor de SSO",
"app.components.Users.ModalCreateBody.block-title.details": "Detalles",
diff --git a/packages/core/admin/admin/src/translations/eu.json b/packages/core/admin/admin/src/translations/eu.json
index 680d31085f0..ebdb9781bd2 100644
--- a/packages/core/admin/admin/src/translations/eu.json
+++ b/packages/core/admin/admin/src/translations/eu.json
@@ -407,12 +407,6 @@
"app.components.PluginCard.more-details": "Xehetasun gehiago",
"app.components.ToggleCheckbox.off-label": "Gezurra",
"app.components.ToggleCheckbox.on-label": "Egia",
- "app.components.UpgradePlanModal.button": "Gehiago ikasi.",
- "app.components.UpgradePlanModal.limit-reached": "Mugara iritsi zara",
- "app.components.UpgradePlanModal.text-ce": "Community Edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
- "app.components.UpgradePlanModal.text-power": "Desblokeatu Strapi-ren botere osoa zure plana Enterprise Edition-era berrituz",
- "app.components.UpgradePlanModal.text-strapi": "Strapi-ren planera eguneratuz",
"app.components.Users.MagicLink.connect": "Kopiatu eta partekatu esteka hau erabiltzaile honi sarbidea emateko",
"app.components.Users.MagicLink.connect.sso": "Bidali esteka hau erabiltzaileari, lehen saioa SSO hornitzaile baten bidez egiteko",
"app.components.Users.ModalCreateBody.block-title.details": "Erabiltzailearen datuak",
diff --git a/packages/core/admin/admin/src/translations/fr.json b/packages/core/admin/admin/src/translations/fr.json
index 823d4346b87..9224b226097 100644
--- a/packages/core/admin/admin/src/translations/fr.json
+++ b/packages/core/admin/admin/src/translations/fr.json
@@ -274,12 +274,6 @@
"app.components.PluginCard.more-details": "Plus de détails",
"app.components.ToggleCheckbox.off-label": "Désactivé",
"app.components.ToggleCheckbox.on-label": "Activé",
- "app.components.UpgradePlanModal.button": "En savoir plus",
- "app.components.UpgradePlanModal.limit-reached": "Vous avez atteint la limite",
- "app.components.UpgradePlanModal.text-ce": "Edition Communauté",
- "app.components.UpgradePlanModal.text-ee": "Edition Entreprise",
- "app.components.UpgradePlanModal.text-power": "Débloquez toute la puissance de Strapi en mettant votre plan à niveau vers l'Edition Entreprise",
- "app.components.UpgradePlanModal.text-strapi": "de Strapi en mettant votre plan à niveau vers",
"app.components.Users.MagicLink.connect": "Envoyez ce lien à l'utilisateur pour qu'il se connecte.",
"app.components.Users.MagicLink.connect.sso": "Envoyez ce lien à l'utilisateur, la première connexion peut être effectué via un fournisseur SSO",
"app.components.Users.ModalCreateBody.block-title.details": "Détails",
diff --git a/packages/core/admin/admin/src/translations/gu.json b/packages/core/admin/admin/src/translations/gu.json
index c837f7441cc..73835b5ec0b 100644
--- a/packages/core/admin/admin/src/translations/gu.json
+++ b/packages/core/admin/admin/src/translations/gu.json
@@ -247,12 +247,6 @@
"app.components.PluginCard.more-details": "વધુ વિગતો",
"app.components.ToggleCheckbox.off-label": "ખોટું",
"app.components.ToggleCheckbox.on-label": "True",
- "app.components.UpgradePlanModal.button": "વધુ જાણો",
- "app.components.UpgradePlanModal.limit-reached": "તમે મર્યાદા પર પહોંચી ગયા છો",
- "app.components.UpgradePlanModal.text-ce": "સમુદાય આવૃત્તિ",
- "app.components.UpgradePlanModal.text-ee": "એન્ટરપ્રાઇઝ એડિશન",
- "app.components.UpgradePlanModal.text-power": "તમારા પ્લાનને એન્ટરપ્રાઇઝ એડિશનમાં અપગ્રેડ કરીને સ્ટ્રેપીની સંપૂર્ણ શક્તિને અનલોક કરો",
- "app.components.UpgradePlanModal.text-strapi": "તમારી યોજનાને અપગ્રેડ કરીને સ્ટ્રેપીનું",
"app.components.Users.MagicLink.connect": "આ વપરાશકર્તાને ઍક્સેસ આપવા માટે આ લિંક કૉપિ કરો અને શેર કરો",
"app.components.Users.MagicLink.connect.sso": "વપરાશકર્તાને આ લિંક મોકલો, પ્રથમ લૉગિન SSO પ્રદાતા દ્વારા કરી શકાય છે",
"app.components.Users.ModalCreateBody.block-title.details": "વપરાશકર્તા વિગતો",
diff --git a/packages/core/admin/admin/src/translations/he.json b/packages/core/admin/admin/src/translations/he.json
index 40e5896bef1..e10016eda24 100644
--- a/packages/core/admin/admin/src/translations/he.json
+++ b/packages/core/admin/admin/src/translations/he.json
@@ -198,12 +198,6 @@
"app.components.PluginCard.compatible": "תואם לאפליקציה שלך",
"app.components.PluginCard.compatibleCommunity": "תואם לקהילה",
"app.components.PluginCard.more-details": "פרטים נוספים",
- "app.components.UpgradePlanModal.button": "מידע נוסף",
- "app.components.UpgradePlanModal.limit-reached": "הגעת למגבלה",
- "app.components.UpgradePlanModal.text-ce": "גרסת קהילה",
- "app.components.UpgradePlanModal.text-ee": "גרסה לארגונים",
- "app.components.UpgradePlanModal.text-power": "פתח את מלוא הכוח",
- "app.components.UpgradePlanModal.text-strapi": "של סטראפי על ידי שדרוג התוכנית ל",
"app.components.Users.MagicLink.connect": "שלח קישור זה למשתמש כדי שיוכל להתחבר.",
"app.components.Users.MagicLink.connect.sso": "שלח את הקישור הזה למשתמש, ההתחברות הראשונה יכולה להתבצע באמצעות ספק SSO",
"app.components.Users.ModalCreateBody.block-title.details": "פרטים",
diff --git a/packages/core/admin/admin/src/translations/hi.json b/packages/core/admin/admin/src/translations/hi.json
index 3155f0fb18d..bef604a81a9 100644
--- a/packages/core/admin/admin/src/translations/hi.json
+++ b/packages/core/admin/admin/src/translations/hi.json
@@ -328,12 +328,6 @@
"app.components.PluginCard.more-details": "अधिक जानकारी",
"app.components.ToggleCheckbox.off-label": "असत्य",
"app.components.ToggleCheckbox.on-label": "सत्य",
- "app.components.UpgradePlanModal.button": "और अधिक जानें",
- "app.components.UpgradePlanModal.limit-reached": "आप सीमा तक पहुंच गए हैं",
- "app.components.UpgradePlanModal.text-ce": "सामुदायिक संस्करण",
- "app.components.UpgradePlanModal.text-ee": "एंटरप्राइज़ संस्करण",
- "app.components.UpgradePlanModal.text-power": "अपनी योजना को एंटरप्राइज़ संस्करण में अपग्रेड करके Strapi की पूरी शक्ति को अनलॉक करें",
- "app.components.UpgradePlanModal.text-strapi": "अपनी योजना को में अपग्रेड करके Strapi का",
"app.components.Users.MagicLink.connect": "इस उपयोगकर्ता को एक्सेस देने के लिए इस लिंक को कॉपी और शेयर करें",
"app.components.Users.MagicLink.connect.sso": "उपयोगकर्ता को यह लिंक भेजें, पहला लॉगिन एसएसओ प्रदाता के माध्यम से किया जा सकता है",
"app.components.Users.ModalCreateBody.block-title.details": "उपयोगकर्ता की जानकारी",
diff --git a/packages/core/admin/admin/src/translations/hu.json b/packages/core/admin/admin/src/translations/hu.json
index 007611352fe..9c2686af298 100644
--- a/packages/core/admin/admin/src/translations/hu.json
+++ b/packages/core/admin/admin/src/translations/hu.json
@@ -408,12 +408,6 @@
"app.components.PluginCard.more-details": "További részletek",
"app.components.ToggleCheckbox.off-label": "Kikapcsol",
"app.components.ToggleCheckbox.on-label": "Bekapcsol",
- "app.components.UpgradePlanModal.button": "Tudj meg többet",
- "app.components.UpgradePlanModal.limit-reached": "Elérted a korlátot",
- "app.components.UpgradePlanModal.text-ce": "Közösségi kiadás",
- "app.components.UpgradePlanModal.text-ee": "Vállalati kiadás",
- "app.components.UpgradePlanModal.text-power": "Frissítsen a Vállalati kiadásra, hogy megismerhesse a Strapi teljes funkcionalitását.",
- "app.components.UpgradePlanModal.text-strapi": "a Strapi, ha frissíti tervét a",
"app.components.Users.MagicLink.connect": "Másolja ki és ossza meg ezt a linket, hogy hozzáférést biztosítson ehhez a felhasználóhoz",
"app.components.Users.MagicLink.connect.sso": "Küldje el ezt a linket a felhasználónak. Az első bejelentkezés történhet SSO szolgáltatón keresztül",
"app.components.Users.ModalCreateBody.block-title.details": "Felhasználói adatok",
diff --git a/packages/core/admin/admin/src/translations/id.json b/packages/core/admin/admin/src/translations/id.json
index 528090e6783..087b9c2542b 100644
--- a/packages/core/admin/admin/src/translations/id.json
+++ b/packages/core/admin/admin/src/translations/id.json
@@ -179,12 +179,6 @@
"app.components.PluginCard.compatible": "Kompatibel dengan aplikasi Anda",
"app.components.PluginCard.compatibleCommunity": "Kompatibel dengan komunitas",
"app.components.PluginCard.more-details": "Keterangan lebih lanjut",
- "app.components.UpgradePlanModal.button": "BELAJAR LAGI",
- "app.components.UpgradePlanModal.limit-reached": "Anda telah mencapai batas",
- "app.components.UpgradePlanModal.text-ce": "Edisi Komunitas",
- "app.components.UpgradePlanModal.text-ee": "Edisi Perusahaan",
- "app.components.UpgradePlanModal.text-power": "Buka kunci kekuatan penuh",
- "app.components.UpgradePlanModal.text-strapi": "dari Strapi dengan meningkatkan rencana Anda ke",
"app.components.Users.MagicLink.connect": "Kirim tautan ini ke pengguna agar mereka dapat terhubung.",
"app.components.Users.ModalCreateBody.block-title.details": "Detail",
"app.components.Users.ModalCreateBody.block-title.roles": "Peran pengguna",
diff --git a/packages/core/admin/admin/src/translations/it.json b/packages/core/admin/admin/src/translations/it.json
index 8d90a41b951..eabc002e1f7 100644
--- a/packages/core/admin/admin/src/translations/it.json
+++ b/packages/core/admin/admin/src/translations/it.json
@@ -187,12 +187,6 @@
"app.components.PluginCard.compatible": "Compatibile con la tua app",
"app.components.PluginCard.compatibleCommunity": "Compatibile con la comunità",
"app.components.PluginCard.more-details": "Più dettagli",
- "app.components.UpgradePlanModal.button": "LEGGI DI PIÙ",
- "app.components.UpgradePlanModal.limit-reached": "Hai raggiunto il limite",
- "app.components.UpgradePlanModal.text-ce": "Community edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise edition",
- "app.components.UpgradePlanModal.text-power": "Sblocca tutte le funzionalità",
- "app.components.UpgradePlanModal.text-strapi": "di Strapi aggiornando il tuo piano alla versione",
"app.components.Users.MagicLink.connect": "Invia link all'utente per connettersi.",
"app.components.Users.ModalCreateBody.block-title.details": "Dettagli",
"app.components.Users.ModalCreateBody.block-title.roles": "Ruoli utente",
diff --git a/packages/core/admin/admin/src/translations/ja.json b/packages/core/admin/admin/src/translations/ja.json
index 13aed74c20a..c73c9b9fb3b 100644
--- a/packages/core/admin/admin/src/translations/ja.json
+++ b/packages/core/admin/admin/src/translations/ja.json
@@ -274,12 +274,6 @@
"app.components.PluginCard.more-details": "詳細を見る",
"app.components.ToggleCheckbox.off-label": "False",
"app.components.ToggleCheckbox.on-label": "True",
- "app.components.UpgradePlanModal.button": "Learn more",
- "app.components.UpgradePlanModal.limit-reached": "You have reached the limit",
- "app.components.UpgradePlanModal.text-ce": "Community Edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
- "app.components.UpgradePlanModal.text-power": "Unlock the full power of Strapi by upgrading your plan to the Enterprise Edition",
- "app.components.UpgradePlanModal.text-strapi": "of Strapi by upgrading your plan to the",
"app.components.Users.MagicLink.connect": "Copy and share this link to give access to this user",
"app.components.Users.MagicLink.connect.sso": "Send this link to the user, the first login can be made via a SSO provider",
"app.components.Users.ModalCreateBody.block-title.details": "User details",
diff --git a/packages/core/admin/admin/src/translations/ko.json b/packages/core/admin/admin/src/translations/ko.json
index fe14839030f..602dd34308b 100644
--- a/packages/core/admin/admin/src/translations/ko.json
+++ b/packages/core/admin/admin/src/translations/ko.json
@@ -273,12 +273,6 @@
"app.components.PluginCard.more-details": "[더보기]",
"app.components.ToggleCheckbox.off-label": "False",
"app.components.ToggleCheckbox.on-label": "True",
- "app.components.UpgradePlanModal.button": "Learn more",
- "app.components.UpgradePlanModal.limit-reached": "You have reached the limit",
- "app.components.UpgradePlanModal.text-ce": "Community Edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
- "app.components.UpgradePlanModal.text-power": "Enterprise Edition 플랜으로 업그레이드하고 Strapi의 모든 기능을 활용해보세요.",
- "app.components.UpgradePlanModal.text-strapi": "of Strapi by upgrading your plan to the",
"app.components.Users.MagicLink.connect": "이 링크를 가입할 사용자에게 보내주세요.",
"app.components.Users.MagicLink.connect.sso": "이 링크를 가입할 사용자에게 보내주세요. SSO 프로바이더를 통해 처음 로그인할 수 있습니다.",
"app.components.Users.ModalCreateBody.block-title.details": "상세 정보",
diff --git a/packages/core/admin/admin/src/translations/ml.json b/packages/core/admin/admin/src/translations/ml.json
index 0759dc40292..a28616f8df1 100644
--- a/packages/core/admin/admin/src/translations/ml.json
+++ b/packages/core/admin/admin/src/translations/ml.json
@@ -328,12 +328,6 @@
"app.components.PluginCard.more-details": "കൂടുതൽ വിശദാംശങ്ങൾ",
"app.components.ToggleCheckbox.off-label": "തെറ്റായ",
"app.components.ToggleCheckbox.on-label": "സത്യം",
- "app.components.UpgradePlanModal.button": "കൂടുതലറിയുക",
- "app.components.UpgradePlanModal.limit-reached": "നിങ്ങൾ പരിധിയിലെത്തി",
- "app.components.UpgradePlanModal.text-ce": "കമ്മ്യൂണിറ്റി പതിപ്പ്",
- "app.components.UpgradePlanModal.text-ee": "എന്റർപ്രൈസ് പതിപ്പ്",
- "app.components.UpgradePlanModal.text-power": "നിങ്ങളുടെ പ്ലാൻ എന്റർപ്രൈസ് പതിപ്പിലേക്ക് അപ്ഗ്രേഡ് ചെയ്യുന്നതിലൂടെ സ്ട്രാപിയുടെ മുഴുവൻ ശക്തിയും അൺലോക്ക് ചെയ്യുക",
- "app.components.UpgradePlanModal.text-strapi": "നിങ്ങളുടെ പ്ലാൻ അപ്ഗ്രേഡ് ചെയ്തുകൊണ്ട് സ്ട്രാപിയുടെ",
"app.components.Users.MagicLink.connect": "ഈ ഉപയോക്താവിന് ആക്സസ് നൽകുന്നതിന് ഈ ലിങ്ക് പകർത്തി പങ്കിടുക",
"app.components.Users.MagicLink.connect.sso": "ഈ ലിങ്ക് ഉപയോക്താവിന് അയയ്ക്കുക, ആദ്യ ലോഗിൻ ഒരു SSO ദാതാവ് വഴി നടത്താം",
"app.components.Users.ModalCreateBody.block-title.details": "ഉപയോക്തൃ വിശദാംശങ്ങൾ",
diff --git a/packages/core/admin/admin/src/translations/nl.json b/packages/core/admin/admin/src/translations/nl.json
index 4e7002ffc73..78c777680f9 100644
--- a/packages/core/admin/admin/src/translations/nl.json
+++ b/packages/core/admin/admin/src/translations/nl.json
@@ -403,12 +403,6 @@
"app.components.PluginCard.more-details": "Meer details",
"app.components.ToggleCheckbox.off-label": "Uit",
"app.components.ToggleCheckbox.on-label": "Aan",
- "app.components.UpgradePlanModal.button": "Ontdek meer",
- "app.components.UpgradePlanModal.limit-reached": "Je hebt de limiet bereikt",
- "app.components.UpgradePlanModal.text-ce": "Community Editie",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Editie",
- "app.components.UpgradePlanModal.text-power": "Ontgrendel alles van Strapi door je abonnement up te graden naar de Enterprise Editie",
- "app.components.UpgradePlanModal.text-strapi": "van Strapi door je abonnement up te graden naar de",
"app.components.Users.MagicLink.connect": "Kopieer en deel deze link om toegang te geven aan deze gebruiker",
"app.components.Users.MagicLink.connect.sso": "Stuur deze link naar de gebruiker, de eerste login kan gedaan worden via een SSO provider",
"app.components.Users.ModalCreateBody.block-title.details": "Gebruiker details",
diff --git a/packages/core/admin/admin/src/translations/no.json b/packages/core/admin/admin/src/translations/no.json
index 8de2b17f50b..e69b3a5b779 100644
--- a/packages/core/admin/admin/src/translations/no.json
+++ b/packages/core/admin/admin/src/translations/no.json
@@ -188,12 +188,6 @@
"app.components.PluginCard.compatible": "Kompatibel med din app",
"app.components.PluginCard.compatibleCommunity": "Kompatibel med fellesskapet",
"app.components.PluginCard.more-details": "Flere detaljer",
- "app.components.UpgradePlanModal.button": "LÆR MER",
- "app.components.UpgradePlanModal.limit-reached": "Du har nådd grensen",
- "app.components.UpgradePlanModal.text-ce": "Community Edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
- "app.components.UpgradePlanModal.text-power": "Lås opp for det fulle potentialet",
- "app.components.UpgradePlanModal.text-strapi": "av Strapi ved å oppgradere til",
"app.components.Users.MagicLink.connect": "Send denne linken til brukeren slik at de kan koble til.",
"app.components.Users.ModalCreateBody.block-title.details": "Detaljer",
"app.components.Users.ModalCreateBody.block-title.roles": "Brukerens roller",
diff --git a/packages/core/admin/admin/src/translations/pl.json b/packages/core/admin/admin/src/translations/pl.json
index 1fcb176b3b8..f5dc62ab999 100644
--- a/packages/core/admin/admin/src/translations/pl.json
+++ b/packages/core/admin/admin/src/translations/pl.json
@@ -328,12 +328,6 @@
"app.components.PluginCard.more-details": "Więcej szczegółów",
"app.components.ToggleCheckbox.off-label": "Nie",
"app.components.ToggleCheckbox.on-label": "Tak",
- "app.components.UpgradePlanModal.button": "Dowiedz się więcej",
- "app.components.UpgradePlanModal.limit-reached": "Osiągnąłeś limit",
- "app.components.UpgradePlanModal.text-ce": "Wydanie społecznościowe",
- "app.components.UpgradePlanModal.text-ee": "Wersja Enterprise",
- "app.components.UpgradePlanModal.text-power": "Odblokuj pełną moc",
- "app.components.UpgradePlanModal.text-strapi": "Strapi, ulepszając swój plan do",
"app.components.Users.MagicLink.connect": "Wyślij ten link do użytkownika, aby mógł się połączyć.",
"app.components.Users.MagicLink.connect.sso": "Wyślij ten link do użytkownika, pierwsze logowanie można wykonać za pośrednictwem dostawcy SSO",
"app.components.Users.ModalCreateBody.block-title.details": "Szczegóły",
diff --git a/packages/core/admin/admin/src/translations/pt-BR.json b/packages/core/admin/admin/src/translations/pt-BR.json
index cab25256f95..4b5f91bc7cf 100644
--- a/packages/core/admin/admin/src/translations/pt-BR.json
+++ b/packages/core/admin/admin/src/translations/pt-BR.json
@@ -363,12 +363,6 @@
"app.components.PluginCard.more-details": "Mais detalhes",
"app.components.ToggleCheckbox.off-label": "Desativado",
"app.components.ToggleCheckbox.on-label": "Ativado",
- "app.components.UpgradePlanModal.button": "Saiba mais",
- "app.components.UpgradePlanModal.limit-reached": "Você atingiu o limite",
- "app.components.UpgradePlanModal.text-ce": "Edição Community",
- "app.components.UpgradePlanModal.text-ee": "Edição Enterprise",
- "app.components.UpgradePlanModal.text-power": "Desbloqueie todo o potencial do Strapi atualizando seu plano para a Edição Enterprise",
- "app.components.UpgradePlanModal.text-strapi": "do Strapi atualizando seu plano para a",
"app.components.Users.MagicLink.connect": "Copie e compartilhe esse link para dar acesso ao usuário",
"app.components.Users.MagicLink.connect.sso": "Envie esse link para o usuário. O primeiro login pode ser feito por um provedor de SSO",
"app.components.Users.ModalCreateBody.block-title.details": "Detalhes do usuário",
diff --git a/packages/core/admin/admin/src/translations/ru.json b/packages/core/admin/admin/src/translations/ru.json
index 8fd7eb5098f..523a04a7397 100644
--- a/packages/core/admin/admin/src/translations/ru.json
+++ b/packages/core/admin/admin/src/translations/ru.json
@@ -451,12 +451,6 @@
"app.components.PluginCard.more-details": "Больше деталей",
"app.components.ToggleCheckbox.off-label": "Нет",
"app.components.ToggleCheckbox.on-label": "Да",
- "app.components.UpgradePlanModal.button": "Узнать больше",
- "app.components.UpgradePlanModal.limit-reached": "Вы достигли предела",
- "app.components.UpgradePlanModal.text-ce": "Community Edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
- "app.components.UpgradePlanModal.text-power": "Откройте для себя всю мощь Strapi, обновив свой план до Enterprise Edition.",
- "app.components.UpgradePlanModal.text-strapi": "Strapi, обновив свой план до",
"app.components.Users.MagicLink.connect": "Отправьте эту ссылку пользователю, чтобы предоставить ему доступ",
"app.components.Users.MagicLink.connect.sso": "Отправьте эту ссылку пользователю, первый вход может быть осуществлен через провайдера SSO",
"app.components.Users.ModalCreateBody.block-title.details": "Детали",
diff --git a/packages/core/admin/admin/src/translations/sa.json b/packages/core/admin/admin/src/translations/sa.json
index fff79ac6acd..1b38684f496 100644
--- a/packages/core/admin/admin/src/translations/sa.json
+++ b/packages/core/admin/admin/src/translations/sa.json
@@ -328,12 +328,6 @@
"app.components.PluginCard.more-details": "अधिकविवरणम्",
"app.components.ToggleCheckbox.off-label": "मिथ्या",
"app.components.ToggleCheckbox.on-label": "सत्यम्",
- "app.components.UpgradePlanModal.button": "अधिकं ज्ञातुं",
- "app.components.UpgradePlanModal.limit-reached": "भवन्तः सीमां प्राप्तवन्तः",
- "app.components.UpgradePlanModal.text-ce": "समुदाय संस्करण",
- "app.components.UpgradePlanModal.text-ee": "उद्यम संस्करण",
- "app.components.UpgradePlanModal.text-power": "उद्यमसंस्करणं प्रति स्वयोजनां उन्नयनं कृत्वा Strapi इत्यस्य पूर्णशक्तिं अनलॉक् कुर्वन्तु",
- "app.components.UpgradePlanModal.text-strapi": "अपनी योजना को उन्नयन करके Strapi का",
"app.components.Users.MagicLink.connect": "अस्य उपयोक्त्रे प्रवेशं दातुं एतत् लिङ्क् प्रतिलिख्य साझां कुर्वन्तु",
"app.components.Users.MagicLink.connect.sso": "इदं लिङ्कं उपयोक्त्रे प्रेषयन्तु, प्रथमं प्रवेशं SSO प्रदातृद्वारा कर्तुं शक्यते",
"app.components.Users.ModalCreateBody.block-title.details": "उपयोक्तृविवरणम्",
diff --git a/packages/core/admin/admin/src/translations/sk.json b/packages/core/admin/admin/src/translations/sk.json
index 54d8b2864df..b47c66c6203 100644
--- a/packages/core/admin/admin/src/translations/sk.json
+++ b/packages/core/admin/admin/src/translations/sk.json
@@ -283,12 +283,6 @@
"app.components.PluginCard.more-details": "Viac detailov",
"app.components.ToggleCheckbox.off-label": "False",
"app.components.ToggleCheckbox.on-label": "True",
- "app.components.UpgradePlanModal.button": "Zobraziť viac",
- "app.components.UpgradePlanModal.limit-reached": "Presiahli ste limit",
- "app.components.UpgradePlanModal.text-ce": "Community Edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
- "app.components.UpgradePlanModal.text-power": "Odblokovať plný potenciál",
- "app.components.UpgradePlanModal.text-strapi": "rozšírením Vášho Strapi plánu na",
"app.components.Users.MagicLink.connect": "Skopírujte a zazdieľajte tento link používateľovi pre pihlásenie.",
"app.components.Users.MagicLink.connect.sso": "Odošlite tento link používateľovi. Prvé prihlásenie môže byť vykonané cez SSO poskytovateľa",
"app.components.Users.ModalCreateBody.block-title.details": "Detaily",
diff --git a/packages/core/admin/admin/src/translations/sv.json b/packages/core/admin/admin/src/translations/sv.json
index b8c5d983529..310d6532c54 100644
--- a/packages/core/admin/admin/src/translations/sv.json
+++ b/packages/core/admin/admin/src/translations/sv.json
@@ -403,12 +403,6 @@
"app.components.PluginCard.more-details": "Mer detaljer",
"app.components.ToggleCheckbox.off-label": "Av",
"app.components.ToggleCheckbox.on-label": "På",
- "app.components.UpgradePlanModal.button": "Lär dig mer",
- "app.components.UpgradePlanModal.limit-reached": "Du har nått gränsen",
- "app.components.UpgradePlanModal.text-ce": "Community Edition",
- "app.components.UpgradePlanModal.text-ee": "Enterprise Edition",
- "app.components.UpgradePlanModal.text-power": "Lås upp Strapis fulla kraft genom att uppgradera din plan till Enterprise Edition",
- "app.components.UpgradePlanModal.text-strapi": "av Strapi genom att uppgradera din plan till",
"app.components.Users.MagicLink.connect": "Kopiera och dela denna länk för att ge åtkomst till denna användare",
"app.components.Users.MagicLink.connect.sso": "Skicka denna länk till användaren, den första inloggningen kan göras via en SSO-tjänst",
"app.components.Users.ModalCreateBody.block-title.details": "Användarinformation",
diff --git a/packages/core/admin/admin/src/translations/th.json b/packages/core/admin/admin/src/translations/th.json
index 62e449d8ee1..267e41cd4e3 100644
--- a/packages/core/admin/admin/src/translations/th.json
+++ b/packages/core/admin/admin/src/translations/th.json
@@ -176,12 +176,6 @@
"app.components.PluginCard.compatible": "เข้ากันได้กับแอปของคุณ",
"app.components.PluginCard.compatibleCommunity": "ทำงานร่วมกันได้กับเวอร์ชั่นชุมชน",
"app.components.PluginCard.more-details": "รายละเอียดเพิ่มเติม",
- "app.components.UpgradePlanModal.button": "เรียนรู้เพิ่มเติม",
- "app.components.UpgradePlanModal.limit-reached": "คุณได้ถึงขีดจำกัดแล้ว",
- "app.components.UpgradePlanModal.text-ce": "เอดิชันชุมชน",
- "app.components.UpgradePlanModal.text-ee": "เอดิชัน Enterprise",
- "app.components.UpgradePlanModal.text-power": "ปลดล็อกรูปแบบเต็ม",
- "app.components.UpgradePlanModal.text-strapi": "ของ Strapi โดยการอัพเกรดแผนของคุณไปยัง",
"app.components.Users.MagicLink.connect": "ส่งลิงก์นี้ไปยังผู้ใช้เพื่อให้ผู้ใช้สามารถเชื่อมต่อ",
"app.components.Users.ModalCreateBody.block-title.details": "รายละเอียด",
"app.components.Users.ModalCreateBody.block-title.roles": "บทบาทของผู้ใช้",
diff --git a/packages/core/admin/admin/src/translations/tr.json b/packages/core/admin/admin/src/translations/tr.json
index e7ad93d4ead..60d20aef278 100644
--- a/packages/core/admin/admin/src/translations/tr.json
+++ b/packages/core/admin/admin/src/translations/tr.json
@@ -383,12 +383,6 @@
"app.components.PluginCard.more-details": "Daha fazla detay",
"app.components.ToggleCheckbox.off-label": "Yanlış",
"app.components.ToggleCheckbox.on-label": "Doğru",
- "app.components.UpgradePlanModal.button": "Daha fazla bilgi al",
- "app.components.UpgradePlanModal.limit-reached": "Sınıra ulaştın",
- "app.components.UpgradePlanModal.text-ce": "Topluluk Sürümü",
- "app.components.UpgradePlanModal.text-ee": "Kurumsal Sürüm",
- "app.components.UpgradePlanModal.text-power": "Kurumsal Sürüme yükselterek Strapinin tüm gücünü ortaya çıkar.",
- "app.components.UpgradePlanModal.text-strapi": "of Strapi by upgrading your plan to the",
"app.components.Users.ModalCreateBody.block-title.roles.description": "Bir kullanıcı bir ya da daha fazla role sahip olabilir",
"app.components.listPlugins.button": "Yeni eklenti ekle",
"app.components.listPlugins.title.none": "Yüklenen eklenti bulunmamaktadır.",
diff --git a/packages/core/admin/admin/src/translations/zh-Hans.json b/packages/core/admin/admin/src/translations/zh-Hans.json
index 6bc1d1e6562..103086270e1 100644
--- a/packages/core/admin/admin/src/translations/zh-Hans.json
+++ b/packages/core/admin/admin/src/translations/zh-Hans.json
@@ -320,12 +320,6 @@
"app.components.PluginCard.more-details": "更多详情",
"app.components.ToggleCheckbox.off-label": "关",
"app.components.ToggleCheckbox.on-label": "开",
- "app.components.UpgradePlanModal.button": "了角更多",
- "app.components.UpgradePlanModal.limit-reached": "已经达到上限",
- "app.components.UpgradePlanModal.text-ce": "社区版",
- "app.components.UpgradePlanModal.text-ee": "企业版",
- "app.components.UpgradePlanModal.text-power": "通过将您的方案升级到企业版,开启 Strapi 的全部功能",
- "app.components.UpgradePlanModal.text-strapi": "通过将你的方案升级到 Strapi 的",
"app.components.Users.MagicLink.connect": "复制并分享此链接给用户以开通账户",
"app.components.Users.MagicLink.connect.sso": "将此链接发送给用户,第一次登录可以通过SSO供应商进行。",
"app.components.Users.ModalCreateBody.block-title.details": "用户详情",
diff --git a/packages/core/admin/admin/src/translations/zh.json b/packages/core/admin/admin/src/translations/zh.json
index 3e4f8970ef8..f647eba3659 100644
--- a/packages/core/admin/admin/src/translations/zh.json
+++ b/packages/core/admin/admin/src/translations/zh.json
@@ -403,12 +403,6 @@
"app.components.PluginCard.more-details": "顯示更多",
"app.components.ToggleCheckbox.off-label": "關",
"app.components.ToggleCheckbox.on-label": "開",
- "app.components.UpgradePlanModal.button": "學習更多",
- "app.components.UpgradePlanModal.limit-reached": "已經到達上限",
- "app.components.UpgradePlanModal.text-ce": "社群版",
- "app.components.UpgradePlanModal.text-ee": "企業版",
- "app.components.UpgradePlanModal.text-power": "透過將升級您的方案到企業版來解鎖 Strapi 的全部功能",
- "app.components.UpgradePlanModal.text-strapi": "透過將升級您的方案到",
"app.components.Users.MagicLink.connect": "將此連結傳送給使用者讓他可以開通帳號。",
"app.components.Users.MagicLink.connect.sso": "將此連結傳送給使用者讓他可以透過 SSO 登入。",
"app.components.Users.ModalCreateBody.block-title.details": "使用者詳細資料",
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/index.js
deleted file mode 100644
index 6cd2815be23..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/CreatePage/index.js
+++ /dev/null
@@ -1,270 +0,0 @@
-import React, { useRef, useState } from 'react';
-import { format } from 'date-fns';
-import {
- CheckPagePermissions,
- Form,
- LoadingIndicatorPage,
- SettingsPageTitle,
- request,
- useNotification,
- useOverlayBlocker,
- useTracking,
- Link,
-} from '@strapi/helper-plugin';
-import {
- Box,
- Button,
- ContentLayout,
- HeaderLayout,
- Grid,
- GridItem,
- Main,
- Flex,
- Typography,
- TextInput,
- Textarea,
-} from '@strapi/design-system';
-import { ArrowLeft } from '@strapi/icons';
-import { Formik } from 'formik';
-import get from 'lodash/get';
-import isEmpty from 'lodash/isEmpty';
-import { useIntl } from 'react-intl';
-import { useHistory, useRouteMatch } from 'react-router-dom';
-import styled from 'styled-components';
-import Permissions from '../../../../../../../admin/src/pages/SettingsPage/pages/Roles/EditPage/components/Permissions';
-import { useFetchPermissionsLayout, useFetchRole } from '../../../../../../../admin/src/hooks';
-import adminPermissions from '../../../../../../../admin/src/permissions';
-import schema from './utils/schema';
-
-const UsersRoleNumber = styled.div`
- border: 1px solid ${({ theme }) => theme.colors.primary200};
- background: ${({ theme }) => theme.colors.primary100};
- padding: ${({ theme }) => `${theme.spaces[2]} ${theme.spaces[4]}`};
- color: ${({ theme }) => theme.colors.primary600};
- border-radius: ${({ theme }) => theme.borderRadius};
- font-size: ${12 / 16}rem;
- font-weight: bold;
-`;
-
-const CreatePage = () => {
- const toggleNotification = useNotification();
- const { lockApp, unlockApp } = useOverlayBlocker();
- const { formatMessage } = useIntl();
- const [isSubmitting, setIsSubmiting] = useState(false);
- const { replace } = useHistory();
- const permissionsRef = useRef();
- const { trackUsage } = useTracking();
- const params = useRouteMatch('/settings/roles/duplicate/:id');
- const id = get(params, 'params.id', null);
- const { isLoading: isLayoutLoading, data: permissionsLayout } = useFetchPermissionsLayout();
- const { permissions: rolePermissions, isLoading: isRoleLoading } = useFetchRole(id);
-
- const handleCreateRoleSubmit = (data) => {
- lockApp();
- setIsSubmiting(true);
-
- if (id) {
- trackUsage('willDuplicateRole');
- } else {
- trackUsage('willCreateNewRole');
- }
-
- Promise.resolve(
- request('/admin/roles', {
- method: 'POST',
- body: data,
- })
- )
- .then(async (res) => {
- const { permissionsToSend } = permissionsRef.current.getPermissions();
-
- if (id) {
- trackUsage('didDuplicateRole');
- } else {
- trackUsage('didCreateNewRole');
- }
-
- if (res.data.id && !isEmpty(permissionsToSend)) {
- await request(`/admin/roles/${res.data.id}/permissions`, {
- method: 'PUT',
- body: { permissions: permissionsToSend },
- });
- }
-
- return res;
- })
- .then((res) => {
- setIsSubmiting(false);
- toggleNotification({
- type: 'success',
- message: { id: 'Settings.roles.created', defaultMessage: 'created' },
- });
- replace(`/settings/roles/${res.data.id}`);
- })
- .catch((err) => {
- console.error(err);
- setIsSubmiting(false);
- toggleNotification({
- type: 'warning',
- message: { id: 'notification.error' },
- });
- })
- .finally(() => {
- unlockApp();
- });
- };
-
- const defaultDescription = `${formatMessage({
- id: 'Settings.roles.form.created',
- defaultMessage: 'Created',
- })} ${format(new Date(), 'PPP')}`;
-
- return (
-
-
-
- {({ handleSubmit, values, errors, handleReset, handleChange }) => (
-
- <>
-
- {
- handleReset();
- permissionsRef.current.resetForm();
- }}
- size="L"
- >
- {formatMessage({
- id: 'app.components.Button.reset',
- defaultMessage: 'Reset',
- })}
-
-
- {formatMessage({
- id: 'global.save',
- defaultMessage: 'Save',
- })}
-
-
- }
- title={formatMessage({
- id: 'Settings.roles.create.title',
- defaultMessage: 'Create a role',
- })}
- subtitle={formatMessage({
- id: 'Settings.roles.create.description',
- defaultMessage: 'Define the rights given to the role',
- })}
- navigationAction={
- } to="/settings/roles">
- {formatMessage({
- id: 'global.back',
- defaultMessage: 'Back',
- })}
-
- }
- />
-
-
-
-
-
-
-
-
- {formatMessage({
- id: 'global.details',
- defaultMessage: 'Details',
- })}
-
-
-
-
- {formatMessage({
- id: 'Settings.roles.form.description',
- defaultMessage: 'Name and description of the role',
- })}
-
-
-
-
- {formatMessage(
- {
- id: 'Settings.roles.form.button.users-with-role',
- defaultMessage:
- '{number, plural, =0 {# users} one {# user} other {# users}} with this role',
- },
- { number: 0 }
- )}
-
-
-
-
-
-
-
-
- {values.description}
-
-
-
-
-
- {!isLayoutLoading && !isRoleLoading ? (
-
-
-
- ) : (
-
-
-
- )}
-
-
- >
-
- )}
-
-
- );
-};
-
-export default function () {
- return (
-
-
-
- );
-}
-
-export { CreatePage };
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js
deleted file mode 100644
index 5afcc16a9d4..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ActionRow/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = false;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ConditionsSelect/MenuList/utils/constants.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ConditionsSelect/MenuList/utils/constants.js
deleted file mode 100644
index 5afcc16a9d4..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ConditionsModal/ConditionsSelect/MenuList/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = false;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/utils/constants.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/utils/constants.js
deleted file mode 100644
index 5afcc16a9d4..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/Collapse/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = false;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js
deleted file mode 100644
index 5afcc16a9d4..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/ActionRow/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = false;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/utils/constants.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/utils/constants.js
deleted file mode 100644
index 5afcc16a9d4..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/ContentTypeCollapse/CollapsePropertyMatrix/SubActionRow/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = false;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js
deleted file mode 100644
index 5afcc16a9d4..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/GlobalActions/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = false;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js
deleted file mode 100644
index 5afcc16a9d4..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/EditPage/components/PluginsAndSettings/SubCategory/utils/constants.js
+++ /dev/null
@@ -1,3 +0,0 @@
-const IS_DISABLED = false;
-
-export default IS_DISABLED;
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js
deleted file mode 100644
index 6666e216813..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/index.js
+++ /dev/null
@@ -1,376 +0,0 @@
-import React, { useCallback, useEffect, useReducer, useState } from 'react';
-import {
- ConfirmDialog,
- LoadingIndicatorPage,
- SearchURLQuery,
- SettingsPageTitle,
- getFetchClient,
- useNotification,
- useQueryParams,
- useRBAC,
- useFocusWhenNavigate,
-} from '@strapi/helper-plugin';
-import { Plus, Trash, Duplicate, Pencil } from '@strapi/icons';
-import {
- Button,
- ActionLayout,
- ContentLayout,
- HeaderLayout,
- Main,
- Table,
- Tbody,
- TFooter,
- Thead,
- Th,
- Tr,
- Typography,
- VisuallyHidden,
-} from '@strapi/design-system';
-import { get } from 'lodash';
-import matchSorter from 'match-sorter';
-import { useIntl } from 'react-intl';
-import { useHistory } from 'react-router-dom';
-import { useRolesList } from '../../../../../../../admin/src/hooks';
-import adminPermissions from '../../../../../../../admin/src/permissions';
-import EmptyRole from '../../../../../../../admin/src/pages/SettingsPage/pages/Roles/ListPage/components/EmptyRole';
-import BaseRoleRow from '../../../../../../../admin/src/pages/SettingsPage/pages/Roles/ListPage/components/RoleRow';
-import reducer, { initialState } from './reducer';
-
-const useSortedRoles = () => {
- useFocusWhenNavigate();
- const {
- isLoading: isLoadingForPermissions,
- allowedActions: { canCreate, canDelete, canRead, canUpdate },
- } = useRBAC(adminPermissions.settings.roles);
-
- const { getData, roles, isLoading } = useRolesList(false);
- const [{ query }] = useQueryParams();
- const _q = query?._q || '';
- const sortedRoles = matchSorter(roles, _q, { keys: ['name', 'description'] });
-
- useEffect(() => {
- if (!isLoadingForPermissions && canRead) {
- getData();
- }
- }, [isLoadingForPermissions, canRead, getData]);
-
- return {
- isLoadingForPermissions,
- canCreate,
- canDelete,
- canRead,
- canUpdate,
- isLoading,
- getData,
- sortedRoles,
- roles,
- };
-};
-
-const useRoleActions = ({ getData, canCreate, canDelete, canUpdate }) => {
- const { formatMessage } = useIntl();
-
- const toggleNotification = useNotification();
- const [isWarningDeleteAllOpened, setIsWarningDeleteAllOpenend] = useState(false);
- const { push } = useHistory();
- const [{ selectedRoles, showModalConfirmButtonLoading, roleToDelete }, dispatch] = useReducer(
- reducer,
- initialState
- );
-
- const { post } = getFetchClient();
-
- const handleDeleteData = async () => {
- try {
- dispatch({
- type: 'ON_REMOVE_ROLES',
- });
-
- await post('/admin/roles/batch-delete', {
- ids: [roleToDelete],
- });
-
- await getData();
-
- dispatch({
- type: 'RESET_DATA_TO_DELETE',
- });
- } catch (err) {
- const errorIds = get(err, ['response', 'payload', 'data', 'ids'], null);
-
- if (errorIds && Array.isArray(errorIds)) {
- const errorsMsg = errorIds.join('\n');
- toggleNotification({
- type: 'warning',
- message: errorsMsg,
- });
- } else {
- toggleNotification({
- type: 'warning',
- message: { id: 'notification.error' },
- });
- }
- }
- handleToggleModal();
- };
-
- const onRoleDuplicate = useCallback(
- (id) => {
- push(`/settings/roles/duplicate/${id}`);
- },
- [push]
- );
-
- const handleNewRoleClick = () => push('/settings/roles/new');
-
- const onRoleRemove = useCallback((roleId) => {
- dispatch({
- type: 'SET_ROLE_TO_DELETE',
- id: roleId,
- });
-
- handleToggleModal();
- }, []);
-
- const handleToggleModal = () => setIsWarningDeleteAllOpenend((prev) => !prev);
-
- const handleGoTo = useCallback(
- (id) => {
- push(`/settings/roles/${id}`);
- },
- [push]
- );
-
- const handleClickDelete = useCallback(
- (e, role) => {
- e.preventDefault();
- e.stopPropagation();
-
- if (role.usersCount) {
- toggleNotification({
- type: 'info',
- message: { id: 'Roles.ListPage.notification.delete-not-allowed' },
- });
- } else {
- onRoleRemove(role.id);
- }
- },
- [toggleNotification, onRoleRemove]
- );
-
- const handleClickDuplicate = useCallback(
- (e, role) => {
- e.preventDefault();
- e.stopPropagation();
- onRoleDuplicate(role.id);
- },
- [onRoleDuplicate]
- );
-
- const getIcons = useCallback(
- (role) => [
- ...(canCreate
- ? [
- {
- onClick: (e) => handleClickDuplicate(e, role),
- label: formatMessage({ id: 'app.utils.duplicate', defaultMessage: 'Duplicate' }),
- icon: ,
- },
- ]
- : []),
- ...(canUpdate
- ? [
- {
- onClick: () => handleGoTo(role.id),
- label: formatMessage({ id: 'app.utils.edit', defaultMessage: 'Edit' }),
- icon: ,
- },
- ]
- : []),
- ...(canDelete
- ? [
- {
- onClick: (e) => handleClickDelete(e, role),
- label: formatMessage({ id: 'global.delete', defaultMessage: 'Delete' }),
- icon: ,
- },
- ]
- : []),
- ],
- [
- formatMessage,
- handleClickDelete,
- handleClickDuplicate,
- handleGoTo,
- canCreate,
- canUpdate,
- canDelete,
- ]
- );
-
- return {
- handleNewRoleClick,
- getIcons,
- selectedRoles,
- isWarningDeleteAllOpened,
- showModalConfirmButtonLoading,
- handleToggleModal,
- handleDeleteData,
- };
-};
-
-const RoleListPage = () => {
- const { formatMessage } = useIntl();
-
- const {
- isLoadingForPermissions,
- canCreate,
- canRead,
- canDelete,
- canUpdate,
- isLoading,
- getData,
- sortedRoles,
- } = useSortedRoles();
-
- const {
- handleNewRoleClick,
- getIcons,
- isWarningDeleteAllOpened,
- showModalConfirmButtonLoading,
- handleToggleModal,
- handleDeleteData,
- } = useRoleActions({ getData, canCreate, canDelete, canUpdate });
-
- // ! TODO - Show the search bar only if the user is allowed to read - add the search input
- // canRead
-
- const rowCount = sortedRoles.length + 1;
- const colCount = 6;
-
- if (isLoadingForPermissions) {
- return (
-
-
-
- );
- }
-
- const title = formatMessage({
- id: 'global.roles',
- defaultMessage: 'roles',
- });
-
- return (
-
-
- } size="S">
- {formatMessage({
- id: 'Settings.roles.list.button.add',
- defaultMessage: 'Add new role',
- })}
-
- ) : null
- }
- title={title}
- subtitle={formatMessage({
- id: 'Settings.roles.list.description',
- defaultMessage: 'List of roles',
- })}
- as="h2"
- />
- {canRead && (
-
- }
- />
- )}
- {canRead && (
-
- }>
- {formatMessage({
- id: 'Settings.roles.list.button.add',
- defaultMessage: 'Add new role',
- })}
-
- ) : null
- }
- >
-
-
-
-
- {formatMessage({
- id: 'global.name',
- defaultMessage: 'Name',
- })}
-
-
-
-
- {formatMessage({
- id: 'global.description',
- defaultMessage: 'Description',
- })}
-
-
-
-
- {formatMessage({
- id: 'global.users',
- defaultMessage: 'Users',
- })}
-
-
-
-
- {formatMessage({
- id: 'global.actions',
- defaultMessage: 'Actions',
- })}
-
-
-
-
-
- {sortedRoles?.map((role, index) => (
-
- ))}
-
-
- {!rowCount && !isLoading && }
-
- )}
-
-
- );
-};
-
-export default RoleListPage;
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/tests/index.test.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/tests/index.test.js
deleted file mode 100644
index ea8eef1ba63..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ListPage/tests/index.test.js
+++ /dev/null
@@ -1,165 +0,0 @@
-/**
- *
- * Tests for ListPage
- *
- */
-
-import React from 'react';
-import { render, screen } from '@testing-library/react';
-import { createMemoryHistory } from 'history';
-import { Router } from 'react-router-dom';
-import { IntlProvider } from 'react-intl';
-import { useRBAC, TrackingProvider } from '@strapi/helper-plugin';
-import { lightTheme, darkTheme } from '@strapi/design-system';
-import { useRolesList } from '../../../../../../../../admin/src/hooks';
-
-import Theme from '../../../../../../../../admin/src/components/Theme';
-import ThemeToggleProvider from '../../../../../../../../admin/src/components/ThemeToggleProvider';
-import ListPage from '../index';
-
-jest.mock('@strapi/helper-plugin', () => ({
- ...jest.requireActual('@strapi/helper-plugin'),
- useNotification: jest.fn(),
- useRBAC: jest.fn(() => ({
- isLoading: true,
- allowedActions: { canCreate: true, canDelete: true, canRead: true, canUpdate: true },
- })),
-}));
-
-jest.mock('../../../../../../../../admin/src/hooks', () => ({
- ...jest.requireActual('../../../../../../../../admin/src/hooks'),
- useRolesList: jest.fn(),
-}));
-
-const makeApp = (history) => (
-
-
-
-
-
-
-
-
-
-
-
-);
-
-describe(' ', () => {
- it('renders and matches the snapshot', () => {
- useRolesList.mockImplementationOnce(() => ({
- roles: [],
- isLoading: true,
- getData: jest.fn(),
- }));
- const history = createMemoryHistory();
- const App = makeApp(history);
-
- const {
- container: { firstChild },
- } = render(App);
-
- expect(firstChild).toMatchInlineSnapshot(`
- .c1 {
- -webkit-align-items: center;
- -webkit-box-align: center;
- -ms-flex-align: center;
- align-items: center;
- display: -webkit-box;
- display: -webkit-flex;
- display: -ms-flexbox;
- display: flex;
- -webkit-flex-direction: row;
- -ms-flex-direction: row;
- flex-direction: row;
- -webkit-box-pack: space-around;
- -webkit-justify-content: space-around;
- -ms-flex-pack: space-around;
- justify-content: space-around;
- }
-
- .c3 {
- border: 0;
- -webkit-clip: rect(0 0 0 0);
- clip: rect(0 0 0 0);
- height: 1px;
- margin: -1px;
- overflow: hidden;
- padding: 0;
- position: absolute;
- width: 1px;
- }
-
- .c4 {
- -webkit-animation: gzYjWD 1s infinite linear;
- animation: gzYjWD 1s infinite linear;
- will-change: transform;
- }
-
- .c0:focus-visible {
- outline: none;
- }
-
- .c2 {
- height: 100vh;
- }
-
-
-
-
-
- Loading content.
-
-
-
-
-
- `);
- });
-
- it('should show a list of roles', () => {
- useRolesList.mockImplementationOnce(() => ({
- roles: [
- {
- code: 'strapi-super-admin',
- created_at: '2021-08-24T14:37:20.384Z',
- description: 'Super Admins can access and manage all features and settings.',
- id: 1,
- name: 'Super Admin',
- updatedAt: '2021-08-24T14:37:20.384Z',
- usersCount: 1,
- },
- ],
- isLoading: false,
- getData: jest.fn(),
- }));
-
- useRBAC.mockImplementationOnce(() => ({
- isLoading: false,
- allowedActions: { canCreate: true, canDelete: true, canRead: true, canUpdate: true },
- }));
- const history = createMemoryHistory();
- const App = makeApp(history);
-
- render(App);
-
- expect(screen.getByText('Super Admin')).toBeInTheDocument();
- });
-});
diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ProtectedListPage/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ProtectedListPage/index.js
deleted file mode 100644
index 45de9f6fe1a..00000000000
--- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/Roles/ProtectedListPage/index.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import { CheckPagePermissions } from '@strapi/helper-plugin';
-import adminPermissions from '../../../../../../../admin/src/permissions';
-import ListPage from '../ListPage';
-
-const ProtectedListPage = () => (
-
-
-
-);
-
-export default ProtectedListPage;
From 6c36872dd2cf80fda312365cc75ac7a3b68883e1 Mon Sep 17 00:00:00 2001
From: Alexandre Bodin
Date: Mon, 6 Mar 2023 22:17:56 +0100
Subject: [PATCH 3/8] Fix test
---
packages/core/admin/server/controllers/role.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/core/admin/server/controllers/role.js b/packages/core/admin/server/controllers/role.js
index 03c4b44644a..e5a9372939b 100644
--- a/packages/core/admin/server/controllers/role.js
+++ b/packages/core/admin/server/controllers/role.js
@@ -142,9 +142,9 @@ module.exports = {
const sanitizedPermissions = permissions.map(permissionService.sanitizePermission);
- return ctx.send({
+ ctx.body = {
data: sanitizedPermissions,
- });
+ };
},
/**
From e3a4b431bafa54d76ac8244177a46cd65da2b9cf Mon Sep 17 00:00:00 2001
From: Alexandre Bodin
Date: Tue, 7 Mar 2023 08:41:07 +0100
Subject: [PATCH 4/8] clean comment
---
packages/core/admin/ee/server/routes/index.js | 49 -------------------
1 file changed, 49 deletions(-)
diff --git a/packages/core/admin/ee/server/routes/index.js b/packages/core/admin/ee/server/routes/index.js
index 857423dd89b..180061cb892 100644
--- a/packages/core/admin/ee/server/routes/index.js
+++ b/packages/core/admin/ee/server/routes/index.js
@@ -11,55 +11,6 @@ const enableFeatureMiddleware = (featureName) => (ctx, next) => {
};
module.exports = [
- // {
- // method: 'POST',
- // path: '/roles',
- // handler: 'role.create',
- // config: {
- // policies: [
- // 'admin::isAuthenticatedAdmin',
- // {
- // name: 'admin::hasPermissions',
- // config: {
- // actions: ['admin::roles.create'],
- // },
- // },
- // ],
- // },
- // },
- // {
- // method: 'DELETE',
- // path: '/roles/:id',
- // handler: 'role.deleteOne',
- // config: {
- // policies: [
- // 'admin::isAuthenticatedAdmin',
- // {
- // name: 'admin::hasPermissions',
- // config: {
- // actions: ['admin::roles.delete'],
- // },
- // },
- // ],
- // },
- // },
- // {
- // method: 'POST',
- // path: '/roles/batch-delete',
- // handler: 'role.deleteMany',
- // config: {
- // policies: [
- // 'admin::isAuthenticatedAdmin',
- // {
- // name: 'admin::hasPermissions',
- // config: {
- // actions: ['admin::roles.delete'],
- // },
- // },
- // ],
- // },
- // },
-
// SSO
{
method: 'GET',
From 11821500d2c3ba9494981a25960e47a02ed6c09e Mon Sep 17 00:00:00 2001
From: Alexandre Bodin
Date: Tue, 7 Mar 2023 14:17:01 +0100
Subject: [PATCH 5/8] Update FE snapshot
---
.../pages/Roles/ListPage/tests/index.test.js | 552 +-----------------
1 file changed, 21 insertions(+), 531 deletions(-)
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/tests/index.test.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/tests/index.test.js
index 49579a1ecbd..46d64fd5f8a 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/tests/index.test.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/Roles/ListPage/tests/index.test.js
@@ -62,62 +62,6 @@ describe(' ', () => {
expect(firstChild).toMatchInlineSnapshot(`
.c1 {
- background: #f6f6f9;
- padding-top: 40px;
- padding-right: 56px;
- padding-bottom: 40px;
- padding-left: 56px;
- }
-
- .c6 {
- background: #4945ff;
- padding: 8px;
- padding-right: 16px;
- padding-left: 16px;
- border-radius: 4px;
- border-color: #4945ff;
- border: 1px solid #4945ff;
- cursor: pointer;
- }
-
- .c12 {
- padding-right: 56px;
- padding-left: 56px;
- }
-
- .c13 {
- background: #ffffff;
- border-radius: 4px;
- box-shadow: 0px 1px 4px rgba(33,33,52,0.1);
- }
-
- .c15 {
- position: relative;
- }
-
- .c17 {
- padding-right: 24px;
- padding-left: 24px;
- }
-
- .c27 {
- background: #eaeaef;
- }
-
- .c29 {
- background: #f0f0ff;
- padding: 20px;
- }
-
- .c31 {
- background: #d9d8ff;
- }
-
- .c33 {
- padding-left: 12px;
- }
-
- .c2 {
-webkit-align-items: center;
-webkit-box-align: center;
-ms-flex-align: center;
@@ -129,126 +73,13 @@ describe(' ', () => {
-webkit-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
- -webkit-box-pack: justify;
- -webkit-justify-content: space-between;
- -ms-flex-pack: justify;
- justify-content: space-between;
+ -webkit-box-pack: space-around;
+ -webkit-justify-content: space-around;
+ -ms-flex-pack: space-around;
+ justify-content: space-around;
}
.c3 {
- -webkit-align-items: center;
- -webkit-box-align: center;
- -ms-flex-align: center;
- align-items: center;
- display: -webkit-box;
- display: -webkit-flex;
- display: -ms-flexbox;
- display: flex;
- -webkit-flex-direction: row;
- -ms-flex-direction: row;
- flex-direction: row;
- }
-
- .c7 {
- -webkit-align-items: center;
- -webkit-box-align: center;
- -ms-flex-align: center;
- align-items: center;
- display: -webkit-box;
- display: -webkit-flex;
- display: -ms-flexbox;
- display: flex;
- -webkit-flex-direction: row;
- -ms-flex-direction: row;
- flex-direction: row;
- gap: 8px;
- }
-
- .c5 {
- font-weight: 600;
- font-size: 2rem;
- line-height: 1.25;
- color: #32324d;
- }
-
- .c10 {
- font-size: 0.75rem;
- line-height: 1.33;
- font-weight: 600;
- color: #ffffff;
- }
-
- .c11 {
- font-size: 1rem;
- line-height: 1.5;
- color: #666687;
- }
-
- .c23 {
- font-weight: 600;
- font-size: 0.6875rem;
- line-height: 1.45;
- text-transform: uppercase;
- color: #666687;
- }
-
- .c34 {
- font-size: 0.75rem;
- line-height: 1.33;
- font-weight: 600;
- color: #4945ff;
- }
-
- .c8 {
- position: relative;
- outline: none;
- }
-
- .c8 svg {
- height: 12px;
- width: 12px;
- }
-
- .c8 svg > g,
- .c8 svg path {
- fill: #ffffff;
- }
-
- .c8[aria-disabled='true'] {
- pointer-events: none;
- }
-
- .c8:after {
- -webkit-transition-property: all;
- transition-property: all;
- -webkit-transition-duration: 0.2s;
- transition-duration: 0.2s;
- border-radius: 8px;
- content: '';
- position: absolute;
- top: -4px;
- bottom: -4px;
- left: -4px;
- right: -4px;
- border: 2px solid transparent;
- }
-
- .c8:focus-visible {
- outline: none;
- }
-
- .c8:focus-visible:after {
- border-radius: 8px;
- content: '';
- position: absolute;
- top: -5px;
- bottom: -5px;
- left: -5px;
- right: -5px;
- border: 2px solid #4945ff;
- }
-
- .c25 {
border: 0;
-webkit-clip: rect(0 0 0 0);
clip: rect(0 0 0 0);
@@ -260,174 +91,18 @@ describe(' ', () => {
width: 1px;
}
- .c9 {
- height: 2rem;
- }
-
- .c9[aria-disabled='true'] {
- border: 1px solid #dcdce4;
- background: #eaeaef;
- }
-
- .c9[aria-disabled='true'] .c4 {
- color: #666687;
- }
-
- .c9[aria-disabled='true'] svg > g,.c9[aria-disabled='true'] svg path {
- fill: #666687;
- }
-
- .c9[aria-disabled='true']:active {
- border: 1px solid #dcdce4;
- background: #eaeaef;
- }
-
- .c9[aria-disabled='true']:active .c4 {
- color: #666687;
- }
-
- .c9[aria-disabled='true']:active svg > g,.c9[aria-disabled='true']:active svg path {
- fill: #666687;
- }
-
- .c9:hover {
- border: 1px solid #7b79ff;
- background: #7b79ff;
- }
-
- .c9:active {
- border: 1px solid #4945ff;
- background: #4945ff;
- }
-
- .c9 svg > g,
- .c9 svg path {
- fill: #ffffff;
- }
-
- .c28 {
- height: 1px;
- border: none;
- -webkit-flex-shrink: 0;
- -ms-flex-negative: 0;
- flex-shrink: 0;
- margin: 0;
+ .c4 {
+ -webkit-animation: gzYjWD 1s infinite linear;
+ animation: gzYjWD 1s infinite linear;
+ will-change: transform;
}
.c0:focus-visible {
outline: none;
}
- .c14 {
- overflow: hidden;
- border: 1px solid #eaeaef;
- }
-
- .c19 {
- width: 100%;
- white-space: nowrap;
- }
-
- .c16:before {
- background: linear-gradient(90deg,#c0c0cf 0%,rgba(0,0,0,0) 100%);
- opacity: 0.2;
- position: absolute;
- height: 100%;
- box-shadow: 0px 1px 4px rgba(33,33,52,0.1);
- width: 8px;
- left: 0;
- }
-
- .c16:after {
- background: linear-gradient(270deg,#c0c0cf 0%,rgba(0,0,0,0) 100%);
- opacity: 0.2;
- position: absolute;
- height: 100%;
- box-shadow: 0px 1px 4px rgba(33,33,52,0.1);
- width: 8px;
- right: 0;
- top: 0;
- }
-
- .c18 {
- overflow-x: auto;
- }
-
- .c26 tr:last-of-type {
- border-bottom: none;
- }
-
- .c20 {
- border-bottom: 1px solid #eaeaef;
- }
-
- .c21 {
- border-bottom: 1px solid #eaeaef;
- }
-
- .c21 td,
- .c21 th {
- padding: 16px;
- }
-
- .c21 td:first-of-type,
- .c21 th:first-of-type {
- padding: 0 4px;
- }
-
- .c21 th {
- padding-top: 0;
- padding-bottom: 0;
- height: 3.5rem;
- }
-
- .c22 {
- vertical-align: middle;
- text-align: left;
- color: #666687;
- outline-offset: -4px;
- }
-
- .c22 input {
- vertical-align: sub;
- }
-
- .c24 svg {
- height: 0.25rem;
- }
-
- .c32 {
- height: 1.5rem;
- width: 1.5rem;
- border-radius: 50%;
- display: -webkit-box;
- display: -webkit-flex;
- display: -ms-flexbox;
- display: flex;
- -webkit-box-pack: center;
- -webkit-justify-content: center;
- -ms-flex-pack: center;
- justify-content: center;
- -webkit-align-items: center;
- -webkit-box-align: center;
- -ms-flex-align: center;
- align-items: center;
- }
-
- .c32 svg {
- height: 0.625rem;
- width: 0.625rem;
- }
-
- .c32 svg path {
- fill: #4945ff;
- }
-
- .c30 {
- border-radius: 0 0 4px 4px;
- display: block;
- width: 100%;
- border: none;
+ .c2 {
+ height: 100vh;
}
', () => {
tabindex="-1"
>
-
-
- roles
-
-
-
-
-
- Add new role
-
-
-
-
- List of roles
-
-
-
-
-
-
-
-
-
-
-
-
-
- Name
-
-
-
-
-
-
-
- Description
-
-
-
-
-
-
-
- Users
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Add new role
-
-
-
-
+ Loading content.
+
From 29dadec29289f683d6708e60735046baa7daa644 Mon Sep 17 00:00:00 2001
From: Alexandre Bodin
Date: Thu, 9 Mar 2023 02:43:16 +0100
Subject: [PATCH 6/8] Feedbacks
---
packages/core/admin/ee/server/controllers/index.js | 5 ++---
packages/core/admin/server/validation/permission.js | 1 -
2 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/packages/core/admin/ee/server/controllers/index.js b/packages/core/admin/ee/server/controllers/index.js
index fd8d5d1e22e..68d6f1cb02d 100644
--- a/packages/core/admin/ee/server/controllers/index.js
+++ b/packages/core/admin/ee/server/controllers/index.js
@@ -2,9 +2,8 @@
module.exports = {
authentication: require('./authentication'),
- // permission: require('./permission'),
- // role: require('./role'),
- // user: require('./user'),
+ role: require('./role'),
+ user: require('./user'),
auditLogs: require('./audit-logs'),
admin: require('./admin'),
};
diff --git a/packages/core/admin/server/validation/permission.js b/packages/core/admin/server/validation/permission.js
index 11cf98640b3..15d5e9e8a37 100644
--- a/packages/core/admin/server/validation/permission.js
+++ b/packages/core/admin/server/validation/permission.js
@@ -50,7 +50,6 @@ const actionsExistSchema = yup
// exports
module.exports = {
- // validatedUpdatePermissionsInput,
validatedUpdatePermissionsInput: validateYupSchema(validators.updatePermissions),
validatePermissionsExist: validateYupSchema(actionsExistSchema),
validateCheckPermissionsInput: validateYupSchema(checkPermissionsSchema),
From 4f462fb5361c33140cba67d58211c025c532e9f7 Mon Sep 17 00:00:00 2001
From: Alexandre Bodin
Date: Mon, 13 Mar 2023 14:17:34 +0100
Subject: [PATCH 7/8] Remove unused assets
---
.../src/assets/images/hot-air-balloon.png | Bin 111059 -> 0 bytes
.../src/assets/images/upgrade-details.png | Bin 1422 -> 0 bytes
2 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 packages/core/admin/admin/src/assets/images/hot-air-balloon.png
delete mode 100644 packages/core/admin/admin/src/assets/images/upgrade-details.png
diff --git a/packages/core/admin/admin/src/assets/images/hot-air-balloon.png b/packages/core/admin/admin/src/assets/images/hot-air-balloon.png
deleted file mode 100644
index 1e5c1736e728d529eb51b58dab25edaacf82056a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 111059
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 OT^@5ELy?K^+3i!
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