@strapi/admin 5.0.0-beta.3 → 5.0.0-beta.5

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