@strapi/content-manager 0.0.0-experimental.d954d57341a6623992a0d211daaec8e245c3517d → 0.0.0-experimental.dad3c50630ca4fd9eccdcbe549ee632fc572e23d

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 (225) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -1
  3. package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -1
  4. package/dist/_chunks/{ComponentConfigurationPage-WRPUXGd6.js → ComponentConfigurationPage-BLWQy8ru.js} +5 -6
  5. package/dist/_chunks/{ComponentConfigurationPage-WRPUXGd6.js.map → ComponentConfigurationPage-BLWQy8ru.js.map} +1 -1
  6. package/dist/_chunks/{ComponentConfigurationPage-gdUj_t-O.mjs → ComponentConfigurationPage-CtIa3aa2.mjs} +4 -4
  7. package/dist/_chunks/{ComponentConfigurationPage-gdUj_t-O.mjs.map → ComponentConfigurationPage-CtIa3aa2.mjs.map} +1 -1
  8. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js → ComponentIcon-CRbtQEUV.js} +2 -3
  9. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js.map → ComponentIcon-CRbtQEUV.js.map} +1 -1
  10. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -1
  11. package/dist/_chunks/{EditConfigurationPage-BwuIPOJG.mjs → EditConfigurationPage-DsPR2DVk.mjs} +4 -4
  12. package/dist/_chunks/{EditConfigurationPage-BwuIPOJG.mjs.map → EditConfigurationPage-DsPR2DVk.mjs.map} +1 -1
  13. package/dist/_chunks/{EditConfigurationPage-C1vjMBgy.js → EditConfigurationPage-RQkymxCy.js} +5 -6
  14. package/dist/_chunks/{EditConfigurationPage-C1vjMBgy.js.map → EditConfigurationPage-RQkymxCy.js.map} +1 -1
  15. package/dist/_chunks/{EditViewPage-DbcGfyqK.js → EditViewPage-B-kExt8C.js} +63 -13
  16. package/dist/_chunks/EditViewPage-B-kExt8C.js.map +1 -0
  17. package/dist/_chunks/{EditViewPage-0MiFkXa8.mjs → EditViewPage-BPyVuPfM.mjs} +63 -12
  18. package/dist/_chunks/EditViewPage-BPyVuPfM.mjs.map +1 -0
  19. package/dist/_chunks/{Field-BG1xu38N.js → Field-DPIsQRre.js} +680 -261
  20. package/dist/_chunks/Field-DPIsQRre.js.map +1 -0
  21. package/dist/_chunks/{Field-BDMSCcy5.mjs → Field-Dltnt1km.mjs} +676 -257
  22. package/dist/_chunks/Field-Dltnt1km.mjs.map +1 -0
  23. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  24. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  25. package/dist/_chunks/{Form-9BnFyUjy.js → Form-BFi4MXMT.js} +43 -22
  26. package/dist/_chunks/Form-BFi4MXMT.js.map +1 -0
  27. package/dist/_chunks/{Form-CPVWavB8.mjs → Form-C1IcWm1u.mjs} +41 -19
  28. package/dist/_chunks/Form-C1IcWm1u.mjs.map +1 -0
  29. package/dist/_chunks/{History-BVpd8LP3.mjs → History-04ChQ4pl.mjs} +108 -116
  30. package/dist/_chunks/History-04ChQ4pl.mjs.map +1 -0
  31. package/dist/_chunks/{History-BWWxLt2Z.js → History-wjcK4L0C.js} +107 -116
  32. package/dist/_chunks/History-wjcK4L0C.js.map +1 -0
  33. package/dist/_chunks/{ListConfigurationPage-DozVMKcR.mjs → ListConfigurationPage-BYqPYLSU.mjs} +25 -12
  34. package/dist/_chunks/ListConfigurationPage-BYqPYLSU.mjs.map +1 -0
  35. package/dist/_chunks/{ListConfigurationPage-6swzjdAZ.js → ListConfigurationPage-CRbxIC3J.js} +25 -13
  36. package/dist/_chunks/ListConfigurationPage-CRbxIC3J.js.map +1 -0
  37. package/dist/_chunks/{ListViewPage-BlzfjS2Q.js → ListViewPage-D5NY9183.js} +121 -81
  38. package/dist/_chunks/ListViewPage-D5NY9183.js.map +1 -0
  39. package/dist/_chunks/{ListViewPage-Ds0ulgfG.mjs → ListViewPage-FU2LBuhl.mjs} +118 -77
  40. package/dist/_chunks/ListViewPage-FU2LBuhl.mjs.map +1 -0
  41. package/dist/_chunks/{NoContentTypePage-D2nCCWEl.js → NoContentTypePage-BgQVE_Qb.js} +2 -2
  42. package/dist/_chunks/{NoContentTypePage-D2nCCWEl.js.map → NoContentTypePage-BgQVE_Qb.js.map} +1 -1
  43. package/dist/_chunks/{NoContentTypePage-BH11kaKt.mjs → NoContentTypePage-DCKUkwb8.mjs} +2 -2
  44. package/dist/_chunks/{NoContentTypePage-BH11kaKt.mjs.map → NoContentTypePage-DCKUkwb8.mjs.map} +1 -1
  45. package/dist/_chunks/{NoPermissionsPage-DN_JlsU2.js → NoPermissionsPage-C5jwn70o.js} +2 -2
  46. package/dist/_chunks/{NoPermissionsPage-DN_JlsU2.js.map → NoPermissionsPage-C5jwn70o.js.map} +1 -1
  47. package/dist/_chunks/{NoPermissionsPage-BT2Tn0D_.mjs → NoPermissionsPage-jqve7C8l.mjs} +2 -2
  48. package/dist/_chunks/{NoPermissionsPage-BT2Tn0D_.mjs.map → NoPermissionsPage-jqve7C8l.mjs.map} +1 -1
  49. package/dist/_chunks/Preview-BMYN548c.mjs +294 -0
  50. package/dist/_chunks/Preview-BMYN548c.mjs.map +1 -0
  51. package/dist/_chunks/Preview-DaOihysv.js +312 -0
  52. package/dist/_chunks/Preview-DaOihysv.js.map +1 -0
  53. package/dist/_chunks/{Relations-CcgFTcWo.js → Relations-CTGM7Hv5.js} +76 -43
  54. package/dist/_chunks/Relations-CTGM7Hv5.js.map +1 -0
  55. package/dist/_chunks/{Relations-Dnag3fhV.mjs → Relations-gscPkxjF.mjs} +76 -42
  56. package/dist/_chunks/Relations-gscPkxjF.mjs.map +1 -0
  57. package/dist/_chunks/{en-fbKQxLGn.js → en-BzQmavmK.js} +37 -18
  58. package/dist/_chunks/{en-fbKQxLGn.js.map → en-BzQmavmK.js.map} +1 -1
  59. package/dist/_chunks/{en-Ux26r5pl.mjs → en-CSxLmrh1.mjs} +37 -18
  60. package/dist/_chunks/{en-Ux26r5pl.mjs.map → en-CSxLmrh1.mjs.map} +1 -1
  61. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  62. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  63. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  64. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  65. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  66. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  67. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  68. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  69. package/dist/_chunks/hooks-BAaaKPS_.js.map +1 -1
  70. package/dist/_chunks/{index-CWpLBSt0.js → index-Ca7YWlAA.js} +1271 -746
  71. package/dist/_chunks/index-Ca7YWlAA.js.map +1 -0
  72. package/dist/_chunks/{index-JNNNKUHs.mjs → index-DqasUQ6Q.mjs} +1288 -763
  73. package/dist/_chunks/index-DqasUQ6Q.mjs.map +1 -0
  74. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  75. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  76. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  77. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  78. package/dist/_chunks/{layout--iHdZzRk.js → layout-BW80JSCd.js} +27 -15
  79. package/dist/_chunks/layout-BW80JSCd.js.map +1 -0
  80. package/dist/_chunks/{layout-DC503LnF.mjs → layout-W3clJSCy.mjs} +28 -15
  81. package/dist/_chunks/layout-W3clJSCy.mjs.map +1 -0
  82. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  83. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  84. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  85. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  86. package/dist/_chunks/{relations-CTje5t-a.mjs → relations-BlDkoeWh.mjs} +6 -7
  87. package/dist/_chunks/relations-BlDkoeWh.mjs.map +1 -0
  88. package/dist/_chunks/{relations-BbHizA5K.js → relations-C9Usz9k5.js} +6 -7
  89. package/dist/_chunks/relations-C9Usz9k5.js.map +1 -0
  90. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  91. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  92. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  93. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  94. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js → useDragAndDrop-BMtgCYzL.js} +5 -9
  95. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js.map → useDragAndDrop-BMtgCYzL.js.map} +1 -1
  96. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs → useDragAndDrop-DJ6jqvZN.mjs} +4 -7
  97. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs.map → useDragAndDrop-DJ6jqvZN.mjs.map} +1 -1
  98. package/dist/admin/index.js +3 -1
  99. package/dist/admin/index.js.map +1 -1
  100. package/dist/admin/index.mjs +5 -3
  101. package/dist/admin/src/content-manager.d.ts +3 -2
  102. package/dist/admin/src/exports.d.ts +2 -1
  103. package/dist/admin/src/history/index.d.ts +3 -0
  104. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  105. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  106. package/dist/admin/src/index.d.ts +1 -0
  107. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  108. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +2 -1
  109. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +3 -3
  110. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.d.ts +7 -0
  111. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/utils/prismLanguages.d.ts +49 -0
  112. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +5 -0
  113. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.d.ts +4 -1
  114. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +4 -1
  115. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  116. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  117. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  118. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  119. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  120. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  121. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  122. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  123. package/dist/admin/src/preview/index.d.ts +4 -0
  124. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  125. package/dist/admin/src/preview/routes.d.ts +3 -0
  126. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  127. package/dist/admin/src/router.d.ts +1 -1
  128. package/dist/admin/src/services/api.d.ts +1 -1
  129. package/dist/admin/src/services/components.d.ts +2 -2
  130. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  131. package/dist/admin/src/services/documents.d.ts +19 -20
  132. package/dist/admin/src/services/init.d.ts +1 -1
  133. package/dist/admin/src/services/relations.d.ts +2 -2
  134. package/dist/admin/src/services/uid.d.ts +3 -3
  135. package/dist/admin/src/utils/validation.d.ts +4 -1
  136. package/dist/server/index.js +765 -437
  137. package/dist/server/index.js.map +1 -1
  138. package/dist/server/index.mjs +766 -437
  139. package/dist/server/index.mjs.map +1 -1
  140. package/dist/server/src/bootstrap.d.ts.map +1 -1
  141. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  142. package/dist/server/src/controllers/index.d.ts.map +1 -1
  143. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  144. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  145. package/dist/server/src/controllers/utils/metadata.d.ts +16 -1
  146. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  147. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  148. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  149. package/dist/server/src/history/controllers/history-version.d.ts +1 -1
  150. package/dist/server/src/history/controllers/history-version.d.ts.map +1 -1
  151. package/dist/server/src/history/services/history.d.ts +3 -3
  152. package/dist/server/src/history/services/history.d.ts.map +1 -1
  153. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  154. package/dist/server/src/history/services/utils.d.ts +8 -12
  155. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  156. package/dist/server/src/index.d.ts +7 -6
  157. package/dist/server/src/index.d.ts.map +1 -1
  158. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  159. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  160. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  161. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  162. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  163. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  164. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  165. package/dist/server/src/preview/index.d.ts +4 -0
  166. package/dist/server/src/preview/index.d.ts.map +1 -0
  167. package/dist/server/src/preview/routes/index.d.ts +8 -0
  168. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  169. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  170. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  171. package/dist/server/src/preview/services/index.d.ts +16 -0
  172. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  173. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  174. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  175. package/dist/server/src/preview/services/preview.d.ts +12 -0
  176. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  177. package/dist/server/src/preview/utils.d.ts +19 -0
  178. package/dist/server/src/preview/utils.d.ts.map +1 -0
  179. package/dist/server/src/register.d.ts.map +1 -1
  180. package/dist/server/src/routes/index.d.ts.map +1 -1
  181. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  182. package/dist/server/src/services/document-metadata.d.ts +12 -10
  183. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  184. package/dist/server/src/services/index.d.ts +7 -6
  185. package/dist/server/src/services/index.d.ts.map +1 -1
  186. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  187. package/dist/server/src/services/utils/populate.d.ts +2 -2
  188. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  189. package/dist/server/src/utils/index.d.ts +2 -0
  190. package/dist/server/src/utils/index.d.ts.map +1 -1
  191. package/dist/shared/contracts/collection-types.d.ts +3 -1
  192. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  193. package/dist/shared/contracts/index.d.ts +1 -0
  194. package/dist/shared/contracts/index.d.ts.map +1 -1
  195. package/dist/shared/contracts/preview.d.ts +27 -0
  196. package/dist/shared/contracts/preview.d.ts.map +1 -0
  197. package/dist/shared/index.js +4 -0
  198. package/dist/shared/index.js.map +1 -1
  199. package/dist/shared/index.mjs +4 -0
  200. package/dist/shared/index.mjs.map +1 -1
  201. package/package.json +17 -15
  202. package/dist/_chunks/EditViewPage-0MiFkXa8.mjs.map +0 -1
  203. package/dist/_chunks/EditViewPage-DbcGfyqK.js.map +0 -1
  204. package/dist/_chunks/Field-BDMSCcy5.mjs.map +0 -1
  205. package/dist/_chunks/Field-BG1xu38N.js.map +0 -1
  206. package/dist/_chunks/Form-9BnFyUjy.js.map +0 -1
  207. package/dist/_chunks/Form-CPVWavB8.mjs.map +0 -1
  208. package/dist/_chunks/History-BVpd8LP3.mjs.map +0 -1
  209. package/dist/_chunks/History-BWWxLt2Z.js.map +0 -1
  210. package/dist/_chunks/ListConfigurationPage-6swzjdAZ.js.map +0 -1
  211. package/dist/_chunks/ListConfigurationPage-DozVMKcR.mjs.map +0 -1
  212. package/dist/_chunks/ListViewPage-BlzfjS2Q.js.map +0 -1
  213. package/dist/_chunks/ListViewPage-Ds0ulgfG.mjs.map +0 -1
  214. package/dist/_chunks/Relations-CcgFTcWo.js.map +0 -1
  215. package/dist/_chunks/Relations-Dnag3fhV.mjs.map +0 -1
  216. package/dist/_chunks/index-CWpLBSt0.js.map +0 -1
  217. package/dist/_chunks/index-JNNNKUHs.mjs.map +0 -1
  218. package/dist/_chunks/layout--iHdZzRk.js.map +0 -1
  219. package/dist/_chunks/layout-DC503LnF.mjs.map +0 -1
  220. package/dist/_chunks/relations-BbHizA5K.js.map +0 -1
  221. package/dist/_chunks/relations-CTje5t-a.mjs.map +0 -1
  222. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  223. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  224. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  225. package/strapi-server.js +0 -3
@@ -10,8 +10,7 @@ const qs = require("qs");
10
10
  const slugify = require("@sindresorhus/slugify");
11
11
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
12
12
  function _interopNamespace(e) {
13
- if (e && e.__esModule)
14
- return e;
13
+ if (e && e.__esModule) return e;
15
14
  const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
16
15
  if (e) {
17
16
  for (const k in e) {
@@ -33,10 +32,10 @@ const isNil__default = /* @__PURE__ */ _interopDefault(isNil);
33
32
  const ___default = /* @__PURE__ */ _interopDefault(_);
34
33
  const qs__default = /* @__PURE__ */ _interopDefault(qs);
35
34
  const slugify__default = /* @__PURE__ */ _interopDefault(slugify);
36
- const getService$1 = (name) => {
35
+ const getService$2 = (name) => {
37
36
  return strapi.plugin("content-manager").service(name);
38
37
  };
39
- function getService(strapi2, name) {
38
+ function getService$1(strapi2, name) {
40
39
  return strapi2.service(`plugin::content-manager.${name}`);
41
40
  }
42
41
  const historyRestoreVersionSchema = yup__namespace.object().shape({
@@ -72,7 +71,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
72
71
  if (!isSingleType && (!contentTypeUid || !ctx.query.documentId)) {
73
72
  throw new strapiUtils.errors.ForbiddenError("contentType and documentId are required");
74
73
  }
75
- const permissionChecker2 = getService$1("permission-checker").create({
74
+ const permissionChecker2 = getService$2("permission-checker").create({
76
75
  userAbility: ctx.state.userAbility,
77
76
  model: ctx.query.contentType
78
77
  });
@@ -80,7 +79,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
80
79
  return ctx.forbidden();
81
80
  }
82
81
  const query = await permissionChecker2.sanitizeQuery(ctx.query);
83
- const { results, pagination } = await getService(strapi2, "history").findVersionsPage({
82
+ const { results, pagination } = await getService$1(strapi2, "history").findVersionsPage({
84
83
  query: {
85
84
  ...query,
86
85
  ...getValidPagination({ page: query.page, pageSize: query.pageSize })
@@ -105,14 +104,14 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
105
104
  async restoreVersion(ctx) {
106
105
  const request = ctx.request;
107
106
  await validateRestoreVersion(request.body, "contentType is required");
108
- const permissionChecker2 = getService$1("permission-checker").create({
107
+ const permissionChecker2 = getService$2("permission-checker").create({
109
108
  userAbility: ctx.state.userAbility,
110
109
  model: request.body.contentType
111
110
  });
112
111
  if (permissionChecker2.cannot.update()) {
113
112
  throw new strapiUtils.errors.ForbiddenError();
114
113
  }
115
- const restoredDocument = await getService(strapi2, "history").restoreVersion(
114
+ const restoredDocument = await getService$1(strapi2, "history").restoreVersion(
116
115
  request.params.versionId
117
116
  );
118
117
  return {
@@ -121,7 +120,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
121
120
  }
122
121
  };
123
122
  };
124
- const controllers$1 = {
123
+ const controllers$2 = {
125
124
  "history-version": createHistoryVersionController
126
125
  /**
127
126
  * Casting is needed because the types aren't aware that Strapi supports
@@ -167,8 +166,7 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
167
166
  };
168
167
  const getRelationRestoreValue = async (versionRelationData, attribute) => {
169
168
  if (Array.isArray(versionRelationData)) {
170
- if (versionRelationData.length === 0)
171
- return versionRelationData;
169
+ if (versionRelationData.length === 0) return versionRelationData;
172
170
  const existingAndMissingRelations = await Promise.all(
173
171
  versionRelationData.map((relation) => {
174
172
  return strapi2.documents(attribute.target).findOne({
@@ -177,19 +175,16 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
177
175
  });
178
176
  })
179
177
  );
180
- return existingAndMissingRelations.filter(
181
- (relation) => relation !== null
182
- );
178
+ return existingAndMissingRelations.filter((relation) => relation !== null);
183
179
  }
184
180
  return strapi2.documents(attribute.target).findOne({
185
181
  documentId: versionRelationData.documentId,
186
182
  locale: versionRelationData.locale || void 0
187
183
  });
188
184
  };
189
- const getMediaRestoreValue = async (versionRelationData, attribute) => {
190
- if (attribute.multiple) {
185
+ const getMediaRestoreValue = async (versionRelationData) => {
186
+ if (Array.isArray(versionRelationData)) {
191
187
  const existingAndMissingMedias = await Promise.all(
192
- // @ts-expect-error Fix the type definitions so this isn't any
193
188
  versionRelationData.map((media) => {
194
189
  return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
195
190
  })
@@ -199,10 +194,11 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
199
194
  return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
200
195
  };
201
196
  const localesService = strapi2.plugin("i18n")?.service("locales");
197
+ const i18nContentTypeService = strapi2.plugin("i18n")?.service("content-types");
202
198
  const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
199
+ const isLocalizedContentType = (model) => i18nContentTypeService ? i18nContentTypeService.isLocalizedContentType(model) : false;
203
200
  const getLocaleDictionary = async () => {
204
- if (!localesService)
205
- return {};
201
+ if (!localesService) return {};
206
202
  const locales = await localesService.find() || [];
207
203
  return locales.reduce(
208
204
  (acc, locale) => {
@@ -226,31 +222,53 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
226
222
  const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
227
223
  return documentMetadataService.getStatus(document, meta.availableStatus);
228
224
  };
229
- const getDeepPopulate2 = (uid2) => {
225
+ const getComponentFields = (componentUID) => {
226
+ return Object.entries(strapi2.getModel(componentUID).attributes).reduce(
227
+ (fieldsAcc, [key, attribute]) => {
228
+ if (!["relation", "media", "component", "dynamiczone"].includes(attribute.type)) {
229
+ fieldsAcc.push(key);
230
+ }
231
+ return fieldsAcc;
232
+ },
233
+ []
234
+ );
235
+ };
236
+ const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
230
237
  const model = strapi2.getModel(uid2);
231
238
  const attributes = Object.entries(model.attributes);
239
+ const fieldSelector = useDatabaseSyntax ? "select" : "fields";
232
240
  return attributes.reduce((acc, [attributeName, attribute]) => {
233
241
  switch (attribute.type) {
234
242
  case "relation": {
243
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
244
+ if (isMorphRelation) {
245
+ break;
246
+ }
235
247
  const isVisible2 = strapiUtils.contentTypes.isVisibleAttribute(model, attributeName);
236
248
  if (isVisible2) {
237
- acc[attributeName] = { fields: ["documentId", "locale", "publishedAt"] };
249
+ acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
238
250
  }
239
251
  break;
240
252
  }
241
253
  case "media": {
242
- acc[attributeName] = { fields: ["id"] };
254
+ acc[attributeName] = { [fieldSelector]: ["id"] };
243
255
  break;
244
256
  }
245
257
  case "component": {
246
258
  const populate = getDeepPopulate2(attribute.component);
247
- acc[attributeName] = { populate };
259
+ acc[attributeName] = {
260
+ populate,
261
+ [fieldSelector]: getComponentFields(attribute.component)
262
+ };
248
263
  break;
249
264
  }
250
265
  case "dynamiczone": {
251
266
  const populatedComponents = (attribute.components || []).reduce(
252
267
  (acc2, componentUID) => {
253
- acc2[componentUID] = { populate: getDeepPopulate2(componentUID) };
268
+ acc2[componentUID] = {
269
+ populate: getDeepPopulate2(componentUID),
270
+ [fieldSelector]: getComponentFields(componentUID)
271
+ };
254
272
  return acc2;
255
273
  },
256
274
  {}
@@ -312,6 +330,7 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
312
330
  getRelationRestoreValue,
313
331
  getMediaRestoreValue,
314
332
  getDefaultLocale,
333
+ isLocalizedContentType,
315
334
  getLocaleDictionary,
316
335
  getRetentionDays,
317
336
  getVersionStatus,
@@ -334,7 +353,13 @@ const createHistoryService = ({ strapi: strapi2 }) => {
334
353
  });
335
354
  },
336
355
  async findVersionsPage(params) {
337
- const locale = params.query.locale || await serviceUtils.getDefaultLocale();
356
+ const schema = strapi2.getModel(params.query.contentType);
357
+ const isLocalizedContentType = serviceUtils.isLocalizedContentType(schema);
358
+ const defaultLocale = await serviceUtils.getDefaultLocale();
359
+ let locale = null;
360
+ if (isLocalizedContentType) {
361
+ locale = params.query.locale || defaultLocale;
362
+ }
338
363
  const [{ results, pagination }, localeDictionary] = await Promise.all([
339
364
  query.findPage({
340
365
  ...params.query,
@@ -350,78 +375,76 @@ const createHistoryService = ({ strapi: strapi2 }) => {
350
375
  }),
351
376
  serviceUtils.getLocaleDictionary()
352
377
  ]);
353
- const populateEntryRelations = async (entry) => {
354
- const entryWithRelations = await Object.entries(entry.schema).reduce(
355
- async (currentDataWithRelations, [attributeKey, attributeSchema]) => {
356
- const attributeValue = entry.data[attributeKey];
357
- const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
358
- if (attributeSchema.type === "media") {
359
- const permissionChecker2 = getService$1("permission-checker").create({
360
- userAbility: params.state.userAbility,
361
- model: "plugin::upload.file"
362
- });
363
- const response = await serviceUtils.buildMediaResponse(attributeValues);
364
- const sanitizedResults = await Promise.all(
365
- response.results.map((media) => permissionChecker2.sanitizeOutput(media))
366
- );
367
- return {
368
- ...await currentDataWithRelations,
369
- [attributeKey]: {
370
- results: sanitizedResults,
371
- meta: response.meta
372
- }
373
- };
378
+ const populateEntry = async (entry) => {
379
+ return strapiUtils.traverseEntity(
380
+ async (options, utils) => {
381
+ if (!options.attribute) return;
382
+ if (!options.value) return;
383
+ const currentValue = Array.isArray(options.value) ? options.value : [options.value];
384
+ if (options.attribute.type === "component") {
385
+ utils.remove("id");
374
386
  }
375
- if (attributeSchema.type === "relation" && attributeSchema.relation !== "morphToOne" && attributeSchema.relation !== "morphToMany") {
376
- if (attributeSchema.target === "admin::user") {
387
+ if (options.attribute.type === "relation" && // TODO: handle polymorphic relations
388
+ options.attribute.relation !== "morphToOne" && options.attribute.relation !== "morphToMany") {
389
+ if (options.attribute.target === "admin::user") {
377
390
  const adminUsers = await Promise.all(
378
- attributeValues.map((userToPopulate) => {
391
+ currentValue.map((userToPopulate) => {
379
392
  if (userToPopulate == null) {
380
393
  return null;
381
394
  }
382
- return strapi2.query("admin::user").findOne({ where: { id: userToPopulate.id } });
395
+ return strapi2.query("admin::user").findOne({
396
+ where: {
397
+ ...userToPopulate.id ? { id: userToPopulate.id } : {},
398
+ ...userToPopulate.documentId ? { documentId: userToPopulate.documentId } : {}
399
+ }
400
+ });
383
401
  })
384
402
  );
385
- return {
386
- ...await currentDataWithRelations,
387
- /**
388
- * Ideally we would return the same "{results: [], meta: {}}" shape, however,
389
- * when sanitizing the data as a whole in the controller before sending to the client,
390
- * the data for admin relation user is completely sanitized if we return an object here as opposed to an array.
391
- */
392
- [attributeKey]: adminUsers
393
- };
403
+ utils.set(options.key, adminUsers);
394
404
  }
395
- const permissionChecker2 = getService$1("permission-checker").create({
405
+ const permissionChecker2 = getService$2("permission-checker").create({
396
406
  userAbility: params.state.userAbility,
397
- model: attributeSchema.target
407
+ model: options.attribute.target
398
408
  });
399
409
  const response = await serviceUtils.buildRelationReponse(
400
- attributeValues,
401
- attributeSchema
410
+ currentValue,
411
+ options.attribute
402
412
  );
403
413
  const sanitizedResults = await Promise.all(
404
414
  response.results.map((media) => permissionChecker2.sanitizeOutput(media))
405
415
  );
406
- return {
407
- ...await currentDataWithRelations,
408
- [attributeKey]: {
409
- results: sanitizedResults,
410
- meta: response.meta
411
- }
412
- };
416
+ utils.set(options.key, {
417
+ results: sanitizedResults,
418
+ meta: response.meta
419
+ });
413
420
  }
414
- return currentDataWithRelations;
421
+ if (options.attribute.type === "media") {
422
+ const permissionChecker2 = getService$2("permission-checker").create({
423
+ userAbility: params.state.userAbility,
424
+ model: "plugin::upload.file"
425
+ });
426
+ const response = await serviceUtils.buildMediaResponse(currentValue);
427
+ const sanitizedResults = await Promise.all(
428
+ response.results.map((media) => permissionChecker2.sanitizeOutput(media))
429
+ );
430
+ utils.set(options.key, {
431
+ results: sanitizedResults,
432
+ meta: response.meta
433
+ });
434
+ }
435
+ },
436
+ {
437
+ schema,
438
+ getModel: strapi2.getModel.bind(strapi2)
415
439
  },
416
- Promise.resolve(entry.data)
440
+ entry.data
417
441
  );
418
- return entryWithRelations;
419
442
  };
420
443
  const formattedResults = await Promise.all(
421
444
  results.map(async (result) => {
422
445
  return {
423
446
  ...result,
424
- data: await populateEntryRelations(result),
447
+ data: await populateEntry(result),
425
448
  meta: {
426
449
  unknownAttributes: serviceUtils.getSchemaAttributesDiff(
427
450
  result.schema,
@@ -452,30 +475,44 @@ const createHistoryService = ({ strapi: strapi2 }) => {
452
475
  // Clone to avoid mutating the original version data
453
476
  structuredClone(version.data)
454
477
  );
455
- const sanitizedSchemaAttributes = fp.omit(
456
- FIELDS_TO_IGNORE,
457
- contentTypeSchemaAttributes
458
- );
459
- const reducer = strapiUtils.async.reduce(Object.entries(sanitizedSchemaAttributes));
460
- const dataWithoutMissingRelations = await reducer(
461
- async (previousRelationAttributes, [name, attribute]) => {
462
- const versionRelationData = version.data[name];
463
- if (!versionRelationData) {
464
- return previousRelationAttributes;
478
+ const schema = structuredClone(version.schema);
479
+ schema.attributes = fp.omit(FIELDS_TO_IGNORE, contentTypeSchemaAttributes);
480
+ const dataWithoutMissingRelations = await strapiUtils.traverseEntity(
481
+ async (options, utils) => {
482
+ if (!options.attribute) return;
483
+ if (options.attribute.type === "component") {
484
+ utils.remove("id");
485
+ if (options.attribute.repeatable && options.value === null) {
486
+ utils.set(options.key, []);
487
+ }
465
488
  }
466
- if (attribute.type === "relation" && // TODO: handle polymorphic relations
467
- attribute.relation !== "morphToOne" && attribute.relation !== "morphToMany") {
468
- const data2 = await serviceUtils.getRelationRestoreValue(versionRelationData, attribute);
469
- previousRelationAttributes[name] = data2;
489
+ if (options.attribute.type === "dynamiczone") {
490
+ if (options.value === null) {
491
+ utils.set(options.key, []);
492
+ }
470
493
  }
471
- if (attribute.type === "media") {
472
- const data2 = await serviceUtils.getMediaRestoreValue(versionRelationData, attribute);
473
- previousRelationAttributes[name] = data2;
494
+ if (options.attribute.type === "relation" && // TODO: handle polymorphic relations
495
+ options.attribute.relation !== "morphToOne" && options.attribute.relation !== "morphToMany") {
496
+ if (!options.value) return;
497
+ const data2 = await serviceUtils.getRelationRestoreValue(
498
+ options.value,
499
+ options.attribute
500
+ );
501
+ utils.set(options.key, data2);
502
+ }
503
+ if (options.attribute.type === "media") {
504
+ if (!options.value) return;
505
+ const data2 = await serviceUtils.getMediaRestoreValue(
506
+ options.value
507
+ );
508
+ utils.set(options.key, data2);
474
509
  }
475
- return previousRelationAttributes;
476
510
  },
477
- // Clone to avoid mutating the original version data
478
- structuredClone(dataWithoutAddedAttributes)
511
+ {
512
+ schema,
513
+ getModel: strapi2.getModel.bind(strapi2)
514
+ },
515
+ dataWithoutAddedAttributes
479
516
  );
480
517
  const data = fp.omit(["id", ...Object.keys(schemaDiff.removed)], dataWithoutMissingRelations);
481
518
  const restoredDocument = await strapi2.documents(version.contentType).update({
@@ -490,13 +527,47 @@ const createHistoryService = ({ strapi: strapi2 }) => {
490
527
  }
491
528
  };
492
529
  };
530
+ const shouldCreateHistoryVersion = (context) => {
531
+ if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
532
+ return false;
533
+ }
534
+ if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
535
+ return false;
536
+ }
537
+ if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
538
+ return false;
539
+ }
540
+ if (!context.contentType.uid.startsWith("api::")) {
541
+ return false;
542
+ }
543
+ return true;
544
+ };
545
+ const getSchemas = (uid2) => {
546
+ const attributesSchema = strapi.getModel(uid2).attributes;
547
+ const componentsSchemas = Object.keys(attributesSchema).reduce(
548
+ (currentComponentSchemas, key) => {
549
+ const fieldSchema = attributesSchema[key];
550
+ if (fieldSchema.type === "component") {
551
+ const componentSchema = strapi.getModel(fieldSchema.component).attributes;
552
+ return {
553
+ ...currentComponentSchemas,
554
+ [fieldSchema.component]: componentSchema
555
+ };
556
+ }
557
+ return currentComponentSchemas;
558
+ },
559
+ {}
560
+ );
561
+ return {
562
+ schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
563
+ componentsSchemas
564
+ };
565
+ };
493
566
  const createLifecyclesService = ({ strapi: strapi2 }) => {
494
567
  const state = {
495
568
  deleteExpiredJob: null,
496
569
  isInitialized: false
497
570
  };
498
- const query = strapi2.db.query(HISTORY_VERSION_UID);
499
- const historyService = getService(strapi2, "history");
500
571
  const serviceUtils = createServiceUtils({ strapi: strapi2 });
501
572
  return {
502
573
  async bootstrap() {
@@ -504,76 +575,62 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
504
575
  return;
505
576
  }
506
577
  strapi2.documents.use(async (context, next) => {
507
- if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
508
- return next();
509
- }
510
- if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
511
- return next();
512
- }
513
- if (context.action === "update" && strapi2.requestContext.get()?.request.url.endsWith("/actions/publish")) {
514
- return next();
515
- }
516
- const contentTypeUid = context.contentType.uid;
517
- if (!contentTypeUid.startsWith("api::")) {
518
- return next();
519
- }
520
578
  const result = await next();
521
- const documentContext = {
522
- documentId: context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId,
523
- locale: context.params?.locale
524
- };
579
+ if (!shouldCreateHistoryVersion(context)) {
580
+ return result;
581
+ }
582
+ const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
525
583
  const defaultLocale = await serviceUtils.getDefaultLocale();
526
- const locale = documentContext.locale || defaultLocale;
527
- if (Array.isArray(locale)) {
528
- strapi2.log.warn(
529
- "[Content manager history middleware]: An array of locales was provided, but only a single locale is supported for the findOne operation."
530
- );
531
- return next();
584
+ const locales = fp.castArray(context.params?.locale || defaultLocale);
585
+ if (!locales.length) {
586
+ return result;
532
587
  }
533
- const document = await strapi2.documents(contentTypeUid).findOne({
534
- documentId: documentContext.documentId,
535
- locale,
536
- populate: serviceUtils.getDeepPopulate(contentTypeUid)
588
+ const uid2 = context.contentType.uid;
589
+ const schemas = getSchemas(uid2);
590
+ const model = strapi2.getModel(uid2);
591
+ const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
592
+ const localeEntries = await strapi2.db.query(uid2).findMany({
593
+ where: {
594
+ documentId,
595
+ ...isLocalizedContentType ? { locale: { $in: locales } } : {},
596
+ ...strapiUtils.contentTypes.hasDraftAndPublish(strapi2.contentTypes[uid2]) ? { publishedAt: null } : {}
597
+ },
598
+ populate: serviceUtils.getDeepPopulate(
599
+ uid2,
600
+ true
601
+ /* use database syntax */
602
+ )
537
603
  });
538
- const status = await serviceUtils.getVersionStatus(contentTypeUid, document);
539
- const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
540
- const componentsSchemas = Object.keys(
541
- attributesSchema
542
- ).reduce((currentComponentSchemas, key) => {
543
- const fieldSchema = attributesSchema[key];
544
- if (fieldSchema.type === "component") {
545
- const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
546
- return {
547
- ...currentComponentSchemas,
548
- [fieldSchema.component]: componentSchema
549
- };
550
- }
551
- return currentComponentSchemas;
552
- }, {});
553
604
  await strapi2.db.transaction(async ({ onCommit }) => {
554
- onCommit(() => {
555
- historyService.createVersion({
556
- contentType: contentTypeUid,
557
- data: fp.omit(FIELDS_TO_IGNORE, document),
558
- schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
559
- componentsSchemas,
560
- relatedDocumentId: documentContext.documentId,
561
- locale,
562
- status
563
- });
605
+ onCommit(async () => {
606
+ for (const entry of localeEntries) {
607
+ const status = await serviceUtils.getVersionStatus(uid2, entry);
608
+ await getService$1(strapi2, "history").createVersion({
609
+ contentType: uid2,
610
+ data: fp.omit(FIELDS_TO_IGNORE, entry),
611
+ relatedDocumentId: documentId,
612
+ locale: entry.locale,
613
+ status,
614
+ ...schemas
615
+ });
616
+ }
564
617
  });
565
618
  });
566
619
  return result;
567
620
  });
568
- state.deleteExpiredJob = nodeSchedule.scheduleJob("0 0 * * *", () => {
621
+ state.deleteExpiredJob = nodeSchedule.scheduleJob("historyDaily", "0 0 * * *", () => {
569
622
  const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
570
623
  const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
571
- query.deleteMany({
624
+ strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
572
625
  where: {
573
626
  created_at: {
574
- $lt: expirationDate.toISOString()
627
+ $lt: expirationDate
575
628
  }
576
629
  }
630
+ }).catch((error) => {
631
+ if (error instanceof Error) {
632
+ strapi2.log.error("Error deleting expired history versions", error.message);
633
+ }
577
634
  });
578
635
  });
579
636
  state.isInitialized = true;
@@ -585,17 +642,17 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
585
642
  }
586
643
  };
587
644
  };
588
- const services$1 = {
645
+ const services$2 = {
589
646
  history: createHistoryService,
590
647
  lifecycles: createLifecyclesService
591
648
  };
592
- const info = { pluginName: "content-manager", type: "admin" };
649
+ const info$1 = { pluginName: "content-manager", type: "admin" };
593
650
  const historyVersionRouter = {
594
651
  type: "admin",
595
652
  routes: [
596
653
  {
597
654
  method: "GET",
598
- info,
655
+ info: info$1,
599
656
  path: "/history-versions",
600
657
  handler: "history-version.findMany",
601
658
  config: {
@@ -604,7 +661,7 @@ const historyVersionRouter = {
604
661
  },
605
662
  {
606
663
  method: "PUT",
607
- info,
664
+ info: info$1,
608
665
  path: "/history-versions/:versionId/restore",
609
666
  handler: "history-version.restoreVersion",
610
667
  config: {
@@ -613,7 +670,7 @@ const historyVersionRouter = {
613
670
  }
614
671
  ]
615
672
  };
616
- const routes$1 = {
673
+ const routes$2 = {
617
674
  "history-version": historyVersionRouter
618
675
  };
619
676
  const historyVersion = {
@@ -660,21 +717,21 @@ const historyVersion = {
660
717
  }
661
718
  }
662
719
  };
663
- const getFeature = () => {
720
+ const getFeature$1 = () => {
664
721
  if (strapi.ee.features.isEnabled("cms-content-history")) {
665
722
  return {
666
723
  register({ strapi: strapi2 }) {
667
724
  strapi2.get("models").add(historyVersion);
668
725
  },
669
726
  bootstrap({ strapi: strapi2 }) {
670
- getService(strapi2, "lifecycles").bootstrap();
727
+ getService$1(strapi2, "lifecycles").bootstrap();
671
728
  },
672
729
  destroy({ strapi: strapi2 }) {
673
- getService(strapi2, "lifecycles").destroy();
730
+ getService$1(strapi2, "lifecycles").destroy();
674
731
  },
675
- controllers: controllers$1,
676
- services: services$1,
677
- routes: routes$1
732
+ controllers: controllers$2,
733
+ services: services$2,
734
+ routes: routes$2
678
735
  };
679
736
  }
680
737
  return {
@@ -683,9 +740,201 @@ const getFeature = () => {
683
740
  }
684
741
  };
685
742
  };
686
- const history = getFeature();
743
+ const history = getFeature$1();
744
+ const info = { pluginName: "content-manager", type: "admin" };
745
+ const previewRouter = {
746
+ type: "admin",
747
+ routes: [
748
+ {
749
+ method: "GET",
750
+ info,
751
+ path: "/preview/url/:contentType",
752
+ handler: "preview.getPreviewUrl",
753
+ config: {
754
+ policies: ["admin::isAuthenticatedAdmin"]
755
+ }
756
+ }
757
+ ]
758
+ };
759
+ const routes$1 = {
760
+ preview: previewRouter
761
+ };
762
+ function getService(strapi2, name) {
763
+ return strapi2.service(`plugin::content-manager.${name}`);
764
+ }
765
+ const getPreviewUrlSchema = yup__namespace.object().shape({
766
+ // Will be undefined for single types
767
+ documentId: yup__namespace.string(),
768
+ locale: yup__namespace.string().nullable(),
769
+ status: yup__namespace.string()
770
+ }).required();
771
+ const validatePreviewUrl = async (strapi2, uid2, params) => {
772
+ await strapiUtils.validateYupSchema(getPreviewUrlSchema)(params);
773
+ const newParams = fp.pick(["documentId", "locale", "status"], params);
774
+ const model = strapi2.getModel(uid2);
775
+ if (!model || model.modelType !== "contentType") {
776
+ throw new strapiUtils.errors.ValidationError("Invalid content type");
777
+ }
778
+ const isSingleType = model?.kind === "singleType";
779
+ if (!isSingleType && !params.documentId) {
780
+ throw new strapiUtils.errors.ValidationError("documentId is required for Collection Types");
781
+ }
782
+ if (isSingleType) {
783
+ const doc = await strapi2.documents(uid2).findFirst();
784
+ if (!doc) {
785
+ throw new strapiUtils.errors.NotFoundError("Document not found");
786
+ }
787
+ newParams.documentId = doc?.documentId;
788
+ }
789
+ if (!newParams.status) {
790
+ const isDPEnabled = model?.options?.draftAndPublish;
791
+ newParams.status = isDPEnabled ? "draft" : "published";
792
+ }
793
+ return newParams;
794
+ };
795
+ const createPreviewController = () => {
796
+ return {
797
+ /**
798
+ * Transforms an entry into a preview URL, so that it can be previewed
799
+ * in the Content Manager.
800
+ */
801
+ async getPreviewUrl(ctx) {
802
+ const uid2 = ctx.params.contentType;
803
+ const query = ctx.request.query;
804
+ const params = await validatePreviewUrl(strapi, uid2, query);
805
+ const previewService = getService(strapi, "preview");
806
+ const url = await previewService.getPreviewUrl(uid2, params);
807
+ if (!url) {
808
+ ctx.status = 204;
809
+ }
810
+ return {
811
+ data: { url }
812
+ };
813
+ }
814
+ };
815
+ };
816
+ const controllers$1 = {
817
+ preview: createPreviewController
818
+ /**
819
+ * Casting is needed because the types aren't aware that Strapi supports
820
+ * passing a controller factory as the value, instead of a controller object directly
821
+ */
822
+ };
823
+ const createPreviewService = ({ strapi: strapi2 }) => {
824
+ const config = getService(strapi2, "preview-config");
825
+ return {
826
+ async getPreviewUrl(uid2, params) {
827
+ const handler = config.getPreviewHandler();
828
+ try {
829
+ return handler(uid2, params);
830
+ } catch (error) {
831
+ strapi2.log.error(`Failed to get preview URL: ${error}`);
832
+ throw new strapiUtils.errors.ApplicationError("Failed to get preview URL");
833
+ }
834
+ return;
835
+ }
836
+ };
837
+ };
838
+ const extendMiddlewareConfiguration = (middleware = { name: "", config: {} }) => {
839
+ const middlewares = strapi.config.get("middlewares");
840
+ const configuredMiddlewares = middlewares.map((currentMiddleware) => {
841
+ if (currentMiddleware === middleware.name) {
842
+ return middleware;
843
+ }
844
+ if (currentMiddleware.name === middleware.name) {
845
+ return fp.mergeWith(
846
+ (objValue, srcValue) => {
847
+ if (Array.isArray(objValue)) {
848
+ return objValue.concat(srcValue);
849
+ }
850
+ return void 0;
851
+ },
852
+ currentMiddleware,
853
+ middleware
854
+ );
855
+ }
856
+ return currentMiddleware;
857
+ });
858
+ strapi.config.set("middlewares", configuredMiddlewares);
859
+ };
860
+ const createPreviewConfigService = ({ strapi: strapi2 }) => {
861
+ return {
862
+ register() {
863
+ if (!this.isEnabled()) {
864
+ return;
865
+ }
866
+ const config = strapi2.config.get("admin.preview");
867
+ if (config.config?.allowedOrigins) {
868
+ extendMiddlewareConfiguration({
869
+ name: "strapi::security",
870
+ config: {
871
+ contentSecurityPolicy: {
872
+ directives: {
873
+ "frame-src": config.config.allowedOrigins
874
+ }
875
+ }
876
+ }
877
+ });
878
+ }
879
+ },
880
+ isEnabled() {
881
+ const config = strapi2.config.get("admin.preview");
882
+ if (!config) {
883
+ return false;
884
+ }
885
+ return config?.enabled ?? true;
886
+ },
887
+ /**
888
+ * Validate if the configuration is valid
889
+ */
890
+ validate() {
891
+ if (!this.isEnabled()) {
892
+ return;
893
+ }
894
+ const handler = this.getPreviewHandler();
895
+ if (typeof handler !== "function") {
896
+ throw new strapiUtils.errors.ValidationError(
897
+ "Preview configuration is invalid. Handler must be a function"
898
+ );
899
+ }
900
+ },
901
+ /**
902
+ * Utility to get the preview handler from the configuration
903
+ */
904
+ getPreviewHandler() {
905
+ const config = strapi2.config.get("admin.preview");
906
+ const emptyHandler = () => {
907
+ return void 0;
908
+ };
909
+ if (!this.isEnabled()) {
910
+ return emptyHandler;
911
+ }
912
+ return config?.config?.handler || emptyHandler;
913
+ }
914
+ };
915
+ };
916
+ const services$1 = {
917
+ preview: createPreviewService,
918
+ "preview-config": createPreviewConfigService
919
+ };
920
+ const getFeature = () => {
921
+ return {
922
+ register() {
923
+ const config = getService(strapi, "preview-config");
924
+ config.validate();
925
+ config.register();
926
+ },
927
+ bootstrap() {
928
+ },
929
+ routes: routes$1,
930
+ controllers: controllers$1,
931
+ services: services$1
932
+ };
933
+ };
934
+ const preview = getFeature();
687
935
  const register = async ({ strapi: strapi2 }) => {
688
936
  await history.register?.({ strapi: strapi2 });
937
+ await preview.register?.({ strapi: strapi2 });
689
938
  };
690
939
  const ALLOWED_WEBHOOK_EVENTS = {
691
940
  ENTRY_PUBLISH: "entry.publish",
@@ -695,11 +944,12 @@ const bootstrap = async () => {
695
944
  Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
696
945
  strapi.get("webhookStore").addAllowedEvent(key, value);
697
946
  });
698
- getService$1("field-sizes").setCustomFieldInputSizes();
699
- await getService$1("components").syncConfigurations();
700
- await getService$1("content-types").syncConfigurations();
701
- await getService$1("permission").registerPermissions();
947
+ getService$2("field-sizes").setCustomFieldInputSizes();
948
+ await getService$2("components").syncConfigurations();
949
+ await getService$2("content-types").syncConfigurations();
950
+ await getService$2("permission").registerPermissions();
702
951
  await history.bootstrap?.({ strapi });
952
+ await preview.bootstrap?.({ strapi });
703
953
  };
704
954
  const destroy = async ({ strapi: strapi2 }) => {
705
955
  await history.destroy?.({ strapi: strapi2 });
@@ -1189,7 +1439,8 @@ const admin = {
1189
1439
  };
1190
1440
  const routes = {
1191
1441
  admin,
1192
- ...history.routes ? history.routes : {}
1442
+ ...history.routes ? history.routes : {},
1443
+ ...preview.routes ? preview.routes : {}
1193
1444
  };
1194
1445
  const hasPermissionsSchema = strapiUtils.yup.object({
1195
1446
  actions: strapiUtils.yup.array().of(strapiUtils.yup.string()),
@@ -1200,6 +1451,11 @@ const { createPolicy } = strapiUtils.policy;
1200
1451
  const hasPermissions = createPolicy({
1201
1452
  name: "plugin::content-manager.hasPermissions",
1202
1453
  validator: validateHasPermissionsInput,
1454
+ /**
1455
+ * NOTE: Action aliases are currently not checked at this level (policy).
1456
+ * This is currently the intended behavior to avoid changing the behavior of API related permissions.
1457
+ * If you want to add support for it, please create a dedicated RFC with a list of potential side effect this could have.
1458
+ */
1203
1459
  handler(ctx, config = {}) {
1204
1460
  const { actions = [], hasAtLeastOne = false } = config;
1205
1461
  const { userAbility } = ctx.state;
@@ -1247,8 +1503,7 @@ const isSortable = (schema, name) => {
1247
1503
  if (!___default.default.has(schema.attributes, name)) {
1248
1504
  return false;
1249
1505
  }
1250
- if (schema.modelType === "component" && name === "id")
1251
- return false;
1506
+ if (schema.modelType === "component" && name === "id") return false;
1252
1507
  const attribute = schema.attributes[name];
1253
1508
  if (NON_SORTABLES.includes(attribute.type)) {
1254
1509
  return false;
@@ -1393,8 +1648,7 @@ const createDefaultSettings = async (schema) => {
1393
1648
  };
1394
1649
  };
1395
1650
  const syncSettings = async (configuration, schema) => {
1396
- if (fp.isEmpty(configuration.settings))
1397
- return createDefaultSettings(schema);
1651
+ if (fp.isEmpty(configuration.settings)) return createDefaultSettings(schema);
1398
1652
  const defaultField = getDefaultMainField(schema);
1399
1653
  const { mainField = defaultField, defaultSortBy = defaultField } = configuration.settings || {};
1400
1654
  return {
@@ -1441,7 +1695,7 @@ const createMetadasSchema = (schema) => {
1441
1695
  if (!value) {
1442
1696
  return strapiUtils.yup.string();
1443
1697
  }
1444
- const targetSchema = getService$1("content-types").findContentType(
1698
+ const targetSchema = getService$2("content-types").findContentType(
1445
1699
  schema.attributes[key].targetModel
1446
1700
  );
1447
1701
  if (!targetSchema) {
@@ -1570,8 +1824,7 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1570
1824
  }
1571
1825
  switch (attribute.type) {
1572
1826
  case "relation": {
1573
- if (canCreate(attributePath))
1574
- return body2;
1827
+ if (canCreate(attributePath)) return body2;
1575
1828
  return fp.set(attributePath, { set: [] }, body2);
1576
1829
  }
1577
1830
  case "component": {
@@ -1581,8 +1834,7 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1581
1834
  ]);
1582
1835
  }
1583
1836
  default: {
1584
- if (canCreate(attributePath))
1585
- return body2;
1837
+ if (canCreate(attributePath)) return body2;
1586
1838
  return fp.set(attributePath, null, body2);
1587
1839
  }
1588
1840
  }
@@ -1593,9 +1845,11 @@ const multipleLocaleSchema = strapiUtils.yup.lazy(
1593
1845
  (value) => Array.isArray(value) ? strapiUtils.yup.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1594
1846
  );
1595
1847
  const statusSchema = strapiUtils.yup.mixed().oneOf(["draft", "published"], "Invalid status");
1596
- const getDocumentLocaleAndStatus = async (request, opts = { allowMultipleLocales: false }) => {
1848
+ const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
1597
1849
  const { allowMultipleLocales } = opts;
1598
- const { locale, status, ...rest } = request || {};
1850
+ const { locale, status: providedStatus, ...rest } = request || {};
1851
+ const defaultStatus = strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
1852
+ const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
1599
1853
  const schema = strapiUtils.yup.object().shape({
1600
1854
  locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1601
1855
  status: statusSchema
@@ -1608,7 +1862,7 @@ const getDocumentLocaleAndStatus = async (request, opts = { allowMultipleLocales
1608
1862
  }
1609
1863
  };
1610
1864
  const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
1611
- const documentMetadata2 = getService$1("document-metadata");
1865
+ const documentMetadata2 = getService$2("document-metadata");
1612
1866
  const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
1613
1867
  let {
1614
1868
  meta: { availableLocales, availableStatus }
@@ -1634,8 +1888,8 @@ const createDocument = async (ctx, opts) => {
1634
1888
  const { userAbility, user } = ctx.state;
1635
1889
  const { model } = ctx.params;
1636
1890
  const { body } = ctx.request;
1637
- const documentManager2 = getService$1("document-manager");
1638
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1891
+ const documentManager2 = getService$2("document-manager");
1892
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1639
1893
  if (permissionChecker2.cannot.create()) {
1640
1894
  throw new strapiUtils.errors.ForbiddenError();
1641
1895
  }
@@ -1643,7 +1897,7 @@ const createDocument = async (ctx, opts) => {
1643
1897
  const setCreator = strapiUtils.setCreatorFields({ user });
1644
1898
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1645
1899
  const sanitizedBody = await sanitizeFn(body);
1646
- const { locale, status = "draft" } = await getDocumentLocaleAndStatus(body);
1900
+ const { locale, status } = await getDocumentLocaleAndStatus(body, model);
1647
1901
  return documentManager2.create(model, {
1648
1902
  data: sanitizedBody,
1649
1903
  locale,
@@ -1655,14 +1909,14 @@ const updateDocument = async (ctx, opts) => {
1655
1909
  const { userAbility, user } = ctx.state;
1656
1910
  const { id, model } = ctx.params;
1657
1911
  const { body } = ctx.request;
1658
- const documentManager2 = getService$1("document-manager");
1659
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1912
+ const documentManager2 = getService$2("document-manager");
1913
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1660
1914
  if (permissionChecker2.cannot.update()) {
1661
1915
  throw new strapiUtils.errors.ForbiddenError();
1662
1916
  }
1663
1917
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1664
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1665
- const { locale } = await getDocumentLocaleAndStatus(body);
1918
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
1919
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1666
1920
  const [documentVersion, documentExists] = await Promise.all([
1667
1921
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1668
1922
  documentManager2.exists(model, id)
@@ -1678,7 +1932,7 @@ const updateDocument = async (ctx, opts) => {
1678
1932
  throw new strapiUtils.errors.ForbiddenError();
1679
1933
  }
1680
1934
  const pickPermittedFields = documentVersion ? permissionChecker2.sanitizeUpdateInput(documentVersion) : permissionChecker2.sanitizeCreateInput;
1681
- const setCreator = strapiUtils.setCreatorFields({ user, isEdition: true });
1935
+ const setCreator = documentVersion ? strapiUtils.setCreatorFields({ user, isEdition: true }) : strapiUtils.setCreatorFields({ user });
1682
1936
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1683
1937
  const sanitizedBody = await sanitizeFn(body);
1684
1938
  return documentManager2.update(documentVersion?.documentId || id, model, {
@@ -1692,15 +1946,15 @@ const collectionTypes = {
1692
1946
  const { userAbility } = ctx.state;
1693
1947
  const { model } = ctx.params;
1694
1948
  const { query } = ctx.request;
1695
- const documentMetadata2 = getService$1("document-metadata");
1696
- const documentManager2 = getService$1("document-manager");
1697
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1949
+ const documentMetadata2 = getService$2("document-metadata");
1950
+ const documentManager2 = getService$2("document-manager");
1951
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1698
1952
  if (permissionChecker2.cannot.read()) {
1699
1953
  return ctx.forbidden();
1700
1954
  }
1701
1955
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1702
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1703
- const { locale, status } = await getDocumentLocaleAndStatus(query);
1956
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1957
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
1704
1958
  const { results: documents, pagination } = await documentManager2.findPage(
1705
1959
  { ...permissionQuery, populate, locale, status },
1706
1960
  model
@@ -1728,14 +1982,14 @@ const collectionTypes = {
1728
1982
  async findOne(ctx) {
1729
1983
  const { userAbility } = ctx.state;
1730
1984
  const { model, id } = ctx.params;
1731
- const documentManager2 = getService$1("document-manager");
1732
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1985
+ const documentManager2 = getService$2("document-manager");
1986
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1733
1987
  if (permissionChecker2.cannot.read()) {
1734
1988
  return ctx.forbidden();
1735
1989
  }
1736
1990
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1737
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1738
- const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1991
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1992
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
1739
1993
  const version = await documentManager2.findOne(id, model, {
1740
1994
  populate,
1741
1995
  locale,
@@ -1750,7 +2004,7 @@ const collectionTypes = {
1750
2004
  permissionChecker2,
1751
2005
  model,
1752
2006
  // @ts-expect-error TODO: fix
1753
- { id, locale, publishedAt: null },
2007
+ { documentId: id, locale, publishedAt: null },
1754
2008
  { availableLocales: true, availableStatus: false }
1755
2009
  );
1756
2010
  ctx.body = { data: {}, meta };
@@ -1765,7 +2019,7 @@ const collectionTypes = {
1765
2019
  async create(ctx) {
1766
2020
  const { userAbility } = ctx.state;
1767
2021
  const { model } = ctx.params;
1768
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2022
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1769
2023
  const [totalEntries, document] = await Promise.all([
1770
2024
  strapi.db.query(model).count(),
1771
2025
  createDocument(ctx)
@@ -1786,7 +2040,7 @@ const collectionTypes = {
1786
2040
  async update(ctx) {
1787
2041
  const { userAbility } = ctx.state;
1788
2042
  const { model } = ctx.params;
1789
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2043
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1790
2044
  const updatedVersion = await updateDocument(ctx);
1791
2045
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1792
2046
  ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
@@ -1795,14 +2049,14 @@ const collectionTypes = {
1795
2049
  const { userAbility, user } = ctx.state;
1796
2050
  const { model, sourceId: id } = ctx.params;
1797
2051
  const { body } = ctx.request;
1798
- const documentManager2 = getService$1("document-manager");
1799
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2052
+ const documentManager2 = getService$2("document-manager");
2053
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1800
2054
  if (permissionChecker2.cannot.create()) {
1801
2055
  return ctx.forbidden();
1802
2056
  }
1803
2057
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1804
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1805
- const { locale } = await getDocumentLocaleAndStatus(body);
2058
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2059
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1806
2060
  const document = await documentManager2.findOne(id, model, {
1807
2061
  populate,
1808
2062
  locale,
@@ -1840,14 +2094,14 @@ const collectionTypes = {
1840
2094
  async delete(ctx) {
1841
2095
  const { userAbility } = ctx.state;
1842
2096
  const { id, model } = ctx.params;
1843
- const documentManager2 = getService$1("document-manager");
1844
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2097
+ const documentManager2 = getService$2("document-manager");
2098
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1845
2099
  if (permissionChecker2.cannot.delete()) {
1846
2100
  return ctx.forbidden();
1847
2101
  }
1848
2102
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1849
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1850
- const { locale } = await getDocumentLocaleAndStatus(ctx.query);
2103
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2104
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
1851
2105
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1852
2106
  if (documentLocales.length === 0) {
1853
2107
  return ctx.notFound();
@@ -1868,19 +2122,42 @@ const collectionTypes = {
1868
2122
  const { userAbility } = ctx.state;
1869
2123
  const { id, model } = ctx.params;
1870
2124
  const { body } = ctx.request;
1871
- const documentManager2 = getService$1("document-manager");
1872
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2125
+ const documentManager2 = getService$2("document-manager");
2126
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1873
2127
  if (permissionChecker2.cannot.publish()) {
1874
2128
  return ctx.forbidden();
1875
2129
  }
1876
2130
  const publishedDocument = await strapi.db.transaction(async () => {
1877
2131
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1878
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1879
- const document = id ? await updateDocument(ctx, { populate }) : await createDocument(ctx, { populate });
2132
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
2133
+ let document;
2134
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2135
+ const isCreate = fp.isNil(id);
2136
+ if (isCreate) {
2137
+ if (permissionChecker2.cannot.create()) {
2138
+ throw new strapiUtils.errors.ForbiddenError();
2139
+ }
2140
+ document = await createDocument(ctx, { populate });
2141
+ }
2142
+ const isUpdate = !isCreate;
2143
+ if (isUpdate) {
2144
+ const documentExists = documentManager2.exists(model, id);
2145
+ if (!documentExists) {
2146
+ throw new strapiUtils.errors.NotFoundError("Document not found");
2147
+ }
2148
+ document = await documentManager2.findOne(id, model, { populate, locale });
2149
+ if (!document) {
2150
+ if (permissionChecker2.cannot.create({ locale }) || permissionChecker2.cannot.publish({ locale })) {
2151
+ throw new strapiUtils.errors.ForbiddenError();
2152
+ }
2153
+ document = await updateDocument(ctx);
2154
+ } else if (permissionChecker2.can.update(document)) {
2155
+ await updateDocument(ctx);
2156
+ }
2157
+ }
1880
2158
  if (permissionChecker2.cannot.publish(document)) {
1881
2159
  throw new strapiUtils.errors.ForbiddenError();
1882
2160
  }
1883
- const { locale } = await getDocumentLocaleAndStatus(body);
1884
2161
  const publishResult = await documentManager2.publish(document.documentId, model, {
1885
2162
  locale
1886
2163
  // TODO: Allow setting creator fields on publish
@@ -1900,14 +2177,16 @@ const collectionTypes = {
1900
2177
  const { body } = ctx.request;
1901
2178
  const { documentIds } = body;
1902
2179
  await validateBulkActionInput(body);
1903
- const documentManager2 = getService$1("document-manager");
1904
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2180
+ const documentManager2 = getService$2("document-manager");
2181
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1905
2182
  if (permissionChecker2.cannot.publish()) {
1906
2183
  return ctx.forbidden();
1907
2184
  }
1908
2185
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1909
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1910
- const { locale } = await getDocumentLocaleAndStatus(body, { allowMultipleLocales: true });
2186
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
2187
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
2188
+ allowMultipleLocales: true
2189
+ });
1911
2190
  const entityPromises = documentIds.map(
1912
2191
  (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
1913
2192
  );
@@ -1929,12 +2208,14 @@ const collectionTypes = {
1929
2208
  const { body } = ctx.request;
1930
2209
  const { documentIds } = body;
1931
2210
  await validateBulkActionInput(body);
1932
- const documentManager2 = getService$1("document-manager");
1933
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2211
+ const documentManager2 = getService$2("document-manager");
2212
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1934
2213
  if (permissionChecker2.cannot.unpublish()) {
1935
2214
  return ctx.forbidden();
1936
2215
  }
1937
- const { locale } = await getDocumentLocaleAndStatus(body);
2216
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
2217
+ allowMultipleLocales: true
2218
+ });
1938
2219
  const entityPromises = documentIds.map(
1939
2220
  (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
1940
2221
  );
@@ -1957,8 +2238,8 @@ const collectionTypes = {
1957
2238
  const {
1958
2239
  body: { discardDraft, ...body }
1959
2240
  } = ctx.request;
1960
- const documentManager2 = getService$1("document-manager");
1961
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2241
+ const documentManager2 = getService$2("document-manager");
2242
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1962
2243
  if (permissionChecker2.cannot.unpublish()) {
1963
2244
  return ctx.forbidden();
1964
2245
  }
@@ -1966,8 +2247,8 @@ const collectionTypes = {
1966
2247
  return ctx.forbidden();
1967
2248
  }
1968
2249
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1969
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1970
- const { locale } = await getDocumentLocaleAndStatus(body);
2250
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2251
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1971
2252
  const document = await documentManager2.findOne(id, model, {
1972
2253
  populate,
1973
2254
  locale,
@@ -1997,14 +2278,14 @@ const collectionTypes = {
1997
2278
  const { userAbility } = ctx.state;
1998
2279
  const { id, model } = ctx.params;
1999
2280
  const { body } = ctx.request;
2000
- const documentManager2 = getService$1("document-manager");
2001
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2281
+ const documentManager2 = getService$2("document-manager");
2282
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2002
2283
  if (permissionChecker2.cannot.discard()) {
2003
2284
  return ctx.forbidden();
2004
2285
  }
2005
2286
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
2006
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2007
- const { locale } = await getDocumentLocaleAndStatus(body);
2287
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2288
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2008
2289
  const document = await documentManager2.findOne(id, model, {
2009
2290
  populate,
2010
2291
  locale,
@@ -2028,14 +2309,14 @@ const collectionTypes = {
2028
2309
  const { query, body } = ctx.request;
2029
2310
  const { documentIds } = body;
2030
2311
  await validateBulkActionInput(body);
2031
- const documentManager2 = getService$1("document-manager");
2032
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2312
+ const documentManager2 = getService$2("document-manager");
2313
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2033
2314
  if (permissionChecker2.cannot.delete()) {
2034
2315
  return ctx.forbidden();
2035
2316
  }
2036
2317
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
2037
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2038
- const { locale } = await getDocumentLocaleAndStatus(body);
2318
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2319
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2039
2320
  const documentLocales = await documentManager2.findLocales(documentIds, model, {
2040
2321
  populate,
2041
2322
  locale
@@ -2055,14 +2336,14 @@ const collectionTypes = {
2055
2336
  async countDraftRelations(ctx) {
2056
2337
  const { userAbility } = ctx.state;
2057
2338
  const { model, id } = ctx.params;
2058
- const documentManager2 = getService$1("document-manager");
2059
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2339
+ const documentManager2 = getService$2("document-manager");
2340
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2060
2341
  if (permissionChecker2.cannot.read()) {
2061
2342
  return ctx.forbidden();
2062
2343
  }
2063
2344
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2064
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2065
- const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
2345
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2346
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
2066
2347
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
2067
2348
  if (!entity) {
2068
2349
  return ctx.notFound();
@@ -2080,12 +2361,12 @@ const collectionTypes = {
2080
2361
  const ids = ctx.request.query.documentIds;
2081
2362
  const locale = ctx.request.query.locale;
2082
2363
  const { model } = ctx.params;
2083
- const documentManager2 = getService$1("document-manager");
2084
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2364
+ const documentManager2 = getService$2("document-manager");
2365
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2085
2366
  if (permissionChecker2.cannot.read()) {
2086
2367
  return ctx.forbidden();
2087
2368
  }
2088
- const entities = await documentManager2.findMany(
2369
+ const documents = await documentManager2.findMany(
2089
2370
  {
2090
2371
  filters: {
2091
2372
  documentId: ids
@@ -2094,7 +2375,7 @@ const collectionTypes = {
2094
2375
  },
2095
2376
  model
2096
2377
  );
2097
- if (!entities) {
2378
+ if (!documents) {
2098
2379
  return ctx.notFound();
2099
2380
  }
2100
2381
  const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
@@ -2105,13 +2386,13 @@ const collectionTypes = {
2105
2386
  };
2106
2387
  const components$1 = {
2107
2388
  findComponents(ctx) {
2108
- const components2 = getService$1("components").findAllComponents();
2109
- const { toDto } = getService$1("data-mapper");
2389
+ const components2 = getService$2("components").findAllComponents();
2390
+ const { toDto } = getService$2("data-mapper");
2110
2391
  ctx.body = { data: components2.map(toDto) };
2111
2392
  },
2112
2393
  async findComponentConfiguration(ctx) {
2113
2394
  const { uid: uid2 } = ctx.params;
2114
- const componentService = getService$1("components");
2395
+ const componentService = getService$2("components");
2115
2396
  const component = componentService.findComponent(uid2);
2116
2397
  if (!component) {
2117
2398
  return ctx.notFound("component.notFound");
@@ -2128,7 +2409,7 @@ const components$1 = {
2128
2409
  async updateComponentConfiguration(ctx) {
2129
2410
  const { uid: uid2 } = ctx.params;
2130
2411
  const { body } = ctx.request;
2131
- const componentService = getService$1("components");
2412
+ const componentService = getService$2("components");
2132
2413
  const component = componentService.findComponent(uid2);
2133
2414
  if (!component) {
2134
2415
  return ctx.notFound("component.notFound");
@@ -2162,12 +2443,12 @@ const contentTypes = {
2162
2443
  } catch (error) {
2163
2444
  return ctx.send({ error }, 400);
2164
2445
  }
2165
- const contentTypes2 = getService$1("content-types").findContentTypesByKind(kind);
2166
- const { toDto } = getService$1("data-mapper");
2446
+ const contentTypes2 = getService$2("content-types").findContentTypesByKind(kind);
2447
+ const { toDto } = getService$2("data-mapper");
2167
2448
  ctx.body = { data: contentTypes2.map(toDto) };
2168
2449
  },
2169
2450
  async findContentTypesSettings(ctx) {
2170
- const { findAllContentTypes, findConfiguration } = getService$1("content-types");
2451
+ const { findAllContentTypes, findConfiguration } = getService$2("content-types");
2171
2452
  const contentTypes2 = await findAllContentTypes();
2172
2453
  const configurations = await Promise.all(
2173
2454
  contentTypes2.map(async (contentType) => {
@@ -2181,7 +2462,7 @@ const contentTypes = {
2181
2462
  },
2182
2463
  async findContentTypeConfiguration(ctx) {
2183
2464
  const { uid: uid2 } = ctx.params;
2184
- const contentTypeService = getService$1("content-types");
2465
+ const contentTypeService = getService$2("content-types");
2185
2466
  const contentType = await contentTypeService.findContentType(uid2);
2186
2467
  if (!contentType) {
2187
2468
  return ctx.notFound("contentType.notFound");
@@ -2203,13 +2484,13 @@ const contentTypes = {
2203
2484
  const { userAbility } = ctx.state;
2204
2485
  const { uid: uid2 } = ctx.params;
2205
2486
  const { body } = ctx.request;
2206
- const contentTypeService = getService$1("content-types");
2207
- const metricsService = getService$1("metrics");
2487
+ const contentTypeService = getService$2("content-types");
2488
+ const metricsService = getService$2("metrics");
2208
2489
  const contentType = await contentTypeService.findContentType(uid2);
2209
2490
  if (!contentType) {
2210
2491
  return ctx.notFound("contentType.notFound");
2211
2492
  }
2212
- if (!getService$1("permission").canConfigureContentType({ userAbility, contentType })) {
2493
+ if (!getService$2("permission").canConfigureContentType({ userAbility, contentType })) {
2213
2494
  return ctx.forbidden();
2214
2495
  }
2215
2496
  let input;
@@ -2242,10 +2523,10 @@ const contentTypes = {
2242
2523
  };
2243
2524
  const init = {
2244
2525
  getInitData(ctx) {
2245
- const { toDto } = getService$1("data-mapper");
2246
- const { findAllComponents } = getService$1("components");
2247
- const { getAllFieldSizes } = getService$1("field-sizes");
2248
- const { findAllContentTypes } = getService$1("content-types");
2526
+ const { toDto } = getService$2("data-mapper");
2527
+ const { findAllComponents } = getService$2("components");
2528
+ const { getAllFieldSizes } = getService$2("field-sizes");
2529
+ const { findAllContentTypes } = getService$2("content-types");
2249
2530
  ctx.body = {
2250
2531
  data: {
2251
2532
  fieldSizes: getAllFieldSizes(),
@@ -2281,36 +2562,41 @@ const addFiltersClause = (params, filtersClause) => {
2281
2562
  params.filters.$and.push(filtersClause);
2282
2563
  };
2283
2564
  const sanitizeMainField = (model, mainField, userAbility) => {
2284
- const permissionChecker2 = getService$1("permission-checker").create({
2565
+ const permissionChecker2 = getService$2("permission-checker").create({
2285
2566
  userAbility,
2286
2567
  model: model.uid
2287
2568
  });
2288
- if (!isListable(model, mainField)) {
2569
+ const isMainFieldListable = isListable(model, mainField);
2570
+ const canReadMainField = permissionChecker2.can.read(null, mainField);
2571
+ if (!isMainFieldListable || !canReadMainField) {
2289
2572
  return "id";
2290
2573
  }
2291
- if (permissionChecker2.cannot.read(null, mainField)) {
2292
- if (model.uid === "plugin::users-permissions.role") {
2293
- const userPermissionChecker = getService$1("permission-checker").create({
2294
- userAbility,
2295
- model: "plugin::users-permissions.user"
2296
- });
2297
- if (userPermissionChecker.can.read()) {
2298
- return "name";
2299
- }
2300
- }
2301
- return "id";
2574
+ if (model.uid === "plugin::users-permissions.role") {
2575
+ return "name";
2302
2576
  }
2303
2577
  return mainField;
2304
2578
  };
2305
- const addStatusToRelations = async (uid2, relations2) => {
2306
- if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi.contentTypes[uid2])) {
2579
+ const addStatusToRelations = async (targetUid, relations2) => {
2580
+ if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(targetUid))) {
2581
+ return relations2;
2582
+ }
2583
+ const documentMetadata2 = getService$2("document-metadata");
2584
+ if (!relations2.length) {
2307
2585
  return relations2;
2308
2586
  }
2309
- const documentMetadata2 = getService$1("document-metadata");
2310
- const documentsAvailableStatus = await documentMetadata2.getManyAvailableStatus(uid2, relations2);
2587
+ const firstRelation = relations2[0];
2588
+ const filters = {
2589
+ documentId: { $in: relations2.map((r) => r.documentId) },
2590
+ // NOTE: find the "opposite" status
2591
+ publishedAt: firstRelation.publishedAt !== null ? { $null: true } : { $notNull: true }
2592
+ };
2593
+ const availableStatus = await strapi.query(targetUid).findMany({
2594
+ select: ["id", "documentId", "locale", "updatedAt", "createdAt", "publishedAt"],
2595
+ filters
2596
+ });
2311
2597
  return relations2.map((relation) => {
2312
- const availableStatuses = documentsAvailableStatus.filter(
2313
- (availableDocument) => availableDocument.documentId === relation.documentId
2598
+ const availableStatuses = availableStatus.filter(
2599
+ (availableDocument) => availableDocument.documentId === relation.documentId && (relation.locale ? availableDocument.locale === relation.locale : true)
2314
2600
  );
2315
2601
  return {
2316
2602
  ...relation,
@@ -2331,11 +2617,8 @@ const validateLocale = (sourceUid, targetUid, locale) => {
2331
2617
  const isLocalized = strapi.plugin("i18n").service("content-types").isLocalizedContentType;
2332
2618
  const isSourceLocalized = isLocalized(sourceModel);
2333
2619
  const isTargetLocalized = isLocalized(targetModel);
2334
- let validatedLocale = locale;
2335
- if (!targetModel || !isTargetLocalized)
2336
- validatedLocale = void 0;
2337
2620
  return {
2338
- locale: validatedLocale,
2621
+ locale,
2339
2622
  isSourceLocalized,
2340
2623
  isTargetLocalized
2341
2624
  };
@@ -2344,8 +2627,7 @@ const validateStatus = (sourceUid, status) => {
2344
2627
  const sourceModel = strapi.getModel(sourceUid);
2345
2628
  const isDP = strapiUtils.contentTypes.hasDraftAndPublish;
2346
2629
  const isSourceDP = isDP(sourceModel);
2347
- if (!isSourceDP)
2348
- return { status: void 0 };
2630
+ if (!isSourceDP) return { status: void 0 };
2349
2631
  switch (status) {
2350
2632
  case "published":
2351
2633
  return { status: "published" };
@@ -2375,7 +2657,7 @@ const relations = {
2375
2657
  ctx.request?.query?.locale
2376
2658
  );
2377
2659
  const { status } = validateStatus(sourceUid, ctx.request?.query?.status);
2378
- const permissionChecker2 = getService$1("permission-checker").create({
2660
+ const permissionChecker2 = getService$2("permission-checker").create({
2379
2661
  userAbility,
2380
2662
  model
2381
2663
  });
@@ -2400,7 +2682,7 @@ const relations = {
2400
2682
  where.id = id;
2401
2683
  }
2402
2684
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2403
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2685
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2404
2686
  const currentEntity = await strapi.db.query(model).findOne({
2405
2687
  where,
2406
2688
  populate
@@ -2415,7 +2697,7 @@ const relations = {
2415
2697
  }
2416
2698
  entryId = currentEntity.id;
2417
2699
  }
2418
- const modelConfig = isComponent2 ? await getService$1("components").findConfiguration(sourceSchema) : await getService$1("content-types").findConfiguration(sourceSchema);
2700
+ const modelConfig = isComponent2 ? await getService$2("components").findConfiguration(sourceSchema) : await getService$2("content-types").findConfiguration(sourceSchema);
2419
2701
  const targetSchema = strapi.getModel(targetUid);
2420
2702
  const mainField = fp.flow(
2421
2703
  fp.prop(`metadatas.${targetField}.edit.mainField`),
@@ -2438,7 +2720,7 @@ const relations = {
2438
2720
  attribute,
2439
2721
  fieldsToSelect,
2440
2722
  mainField,
2441
- source: { schema: sourceSchema },
2723
+ source: { schema: sourceSchema, isLocalized: isSourceLocalized },
2442
2724
  target: { schema: targetSchema, isLocalized: isTargetLocalized },
2443
2725
  sourceSchema,
2444
2726
  targetSchema,
@@ -2460,7 +2742,8 @@ const relations = {
2460
2742
  fieldsToSelect,
2461
2743
  mainField,
2462
2744
  source: {
2463
- schema: { uid: sourceUid, modelType: sourceModelType }
2745
+ schema: { uid: sourceUid, modelType: sourceModelType },
2746
+ isLocalized: isSourceLocalized
2464
2747
  },
2465
2748
  target: {
2466
2749
  schema: { uid: targetUid },
@@ -2468,7 +2751,7 @@ const relations = {
2468
2751
  }
2469
2752
  } = await this.extractAndValidateRequestInfo(ctx, id);
2470
2753
  const { idsToOmit, idsToInclude, _q, ...query } = ctx.request.query;
2471
- const permissionChecker2 = getService$1("permission-checker").create({
2754
+ const permissionChecker2 = getService$2("permission-checker").create({
2472
2755
  userAbility: ctx.state.userAbility,
2473
2756
  model: targetUid
2474
2757
  });
@@ -2498,12 +2781,16 @@ const relations = {
2498
2781
  } else {
2499
2782
  where.id = id;
2500
2783
  }
2501
- if (status) {
2502
- where[`${alias}.published_at`] = getPublishedAtClause(status, targetUid);
2784
+ const publishedAt = getPublishedAtClause(status, targetUid);
2785
+ if (!fp.isEmpty(publishedAt)) {
2786
+ where[`${alias}.published_at`] = publishedAt;
2503
2787
  }
2504
- if (filterByLocale) {
2788
+ if (isTargetLocalized && locale) {
2505
2789
  where[`${alias}.locale`] = locale;
2506
2790
  }
2791
+ if (isSourceLocalized && locale) {
2792
+ where.locale = locale;
2793
+ }
2507
2794
  if ((idsToInclude?.length ?? 0) !== 0) {
2508
2795
  where[`${alias}.id`].$notIn = idsToInclude;
2509
2796
  }
@@ -2521,7 +2808,8 @@ const relations = {
2521
2808
  id: { $notIn: fp.uniq(idsToOmit) }
2522
2809
  });
2523
2810
  }
2524
- const res = await strapi.db.query(targetUid).findPage(strapi.get("query-params").transform(targetUid, queryParams));
2811
+ const dbQuery = strapi.get("query-params").transform(targetUid, queryParams);
2812
+ const res = await strapi.db.query(targetUid).findPage(dbQuery);
2525
2813
  ctx.body = {
2526
2814
  ...res,
2527
2815
  results: await addStatusToRelations(targetUid, res.results)
@@ -2536,29 +2824,39 @@ const relations = {
2536
2824
  attribute,
2537
2825
  targetField,
2538
2826
  fieldsToSelect,
2539
- source: {
2540
- schema: { uid: sourceUid }
2541
- },
2542
- target: {
2543
- schema: { uid: targetUid }
2544
- }
2827
+ status,
2828
+ source: { schema: sourceSchema },
2829
+ target: { schema: targetSchema }
2545
2830
  } = await this.extractAndValidateRequestInfo(ctx, id);
2546
- const permissionQuery = await getService$1("permission-checker").create({ userAbility, model: targetUid }).sanitizedQuery.read({ fields: fieldsToSelect });
2831
+ const { uid: sourceUid } = sourceSchema;
2832
+ const { uid: targetUid } = targetSchema;
2833
+ const permissionQuery = await getService$2("permission-checker").create({ userAbility, model: targetUid }).sanitizedQuery.read({ fields: fieldsToSelect });
2547
2834
  const dbQuery = strapi.db.query(sourceUid);
2548
2835
  const loadRelations = strapiUtils.relations.isAnyToMany(attribute) ? (...args) => dbQuery.loadPages(...args) : (...args) => dbQuery.load(...args).then((res2) => ({ results: res2 ? [res2] : [] }));
2836
+ const filters = {};
2837
+ if (sourceSchema?.options?.draftAndPublish) {
2838
+ if (targetSchema?.options?.draftAndPublish) {
2839
+ if (status === "published") {
2840
+ filters.publishedAt = { $notNull: true };
2841
+ } else {
2842
+ filters.publishedAt = { $null: true };
2843
+ }
2844
+ }
2845
+ } else if (targetSchema?.options?.draftAndPublish) {
2846
+ filters.publishedAt = { $null: true };
2847
+ }
2549
2848
  const res = await loadRelations({ id: entryId }, targetField, {
2550
- select: ["id", "documentId", "locale", "publishedAt"],
2849
+ select: ["id", "documentId", "locale", "publishedAt", "updatedAt"],
2551
2850
  ordering: "desc",
2552
2851
  page: ctx.request.query.page,
2553
- pageSize: ctx.request.query.pageSize
2852
+ pageSize: ctx.request.query.pageSize,
2853
+ filters
2554
2854
  });
2555
2855
  const loadedIds = res.results.map((item) => item.id);
2556
2856
  addFiltersClause(permissionQuery, { id: { $in: loadedIds } });
2557
2857
  const sanitizedRes = await loadRelations({ id: entryId }, targetField, {
2558
2858
  ...strapi.get("query-params").transform(targetUid, permissionQuery),
2559
- ordering: "desc",
2560
- page: ctx.request.query.page,
2561
- pageSize: ctx.request.query.pageSize
2859
+ ordering: "desc"
2562
2860
  });
2563
2861
  const relationsUnion = fp.uniqBy("id", fp.concat(sanitizedRes.results, res.results));
2564
2862
  ctx.body = {
@@ -2573,10 +2871,10 @@ const relations = {
2573
2871
  }
2574
2872
  };
2575
2873
  const buildPopulateFromQuery = async (query, model) => {
2576
- return getService$1("populate-builder")(model).populateFromQuery(query).populateDeep(Infinity).countRelations().build();
2874
+ return getService$2("populate-builder")(model).populateFromQuery(query).populateDeep(Infinity).countRelations().build();
2577
2875
  };
2578
2876
  const findDocument = async (query, uid2, opts = {}) => {
2579
- const documentManager2 = getService$1("document-manager");
2877
+ const documentManager2 = getService$2("document-manager");
2580
2878
  const populate = await buildPopulateFromQuery(query, uid2);
2581
2879
  return documentManager2.findMany({ ...opts, populate }, uid2).then((documents) => documents[0]);
2582
2880
  };
@@ -2584,13 +2882,13 @@ const createOrUpdateDocument = async (ctx, opts) => {
2584
2882
  const { user, userAbility } = ctx.state;
2585
2883
  const { model } = ctx.params;
2586
2884
  const { body, query } = ctx.request;
2587
- const documentManager2 = getService$1("document-manager");
2588
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2885
+ const documentManager2 = getService$2("document-manager");
2886
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2589
2887
  if (permissionChecker2.cannot.create() && permissionChecker2.cannot.update()) {
2590
2888
  throw new strapiUtils.errors.ForbiddenError();
2591
2889
  }
2592
2890
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2593
- const { locale } = await getDocumentLocaleAndStatus(body);
2891
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2594
2892
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2595
2893
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2596
2894
  // Find the first document to check if it exists
@@ -2626,12 +2924,12 @@ const singleTypes = {
2626
2924
  const { userAbility } = ctx.state;
2627
2925
  const { model } = ctx.params;
2628
2926
  const { query = {} } = ctx.request;
2629
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2927
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2630
2928
  if (permissionChecker2.cannot.read()) {
2631
2929
  return ctx.forbidden();
2632
2930
  }
2633
2931
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2634
- const { locale, status } = await getDocumentLocaleAndStatus(query);
2932
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
2635
2933
  const version = await findDocument(permissionQuery, model, { locale, status });
2636
2934
  if (!version) {
2637
2935
  if (permissionChecker2.cannot.create()) {
@@ -2645,7 +2943,7 @@ const singleTypes = {
2645
2943
  permissionChecker2,
2646
2944
  model,
2647
2945
  // @ts-expect-error - fix types
2648
- { id: document.documentId, locale, publishedAt: null },
2946
+ { documentId: document.documentId, locale, publishedAt: null },
2649
2947
  { availableLocales: true, availableStatus: false }
2650
2948
  );
2651
2949
  ctx.body = { data: {}, meta };
@@ -2660,7 +2958,7 @@ const singleTypes = {
2660
2958
  async createOrUpdate(ctx) {
2661
2959
  const { userAbility } = ctx.state;
2662
2960
  const { model } = ctx.params;
2663
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2961
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2664
2962
  const document = await createOrUpdateDocument(ctx);
2665
2963
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2666
2964
  ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
@@ -2669,14 +2967,14 @@ const singleTypes = {
2669
2967
  const { userAbility } = ctx.state;
2670
2968
  const { model } = ctx.params;
2671
2969
  const { query = {} } = ctx.request;
2672
- const documentManager2 = getService$1("document-manager");
2673
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2970
+ const documentManager2 = getService$2("document-manager");
2971
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2674
2972
  if (permissionChecker2.cannot.delete()) {
2675
2973
  return ctx.forbidden();
2676
2974
  }
2677
2975
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2678
2976
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2679
- const { locale } = await getDocumentLocaleAndStatus(query);
2977
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2680
2978
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2681
2979
  populate,
2682
2980
  locale
@@ -2698,8 +2996,8 @@ const singleTypes = {
2698
2996
  const { userAbility } = ctx.state;
2699
2997
  const { model } = ctx.params;
2700
2998
  const { query = {} } = ctx.request;
2701
- const documentManager2 = getService$1("document-manager");
2702
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2999
+ const documentManager2 = getService$2("document-manager");
3000
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2703
3001
  if (permissionChecker2.cannot.publish()) {
2704
3002
  return ctx.forbidden();
2705
3003
  }
@@ -2713,7 +3011,7 @@ const singleTypes = {
2713
3011
  if (permissionChecker2.cannot.publish(document)) {
2714
3012
  throw new strapiUtils.errors.ForbiddenError();
2715
3013
  }
2716
- const { locale } = await getDocumentLocaleAndStatus(document);
3014
+ const { locale } = await getDocumentLocaleAndStatus(document, model);
2717
3015
  const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2718
3016
  return publishResult.at(0);
2719
3017
  });
@@ -2727,8 +3025,8 @@ const singleTypes = {
2727
3025
  body: { discardDraft, ...body },
2728
3026
  query = {}
2729
3027
  } = ctx.request;
2730
- const documentManager2 = getService$1("document-manager");
2731
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
3028
+ const documentManager2 = getService$2("document-manager");
3029
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2732
3030
  if (permissionChecker2.cannot.unpublish()) {
2733
3031
  return ctx.forbidden();
2734
3032
  }
@@ -2736,7 +3034,7 @@ const singleTypes = {
2736
3034
  return ctx.forbidden();
2737
3035
  }
2738
3036
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2739
- const { locale } = await getDocumentLocaleAndStatus(body);
3037
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2740
3038
  const document = await findDocument(sanitizedQuery, model, { locale });
2741
3039
  if (!document) {
2742
3040
  return ctx.notFound();
@@ -2762,13 +3060,13 @@ const singleTypes = {
2762
3060
  const { userAbility } = ctx.state;
2763
3061
  const { model } = ctx.params;
2764
3062
  const { body, query = {} } = ctx.request;
2765
- const documentManager2 = getService$1("document-manager");
2766
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
3063
+ const documentManager2 = getService$2("document-manager");
3064
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2767
3065
  if (permissionChecker2.cannot.discard()) {
2768
3066
  return ctx.forbidden();
2769
3067
  }
2770
3068
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2771
- const { locale } = await getDocumentLocaleAndStatus(body);
3069
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2772
3070
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2773
3071
  if (!document) {
2774
3072
  return ctx.notFound();
@@ -2786,9 +3084,9 @@ const singleTypes = {
2786
3084
  const { userAbility } = ctx.state;
2787
3085
  const { model } = ctx.params;
2788
3086
  const { query } = ctx.request;
2789
- const documentManager2 = getService$1("document-manager");
2790
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2791
- const { locale } = await getDocumentLocaleAndStatus(query);
3087
+ const documentManager2 = getService$2("document-manager");
3088
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
3089
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2792
3090
  if (permissionChecker2.cannot.read()) {
2793
3091
  return ctx.forbidden();
2794
3092
  }
@@ -2809,9 +3107,9 @@ const uid$1 = {
2809
3107
  async generateUID(ctx) {
2810
3108
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2811
3109
  const { query = {} } = ctx.request;
2812
- const { locale } = await getDocumentLocaleAndStatus(query);
3110
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2813
3111
  await validateUIDField(contentTypeUID, field);
2814
- const uidService = getService$1("uid");
3112
+ const uidService = getService$2("uid");
2815
3113
  ctx.body = {
2816
3114
  data: await uidService.generateUIDField({ contentTypeUID, field, data, locale })
2817
3115
  };
@@ -2821,9 +3119,9 @@ const uid$1 = {
2821
3119
  ctx.request.body
2822
3120
  );
2823
3121
  const { query = {} } = ctx.request;
2824
- const { locale } = await getDocumentLocaleAndStatus(query);
3122
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2825
3123
  await validateUIDField(contentTypeUID, field);
2826
- const uidService = getService$1("uid");
3124
+ const uidService = getService$2("uid");
2827
3125
  const isAvailable = await uidService.checkUIDAvailability({
2828
3126
  contentTypeUID,
2829
3127
  field,
@@ -2844,7 +3142,8 @@ const controllers = {
2844
3142
  relations,
2845
3143
  "single-types": singleTypes,
2846
3144
  uid: uid$1,
2847
- ...history.controllers ? history.controllers : {}
3145
+ ...history.controllers ? history.controllers : {},
3146
+ ...preview.controllers ? preview.controllers : {}
2848
3147
  };
2849
3148
  const keys = {
2850
3149
  CONFIGURATION: "configuration"
@@ -2973,18 +3272,15 @@ async function syncMetadatas(configuration, schema) {
2973
3272
  ___default.default.set(updatedMeta, ["list", "searchable"], false);
2974
3273
  ___default.default.set(acc, [key], updatedMeta);
2975
3274
  }
2976
- if (!___default.default.has(edit, "mainField"))
2977
- return acc;
3275
+ if (!___default.default.has(edit, "mainField")) return acc;
2978
3276
  if (!isRelation$1(attr)) {
2979
3277
  ___default.default.set(updatedMeta, "edit", ___default.default.omit(edit, ["mainField"]));
2980
3278
  ___default.default.set(acc, [key], updatedMeta);
2981
3279
  return acc;
2982
3280
  }
2983
- if (edit.mainField === "id")
2984
- return acc;
3281
+ if (edit.mainField === "id") return acc;
2985
3282
  const targetSchema = getTargetSchema(attr.targetModel);
2986
- if (!targetSchema)
2987
- return acc;
3283
+ if (!targetSchema) return acc;
2988
3284
  if (!isSortable(targetSchema, edit.mainField) && !isListable(targetSchema, edit.mainField)) {
2989
3285
  ___default.default.set(updatedMeta, ["edit", "mainField"], getDefaultMainField(targetSchema));
2990
3286
  ___default.default.set(acc, [key], updatedMeta);
@@ -2995,12 +3291,12 @@ async function syncMetadatas(configuration, schema) {
2995
3291
  return ___default.default.assign(metasWithDefaults, updatedMetas);
2996
3292
  }
2997
3293
  const getTargetSchema = (targetModel) => {
2998
- return getService$1("content-types").findContentType(targetModel);
3294
+ return getService$2("content-types").findContentType(targetModel);
2999
3295
  };
3000
3296
  const DEFAULT_LIST_LENGTH = 4;
3001
3297
  const MAX_ROW_SIZE = 12;
3002
3298
  const isAllowedFieldSize = (type, size) => {
3003
- const { getFieldSize } = getService$1("field-sizes");
3299
+ const { getFieldSize } = getService$2("field-sizes");
3004
3300
  const fieldSize = getFieldSize(type);
3005
3301
  if (!fieldSize.isResizable && size !== fieldSize.default) {
3006
3302
  return false;
@@ -3008,7 +3304,7 @@ const isAllowedFieldSize = (type, size) => {
3008
3304
  return size <= MAX_ROW_SIZE;
3009
3305
  };
3010
3306
  const getDefaultFieldSize = (attribute) => {
3011
- const { hasFieldSize, getFieldSize } = getService$1("field-sizes");
3307
+ const { hasFieldSize, getFieldSize } = getService$2("field-sizes");
3012
3308
  return getFieldSize(hasFieldSize(attribute.customField) ? attribute.customField : attribute.type).default;
3013
3309
  };
3014
3310
  async function createDefaultLayouts(schema) {
@@ -3029,8 +3325,7 @@ function createDefaultEditLayout(schema) {
3029
3325
  return appendToEditLayout([], keys2, schema);
3030
3326
  }
3031
3327
  function syncLayouts(configuration, schema) {
3032
- if (___default.default.isEmpty(configuration.layouts))
3033
- return createDefaultLayouts(schema);
3328
+ if (___default.default.isEmpty(configuration.layouts)) return createDefaultLayouts(schema);
3034
3329
  const { list = [], editRelations = [], edit = [] } = configuration.layouts || {};
3035
3330
  let cleanList = list.filter((attr) => isListable(schema, attr));
3036
3331
  const cleanEditRelations = editRelations.filter(
@@ -3041,9 +3336,8 @@ function syncLayouts(configuration, schema) {
3041
3336
  for (const row of edit) {
3042
3337
  const newRow = [];
3043
3338
  for (const el of row) {
3044
- if (!hasEditableAttribute(schema, el.name))
3045
- continue;
3046
- const { hasFieldSize } = getService$1("field-sizes");
3339
+ if (!hasEditableAttribute(schema, el.name)) continue;
3340
+ const { hasFieldSize } = getService$2("field-sizes");
3047
3341
  const fieldType = hasFieldSize(schema.attributes[el.name].customField) ? schema.attributes[el.name].customField : schema.attributes[el.name].type;
3048
3342
  if (!isAllowedFieldSize(fieldType, el.size)) {
3049
3343
  elementsToReAppend.push(el.name);
@@ -3073,8 +3367,7 @@ function syncLayouts(configuration, schema) {
3073
3367
  };
3074
3368
  }
3075
3369
  const appendToEditLayout = (layout = [], keysToAppend, schema) => {
3076
- if (keysToAppend.length === 0)
3077
- return layout;
3370
+ if (keysToAppend.length === 0) return layout;
3078
3371
  let currentRowIndex = Math.max(layout.length - 1, 0);
3079
3372
  if (!layout[currentRowIndex]) {
3080
3373
  layout[currentRowIndex] = [];
@@ -3183,17 +3476,17 @@ const configurationService$1 = createConfigurationService({
3183
3476
  isComponent: true,
3184
3477
  prefix: STORE_KEY_PREFIX,
3185
3478
  getModels() {
3186
- const { toContentManagerModel } = getService$1("data-mapper");
3479
+ const { toContentManagerModel } = getService$2("data-mapper");
3187
3480
  return fp.mapValues(toContentManagerModel, strapi.components);
3188
3481
  }
3189
3482
  });
3190
3483
  const components = ({ strapi: strapi2 }) => ({
3191
3484
  findAllComponents() {
3192
- const { toContentManagerModel } = getService$1("data-mapper");
3485
+ const { toContentManagerModel } = getService$2("data-mapper");
3193
3486
  return Object.values(strapi2.components).map(toContentManagerModel);
3194
3487
  },
3195
3488
  findComponent(uid2) {
3196
- const { toContentManagerModel } = getService$1("data-mapper");
3489
+ const { toContentManagerModel } = getService$2("data-mapper");
3197
3490
  const component = strapi2.components[uid2];
3198
3491
  return fp.isNil(component) ? component : toContentManagerModel(component);
3199
3492
  },
@@ -3244,17 +3537,17 @@ const configurationService = createConfigurationService({
3244
3537
  storeUtils,
3245
3538
  prefix: "content_types",
3246
3539
  getModels() {
3247
- const { toContentManagerModel } = getService$1("data-mapper");
3540
+ const { toContentManagerModel } = getService$2("data-mapper");
3248
3541
  return fp.mapValues(toContentManagerModel, strapi.contentTypes);
3249
3542
  }
3250
3543
  });
3251
3544
  const service = ({ strapi: strapi2 }) => ({
3252
3545
  findAllContentTypes() {
3253
- const { toContentManagerModel } = getService$1("data-mapper");
3546
+ const { toContentManagerModel } = getService$2("data-mapper");
3254
3547
  return Object.values(strapi2.contentTypes).map(toContentManagerModel);
3255
3548
  },
3256
3549
  findContentType(uid2) {
3257
- const { toContentManagerModel } = getService$1("data-mapper");
3550
+ const { toContentManagerModel } = getService$2("data-mapper");
3258
3551
  const contentType = strapi2.contentTypes[uid2];
3259
3552
  return fp.isNil(contentType) ? contentType : toContentManagerModel(contentType);
3260
3553
  },
@@ -3283,7 +3576,7 @@ const service = ({ strapi: strapi2 }) => ({
3283
3576
  return this.findConfiguration(contentType);
3284
3577
  },
3285
3578
  findComponentsConfigurations(contentType) {
3286
- return getService$1("components").findComponentsConfigurations(contentType);
3579
+ return getService$2("components").findComponentsConfigurations(contentType);
3287
3580
  },
3288
3581
  syncConfigurations() {
3289
3582
  return configurationService.syncConfigurations();
@@ -3464,12 +3757,27 @@ const createPermissionChecker = (strapi2) => ({ userAbility, model }) => {
3464
3757
  ability: userAbility,
3465
3758
  model
3466
3759
  });
3467
- const toSubject = (entity) => entity ? permissionsManager.toSubject(entity, model) : model;
3760
+ const { actionProvider } = strapi2.service("admin::permission");
3761
+ const toSubject = (entity) => {
3762
+ return entity ? permissionsManager.toSubject(entity, model) : model;
3763
+ };
3468
3764
  const can = (action, entity, field) => {
3469
- return userAbility.can(action, toSubject(entity), field);
3765
+ const subject = toSubject(entity);
3766
+ const aliases = actionProvider.unstable_aliases(action, model);
3767
+ return (
3768
+ // Test the original action to see if it passes
3769
+ userAbility.can(action, subject, field) || // Else try every known alias if at least one of them succeed, then the user "can"
3770
+ aliases.some((alias) => userAbility.can(alias, subject, field))
3771
+ );
3470
3772
  };
3471
3773
  const cannot = (action, entity, field) => {
3472
- return userAbility.cannot(action, toSubject(entity), field);
3774
+ const subject = toSubject(entity);
3775
+ const aliases = actionProvider.unstable_aliases(action, model);
3776
+ return (
3777
+ // Test both the original action
3778
+ userAbility.cannot(action, subject, field) && // and every known alias, if all of them fail (cannot), then the user truly "cannot"
3779
+ aliases.every((alias) => userAbility.cannot(alias, subject, field))
3780
+ );
3473
3781
  };
3474
3782
  const sanitizeOutput = (data, { action = ACTIONS.read } = {}) => {
3475
3783
  return permissionsManager.sanitizeOutput(data, { subject: toSubject(data), action });
@@ -3540,7 +3848,7 @@ const permission = ({ strapi: strapi2 }) => ({
3540
3848
  return userAbility.can(action);
3541
3849
  },
3542
3850
  async registerPermissions() {
3543
- const displayedContentTypes = getService$1("content-types").findDisplayedContentTypes();
3851
+ const displayedContentTypes = getService$2("content-types").findDisplayedContentTypes();
3544
3852
  const contentTypesUids = displayedContentTypes.map(fp.prop("uid"));
3545
3853
  const actions = [
3546
3854
  {
@@ -3625,6 +3933,12 @@ function getPopulateForRelation(attribute, model, attributeName, { countMany, co
3625
3933
  if (initialPopulate) {
3626
3934
  return initialPopulate;
3627
3935
  }
3936
+ if (attributeName === "localizations") {
3937
+ const validationPopulate = getPopulateForValidation(model.uid);
3938
+ return {
3939
+ populate: validationPopulate.populate
3940
+ };
3941
+ }
3628
3942
  if (!isVisibleAttribute$1(model, attributeName)) {
3629
3943
  return true;
3630
3944
  }
@@ -3684,6 +3998,9 @@ const getDeepPopulate = (uid2, {
3684
3998
  return {};
3685
3999
  }
3686
4000
  const model = strapi.getModel(uid2);
4001
+ if (!model) {
4002
+ return {};
4003
+ }
3687
4004
  return Object.keys(model.attributes).reduce(
3688
4005
  (populateAcc, attributeName) => fp.merge(
3689
4006
  populateAcc,
@@ -3703,40 +4020,46 @@ const getDeepPopulate = (uid2, {
3703
4020
  {}
3704
4021
  );
3705
4022
  };
3706
- const getValidatableFieldsPopulate = (uid2, {
3707
- initialPopulate = {},
3708
- countMany = false,
3709
- countOne = false,
3710
- maxLevel = Infinity
3711
- } = {}, level = 1) => {
3712
- if (level > maxLevel) {
4023
+ const getPopulateForValidation = (uid2) => {
4024
+ const model = strapi.getModel(uid2);
4025
+ if (!model) {
3713
4026
  return {};
3714
4027
  }
3715
- const model = strapi.getModel(uid2);
3716
4028
  return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
3717
- if (!getDoesAttributeRequireValidation(attribute)) {
4029
+ if (isScalarAttribute(attribute)) {
4030
+ if (getDoesAttributeRequireValidation(attribute)) {
4031
+ populateAcc.fields = populateAcc.fields || [];
4032
+ populateAcc.fields.push(attributeName);
4033
+ }
3718
4034
  return populateAcc;
3719
4035
  }
3720
- if (isScalarAttribute(attribute)) {
3721
- return fp.merge(populateAcc, {
3722
- [attributeName]: true
3723
- });
4036
+ if (isComponent(attribute)) {
4037
+ const component = attribute.component;
4038
+ const componentResult = getPopulateForValidation(component);
4039
+ if (Object.keys(componentResult).length > 0) {
4040
+ populateAcc.populate = populateAcc.populate || {};
4041
+ populateAcc.populate[attributeName] = componentResult;
4042
+ }
4043
+ return populateAcc;
3724
4044
  }
3725
- return fp.merge(
3726
- populateAcc,
3727
- getPopulateFor(
3728
- attributeName,
3729
- model,
3730
- {
3731
- // @ts-expect-error - improve types
3732
- initialPopulate: initialPopulate?.[attributeName],
3733
- countMany,
3734
- countOne,
3735
- maxLevel
4045
+ if (isDynamicZone(attribute)) {
4046
+ const components2 = attribute.components;
4047
+ const componentsResult = (components2 || []).reduce(
4048
+ (acc, componentUID) => {
4049
+ const componentResult = getPopulateForValidation(componentUID);
4050
+ if (Object.keys(componentResult).length > 0) {
4051
+ acc[componentUID] = componentResult;
4052
+ }
4053
+ return acc;
3736
4054
  },
3737
- level
3738
- )
3739
- );
4055
+ {}
4056
+ );
4057
+ if (Object.keys(componentsResult).length > 0) {
4058
+ populateAcc.populate = populateAcc.populate || {};
4059
+ populateAcc.populate[attributeName] = { on: componentsResult };
4060
+ }
4061
+ }
4062
+ return populateAcc;
3740
4063
  }, {});
3741
4064
  };
3742
4065
  const getDeepPopulateDraftCount = (uid2) => {
@@ -3746,6 +4069,10 @@ const getDeepPopulateDraftCount = (uid2) => {
3746
4069
  const attribute = model.attributes[attributeName];
3747
4070
  switch (attribute.type) {
3748
4071
  case "relation": {
4072
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
4073
+ if (isMorphRelation) {
4074
+ break;
4075
+ }
3749
4076
  if (isVisibleAttribute$1(model, attributeName)) {
3750
4077
  populateAcc[attributeName] = {
3751
4078
  count: true,
@@ -3812,7 +4139,7 @@ const getQueryPopulate = async (uid2, query) => {
3812
4139
  return populateQuery;
3813
4140
  };
3814
4141
  const buildDeepPopulate = (uid2) => {
3815
- return getService$1("populate-builder")(uid2).populateDeep(Infinity).countRelations().build();
4142
+ return getService$2("populate-builder")(uid2).populateDeep(Infinity).countRelations().build();
3816
4143
  };
3817
4144
  const populateBuilder = (uid2) => {
3818
4145
  let getInitialPopulate = async () => {
@@ -3974,7 +4301,6 @@ const AVAILABLE_LOCALES_FIELDS = [
3974
4301
  "locale",
3975
4302
  "updatedAt",
3976
4303
  "createdAt",
3977
- "status",
3978
4304
  "publishedAt",
3979
4305
  "documentId"
3980
4306
  ];
@@ -3995,34 +4321,20 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3995
4321
  /**
3996
4322
  * Returns available locales of a document for the current status
3997
4323
  */
3998
- async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
4324
+ async getAvailableLocales(uid2, version, allVersions) {
3999
4325
  const versionsByLocale = fp.groupBy("locale", allVersions);
4000
- delete versionsByLocale[version.locale];
4326
+ if (version.locale) {
4327
+ delete versionsByLocale[version.locale];
4328
+ }
4001
4329
  const model = strapi2.getModel(uid2);
4002
- const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
4003
- const traversalFunction = async (localeVersion) => strapiUtils.traverseEntity(
4004
- ({ key }, { remove }) => {
4005
- if (keysToKeep.includes(key)) {
4006
- return;
4007
- }
4008
- remove(key);
4009
- },
4010
- { schema: model, getModel: strapi2.getModel.bind(strapi2) },
4011
- // @ts-expect-error fix types DocumentVersion incompatible with Data
4012
- localeVersion
4013
- );
4014
4330
  const mappingResult = await strapiUtils.async.map(
4015
4331
  Object.values(versionsByLocale),
4016
4332
  async (localeVersions) => {
4017
- const mappedLocaleVersions = await strapiUtils.async.map(
4018
- localeVersions,
4019
- traversalFunction
4020
- );
4021
4333
  if (!strapiUtils.contentTypes.hasDraftAndPublish(model)) {
4022
- return mappedLocaleVersions[0];
4334
+ return localeVersions[0];
4023
4335
  }
4024
- const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
4025
- const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
4336
+ const draftVersion = localeVersions.find((v) => v.publishedAt === null);
4337
+ const otherVersions = localeVersions.filter((v) => v.id !== draftVersion?.id);
4026
4338
  if (!draftVersion) {
4027
4339
  return;
4028
4340
  }
@@ -4044,8 +4356,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4044
4356
  const matchStatus = status === "published" ? v.publishedAt !== null : v.publishedAt === null;
4045
4357
  return matchLocale && matchStatus;
4046
4358
  });
4047
- if (!availableStatus)
4048
- return availableStatus;
4359
+ if (!availableStatus) return availableStatus;
4049
4360
  return fp.pick(AVAILABLE_STATUS_FIELDS, availableStatus);
4050
4361
  },
4051
4362
  /**
@@ -4055,18 +4366,19 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4055
4366
  * @returns
4056
4367
  */
4057
4368
  async getManyAvailableStatus(uid2, documents) {
4058
- if (!documents.length)
4059
- return [];
4369
+ if (!documents.length) return [];
4060
4370
  const status = documents[0].publishedAt !== null ? "published" : "draft";
4061
- const locale = documents[0]?.locale;
4062
- const otherStatus = status === "published" ? "draft" : "published";
4063
- return strapi2.documents(uid2).findMany({
4064
- filters: {
4065
- documentId: { $in: documents.map((d) => d.documentId).filter(Boolean) }
4066
- },
4067
- status: otherStatus,
4068
- locale,
4069
- fields: ["documentId", "locale", "updatedAt", "createdAt", "publishedAt"]
4371
+ const locales = documents.map((d) => d.locale).filter(Boolean);
4372
+ const where = {
4373
+ documentId: { $in: documents.map((d) => d.documentId).filter(Boolean) },
4374
+ publishedAt: { $null: status === "published" }
4375
+ };
4376
+ if (locales.length) {
4377
+ where.locale = { $in: locales };
4378
+ }
4379
+ return strapi2.query(uid2).findMany({
4380
+ where,
4381
+ select: ["id", "documentId", "locale", "updatedAt", "createdAt", "publishedAt"]
4070
4382
  });
4071
4383
  },
4072
4384
  getStatus(version, otherDocumentStatuses) {
@@ -4083,10 +4395,8 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4083
4395
  } else if (otherVersion) {
4084
4396
  draftVersion = otherVersion;
4085
4397
  }
4086
- if (!draftVersion)
4087
- return CONTENT_MANAGER_STATUS.PUBLISHED;
4088
- if (!publishedVersion)
4089
- return CONTENT_MANAGER_STATUS.DRAFT;
4398
+ if (!draftVersion) return CONTENT_MANAGER_STATUS.PUBLISHED;
4399
+ if (!publishedVersion) return CONTENT_MANAGER_STATUS.DRAFT;
4090
4400
  const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
4091
4401
  return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
4092
4402
  },
@@ -4094,11 +4404,9 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4094
4404
  // We could refactor this so the locales are only loaded when they're
4095
4405
  // needed. e.g. in the bulk locale action modal.
4096
4406
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4097
- const populate = getValidatableFieldsPopulate(uid2);
4098
- const versions = await strapi2.db.query(uid2).findMany({
4099
- where: { documentId: version.documentId },
4407
+ const { populate = {}, fields = [] } = getPopulateForValidation(uid2);
4408
+ const params = {
4100
4409
  populate: {
4101
- // Populate only fields that require validation for bulk locale actions
4102
4410
  ...populate,
4103
4411
  // NOTE: creator fields are selected in this way to avoid exposing sensitive data
4104
4412
  createdBy: {
@@ -4107,9 +4415,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4107
4415
  updatedBy: {
4108
4416
  select: ["id", "firstname", "lastname", "email"]
4109
4417
  }
4418
+ },
4419
+ fields: fp.uniq([...AVAILABLE_LOCALES_FIELDS, ...fields]),
4420
+ filters: {
4421
+ documentId: version.documentId
4110
4422
  }
4111
- });
4112
- const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
4423
+ };
4424
+ const dbParams = strapi2.get("query-params").transform(uid2, params);
4425
+ const versions = await strapi2.db.query(uid2).findMany(dbParams);
4426
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions) : [];
4113
4427
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
4114
4428
  return {
4115
4429
  availableLocales: availableLocalesResult,
@@ -4123,13 +4437,29 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4123
4437
  */
4124
4438
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
4125
4439
  if (!document) {
4126
- return document;
4440
+ return {
4441
+ data: document,
4442
+ meta: {
4443
+ availableLocales: [],
4444
+ availableStatus: []
4445
+ }
4446
+ };
4127
4447
  }
4128
4448
  const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
4129
4449
  if (!hasDraftAndPublish) {
4130
4450
  opts.availableStatus = false;
4131
4451
  }
4132
4452
  const meta = await this.getMetadata(uid2, document, opts);
4453
+ if (document.localizations) {
4454
+ const otherStatus = await this.getManyAvailableStatus(uid2, document.localizations);
4455
+ document.localizations = document.localizations.map((d) => {
4456
+ const status = otherStatus.find((s) => s.documentId === d.documentId);
4457
+ return {
4458
+ ...d,
4459
+ status: this.getStatus(d, status ? [status] : [])
4460
+ };
4461
+ });
4462
+ }
4133
4463
  return {
4134
4464
  data: {
4135
4465
  ...document,
@@ -4231,10 +4561,7 @@ const documentManager = ({ strapi: strapi2 }) => {
4231
4561
  async clone(id, body, uid2) {
4232
4562
  const populate = await buildDeepPopulate(uid2);
4233
4563
  const params = {
4234
- data: {
4235
- ...omitIdField(body),
4236
- [PUBLISHED_AT_ATTRIBUTE]: null
4237
- },
4564
+ data: omitIdField(body),
4238
4565
  populate
4239
4566
  };
4240
4567
  return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
@@ -4350,7 +4677,8 @@ const services = {
4350
4677
  permission,
4351
4678
  "populate-builder": populateBuilder$1,
4352
4679
  uid,
4353
- ...history.services ? history.services : {}
4680
+ ...history.services ? history.services : {},
4681
+ ...preview.services ? preview.services : {}
4354
4682
  };
4355
4683
  const index = () => {
4356
4684
  return {