@strapi/admin 4.9.2 → 4.10.0-beta.1

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 (77) hide show
  1. package/admin/src/content-manager/components/DynamicTable/CellContent/PublicationState/PublicationState.js +26 -0
  2. package/admin/src/content-manager/components/DynamicTable/CellContent/PublicationState/index.js +1 -0
  3. package/admin/src/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +2 -0
  4. package/admin/src/content-manager/components/DynamicTable/index.js +25 -49
  5. package/admin/src/content-manager/components/DynamicZone/components/DynamicComponent.js +2 -0
  6. package/admin/src/content-manager/pages/EditView/Information/index.js +77 -53
  7. package/admin/src/content-manager/pages/EditView/InformationBox/InformationBoxCE.js +13 -0
  8. package/admin/src/content-manager/pages/EditView/InformationBox/index.js +3 -0
  9. package/admin/src/content-manager/pages/EditView/index.js +3 -4
  10. package/admin/src/content-manager/pages/ListView/index.js +6 -9
  11. package/admin/src/content-manager/sharedReducers/crudReducer/actions.js +6 -0
  12. package/admin/src/content-manager/sharedReducers/crudReducer/constants.js +1 -0
  13. package/admin/src/content-manager/sharedReducers/crudReducer/reducer.js +5 -0
  14. package/admin/src/index.js +1 -0
  15. package/admin/src/translations/en.json +6 -0
  16. package/build/{Admin-authenticatedApp.217db666.chunk.js → Admin-authenticatedApp.52c88751.chunk.js} +2 -2
  17. package/build/{Admin_settingsPage.1dbfc9ce.chunk.js → Admin_settingsPage.257b3477.chunk.js} +7 -7
  18. package/build/{admin-app.558af642.chunk.js → admin-app.dfaeea5d.chunk.js} +18 -18
  19. package/build/content-manager.def692c2.chunk.js +1130 -0
  20. package/build/content-type-builder-translation-en-json.510e88ca.chunk.js +1 -0
  21. package/build/content-type-builder.5e1f4afc.chunk.js +126 -0
  22. package/build/en-json.08303b37.chunk.js +1 -0
  23. package/build/index.html +1 -1
  24. package/build/{main.ef8db4a2.js → main.120be100.js} +145 -145
  25. package/build/review-workflows-settings.9092ed72.chunk.js +106 -0
  26. package/build/{runtime~main.3a92d953.js → runtime~main.112b3101.js} +1 -1
  27. package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/ReviewWorkflowsStageEE.js +15 -0
  28. package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/getTableColumn.js +45 -0
  29. package/ee/admin/content-manager/components/DynamicTable/CellContent/ReviewWorkflowsStage/index.js +3 -0
  30. package/ee/admin/content-manager/pages/EditView/InformationBox/InformationBoxEE.js +135 -0
  31. package/ee/admin/content-manager/pages/EditView/InformationBox/index.js +3 -0
  32. package/ee/admin/hooks/useSettingsMenu/utils/customAdminLinks.js +12 -12
  33. package/ee/admin/hooks/useSettingsMenu/utils/customGlobalLinks.js +21 -13
  34. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/ReviewWorkflows.js +199 -0
  35. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/actions/index.js +42 -0
  36. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/AddStage/AddStage.js +87 -0
  37. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/AddStage/index.js +1 -0
  38. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +90 -0
  39. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/index.js +1 -0
  40. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stages.js +92 -0
  41. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/index.js +1 -0
  42. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/constants.js +6 -0
  43. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/hooks/useReviewWorkflows.js +35 -0
  44. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/index.js +3 -0
  45. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/reducer/index.js +122 -0
  46. package/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/utils/getWorkflowValidationSchema.js +25 -0
  47. package/ee/admin/pages/SettingsPage/utils/customRoutes.js +16 -2
  48. package/ee/admin/permissions/customPermissions.js +3 -0
  49. package/ee/server/bootstrap.js +13 -0
  50. package/ee/server/config/admin-actions.js +10 -0
  51. package/ee/server/constants/default-stages.json +14 -0
  52. package/ee/server/constants/default-workflow.json +1 -0
  53. package/ee/server/constants/workflows.js +8 -0
  54. package/ee/server/content-types/index.js +9 -0
  55. package/ee/server/content-types/workflow/index.js +31 -0
  56. package/ee/server/content-types/workflow-stage/index.js +36 -0
  57. package/ee/server/controllers/index.js +2 -0
  58. package/ee/server/controllers/workflows/index.js +36 -0
  59. package/ee/server/controllers/workflows/stages/index.js +102 -0
  60. package/ee/server/index.js +1 -0
  61. package/ee/server/middlewares/review-workflows.js +40 -0
  62. package/ee/server/register.js +8 -0
  63. package/ee/server/routes/index.js +104 -0
  64. package/ee/server/services/index.js +4 -0
  65. package/ee/server/services/review-workflows/entity-service-decorator.js +54 -0
  66. package/ee/server/services/review-workflows/review-workflows.js +111 -0
  67. package/ee/server/services/review-workflows/stages.js +249 -0
  68. package/ee/server/services/review-workflows/workflows.js +25 -0
  69. package/ee/server/utils/index.js +8 -0
  70. package/ee/server/utils/persisted-tables.js +114 -22
  71. package/ee/server/utils/review-workflows.js +34 -0
  72. package/ee/server/validation/review-workflows.js +24 -0
  73. package/package.json +9 -9
  74. package/build/content-manager.d1565bfc.chunk.js +0 -1132
  75. package/build/content-type-builder-translation-en-json.6c8e69ab.chunk.js +0 -1
  76. package/build/content-type-builder.9d780e7f.chunk.js +0 -126
  77. package/build/en-json.cf600231.chunk.js +0 -1
@@ -0,0 +1,25 @@
1
+ import * as yup from 'yup';
2
+
3
+ export function getWorkflowValidationSchema({ formatMessage }) {
4
+ return yup.object({
5
+ stages: yup.array().of(
6
+ yup.object().shape({
7
+ name: yup
8
+ .string()
9
+ .required(
10
+ formatMessage({
11
+ id: 'Settings.review-workflows.validation.stage.name',
12
+ defaultMessage: 'Name is required',
13
+ })
14
+ )
15
+ .max(
16
+ 255,
17
+ formatMessage({
18
+ id: 'Settings.review-workflows.validation.stage.max-length',
19
+ defaultMessage: 'Name can not be longer than 255 characters',
20
+ })
21
+ ),
22
+ })
23
+ ),
24
+ });
25
+ }
@@ -1,6 +1,6 @@
1
1
  const routes = [];
2
2
 
3
- if (strapi.features.isEnabled(strapi.features.SSO)) {
3
+ if (window.strapi.features.isEnabled(window.strapi.features.SSO)) {
4
4
  routes.push({
5
5
  async Component() {
6
6
  const component = await import(
@@ -14,7 +14,21 @@ if (strapi.features.isEnabled(strapi.features.SSO)) {
14
14
  });
15
15
  }
16
16
 
17
- if (strapi.features.isEnabled(strapi.features.AUDIT_LOGS)) {
17
+ if (window.strapi.features.isEnabled(window.strapi.features.REVIEW_WORKFLOWS)) {
18
+ routes.push({
19
+ async Component() {
20
+ const component = await import(
21
+ /* webpackChunkName: "review-workflows-settings" */ '../pages/ReviewWorkflows'
22
+ );
23
+
24
+ return component;
25
+ },
26
+ to: '/settings/review-workflows',
27
+ exact: true,
28
+ });
29
+ }
30
+
31
+ if (window.strapi.features.isEnabled(window.strapi.features.AUDIT_LOGS)) {
18
32
  routes.push({
19
33
  async Component() {
20
34
  const component = await import(
@@ -4,6 +4,9 @@ const customPermissions = {
4
4
  main: [{ action: 'admin::audit-logs.read', subject: null }],
5
5
  read: [{ action: 'admin::audit-logs.read', subject: null }],
6
6
  },
7
+ 'review-workflows': {
8
+ main: [{ action: 'admin::review-workflows.read', subject: null }],
9
+ },
7
10
  sso: {
8
11
  main: [{ action: 'admin::provider-login.read', subject: null }],
9
12
  read: [{ action: 'admin::provider-login.read', subject: null }],
@@ -20,6 +20,19 @@ module.exports = async () => {
20
20
  await actionProvider.registerMany(actions.auditLogs);
21
21
  }
22
22
 
23
+ if (features.isEnabled('review-workflows')) {
24
+ await persistTablesWithPrefix('strapi_workflows');
25
+
26
+ const { bootstrap: rwBootstrap } = getService('review-workflows');
27
+
28
+ await rwBootstrap();
29
+ await actionProvider.registerMany(actions.reviewWorkflows);
30
+
31
+ // Decorate the entity service with review workflow logic
32
+ const { decorator } = getService('review-workflows-decorator');
33
+ strapi.entityService.decorate(decorator);
34
+ }
35
+
23
36
  await getService('seat-enforcement').seatEnforcementWorkflow();
24
37
 
25
38
  await executeCEBootstrap();
@@ -29,4 +29,14 @@ module.exports = {
29
29
  subCategory: 'options',
30
30
  },
31
31
  ],
32
+ reviewWorkflows: [
33
+ {
34
+ uid: 'review-workflows.read',
35
+ displayName: 'Read',
36
+ pluginName: 'admin',
37
+ section: 'settings',
38
+ category: 'review workflows',
39
+ subCategory: 'options',
40
+ },
41
+ ],
32
42
  };
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "name": "To do"
4
+ },
5
+ {
6
+ "name": "Ready to review"
7
+ },
8
+ {
9
+ "name": "In progress"
10
+ },
11
+ {
12
+ "name": "Reviewed"
13
+ }
14
+ ]
@@ -0,0 +1,8 @@
1
+ 'use strict';
2
+
3
+ // TODO concatenate admin + content type singular name
4
+ module.exports = {
5
+ WORKFLOW_MODEL_UID: 'admin::workflow',
6
+ STAGE_MODEL_UID: 'admin::workflow-stage',
7
+ ENTITY_STAGE_ATTRIBUTE: 'strapi_reviewWorkflows_stage',
8
+ };
@@ -0,0 +1,9 @@
1
+ 'use strict';
2
+
3
+ const workflow = require('./workflow');
4
+ const workflowStage = require('./workflow-stage');
5
+
6
+ module.exports = {
7
+ workflow,
8
+ 'workflow-stage': workflowStage,
9
+ };
@@ -0,0 +1,31 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ schema: {
5
+ collectionName: 'strapi_workflows',
6
+ info: {
7
+ name: 'Workflow',
8
+ description: '',
9
+ singularName: 'workflow',
10
+ pluralName: 'workflows',
11
+ displayName: 'Workflow',
12
+ },
13
+ options: {},
14
+ pluginOptions: {
15
+ 'content-manager': {
16
+ visible: false,
17
+ },
18
+ 'content-type-builder': {
19
+ visible: false,
20
+ },
21
+ },
22
+ attributes: {
23
+ stages: {
24
+ type: 'relation',
25
+ target: 'admin::workflow-stage',
26
+ relation: 'oneToMany',
27
+ mappedBy: 'workflow',
28
+ },
29
+ },
30
+ },
31
+ };
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ module.exports = {
4
+ schema: {
5
+ collectionName: 'strapi_workflows_stages',
6
+ info: {
7
+ name: 'Workflow Stage',
8
+ description: '',
9
+ singularName: 'workflow-stage',
10
+ pluralName: 'workflow-stages',
11
+ displayName: 'Stages',
12
+ },
13
+ options: {},
14
+ pluginOptions: {
15
+ 'content-manager': {
16
+ visible: false,
17
+ },
18
+ 'content-type-builder': {
19
+ visible: false,
20
+ },
21
+ },
22
+ attributes: {
23
+ name: {
24
+ type: 'string',
25
+ configurable: false,
26
+ },
27
+ workflow: {
28
+ type: 'relation',
29
+ target: 'admin::workflow',
30
+ relation: 'manyToOne',
31
+ inversedBy: 'stages',
32
+ configurable: false,
33
+ },
34
+ },
35
+ },
36
+ };
@@ -6,4 +6,6 @@ module.exports = {
6
6
  user: require('./user'),
7
7
  auditLogs: require('./audit-logs'),
8
8
  admin: require('./admin'),
9
+ workflows: require('./workflows'),
10
+ stages: require('./workflows/stages'),
9
11
  };
@@ -0,0 +1,36 @@
1
+ 'use strict';
2
+
3
+ const { getService } = require('../../utils');
4
+
5
+ module.exports = {
6
+ /**
7
+ * List all workflows
8
+ * @param {import('koa').BaseContext} ctx - koa context
9
+ */
10
+ async find(ctx) {
11
+ const { populate } = ctx.query;
12
+ const workflowService = getService('workflows');
13
+ const data = await workflowService.find({
14
+ populate,
15
+ });
16
+
17
+ ctx.body = {
18
+ data,
19
+ };
20
+ },
21
+ /**
22
+ * Get one workflow based on its id contained in request parameters
23
+ * @param {import('koa').BaseContext} ctx - koa context
24
+ */
25
+ async findById(ctx) {
26
+ const { id } = ctx.params;
27
+ const { populate } = ctx.query;
28
+
29
+ const workflowService = getService('workflows');
30
+ const data = await workflowService.findById(id, { populate });
31
+
32
+ ctx.body = {
33
+ data,
34
+ };
35
+ },
36
+ };
@@ -0,0 +1,102 @@
1
+ 'use strict';
2
+
3
+ const { ApplicationError } = require('@strapi/utils/lib/errors');
4
+ const { getService } = require('../../../utils');
5
+ const { hasReviewWorkflow } = require('../../../utils/review-workflows');
6
+ const {
7
+ validateUpdateStages,
8
+ validateUpdateStageOnEntity,
9
+ } = require('../../../validation/review-workflows');
10
+
11
+ module.exports = {
12
+ /**
13
+ * List all stages
14
+ * @param {import('koa').BaseContext} ctx - koa context
15
+ */
16
+ async find(ctx) {
17
+ const { workflow_id: workflowId } = ctx.params;
18
+ const { populate } = ctx.query;
19
+ const stagesService = getService('stages');
20
+
21
+ const data = await stagesService.find({
22
+ workflowId,
23
+ populate,
24
+ });
25
+
26
+ ctx.body = {
27
+ data,
28
+ };
29
+ },
30
+ /**
31
+ * Get one stage
32
+ * @param {import('koa').BaseContext} ctx - koa context
33
+ */
34
+ async findById(ctx) {
35
+ const { id, workflow_id: workflowId } = ctx.params;
36
+ const { populate } = ctx.query;
37
+ const stagesService = getService('stages');
38
+
39
+ const data = await stagesService.findById(id, {
40
+ workflowId,
41
+ populate,
42
+ });
43
+
44
+ ctx.body = {
45
+ data,
46
+ };
47
+ },
48
+
49
+ /**
50
+ * Replace all stages in a workflow
51
+ * @param {import('koa').BaseContext} ctx - koa context
52
+ *
53
+ */
54
+ async replace(ctx) {
55
+ const { workflow_id: workflowId } = ctx.params;
56
+ const stagesService = getService('stages');
57
+ const {
58
+ body: { data: stages },
59
+ } = ctx.request;
60
+
61
+ const stagesValidated = await validateUpdateStages(stages);
62
+
63
+ const data = await stagesService.replaceWorkflowStages(workflowId, stagesValidated);
64
+
65
+ ctx.body = { data };
66
+ },
67
+
68
+ /**
69
+ * Updates an entity's stage.
70
+ * @async
71
+ * @param {Object} ctx - The Koa context object.
72
+ * @param {Object} ctx.params - An object containing the parameters from the request URL.
73
+ * @param {string} ctx.params.model_uid - The model UID of the entity.
74
+ * @param {string} ctx.params.id - The ID of the entity to update.
75
+ * @param {Object} ctx.request.body.data - Optional data object containing the new stage ID for the entity.
76
+ * @param {string} ctx.request.body.data.id - The ID of the new stage for the entity.
77
+ * @throws {ApplicationError} If review workflows is not activated on the specified model UID.
78
+ * @throws {ValidationError} If the `data` object in the request body fails to pass validation.
79
+ * @returns {Promise<void>} A promise that resolves when the entity's stage has been updated.
80
+ */
81
+ async updateEntity(ctx) {
82
+ const stagesService = getService('stages');
83
+ const { model_uid: modelUID, id: entityIdString } = ctx.params;
84
+ const entityId = Number(entityIdString);
85
+
86
+ const { id: stageId } = await validateUpdateStageOnEntity(
87
+ ctx.request?.body?.data,
88
+ 'You should pass an id to the body of the put request.'
89
+ );
90
+
91
+ if (!hasReviewWorkflow({ strapi }, modelUID)) {
92
+ throw new ApplicationError(`Review workflows is not activated on ${modelUID}.`);
93
+ }
94
+
95
+ // TODO When multiple workflows are possible, check if the stage is part of the right one
96
+ // Didn't need this today as their can only be one workflow
97
+
98
+ const data = await stagesService.updateEntity({ id: entityId, modelUID }, stageId);
99
+
100
+ ctx.body = { data };
101
+ },
102
+ };
@@ -2,6 +2,7 @@
2
2
 
3
3
  module.exports = {
4
4
  register: require('./register'),
5
+ contentTypes: require('./content-types'),
5
6
  bootstrap: require('./bootstrap'),
6
7
  destroy: require('./destroy'),
7
8
  routes: require('./routes'),
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+
3
+ const { set } = require('lodash/fp');
4
+
5
+ module.exports = {
6
+ contentTypeMiddleware,
7
+ };
8
+
9
+ /**
10
+ * A Strapi middleware function that adds support for review workflows.
11
+ *
12
+ * Why is it needed ?
13
+ * For now, the admin panel cannot have anything but top-level attributes in the content-type for options.
14
+ * But we need the CE part to be agnostics from Review Workflow (which is an EE feature).
15
+ * CE handle the `options` object, that's why we move the reviewWorkflows boolean to the options object.
16
+ *
17
+ * @param {object} strapi - The Strapi instance.
18
+ */
19
+ function contentTypeMiddleware(strapi) {
20
+ /**
21
+ * A middleware function that moves the `reviewWorkflows` attribute from the top level of
22
+ * the request body to the `options` object within the request body.
23
+ *
24
+ * @param {object} ctx - The Koa context object.
25
+ */
26
+ const moveReviewWorkflowOption = (ctx) => {
27
+ // Move reviewWorkflows to options.reviewWorkflows
28
+ const { reviewWorkflows, ...contentType } = ctx.request.body.contentType;
29
+
30
+ if (typeof reviewWorkflows === 'boolean') {
31
+ ctx.request.body.contentType = set('options.reviewWorkflows', reviewWorkflows, contentType);
32
+ }
33
+ };
34
+ strapi.server.router.use('/content-type-builder/content-types/:uid?', (ctx, next) => {
35
+ if (ctx.method === 'PUT' || ctx.method === 'POST') {
36
+ moveReviewWorkflowOption(ctx);
37
+ }
38
+ return next();
39
+ });
40
+ }
@@ -1,8 +1,11 @@
1
1
  'use strict';
2
2
 
3
+ const { features } = require('@strapi/strapi/lib/utils/ee');
3
4
  const executeCERegister = require('../../server/register');
4
5
  const migrateAuditLogsTable = require('./migrations/audit-logs-table');
5
6
  const createAuditLogsService = require('./services/audit-logs');
7
+ const reviewWorkflowsMiddlewares = require('./middlewares/review-workflows');
8
+ const { getService } = require('./utils');
6
9
 
7
10
  module.exports = async ({ strapi }) => {
8
11
  const auditLogsIsEnabled = strapi.config.get('admin.auditLogs.enabled', true);
@@ -13,6 +16,11 @@ module.exports = async ({ strapi }) => {
13
16
  strapi.container.register('audit-logs', auditLogsService);
14
17
  await auditLogsService.register();
15
18
  }
19
+ if (features.isEnabled('review-workflows')) {
20
+ const reviewWorkflowService = getService('review-workflows');
16
21
 
22
+ reviewWorkflowsMiddlewares.contentTypeMiddleware(strapi);
23
+ await reviewWorkflowService.register();
24
+ }
17
25
  await executeCERegister({ strapi });
18
26
  };
@@ -122,4 +122,108 @@ module.exports = [
122
122
  ],
123
123
  },
124
124
  },
125
+
126
+ // Review workflow
127
+ {
128
+ method: 'GET',
129
+ path: '/review-workflows/workflows',
130
+ handler: 'workflows.find',
131
+ config: {
132
+ middlewares: [enableFeatureMiddleware('review-workflows')],
133
+ policies: [
134
+ 'admin::isAuthenticatedAdmin',
135
+ {
136
+ name: 'admin::hasPermissions',
137
+ config: {
138
+ actions: ['admin::review-workflows.read'],
139
+ },
140
+ },
141
+ ],
142
+ },
143
+ },
144
+ {
145
+ method: 'GET',
146
+ path: '/review-workflows/workflows/:id',
147
+ handler: 'workflows.findById',
148
+ config: {
149
+ middlewares: [enableFeatureMiddleware('review-workflows')],
150
+ policies: [
151
+ 'admin::isAuthenticatedAdmin',
152
+ {
153
+ name: 'admin::hasPermissions',
154
+ config: {
155
+ actions: ['admin::review-workflows.read'],
156
+ },
157
+ },
158
+ ],
159
+ },
160
+ },
161
+ {
162
+ method: 'GET',
163
+ path: '/review-workflows/workflows/:workflow_id/stages',
164
+ handler: 'stages.find',
165
+ config: {
166
+ middlewares: [enableFeatureMiddleware('review-workflows')],
167
+ policies: [
168
+ 'admin::isAuthenticatedAdmin',
169
+ {
170
+ name: 'admin::hasPermissions',
171
+ config: {
172
+ actions: ['admin::review-workflows.read'],
173
+ },
174
+ },
175
+ ],
176
+ },
177
+ },
178
+ {
179
+ method: 'PUT',
180
+ path: '/review-workflows/workflows/:workflow_id/stages',
181
+ handler: 'stages.replace',
182
+ config: {
183
+ middlewares: [enableFeatureMiddleware('review-workflows')],
184
+ policies: [
185
+ 'admin::isAuthenticatedAdmin',
186
+ {
187
+ name: 'admin::hasPermissions',
188
+ config: {
189
+ actions: ['admin::review-workflows.read'],
190
+ },
191
+ },
192
+ ],
193
+ },
194
+ },
195
+ {
196
+ method: 'GET',
197
+ path: '/review-workflows/workflows/:workflow_id/stages/:id',
198
+ handler: 'stages.findById',
199
+ config: {
200
+ middlewares: [enableFeatureMiddleware('review-workflows')],
201
+ policies: [
202
+ 'admin::isAuthenticatedAdmin',
203
+ {
204
+ name: 'admin::hasPermissions',
205
+ config: {
206
+ actions: ['admin::review-workflows.read'],
207
+ },
208
+ },
209
+ ],
210
+ },
211
+ },
212
+ {
213
+ method: 'PUT',
214
+ path: '/content-manager/(collection|single)-types/:model_uid/:id/stage',
215
+ handler: 'stages.updateEntity',
216
+ config: {
217
+ middlewares: [enableFeatureMiddleware('review-workflows')],
218
+ policies: [
219
+ 'admin::isAuthenticatedAdmin',
220
+ {
221
+ name: 'admin::hasPermissions',
222
+ config: {
223
+ actions: ['admin::review-workflows.read'],
224
+ },
225
+ },
226
+ ],
227
+ },
228
+ },
125
229
  ];
@@ -5,4 +5,8 @@ module.exports = {
5
5
  role: require('./role'),
6
6
  user: require('./user'),
7
7
  'seat-enforcement': require('./seat-enforcement'),
8
+ workflows: require('./review-workflows/workflows'),
9
+ stages: require('./review-workflows/stages'),
10
+ 'review-workflows': require('./review-workflows/review-workflows'),
11
+ 'review-workflows-decorator': require('./review-workflows/entity-service-decorator'),
8
12
  };
@@ -0,0 +1,54 @@
1
+ 'use strict';
2
+
3
+ const { isNil, isNull } = require('lodash/fp');
4
+ const { ENTITY_STAGE_ATTRIBUTE } = require('../../constants/workflows');
5
+ const { hasReviewWorkflow, getDefaultWorkflow } = require('../../utils/review-workflows');
6
+
7
+ /**
8
+ * Assigns the entity data to the default workflow stage if no stage is present in the data
9
+ * @param {Object} data
10
+ * @returns
11
+ */
12
+ const getDataWithStage = async (data) => {
13
+ if (!isNil(ENTITY_STAGE_ATTRIBUTE, data)) {
14
+ const defaultWorkflow = await getDefaultWorkflow({ strapi });
15
+ return { ...data, [ENTITY_STAGE_ATTRIBUTE]: defaultWorkflow.stages[0].id };
16
+ }
17
+ return data;
18
+ };
19
+
20
+ /**
21
+ * Decorates the entity service with RW business logic
22
+ * @param {object} service - entity service
23
+ */
24
+ const decorator = (service) => ({
25
+ async create(uid, opts = {}) {
26
+ const hasRW = hasReviewWorkflow({ strapi }, uid);
27
+
28
+ if (!hasRW) {
29
+ return service.create.call(this, uid, opts);
30
+ }
31
+
32
+ const data = await getDataWithStage(opts.data);
33
+ return service.create.call(this, uid, { ...opts, data });
34
+ },
35
+ async update(uid, entityId, opts = {}) {
36
+ const hasRW = hasReviewWorkflow({ strapi }, uid);
37
+
38
+ if (!hasRW) {
39
+ return service.update.call(this, uid, entityId, opts);
40
+ }
41
+
42
+ // Prevents the stage from being set to null
43
+ const data = { ...opts.data };
44
+ if (isNull(data[ENTITY_STAGE_ATTRIBUTE])) {
45
+ delete data[ENTITY_STAGE_ATTRIBUTE];
46
+ }
47
+
48
+ return service.update.call(this, uid, entityId, { ...opts, data });
49
+ },
50
+ });
51
+
52
+ module.exports = () => ({
53
+ decorator,
54
+ });