@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.
Files changed (175) hide show
  1. package/dist/admin/{AuthenticatedLayout-1x0WC6c6.js → AuthenticatedLayout-Aibt647F.js} +11 -11
  2. package/dist/admin/{AuthenticatedLayout-1x0WC6c6.js.map → AuthenticatedLayout-Aibt647F.js.map} +1 -1
  3. package/dist/admin/{AuthenticatedLayout-zaOd05gw.mjs → AuthenticatedLayout-UPRVctuH.mjs} +11 -11
  4. package/dist/admin/{AuthenticatedLayout-zaOd05gw.mjs.map → AuthenticatedLayout-UPRVctuH.mjs.map} +1 -1
  5. package/dist/admin/{CreatePage-J84OUqE4.js → CreatePage-EngKiM_9.js} +3 -3
  6. package/dist/admin/{CreatePage-J84OUqE4.js.map → CreatePage-EngKiM_9.js.map} +1 -1
  7. package/dist/admin/{CreatePage-pniatTP-.mjs → CreatePage-S7eVTkzK.mjs} +3 -3
  8. package/dist/admin/{CreatePage-pniatTP-.mjs.map → CreatePage-S7eVTkzK.mjs.map} +1 -1
  9. package/dist/admin/{CreatePage-gSoe3n8A.mjs → CreatePage-dhyIsOBI.mjs} +2 -2
  10. package/dist/admin/{CreatePage-gSoe3n8A.mjs.map → CreatePage-dhyIsOBI.mjs.map} +1 -1
  11. package/dist/admin/{CreatePage-z4ERqv1R.js → CreatePage-h5o6e7Ls.js} +2 -2
  12. package/dist/admin/{CreatePage-z4ERqv1R.js.map → CreatePage-h5o6e7Ls.js.map} +1 -1
  13. package/dist/admin/{CreateView-NwMXZjDh.js → CreateView-8qevpt9o.js} +2 -2
  14. package/dist/admin/{CreateView-NwMXZjDh.js.map → CreateView-8qevpt9o.js.map} +1 -1
  15. package/dist/admin/{CreateView-sFb3ySEj.mjs → CreateView-HQFmuEYU.mjs} +2 -2
  16. package/dist/admin/{CreateView-sFb3ySEj.mjs.map → CreateView-HQFmuEYU.mjs.map} +1 -1
  17. package/dist/admin/{CreateView-BdO0Pvex.mjs → CreateView-YBUhPBBg.mjs} +2 -2
  18. package/dist/admin/{CreateView-BdO0Pvex.mjs.map → CreateView-YBUhPBBg.mjs.map} +1 -1
  19. package/dist/admin/{CreateView-JNmfRawb.js → CreateView-chaJt-l6.js} +2 -2
  20. package/dist/admin/{CreateView-JNmfRawb.js.map → CreateView-chaJt-l6.js.map} +1 -1
  21. package/dist/admin/{EditPage-dAyJwJfs.js → EditPage-3zbE4Ls0.js} +5 -5
  22. package/dist/admin/{EditPage-dAyJwJfs.js.map → EditPage-3zbE4Ls0.js.map} +1 -1
  23. package/dist/admin/{EditPage-eR6riBsR.js → EditPage-H9aQSaaH.js} +3 -3
  24. package/dist/admin/{EditPage-eR6riBsR.js.map → EditPage-H9aQSaaH.js.map} +1 -1
  25. package/dist/admin/{EditPage-IQRLHMer.mjs → EditPage-KxrcV7mh.mjs} +5 -5
  26. package/dist/admin/{EditPage-IQRLHMer.mjs.map → EditPage-KxrcV7mh.mjs.map} +1 -1
  27. package/dist/admin/{EditPage-yztdPcnM.mjs → EditPage-OcSrQNeJ.mjs} +4 -4
  28. package/dist/admin/{EditPage-yztdPcnM.mjs.map → EditPage-OcSrQNeJ.mjs.map} +1 -1
  29. package/dist/admin/{EditPage-3wx6NJgA.mjs → EditPage-oenQhubv.mjs} +3 -3
  30. package/dist/admin/{EditPage-3wx6NJgA.mjs.map → EditPage-oenQhubv.mjs.map} +1 -1
  31. package/dist/admin/{EditPage-rdOE18EC.js → EditPage-pk60xOuF.js} +4 -4
  32. package/dist/admin/{EditPage-rdOE18EC.js.map → EditPage-pk60xOuF.js.map} +1 -1
  33. package/dist/admin/{EditView-oyHNBKvL.js → EditView-BiqoDvKZ.js} +3 -3
  34. package/dist/admin/{EditView-oyHNBKvL.js.map → EditView-BiqoDvKZ.js.map} +1 -1
  35. package/dist/admin/{EditView-fbzip_S8.mjs → EditView-dHW8NJN4.mjs} +3 -3
  36. package/dist/admin/{EditView-fbzip_S8.mjs.map → EditView-dHW8NJN4.mjs.map} +1 -1
  37. package/dist/admin/{EditViewPage-rDgxiutd.mjs → EditViewPage-O9_heFiZ.mjs} +3 -3
  38. package/dist/admin/{EditViewPage-rDgxiutd.mjs.map → EditViewPage-O9_heFiZ.mjs.map} +1 -1
  39. package/dist/admin/{EditViewPage-LdDGNi6d.js → EditViewPage-OMo0nfgm.js} +3 -3
  40. package/dist/admin/{EditViewPage-LdDGNi6d.js.map → EditViewPage-OMo0nfgm.js.map} +1 -1
  41. package/dist/admin/{EventsTable--peC0Geq.js → EventsTable-1IsAibDQ.js} +2 -2
  42. package/dist/admin/{EventsTable--peC0Geq.js.map → EventsTable-1IsAibDQ.js.map} +1 -1
  43. package/dist/admin/{EventsTable-rNMPq-X1.mjs → EventsTable-Xh9FIU87.mjs} +2 -2
  44. package/dist/admin/{EventsTable-rNMPq-X1.mjs.map → EventsTable-Xh9FIU87.mjs.map} +1 -1
  45. package/dist/admin/{HomePage-qgAro8dS.mjs → HomePage-D-bJ3jts.mjs} +3 -3
  46. package/dist/admin/{HomePage-qgAro8dS.mjs.map → HomePage-D-bJ3jts.mjs.map} +1 -1
  47. package/dist/admin/{HomePage-14WM2hIE.js → HomePage-OeCZ2DCC.js} +2 -2
  48. package/dist/admin/{HomePage-14WM2hIE.js.map → HomePage-OeCZ2DCC.js.map} +1 -1
  49. package/dist/admin/{HomePage-e2Bs6Kar.js → HomePage-Xf5Ghz92.js} +3 -3
  50. package/dist/admin/{HomePage-e2Bs6Kar.js.map → HomePage-Xf5Ghz92.js.map} +1 -1
  51. package/dist/admin/{HomePage-zQ7HL9y6.mjs → HomePage-oNgvRlya.mjs} +2 -2
  52. package/dist/admin/{HomePage-zQ7HL9y6.mjs.map → HomePage-oNgvRlya.mjs.map} +1 -1
  53. package/dist/admin/{Layout-KAX2c8wc.js → Layout-BuMuReXo.js} +3 -3
  54. package/dist/admin/{Layout-KAX2c8wc.js.map → Layout-BuMuReXo.js.map} +1 -1
  55. package/dist/admin/{Layout-mEj5c1Bi.mjs → Layout-lSZESFtG.mjs} +3 -3
  56. package/dist/admin/{Layout-mEj5c1Bi.mjs.map → Layout-lSZESFtG.mjs.map} +1 -1
  57. package/dist/admin/{ListPage-asZ9aDzG.mjs → ListPage-1HhDIdVj.mjs} +3 -3
  58. package/dist/admin/{ListPage-asZ9aDzG.mjs.map → ListPage-1HhDIdVj.mjs.map} +1 -1
  59. package/dist/admin/{ListPage-ARW72ceH.js → ListPage-6HUcvFyL.js} +2 -2
  60. package/dist/admin/{ListPage-ARW72ceH.js.map → ListPage-6HUcvFyL.js.map} +1 -1
  61. package/dist/admin/{ListPage-MvgbZlgc.js → ListPage-6O-9Hx-B.js} +2 -2
  62. package/dist/admin/{ListPage-MvgbZlgc.js.map → ListPage-6O-9Hx-B.js.map} +1 -1
  63. package/dist/admin/{ListPage-DxI49fdQ.js → ListPage-A2MPjJxi.js} +3 -3
  64. package/dist/admin/{ListPage-DxI49fdQ.js.map → ListPage-A2MPjJxi.js.map} +1 -1
  65. package/dist/admin/{ListPage-ZDEeaYvm.mjs → ListPage-a9qp7eE2.mjs} +2 -2
  66. package/dist/admin/{ListPage-ZDEeaYvm.mjs.map → ListPage-a9qp7eE2.mjs.map} +1 -1
  67. package/dist/admin/{ListPage-romICg1Z.js → ListPage-brwMJDfa.js} +2 -2
  68. package/dist/admin/{ListPage-romICg1Z.js.map → ListPage-brwMJDfa.js.map} +1 -1
  69. package/dist/admin/{ListPage-BIpKdUdv.mjs → ListPage-fE2OLTTE.mjs} +2 -2
  70. package/dist/admin/{ListPage-BIpKdUdv.mjs.map → ListPage-fE2OLTTE.mjs.map} +1 -1
  71. package/dist/admin/{ListPage-tniramRN.mjs → ListPage-nGtmXc03.mjs} +5 -5
  72. package/dist/admin/{ListPage-tniramRN.mjs.map → ListPage-nGtmXc03.mjs.map} +1 -1
  73. package/dist/admin/{ListPage-AUimd0cK.js → ListPage-vVa7B6wq.js} +5 -5
  74. package/dist/admin/{ListPage-AUimd0cK.js.map → ListPage-vVa7B6wq.js.map} +1 -1
  75. package/dist/admin/{ListPage-AI8neqxV.mjs → ListPage-xo4o9urf.mjs} +2 -2
  76. package/dist/admin/{ListPage-AI8neqxV.mjs.map → ListPage-xo4o9urf.mjs.map} +1 -1
  77. package/dist/admin/{ListView-GYuN24HV.mjs → ListView-2uHZ2TWm.mjs} +7 -4
  78. package/dist/admin/ListView-2uHZ2TWm.mjs.map +1 -0
  79. package/dist/admin/{ListView-6hlTFx5H.mjs → ListView-9exyBSMW.mjs} +8 -5
  80. package/dist/admin/ListView-9exyBSMW.mjs.map +1 -0
  81. package/dist/admin/{ListView-1VRmjfh8.js → ListView-VjkXS0Tf.js} +7 -4
  82. package/dist/admin/ListView-VjkXS0Tf.js.map +1 -0
  83. package/dist/admin/{ListView-ph55AtDF.js → ListView-upndjhrB.js} +8 -5
  84. package/dist/admin/ListView-upndjhrB.js.map +1 -0
  85. package/dist/admin/{Login-LxE58baP.mjs → Login-h0-a1KH5.mjs} +2 -2
  86. package/dist/admin/{Login-LxE58baP.mjs.map → Login-h0-a1KH5.mjs.map} +1 -1
  87. package/dist/admin/{Login-WeDT3kAj.js → Login-lAG7DHV7.js} +2 -2
  88. package/dist/admin/{Login-WeDT3kAj.js.map → Login-lAG7DHV7.js.map} +1 -1
  89. package/dist/admin/{MagicLinkEE-_fUwHKdf.js → MagicLinkEE-ApwIgTej.js} +3 -3
  90. package/dist/admin/{MagicLinkEE-_fUwHKdf.js.map → MagicLinkEE-ApwIgTej.js.map} +1 -1
  91. package/dist/admin/{MagicLinkEE-AO_SBYi5.mjs → MagicLinkEE-kjtWcwj6.mjs} +3 -3
  92. package/dist/admin/{MagicLinkEE-AO_SBYi5.mjs.map → MagicLinkEE-kjtWcwj6.mjs.map} +1 -1
  93. package/dist/admin/{MarketplacePage-m8Lf3adM.js → MarketplacePage-hNQbTtRI.js} +2 -2
  94. package/dist/admin/{MarketplacePage-m8Lf3adM.js.map → MarketplacePage-hNQbTtRI.js.map} +1 -1
  95. package/dist/admin/{MarketplacePage-26cUn4e5.mjs → MarketplacePage-mbqZ9JDl.mjs} +2 -2
  96. package/dist/admin/{MarketplacePage-26cUn4e5.mjs.map → MarketplacePage-mbqZ9JDl.mjs.map} +1 -1
  97. package/dist/admin/{Permissions-VzklM1ie.mjs → Permissions-RNmyxA77.mjs} +2 -2
  98. package/dist/admin/{Permissions-VzklM1ie.mjs.map → Permissions-RNmyxA77.mjs.map} +1 -1
  99. package/dist/admin/{Permissions-NRzJqZEr.js → Permissions-v5pxfHqR.js} +2 -2
  100. package/dist/admin/{Permissions-NRzJqZEr.js.map → Permissions-v5pxfHqR.js.map} +1 -1
  101. package/dist/admin/{ProfilePage-0d-k7mtP.mjs → ProfilePage-bDOjI8F-.mjs} +3 -3
  102. package/dist/admin/{ProfilePage-0d-k7mtP.mjs.map → ProfilePage-bDOjI8F-.mjs.map} +1 -1
  103. package/dist/admin/{ProfilePage-oSih4pcL.js → ProfilePage-ivIjdblf.js} +3 -3
  104. package/dist/admin/{ProfilePage-oSih4pcL.js.map → ProfilePage-ivIjdblf.js.map} +1 -1
  105. package/dist/admin/{SelectRoles-V_PZUtVT.mjs → SelectRoles-8cfbyMVr.mjs} +3 -3
  106. package/dist/admin/{SelectRoles-V_PZUtVT.mjs.map → SelectRoles-8cfbyMVr.mjs.map} +1 -1
  107. package/dist/admin/{SelectRoles-ZsRUnSdr.js → SelectRoles-ZZPSrkZE.js} +3 -3
  108. package/dist/admin/{SelectRoles-ZsRUnSdr.js.map → SelectRoles-ZZPSrkZE.js.map} +1 -1
  109. package/dist/admin/{SingleSignOnPage-kbLz1A_y.mjs → SingleSignOnPage-Ots_q1kK.mjs} +4 -4
  110. package/dist/admin/SingleSignOnPage-Ots_q1kK.mjs.map +1 -0
  111. package/dist/admin/{SingleSignOnPage-jtjy4_4U.js → SingleSignOnPage-dKN-oVc8.js} +4 -4
  112. package/dist/admin/SingleSignOnPage-dKN-oVc8.js.map +1 -0
  113. package/dist/admin/{Table-ZFyORhH3.mjs → Table-ILoLTYT2.mjs} +2 -2
  114. package/dist/admin/{Table-ZFyORhH3.mjs.map → Table-ILoLTYT2.mjs.map} +1 -1
  115. package/dist/admin/{Table-kMt3jQ2g.js → Table-rh1Goj9B.js} +2 -2
  116. package/dist/admin/{Table-kMt3jQ2g.js.map → Table-rh1Goj9B.js.map} +1 -1
  117. package/dist/admin/{TokenTypeSelect-Ngz4ONEL.js → TokenTypeSelect-Bc6Gx5Og.js} +2 -2
  118. package/dist/admin/{TokenTypeSelect-Ngz4ONEL.js.map → TokenTypeSelect-Bc6Gx5Og.js.map} +1 -1
  119. package/dist/admin/{TokenTypeSelect-oOKwvsQ2.mjs → TokenTypeSelect-oUpUNcrP.mjs} +2 -2
  120. package/dist/admin/{TokenTypeSelect-oOKwvsQ2.mjs.map → TokenTypeSelect-oUpUNcrP.mjs.map} +1 -1
  121. package/dist/admin/{UseCasePage-qrhHiF5S.js → UseCasePage-NYPLK0s5.js} +2 -2
  122. package/dist/admin/{UseCasePage-qrhHiF5S.js.map → UseCasePage-NYPLK0s5.js.map} +1 -1
  123. package/dist/admin/{UseCasePage-2wum_xzS.mjs → UseCasePage-siicRv21.mjs} +2 -2
  124. package/dist/admin/{UseCasePage-2wum_xzS.mjs.map → UseCasePage-siicRv21.mjs.map} +1 -1
  125. package/dist/admin/{constants-enREoOEa.mjs → constants-GoVT0wVo.mjs} +2 -2
  126. package/dist/admin/{constants-enREoOEa.mjs.map → constants-GoVT0wVo.mjs.map} +1 -1
  127. package/dist/admin/{constants-jQFnBWVd.js → constants-wYcX8us0.js} +2 -2
  128. package/dist/admin/{constants-jQFnBWVd.js.map → constants-wYcX8us0.js.map} +1 -1
  129. package/dist/admin/{index-dcaxfEDi.js → index-e8PHZx2A.js} +48 -52
  130. package/dist/admin/index-e8PHZx2A.js.map +1 -0
  131. package/dist/admin/{index-oBxjGftZ.mjs → index-i37IN_fH.mjs} +51 -55
  132. package/dist/admin/index-i37IN_fH.mjs.map +1 -0
  133. package/dist/admin/index.js +1 -1
  134. package/dist/admin/index.mjs +1 -1
  135. package/dist/admin/src/components/Form.d.ts +1 -1
  136. package/dist/admin/src/hooks/useAdminRoles.d.ts +2 -2
  137. package/dist/admin/src/index.d.ts +1 -1
  138. package/dist/admin/src/services/auth.d.ts +3 -3
  139. package/dist/admin/src/services/users.d.ts +4 -4
  140. package/dist/admin/{useAdminRoles-Q2BxHs4o.js → useAdminRoles-JOPnwG5K.js} +2 -2
  141. package/dist/admin/{useAdminRoles-Q2BxHs4o.js.map → useAdminRoles-JOPnwG5K.js.map} +1 -1
  142. package/dist/admin/{useAdminRoles-HWIbyk2Z.mjs → useAdminRoles-qJLjTITl.mjs} +2 -2
  143. package/dist/admin/{useAdminRoles-HWIbyk2Z.mjs.map → useAdminRoles-qJLjTITl.mjs.map} +1 -1
  144. package/dist/admin/{validation-gH-06ERZ.mjs → validation-man-qOMc.mjs} +2 -2
  145. package/dist/admin/{validation-gH-06ERZ.mjs.map → validation-man-qOMc.mjs.map} +1 -1
  146. package/dist/admin/{validation-2MGgF10o.js → validation-mliIGlD0.js} +2 -2
  147. package/dist/admin/{validation-2MGgF10o.js.map → validation-mliIGlD0.js.map} +1 -1
  148. package/dist/ee/admin/src/hooks/useLicenseLimits.d.ts +2 -2
  149. package/dist/ee/server/index.js +102 -1916
  150. package/dist/ee/server/index.js.map +1 -1
  151. package/dist/ee/server/index.mjs +107 -1920
  152. package/dist/ee/server/index.mjs.map +1 -1
  153. package/dist/package.json.d.ts +9 -9
  154. package/dist/server/index.js +65 -48
  155. package/dist/server/index.js.map +1 -1
  156. package/dist/server/index.mjs +41 -41
  157. package/dist/server/index.mjs.map +1 -1
  158. package/dist/server/src/bootstrap.d.ts.map +1 -1
  159. package/dist/server/src/domain/permission/index.d.ts.map +1 -1
  160. package/dist/server/src/middlewares/rateLimit.d.ts.map +1 -1
  161. package/dist/server/src/register.d.ts.map +1 -1
  162. package/dist/server/src/routes/serve-admin-panel.d.ts.map +1 -1
  163. package/dist/server/src/services/permission/sections-builder/handlers.d.ts.map +1 -1
  164. package/dist/server/src/services/transfer/token.d.ts.map +1 -1
  165. package/dist/shared/contracts/admin.d.ts +7 -1
  166. package/dist/shared/contracts/admin.d.ts.map +1 -1
  167. package/package.json +10 -10
  168. package/dist/admin/ListView-1VRmjfh8.js.map +0 -1
  169. package/dist/admin/ListView-6hlTFx5H.mjs.map +0 -1
  170. package/dist/admin/ListView-GYuN24HV.mjs.map +0 -1
  171. package/dist/admin/ListView-ph55AtDF.js.map +0 -1
  172. package/dist/admin/SingleSignOnPage-jtjy4_4U.js.map +0 -1
  173. package/dist/admin/SingleSignOnPage-kbLz1A_y.mjs.map +0 -1
  174. package/dist/admin/index-dcaxfEDi.js.map +0 -1
  175. package/dist/admin/index-oBxjGftZ.mjs.map +0 -1
@@ -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 "@strapi/types";
5
- import { isNil, castArray, pipe, pickBy, get, keys, differenceWith, isEqual, getOr, has, clamp, difference, set, merge, map, uniq, isEmpty, toLower, isFunction, toString, toNumber, assign, reverse, take, prop, drop, pick, isString, defaultsDeep, forEach, filter, flow, size, mean, max, sum, defaultTo, mapValues, curry, includes, isArray, omit, isUndefined, update, property } from "lodash/fp";
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 semver from "semver";
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$1 = "admin";
96
+ const name = "admin";
98
97
  const adminAuthStrategy = {
99
- name: name$1,
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 workflow = {
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 reviewWorkflows$1 = {
1336
- type: "admin",
1337
- routes: [
1338
- // Review workflow
1339
- {
1340
- method: "POST",
1341
- path: "/review-workflows/workflows",
1342
- handler: "workflows.create",
1343
- config: {
1344
- middlewares: [enableFeatureMiddleware("review-workflows")],
1345
- policies: [
1346
- "admin::isAuthenticatedAdmin",
1347
- {
1348
- name: "admin::hasPermissions",
1349
- config: {
1350
- actions: ["admin::review-workflows.create"]
1351
- }
1352
- }
1353
- ]
1354
- }
1355
- },
1356
- {
1357
- method: "PUT",
1358
- path: "/review-workflows/workflows/:id",
1359
- handler: "workflows.update",
1360
- config: {
1361
- middlewares: [enableFeatureMiddleware("review-workflows")],
1362
- policies: [
1363
- "admin::isAuthenticatedAdmin",
1364
- {
1365
- name: "admin::hasPermissions",
1366
- config: {
1367
- actions: ["admin::review-workflows.update"]
1368
- }
1369
- }
1370
- ]
1371
- }
1372
- },
1373
- {
1374
- method: "DELETE",
1375
- path: "/review-workflows/workflows/:id",
1376
- handler: "workflows.delete",
1377
- config: {
1378
- middlewares: [enableFeatureMiddleware("review-workflows")],
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$6();
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$5 } = errors;
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$5(
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$3 } = errors;
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$3("You must have at least one user with super admin role.");
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$3("You must have at least one user with super admin role.");
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$3("You must have at least one user with super admin role.");
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$3("You must have at least one user with super admin role.");
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((property2, action) => {
3193
- return pipe(prop("options.applyToProperties"), includes(property2))(action);
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((property2) => applyToProperties.includes(property2));
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,