@strapi/admin 4.12.7 → 4.13.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. package/admin/src/components/NpsSurvey/hooks/useNpsSurveySettings.js +17 -0
  2. package/admin/src/components/NpsSurvey/index.js +365 -0
  3. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumns.js +2 -0
  4. package/admin/src/content-manager/components/DynamicZone/components/DynamicZoneLabel.js +1 -1
  5. package/admin/src/content-manager/components/EditViewDataManagerProvider/reducer.js +8 -1
  6. package/admin/src/content-manager/components/Filter/CustomInputs/AdminUsersFilter.js +42 -0
  7. package/admin/src/content-manager/components/Filter/Filter.js +54 -0
  8. package/admin/src/content-manager/components/Filter/index.js +1 -0
  9. package/admin/src/content-manager/components/InputUID/index.js +222 -216
  10. package/admin/src/content-manager/components/RelationInput/RelationInput.js +4 -0
  11. package/admin/src/content-manager/components/RepeatableComponent/index.js +32 -2
  12. package/admin/src/content-manager/components/Wysiwyg/Editor.js +432 -68
  13. package/admin/src/content-manager/components/Wysiwyg/WysiwygStyles.js +0 -7
  14. package/admin/src/content-manager/components/Wysiwyg/index.js +147 -152
  15. package/admin/src/content-manager/hooks/useAllowedAttributes.js +47 -0
  16. package/admin/src/content-manager/pages/EditView/Information/index.js +9 -8
  17. package/admin/src/content-manager/pages/ListSettingsView/components/Settings.js +40 -7
  18. package/admin/src/content-manager/pages/ListSettingsView/index.js +6 -2
  19. package/admin/src/content-manager/pages/ListView/components/FieldPicker/index.js +67 -69
  20. package/admin/src/content-manager/pages/ListView/components/ViewSettingsMenu/index.js +80 -0
  21. package/admin/src/content-manager/pages/ListView/index.js +236 -67
  22. package/admin/src/content-manager/utils/getDisplayName.js +33 -0
  23. package/admin/src/content-manager/utils/index.js +1 -0
  24. package/admin/src/pages/Admin/index.js +3 -1
  25. package/admin/src/pages/AuthPage/components/Register/index.js +5 -0
  26. package/admin/src/pages/SettingsPage/components/SettingsNav/index.js +3 -3
  27. package/admin/src/pages/SettingsPage/index.js +16 -26
  28. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js +69 -35
  29. package/admin/src/plugins.js +7 -8
  30. package/admin/src/translations/en.json +10 -1
  31. package/build/{1049.f76cb14b.chunk.js → 1049.ec69f5e0.chunk.js} +1 -1
  32. package/build/1227.9f37e1dc.chunk.js +1 -0
  33. package/build/{1386.879bcd90.chunk.js → 1386.ea73b677.chunk.js} +1 -1
  34. package/build/1504.eff012f7.chunk.js +95 -0
  35. package/build/{2225.c6244756.chunk.js → 2225.649fb7bc.chunk.js} +11 -11
  36. package/build/2237.b832ae6e.chunk.js +114 -0
  37. package/build/2379.1f98a31a.chunk.js +1 -0
  38. package/build/2395.0e5e8ded.chunk.js +26 -0
  39. package/build/2801.8e1aa82a.chunk.js +1 -0
  40. package/build/{3483.03c24f96.chunk.js → 3483.19381b40.chunk.js} +1 -1
  41. package/build/4174.f1f39e40.chunk.js +1 -0
  42. package/build/4546.a5946d22.chunk.js +1 -0
  43. package/build/4724.aea5c8c1.chunk.js +6 -0
  44. package/build/502.7bba43b1.chunk.js +1 -0
  45. package/build/6158.c3c13c20.chunk.js +1 -0
  46. package/build/7464.eb057bec.chunk.js +1 -0
  47. package/build/78.dcc6df5c.chunk.js +1 -0
  48. package/build/8276.be3ed581.chunk.js +26 -0
  49. package/build/{Admin-authenticatedApp.31497f74.chunk.js → Admin-authenticatedApp.43b6ec9a.chunk.js} +2 -2
  50. package/build/{Admin_InternalErrorPage.f45f2462.chunk.js → Admin_InternalErrorPage.38155af3.chunk.js} +1 -1
  51. package/build/{Admin_homePage.ac9dfb86.chunk.js → Admin_homePage.6f128523.chunk.js} +1 -1
  52. package/build/{Admin_marketplace.c94239f6.chunk.js → Admin_marketplace.061a6e5a.chunk.js} +1 -1
  53. package/build/{Admin_pluginsPage.bbe79434.chunk.js → Admin_pluginsPage.16f837b8.chunk.js} +1 -1
  54. package/build/{Admin_profilePage.192edc52.chunk.js → Admin_profilePage.678bce24.chunk.js} +2 -2
  55. package/build/Admin_settingsPage.af7309e4.chunk.js +111 -0
  56. package/build/{Upload_ConfigureTheView.345ac1e0.chunk.js → Upload_ConfigureTheView.3fc1c100.chunk.js} +1 -1
  57. package/build/admin-app.d63bd229.chunk.js +36 -0
  58. package/build/{admin-edit-roles-page.6d567273.chunk.js → admin-edit-roles-page.38a6c863.chunk.js} +3 -3
  59. package/build/{admin-edit-users.acfd4128.chunk.js → admin-edit-users.545fc882.chunk.js} +2 -2
  60. package/build/{admin-roles-list.23ddff26.chunk.js → admin-roles-list.1e2e814d.chunk.js} +1 -1
  61. package/build/{admin-users.00e20017.chunk.js → admin-users.b8ea5677.chunk.js} +2 -2
  62. package/build/{api-tokens-create-page.46c2ea84.chunk.js → api-tokens-create-page.e0c15627.chunk.js} +1 -1
  63. package/build/{api-tokens-edit-page.58139df9.chunk.js → api-tokens-edit-page.9f2dce47.chunk.js} +1 -1
  64. package/build/{api-tokens-list-page.0af7d431.chunk.js → api-tokens-list-page.d747051c.chunk.js} +2 -2
  65. package/build/{audit-logs-settings-page.0f73ccf8.chunk.js → audit-logs-settings-page.96f9d608.chunk.js} +1 -1
  66. package/build/content-manager.ccff1078.chunk.js +1097 -0
  67. package/build/{content-type-builder-list-view.bf9be456.chunk.js → content-type-builder-list-view.b71cf240.chunk.js} +1 -1
  68. package/build/{content-type-builder.cd999f6e.chunk.js → content-type-builder.e5669749.chunk.js} +2 -2
  69. package/build/email-settings-page.2809f0bf.chunk.js +11 -0
  70. package/build/en-json.e12fd5fc.chunk.js +1 -0
  71. package/build/{i18n-settings-page.47f78016.chunk.js → i18n-settings-page.5f716172.chunk.js} +1 -1
  72. package/build/index.html +1 -1
  73. package/build/main.c6c9e04c.js +2859 -0
  74. package/build/review-workflows-settings-create-view.4a156a19.chunk.js +1 -0
  75. package/build/review-workflows-settings-edit-view.ce984d1f.chunk.js +1 -0
  76. package/build/review-workflows-settings-list-view.419b8deb.chunk.js +56 -0
  77. package/build/{runtime~main.d515c521.js → runtime~main.dcf1cb45.js} +2 -2
  78. package/build/{sso-settings-page.12b6d8ae.chunk.js → sso-settings-page.45153df5.chunk.js} +1 -1
  79. package/build/{transfer-tokens-create-page.1597e6ab.chunk.js → transfer-tokens-create-page.ebba16d8.chunk.js} +1 -1
  80. package/build/{transfer-tokens-edit-page.8741529f.chunk.js → transfer-tokens-edit-page.d7bb2b3e.chunk.js} +1 -1
  81. package/build/{transfer-tokens-list-page.d6986b03.chunk.js → transfer-tokens-list-page.cfe1736c.chunk.js} +2 -2
  82. package/build/{upload-settings.7f93d4c0.chunk.js → upload-settings.cc5ad813.chunk.js} +1 -1
  83. package/build/{upload.37488080.chunk.js → upload.756efc28.chunk.js} +1 -1
  84. package/build/users-advanced-settings-page.818d84eb.chunk.js +9 -0
  85. package/build/users-email-settings-page.c1967c09.chunk.js +9 -0
  86. package/build/users-providers-settings-page.11893e08.chunk.js +14 -0
  87. package/build/users-roles-settings-page.2b051e6a.chunk.js +55 -0
  88. package/build/{webhook-edit-page.6cb479ff.chunk.js → webhook-edit-page.de45c635.chunk.js} +2 -2
  89. package/build/{webhook-list-page.65e1b5bb.chunk.js → webhook-list-page.ca91df8b.chunk.js} +1 -1
  90. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/AssigneeFilter.js +42 -0
  91. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/StageFilter.js +70 -0
  92. package/ee/admin/content-manager/components/Filter/CustomInputs/ReviewWorkflows/constants.js +71 -0
  93. package/ee/admin/content-manager/pages/EditView/InformationBox/InformationBoxEE.js +9 -217
  94. package/ee/admin/content-manager/pages/EditView/InformationBox/components/AssigneeSelect/AssigneeSelect.js +147 -0
  95. package/ee/admin/content-manager/pages/EditView/InformationBox/components/AssigneeSelect/index.js +1 -0
  96. package/ee/admin/content-manager/pages/EditView/InformationBox/components/StageSelect/StageSelect.js +243 -0
  97. package/ee/admin/content-manager/pages/EditView/InformationBox/components/StageSelect/index.js +1 -0
  98. package/ee/admin/content-manager/pages/EditView/InformationBox/constants.js +2 -0
  99. package/ee/admin/content-manager/pages/ListSettingsView/constants.js +7 -0
  100. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/ReviewWorkflowsAssigneeEE.js +21 -0
  101. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/constants.js +44 -17
  102. package/ee/admin/content-manager/pages/ListView/ReviewWorkflowsColumn/index.js +1 -0
  103. package/ee/admin/hooks/useAuthProviders.js +25 -0
  104. package/ee/admin/hooks/{useLicenseLimitNotification/index.js → useLicenseLimitNotification.js} +2 -4
  105. package/ee/admin/hooks/{useLicenseLimits/useLicenseLimits.js → useLicenseLimits.js} +4 -1
  106. package/ee/admin/pages/AuthPage/components/Login/index.js +8 -4
  107. package/ee/admin/pages/AuthPage/components/Providers/index.js +8 -5
  108. package/ee/admin/pages/HomePage/index.js +1 -1
  109. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js +1 -1
  110. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js +1 -1
  111. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js +1 -1
  112. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/CreateAction/index.js +1 -1
  113. package/ee/admin/pages/SettingsPage/pages/Users/ListPage/index.js +1 -1
  114. package/ee/server/constants/workflows.js +1 -0
  115. package/ee/server/controllers/index.js +1 -0
  116. package/ee/server/controllers/workflows/assignees/index.js +44 -0
  117. package/ee/server/routes/review-workflows.js +17 -0
  118. package/ee/server/services/index.js +1 -0
  119. package/ee/server/services/review-workflows/assignees.js +54 -0
  120. package/ee/server/services/review-workflows/metrics/index.js +5 -0
  121. package/ee/server/services/review-workflows/review-workflows.js +20 -11
  122. package/ee/server/validation/review-workflows.js +8 -0
  123. package/index.js +2 -6
  124. package/package.json +9 -9
  125. package/scripts/build.js +15 -15
  126. package/scripts/create-dev-plugins-file.js +5 -38
  127. package/server/controllers/role.js +2 -0
  128. package/server/controllers/user.js +2 -0
  129. package/server/services/permission/permissions-manager/index.js +3 -1
  130. package/server/services/permission/permissions-manager/sanitize.js +19 -7
  131. package/server/services/permission/permissions-manager/validate.js +218 -0
  132. package/utils/create-cache-dir.js +62 -16
  133. package/utils/create-plugins-exclude-path.js +3 -23
  134. package/utils/get-plugins.js +110 -0
  135. package/utils/index.js +1 -1
  136. package/webpack.config.js +10 -13
  137. package/admin/src/content-manager/components/AttributeFilter/Filters.js +0 -58
  138. package/admin/src/content-manager/components/AttributeFilter/hooks/useAllowedAttributes.js +0 -42
  139. package/admin/src/content-manager/components/AttributeFilter/index.js +0 -40
  140. package/admin/src/content-manager/components/Wysiwyg/EditorStylesContainer.js +0 -344
  141. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/api.js +0 -23
  142. package/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/utils/prefixAllUrls.js +0 -17
  143. package/admin/src/pages/SettingsPage/utils/createSectionsRoutes.js +0 -11
  144. package/admin/src/pages/SettingsPage/utils/getSectionsToDisplay.js +0 -5
  145. package/admin/src/pages/SettingsPage/utils/index.js +0 -2
  146. package/build/2379.f1641312.chunk.js +0 -1
  147. package/build/2395.46f8d0c1.chunk.js +0 -26
  148. package/build/2801.5cef5ec8.chunk.js +0 -1
  149. package/build/3929.5632f24d.chunk.js +0 -114
  150. package/build/3984.dda474f7.chunk.js +0 -1
  151. package/build/4546.7a3c0d03.chunk.js +0 -1
  152. package/build/502.8ae8ef60.chunk.js +0 -1
  153. package/build/5483.6dd2e776.chunk.js +0 -6
  154. package/build/5542.2415a393.chunk.js +0 -63
  155. package/build/6158.c974fd83.chunk.js +0 -1
  156. package/build/7464.3e64a1d5.chunk.js +0 -1
  157. package/build/8276.10a3f883.chunk.js +0 -26
  158. package/build/Admin_settingsPage.97cb9d41.chunk.js +0 -111
  159. package/build/admin-app.91898385.chunk.js +0 -36
  160. package/build/content-manager.b40f79c0.chunk.js +0 -1099
  161. package/build/email-settings-page.d494d1eb.chunk.js +0 -11
  162. package/build/en-json.08c05fcf.chunk.js +0 -1
  163. package/build/main.9dbe4579.js +0 -2859
  164. package/build/review-workflows-settings-create-view.cb08cfa2.chunk.js +0 -1
  165. package/build/review-workflows-settings-edit-view.3c7cbe63.chunk.js +0 -1
  166. package/build/review-workflows-settings-list-view.1611dc1f.chunk.js +0 -56
  167. package/build/users-advanced-settings-page.f0760eb8.chunk.js +0 -9
  168. package/build/users-email-settings-page.ff4b32f3.chunk.js +0 -9
  169. package/build/users-providers-settings-page.48de0306.chunk.js +0 -14
  170. package/build/users-roles-settings-page.9d9a1eff.chunk.js +0 -30
  171. package/ee/admin/hooks/index.js +0 -4
  172. package/ee/admin/hooks/useAuthProviders/index.js +0 -50
  173. package/ee/admin/hooks/useAuthProviders/reducer.js +0 -26
  174. package/ee/admin/hooks/useLicenseLimits/index.js +0 -1
  175. package/scripts/create-plugins-file.js +0 -92
  176. package/utils/get-plugins-path.js +0 -41
  177. /package/ee/admin/hooks/{useLicenseLimits/__mocks__/index.js → __mocks__/useLicenseLimits.js} +0 -0
  178. /package/server/services/permission/permissions-manager/{query-builers.js → query-builders.js} +0 -0
@@ -6,7 +6,7 @@ import isNil from 'lodash/isNil';
6
6
  import PropTypes from 'prop-types';
7
7
  import { useIntl } from 'react-intl';
8
8
 
9
- import { useLicenseLimits } from '../../../../../../hooks';
9
+ import { useLicenseLimits } from '../../../../../../hooks/useLicenseLimits';
10
10
 
11
11
  export const CreateActionEE = ({ onClick }) => {
12
12
  const { formatMessage } = useIntl();
@@ -2,7 +2,7 @@ import * as React from 'react';
2
2
 
3
3
  // eslint-disable-next-line import/no-cycle
4
4
  import { UserListPageCE } from '../../../../../../../admin/src/pages/SettingsPage/pages/Users/ListPage';
5
- import { useLicenseLimitNotification } from '../../../../../hooks';
5
+ import { useLicenseLimitNotification } from '../../../../../hooks/useLicenseLimitNotification';
6
6
 
7
7
  function UserListPageEE() {
8
8
  useLicenseLimitNotification();
@@ -6,6 +6,7 @@ module.exports = {
6
6
  STAGE_MODEL_UID: 'admin::workflow-stage',
7
7
  STAGE_DEFAULT_COLOR: '#4945FF',
8
8
  ENTITY_STAGE_ATTRIBUTE: 'strapi_stage',
9
+ ENTITY_ASSIGNEE_ATTRIBUTE: 'strapi_assignee',
9
10
  MAX_WORKFLOWS: 200,
10
11
  MAX_STAGES_PER_WORKFLOW: 200,
11
12
  ERRORS: {
@@ -8,4 +8,5 @@ module.exports = {
8
8
  admin: require('./admin'),
9
9
  workflows: require('./workflows'),
10
10
  stages: require('./workflows/stages'),
11
+ assignees: require('./workflows/assignees'),
11
12
  };
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ const { getService } = require('../../../utils');
4
+ const { validateUpdateAssigneeOnEntity } = require('../../../validation/review-workflows');
5
+
6
+ module.exports = {
7
+ /**
8
+ * Updates an entity's assignee.
9
+ * @async
10
+ * @param {Object} ctx - The Koa context object.
11
+ * @param {Object} ctx.params - An object containing the parameters from the request URL.
12
+ * @param {string} ctx.params.model_uid - The model UID of the entity.
13
+ * @param {string} ctx.params.id - The ID of the entity to update.
14
+ * @param {Object} ctx.request.body.data - Optional data object containing the new assignee ID for the entity.
15
+ * @param {string} ctx.request.body.data.id - The ID of the new assignee for the entity.
16
+ * @throws {ApplicationError} If review workflows is not activated on the specified model UID.
17
+ * @throws {ValidationError} If the `data` object in the request body fails to pass validation.
18
+ * @returns {Promise<void>} A promise that resolves when the entity's assignee has been updated.
19
+ */
20
+ async updateEntity(ctx) {
21
+ const assigneeService = getService('assignees');
22
+ const workflowService = getService('workflows');
23
+
24
+ const { model_uid: model, id } = ctx.params;
25
+
26
+ const { sanitizeOutput } = strapi
27
+ .plugin('content-manager')
28
+ .service('permission-checker')
29
+ .create({ userAbility: ctx.state.userAbility, model });
30
+
31
+ // TODO: check if user has update permission on the entity
32
+
33
+ const { id: assigneeId } = await validateUpdateAssigneeOnEntity(
34
+ ctx.request?.body?.data,
35
+ 'You should pass a valid id to the body of the put request.'
36
+ );
37
+
38
+ await workflowService.assertContentTypeBelongsToWorkflow(model);
39
+
40
+ const entity = await assigneeService.updateEntityAssignee(id, model, assigneeId);
41
+
42
+ ctx.body = { data: await sanitizeOutput(entity) };
43
+ },
44
+ };
@@ -142,5 +142,22 @@ module.exports = {
142
142
  ],
143
143
  },
144
144
  },
145
+ {
146
+ method: 'PUT',
147
+ path: '/content-manager/(collection|single)-types/:model_uid/:id/assignee',
148
+ handler: 'assignees.updateEntity',
149
+ config: {
150
+ middlewares: [enableFeatureMiddleware('review-workflows')],
151
+ policies: [
152
+ 'admin::isAuthenticatedAdmin',
153
+ {
154
+ name: 'admin::hasPermissions',
155
+ config: {
156
+ actions: ['admin::users.read', 'admin::review-workflows.read'],
157
+ },
158
+ },
159
+ ],
160
+ },
161
+ },
145
162
  ],
146
163
  };
@@ -8,6 +8,7 @@ module.exports = {
8
8
  'seat-enforcement': require('./seat-enforcement'),
9
9
  workflows: require('./review-workflows/workflows'),
10
10
  stages: require('./review-workflows/stages'),
11
+ assignees: require('./review-workflows/assignees'),
11
12
  'review-workflows': require('./review-workflows/review-workflows'),
12
13
  'review-workflows-validation': require('./review-workflows/validation'),
13
14
  'review-workflows-decorator': require('./review-workflows/entity-service-decorator'),
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ const { ApplicationError } = require('@strapi/utils').errors;
4
+ const { isNil } = require('lodash/fp');
5
+ const { ENTITY_ASSIGNEE_ATTRIBUTE } = require('../../constants/workflows');
6
+ const { getService } = require('../../utils');
7
+
8
+ module.exports = ({ strapi }) => {
9
+ const metrics = getService('review-workflows-metrics', { strapi });
10
+
11
+ return {
12
+ async findEntityAssigneeId(id, model) {
13
+ const entity = await strapi.entityService.findOne(model, id, {
14
+ populate: [ENTITY_ASSIGNEE_ATTRIBUTE],
15
+ fields: [],
16
+ });
17
+
18
+ return entity?.[ENTITY_ASSIGNEE_ATTRIBUTE]?.id ?? null;
19
+ },
20
+
21
+ /**
22
+ * Update the assignee of an entity
23
+ */
24
+ async updateEntityAssignee(id, model, assigneeId) {
25
+ if (isNil(assigneeId)) {
26
+ return this.deleteEntityAssignee(id, model);
27
+ }
28
+
29
+ const userExists = await getService('user', { strapi }).exists({ id: assigneeId });
30
+
31
+ if (!userExists) {
32
+ throw new ApplicationError(`Selected user does not exist`);
33
+ }
34
+
35
+ metrics.sendDidEditAssignee(await this.findEntityAssigneeId(id, model), assigneeId);
36
+
37
+ return strapi.entityService.update(model, id, {
38
+ data: { [ENTITY_ASSIGNEE_ATTRIBUTE]: assigneeId },
39
+ populate: [ENTITY_ASSIGNEE_ATTRIBUTE],
40
+ fields: [],
41
+ });
42
+ },
43
+
44
+ async deleteEntityAssignee(id, model) {
45
+ metrics.sendDidEditAssignee(await this.findEntityAssigneeId(id, model), null);
46
+
47
+ return strapi.entityService.update(model, id, {
48
+ data: { [ENTITY_ASSIGNEE_ATTRIBUTE]: null },
49
+ populate: [ENTITY_ASSIGNEE_ATTRIBUTE],
50
+ fields: [],
51
+ });
52
+ },
53
+ };
54
+ };
@@ -24,6 +24,10 @@ const sendDidEditWorkflow = async () => {
24
24
  strapi.telemetry.send('didEditWorkflow', {});
25
25
  };
26
26
 
27
+ const sendDidEditAssignee = async (fromId, toId) => {
28
+ strapi.telemetry.send('didEditAssignee', { from: fromId, to: toId });
29
+ };
30
+
27
31
  const sendDidSendReviewWorkflowPropertiesOnceAWeek = async (
28
32
  numberOfActiveWorkflows,
29
33
  avgStagesCount,
@@ -48,4 +52,5 @@ module.exports = {
48
52
  sendDidCreateWorkflow,
49
53
  sendDidEditWorkflow,
50
54
  sendDidSendReviewWorkflowPropertiesOnceAWeek,
55
+ sendDidEditAssignee,
51
56
  };
@@ -8,6 +8,8 @@ const defaultStages = require('../../constants/default-stages.json');
8
8
  const defaultWorkflow = require('../../constants/default-workflow.json');
9
9
  const {
10
10
  ENTITY_STAGE_ATTRIBUTE,
11
+ ENTITY_ASSIGNEE_ATTRIBUTE,
12
+ STAGE_MODEL_UID,
11
13
  MAX_WORKFLOWS,
12
14
  MAX_STAGES_PER_WORKFLOW,
13
15
  } = require('../../constants/workflows');
@@ -52,19 +54,26 @@ function extendReviewWorkflowContentTypes({ strapi }) {
52
54
  );
53
55
  return contentType;
54
56
  };
55
- const setStageAttribute = set(`attributes.${ENTITY_STAGE_ATTRIBUTE}`, {
56
- writable: true,
57
- private: false,
58
- configurable: false,
59
- visible: false,
60
- useJoinTable: true, // We want a join table to persist data when downgrading to CE
61
- type: 'relation',
62
- relation: 'oneToOne',
63
- target: 'admin::workflow-stage',
64
- });
57
+
58
+ const setRelation = (path, target) =>
59
+ set(path, {
60
+ writable: true,
61
+ private: false,
62
+ configurable: false,
63
+ visible: false,
64
+ useJoinTable: true, // We want a join table to persist data when downgrading to CE
65
+ type: 'relation',
66
+ relation: 'oneToOne',
67
+ target,
68
+ });
69
+
70
+ const setReviewWorkflowAttributes = pipe([
71
+ setRelation(`attributes.${ENTITY_STAGE_ATTRIBUTE}`, STAGE_MODEL_UID),
72
+ setRelation(`attributes.${ENTITY_ASSIGNEE_ATTRIBUTE}`, 'admin::user'),
73
+ ]);
65
74
 
66
75
  const extendContentTypeIfCompatible = cond([
67
- [assertContentTypeCompatibility, setStageAttribute],
76
+ [assertContentTypeCompatibility, setReviewWorkflowAttributes],
68
77
  [stubTrue, incompatibleContentTypeAlert],
69
78
  ]);
70
79
  strapi.container.get('content-types').extend(contentTypeUID, extendContentTypeIfCompatible);
@@ -66,8 +66,16 @@ const validateWorkflowUpdateSchema = yup.object().shape({
66
66
  contentTypes: validateContentTypes,
67
67
  });
68
68
 
69
+ const validateUpdateAssigneeOnEntity = yup
70
+ .object()
71
+ .shape({
72
+ id: yup.number().integer().min(1).nullable(),
73
+ })
74
+ .required();
75
+
69
76
  module.exports = {
70
77
  validateWorkflowCreate: validateYupSchema(validateWorkflowCreateSchema),
71
78
  validateUpdateStageOnEntity: validateYupSchema(validateUpdateStageOnEntity),
79
+ validateUpdateAssigneeOnEntity: validateYupSchema(validateUpdateAssigneeOnEntity),
72
80
  validateWorkflowUpdate: validateYupSchema(validateWorkflowUpdateSchema),
73
81
  };
package/index.js CHANGED
@@ -30,7 +30,6 @@ async function build({ appDir, buildDestDir, env, forceBuild, optimize, options,
30
30
  const entry = path.resolve(cacheDir, 'admin', 'src');
31
31
  const dest = path.resolve(buildDestDir, 'build');
32
32
 
33
- const pluginsPath = Object.keys(plugins).map((pluginName) => plugins[pluginName].pathToPlugin);
34
33
  const enforceSourceMaps = process.env.STRAPI_ENFORCE_SOURCEMAPS === 'true' ?? false;
35
34
 
36
35
  // Either use the tsconfig file from the generated app or the one inside the .cache folder
@@ -41,14 +40,13 @@ async function build({ appDir, buildDestDir, env, forceBuild, optimize, options,
41
40
 
42
41
  const config = getCustomWebpackConfig(appDir, {
43
42
  appDir,
44
- cacheDir,
45
43
  dest,
46
44
  enforceSourceMaps,
47
45
  entry,
48
46
  env,
49
47
  optimize,
50
48
  options,
51
- pluginsPath,
49
+ plugins,
52
50
  tsConfigFilePath,
53
51
  });
54
52
 
@@ -100,8 +98,6 @@ async function watchAdmin({ appDir, browser, buildDestDir, host, options, plugin
100
98
  const dest = path.join(buildDestDir, 'build');
101
99
  const env = 'development';
102
100
 
103
- const pluginsPath = Object.keys(plugins).map((pluginName) => plugins[pluginName].pathToPlugin);
104
-
105
101
  // Either use the tsconfig file from the generated app or the one inside the .cache folder
106
102
  // so we can develop plugins in TS while being in a JS app
107
103
  const tsConfigFilePath = useTypeScript
@@ -115,7 +111,7 @@ async function watchAdmin({ appDir, browser, buildDestDir, host, options, plugin
115
111
  entry,
116
112
  env,
117
113
  options,
118
- pluginsPath,
114
+ plugins,
119
115
  devServer: {
120
116
  port,
121
117
  client: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@strapi/admin",
3
- "version": "4.12.7",
3
+ "version": "4.13.0-alpha.0",
4
4
  "description": "Strapi Admin",
5
5
  "repository": {
6
6
  "type": "git",
@@ -42,15 +42,15 @@
42
42
  "dependencies": {
43
43
  "@casl/ability": "^5.4.3",
44
44
  "@pmmmwh/react-refresh-webpack-plugin": "0.5.10",
45
- "@strapi/data-transfer": "4.12.7",
45
+ "@strapi/data-transfer": "4.13.0-alpha.0",
46
46
  "@strapi/design-system": "1.9.0",
47
- "@strapi/helper-plugin": "4.12.7",
47
+ "@strapi/helper-plugin": "4.13.0-alpha.0",
48
48
  "@strapi/icons": "1.9.0",
49
- "@strapi/permissions": "4.12.7",
50
- "@strapi/provider-audit-logs-local": "4.12.7",
51
- "@strapi/typescript-utils": "4.12.7",
52
- "@strapi/utils": "4.12.7",
53
- "axios": "1.4.0",
49
+ "@strapi/permissions": "4.13.0-alpha.0",
50
+ "@strapi/provider-audit-logs-local": "4.13.0-alpha.0",
51
+ "@strapi/typescript-utils": "4.13.0-alpha.0",
52
+ "@strapi/utils": "4.13.0-alpha.0",
53
+ "axios": "1.5.0",
54
54
  "bcryptjs": "2.4.3",
55
55
  "browserslist": "^4.17.3",
56
56
  "browserslist-to-esbuild": "1.2.0",
@@ -154,5 +154,5 @@
154
154
  }
155
155
  }
156
156
  },
157
- "gitHead": "fefc4a24acc60a59c00049e67a99ec1069be2add"
157
+ "gitHead": "41844c2867621a1f47dd6ae6ac83283aa54b22f8"
158
158
  }
package/scripts/build.js CHANGED
@@ -7,14 +7,8 @@ const { isObject } = require('lodash');
7
7
  const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
8
8
 
9
9
  const webpackConfig = require('../webpack.config');
10
- const getPluginsPath = require('../utils/get-plugins-path');
11
- const {
12
- getCorePluginsPath,
13
- getPluginToInstallPath,
14
- createPluginsFile,
15
- } = require('./create-plugins-file');
16
-
17
- const PLUGINS_TO_INSTALL = ['i18n', 'users-permissions'];
10
+ const { getPlugins } = require('../utils/get-plugins');
11
+ const { createPluginsJs } = require('../utils/create-cache-dir');
18
12
 
19
13
  // Wrapper that outputs the webpack speed
20
14
  const smp = new SpeedMeasurePlugin();
@@ -24,18 +18,24 @@ const buildAdmin = async () => {
24
18
  const dest = path.join(__dirname, '..', 'build');
25
19
  const tsConfigFilePath = path.join(__dirname, '..', 'admin', 'src', 'tsconfig.json');
26
20
 
27
- const corePlugins = getCorePluginsPath();
28
- const plugins = getPluginToInstallPath(PLUGINS_TO_INSTALL);
29
- const allPlugins = { ...corePlugins, ...plugins };
30
- const pluginsPath = getPluginsPath();
21
+ /**
22
+ * We _always_ install these FE plugins, they're considered "core"
23
+ * and are typically marked as `required` in their package.json
24
+ */
25
+ const plugins = getPlugins([
26
+ '@strapi/plugin-content-type-builder',
27
+ '@strapi/plugin-email',
28
+ '@strapi/plugin-upload',
29
+ '@strapi/plugin-i18n',
30
+ '@strapi/plugin-users-permissions',
31
+ ]);
31
32
 
32
- await createPluginsFile(allPlugins);
33
+ await createPluginsJs(plugins, path.join(__dirname, '..'));
33
34
 
34
35
  const args = {
35
36
  entry,
36
37
  dest,
37
- cacheDir: path.join(__dirname, '..'),
38
- pluginsPath,
38
+ plugins,
39
39
  env: 'production',
40
40
  optimize: true,
41
41
  options: {
@@ -1,40 +1,9 @@
1
1
  'use strict';
2
2
 
3
- const { join, resolve, relative } = require('path');
4
- const { promisify } = require('util');
5
-
6
- // eslint-disable-next-line import/no-extraneous-dependencies
7
- const glob = promisify(require('glob').glob);
3
+ const { join } = require('path');
8
4
  const fs = require('fs-extra');
9
- const { getCorePluginsPath, createPluginsFile } = require('./create-plugins-file');
10
-
11
- /**
12
- * Retrieve all plugins that are inside the plugins folder
13
- * @returns Object the plugins
14
- */
15
- const getPluginsPackages = async () => {
16
- const pathToPackages = resolve(__dirname, '..', '..', '..', 'plugins', '*');
17
- const pluginsPackageDirs = await glob(pathToPackages);
18
-
19
- return pluginsPackageDirs
20
- .filter((pluginDir) => {
21
- return fs.existsSync(join(pluginDir, 'admin', 'src', 'index.js'));
22
- })
23
- .reduce((acc, current) => {
24
- const depName = current.replace(/\\/g, '/').split('/').slice(-1)[0];
25
-
26
- const adminEntryPoint = join(__dirname, '..', 'admin', 'src');
27
-
28
- const pathToPlugin = join(relative(adminEntryPoint, current), 'admin', 'src').replace(
29
- /\\/g,
30
- '/'
31
- );
32
-
33
- acc[depName] = pathToPlugin;
34
-
35
- return acc;
36
- }, {});
37
- };
5
+ const { getPlugins } = require('../utils/get-plugins');
6
+ const { createPluginsJs } = require('../utils/create-cache-dir');
38
7
 
39
8
  /**
40
9
  * Write the plugins.js file or copy the plugins-dev.js file if it exists
@@ -49,11 +18,9 @@ const createFile = async () => {
49
18
  return;
50
19
  }
51
20
 
52
- const corePlugins = getCorePluginsPath();
53
- const plugins = await getPluginsPackages();
54
- const allPlugins = { ...corePlugins, ...plugins };
21
+ const plugins = getPlugins();
55
22
 
56
- return createPluginsFile(allPlugins);
23
+ return createPluginsJs(plugins, join(__dirname, '..'));
57
24
  };
58
25
 
59
26
  createFile()
@@ -55,6 +55,8 @@ module.exports = {
55
55
  ability: ctx.state.userAbility,
56
56
  model: 'admin::role',
57
57
  });
58
+
59
+ await permissionsManager.validateQuery(query);
58
60
  const sanitizedQuery = await permissionsManager.sanitizeQuery(query);
59
61
 
60
62
  const roles = await getService('role').findAllWithUsersCount(sanitizedQuery);
@@ -52,6 +52,8 @@ module.exports = {
52
52
  ability: ctx.state.userAbility,
53
53
  model: 'admin::user',
54
54
  });
55
+
56
+ await permissionsManager.validateQuery(ctx.query);
55
57
  const sanitizedQuery = await permissionsManager.sanitizeQuery(ctx.query);
56
58
 
57
59
  const { results, pagination } = await userService.findPage(sanitizedQuery);
@@ -4,8 +4,9 @@ const _ = require('lodash');
4
4
  const { cloneDeep, isPlainObject } = require('lodash/fp');
5
5
  const { subject: asSubject } = require('@casl/ability');
6
6
  const createSanitizeHelpers = require('./sanitize');
7
+ const createValidateHelpers = require('./validate');
7
8
 
8
- const { buildStrapiQuery, buildCaslQuery } = require('./query-builers');
9
+ const { buildStrapiQuery, buildCaslQuery } = require('./query-builders');
9
10
 
10
11
  module.exports = ({ ability, action, model }) => ({
11
12
  ability,
@@ -48,4 +49,5 @@ module.exports = ({ ability, action, model }) => ({
48
49
  },
49
50
 
50
51
  ...createSanitizeHelpers({ action, ability, model }),
52
+ ...createValidateHelpers({ action, ability, model }),
51
53
  });
@@ -45,7 +45,7 @@ const STATIC_FIELDS = [ID_ATTRIBUTE];
45
45
  module.exports = ({ action, ability, model }) => {
46
46
  const schema = strapi.getModel(model);
47
47
 
48
- const { allowedFields } = sanitize.visitors;
48
+ const { removeDisallowedFields } = sanitize.visitors;
49
49
 
50
50
  const createSanitizeQuery = (options = {}) => {
51
51
  const { fields } = options;
@@ -54,8 +54,9 @@ module.exports = ({ action, ability, model }) => {
54
54
  const permittedFields = fields.shouldIncludeAll ? null : getQueryFields(fields.permitted);
55
55
 
56
56
  const sanitizeFilters = pipeAsync(
57
- traverse.traverseQueryFilters(allowedFields(permittedFields), { schema }),
57
+ traverse.traverseQueryFilters(removeDisallowedFields(permittedFields), { schema }),
58
58
  traverse.traverseQueryFilters(omitDisallowedAdminUserFields, { schema }),
59
+ traverse.traverseQueryFilters(omitHiddenFields, { schema }),
59
60
  traverse.traverseQueryFilters(removePassword, { schema }),
60
61
  traverse.traverseQueryFilters(
61
62
  ({ key, value }, { remove }) => {
@@ -68,8 +69,9 @@ module.exports = ({ action, ability, model }) => {
68
69
  );
69
70
 
70
71
  const sanitizeSort = pipeAsync(
71
- traverse.traverseQuerySort(allowedFields(permittedFields), { schema }),
72
+ traverse.traverseQuerySort(removeDisallowedFields(permittedFields), { schema }),
72
73
  traverse.traverseQuerySort(omitDisallowedAdminUserFields, { schema }),
74
+ traverse.traverseQuerySort(omitHiddenFields, { schema }),
73
75
  traverse.traverseQuerySort(removePassword, { schema }),
74
76
  traverse.traverseQuerySort(
75
77
  ({ key, attribute, value }, { remove }) => {
@@ -82,13 +84,15 @@ module.exports = ({ action, ability, model }) => {
82
84
  );
83
85
 
84
86
  const sanitizePopulate = pipeAsync(
85
- traverse.traverseQueryPopulate(allowedFields(permittedFields), { schema }),
87
+ traverse.traverseQueryPopulate(removeDisallowedFields(permittedFields), { schema }),
86
88
  traverse.traverseQueryPopulate(omitDisallowedAdminUserFields, { schema }),
89
+ traverse.traverseQueryPopulate(omitHiddenFields, { schema }),
87
90
  traverse.traverseQueryPopulate(removePassword, { schema })
88
91
  );
89
92
 
90
93
  const sanitizeFields = pipeAsync(
91
- traverse.traverseQueryFields(allowedFields(permittedFields), { schema }),
94
+ traverse.traverseQueryFields(removeDisallowedFields(permittedFields), { schema }),
95
+ traverse.traverseQueryFields(omitHiddenFields, { schema }),
92
96
  traverse.traverseQueryFields(removePassword, { schema })
93
97
  );
94
98
 
@@ -126,7 +130,7 @@ module.exports = ({ action, ability, model }) => {
126
130
  // Remove unallowed fields from admin::user relations
127
131
  traverseEntity(pickAllowedAdminUserFields, { schema }),
128
132
  // Remove not allowed fields (RBAC)
129
- traverseEntity(allowedFields(permittedFields), { schema }),
133
+ traverseEntity(removeDisallowedFields(permittedFields), { schema }),
130
134
  // Remove all fields of type 'password'
131
135
  sanitize.sanitizers.sanitizePasswords(schema)
132
136
  );
@@ -141,7 +145,7 @@ module.exports = ({ action, ability, model }) => {
141
145
  // Remove fields hidden from the admin
142
146
  traverseEntity(omitHiddenFields, { schema }),
143
147
  // Remove not allowed fields (RBAC)
144
- traverseEntity(allowedFields(permittedFields), { schema }),
148
+ traverseEntity(removeDisallowedFields(permittedFields), { schema }),
145
149
  // Remove roles from createdBy & updateBy fields
146
150
  omitCreatorRoles
147
151
  );
@@ -256,13 +260,21 @@ module.exports = ({ action, ability, model }) => {
256
260
  };
257
261
 
258
262
  const getQueryFields = (fields = []) => {
263
+ const nonVisibleAttributes = getNonVisibleAttributes(schema);
264
+ const writableAttributes = getWritableAttributes(schema);
265
+
266
+ const nonVisibleWritableAttributes = intersection(nonVisibleAttributes, writableAttributes);
267
+
259
268
  return uniq([
260
269
  ...fields,
261
270
  ...STATIC_FIELDS,
262
271
  ...COMPONENT_FIELDS,
272
+ ...nonVisibleWritableAttributes,
263
273
  CREATED_AT_ATTRIBUTE,
264
274
  UPDATED_AT_ATTRIBUTE,
265
275
  PUBLISHED_AT_ATTRIBUTE,
276
+ CREATED_BY_ATTRIBUTE,
277
+ UPDATED_BY_ATTRIBUTE,
266
278
  ]);
267
279
  };
268
280