@strapi/admin 5.0.0-beta.3 → 5.0.0-beta.4
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.
- package/dist/admin/{AuthenticatedLayout-1x0WC6c6.js → AuthenticatedLayout-Aibt647F.js} +11 -11
- package/dist/admin/{AuthenticatedLayout-1x0WC6c6.js.map → AuthenticatedLayout-Aibt647F.js.map} +1 -1
- package/dist/admin/{AuthenticatedLayout-zaOd05gw.mjs → AuthenticatedLayout-UPRVctuH.mjs} +11 -11
- package/dist/admin/{AuthenticatedLayout-zaOd05gw.mjs.map → AuthenticatedLayout-UPRVctuH.mjs.map} +1 -1
- package/dist/admin/{CreatePage-J84OUqE4.js → CreatePage-EngKiM_9.js} +3 -3
- package/dist/admin/{CreatePage-J84OUqE4.js.map → CreatePage-EngKiM_9.js.map} +1 -1
- package/dist/admin/{CreatePage-pniatTP-.mjs → CreatePage-S7eVTkzK.mjs} +3 -3
- package/dist/admin/{CreatePage-pniatTP-.mjs.map → CreatePage-S7eVTkzK.mjs.map} +1 -1
- package/dist/admin/{CreatePage-gSoe3n8A.mjs → CreatePage-dhyIsOBI.mjs} +2 -2
- package/dist/admin/{CreatePage-gSoe3n8A.mjs.map → CreatePage-dhyIsOBI.mjs.map} +1 -1
- package/dist/admin/{CreatePage-z4ERqv1R.js → CreatePage-h5o6e7Ls.js} +2 -2
- package/dist/admin/{CreatePage-z4ERqv1R.js.map → CreatePage-h5o6e7Ls.js.map} +1 -1
- package/dist/admin/{CreateView-NwMXZjDh.js → CreateView-8qevpt9o.js} +2 -2
- package/dist/admin/{CreateView-NwMXZjDh.js.map → CreateView-8qevpt9o.js.map} +1 -1
- package/dist/admin/{CreateView-sFb3ySEj.mjs → CreateView-HQFmuEYU.mjs} +2 -2
- package/dist/admin/{CreateView-sFb3ySEj.mjs.map → CreateView-HQFmuEYU.mjs.map} +1 -1
- package/dist/admin/{CreateView-BdO0Pvex.mjs → CreateView-YBUhPBBg.mjs} +2 -2
- package/dist/admin/{CreateView-BdO0Pvex.mjs.map → CreateView-YBUhPBBg.mjs.map} +1 -1
- package/dist/admin/{CreateView-JNmfRawb.js → CreateView-chaJt-l6.js} +2 -2
- package/dist/admin/{CreateView-JNmfRawb.js.map → CreateView-chaJt-l6.js.map} +1 -1
- package/dist/admin/{EditPage-dAyJwJfs.js → EditPage-3zbE4Ls0.js} +5 -5
- package/dist/admin/{EditPage-dAyJwJfs.js.map → EditPage-3zbE4Ls0.js.map} +1 -1
- package/dist/admin/{EditPage-eR6riBsR.js → EditPage-H9aQSaaH.js} +3 -3
- package/dist/admin/{EditPage-eR6riBsR.js.map → EditPage-H9aQSaaH.js.map} +1 -1
- package/dist/admin/{EditPage-IQRLHMer.mjs → EditPage-KxrcV7mh.mjs} +5 -5
- package/dist/admin/{EditPage-IQRLHMer.mjs.map → EditPage-KxrcV7mh.mjs.map} +1 -1
- package/dist/admin/{EditPage-yztdPcnM.mjs → EditPage-OcSrQNeJ.mjs} +4 -4
- package/dist/admin/{EditPage-yztdPcnM.mjs.map → EditPage-OcSrQNeJ.mjs.map} +1 -1
- package/dist/admin/{EditPage-3wx6NJgA.mjs → EditPage-oenQhubv.mjs} +3 -3
- package/dist/admin/{EditPage-3wx6NJgA.mjs.map → EditPage-oenQhubv.mjs.map} +1 -1
- package/dist/admin/{EditPage-rdOE18EC.js → EditPage-pk60xOuF.js} +4 -4
- package/dist/admin/{EditPage-rdOE18EC.js.map → EditPage-pk60xOuF.js.map} +1 -1
- package/dist/admin/{EditView-oyHNBKvL.js → EditView-BiqoDvKZ.js} +3 -3
- package/dist/admin/{EditView-oyHNBKvL.js.map → EditView-BiqoDvKZ.js.map} +1 -1
- package/dist/admin/{EditView-fbzip_S8.mjs → EditView-dHW8NJN4.mjs} +3 -3
- package/dist/admin/{EditView-fbzip_S8.mjs.map → EditView-dHW8NJN4.mjs.map} +1 -1
- package/dist/admin/{EditViewPage-rDgxiutd.mjs → EditViewPage-O9_heFiZ.mjs} +3 -3
- package/dist/admin/{EditViewPage-rDgxiutd.mjs.map → EditViewPage-O9_heFiZ.mjs.map} +1 -1
- package/dist/admin/{EditViewPage-LdDGNi6d.js → EditViewPage-OMo0nfgm.js} +3 -3
- package/dist/admin/{EditViewPage-LdDGNi6d.js.map → EditViewPage-OMo0nfgm.js.map} +1 -1
- package/dist/admin/{EventsTable--peC0Geq.js → EventsTable-1IsAibDQ.js} +2 -2
- package/dist/admin/{EventsTable--peC0Geq.js.map → EventsTable-1IsAibDQ.js.map} +1 -1
- package/dist/admin/{EventsTable-rNMPq-X1.mjs → EventsTable-Xh9FIU87.mjs} +2 -2
- package/dist/admin/{EventsTable-rNMPq-X1.mjs.map → EventsTable-Xh9FIU87.mjs.map} +1 -1
- package/dist/admin/{HomePage-qgAro8dS.mjs → HomePage-D-bJ3jts.mjs} +3 -3
- package/dist/admin/{HomePage-qgAro8dS.mjs.map → HomePage-D-bJ3jts.mjs.map} +1 -1
- package/dist/admin/{HomePage-14WM2hIE.js → HomePage-OeCZ2DCC.js} +2 -2
- package/dist/admin/{HomePage-14WM2hIE.js.map → HomePage-OeCZ2DCC.js.map} +1 -1
- package/dist/admin/{HomePage-e2Bs6Kar.js → HomePage-Xf5Ghz92.js} +3 -3
- package/dist/admin/{HomePage-e2Bs6Kar.js.map → HomePage-Xf5Ghz92.js.map} +1 -1
- package/dist/admin/{HomePage-zQ7HL9y6.mjs → HomePage-oNgvRlya.mjs} +2 -2
- package/dist/admin/{HomePage-zQ7HL9y6.mjs.map → HomePage-oNgvRlya.mjs.map} +1 -1
- package/dist/admin/{Layout-KAX2c8wc.js → Layout-BuMuReXo.js} +3 -3
- package/dist/admin/{Layout-KAX2c8wc.js.map → Layout-BuMuReXo.js.map} +1 -1
- package/dist/admin/{Layout-mEj5c1Bi.mjs → Layout-lSZESFtG.mjs} +3 -3
- package/dist/admin/{Layout-mEj5c1Bi.mjs.map → Layout-lSZESFtG.mjs.map} +1 -1
- package/dist/admin/{ListPage-asZ9aDzG.mjs → ListPage-1HhDIdVj.mjs} +3 -3
- package/dist/admin/{ListPage-asZ9aDzG.mjs.map → ListPage-1HhDIdVj.mjs.map} +1 -1
- package/dist/admin/{ListPage-ARW72ceH.js → ListPage-6HUcvFyL.js} +2 -2
- package/dist/admin/{ListPage-ARW72ceH.js.map → ListPage-6HUcvFyL.js.map} +1 -1
- package/dist/admin/{ListPage-MvgbZlgc.js → ListPage-6O-9Hx-B.js} +2 -2
- package/dist/admin/{ListPage-MvgbZlgc.js.map → ListPage-6O-9Hx-B.js.map} +1 -1
- package/dist/admin/{ListPage-DxI49fdQ.js → ListPage-A2MPjJxi.js} +3 -3
- package/dist/admin/{ListPage-DxI49fdQ.js.map → ListPage-A2MPjJxi.js.map} +1 -1
- package/dist/admin/{ListPage-ZDEeaYvm.mjs → ListPage-a9qp7eE2.mjs} +2 -2
- package/dist/admin/{ListPage-ZDEeaYvm.mjs.map → ListPage-a9qp7eE2.mjs.map} +1 -1
- package/dist/admin/{ListPage-romICg1Z.js → ListPage-brwMJDfa.js} +2 -2
- package/dist/admin/{ListPage-romICg1Z.js.map → ListPage-brwMJDfa.js.map} +1 -1
- package/dist/admin/{ListPage-BIpKdUdv.mjs → ListPage-fE2OLTTE.mjs} +2 -2
- package/dist/admin/{ListPage-BIpKdUdv.mjs.map → ListPage-fE2OLTTE.mjs.map} +1 -1
- package/dist/admin/{ListPage-tniramRN.mjs → ListPage-nGtmXc03.mjs} +5 -5
- package/dist/admin/{ListPage-tniramRN.mjs.map → ListPage-nGtmXc03.mjs.map} +1 -1
- package/dist/admin/{ListPage-AUimd0cK.js → ListPage-vVa7B6wq.js} +5 -5
- package/dist/admin/{ListPage-AUimd0cK.js.map → ListPage-vVa7B6wq.js.map} +1 -1
- package/dist/admin/{ListPage-AI8neqxV.mjs → ListPage-xo4o9urf.mjs} +2 -2
- package/dist/admin/{ListPage-AI8neqxV.mjs.map → ListPage-xo4o9urf.mjs.map} +1 -1
- package/dist/admin/{ListView-GYuN24HV.mjs → ListView-2uHZ2TWm.mjs} +7 -4
- package/dist/admin/ListView-2uHZ2TWm.mjs.map +1 -0
- package/dist/admin/{ListView-6hlTFx5H.mjs → ListView-9exyBSMW.mjs} +8 -5
- package/dist/admin/ListView-9exyBSMW.mjs.map +1 -0
- package/dist/admin/{ListView-1VRmjfh8.js → ListView-VjkXS0Tf.js} +7 -4
- package/dist/admin/ListView-VjkXS0Tf.js.map +1 -0
- package/dist/admin/{ListView-ph55AtDF.js → ListView-upndjhrB.js} +8 -5
- package/dist/admin/ListView-upndjhrB.js.map +1 -0
- package/dist/admin/{Login-LxE58baP.mjs → Login-h0-a1KH5.mjs} +2 -2
- package/dist/admin/{Login-LxE58baP.mjs.map → Login-h0-a1KH5.mjs.map} +1 -1
- package/dist/admin/{Login-WeDT3kAj.js → Login-lAG7DHV7.js} +2 -2
- package/dist/admin/{Login-WeDT3kAj.js.map → Login-lAG7DHV7.js.map} +1 -1
- package/dist/admin/{MagicLinkEE-_fUwHKdf.js → MagicLinkEE-ApwIgTej.js} +3 -3
- package/dist/admin/{MagicLinkEE-_fUwHKdf.js.map → MagicLinkEE-ApwIgTej.js.map} +1 -1
- package/dist/admin/{MagicLinkEE-AO_SBYi5.mjs → MagicLinkEE-kjtWcwj6.mjs} +3 -3
- package/dist/admin/{MagicLinkEE-AO_SBYi5.mjs.map → MagicLinkEE-kjtWcwj6.mjs.map} +1 -1
- package/dist/admin/{MarketplacePage-m8Lf3adM.js → MarketplacePage-hNQbTtRI.js} +2 -2
- package/dist/admin/{MarketplacePage-m8Lf3adM.js.map → MarketplacePage-hNQbTtRI.js.map} +1 -1
- package/dist/admin/{MarketplacePage-26cUn4e5.mjs → MarketplacePage-mbqZ9JDl.mjs} +2 -2
- package/dist/admin/{MarketplacePage-26cUn4e5.mjs.map → MarketplacePage-mbqZ9JDl.mjs.map} +1 -1
- package/dist/admin/{Permissions-VzklM1ie.mjs → Permissions-RNmyxA77.mjs} +2 -2
- package/dist/admin/{Permissions-VzklM1ie.mjs.map → Permissions-RNmyxA77.mjs.map} +1 -1
- package/dist/admin/{Permissions-NRzJqZEr.js → Permissions-v5pxfHqR.js} +2 -2
- package/dist/admin/{Permissions-NRzJqZEr.js.map → Permissions-v5pxfHqR.js.map} +1 -1
- package/dist/admin/{ProfilePage-0d-k7mtP.mjs → ProfilePage-bDOjI8F-.mjs} +3 -3
- package/dist/admin/{ProfilePage-0d-k7mtP.mjs.map → ProfilePage-bDOjI8F-.mjs.map} +1 -1
- package/dist/admin/{ProfilePage-oSih4pcL.js → ProfilePage-ivIjdblf.js} +3 -3
- package/dist/admin/{ProfilePage-oSih4pcL.js.map → ProfilePage-ivIjdblf.js.map} +1 -1
- package/dist/admin/{SelectRoles-V_PZUtVT.mjs → SelectRoles-8cfbyMVr.mjs} +3 -3
- package/dist/admin/{SelectRoles-V_PZUtVT.mjs.map → SelectRoles-8cfbyMVr.mjs.map} +1 -1
- package/dist/admin/{SelectRoles-ZsRUnSdr.js → SelectRoles-ZZPSrkZE.js} +3 -3
- package/dist/admin/{SelectRoles-ZsRUnSdr.js.map → SelectRoles-ZZPSrkZE.js.map} +1 -1
- package/dist/admin/{SingleSignOnPage-kbLz1A_y.mjs → SingleSignOnPage-Ots_q1kK.mjs} +4 -4
- package/dist/admin/SingleSignOnPage-Ots_q1kK.mjs.map +1 -0
- package/dist/admin/{SingleSignOnPage-jtjy4_4U.js → SingleSignOnPage-dKN-oVc8.js} +4 -4
- package/dist/admin/SingleSignOnPage-dKN-oVc8.js.map +1 -0
- package/dist/admin/{Table-ZFyORhH3.mjs → Table-ILoLTYT2.mjs} +2 -2
- package/dist/admin/{Table-ZFyORhH3.mjs.map → Table-ILoLTYT2.mjs.map} +1 -1
- package/dist/admin/{Table-kMt3jQ2g.js → Table-rh1Goj9B.js} +2 -2
- package/dist/admin/{Table-kMt3jQ2g.js.map → Table-rh1Goj9B.js.map} +1 -1
- package/dist/admin/{TokenTypeSelect-Ngz4ONEL.js → TokenTypeSelect-Bc6Gx5Og.js} +2 -2
- package/dist/admin/{TokenTypeSelect-Ngz4ONEL.js.map → TokenTypeSelect-Bc6Gx5Og.js.map} +1 -1
- package/dist/admin/{TokenTypeSelect-oOKwvsQ2.mjs → TokenTypeSelect-oUpUNcrP.mjs} +2 -2
- package/dist/admin/{TokenTypeSelect-oOKwvsQ2.mjs.map → TokenTypeSelect-oUpUNcrP.mjs.map} +1 -1
- package/dist/admin/{UseCasePage-qrhHiF5S.js → UseCasePage-NYPLK0s5.js} +2 -2
- package/dist/admin/{UseCasePage-qrhHiF5S.js.map → UseCasePage-NYPLK0s5.js.map} +1 -1
- package/dist/admin/{UseCasePage-2wum_xzS.mjs → UseCasePage-siicRv21.mjs} +2 -2
- package/dist/admin/{UseCasePage-2wum_xzS.mjs.map → UseCasePage-siicRv21.mjs.map} +1 -1
- package/dist/admin/{constants-enREoOEa.mjs → constants-GoVT0wVo.mjs} +2 -2
- package/dist/admin/{constants-enREoOEa.mjs.map → constants-GoVT0wVo.mjs.map} +1 -1
- package/dist/admin/{constants-jQFnBWVd.js → constants-wYcX8us0.js} +2 -2
- package/dist/admin/{constants-jQFnBWVd.js.map → constants-wYcX8us0.js.map} +1 -1
- package/dist/admin/{index-dcaxfEDi.js → index-e8PHZx2A.js} +48 -52
- package/dist/admin/index-e8PHZx2A.js.map +1 -0
- package/dist/admin/{index-oBxjGftZ.mjs → index-i37IN_fH.mjs} +51 -55
- package/dist/admin/index-i37IN_fH.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/Form.d.ts +1 -1
- package/dist/admin/src/hooks/useAdminRoles.d.ts +2 -2
- package/dist/admin/src/index.d.ts +1 -1
- package/dist/admin/src/services/auth.d.ts +3 -3
- package/dist/admin/src/services/users.d.ts +4 -4
- package/dist/admin/{useAdminRoles-Q2BxHs4o.js → useAdminRoles-JOPnwG5K.js} +2 -2
- package/dist/admin/{useAdminRoles-Q2BxHs4o.js.map → useAdminRoles-JOPnwG5K.js.map} +1 -1
- package/dist/admin/{useAdminRoles-HWIbyk2Z.mjs → useAdminRoles-qJLjTITl.mjs} +2 -2
- package/dist/admin/{useAdminRoles-HWIbyk2Z.mjs.map → useAdminRoles-qJLjTITl.mjs.map} +1 -1
- package/dist/admin/{validation-gH-06ERZ.mjs → validation-man-qOMc.mjs} +2 -2
- package/dist/admin/{validation-gH-06ERZ.mjs.map → validation-man-qOMc.mjs.map} +1 -1
- package/dist/admin/{validation-2MGgF10o.js → validation-mliIGlD0.js} +2 -2
- package/dist/admin/{validation-2MGgF10o.js.map → validation-mliIGlD0.js.map} +1 -1
- package/dist/ee/admin/src/hooks/useLicenseLimits.d.ts +2 -2
- package/dist/ee/server/index.js +102 -1916
- package/dist/ee/server/index.js.map +1 -1
- package/dist/ee/server/index.mjs +107 -1920
- package/dist/ee/server/index.mjs.map +1 -1
- package/dist/package.json.d.ts +9 -9
- package/dist/server/index.js +65 -48
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +41 -41
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/domain/permission/index.d.ts.map +1 -1
- package/dist/server/src/middlewares/rateLimit.d.ts.map +1 -1
- package/dist/server/src/register.d.ts.map +1 -1
- package/dist/server/src/routes/serve-admin-panel.d.ts.map +1 -1
- package/dist/server/src/services/permission/sections-builder/handlers.d.ts.map +1 -1
- package/dist/server/src/services/transfer/token.d.ts.map +1 -1
- package/dist/shared/contracts/admin.d.ts +7 -1
- package/dist/shared/contracts/admin.d.ts.map +1 -1
- package/package.json +10 -10
- package/dist/admin/ListView-1VRmjfh8.js.map +0 -1
- package/dist/admin/ListView-6hlTFx5H.mjs.map +0 -1
- package/dist/admin/ListView-GYuN24HV.mjs.map +0 -1
- package/dist/admin/ListView-ph55AtDF.js.map +0 -1
- package/dist/admin/SingleSignOnPage-jtjy4_4U.js.map +0 -1
- package/dist/admin/SingleSignOnPage-kbLz1A_y.mjs.map +0 -1
- package/dist/admin/index-dcaxfEDi.js.map +0 -1
- package/dist/admin/index-oBxjGftZ.mjs.map +0 -1
package/dist/ee/server/index.mjs
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { resolve, basename, join, extname } from "path";
|
|
2
2
|
import fse from "fs-extra";
|
|
3
3
|
import koaStatic from "koa-static";
|
|
4
|
-
import "
|
|
5
|
-
import {
|
|
6
|
-
import { differenceInHours, parseISO, add } from "date-fns";
|
|
4
|
+
import { isNil, castArray, merge, map, uniq, difference, differenceWith, isEqual, isEmpty, toLower, isFunction, toString, pipe, toNumber, assign, reverse, take, prop, drop, pick, mapValues, curry, includes, isArray, set, omit, has, isUndefined } from "lodash/fp";
|
|
5
|
+
import { differenceInHours, parseISO } from "date-fns";
|
|
7
6
|
import { errors, async, arrays, yup, validateYupSchema, env } from "@strapi/utils";
|
|
8
|
-
import
|
|
7
|
+
import "@strapi/types";
|
|
9
8
|
import localProvider from "@strapi/provider-audit-logs-local";
|
|
10
9
|
import { scheduleJob } from "node-schedule";
|
|
11
10
|
import _ from "lodash";
|
|
@@ -94,9 +93,9 @@ const authenticate$2 = async (ctx) => {
|
|
|
94
93
|
ability: userAbility
|
|
95
94
|
};
|
|
96
95
|
};
|
|
97
|
-
const name
|
|
96
|
+
const name = "admin";
|
|
98
97
|
const adminAuthStrategy = {
|
|
99
|
-
name
|
|
98
|
+
name,
|
|
100
99
|
authenticate: authenticate$2
|
|
101
100
|
};
|
|
102
101
|
const DAY_IN_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -245,253 +244,6 @@ async function migrateAuditLogsTable({ oldContentTypes, contentTypes }) {
|
|
|
245
244
|
await strapi.db.getSchemaConnection().renameTable("audit_logs", "strapi_audit_logs");
|
|
246
245
|
await strapi.db.getSchemaConnection().renameTable("audit_logs_user_links", "strapi_audit_logs_user_links");
|
|
247
246
|
}
|
|
248
|
-
const WORKFLOW_MODEL_UID = "admin::workflow";
|
|
249
|
-
const STAGE_MODEL_UID = "admin::workflow-stage";
|
|
250
|
-
const STAGE_TRANSITION_UID = "admin::review-workflows.stage.transition";
|
|
251
|
-
const STAGE_DEFAULT_COLOR = "#4945FF";
|
|
252
|
-
const ENTITY_STAGE_ATTRIBUTE = "strapi_stage";
|
|
253
|
-
const ENTITY_ASSIGNEE_ATTRIBUTE = "strapi_assignee";
|
|
254
|
-
const MAX_WORKFLOWS = 200;
|
|
255
|
-
const MAX_STAGES_PER_WORKFLOW = 200;
|
|
256
|
-
const ERRORS = {
|
|
257
|
-
WORKFLOW_WITHOUT_STAGES: "A workflow must have at least one stage.",
|
|
258
|
-
WORKFLOWS_LIMIT: "You’ve reached the limit of workflows in your plan. Delete a workflow or contact Sales to enable more workflows.",
|
|
259
|
-
STAGES_LIMIT: "You’ve reached the limit of stages for this workflow in your plan. Try deleting some stages or contact Sales to enable more stages.",
|
|
260
|
-
DUPLICATED_STAGE_NAME: "Stage names must be unique."
|
|
261
|
-
};
|
|
262
|
-
const WORKFLOW_POPULATE = {
|
|
263
|
-
stages: {
|
|
264
|
-
populate: {
|
|
265
|
-
permissions: {
|
|
266
|
-
fields: ["action", "actionParameters"],
|
|
267
|
-
populate: {
|
|
268
|
-
role: { fields: ["id", "name"] }
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
};
|
|
274
|
-
async function migrateReviewWorkflowStagesColor({ oldContentTypes, contentTypes }) {
|
|
275
|
-
const hadColor = !!oldContentTypes?.["admin::workflow-stage"]?.attributes?.color;
|
|
276
|
-
const hasColor = !!contentTypes?.["admin::workflow-stage"]?.attributes?.color;
|
|
277
|
-
if (!hadColor && hasColor) {
|
|
278
|
-
await strapi.db.query("admin::workflow-stage").updateMany({
|
|
279
|
-
data: {
|
|
280
|
-
color: STAGE_DEFAULT_COLOR
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
const getService = (name2, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
286
|
-
return strapi2.service(`admin::${name2}`);
|
|
287
|
-
};
|
|
288
|
-
async function migrateReviewWorkflowStagesRoles({ oldContentTypes, contentTypes }) {
|
|
289
|
-
const stageUID = "admin::workflow-stage";
|
|
290
|
-
const hadRolePermissions = !!oldContentTypes?.[stageUID]?.attributes?.permissions;
|
|
291
|
-
const hasRolePermissions = !!contentTypes?.[stageUID]?.attributes?.permissions;
|
|
292
|
-
if (!hadRolePermissions && hasRolePermissions) {
|
|
293
|
-
const roleUID = "admin::role";
|
|
294
|
-
strapi.log.info(
|
|
295
|
-
`Migrating all existing review workflow stages to have RBAC permissions for all ${roleUID}.`
|
|
296
|
-
);
|
|
297
|
-
const stagePermissionsService = getService("stage-permissions");
|
|
298
|
-
const stages2 = await strapi.db.query(stageUID).findMany();
|
|
299
|
-
const roles2 = await strapi.db.query(roleUID).findMany();
|
|
300
|
-
const groupedPermissions = {};
|
|
301
|
-
roles2.map((role2) => role2.id).forEach((roleId) => {
|
|
302
|
-
stages2.map((stage) => stage.id).forEach((stageId) => {
|
|
303
|
-
if (!groupedPermissions[stageId]) {
|
|
304
|
-
groupedPermissions[stageId] = [];
|
|
305
|
-
}
|
|
306
|
-
groupedPermissions[stageId].push({
|
|
307
|
-
roleId,
|
|
308
|
-
fromStage: stageId,
|
|
309
|
-
action: STAGE_TRANSITION_UID
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
for (const [stageId, permissions] of Object.entries(groupedPermissions)) {
|
|
314
|
-
const numericalStageId = Number(stageId);
|
|
315
|
-
if (Number.isNaN(numericalStageId)) {
|
|
316
|
-
strapi.log.warn(
|
|
317
|
-
`Unable to apply ${roleUID} migration for ${stageUID} with id ${stageId}. The stage does not have a numerical id.`
|
|
318
|
-
);
|
|
319
|
-
continue;
|
|
320
|
-
}
|
|
321
|
-
const stagePermissions2 = await stagePermissionsService.registerMany(permissions);
|
|
322
|
-
await strapi.db.query(STAGE_MODEL_UID).update({
|
|
323
|
-
where: { id: numericalStageId },
|
|
324
|
-
data: {
|
|
325
|
-
permissions: stagePermissions2.flat().map((permission2) => permission2.id)
|
|
326
|
-
}
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
const name = "Default";
|
|
332
|
-
const defaultWorkflow = {
|
|
333
|
-
name
|
|
334
|
-
};
|
|
335
|
-
async function migrateReviewWorkflowName({ oldContentTypes, contentTypes }) {
|
|
336
|
-
const hadName = !!oldContentTypes?.[WORKFLOW_MODEL_UID]?.attributes?.name;
|
|
337
|
-
const hasName = !!contentTypes?.[WORKFLOW_MODEL_UID]?.attributes?.name;
|
|
338
|
-
if (!hadName && hasName) {
|
|
339
|
-
await strapi.db.query(WORKFLOW_MODEL_UID).updateMany({
|
|
340
|
-
where: {
|
|
341
|
-
name: { $null: true }
|
|
342
|
-
},
|
|
343
|
-
data: {
|
|
344
|
-
name: defaultWorkflow.name
|
|
345
|
-
}
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
async function migrateWorkflowsContentTypes({ oldContentTypes, contentTypes }) {
|
|
350
|
-
const hadContentTypes = !!oldContentTypes?.[WORKFLOW_MODEL_UID]?.attributes?.contentTypes;
|
|
351
|
-
const hasContentTypes = !!contentTypes?.[WORKFLOW_MODEL_UID]?.attributes?.contentTypes;
|
|
352
|
-
if (!hadContentTypes && hasContentTypes) {
|
|
353
|
-
await strapi.db.query(WORKFLOW_MODEL_UID).updateMany({ data: { contentTypes: [] } });
|
|
354
|
-
const contentTypes2 = pipe([pickBy(get("options.reviewWorkflows")), keys])(oldContentTypes);
|
|
355
|
-
if (contentTypes2.length) {
|
|
356
|
-
await strapi.db.query(WORKFLOW_MODEL_UID).update({ where: { id: { $notNull: true } }, data: { contentTypes: contentTypes2 } });
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
const transformTableName = (table) => {
|
|
361
|
-
if (typeof table === "string") {
|
|
362
|
-
return { name: table };
|
|
363
|
-
}
|
|
364
|
-
return table;
|
|
365
|
-
};
|
|
366
|
-
async function findTables({ strapi: strapi2 }, regex) {
|
|
367
|
-
const tables = await strapi2.db.dialect.schemaInspector.getTables();
|
|
368
|
-
return tables.filter((tableName) => regex.test(tableName));
|
|
369
|
-
}
|
|
370
|
-
async function addPersistTables({ strapi: strapi2 }, tableNames) {
|
|
371
|
-
const persistedTables = await getPersistedTables({ strapi: strapi2 });
|
|
372
|
-
const tables = tableNames.map(transformTableName);
|
|
373
|
-
const notPersistedTableNames = differenceWith(isEqual, tables, persistedTables);
|
|
374
|
-
const tablesToPersist = differenceWith(
|
|
375
|
-
(t1, t2) => t1.name === t2.name,
|
|
376
|
-
persistedTables,
|
|
377
|
-
notPersistedTableNames
|
|
378
|
-
);
|
|
379
|
-
if (!notPersistedTableNames.length) {
|
|
380
|
-
return;
|
|
381
|
-
}
|
|
382
|
-
tablesToPersist.push(...notPersistedTableNames);
|
|
383
|
-
await strapi2.store.set({
|
|
384
|
-
type: "core",
|
|
385
|
-
key: "persisted_tables",
|
|
386
|
-
value: tablesToPersist
|
|
387
|
-
});
|
|
388
|
-
}
|
|
389
|
-
async function getPersistedTables({ strapi: strapi2 }) {
|
|
390
|
-
const persistedTables = await strapi2.store.get({
|
|
391
|
-
type: "core",
|
|
392
|
-
key: "persisted_tables"
|
|
393
|
-
});
|
|
394
|
-
return (persistedTables || []).map(transformTableName);
|
|
395
|
-
}
|
|
396
|
-
async function setPersistedTables({ strapi: strapi2 }, tableNames) {
|
|
397
|
-
await strapi2.store.set({
|
|
398
|
-
type: "core",
|
|
399
|
-
key: "persisted_tables",
|
|
400
|
-
value: tableNames
|
|
401
|
-
});
|
|
402
|
-
}
|
|
403
|
-
const persistTablesWithPrefix = async (tableNamePrefix) => {
|
|
404
|
-
const tableNameRegex = new RegExp(`^${tableNamePrefix}.*`);
|
|
405
|
-
const tableNames = await findTables({ strapi }, tableNameRegex);
|
|
406
|
-
await addPersistTables({ strapi }, tableNames);
|
|
407
|
-
};
|
|
408
|
-
const removePersistedTablesWithSuffix = async (tableNameSuffix) => {
|
|
409
|
-
const tableNameRegex = new RegExp(`.*${tableNameSuffix}$`);
|
|
410
|
-
const persistedTables = await getPersistedTables({ strapi });
|
|
411
|
-
const filteredPersistedTables = persistedTables.filter((table) => {
|
|
412
|
-
return !tableNameRegex.test(table.name);
|
|
413
|
-
});
|
|
414
|
-
if (filteredPersistedTables.length === persistedTables.length) {
|
|
415
|
-
return;
|
|
416
|
-
}
|
|
417
|
-
await setPersistedTables({ strapi }, filteredPersistedTables);
|
|
418
|
-
};
|
|
419
|
-
const persistTables = async (tables) => {
|
|
420
|
-
await addPersistTables({ strapi }, tables);
|
|
421
|
-
};
|
|
422
|
-
function checkVersionThreshold(startVersion, currentVersion, thresholdVersion) {
|
|
423
|
-
return semver.gte(currentVersion, thresholdVersion) && semver.lt(startVersion, thresholdVersion);
|
|
424
|
-
}
|
|
425
|
-
async function migrateStageAttribute({ oldContentTypes, contentTypes }) {
|
|
426
|
-
const getRWVersion = getOr("0.0.0", `${STAGE_MODEL_UID}.options.version`);
|
|
427
|
-
const oldRWVersion = getRWVersion(oldContentTypes);
|
|
428
|
-
const currentRWVersion = getRWVersion(contentTypes);
|
|
429
|
-
const migrationNeeded = checkVersionThreshold(oldRWVersion, currentRWVersion, "1.1.0");
|
|
430
|
-
if (migrationNeeded) {
|
|
431
|
-
const oldAttributeTableName = "strapi_review_workflows_stage";
|
|
432
|
-
const newAttributeTableName = "strapi_stage";
|
|
433
|
-
const tables = await findTables({ strapi }, new RegExp(oldAttributeTableName));
|
|
434
|
-
await async.map(tables, async (tableName) => {
|
|
435
|
-
const newTableName = tableName.replace(oldAttributeTableName, newAttributeTableName);
|
|
436
|
-
const alreadyHasNextTable = await strapi.db.connection.schema.hasTable(newTableName);
|
|
437
|
-
if (alreadyHasNextTable) {
|
|
438
|
-
const dataInTable = await strapi.db.connection(newTableName).select().limit(1);
|
|
439
|
-
if (!dataInTable.length) {
|
|
440
|
-
await strapi.db.connection.schema.dropTable(newTableName);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
try {
|
|
444
|
-
await strapi.db.connection.schema.renameTable(tableName, newTableName);
|
|
445
|
-
} catch (e) {
|
|
446
|
-
strapi.log.warn(
|
|
447
|
-
`An error occurred during the migration of ${tableName} table to ${newTableName}.
|
|
448
|
-
If ${newTableName} already exists, migration can't be done automatically.`
|
|
449
|
-
);
|
|
450
|
-
strapi.log.warn(e.message);
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
const getVisibleContentTypesUID = pipe([
|
|
456
|
-
// Pick only content-types visible in the content-manager and option is not false
|
|
457
|
-
pickBy(
|
|
458
|
-
(value) => getOr(true, "pluginOptions.content-manager.visible", value) && !getOr(false, "options.noStageAttribute", value)
|
|
459
|
-
),
|
|
460
|
-
// Get UIDs
|
|
461
|
-
keys
|
|
462
|
-
]);
|
|
463
|
-
const hasStageAttribute = has(["attributes", ENTITY_STAGE_ATTRIBUTE]);
|
|
464
|
-
const getWorkflowContentTypeFilter = ({ strapi: strapi2 }, contentType) => {
|
|
465
|
-
if (strapi2.db.dialect.supportsOperator("$jsonSupersetOf")) {
|
|
466
|
-
return { $jsonSupersetOf: JSON.stringify([contentType]) };
|
|
467
|
-
}
|
|
468
|
-
return { $contains: `"${contentType}"` };
|
|
469
|
-
};
|
|
470
|
-
const clampMaxWorkflows = clamp(1, MAX_WORKFLOWS);
|
|
471
|
-
const clampMaxStagesPerWorkflow = clamp(1, MAX_STAGES_PER_WORKFLOW);
|
|
472
|
-
async function migrateDeletedCTInWorkflows({ oldContentTypes, contentTypes }) {
|
|
473
|
-
const deletedContentTypes = difference(keys(oldContentTypes), keys(contentTypes)) ?? [];
|
|
474
|
-
if (deletedContentTypes.length) {
|
|
475
|
-
await async.map(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
476
|
-
const workflow2 = await strapi.db.query(WORKFLOW_MODEL_UID).findOne({
|
|
477
|
-
select: ["id", "contentTypes"],
|
|
478
|
-
where: {
|
|
479
|
-
contentTypes: getWorkflowContentTypeFilter({ strapi }, deletedContentTypeUID)
|
|
480
|
-
}
|
|
481
|
-
});
|
|
482
|
-
if (workflow2) {
|
|
483
|
-
await strapi.db.query(WORKFLOW_MODEL_UID).update({
|
|
484
|
-
where: { id: workflow2.id },
|
|
485
|
-
data: {
|
|
486
|
-
contentTypes: workflow2.contentTypes.filter(
|
|
487
|
-
(contentTypeUID) => contentTypeUID !== deletedContentTypeUID
|
|
488
|
-
)
|
|
489
|
-
}
|
|
490
|
-
});
|
|
491
|
-
}
|
|
492
|
-
});
|
|
493
|
-
}
|
|
494
|
-
}
|
|
495
247
|
const DEFAULT_RETENTION_DAYS = 90;
|
|
496
248
|
const defaultEvents = [
|
|
497
249
|
"entry.create",
|
|
@@ -658,23 +410,6 @@ const createAuditLogsService = (strapi2) => {
|
|
|
658
410
|
}
|
|
659
411
|
};
|
|
660
412
|
};
|
|
661
|
-
function contentTypeMiddleware(strapi2) {
|
|
662
|
-
const moveReviewWorkflowOption = (ctx) => {
|
|
663
|
-
const { reviewWorkflows: reviewWorkflows2, ...contentType } = ctx.request.body.contentType;
|
|
664
|
-
if (typeof reviewWorkflows2 === "boolean") {
|
|
665
|
-
ctx.request.body.contentType = set("options.reviewWorkflows", reviewWorkflows2, contentType);
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
strapi2.server.router.use("/content-type-builder/content-types/:uid?", (ctx, next) => {
|
|
669
|
-
if (ctx.method === "PUT" || ctx.method === "POST") {
|
|
670
|
-
moveReviewWorkflowOption(ctx);
|
|
671
|
-
}
|
|
672
|
-
return next();
|
|
673
|
-
});
|
|
674
|
-
}
|
|
675
|
-
const reviewWorkflowsMiddlewares = {
|
|
676
|
-
contentTypeMiddleware
|
|
677
|
-
};
|
|
678
413
|
const register = async ({ strapi: strapi2 }) => {
|
|
679
414
|
const auditLogsIsEnabled = strapi2.config.get("admin.auditLogs.enabled", true);
|
|
680
415
|
if (auditLogsIsEnabled) {
|
|
@@ -683,105 +418,9 @@ const register = async ({ strapi: strapi2 }) => {
|
|
|
683
418
|
strapi2.add("audit-logs", auditLogsService);
|
|
684
419
|
await auditLogsService.register();
|
|
685
420
|
}
|
|
686
|
-
if (strapi2.ee.features.isEnabled("review-workflows")) {
|
|
687
|
-
strapi2.hook("strapi::content-types.beforeSync").register(migrateStageAttribute);
|
|
688
|
-
strapi2.hook("strapi::content-types.afterSync").register(migrateReviewWorkflowStagesColor).register(migrateReviewWorkflowStagesRoles).register(migrateReviewWorkflowName).register(migrateWorkflowsContentTypes).register(migrateDeletedCTInWorkflows);
|
|
689
|
-
const reviewWorkflowService = getService("review-workflows");
|
|
690
|
-
reviewWorkflowsMiddlewares.contentTypeMiddleware(strapi2);
|
|
691
|
-
await reviewWorkflowService.register(strapi2.ee.features.get("review-workflows"));
|
|
692
|
-
}
|
|
693
421
|
await executeCERegister({ strapi: strapi2 });
|
|
694
422
|
};
|
|
695
|
-
const
|
|
696
|
-
schema: {
|
|
697
|
-
collectionName: "strapi_workflows",
|
|
698
|
-
info: {
|
|
699
|
-
name: "Workflow",
|
|
700
|
-
description: "",
|
|
701
|
-
singularName: "workflow",
|
|
702
|
-
pluralName: "workflows",
|
|
703
|
-
displayName: "Workflow"
|
|
704
|
-
},
|
|
705
|
-
options: {},
|
|
706
|
-
pluginOptions: {
|
|
707
|
-
"content-manager": {
|
|
708
|
-
visible: false
|
|
709
|
-
},
|
|
710
|
-
"content-type-builder": {
|
|
711
|
-
visible: false
|
|
712
|
-
}
|
|
713
|
-
},
|
|
714
|
-
attributes: {
|
|
715
|
-
name: {
|
|
716
|
-
type: "string",
|
|
717
|
-
required: true,
|
|
718
|
-
unique: true
|
|
719
|
-
},
|
|
720
|
-
stages: {
|
|
721
|
-
type: "relation",
|
|
722
|
-
target: "admin::workflow-stage",
|
|
723
|
-
relation: "oneToMany",
|
|
724
|
-
mappedBy: "workflow"
|
|
725
|
-
},
|
|
726
|
-
contentTypes: {
|
|
727
|
-
type: "json",
|
|
728
|
-
required: true,
|
|
729
|
-
default: []
|
|
730
|
-
}
|
|
731
|
-
}
|
|
732
|
-
}
|
|
733
|
-
};
|
|
734
|
-
const workflowStage = {
|
|
735
|
-
schema: {
|
|
736
|
-
collectionName: "strapi_workflows_stages",
|
|
737
|
-
info: {
|
|
738
|
-
name: "Workflow Stage",
|
|
739
|
-
description: "",
|
|
740
|
-
singularName: "workflow-stage",
|
|
741
|
-
pluralName: "workflow-stages",
|
|
742
|
-
displayName: "Stages"
|
|
743
|
-
},
|
|
744
|
-
options: {
|
|
745
|
-
version: "1.1.0"
|
|
746
|
-
},
|
|
747
|
-
pluginOptions: {
|
|
748
|
-
"content-manager": {
|
|
749
|
-
visible: false
|
|
750
|
-
},
|
|
751
|
-
"content-type-builder": {
|
|
752
|
-
visible: false
|
|
753
|
-
}
|
|
754
|
-
},
|
|
755
|
-
attributes: {
|
|
756
|
-
name: {
|
|
757
|
-
type: "string",
|
|
758
|
-
configurable: false
|
|
759
|
-
},
|
|
760
|
-
color: {
|
|
761
|
-
type: "string",
|
|
762
|
-
configurable: false,
|
|
763
|
-
default: STAGE_DEFAULT_COLOR
|
|
764
|
-
},
|
|
765
|
-
workflow: {
|
|
766
|
-
type: "relation",
|
|
767
|
-
target: "admin::workflow",
|
|
768
|
-
relation: "manyToOne",
|
|
769
|
-
inversedBy: "stages",
|
|
770
|
-
configurable: false
|
|
771
|
-
},
|
|
772
|
-
permissions: {
|
|
773
|
-
type: "relation",
|
|
774
|
-
target: "admin::permission",
|
|
775
|
-
relation: "manyToMany",
|
|
776
|
-
configurable: false
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
};
|
|
781
|
-
const index$3 = {
|
|
782
|
-
workflow,
|
|
783
|
-
"workflow-stage": workflowStage
|
|
784
|
-
};
|
|
423
|
+
const index$3 = {};
|
|
785
424
|
const actions$1 = [
|
|
786
425
|
{
|
|
787
426
|
uid: "marketplace.read",
|
|
@@ -1094,6 +733,9 @@ const executeCEBootstrap = async ({ strapi: strapi2 }) => {
|
|
|
1094
733
|
transferService.token.checkSaltIsDefined();
|
|
1095
734
|
tokenService.checkSecretIsDefined();
|
|
1096
735
|
};
|
|
736
|
+
const getService = (name2, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
737
|
+
return strapi2.service(`admin::${name2}`);
|
|
738
|
+
};
|
|
1097
739
|
const actions = {
|
|
1098
740
|
sso: [
|
|
1099
741
|
{
|
|
@@ -1122,48 +764,49 @@ const actions = {
|
|
|
1122
764
|
category: "audit logs",
|
|
1123
765
|
subCategory: "options"
|
|
1124
766
|
}
|
|
1125
|
-
],
|
|
1126
|
-
reviewWorkflows: [
|
|
1127
|
-
{
|
|
1128
|
-
uid: "review-workflows.create",
|
|
1129
|
-
displayName: "Create",
|
|
1130
|
-
pluginName: "admin",
|
|
1131
|
-
section: "settings",
|
|
1132
|
-
category: "review workflows",
|
|
1133
|
-
subCategory: "options"
|
|
1134
|
-
},
|
|
1135
|
-
{
|
|
1136
|
-
uid: "review-workflows.read",
|
|
1137
|
-
displayName: "Read",
|
|
1138
|
-
pluginName: "admin",
|
|
1139
|
-
section: "settings",
|
|
1140
|
-
category: "review workflows",
|
|
1141
|
-
subCategory: "options"
|
|
1142
|
-
},
|
|
1143
|
-
{
|
|
1144
|
-
uid: "review-workflows.update",
|
|
1145
|
-
displayName: "Update",
|
|
1146
|
-
pluginName: "admin",
|
|
1147
|
-
section: "settings",
|
|
1148
|
-
category: "review workflows",
|
|
1149
|
-
subCategory: "options"
|
|
1150
|
-
},
|
|
1151
|
-
{
|
|
1152
|
-
uid: "review-workflows.delete",
|
|
1153
|
-
displayName: "Delete",
|
|
1154
|
-
pluginName: "admin",
|
|
1155
|
-
section: "settings",
|
|
1156
|
-
category: "review workflows",
|
|
1157
|
-
subCategory: "options"
|
|
1158
|
-
},
|
|
1159
|
-
{
|
|
1160
|
-
uid: "review-workflows.stage.transition",
|
|
1161
|
-
displayName: "Change stage",
|
|
1162
|
-
pluginName: "admin",
|
|
1163
|
-
section: "internal"
|
|
1164
|
-
}
|
|
1165
767
|
]
|
|
1166
768
|
};
|
|
769
|
+
const transformTableName = (table) => {
|
|
770
|
+
if (typeof table === "string") {
|
|
771
|
+
return { name: table };
|
|
772
|
+
}
|
|
773
|
+
return table;
|
|
774
|
+
};
|
|
775
|
+
async function findTables({ strapi: strapi2 }, regex) {
|
|
776
|
+
const tables = await strapi2.db.dialect.schemaInspector.getTables();
|
|
777
|
+
return tables.filter((tableName) => regex.test(tableName));
|
|
778
|
+
}
|
|
779
|
+
async function addPersistTables({ strapi: strapi2 }, tableNames) {
|
|
780
|
+
const persistedTables = await getPersistedTables({ strapi: strapi2 });
|
|
781
|
+
const tables = tableNames.map(transformTableName);
|
|
782
|
+
const notPersistedTableNames = differenceWith(isEqual, tables, persistedTables);
|
|
783
|
+
const tablesToPersist = differenceWith(
|
|
784
|
+
(t1, t2) => t1.name === t2.name,
|
|
785
|
+
persistedTables,
|
|
786
|
+
notPersistedTableNames
|
|
787
|
+
);
|
|
788
|
+
if (!notPersistedTableNames.length) {
|
|
789
|
+
return;
|
|
790
|
+
}
|
|
791
|
+
tablesToPersist.push(...notPersistedTableNames);
|
|
792
|
+
await strapi2.store.set({
|
|
793
|
+
type: "core",
|
|
794
|
+
key: "persisted_tables",
|
|
795
|
+
value: tablesToPersist
|
|
796
|
+
});
|
|
797
|
+
}
|
|
798
|
+
async function getPersistedTables({ strapi: strapi2 }) {
|
|
799
|
+
const persistedTables = await strapi2.store.get({
|
|
800
|
+
type: "core",
|
|
801
|
+
key: "persisted_tables"
|
|
802
|
+
});
|
|
803
|
+
return (persistedTables || []).map(transformTableName);
|
|
804
|
+
}
|
|
805
|
+
const persistTablesWithPrefix = async (tableNamePrefix) => {
|
|
806
|
+
const tableNameRegex = new RegExp(`^${tableNamePrefix}.*`);
|
|
807
|
+
const tableNames = await findTables({ strapi }, tableNameRegex);
|
|
808
|
+
await addPersistTables({ strapi }, tableNames);
|
|
809
|
+
};
|
|
1167
810
|
const bootstrap = async (args) => {
|
|
1168
811
|
const { actionProvider } = getService("permission");
|
|
1169
812
|
if (strapi.ee.features.isEnabled("sso")) {
|
|
@@ -1173,14 +816,6 @@ const bootstrap = async (args) => {
|
|
|
1173
816
|
await persistTablesWithPrefix("strapi_audit_logs");
|
|
1174
817
|
await actionProvider.registerMany(actions.auditLogs);
|
|
1175
818
|
}
|
|
1176
|
-
if (strapi.ee.features.isEnabled("review-workflows")) {
|
|
1177
|
-
await persistTablesWithPrefix("strapi_workflows");
|
|
1178
|
-
const { bootstrap: rwBootstrap } = getService("review-workflows");
|
|
1179
|
-
await rwBootstrap();
|
|
1180
|
-
await actionProvider.registerMany(actions.reviewWorkflows);
|
|
1181
|
-
getService("review-workflows-decorator");
|
|
1182
|
-
await getService("review-workflows-weekly-metrics").registerCron();
|
|
1183
|
-
}
|
|
1184
819
|
await getService("seat-enforcement").seatEnforcementWorkflow();
|
|
1185
820
|
await executeCEBootstrap(args);
|
|
1186
821
|
};
|
|
@@ -1332,211 +967,50 @@ const auditLogs$1 = {
|
|
|
1332
967
|
}
|
|
1333
968
|
]
|
|
1334
969
|
};
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
{
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
policies: [
|
|
1380
|
-
"admin::isAuthenticatedAdmin",
|
|
1381
|
-
{
|
|
1382
|
-
name: "admin::hasPermissions",
|
|
1383
|
-
config: {
|
|
1384
|
-
actions: ["admin::review-workflows.delete"]
|
|
1385
|
-
}
|
|
1386
|
-
}
|
|
1387
|
-
]
|
|
1388
|
-
}
|
|
1389
|
-
},
|
|
1390
|
-
{
|
|
1391
|
-
method: "GET",
|
|
1392
|
-
path: "/review-workflows/workflows",
|
|
1393
|
-
handler: "workflows.find",
|
|
1394
|
-
config: {
|
|
1395
|
-
middlewares: [enableFeatureMiddleware("review-workflows")],
|
|
1396
|
-
policies: [
|
|
1397
|
-
"admin::isAuthenticatedAdmin",
|
|
1398
|
-
{
|
|
1399
|
-
name: "admin::hasPermissions",
|
|
1400
|
-
config: {
|
|
1401
|
-
actions: ["admin::review-workflows.read"]
|
|
1402
|
-
}
|
|
1403
|
-
}
|
|
1404
|
-
]
|
|
1405
|
-
}
|
|
1406
|
-
},
|
|
1407
|
-
{
|
|
1408
|
-
method: "GET",
|
|
1409
|
-
path: "/review-workflows/workflows/:id",
|
|
1410
|
-
handler: "workflows.findById",
|
|
1411
|
-
config: {
|
|
1412
|
-
middlewares: [enableFeatureMiddleware("review-workflows")],
|
|
1413
|
-
policies: [
|
|
1414
|
-
"admin::isAuthenticatedAdmin",
|
|
1415
|
-
{
|
|
1416
|
-
name: "admin::hasPermissions",
|
|
1417
|
-
config: {
|
|
1418
|
-
actions: ["admin::review-workflows.read"]
|
|
1419
|
-
}
|
|
1420
|
-
}
|
|
1421
|
-
]
|
|
1422
|
-
}
|
|
1423
|
-
},
|
|
1424
|
-
{
|
|
1425
|
-
method: "GET",
|
|
1426
|
-
path: "/review-workflows/workflows/:workflow_id/stages",
|
|
1427
|
-
handler: "stages.find",
|
|
1428
|
-
config: {
|
|
1429
|
-
middlewares: [enableFeatureMiddleware("review-workflows")],
|
|
1430
|
-
policies: [
|
|
1431
|
-
"admin::isAuthenticatedAdmin",
|
|
1432
|
-
{
|
|
1433
|
-
name: "admin::hasPermissions",
|
|
1434
|
-
config: {
|
|
1435
|
-
actions: ["admin::review-workflows.read"]
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
]
|
|
1439
|
-
}
|
|
1440
|
-
},
|
|
1441
|
-
{
|
|
1442
|
-
method: "GET",
|
|
1443
|
-
path: "/review-workflows/workflows/:workflow_id/stages/:id",
|
|
1444
|
-
handler: "stages.findById",
|
|
1445
|
-
config: {
|
|
1446
|
-
middlewares: [enableFeatureMiddleware("review-workflows")],
|
|
1447
|
-
policies: [
|
|
1448
|
-
"admin::isAuthenticatedAdmin",
|
|
1449
|
-
{
|
|
1450
|
-
name: "admin::hasPermissions",
|
|
1451
|
-
config: {
|
|
1452
|
-
actions: ["admin::review-workflows.read"]
|
|
1453
|
-
}
|
|
1454
|
-
}
|
|
1455
|
-
]
|
|
1456
|
-
}
|
|
1457
|
-
},
|
|
1458
|
-
{
|
|
1459
|
-
method: "PUT",
|
|
1460
|
-
path: "/content-manager/(collection|single)-types/:model_uid/:id/stage",
|
|
1461
|
-
handler: "stages.updateEntity",
|
|
1462
|
-
config: {
|
|
1463
|
-
middlewares: [enableFeatureMiddleware("review-workflows")],
|
|
1464
|
-
policies: ["admin::isAuthenticatedAdmin"]
|
|
1465
|
-
}
|
|
1466
|
-
},
|
|
1467
|
-
{
|
|
1468
|
-
method: "GET",
|
|
1469
|
-
path: "/content-manager/(collection|single)-types/:model_uid/:id/stages",
|
|
1470
|
-
handler: "stages.listAvailableStages",
|
|
1471
|
-
config: {
|
|
1472
|
-
middlewares: [enableFeatureMiddleware("review-workflows")],
|
|
1473
|
-
policies: ["admin::isAuthenticatedAdmin"]
|
|
1474
|
-
}
|
|
1475
|
-
},
|
|
1476
|
-
{
|
|
1477
|
-
method: "PUT",
|
|
1478
|
-
path: "/content-manager/(collection|single)-types/:model_uid/:id/assignee",
|
|
1479
|
-
handler: "assignees.updateEntity",
|
|
1480
|
-
config: {
|
|
1481
|
-
middlewares: [enableFeatureMiddleware("review-workflows")],
|
|
1482
|
-
policies: [
|
|
1483
|
-
"admin::isAuthenticatedAdmin",
|
|
1484
|
-
{
|
|
1485
|
-
name: "admin::hasPermissions",
|
|
1486
|
-
config: {
|
|
1487
|
-
actions: ["admin::users.read", "admin::review-workflows.read"]
|
|
1488
|
-
}
|
|
1489
|
-
}
|
|
1490
|
-
]
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
|
-
]
|
|
1494
|
-
};
|
|
1495
|
-
const index$2 = {
|
|
1496
|
-
sso: sso$1,
|
|
1497
|
-
"license-limit": licenseLimit,
|
|
1498
|
-
"audit-logs": auditLogs$1,
|
|
1499
|
-
"review-workflows": reviewWorkflows$1
|
|
1500
|
-
};
|
|
1501
|
-
const isSsoLocked = async (user2) => {
|
|
1502
|
-
if (!strapi.ee.features.isEnabled("sso")) {
|
|
1503
|
-
return false;
|
|
1504
|
-
}
|
|
1505
|
-
if (!user2) {
|
|
1506
|
-
throw new Error("Missing user object");
|
|
1507
|
-
}
|
|
1508
|
-
const adminStore = await strapi.store({ type: "core", name: "admin" });
|
|
1509
|
-
const { providers } = await adminStore.get({ key: "auth" });
|
|
1510
|
-
const lockedRoles = providers.ssoLockedRoles ?? [];
|
|
1511
|
-
if (isEmpty(lockedRoles)) {
|
|
1512
|
-
return false;
|
|
1513
|
-
}
|
|
1514
|
-
const roles2 = (
|
|
1515
|
-
// If the roles are pre-loaded for the given user, then use them
|
|
1516
|
-
user2.roles ?? // Otherwise, try to load the role based on the given user ID
|
|
1517
|
-
await strapi.db.query("admin::user").load(user2, "roles", { roles: { fields: ["id"] } }) ?? // If the query fails somehow, default to an empty array
|
|
1518
|
-
[]
|
|
1519
|
-
);
|
|
1520
|
-
const isLocked = lockedRoles.some(
|
|
1521
|
-
(lockedId) => (
|
|
1522
|
-
// lockedRoles will be a string to avoid issues with frontend and bigints
|
|
1523
|
-
roles2.some((role2) => lockedId === role2.id.toString())
|
|
1524
|
-
)
|
|
1525
|
-
);
|
|
1526
|
-
return isLocked;
|
|
1527
|
-
};
|
|
1528
|
-
const { ApplicationError: ApplicationError$6 } = errors;
|
|
1529
|
-
const forgotPassword = async ({ email: email2 } = {}) => {
|
|
1530
|
-
const user2 = await strapi.db.query("admin::user").findOne({ where: { email: email2, isActive: true } });
|
|
1531
|
-
if (!user2 || await isSsoLocked(user2)) {
|
|
1532
|
-
return;
|
|
1533
|
-
}
|
|
1534
|
-
const resetPasswordToken = getService("token").createToken();
|
|
1535
|
-
await getService("user").updateById(user2.id, { resetPasswordToken });
|
|
1536
|
-
const url = `${strapi.config.get(
|
|
1537
|
-
"admin.absoluteUrl"
|
|
1538
|
-
)}/auth/reset-password?code=${resetPasswordToken}`;
|
|
1539
|
-
return strapi.plugin("email").service("email").sendTemplatedEmail(
|
|
970
|
+
const index$2 = {
|
|
971
|
+
sso: sso$1,
|
|
972
|
+
"license-limit": licenseLimit,
|
|
973
|
+
"audit-logs": auditLogs$1
|
|
974
|
+
};
|
|
975
|
+
const isSsoLocked = async (user2) => {
|
|
976
|
+
if (!strapi.ee.features.isEnabled("sso")) {
|
|
977
|
+
return false;
|
|
978
|
+
}
|
|
979
|
+
if (!user2) {
|
|
980
|
+
throw new Error("Missing user object");
|
|
981
|
+
}
|
|
982
|
+
const adminStore = await strapi.store({ type: "core", name: "admin" });
|
|
983
|
+
const { providers } = await adminStore.get({ key: "auth" });
|
|
984
|
+
const lockedRoles = providers.ssoLockedRoles ?? [];
|
|
985
|
+
if (isEmpty(lockedRoles)) {
|
|
986
|
+
return false;
|
|
987
|
+
}
|
|
988
|
+
const roles2 = (
|
|
989
|
+
// If the roles are pre-loaded for the given user, then use them
|
|
990
|
+
user2.roles ?? // Otherwise, try to load the role based on the given user ID
|
|
991
|
+
await strapi.db.query("admin::user").load(user2, "roles", { roles: { fields: ["id"] } }) ?? // If the query fails somehow, default to an empty array
|
|
992
|
+
[]
|
|
993
|
+
);
|
|
994
|
+
const isLocked = lockedRoles.some(
|
|
995
|
+
(lockedId) => (
|
|
996
|
+
// lockedRoles will be a string to avoid issues with frontend and bigints
|
|
997
|
+
roles2.some((role2) => lockedId === role2.id.toString())
|
|
998
|
+
)
|
|
999
|
+
);
|
|
1000
|
+
return isLocked;
|
|
1001
|
+
};
|
|
1002
|
+
const { ApplicationError: ApplicationError$2 } = errors;
|
|
1003
|
+
const forgotPassword = async ({ email: email2 } = {}) => {
|
|
1004
|
+
const user2 = await strapi.db.query("admin::user").findOne({ where: { email: email2, isActive: true } });
|
|
1005
|
+
if (!user2 || await isSsoLocked(user2)) {
|
|
1006
|
+
return;
|
|
1007
|
+
}
|
|
1008
|
+
const resetPasswordToken = getService("token").createToken();
|
|
1009
|
+
await getService("user").updateById(user2.id, { resetPasswordToken });
|
|
1010
|
+
const url = `${strapi.config.get(
|
|
1011
|
+
"admin.absoluteUrl"
|
|
1012
|
+
)}/auth/reset-password?code=${resetPasswordToken}`;
|
|
1013
|
+
return strapi.plugin("email").service("email").sendTemplatedEmail(
|
|
1540
1014
|
{
|
|
1541
1015
|
to: user2.email,
|
|
1542
1016
|
from: strapi.config.get("admin.forgotPassword.from"),
|
|
@@ -1554,7 +1028,7 @@ const forgotPassword = async ({ email: email2 } = {}) => {
|
|
|
1554
1028
|
const resetPassword = async ({ resetPasswordToken, password: password2 } = {}) => {
|
|
1555
1029
|
const matchingUser = await strapi.db.query("admin::user").findOne({ where: { resetPasswordToken, isActive: true } });
|
|
1556
1030
|
if (!matchingUser || await isSsoLocked(matchingUser)) {
|
|
1557
|
-
throw new ApplicationError$
|
|
1031
|
+
throw new ApplicationError$2();
|
|
1558
1032
|
}
|
|
1559
1033
|
return getService("user").updateById(matchingUser.id, {
|
|
1560
1034
|
password: password2,
|
|
@@ -1678,7 +1152,7 @@ const passport = {
|
|
|
1678
1152
|
getPassportStrategies,
|
|
1679
1153
|
...sso
|
|
1680
1154
|
};
|
|
1681
|
-
const { ApplicationError: ApplicationError$
|
|
1155
|
+
const { ApplicationError: ApplicationError$1 } = errors;
|
|
1682
1156
|
const ssoCheckRolesIdForDeletion = async (ids) => {
|
|
1683
1157
|
const adminStore = await strapi.store({ type: "core", name: "admin" });
|
|
1684
1158
|
const {
|
|
@@ -1686,7 +1160,7 @@ const ssoCheckRolesIdForDeletion = async (ids) => {
|
|
|
1686
1160
|
} = await adminStore.get({ key: "auth" });
|
|
1687
1161
|
for (const roleId of ids) {
|
|
1688
1162
|
if (defaultRole && toString(defaultRole) === toString(roleId)) {
|
|
1689
|
-
throw new ApplicationError$
|
|
1163
|
+
throw new ApplicationError$1(
|
|
1690
1164
|
"This role is used as the default SSO role. Make sure to change this configuration before deleting the role"
|
|
1691
1165
|
);
|
|
1692
1166
|
}
|
|
@@ -1699,7 +1173,7 @@ const { SUPER_ADMIN_CODE: SUPER_ADMIN_CODE$2 } = constants;
|
|
|
1699
1173
|
const hasSuperAdminRole = (user2) => {
|
|
1700
1174
|
return user2.roles.filter((role2) => role2.code === SUPER_ADMIN_CODE$2).length > 0;
|
|
1701
1175
|
};
|
|
1702
|
-
const { ValidationError: ValidationError$
|
|
1176
|
+
const { ValidationError: ValidationError$1 } = errors;
|
|
1703
1177
|
const { SUPER_ADMIN_CODE: SUPER_ADMIN_CODE$1 } = constants;
|
|
1704
1178
|
const updateEEDisabledUsersList = async (id, input) => {
|
|
1705
1179
|
const disabledUsers = await getService("seat-enforcement").getDisabledUserList();
|
|
@@ -1744,13 +1218,13 @@ const updateById = async (id, attributes) => {
|
|
|
1744
1218
|
const superAdminRole = await getService("role").getSuperAdminWithUsersCount();
|
|
1745
1219
|
const willRemoveSuperAdminRole = !arrays.includesString(attributes.roles, superAdminRole.id);
|
|
1746
1220
|
if (lastAdminUser && willRemoveSuperAdminRole) {
|
|
1747
|
-
throw new ValidationError$
|
|
1221
|
+
throw new ValidationError$1("You must have at least one user with super admin role.");
|
|
1748
1222
|
}
|
|
1749
1223
|
}
|
|
1750
1224
|
if (attributes.isActive === false) {
|
|
1751
1225
|
const lastAdminUser = await isLastSuperAdminUser(id);
|
|
1752
1226
|
if (lastAdminUser) {
|
|
1753
|
-
throw new ValidationError$
|
|
1227
|
+
throw new ValidationError$1("You must have at least one user with super admin role.");
|
|
1754
1228
|
}
|
|
1755
1229
|
}
|
|
1756
1230
|
if (_.has(attributes, "password")) {
|
|
@@ -1789,7 +1263,7 @@ const deleteById = async (id) => {
|
|
|
1789
1263
|
if (userToDelete.roles.some((r) => r.code === SUPER_ADMIN_CODE$1)) {
|
|
1790
1264
|
const superAdminRole = await getService("role").getSuperAdminWithUsersCount();
|
|
1791
1265
|
if (superAdminRole.usersCount === 1) {
|
|
1792
|
-
throw new ValidationError$
|
|
1266
|
+
throw new ValidationError$1("You must have at least one user with super admin role.");
|
|
1793
1267
|
}
|
|
1794
1268
|
}
|
|
1795
1269
|
}
|
|
@@ -1807,7 +1281,7 @@ const deleteByIds = async (ids) => {
|
|
|
1807
1281
|
}
|
|
1808
1282
|
});
|
|
1809
1283
|
if (superAdminRole.usersCount === nbOfSuperAdminToDelete) {
|
|
1810
|
-
throw new ValidationError$
|
|
1284
|
+
throw new ValidationError$1("You must have at least one user with super admin role.");
|
|
1811
1285
|
}
|
|
1812
1286
|
const deletedUsers = [];
|
|
1813
1287
|
for (const id of ids) {
|
|
@@ -1967,951 +1441,13 @@ const seatEnforcement = {
|
|
|
1967
1441
|
seatEnforcementWorkflow,
|
|
1968
1442
|
getDisabledUserList
|
|
1969
1443
|
};
|
|
1970
|
-
const workflowsContentTypesFactory = ({ strapi: strapi2 }) => {
|
|
1971
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
1972
|
-
const stagesService = getService("stages", { strapi: strapi2 });
|
|
1973
|
-
const updateContentTypeConfig = async (uid, reviewWorkflowOption) => {
|
|
1974
|
-
const modelConfig = await contentManagerContentTypeService.findConfiguration(uid);
|
|
1975
|
-
await contentManagerContentTypeService.updateConfiguration(
|
|
1976
|
-
{ uid },
|
|
1977
|
-
{ options: merge(modelConfig.options, { reviewWorkflows: reviewWorkflowOption }) }
|
|
1978
|
-
);
|
|
1979
|
-
};
|
|
1980
|
-
return {
|
|
1981
|
-
/**
|
|
1982
|
-
* Migrates entities stages. Used when a content type is assigned to a workflow.
|
|
1983
|
-
* @param {*} options
|
|
1984
|
-
* @param {Array<string>} options.srcContentTypes - The content types assigned to the previous workflow
|
|
1985
|
-
* @param {Array<string>} options.destContentTypes - The content types assigned to the new workflow
|
|
1986
|
-
* @param {Workflow.Stage} options.stageId - The new stage to assign the entities to
|
|
1987
|
-
*/
|
|
1988
|
-
async migrate({ srcContentTypes = [], destContentTypes, stageId }) {
|
|
1989
|
-
const workflowsService = getService("workflows", { strapi: strapi2 });
|
|
1990
|
-
const { created, deleted } = diffContentTypes(srcContentTypes, destContentTypes);
|
|
1991
|
-
await async.map(
|
|
1992
|
-
created,
|
|
1993
|
-
async (uid) => {
|
|
1994
|
-
const srcWorkflows = await workflowsService._getAssignedWorkflows(uid, {});
|
|
1995
|
-
if (srcWorkflows.length) {
|
|
1996
|
-
await stagesService.updateEntitiesStage(uid, { toStageId: stageId });
|
|
1997
|
-
await async.map(
|
|
1998
|
-
srcWorkflows,
|
|
1999
|
-
(srcWorkflow) => this.transferContentTypes(srcWorkflow, uid)
|
|
2000
|
-
);
|
|
2001
|
-
}
|
|
2002
|
-
await updateContentTypeConfig(uid, true);
|
|
2003
|
-
return stagesService.updateEntitiesStage(uid, {
|
|
2004
|
-
fromStageId: null,
|
|
2005
|
-
toStageId: stageId
|
|
2006
|
-
});
|
|
2007
|
-
},
|
|
2008
|
-
// transferContentTypes can cause race conditions if called in parallel when updating the same workflow
|
|
2009
|
-
{ concurrency: 1 }
|
|
2010
|
-
);
|
|
2011
|
-
await async.map(deleted, async (uid) => {
|
|
2012
|
-
await updateContentTypeConfig(uid, false);
|
|
2013
|
-
await stagesService.deleteAllEntitiesStage(uid, {});
|
|
2014
|
-
});
|
|
2015
|
-
},
|
|
2016
|
-
/**
|
|
2017
|
-
* Filters the content types assigned to a workflow
|
|
2018
|
-
* @param {Workflow} srcWorkflow - The workflow to transfer from
|
|
2019
|
-
* @param {string} uid - The content type uid
|
|
2020
|
-
*/
|
|
2021
|
-
async transferContentTypes(srcWorkflow, uid) {
|
|
2022
|
-
await strapi2.db.query(WORKFLOW_MODEL_UID).update({
|
|
2023
|
-
where: {
|
|
2024
|
-
id: srcWorkflow.id
|
|
2025
|
-
},
|
|
2026
|
-
data: {
|
|
2027
|
-
contentTypes: srcWorkflow.contentTypes.filter((contentType) => contentType !== uid)
|
|
2028
|
-
}
|
|
2029
|
-
});
|
|
2030
|
-
}
|
|
2031
|
-
};
|
|
2032
|
-
};
|
|
2033
|
-
const diffContentTypes = (srcContentTypes, destContentTypes) => {
|
|
2034
|
-
const created = difference(destContentTypes, srcContentTypes);
|
|
2035
|
-
const deleted = difference(srcContentTypes, destContentTypes);
|
|
2036
|
-
return { created, deleted };
|
|
2037
|
-
};
|
|
2038
|
-
const { ApplicationError: ApplicationError$4 } = errors;
|
|
2039
|
-
const processFilters = ({ strapi: strapi2 }, filters = {}) => {
|
|
2040
|
-
const processedFilters = { ...filters };
|
|
2041
|
-
if (isString(filters.contentTypes)) {
|
|
2042
|
-
processedFilters.contentTypes = getWorkflowContentTypeFilter({ strapi: strapi2 }, filters.contentTypes);
|
|
2043
|
-
}
|
|
2044
|
-
return processedFilters;
|
|
2045
|
-
};
|
|
2046
|
-
const processPopulate = (populate) => {
|
|
2047
|
-
if (!populate) {
|
|
2048
|
-
return populate;
|
|
2049
|
-
}
|
|
2050
|
-
return WORKFLOW_POPULATE;
|
|
2051
|
-
};
|
|
2052
|
-
const workflows$1 = ({ strapi: strapi2 }) => {
|
|
2053
|
-
const workflowsContentTypes = workflowsContentTypesFactory({ strapi: strapi2 });
|
|
2054
|
-
const workflowsValidationService = getService("review-workflows-validation", { strapi: strapi2 });
|
|
2055
|
-
const metrics2 = getService("review-workflows-metrics", { strapi: strapi2 });
|
|
2056
|
-
return {
|
|
2057
|
-
/**
|
|
2058
|
-
* Returns all the workflows matching the user-defined filters.
|
|
2059
|
-
* @param {object} opts - Options for the query.
|
|
2060
|
-
* @param {object} opts.filters - Filters object.
|
|
2061
|
-
* @returns {Promise<object[]>} - List of workflows that match the user's filters.
|
|
2062
|
-
*/
|
|
2063
|
-
async find(opts = {}) {
|
|
2064
|
-
const filters = processFilters({ strapi: strapi2 }, opts.filters);
|
|
2065
|
-
const populate = processPopulate(opts.populate);
|
|
2066
|
-
const query = strapi2.get("query-params").transform(WORKFLOW_MODEL_UID, {
|
|
2067
|
-
...opts,
|
|
2068
|
-
filters,
|
|
2069
|
-
populate
|
|
2070
|
-
});
|
|
2071
|
-
return strapi2.db.query(WORKFLOW_MODEL_UID).findMany(query);
|
|
2072
|
-
},
|
|
2073
|
-
/**
|
|
2074
|
-
* Returns the workflow with the specified ID.
|
|
2075
|
-
* @param {string} id - ID of the requested workflow.
|
|
2076
|
-
* @param {object} opts - Options for the query.
|
|
2077
|
-
* @returns {Promise<object>} - Workflow object matching the requested ID.
|
|
2078
|
-
*/
|
|
2079
|
-
findById(id, opts = {}) {
|
|
2080
|
-
const populate = processPopulate(opts.populate);
|
|
2081
|
-
const query = strapi2.get("query-params").transform(WORKFLOW_MODEL_UID, { populate });
|
|
2082
|
-
return strapi2.db.query(WORKFLOW_MODEL_UID).findOne({
|
|
2083
|
-
...query,
|
|
2084
|
-
where: { id }
|
|
2085
|
-
});
|
|
2086
|
-
},
|
|
2087
|
-
/**
|
|
2088
|
-
* Creates a new workflow.
|
|
2089
|
-
* @param {object} opts - Options for creating the new workflow.
|
|
2090
|
-
* @returns {Promise<object>} - Workflow object that was just created.
|
|
2091
|
-
* @throws {ValidationError} - If the workflow has no stages.
|
|
2092
|
-
*/
|
|
2093
|
-
async create(opts) {
|
|
2094
|
-
let createOpts = { ...opts, populate: WORKFLOW_POPULATE };
|
|
2095
|
-
workflowsValidationService.validateWorkflowStages(opts.data.stages);
|
|
2096
|
-
await workflowsValidationService.validateWorkflowCount(1);
|
|
2097
|
-
return strapi2.db.transaction(async () => {
|
|
2098
|
-
const stages2 = await getService("stages", { strapi: strapi2 }).createMany(opts.data.stages);
|
|
2099
|
-
const mapIds = map(get("id"));
|
|
2100
|
-
createOpts = set("data.stages", mapIds(stages2), createOpts);
|
|
2101
|
-
if (opts.data.contentTypes) {
|
|
2102
|
-
await workflowsContentTypes.migrate({
|
|
2103
|
-
destContentTypes: opts.data.contentTypes,
|
|
2104
|
-
stageId: stages2[0].id
|
|
2105
|
-
});
|
|
2106
|
-
}
|
|
2107
|
-
metrics2.sendDidCreateWorkflow();
|
|
2108
|
-
return strapi2.db.query(WORKFLOW_MODEL_UID).create(strapi2.get("query-params").transform(WORKFLOW_MODEL_UID, createOpts));
|
|
2109
|
-
});
|
|
2110
|
-
},
|
|
2111
|
-
/**
|
|
2112
|
-
* Updates an existing workflow.
|
|
2113
|
-
* @param {object} workflow - The existing workflow to update.
|
|
2114
|
-
* @param {object} opts - Options for updating the workflow.
|
|
2115
|
-
* @returns {Promise<object>} - Workflow object that was just updated.
|
|
2116
|
-
* @throws {ApplicationError} - If the supplied stage ID does not belong to the workflow.
|
|
2117
|
-
*/
|
|
2118
|
-
async update(workflow2, opts) {
|
|
2119
|
-
const stageService = getService("stages", { strapi: strapi2 });
|
|
2120
|
-
let updateOpts = { ...opts, populate: { ...WORKFLOW_POPULATE } };
|
|
2121
|
-
let updatedStageIds;
|
|
2122
|
-
await workflowsValidationService.validateWorkflowCount();
|
|
2123
|
-
return strapi2.db.transaction(async () => {
|
|
2124
|
-
if (opts.data.stages) {
|
|
2125
|
-
workflowsValidationService.validateWorkflowStages(opts.data.stages);
|
|
2126
|
-
opts.data.stages.forEach(
|
|
2127
|
-
(stage) => this.assertStageBelongsToWorkflow(stage.id, workflow2)
|
|
2128
|
-
);
|
|
2129
|
-
updatedStageIds = await stageService.replaceStages(workflow2.stages, opts.data.stages, workflow2.contentTypes).then((stages2) => stages2.map((stage) => stage.id));
|
|
2130
|
-
updateOpts = set("data.stages", updatedStageIds, updateOpts);
|
|
2131
|
-
}
|
|
2132
|
-
if (opts.data.contentTypes) {
|
|
2133
|
-
await workflowsContentTypes.migrate({
|
|
2134
|
-
srcContentTypes: workflow2.contentTypes,
|
|
2135
|
-
destContentTypes: opts.data.contentTypes,
|
|
2136
|
-
stageId: updatedStageIds ? updatedStageIds[0] : workflow2.stages[0].id
|
|
2137
|
-
});
|
|
2138
|
-
}
|
|
2139
|
-
metrics2.sendDidEditWorkflow();
|
|
2140
|
-
const query = strapi2.get("query-params").transform(WORKFLOW_MODEL_UID, updateOpts);
|
|
2141
|
-
return strapi2.db.query(WORKFLOW_MODEL_UID).update({
|
|
2142
|
-
...query,
|
|
2143
|
-
where: { id: workflow2.id }
|
|
2144
|
-
});
|
|
2145
|
-
});
|
|
2146
|
-
},
|
|
2147
|
-
/**
|
|
2148
|
-
* Deletes an existing workflow.
|
|
2149
|
-
* Also deletes all the workflow stages and migrate all assigned the content types.
|
|
2150
|
-
* @param {*} workflow
|
|
2151
|
-
* @param {*} opts
|
|
2152
|
-
* @returns
|
|
2153
|
-
*/
|
|
2154
|
-
async delete(workflow2, opts) {
|
|
2155
|
-
const stageService = getService("stages", { strapi: strapi2 });
|
|
2156
|
-
const workflowCount = await this.count();
|
|
2157
|
-
if (workflowCount <= 1) {
|
|
2158
|
-
throw new ApplicationError$4("Can not delete the last workflow");
|
|
2159
|
-
}
|
|
2160
|
-
return strapi2.db.transaction(async () => {
|
|
2161
|
-
await stageService.deleteMany(workflow2.stages);
|
|
2162
|
-
await workflowsContentTypes.migrate({
|
|
2163
|
-
srcContentTypes: workflow2.contentTypes,
|
|
2164
|
-
destContentTypes: []
|
|
2165
|
-
});
|
|
2166
|
-
const query = strapi2.get("query-params").transform(WORKFLOW_MODEL_UID, opts);
|
|
2167
|
-
return strapi2.db.query(WORKFLOW_MODEL_UID).delete({
|
|
2168
|
-
...query,
|
|
2169
|
-
where: { id: workflow2.id }
|
|
2170
|
-
});
|
|
2171
|
-
});
|
|
2172
|
-
},
|
|
2173
|
-
/**
|
|
2174
|
-
* Returns the total count of workflows.
|
|
2175
|
-
* @returns {Promise<number>} - Total count of workflows.
|
|
2176
|
-
*/
|
|
2177
|
-
count() {
|
|
2178
|
-
return strapi2.db.query(WORKFLOW_MODEL_UID).count();
|
|
2179
|
-
},
|
|
2180
|
-
/**
|
|
2181
|
-
* Finds the assigned workflow for a given content type ID.
|
|
2182
|
-
* @param {string} uid - Content type ID to find the assigned workflow for.
|
|
2183
|
-
* @param {object} opts - Options for the query.
|
|
2184
|
-
* @returns {Promise<object|null>} - Assigned workflow object if found, or null.
|
|
2185
|
-
*/
|
|
2186
|
-
async getAssignedWorkflow(uid, opts = {}) {
|
|
2187
|
-
const workflows2 = await this._getAssignedWorkflows(uid, opts);
|
|
2188
|
-
return workflows2.length > 0 ? workflows2[0] : null;
|
|
2189
|
-
},
|
|
2190
|
-
/**
|
|
2191
|
-
* Finds all the assigned workflows for a given content type ID.
|
|
2192
|
-
* Normally, there should only be one workflow assigned to a content type.
|
|
2193
|
-
* However, edge cases can occur where a content type is assigned to multiple workflows.
|
|
2194
|
-
* @param {string} uid - Content type ID to find the assigned workflows for.
|
|
2195
|
-
* @param {object} opts - Options for the query.
|
|
2196
|
-
* @returns {Promise<object[]>} - List of assigned workflow objects.
|
|
2197
|
-
*/
|
|
2198
|
-
async _getAssignedWorkflows(uid, opts = {}) {
|
|
2199
|
-
return this.find({
|
|
2200
|
-
...opts,
|
|
2201
|
-
filters: { contentTypes: getWorkflowContentTypeFilter({ strapi: strapi2 }, uid) }
|
|
2202
|
-
});
|
|
2203
|
-
},
|
|
2204
|
-
/**
|
|
2205
|
-
* Asserts that a content type has an assigned workflow.
|
|
2206
|
-
* @param {string} uid - Content type ID to verify the assignment of.
|
|
2207
|
-
* @returns {Promise<object>} - Workflow object associated with the content type ID.
|
|
2208
|
-
* @throws {ApplicationError} - If no assigned workflow is found for the content type ID.
|
|
2209
|
-
*/
|
|
2210
|
-
async assertContentTypeBelongsToWorkflow(uid) {
|
|
2211
|
-
const workflow2 = await this.getAssignedWorkflow(uid, {
|
|
2212
|
-
populate: "stages"
|
|
2213
|
-
});
|
|
2214
|
-
if (!workflow2) {
|
|
2215
|
-
throw new ApplicationError$4(`Review workflows is not activated on Content Type ${uid}.`);
|
|
2216
|
-
}
|
|
2217
|
-
return workflow2;
|
|
2218
|
-
},
|
|
2219
|
-
/**
|
|
2220
|
-
* Asserts that a stage belongs to a given workflow.
|
|
2221
|
-
* @param {string} stageId - ID of stage to check.
|
|
2222
|
-
* @param {object} workflow - Workflow object to check against.
|
|
2223
|
-
* @returns
|
|
2224
|
-
* @throws {ApplicationError} - If the stage does not belong to the specified workflow.
|
|
2225
|
-
*/
|
|
2226
|
-
assertStageBelongsToWorkflow(stageId, workflow2) {
|
|
2227
|
-
if (!stageId) {
|
|
2228
|
-
return;
|
|
2229
|
-
}
|
|
2230
|
-
const belongs = workflow2.stages.some((stage) => stage.id === stageId);
|
|
2231
|
-
if (!belongs) {
|
|
2232
|
-
throw new ApplicationError$4(`Stage does not belong to workflow "${workflow2.name}"`);
|
|
2233
|
-
}
|
|
2234
|
-
}
|
|
2235
|
-
};
|
|
2236
|
-
};
|
|
2237
|
-
const { ApplicationError: ApplicationError$3, ValidationError: ValidationError$2 } = errors;
|
|
2238
|
-
const sanitizedStageFields = ["id", "name", "workflow", "color"];
|
|
2239
|
-
const sanitizeStageFields = pick(sanitizedStageFields);
|
|
2240
|
-
const stages$1 = ({ strapi: strapi2 }) => {
|
|
2241
|
-
const metrics2 = getService("review-workflows-metrics", { strapi: strapi2 });
|
|
2242
|
-
const stagePermissionsService = getService("stage-permissions", { strapi: strapi2 });
|
|
2243
|
-
const workflowsValidationService = getService("review-workflows-validation", { strapi: strapi2 });
|
|
2244
|
-
return {
|
|
2245
|
-
find({ workflowId, populate }) {
|
|
2246
|
-
return strapi2.db.query(STAGE_MODEL_UID).findMany({
|
|
2247
|
-
where: { workflow: workflowId },
|
|
2248
|
-
populate
|
|
2249
|
-
});
|
|
2250
|
-
},
|
|
2251
|
-
findById(id, { populate } = {}) {
|
|
2252
|
-
return strapi2.db.query(STAGE_MODEL_UID).findOne({
|
|
2253
|
-
where: { id },
|
|
2254
|
-
populate
|
|
2255
|
-
});
|
|
2256
|
-
},
|
|
2257
|
-
async createMany(stagesList, { fields } = {}) {
|
|
2258
|
-
const params = { select: fields ?? "*" };
|
|
2259
|
-
const stages2 = await Promise.all(
|
|
2260
|
-
stagesList.map(
|
|
2261
|
-
(stage) => strapi2.db.query(STAGE_MODEL_UID).create({
|
|
2262
|
-
data: sanitizeStageFields(stage),
|
|
2263
|
-
...params
|
|
2264
|
-
})
|
|
2265
|
-
)
|
|
2266
|
-
);
|
|
2267
|
-
await async.reduce(stagesList)(async (_2, stage, idx) => {
|
|
2268
|
-
if (!stage.permissions || stage.permissions.length === 0) {
|
|
2269
|
-
return;
|
|
2270
|
-
}
|
|
2271
|
-
const stagePermissions2 = stage.permissions;
|
|
2272
|
-
const stageId = stages2[idx].id;
|
|
2273
|
-
const permissions = await async.map(
|
|
2274
|
-
stagePermissions2,
|
|
2275
|
-
// Register each stage permission
|
|
2276
|
-
(permission2) => stagePermissionsService.register({
|
|
2277
|
-
roleId: permission2.role,
|
|
2278
|
-
action: permission2.action,
|
|
2279
|
-
fromStage: stageId
|
|
2280
|
-
})
|
|
2281
|
-
);
|
|
2282
|
-
await strapi2.db.query(STAGE_MODEL_UID).update({
|
|
2283
|
-
where: { id: stageId },
|
|
2284
|
-
data: {
|
|
2285
|
-
permissions: permissions.flat().map((p) => p.id)
|
|
2286
|
-
}
|
|
2287
|
-
});
|
|
2288
|
-
}, []);
|
|
2289
|
-
metrics2.sendDidCreateStage();
|
|
2290
|
-
return stages2;
|
|
2291
|
-
},
|
|
2292
|
-
async update(srcStage, destStage) {
|
|
2293
|
-
let stagePermissions2 = srcStage?.permissions ?? [];
|
|
2294
|
-
const stageId = destStage.id;
|
|
2295
|
-
if (destStage.permissions) {
|
|
2296
|
-
await this.deleteStagePermissions([srcStage]);
|
|
2297
|
-
const permissions = await async.map(
|
|
2298
|
-
destStage.permissions,
|
|
2299
|
-
(permission2) => stagePermissionsService.register({
|
|
2300
|
-
roleId: permission2.role,
|
|
2301
|
-
action: permission2.action,
|
|
2302
|
-
fromStage: stageId
|
|
2303
|
-
})
|
|
2304
|
-
);
|
|
2305
|
-
stagePermissions2 = permissions.flat().map((p) => p.id);
|
|
2306
|
-
}
|
|
2307
|
-
const stage = await strapi2.db.query(STAGE_MODEL_UID).update({
|
|
2308
|
-
where: { id: stageId },
|
|
2309
|
-
data: {
|
|
2310
|
-
...destStage,
|
|
2311
|
-
permissions: stagePermissions2
|
|
2312
|
-
}
|
|
2313
|
-
});
|
|
2314
|
-
metrics2.sendDidEditStage();
|
|
2315
|
-
return stage;
|
|
2316
|
-
},
|
|
2317
|
-
async delete(stage) {
|
|
2318
|
-
await this.deleteStagePermissions([stage]);
|
|
2319
|
-
const deletedStage = await strapi2.db.query(STAGE_MODEL_UID).delete({
|
|
2320
|
-
where: { id: stage.id }
|
|
2321
|
-
});
|
|
2322
|
-
metrics2.sendDidDeleteStage();
|
|
2323
|
-
return deletedStage;
|
|
2324
|
-
},
|
|
2325
|
-
async deleteMany(stages2) {
|
|
2326
|
-
await this.deleteStagePermissions(stages2);
|
|
2327
|
-
return strapi2.db.query(STAGE_MODEL_UID).deleteMany({
|
|
2328
|
-
where: { id: { $in: stages2.map((s) => s.id) } }
|
|
2329
|
-
});
|
|
2330
|
-
},
|
|
2331
|
-
async deleteStagePermissions(stages2) {
|
|
2332
|
-
const permissions = stages2.map((s) => s.permissions || []).flat();
|
|
2333
|
-
await stagePermissionsService.unregister(permissions || []);
|
|
2334
|
-
},
|
|
2335
|
-
count({ workflowId } = {}) {
|
|
2336
|
-
const opts = {};
|
|
2337
|
-
if (workflowId) {
|
|
2338
|
-
opts.where = {
|
|
2339
|
-
workflow: workflowId
|
|
2340
|
-
};
|
|
2341
|
-
}
|
|
2342
|
-
return strapi2.db.query(STAGE_MODEL_UID).count(opts);
|
|
2343
|
-
},
|
|
2344
|
-
async replaceStages(srcStages, destStages, contentTypesToMigrate = []) {
|
|
2345
|
-
const { created, updated, deleted } = getDiffBetweenStages(srcStages, destStages);
|
|
2346
|
-
assertAtLeastOneStageRemain(srcStages || [], { created, deleted });
|
|
2347
|
-
return strapi2.db.transaction(async ({ trx }) => {
|
|
2348
|
-
const createdStages = await this.createMany(created, { fields: ["id"] });
|
|
2349
|
-
const createdStagesIds = map("id", createdStages);
|
|
2350
|
-
await async.map(updated, (destStage) => {
|
|
2351
|
-
const srcStage = srcStages.find((s) => s.id === destStage.id);
|
|
2352
|
-
return this.update(srcStage, destStage);
|
|
2353
|
-
});
|
|
2354
|
-
await async.map(deleted, async (stage) => {
|
|
2355
|
-
const nearestStage = findNearestMatchingStage(
|
|
2356
|
-
[...srcStages, ...createdStages],
|
|
2357
|
-
srcStages.findIndex((s) => s.id === stage.id),
|
|
2358
|
-
(targetStage) => {
|
|
2359
|
-
return !deleted.find((s) => s.id === targetStage.id);
|
|
2360
|
-
}
|
|
2361
|
-
);
|
|
2362
|
-
await async.map(contentTypesToMigrate, (contentTypeUID) => {
|
|
2363
|
-
this.updateEntitiesStage(contentTypeUID, {
|
|
2364
|
-
fromStageId: stage.id,
|
|
2365
|
-
toStageId: nearestStage.id,
|
|
2366
|
-
trx
|
|
2367
|
-
});
|
|
2368
|
-
});
|
|
2369
|
-
return this.delete(stage);
|
|
2370
|
-
});
|
|
2371
|
-
return destStages.map((stage) => ({
|
|
2372
|
-
...stage,
|
|
2373
|
-
id: stage.id ?? createdStagesIds.shift()
|
|
2374
|
-
}));
|
|
2375
|
-
});
|
|
2376
|
-
},
|
|
2377
|
-
/**
|
|
2378
|
-
* Update the stage of an entity
|
|
2379
|
-
*
|
|
2380
|
-
* @param {object} entityInfo
|
|
2381
|
-
* @param {number} entityInfo.id - Entity id
|
|
2382
|
-
* @param {string} entityInfo.modelUID - the content-type of the entity
|
|
2383
|
-
* @param {number} stageId - The id of the stage to assign to the entity
|
|
2384
|
-
*/
|
|
2385
|
-
async updateEntity(entityInfo, stageId) {
|
|
2386
|
-
const stage = await this.findById(stageId);
|
|
2387
|
-
await workflowsValidationService.validateWorkflowCount();
|
|
2388
|
-
if (!stage) {
|
|
2389
|
-
throw new ApplicationError$3(`Selected stage does not exist`);
|
|
2390
|
-
}
|
|
2391
|
-
const entity = await strapi2.db.query(entityInfo.modelUID).update({
|
|
2392
|
-
where: {
|
|
2393
|
-
id: entityInfo.id
|
|
2394
|
-
},
|
|
2395
|
-
data: { [ENTITY_STAGE_ATTRIBUTE]: stageId },
|
|
2396
|
-
populate: [ENTITY_STAGE_ATTRIBUTE]
|
|
2397
|
-
});
|
|
2398
|
-
metrics2.sendDidChangeEntryStage();
|
|
2399
|
-
return entity;
|
|
2400
|
-
},
|
|
2401
|
-
/**
|
|
2402
|
-
* Updates entity stages of a content type:
|
|
2403
|
-
* - If fromStageId is undefined, all entities with an existing stage will be assigned the new stage
|
|
2404
|
-
* - If fromStageId is null, all entities without a stage will be assigned the new stage
|
|
2405
|
-
* - If fromStageId is a number, all entities with that stage will be assigned the new stage
|
|
2406
|
-
*
|
|
2407
|
-
* For performance reasons we use knex queries directly.
|
|
2408
|
-
*
|
|
2409
|
-
* @param {string} contentTypeUID
|
|
2410
|
-
* @param {number | undefined | null} fromStageId
|
|
2411
|
-
* @param {number} toStageId
|
|
2412
|
-
* @param {import('knex').Knex.Transaction} trx
|
|
2413
|
-
* @returns
|
|
2414
|
-
*/
|
|
2415
|
-
async updateEntitiesStage(contentTypeUID, { fromStageId, toStageId }) {
|
|
2416
|
-
const { attributes, tableName } = strapi2.db.metadata.get(contentTypeUID);
|
|
2417
|
-
const joinTable = attributes[ENTITY_STAGE_ATTRIBUTE].joinTable;
|
|
2418
|
-
const joinColumn = joinTable.joinColumn.name;
|
|
2419
|
-
const invJoinColumn = joinTable.inverseJoinColumn.name;
|
|
2420
|
-
await workflowsValidationService.validateWorkflowCount();
|
|
2421
|
-
return strapi2.db.transaction(async ({ trx }) => {
|
|
2422
|
-
if (fromStageId === void 0) {
|
|
2423
|
-
return strapi2.db.getConnection().from(joinTable.name).update({ [invJoinColumn]: toStageId }).transacting(trx);
|
|
2424
|
-
}
|
|
2425
|
-
const selectStatement = strapi2.db.getConnection().select({ [joinColumn]: "t1.id", [invJoinColumn]: toStageId }).from(`${tableName} as t1`).leftJoin(`${joinTable.name} as t2`, `t1.id`, `t2.${joinColumn}`).where(`t2.${invJoinColumn}`, fromStageId).toSQL();
|
|
2426
|
-
return strapi2.db.getConnection(joinTable.name).insert(
|
|
2427
|
-
strapi2.db.connection.raw(
|
|
2428
|
-
`(${joinColumn}, ${invJoinColumn}) ${selectStatement.sql}`,
|
|
2429
|
-
selectStatement.bindings
|
|
2430
|
-
)
|
|
2431
|
-
).transacting(trx);
|
|
2432
|
-
});
|
|
2433
|
-
},
|
|
2434
|
-
/**
|
|
2435
|
-
* Deletes all entity stages of a content type
|
|
2436
|
-
* @param {string} contentTypeUID
|
|
2437
|
-
* @returns
|
|
2438
|
-
*/
|
|
2439
|
-
async deleteAllEntitiesStage(contentTypeUID) {
|
|
2440
|
-
const { attributes } = strapi2.db.metadata.get(contentTypeUID);
|
|
2441
|
-
const joinTable = attributes[ENTITY_STAGE_ATTRIBUTE].joinTable;
|
|
2442
|
-
return strapi2.db.transaction(
|
|
2443
|
-
async ({ trx }) => strapi2.db.getConnection().from(joinTable.name).delete().transacting(trx)
|
|
2444
|
-
);
|
|
2445
|
-
}
|
|
2446
|
-
};
|
|
2447
|
-
};
|
|
2448
|
-
function getDiffBetweenStages(sourceStages, comparisonStages) {
|
|
2449
|
-
const result = comparisonStages.reduce(
|
|
2450
|
-
// ...
|
|
2451
|
-
(acc, stageToCompare) => {
|
|
2452
|
-
const srcStage = sourceStages.find((stage) => stage.id === stageToCompare.id);
|
|
2453
|
-
if (!srcStage) {
|
|
2454
|
-
acc.created.push(stageToCompare);
|
|
2455
|
-
} else if (!isEqual(
|
|
2456
|
-
pick(["name", "color", "permissions"], srcStage),
|
|
2457
|
-
pick(["name", "color", "permissions"], stageToCompare)
|
|
2458
|
-
)) {
|
|
2459
|
-
acc.updated.push(stageToCompare);
|
|
2460
|
-
}
|
|
2461
|
-
return acc;
|
|
2462
|
-
},
|
|
2463
|
-
{ created: [], updated: [] }
|
|
2464
|
-
);
|
|
2465
|
-
result.deleted = sourceStages.filter(
|
|
2466
|
-
(srcStage) => !comparisonStages.some((cmpStage) => cmpStage.id === srcStage.id)
|
|
2467
|
-
);
|
|
2468
|
-
return result;
|
|
2469
|
-
}
|
|
2470
|
-
function assertAtLeastOneStageRemain(workflowStages, diffStages) {
|
|
2471
|
-
const remainingStagesCount = workflowStages.length - diffStages.deleted.length + diffStages.created.length;
|
|
2472
|
-
if (remainingStagesCount < 1) {
|
|
2473
|
-
throw new ValidationError$2(ERRORS.WORKFLOW_WITHOUT_STAGES);
|
|
2474
|
-
}
|
|
2475
|
-
}
|
|
2476
|
-
function findNearestMatchingStage(stages2, startIndex, condition) {
|
|
2477
|
-
for (let i = startIndex; i >= 0; i -= 1) {
|
|
2478
|
-
if (condition(stages2[i])) {
|
|
2479
|
-
return stages2[i];
|
|
2480
|
-
}
|
|
2481
|
-
}
|
|
2482
|
-
const remainingArray = stages2.slice(startIndex + 1);
|
|
2483
|
-
const nearestObject = remainingArray.filter(condition)[0];
|
|
2484
|
-
return nearestObject;
|
|
2485
|
-
}
|
|
2486
|
-
const { ApplicationError: ApplicationError$2 } = errors;
|
|
2487
|
-
const validActions = [STAGE_TRANSITION_UID];
|
|
2488
|
-
const stagePermissions = ({ strapi: strapi2 }) => {
|
|
2489
|
-
const roleService = getService("role");
|
|
2490
|
-
const permissionService = getService("permission");
|
|
2491
|
-
return {
|
|
2492
|
-
async register({ roleId, action, fromStage }) {
|
|
2493
|
-
if (!validActions.includes(action)) {
|
|
2494
|
-
throw new ApplicationError$2(`Invalid action ${action}`);
|
|
2495
|
-
}
|
|
2496
|
-
const permissions = await roleService.addPermissions(roleId, [
|
|
2497
|
-
{
|
|
2498
|
-
action,
|
|
2499
|
-
actionParameters: {
|
|
2500
|
-
from: fromStage
|
|
2501
|
-
}
|
|
2502
|
-
}
|
|
2503
|
-
]);
|
|
2504
|
-
return permissions;
|
|
2505
|
-
},
|
|
2506
|
-
async registerMany(permissions) {
|
|
2507
|
-
return async.map(permissions, this.register);
|
|
2508
|
-
},
|
|
2509
|
-
async unregister(permissions) {
|
|
2510
|
-
const permissionIds = permissions.map(prop("id"));
|
|
2511
|
-
await permissionService.deleteByIds(permissionIds);
|
|
2512
|
-
},
|
|
2513
|
-
can(action, fromStage) {
|
|
2514
|
-
const requestState = strapi2.requestContext.get()?.state;
|
|
2515
|
-
if (!requestState) {
|
|
2516
|
-
return false;
|
|
2517
|
-
}
|
|
2518
|
-
const userRoles = requestState.user?.roles;
|
|
2519
|
-
if (userRoles?.some((role2) => role2.code === "strapi-super-admin")) {
|
|
2520
|
-
return true;
|
|
2521
|
-
}
|
|
2522
|
-
return requestState.userAbility.can({
|
|
2523
|
-
name: action,
|
|
2524
|
-
params: { from: fromStage }
|
|
2525
|
-
});
|
|
2526
|
-
}
|
|
2527
|
-
};
|
|
2528
|
-
};
|
|
2529
|
-
const { ApplicationError: ApplicationError$1 } = errors;
|
|
2530
|
-
const assignees$1 = ({ strapi: strapi2 }) => {
|
|
2531
|
-
const metrics2 = getService("review-workflows-metrics", { strapi: strapi2 });
|
|
2532
|
-
return {
|
|
2533
|
-
async findEntityAssigneeId(id, model) {
|
|
2534
|
-
const entity = await strapi2.db.query(model).findOne({
|
|
2535
|
-
where: { id },
|
|
2536
|
-
populate: [ENTITY_ASSIGNEE_ATTRIBUTE],
|
|
2537
|
-
select: []
|
|
2538
|
-
});
|
|
2539
|
-
return entity?.[ENTITY_ASSIGNEE_ATTRIBUTE]?.id ?? null;
|
|
2540
|
-
},
|
|
2541
|
-
/**
|
|
2542
|
-
* Update the assignee of an entity
|
|
2543
|
-
*/
|
|
2544
|
-
async updateEntityAssignee(id, model, assigneeId) {
|
|
2545
|
-
if (isNil(assigneeId)) {
|
|
2546
|
-
return this.deleteEntityAssignee(id, model);
|
|
2547
|
-
}
|
|
2548
|
-
const userExists = await getService("user", { strapi: strapi2 }).exists({ id: assigneeId });
|
|
2549
|
-
if (!userExists) {
|
|
2550
|
-
throw new ApplicationError$1(`Selected user does not exist`);
|
|
2551
|
-
}
|
|
2552
|
-
metrics2.sendDidEditAssignee(await this.findEntityAssigneeId(id, model), assigneeId);
|
|
2553
|
-
return strapi2.db.query(model).update({
|
|
2554
|
-
where: { id },
|
|
2555
|
-
data: { [ENTITY_ASSIGNEE_ATTRIBUTE]: assigneeId },
|
|
2556
|
-
populate: [ENTITY_ASSIGNEE_ATTRIBUTE],
|
|
2557
|
-
select: []
|
|
2558
|
-
});
|
|
2559
|
-
},
|
|
2560
|
-
async deleteEntityAssignee(id, model) {
|
|
2561
|
-
metrics2.sendDidEditAssignee(await this.findEntityAssigneeId(id, model), null);
|
|
2562
|
-
return strapi2.db.query(model).update({
|
|
2563
|
-
where: { id },
|
|
2564
|
-
data: { [ENTITY_ASSIGNEE_ATTRIBUTE]: null },
|
|
2565
|
-
populate: [ENTITY_ASSIGNEE_ATTRIBUTE],
|
|
2566
|
-
select: []
|
|
2567
|
-
});
|
|
2568
|
-
}
|
|
2569
|
-
};
|
|
2570
|
-
};
|
|
2571
|
-
const defaultStages = [
|
|
2572
|
-
{
|
|
2573
|
-
name: "To do",
|
|
2574
|
-
color: "#4945FF"
|
|
2575
|
-
},
|
|
2576
|
-
{
|
|
2577
|
-
name: "Ready to review",
|
|
2578
|
-
color: "#9736E8"
|
|
2579
|
-
},
|
|
2580
|
-
{
|
|
2581
|
-
name: "In progress",
|
|
2582
|
-
color: "#EE5E52"
|
|
2583
|
-
},
|
|
2584
|
-
{
|
|
2585
|
-
name: "Reviewed",
|
|
2586
|
-
color: "#328048"
|
|
2587
|
-
}
|
|
2588
|
-
];
|
|
2589
|
-
const WORKFLOW_UPDATE_STAGE = "review-workflows.updateEntryStage";
|
|
2590
|
-
const webhookEvents = {
|
|
2591
|
-
WORKFLOW_UPDATE_STAGE
|
|
2592
|
-
};
|
|
2593
|
-
const DEFAULT_OPTIONS = {
|
|
2594
|
-
numberOfWorkflows: MAX_WORKFLOWS,
|
|
2595
|
-
stagesPerWorkflow: MAX_STAGES_PER_WORKFLOW
|
|
2596
|
-
};
|
|
2597
|
-
async function initDefaultWorkflow({ workflowsService, stagesService }) {
|
|
2598
|
-
const wfCount = await workflowsService.count();
|
|
2599
|
-
const stagesCount = await stagesService.count();
|
|
2600
|
-
if (wfCount === 0 && stagesCount === 0) {
|
|
2601
|
-
const workflow2 = {
|
|
2602
|
-
...defaultWorkflow,
|
|
2603
|
-
contentTypes: [],
|
|
2604
|
-
stages: defaultStages
|
|
2605
|
-
};
|
|
2606
|
-
await workflowsService.create({ data: workflow2 });
|
|
2607
|
-
}
|
|
2608
|
-
}
|
|
2609
|
-
const setRelation = (attributeName, target) => (contentType) => {
|
|
2610
|
-
Object.assign(contentType.attributes, {
|
|
2611
|
-
[attributeName]: {
|
|
2612
|
-
writable: true,
|
|
2613
|
-
private: false,
|
|
2614
|
-
configurable: false,
|
|
2615
|
-
visible: false,
|
|
2616
|
-
useJoinTable: true,
|
|
2617
|
-
// We want a join table to persist data when downgrading to CE
|
|
2618
|
-
type: "relation",
|
|
2619
|
-
relation: "oneToOne",
|
|
2620
|
-
target
|
|
2621
|
-
}
|
|
2622
|
-
});
|
|
2623
|
-
return contentType;
|
|
2624
|
-
};
|
|
2625
|
-
const setStageAttribute = setRelation(ENTITY_STAGE_ATTRIBUTE, STAGE_MODEL_UID);
|
|
2626
|
-
const setAssigneeAttribute = setRelation(ENTITY_ASSIGNEE_ATTRIBUTE, "admin::user");
|
|
2627
|
-
const setReviewWorkflowAttributes = (contentType) => {
|
|
2628
|
-
setStageAttribute(contentType);
|
|
2629
|
-
setAssigneeAttribute(contentType);
|
|
2630
|
-
};
|
|
2631
|
-
function extendReviewWorkflowContentTypes({ strapi: strapi2 }) {
|
|
2632
|
-
const extendContentType = (contentTypeUID) => {
|
|
2633
|
-
strapi2.get("content-types").extend(contentTypeUID, setReviewWorkflowAttributes);
|
|
2634
|
-
};
|
|
2635
|
-
pipe([
|
|
2636
|
-
getVisibleContentTypesUID,
|
|
2637
|
-
// Iterate over UIDs to extend the content-type
|
|
2638
|
-
forEach(extendContentType)
|
|
2639
|
-
])(strapi2.contentTypes);
|
|
2640
|
-
}
|
|
2641
|
-
function persistStagesJoinTables({ strapi: strapi2 }) {
|
|
2642
|
-
return async ({ contentTypes }) => {
|
|
2643
|
-
const getStageTableToPersist = (contentTypeUID) => {
|
|
2644
|
-
const { attributes, tableName } = strapi2.db.metadata.get(contentTypeUID);
|
|
2645
|
-
const joinTableName = attributes[ENTITY_STAGE_ATTRIBUTE].joinTable.name;
|
|
2646
|
-
return {
|
|
2647
|
-
name: joinTableName,
|
|
2648
|
-
dependsOn: [{ name: tableName }]
|
|
2649
|
-
};
|
|
2650
|
-
};
|
|
2651
|
-
const joinTablesToPersist = pipe([
|
|
2652
|
-
getVisibleContentTypesUID,
|
|
2653
|
-
filter((uid) => hasStageAttribute(contentTypes[uid])),
|
|
2654
|
-
map(getStageTableToPersist)
|
|
2655
|
-
])(contentTypes);
|
|
2656
|
-
await removePersistedTablesWithSuffix("_strapi_stage_links");
|
|
2657
|
-
await persistTables(joinTablesToPersist);
|
|
2658
|
-
};
|
|
2659
|
-
}
|
|
2660
|
-
const registerWebhookEvents = async ({ strapi: strapi2 }) => Object.entries(webhookEvents).forEach(
|
|
2661
|
-
([eventKey, event]) => strapi2.get("webhookStore").addAllowedEvent(eventKey, event)
|
|
2662
|
-
);
|
|
2663
|
-
const reviewWorkflows = ({ strapi: strapi2 }) => {
|
|
2664
|
-
const workflowsService = getService("workflows", { strapi: strapi2 });
|
|
2665
|
-
const stagesService = getService("stages", { strapi: strapi2 });
|
|
2666
|
-
const workflowsValidationService = getService("review-workflows-validation", { strapi: strapi2 });
|
|
2667
|
-
return {
|
|
2668
|
-
async bootstrap() {
|
|
2669
|
-
await registerWebhookEvents({ strapi: strapi2 });
|
|
2670
|
-
await initDefaultWorkflow({ workflowsService, stagesService, strapi: strapi2 });
|
|
2671
|
-
},
|
|
2672
|
-
async register({ options } = { options: {} }) {
|
|
2673
|
-
extendReviewWorkflowContentTypes({ strapi: strapi2 });
|
|
2674
|
-
strapi2.hook("strapi::content-types.afterSync").register(persistStagesJoinTables({ strapi: strapi2 }));
|
|
2675
|
-
const reviewWorkflowsOptions = defaultsDeep(DEFAULT_OPTIONS, options);
|
|
2676
|
-
workflowsValidationService.register(reviewWorkflowsOptions);
|
|
2677
|
-
}
|
|
2678
|
-
};
|
|
2679
|
-
};
|
|
2680
|
-
const { ValidationError: ValidationError$1 } = errors;
|
|
2681
|
-
const reviewWorkflowsValidation = ({ strapi: strapi2 }) => {
|
|
2682
|
-
return {
|
|
2683
|
-
limits: {
|
|
2684
|
-
numberOfWorkflows: MAX_WORKFLOWS,
|
|
2685
|
-
stagesPerWorkflow: MAX_STAGES_PER_WORKFLOW
|
|
2686
|
-
},
|
|
2687
|
-
register({ numberOfWorkflows, stagesPerWorkflow }) {
|
|
2688
|
-
if (!Object.isFrozen(this.limits)) {
|
|
2689
|
-
this.limits.numberOfWorkflows = clampMaxWorkflows(
|
|
2690
|
-
numberOfWorkflows || this.limits.numberOfWorkflows
|
|
2691
|
-
);
|
|
2692
|
-
this.limits.stagesPerWorkflow = clampMaxStagesPerWorkflow(
|
|
2693
|
-
stagesPerWorkflow || this.limits.stagesPerWorkflow
|
|
2694
|
-
);
|
|
2695
|
-
Object.freeze(this.limits);
|
|
2696
|
-
}
|
|
2697
|
-
},
|
|
2698
|
-
/**
|
|
2699
|
-
* Validates the stages of a workflow.
|
|
2700
|
-
* @param {Array} stages - Array of stages to be validated.
|
|
2701
|
-
* @throws {ValidationError} - If the workflow has no stages or exceeds the limit.
|
|
2702
|
-
*/
|
|
2703
|
-
validateWorkflowStages(stages2) {
|
|
2704
|
-
if (!stages2 || stages2.length === 0) {
|
|
2705
|
-
throw new ValidationError$1(ERRORS.WORKFLOW_WITHOUT_STAGES);
|
|
2706
|
-
}
|
|
2707
|
-
if (stages2.length > this.limits.stagesPerWorkflow) {
|
|
2708
|
-
throw new ValidationError$1(ERRORS.STAGES_LIMIT);
|
|
2709
|
-
}
|
|
2710
|
-
const stageNames = stages2.map((stage) => stage.name);
|
|
2711
|
-
if (uniq(stageNames).length !== stageNames.length) {
|
|
2712
|
-
throw new ValidationError$1(ERRORS.DUPLICATED_STAGE_NAME);
|
|
2713
|
-
}
|
|
2714
|
-
},
|
|
2715
|
-
async validateWorkflowCountStages(workflowId, countAddedStages = 0) {
|
|
2716
|
-
const stagesService = getService("stages", { strapi: strapi2 });
|
|
2717
|
-
const countWorkflowStages = await stagesService.count({ workflowId });
|
|
2718
|
-
if (countWorkflowStages + countAddedStages > this.limits.stagesPerWorkflow) {
|
|
2719
|
-
throw new ValidationError$1(ERRORS.STAGES_LIMIT);
|
|
2720
|
-
}
|
|
2721
|
-
},
|
|
2722
|
-
/**
|
|
2723
|
-
* Validates the count of existing and added workflows.
|
|
2724
|
-
* @param {number} [countAddedWorkflows=0] - The count of workflows to be added.
|
|
2725
|
-
* @throws {ValidationError} - If the total count of workflows exceeds the limit.
|
|
2726
|
-
* @returns {Promise<void>} - A Promise that resolves when the validation is completed.
|
|
2727
|
-
*/
|
|
2728
|
-
async validateWorkflowCount(countAddedWorkflows = 0) {
|
|
2729
|
-
const workflowsService = getService("workflows", { strapi: strapi2 });
|
|
2730
|
-
const countWorkflows = await workflowsService.count();
|
|
2731
|
-
if (countWorkflows + countAddedWorkflows > this.limits.numberOfWorkflows) {
|
|
2732
|
-
throw new ValidationError$1(ERRORS.WORKFLOWS_LIMIT);
|
|
2733
|
-
}
|
|
2734
|
-
}
|
|
2735
|
-
};
|
|
2736
|
-
};
|
|
2737
|
-
const getDataWithStage = async (workflow2, data) => {
|
|
2738
|
-
if (!isNil(ENTITY_STAGE_ATTRIBUTE)) {
|
|
2739
|
-
return { ...data, [ENTITY_STAGE_ATTRIBUTE]: workflow2.stages[0].id };
|
|
2740
|
-
}
|
|
2741
|
-
return data;
|
|
2742
|
-
};
|
|
2743
|
-
const getEntityStage = async (uid, id) => {
|
|
2744
|
-
const entity = await strapi.db.query(uid).findOne({
|
|
2745
|
-
where: { id },
|
|
2746
|
-
populate: {
|
|
2747
|
-
[ENTITY_STAGE_ATTRIBUTE]: {
|
|
2748
|
-
populate: {
|
|
2749
|
-
workflow: true
|
|
2750
|
-
}
|
|
2751
|
-
}
|
|
2752
|
-
}
|
|
2753
|
-
});
|
|
2754
|
-
return entity?.[ENTITY_STAGE_ATTRIBUTE] ?? {};
|
|
2755
|
-
};
|
|
2756
|
-
const decorator = (service) => ({
|
|
2757
|
-
async create(uid, opts = {}) {
|
|
2758
|
-
const workflow2 = await getService("workflows").getAssignedWorkflow(uid, {
|
|
2759
|
-
populate: "stages"
|
|
2760
|
-
});
|
|
2761
|
-
if (!workflow2) {
|
|
2762
|
-
return service.create.call(this, uid, opts);
|
|
2763
|
-
}
|
|
2764
|
-
const data = await getDataWithStage(workflow2, opts.data);
|
|
2765
|
-
return service.create.call(this, uid, { ...opts, data });
|
|
2766
|
-
},
|
|
2767
|
-
async update(uid, entityId, opts = {}) {
|
|
2768
|
-
const data = { ...opts.data };
|
|
2769
|
-
if (isNil(data[ENTITY_STAGE_ATTRIBUTE])) {
|
|
2770
|
-
delete data[ENTITY_STAGE_ATTRIBUTE];
|
|
2771
|
-
return service.update.call(this, uid, entityId, { ...opts, data });
|
|
2772
|
-
}
|
|
2773
|
-
const previousStage = await getEntityStage(uid, entityId);
|
|
2774
|
-
const updatedEntity = await service.update.call(this, uid, entityId, { ...opts, data });
|
|
2775
|
-
const updatedStage = updatedEntity[ENTITY_STAGE_ATTRIBUTE];
|
|
2776
|
-
if (updatedStage && previousStage?.id && previousStage.id !== updatedStage.id) {
|
|
2777
|
-
const model = strapi.getModel(uid);
|
|
2778
|
-
strapi.eventHub.emit(WORKFLOW_UPDATE_STAGE, {
|
|
2779
|
-
model: model.modelName,
|
|
2780
|
-
uid: model.uid,
|
|
2781
|
-
entity: {
|
|
2782
|
-
id: entityId
|
|
2783
|
-
},
|
|
2784
|
-
workflow: {
|
|
2785
|
-
id: previousStage.workflow.id,
|
|
2786
|
-
stages: {
|
|
2787
|
-
from: {
|
|
2788
|
-
id: previousStage.id,
|
|
2789
|
-
name: previousStage.name
|
|
2790
|
-
},
|
|
2791
|
-
to: {
|
|
2792
|
-
id: updatedStage.id,
|
|
2793
|
-
name: updatedStage.name
|
|
2794
|
-
}
|
|
2795
|
-
}
|
|
2796
|
-
}
|
|
2797
|
-
});
|
|
2798
|
-
}
|
|
2799
|
-
return updatedEntity;
|
|
2800
|
-
}
|
|
2801
|
-
});
|
|
2802
|
-
const reviewWorkflowsDecorator = () => ({
|
|
2803
|
-
decorator
|
|
2804
|
-
});
|
|
2805
|
-
const sendDidCreateStage = async () => {
|
|
2806
|
-
strapi.telemetry.send("didCreateStage", {});
|
|
2807
|
-
};
|
|
2808
|
-
const sendDidEditStage = async () => {
|
|
2809
|
-
strapi.telemetry.send("didEditStage", {});
|
|
2810
|
-
};
|
|
2811
|
-
const sendDidDeleteStage = async () => {
|
|
2812
|
-
strapi.telemetry.send("didDeleteStage", {});
|
|
2813
|
-
};
|
|
2814
|
-
const sendDidChangeEntryStage = async () => {
|
|
2815
|
-
strapi.telemetry.send("didChangeEntryStage", {});
|
|
2816
|
-
};
|
|
2817
|
-
const sendDidCreateWorkflow = async () => {
|
|
2818
|
-
strapi.telemetry.send("didCreateWorkflow", {});
|
|
2819
|
-
};
|
|
2820
|
-
const sendDidEditWorkflow = async () => {
|
|
2821
|
-
strapi.telemetry.send("didEditWorkflow", {});
|
|
2822
|
-
};
|
|
2823
|
-
const sendDidEditAssignee = async (fromId, toId) => {
|
|
2824
|
-
strapi.telemetry.send("didEditAssignee", { from: fromId, to: toId });
|
|
2825
|
-
};
|
|
2826
|
-
const sendDidSendReviewWorkflowPropertiesOnceAWeek = async (numberOfActiveWorkflows, avgStagesCount, maxStagesCount, activatedContentTypes) => {
|
|
2827
|
-
strapi.telemetry.send("didSendReviewWorkflowPropertiesOnceAWeek", {
|
|
2828
|
-
groupProperties: {
|
|
2829
|
-
numberOfActiveWorkflows,
|
|
2830
|
-
avgStagesCount,
|
|
2831
|
-
maxStagesCount,
|
|
2832
|
-
activatedContentTypes
|
|
2833
|
-
}
|
|
2834
|
-
});
|
|
2835
|
-
};
|
|
2836
|
-
const reviewWorkflowsMetrics = {
|
|
2837
|
-
sendDidCreateStage,
|
|
2838
|
-
sendDidEditStage,
|
|
2839
|
-
sendDidDeleteStage,
|
|
2840
|
-
sendDidChangeEntryStage,
|
|
2841
|
-
sendDidCreateWorkflow,
|
|
2842
|
-
sendDidEditWorkflow,
|
|
2843
|
-
sendDidSendReviewWorkflowPropertiesOnceAWeek,
|
|
2844
|
-
sendDidEditAssignee
|
|
2845
|
-
};
|
|
2846
|
-
const ONE_WEEK = 7 * 24 * 60 * 60 * 1e3;
|
|
2847
|
-
const getWeeklyCronScheduleAt = (date) => `${date.getSeconds()} ${date.getMinutes()} ${date.getHours()} * * ${date.getDay()}`;
|
|
2848
|
-
const reviewWorkflowsWeeklyMetrics = ({ strapi: strapi2 }) => {
|
|
2849
|
-
const metrics2 = getService("review-workflows-metrics", { strapi: strapi2 });
|
|
2850
|
-
const workflowsService = getService("workflows", { strapi: strapi2 });
|
|
2851
|
-
const getMetricsStoreValue = async () => {
|
|
2852
|
-
const value = await strapi2.store.get({ type: "plugin", name: "ee", key: "metrics" });
|
|
2853
|
-
return defaultTo({}, value);
|
|
2854
|
-
};
|
|
2855
|
-
const setMetricsStoreValue = (value) => strapi2.store.set({ type: "plugin", name: "ee", key: "metrics", value });
|
|
2856
|
-
return {
|
|
2857
|
-
async computeMetrics() {
|
|
2858
|
-
const workflows2 = await workflowsService.find({ populate: "stages" });
|
|
2859
|
-
const stagesCount = flow(
|
|
2860
|
-
map("stages"),
|
|
2861
|
-
// Number of stages per workflow
|
|
2862
|
-
map(size)
|
|
2863
|
-
)(workflows2);
|
|
2864
|
-
const contentTypesCount = flow(
|
|
2865
|
-
map("contentTypes"),
|
|
2866
|
-
// Number of content types per workflow
|
|
2867
|
-
map(size)
|
|
2868
|
-
)(workflows2);
|
|
2869
|
-
return {
|
|
2870
|
-
numberOfActiveWorkflows: size(workflows2),
|
|
2871
|
-
avgStagesCount: mean(stagesCount),
|
|
2872
|
-
maxStagesCount: max(stagesCount),
|
|
2873
|
-
activatedContentTypes: sum(contentTypesCount)
|
|
2874
|
-
};
|
|
2875
|
-
},
|
|
2876
|
-
async sendMetrics() {
|
|
2877
|
-
const computedMetrics = await this.computeMetrics();
|
|
2878
|
-
metrics2.sendDidSendReviewWorkflowPropertiesOnceAWeek(computedMetrics);
|
|
2879
|
-
const metricsInfoStored = await getMetricsStoreValue();
|
|
2880
|
-
await setMetricsStoreValue({ ...metricsInfoStored, lastWeeklyUpdate: (/* @__PURE__ */ new Date()).getTime() });
|
|
2881
|
-
},
|
|
2882
|
-
async ensureWeeklyStoredCronSchedule() {
|
|
2883
|
-
const metricsInfoStored = await getMetricsStoreValue();
|
|
2884
|
-
const { weeklySchedule: currentSchedule, lastWeeklyUpdate } = metricsInfoStored;
|
|
2885
|
-
const now = /* @__PURE__ */ new Date();
|
|
2886
|
-
let weeklySchedule = currentSchedule;
|
|
2887
|
-
if (!currentSchedule || !lastWeeklyUpdate || lastWeeklyUpdate + ONE_WEEK < now.getTime()) {
|
|
2888
|
-
weeklySchedule = getWeeklyCronScheduleAt(add(now, { seconds: 10 }));
|
|
2889
|
-
await setMetricsStoreValue({ ...metricsInfoStored, weeklySchedule });
|
|
2890
|
-
}
|
|
2891
|
-
return weeklySchedule;
|
|
2892
|
-
},
|
|
2893
|
-
async registerCron() {
|
|
2894
|
-
const weeklySchedule = await this.ensureWeeklyStoredCronSchedule();
|
|
2895
|
-
strapi2.cron.add({ [weeklySchedule]: this.sendMetrics.bind(this) });
|
|
2896
|
-
}
|
|
2897
|
-
};
|
|
2898
|
-
};
|
|
2899
1444
|
const index$1 = {
|
|
2900
1445
|
auth,
|
|
2901
1446
|
passport,
|
|
2902
1447
|
role: role$1,
|
|
2903
1448
|
user: user$1,
|
|
2904
1449
|
metrics,
|
|
2905
|
-
"seat-enforcement": seatEnforcement
|
|
2906
|
-
workflows: workflows$1,
|
|
2907
|
-
stages: stages$1,
|
|
2908
|
-
"stage-permissions": stagePermissions,
|
|
2909
|
-
assignees: assignees$1,
|
|
2910
|
-
"review-workflows": reviewWorkflows,
|
|
2911
|
-
"review-workflows-validation": reviewWorkflowsValidation,
|
|
2912
|
-
"review-workflows-decorator": reviewWorkflowsDecorator,
|
|
2913
|
-
"review-workflows-metrics": reviewWorkflowsMetrics,
|
|
2914
|
-
"review-workflows-weekly-metrics": reviewWorkflowsWeeklyMetrics
|
|
1450
|
+
"seat-enforcement": seatEnforcement
|
|
2915
1451
|
};
|
|
2916
1452
|
const providerOptionsUpdateSchema = yup.object().shape({
|
|
2917
1453
|
autoRegister: yup.boolean().required(),
|
|
@@ -3189,8 +1725,8 @@ const assignOrOmitSubCategory = (action) => {
|
|
|
3189
1725
|
const shouldHaveSubCategory = ["settings", "plugins"].includes(action.section);
|
|
3190
1726
|
return shouldHaveSubCategory ? set("subCategory", action.subCategory || "general", action) : omit("subCategory", action);
|
|
3191
1727
|
};
|
|
3192
|
-
const appliesToProperty = curry((
|
|
3193
|
-
return pipe(prop("options.applyToProperties"), includes(
|
|
1728
|
+
const appliesToProperty = curry((property, action) => {
|
|
1729
|
+
return pipe(prop("options.applyToProperties"), includes(property))(action);
|
|
3194
1730
|
});
|
|
3195
1731
|
const appliesToSubject = curry((subject, action) => {
|
|
3196
1732
|
return isArray(action.subjects) && includes(subject, action.subjects);
|
|
@@ -3317,7 +1853,7 @@ const permission = yup.object().shape({
|
|
|
3317
1853
|
if (!isArray(applyToProperties)) {
|
|
3318
1854
|
return false;
|
|
3319
1855
|
}
|
|
3320
|
-
return Object.keys(properties).every((
|
|
1856
|
+
return Object.keys(properties).every((property) => applyToProperties.includes(property));
|
|
3321
1857
|
}).test(
|
|
3322
1858
|
"fields-property",
|
|
3323
1859
|
"Invalid fields property at ${path}",
|
|
@@ -3552,361 +2088,12 @@ const admin = {
|
|
|
3552
2088
|
return { data };
|
|
3553
2089
|
}
|
|
3554
2090
|
};
|
|
3555
|
-
const stageObject = yup.object().shape({
|
|
3556
|
-
id: yup.number().integer().min(1),
|
|
3557
|
-
name: yup.string().max(255).required(),
|
|
3558
|
-
color: yup.string().matches(/^#(?:[0-9a-fA-F]{3}){1,2}$/i),
|
|
3559
|
-
// hex color
|
|
3560
|
-
permissions: yup.array().of(
|
|
3561
|
-
yup.object().shape({
|
|
3562
|
-
role: yup.number().integer().min(1).required(),
|
|
3563
|
-
action: yup.string().oneOf([STAGE_TRANSITION_UID]).required(),
|
|
3564
|
-
actionParameters: yup.object().shape({
|
|
3565
|
-
from: yup.number().integer().min(1).required(),
|
|
3566
|
-
to: yup.number().integer().min(1)
|
|
3567
|
-
})
|
|
3568
|
-
})
|
|
3569
|
-
)
|
|
3570
|
-
});
|
|
3571
|
-
const validateUpdateStageOnEntitySchema = yup.object().shape({
|
|
3572
|
-
id: yup.number().integer().min(1).required()
|
|
3573
|
-
}).required();
|
|
3574
|
-
const validateContentTypes = yup.array().of(
|
|
3575
|
-
// @ts-expect-error yup types
|
|
3576
|
-
yup.string().test({
|
|
3577
|
-
name: "content-type-exists",
|
|
3578
|
-
message: (value) => `Content type ${value.originalValue} does not exist`,
|
|
3579
|
-
test(uid) {
|
|
3580
|
-
return strapi.getModel(uid);
|
|
3581
|
-
}
|
|
3582
|
-
}).test({
|
|
3583
|
-
name: "content-type-review-workflow-enabled",
|
|
3584
|
-
message: (value) => `Content type ${value.originalValue} does not have review workflow enabled`,
|
|
3585
|
-
test(uid) {
|
|
3586
|
-
const model = strapi.getModel(uid);
|
|
3587
|
-
return hasStageAttribute(model);
|
|
3588
|
-
}
|
|
3589
|
-
})
|
|
3590
|
-
);
|
|
3591
|
-
const validateWorkflowCreateSchema = yup.object().shape({
|
|
3592
|
-
name: yup.string().max(255).min(1, "Workflow name can not be empty").required(),
|
|
3593
|
-
stages: yup.array().of(stageObject).uniqueProperty("name", "Stage name must be unique").min(1, "Can not create a workflow without stages").max(200, "Can not have more than 200 stages").required("Can not create a workflow without stages"),
|
|
3594
|
-
contentTypes: validateContentTypes
|
|
3595
|
-
});
|
|
3596
|
-
const validateWorkflowUpdateSchema = yup.object().shape({
|
|
3597
|
-
name: yup.string().max(255).min(1, "Workflow name can not be empty"),
|
|
3598
|
-
stages: yup.array().of(stageObject).uniqueProperty("name", "Stage name must be unique").min(1, "Can not update a workflow without stages").max(200, "Can not have more than 200 stages"),
|
|
3599
|
-
contentTypes: validateContentTypes
|
|
3600
|
-
});
|
|
3601
|
-
const validateUpdateAssigneeOnEntitySchema = yup.object().shape({
|
|
3602
|
-
id: yup.number().integer().min(1).nullable()
|
|
3603
|
-
}).required();
|
|
3604
|
-
const validateWorkflowCreate = validateYupSchema(validateWorkflowCreateSchema);
|
|
3605
|
-
const validateUpdateStageOnEntity = validateYupSchema(validateUpdateStageOnEntitySchema);
|
|
3606
|
-
const validateUpdateAssigneeOnEntity = validateYupSchema(
|
|
3607
|
-
validateUpdateAssigneeOnEntitySchema
|
|
3608
|
-
);
|
|
3609
|
-
const validateWorkflowUpdate = validateYupSchema(validateWorkflowUpdateSchema);
|
|
3610
|
-
function getWorkflowsPermissionChecker({ strapi: strapi2 }, userAbility) {
|
|
3611
|
-
return strapi2.plugin("content-manager").service("permission-checker").create({ userAbility, model: WORKFLOW_MODEL_UID });
|
|
3612
|
-
}
|
|
3613
|
-
function formatWorkflowToAdmin(workflow2) {
|
|
3614
|
-
if (!workflow2)
|
|
3615
|
-
return;
|
|
3616
|
-
if (!workflow2.stages)
|
|
3617
|
-
return workflow2;
|
|
3618
|
-
const transformPermissions = map(update("role", property("id")));
|
|
3619
|
-
const transformStages = map(update("permissions", transformPermissions));
|
|
3620
|
-
return update("stages", transformStages, workflow2);
|
|
3621
|
-
}
|
|
3622
|
-
const workflows = {
|
|
3623
|
-
/**
|
|
3624
|
-
* Create a new workflow
|
|
3625
|
-
* @param {import('koa').BaseContext} ctx - koa context
|
|
3626
|
-
*/
|
|
3627
|
-
async create(ctx) {
|
|
3628
|
-
const { body, query } = ctx.request;
|
|
3629
|
-
const { sanitizeCreateInput, sanitizeOutput, sanitizedQuery } = getWorkflowsPermissionChecker(
|
|
3630
|
-
{ strapi },
|
|
3631
|
-
ctx.state.userAbility
|
|
3632
|
-
);
|
|
3633
|
-
const { populate } = await sanitizedQuery.create(query);
|
|
3634
|
-
const workflowBody = await validateWorkflowCreate(body.data);
|
|
3635
|
-
const workflowService = getService("workflows");
|
|
3636
|
-
const createdWorkflow = await workflowService.create({
|
|
3637
|
-
data: await sanitizeCreateInput(workflowBody),
|
|
3638
|
-
populate
|
|
3639
|
-
}).then(formatWorkflowToAdmin);
|
|
3640
|
-
ctx.body = {
|
|
3641
|
-
data: await sanitizeOutput(createdWorkflow)
|
|
3642
|
-
};
|
|
3643
|
-
},
|
|
3644
|
-
/**
|
|
3645
|
-
* Update a workflow
|
|
3646
|
-
* @param {import('koa').BaseContext} ctx - koa context
|
|
3647
|
-
*/
|
|
3648
|
-
async update(ctx) {
|
|
3649
|
-
const { id } = ctx.params;
|
|
3650
|
-
const { body, query } = ctx.request;
|
|
3651
|
-
const workflowService = getService("workflows");
|
|
3652
|
-
const { sanitizeUpdateInput, sanitizeOutput, sanitizedQuery } = getWorkflowsPermissionChecker(
|
|
3653
|
-
{ strapi },
|
|
3654
|
-
ctx.state.userAbility
|
|
3655
|
-
);
|
|
3656
|
-
const { populate } = await sanitizedQuery.update(query);
|
|
3657
|
-
const workflowBody = await validateWorkflowUpdate(body.data);
|
|
3658
|
-
const workflow2 = await workflowService.findById(id, { populate: WORKFLOW_POPULATE });
|
|
3659
|
-
if (!workflow2) {
|
|
3660
|
-
return ctx.notFound();
|
|
3661
|
-
}
|
|
3662
|
-
const getPermittedFieldToUpdate = sanitizeUpdateInput(workflow2);
|
|
3663
|
-
const dataToUpdate = await getPermittedFieldToUpdate(workflowBody);
|
|
3664
|
-
const updatedWorkflow = await workflowService.update(workflow2, {
|
|
3665
|
-
data: dataToUpdate,
|
|
3666
|
-
populate
|
|
3667
|
-
}).then(formatWorkflowToAdmin);
|
|
3668
|
-
ctx.body = {
|
|
3669
|
-
data: await sanitizeOutput(updatedWorkflow)
|
|
3670
|
-
};
|
|
3671
|
-
},
|
|
3672
|
-
/**
|
|
3673
|
-
* Delete a workflow
|
|
3674
|
-
* @param {import('koa').BaseContext} ctx - koa context
|
|
3675
|
-
*/
|
|
3676
|
-
async delete(ctx) {
|
|
3677
|
-
const { id } = ctx.params;
|
|
3678
|
-
const { query } = ctx.request;
|
|
3679
|
-
const workflowService = getService("workflows");
|
|
3680
|
-
const { sanitizeOutput, sanitizedQuery } = getWorkflowsPermissionChecker(
|
|
3681
|
-
{ strapi },
|
|
3682
|
-
ctx.state.userAbility
|
|
3683
|
-
);
|
|
3684
|
-
const { populate } = await sanitizedQuery.delete(query);
|
|
3685
|
-
const workflow2 = await workflowService.findById(id, { populate: WORKFLOW_POPULATE });
|
|
3686
|
-
if (!workflow2) {
|
|
3687
|
-
return ctx.notFound("Workflow doesn't exist");
|
|
3688
|
-
}
|
|
3689
|
-
const deletedWorkflow = await workflowService.delete(workflow2, { populate }).then(formatWorkflowToAdmin);
|
|
3690
|
-
ctx.body = {
|
|
3691
|
-
data: await sanitizeOutput(deletedWorkflow)
|
|
3692
|
-
};
|
|
3693
|
-
},
|
|
3694
|
-
/**
|
|
3695
|
-
* List all workflows
|
|
3696
|
-
* @param {import('koa').BaseContext} ctx - koa context
|
|
3697
|
-
*/
|
|
3698
|
-
async find(ctx) {
|
|
3699
|
-
const { query } = ctx.request;
|
|
3700
|
-
const workflowService = getService("workflows");
|
|
3701
|
-
const { sanitizeOutput, sanitizedQuery } = getWorkflowsPermissionChecker(
|
|
3702
|
-
{ strapi },
|
|
3703
|
-
ctx.state.userAbility
|
|
3704
|
-
);
|
|
3705
|
-
const { populate, filters, sort } = await sanitizedQuery.read(query);
|
|
3706
|
-
const [workflows2, workflowCount] = await Promise.all([
|
|
3707
|
-
workflowService.find({ populate, filters, sort }).then(map(formatWorkflowToAdmin)),
|
|
3708
|
-
workflowService.count()
|
|
3709
|
-
]);
|
|
3710
|
-
ctx.body = {
|
|
3711
|
-
data: await async.map(workflows2, sanitizeOutput),
|
|
3712
|
-
meta: {
|
|
3713
|
-
workflowCount
|
|
3714
|
-
}
|
|
3715
|
-
};
|
|
3716
|
-
},
|
|
3717
|
-
/**
|
|
3718
|
-
* Get one workflow based on its id contained in request parameters
|
|
3719
|
-
* Returns count of workflows in meta, used to prevent workflow edition when
|
|
3720
|
-
* max workflow count is reached for the current plan
|
|
3721
|
-
* @param {import('koa').BaseContext} ctx - koa context
|
|
3722
|
-
*/
|
|
3723
|
-
async findById(ctx) {
|
|
3724
|
-
const { id } = ctx.params;
|
|
3725
|
-
const { query } = ctx.request;
|
|
3726
|
-
const { sanitizeOutput, sanitizedQuery } = getWorkflowsPermissionChecker(
|
|
3727
|
-
{ strapi },
|
|
3728
|
-
ctx.state.userAbility
|
|
3729
|
-
);
|
|
3730
|
-
const { populate } = await sanitizedQuery.read(query);
|
|
3731
|
-
const workflowService = getService("workflows");
|
|
3732
|
-
const [workflow2, workflowCount] = await Promise.all([
|
|
3733
|
-
workflowService.findById(id, { populate }).then(formatWorkflowToAdmin),
|
|
3734
|
-
workflowService.count()
|
|
3735
|
-
]);
|
|
3736
|
-
ctx.body = {
|
|
3737
|
-
data: await sanitizeOutput(workflow2),
|
|
3738
|
-
meta: { workflowCount }
|
|
3739
|
-
};
|
|
3740
|
-
}
|
|
3741
|
-
};
|
|
3742
|
-
function sanitizeStage({ strapi: strapi2 }, userAbility) {
|
|
3743
|
-
const permissionChecker = strapi2.plugin("content-manager").service("permission-checker").create({ userAbility, model: STAGE_MODEL_UID });
|
|
3744
|
-
return (entity) => permissionChecker.sanitizeOutput(entity);
|
|
3745
|
-
}
|
|
3746
|
-
const stages = {
|
|
3747
|
-
/**
|
|
3748
|
-
* List all stages
|
|
3749
|
-
* @param {import('koa').BaseContext} ctx - koa context
|
|
3750
|
-
*/
|
|
3751
|
-
async find(ctx) {
|
|
3752
|
-
const { workflow_id: workflowId } = ctx.params;
|
|
3753
|
-
const { populate } = ctx.query;
|
|
3754
|
-
const stagesService = getService("stages");
|
|
3755
|
-
const sanitizer = sanitizeStage({ strapi }, ctx.state.userAbility);
|
|
3756
|
-
const stages2 = await stagesService.find({
|
|
3757
|
-
workflowId,
|
|
3758
|
-
populate
|
|
3759
|
-
});
|
|
3760
|
-
ctx.body = {
|
|
3761
|
-
data: await async.map(stages2, sanitizer)
|
|
3762
|
-
};
|
|
3763
|
-
},
|
|
3764
|
-
/**
|
|
3765
|
-
* Get one stage
|
|
3766
|
-
* @param {import('koa').BaseContext} ctx - koa context
|
|
3767
|
-
*/
|
|
3768
|
-
async findById(ctx) {
|
|
3769
|
-
const { id, workflow_id: workflowId } = ctx.params;
|
|
3770
|
-
const { populate } = ctx.query;
|
|
3771
|
-
const stagesService = getService("stages");
|
|
3772
|
-
const sanitizer = sanitizeStage({ strapi }, ctx.state.userAbility);
|
|
3773
|
-
const stage = await stagesService.findById(id, {
|
|
3774
|
-
workflowId,
|
|
3775
|
-
populate
|
|
3776
|
-
});
|
|
3777
|
-
ctx.body = {
|
|
3778
|
-
data: await sanitizer(stage)
|
|
3779
|
-
};
|
|
3780
|
-
},
|
|
3781
|
-
/**
|
|
3782
|
-
* Updates an entity's stage.
|
|
3783
|
-
* @async
|
|
3784
|
-
* @param {Object} ctx - The Koa context object.
|
|
3785
|
-
* @param {Object} ctx.params - An object containing the parameters from the request URL.
|
|
3786
|
-
* @param {string} ctx.params.model_uid - The model UID of the entity.
|
|
3787
|
-
* @param {string} ctx.params.id - The ID of the entity to update.
|
|
3788
|
-
* @param {Object} ctx.request.body.data - Optional data object containing the new stage ID for the entity.
|
|
3789
|
-
* @param {string} ctx.request.body.data.id - The ID of the new stage for the entity.
|
|
3790
|
-
* @throws {ApplicationError} If review workflows is not activated on the specified model UID.
|
|
3791
|
-
* @throws {ValidationError} If the `data` object in the request body fails to pass validation.
|
|
3792
|
-
* @returns {Promise<void>} A promise that resolves when the entity's stage has been updated.
|
|
3793
|
-
*/
|
|
3794
|
-
async updateEntity(ctx) {
|
|
3795
|
-
const stagesService = getService("stages");
|
|
3796
|
-
const stagePermissions2 = getService("stage-permissions");
|
|
3797
|
-
const workflowService = getService("workflows");
|
|
3798
|
-
const { model_uid: modelUID, id } = ctx.params;
|
|
3799
|
-
const { body } = ctx.request;
|
|
3800
|
-
const { sanitizeOutput } = strapi.plugin("content-manager").service("permission-checker").create({ userAbility: ctx.state.userAbility, model: modelUID });
|
|
3801
|
-
const entity = await strapi.db.query(modelUID).findOne({
|
|
3802
|
-
where: { id },
|
|
3803
|
-
populate: [ENTITY_STAGE_ATTRIBUTE]
|
|
3804
|
-
});
|
|
3805
|
-
if (!entity) {
|
|
3806
|
-
ctx.throw(404, "Entity not found");
|
|
3807
|
-
}
|
|
3808
|
-
const canTransition = stagePermissions2.can(
|
|
3809
|
-
STAGE_TRANSITION_UID,
|
|
3810
|
-
entity[ENTITY_STAGE_ATTRIBUTE]?.id
|
|
3811
|
-
);
|
|
3812
|
-
if (!canTransition) {
|
|
3813
|
-
ctx.throw(403, "Forbidden stage transition");
|
|
3814
|
-
}
|
|
3815
|
-
const { id: stageId } = await validateUpdateStageOnEntity(
|
|
3816
|
-
{ id: Number(body?.data?.id) },
|
|
3817
|
-
"You should pass an id to the body of the put request."
|
|
3818
|
-
);
|
|
3819
|
-
const workflow2 = await workflowService.assertContentTypeBelongsToWorkflow(modelUID);
|
|
3820
|
-
workflowService.assertStageBelongsToWorkflow(stageId, workflow2);
|
|
3821
|
-
const updatedEntity = await stagesService.updateEntity({ id: entity.id, modelUID }, stageId);
|
|
3822
|
-
ctx.body = { data: await sanitizeOutput(updatedEntity) };
|
|
3823
|
-
},
|
|
3824
|
-
/**
|
|
3825
|
-
* List all the stages that are available for a user to transition an entity to.
|
|
3826
|
-
* If the user has permission to change the current stage of the entity every other stage in the workflow is returned
|
|
3827
|
-
* @async
|
|
3828
|
-
* @param {*} ctx
|
|
3829
|
-
* @param {string} ctx.params.model_uid - The model UID of the entity.
|
|
3830
|
-
* @param {string} ctx.params.id - The ID of the entity.
|
|
3831
|
-
* @throws {ApplicationError} If review workflows is not activated on the specified model UID.
|
|
3832
|
-
*/
|
|
3833
|
-
async listAvailableStages(ctx) {
|
|
3834
|
-
const stagePermissions2 = getService("stage-permissions");
|
|
3835
|
-
const workflowService = getService("workflows");
|
|
3836
|
-
const { model_uid: modelUID, id } = ctx.params;
|
|
3837
|
-
if (strapi.plugin("content-manager").service("permission-checker").create({ userAbility: ctx.state.userAbility, model: modelUID }).cannot.read()) {
|
|
3838
|
-
return ctx.forbidden();
|
|
3839
|
-
}
|
|
3840
|
-
const entity = await strapi.db.query(modelUID).findOne({
|
|
3841
|
-
where: { id },
|
|
3842
|
-
populate: [ENTITY_STAGE_ATTRIBUTE]
|
|
3843
|
-
});
|
|
3844
|
-
if (!entity) {
|
|
3845
|
-
ctx.throw(404, "Entity not found");
|
|
3846
|
-
}
|
|
3847
|
-
const entityStageId = entity[ENTITY_STAGE_ATTRIBUTE]?.id;
|
|
3848
|
-
const canTransition = stagePermissions2.can(STAGE_TRANSITION_UID, entityStageId);
|
|
3849
|
-
const [workflowCount, { stages: workflowStages }] = await Promise.all([
|
|
3850
|
-
workflowService.count(),
|
|
3851
|
-
workflowService.getAssignedWorkflow(modelUID, {
|
|
3852
|
-
populate: "stages"
|
|
3853
|
-
})
|
|
3854
|
-
]);
|
|
3855
|
-
const meta = {
|
|
3856
|
-
stageCount: workflowStages.length,
|
|
3857
|
-
workflowCount
|
|
3858
|
-
};
|
|
3859
|
-
if (!canTransition) {
|
|
3860
|
-
ctx.body = {
|
|
3861
|
-
data: [],
|
|
3862
|
-
meta
|
|
3863
|
-
};
|
|
3864
|
-
return;
|
|
3865
|
-
}
|
|
3866
|
-
const data = workflowStages.filter((stage) => stage.id !== entityStageId);
|
|
3867
|
-
ctx.body = {
|
|
3868
|
-
data,
|
|
3869
|
-
meta
|
|
3870
|
-
};
|
|
3871
|
-
}
|
|
3872
|
-
};
|
|
3873
|
-
const assignees = {
|
|
3874
|
-
/**
|
|
3875
|
-
* Updates an entity's assignee.
|
|
3876
|
-
* @async
|
|
3877
|
-
* @param {Object} ctx - The Koa context object.
|
|
3878
|
-
* @param {Object} ctx.params - An object containing the parameters from the request URL.
|
|
3879
|
-
* @param {string} ctx.params.model_uid - The model UID of the entity.
|
|
3880
|
-
* @param {string} ctx.params.id - The ID of the entity to update.
|
|
3881
|
-
* @param {Object} ctx.request.body.data - Optional data object containing the new assignee ID for the entity.
|
|
3882
|
-
* @param {string} ctx.request.body.data.id - The ID of the new assignee for the entity.
|
|
3883
|
-
* @throws {ApplicationError} If review workflows is not activated on the specified model UID.
|
|
3884
|
-
* @throws {ValidationError} If the `data` object in the request body fails to pass validation.
|
|
3885
|
-
* @returns {Promise<void>} A promise that resolves when the entity's assignee has been updated.
|
|
3886
|
-
*/
|
|
3887
|
-
async updateEntity(ctx) {
|
|
3888
|
-
const assigneeService = getService("assignees");
|
|
3889
|
-
const workflowService = getService("workflows");
|
|
3890
|
-
const { model_uid: model, id } = ctx.params;
|
|
3891
|
-
const { sanitizeOutput } = strapi.plugin("content-manager").service("permission-checker").create({ userAbility: ctx.state.userAbility, model });
|
|
3892
|
-
const { id: assigneeId } = await validateUpdateAssigneeOnEntity(
|
|
3893
|
-
ctx.request?.body?.data,
|
|
3894
|
-
"You should pass a valid id to the body of the put request."
|
|
3895
|
-
);
|
|
3896
|
-
await workflowService.assertContentTypeBelongsToWorkflow(model);
|
|
3897
|
-
const entity = await assigneeService.updateEntityAssignee(id, model, assigneeId);
|
|
3898
|
-
ctx.body = { data: await sanitizeOutput(entity) };
|
|
3899
|
-
}
|
|
3900
|
-
};
|
|
3901
2091
|
const index = {
|
|
3902
2092
|
authentication,
|
|
3903
2093
|
role,
|
|
3904
2094
|
user,
|
|
3905
2095
|
auditLogs,
|
|
3906
|
-
admin
|
|
3907
|
-
workflows,
|
|
3908
|
-
stages,
|
|
3909
|
-
assignees
|
|
2096
|
+
admin
|
|
3910
2097
|
};
|
|
3911
2098
|
export {
|
|
3912
2099
|
bootstrap,
|