@strapi/content-manager 0.0.0-experimental.a65a85fdea97faae8679d3ffc5f9d79af61abd26 → 0.0.0-experimental.a687a6977f91492ccfc6771bf398a5236ece3c94

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 (260) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{CardDragPreview-DSVYodBX.js → CardDragPreview-C0QyJgRA.js} +10 -14
  3. package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -0
  4. package/dist/_chunks/{CardDragPreview-ikSG4M46.mjs → CardDragPreview-DOxamsuj.mjs} +7 -9
  5. package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -0
  6. package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js → ComponentConfigurationPage-BebDdCkl.js} +4 -4
  7. package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js.map → ComponentConfigurationPage-BebDdCkl.js.map} +1 -1
  8. package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs → ComponentConfigurationPage-XdGcgtZh.mjs} +4 -4
  9. package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs.map → ComponentConfigurationPage-XdGcgtZh.mjs.map} +1 -1
  10. package/dist/_chunks/{ComponentIcon-BBQsYCVn.js → ComponentIcon-BXdiCGQp.js} +8 -2
  11. package/dist/_chunks/ComponentIcon-BXdiCGQp.js.map +1 -0
  12. package/dist/_chunks/{ComponentIcon-BOFnK76n.mjs → ComponentIcon-u4bIXTFY.mjs} +9 -3
  13. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -0
  14. package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs → EditConfigurationPage-DaNf9MoK.mjs} +4 -4
  15. package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs.map → EditConfigurationPage-DaNf9MoK.mjs.map} +1 -1
  16. package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js → EditConfigurationPage-sdGi-bna.js} +4 -4
  17. package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js.map → EditConfigurationPage-sdGi-bna.js.map} +1 -1
  18. package/dist/_chunks/{EditViewPage-CzOT5Kpj.js → EditViewPage-BRA-JSnw.js} +101 -52
  19. package/dist/_chunks/EditViewPage-BRA-JSnw.js.map +1 -0
  20. package/dist/_chunks/EditViewPage-DtbTsNeX.mjs +254 -0
  21. package/dist/_chunks/EditViewPage-DtbTsNeX.mjs.map +1 -0
  22. package/dist/_chunks/{Field-Dlh0uGnL.mjs → Field-CDmB9zqh.mjs} +1075 -812
  23. package/dist/_chunks/Field-CDmB9zqh.mjs.map +1 -0
  24. package/dist/_chunks/{Field-Caef4JjM.js → Field-DoVRA4co.js} +1120 -858
  25. package/dist/_chunks/Field-DoVRA4co.js.map +1 -0
  26. package/dist/_chunks/{Form-BzuAjtRq.js → Form-BEh514bg.js} +69 -49
  27. package/dist/_chunks/Form-BEh514bg.js.map +1 -0
  28. package/dist/_chunks/{Form-EnaQL_6L.mjs → Form-Cle2X-4n.mjs} +70 -49
  29. package/dist/_chunks/Form-Cle2X-4n.mjs.map +1 -0
  30. package/dist/_chunks/{History-C17LiyRg.js → History-BsPXl1XH.js} +182 -146
  31. package/dist/_chunks/History-BsPXl1XH.js.map +1 -0
  32. package/dist/_chunks/{History-D6sbCJvo.mjs → History-ktAPcvHJ.mjs} +182 -145
  33. package/dist/_chunks/History-ktAPcvHJ.mjs.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-Ce4qs7qE.mjs → ListConfigurationPage-BNAS_v2s.mjs} +71 -60
  35. package/dist/_chunks/ListConfigurationPage-BNAS_v2s.mjs.map +1 -0
  36. package/dist/_chunks/{ListConfigurationPage-Dks5SX6f.js → ListConfigurationPage-CN1-7Pgi.js} +73 -63
  37. package/dist/_chunks/ListConfigurationPage-CN1-7Pgi.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-Be7S5aKL.mjs → ListViewPage-B15c_0Vt.mjs} +143 -139
  39. package/dist/_chunks/ListViewPage-B15c_0Vt.mjs.map +1 -0
  40. package/dist/_chunks/{ListViewPage-BwrZrPsh.js → ListViewPage-BUKFOJuS.js} +146 -142
  41. package/dist/_chunks/ListViewPage-BUKFOJuS.js.map +1 -0
  42. package/dist/_chunks/{NoContentTypePage-CIPmYQMm.mjs → NoContentTypePage-CAgdmhlo.mjs} +7 -7
  43. package/dist/_chunks/NoContentTypePage-CAgdmhlo.mjs.map +1 -0
  44. package/dist/_chunks/{NoContentTypePage-Cu5r1-JT.js → NoContentTypePage-Cs7Kk9EW.js} +5 -5
  45. package/dist/_chunks/NoContentTypePage-Cs7Kk9EW.js.map +1 -0
  46. package/dist/_chunks/{NoPermissionsPage-C-j6TEUF.js → NoPermissionsPage-DRPnEq9t.js} +4 -5
  47. package/dist/_chunks/NoPermissionsPage-DRPnEq9t.js.map +1 -0
  48. package/dist/_chunks/{NoPermissionsPage-DhJ7LYrr.mjs → NoPermissionsPage-JxX4Y4RJ.mjs} +5 -6
  49. package/dist/_chunks/NoPermissionsPage-JxX4Y4RJ.mjs.map +1 -0
  50. package/dist/_chunks/Preview-BKuVG1dV.js +286 -0
  51. package/dist/_chunks/Preview-BKuVG1dV.js.map +1 -0
  52. package/dist/_chunks/Preview-CQlTtbLc.mjs +267 -0
  53. package/dist/_chunks/Preview-CQlTtbLc.mjs.map +1 -0
  54. package/dist/_chunks/{Relations-CY7AtkDA.mjs → Relations-DMQ93R3L.mjs} +135 -89
  55. package/dist/_chunks/Relations-DMQ93R3L.mjs.map +1 -0
  56. package/dist/_chunks/{Relations-Czs-uZ-s.js → Relations-DTEGSaM_.js} +138 -93
  57. package/dist/_chunks/Relations-DTEGSaM_.js.map +1 -0
  58. package/dist/_chunks/{en-MBPul9Su.mjs → en-CfIXaZf9.mjs} +36 -17
  59. package/dist/_chunks/{en-MBPul9Su.mjs.map → en-CfIXaZf9.mjs.map} +1 -1
  60. package/dist/_chunks/{en-C-V1_90f.js → en-DTWPCdTS.js} +36 -17
  61. package/dist/_chunks/{en-C-V1_90f.js.map → en-DTWPCdTS.js.map} +1 -1
  62. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  63. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  64. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  65. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  66. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  67. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  68. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  69. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  70. package/dist/_chunks/{index-DNVx8ssZ.mjs → index-BdUq-Dtg.mjs} +1839 -831
  71. package/dist/_chunks/index-BdUq-Dtg.mjs.map +1 -0
  72. package/dist/_chunks/{index-X_2tafck.js → index-DjV7tyGu.js} +1826 -818
  73. package/dist/_chunks/index-DjV7tyGu.js.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-Dnh0PNp9.mjs → layout-BJ8CpEJu.mjs} +47 -29
  79. package/dist/_chunks/layout-BJ8CpEJu.mjs.map +1 -0
  80. package/dist/_chunks/{layout-dBc7wN7L.js → layout-Cx9LHtH5.js} +47 -31
  81. package/dist/_chunks/layout-Cx9LHtH5.js.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-4pHtBrHJ.js → relations-5bc76OJz.js} +6 -7
  87. package/dist/_chunks/relations-5bc76OJz.js.map +1 -0
  88. package/dist/_chunks/{relations-Dx7tMKJN.mjs → relations-BeezTo41.mjs} +6 -7
  89. package/dist/_chunks/relations-BeezTo41.mjs.map +1 -0
  90. package/dist/_chunks/useDebounce-CtcjDB3L.js +28 -0
  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-DdHgKsqq.mjs.map +1 -1
  95. package/dist/_chunks/useDragAndDrop-J0TUUbR6.js.map +1 -1
  96. package/dist/admin/index.js +3 -1
  97. package/dist/admin/index.js.map +1 -1
  98. package/dist/admin/index.mjs +9 -7
  99. package/dist/admin/src/components/ComponentIcon.d.ts +6 -3
  100. package/dist/admin/src/content-manager.d.ts +3 -3
  101. package/dist/admin/src/exports.d.ts +2 -1
  102. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -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 +37 -9
  106. package/dist/admin/src/hooks/useDocumentActions.d.ts +24 -3
  107. package/dist/admin/src/hooks/useDocumentLayout.d.ts +2 -2
  108. package/dist/admin/src/hooks/useDragAndDrop.d.ts +4 -4
  109. package/dist/admin/src/hooks/useKeyboardDragAndDrop.d.ts +1 -1
  110. package/dist/admin/src/index.d.ts +1 -0
  111. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  112. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +11 -4
  113. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
  114. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksInput.d.ts +3 -3
  115. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +4 -0
  116. package/dist/admin/src/pages/EditView/components/FormInputs/Component/Input.d.ts +2 -2
  117. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +3 -5
  118. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
  119. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +30 -18
  120. package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
  121. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +3 -49
  122. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
  123. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  124. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +16 -53
  125. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  126. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
  127. package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
  128. package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
  129. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +9 -26
  130. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  131. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  132. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  133. package/dist/admin/src/preview/constants.d.ts +1 -0
  134. package/dist/admin/src/preview/index.d.ts +4 -0
  135. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  136. package/dist/admin/src/preview/routes.d.ts +3 -0
  137. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  138. package/dist/admin/src/router.d.ts +1 -1
  139. package/dist/admin/src/services/api.d.ts +2 -3
  140. package/dist/admin/src/services/components.d.ts +2 -2
  141. package/dist/admin/src/services/contentTypes.d.ts +5 -5
  142. package/dist/admin/src/services/documents.d.ts +31 -20
  143. package/dist/admin/src/services/init.d.ts +2 -2
  144. package/dist/admin/src/services/relations.d.ts +3 -3
  145. package/dist/admin/src/services/uid.d.ts +3 -3
  146. package/dist/admin/src/utils/api.d.ts +4 -18
  147. package/dist/admin/src/utils/validation.d.ts +5 -7
  148. package/dist/server/index.js +1008 -594
  149. package/dist/server/index.js.map +1 -1
  150. package/dist/server/index.mjs +1014 -600
  151. package/dist/server/index.mjs.map +1 -1
  152. package/dist/server/src/bootstrap.d.ts.map +1 -1
  153. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  154. package/dist/server/src/controllers/index.d.ts.map +1 -1
  155. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  156. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  157. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  158. package/dist/server/src/controllers/utils/metadata.d.ts +22 -0
  159. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
  160. package/dist/server/src/controllers/validation/dimensions.d.ts +11 -0
  161. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
  162. package/dist/server/src/controllers/validation/index.d.ts +1 -1
  163. package/dist/server/src/history/services/history.d.ts +2 -4
  164. package/dist/server/src/history/services/history.d.ts.map +1 -1
  165. package/dist/server/src/history/services/index.d.ts +6 -2
  166. package/dist/server/src/history/services/index.d.ts.map +1 -1
  167. package/dist/server/src/history/services/lifecycles.d.ts +9 -0
  168. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -0
  169. package/dist/server/src/history/services/utils.d.ts +41 -9
  170. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  171. package/dist/server/src/history/utils.d.ts +6 -2
  172. package/dist/server/src/history/utils.d.ts.map +1 -1
  173. package/dist/server/src/index.d.ts +21 -42
  174. package/dist/server/src/index.d.ts.map +1 -1
  175. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  176. package/dist/server/src/preview/constants.d.ts +2 -0
  177. package/dist/server/src/preview/constants.d.ts.map +1 -0
  178. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  179. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  180. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  181. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  182. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  183. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  184. package/dist/server/src/preview/index.d.ts +4 -0
  185. package/dist/server/src/preview/index.d.ts.map +1 -0
  186. package/dist/server/src/preview/routes/index.d.ts +8 -0
  187. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  188. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  189. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  190. package/dist/server/src/preview/services/index.d.ts +15 -0
  191. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  192. package/dist/server/src/preview/services/preview-config.d.ts +30 -0
  193. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  194. package/dist/server/src/preview/services/preview.d.ts +12 -0
  195. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  196. package/dist/server/src/preview/utils.d.ts +18 -0
  197. package/dist/server/src/preview/utils.d.ts.map +1 -0
  198. package/dist/server/src/routes/index.d.ts.map +1 -1
  199. package/dist/server/src/services/document-manager.d.ts +13 -12
  200. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  201. package/dist/server/src/services/document-metadata.d.ts +14 -35
  202. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  203. package/dist/server/src/services/index.d.ts +21 -42
  204. package/dist/server/src/services/index.d.ts.map +1 -1
  205. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  206. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  207. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  208. package/dist/server/src/services/utils/populate.d.ts +8 -1
  209. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  210. package/dist/server/src/utils/index.d.ts +2 -0
  211. package/dist/server/src/utils/index.d.ts.map +1 -1
  212. package/dist/shared/contracts/collection-types.d.ts +17 -7
  213. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  214. package/dist/shared/contracts/index.d.ts +1 -0
  215. package/dist/shared/contracts/index.d.ts.map +1 -1
  216. package/dist/shared/contracts/preview.d.ts +27 -0
  217. package/dist/shared/contracts/preview.d.ts.map +1 -0
  218. package/dist/shared/contracts/relations.d.ts +2 -2
  219. package/dist/shared/contracts/relations.d.ts.map +1 -1
  220. package/dist/shared/index.js +4 -0
  221. package/dist/shared/index.js.map +1 -1
  222. package/dist/shared/index.mjs +4 -0
  223. package/dist/shared/index.mjs.map +1 -1
  224. package/package.json +19 -20
  225. package/dist/_chunks/CardDragPreview-DSVYodBX.js.map +0 -1
  226. package/dist/_chunks/CardDragPreview-ikSG4M46.mjs.map +0 -1
  227. package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
  228. package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
  229. package/dist/_chunks/EditViewPage-Bm8lgcm6.mjs +0 -203
  230. package/dist/_chunks/EditViewPage-Bm8lgcm6.mjs.map +0 -1
  231. package/dist/_chunks/EditViewPage-CzOT5Kpj.js.map +0 -1
  232. package/dist/_chunks/Field-Caef4JjM.js.map +0 -1
  233. package/dist/_chunks/Field-Dlh0uGnL.mjs.map +0 -1
  234. package/dist/_chunks/Form-BzuAjtRq.js.map +0 -1
  235. package/dist/_chunks/Form-EnaQL_6L.mjs.map +0 -1
  236. package/dist/_chunks/History-C17LiyRg.js.map +0 -1
  237. package/dist/_chunks/History-D6sbCJvo.mjs.map +0 -1
  238. package/dist/_chunks/ListConfigurationPage-Ce4qs7qE.mjs.map +0 -1
  239. package/dist/_chunks/ListConfigurationPage-Dks5SX6f.js.map +0 -1
  240. package/dist/_chunks/ListViewPage-Be7S5aKL.mjs.map +0 -1
  241. package/dist/_chunks/ListViewPage-BwrZrPsh.js.map +0 -1
  242. package/dist/_chunks/NoContentTypePage-CIPmYQMm.mjs.map +0 -1
  243. package/dist/_chunks/NoContentTypePage-Cu5r1-JT.js.map +0 -1
  244. package/dist/_chunks/NoPermissionsPage-C-j6TEUF.js.map +0 -1
  245. package/dist/_chunks/NoPermissionsPage-DhJ7LYrr.mjs.map +0 -1
  246. package/dist/_chunks/Relations-CY7AtkDA.mjs.map +0 -1
  247. package/dist/_chunks/Relations-Czs-uZ-s.js.map +0 -1
  248. package/dist/_chunks/index-DNVx8ssZ.mjs.map +0 -1
  249. package/dist/_chunks/index-X_2tafck.js.map +0 -1
  250. package/dist/_chunks/layout-Dnh0PNp9.mjs.map +0 -1
  251. package/dist/_chunks/layout-dBc7wN7L.js.map +0 -1
  252. package/dist/_chunks/relations-4pHtBrHJ.js.map +0 -1
  253. package/dist/_chunks/relations-Dx7tMKJN.mjs.map +0 -1
  254. package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
  255. package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
  256. package/dist/_chunks/urls-DzZya_gm.js +0 -6
  257. package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
  258. package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
  259. package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
  260. package/strapi-server.js +0 -3
@@ -33,10 +33,10 @@ const isNil__default = /* @__PURE__ */ _interopDefault(isNil);
33
33
  const ___default = /* @__PURE__ */ _interopDefault(_);
34
34
  const qs__default = /* @__PURE__ */ _interopDefault(qs);
35
35
  const slugify__default = /* @__PURE__ */ _interopDefault(slugify);
36
- const getService$1 = (name) => {
36
+ const getService$2 = (name) => {
37
37
  return strapi.plugin("content-manager").service(name);
38
38
  };
39
- function getService(strapi2, name) {
39
+ function getService$1(strapi2, name) {
40
40
  return strapi2.service(`plugin::content-manager.${name}`);
41
41
  }
42
42
  const historyRestoreVersionSchema = yup__namespace.object().shape({
@@ -72,7 +72,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
72
72
  if (!isSingleType && (!contentTypeUid || !ctx.query.documentId)) {
73
73
  throw new strapiUtils.errors.ForbiddenError("contentType and documentId are required");
74
74
  }
75
- const permissionChecker2 = getService$1("permission-checker").create({
75
+ const permissionChecker2 = getService$2("permission-checker").create({
76
76
  userAbility: ctx.state.userAbility,
77
77
  model: ctx.query.contentType
78
78
  });
@@ -80,7 +80,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
80
80
  return ctx.forbidden();
81
81
  }
82
82
  const query = await permissionChecker2.sanitizeQuery(ctx.query);
83
- const { results, pagination } = await getService(strapi2, "history").findVersionsPage({
83
+ const { results, pagination } = await getService$1(strapi2, "history").findVersionsPage({
84
84
  query: {
85
85
  ...query,
86
86
  ...getValidPagination({ page: query.page, pageSize: query.pageSize })
@@ -105,14 +105,14 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
105
105
  async restoreVersion(ctx) {
106
106
  const request = ctx.request;
107
107
  await validateRestoreVersion(request.body, "contentType is required");
108
- const permissionChecker2 = getService$1("permission-checker").create({
108
+ const permissionChecker2 = getService$2("permission-checker").create({
109
109
  userAbility: ctx.state.userAbility,
110
110
  model: request.body.contentType
111
111
  });
112
112
  if (permissionChecker2.cannot.update()) {
113
113
  throw new strapiUtils.errors.ForbiddenError();
114
114
  }
115
- const restoredDocument = await getService(strapi2, "history").restoreVersion(
115
+ const restoredDocument = await getService$1(strapi2, "history").restoreVersion(
116
116
  request.params.versionId
117
117
  );
118
118
  return {
@@ -121,7 +121,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
121
121
  }
122
122
  };
123
123
  };
124
- const controllers$1 = {
124
+ const controllers$2 = {
125
125
  "history-version": createHistoryVersionController
126
126
  /**
127
127
  * Casting is needed because the types aren't aware that Strapi supports
@@ -138,43 +138,70 @@ const FIELDS_TO_IGNORE = [
138
138
  "strapi_stage",
139
139
  "strapi_assignee"
140
140
  ];
141
- const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
142
- const sanitizedContentTypeSchemaAttributes = fp.omit(FIELDS_TO_IGNORE, contentTypeSchemaAttributes);
143
- const reduceDifferenceToAttributesObject = (diffKeys, source) => {
144
- return diffKeys.reduce((previousAttributesObject, diffKey) => {
145
- previousAttributesObject[diffKey] = source[diffKey];
146
- return previousAttributesObject;
147
- }, {});
148
- };
149
- const versionSchemaKeys = Object.keys(versionSchemaAttributes);
150
- const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
151
- const uniqueToContentType = fp.difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
152
- const added = reduceDifferenceToAttributesObject(
153
- uniqueToContentType,
154
- sanitizedContentTypeSchemaAttributes
155
- );
156
- const uniqueToVersion = fp.difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
157
- const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
158
- return { added, removed };
159
- };
160
141
  const DEFAULT_RETENTION_DAYS = 90;
161
- const createHistoryService = ({ strapi: strapi2 }) => {
162
- const state = {
163
- deleteExpiredJob: null,
164
- isInitialized: false
142
+ const createServiceUtils = ({ strapi: strapi2 }) => {
143
+ const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
144
+ const sanitizedContentTypeSchemaAttributes = fp.omit(
145
+ FIELDS_TO_IGNORE,
146
+ contentTypeSchemaAttributes
147
+ );
148
+ const reduceDifferenceToAttributesObject = (diffKeys, source) => {
149
+ return diffKeys.reduce(
150
+ (previousAttributesObject, diffKey) => {
151
+ previousAttributesObject[diffKey] = source[diffKey];
152
+ return previousAttributesObject;
153
+ },
154
+ {}
155
+ );
156
+ };
157
+ const versionSchemaKeys = Object.keys(versionSchemaAttributes);
158
+ const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
159
+ const uniqueToContentType = fp.difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
160
+ const added = reduceDifferenceToAttributesObject(
161
+ uniqueToContentType,
162
+ sanitizedContentTypeSchemaAttributes
163
+ );
164
+ const uniqueToVersion = fp.difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
165
+ const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
166
+ return { added, removed };
165
167
  };
166
- const query = strapi2.db.query(HISTORY_VERSION_UID);
167
- const getRetentionDays = (strapi22) => {
168
- const featureConfig = strapi22.ee.features.get("cms-content-history");
169
- const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
170
- const userRetentionDays = strapi22.config.get("admin.history.retentionDays");
171
- if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
172
- return userRetentionDays;
168
+ const getRelationRestoreValue = async (versionRelationData, attribute) => {
169
+ if (Array.isArray(versionRelationData)) {
170
+ if (versionRelationData.length === 0)
171
+ return versionRelationData;
172
+ const existingAndMissingRelations = await Promise.all(
173
+ versionRelationData.map((relation) => {
174
+ return strapi2.documents(attribute.target).findOne({
175
+ documentId: relation.documentId,
176
+ locale: relation.locale || void 0
177
+ });
178
+ })
179
+ );
180
+ return existingAndMissingRelations.filter(
181
+ (relation) => relation !== null
182
+ );
173
183
  }
174
- return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
184
+ return strapi2.documents(attribute.target).findOne({
185
+ documentId: versionRelationData.documentId,
186
+ locale: versionRelationData.locale || void 0
187
+ });
188
+ };
189
+ const getMediaRestoreValue = async (versionRelationData, attribute) => {
190
+ if (attribute.multiple) {
191
+ const existingAndMissingMedias = await Promise.all(
192
+ // @ts-expect-error Fix the type definitions so this isn't any
193
+ versionRelationData.map((media) => {
194
+ return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
195
+ })
196
+ );
197
+ return existingAndMissingMedias.filter((media) => media != null);
198
+ }
199
+ return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
175
200
  };
176
201
  const localesService = strapi2.plugin("i18n")?.service("locales");
202
+ const i18nContentTypeService = strapi2.plugin("i18n")?.service("content-types");
177
203
  const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
204
+ const isLocalizedContentType = (model) => i18nContentTypeService ? i18nContentTypeService.isLocalizedContentType(model) : false;
178
205
  const getLocaleDictionary = async () => {
179
206
  if (!localesService)
180
207
  return {};
@@ -187,36 +214,67 @@ const createHistoryService = ({ strapi: strapi2 }) => {
187
214
  {}
188
215
  );
189
216
  };
217
+ const getRetentionDays = () => {
218
+ const featureConfig = strapi2.ee.features.get("cms-content-history");
219
+ const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
220
+ const userRetentionDays = strapi2.config.get("admin.history.retentionDays");
221
+ if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
222
+ return userRetentionDays;
223
+ }
224
+ return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
225
+ };
190
226
  const getVersionStatus = async (contentTypeUid, document) => {
191
227
  const documentMetadataService = strapi2.plugin("content-manager").service("document-metadata");
192
228
  const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
193
229
  return documentMetadataService.getStatus(document, meta.availableStatus);
194
230
  };
195
- const getDeepPopulate2 = (uid2) => {
231
+ const getComponentFields = (componentUID) => {
232
+ return Object.entries(strapi2.getModel(componentUID).attributes).reduce(
233
+ (fieldsAcc, [key, attribute]) => {
234
+ if (!["relation", "media", "component", "dynamiczone"].includes(attribute.type)) {
235
+ fieldsAcc.push(key);
236
+ }
237
+ return fieldsAcc;
238
+ },
239
+ []
240
+ );
241
+ };
242
+ const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
196
243
  const model = strapi2.getModel(uid2);
197
244
  const attributes = Object.entries(model.attributes);
245
+ const fieldSelector = useDatabaseSyntax ? "select" : "fields";
198
246
  return attributes.reduce((acc, [attributeName, attribute]) => {
199
247
  switch (attribute.type) {
200
248
  case "relation": {
249
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
250
+ if (isMorphRelation) {
251
+ break;
252
+ }
201
253
  const isVisible2 = strapiUtils.contentTypes.isVisibleAttribute(model, attributeName);
202
254
  if (isVisible2) {
203
- acc[attributeName] = { fields: ["documentId", "locale", "publishedAt"] };
255
+ acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
204
256
  }
205
257
  break;
206
258
  }
207
259
  case "media": {
208
- acc[attributeName] = { fields: ["id"] };
260
+ acc[attributeName] = { [fieldSelector]: ["id"] };
209
261
  break;
210
262
  }
211
263
  case "component": {
212
264
  const populate = getDeepPopulate2(attribute.component);
213
- acc[attributeName] = { populate };
265
+ acc[attributeName] = {
266
+ populate,
267
+ [fieldSelector]: getComponentFields(attribute.component)
268
+ };
214
269
  break;
215
270
  }
216
271
  case "dynamiczone": {
217
272
  const populatedComponents = (attribute.components || []).reduce(
218
273
  (acc2, componentUID) => {
219
- acc2[componentUID] = { populate: getDeepPopulate2(componentUID) };
274
+ acc2[componentUID] = {
275
+ populate: getDeepPopulate2(componentUID),
276
+ [fieldSelector]: getComponentFields(componentUID)
277
+ };
220
278
  return acc2;
221
279
  },
222
280
  {}
@@ -228,80 +286,69 @@ const createHistoryService = ({ strapi: strapi2 }) => {
228
286
  return acc;
229
287
  }, {});
230
288
  };
231
- return {
232
- async bootstrap() {
233
- if (state.isInitialized) {
234
- return;
235
- }
236
- strapi2.documents.use(async (context, next) => {
237
- if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
238
- return next();
289
+ const buildMediaResponse = async (values) => {
290
+ return values.slice(0, 25).reduce(
291
+ async (currentRelationDataPromise, entry) => {
292
+ const currentRelationData = await currentRelationDataPromise;
293
+ if (!entry) {
294
+ return currentRelationData;
239
295
  }
240
- if (context.action !== "create" && context.action !== "update" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
241
- return next();
296
+ const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
297
+ if (relatedEntry) {
298
+ currentRelationData.results.push(relatedEntry);
299
+ } else {
300
+ currentRelationData.meta.missingCount += 1;
242
301
  }
243
- const contentTypeUid = context.contentType.uid;
244
- if (!contentTypeUid.startsWith("api::")) {
245
- return next();
302
+ return currentRelationData;
303
+ },
304
+ Promise.resolve({
305
+ results: [],
306
+ meta: { missingCount: 0 }
307
+ })
308
+ );
309
+ };
310
+ const buildRelationReponse = async (values, attributeSchema) => {
311
+ return values.slice(0, 25).reduce(
312
+ async (currentRelationDataPromise, entry) => {
313
+ const currentRelationData = await currentRelationDataPromise;
314
+ if (!entry) {
315
+ return currentRelationData;
246
316
  }
247
- const result = await next();
248
- const documentContext = context.action === "create" ? { documentId: result.documentId, locale: context.params?.locale } : { documentId: context.params.documentId, locale: context.params?.locale };
249
- const defaultLocale = await getDefaultLocale();
250
- const locale = documentContext.locale || defaultLocale;
251
- const document = await strapi2.documents(contentTypeUid).findOne({
252
- documentId: documentContext.documentId,
253
- locale,
254
- populate: getDeepPopulate2(contentTypeUid)
255
- });
256
- const status = await getVersionStatus(contentTypeUid, document);
257
- const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
258
- const componentsSchemas = Object.keys(
259
- attributesSchema
260
- ).reduce((currentComponentSchemas, key) => {
261
- const fieldSchema = attributesSchema[key];
262
- if (fieldSchema.type === "component") {
263
- const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
264
- return {
265
- ...currentComponentSchemas,
266
- [fieldSchema.component]: componentSchema
267
- };
268
- }
269
- return currentComponentSchemas;
270
- }, {});
271
- await strapi2.db.transaction(async ({ onCommit }) => {
272
- onCommit(() => {
273
- this.createVersion({
274
- contentType: contentTypeUid,
275
- data: fp.omit(FIELDS_TO_IGNORE, document),
276
- schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
277
- componentsSchemas,
278
- relatedDocumentId: documentContext.documentId,
279
- locale,
280
- status
281
- });
317
+ const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
318
+ if (relatedEntry) {
319
+ currentRelationData.results.push({
320
+ ...relatedEntry,
321
+ status: await getVersionStatus(attributeSchema.target, relatedEntry)
282
322
  });
283
- });
284
- return result;
285
- });
286
- const retentionDays = getRetentionDays(strapi2);
287
- state.deleteExpiredJob = nodeSchedule.scheduleJob("0 0 * * *", () => {
288
- const retentionDaysInMilliseconds = retentionDays * 24 * 60 * 60 * 1e3;
289
- const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
290
- query.deleteMany({
291
- where: {
292
- created_at: {
293
- $lt: expirationDate.toISOString()
294
- }
295
- }
296
- });
297
- });
298
- state.isInitialized = true;
299
- },
300
- async destroy() {
301
- if (state.deleteExpiredJob) {
302
- state.deleteExpiredJob.cancel();
303
- }
304
- },
323
+ } else {
324
+ currentRelationData.meta.missingCount += 1;
325
+ }
326
+ return currentRelationData;
327
+ },
328
+ Promise.resolve({
329
+ results: [],
330
+ meta: { missingCount: 0 }
331
+ })
332
+ );
333
+ };
334
+ return {
335
+ getSchemaAttributesDiff,
336
+ getRelationRestoreValue,
337
+ getMediaRestoreValue,
338
+ getDefaultLocale,
339
+ isLocalizedContentType,
340
+ getLocaleDictionary,
341
+ getRetentionDays,
342
+ getVersionStatus,
343
+ getDeepPopulate: getDeepPopulate2,
344
+ buildMediaResponse,
345
+ buildRelationReponse
346
+ };
347
+ };
348
+ const createHistoryService = ({ strapi: strapi2 }) => {
349
+ const query = strapi2.db.query(HISTORY_VERSION_UID);
350
+ const serviceUtils = createServiceUtils({ strapi: strapi2 });
351
+ return {
305
352
  async createVersion(historyVersionData) {
306
353
  await query.create({
307
354
  data: {
@@ -312,7 +359,13 @@ const createHistoryService = ({ strapi: strapi2 }) => {
312
359
  });
313
360
  },
314
361
  async findVersionsPage(params) {
315
- const locale = params.query.locale || await getDefaultLocale();
362
+ const model = strapi2.getModel(params.query.contentType);
363
+ const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
364
+ const defaultLocale = await serviceUtils.getDefaultLocale();
365
+ let locale = null;
366
+ if (isLocalizedContentType) {
367
+ locale = params.query.locale || defaultLocale;
368
+ }
316
369
  const [{ results, pagination }, localeDictionary] = await Promise.all([
317
370
  query.findPage({
318
371
  ...params.query,
@@ -326,82 +379,43 @@ const createHistoryService = ({ strapi: strapi2 }) => {
326
379
  populate: ["createdBy"],
327
380
  orderBy: [{ createdAt: "desc" }]
328
381
  }),
329
- getLocaleDictionary()
382
+ serviceUtils.getLocaleDictionary()
330
383
  ]);
331
- const buildRelationReponse = async (values, attributeSchema) => {
332
- return values.slice(0, 25).reduce(
333
- async (currentRelationDataPromise, entry) => {
334
- const currentRelationData = await currentRelationDataPromise;
335
- if (!entry) {
336
- return currentRelationData;
337
- }
338
- const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
339
- const permissionChecker2 = getService$1("permission-checker").create({
340
- userAbility: params.state.userAbility,
341
- model: attributeSchema.target
342
- });
343
- const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
344
- if (sanitizedEntry) {
345
- currentRelationData.results.push({
346
- ...sanitizedEntry,
347
- status: await getVersionStatus(attributeSchema.target, sanitizedEntry)
348
- });
349
- } else {
350
- currentRelationData.meta.missingCount += 1;
351
- }
352
- return currentRelationData;
353
- },
354
- Promise.resolve({
355
- results: [],
356
- meta: { missingCount: 0 }
357
- })
358
- );
359
- };
360
- const buildMediaResponse = async (values) => {
361
- return values.slice(0, 25).reduce(
362
- async (currentRelationDataPromise, entry) => {
363
- const currentRelationData = await currentRelationDataPromise;
364
- if (!entry) {
365
- return currentRelationData;
366
- }
367
- const permissionChecker2 = getService$1("permission-checker").create({
368
- userAbility: params.state.userAbility,
369
- model: "plugin::upload.file"
370
- });
371
- const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
372
- const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
373
- if (sanitizedEntry) {
374
- currentRelationData.results.push(sanitizedEntry);
375
- } else {
376
- currentRelationData.meta.missingCount += 1;
377
- }
378
- return currentRelationData;
379
- },
380
- Promise.resolve({
381
- results: [],
382
- meta: { missingCount: 0 }
383
- })
384
- );
385
- };
386
384
  const populateEntryRelations = async (entry) => {
387
385
  const entryWithRelations = await Object.entries(entry.schema).reduce(
388
386
  async (currentDataWithRelations, [attributeKey, attributeSchema]) => {
389
387
  const attributeValue = entry.data[attributeKey];
390
388
  const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
391
389
  if (attributeSchema.type === "media") {
390
+ const permissionChecker2 = getService$2("permission-checker").create({
391
+ userAbility: params.state.userAbility,
392
+ model: "plugin::upload.file"
393
+ });
394
+ const response = await serviceUtils.buildMediaResponse(attributeValues);
395
+ const sanitizedResults = await Promise.all(
396
+ response.results.map((media) => permissionChecker2.sanitizeOutput(media))
397
+ );
392
398
  return {
393
399
  ...await currentDataWithRelations,
394
- [attributeKey]: await buildMediaResponse(attributeValues)
400
+ [attributeKey]: {
401
+ results: sanitizedResults,
402
+ meta: response.meta
403
+ }
395
404
  };
396
405
  }
397
406
  if (attributeSchema.type === "relation" && attributeSchema.relation !== "morphToOne" && attributeSchema.relation !== "morphToMany") {
398
407
  if (attributeSchema.target === "admin::user") {
399
408
  const adminUsers = await Promise.all(
400
- attributeValues.map(async (userToPopulate) => {
409
+ attributeValues.map((userToPopulate) => {
401
410
  if (userToPopulate == null) {
402
411
  return null;
403
412
  }
404
- return strapi2.query("admin::user").findOne({ where: { id: userToPopulate.id } });
413
+ return strapi2.query("admin::user").findOne({
414
+ where: {
415
+ ...userToPopulate.id ? { id: userToPopulate.id } : {},
416
+ ...userToPopulate.documentId ? { documentId: userToPopulate.documentId } : {}
417
+ }
418
+ });
405
419
  })
406
420
  );
407
421
  return {
@@ -414,9 +428,23 @@ const createHistoryService = ({ strapi: strapi2 }) => {
414
428
  [attributeKey]: adminUsers
415
429
  };
416
430
  }
431
+ const permissionChecker2 = getService$2("permission-checker").create({
432
+ userAbility: params.state.userAbility,
433
+ model: attributeSchema.target
434
+ });
435
+ const response = await serviceUtils.buildRelationReponse(
436
+ attributeValues,
437
+ attributeSchema
438
+ );
439
+ const sanitizedResults = await Promise.all(
440
+ response.results.map((media) => permissionChecker2.sanitizeOutput(media))
441
+ );
417
442
  return {
418
443
  ...await currentDataWithRelations,
419
- [attributeKey]: await buildRelationReponse(attributeValues, attributeSchema)
444
+ [attributeKey]: {
445
+ results: sanitizedResults,
446
+ meta: response.meta
447
+ }
420
448
  };
421
449
  }
422
450
  return currentDataWithRelations;
@@ -431,7 +459,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
431
459
  ...result,
432
460
  data: await populateEntryRelations(result),
433
461
  meta: {
434
- unknownAttributes: getSchemaAttributesDiff(
462
+ unknownAttributes: serviceUtils.getSchemaAttributesDiff(
435
463
  result.schema,
436
464
  strapi2.getModel(params.query.contentType).attributes
437
465
  )
@@ -448,7 +476,10 @@ const createHistoryService = ({ strapi: strapi2 }) => {
448
476
  async restoreVersion(versionId) {
449
477
  const version = await query.findOne({ where: { id: versionId } });
450
478
  const contentTypeSchemaAttributes = strapi2.getModel(version.contentType).attributes;
451
- const schemaDiff = getSchemaAttributesDiff(version.schema, contentTypeSchemaAttributes);
479
+ const schemaDiff = serviceUtils.getSchemaAttributesDiff(
480
+ version.schema,
481
+ contentTypeSchemaAttributes
482
+ );
452
483
  const dataWithoutAddedAttributes = Object.keys(schemaDiff.added).reduce(
453
484
  (currentData, addedKey) => {
454
485
  currentData[addedKey] = null;
@@ -461,61 +492,26 @@ const createHistoryService = ({ strapi: strapi2 }) => {
461
492
  FIELDS_TO_IGNORE,
462
493
  contentTypeSchemaAttributes
463
494
  );
464
- const dataWithoutMissingRelations = await Object.entries(sanitizedSchemaAttributes).reduce(
465
- async (previousRelationAttributesPromise, [name, attribute]) => {
466
- const previousRelationAttributes = await previousRelationAttributesPromise;
467
- const relationData = version.data[name];
468
- if (relationData === null) {
495
+ const reducer = strapiUtils.async.reduce(Object.entries(sanitizedSchemaAttributes));
496
+ const dataWithoutMissingRelations = await reducer(
497
+ async (previousRelationAttributes, [name, attribute]) => {
498
+ const versionRelationData = version.data[name];
499
+ if (!versionRelationData) {
469
500
  return previousRelationAttributes;
470
501
  }
471
502
  if (attribute.type === "relation" && // TODO: handle polymorphic relations
472
503
  attribute.relation !== "morphToOne" && attribute.relation !== "morphToMany") {
473
- if (Array.isArray(relationData)) {
474
- if (relationData.length === 0)
475
- return previousRelationAttributes;
476
- const existingAndMissingRelations = await Promise.all(
477
- relationData.map((relation) => {
478
- return strapi2.documents(attribute.target).findOne({
479
- documentId: relation.documentId,
480
- locale: relation.locale || void 0
481
- });
482
- })
483
- );
484
- const existingRelations = existingAndMissingRelations.filter(
485
- (relation) => relation !== null
486
- );
487
- previousRelationAttributes[name] = existingRelations;
488
- } else {
489
- const existingRelation = await strapi2.documents(attribute.target).findOne({
490
- documentId: relationData.documentId,
491
- locale: relationData.locale || void 0
492
- });
493
- if (!existingRelation) {
494
- previousRelationAttributes[name] = null;
495
- }
496
- }
504
+ const data2 = await serviceUtils.getRelationRestoreValue(versionRelationData, attribute);
505
+ previousRelationAttributes[name] = data2;
497
506
  }
498
507
  if (attribute.type === "media") {
499
- if (attribute.multiple) {
500
- const existingAndMissingMedias = await Promise.all(
501
- // @ts-expect-error Fix the type definitions so this isn't any
502
- relationData.map((media) => {
503
- return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
504
- })
505
- );
506
- const existingMedias = existingAndMissingMedias.filter((media) => media != null);
507
- previousRelationAttributes[name] = existingMedias;
508
- } else {
509
- const existingMedia = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: version.data[name].id } });
510
- if (!existingMedia) {
511
- previousRelationAttributes[name] = null;
512
- }
513
- }
508
+ const data2 = await serviceUtils.getMediaRestoreValue(versionRelationData, attribute);
509
+ previousRelationAttributes[name] = data2;
514
510
  }
515
511
  return previousRelationAttributes;
516
512
  },
517
513
  // Clone to avoid mutating the original version data
518
- Promise.resolve(structuredClone(dataWithoutAddedAttributes))
514
+ structuredClone(dataWithoutAddedAttributes)
519
515
  );
520
516
  const data = fp.omit(["id", ...Object.keys(schemaDiff.removed)], dataWithoutMissingRelations);
521
517
  const restoredDocument = await strapi2.documents(version.contentType).update({
@@ -530,16 +526,132 @@ const createHistoryService = ({ strapi: strapi2 }) => {
530
526
  }
531
527
  };
532
528
  };
533
- const services$1 = {
534
- history: createHistoryService
529
+ const shouldCreateHistoryVersion = (context) => {
530
+ if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
531
+ return false;
532
+ }
533
+ if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
534
+ return false;
535
+ }
536
+ if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
537
+ return false;
538
+ }
539
+ if (!context.contentType.uid.startsWith("api::")) {
540
+ return false;
541
+ }
542
+ return true;
535
543
  };
536
- const info = { pluginName: "content-manager", type: "admin" };
544
+ const getSchemas = (uid2) => {
545
+ const attributesSchema = strapi.getModel(uid2).attributes;
546
+ const componentsSchemas = Object.keys(attributesSchema).reduce(
547
+ (currentComponentSchemas, key) => {
548
+ const fieldSchema = attributesSchema[key];
549
+ if (fieldSchema.type === "component") {
550
+ const componentSchema = strapi.getModel(fieldSchema.component).attributes;
551
+ return {
552
+ ...currentComponentSchemas,
553
+ [fieldSchema.component]: componentSchema
554
+ };
555
+ }
556
+ return currentComponentSchemas;
557
+ },
558
+ {}
559
+ );
560
+ return {
561
+ schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
562
+ componentsSchemas
563
+ };
564
+ };
565
+ const createLifecyclesService = ({ strapi: strapi2 }) => {
566
+ const state = {
567
+ deleteExpiredJob: null,
568
+ isInitialized: false
569
+ };
570
+ const serviceUtils = createServiceUtils({ strapi: strapi2 });
571
+ return {
572
+ async bootstrap() {
573
+ if (state.isInitialized) {
574
+ return;
575
+ }
576
+ strapi2.documents.use(async (context, next) => {
577
+ const result = await next();
578
+ if (!shouldCreateHistoryVersion(context)) {
579
+ return result;
580
+ }
581
+ const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
582
+ const defaultLocale = await serviceUtils.getDefaultLocale();
583
+ const locales = fp.castArray(context.params?.locale || defaultLocale);
584
+ if (!locales.length) {
585
+ return result;
586
+ }
587
+ const uid2 = context.contentType.uid;
588
+ const schemas = getSchemas(uid2);
589
+ const model = strapi2.getModel(uid2);
590
+ const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
591
+ const localeEntries = await strapi2.db.query(uid2).findMany({
592
+ where: {
593
+ documentId,
594
+ ...isLocalizedContentType ? { locale: { $in: locales } } : {},
595
+ ...strapiUtils.contentTypes.hasDraftAndPublish(strapi2.contentTypes[uid2]) ? { publishedAt: null } : {}
596
+ },
597
+ populate: serviceUtils.getDeepPopulate(
598
+ uid2,
599
+ true
600
+ /* use database syntax */
601
+ )
602
+ });
603
+ await strapi2.db.transaction(async ({ onCommit }) => {
604
+ onCommit(async () => {
605
+ for (const entry of localeEntries) {
606
+ const status = await serviceUtils.getVersionStatus(uid2, entry);
607
+ await getService$1(strapi2, "history").createVersion({
608
+ contentType: uid2,
609
+ data: fp.omit(FIELDS_TO_IGNORE, entry),
610
+ relatedDocumentId: documentId,
611
+ locale: entry.locale,
612
+ status,
613
+ ...schemas
614
+ });
615
+ }
616
+ });
617
+ });
618
+ return result;
619
+ });
620
+ state.deleteExpiredJob = nodeSchedule.scheduleJob("historyDaily", "0 0 * * *", () => {
621
+ const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
622
+ const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
623
+ strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
624
+ where: {
625
+ created_at: {
626
+ $lt: expirationDate
627
+ }
628
+ }
629
+ }).catch((error) => {
630
+ if (error instanceof Error) {
631
+ strapi2.log.error("Error deleting expired history versions", error.message);
632
+ }
633
+ });
634
+ });
635
+ state.isInitialized = true;
636
+ },
637
+ async destroy() {
638
+ if (state.deleteExpiredJob) {
639
+ state.deleteExpiredJob.cancel();
640
+ }
641
+ }
642
+ };
643
+ };
644
+ const services$2 = {
645
+ history: createHistoryService,
646
+ lifecycles: createLifecyclesService
647
+ };
648
+ const info$1 = { pluginName: "content-manager", type: "admin" };
537
649
  const historyVersionRouter = {
538
650
  type: "admin",
539
651
  routes: [
540
652
  {
541
653
  method: "GET",
542
- info,
654
+ info: info$1,
543
655
  path: "/history-versions",
544
656
  handler: "history-version.findMany",
545
657
  config: {
@@ -548,7 +660,7 @@ const historyVersionRouter = {
548
660
  },
549
661
  {
550
662
  method: "PUT",
551
- info,
663
+ info: info$1,
552
664
  path: "/history-versions/:versionId/restore",
553
665
  handler: "history-version.restoreVersion",
554
666
  config: {
@@ -557,7 +669,7 @@ const historyVersionRouter = {
557
669
  }
558
670
  ]
559
671
  };
560
- const routes$1 = {
672
+ const routes$2 = {
561
673
  "history-version": historyVersionRouter
562
674
  };
563
675
  const historyVersion = {
@@ -604,21 +716,21 @@ const historyVersion = {
604
716
  }
605
717
  }
606
718
  };
607
- const getFeature = () => {
719
+ const getFeature$1 = () => {
608
720
  if (strapi.ee.features.isEnabled("cms-content-history")) {
609
721
  return {
610
722
  register({ strapi: strapi2 }) {
611
723
  strapi2.get("models").add(historyVersion);
612
724
  },
613
725
  bootstrap({ strapi: strapi2 }) {
614
- getService(strapi2, "history").bootstrap();
726
+ getService$1(strapi2, "lifecycles").bootstrap();
615
727
  },
616
728
  destroy({ strapi: strapi2 }) {
617
- getService(strapi2, "history").destroy();
729
+ getService$1(strapi2, "lifecycles").destroy();
618
730
  },
619
- controllers: controllers$1,
620
- services: services$1,
621
- routes: routes$1
731
+ controllers: controllers$2,
732
+ services: services$2,
733
+ routes: routes$2
622
734
  };
623
735
  }
624
736
  return {
@@ -627,7 +739,7 @@ const getFeature = () => {
627
739
  }
628
740
  };
629
741
  };
630
- const history = getFeature();
742
+ const history = getFeature$1();
631
743
  const register = async ({ strapi: strapi2 }) => {
632
744
  await history.register?.({ strapi: strapi2 });
633
745
  };
@@ -635,15 +747,165 @@ const ALLOWED_WEBHOOK_EVENTS = {
635
747
  ENTRY_PUBLISH: "entry.publish",
636
748
  ENTRY_UNPUBLISH: "entry.unpublish"
637
749
  };
750
+ const FEATURE_ID = "preview";
751
+ const info = { pluginName: "content-manager", type: "admin" };
752
+ const previewRouter = {
753
+ type: "admin",
754
+ routes: [
755
+ {
756
+ method: "GET",
757
+ info,
758
+ path: "/preview/url/:contentType",
759
+ handler: "preview.getPreviewUrl",
760
+ config: {
761
+ policies: ["admin::isAuthenticatedAdmin"]
762
+ }
763
+ }
764
+ ]
765
+ };
766
+ const routes$1 = {
767
+ preview: previewRouter
768
+ };
769
+ function getService(strapi2, name) {
770
+ return strapi2.service(`plugin::content-manager.${name}`);
771
+ }
772
+ const getPreviewUrlSchema = yup__namespace.object().shape({
773
+ // Will be undefined for single types
774
+ documentId: yup__namespace.string(),
775
+ locale: yup__namespace.string().nullable(),
776
+ status: yup__namespace.string()
777
+ }).required();
778
+ const validatePreviewUrl = async (strapi2, uid2, params) => {
779
+ await strapiUtils.validateYupSchema(getPreviewUrlSchema)(params);
780
+ const newParams = fp.pick(["documentId", "locale", "status"], params);
781
+ const model = strapi2.getModel(uid2);
782
+ if (!model || model.modelType !== "contentType") {
783
+ throw new strapiUtils.errors.ValidationError("Invalid content type");
784
+ }
785
+ const isSingleType = model?.kind === "singleType";
786
+ if (!isSingleType && !params.documentId) {
787
+ throw new strapiUtils.errors.ValidationError("documentId is required for Collection Types");
788
+ }
789
+ if (isSingleType) {
790
+ const doc = await strapi2.documents(uid2).findFirst();
791
+ if (!doc) {
792
+ throw new strapiUtils.errors.NotFoundError("Document not found");
793
+ }
794
+ newParams.documentId = doc?.documentId;
795
+ }
796
+ return newParams;
797
+ };
798
+ const createPreviewController = () => {
799
+ return {
800
+ /**
801
+ * Transforms an entry into a preview URL, so that it can be previewed
802
+ * in the Content Manager.
803
+ */
804
+ async getPreviewUrl(ctx) {
805
+ const uid2 = ctx.params.contentType;
806
+ const query = ctx.request.query;
807
+ const params = await validatePreviewUrl(strapi, uid2, query);
808
+ const previewService = getService(strapi, "preview");
809
+ const url = await previewService.getPreviewUrl(uid2, params);
810
+ if (!url) {
811
+ ctx.status = 204;
812
+ }
813
+ return {
814
+ data: { url }
815
+ };
816
+ }
817
+ };
818
+ };
819
+ const controllers$1 = {
820
+ preview: createPreviewController
821
+ /**
822
+ * Casting is needed because the types aren't aware that Strapi supports
823
+ * passing a controller factory as the value, instead of a controller object directly
824
+ */
825
+ };
826
+ const createPreviewService = ({ strapi: strapi2 }) => {
827
+ const config = getService(strapi2, "preview-config");
828
+ return {
829
+ async getPreviewUrl(uid2, params) {
830
+ const handler = config.getPreviewHandler();
831
+ try {
832
+ return handler(uid2, params);
833
+ } catch (error) {
834
+ strapi2.log.error(`Failed to get preview URL: ${error}`);
835
+ throw new strapiUtils.errors.ApplicationError("Failed to get preview URL");
836
+ }
837
+ return;
838
+ }
839
+ };
840
+ };
841
+ const createPreviewConfigService = ({ strapi: strapi2 }) => {
842
+ return {
843
+ isEnabled() {
844
+ const config = strapi2.config.get("admin.preview");
845
+ if (!config) {
846
+ return false;
847
+ }
848
+ return config?.enabled ?? true;
849
+ },
850
+ /**
851
+ * Validate if the configuration is valid
852
+ */
853
+ validate() {
854
+ if (!this.isEnabled()) {
855
+ return;
856
+ }
857
+ const handler = this.getPreviewHandler();
858
+ if (typeof handler !== "function") {
859
+ throw new strapiUtils.errors.ValidationError(
860
+ "Preview configuration is invalid. Handler must be a function"
861
+ );
862
+ }
863
+ },
864
+ /**
865
+ * Utility to get the preview handler from the configuration
866
+ */
867
+ getPreviewHandler() {
868
+ const config = strapi2.config.get("admin.preview");
869
+ const emptyHandler = () => {
870
+ return void 0;
871
+ };
872
+ if (!this.isEnabled()) {
873
+ return emptyHandler;
874
+ }
875
+ return config?.config?.handler || emptyHandler;
876
+ }
877
+ };
878
+ };
879
+ const services$1 = {
880
+ preview: createPreviewService,
881
+ "preview-config": createPreviewConfigService
882
+ };
883
+ const getFeature = () => {
884
+ if (!strapi.features.future.isEnabled(FEATURE_ID)) {
885
+ return {};
886
+ }
887
+ return {
888
+ bootstrap() {
889
+ console.log("Bootstrapping preview server");
890
+ const config = getService(strapi, "preview-config");
891
+ config.validate();
892
+ },
893
+ routes: routes$1,
894
+ controllers: controllers$1,
895
+ services: services$1
896
+ };
897
+ };
898
+ const preview = getFeature();
638
899
  const bootstrap = async () => {
639
900
  Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
640
901
  strapi.get("webhookStore").addAllowedEvent(key, value);
641
902
  });
642
- getService$1("field-sizes").setCustomFieldInputSizes();
643
- await getService$1("components").syncConfigurations();
644
- await getService$1("content-types").syncConfigurations();
645
- await getService$1("permission").registerPermissions();
903
+ getService$2("field-sizes").setCustomFieldInputSizes();
904
+ await getService$2("components").syncConfigurations();
905
+ await getService$2("content-types").syncConfigurations();
906
+ await getService$2("permission").registerPermissions();
646
907
  await history.bootstrap?.({ strapi });
908
+ await preview.bootstrap?.({ strapi });
647
909
  };
648
910
  const destroy = async ({ strapi: strapi2 }) => {
649
911
  await history.destroy?.({ strapi: strapi2 });
@@ -1133,7 +1395,8 @@ const admin = {
1133
1395
  };
1134
1396
  const routes = {
1135
1397
  admin,
1136
- ...history.routes ? history.routes : {}
1398
+ ...history.routes ? history.routes : {},
1399
+ ...preview.routes ? preview.routes : {}
1137
1400
  };
1138
1401
  const hasPermissionsSchema = strapiUtils.yup.object({
1139
1402
  actions: strapiUtils.yup.array().of(strapiUtils.yup.string()),
@@ -1144,6 +1407,11 @@ const { createPolicy } = strapiUtils.policy;
1144
1407
  const hasPermissions = createPolicy({
1145
1408
  name: "plugin::content-manager.hasPermissions",
1146
1409
  validator: validateHasPermissionsInput,
1410
+ /**
1411
+ * NOTE: Action aliases are currently not checked at this level (policy).
1412
+ * This is currently the intended behavior to avoid changing the behavior of API related permissions.
1413
+ * If you want to add support for it, please create a dedicated RFC with a list of potential side effect this could have.
1414
+ */
1147
1415
  handler(ctx, config = {}) {
1148
1416
  const { actions = [], hasAtLeastOne = false } = config;
1149
1417
  const { userAbility } = ctx.state;
@@ -1385,7 +1653,7 @@ const createMetadasSchema = (schema) => {
1385
1653
  if (!value) {
1386
1654
  return strapiUtils.yup.string();
1387
1655
  }
1388
- const targetSchema = getService$1("content-types").findContentType(
1656
+ const targetSchema = getService$2("content-types").findContentType(
1389
1657
  schema.attributes[key].targetModel
1390
1658
  );
1391
1659
  if (!targetSchema) {
@@ -1433,7 +1701,7 @@ const { PaginationError, ValidationError } = strapiUtils.errors;
1433
1701
  const TYPES = ["singleType", "collectionType"];
1434
1702
  const kindSchema = strapiUtils.yup.string().oneOf(TYPES).nullable();
1435
1703
  const bulkActionInputSchema = strapiUtils.yup.object({
1436
- ids: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
1704
+ documentIds: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
1437
1705
  }).required();
1438
1706
  const generateUIDInputSchema = strapiUtils.yup.object({
1439
1707
  contentTypeUID: strapiUtils.yup.string().required(),
@@ -1532,22 +1800,56 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1532
1800
  }
1533
1801
  }, body);
1534
1802
  };
1535
- const getDocumentLocaleAndStatus = (request) => {
1536
- const { locale, status, ...rest } = request || {};
1537
- if (!fp.isNil(locale) && typeof locale !== "string") {
1538
- throw new strapiUtils.errors.ValidationError(`Invalid locale: ${locale}`);
1539
- }
1540
- if (!fp.isNil(status) && !["draft", "published"].includes(status)) {
1541
- throw new strapiUtils.errors.ValidationError(`Invalid status: ${status}`);
1803
+ const singleLocaleSchema = strapiUtils.yup.string().nullable();
1804
+ const multipleLocaleSchema = strapiUtils.yup.lazy(
1805
+ (value) => Array.isArray(value) ? strapiUtils.yup.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1806
+ );
1807
+ const statusSchema = strapiUtils.yup.mixed().oneOf(["draft", "published"], "Invalid status");
1808
+ const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
1809
+ const { allowMultipleLocales } = opts;
1810
+ const { locale, status: providedStatus, ...rest } = request || {};
1811
+ const defaultStatus = strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
1812
+ const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
1813
+ const schema = strapiUtils.yup.object().shape({
1814
+ locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1815
+ status: statusSchema
1816
+ });
1817
+ try {
1818
+ await strapiUtils.validateYupSchema(schema, { strict: true, abortEarly: false })(request);
1819
+ return { locale, status, ...rest };
1820
+ } catch (error) {
1821
+ throw new strapiUtils.errors.ValidationError(`Validation error: ${error.message}`);
1542
1822
  }
1543
- return { locale, status, ...rest };
1823
+ };
1824
+ const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
1825
+ const documentMetadata2 = getService$2("document-metadata");
1826
+ const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
1827
+ let {
1828
+ meta: { availableLocales, availableStatus }
1829
+ } = serviceOutput;
1830
+ const metadataSanitizer = permissionChecker2.sanitizeOutput;
1831
+ availableLocales = await strapiUtils.async.map(
1832
+ availableLocales,
1833
+ async (localeDocument) => metadataSanitizer(localeDocument)
1834
+ );
1835
+ availableStatus = await strapiUtils.async.map(
1836
+ availableStatus,
1837
+ async (statusDocument) => metadataSanitizer(statusDocument)
1838
+ );
1839
+ return {
1840
+ ...serviceOutput,
1841
+ meta: {
1842
+ availableLocales,
1843
+ availableStatus
1844
+ }
1845
+ };
1544
1846
  };
1545
1847
  const createDocument = async (ctx, opts) => {
1546
1848
  const { userAbility, user } = ctx.state;
1547
1849
  const { model } = ctx.params;
1548
1850
  const { body } = ctx.request;
1549
- const documentManager2 = getService$1("document-manager");
1550
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1851
+ const documentManager2 = getService$2("document-manager");
1852
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1551
1853
  if (permissionChecker2.cannot.create()) {
1552
1854
  throw new strapiUtils.errors.ForbiddenError();
1553
1855
  }
@@ -1555,7 +1857,7 @@ const createDocument = async (ctx, opts) => {
1555
1857
  const setCreator = strapiUtils.setCreatorFields({ user });
1556
1858
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1557
1859
  const sanitizedBody = await sanitizeFn(body);
1558
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(body);
1860
+ const { locale, status } = await getDocumentLocaleAndStatus(body, model);
1559
1861
  return documentManager2.create(model, {
1560
1862
  data: sanitizedBody,
1561
1863
  locale,
@@ -1567,14 +1869,14 @@ const updateDocument = async (ctx, opts) => {
1567
1869
  const { userAbility, user } = ctx.state;
1568
1870
  const { id, model } = ctx.params;
1569
1871
  const { body } = ctx.request;
1570
- const documentManager2 = getService$1("document-manager");
1571
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1872
+ const documentManager2 = getService$2("document-manager");
1873
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1572
1874
  if (permissionChecker2.cannot.update()) {
1573
1875
  throw new strapiUtils.errors.ForbiddenError();
1574
1876
  }
1575
1877
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1576
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1577
- const { locale } = getDocumentLocaleAndStatus(body);
1878
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
1879
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1578
1880
  const [documentVersion, documentExists] = await Promise.all([
1579
1881
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1580
1882
  documentManager2.exists(model, id)
@@ -1590,7 +1892,7 @@ const updateDocument = async (ctx, opts) => {
1590
1892
  throw new strapiUtils.errors.ForbiddenError();
1591
1893
  }
1592
1894
  const pickPermittedFields = documentVersion ? permissionChecker2.sanitizeUpdateInput(documentVersion) : permissionChecker2.sanitizeCreateInput;
1593
- const setCreator = strapiUtils.setCreatorFields({ user, isEdition: true });
1895
+ const setCreator = documentVersion ? strapiUtils.setCreatorFields({ user, isEdition: true }) : strapiUtils.setCreatorFields({ user });
1594
1896
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1595
1897
  const sanitizedBody = await sanitizeFn(body);
1596
1898
  return documentManager2.update(documentVersion?.documentId || id, model, {
@@ -1604,15 +1906,15 @@ const collectionTypes = {
1604
1906
  const { userAbility } = ctx.state;
1605
1907
  const { model } = ctx.params;
1606
1908
  const { query } = ctx.request;
1607
- const documentMetadata2 = getService$1("document-metadata");
1608
- const documentManager2 = getService$1("document-manager");
1609
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1909
+ const documentMetadata2 = getService$2("document-metadata");
1910
+ const documentManager2 = getService$2("document-manager");
1911
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1610
1912
  if (permissionChecker2.cannot.read()) {
1611
1913
  return ctx.forbidden();
1612
1914
  }
1613
1915
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1614
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1615
- const { locale, status } = getDocumentLocaleAndStatus(query);
1916
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1917
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
1616
1918
  const { results: documents, pagination } = await documentManager2.findPage(
1617
1919
  { ...permissionQuery, populate, locale, status },
1618
1920
  model
@@ -1640,15 +1942,14 @@ const collectionTypes = {
1640
1942
  async findOne(ctx) {
1641
1943
  const { userAbility } = ctx.state;
1642
1944
  const { model, id } = ctx.params;
1643
- const documentManager2 = getService$1("document-manager");
1644
- const documentMetadata2 = getService$1("document-metadata");
1645
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1945
+ const documentManager2 = getService$2("document-manager");
1946
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1646
1947
  if (permissionChecker2.cannot.read()) {
1647
1948
  return ctx.forbidden();
1648
1949
  }
1649
1950
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1650
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1651
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
1951
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1952
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
1652
1953
  const version = await documentManager2.findOne(id, model, {
1653
1954
  populate,
1654
1955
  locale,
@@ -1659,9 +1960,11 @@ const collectionTypes = {
1659
1960
  if (!exists) {
1660
1961
  return ctx.notFound();
1661
1962
  }
1662
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
1963
+ const { meta } = await formatDocumentWithMetadata(
1964
+ permissionChecker2,
1663
1965
  model,
1664
- { id, locale, publishedAt: null },
1966
+ // @ts-expect-error TODO: fix
1967
+ { documentId: id, locale, publishedAt: null },
1665
1968
  { availableLocales: true, availableStatus: false }
1666
1969
  );
1667
1970
  ctx.body = { data: {}, meta };
@@ -1671,20 +1974,19 @@ const collectionTypes = {
1671
1974
  return ctx.forbidden();
1672
1975
  }
1673
1976
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
1674
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1977
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1675
1978
  },
1676
1979
  async create(ctx) {
1677
1980
  const { userAbility } = ctx.state;
1678
1981
  const { model } = ctx.params;
1679
- const documentMetadata2 = getService$1("document-metadata");
1680
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1982
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1681
1983
  const [totalEntries, document] = await Promise.all([
1682
1984
  strapi.db.query(model).count(),
1683
1985
  createDocument(ctx)
1684
1986
  ]);
1685
1987
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
1686
1988
  ctx.status = 201;
1687
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1989
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1688
1990
  // Empty metadata as it's not relevant for a new document
1689
1991
  availableLocales: false,
1690
1992
  availableStatus: false
@@ -1698,25 +2000,23 @@ const collectionTypes = {
1698
2000
  async update(ctx) {
1699
2001
  const { userAbility } = ctx.state;
1700
2002
  const { model } = ctx.params;
1701
- const documentMetadata2 = getService$1("document-metadata");
1702
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2003
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1703
2004
  const updatedVersion = await updateDocument(ctx);
1704
2005
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1705
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedVersion);
2006
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
1706
2007
  },
1707
2008
  async clone(ctx) {
1708
2009
  const { userAbility, user } = ctx.state;
1709
2010
  const { model, sourceId: id } = ctx.params;
1710
2011
  const { body } = ctx.request;
1711
- const documentManager2 = getService$1("document-manager");
1712
- const documentMetadata2 = getService$1("document-metadata");
1713
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2012
+ const documentManager2 = getService$2("document-manager");
2013
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1714
2014
  if (permissionChecker2.cannot.create()) {
1715
2015
  return ctx.forbidden();
1716
2016
  }
1717
2017
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1718
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1719
- const { locale } = getDocumentLocaleAndStatus(body);
2018
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2019
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1720
2020
  const document = await documentManager2.findOne(id, model, {
1721
2021
  populate,
1722
2022
  locale,
@@ -1732,7 +2032,7 @@ const collectionTypes = {
1732
2032
  const sanitizedBody = await sanitizeFn(body);
1733
2033
  const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
1734
2034
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
1735
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
2035
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1736
2036
  // Empty metadata as it's not relevant for a new document
1737
2037
  availableLocales: false,
1738
2038
  availableStatus: false
@@ -1754,14 +2054,14 @@ const collectionTypes = {
1754
2054
  async delete(ctx) {
1755
2055
  const { userAbility } = ctx.state;
1756
2056
  const { id, model } = ctx.params;
1757
- const documentManager2 = getService$1("document-manager");
1758
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2057
+ const documentManager2 = getService$2("document-manager");
2058
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1759
2059
  if (permissionChecker2.cannot.delete()) {
1760
2060
  return ctx.forbidden();
1761
2061
  }
1762
2062
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1763
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1764
- const { locale } = getDocumentLocaleAndStatus(ctx.query);
2063
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2064
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
1765
2065
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1766
2066
  if (documentLocales.length === 0) {
1767
2067
  return ctx.notFound();
@@ -1782,44 +2082,75 @@ const collectionTypes = {
1782
2082
  const { userAbility } = ctx.state;
1783
2083
  const { id, model } = ctx.params;
1784
2084
  const { body } = ctx.request;
1785
- const documentManager2 = getService$1("document-manager");
1786
- const documentMetadata2 = getService$1("document-metadata");
1787
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2085
+ const documentManager2 = getService$2("document-manager");
2086
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1788
2087
  if (permissionChecker2.cannot.publish()) {
1789
2088
  return ctx.forbidden();
1790
2089
  }
1791
2090
  const publishedDocument = await strapi.db.transaction(async () => {
1792
2091
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1793
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1794
- const document = id ? await updateDocument(ctx, { populate }) : await createDocument(ctx, { populate });
2092
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
2093
+ let document;
2094
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2095
+ const isCreate = fp.isNil(id);
2096
+ if (isCreate) {
2097
+ if (permissionChecker2.cannot.create()) {
2098
+ throw new strapiUtils.errors.ForbiddenError();
2099
+ }
2100
+ document = await createDocument(ctx, { populate });
2101
+ }
2102
+ const isUpdate = !isCreate;
2103
+ if (isUpdate) {
2104
+ const documentExists = documentManager2.exists(model, id);
2105
+ if (!documentExists) {
2106
+ throw new strapiUtils.errors.NotFoundError("Document not found");
2107
+ }
2108
+ document = await documentManager2.findOne(id, model, { populate, locale });
2109
+ if (!document) {
2110
+ if (permissionChecker2.cannot.create({ locale }) || permissionChecker2.cannot.publish({ locale })) {
2111
+ throw new strapiUtils.errors.ForbiddenError();
2112
+ }
2113
+ document = await updateDocument(ctx);
2114
+ } else if (permissionChecker2.can.update(document)) {
2115
+ await updateDocument(ctx);
2116
+ }
2117
+ }
1795
2118
  if (permissionChecker2.cannot.publish(document)) {
1796
2119
  throw new strapiUtils.errors.ForbiddenError();
1797
2120
  }
1798
- const { locale } = getDocumentLocaleAndStatus(body);
1799
- return documentManager2.publish(document.documentId, model, {
2121
+ const publishResult = await documentManager2.publish(document.documentId, model, {
1800
2122
  locale
1801
2123
  // TODO: Allow setting creator fields on publish
1802
2124
  // data: setCreatorFields({ user, isEdition: true })({}),
1803
2125
  });
2126
+ if (!publishResult || publishResult.length === 0) {
2127
+ throw new strapiUtils.errors.NotFoundError("Document not found or already published.");
2128
+ }
2129
+ return publishResult[0];
1804
2130
  });
1805
2131
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
1806
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2132
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1807
2133
  },
1808
2134
  async bulkPublish(ctx) {
1809
2135
  const { userAbility } = ctx.state;
1810
2136
  const { model } = ctx.params;
1811
2137
  const { body } = ctx.request;
1812
- const { ids } = body;
2138
+ const { documentIds } = body;
1813
2139
  await validateBulkActionInput(body);
1814
- const documentManager2 = getService$1("document-manager");
1815
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2140
+ const documentManager2 = getService$2("document-manager");
2141
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1816
2142
  if (permissionChecker2.cannot.publish()) {
1817
2143
  return ctx.forbidden();
1818
2144
  }
1819
2145
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1820
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1821
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1822
- const entities = await Promise.all(entityPromises);
2146
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
2147
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
2148
+ allowMultipleLocales: true
2149
+ });
2150
+ const entityPromises = documentIds.map(
2151
+ (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
2152
+ );
2153
+ const entities = (await Promise.all(entityPromises)).flat();
1823
2154
  for (const entity of entities) {
1824
2155
  if (!entity) {
1825
2156
  return ctx.notFound();
@@ -1828,24 +2159,27 @@ const collectionTypes = {
1828
2159
  return ctx.forbidden();
1829
2160
  }
1830
2161
  }
1831
- const { count } = await documentManager2.publishMany(entities, model);
2162
+ const count = await documentManager2.publishMany(model, documentIds, locale);
1832
2163
  ctx.body = { count };
1833
2164
  },
1834
2165
  async bulkUnpublish(ctx) {
1835
2166
  const { userAbility } = ctx.state;
1836
2167
  const { model } = ctx.params;
1837
2168
  const { body } = ctx.request;
1838
- const { ids } = body;
2169
+ const { documentIds } = body;
1839
2170
  await validateBulkActionInput(body);
1840
- const documentManager2 = getService$1("document-manager");
1841
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2171
+ const documentManager2 = getService$2("document-manager");
2172
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1842
2173
  if (permissionChecker2.cannot.unpublish()) {
1843
2174
  return ctx.forbidden();
1844
2175
  }
1845
- const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1846
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1847
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1848
- const entities = await Promise.all(entityPromises);
2176
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
2177
+ allowMultipleLocales: true
2178
+ });
2179
+ const entityPromises = documentIds.map(
2180
+ (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
2181
+ );
2182
+ const entities = (await Promise.all(entityPromises)).flat();
1849
2183
  for (const entity of entities) {
1850
2184
  if (!entity) {
1851
2185
  return ctx.notFound();
@@ -1854,7 +2188,8 @@ const collectionTypes = {
1854
2188
  return ctx.forbidden();
1855
2189
  }
1856
2190
  }
1857
- const { count } = await documentManager2.unpublishMany(entities, model);
2191
+ const entitiesIds = entities.map((document) => document.documentId);
2192
+ const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
1858
2193
  ctx.body = { count };
1859
2194
  },
1860
2195
  async unpublish(ctx) {
@@ -1863,9 +2198,8 @@ const collectionTypes = {
1863
2198
  const {
1864
2199
  body: { discardDraft, ...body }
1865
2200
  } = ctx.request;
1866
- const documentManager2 = getService$1("document-manager");
1867
- const documentMetadata2 = getService$1("document-metadata");
1868
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2201
+ const documentManager2 = getService$2("document-manager");
2202
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1869
2203
  if (permissionChecker2.cannot.unpublish()) {
1870
2204
  return ctx.forbidden();
1871
2205
  }
@@ -1873,8 +2207,8 @@ const collectionTypes = {
1873
2207
  return ctx.forbidden();
1874
2208
  }
1875
2209
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1876
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1877
- const { locale } = getDocumentLocaleAndStatus(body);
2210
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2211
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1878
2212
  const document = await documentManager2.findOne(id, model, {
1879
2213
  populate,
1880
2214
  locale,
@@ -1896,7 +2230,7 @@ const collectionTypes = {
1896
2230
  ctx.body = await strapiUtils.async.pipe(
1897
2231
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
1898
2232
  permissionChecker2.sanitizeOutput,
1899
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2233
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1900
2234
  )(document);
1901
2235
  });
1902
2236
  },
@@ -1904,15 +2238,14 @@ const collectionTypes = {
1904
2238
  const { userAbility } = ctx.state;
1905
2239
  const { id, model } = ctx.params;
1906
2240
  const { body } = ctx.request;
1907
- const documentManager2 = getService$1("document-manager");
1908
- const documentMetadata2 = getService$1("document-metadata");
1909
- 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 });
1910
2243
  if (permissionChecker2.cannot.discard()) {
1911
2244
  return ctx.forbidden();
1912
2245
  }
1913
2246
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
1914
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1915
- const { locale } = getDocumentLocaleAndStatus(body);
2247
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2248
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1916
2249
  const document = await documentManager2.findOne(id, model, {
1917
2250
  populate,
1918
2251
  locale,
@@ -1927,42 +2260,50 @@ const collectionTypes = {
1927
2260
  ctx.body = await strapiUtils.async.pipe(
1928
2261
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
1929
2262
  permissionChecker2.sanitizeOutput,
1930
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2263
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1931
2264
  )(document);
1932
2265
  },
1933
2266
  async bulkDelete(ctx) {
1934
2267
  const { userAbility } = ctx.state;
1935
2268
  const { model } = ctx.params;
1936
2269
  const { query, body } = ctx.request;
1937
- const { ids } = body;
2270
+ const { documentIds } = body;
1938
2271
  await validateBulkActionInput(body);
1939
- const documentManager2 = getService$1("document-manager");
1940
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2272
+ const documentManager2 = getService$2("document-manager");
2273
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1941
2274
  if (permissionChecker2.cannot.delete()) {
1942
2275
  return ctx.forbidden();
1943
2276
  }
1944
2277
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
1945
- const idsWhereClause = { id: { $in: ids } };
1946
- const params = {
1947
- ...permissionQuery,
1948
- filters: {
1949
- $and: [idsWhereClause].concat(permissionQuery.filters || [])
2278
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2279
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2280
+ const documentLocales = await documentManager2.findLocales(documentIds, model, {
2281
+ populate,
2282
+ locale
2283
+ });
2284
+ if (documentLocales.length === 0) {
2285
+ return ctx.notFound();
2286
+ }
2287
+ for (const document of documentLocales) {
2288
+ if (permissionChecker2.cannot.delete(document)) {
2289
+ return ctx.forbidden();
1950
2290
  }
1951
- };
1952
- const { count } = await documentManager2.deleteMany(params, model);
2291
+ }
2292
+ const localeDocumentsIds = documentLocales.map((document) => document.documentId);
2293
+ const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
1953
2294
  ctx.body = { count };
1954
2295
  },
1955
2296
  async countDraftRelations(ctx) {
1956
2297
  const { userAbility } = ctx.state;
1957
2298
  const { model, id } = ctx.params;
1958
- const documentManager2 = getService$1("document-manager");
1959
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2299
+ const documentManager2 = getService$2("document-manager");
2300
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1960
2301
  if (permissionChecker2.cannot.read()) {
1961
2302
  return ctx.forbidden();
1962
2303
  }
1963
2304
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1964
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1965
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
2305
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2306
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
1966
2307
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
1967
2308
  if (!entity) {
1968
2309
  return ctx.notFound();
@@ -1977,24 +2318,24 @@ const collectionTypes = {
1977
2318
  },
1978
2319
  async countManyEntriesDraftRelations(ctx) {
1979
2320
  const { userAbility } = ctx.state;
1980
- const ids = ctx.request.query.ids;
2321
+ const ids = ctx.request.query.documentIds;
1981
2322
  const locale = ctx.request.query.locale;
1982
2323
  const { model } = ctx.params;
1983
- const documentManager2 = getService$1("document-manager");
1984
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2324
+ const documentManager2 = getService$2("document-manager");
2325
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1985
2326
  if (permissionChecker2.cannot.read()) {
1986
2327
  return ctx.forbidden();
1987
2328
  }
1988
- const entities = await documentManager2.findMany(
2329
+ const documents = await documentManager2.findMany(
1989
2330
  {
1990
2331
  filters: {
1991
- id: ids
2332
+ documentId: ids
1992
2333
  },
1993
2334
  locale
1994
2335
  },
1995
2336
  model
1996
2337
  );
1997
- if (!entities) {
2338
+ if (!documents) {
1998
2339
  return ctx.notFound();
1999
2340
  }
2000
2341
  const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
@@ -2005,13 +2346,13 @@ const collectionTypes = {
2005
2346
  };
2006
2347
  const components$1 = {
2007
2348
  findComponents(ctx) {
2008
- const components2 = getService$1("components").findAllComponents();
2009
- const { toDto } = getService$1("data-mapper");
2349
+ const components2 = getService$2("components").findAllComponents();
2350
+ const { toDto } = getService$2("data-mapper");
2010
2351
  ctx.body = { data: components2.map(toDto) };
2011
2352
  },
2012
2353
  async findComponentConfiguration(ctx) {
2013
2354
  const { uid: uid2 } = ctx.params;
2014
- const componentService = getService$1("components");
2355
+ const componentService = getService$2("components");
2015
2356
  const component = componentService.findComponent(uid2);
2016
2357
  if (!component) {
2017
2358
  return ctx.notFound("component.notFound");
@@ -2028,7 +2369,7 @@ const components$1 = {
2028
2369
  async updateComponentConfiguration(ctx) {
2029
2370
  const { uid: uid2 } = ctx.params;
2030
2371
  const { body } = ctx.request;
2031
- const componentService = getService$1("components");
2372
+ const componentService = getService$2("components");
2032
2373
  const component = componentService.findComponent(uid2);
2033
2374
  if (!component) {
2034
2375
  return ctx.notFound("component.notFound");
@@ -2062,12 +2403,12 @@ const contentTypes = {
2062
2403
  } catch (error) {
2063
2404
  return ctx.send({ error }, 400);
2064
2405
  }
2065
- const contentTypes2 = getService$1("content-types").findContentTypesByKind(kind);
2066
- const { toDto } = getService$1("data-mapper");
2406
+ const contentTypes2 = getService$2("content-types").findContentTypesByKind(kind);
2407
+ const { toDto } = getService$2("data-mapper");
2067
2408
  ctx.body = { data: contentTypes2.map(toDto) };
2068
2409
  },
2069
2410
  async findContentTypesSettings(ctx) {
2070
- const { findAllContentTypes, findConfiguration } = getService$1("content-types");
2411
+ const { findAllContentTypes, findConfiguration } = getService$2("content-types");
2071
2412
  const contentTypes2 = await findAllContentTypes();
2072
2413
  const configurations = await Promise.all(
2073
2414
  contentTypes2.map(async (contentType) => {
@@ -2081,7 +2422,7 @@ const contentTypes = {
2081
2422
  },
2082
2423
  async findContentTypeConfiguration(ctx) {
2083
2424
  const { uid: uid2 } = ctx.params;
2084
- const contentTypeService = getService$1("content-types");
2425
+ const contentTypeService = getService$2("content-types");
2085
2426
  const contentType = await contentTypeService.findContentType(uid2);
2086
2427
  if (!contentType) {
2087
2428
  return ctx.notFound("contentType.notFound");
@@ -2103,13 +2444,13 @@ const contentTypes = {
2103
2444
  const { userAbility } = ctx.state;
2104
2445
  const { uid: uid2 } = ctx.params;
2105
2446
  const { body } = ctx.request;
2106
- const contentTypeService = getService$1("content-types");
2107
- const metricsService = getService$1("metrics");
2447
+ const contentTypeService = getService$2("content-types");
2448
+ const metricsService = getService$2("metrics");
2108
2449
  const contentType = await contentTypeService.findContentType(uid2);
2109
2450
  if (!contentType) {
2110
2451
  return ctx.notFound("contentType.notFound");
2111
2452
  }
2112
- if (!getService$1("permission").canConfigureContentType({ userAbility, contentType })) {
2453
+ if (!getService$2("permission").canConfigureContentType({ userAbility, contentType })) {
2113
2454
  return ctx.forbidden();
2114
2455
  }
2115
2456
  let input;
@@ -2142,10 +2483,10 @@ const contentTypes = {
2142
2483
  };
2143
2484
  const init = {
2144
2485
  getInitData(ctx) {
2145
- const { toDto } = getService$1("data-mapper");
2146
- const { findAllComponents } = getService$1("components");
2147
- const { getAllFieldSizes } = getService$1("field-sizes");
2148
- const { findAllContentTypes } = getService$1("content-types");
2486
+ const { toDto } = getService$2("data-mapper");
2487
+ const { findAllComponents } = getService$2("components");
2488
+ const { getAllFieldSizes } = getService$2("field-sizes");
2489
+ const { findAllContentTypes } = getService$2("content-types");
2149
2490
  ctx.body = {
2150
2491
  data: {
2151
2492
  fieldSizes: getAllFieldSizes(),
@@ -2181,36 +2522,41 @@ const addFiltersClause = (params, filtersClause) => {
2181
2522
  params.filters.$and.push(filtersClause);
2182
2523
  };
2183
2524
  const sanitizeMainField = (model, mainField, userAbility) => {
2184
- const permissionChecker2 = getService$1("permission-checker").create({
2525
+ const permissionChecker2 = getService$2("permission-checker").create({
2185
2526
  userAbility,
2186
2527
  model: model.uid
2187
2528
  });
2188
- if (!isListable(model, mainField)) {
2529
+ const isMainFieldListable = isListable(model, mainField);
2530
+ const canReadMainField = permissionChecker2.can.read(null, mainField);
2531
+ if (!isMainFieldListable || !canReadMainField) {
2189
2532
  return "id";
2190
2533
  }
2191
- if (permissionChecker2.cannot.read(null, mainField)) {
2192
- if (model.uid === "plugin::users-permissions.role") {
2193
- const userPermissionChecker = getService$1("permission-checker").create({
2194
- userAbility,
2195
- model: "plugin::users-permissions.user"
2196
- });
2197
- if (userPermissionChecker.can.read()) {
2198
- return "name";
2199
- }
2200
- }
2201
- return "id";
2534
+ if (model.uid === "plugin::users-permissions.role") {
2535
+ return "name";
2202
2536
  }
2203
2537
  return mainField;
2204
2538
  };
2205
- const addStatusToRelations = async (uid2, relations2) => {
2206
- if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi.contentTypes[uid2])) {
2539
+ const addStatusToRelations = async (targetUid, relations2) => {
2540
+ if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(targetUid))) {
2207
2541
  return relations2;
2208
2542
  }
2209
- const documentMetadata2 = getService$1("document-metadata");
2210
- const documentsAvailableStatus = await documentMetadata2.getManyAvailableStatus(uid2, relations2);
2543
+ const documentMetadata2 = getService$2("document-metadata");
2544
+ if (!relations2.length) {
2545
+ return relations2;
2546
+ }
2547
+ const firstRelation = relations2[0];
2548
+ const filters = {
2549
+ documentId: { $in: relations2.map((r) => r.documentId) },
2550
+ // NOTE: find the "opposite" status
2551
+ publishedAt: firstRelation.publishedAt !== null ? { $null: true } : { $notNull: true }
2552
+ };
2553
+ const availableStatus = await strapi.query(targetUid).findMany({
2554
+ select: ["id", "documentId", "locale", "updatedAt", "createdAt", "publishedAt"],
2555
+ filters
2556
+ });
2211
2557
  return relations2.map((relation) => {
2212
- const availableStatuses = documentsAvailableStatus.filter(
2213
- (availableDocument) => availableDocument.documentId === relation.documentId
2558
+ const availableStatuses = availableStatus.filter(
2559
+ (availableDocument) => availableDocument.documentId === relation.documentId && (relation.locale ? availableDocument.locale === relation.locale : true)
2214
2560
  );
2215
2561
  return {
2216
2562
  ...relation,
@@ -2231,11 +2577,8 @@ const validateLocale = (sourceUid, targetUid, locale) => {
2231
2577
  const isLocalized = strapi.plugin("i18n").service("content-types").isLocalizedContentType;
2232
2578
  const isSourceLocalized = isLocalized(sourceModel);
2233
2579
  const isTargetLocalized = isLocalized(targetModel);
2234
- let validatedLocale = locale;
2235
- if (!targetModel || !isTargetLocalized)
2236
- validatedLocale = void 0;
2237
2580
  return {
2238
- locale: validatedLocale,
2581
+ locale,
2239
2582
  isSourceLocalized,
2240
2583
  isTargetLocalized
2241
2584
  };
@@ -2275,7 +2618,7 @@ const relations = {
2275
2618
  ctx.request?.query?.locale
2276
2619
  );
2277
2620
  const { status } = validateStatus(sourceUid, ctx.request?.query?.status);
2278
- const permissionChecker2 = getService$1("permission-checker").create({
2621
+ const permissionChecker2 = getService$2("permission-checker").create({
2279
2622
  userAbility,
2280
2623
  model
2281
2624
  });
@@ -2300,7 +2643,7 @@ const relations = {
2300
2643
  where.id = id;
2301
2644
  }
2302
2645
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2303
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2646
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2304
2647
  const currentEntity = await strapi.db.query(model).findOne({
2305
2648
  where,
2306
2649
  populate
@@ -2315,7 +2658,7 @@ const relations = {
2315
2658
  }
2316
2659
  entryId = currentEntity.id;
2317
2660
  }
2318
- const modelConfig = isComponent2 ? await getService$1("components").findConfiguration(sourceSchema) : await getService$1("content-types").findConfiguration(sourceSchema);
2661
+ const modelConfig = isComponent2 ? await getService$2("components").findConfiguration(sourceSchema) : await getService$2("content-types").findConfiguration(sourceSchema);
2319
2662
  const targetSchema = strapi.getModel(targetUid);
2320
2663
  const mainField = fp.flow(
2321
2664
  fp.prop(`metadatas.${targetField}.edit.mainField`),
@@ -2338,7 +2681,7 @@ const relations = {
2338
2681
  attribute,
2339
2682
  fieldsToSelect,
2340
2683
  mainField,
2341
- source: { schema: sourceSchema },
2684
+ source: { schema: sourceSchema, isLocalized: isSourceLocalized },
2342
2685
  target: { schema: targetSchema, isLocalized: isTargetLocalized },
2343
2686
  sourceSchema,
2344
2687
  targetSchema,
@@ -2360,7 +2703,8 @@ const relations = {
2360
2703
  fieldsToSelect,
2361
2704
  mainField,
2362
2705
  source: {
2363
- schema: { uid: sourceUid, modelType: sourceModelType }
2706
+ schema: { uid: sourceUid, modelType: sourceModelType },
2707
+ isLocalized: isSourceLocalized
2364
2708
  },
2365
2709
  target: {
2366
2710
  schema: { uid: targetUid },
@@ -2368,7 +2712,7 @@ const relations = {
2368
2712
  }
2369
2713
  } = await this.extractAndValidateRequestInfo(ctx, id);
2370
2714
  const { idsToOmit, idsToInclude, _q, ...query } = ctx.request.query;
2371
- const permissionChecker2 = getService$1("permission-checker").create({
2715
+ const permissionChecker2 = getService$2("permission-checker").create({
2372
2716
  userAbility: ctx.state.userAbility,
2373
2717
  model: targetUid
2374
2718
  });
@@ -2398,12 +2742,16 @@ const relations = {
2398
2742
  } else {
2399
2743
  where.id = id;
2400
2744
  }
2401
- if (status) {
2402
- where[`${alias}.published_at`] = getPublishedAtClause(status, targetUid);
2745
+ const publishedAt = getPublishedAtClause(status, targetUid);
2746
+ if (!fp.isEmpty(publishedAt)) {
2747
+ where[`${alias}.published_at`] = publishedAt;
2403
2748
  }
2404
- if (filterByLocale) {
2749
+ if (isTargetLocalized && locale) {
2405
2750
  where[`${alias}.locale`] = locale;
2406
2751
  }
2752
+ if (isSourceLocalized && locale) {
2753
+ where.locale = locale;
2754
+ }
2407
2755
  if ((idsToInclude?.length ?? 0) !== 0) {
2408
2756
  where[`${alias}.id`].$notIn = idsToInclude;
2409
2757
  }
@@ -2421,7 +2769,8 @@ const relations = {
2421
2769
  id: { $notIn: fp.uniq(idsToOmit) }
2422
2770
  });
2423
2771
  }
2424
- const res = await strapi.db.query(targetUid).findPage(strapi.get("query-params").transform(targetUid, queryParams));
2772
+ const dbQuery = strapi.get("query-params").transform(targetUid, queryParams);
2773
+ const res = await strapi.db.query(targetUid).findPage(dbQuery);
2425
2774
  ctx.body = {
2426
2775
  ...res,
2427
2776
  results: await addStatusToRelations(targetUid, res.results)
@@ -2436,29 +2785,39 @@ const relations = {
2436
2785
  attribute,
2437
2786
  targetField,
2438
2787
  fieldsToSelect,
2439
- source: {
2440
- schema: { uid: sourceUid }
2441
- },
2442
- target: {
2443
- schema: { uid: targetUid }
2444
- }
2788
+ status,
2789
+ source: { schema: sourceSchema },
2790
+ target: { schema: targetSchema }
2445
2791
  } = await this.extractAndValidateRequestInfo(ctx, id);
2446
- const permissionQuery = await getService$1("permission-checker").create({ userAbility, model: targetUid }).sanitizedQuery.read({ fields: fieldsToSelect });
2792
+ const { uid: sourceUid } = sourceSchema;
2793
+ const { uid: targetUid } = targetSchema;
2794
+ const permissionQuery = await getService$2("permission-checker").create({ userAbility, model: targetUid }).sanitizedQuery.read({ fields: fieldsToSelect });
2447
2795
  const dbQuery = strapi.db.query(sourceUid);
2448
2796
  const loadRelations = strapiUtils.relations.isAnyToMany(attribute) ? (...args) => dbQuery.loadPages(...args) : (...args) => dbQuery.load(...args).then((res2) => ({ results: res2 ? [res2] : [] }));
2797
+ const filters = {};
2798
+ if (sourceSchema?.options?.draftAndPublish) {
2799
+ if (targetSchema?.options?.draftAndPublish) {
2800
+ if (status === "published") {
2801
+ filters.publishedAt = { $notNull: true };
2802
+ } else {
2803
+ filters.publishedAt = { $null: true };
2804
+ }
2805
+ }
2806
+ } else if (targetSchema?.options?.draftAndPublish) {
2807
+ filters.publishedAt = { $null: true };
2808
+ }
2449
2809
  const res = await loadRelations({ id: entryId }, targetField, {
2450
- select: ["id", "documentId", "locale", "publishedAt"],
2810
+ select: ["id", "documentId", "locale", "publishedAt", "updatedAt"],
2451
2811
  ordering: "desc",
2452
2812
  page: ctx.request.query.page,
2453
- pageSize: ctx.request.query.pageSize
2813
+ pageSize: ctx.request.query.pageSize,
2814
+ filters
2454
2815
  });
2455
2816
  const loadedIds = res.results.map((item) => item.id);
2456
2817
  addFiltersClause(permissionQuery, { id: { $in: loadedIds } });
2457
2818
  const sanitizedRes = await loadRelations({ id: entryId }, targetField, {
2458
2819
  ...strapi.get("query-params").transform(targetUid, permissionQuery),
2459
- ordering: "desc",
2460
- page: ctx.request.query.page,
2461
- pageSize: ctx.request.query.pageSize
2820
+ ordering: "desc"
2462
2821
  });
2463
2822
  const relationsUnion = fp.uniqBy("id", fp.concat(sanitizedRes.results, res.results));
2464
2823
  ctx.body = {
@@ -2473,10 +2832,10 @@ const relations = {
2473
2832
  }
2474
2833
  };
2475
2834
  const buildPopulateFromQuery = async (query, model) => {
2476
- return getService$1("populate-builder")(model).populateFromQuery(query).populateDeep(Infinity).countRelations().build();
2835
+ return getService$2("populate-builder")(model).populateFromQuery(query).populateDeep(Infinity).countRelations().build();
2477
2836
  };
2478
2837
  const findDocument = async (query, uid2, opts = {}) => {
2479
- const documentManager2 = getService$1("document-manager");
2838
+ const documentManager2 = getService$2("document-manager");
2480
2839
  const populate = await buildPopulateFromQuery(query, uid2);
2481
2840
  return documentManager2.findMany({ ...opts, populate }, uid2).then((documents) => documents[0]);
2482
2841
  };
@@ -2484,13 +2843,13 @@ const createOrUpdateDocument = async (ctx, opts) => {
2484
2843
  const { user, userAbility } = ctx.state;
2485
2844
  const { model } = ctx.params;
2486
2845
  const { body, query } = ctx.request;
2487
- const documentManager2 = getService$1("document-manager");
2488
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2846
+ const documentManager2 = getService$2("document-manager");
2847
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2489
2848
  if (permissionChecker2.cannot.create() && permissionChecker2.cannot.update()) {
2490
2849
  throw new strapiUtils.errors.ForbiddenError();
2491
2850
  }
2492
2851
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2493
- const { locale } = getDocumentLocaleAndStatus(body);
2852
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2494
2853
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2495
2854
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2496
2855
  // Find the first document to check if it exists
@@ -2526,13 +2885,12 @@ const singleTypes = {
2526
2885
  const { userAbility } = ctx.state;
2527
2886
  const { model } = ctx.params;
2528
2887
  const { query = {} } = ctx.request;
2529
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2530
- const documentMetadata2 = getService$1("document-metadata");
2888
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2531
2889
  if (permissionChecker2.cannot.read()) {
2532
2890
  return ctx.forbidden();
2533
2891
  }
2534
2892
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2535
- const { locale, status } = getDocumentLocaleAndStatus(query);
2893
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
2536
2894
  const version = await findDocument(permissionQuery, model, { locale, status });
2537
2895
  if (!version) {
2538
2896
  if (permissionChecker2.cannot.create()) {
@@ -2542,9 +2900,11 @@ const singleTypes = {
2542
2900
  if (!document) {
2543
2901
  return ctx.notFound();
2544
2902
  }
2545
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
2903
+ const { meta } = await formatDocumentWithMetadata(
2904
+ permissionChecker2,
2546
2905
  model,
2547
- { id: document.documentId, locale, publishedAt: null },
2906
+ // @ts-expect-error - fix types
2907
+ { documentId: document.documentId, locale, publishedAt: null },
2548
2908
  { availableLocales: true, availableStatus: false }
2549
2909
  );
2550
2910
  ctx.body = { data: {}, meta };
@@ -2554,29 +2914,28 @@ const singleTypes = {
2554
2914
  return ctx.forbidden();
2555
2915
  }
2556
2916
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
2557
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2917
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2558
2918
  },
2559
2919
  async createOrUpdate(ctx) {
2560
2920
  const { userAbility } = ctx.state;
2561
2921
  const { model } = ctx.params;
2562
- const documentMetadata2 = getService$1("document-metadata");
2563
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2922
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2564
2923
  const document = await createOrUpdateDocument(ctx);
2565
2924
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2566
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2925
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2567
2926
  },
2568
2927
  async delete(ctx) {
2569
2928
  const { userAbility } = ctx.state;
2570
2929
  const { model } = ctx.params;
2571
2930
  const { query = {} } = ctx.request;
2572
- const documentManager2 = getService$1("document-manager");
2573
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2931
+ const documentManager2 = getService$2("document-manager");
2932
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2574
2933
  if (permissionChecker2.cannot.delete()) {
2575
2934
  return ctx.forbidden();
2576
2935
  }
2577
2936
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2578
2937
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2579
- const { locale } = getDocumentLocaleAndStatus(query);
2938
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2580
2939
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2581
2940
  populate,
2582
2941
  locale
@@ -2598,9 +2957,8 @@ const singleTypes = {
2598
2957
  const { userAbility } = ctx.state;
2599
2958
  const { model } = ctx.params;
2600
2959
  const { query = {} } = ctx.request;
2601
- const documentManager2 = getService$1("document-manager");
2602
- const documentMetadata2 = getService$1("document-metadata");
2603
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2960
+ const documentManager2 = getService$2("document-manager");
2961
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2604
2962
  if (permissionChecker2.cannot.publish()) {
2605
2963
  return ctx.forbidden();
2606
2964
  }
@@ -2614,11 +2972,12 @@ const singleTypes = {
2614
2972
  if (permissionChecker2.cannot.publish(document)) {
2615
2973
  throw new strapiUtils.errors.ForbiddenError();
2616
2974
  }
2617
- const { locale } = getDocumentLocaleAndStatus(document);
2618
- return documentManager2.publish(document.documentId, model, { locale });
2975
+ const { locale } = await getDocumentLocaleAndStatus(document, model);
2976
+ const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2977
+ return publishResult.at(0);
2619
2978
  });
2620
2979
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
2621
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2980
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2622
2981
  },
2623
2982
  async unpublish(ctx) {
2624
2983
  const { userAbility } = ctx.state;
@@ -2627,9 +2986,8 @@ const singleTypes = {
2627
2986
  body: { discardDraft, ...body },
2628
2987
  query = {}
2629
2988
  } = ctx.request;
2630
- const documentManager2 = getService$1("document-manager");
2631
- const documentMetadata2 = getService$1("document-metadata");
2632
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2989
+ const documentManager2 = getService$2("document-manager");
2990
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2633
2991
  if (permissionChecker2.cannot.unpublish()) {
2634
2992
  return ctx.forbidden();
2635
2993
  }
@@ -2637,7 +2995,7 @@ const singleTypes = {
2637
2995
  return ctx.forbidden();
2638
2996
  }
2639
2997
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2640
- const { locale } = getDocumentLocaleAndStatus(body);
2998
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2641
2999
  const document = await findDocument(sanitizedQuery, model, { locale });
2642
3000
  if (!document) {
2643
3001
  return ctx.notFound();
@@ -2655,7 +3013,7 @@ const singleTypes = {
2655
3013
  ctx.body = await strapiUtils.async.pipe(
2656
3014
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
2657
3015
  permissionChecker2.sanitizeOutput,
2658
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
3016
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2659
3017
  )(document);
2660
3018
  });
2661
3019
  },
@@ -2663,14 +3021,13 @@ const singleTypes = {
2663
3021
  const { userAbility } = ctx.state;
2664
3022
  const { model } = ctx.params;
2665
3023
  const { body, query = {} } = ctx.request;
2666
- const documentManager2 = getService$1("document-manager");
2667
- const documentMetadata2 = getService$1("document-metadata");
2668
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
3024
+ const documentManager2 = getService$2("document-manager");
3025
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2669
3026
  if (permissionChecker2.cannot.discard()) {
2670
3027
  return ctx.forbidden();
2671
3028
  }
2672
3029
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2673
- const { locale } = getDocumentLocaleAndStatus(body);
3030
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2674
3031
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2675
3032
  if (!document) {
2676
3033
  return ctx.notFound();
@@ -2681,16 +3038,16 @@ const singleTypes = {
2681
3038
  ctx.body = await strapiUtils.async.pipe(
2682
3039
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
2683
3040
  permissionChecker2.sanitizeOutput,
2684
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
3041
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2685
3042
  )(document);
2686
3043
  },
2687
3044
  async countDraftRelations(ctx) {
2688
3045
  const { userAbility } = ctx.state;
2689
3046
  const { model } = ctx.params;
2690
3047
  const { query } = ctx.request;
2691
- const documentManager2 = getService$1("document-manager");
2692
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2693
- const { locale } = getDocumentLocaleAndStatus(query);
3048
+ const documentManager2 = getService$2("document-manager");
3049
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
3050
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2694
3051
  if (permissionChecker2.cannot.read()) {
2695
3052
  return ctx.forbidden();
2696
3053
  }
@@ -2711,9 +3068,9 @@ const uid$1 = {
2711
3068
  async generateUID(ctx) {
2712
3069
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2713
3070
  const { query = {} } = ctx.request;
2714
- const { locale } = getDocumentLocaleAndStatus(query);
3071
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2715
3072
  await validateUIDField(contentTypeUID, field);
2716
- const uidService = getService$1("uid");
3073
+ const uidService = getService$2("uid");
2717
3074
  ctx.body = {
2718
3075
  data: await uidService.generateUIDField({ contentTypeUID, field, data, locale })
2719
3076
  };
@@ -2723,9 +3080,9 @@ const uid$1 = {
2723
3080
  ctx.request.body
2724
3081
  );
2725
3082
  const { query = {} } = ctx.request;
2726
- const { locale } = getDocumentLocaleAndStatus(query);
3083
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2727
3084
  await validateUIDField(contentTypeUID, field);
2728
- const uidService = getService$1("uid");
3085
+ const uidService = getService$2("uid");
2729
3086
  const isAvailable = await uidService.checkUIDAvailability({
2730
3087
  contentTypeUID,
2731
3088
  field,
@@ -2746,7 +3103,8 @@ const controllers = {
2746
3103
  relations,
2747
3104
  "single-types": singleTypes,
2748
3105
  uid: uid$1,
2749
- ...history.controllers ? history.controllers : {}
3106
+ ...history.controllers ? history.controllers : {},
3107
+ ...preview.controllers ? preview.controllers : {}
2750
3108
  };
2751
3109
  const keys = {
2752
3110
  CONFIGURATION: "configuration"
@@ -2897,12 +3255,12 @@ async function syncMetadatas(configuration, schema) {
2897
3255
  return ___default.default.assign(metasWithDefaults, updatedMetas);
2898
3256
  }
2899
3257
  const getTargetSchema = (targetModel) => {
2900
- return getService$1("content-types").findContentType(targetModel);
3258
+ return getService$2("content-types").findContentType(targetModel);
2901
3259
  };
2902
3260
  const DEFAULT_LIST_LENGTH = 4;
2903
3261
  const MAX_ROW_SIZE = 12;
2904
3262
  const isAllowedFieldSize = (type, size) => {
2905
- const { getFieldSize } = getService$1("field-sizes");
3263
+ const { getFieldSize } = getService$2("field-sizes");
2906
3264
  const fieldSize = getFieldSize(type);
2907
3265
  if (!fieldSize.isResizable && size !== fieldSize.default) {
2908
3266
  return false;
@@ -2910,7 +3268,7 @@ const isAllowedFieldSize = (type, size) => {
2910
3268
  return size <= MAX_ROW_SIZE;
2911
3269
  };
2912
3270
  const getDefaultFieldSize = (attribute) => {
2913
- const { hasFieldSize, getFieldSize } = getService$1("field-sizes");
3271
+ const { hasFieldSize, getFieldSize } = getService$2("field-sizes");
2914
3272
  return getFieldSize(hasFieldSize(attribute.customField) ? attribute.customField : attribute.type).default;
2915
3273
  };
2916
3274
  async function createDefaultLayouts(schema) {
@@ -2945,7 +3303,7 @@ function syncLayouts(configuration, schema) {
2945
3303
  for (const el of row) {
2946
3304
  if (!hasEditableAttribute(schema, el.name))
2947
3305
  continue;
2948
- const { hasFieldSize } = getService$1("field-sizes");
3306
+ const { hasFieldSize } = getService$2("field-sizes");
2949
3307
  const fieldType = hasFieldSize(schema.attributes[el.name].customField) ? schema.attributes[el.name].customField : schema.attributes[el.name].type;
2950
3308
  if (!isAllowedFieldSize(fieldType, el.size)) {
2951
3309
  elementsToReAppend.push(el.name);
@@ -3085,17 +3443,17 @@ const configurationService$1 = createConfigurationService({
3085
3443
  isComponent: true,
3086
3444
  prefix: STORE_KEY_PREFIX,
3087
3445
  getModels() {
3088
- const { toContentManagerModel } = getService$1("data-mapper");
3446
+ const { toContentManagerModel } = getService$2("data-mapper");
3089
3447
  return fp.mapValues(toContentManagerModel, strapi.components);
3090
3448
  }
3091
3449
  });
3092
3450
  const components = ({ strapi: strapi2 }) => ({
3093
3451
  findAllComponents() {
3094
- const { toContentManagerModel } = getService$1("data-mapper");
3452
+ const { toContentManagerModel } = getService$2("data-mapper");
3095
3453
  return Object.values(strapi2.components).map(toContentManagerModel);
3096
3454
  },
3097
3455
  findComponent(uid2) {
3098
- const { toContentManagerModel } = getService$1("data-mapper");
3456
+ const { toContentManagerModel } = getService$2("data-mapper");
3099
3457
  const component = strapi2.components[uid2];
3100
3458
  return fp.isNil(component) ? component : toContentManagerModel(component);
3101
3459
  },
@@ -3146,17 +3504,17 @@ const configurationService = createConfigurationService({
3146
3504
  storeUtils,
3147
3505
  prefix: "content_types",
3148
3506
  getModels() {
3149
- const { toContentManagerModel } = getService$1("data-mapper");
3507
+ const { toContentManagerModel } = getService$2("data-mapper");
3150
3508
  return fp.mapValues(toContentManagerModel, strapi.contentTypes);
3151
3509
  }
3152
3510
  });
3153
3511
  const service = ({ strapi: strapi2 }) => ({
3154
3512
  findAllContentTypes() {
3155
- const { toContentManagerModel } = getService$1("data-mapper");
3513
+ const { toContentManagerModel } = getService$2("data-mapper");
3156
3514
  return Object.values(strapi2.contentTypes).map(toContentManagerModel);
3157
3515
  },
3158
3516
  findContentType(uid2) {
3159
- const { toContentManagerModel } = getService$1("data-mapper");
3517
+ const { toContentManagerModel } = getService$2("data-mapper");
3160
3518
  const contentType = strapi2.contentTypes[uid2];
3161
3519
  return fp.isNil(contentType) ? contentType : toContentManagerModel(contentType);
3162
3520
  },
@@ -3185,7 +3543,7 @@ const service = ({ strapi: strapi2 }) => ({
3185
3543
  return this.findConfiguration(contentType);
3186
3544
  },
3187
3545
  findComponentsConfigurations(contentType) {
3188
- return getService$1("components").findComponentsConfigurations(contentType);
3546
+ return getService$2("components").findComponentsConfigurations(contentType);
3189
3547
  },
3190
3548
  syncConfigurations() {
3191
3549
  return configurationService.syncConfigurations();
@@ -3366,12 +3724,27 @@ const createPermissionChecker = (strapi2) => ({ userAbility, model }) => {
3366
3724
  ability: userAbility,
3367
3725
  model
3368
3726
  });
3369
- const toSubject = (entity) => entity ? permissionsManager.toSubject(entity, model) : model;
3727
+ const { actionProvider } = strapi2.service("admin::permission");
3728
+ const toSubject = (entity) => {
3729
+ return entity ? permissionsManager.toSubject(entity, model) : model;
3730
+ };
3370
3731
  const can = (action, entity, field) => {
3371
- return userAbility.can(action, toSubject(entity), field);
3732
+ const subject = toSubject(entity);
3733
+ const aliases = actionProvider.unstable_aliases(action, model);
3734
+ return (
3735
+ // Test the original action to see if it passes
3736
+ userAbility.can(action, subject, field) || // Else try every known alias if at least one of them succeed, then the user "can"
3737
+ aliases.some((alias) => userAbility.can(alias, subject, field))
3738
+ );
3372
3739
  };
3373
3740
  const cannot = (action, entity, field) => {
3374
- return userAbility.cannot(action, toSubject(entity), field);
3741
+ const subject = toSubject(entity);
3742
+ const aliases = actionProvider.unstable_aliases(action, model);
3743
+ return (
3744
+ // Test both the original action
3745
+ userAbility.cannot(action, subject, field) && // and every known alias, if all of them fail (cannot), then the user truly "cannot"
3746
+ aliases.every((alias) => userAbility.cannot(alias, subject, field))
3747
+ );
3375
3748
  };
3376
3749
  const sanitizeOutput = (data, { action = ACTIONS.read } = {}) => {
3377
3750
  return permissionsManager.sanitizeOutput(data, { subject: toSubject(data), action });
@@ -3442,7 +3815,7 @@ const permission = ({ strapi: strapi2 }) => ({
3442
3815
  return userAbility.can(action);
3443
3816
  },
3444
3817
  async registerPermissions() {
3445
- const displayedContentTypes = getService$1("content-types").findDisplayedContentTypes();
3818
+ const displayedContentTypes = getService$2("content-types").findDisplayedContentTypes();
3446
3819
  const contentTypesUids = displayedContentTypes.map(fp.prop("uid"));
3447
3820
  const actions = [
3448
3821
  {
@@ -3514,7 +3887,7 @@ const permission = ({ strapi: strapi2 }) => ({
3514
3887
  await strapi2.service("admin::permission").actionProvider.registerMany(actions);
3515
3888
  }
3516
3889
  });
3517
- const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils__default.default.contentTypes;
3890
+ const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils__default.default.contentTypes;
3518
3891
  const { isAnyToMany } = strapiUtils__default.default.relations;
3519
3892
  const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils__default.default.contentTypes.constants;
3520
3893
  const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
@@ -3605,6 +3978,42 @@ const getDeepPopulate = (uid2, {
3605
3978
  {}
3606
3979
  );
3607
3980
  };
3981
+ const getValidatableFieldsPopulate = (uid2, {
3982
+ initialPopulate = {},
3983
+ countMany = false,
3984
+ countOne = false,
3985
+ maxLevel = Infinity
3986
+ } = {}, level = 1) => {
3987
+ if (level > maxLevel) {
3988
+ return {};
3989
+ }
3990
+ const model = strapi.getModel(uid2);
3991
+ return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
3992
+ if (!getDoesAttributeRequireValidation(attribute)) {
3993
+ return populateAcc;
3994
+ }
3995
+ if (isScalarAttribute(attribute)) {
3996
+ return fp.merge(populateAcc, {
3997
+ [attributeName]: true
3998
+ });
3999
+ }
4000
+ return fp.merge(
4001
+ populateAcc,
4002
+ getPopulateFor(
4003
+ attributeName,
4004
+ model,
4005
+ {
4006
+ // @ts-expect-error - improve types
4007
+ initialPopulate: initialPopulate?.[attributeName],
4008
+ countMany,
4009
+ countOne,
4010
+ maxLevel
4011
+ },
4012
+ level
4013
+ )
4014
+ );
4015
+ }, {});
4016
+ };
3608
4017
  const getDeepPopulateDraftCount = (uid2) => {
3609
4018
  const model = strapi.getModel(uid2);
3610
4019
  let hasRelations = false;
@@ -3612,6 +4021,10 @@ const getDeepPopulateDraftCount = (uid2) => {
3612
4021
  const attribute = model.attributes[attributeName];
3613
4022
  switch (attribute.type) {
3614
4023
  case "relation": {
4024
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
4025
+ if (isMorphRelation) {
4026
+ break;
4027
+ }
3615
4028
  if (isVisibleAttribute$1(model, attributeName)) {
3616
4029
  populateAcc[attributeName] = {
3617
4030
  count: true,
@@ -3626,22 +4039,24 @@ const getDeepPopulateDraftCount = (uid2) => {
3626
4039
  attribute.component
3627
4040
  );
3628
4041
  if (childHasRelations) {
3629
- populateAcc[attributeName] = { populate: populate2 };
4042
+ populateAcc[attributeName] = {
4043
+ populate: populate2
4044
+ };
3630
4045
  hasRelations = true;
3631
4046
  }
3632
4047
  break;
3633
4048
  }
3634
4049
  case "dynamiczone": {
3635
- const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
3636
- const { populate: populate2, hasRelations: childHasRelations } = getDeepPopulateDraftCount(componentUID);
3637
- if (childHasRelations) {
4050
+ const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
4051
+ const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
4052
+ if (componentHasRelations) {
3638
4053
  hasRelations = true;
3639
- return fp.merge(acc, populate2);
4054
+ return { ...acc, [componentUID]: { populate: componentPopulate } };
3640
4055
  }
3641
4056
  return acc;
3642
4057
  }, {});
3643
- if (!fp.isEmpty(dzPopulate)) {
3644
- populateAcc[attributeName] = { populate: dzPopulate };
4058
+ if (!fp.isEmpty(dzPopulateFragment)) {
4059
+ populateAcc[attributeName] = { on: dzPopulateFragment };
3645
4060
  }
3646
4061
  break;
3647
4062
  }
@@ -3676,7 +4091,7 @@ const getQueryPopulate = async (uid2, query) => {
3676
4091
  return populateQuery;
3677
4092
  };
3678
4093
  const buildDeepPopulate = (uid2) => {
3679
- return getService$1("populate-builder")(uid2).populateDeep(Infinity).countRelations().build();
4094
+ return getService$2("populate-builder")(uid2).populateDeep(Infinity).countRelations().build();
3680
4095
  };
3681
4096
  const populateBuilder = (uid2) => {
3682
4097
  let getInitialPopulate = async () => {
@@ -3833,41 +4248,72 @@ const AVAILABLE_STATUS_FIELDS = [
3833
4248
  "updatedBy",
3834
4249
  "status"
3835
4250
  ];
3836
- const AVAILABLE_LOCALES_FIELDS = ["id", "locale", "updatedAt", "createdAt", "status"];
4251
+ const AVAILABLE_LOCALES_FIELDS = [
4252
+ "id",
4253
+ "locale",
4254
+ "updatedAt",
4255
+ "createdAt",
4256
+ "status",
4257
+ "publishedAt",
4258
+ "documentId"
4259
+ ];
3837
4260
  const CONTENT_MANAGER_STATUS = {
3838
4261
  PUBLISHED: "published",
3839
4262
  DRAFT: "draft",
3840
4263
  MODIFIED: "modified"
3841
4264
  };
3842
- const areDatesEqual = (date1, date2, threshold) => {
3843
- if (!date1 || !date2) {
4265
+ const getIsVersionLatestModification = (version, otherVersion) => {
4266
+ if (!version || !version.updatedAt) {
3844
4267
  return false;
3845
4268
  }
3846
- const time1 = new Date(date1).getTime();
3847
- const time2 = new Date(date2).getTime();
3848
- const difference = Math.abs(time1 - time2);
3849
- return difference <= threshold;
4269
+ const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
4270
+ const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
4271
+ return versionUpdatedAt > otherUpdatedAt;
3850
4272
  };
3851
4273
  const documentMetadata = ({ strapi: strapi2 }) => ({
3852
4274
  /**
3853
4275
  * Returns available locales of a document for the current status
3854
4276
  */
3855
- getAvailableLocales(uid2, version, allVersions) {
4277
+ async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
3856
4278
  const versionsByLocale = fp.groupBy("locale", allVersions);
3857
- delete versionsByLocale[version.locale];
3858
- return Object.values(versionsByLocale).map((localeVersions) => {
3859
- if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2))) {
3860
- return fp.pick(AVAILABLE_LOCALES_FIELDS, localeVersions[0]);
4279
+ if (version.locale) {
4280
+ delete versionsByLocale[version.locale];
4281
+ }
4282
+ const model = strapi2.getModel(uid2);
4283
+ const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
4284
+ const traversalFunction = async (localeVersion) => strapiUtils.traverseEntity(
4285
+ ({ key }, { remove }) => {
4286
+ if (keysToKeep.includes(key)) {
4287
+ return;
4288
+ }
4289
+ remove(key);
4290
+ },
4291
+ { schema: model, getModel: strapi2.getModel.bind(strapi2) },
4292
+ // @ts-expect-error fix types DocumentVersion incompatible with Data
4293
+ localeVersion
4294
+ );
4295
+ const mappingResult = await strapiUtils.async.map(
4296
+ Object.values(versionsByLocale),
4297
+ async (localeVersions) => {
4298
+ const mappedLocaleVersions = await strapiUtils.async.map(
4299
+ localeVersions,
4300
+ traversalFunction
4301
+ );
4302
+ if (!strapiUtils.contentTypes.hasDraftAndPublish(model)) {
4303
+ return mappedLocaleVersions[0];
4304
+ }
4305
+ const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
4306
+ const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
4307
+ if (!draftVersion) {
4308
+ return;
4309
+ }
4310
+ return {
4311
+ ...draftVersion,
4312
+ status: this.getStatus(draftVersion, otherVersions)
4313
+ };
3861
4314
  }
3862
- const draftVersion = localeVersions.find((v) => v.publishedAt === null);
3863
- const otherVersions = localeVersions.filter((v) => v.id !== draftVersion?.id);
3864
- if (!draftVersion)
3865
- return;
3866
- return {
3867
- ...fp.pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
3868
- status: this.getStatus(draftVersion, otherVersions)
3869
- };
3870
- }).filter(Boolean);
4315
+ );
4316
+ return mappingResult.filter(Boolean);
3871
4317
  },
3872
4318
  /**
3873
4319
  * Returns available status of a document for the current locale
@@ -3905,26 +4351,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3905
4351
  });
3906
4352
  },
3907
4353
  getStatus(version, otherDocumentStatuses) {
3908
- const isDraft = version.publishedAt === null;
3909
- if (!otherDocumentStatuses?.length) {
3910
- return isDraft ? CONTENT_MANAGER_STATUS.DRAFT : CONTENT_MANAGER_STATUS.PUBLISHED;
3911
- }
3912
- if (isDraft) {
3913
- const publishedVersion = otherDocumentStatuses?.find((d) => d.publishedAt !== null);
3914
- if (!publishedVersion) {
3915
- return CONTENT_MANAGER_STATUS.DRAFT;
3916
- }
4354
+ let draftVersion;
4355
+ let publishedVersion;
4356
+ if (version.publishedAt) {
4357
+ publishedVersion = version;
4358
+ } else {
4359
+ draftVersion = version;
3917
4360
  }
3918
- if (areDatesEqual(version.updatedAt, otherDocumentStatuses.at(0)?.updatedAt, 500)) {
3919
- return CONTENT_MANAGER_STATUS.PUBLISHED;
4361
+ const otherVersion = otherDocumentStatuses?.at(0);
4362
+ if (otherVersion?.publishedAt) {
4363
+ publishedVersion = otherVersion;
4364
+ } else if (otherVersion) {
4365
+ draftVersion = otherVersion;
3920
4366
  }
3921
- return CONTENT_MANAGER_STATUS.MODIFIED;
4367
+ if (!draftVersion)
4368
+ return CONTENT_MANAGER_STATUS.PUBLISHED;
4369
+ if (!publishedVersion)
4370
+ return CONTENT_MANAGER_STATUS.DRAFT;
4371
+ const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
4372
+ return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
3922
4373
  },
4374
+ // TODO is it necessary to return metadata on every page of the CM
4375
+ // We could refactor this so the locales are only loaded when they're
4376
+ // needed. e.g. in the bulk locale action modal.
3923
4377
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4378
+ const populate = getValidatableFieldsPopulate(uid2);
3924
4379
  const versions = await strapi2.db.query(uid2).findMany({
3925
4380
  where: { documentId: version.documentId },
3926
- select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
3927
4381
  populate: {
4382
+ // Populate only fields that require validation for bulk locale actions
4383
+ ...populate,
4384
+ // NOTE: creator fields are selected in this way to avoid exposing sensitive data
3928
4385
  createdBy: {
3929
4386
  select: ["id", "firstname", "lastname", "email"]
3930
4387
  },
@@ -3933,7 +4390,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3933
4390
  }
3934
4391
  }
3935
4392
  });
3936
- const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
4393
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
3937
4394
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
3938
4395
  return {
3939
4396
  availableLocales: availableLocalesResult,
@@ -3946,8 +4403,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3946
4403
  * - Available status of the document for the current locale
3947
4404
  */
3948
4405
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
3949
- if (!document)
3950
- return document;
4406
+ if (!document) {
4407
+ return {
4408
+ data: document,
4409
+ meta: {
4410
+ availableLocales: [],
4411
+ availableStatus: []
4412
+ }
4413
+ };
4414
+ }
3951
4415
  const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
3952
4416
  if (!hasDraftAndPublish) {
3953
4417
  opts.availableStatus = false;
@@ -3997,26 +4461,9 @@ const sumDraftCounts = (entity, uid2) => {
3997
4461
  }, 0);
3998
4462
  };
3999
4463
  const { ApplicationError } = strapiUtils.errors;
4000
- const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
4001
4464
  const { PUBLISHED_AT_ATTRIBUTE } = strapiUtils.contentTypes.constants;
4002
4465
  const omitPublishedAtField = fp.omit(PUBLISHED_AT_ATTRIBUTE);
4003
4466
  const omitIdField = fp.omit("id");
4004
- const emitEvent = async (uid2, event, document) => {
4005
- const modelDef = strapi.getModel(uid2);
4006
- const sanitizedDocument = await strapiUtils.sanitize.sanitizers.defaultSanitizeOutput(
4007
- {
4008
- schema: modelDef,
4009
- getModel(uid22) {
4010
- return strapi.getModel(uid22);
4011
- }
4012
- },
4013
- document
4014
- );
4015
- strapi.eventHub.emit(event, {
4016
- model: modelDef.modelName,
4017
- entry: sanitizedDocument
4018
- });
4019
- };
4020
4467
  const documentManager = ({ strapi: strapi2 }) => {
4021
4468
  return {
4022
4469
  async findOne(id, uid2, opts = {}) {
@@ -4035,6 +4482,9 @@ const documentManager = ({ strapi: strapi2 }) => {
4035
4482
  } else if (opts.locale && opts.locale !== "*") {
4036
4483
  where.locale = opts.locale;
4037
4484
  }
4485
+ if (typeof opts.isPublished === "boolean") {
4486
+ where.publishedAt = { $notNull: opts.isPublished };
4487
+ }
4038
4488
  return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
4039
4489
  },
4040
4490
  async findMany(opts, uid2) {
@@ -4042,20 +4492,16 @@ const documentManager = ({ strapi: strapi2 }) => {
4042
4492
  return strapi2.documents(uid2).findMany(params);
4043
4493
  },
4044
4494
  async findPage(opts, uid2) {
4045
- const page = Number(opts?.page) || 1;
4046
- const pageSize = Number(opts?.pageSize) || 10;
4495
+ const params = strapiUtils.pagination.withDefaultPagination(opts || {}, {
4496
+ maxLimit: 1e3
4497
+ });
4047
4498
  const [documents, total = 0] = await Promise.all([
4048
- strapi2.documents(uid2).findMany(opts),
4049
- strapi2.documents(uid2).count(opts)
4499
+ strapi2.documents(uid2).findMany(params),
4500
+ strapi2.documents(uid2).count(params)
4050
4501
  ]);
4051
4502
  return {
4052
4503
  results: documents,
4053
- pagination: {
4054
- page,
4055
- pageSize,
4056
- pageCount: Math.ceil(total / pageSize),
4057
- total
4058
- }
4504
+ pagination: strapiUtils.pagination.transformPagedPaginationInfo(params, total)
4059
4505
  };
4060
4506
  },
4061
4507
  async create(uid2, opts = {}) {
@@ -4072,10 +4518,7 @@ const documentManager = ({ strapi: strapi2 }) => {
4072
4518
  async clone(id, body, uid2) {
4073
4519
  const populate = await buildDeepPopulate(uid2);
4074
4520
  const params = {
4075
- data: {
4076
- ...omitIdField(body),
4077
- [PUBLISHED_AT_ATTRIBUTE]: null
4078
- },
4521
+ data: omitIdField(body),
4079
4522
  populate
4080
4523
  };
4081
4524
  return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
@@ -4101,70 +4544,36 @@ const documentManager = ({ strapi: strapi2 }) => {
4101
4544
  return {};
4102
4545
  },
4103
4546
  // FIXME: handle relations
4104
- async deleteMany(opts, uid2) {
4105
- const docs = await strapi2.documents(uid2).findMany(opts);
4106
- for (const doc of docs) {
4107
- await strapi2.documents(uid2).delete({ documentId: doc.documentId });
4108
- }
4109
- return { count: docs.length };
4547
+ async deleteMany(documentIds, uid2, opts = {}) {
4548
+ const deletedEntries = await strapi2.db.transaction(async () => {
4549
+ return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
4550
+ });
4551
+ return { count: deletedEntries.length };
4110
4552
  },
4111
4553
  async publish(id, uid2, opts = {}) {
4112
4554
  const populate = await buildDeepPopulate(uid2);
4113
4555
  const params = { ...opts, populate };
4114
- return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries.at(0));
4556
+ return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
4115
4557
  },
4116
- async publishMany(entities, uid2) {
4117
- if (!entities.length) {
4118
- return null;
4119
- }
4120
- await Promise.all(
4121
- entities.map((document) => {
4122
- return strapi2.entityValidator.validateEntityCreation(
4123
- strapi2.getModel(uid2),
4124
- document,
4125
- void 0,
4126
- // @ts-expect-error - FIXME: entity here is unnecessary
4127
- document
4128
- );
4129
- })
4130
- );
4131
- const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4132
- const filters = { id: { $in: entitiesToPublish } };
4133
- const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
4134
- const populate = await buildDeepPopulate(uid2);
4135
- const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4136
- where: filters,
4137
- data
4138
- });
4139
- const publishedEntities = await strapi2.db.query(uid2).findMany({
4140
- where: filters,
4141
- populate
4558
+ async publishMany(uid2, documentIds, locale) {
4559
+ return strapi2.db.transaction(async () => {
4560
+ const results = await Promise.all(
4561
+ documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
4562
+ );
4563
+ const publishedEntitiesCount = results.flat().filter(Boolean).length;
4564
+ return publishedEntitiesCount;
4142
4565
  });
4143
- await Promise.all(
4144
- publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
4145
- );
4146
- return publishedEntitiesCount;
4147
4566
  },
4148
- async unpublishMany(documents, uid2) {
4149
- if (!documents.length) {
4150
- return null;
4151
- }
4152
- const entitiesToUnpublish = documents.filter((doc) => doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4153
- const filters = { id: { $in: entitiesToUnpublish } };
4154
- const data = { [PUBLISHED_AT_ATTRIBUTE]: null };
4155
- const populate = await buildDeepPopulate(uid2);
4156
- const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4157
- where: filters,
4158
- data
4159
- });
4160
- const unpublishedEntities = await strapi2.db.query(uid2).findMany({
4161
- where: filters,
4162
- populate
4567
+ async unpublishMany(documentIds, uid2, opts = {}) {
4568
+ const unpublishedEntries = await strapi2.db.transaction(async () => {
4569
+ return Promise.all(
4570
+ documentIds.map(
4571
+ (id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
4572
+ )
4573
+ );
4163
4574
  });
4164
- await Promise.all(
4165
- unpublishedEntities.map((doc) => emitEvent(uid2, ENTRY_UNPUBLISH, doc))
4166
- );
4167
- return unpublishedEntitiesCount;
4575
+ const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
4576
+ return { count: unpublishedEntitiesCount };
4168
4577
  },
4169
4578
  async unpublish(id, uid2, opts = {}) {
4170
4579
  const populate = await buildDeepPopulate(uid2);
@@ -4189,16 +4598,20 @@ const documentManager = ({ strapi: strapi2 }) => {
4189
4598
  }
4190
4599
  return sumDraftCounts(document, uid2);
4191
4600
  },
4192
- async countManyEntriesDraftRelations(ids, uid2, locale) {
4601
+ async countManyEntriesDraftRelations(documentIds, uid2, locale) {
4193
4602
  const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
4194
4603
  if (!hasRelations) {
4195
4604
  return 0;
4196
4605
  }
4606
+ let localeFilter = {};
4607
+ if (locale) {
4608
+ localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
4609
+ }
4197
4610
  const entities = await strapi2.db.query(uid2).findMany({
4198
4611
  populate,
4199
4612
  where: {
4200
- id: { $in: ids },
4201
- ...locale ? { locale } : {}
4613
+ documentId: { $in: documentIds },
4614
+ ...localeFilter
4202
4615
  }
4203
4616
  });
4204
4617
  const totalNumberDraftRelations = entities.reduce(
@@ -4221,7 +4634,8 @@ const services = {
4221
4634
  permission,
4222
4635
  "populate-builder": populateBuilder$1,
4223
4636
  uid,
4224
- ...history.services ? history.services : {}
4637
+ ...history.services ? history.services : {},
4638
+ ...preview.services ? preview.services : {}
4225
4639
  };
4226
4640
  const index = () => {
4227
4641
  return {