@strapi/content-manager 0.0.0-experimental.e60ec1829240dae21c1e1d29076681c322288813 → 0.0.0-experimental.eba25ec571b091c6bde1104eb6c753debdf15462

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 (253) 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-BPvzFjM7.mjs → ComponentConfigurationPage-BaJMOQyq.mjs} +4 -4
  7. package/dist/_chunks/{ComponentConfigurationPage-BPvzFjM7.mjs.map → ComponentConfigurationPage-BaJMOQyq.mjs.map} +1 -1
  8. package/dist/_chunks/{ComponentConfigurationPage-DjWJdz6Y.js → ComponentConfigurationPage-N-CTtgQa.js} +4 -4
  9. package/dist/_chunks/{ComponentConfigurationPage-DjWJdz6Y.js.map → ComponentConfigurationPage-N-CTtgQa.js.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-Dmv83RlS.js → EditConfigurationPage-BHkjAbxH.js} +4 -4
  15. package/dist/_chunks/{EditConfigurationPage-Dmv83RlS.js.map → EditConfigurationPage-BHkjAbxH.js.map} +1 -1
  16. package/dist/_chunks/{EditConfigurationPage-DacbqQ_f.mjs → EditConfigurationPage-CKK-5LfX.mjs} +4 -4
  17. package/dist/_chunks/{EditConfigurationPage-DacbqQ_f.mjs.map → EditConfigurationPage-CKK-5LfX.mjs.map} +1 -1
  18. package/dist/_chunks/EditViewPage-B11aeMcf.mjs +254 -0
  19. package/dist/_chunks/EditViewPage-B11aeMcf.mjs.map +1 -0
  20. package/dist/_chunks/{EditViewPage-DvNpQkam.js → EditViewPage-QPUftxUd.js} +101 -52
  21. package/dist/_chunks/EditViewPage-QPUftxUd.js.map +1 -0
  22. package/dist/_chunks/{Field-6gvGdPBV.mjs → Field-Bj_RgtGo.mjs} +1077 -814
  23. package/dist/_chunks/Field-Bj_RgtGo.mjs.map +1 -0
  24. package/dist/_chunks/{Field-DmVKIAOo.js → Field-DUK83cfh.js} +1121 -859
  25. package/dist/_chunks/Field-DUK83cfh.js.map +1 -0
  26. package/dist/_chunks/{Form-CPZC9vWa.js → Form-DHmBRlHd.js} +66 -46
  27. package/dist/_chunks/Form-DHmBRlHd.js.map +1 -0
  28. package/dist/_chunks/{Form-DW6K1IH-.mjs → Form-DLMSoXV7.mjs} +66 -45
  29. package/dist/_chunks/Form-DLMSoXV7.mjs.map +1 -0
  30. package/dist/_chunks/{History-Dmr9fmUA.mjs → History-CfCSNlG9.mjs} +181 -144
  31. package/dist/_chunks/History-CfCSNlG9.mjs.map +1 -0
  32. package/dist/_chunks/{History-DeAPlvtv.js → History-Di3zm4HT.js} +181 -145
  33. package/dist/_chunks/History-Di3zm4HT.js.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-DhwvYcNv.mjs → ListConfigurationPage-0mtv_iqk.mjs} +67 -56
  35. package/dist/_chunks/ListConfigurationPage-0mtv_iqk.mjs.map +1 -0
  36. package/dist/_chunks/{ListConfigurationPage-DPCwW5Vr.js → ListConfigurationPage-Cq361KIt.js} +70 -60
  37. package/dist/_chunks/ListConfigurationPage-Cq361KIt.js.map +1 -0
  38. package/dist/_chunks/{ListViewPage-BtAwuYLE.mjs → ListViewPage-BxLVROX8.mjs} +175 -159
  39. package/dist/_chunks/ListViewPage-BxLVROX8.mjs.map +1 -0
  40. package/dist/_chunks/{ListViewPage-5ySZ-VUs.js → ListViewPage-DFDcG8gM.js} +178 -162
  41. package/dist/_chunks/ListViewPage-DFDcG8gM.js.map +1 -0
  42. package/dist/_chunks/{NoContentTypePage-DSPxnxxp.mjs → NoContentTypePage-BRfDd67_.mjs} +3 -3
  43. package/dist/_chunks/NoContentTypePage-BRfDd67_.mjs.map +1 -0
  44. package/dist/_chunks/{NoContentTypePage-DOC_yWOf.js → NoContentTypePage-BSyvnDZZ.js} +3 -3
  45. package/dist/_chunks/NoContentTypePage-BSyvnDZZ.js.map +1 -0
  46. package/dist/_chunks/{NoPermissionsPage-UWDC-1Tw.mjs → NoPermissionsPage-CV9V8KWa.mjs} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-UWDC-1Tw.mjs.map → NoPermissionsPage-CV9V8KWa.mjs.map} +1 -1
  48. package/dist/_chunks/{NoPermissionsPage-Dwu8rRJu.js → NoPermissionsPage-DyLphsn_.js} +2 -2
  49. package/dist/_chunks/{NoPermissionsPage-Dwu8rRJu.js.map → NoPermissionsPage-DyLphsn_.js.map} +1 -1
  50. package/dist/_chunks/Preview-C_B1nx3g.mjs +272 -0
  51. package/dist/_chunks/Preview-C_B1nx3g.mjs.map +1 -0
  52. package/dist/_chunks/Preview-D_3aO6Ly.js +291 -0
  53. package/dist/_chunks/Preview-D_3aO6Ly.js.map +1 -0
  54. package/dist/_chunks/{Relations-J8cscLlR.mjs → Relations-C6pwmDXh.mjs} +135 -89
  55. package/dist/_chunks/Relations-C6pwmDXh.mjs.map +1 -0
  56. package/dist/_chunks/{Relations-CgWtgnPe.js → Relations-Cne2AlrL.js} +138 -93
  57. package/dist/_chunks/Relations-Cne2AlrL.js.map +1 -0
  58. package/dist/_chunks/{en-MBPul9Su.mjs → en-DhFUjrNW.mjs} +37 -18
  59. package/dist/_chunks/{en-MBPul9Su.mjs.map → en-DhFUjrNW.mjs.map} +1 -1
  60. package/dist/_chunks/{en-C-V1_90f.js → en-Ic0kXjxB.js} +37 -18
  61. package/dist/_chunks/{en-C-V1_90f.js.map → en-Ic0kXjxB.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-CwRRo1V9.mjs → index-BpxR3En4.mjs} +1835 -824
  71. package/dist/_chunks/index-BpxR3En4.mjs.map +1 -0
  72. package/dist/_chunks/{index-C6AH2hEl.js → index-T-aWjbj2.js} +1788 -777
  73. package/dist/_chunks/index-T-aWjbj2.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-B_SXLhqf.js → layout-BEuNwv-F.js} +45 -29
  79. package/dist/_chunks/layout-BEuNwv-F.js.map +1 -0
  80. package/dist/_chunks/{layout-jIDzX0Fp.mjs → layout-DhMZ_lDx.mjs} +45 -27
  81. package/dist/_chunks/layout-DhMZ_lDx.mjs.map +1 -0
  82. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  83. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  84. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  85. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  86. package/dist/_chunks/{relations-CuvIgCqI.mjs → relations-BdnxoX6f.mjs} +6 -7
  87. package/dist/_chunks/relations-BdnxoX6f.mjs.map +1 -0
  88. package/dist/_chunks/{relations-iBMa_OFG.js → relations-kLcuobLk.js} +6 -7
  89. package/dist/_chunks/relations-kLcuobLk.js.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 +852 -436
  149. package/dist/server/index.js.map +1 -1
  150. package/dist/server/index.mjs +859 -443
  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.map +1 -1
  164. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  165. package/dist/server/src/history/services/utils.d.ts +4 -4
  166. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  167. package/dist/server/src/index.d.ts +21 -42
  168. package/dist/server/src/index.d.ts.map +1 -1
  169. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  170. package/dist/server/src/preview/constants.d.ts +2 -0
  171. package/dist/server/src/preview/constants.d.ts.map +1 -0
  172. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  173. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  174. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  175. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  176. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  177. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  178. package/dist/server/src/preview/index.d.ts +4 -0
  179. package/dist/server/src/preview/index.d.ts.map +1 -0
  180. package/dist/server/src/preview/routes/index.d.ts +8 -0
  181. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  182. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  183. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  184. package/dist/server/src/preview/services/index.d.ts +16 -0
  185. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  186. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  187. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  188. package/dist/server/src/preview/services/preview.d.ts +12 -0
  189. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  190. package/dist/server/src/preview/utils.d.ts +19 -0
  191. package/dist/server/src/preview/utils.d.ts.map +1 -0
  192. package/dist/server/src/register.d.ts.map +1 -1
  193. package/dist/server/src/routes/index.d.ts.map +1 -1
  194. package/dist/server/src/services/document-manager.d.ts +13 -12
  195. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  196. package/dist/server/src/services/document-metadata.d.ts +14 -35
  197. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  198. package/dist/server/src/services/index.d.ts +21 -42
  199. package/dist/server/src/services/index.d.ts.map +1 -1
  200. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  201. package/dist/server/src/services/utils/configuration/index.d.ts +2 -2
  202. package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
  203. package/dist/server/src/services/utils/populate.d.ts +8 -1
  204. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  205. package/dist/server/src/utils/index.d.ts +2 -0
  206. package/dist/server/src/utils/index.d.ts.map +1 -1
  207. package/dist/shared/contracts/collection-types.d.ts +17 -7
  208. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  209. package/dist/shared/contracts/index.d.ts +1 -0
  210. package/dist/shared/contracts/index.d.ts.map +1 -1
  211. package/dist/shared/contracts/preview.d.ts +27 -0
  212. package/dist/shared/contracts/preview.d.ts.map +1 -0
  213. package/dist/shared/contracts/relations.d.ts +2 -2
  214. package/dist/shared/contracts/relations.d.ts.map +1 -1
  215. package/dist/shared/index.js +4 -0
  216. package/dist/shared/index.js.map +1 -1
  217. package/dist/shared/index.mjs +4 -0
  218. package/dist/shared/index.mjs.map +1 -1
  219. package/package.json +19 -20
  220. package/dist/_chunks/CardDragPreview-DSVYodBX.js.map +0 -1
  221. package/dist/_chunks/CardDragPreview-ikSG4M46.mjs.map +0 -1
  222. package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
  223. package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
  224. package/dist/_chunks/EditViewPage-DDS6H9HO.mjs +0 -203
  225. package/dist/_chunks/EditViewPage-DDS6H9HO.mjs.map +0 -1
  226. package/dist/_chunks/EditViewPage-DvNpQkam.js.map +0 -1
  227. package/dist/_chunks/Field-6gvGdPBV.mjs.map +0 -1
  228. package/dist/_chunks/Field-DmVKIAOo.js.map +0 -1
  229. package/dist/_chunks/Form-CPZC9vWa.js.map +0 -1
  230. package/dist/_chunks/Form-DW6K1IH-.mjs.map +0 -1
  231. package/dist/_chunks/History-DeAPlvtv.js.map +0 -1
  232. package/dist/_chunks/History-Dmr9fmUA.mjs.map +0 -1
  233. package/dist/_chunks/ListConfigurationPage-DPCwW5Vr.js.map +0 -1
  234. package/dist/_chunks/ListConfigurationPage-DhwvYcNv.mjs.map +0 -1
  235. package/dist/_chunks/ListViewPage-5ySZ-VUs.js.map +0 -1
  236. package/dist/_chunks/ListViewPage-BtAwuYLE.mjs.map +0 -1
  237. package/dist/_chunks/NoContentTypePage-DOC_yWOf.js.map +0 -1
  238. package/dist/_chunks/NoContentTypePage-DSPxnxxp.mjs.map +0 -1
  239. package/dist/_chunks/Relations-CgWtgnPe.js.map +0 -1
  240. package/dist/_chunks/Relations-J8cscLlR.mjs.map +0 -1
  241. package/dist/_chunks/index-C6AH2hEl.js.map +0 -1
  242. package/dist/_chunks/index-CwRRo1V9.mjs.map +0 -1
  243. package/dist/_chunks/layout-B_SXLhqf.js.map +0 -1
  244. package/dist/_chunks/layout-jIDzX0Fp.mjs.map +0 -1
  245. package/dist/_chunks/relations-CuvIgCqI.mjs.map +0 -1
  246. package/dist/_chunks/relations-iBMa_OFG.js.map +0 -1
  247. package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
  248. package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
  249. package/dist/_chunks/urls-DzZya_gm.js +0 -6
  250. package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
  251. package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
  252. package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
  253. 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
@@ -199,7 +199,9 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
199
199
  return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
200
200
  };
201
201
  const localesService = strapi2.plugin("i18n")?.service("locales");
202
+ const i18nContentTypeService = strapi2.plugin("i18n")?.service("content-types");
202
203
  const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
204
+ const isLocalizedContentType = (model) => i18nContentTypeService ? i18nContentTypeService.isLocalizedContentType(model) : false;
203
205
  const getLocaleDictionary = async () => {
204
206
  if (!localesService)
205
207
  return {};
@@ -226,31 +228,53 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
226
228
  const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
227
229
  return documentMetadataService.getStatus(document, meta.availableStatus);
228
230
  };
229
- 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) => {
230
243
  const model = strapi2.getModel(uid2);
231
244
  const attributes = Object.entries(model.attributes);
245
+ const fieldSelector = useDatabaseSyntax ? "select" : "fields";
232
246
  return attributes.reduce((acc, [attributeName, attribute]) => {
233
247
  switch (attribute.type) {
234
248
  case "relation": {
249
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
250
+ if (isMorphRelation) {
251
+ break;
252
+ }
235
253
  const isVisible2 = strapiUtils.contentTypes.isVisibleAttribute(model, attributeName);
236
254
  if (isVisible2) {
237
- acc[attributeName] = { fields: ["documentId", "locale", "publishedAt"] };
255
+ acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
238
256
  }
239
257
  break;
240
258
  }
241
259
  case "media": {
242
- acc[attributeName] = { fields: ["id"] };
260
+ acc[attributeName] = { [fieldSelector]: ["id"] };
243
261
  break;
244
262
  }
245
263
  case "component": {
246
264
  const populate = getDeepPopulate2(attribute.component);
247
- acc[attributeName] = { populate };
265
+ acc[attributeName] = {
266
+ populate,
267
+ [fieldSelector]: getComponentFields(attribute.component)
268
+ };
248
269
  break;
249
270
  }
250
271
  case "dynamiczone": {
251
272
  const populatedComponents = (attribute.components || []).reduce(
252
273
  (acc2, componentUID) => {
253
- acc2[componentUID] = { populate: getDeepPopulate2(componentUID) };
274
+ acc2[componentUID] = {
275
+ populate: getDeepPopulate2(componentUID),
276
+ [fieldSelector]: getComponentFields(componentUID)
277
+ };
254
278
  return acc2;
255
279
  },
256
280
  {}
@@ -312,6 +336,7 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
312
336
  getRelationRestoreValue,
313
337
  getMediaRestoreValue,
314
338
  getDefaultLocale,
339
+ isLocalizedContentType,
315
340
  getLocaleDictionary,
316
341
  getRetentionDays,
317
342
  getVersionStatus,
@@ -334,7 +359,13 @@ const createHistoryService = ({ strapi: strapi2 }) => {
334
359
  });
335
360
  },
336
361
  async findVersionsPage(params) {
337
- const locale = params.query.locale || await serviceUtils.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
+ }
338
369
  const [{ results, pagination }, localeDictionary] = await Promise.all([
339
370
  query.findPage({
340
371
  ...params.query,
@@ -356,7 +387,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
356
387
  const attributeValue = entry.data[attributeKey];
357
388
  const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
358
389
  if (attributeSchema.type === "media") {
359
- const permissionChecker2 = getService$1("permission-checker").create({
390
+ const permissionChecker2 = getService$2("permission-checker").create({
360
391
  userAbility: params.state.userAbility,
361
392
  model: "plugin::upload.file"
362
393
  });
@@ -379,7 +410,12 @@ const createHistoryService = ({ strapi: strapi2 }) => {
379
410
  if (userToPopulate == null) {
380
411
  return null;
381
412
  }
382
- 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
+ });
383
419
  })
384
420
  );
385
421
  return {
@@ -392,7 +428,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
392
428
  [attributeKey]: adminUsers
393
429
  };
394
430
  }
395
- const permissionChecker2 = getService$1("permission-checker").create({
431
+ const permissionChecker2 = getService$2("permission-checker").create({
396
432
  userAbility: params.state.userAbility,
397
433
  model: attributeSchema.target
398
434
  });
@@ -490,13 +526,47 @@ const createHistoryService = ({ strapi: strapi2 }) => {
490
526
  }
491
527
  };
492
528
  };
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;
543
+ };
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
+ };
493
565
  const createLifecyclesService = ({ strapi: strapi2 }) => {
494
566
  const state = {
495
567
  deleteExpiredJob: null,
496
568
  isInitialized: false
497
569
  };
498
- const query = strapi2.db.query(HISTORY_VERSION_UID);
499
- const historyService = getService(strapi2, "history");
500
570
  const serviceUtils = createServiceUtils({ strapi: strapi2 });
501
571
  return {
502
572
  async bootstrap() {
@@ -504,65 +574,62 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
504
574
  return;
505
575
  }
506
576
  strapi2.documents.use(async (context, next) => {
507
- if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
508
- return next();
509
- }
510
- if (context.action !== "create" && context.action !== "update" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
511
- return next();
512
- }
513
- const contentTypeUid = context.contentType.uid;
514
- if (!contentTypeUid.startsWith("api::")) {
515
- return next();
516
- }
517
577
  const result = await next();
518
- const documentContext = context.action === "create" ? { documentId: result.documentId, locale: context.params?.locale } : { documentId: context.params.documentId, locale: context.params?.locale };
578
+ if (!shouldCreateHistoryVersion(context)) {
579
+ return result;
580
+ }
581
+ const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
519
582
  const defaultLocale = await serviceUtils.getDefaultLocale();
520
- const locale = documentContext.locale || defaultLocale;
521
- const document = await strapi2.documents(contentTypeUid).findOne({
522
- documentId: documentContext.documentId,
523
- locale,
524
- populate: serviceUtils.getDeepPopulate(contentTypeUid)
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
+ )
525
602
  });
526
- const status = await serviceUtils.getVersionStatus(contentTypeUid, document);
527
- const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
528
- const componentsSchemas = Object.keys(
529
- attributesSchema
530
- ).reduce((currentComponentSchemas, key) => {
531
- const fieldSchema = attributesSchema[key];
532
- if (fieldSchema.type === "component") {
533
- const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
534
- return {
535
- ...currentComponentSchemas,
536
- [fieldSchema.component]: componentSchema
537
- };
538
- }
539
- return currentComponentSchemas;
540
- }, {});
541
603
  await strapi2.db.transaction(async ({ onCommit }) => {
542
- onCommit(() => {
543
- historyService.createVersion({
544
- contentType: contentTypeUid,
545
- data: fp.omit(FIELDS_TO_IGNORE, document),
546
- schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
547
- componentsSchemas,
548
- relatedDocumentId: documentContext.documentId,
549
- locale,
550
- status
551
- });
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
+ }
552
616
  });
553
617
  });
554
618
  return result;
555
619
  });
556
- const retentionDays = serviceUtils.getRetentionDays();
557
- state.deleteExpiredJob = nodeSchedule.scheduleJob("0 0 * * *", () => {
558
- const retentionDaysInMilliseconds = retentionDays * 24 * 60 * 60 * 1e3;
620
+ state.deleteExpiredJob = nodeSchedule.scheduleJob("historyDaily", "0 0 * * *", () => {
621
+ const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
559
622
  const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
560
- query.deleteMany({
623
+ strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
561
624
  where: {
562
625
  created_at: {
563
- $lt: expirationDate.toISOString()
626
+ $lt: expirationDate
564
627
  }
565
628
  }
629
+ }).catch((error) => {
630
+ if (error instanceof Error) {
631
+ strapi2.log.error("Error deleting expired history versions", error.message);
632
+ }
566
633
  });
567
634
  });
568
635
  state.isInitialized = true;
@@ -574,17 +641,17 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
574
641
  }
575
642
  };
576
643
  };
577
- const services$1 = {
644
+ const services$2 = {
578
645
  history: createHistoryService,
579
646
  lifecycles: createLifecyclesService
580
647
  };
581
- const info = { pluginName: "content-manager", type: "admin" };
648
+ const info$1 = { pluginName: "content-manager", type: "admin" };
582
649
  const historyVersionRouter = {
583
650
  type: "admin",
584
651
  routes: [
585
652
  {
586
653
  method: "GET",
587
- info,
654
+ info: info$1,
588
655
  path: "/history-versions",
589
656
  handler: "history-version.findMany",
590
657
  config: {
@@ -593,7 +660,7 @@ const historyVersionRouter = {
593
660
  },
594
661
  {
595
662
  method: "PUT",
596
- info,
663
+ info: info$1,
597
664
  path: "/history-versions/:versionId/restore",
598
665
  handler: "history-version.restoreVersion",
599
666
  config: {
@@ -602,7 +669,7 @@ const historyVersionRouter = {
602
669
  }
603
670
  ]
604
671
  };
605
- const routes$1 = {
672
+ const routes$2 = {
606
673
  "history-version": historyVersionRouter
607
674
  };
608
675
  const historyVersion = {
@@ -649,21 +716,21 @@ const historyVersion = {
649
716
  }
650
717
  }
651
718
  };
652
- const getFeature = () => {
719
+ const getFeature$1 = () => {
653
720
  if (strapi.ee.features.isEnabled("cms-content-history")) {
654
721
  return {
655
722
  register({ strapi: strapi2 }) {
656
723
  strapi2.get("models").add(historyVersion);
657
724
  },
658
725
  bootstrap({ strapi: strapi2 }) {
659
- getService(strapi2, "lifecycles").bootstrap();
726
+ getService$1(strapi2, "lifecycles").bootstrap();
660
727
  },
661
728
  destroy({ strapi: strapi2 }) {
662
- getService(strapi2, "lifecycles").destroy();
729
+ getService$1(strapi2, "lifecycles").destroy();
663
730
  },
664
- controllers: controllers$1,
665
- services: services$1,
666
- routes: routes$1
731
+ controllers: controllers$2,
732
+ services: services$2,
733
+ routes: routes$2
667
734
  };
668
735
  }
669
736
  return {
@@ -672,9 +739,205 @@ const getFeature = () => {
672
739
  }
673
740
  };
674
741
  };
675
- const history = getFeature();
742
+ const history = getFeature$1();
743
+ const FEATURE_ID = "preview";
744
+ const info = { pluginName: "content-manager", type: "admin" };
745
+ const previewRouter = {
746
+ type: "admin",
747
+ routes: [
748
+ {
749
+ method: "GET",
750
+ info,
751
+ path: "/preview/url/:contentType",
752
+ handler: "preview.getPreviewUrl",
753
+ config: {
754
+ policies: ["admin::isAuthenticatedAdmin"]
755
+ }
756
+ }
757
+ ]
758
+ };
759
+ const routes$1 = {
760
+ preview: previewRouter
761
+ };
762
+ function getService(strapi2, name) {
763
+ return strapi2.service(`plugin::content-manager.${name}`);
764
+ }
765
+ const getPreviewUrlSchema = yup__namespace.object().shape({
766
+ // Will be undefined for single types
767
+ documentId: yup__namespace.string(),
768
+ locale: yup__namespace.string().nullable(),
769
+ status: yup__namespace.string()
770
+ }).required();
771
+ const validatePreviewUrl = async (strapi2, uid2, params) => {
772
+ await strapiUtils.validateYupSchema(getPreviewUrlSchema)(params);
773
+ const newParams = fp.pick(["documentId", "locale", "status"], params);
774
+ const model = strapi2.getModel(uid2);
775
+ if (!model || model.modelType !== "contentType") {
776
+ throw new strapiUtils.errors.ValidationError("Invalid content type");
777
+ }
778
+ const isSingleType = model?.kind === "singleType";
779
+ if (!isSingleType && !params.documentId) {
780
+ throw new strapiUtils.errors.ValidationError("documentId is required for Collection Types");
781
+ }
782
+ if (isSingleType) {
783
+ const doc = await strapi2.documents(uid2).findFirst();
784
+ if (!doc) {
785
+ throw new strapiUtils.errors.NotFoundError("Document not found");
786
+ }
787
+ newParams.documentId = doc?.documentId;
788
+ }
789
+ if (!newParams.status) {
790
+ const isDPEnabled = model?.options?.draftAndPublish;
791
+ newParams.status = isDPEnabled ? "draft" : "published";
792
+ }
793
+ return newParams;
794
+ };
795
+ const createPreviewController = () => {
796
+ return {
797
+ /**
798
+ * Transforms an entry into a preview URL, so that it can be previewed
799
+ * in the Content Manager.
800
+ */
801
+ async getPreviewUrl(ctx) {
802
+ const uid2 = ctx.params.contentType;
803
+ const query = ctx.request.query;
804
+ const params = await validatePreviewUrl(strapi, uid2, query);
805
+ const previewService = getService(strapi, "preview");
806
+ const url = await previewService.getPreviewUrl(uid2, params);
807
+ if (!url) {
808
+ ctx.status = 204;
809
+ }
810
+ return {
811
+ data: { url }
812
+ };
813
+ }
814
+ };
815
+ };
816
+ const controllers$1 = {
817
+ preview: createPreviewController
818
+ /**
819
+ * Casting is needed because the types aren't aware that Strapi supports
820
+ * passing a controller factory as the value, instead of a controller object directly
821
+ */
822
+ };
823
+ const createPreviewService = ({ strapi: strapi2 }) => {
824
+ const config = getService(strapi2, "preview-config");
825
+ return {
826
+ async getPreviewUrl(uid2, params) {
827
+ const handler = config.getPreviewHandler();
828
+ try {
829
+ return handler(uid2, params);
830
+ } catch (error) {
831
+ strapi2.log.error(`Failed to get preview URL: ${error}`);
832
+ throw new strapiUtils.errors.ApplicationError("Failed to get preview URL");
833
+ }
834
+ return;
835
+ }
836
+ };
837
+ };
838
+ const extendMiddlewareConfiguration = (middleware = { name: "", config: {} }) => {
839
+ const middlewares = strapi.config.get("middlewares");
840
+ const configuredMiddlewares = middlewares.map((currentMiddleware) => {
841
+ if (currentMiddleware === middleware.name) {
842
+ return middleware;
843
+ }
844
+ if (currentMiddleware.name === middleware.name) {
845
+ return fp.mergeWith(
846
+ (objValue, srcValue) => {
847
+ if (Array.isArray(objValue)) {
848
+ return objValue.concat(srcValue);
849
+ }
850
+ return void 0;
851
+ },
852
+ currentMiddleware,
853
+ middleware
854
+ );
855
+ }
856
+ return currentMiddleware;
857
+ });
858
+ strapi.config.set("middlewares", configuredMiddlewares);
859
+ };
860
+ const createPreviewConfigService = ({ strapi: strapi2 }) => {
861
+ return {
862
+ register() {
863
+ if (!this.isEnabled()) {
864
+ return;
865
+ }
866
+ const config = strapi2.config.get("admin.preview");
867
+ if (config.config?.allowedOrigins) {
868
+ extendMiddlewareConfiguration({
869
+ name: "strapi::security",
870
+ config: {
871
+ contentSecurityPolicy: {
872
+ directives: {
873
+ "frame-src": config.config.allowedOrigins
874
+ }
875
+ }
876
+ }
877
+ });
878
+ }
879
+ },
880
+ isEnabled() {
881
+ const config = strapi2.config.get("admin.preview");
882
+ if (!config) {
883
+ return false;
884
+ }
885
+ return config?.enabled ?? true;
886
+ },
887
+ /**
888
+ * Validate if the configuration is valid
889
+ */
890
+ validate() {
891
+ if (!this.isEnabled()) {
892
+ return;
893
+ }
894
+ const handler = this.getPreviewHandler();
895
+ if (typeof handler !== "function") {
896
+ throw new strapiUtils.errors.ValidationError(
897
+ "Preview configuration is invalid. Handler must be a function"
898
+ );
899
+ }
900
+ },
901
+ /**
902
+ * Utility to get the preview handler from the configuration
903
+ */
904
+ getPreviewHandler() {
905
+ const config = strapi2.config.get("admin.preview");
906
+ const emptyHandler = () => {
907
+ return void 0;
908
+ };
909
+ if (!this.isEnabled()) {
910
+ return emptyHandler;
911
+ }
912
+ return config?.config?.handler || emptyHandler;
913
+ }
914
+ };
915
+ };
916
+ const services$1 = {
917
+ preview: createPreviewService,
918
+ "preview-config": createPreviewConfigService
919
+ };
920
+ const getFeature = () => {
921
+ if (!strapi.features.future.isEnabled(FEATURE_ID)) {
922
+ return {};
923
+ }
924
+ return {
925
+ register() {
926
+ const config = getService(strapi, "preview-config");
927
+ config.validate();
928
+ config.register();
929
+ },
930
+ bootstrap() {
931
+ },
932
+ routes: routes$1,
933
+ controllers: controllers$1,
934
+ services: services$1
935
+ };
936
+ };
937
+ const preview = getFeature();
676
938
  const register = async ({ strapi: strapi2 }) => {
677
939
  await history.register?.({ strapi: strapi2 });
940
+ await preview.register?.({ strapi: strapi2 });
678
941
  };
679
942
  const ALLOWED_WEBHOOK_EVENTS = {
680
943
  ENTRY_PUBLISH: "entry.publish",
@@ -684,11 +947,12 @@ const bootstrap = async () => {
684
947
  Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
685
948
  strapi.get("webhookStore").addAllowedEvent(key, value);
686
949
  });
687
- getService$1("field-sizes").setCustomFieldInputSizes();
688
- await getService$1("components").syncConfigurations();
689
- await getService$1("content-types").syncConfigurations();
690
- await getService$1("permission").registerPermissions();
950
+ getService$2("field-sizes").setCustomFieldInputSizes();
951
+ await getService$2("components").syncConfigurations();
952
+ await getService$2("content-types").syncConfigurations();
953
+ await getService$2("permission").registerPermissions();
691
954
  await history.bootstrap?.({ strapi });
955
+ await preview.bootstrap?.({ strapi });
692
956
  };
693
957
  const destroy = async ({ strapi: strapi2 }) => {
694
958
  await history.destroy?.({ strapi: strapi2 });
@@ -1178,7 +1442,8 @@ const admin = {
1178
1442
  };
1179
1443
  const routes = {
1180
1444
  admin,
1181
- ...history.routes ? history.routes : {}
1445
+ ...history.routes ? history.routes : {},
1446
+ ...preview.routes ? preview.routes : {}
1182
1447
  };
1183
1448
  const hasPermissionsSchema = strapiUtils.yup.object({
1184
1449
  actions: strapiUtils.yup.array().of(strapiUtils.yup.string()),
@@ -1189,6 +1454,11 @@ const { createPolicy } = strapiUtils.policy;
1189
1454
  const hasPermissions = createPolicy({
1190
1455
  name: "plugin::content-manager.hasPermissions",
1191
1456
  validator: validateHasPermissionsInput,
1457
+ /**
1458
+ * NOTE: Action aliases are currently not checked at this level (policy).
1459
+ * This is currently the intended behavior to avoid changing the behavior of API related permissions.
1460
+ * If you want to add support for it, please create a dedicated RFC with a list of potential side effect this could have.
1461
+ */
1192
1462
  handler(ctx, config = {}) {
1193
1463
  const { actions = [], hasAtLeastOne = false } = config;
1194
1464
  const { userAbility } = ctx.state;
@@ -1430,7 +1700,7 @@ const createMetadasSchema = (schema) => {
1430
1700
  if (!value) {
1431
1701
  return strapiUtils.yup.string();
1432
1702
  }
1433
- const targetSchema = getService$1("content-types").findContentType(
1703
+ const targetSchema = getService$2("content-types").findContentType(
1434
1704
  schema.attributes[key].targetModel
1435
1705
  );
1436
1706
  if (!targetSchema) {
@@ -1478,7 +1748,7 @@ const { PaginationError, ValidationError } = strapiUtils.errors;
1478
1748
  const TYPES = ["singleType", "collectionType"];
1479
1749
  const kindSchema = strapiUtils.yup.string().oneOf(TYPES).nullable();
1480
1750
  const bulkActionInputSchema = strapiUtils.yup.object({
1481
- ids: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
1751
+ documentIds: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
1482
1752
  }).required();
1483
1753
  const generateUIDInputSchema = strapiUtils.yup.object({
1484
1754
  contentTypeUID: strapiUtils.yup.string().required(),
@@ -1577,22 +1847,56 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1577
1847
  }
1578
1848
  }, body);
1579
1849
  };
1580
- const getDocumentLocaleAndStatus = (request) => {
1581
- const { locale, status, ...rest } = request || {};
1582
- if (!fp.isNil(locale) && typeof locale !== "string") {
1583
- throw new strapiUtils.errors.ValidationError(`Invalid locale: ${locale}`);
1584
- }
1585
- if (!fp.isNil(status) && !["draft", "published"].includes(status)) {
1586
- throw new strapiUtils.errors.ValidationError(`Invalid status: ${status}`);
1850
+ const singleLocaleSchema = strapiUtils.yup.string().nullable();
1851
+ const multipleLocaleSchema = strapiUtils.yup.lazy(
1852
+ (value) => Array.isArray(value) ? strapiUtils.yup.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1853
+ );
1854
+ const statusSchema = strapiUtils.yup.mixed().oneOf(["draft", "published"], "Invalid status");
1855
+ const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
1856
+ const { allowMultipleLocales } = opts;
1857
+ const { locale, status: providedStatus, ...rest } = request || {};
1858
+ const defaultStatus = strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
1859
+ const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
1860
+ const schema = strapiUtils.yup.object().shape({
1861
+ locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1862
+ status: statusSchema
1863
+ });
1864
+ try {
1865
+ await strapiUtils.validateYupSchema(schema, { strict: true, abortEarly: false })(request);
1866
+ return { locale, status, ...rest };
1867
+ } catch (error) {
1868
+ throw new strapiUtils.errors.ValidationError(`Validation error: ${error.message}`);
1587
1869
  }
1588
- return { locale, status, ...rest };
1870
+ };
1871
+ const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
1872
+ const documentMetadata2 = getService$2("document-metadata");
1873
+ const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
1874
+ let {
1875
+ meta: { availableLocales, availableStatus }
1876
+ } = serviceOutput;
1877
+ const metadataSanitizer = permissionChecker2.sanitizeOutput;
1878
+ availableLocales = await strapiUtils.async.map(
1879
+ availableLocales,
1880
+ async (localeDocument) => metadataSanitizer(localeDocument)
1881
+ );
1882
+ availableStatus = await strapiUtils.async.map(
1883
+ availableStatus,
1884
+ async (statusDocument) => metadataSanitizer(statusDocument)
1885
+ );
1886
+ return {
1887
+ ...serviceOutput,
1888
+ meta: {
1889
+ availableLocales,
1890
+ availableStatus
1891
+ }
1892
+ };
1589
1893
  };
1590
1894
  const createDocument = async (ctx, opts) => {
1591
1895
  const { userAbility, user } = ctx.state;
1592
1896
  const { model } = ctx.params;
1593
1897
  const { body } = ctx.request;
1594
- const documentManager2 = getService$1("document-manager");
1595
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1898
+ const documentManager2 = getService$2("document-manager");
1899
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1596
1900
  if (permissionChecker2.cannot.create()) {
1597
1901
  throw new strapiUtils.errors.ForbiddenError();
1598
1902
  }
@@ -1600,7 +1904,7 @@ const createDocument = async (ctx, opts) => {
1600
1904
  const setCreator = strapiUtils.setCreatorFields({ user });
1601
1905
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1602
1906
  const sanitizedBody = await sanitizeFn(body);
1603
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(body);
1907
+ const { locale, status } = await getDocumentLocaleAndStatus(body, model);
1604
1908
  return documentManager2.create(model, {
1605
1909
  data: sanitizedBody,
1606
1910
  locale,
@@ -1612,14 +1916,14 @@ const updateDocument = async (ctx, opts) => {
1612
1916
  const { userAbility, user } = ctx.state;
1613
1917
  const { id, model } = ctx.params;
1614
1918
  const { body } = ctx.request;
1615
- const documentManager2 = getService$1("document-manager");
1616
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1919
+ const documentManager2 = getService$2("document-manager");
1920
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1617
1921
  if (permissionChecker2.cannot.update()) {
1618
1922
  throw new strapiUtils.errors.ForbiddenError();
1619
1923
  }
1620
1924
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1621
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1622
- const { locale } = getDocumentLocaleAndStatus(body);
1925
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
1926
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1623
1927
  const [documentVersion, documentExists] = await Promise.all([
1624
1928
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1625
1929
  documentManager2.exists(model, id)
@@ -1635,7 +1939,7 @@ const updateDocument = async (ctx, opts) => {
1635
1939
  throw new strapiUtils.errors.ForbiddenError();
1636
1940
  }
1637
1941
  const pickPermittedFields = documentVersion ? permissionChecker2.sanitizeUpdateInput(documentVersion) : permissionChecker2.sanitizeCreateInput;
1638
- const setCreator = strapiUtils.setCreatorFields({ user, isEdition: true });
1942
+ const setCreator = documentVersion ? strapiUtils.setCreatorFields({ user, isEdition: true }) : strapiUtils.setCreatorFields({ user });
1639
1943
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1640
1944
  const sanitizedBody = await sanitizeFn(body);
1641
1945
  return documentManager2.update(documentVersion?.documentId || id, model, {
@@ -1649,15 +1953,15 @@ const collectionTypes = {
1649
1953
  const { userAbility } = ctx.state;
1650
1954
  const { model } = ctx.params;
1651
1955
  const { query } = ctx.request;
1652
- const documentMetadata2 = getService$1("document-metadata");
1653
- const documentManager2 = getService$1("document-manager");
1654
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1956
+ const documentMetadata2 = getService$2("document-metadata");
1957
+ const documentManager2 = getService$2("document-manager");
1958
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1655
1959
  if (permissionChecker2.cannot.read()) {
1656
1960
  return ctx.forbidden();
1657
1961
  }
1658
1962
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1659
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1660
- const { locale, status } = getDocumentLocaleAndStatus(query);
1963
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1964
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
1661
1965
  const { results: documents, pagination } = await documentManager2.findPage(
1662
1966
  { ...permissionQuery, populate, locale, status },
1663
1967
  model
@@ -1685,15 +1989,14 @@ const collectionTypes = {
1685
1989
  async findOne(ctx) {
1686
1990
  const { userAbility } = ctx.state;
1687
1991
  const { model, id } = ctx.params;
1688
- const documentManager2 = getService$1("document-manager");
1689
- const documentMetadata2 = getService$1("document-metadata");
1690
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1992
+ const documentManager2 = getService$2("document-manager");
1993
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1691
1994
  if (permissionChecker2.cannot.read()) {
1692
1995
  return ctx.forbidden();
1693
1996
  }
1694
1997
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1695
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1696
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
1998
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1999
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
1697
2000
  const version = await documentManager2.findOne(id, model, {
1698
2001
  populate,
1699
2002
  locale,
@@ -1704,9 +2007,11 @@ const collectionTypes = {
1704
2007
  if (!exists) {
1705
2008
  return ctx.notFound();
1706
2009
  }
1707
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
2010
+ const { meta } = await formatDocumentWithMetadata(
2011
+ permissionChecker2,
1708
2012
  model,
1709
- { id, locale, publishedAt: null },
2013
+ // @ts-expect-error TODO: fix
2014
+ { documentId: id, locale, publishedAt: null },
1710
2015
  { availableLocales: true, availableStatus: false }
1711
2016
  );
1712
2017
  ctx.body = { data: {}, meta };
@@ -1716,20 +2021,19 @@ const collectionTypes = {
1716
2021
  return ctx.forbidden();
1717
2022
  }
1718
2023
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
1719
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2024
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1720
2025
  },
1721
2026
  async create(ctx) {
1722
2027
  const { userAbility } = ctx.state;
1723
2028
  const { model } = ctx.params;
1724
- const documentMetadata2 = getService$1("document-metadata");
1725
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2029
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1726
2030
  const [totalEntries, document] = await Promise.all([
1727
2031
  strapi.db.query(model).count(),
1728
2032
  createDocument(ctx)
1729
2033
  ]);
1730
2034
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
1731
2035
  ctx.status = 201;
1732
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
2036
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1733
2037
  // Empty metadata as it's not relevant for a new document
1734
2038
  availableLocales: false,
1735
2039
  availableStatus: false
@@ -1743,25 +2047,23 @@ const collectionTypes = {
1743
2047
  async update(ctx) {
1744
2048
  const { userAbility } = ctx.state;
1745
2049
  const { model } = ctx.params;
1746
- const documentMetadata2 = getService$1("document-metadata");
1747
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2050
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1748
2051
  const updatedVersion = await updateDocument(ctx);
1749
2052
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1750
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedVersion);
2053
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
1751
2054
  },
1752
2055
  async clone(ctx) {
1753
2056
  const { userAbility, user } = ctx.state;
1754
2057
  const { model, sourceId: id } = ctx.params;
1755
2058
  const { body } = ctx.request;
1756
- const documentManager2 = getService$1("document-manager");
1757
- const documentMetadata2 = getService$1("document-metadata");
1758
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2059
+ const documentManager2 = getService$2("document-manager");
2060
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1759
2061
  if (permissionChecker2.cannot.create()) {
1760
2062
  return ctx.forbidden();
1761
2063
  }
1762
2064
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1763
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1764
- const { locale } = getDocumentLocaleAndStatus(body);
2065
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2066
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1765
2067
  const document = await documentManager2.findOne(id, model, {
1766
2068
  populate,
1767
2069
  locale,
@@ -1777,7 +2079,7 @@ const collectionTypes = {
1777
2079
  const sanitizedBody = await sanitizeFn(body);
1778
2080
  const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
1779
2081
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
1780
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
2082
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1781
2083
  // Empty metadata as it's not relevant for a new document
1782
2084
  availableLocales: false,
1783
2085
  availableStatus: false
@@ -1799,14 +2101,14 @@ const collectionTypes = {
1799
2101
  async delete(ctx) {
1800
2102
  const { userAbility } = ctx.state;
1801
2103
  const { id, model } = ctx.params;
1802
- const documentManager2 = getService$1("document-manager");
1803
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2104
+ const documentManager2 = getService$2("document-manager");
2105
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1804
2106
  if (permissionChecker2.cannot.delete()) {
1805
2107
  return ctx.forbidden();
1806
2108
  }
1807
2109
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1808
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1809
- const { locale } = getDocumentLocaleAndStatus(ctx.query);
2110
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2111
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
1810
2112
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1811
2113
  if (documentLocales.length === 0) {
1812
2114
  return ctx.notFound();
@@ -1827,44 +2129,75 @@ const collectionTypes = {
1827
2129
  const { userAbility } = ctx.state;
1828
2130
  const { id, model } = ctx.params;
1829
2131
  const { body } = ctx.request;
1830
- const documentManager2 = getService$1("document-manager");
1831
- const documentMetadata2 = getService$1("document-metadata");
1832
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2132
+ const documentManager2 = getService$2("document-manager");
2133
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1833
2134
  if (permissionChecker2.cannot.publish()) {
1834
2135
  return ctx.forbidden();
1835
2136
  }
1836
2137
  const publishedDocument = await strapi.db.transaction(async () => {
1837
2138
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1838
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1839
- const document = id ? await updateDocument(ctx, { populate }) : await createDocument(ctx, { populate });
2139
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
2140
+ let document;
2141
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2142
+ const isCreate = fp.isNil(id);
2143
+ if (isCreate) {
2144
+ if (permissionChecker2.cannot.create()) {
2145
+ throw new strapiUtils.errors.ForbiddenError();
2146
+ }
2147
+ document = await createDocument(ctx, { populate });
2148
+ }
2149
+ const isUpdate = !isCreate;
2150
+ if (isUpdate) {
2151
+ const documentExists = documentManager2.exists(model, id);
2152
+ if (!documentExists) {
2153
+ throw new strapiUtils.errors.NotFoundError("Document not found");
2154
+ }
2155
+ document = await documentManager2.findOne(id, model, { populate, locale });
2156
+ if (!document) {
2157
+ if (permissionChecker2.cannot.create({ locale }) || permissionChecker2.cannot.publish({ locale })) {
2158
+ throw new strapiUtils.errors.ForbiddenError();
2159
+ }
2160
+ document = await updateDocument(ctx);
2161
+ } else if (permissionChecker2.can.update(document)) {
2162
+ await updateDocument(ctx);
2163
+ }
2164
+ }
1840
2165
  if (permissionChecker2.cannot.publish(document)) {
1841
2166
  throw new strapiUtils.errors.ForbiddenError();
1842
2167
  }
1843
- const { locale } = getDocumentLocaleAndStatus(body);
1844
- return documentManager2.publish(document.documentId, model, {
2168
+ const publishResult = await documentManager2.publish(document.documentId, model, {
1845
2169
  locale
1846
2170
  // TODO: Allow setting creator fields on publish
1847
2171
  // data: setCreatorFields({ user, isEdition: true })({}),
1848
2172
  });
2173
+ if (!publishResult || publishResult.length === 0) {
2174
+ throw new strapiUtils.errors.NotFoundError("Document not found or already published.");
2175
+ }
2176
+ return publishResult[0];
1849
2177
  });
1850
2178
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
1851
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2179
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1852
2180
  },
1853
2181
  async bulkPublish(ctx) {
1854
2182
  const { userAbility } = ctx.state;
1855
2183
  const { model } = ctx.params;
1856
2184
  const { body } = ctx.request;
1857
- const { ids } = body;
2185
+ const { documentIds } = body;
1858
2186
  await validateBulkActionInput(body);
1859
- const documentManager2 = getService$1("document-manager");
1860
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2187
+ const documentManager2 = getService$2("document-manager");
2188
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1861
2189
  if (permissionChecker2.cannot.publish()) {
1862
2190
  return ctx.forbidden();
1863
2191
  }
1864
2192
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1865
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1866
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1867
- const entities = await Promise.all(entityPromises);
2193
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
2194
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
2195
+ allowMultipleLocales: true
2196
+ });
2197
+ const entityPromises = documentIds.map(
2198
+ (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
2199
+ );
2200
+ const entities = (await Promise.all(entityPromises)).flat();
1868
2201
  for (const entity of entities) {
1869
2202
  if (!entity) {
1870
2203
  return ctx.notFound();
@@ -1873,24 +2206,27 @@ const collectionTypes = {
1873
2206
  return ctx.forbidden();
1874
2207
  }
1875
2208
  }
1876
- const { count } = await documentManager2.publishMany(entities, model);
2209
+ const count = await documentManager2.publishMany(model, documentIds, locale);
1877
2210
  ctx.body = { count };
1878
2211
  },
1879
2212
  async bulkUnpublish(ctx) {
1880
2213
  const { userAbility } = ctx.state;
1881
2214
  const { model } = ctx.params;
1882
2215
  const { body } = ctx.request;
1883
- const { ids } = body;
2216
+ const { documentIds } = body;
1884
2217
  await validateBulkActionInput(body);
1885
- const documentManager2 = getService$1("document-manager");
1886
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2218
+ const documentManager2 = getService$2("document-manager");
2219
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1887
2220
  if (permissionChecker2.cannot.unpublish()) {
1888
2221
  return ctx.forbidden();
1889
2222
  }
1890
- const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1891
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1892
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1893
- const entities = await Promise.all(entityPromises);
2223
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
2224
+ allowMultipleLocales: true
2225
+ });
2226
+ const entityPromises = documentIds.map(
2227
+ (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
2228
+ );
2229
+ const entities = (await Promise.all(entityPromises)).flat();
1894
2230
  for (const entity of entities) {
1895
2231
  if (!entity) {
1896
2232
  return ctx.notFound();
@@ -1899,7 +2235,8 @@ const collectionTypes = {
1899
2235
  return ctx.forbidden();
1900
2236
  }
1901
2237
  }
1902
- const { count } = await documentManager2.unpublishMany(entities, model);
2238
+ const entitiesIds = entities.map((document) => document.documentId);
2239
+ const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
1903
2240
  ctx.body = { count };
1904
2241
  },
1905
2242
  async unpublish(ctx) {
@@ -1908,9 +2245,8 @@ const collectionTypes = {
1908
2245
  const {
1909
2246
  body: { discardDraft, ...body }
1910
2247
  } = ctx.request;
1911
- const documentManager2 = getService$1("document-manager");
1912
- const documentMetadata2 = getService$1("document-metadata");
1913
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2248
+ const documentManager2 = getService$2("document-manager");
2249
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1914
2250
  if (permissionChecker2.cannot.unpublish()) {
1915
2251
  return ctx.forbidden();
1916
2252
  }
@@ -1918,8 +2254,8 @@ const collectionTypes = {
1918
2254
  return ctx.forbidden();
1919
2255
  }
1920
2256
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1921
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1922
- const { locale } = getDocumentLocaleAndStatus(body);
2257
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2258
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1923
2259
  const document = await documentManager2.findOne(id, model, {
1924
2260
  populate,
1925
2261
  locale,
@@ -1941,7 +2277,7 @@ const collectionTypes = {
1941
2277
  ctx.body = await strapiUtils.async.pipe(
1942
2278
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
1943
2279
  permissionChecker2.sanitizeOutput,
1944
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2280
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1945
2281
  )(document);
1946
2282
  });
1947
2283
  },
@@ -1949,15 +2285,14 @@ const collectionTypes = {
1949
2285
  const { userAbility } = ctx.state;
1950
2286
  const { id, model } = ctx.params;
1951
2287
  const { body } = ctx.request;
1952
- const documentManager2 = getService$1("document-manager");
1953
- const documentMetadata2 = getService$1("document-metadata");
1954
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2288
+ const documentManager2 = getService$2("document-manager");
2289
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1955
2290
  if (permissionChecker2.cannot.discard()) {
1956
2291
  return ctx.forbidden();
1957
2292
  }
1958
2293
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
1959
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1960
- const { locale } = getDocumentLocaleAndStatus(body);
2294
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2295
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1961
2296
  const document = await documentManager2.findOne(id, model, {
1962
2297
  populate,
1963
2298
  locale,
@@ -1972,42 +2307,50 @@ const collectionTypes = {
1972
2307
  ctx.body = await strapiUtils.async.pipe(
1973
2308
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
1974
2309
  permissionChecker2.sanitizeOutput,
1975
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2310
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1976
2311
  )(document);
1977
2312
  },
1978
2313
  async bulkDelete(ctx) {
1979
2314
  const { userAbility } = ctx.state;
1980
2315
  const { model } = ctx.params;
1981
2316
  const { query, body } = ctx.request;
1982
- const { ids } = body;
2317
+ const { documentIds } = body;
1983
2318
  await validateBulkActionInput(body);
1984
- const documentManager2 = getService$1("document-manager");
1985
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2319
+ const documentManager2 = getService$2("document-manager");
2320
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1986
2321
  if (permissionChecker2.cannot.delete()) {
1987
2322
  return ctx.forbidden();
1988
2323
  }
1989
2324
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
1990
- const idsWhereClause = { id: { $in: ids } };
1991
- const params = {
1992
- ...permissionQuery,
1993
- filters: {
1994
- $and: [idsWhereClause].concat(permissionQuery.filters || [])
2325
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2326
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2327
+ const documentLocales = await documentManager2.findLocales(documentIds, model, {
2328
+ populate,
2329
+ locale
2330
+ });
2331
+ if (documentLocales.length === 0) {
2332
+ return ctx.notFound();
2333
+ }
2334
+ for (const document of documentLocales) {
2335
+ if (permissionChecker2.cannot.delete(document)) {
2336
+ return ctx.forbidden();
1995
2337
  }
1996
- };
1997
- const { count } = await documentManager2.deleteMany(params, model);
2338
+ }
2339
+ const localeDocumentsIds = documentLocales.map((document) => document.documentId);
2340
+ const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
1998
2341
  ctx.body = { count };
1999
2342
  },
2000
2343
  async countDraftRelations(ctx) {
2001
2344
  const { userAbility } = ctx.state;
2002
2345
  const { model, id } = ctx.params;
2003
- const documentManager2 = getService$1("document-manager");
2004
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2346
+ const documentManager2 = getService$2("document-manager");
2347
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2005
2348
  if (permissionChecker2.cannot.read()) {
2006
2349
  return ctx.forbidden();
2007
2350
  }
2008
2351
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2009
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2010
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
2352
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2353
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
2011
2354
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
2012
2355
  if (!entity) {
2013
2356
  return ctx.notFound();
@@ -2022,24 +2365,24 @@ const collectionTypes = {
2022
2365
  },
2023
2366
  async countManyEntriesDraftRelations(ctx) {
2024
2367
  const { userAbility } = ctx.state;
2025
- const ids = ctx.request.query.ids;
2368
+ const ids = ctx.request.query.documentIds;
2026
2369
  const locale = ctx.request.query.locale;
2027
2370
  const { model } = ctx.params;
2028
- const documentManager2 = getService$1("document-manager");
2029
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2371
+ const documentManager2 = getService$2("document-manager");
2372
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2030
2373
  if (permissionChecker2.cannot.read()) {
2031
2374
  return ctx.forbidden();
2032
2375
  }
2033
- const entities = await documentManager2.findMany(
2376
+ const documents = await documentManager2.findMany(
2034
2377
  {
2035
2378
  filters: {
2036
- id: ids
2379
+ documentId: ids
2037
2380
  },
2038
2381
  locale
2039
2382
  },
2040
2383
  model
2041
2384
  );
2042
- if (!entities) {
2385
+ if (!documents) {
2043
2386
  return ctx.notFound();
2044
2387
  }
2045
2388
  const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
@@ -2050,13 +2393,13 @@ const collectionTypes = {
2050
2393
  };
2051
2394
  const components$1 = {
2052
2395
  findComponents(ctx) {
2053
- const components2 = getService$1("components").findAllComponents();
2054
- const { toDto } = getService$1("data-mapper");
2396
+ const components2 = getService$2("components").findAllComponents();
2397
+ const { toDto } = getService$2("data-mapper");
2055
2398
  ctx.body = { data: components2.map(toDto) };
2056
2399
  },
2057
2400
  async findComponentConfiguration(ctx) {
2058
2401
  const { uid: uid2 } = ctx.params;
2059
- const componentService = getService$1("components");
2402
+ const componentService = getService$2("components");
2060
2403
  const component = componentService.findComponent(uid2);
2061
2404
  if (!component) {
2062
2405
  return ctx.notFound("component.notFound");
@@ -2073,7 +2416,7 @@ const components$1 = {
2073
2416
  async updateComponentConfiguration(ctx) {
2074
2417
  const { uid: uid2 } = ctx.params;
2075
2418
  const { body } = ctx.request;
2076
- const componentService = getService$1("components");
2419
+ const componentService = getService$2("components");
2077
2420
  const component = componentService.findComponent(uid2);
2078
2421
  if (!component) {
2079
2422
  return ctx.notFound("component.notFound");
@@ -2107,12 +2450,12 @@ const contentTypes = {
2107
2450
  } catch (error) {
2108
2451
  return ctx.send({ error }, 400);
2109
2452
  }
2110
- const contentTypes2 = getService$1("content-types").findContentTypesByKind(kind);
2111
- const { toDto } = getService$1("data-mapper");
2453
+ const contentTypes2 = getService$2("content-types").findContentTypesByKind(kind);
2454
+ const { toDto } = getService$2("data-mapper");
2112
2455
  ctx.body = { data: contentTypes2.map(toDto) };
2113
2456
  },
2114
2457
  async findContentTypesSettings(ctx) {
2115
- const { findAllContentTypes, findConfiguration } = getService$1("content-types");
2458
+ const { findAllContentTypes, findConfiguration } = getService$2("content-types");
2116
2459
  const contentTypes2 = await findAllContentTypes();
2117
2460
  const configurations = await Promise.all(
2118
2461
  contentTypes2.map(async (contentType) => {
@@ -2126,7 +2469,7 @@ const contentTypes = {
2126
2469
  },
2127
2470
  async findContentTypeConfiguration(ctx) {
2128
2471
  const { uid: uid2 } = ctx.params;
2129
- const contentTypeService = getService$1("content-types");
2472
+ const contentTypeService = getService$2("content-types");
2130
2473
  const contentType = await contentTypeService.findContentType(uid2);
2131
2474
  if (!contentType) {
2132
2475
  return ctx.notFound("contentType.notFound");
@@ -2148,13 +2491,13 @@ const contentTypes = {
2148
2491
  const { userAbility } = ctx.state;
2149
2492
  const { uid: uid2 } = ctx.params;
2150
2493
  const { body } = ctx.request;
2151
- const contentTypeService = getService$1("content-types");
2152
- const metricsService = getService$1("metrics");
2494
+ const contentTypeService = getService$2("content-types");
2495
+ const metricsService = getService$2("metrics");
2153
2496
  const contentType = await contentTypeService.findContentType(uid2);
2154
2497
  if (!contentType) {
2155
2498
  return ctx.notFound("contentType.notFound");
2156
2499
  }
2157
- if (!getService$1("permission").canConfigureContentType({ userAbility, contentType })) {
2500
+ if (!getService$2("permission").canConfigureContentType({ userAbility, contentType })) {
2158
2501
  return ctx.forbidden();
2159
2502
  }
2160
2503
  let input;
@@ -2187,10 +2530,10 @@ const contentTypes = {
2187
2530
  };
2188
2531
  const init = {
2189
2532
  getInitData(ctx) {
2190
- const { toDto } = getService$1("data-mapper");
2191
- const { findAllComponents } = getService$1("components");
2192
- const { getAllFieldSizes } = getService$1("field-sizes");
2193
- const { findAllContentTypes } = getService$1("content-types");
2533
+ const { toDto } = getService$2("data-mapper");
2534
+ const { findAllComponents } = getService$2("components");
2535
+ const { getAllFieldSizes } = getService$2("field-sizes");
2536
+ const { findAllContentTypes } = getService$2("content-types");
2194
2537
  ctx.body = {
2195
2538
  data: {
2196
2539
  fieldSizes: getAllFieldSizes(),
@@ -2226,36 +2569,41 @@ const addFiltersClause = (params, filtersClause) => {
2226
2569
  params.filters.$and.push(filtersClause);
2227
2570
  };
2228
2571
  const sanitizeMainField = (model, mainField, userAbility) => {
2229
- const permissionChecker2 = getService$1("permission-checker").create({
2572
+ const permissionChecker2 = getService$2("permission-checker").create({
2230
2573
  userAbility,
2231
2574
  model: model.uid
2232
2575
  });
2233
- if (!isListable(model, mainField)) {
2576
+ const isMainFieldListable = isListable(model, mainField);
2577
+ const canReadMainField = permissionChecker2.can.read(null, mainField);
2578
+ if (!isMainFieldListable || !canReadMainField) {
2234
2579
  return "id";
2235
2580
  }
2236
- if (permissionChecker2.cannot.read(null, mainField)) {
2237
- if (model.uid === "plugin::users-permissions.role") {
2238
- const userPermissionChecker = getService$1("permission-checker").create({
2239
- userAbility,
2240
- model: "plugin::users-permissions.user"
2241
- });
2242
- if (userPermissionChecker.can.read()) {
2243
- return "name";
2244
- }
2245
- }
2246
- return "id";
2581
+ if (model.uid === "plugin::users-permissions.role") {
2582
+ return "name";
2247
2583
  }
2248
2584
  return mainField;
2249
2585
  };
2250
- const addStatusToRelations = async (uid2, relations2) => {
2251
- if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi.contentTypes[uid2])) {
2586
+ const addStatusToRelations = async (targetUid, relations2) => {
2587
+ if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(targetUid))) {
2252
2588
  return relations2;
2253
2589
  }
2254
- const documentMetadata2 = getService$1("document-metadata");
2255
- const documentsAvailableStatus = await documentMetadata2.getManyAvailableStatus(uid2, relations2);
2590
+ const documentMetadata2 = getService$2("document-metadata");
2591
+ if (!relations2.length) {
2592
+ return relations2;
2593
+ }
2594
+ const firstRelation = relations2[0];
2595
+ const filters = {
2596
+ documentId: { $in: relations2.map((r) => r.documentId) },
2597
+ // NOTE: find the "opposite" status
2598
+ publishedAt: firstRelation.publishedAt !== null ? { $null: true } : { $notNull: true }
2599
+ };
2600
+ const availableStatus = await strapi.query(targetUid).findMany({
2601
+ select: ["id", "documentId", "locale", "updatedAt", "createdAt", "publishedAt"],
2602
+ filters
2603
+ });
2256
2604
  return relations2.map((relation) => {
2257
- const availableStatuses = documentsAvailableStatus.filter(
2258
- (availableDocument) => availableDocument.documentId === relation.documentId
2605
+ const availableStatuses = availableStatus.filter(
2606
+ (availableDocument) => availableDocument.documentId === relation.documentId && (relation.locale ? availableDocument.locale === relation.locale : true)
2259
2607
  );
2260
2608
  return {
2261
2609
  ...relation,
@@ -2276,11 +2624,8 @@ const validateLocale = (sourceUid, targetUid, locale) => {
2276
2624
  const isLocalized = strapi.plugin("i18n").service("content-types").isLocalizedContentType;
2277
2625
  const isSourceLocalized = isLocalized(sourceModel);
2278
2626
  const isTargetLocalized = isLocalized(targetModel);
2279
- let validatedLocale = locale;
2280
- if (!targetModel || !isTargetLocalized)
2281
- validatedLocale = void 0;
2282
2627
  return {
2283
- locale: validatedLocale,
2628
+ locale,
2284
2629
  isSourceLocalized,
2285
2630
  isTargetLocalized
2286
2631
  };
@@ -2320,7 +2665,7 @@ const relations = {
2320
2665
  ctx.request?.query?.locale
2321
2666
  );
2322
2667
  const { status } = validateStatus(sourceUid, ctx.request?.query?.status);
2323
- const permissionChecker2 = getService$1("permission-checker").create({
2668
+ const permissionChecker2 = getService$2("permission-checker").create({
2324
2669
  userAbility,
2325
2670
  model
2326
2671
  });
@@ -2345,7 +2690,7 @@ const relations = {
2345
2690
  where.id = id;
2346
2691
  }
2347
2692
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2348
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2693
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2349
2694
  const currentEntity = await strapi.db.query(model).findOne({
2350
2695
  where,
2351
2696
  populate
@@ -2360,7 +2705,7 @@ const relations = {
2360
2705
  }
2361
2706
  entryId = currentEntity.id;
2362
2707
  }
2363
- const modelConfig = isComponent2 ? await getService$1("components").findConfiguration(sourceSchema) : await getService$1("content-types").findConfiguration(sourceSchema);
2708
+ const modelConfig = isComponent2 ? await getService$2("components").findConfiguration(sourceSchema) : await getService$2("content-types").findConfiguration(sourceSchema);
2364
2709
  const targetSchema = strapi.getModel(targetUid);
2365
2710
  const mainField = fp.flow(
2366
2711
  fp.prop(`metadatas.${targetField}.edit.mainField`),
@@ -2383,7 +2728,7 @@ const relations = {
2383
2728
  attribute,
2384
2729
  fieldsToSelect,
2385
2730
  mainField,
2386
- source: { schema: sourceSchema },
2731
+ source: { schema: sourceSchema, isLocalized: isSourceLocalized },
2387
2732
  target: { schema: targetSchema, isLocalized: isTargetLocalized },
2388
2733
  sourceSchema,
2389
2734
  targetSchema,
@@ -2405,7 +2750,8 @@ const relations = {
2405
2750
  fieldsToSelect,
2406
2751
  mainField,
2407
2752
  source: {
2408
- schema: { uid: sourceUid, modelType: sourceModelType }
2753
+ schema: { uid: sourceUid, modelType: sourceModelType },
2754
+ isLocalized: isSourceLocalized
2409
2755
  },
2410
2756
  target: {
2411
2757
  schema: { uid: targetUid },
@@ -2413,7 +2759,7 @@ const relations = {
2413
2759
  }
2414
2760
  } = await this.extractAndValidateRequestInfo(ctx, id);
2415
2761
  const { idsToOmit, idsToInclude, _q, ...query } = ctx.request.query;
2416
- const permissionChecker2 = getService$1("permission-checker").create({
2762
+ const permissionChecker2 = getService$2("permission-checker").create({
2417
2763
  userAbility: ctx.state.userAbility,
2418
2764
  model: targetUid
2419
2765
  });
@@ -2443,12 +2789,16 @@ const relations = {
2443
2789
  } else {
2444
2790
  where.id = id;
2445
2791
  }
2446
- if (status) {
2447
- where[`${alias}.published_at`] = getPublishedAtClause(status, targetUid);
2792
+ const publishedAt = getPublishedAtClause(status, targetUid);
2793
+ if (!fp.isEmpty(publishedAt)) {
2794
+ where[`${alias}.published_at`] = publishedAt;
2448
2795
  }
2449
- if (filterByLocale) {
2796
+ if (isTargetLocalized && locale) {
2450
2797
  where[`${alias}.locale`] = locale;
2451
2798
  }
2799
+ if (isSourceLocalized && locale) {
2800
+ where.locale = locale;
2801
+ }
2452
2802
  if ((idsToInclude?.length ?? 0) !== 0) {
2453
2803
  where[`${alias}.id`].$notIn = idsToInclude;
2454
2804
  }
@@ -2466,7 +2816,8 @@ const relations = {
2466
2816
  id: { $notIn: fp.uniq(idsToOmit) }
2467
2817
  });
2468
2818
  }
2469
- const res = await strapi.db.query(targetUid).findPage(strapi.get("query-params").transform(targetUid, queryParams));
2819
+ const dbQuery = strapi.get("query-params").transform(targetUid, queryParams);
2820
+ const res = await strapi.db.query(targetUid).findPage(dbQuery);
2470
2821
  ctx.body = {
2471
2822
  ...res,
2472
2823
  results: await addStatusToRelations(targetUid, res.results)
@@ -2481,29 +2832,39 @@ const relations = {
2481
2832
  attribute,
2482
2833
  targetField,
2483
2834
  fieldsToSelect,
2484
- source: {
2485
- schema: { uid: sourceUid }
2486
- },
2487
- target: {
2488
- schema: { uid: targetUid }
2489
- }
2835
+ status,
2836
+ source: { schema: sourceSchema },
2837
+ target: { schema: targetSchema }
2490
2838
  } = await this.extractAndValidateRequestInfo(ctx, id);
2491
- const permissionQuery = await getService$1("permission-checker").create({ userAbility, model: targetUid }).sanitizedQuery.read({ fields: fieldsToSelect });
2839
+ const { uid: sourceUid } = sourceSchema;
2840
+ const { uid: targetUid } = targetSchema;
2841
+ const permissionQuery = await getService$2("permission-checker").create({ userAbility, model: targetUid }).sanitizedQuery.read({ fields: fieldsToSelect });
2492
2842
  const dbQuery = strapi.db.query(sourceUid);
2493
2843
  const loadRelations = strapiUtils.relations.isAnyToMany(attribute) ? (...args) => dbQuery.loadPages(...args) : (...args) => dbQuery.load(...args).then((res2) => ({ results: res2 ? [res2] : [] }));
2844
+ const filters = {};
2845
+ if (sourceSchema?.options?.draftAndPublish) {
2846
+ if (targetSchema?.options?.draftAndPublish) {
2847
+ if (status === "published") {
2848
+ filters.publishedAt = { $notNull: true };
2849
+ } else {
2850
+ filters.publishedAt = { $null: true };
2851
+ }
2852
+ }
2853
+ } else if (targetSchema?.options?.draftAndPublish) {
2854
+ filters.publishedAt = { $null: true };
2855
+ }
2494
2856
  const res = await loadRelations({ id: entryId }, targetField, {
2495
- select: ["id", "documentId", "locale", "publishedAt"],
2857
+ select: ["id", "documentId", "locale", "publishedAt", "updatedAt"],
2496
2858
  ordering: "desc",
2497
2859
  page: ctx.request.query.page,
2498
- pageSize: ctx.request.query.pageSize
2860
+ pageSize: ctx.request.query.pageSize,
2861
+ filters
2499
2862
  });
2500
2863
  const loadedIds = res.results.map((item) => item.id);
2501
2864
  addFiltersClause(permissionQuery, { id: { $in: loadedIds } });
2502
2865
  const sanitizedRes = await loadRelations({ id: entryId }, targetField, {
2503
2866
  ...strapi.get("query-params").transform(targetUid, permissionQuery),
2504
- ordering: "desc",
2505
- page: ctx.request.query.page,
2506
- pageSize: ctx.request.query.pageSize
2867
+ ordering: "desc"
2507
2868
  });
2508
2869
  const relationsUnion = fp.uniqBy("id", fp.concat(sanitizedRes.results, res.results));
2509
2870
  ctx.body = {
@@ -2518,10 +2879,10 @@ const relations = {
2518
2879
  }
2519
2880
  };
2520
2881
  const buildPopulateFromQuery = async (query, model) => {
2521
- return getService$1("populate-builder")(model).populateFromQuery(query).populateDeep(Infinity).countRelations().build();
2882
+ return getService$2("populate-builder")(model).populateFromQuery(query).populateDeep(Infinity).countRelations().build();
2522
2883
  };
2523
2884
  const findDocument = async (query, uid2, opts = {}) => {
2524
- const documentManager2 = getService$1("document-manager");
2885
+ const documentManager2 = getService$2("document-manager");
2525
2886
  const populate = await buildPopulateFromQuery(query, uid2);
2526
2887
  return documentManager2.findMany({ ...opts, populate }, uid2).then((documents) => documents[0]);
2527
2888
  };
@@ -2529,13 +2890,13 @@ const createOrUpdateDocument = async (ctx, opts) => {
2529
2890
  const { user, userAbility } = ctx.state;
2530
2891
  const { model } = ctx.params;
2531
2892
  const { body, query } = ctx.request;
2532
- const documentManager2 = getService$1("document-manager");
2533
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2893
+ const documentManager2 = getService$2("document-manager");
2894
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2534
2895
  if (permissionChecker2.cannot.create() && permissionChecker2.cannot.update()) {
2535
2896
  throw new strapiUtils.errors.ForbiddenError();
2536
2897
  }
2537
2898
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2538
- const { locale } = getDocumentLocaleAndStatus(body);
2899
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2539
2900
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2540
2901
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2541
2902
  // Find the first document to check if it exists
@@ -2571,13 +2932,12 @@ const singleTypes = {
2571
2932
  const { userAbility } = ctx.state;
2572
2933
  const { model } = ctx.params;
2573
2934
  const { query = {} } = ctx.request;
2574
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2575
- const documentMetadata2 = getService$1("document-metadata");
2935
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2576
2936
  if (permissionChecker2.cannot.read()) {
2577
2937
  return ctx.forbidden();
2578
2938
  }
2579
2939
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2580
- const { locale, status } = getDocumentLocaleAndStatus(query);
2940
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
2581
2941
  const version = await findDocument(permissionQuery, model, { locale, status });
2582
2942
  if (!version) {
2583
2943
  if (permissionChecker2.cannot.create()) {
@@ -2587,9 +2947,11 @@ const singleTypes = {
2587
2947
  if (!document) {
2588
2948
  return ctx.notFound();
2589
2949
  }
2590
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
2950
+ const { meta } = await formatDocumentWithMetadata(
2951
+ permissionChecker2,
2591
2952
  model,
2592
- { id: document.documentId, locale, publishedAt: null },
2953
+ // @ts-expect-error - fix types
2954
+ { documentId: document.documentId, locale, publishedAt: null },
2593
2955
  { availableLocales: true, availableStatus: false }
2594
2956
  );
2595
2957
  ctx.body = { data: {}, meta };
@@ -2599,29 +2961,28 @@ const singleTypes = {
2599
2961
  return ctx.forbidden();
2600
2962
  }
2601
2963
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
2602
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2964
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2603
2965
  },
2604
2966
  async createOrUpdate(ctx) {
2605
2967
  const { userAbility } = ctx.state;
2606
2968
  const { model } = ctx.params;
2607
- const documentMetadata2 = getService$1("document-metadata");
2608
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2969
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2609
2970
  const document = await createOrUpdateDocument(ctx);
2610
2971
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2611
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2972
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2612
2973
  },
2613
2974
  async delete(ctx) {
2614
2975
  const { userAbility } = ctx.state;
2615
2976
  const { model } = ctx.params;
2616
2977
  const { query = {} } = ctx.request;
2617
- const documentManager2 = getService$1("document-manager");
2618
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2978
+ const documentManager2 = getService$2("document-manager");
2979
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2619
2980
  if (permissionChecker2.cannot.delete()) {
2620
2981
  return ctx.forbidden();
2621
2982
  }
2622
2983
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2623
2984
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2624
- const { locale } = getDocumentLocaleAndStatus(query);
2985
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2625
2986
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2626
2987
  populate,
2627
2988
  locale
@@ -2643,9 +3004,8 @@ const singleTypes = {
2643
3004
  const { userAbility } = ctx.state;
2644
3005
  const { model } = ctx.params;
2645
3006
  const { query = {} } = ctx.request;
2646
- const documentManager2 = getService$1("document-manager");
2647
- const documentMetadata2 = getService$1("document-metadata");
2648
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
3007
+ const documentManager2 = getService$2("document-manager");
3008
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2649
3009
  if (permissionChecker2.cannot.publish()) {
2650
3010
  return ctx.forbidden();
2651
3011
  }
@@ -2659,11 +3019,12 @@ const singleTypes = {
2659
3019
  if (permissionChecker2.cannot.publish(document)) {
2660
3020
  throw new strapiUtils.errors.ForbiddenError();
2661
3021
  }
2662
- const { locale } = getDocumentLocaleAndStatus(document);
2663
- return documentManager2.publish(document.documentId, model, { locale });
3022
+ const { locale } = await getDocumentLocaleAndStatus(document, model);
3023
+ const publishResult = await documentManager2.publish(document.documentId, model, { locale });
3024
+ return publishResult.at(0);
2664
3025
  });
2665
3026
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
2666
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
3027
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2667
3028
  },
2668
3029
  async unpublish(ctx) {
2669
3030
  const { userAbility } = ctx.state;
@@ -2672,9 +3033,8 @@ const singleTypes = {
2672
3033
  body: { discardDraft, ...body },
2673
3034
  query = {}
2674
3035
  } = ctx.request;
2675
- const documentManager2 = getService$1("document-manager");
2676
- const documentMetadata2 = getService$1("document-metadata");
2677
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
3036
+ const documentManager2 = getService$2("document-manager");
3037
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2678
3038
  if (permissionChecker2.cannot.unpublish()) {
2679
3039
  return ctx.forbidden();
2680
3040
  }
@@ -2682,7 +3042,7 @@ const singleTypes = {
2682
3042
  return ctx.forbidden();
2683
3043
  }
2684
3044
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2685
- const { locale } = getDocumentLocaleAndStatus(body);
3045
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2686
3046
  const document = await findDocument(sanitizedQuery, model, { locale });
2687
3047
  if (!document) {
2688
3048
  return ctx.notFound();
@@ -2700,7 +3060,7 @@ const singleTypes = {
2700
3060
  ctx.body = await strapiUtils.async.pipe(
2701
3061
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
2702
3062
  permissionChecker2.sanitizeOutput,
2703
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
3063
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2704
3064
  )(document);
2705
3065
  });
2706
3066
  },
@@ -2708,14 +3068,13 @@ const singleTypes = {
2708
3068
  const { userAbility } = ctx.state;
2709
3069
  const { model } = ctx.params;
2710
3070
  const { body, query = {} } = ctx.request;
2711
- const documentManager2 = getService$1("document-manager");
2712
- const documentMetadata2 = getService$1("document-metadata");
2713
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
3071
+ const documentManager2 = getService$2("document-manager");
3072
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2714
3073
  if (permissionChecker2.cannot.discard()) {
2715
3074
  return ctx.forbidden();
2716
3075
  }
2717
3076
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2718
- const { locale } = getDocumentLocaleAndStatus(body);
3077
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2719
3078
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2720
3079
  if (!document) {
2721
3080
  return ctx.notFound();
@@ -2726,16 +3085,16 @@ const singleTypes = {
2726
3085
  ctx.body = await strapiUtils.async.pipe(
2727
3086
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
2728
3087
  permissionChecker2.sanitizeOutput,
2729
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
3088
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2730
3089
  )(document);
2731
3090
  },
2732
3091
  async countDraftRelations(ctx) {
2733
3092
  const { userAbility } = ctx.state;
2734
3093
  const { model } = ctx.params;
2735
3094
  const { query } = ctx.request;
2736
- const documentManager2 = getService$1("document-manager");
2737
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2738
- const { locale } = getDocumentLocaleAndStatus(query);
3095
+ const documentManager2 = getService$2("document-manager");
3096
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
3097
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2739
3098
  if (permissionChecker2.cannot.read()) {
2740
3099
  return ctx.forbidden();
2741
3100
  }
@@ -2756,9 +3115,9 @@ const uid$1 = {
2756
3115
  async generateUID(ctx) {
2757
3116
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2758
3117
  const { query = {} } = ctx.request;
2759
- const { locale } = getDocumentLocaleAndStatus(query);
3118
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2760
3119
  await validateUIDField(contentTypeUID, field);
2761
- const uidService = getService$1("uid");
3120
+ const uidService = getService$2("uid");
2762
3121
  ctx.body = {
2763
3122
  data: await uidService.generateUIDField({ contentTypeUID, field, data, locale })
2764
3123
  };
@@ -2768,9 +3127,9 @@ const uid$1 = {
2768
3127
  ctx.request.body
2769
3128
  );
2770
3129
  const { query = {} } = ctx.request;
2771
- const { locale } = getDocumentLocaleAndStatus(query);
3130
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2772
3131
  await validateUIDField(contentTypeUID, field);
2773
- const uidService = getService$1("uid");
3132
+ const uidService = getService$2("uid");
2774
3133
  const isAvailable = await uidService.checkUIDAvailability({
2775
3134
  contentTypeUID,
2776
3135
  field,
@@ -2791,7 +3150,8 @@ const controllers = {
2791
3150
  relations,
2792
3151
  "single-types": singleTypes,
2793
3152
  uid: uid$1,
2794
- ...history.controllers ? history.controllers : {}
3153
+ ...history.controllers ? history.controllers : {},
3154
+ ...preview.controllers ? preview.controllers : {}
2795
3155
  };
2796
3156
  const keys = {
2797
3157
  CONFIGURATION: "configuration"
@@ -2942,12 +3302,12 @@ async function syncMetadatas(configuration, schema) {
2942
3302
  return ___default.default.assign(metasWithDefaults, updatedMetas);
2943
3303
  }
2944
3304
  const getTargetSchema = (targetModel) => {
2945
- return getService$1("content-types").findContentType(targetModel);
3305
+ return getService$2("content-types").findContentType(targetModel);
2946
3306
  };
2947
3307
  const DEFAULT_LIST_LENGTH = 4;
2948
3308
  const MAX_ROW_SIZE = 12;
2949
3309
  const isAllowedFieldSize = (type, size) => {
2950
- const { getFieldSize } = getService$1("field-sizes");
3310
+ const { getFieldSize } = getService$2("field-sizes");
2951
3311
  const fieldSize = getFieldSize(type);
2952
3312
  if (!fieldSize.isResizable && size !== fieldSize.default) {
2953
3313
  return false;
@@ -2955,7 +3315,7 @@ const isAllowedFieldSize = (type, size) => {
2955
3315
  return size <= MAX_ROW_SIZE;
2956
3316
  };
2957
3317
  const getDefaultFieldSize = (attribute) => {
2958
- const { hasFieldSize, getFieldSize } = getService$1("field-sizes");
3318
+ const { hasFieldSize, getFieldSize } = getService$2("field-sizes");
2959
3319
  return getFieldSize(hasFieldSize(attribute.customField) ? attribute.customField : attribute.type).default;
2960
3320
  };
2961
3321
  async function createDefaultLayouts(schema) {
@@ -2990,7 +3350,7 @@ function syncLayouts(configuration, schema) {
2990
3350
  for (const el of row) {
2991
3351
  if (!hasEditableAttribute(schema, el.name))
2992
3352
  continue;
2993
- const { hasFieldSize } = getService$1("field-sizes");
3353
+ const { hasFieldSize } = getService$2("field-sizes");
2994
3354
  const fieldType = hasFieldSize(schema.attributes[el.name].customField) ? schema.attributes[el.name].customField : schema.attributes[el.name].type;
2995
3355
  if (!isAllowedFieldSize(fieldType, el.size)) {
2996
3356
  elementsToReAppend.push(el.name);
@@ -3130,17 +3490,17 @@ const configurationService$1 = createConfigurationService({
3130
3490
  isComponent: true,
3131
3491
  prefix: STORE_KEY_PREFIX,
3132
3492
  getModels() {
3133
- const { toContentManagerModel } = getService$1("data-mapper");
3493
+ const { toContentManagerModel } = getService$2("data-mapper");
3134
3494
  return fp.mapValues(toContentManagerModel, strapi.components);
3135
3495
  }
3136
3496
  });
3137
3497
  const components = ({ strapi: strapi2 }) => ({
3138
3498
  findAllComponents() {
3139
- const { toContentManagerModel } = getService$1("data-mapper");
3499
+ const { toContentManagerModel } = getService$2("data-mapper");
3140
3500
  return Object.values(strapi2.components).map(toContentManagerModel);
3141
3501
  },
3142
3502
  findComponent(uid2) {
3143
- const { toContentManagerModel } = getService$1("data-mapper");
3503
+ const { toContentManagerModel } = getService$2("data-mapper");
3144
3504
  const component = strapi2.components[uid2];
3145
3505
  return fp.isNil(component) ? component : toContentManagerModel(component);
3146
3506
  },
@@ -3191,17 +3551,17 @@ const configurationService = createConfigurationService({
3191
3551
  storeUtils,
3192
3552
  prefix: "content_types",
3193
3553
  getModels() {
3194
- const { toContentManagerModel } = getService$1("data-mapper");
3554
+ const { toContentManagerModel } = getService$2("data-mapper");
3195
3555
  return fp.mapValues(toContentManagerModel, strapi.contentTypes);
3196
3556
  }
3197
3557
  });
3198
3558
  const service = ({ strapi: strapi2 }) => ({
3199
3559
  findAllContentTypes() {
3200
- const { toContentManagerModel } = getService$1("data-mapper");
3560
+ const { toContentManagerModel } = getService$2("data-mapper");
3201
3561
  return Object.values(strapi2.contentTypes).map(toContentManagerModel);
3202
3562
  },
3203
3563
  findContentType(uid2) {
3204
- const { toContentManagerModel } = getService$1("data-mapper");
3564
+ const { toContentManagerModel } = getService$2("data-mapper");
3205
3565
  const contentType = strapi2.contentTypes[uid2];
3206
3566
  return fp.isNil(contentType) ? contentType : toContentManagerModel(contentType);
3207
3567
  },
@@ -3230,7 +3590,7 @@ const service = ({ strapi: strapi2 }) => ({
3230
3590
  return this.findConfiguration(contentType);
3231
3591
  },
3232
3592
  findComponentsConfigurations(contentType) {
3233
- return getService$1("components").findComponentsConfigurations(contentType);
3593
+ return getService$2("components").findComponentsConfigurations(contentType);
3234
3594
  },
3235
3595
  syncConfigurations() {
3236
3596
  return configurationService.syncConfigurations();
@@ -3411,12 +3771,27 @@ const createPermissionChecker = (strapi2) => ({ userAbility, model }) => {
3411
3771
  ability: userAbility,
3412
3772
  model
3413
3773
  });
3414
- const toSubject = (entity) => entity ? permissionsManager.toSubject(entity, model) : model;
3774
+ const { actionProvider } = strapi2.service("admin::permission");
3775
+ const toSubject = (entity) => {
3776
+ return entity ? permissionsManager.toSubject(entity, model) : model;
3777
+ };
3415
3778
  const can = (action, entity, field) => {
3416
- return userAbility.can(action, toSubject(entity), field);
3779
+ const subject = toSubject(entity);
3780
+ const aliases = actionProvider.unstable_aliases(action, model);
3781
+ return (
3782
+ // Test the original action to see if it passes
3783
+ userAbility.can(action, subject, field) || // Else try every known alias if at least one of them succeed, then the user "can"
3784
+ aliases.some((alias) => userAbility.can(alias, subject, field))
3785
+ );
3417
3786
  };
3418
3787
  const cannot = (action, entity, field) => {
3419
- return userAbility.cannot(action, toSubject(entity), field);
3788
+ const subject = toSubject(entity);
3789
+ const aliases = actionProvider.unstable_aliases(action, model);
3790
+ return (
3791
+ // Test both the original action
3792
+ userAbility.cannot(action, subject, field) && // and every known alias, if all of them fail (cannot), then the user truly "cannot"
3793
+ aliases.every((alias) => userAbility.cannot(alias, subject, field))
3794
+ );
3420
3795
  };
3421
3796
  const sanitizeOutput = (data, { action = ACTIONS.read } = {}) => {
3422
3797
  return permissionsManager.sanitizeOutput(data, { subject: toSubject(data), action });
@@ -3487,7 +3862,7 @@ const permission = ({ strapi: strapi2 }) => ({
3487
3862
  return userAbility.can(action);
3488
3863
  },
3489
3864
  async registerPermissions() {
3490
- const displayedContentTypes = getService$1("content-types").findDisplayedContentTypes();
3865
+ const displayedContentTypes = getService$2("content-types").findDisplayedContentTypes();
3491
3866
  const contentTypesUids = displayedContentTypes.map(fp.prop("uid"));
3492
3867
  const actions = [
3493
3868
  {
@@ -3559,7 +3934,7 @@ const permission = ({ strapi: strapi2 }) => ({
3559
3934
  await strapi2.service("admin::permission").actionProvider.registerMany(actions);
3560
3935
  }
3561
3936
  });
3562
- const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils__default.default.contentTypes;
3937
+ const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils__default.default.contentTypes;
3563
3938
  const { isAnyToMany } = strapiUtils__default.default.relations;
3564
3939
  const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils__default.default.contentTypes.constants;
3565
3940
  const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
@@ -3650,6 +4025,42 @@ const getDeepPopulate = (uid2, {
3650
4025
  {}
3651
4026
  );
3652
4027
  };
4028
+ const getValidatableFieldsPopulate = (uid2, {
4029
+ initialPopulate = {},
4030
+ countMany = false,
4031
+ countOne = false,
4032
+ maxLevel = Infinity
4033
+ } = {}, level = 1) => {
4034
+ if (level > maxLevel) {
4035
+ return {};
4036
+ }
4037
+ const model = strapi.getModel(uid2);
4038
+ return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
4039
+ if (!getDoesAttributeRequireValidation(attribute)) {
4040
+ return populateAcc;
4041
+ }
4042
+ if (isScalarAttribute(attribute)) {
4043
+ return fp.merge(populateAcc, {
4044
+ [attributeName]: true
4045
+ });
4046
+ }
4047
+ return fp.merge(
4048
+ populateAcc,
4049
+ getPopulateFor(
4050
+ attributeName,
4051
+ model,
4052
+ {
4053
+ // @ts-expect-error - improve types
4054
+ initialPopulate: initialPopulate?.[attributeName],
4055
+ countMany,
4056
+ countOne,
4057
+ maxLevel
4058
+ },
4059
+ level
4060
+ )
4061
+ );
4062
+ }, {});
4063
+ };
3653
4064
  const getDeepPopulateDraftCount = (uid2) => {
3654
4065
  const model = strapi.getModel(uid2);
3655
4066
  let hasRelations = false;
@@ -3657,6 +4068,10 @@ const getDeepPopulateDraftCount = (uid2) => {
3657
4068
  const attribute = model.attributes[attributeName];
3658
4069
  switch (attribute.type) {
3659
4070
  case "relation": {
4071
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
4072
+ if (isMorphRelation) {
4073
+ break;
4074
+ }
3660
4075
  if (isVisibleAttribute$1(model, attributeName)) {
3661
4076
  populateAcc[attributeName] = {
3662
4077
  count: true,
@@ -3671,22 +4086,24 @@ const getDeepPopulateDraftCount = (uid2) => {
3671
4086
  attribute.component
3672
4087
  );
3673
4088
  if (childHasRelations) {
3674
- populateAcc[attributeName] = { populate: populate2 };
4089
+ populateAcc[attributeName] = {
4090
+ populate: populate2
4091
+ };
3675
4092
  hasRelations = true;
3676
4093
  }
3677
4094
  break;
3678
4095
  }
3679
4096
  case "dynamiczone": {
3680
- const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
3681
- const { populate: populate2, hasRelations: childHasRelations } = getDeepPopulateDraftCount(componentUID);
3682
- if (childHasRelations) {
4097
+ const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
4098
+ const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
4099
+ if (componentHasRelations) {
3683
4100
  hasRelations = true;
3684
- return fp.merge(acc, populate2);
4101
+ return { ...acc, [componentUID]: { populate: componentPopulate } };
3685
4102
  }
3686
4103
  return acc;
3687
4104
  }, {});
3688
- if (!fp.isEmpty(dzPopulate)) {
3689
- populateAcc[attributeName] = { populate: dzPopulate };
4105
+ if (!fp.isEmpty(dzPopulateFragment)) {
4106
+ populateAcc[attributeName] = { on: dzPopulateFragment };
3690
4107
  }
3691
4108
  break;
3692
4109
  }
@@ -3721,7 +4138,7 @@ const getQueryPopulate = async (uid2, query) => {
3721
4138
  return populateQuery;
3722
4139
  };
3723
4140
  const buildDeepPopulate = (uid2) => {
3724
- return getService$1("populate-builder")(uid2).populateDeep(Infinity).countRelations().build();
4141
+ return getService$2("populate-builder")(uid2).populateDeep(Infinity).countRelations().build();
3725
4142
  };
3726
4143
  const populateBuilder = (uid2) => {
3727
4144
  let getInitialPopulate = async () => {
@@ -3878,41 +4295,72 @@ const AVAILABLE_STATUS_FIELDS = [
3878
4295
  "updatedBy",
3879
4296
  "status"
3880
4297
  ];
3881
- const AVAILABLE_LOCALES_FIELDS = ["id", "locale", "updatedAt", "createdAt", "status"];
4298
+ const AVAILABLE_LOCALES_FIELDS = [
4299
+ "id",
4300
+ "locale",
4301
+ "updatedAt",
4302
+ "createdAt",
4303
+ "status",
4304
+ "publishedAt",
4305
+ "documentId"
4306
+ ];
3882
4307
  const CONTENT_MANAGER_STATUS = {
3883
4308
  PUBLISHED: "published",
3884
4309
  DRAFT: "draft",
3885
4310
  MODIFIED: "modified"
3886
4311
  };
3887
- const areDatesEqual = (date1, date2, threshold) => {
3888
- if (!date1 || !date2) {
4312
+ const getIsVersionLatestModification = (version, otherVersion) => {
4313
+ if (!version || !version.updatedAt) {
3889
4314
  return false;
3890
4315
  }
3891
- const time1 = new Date(date1).getTime();
3892
- const time2 = new Date(date2).getTime();
3893
- const difference = Math.abs(time1 - time2);
3894
- return difference <= threshold;
4316
+ const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
4317
+ const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
4318
+ return versionUpdatedAt > otherUpdatedAt;
3895
4319
  };
3896
4320
  const documentMetadata = ({ strapi: strapi2 }) => ({
3897
4321
  /**
3898
4322
  * Returns available locales of a document for the current status
3899
4323
  */
3900
- getAvailableLocales(uid2, version, allVersions) {
4324
+ async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
3901
4325
  const versionsByLocale = fp.groupBy("locale", allVersions);
3902
- delete versionsByLocale[version.locale];
3903
- return Object.values(versionsByLocale).map((localeVersions) => {
3904
- if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2))) {
3905
- return fp.pick(AVAILABLE_LOCALES_FIELDS, localeVersions[0]);
3906
- }
3907
- const draftVersion = localeVersions.find((v) => v.publishedAt === null);
3908
- const otherVersions = localeVersions.filter((v) => v.id !== draftVersion?.id);
3909
- if (!draftVersion)
3910
- return;
3911
- return {
3912
- ...fp.pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
3913
- status: this.getStatus(draftVersion, otherVersions)
3914
- };
3915
- }).filter(Boolean);
4326
+ if (version.locale) {
4327
+ delete versionsByLocale[version.locale];
4328
+ }
4329
+ const model = strapi2.getModel(uid2);
4330
+ const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
4331
+ const traversalFunction = async (localeVersion) => strapiUtils.traverseEntity(
4332
+ ({ key }, { remove }) => {
4333
+ if (keysToKeep.includes(key)) {
4334
+ return;
4335
+ }
4336
+ remove(key);
4337
+ },
4338
+ { schema: model, getModel: strapi2.getModel.bind(strapi2) },
4339
+ // @ts-expect-error fix types DocumentVersion incompatible with Data
4340
+ localeVersion
4341
+ );
4342
+ const mappingResult = await strapiUtils.async.map(
4343
+ Object.values(versionsByLocale),
4344
+ async (localeVersions) => {
4345
+ const mappedLocaleVersions = await strapiUtils.async.map(
4346
+ localeVersions,
4347
+ traversalFunction
4348
+ );
4349
+ if (!strapiUtils.contentTypes.hasDraftAndPublish(model)) {
4350
+ return mappedLocaleVersions[0];
4351
+ }
4352
+ const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
4353
+ const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
4354
+ if (!draftVersion) {
4355
+ return;
4356
+ }
4357
+ return {
4358
+ ...draftVersion,
4359
+ status: this.getStatus(draftVersion, otherVersions)
4360
+ };
4361
+ }
4362
+ );
4363
+ return mappingResult.filter(Boolean);
3916
4364
  },
3917
4365
  /**
3918
4366
  * Returns available status of a document for the current locale
@@ -3950,26 +4398,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3950
4398
  });
3951
4399
  },
3952
4400
  getStatus(version, otherDocumentStatuses) {
3953
- const isDraft = version.publishedAt === null;
3954
- if (!otherDocumentStatuses?.length) {
3955
- return isDraft ? CONTENT_MANAGER_STATUS.DRAFT : CONTENT_MANAGER_STATUS.PUBLISHED;
4401
+ let draftVersion;
4402
+ let publishedVersion;
4403
+ if (version.publishedAt) {
4404
+ publishedVersion = version;
4405
+ } else {
4406
+ draftVersion = version;
3956
4407
  }
3957
- if (isDraft) {
3958
- const publishedVersion = otherDocumentStatuses?.find((d) => d.publishedAt !== null);
3959
- if (!publishedVersion) {
3960
- return CONTENT_MANAGER_STATUS.DRAFT;
3961
- }
4408
+ const otherVersion = otherDocumentStatuses?.at(0);
4409
+ if (otherVersion?.publishedAt) {
4410
+ publishedVersion = otherVersion;
4411
+ } else if (otherVersion) {
4412
+ draftVersion = otherVersion;
3962
4413
  }
3963
- if (areDatesEqual(version.updatedAt, otherDocumentStatuses.at(0)?.updatedAt, 500)) {
4414
+ if (!draftVersion)
3964
4415
  return CONTENT_MANAGER_STATUS.PUBLISHED;
3965
- }
3966
- return CONTENT_MANAGER_STATUS.MODIFIED;
4416
+ if (!publishedVersion)
4417
+ return CONTENT_MANAGER_STATUS.DRAFT;
4418
+ const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
4419
+ return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
3967
4420
  },
4421
+ // TODO is it necessary to return metadata on every page of the CM
4422
+ // We could refactor this so the locales are only loaded when they're
4423
+ // needed. e.g. in the bulk locale action modal.
3968
4424
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4425
+ const populate = getValidatableFieldsPopulate(uid2);
3969
4426
  const versions = await strapi2.db.query(uid2).findMany({
3970
4427
  where: { documentId: version.documentId },
3971
- select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
3972
4428
  populate: {
4429
+ // Populate only fields that require validation for bulk locale actions
4430
+ ...populate,
4431
+ // NOTE: creator fields are selected in this way to avoid exposing sensitive data
3973
4432
  createdBy: {
3974
4433
  select: ["id", "firstname", "lastname", "email"]
3975
4434
  },
@@ -3978,7 +4437,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3978
4437
  }
3979
4438
  }
3980
4439
  });
3981
- const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
4440
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
3982
4441
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
3983
4442
  return {
3984
4443
  availableLocales: availableLocalesResult,
@@ -3991,8 +4450,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3991
4450
  * - Available status of the document for the current locale
3992
4451
  */
3993
4452
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
3994
- if (!document)
3995
- return document;
4453
+ if (!document) {
4454
+ return {
4455
+ data: document,
4456
+ meta: {
4457
+ availableLocales: [],
4458
+ availableStatus: []
4459
+ }
4460
+ };
4461
+ }
3996
4462
  const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
3997
4463
  if (!hasDraftAndPublish) {
3998
4464
  opts.availableStatus = false;
@@ -4042,26 +4508,9 @@ const sumDraftCounts = (entity, uid2) => {
4042
4508
  }, 0);
4043
4509
  };
4044
4510
  const { ApplicationError } = strapiUtils.errors;
4045
- const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
4046
4511
  const { PUBLISHED_AT_ATTRIBUTE } = strapiUtils.contentTypes.constants;
4047
4512
  const omitPublishedAtField = fp.omit(PUBLISHED_AT_ATTRIBUTE);
4048
4513
  const omitIdField = fp.omit("id");
4049
- const emitEvent = async (uid2, event, document) => {
4050
- const modelDef = strapi.getModel(uid2);
4051
- const sanitizedDocument = await strapiUtils.sanitize.sanitizers.defaultSanitizeOutput(
4052
- {
4053
- schema: modelDef,
4054
- getModel(uid22) {
4055
- return strapi.getModel(uid22);
4056
- }
4057
- },
4058
- document
4059
- );
4060
- strapi.eventHub.emit(event, {
4061
- model: modelDef.modelName,
4062
- entry: sanitizedDocument
4063
- });
4064
- };
4065
4514
  const documentManager = ({ strapi: strapi2 }) => {
4066
4515
  return {
4067
4516
  async findOne(id, uid2, opts = {}) {
@@ -4080,6 +4529,9 @@ const documentManager = ({ strapi: strapi2 }) => {
4080
4529
  } else if (opts.locale && opts.locale !== "*") {
4081
4530
  where.locale = opts.locale;
4082
4531
  }
4532
+ if (typeof opts.isPublished === "boolean") {
4533
+ where.publishedAt = { $notNull: opts.isPublished };
4534
+ }
4083
4535
  return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
4084
4536
  },
4085
4537
  async findMany(opts, uid2) {
@@ -4087,20 +4539,16 @@ const documentManager = ({ strapi: strapi2 }) => {
4087
4539
  return strapi2.documents(uid2).findMany(params);
4088
4540
  },
4089
4541
  async findPage(opts, uid2) {
4090
- const page = Number(opts?.page) || 1;
4091
- const pageSize = Number(opts?.pageSize) || 10;
4542
+ const params = strapiUtils.pagination.withDefaultPagination(opts || {}, {
4543
+ maxLimit: 1e3
4544
+ });
4092
4545
  const [documents, total = 0] = await Promise.all([
4093
- strapi2.documents(uid2).findMany(opts),
4094
- strapi2.documents(uid2).count(opts)
4546
+ strapi2.documents(uid2).findMany(params),
4547
+ strapi2.documents(uid2).count(params)
4095
4548
  ]);
4096
4549
  return {
4097
4550
  results: documents,
4098
- pagination: {
4099
- page,
4100
- pageSize,
4101
- pageCount: Math.ceil(total / pageSize),
4102
- total
4103
- }
4551
+ pagination: strapiUtils.pagination.transformPagedPaginationInfo(params, total)
4104
4552
  };
4105
4553
  },
4106
4554
  async create(uid2, opts = {}) {
@@ -4117,10 +4565,7 @@ const documentManager = ({ strapi: strapi2 }) => {
4117
4565
  async clone(id, body, uid2) {
4118
4566
  const populate = await buildDeepPopulate(uid2);
4119
4567
  const params = {
4120
- data: {
4121
- ...omitIdField(body),
4122
- [PUBLISHED_AT_ATTRIBUTE]: null
4123
- },
4568
+ data: omitIdField(body),
4124
4569
  populate
4125
4570
  };
4126
4571
  return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
@@ -4146,70 +4591,36 @@ const documentManager = ({ strapi: strapi2 }) => {
4146
4591
  return {};
4147
4592
  },
4148
4593
  // FIXME: handle relations
4149
- async deleteMany(opts, uid2) {
4150
- const docs = await strapi2.documents(uid2).findMany(opts);
4151
- for (const doc of docs) {
4152
- await strapi2.documents(uid2).delete({ documentId: doc.documentId });
4153
- }
4154
- return { count: docs.length };
4594
+ async deleteMany(documentIds, uid2, opts = {}) {
4595
+ const deletedEntries = await strapi2.db.transaction(async () => {
4596
+ return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
4597
+ });
4598
+ return { count: deletedEntries.length };
4155
4599
  },
4156
4600
  async publish(id, uid2, opts = {}) {
4157
4601
  const populate = await buildDeepPopulate(uid2);
4158
4602
  const params = { ...opts, populate };
4159
- return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries.at(0));
4603
+ return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
4160
4604
  },
4161
- async publishMany(entities, uid2) {
4162
- if (!entities.length) {
4163
- return null;
4164
- }
4165
- await Promise.all(
4166
- entities.map((document) => {
4167
- return strapi2.entityValidator.validateEntityCreation(
4168
- strapi2.getModel(uid2),
4169
- document,
4170
- void 0,
4171
- // @ts-expect-error - FIXME: entity here is unnecessary
4172
- document
4173
- );
4174
- })
4175
- );
4176
- const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4177
- const filters = { id: { $in: entitiesToPublish } };
4178
- const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
4179
- const populate = await buildDeepPopulate(uid2);
4180
- const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4181
- where: filters,
4182
- data
4183
- });
4184
- const publishedEntities = await strapi2.db.query(uid2).findMany({
4185
- where: filters,
4186
- populate
4605
+ async publishMany(uid2, documentIds, locale) {
4606
+ return strapi2.db.transaction(async () => {
4607
+ const results = await Promise.all(
4608
+ documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
4609
+ );
4610
+ const publishedEntitiesCount = results.flat().filter(Boolean).length;
4611
+ return publishedEntitiesCount;
4187
4612
  });
4188
- await Promise.all(
4189
- publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
4190
- );
4191
- return publishedEntitiesCount;
4192
4613
  },
4193
- async unpublishMany(documents, uid2) {
4194
- if (!documents.length) {
4195
- return null;
4196
- }
4197
- const entitiesToUnpublish = documents.filter((doc) => doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4198
- const filters = { id: { $in: entitiesToUnpublish } };
4199
- const data = { [PUBLISHED_AT_ATTRIBUTE]: null };
4200
- const populate = await buildDeepPopulate(uid2);
4201
- const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4202
- where: filters,
4203
- data
4204
- });
4205
- const unpublishedEntities = await strapi2.db.query(uid2).findMany({
4206
- where: filters,
4207
- populate
4614
+ async unpublishMany(documentIds, uid2, opts = {}) {
4615
+ const unpublishedEntries = await strapi2.db.transaction(async () => {
4616
+ return Promise.all(
4617
+ documentIds.map(
4618
+ (id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
4619
+ )
4620
+ );
4208
4621
  });
4209
- await Promise.all(
4210
- unpublishedEntities.map((doc) => emitEvent(uid2, ENTRY_UNPUBLISH, doc))
4211
- );
4212
- return unpublishedEntitiesCount;
4622
+ const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
4623
+ return { count: unpublishedEntitiesCount };
4213
4624
  },
4214
4625
  async unpublish(id, uid2, opts = {}) {
4215
4626
  const populate = await buildDeepPopulate(uid2);
@@ -4234,16 +4645,20 @@ const documentManager = ({ strapi: strapi2 }) => {
4234
4645
  }
4235
4646
  return sumDraftCounts(document, uid2);
4236
4647
  },
4237
- async countManyEntriesDraftRelations(ids, uid2, locale) {
4648
+ async countManyEntriesDraftRelations(documentIds, uid2, locale) {
4238
4649
  const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
4239
4650
  if (!hasRelations) {
4240
4651
  return 0;
4241
4652
  }
4653
+ let localeFilter = {};
4654
+ if (locale) {
4655
+ localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
4656
+ }
4242
4657
  const entities = await strapi2.db.query(uid2).findMany({
4243
4658
  populate,
4244
4659
  where: {
4245
- id: { $in: ids },
4246
- ...locale ? { locale } : {}
4660
+ documentId: { $in: documentIds },
4661
+ ...localeFilter
4247
4662
  }
4248
4663
  });
4249
4664
  const totalNumberDraftRelations = entities.reduce(
@@ -4266,7 +4681,8 @@ const services = {
4266
4681
  permission,
4267
4682
  "populate-builder": populateBuilder$1,
4268
4683
  uid,
4269
- ...history.services ? history.services : {}
4684
+ ...history.services ? history.services : {},
4685
+ ...preview.services ? preview.services : {}
4270
4686
  };
4271
4687
  const index = () => {
4272
4688
  return {