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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -1
  3. package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -1
  4. package/dist/_chunks/{ComponentConfigurationPage-WRPUXGd6.js → ComponentConfigurationPage-BLWQy8ru.js} +5 -6
  5. package/dist/_chunks/{ComponentConfigurationPage-WRPUXGd6.js.map → ComponentConfigurationPage-BLWQy8ru.js.map} +1 -1
  6. package/dist/_chunks/{ComponentConfigurationPage-gdUj_t-O.mjs → ComponentConfigurationPage-CtIa3aa2.mjs} +4 -4
  7. package/dist/_chunks/{ComponentConfigurationPage-gdUj_t-O.mjs.map → ComponentConfigurationPage-CtIa3aa2.mjs.map} +1 -1
  8. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js → ComponentIcon-CRbtQEUV.js} +2 -3
  9. package/dist/_chunks/{ComponentIcon-BXdiCGQp.js.map → ComponentIcon-CRbtQEUV.js.map} +1 -1
  10. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -1
  11. package/dist/_chunks/{EditConfigurationPage-BwuIPOJG.mjs → EditConfigurationPage-DsPR2DVk.mjs} +4 -4
  12. package/dist/_chunks/{EditConfigurationPage-BwuIPOJG.mjs.map → EditConfigurationPage-DsPR2DVk.mjs.map} +1 -1
  13. package/dist/_chunks/{EditConfigurationPage-C1vjMBgy.js → EditConfigurationPage-RQkymxCy.js} +5 -6
  14. package/dist/_chunks/{EditConfigurationPage-C1vjMBgy.js.map → EditConfigurationPage-RQkymxCy.js.map} +1 -1
  15. package/dist/_chunks/{EditViewPage-DbcGfyqK.js → EditViewPage-B-kExt8C.js} +63 -13
  16. package/dist/_chunks/EditViewPage-B-kExt8C.js.map +1 -0
  17. package/dist/_chunks/{EditViewPage-0MiFkXa8.mjs → EditViewPage-BPyVuPfM.mjs} +63 -12
  18. package/dist/_chunks/EditViewPage-BPyVuPfM.mjs.map +1 -0
  19. package/dist/_chunks/{Field-BG1xu38N.js → Field-DPIsQRre.js} +680 -261
  20. package/dist/_chunks/Field-DPIsQRre.js.map +1 -0
  21. package/dist/_chunks/{Field-BDMSCcy5.mjs → Field-Dltnt1km.mjs} +676 -257
  22. package/dist/_chunks/Field-Dltnt1km.mjs.map +1 -0
  23. package/dist/_chunks/FieldTypeIcon-CMlNO8PE.mjs.map +1 -1
  24. package/dist/_chunks/FieldTypeIcon-Dnwq_IRF.js.map +1 -1
  25. package/dist/_chunks/{Form-9BnFyUjy.js → Form-BFi4MXMT.js} +43 -22
  26. package/dist/_chunks/Form-BFi4MXMT.js.map +1 -0
  27. package/dist/_chunks/{Form-CPVWavB8.mjs → Form-C1IcWm1u.mjs} +41 -19
  28. package/dist/_chunks/Form-C1IcWm1u.mjs.map +1 -0
  29. package/dist/_chunks/{History-BVpd8LP3.mjs → History-04ChQ4pl.mjs} +108 -116
  30. package/dist/_chunks/History-04ChQ4pl.mjs.map +1 -0
  31. package/dist/_chunks/{History-BWWxLt2Z.js → History-wjcK4L0C.js} +107 -116
  32. package/dist/_chunks/History-wjcK4L0C.js.map +1 -0
  33. package/dist/_chunks/{ListConfigurationPage-DozVMKcR.mjs → ListConfigurationPage-BYqPYLSU.mjs} +25 -12
  34. package/dist/_chunks/ListConfigurationPage-BYqPYLSU.mjs.map +1 -0
  35. package/dist/_chunks/{ListConfigurationPage-6swzjdAZ.js → ListConfigurationPage-CRbxIC3J.js} +25 -13
  36. package/dist/_chunks/ListConfigurationPage-CRbxIC3J.js.map +1 -0
  37. package/dist/_chunks/{ListViewPage-BlzfjS2Q.js → ListViewPage-D5NY9183.js} +121 -81
  38. package/dist/_chunks/ListViewPage-D5NY9183.js.map +1 -0
  39. package/dist/_chunks/{ListViewPage-Ds0ulgfG.mjs → ListViewPage-FU2LBuhl.mjs} +118 -77
  40. package/dist/_chunks/ListViewPage-FU2LBuhl.mjs.map +1 -0
  41. package/dist/_chunks/{NoContentTypePage-D2nCCWEl.js → NoContentTypePage-BgQVE_Qb.js} +2 -2
  42. package/dist/_chunks/{NoContentTypePage-D2nCCWEl.js.map → NoContentTypePage-BgQVE_Qb.js.map} +1 -1
  43. package/dist/_chunks/{NoContentTypePage-BH11kaKt.mjs → NoContentTypePage-DCKUkwb8.mjs} +2 -2
  44. package/dist/_chunks/{NoContentTypePage-BH11kaKt.mjs.map → NoContentTypePage-DCKUkwb8.mjs.map} +1 -1
  45. package/dist/_chunks/{NoPermissionsPage-DN_JlsU2.js → NoPermissionsPage-C5jwn70o.js} +2 -2
  46. package/dist/_chunks/{NoPermissionsPage-DN_JlsU2.js.map → NoPermissionsPage-C5jwn70o.js.map} +1 -1
  47. package/dist/_chunks/{NoPermissionsPage-BT2Tn0D_.mjs → NoPermissionsPage-jqve7C8l.mjs} +2 -2
  48. package/dist/_chunks/{NoPermissionsPage-BT2Tn0D_.mjs.map → NoPermissionsPage-jqve7C8l.mjs.map} +1 -1
  49. package/dist/_chunks/Preview-BMYN548c.mjs +294 -0
  50. package/dist/_chunks/Preview-BMYN548c.mjs.map +1 -0
  51. package/dist/_chunks/Preview-DaOihysv.js +312 -0
  52. package/dist/_chunks/Preview-DaOihysv.js.map +1 -0
  53. package/dist/_chunks/{Relations-CcgFTcWo.js → Relations-CTGM7Hv5.js} +76 -43
  54. package/dist/_chunks/Relations-CTGM7Hv5.js.map +1 -0
  55. package/dist/_chunks/{Relations-Dnag3fhV.mjs → Relations-gscPkxjF.mjs} +76 -42
  56. package/dist/_chunks/Relations-gscPkxjF.mjs.map +1 -0
  57. package/dist/_chunks/{en-fbKQxLGn.js → en-BzQmavmK.js} +37 -18
  58. package/dist/_chunks/{en-fbKQxLGn.js.map → en-BzQmavmK.js.map} +1 -1
  59. package/dist/_chunks/{en-Ux26r5pl.mjs → en-CSxLmrh1.mjs} +37 -18
  60. package/dist/_chunks/{en-Ux26r5pl.mjs.map → en-CSxLmrh1.mjs.map} +1 -1
  61. package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
  62. package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
  63. package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
  64. package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
  65. package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
  66. package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
  67. package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
  68. package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
  69. package/dist/_chunks/hooks-BAaaKPS_.js.map +1 -1
  70. package/dist/_chunks/{index-CWpLBSt0.js → index-Ca7YWlAA.js} +1271 -746
  71. package/dist/_chunks/index-Ca7YWlAA.js.map +1 -0
  72. package/dist/_chunks/{index-JNNNKUHs.mjs → index-DqasUQ6Q.mjs} +1288 -763
  73. package/dist/_chunks/index-DqasUQ6Q.mjs.map +1 -0
  74. package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
  75. package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
  76. package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
  77. package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
  78. package/dist/_chunks/{layout--iHdZzRk.js → layout-BW80JSCd.js} +27 -15
  79. package/dist/_chunks/layout-BW80JSCd.js.map +1 -0
  80. package/dist/_chunks/{layout-DC503LnF.mjs → layout-W3clJSCy.mjs} +28 -15
  81. package/dist/_chunks/layout-W3clJSCy.mjs.map +1 -0
  82. package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
  83. package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
  84. package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
  85. package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
  86. package/dist/_chunks/{relations-CTje5t-a.mjs → relations-BlDkoeWh.mjs} +6 -7
  87. package/dist/_chunks/relations-BlDkoeWh.mjs.map +1 -0
  88. package/dist/_chunks/{relations-BbHizA5K.js → relations-C9Usz9k5.js} +6 -7
  89. package/dist/_chunks/relations-C9Usz9k5.js.map +1 -0
  90. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  91. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  92. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  93. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  94. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js → useDragAndDrop-BMtgCYzL.js} +5 -9
  95. package/dist/_chunks/{useDragAndDrop-J0TUUbR6.js.map → useDragAndDrop-BMtgCYzL.js.map} +1 -1
  96. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs → useDragAndDrop-DJ6jqvZN.mjs} +4 -7
  97. package/dist/_chunks/{useDragAndDrop-DdHgKsqq.mjs.map → useDragAndDrop-DJ6jqvZN.mjs.map} +1 -1
  98. package/dist/admin/index.js +3 -1
  99. package/dist/admin/index.js.map +1 -1
  100. package/dist/admin/index.mjs +5 -3
  101. package/dist/admin/src/content-manager.d.ts +3 -2
  102. package/dist/admin/src/exports.d.ts +2 -1
  103. package/dist/admin/src/history/index.d.ts +3 -0
  104. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  105. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  106. package/dist/admin/src/index.d.ts +1 -0
  107. package/dist/admin/src/pages/EditView/EditViewPage.d.ts +9 -1
  108. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +2 -1
  109. package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +3 -3
  110. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/Code.d.ts +7 -0
  111. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/Blocks/utils/prismLanguages.d.ts +49 -0
  112. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +5 -0
  113. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/DynamicComponent.d.ts +4 -1
  114. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +4 -1
  115. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  116. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  117. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  118. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +4 -48
  119. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  120. package/dist/admin/src/preview/components/PreviewContent.d.ts +2 -0
  121. package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
  122. package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
  123. package/dist/admin/src/preview/index.d.ts +4 -0
  124. package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
  125. package/dist/admin/src/preview/routes.d.ts +3 -0
  126. package/dist/admin/src/preview/services/preview.d.ts +3 -0
  127. package/dist/admin/src/router.d.ts +1 -1
  128. package/dist/admin/src/services/api.d.ts +1 -1
  129. package/dist/admin/src/services/components.d.ts +2 -2
  130. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  131. package/dist/admin/src/services/documents.d.ts +19 -20
  132. package/dist/admin/src/services/init.d.ts +1 -1
  133. package/dist/admin/src/services/relations.d.ts +2 -2
  134. package/dist/admin/src/services/uid.d.ts +3 -3
  135. package/dist/admin/src/utils/validation.d.ts +4 -1
  136. package/dist/server/index.js +765 -437
  137. package/dist/server/index.js.map +1 -1
  138. package/dist/server/index.mjs +766 -437
  139. package/dist/server/index.mjs.map +1 -1
  140. package/dist/server/src/bootstrap.d.ts.map +1 -1
  141. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  142. package/dist/server/src/controllers/index.d.ts.map +1 -1
  143. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  144. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  145. package/dist/server/src/controllers/utils/metadata.d.ts +16 -1
  146. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -1
  147. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  148. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  149. package/dist/server/src/history/controllers/history-version.d.ts +1 -1
  150. package/dist/server/src/history/controllers/history-version.d.ts.map +1 -1
  151. package/dist/server/src/history/services/history.d.ts +3 -3
  152. package/dist/server/src/history/services/history.d.ts.map +1 -1
  153. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  154. package/dist/server/src/history/services/utils.d.ts +8 -12
  155. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  156. package/dist/server/src/index.d.ts +7 -6
  157. package/dist/server/src/index.d.ts.map +1 -1
  158. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  159. package/dist/server/src/preview/controllers/index.d.ts +2 -0
  160. package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
  161. package/dist/server/src/preview/controllers/preview.d.ts +13 -0
  162. package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
  163. package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
  164. package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
  165. package/dist/server/src/preview/index.d.ts +4 -0
  166. package/dist/server/src/preview/index.d.ts.map +1 -0
  167. package/dist/server/src/preview/routes/index.d.ts +8 -0
  168. package/dist/server/src/preview/routes/index.d.ts.map +1 -0
  169. package/dist/server/src/preview/routes/preview.d.ts +4 -0
  170. package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
  171. package/dist/server/src/preview/services/index.d.ts +16 -0
  172. package/dist/server/src/preview/services/index.d.ts.map +1 -0
  173. package/dist/server/src/preview/services/preview-config.d.ts +32 -0
  174. package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
  175. package/dist/server/src/preview/services/preview.d.ts +12 -0
  176. package/dist/server/src/preview/services/preview.d.ts.map +1 -0
  177. package/dist/server/src/preview/utils.d.ts +19 -0
  178. package/dist/server/src/preview/utils.d.ts.map +1 -0
  179. package/dist/server/src/register.d.ts.map +1 -1
  180. package/dist/server/src/routes/index.d.ts.map +1 -1
  181. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  182. package/dist/server/src/services/document-metadata.d.ts +12 -10
  183. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  184. package/dist/server/src/services/index.d.ts +7 -6
  185. package/dist/server/src/services/index.d.ts.map +1 -1
  186. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  187. package/dist/server/src/services/utils/populate.d.ts +2 -2
  188. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  189. package/dist/server/src/utils/index.d.ts +2 -0
  190. package/dist/server/src/utils/index.d.ts.map +1 -1
  191. package/dist/shared/contracts/collection-types.d.ts +3 -1
  192. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  193. package/dist/shared/contracts/index.d.ts +1 -0
  194. package/dist/shared/contracts/index.d.ts.map +1 -1
  195. package/dist/shared/contracts/preview.d.ts +27 -0
  196. package/dist/shared/contracts/preview.d.ts.map +1 -0
  197. package/dist/shared/index.js +4 -0
  198. package/dist/shared/index.js.map +1 -1
  199. package/dist/shared/index.mjs +4 -0
  200. package/dist/shared/index.mjs.map +1 -1
  201. package/package.json +17 -15
  202. package/dist/_chunks/EditViewPage-0MiFkXa8.mjs.map +0 -1
  203. package/dist/_chunks/EditViewPage-DbcGfyqK.js.map +0 -1
  204. package/dist/_chunks/Field-BDMSCcy5.mjs.map +0 -1
  205. package/dist/_chunks/Field-BG1xu38N.js.map +0 -1
  206. package/dist/_chunks/Form-9BnFyUjy.js.map +0 -1
  207. package/dist/_chunks/Form-CPVWavB8.mjs.map +0 -1
  208. package/dist/_chunks/History-BVpd8LP3.mjs.map +0 -1
  209. package/dist/_chunks/History-BWWxLt2Z.js.map +0 -1
  210. package/dist/_chunks/ListConfigurationPage-6swzjdAZ.js.map +0 -1
  211. package/dist/_chunks/ListConfigurationPage-DozVMKcR.mjs.map +0 -1
  212. package/dist/_chunks/ListViewPage-BlzfjS2Q.js.map +0 -1
  213. package/dist/_chunks/ListViewPage-Ds0ulgfG.mjs.map +0 -1
  214. package/dist/_chunks/Relations-CcgFTcWo.js.map +0 -1
  215. package/dist/_chunks/Relations-Dnag3fhV.mjs.map +0 -1
  216. package/dist/_chunks/index-CWpLBSt0.js.map +0 -1
  217. package/dist/_chunks/index-JNNNKUHs.mjs.map +0 -1
  218. package/dist/_chunks/layout--iHdZzRk.js.map +0 -1
  219. package/dist/_chunks/layout-DC503LnF.mjs.map +0 -1
  220. package/dist/_chunks/relations-BbHizA5K.js.map +0 -1
  221. package/dist/_chunks/relations-CTje5t-a.mjs.map +0 -1
  222. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  223. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  224. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  225. package/strapi-server.js +0 -3
@@ -1,5 +1,5 @@
1
- import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1, traverseEntity, pagination } from "@strapi/utils";
2
- import { pick, omit, difference, intersection, pipe, propOr, isEqual, isEmpty, set, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, isNil as isNil$1, getOr, propEq, merge, groupBy, castArray } from "lodash/fp";
1
+ import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, traverseEntity, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1, pagination } from "@strapi/utils";
2
+ import { pick, omit, difference, castArray, mergeWith, intersection, pipe, propOr, isEqual, isEmpty, set, isNil as isNil$1, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, getOr, propEq, merge, groupBy } from "lodash/fp";
3
3
  import "@strapi/types";
4
4
  import * as yup from "yup";
5
5
  import { scheduleJob } from "node-schedule";
@@ -7,10 +7,10 @@ import isNil from "lodash/isNil";
7
7
  import _, { intersection as intersection$1, difference as difference$1 } from "lodash";
8
8
  import qs from "qs";
9
9
  import slugify from "@sindresorhus/slugify";
10
- const getService$1 = (name) => {
10
+ const getService$2 = (name) => {
11
11
  return strapi.plugin("content-manager").service(name);
12
12
  };
13
- function getService(strapi2, name) {
13
+ function getService$1(strapi2, name) {
14
14
  return strapi2.service(`plugin::content-manager.${name}`);
15
15
  }
16
16
  const historyRestoreVersionSchema = yup.object().shape({
@@ -46,7 +46,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
46
46
  if (!isSingleType && (!contentTypeUid || !ctx.query.documentId)) {
47
47
  throw new errors.ForbiddenError("contentType and documentId are required");
48
48
  }
49
- const permissionChecker2 = getService$1("permission-checker").create({
49
+ const permissionChecker2 = getService$2("permission-checker").create({
50
50
  userAbility: ctx.state.userAbility,
51
51
  model: ctx.query.contentType
52
52
  });
@@ -54,7 +54,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
54
54
  return ctx.forbidden();
55
55
  }
56
56
  const query = await permissionChecker2.sanitizeQuery(ctx.query);
57
- const { results, pagination: pagination2 } = await getService(strapi2, "history").findVersionsPage({
57
+ const { results, pagination: pagination2 } = await getService$1(strapi2, "history").findVersionsPage({
58
58
  query: {
59
59
  ...query,
60
60
  ...getValidPagination({ page: query.page, pageSize: query.pageSize })
@@ -79,14 +79,14 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
79
79
  async restoreVersion(ctx) {
80
80
  const request = ctx.request;
81
81
  await validateRestoreVersion(request.body, "contentType is required");
82
- const permissionChecker2 = getService$1("permission-checker").create({
82
+ const permissionChecker2 = getService$2("permission-checker").create({
83
83
  userAbility: ctx.state.userAbility,
84
84
  model: request.body.contentType
85
85
  });
86
86
  if (permissionChecker2.cannot.update()) {
87
87
  throw new errors.ForbiddenError();
88
88
  }
89
- const restoredDocument = await getService(strapi2, "history").restoreVersion(
89
+ const restoredDocument = await getService$1(strapi2, "history").restoreVersion(
90
90
  request.params.versionId
91
91
  );
92
92
  return {
@@ -95,7 +95,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
95
95
  }
96
96
  };
97
97
  };
98
- const controllers$1 = {
98
+ const controllers$2 = {
99
99
  "history-version": createHistoryVersionController
100
100
  /**
101
101
  * Casting is needed because the types aren't aware that Strapi supports
@@ -141,8 +141,7 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
141
141
  };
142
142
  const getRelationRestoreValue = async (versionRelationData, attribute) => {
143
143
  if (Array.isArray(versionRelationData)) {
144
- if (versionRelationData.length === 0)
145
- return versionRelationData;
144
+ if (versionRelationData.length === 0) return versionRelationData;
146
145
  const existingAndMissingRelations = await Promise.all(
147
146
  versionRelationData.map((relation) => {
148
147
  return strapi2.documents(attribute.target).findOne({
@@ -151,19 +150,16 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
151
150
  });
152
151
  })
153
152
  );
154
- return existingAndMissingRelations.filter(
155
- (relation) => relation !== null
156
- );
153
+ return existingAndMissingRelations.filter((relation) => relation !== null);
157
154
  }
158
155
  return strapi2.documents(attribute.target).findOne({
159
156
  documentId: versionRelationData.documentId,
160
157
  locale: versionRelationData.locale || void 0
161
158
  });
162
159
  };
163
- const getMediaRestoreValue = async (versionRelationData, attribute) => {
164
- if (attribute.multiple) {
160
+ const getMediaRestoreValue = async (versionRelationData) => {
161
+ if (Array.isArray(versionRelationData)) {
165
162
  const existingAndMissingMedias = await Promise.all(
166
- // @ts-expect-error Fix the type definitions so this isn't any
167
163
  versionRelationData.map((media) => {
168
164
  return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
169
165
  })
@@ -173,10 +169,11 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
173
169
  return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
174
170
  };
175
171
  const localesService = strapi2.plugin("i18n")?.service("locales");
172
+ const i18nContentTypeService = strapi2.plugin("i18n")?.service("content-types");
176
173
  const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
174
+ const isLocalizedContentType = (model) => i18nContentTypeService ? i18nContentTypeService.isLocalizedContentType(model) : false;
177
175
  const getLocaleDictionary = async () => {
178
- if (!localesService)
179
- return {};
176
+ if (!localesService) return {};
180
177
  const locales = await localesService.find() || [];
181
178
  return locales.reduce(
182
179
  (acc, locale) => {
@@ -200,31 +197,53 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
200
197
  const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
201
198
  return documentMetadataService.getStatus(document, meta.availableStatus);
202
199
  };
203
- const getDeepPopulate2 = (uid2) => {
200
+ const getComponentFields = (componentUID) => {
201
+ return Object.entries(strapi2.getModel(componentUID).attributes).reduce(
202
+ (fieldsAcc, [key, attribute]) => {
203
+ if (!["relation", "media", "component", "dynamiczone"].includes(attribute.type)) {
204
+ fieldsAcc.push(key);
205
+ }
206
+ return fieldsAcc;
207
+ },
208
+ []
209
+ );
210
+ };
211
+ const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
204
212
  const model = strapi2.getModel(uid2);
205
213
  const attributes = Object.entries(model.attributes);
214
+ const fieldSelector = useDatabaseSyntax ? "select" : "fields";
206
215
  return attributes.reduce((acc, [attributeName, attribute]) => {
207
216
  switch (attribute.type) {
208
217
  case "relation": {
218
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
219
+ if (isMorphRelation) {
220
+ break;
221
+ }
209
222
  const isVisible2 = contentTypes$1.isVisibleAttribute(model, attributeName);
210
223
  if (isVisible2) {
211
- acc[attributeName] = { fields: ["documentId", "locale", "publishedAt"] };
224
+ acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
212
225
  }
213
226
  break;
214
227
  }
215
228
  case "media": {
216
- acc[attributeName] = { fields: ["id"] };
229
+ acc[attributeName] = { [fieldSelector]: ["id"] };
217
230
  break;
218
231
  }
219
232
  case "component": {
220
233
  const populate = getDeepPopulate2(attribute.component);
221
- acc[attributeName] = { populate };
234
+ acc[attributeName] = {
235
+ populate,
236
+ [fieldSelector]: getComponentFields(attribute.component)
237
+ };
222
238
  break;
223
239
  }
224
240
  case "dynamiczone": {
225
241
  const populatedComponents = (attribute.components || []).reduce(
226
242
  (acc2, componentUID) => {
227
- acc2[componentUID] = { populate: getDeepPopulate2(componentUID) };
243
+ acc2[componentUID] = {
244
+ populate: getDeepPopulate2(componentUID),
245
+ [fieldSelector]: getComponentFields(componentUID)
246
+ };
228
247
  return acc2;
229
248
  },
230
249
  {}
@@ -286,6 +305,7 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
286
305
  getRelationRestoreValue,
287
306
  getMediaRestoreValue,
288
307
  getDefaultLocale,
308
+ isLocalizedContentType,
289
309
  getLocaleDictionary,
290
310
  getRetentionDays,
291
311
  getVersionStatus,
@@ -308,7 +328,13 @@ const createHistoryService = ({ strapi: strapi2 }) => {
308
328
  });
309
329
  },
310
330
  async findVersionsPage(params) {
311
- const locale = params.query.locale || await serviceUtils.getDefaultLocale();
331
+ const schema = strapi2.getModel(params.query.contentType);
332
+ const isLocalizedContentType = serviceUtils.isLocalizedContentType(schema);
333
+ const defaultLocale = await serviceUtils.getDefaultLocale();
334
+ let locale = null;
335
+ if (isLocalizedContentType) {
336
+ locale = params.query.locale || defaultLocale;
337
+ }
312
338
  const [{ results, pagination: pagination2 }, localeDictionary] = await Promise.all([
313
339
  query.findPage({
314
340
  ...params.query,
@@ -324,78 +350,76 @@ const createHistoryService = ({ strapi: strapi2 }) => {
324
350
  }),
325
351
  serviceUtils.getLocaleDictionary()
326
352
  ]);
327
- const populateEntryRelations = async (entry) => {
328
- const entryWithRelations = await Object.entries(entry.schema).reduce(
329
- async (currentDataWithRelations, [attributeKey, attributeSchema]) => {
330
- const attributeValue = entry.data[attributeKey];
331
- const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
332
- if (attributeSchema.type === "media") {
333
- const permissionChecker2 = getService$1("permission-checker").create({
334
- userAbility: params.state.userAbility,
335
- model: "plugin::upload.file"
336
- });
337
- const response = await serviceUtils.buildMediaResponse(attributeValues);
338
- const sanitizedResults = await Promise.all(
339
- response.results.map((media) => permissionChecker2.sanitizeOutput(media))
340
- );
341
- return {
342
- ...await currentDataWithRelations,
343
- [attributeKey]: {
344
- results: sanitizedResults,
345
- meta: response.meta
346
- }
347
- };
353
+ const populateEntry = async (entry) => {
354
+ return traverseEntity(
355
+ async (options, utils) => {
356
+ if (!options.attribute) return;
357
+ if (!options.value) return;
358
+ const currentValue = Array.isArray(options.value) ? options.value : [options.value];
359
+ if (options.attribute.type === "component") {
360
+ utils.remove("id");
348
361
  }
349
- if (attributeSchema.type === "relation" && attributeSchema.relation !== "morphToOne" && attributeSchema.relation !== "morphToMany") {
350
- if (attributeSchema.target === "admin::user") {
362
+ if (options.attribute.type === "relation" && // TODO: handle polymorphic relations
363
+ options.attribute.relation !== "morphToOne" && options.attribute.relation !== "morphToMany") {
364
+ if (options.attribute.target === "admin::user") {
351
365
  const adminUsers = await Promise.all(
352
- attributeValues.map((userToPopulate) => {
366
+ currentValue.map((userToPopulate) => {
353
367
  if (userToPopulate == null) {
354
368
  return null;
355
369
  }
356
- return strapi2.query("admin::user").findOne({ where: { id: userToPopulate.id } });
370
+ return strapi2.query("admin::user").findOne({
371
+ where: {
372
+ ...userToPopulate.id ? { id: userToPopulate.id } : {},
373
+ ...userToPopulate.documentId ? { documentId: userToPopulate.documentId } : {}
374
+ }
375
+ });
357
376
  })
358
377
  );
359
- return {
360
- ...await currentDataWithRelations,
361
- /**
362
- * Ideally we would return the same "{results: [], meta: {}}" shape, however,
363
- * when sanitizing the data as a whole in the controller before sending to the client,
364
- * the data for admin relation user is completely sanitized if we return an object here as opposed to an array.
365
- */
366
- [attributeKey]: adminUsers
367
- };
378
+ utils.set(options.key, adminUsers);
368
379
  }
369
- const permissionChecker2 = getService$1("permission-checker").create({
380
+ const permissionChecker2 = getService$2("permission-checker").create({
370
381
  userAbility: params.state.userAbility,
371
- model: attributeSchema.target
382
+ model: options.attribute.target
372
383
  });
373
384
  const response = await serviceUtils.buildRelationReponse(
374
- attributeValues,
375
- attributeSchema
385
+ currentValue,
386
+ options.attribute
376
387
  );
377
388
  const sanitizedResults = await Promise.all(
378
389
  response.results.map((media) => permissionChecker2.sanitizeOutput(media))
379
390
  );
380
- return {
381
- ...await currentDataWithRelations,
382
- [attributeKey]: {
383
- results: sanitizedResults,
384
- meta: response.meta
385
- }
386
- };
391
+ utils.set(options.key, {
392
+ results: sanitizedResults,
393
+ meta: response.meta
394
+ });
387
395
  }
388
- return currentDataWithRelations;
396
+ if (options.attribute.type === "media") {
397
+ const permissionChecker2 = getService$2("permission-checker").create({
398
+ userAbility: params.state.userAbility,
399
+ model: "plugin::upload.file"
400
+ });
401
+ const response = await serviceUtils.buildMediaResponse(currentValue);
402
+ const sanitizedResults = await Promise.all(
403
+ response.results.map((media) => permissionChecker2.sanitizeOutput(media))
404
+ );
405
+ utils.set(options.key, {
406
+ results: sanitizedResults,
407
+ meta: response.meta
408
+ });
409
+ }
410
+ },
411
+ {
412
+ schema,
413
+ getModel: strapi2.getModel.bind(strapi2)
389
414
  },
390
- Promise.resolve(entry.data)
415
+ entry.data
391
416
  );
392
- return entryWithRelations;
393
417
  };
394
418
  const formattedResults = await Promise.all(
395
419
  results.map(async (result) => {
396
420
  return {
397
421
  ...result,
398
- data: await populateEntryRelations(result),
422
+ data: await populateEntry(result),
399
423
  meta: {
400
424
  unknownAttributes: serviceUtils.getSchemaAttributesDiff(
401
425
  result.schema,
@@ -426,30 +450,44 @@ const createHistoryService = ({ strapi: strapi2 }) => {
426
450
  // Clone to avoid mutating the original version data
427
451
  structuredClone(version.data)
428
452
  );
429
- const sanitizedSchemaAttributes = omit(
430
- FIELDS_TO_IGNORE,
431
- contentTypeSchemaAttributes
432
- );
433
- const reducer = async.reduce(Object.entries(sanitizedSchemaAttributes));
434
- const dataWithoutMissingRelations = await reducer(
435
- async (previousRelationAttributes, [name, attribute]) => {
436
- const versionRelationData = version.data[name];
437
- if (!versionRelationData) {
438
- return previousRelationAttributes;
453
+ const schema = structuredClone(version.schema);
454
+ schema.attributes = omit(FIELDS_TO_IGNORE, contentTypeSchemaAttributes);
455
+ const dataWithoutMissingRelations = await traverseEntity(
456
+ async (options, utils) => {
457
+ if (!options.attribute) return;
458
+ if (options.attribute.type === "component") {
459
+ utils.remove("id");
460
+ if (options.attribute.repeatable && options.value === null) {
461
+ utils.set(options.key, []);
462
+ }
439
463
  }
440
- if (attribute.type === "relation" && // TODO: handle polymorphic relations
441
- attribute.relation !== "morphToOne" && attribute.relation !== "morphToMany") {
442
- const data2 = await serviceUtils.getRelationRestoreValue(versionRelationData, attribute);
443
- previousRelationAttributes[name] = data2;
464
+ if (options.attribute.type === "dynamiczone") {
465
+ if (options.value === null) {
466
+ utils.set(options.key, []);
467
+ }
444
468
  }
445
- if (attribute.type === "media") {
446
- const data2 = await serviceUtils.getMediaRestoreValue(versionRelationData, attribute);
447
- previousRelationAttributes[name] = data2;
469
+ if (options.attribute.type === "relation" && // TODO: handle polymorphic relations
470
+ options.attribute.relation !== "morphToOne" && options.attribute.relation !== "morphToMany") {
471
+ if (!options.value) return;
472
+ const data2 = await serviceUtils.getRelationRestoreValue(
473
+ options.value,
474
+ options.attribute
475
+ );
476
+ utils.set(options.key, data2);
477
+ }
478
+ if (options.attribute.type === "media") {
479
+ if (!options.value) return;
480
+ const data2 = await serviceUtils.getMediaRestoreValue(
481
+ options.value
482
+ );
483
+ utils.set(options.key, data2);
448
484
  }
449
- return previousRelationAttributes;
450
485
  },
451
- // Clone to avoid mutating the original version data
452
- structuredClone(dataWithoutAddedAttributes)
486
+ {
487
+ schema,
488
+ getModel: strapi2.getModel.bind(strapi2)
489
+ },
490
+ dataWithoutAddedAttributes
453
491
  );
454
492
  const data = omit(["id", ...Object.keys(schemaDiff.removed)], dataWithoutMissingRelations);
455
493
  const restoredDocument = await strapi2.documents(version.contentType).update({
@@ -464,13 +502,47 @@ const createHistoryService = ({ strapi: strapi2 }) => {
464
502
  }
465
503
  };
466
504
  };
505
+ const shouldCreateHistoryVersion = (context) => {
506
+ if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
507
+ return false;
508
+ }
509
+ if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
510
+ return false;
511
+ }
512
+ if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
513
+ return false;
514
+ }
515
+ if (!context.contentType.uid.startsWith("api::")) {
516
+ return false;
517
+ }
518
+ return true;
519
+ };
520
+ const getSchemas = (uid2) => {
521
+ const attributesSchema = strapi.getModel(uid2).attributes;
522
+ const componentsSchemas = Object.keys(attributesSchema).reduce(
523
+ (currentComponentSchemas, key) => {
524
+ const fieldSchema = attributesSchema[key];
525
+ if (fieldSchema.type === "component") {
526
+ const componentSchema = strapi.getModel(fieldSchema.component).attributes;
527
+ return {
528
+ ...currentComponentSchemas,
529
+ [fieldSchema.component]: componentSchema
530
+ };
531
+ }
532
+ return currentComponentSchemas;
533
+ },
534
+ {}
535
+ );
536
+ return {
537
+ schema: omit(FIELDS_TO_IGNORE, attributesSchema),
538
+ componentsSchemas
539
+ };
540
+ };
467
541
  const createLifecyclesService = ({ strapi: strapi2 }) => {
468
542
  const state = {
469
543
  deleteExpiredJob: null,
470
544
  isInitialized: false
471
545
  };
472
- const query = strapi2.db.query(HISTORY_VERSION_UID);
473
- const historyService = getService(strapi2, "history");
474
546
  const serviceUtils = createServiceUtils({ strapi: strapi2 });
475
547
  return {
476
548
  async bootstrap() {
@@ -478,76 +550,62 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
478
550
  return;
479
551
  }
480
552
  strapi2.documents.use(async (context, next) => {
481
- if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
482
- return next();
483
- }
484
- if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
485
- return next();
486
- }
487
- if (context.action === "update" && strapi2.requestContext.get()?.request.url.endsWith("/actions/publish")) {
488
- return next();
489
- }
490
- const contentTypeUid = context.contentType.uid;
491
- if (!contentTypeUid.startsWith("api::")) {
492
- return next();
493
- }
494
553
  const result = await next();
495
- const documentContext = {
496
- documentId: context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId,
497
- locale: context.params?.locale
498
- };
554
+ if (!shouldCreateHistoryVersion(context)) {
555
+ return result;
556
+ }
557
+ const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
499
558
  const defaultLocale = await serviceUtils.getDefaultLocale();
500
- const locale = documentContext.locale || defaultLocale;
501
- if (Array.isArray(locale)) {
502
- strapi2.log.warn(
503
- "[Content manager history middleware]: An array of locales was provided, but only a single locale is supported for the findOne operation."
504
- );
505
- return next();
559
+ const locales = castArray(context.params?.locale || defaultLocale);
560
+ if (!locales.length) {
561
+ return result;
506
562
  }
507
- const document = await strapi2.documents(contentTypeUid).findOne({
508
- documentId: documentContext.documentId,
509
- locale,
510
- populate: serviceUtils.getDeepPopulate(contentTypeUid)
563
+ const uid2 = context.contentType.uid;
564
+ const schemas = getSchemas(uid2);
565
+ const model = strapi2.getModel(uid2);
566
+ const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
567
+ const localeEntries = await strapi2.db.query(uid2).findMany({
568
+ where: {
569
+ documentId,
570
+ ...isLocalizedContentType ? { locale: { $in: locales } } : {},
571
+ ...contentTypes$1.hasDraftAndPublish(strapi2.contentTypes[uid2]) ? { publishedAt: null } : {}
572
+ },
573
+ populate: serviceUtils.getDeepPopulate(
574
+ uid2,
575
+ true
576
+ /* use database syntax */
577
+ )
511
578
  });
512
- const status = await serviceUtils.getVersionStatus(contentTypeUid, document);
513
- const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
514
- const componentsSchemas = Object.keys(
515
- attributesSchema
516
- ).reduce((currentComponentSchemas, key) => {
517
- const fieldSchema = attributesSchema[key];
518
- if (fieldSchema.type === "component") {
519
- const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
520
- return {
521
- ...currentComponentSchemas,
522
- [fieldSchema.component]: componentSchema
523
- };
524
- }
525
- return currentComponentSchemas;
526
- }, {});
527
579
  await strapi2.db.transaction(async ({ onCommit }) => {
528
- onCommit(() => {
529
- historyService.createVersion({
530
- contentType: contentTypeUid,
531
- data: omit(FIELDS_TO_IGNORE, document),
532
- schema: omit(FIELDS_TO_IGNORE, attributesSchema),
533
- componentsSchemas,
534
- relatedDocumentId: documentContext.documentId,
535
- locale,
536
- status
537
- });
580
+ onCommit(async () => {
581
+ for (const entry of localeEntries) {
582
+ const status = await serviceUtils.getVersionStatus(uid2, entry);
583
+ await getService$1(strapi2, "history").createVersion({
584
+ contentType: uid2,
585
+ data: omit(FIELDS_TO_IGNORE, entry),
586
+ relatedDocumentId: documentId,
587
+ locale: entry.locale,
588
+ status,
589
+ ...schemas
590
+ });
591
+ }
538
592
  });
539
593
  });
540
594
  return result;
541
595
  });
542
- state.deleteExpiredJob = scheduleJob("0 0 * * *", () => {
596
+ state.deleteExpiredJob = scheduleJob("historyDaily", "0 0 * * *", () => {
543
597
  const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
544
598
  const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
545
- query.deleteMany({
599
+ strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
546
600
  where: {
547
601
  created_at: {
548
- $lt: expirationDate.toISOString()
602
+ $lt: expirationDate
549
603
  }
550
604
  }
605
+ }).catch((error) => {
606
+ if (error instanceof Error) {
607
+ strapi2.log.error("Error deleting expired history versions", error.message);
608
+ }
551
609
  });
552
610
  });
553
611
  state.isInitialized = true;
@@ -559,17 +617,17 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
559
617
  }
560
618
  };
561
619
  };
562
- const services$1 = {
620
+ const services$2 = {
563
621
  history: createHistoryService,
564
622
  lifecycles: createLifecyclesService
565
623
  };
566
- const info = { pluginName: "content-manager", type: "admin" };
624
+ const info$1 = { pluginName: "content-manager", type: "admin" };
567
625
  const historyVersionRouter = {
568
626
  type: "admin",
569
627
  routes: [
570
628
  {
571
629
  method: "GET",
572
- info,
630
+ info: info$1,
573
631
  path: "/history-versions",
574
632
  handler: "history-version.findMany",
575
633
  config: {
@@ -578,7 +636,7 @@ const historyVersionRouter = {
578
636
  },
579
637
  {
580
638
  method: "PUT",
581
- info,
639
+ info: info$1,
582
640
  path: "/history-versions/:versionId/restore",
583
641
  handler: "history-version.restoreVersion",
584
642
  config: {
@@ -587,7 +645,7 @@ const historyVersionRouter = {
587
645
  }
588
646
  ]
589
647
  };
590
- const routes$1 = {
648
+ const routes$2 = {
591
649
  "history-version": historyVersionRouter
592
650
  };
593
651
  const historyVersion = {
@@ -634,21 +692,21 @@ const historyVersion = {
634
692
  }
635
693
  }
636
694
  };
637
- const getFeature = () => {
695
+ const getFeature$1 = () => {
638
696
  if (strapi.ee.features.isEnabled("cms-content-history")) {
639
697
  return {
640
698
  register({ strapi: strapi2 }) {
641
699
  strapi2.get("models").add(historyVersion);
642
700
  },
643
701
  bootstrap({ strapi: strapi2 }) {
644
- getService(strapi2, "lifecycles").bootstrap();
702
+ getService$1(strapi2, "lifecycles").bootstrap();
645
703
  },
646
704
  destroy({ strapi: strapi2 }) {
647
- getService(strapi2, "lifecycles").destroy();
705
+ getService$1(strapi2, "lifecycles").destroy();
648
706
  },
649
- controllers: controllers$1,
650
- services: services$1,
651
- routes: routes$1
707
+ controllers: controllers$2,
708
+ services: services$2,
709
+ routes: routes$2
652
710
  };
653
711
  }
654
712
  return {
@@ -657,9 +715,201 @@ const getFeature = () => {
657
715
  }
658
716
  };
659
717
  };
660
- const history = getFeature();
718
+ const history = getFeature$1();
719
+ const info = { pluginName: "content-manager", type: "admin" };
720
+ const previewRouter = {
721
+ type: "admin",
722
+ routes: [
723
+ {
724
+ method: "GET",
725
+ info,
726
+ path: "/preview/url/:contentType",
727
+ handler: "preview.getPreviewUrl",
728
+ config: {
729
+ policies: ["admin::isAuthenticatedAdmin"]
730
+ }
731
+ }
732
+ ]
733
+ };
734
+ const routes$1 = {
735
+ preview: previewRouter
736
+ };
737
+ function getService(strapi2, name) {
738
+ return strapi2.service(`plugin::content-manager.${name}`);
739
+ }
740
+ const getPreviewUrlSchema = yup.object().shape({
741
+ // Will be undefined for single types
742
+ documentId: yup.string(),
743
+ locale: yup.string().nullable(),
744
+ status: yup.string()
745
+ }).required();
746
+ const validatePreviewUrl = async (strapi2, uid2, params) => {
747
+ await validateYupSchema(getPreviewUrlSchema)(params);
748
+ const newParams = pick(["documentId", "locale", "status"], params);
749
+ const model = strapi2.getModel(uid2);
750
+ if (!model || model.modelType !== "contentType") {
751
+ throw new errors.ValidationError("Invalid content type");
752
+ }
753
+ const isSingleType = model?.kind === "singleType";
754
+ if (!isSingleType && !params.documentId) {
755
+ throw new errors.ValidationError("documentId is required for Collection Types");
756
+ }
757
+ if (isSingleType) {
758
+ const doc = await strapi2.documents(uid2).findFirst();
759
+ if (!doc) {
760
+ throw new errors.NotFoundError("Document not found");
761
+ }
762
+ newParams.documentId = doc?.documentId;
763
+ }
764
+ if (!newParams.status) {
765
+ const isDPEnabled = model?.options?.draftAndPublish;
766
+ newParams.status = isDPEnabled ? "draft" : "published";
767
+ }
768
+ return newParams;
769
+ };
770
+ const createPreviewController = () => {
771
+ return {
772
+ /**
773
+ * Transforms an entry into a preview URL, so that it can be previewed
774
+ * in the Content Manager.
775
+ */
776
+ async getPreviewUrl(ctx) {
777
+ const uid2 = ctx.params.contentType;
778
+ const query = ctx.request.query;
779
+ const params = await validatePreviewUrl(strapi, uid2, query);
780
+ const previewService = getService(strapi, "preview");
781
+ const url = await previewService.getPreviewUrl(uid2, params);
782
+ if (!url) {
783
+ ctx.status = 204;
784
+ }
785
+ return {
786
+ data: { url }
787
+ };
788
+ }
789
+ };
790
+ };
791
+ const controllers$1 = {
792
+ preview: createPreviewController
793
+ /**
794
+ * Casting is needed because the types aren't aware that Strapi supports
795
+ * passing a controller factory as the value, instead of a controller object directly
796
+ */
797
+ };
798
+ const createPreviewService = ({ strapi: strapi2 }) => {
799
+ const config = getService(strapi2, "preview-config");
800
+ return {
801
+ async getPreviewUrl(uid2, params) {
802
+ const handler = config.getPreviewHandler();
803
+ try {
804
+ return handler(uid2, params);
805
+ } catch (error) {
806
+ strapi2.log.error(`Failed to get preview URL: ${error}`);
807
+ throw new errors.ApplicationError("Failed to get preview URL");
808
+ }
809
+ return;
810
+ }
811
+ };
812
+ };
813
+ const extendMiddlewareConfiguration = (middleware = { name: "", config: {} }) => {
814
+ const middlewares = strapi.config.get("middlewares");
815
+ const configuredMiddlewares = middlewares.map((currentMiddleware) => {
816
+ if (currentMiddleware === middleware.name) {
817
+ return middleware;
818
+ }
819
+ if (currentMiddleware.name === middleware.name) {
820
+ return mergeWith(
821
+ (objValue, srcValue) => {
822
+ if (Array.isArray(objValue)) {
823
+ return objValue.concat(srcValue);
824
+ }
825
+ return void 0;
826
+ },
827
+ currentMiddleware,
828
+ middleware
829
+ );
830
+ }
831
+ return currentMiddleware;
832
+ });
833
+ strapi.config.set("middlewares", configuredMiddlewares);
834
+ };
835
+ const createPreviewConfigService = ({ strapi: strapi2 }) => {
836
+ return {
837
+ register() {
838
+ if (!this.isEnabled()) {
839
+ return;
840
+ }
841
+ const config = strapi2.config.get("admin.preview");
842
+ if (config.config?.allowedOrigins) {
843
+ extendMiddlewareConfiguration({
844
+ name: "strapi::security",
845
+ config: {
846
+ contentSecurityPolicy: {
847
+ directives: {
848
+ "frame-src": config.config.allowedOrigins
849
+ }
850
+ }
851
+ }
852
+ });
853
+ }
854
+ },
855
+ isEnabled() {
856
+ const config = strapi2.config.get("admin.preview");
857
+ if (!config) {
858
+ return false;
859
+ }
860
+ return config?.enabled ?? true;
861
+ },
862
+ /**
863
+ * Validate if the configuration is valid
864
+ */
865
+ validate() {
866
+ if (!this.isEnabled()) {
867
+ return;
868
+ }
869
+ const handler = this.getPreviewHandler();
870
+ if (typeof handler !== "function") {
871
+ throw new errors.ValidationError(
872
+ "Preview configuration is invalid. Handler must be a function"
873
+ );
874
+ }
875
+ },
876
+ /**
877
+ * Utility to get the preview handler from the configuration
878
+ */
879
+ getPreviewHandler() {
880
+ const config = strapi2.config.get("admin.preview");
881
+ const emptyHandler = () => {
882
+ return void 0;
883
+ };
884
+ if (!this.isEnabled()) {
885
+ return emptyHandler;
886
+ }
887
+ return config?.config?.handler || emptyHandler;
888
+ }
889
+ };
890
+ };
891
+ const services$1 = {
892
+ preview: createPreviewService,
893
+ "preview-config": createPreviewConfigService
894
+ };
895
+ const getFeature = () => {
896
+ return {
897
+ register() {
898
+ const config = getService(strapi, "preview-config");
899
+ config.validate();
900
+ config.register();
901
+ },
902
+ bootstrap() {
903
+ },
904
+ routes: routes$1,
905
+ controllers: controllers$1,
906
+ services: services$1
907
+ };
908
+ };
909
+ const preview = getFeature();
661
910
  const register = async ({ strapi: strapi2 }) => {
662
911
  await history.register?.({ strapi: strapi2 });
912
+ await preview.register?.({ strapi: strapi2 });
663
913
  };
664
914
  const ALLOWED_WEBHOOK_EVENTS = {
665
915
  ENTRY_PUBLISH: "entry.publish",
@@ -669,11 +919,12 @@ const bootstrap = async () => {
669
919
  Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
670
920
  strapi.get("webhookStore").addAllowedEvent(key, value);
671
921
  });
672
- getService$1("field-sizes").setCustomFieldInputSizes();
673
- await getService$1("components").syncConfigurations();
674
- await getService$1("content-types").syncConfigurations();
675
- await getService$1("permission").registerPermissions();
922
+ getService$2("field-sizes").setCustomFieldInputSizes();
923
+ await getService$2("components").syncConfigurations();
924
+ await getService$2("content-types").syncConfigurations();
925
+ await getService$2("permission").registerPermissions();
676
926
  await history.bootstrap?.({ strapi });
927
+ await preview.bootstrap?.({ strapi });
677
928
  };
678
929
  const destroy = async ({ strapi: strapi2 }) => {
679
930
  await history.destroy?.({ strapi: strapi2 });
@@ -1163,7 +1414,8 @@ const admin = {
1163
1414
  };
1164
1415
  const routes = {
1165
1416
  admin,
1166
- ...history.routes ? history.routes : {}
1417
+ ...history.routes ? history.routes : {},
1418
+ ...preview.routes ? preview.routes : {}
1167
1419
  };
1168
1420
  const hasPermissionsSchema = yup$1.object({
1169
1421
  actions: yup$1.array().of(yup$1.string()),
@@ -1174,6 +1426,11 @@ const { createPolicy } = policy;
1174
1426
  const hasPermissions = createPolicy({
1175
1427
  name: "plugin::content-manager.hasPermissions",
1176
1428
  validator: validateHasPermissionsInput,
1429
+ /**
1430
+ * NOTE: Action aliases are currently not checked at this level (policy).
1431
+ * This is currently the intended behavior to avoid changing the behavior of API related permissions.
1432
+ * If you want to add support for it, please create a dedicated RFC with a list of potential side effect this could have.
1433
+ */
1177
1434
  handler(ctx, config = {}) {
1178
1435
  const { actions = [], hasAtLeastOne = false } = config;
1179
1436
  const { userAbility } = ctx.state;
@@ -1221,8 +1478,7 @@ const isSortable = (schema, name) => {
1221
1478
  if (!_.has(schema.attributes, name)) {
1222
1479
  return false;
1223
1480
  }
1224
- if (schema.modelType === "component" && name === "id")
1225
- return false;
1481
+ if (schema.modelType === "component" && name === "id") return false;
1226
1482
  const attribute = schema.attributes[name];
1227
1483
  if (NON_SORTABLES.includes(attribute.type)) {
1228
1484
  return false;
@@ -1367,8 +1623,7 @@ const createDefaultSettings = async (schema) => {
1367
1623
  };
1368
1624
  };
1369
1625
  const syncSettings = async (configuration, schema) => {
1370
- if (isEmpty(configuration.settings))
1371
- return createDefaultSettings(schema);
1626
+ if (isEmpty(configuration.settings)) return createDefaultSettings(schema);
1372
1627
  const defaultField = getDefaultMainField(schema);
1373
1628
  const { mainField = defaultField, defaultSortBy = defaultField } = configuration.settings || {};
1374
1629
  return {
@@ -1415,7 +1670,7 @@ const createMetadasSchema = (schema) => {
1415
1670
  if (!value) {
1416
1671
  return yup$1.string();
1417
1672
  }
1418
- const targetSchema = getService$1("content-types").findContentType(
1673
+ const targetSchema = getService$2("content-types").findContentType(
1419
1674
  schema.attributes[key].targetModel
1420
1675
  );
1421
1676
  if (!targetSchema) {
@@ -1544,8 +1799,7 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1544
1799
  }
1545
1800
  switch (attribute.type) {
1546
1801
  case "relation": {
1547
- if (canCreate(attributePath))
1548
- return body2;
1802
+ if (canCreate(attributePath)) return body2;
1549
1803
  return set(attributePath, { set: [] }, body2);
1550
1804
  }
1551
1805
  case "component": {
@@ -1555,8 +1809,7 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1555
1809
  ]);
1556
1810
  }
1557
1811
  default: {
1558
- if (canCreate(attributePath))
1559
- return body2;
1812
+ if (canCreate(attributePath)) return body2;
1560
1813
  return set(attributePath, null, body2);
1561
1814
  }
1562
1815
  }
@@ -1567,9 +1820,11 @@ const multipleLocaleSchema = yup$1.lazy(
1567
1820
  (value) => Array.isArray(value) ? yup$1.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1568
1821
  );
1569
1822
  const statusSchema = yup$1.mixed().oneOf(["draft", "published"], "Invalid status");
1570
- const getDocumentLocaleAndStatus = async (request, opts = { allowMultipleLocales: false }) => {
1823
+ const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
1571
1824
  const { allowMultipleLocales } = opts;
1572
- const { locale, status, ...rest } = request || {};
1825
+ const { locale, status: providedStatus, ...rest } = request || {};
1826
+ const defaultStatus = contentTypes$1.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
1827
+ const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
1573
1828
  const schema = yup$1.object().shape({
1574
1829
  locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1575
1830
  status: statusSchema
@@ -1582,7 +1837,7 @@ const getDocumentLocaleAndStatus = async (request, opts = { allowMultipleLocales
1582
1837
  }
1583
1838
  };
1584
1839
  const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
1585
- const documentMetadata2 = getService$1("document-metadata");
1840
+ const documentMetadata2 = getService$2("document-metadata");
1586
1841
  const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
1587
1842
  let {
1588
1843
  meta: { availableLocales, availableStatus }
@@ -1608,8 +1863,8 @@ const createDocument = async (ctx, opts) => {
1608
1863
  const { userAbility, user } = ctx.state;
1609
1864
  const { model } = ctx.params;
1610
1865
  const { body } = ctx.request;
1611
- const documentManager2 = getService$1("document-manager");
1612
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1866
+ const documentManager2 = getService$2("document-manager");
1867
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1613
1868
  if (permissionChecker2.cannot.create()) {
1614
1869
  throw new errors.ForbiddenError();
1615
1870
  }
@@ -1617,7 +1872,7 @@ const createDocument = async (ctx, opts) => {
1617
1872
  const setCreator = setCreatorFields({ user });
1618
1873
  const sanitizeFn = async.pipe(pickPermittedFields, setCreator);
1619
1874
  const sanitizedBody = await sanitizeFn(body);
1620
- const { locale, status = "draft" } = await getDocumentLocaleAndStatus(body);
1875
+ const { locale, status } = await getDocumentLocaleAndStatus(body, model);
1621
1876
  return documentManager2.create(model, {
1622
1877
  data: sanitizedBody,
1623
1878
  locale,
@@ -1629,14 +1884,14 @@ const updateDocument = async (ctx, opts) => {
1629
1884
  const { userAbility, user } = ctx.state;
1630
1885
  const { id, model } = ctx.params;
1631
1886
  const { body } = ctx.request;
1632
- const documentManager2 = getService$1("document-manager");
1633
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1887
+ const documentManager2 = getService$2("document-manager");
1888
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1634
1889
  if (permissionChecker2.cannot.update()) {
1635
1890
  throw new errors.ForbiddenError();
1636
1891
  }
1637
1892
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1638
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1639
- const { locale } = await getDocumentLocaleAndStatus(body);
1893
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
1894
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1640
1895
  const [documentVersion, documentExists] = await Promise.all([
1641
1896
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1642
1897
  documentManager2.exists(model, id)
@@ -1652,7 +1907,7 @@ const updateDocument = async (ctx, opts) => {
1652
1907
  throw new errors.ForbiddenError();
1653
1908
  }
1654
1909
  const pickPermittedFields = documentVersion ? permissionChecker2.sanitizeUpdateInput(documentVersion) : permissionChecker2.sanitizeCreateInput;
1655
- const setCreator = setCreatorFields({ user, isEdition: true });
1910
+ const setCreator = documentVersion ? setCreatorFields({ user, isEdition: true }) : setCreatorFields({ user });
1656
1911
  const sanitizeFn = async.pipe(pickPermittedFields, setCreator);
1657
1912
  const sanitizedBody = await sanitizeFn(body);
1658
1913
  return documentManager2.update(documentVersion?.documentId || id, model, {
@@ -1666,15 +1921,15 @@ const collectionTypes = {
1666
1921
  const { userAbility } = ctx.state;
1667
1922
  const { model } = ctx.params;
1668
1923
  const { query } = ctx.request;
1669
- const documentMetadata2 = getService$1("document-metadata");
1670
- const documentManager2 = getService$1("document-manager");
1671
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1924
+ const documentMetadata2 = getService$2("document-metadata");
1925
+ const documentManager2 = getService$2("document-manager");
1926
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1672
1927
  if (permissionChecker2.cannot.read()) {
1673
1928
  return ctx.forbidden();
1674
1929
  }
1675
1930
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1676
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1677
- const { locale, status } = await getDocumentLocaleAndStatus(query);
1931
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1932
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
1678
1933
  const { results: documents, pagination: pagination2 } = await documentManager2.findPage(
1679
1934
  { ...permissionQuery, populate, locale, status },
1680
1935
  model
@@ -1702,14 +1957,14 @@ const collectionTypes = {
1702
1957
  async findOne(ctx) {
1703
1958
  const { userAbility } = ctx.state;
1704
1959
  const { model, id } = ctx.params;
1705
- const documentManager2 = getService$1("document-manager");
1706
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1960
+ const documentManager2 = getService$2("document-manager");
1961
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1707
1962
  if (permissionChecker2.cannot.read()) {
1708
1963
  return ctx.forbidden();
1709
1964
  }
1710
1965
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1711
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1712
- const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1966
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1967
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
1713
1968
  const version = await documentManager2.findOne(id, model, {
1714
1969
  populate,
1715
1970
  locale,
@@ -1724,7 +1979,7 @@ const collectionTypes = {
1724
1979
  permissionChecker2,
1725
1980
  model,
1726
1981
  // @ts-expect-error TODO: fix
1727
- { id, locale, publishedAt: null },
1982
+ { documentId: id, locale, publishedAt: null },
1728
1983
  { availableLocales: true, availableStatus: false }
1729
1984
  );
1730
1985
  ctx.body = { data: {}, meta };
@@ -1739,7 +1994,7 @@ const collectionTypes = {
1739
1994
  async create(ctx) {
1740
1995
  const { userAbility } = ctx.state;
1741
1996
  const { model } = ctx.params;
1742
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1997
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1743
1998
  const [totalEntries, document] = await Promise.all([
1744
1999
  strapi.db.query(model).count(),
1745
2000
  createDocument(ctx)
@@ -1760,7 +2015,7 @@ const collectionTypes = {
1760
2015
  async update(ctx) {
1761
2016
  const { userAbility } = ctx.state;
1762
2017
  const { model } = ctx.params;
1763
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2018
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1764
2019
  const updatedVersion = await updateDocument(ctx);
1765
2020
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1766
2021
  ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
@@ -1769,14 +2024,14 @@ const collectionTypes = {
1769
2024
  const { userAbility, user } = ctx.state;
1770
2025
  const { model, sourceId: id } = ctx.params;
1771
2026
  const { body } = ctx.request;
1772
- const documentManager2 = getService$1("document-manager");
1773
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2027
+ const documentManager2 = getService$2("document-manager");
2028
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1774
2029
  if (permissionChecker2.cannot.create()) {
1775
2030
  return ctx.forbidden();
1776
2031
  }
1777
2032
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1778
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1779
- const { locale } = await getDocumentLocaleAndStatus(body);
2033
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2034
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1780
2035
  const document = await documentManager2.findOne(id, model, {
1781
2036
  populate,
1782
2037
  locale,
@@ -1814,14 +2069,14 @@ const collectionTypes = {
1814
2069
  async delete(ctx) {
1815
2070
  const { userAbility } = ctx.state;
1816
2071
  const { id, model } = ctx.params;
1817
- const documentManager2 = getService$1("document-manager");
1818
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2072
+ const documentManager2 = getService$2("document-manager");
2073
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1819
2074
  if (permissionChecker2.cannot.delete()) {
1820
2075
  return ctx.forbidden();
1821
2076
  }
1822
2077
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1823
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1824
- const { locale } = await getDocumentLocaleAndStatus(ctx.query);
2078
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2079
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
1825
2080
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1826
2081
  if (documentLocales.length === 0) {
1827
2082
  return ctx.notFound();
@@ -1842,19 +2097,42 @@ const collectionTypes = {
1842
2097
  const { userAbility } = ctx.state;
1843
2098
  const { id, model } = ctx.params;
1844
2099
  const { body } = ctx.request;
1845
- const documentManager2 = getService$1("document-manager");
1846
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2100
+ const documentManager2 = getService$2("document-manager");
2101
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1847
2102
  if (permissionChecker2.cannot.publish()) {
1848
2103
  return ctx.forbidden();
1849
2104
  }
1850
2105
  const publishedDocument = await strapi.db.transaction(async () => {
1851
2106
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1852
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1853
- const document = id ? await updateDocument(ctx, { populate }) : await createDocument(ctx, { populate });
2107
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
2108
+ let document;
2109
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2110
+ const isCreate = isNil$1(id);
2111
+ if (isCreate) {
2112
+ if (permissionChecker2.cannot.create()) {
2113
+ throw new errors.ForbiddenError();
2114
+ }
2115
+ document = await createDocument(ctx, { populate });
2116
+ }
2117
+ const isUpdate = !isCreate;
2118
+ if (isUpdate) {
2119
+ const documentExists = documentManager2.exists(model, id);
2120
+ if (!documentExists) {
2121
+ throw new errors.NotFoundError("Document not found");
2122
+ }
2123
+ document = await documentManager2.findOne(id, model, { populate, locale });
2124
+ if (!document) {
2125
+ if (permissionChecker2.cannot.create({ locale }) || permissionChecker2.cannot.publish({ locale })) {
2126
+ throw new errors.ForbiddenError();
2127
+ }
2128
+ document = await updateDocument(ctx);
2129
+ } else if (permissionChecker2.can.update(document)) {
2130
+ await updateDocument(ctx);
2131
+ }
2132
+ }
1854
2133
  if (permissionChecker2.cannot.publish(document)) {
1855
2134
  throw new errors.ForbiddenError();
1856
2135
  }
1857
- const { locale } = await getDocumentLocaleAndStatus(body);
1858
2136
  const publishResult = await documentManager2.publish(document.documentId, model, {
1859
2137
  locale
1860
2138
  // TODO: Allow setting creator fields on publish
@@ -1874,14 +2152,16 @@ const collectionTypes = {
1874
2152
  const { body } = ctx.request;
1875
2153
  const { documentIds } = body;
1876
2154
  await validateBulkActionInput(body);
1877
- const documentManager2 = getService$1("document-manager");
1878
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2155
+ const documentManager2 = getService$2("document-manager");
2156
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1879
2157
  if (permissionChecker2.cannot.publish()) {
1880
2158
  return ctx.forbidden();
1881
2159
  }
1882
2160
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1883
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1884
- const { locale } = await getDocumentLocaleAndStatus(body, { allowMultipleLocales: true });
2161
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
2162
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
2163
+ allowMultipleLocales: true
2164
+ });
1885
2165
  const entityPromises = documentIds.map(
1886
2166
  (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
1887
2167
  );
@@ -1903,12 +2183,14 @@ const collectionTypes = {
1903
2183
  const { body } = ctx.request;
1904
2184
  const { documentIds } = body;
1905
2185
  await validateBulkActionInput(body);
1906
- const documentManager2 = getService$1("document-manager");
1907
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2186
+ const documentManager2 = getService$2("document-manager");
2187
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1908
2188
  if (permissionChecker2.cannot.unpublish()) {
1909
2189
  return ctx.forbidden();
1910
2190
  }
1911
- const { locale } = await getDocumentLocaleAndStatus(body);
2191
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
2192
+ allowMultipleLocales: true
2193
+ });
1912
2194
  const entityPromises = documentIds.map(
1913
2195
  (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
1914
2196
  );
@@ -1931,8 +2213,8 @@ const collectionTypes = {
1931
2213
  const {
1932
2214
  body: { discardDraft, ...body }
1933
2215
  } = ctx.request;
1934
- const documentManager2 = getService$1("document-manager");
1935
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2216
+ const documentManager2 = getService$2("document-manager");
2217
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1936
2218
  if (permissionChecker2.cannot.unpublish()) {
1937
2219
  return ctx.forbidden();
1938
2220
  }
@@ -1940,8 +2222,8 @@ const collectionTypes = {
1940
2222
  return ctx.forbidden();
1941
2223
  }
1942
2224
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1943
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1944
- const { locale } = await getDocumentLocaleAndStatus(body);
2225
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2226
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1945
2227
  const document = await documentManager2.findOne(id, model, {
1946
2228
  populate,
1947
2229
  locale,
@@ -1971,14 +2253,14 @@ const collectionTypes = {
1971
2253
  const { userAbility } = ctx.state;
1972
2254
  const { id, model } = ctx.params;
1973
2255
  const { body } = ctx.request;
1974
- const documentManager2 = getService$1("document-manager");
1975
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2256
+ const documentManager2 = getService$2("document-manager");
2257
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
1976
2258
  if (permissionChecker2.cannot.discard()) {
1977
2259
  return ctx.forbidden();
1978
2260
  }
1979
2261
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
1980
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1981
- const { locale } = await getDocumentLocaleAndStatus(body);
2262
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2263
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1982
2264
  const document = await documentManager2.findOne(id, model, {
1983
2265
  populate,
1984
2266
  locale,
@@ -2002,14 +2284,14 @@ const collectionTypes = {
2002
2284
  const { query, body } = ctx.request;
2003
2285
  const { documentIds } = body;
2004
2286
  await validateBulkActionInput(body);
2005
- const documentManager2 = getService$1("document-manager");
2006
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2287
+ const documentManager2 = getService$2("document-manager");
2288
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2007
2289
  if (permissionChecker2.cannot.delete()) {
2008
2290
  return ctx.forbidden();
2009
2291
  }
2010
2292
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
2011
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2012
- const { locale } = await getDocumentLocaleAndStatus(body);
2293
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2294
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2013
2295
  const documentLocales = await documentManager2.findLocales(documentIds, model, {
2014
2296
  populate,
2015
2297
  locale
@@ -2029,14 +2311,14 @@ const collectionTypes = {
2029
2311
  async countDraftRelations(ctx) {
2030
2312
  const { userAbility } = ctx.state;
2031
2313
  const { model, id } = ctx.params;
2032
- const documentManager2 = getService$1("document-manager");
2033
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2314
+ const documentManager2 = getService$2("document-manager");
2315
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2034
2316
  if (permissionChecker2.cannot.read()) {
2035
2317
  return ctx.forbidden();
2036
2318
  }
2037
2319
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2038
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2039
- const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
2320
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2321
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
2040
2322
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
2041
2323
  if (!entity) {
2042
2324
  return ctx.notFound();
@@ -2054,12 +2336,12 @@ const collectionTypes = {
2054
2336
  const ids = ctx.request.query.documentIds;
2055
2337
  const locale = ctx.request.query.locale;
2056
2338
  const { model } = ctx.params;
2057
- const documentManager2 = getService$1("document-manager");
2058
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2339
+ const documentManager2 = getService$2("document-manager");
2340
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2059
2341
  if (permissionChecker2.cannot.read()) {
2060
2342
  return ctx.forbidden();
2061
2343
  }
2062
- const entities = await documentManager2.findMany(
2344
+ const documents = await documentManager2.findMany(
2063
2345
  {
2064
2346
  filters: {
2065
2347
  documentId: ids
@@ -2068,7 +2350,7 @@ const collectionTypes = {
2068
2350
  },
2069
2351
  model
2070
2352
  );
2071
- if (!entities) {
2353
+ if (!documents) {
2072
2354
  return ctx.notFound();
2073
2355
  }
2074
2356
  const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
@@ -2079,13 +2361,13 @@ const collectionTypes = {
2079
2361
  };
2080
2362
  const components$1 = {
2081
2363
  findComponents(ctx) {
2082
- const components2 = getService$1("components").findAllComponents();
2083
- const { toDto } = getService$1("data-mapper");
2364
+ const components2 = getService$2("components").findAllComponents();
2365
+ const { toDto } = getService$2("data-mapper");
2084
2366
  ctx.body = { data: components2.map(toDto) };
2085
2367
  },
2086
2368
  async findComponentConfiguration(ctx) {
2087
2369
  const { uid: uid2 } = ctx.params;
2088
- const componentService = getService$1("components");
2370
+ const componentService = getService$2("components");
2089
2371
  const component = componentService.findComponent(uid2);
2090
2372
  if (!component) {
2091
2373
  return ctx.notFound("component.notFound");
@@ -2102,7 +2384,7 @@ const components$1 = {
2102
2384
  async updateComponentConfiguration(ctx) {
2103
2385
  const { uid: uid2 } = ctx.params;
2104
2386
  const { body } = ctx.request;
2105
- const componentService = getService$1("components");
2387
+ const componentService = getService$2("components");
2106
2388
  const component = componentService.findComponent(uid2);
2107
2389
  if (!component) {
2108
2390
  return ctx.notFound("component.notFound");
@@ -2136,12 +2418,12 @@ const contentTypes = {
2136
2418
  } catch (error) {
2137
2419
  return ctx.send({ error }, 400);
2138
2420
  }
2139
- const contentTypes2 = getService$1("content-types").findContentTypesByKind(kind);
2140
- const { toDto } = getService$1("data-mapper");
2421
+ const contentTypes2 = getService$2("content-types").findContentTypesByKind(kind);
2422
+ const { toDto } = getService$2("data-mapper");
2141
2423
  ctx.body = { data: contentTypes2.map(toDto) };
2142
2424
  },
2143
2425
  async findContentTypesSettings(ctx) {
2144
- const { findAllContentTypes, findConfiguration } = getService$1("content-types");
2426
+ const { findAllContentTypes, findConfiguration } = getService$2("content-types");
2145
2427
  const contentTypes2 = await findAllContentTypes();
2146
2428
  const configurations = await Promise.all(
2147
2429
  contentTypes2.map(async (contentType) => {
@@ -2155,7 +2437,7 @@ const contentTypes = {
2155
2437
  },
2156
2438
  async findContentTypeConfiguration(ctx) {
2157
2439
  const { uid: uid2 } = ctx.params;
2158
- const contentTypeService = getService$1("content-types");
2440
+ const contentTypeService = getService$2("content-types");
2159
2441
  const contentType = await contentTypeService.findContentType(uid2);
2160
2442
  if (!contentType) {
2161
2443
  return ctx.notFound("contentType.notFound");
@@ -2177,13 +2459,13 @@ const contentTypes = {
2177
2459
  const { userAbility } = ctx.state;
2178
2460
  const { uid: uid2 } = ctx.params;
2179
2461
  const { body } = ctx.request;
2180
- const contentTypeService = getService$1("content-types");
2181
- const metricsService = getService$1("metrics");
2462
+ const contentTypeService = getService$2("content-types");
2463
+ const metricsService = getService$2("metrics");
2182
2464
  const contentType = await contentTypeService.findContentType(uid2);
2183
2465
  if (!contentType) {
2184
2466
  return ctx.notFound("contentType.notFound");
2185
2467
  }
2186
- if (!getService$1("permission").canConfigureContentType({ userAbility, contentType })) {
2468
+ if (!getService$2("permission").canConfigureContentType({ userAbility, contentType })) {
2187
2469
  return ctx.forbidden();
2188
2470
  }
2189
2471
  let input;
@@ -2216,10 +2498,10 @@ const contentTypes = {
2216
2498
  };
2217
2499
  const init = {
2218
2500
  getInitData(ctx) {
2219
- const { toDto } = getService$1("data-mapper");
2220
- const { findAllComponents } = getService$1("components");
2221
- const { getAllFieldSizes } = getService$1("field-sizes");
2222
- const { findAllContentTypes } = getService$1("content-types");
2501
+ const { toDto } = getService$2("data-mapper");
2502
+ const { findAllComponents } = getService$2("components");
2503
+ const { getAllFieldSizes } = getService$2("field-sizes");
2504
+ const { findAllContentTypes } = getService$2("content-types");
2223
2505
  ctx.body = {
2224
2506
  data: {
2225
2507
  fieldSizes: getAllFieldSizes(),
@@ -2255,36 +2537,41 @@ const addFiltersClause = (params, filtersClause) => {
2255
2537
  params.filters.$and.push(filtersClause);
2256
2538
  };
2257
2539
  const sanitizeMainField = (model, mainField, userAbility) => {
2258
- const permissionChecker2 = getService$1("permission-checker").create({
2540
+ const permissionChecker2 = getService$2("permission-checker").create({
2259
2541
  userAbility,
2260
2542
  model: model.uid
2261
2543
  });
2262
- if (!isListable(model, mainField)) {
2544
+ const isMainFieldListable = isListable(model, mainField);
2545
+ const canReadMainField = permissionChecker2.can.read(null, mainField);
2546
+ if (!isMainFieldListable || !canReadMainField) {
2263
2547
  return "id";
2264
2548
  }
2265
- if (permissionChecker2.cannot.read(null, mainField)) {
2266
- if (model.uid === "plugin::users-permissions.role") {
2267
- const userPermissionChecker = getService$1("permission-checker").create({
2268
- userAbility,
2269
- model: "plugin::users-permissions.user"
2270
- });
2271
- if (userPermissionChecker.can.read()) {
2272
- return "name";
2273
- }
2274
- }
2275
- return "id";
2549
+ if (model.uid === "plugin::users-permissions.role") {
2550
+ return "name";
2276
2551
  }
2277
2552
  return mainField;
2278
2553
  };
2279
- const addStatusToRelations = async (uid2, relations2) => {
2280
- if (!contentTypes$1.hasDraftAndPublish(strapi.contentTypes[uid2])) {
2554
+ const addStatusToRelations = async (targetUid, relations2) => {
2555
+ if (!contentTypes$1.hasDraftAndPublish(strapi.getModel(targetUid))) {
2556
+ return relations2;
2557
+ }
2558
+ const documentMetadata2 = getService$2("document-metadata");
2559
+ if (!relations2.length) {
2281
2560
  return relations2;
2282
2561
  }
2283
- const documentMetadata2 = getService$1("document-metadata");
2284
- const documentsAvailableStatus = await documentMetadata2.getManyAvailableStatus(uid2, relations2);
2562
+ const firstRelation = relations2[0];
2563
+ const filters = {
2564
+ documentId: { $in: relations2.map((r) => r.documentId) },
2565
+ // NOTE: find the "opposite" status
2566
+ publishedAt: firstRelation.publishedAt !== null ? { $null: true } : { $notNull: true }
2567
+ };
2568
+ const availableStatus = await strapi.query(targetUid).findMany({
2569
+ select: ["id", "documentId", "locale", "updatedAt", "createdAt", "publishedAt"],
2570
+ filters
2571
+ });
2285
2572
  return relations2.map((relation) => {
2286
- const availableStatuses = documentsAvailableStatus.filter(
2287
- (availableDocument) => availableDocument.documentId === relation.documentId
2573
+ const availableStatuses = availableStatus.filter(
2574
+ (availableDocument) => availableDocument.documentId === relation.documentId && (relation.locale ? availableDocument.locale === relation.locale : true)
2288
2575
  );
2289
2576
  return {
2290
2577
  ...relation,
@@ -2305,11 +2592,8 @@ const validateLocale = (sourceUid, targetUid, locale) => {
2305
2592
  const isLocalized = strapi.plugin("i18n").service("content-types").isLocalizedContentType;
2306
2593
  const isSourceLocalized = isLocalized(sourceModel);
2307
2594
  const isTargetLocalized = isLocalized(targetModel);
2308
- let validatedLocale = locale;
2309
- if (!targetModel || !isTargetLocalized)
2310
- validatedLocale = void 0;
2311
2595
  return {
2312
- locale: validatedLocale,
2596
+ locale,
2313
2597
  isSourceLocalized,
2314
2598
  isTargetLocalized
2315
2599
  };
@@ -2318,8 +2602,7 @@ const validateStatus = (sourceUid, status) => {
2318
2602
  const sourceModel = strapi.getModel(sourceUid);
2319
2603
  const isDP = contentTypes$1.hasDraftAndPublish;
2320
2604
  const isSourceDP = isDP(sourceModel);
2321
- if (!isSourceDP)
2322
- return { status: void 0 };
2605
+ if (!isSourceDP) return { status: void 0 };
2323
2606
  switch (status) {
2324
2607
  case "published":
2325
2608
  return { status: "published" };
@@ -2349,7 +2632,7 @@ const relations = {
2349
2632
  ctx.request?.query?.locale
2350
2633
  );
2351
2634
  const { status } = validateStatus(sourceUid, ctx.request?.query?.status);
2352
- const permissionChecker2 = getService$1("permission-checker").create({
2635
+ const permissionChecker2 = getService$2("permission-checker").create({
2353
2636
  userAbility,
2354
2637
  model
2355
2638
  });
@@ -2374,7 +2657,7 @@ const relations = {
2374
2657
  where.id = id;
2375
2658
  }
2376
2659
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2377
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2660
+ const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
2378
2661
  const currentEntity = await strapi.db.query(model).findOne({
2379
2662
  where,
2380
2663
  populate
@@ -2389,7 +2672,7 @@ const relations = {
2389
2672
  }
2390
2673
  entryId = currentEntity.id;
2391
2674
  }
2392
- const modelConfig = isComponent2 ? await getService$1("components").findConfiguration(sourceSchema) : await getService$1("content-types").findConfiguration(sourceSchema);
2675
+ const modelConfig = isComponent2 ? await getService$2("components").findConfiguration(sourceSchema) : await getService$2("content-types").findConfiguration(sourceSchema);
2393
2676
  const targetSchema = strapi.getModel(targetUid);
2394
2677
  const mainField = flow(
2395
2678
  prop(`metadatas.${targetField}.edit.mainField`),
@@ -2412,7 +2695,7 @@ const relations = {
2412
2695
  attribute,
2413
2696
  fieldsToSelect,
2414
2697
  mainField,
2415
- source: { schema: sourceSchema },
2698
+ source: { schema: sourceSchema, isLocalized: isSourceLocalized },
2416
2699
  target: { schema: targetSchema, isLocalized: isTargetLocalized },
2417
2700
  sourceSchema,
2418
2701
  targetSchema,
@@ -2434,7 +2717,8 @@ const relations = {
2434
2717
  fieldsToSelect,
2435
2718
  mainField,
2436
2719
  source: {
2437
- schema: { uid: sourceUid, modelType: sourceModelType }
2720
+ schema: { uid: sourceUid, modelType: sourceModelType },
2721
+ isLocalized: isSourceLocalized
2438
2722
  },
2439
2723
  target: {
2440
2724
  schema: { uid: targetUid },
@@ -2442,7 +2726,7 @@ const relations = {
2442
2726
  }
2443
2727
  } = await this.extractAndValidateRequestInfo(ctx, id);
2444
2728
  const { idsToOmit, idsToInclude, _q, ...query } = ctx.request.query;
2445
- const permissionChecker2 = getService$1("permission-checker").create({
2729
+ const permissionChecker2 = getService$2("permission-checker").create({
2446
2730
  userAbility: ctx.state.userAbility,
2447
2731
  model: targetUid
2448
2732
  });
@@ -2472,12 +2756,16 @@ const relations = {
2472
2756
  } else {
2473
2757
  where.id = id;
2474
2758
  }
2475
- if (status) {
2476
- where[`${alias}.published_at`] = getPublishedAtClause(status, targetUid);
2759
+ const publishedAt = getPublishedAtClause(status, targetUid);
2760
+ if (!isEmpty(publishedAt)) {
2761
+ where[`${alias}.published_at`] = publishedAt;
2477
2762
  }
2478
- if (filterByLocale) {
2763
+ if (isTargetLocalized && locale) {
2479
2764
  where[`${alias}.locale`] = locale;
2480
2765
  }
2766
+ if (isSourceLocalized && locale) {
2767
+ where.locale = locale;
2768
+ }
2481
2769
  if ((idsToInclude?.length ?? 0) !== 0) {
2482
2770
  where[`${alias}.id`].$notIn = idsToInclude;
2483
2771
  }
@@ -2495,7 +2783,8 @@ const relations = {
2495
2783
  id: { $notIn: uniq(idsToOmit) }
2496
2784
  });
2497
2785
  }
2498
- const res = await strapi.db.query(targetUid).findPage(strapi.get("query-params").transform(targetUid, queryParams));
2786
+ const dbQuery = strapi.get("query-params").transform(targetUid, queryParams);
2787
+ const res = await strapi.db.query(targetUid).findPage(dbQuery);
2499
2788
  ctx.body = {
2500
2789
  ...res,
2501
2790
  results: await addStatusToRelations(targetUid, res.results)
@@ -2510,29 +2799,39 @@ const relations = {
2510
2799
  attribute,
2511
2800
  targetField,
2512
2801
  fieldsToSelect,
2513
- source: {
2514
- schema: { uid: sourceUid }
2515
- },
2516
- target: {
2517
- schema: { uid: targetUid }
2518
- }
2802
+ status,
2803
+ source: { schema: sourceSchema },
2804
+ target: { schema: targetSchema }
2519
2805
  } = await this.extractAndValidateRequestInfo(ctx, id);
2520
- const permissionQuery = await getService$1("permission-checker").create({ userAbility, model: targetUid }).sanitizedQuery.read({ fields: fieldsToSelect });
2806
+ const { uid: sourceUid } = sourceSchema;
2807
+ const { uid: targetUid } = targetSchema;
2808
+ const permissionQuery = await getService$2("permission-checker").create({ userAbility, model: targetUid }).sanitizedQuery.read({ fields: fieldsToSelect });
2521
2809
  const dbQuery = strapi.db.query(sourceUid);
2522
2810
  const loadRelations = relations$1.isAnyToMany(attribute) ? (...args) => dbQuery.loadPages(...args) : (...args) => dbQuery.load(...args).then((res2) => ({ results: res2 ? [res2] : [] }));
2811
+ const filters = {};
2812
+ if (sourceSchema?.options?.draftAndPublish) {
2813
+ if (targetSchema?.options?.draftAndPublish) {
2814
+ if (status === "published") {
2815
+ filters.publishedAt = { $notNull: true };
2816
+ } else {
2817
+ filters.publishedAt = { $null: true };
2818
+ }
2819
+ }
2820
+ } else if (targetSchema?.options?.draftAndPublish) {
2821
+ filters.publishedAt = { $null: true };
2822
+ }
2523
2823
  const res = await loadRelations({ id: entryId }, targetField, {
2524
- select: ["id", "documentId", "locale", "publishedAt"],
2824
+ select: ["id", "documentId", "locale", "publishedAt", "updatedAt"],
2525
2825
  ordering: "desc",
2526
2826
  page: ctx.request.query.page,
2527
- pageSize: ctx.request.query.pageSize
2827
+ pageSize: ctx.request.query.pageSize,
2828
+ filters
2528
2829
  });
2529
2830
  const loadedIds = res.results.map((item) => item.id);
2530
2831
  addFiltersClause(permissionQuery, { id: { $in: loadedIds } });
2531
2832
  const sanitizedRes = await loadRelations({ id: entryId }, targetField, {
2532
2833
  ...strapi.get("query-params").transform(targetUid, permissionQuery),
2533
- ordering: "desc",
2534
- page: ctx.request.query.page,
2535
- pageSize: ctx.request.query.pageSize
2834
+ ordering: "desc"
2536
2835
  });
2537
2836
  const relationsUnion = uniqBy("id", concat(sanitizedRes.results, res.results));
2538
2837
  ctx.body = {
@@ -2547,10 +2846,10 @@ const relations = {
2547
2846
  }
2548
2847
  };
2549
2848
  const buildPopulateFromQuery = async (query, model) => {
2550
- return getService$1("populate-builder")(model).populateFromQuery(query).populateDeep(Infinity).countRelations().build();
2849
+ return getService$2("populate-builder")(model).populateFromQuery(query).populateDeep(Infinity).countRelations().build();
2551
2850
  };
2552
2851
  const findDocument = async (query, uid2, opts = {}) => {
2553
- const documentManager2 = getService$1("document-manager");
2852
+ const documentManager2 = getService$2("document-manager");
2554
2853
  const populate = await buildPopulateFromQuery(query, uid2);
2555
2854
  return documentManager2.findMany({ ...opts, populate }, uid2).then((documents) => documents[0]);
2556
2855
  };
@@ -2558,13 +2857,13 @@ const createOrUpdateDocument = async (ctx, opts) => {
2558
2857
  const { user, userAbility } = ctx.state;
2559
2858
  const { model } = ctx.params;
2560
2859
  const { body, query } = ctx.request;
2561
- const documentManager2 = getService$1("document-manager");
2562
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2860
+ const documentManager2 = getService$2("document-manager");
2861
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2563
2862
  if (permissionChecker2.cannot.create() && permissionChecker2.cannot.update()) {
2564
2863
  throw new errors.ForbiddenError();
2565
2864
  }
2566
2865
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2567
- const { locale } = await getDocumentLocaleAndStatus(body);
2866
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2568
2867
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2569
2868
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2570
2869
  // Find the first document to check if it exists
@@ -2600,12 +2899,12 @@ const singleTypes = {
2600
2899
  const { userAbility } = ctx.state;
2601
2900
  const { model } = ctx.params;
2602
2901
  const { query = {} } = ctx.request;
2603
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2902
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2604
2903
  if (permissionChecker2.cannot.read()) {
2605
2904
  return ctx.forbidden();
2606
2905
  }
2607
2906
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2608
- const { locale, status } = await getDocumentLocaleAndStatus(query);
2907
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
2609
2908
  const version = await findDocument(permissionQuery, model, { locale, status });
2610
2909
  if (!version) {
2611
2910
  if (permissionChecker2.cannot.create()) {
@@ -2619,7 +2918,7 @@ const singleTypes = {
2619
2918
  permissionChecker2,
2620
2919
  model,
2621
2920
  // @ts-expect-error - fix types
2622
- { id: document.documentId, locale, publishedAt: null },
2921
+ { documentId: document.documentId, locale, publishedAt: null },
2623
2922
  { availableLocales: true, availableStatus: false }
2624
2923
  );
2625
2924
  ctx.body = { data: {}, meta };
@@ -2634,7 +2933,7 @@ const singleTypes = {
2634
2933
  async createOrUpdate(ctx) {
2635
2934
  const { userAbility } = ctx.state;
2636
2935
  const { model } = ctx.params;
2637
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2936
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2638
2937
  const document = await createOrUpdateDocument(ctx);
2639
2938
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2640
2939
  ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
@@ -2643,14 +2942,14 @@ const singleTypes = {
2643
2942
  const { userAbility } = ctx.state;
2644
2943
  const { model } = ctx.params;
2645
2944
  const { query = {} } = ctx.request;
2646
- const documentManager2 = getService$1("document-manager");
2647
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2945
+ const documentManager2 = getService$2("document-manager");
2946
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2648
2947
  if (permissionChecker2.cannot.delete()) {
2649
2948
  return ctx.forbidden();
2650
2949
  }
2651
2950
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2652
2951
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2653
- const { locale } = await getDocumentLocaleAndStatus(query);
2952
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2654
2953
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2655
2954
  populate,
2656
2955
  locale
@@ -2672,8 +2971,8 @@ const singleTypes = {
2672
2971
  const { userAbility } = ctx.state;
2673
2972
  const { model } = ctx.params;
2674
2973
  const { query = {} } = ctx.request;
2675
- const documentManager2 = getService$1("document-manager");
2676
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2974
+ const documentManager2 = getService$2("document-manager");
2975
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2677
2976
  if (permissionChecker2.cannot.publish()) {
2678
2977
  return ctx.forbidden();
2679
2978
  }
@@ -2687,7 +2986,7 @@ const singleTypes = {
2687
2986
  if (permissionChecker2.cannot.publish(document)) {
2688
2987
  throw new errors.ForbiddenError();
2689
2988
  }
2690
- const { locale } = await getDocumentLocaleAndStatus(document);
2989
+ const { locale } = await getDocumentLocaleAndStatus(document, model);
2691
2990
  const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2692
2991
  return publishResult.at(0);
2693
2992
  });
@@ -2701,8 +3000,8 @@ const singleTypes = {
2701
3000
  body: { discardDraft, ...body },
2702
3001
  query = {}
2703
3002
  } = ctx.request;
2704
- const documentManager2 = getService$1("document-manager");
2705
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
3003
+ const documentManager2 = getService$2("document-manager");
3004
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2706
3005
  if (permissionChecker2.cannot.unpublish()) {
2707
3006
  return ctx.forbidden();
2708
3007
  }
@@ -2710,7 +3009,7 @@ const singleTypes = {
2710
3009
  return ctx.forbidden();
2711
3010
  }
2712
3011
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2713
- const { locale } = await getDocumentLocaleAndStatus(body);
3012
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2714
3013
  const document = await findDocument(sanitizedQuery, model, { locale });
2715
3014
  if (!document) {
2716
3015
  return ctx.notFound();
@@ -2736,13 +3035,13 @@ const singleTypes = {
2736
3035
  const { userAbility } = ctx.state;
2737
3036
  const { model } = ctx.params;
2738
3037
  const { body, query = {} } = ctx.request;
2739
- const documentManager2 = getService$1("document-manager");
2740
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
3038
+ const documentManager2 = getService$2("document-manager");
3039
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
2741
3040
  if (permissionChecker2.cannot.discard()) {
2742
3041
  return ctx.forbidden();
2743
3042
  }
2744
3043
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2745
- const { locale } = await getDocumentLocaleAndStatus(body);
3044
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2746
3045
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2747
3046
  if (!document) {
2748
3047
  return ctx.notFound();
@@ -2760,9 +3059,9 @@ const singleTypes = {
2760
3059
  const { userAbility } = ctx.state;
2761
3060
  const { model } = ctx.params;
2762
3061
  const { query } = ctx.request;
2763
- const documentManager2 = getService$1("document-manager");
2764
- const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2765
- const { locale } = await getDocumentLocaleAndStatus(query);
3062
+ const documentManager2 = getService$2("document-manager");
3063
+ const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
3064
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2766
3065
  if (permissionChecker2.cannot.read()) {
2767
3066
  return ctx.forbidden();
2768
3067
  }
@@ -2783,9 +3082,9 @@ const uid$1 = {
2783
3082
  async generateUID(ctx) {
2784
3083
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2785
3084
  const { query = {} } = ctx.request;
2786
- const { locale } = await getDocumentLocaleAndStatus(query);
3085
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2787
3086
  await validateUIDField(contentTypeUID, field);
2788
- const uidService = getService$1("uid");
3087
+ const uidService = getService$2("uid");
2789
3088
  ctx.body = {
2790
3089
  data: await uidService.generateUIDField({ contentTypeUID, field, data, locale })
2791
3090
  };
@@ -2795,9 +3094,9 @@ const uid$1 = {
2795
3094
  ctx.request.body
2796
3095
  );
2797
3096
  const { query = {} } = ctx.request;
2798
- const { locale } = await getDocumentLocaleAndStatus(query);
3097
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2799
3098
  await validateUIDField(contentTypeUID, field);
2800
- const uidService = getService$1("uid");
3099
+ const uidService = getService$2("uid");
2801
3100
  const isAvailable = await uidService.checkUIDAvailability({
2802
3101
  contentTypeUID,
2803
3102
  field,
@@ -2818,7 +3117,8 @@ const controllers = {
2818
3117
  relations,
2819
3118
  "single-types": singleTypes,
2820
3119
  uid: uid$1,
2821
- ...history.controllers ? history.controllers : {}
3120
+ ...history.controllers ? history.controllers : {},
3121
+ ...preview.controllers ? preview.controllers : {}
2822
3122
  };
2823
3123
  const keys = {
2824
3124
  CONFIGURATION: "configuration"
@@ -2947,18 +3247,15 @@ async function syncMetadatas(configuration, schema) {
2947
3247
  _.set(updatedMeta, ["list", "searchable"], false);
2948
3248
  _.set(acc, [key], updatedMeta);
2949
3249
  }
2950
- if (!_.has(edit, "mainField"))
2951
- return acc;
3250
+ if (!_.has(edit, "mainField")) return acc;
2952
3251
  if (!isRelation$1(attr)) {
2953
3252
  _.set(updatedMeta, "edit", _.omit(edit, ["mainField"]));
2954
3253
  _.set(acc, [key], updatedMeta);
2955
3254
  return acc;
2956
3255
  }
2957
- if (edit.mainField === "id")
2958
- return acc;
3256
+ if (edit.mainField === "id") return acc;
2959
3257
  const targetSchema = getTargetSchema(attr.targetModel);
2960
- if (!targetSchema)
2961
- return acc;
3258
+ if (!targetSchema) return acc;
2962
3259
  if (!isSortable(targetSchema, edit.mainField) && !isListable(targetSchema, edit.mainField)) {
2963
3260
  _.set(updatedMeta, ["edit", "mainField"], getDefaultMainField(targetSchema));
2964
3261
  _.set(acc, [key], updatedMeta);
@@ -2969,12 +3266,12 @@ async function syncMetadatas(configuration, schema) {
2969
3266
  return _.assign(metasWithDefaults, updatedMetas);
2970
3267
  }
2971
3268
  const getTargetSchema = (targetModel) => {
2972
- return getService$1("content-types").findContentType(targetModel);
3269
+ return getService$2("content-types").findContentType(targetModel);
2973
3270
  };
2974
3271
  const DEFAULT_LIST_LENGTH = 4;
2975
3272
  const MAX_ROW_SIZE = 12;
2976
3273
  const isAllowedFieldSize = (type, size) => {
2977
- const { getFieldSize } = getService$1("field-sizes");
3274
+ const { getFieldSize } = getService$2("field-sizes");
2978
3275
  const fieldSize = getFieldSize(type);
2979
3276
  if (!fieldSize.isResizable && size !== fieldSize.default) {
2980
3277
  return false;
@@ -2982,7 +3279,7 @@ const isAllowedFieldSize = (type, size) => {
2982
3279
  return size <= MAX_ROW_SIZE;
2983
3280
  };
2984
3281
  const getDefaultFieldSize = (attribute) => {
2985
- const { hasFieldSize, getFieldSize } = getService$1("field-sizes");
3282
+ const { hasFieldSize, getFieldSize } = getService$2("field-sizes");
2986
3283
  return getFieldSize(hasFieldSize(attribute.customField) ? attribute.customField : attribute.type).default;
2987
3284
  };
2988
3285
  async function createDefaultLayouts(schema) {
@@ -3003,8 +3300,7 @@ function createDefaultEditLayout(schema) {
3003
3300
  return appendToEditLayout([], keys2, schema);
3004
3301
  }
3005
3302
  function syncLayouts(configuration, schema) {
3006
- if (_.isEmpty(configuration.layouts))
3007
- return createDefaultLayouts(schema);
3303
+ if (_.isEmpty(configuration.layouts)) return createDefaultLayouts(schema);
3008
3304
  const { list = [], editRelations = [], edit = [] } = configuration.layouts || {};
3009
3305
  let cleanList = list.filter((attr) => isListable(schema, attr));
3010
3306
  const cleanEditRelations = editRelations.filter(
@@ -3015,9 +3311,8 @@ function syncLayouts(configuration, schema) {
3015
3311
  for (const row of edit) {
3016
3312
  const newRow = [];
3017
3313
  for (const el of row) {
3018
- if (!hasEditableAttribute(schema, el.name))
3019
- continue;
3020
- const { hasFieldSize } = getService$1("field-sizes");
3314
+ if (!hasEditableAttribute(schema, el.name)) continue;
3315
+ const { hasFieldSize } = getService$2("field-sizes");
3021
3316
  const fieldType = hasFieldSize(schema.attributes[el.name].customField) ? schema.attributes[el.name].customField : schema.attributes[el.name].type;
3022
3317
  if (!isAllowedFieldSize(fieldType, el.size)) {
3023
3318
  elementsToReAppend.push(el.name);
@@ -3047,8 +3342,7 @@ function syncLayouts(configuration, schema) {
3047
3342
  };
3048
3343
  }
3049
3344
  const appendToEditLayout = (layout = [], keysToAppend, schema) => {
3050
- if (keysToAppend.length === 0)
3051
- return layout;
3345
+ if (keysToAppend.length === 0) return layout;
3052
3346
  let currentRowIndex = Math.max(layout.length - 1, 0);
3053
3347
  if (!layout[currentRowIndex]) {
3054
3348
  layout[currentRowIndex] = [];
@@ -3157,17 +3451,17 @@ const configurationService$1 = createConfigurationService({
3157
3451
  isComponent: true,
3158
3452
  prefix: STORE_KEY_PREFIX,
3159
3453
  getModels() {
3160
- const { toContentManagerModel } = getService$1("data-mapper");
3454
+ const { toContentManagerModel } = getService$2("data-mapper");
3161
3455
  return mapValues(toContentManagerModel, strapi.components);
3162
3456
  }
3163
3457
  });
3164
3458
  const components = ({ strapi: strapi2 }) => ({
3165
3459
  findAllComponents() {
3166
- const { toContentManagerModel } = getService$1("data-mapper");
3460
+ const { toContentManagerModel } = getService$2("data-mapper");
3167
3461
  return Object.values(strapi2.components).map(toContentManagerModel);
3168
3462
  },
3169
3463
  findComponent(uid2) {
3170
- const { toContentManagerModel } = getService$1("data-mapper");
3464
+ const { toContentManagerModel } = getService$2("data-mapper");
3171
3465
  const component = strapi2.components[uid2];
3172
3466
  return isNil$1(component) ? component : toContentManagerModel(component);
3173
3467
  },
@@ -3218,17 +3512,17 @@ const configurationService = createConfigurationService({
3218
3512
  storeUtils,
3219
3513
  prefix: "content_types",
3220
3514
  getModels() {
3221
- const { toContentManagerModel } = getService$1("data-mapper");
3515
+ const { toContentManagerModel } = getService$2("data-mapper");
3222
3516
  return mapValues(toContentManagerModel, strapi.contentTypes);
3223
3517
  }
3224
3518
  });
3225
3519
  const service = ({ strapi: strapi2 }) => ({
3226
3520
  findAllContentTypes() {
3227
- const { toContentManagerModel } = getService$1("data-mapper");
3521
+ const { toContentManagerModel } = getService$2("data-mapper");
3228
3522
  return Object.values(strapi2.contentTypes).map(toContentManagerModel);
3229
3523
  },
3230
3524
  findContentType(uid2) {
3231
- const { toContentManagerModel } = getService$1("data-mapper");
3525
+ const { toContentManagerModel } = getService$2("data-mapper");
3232
3526
  const contentType = strapi2.contentTypes[uid2];
3233
3527
  return isNil$1(contentType) ? contentType : toContentManagerModel(contentType);
3234
3528
  },
@@ -3257,7 +3551,7 @@ const service = ({ strapi: strapi2 }) => ({
3257
3551
  return this.findConfiguration(contentType);
3258
3552
  },
3259
3553
  findComponentsConfigurations(contentType) {
3260
- return getService$1("components").findComponentsConfigurations(contentType);
3554
+ return getService$2("components").findComponentsConfigurations(contentType);
3261
3555
  },
3262
3556
  syncConfigurations() {
3263
3557
  return configurationService.syncConfigurations();
@@ -3438,12 +3732,27 @@ const createPermissionChecker = (strapi2) => ({ userAbility, model }) => {
3438
3732
  ability: userAbility,
3439
3733
  model
3440
3734
  });
3441
- const toSubject = (entity) => entity ? permissionsManager.toSubject(entity, model) : model;
3735
+ const { actionProvider } = strapi2.service("admin::permission");
3736
+ const toSubject = (entity) => {
3737
+ return entity ? permissionsManager.toSubject(entity, model) : model;
3738
+ };
3442
3739
  const can = (action, entity, field) => {
3443
- return userAbility.can(action, toSubject(entity), field);
3740
+ const subject = toSubject(entity);
3741
+ const aliases = actionProvider.unstable_aliases(action, model);
3742
+ return (
3743
+ // Test the original action to see if it passes
3744
+ userAbility.can(action, subject, field) || // Else try every known alias if at least one of them succeed, then the user "can"
3745
+ aliases.some((alias) => userAbility.can(alias, subject, field))
3746
+ );
3444
3747
  };
3445
3748
  const cannot = (action, entity, field) => {
3446
- return userAbility.cannot(action, toSubject(entity), field);
3749
+ const subject = toSubject(entity);
3750
+ const aliases = actionProvider.unstable_aliases(action, model);
3751
+ return (
3752
+ // Test both the original action
3753
+ userAbility.cannot(action, subject, field) && // and every known alias, if all of them fail (cannot), then the user truly "cannot"
3754
+ aliases.every((alias) => userAbility.cannot(alias, subject, field))
3755
+ );
3447
3756
  };
3448
3757
  const sanitizeOutput = (data, { action = ACTIONS.read } = {}) => {
3449
3758
  return permissionsManager.sanitizeOutput(data, { subject: toSubject(data), action });
@@ -3514,7 +3823,7 @@ const permission = ({ strapi: strapi2 }) => ({
3514
3823
  return userAbility.can(action);
3515
3824
  },
3516
3825
  async registerPermissions() {
3517
- const displayedContentTypes = getService$1("content-types").findDisplayedContentTypes();
3826
+ const displayedContentTypes = getService$2("content-types").findDisplayedContentTypes();
3518
3827
  const contentTypesUids = displayedContentTypes.map(prop("uid"));
3519
3828
  const actions = [
3520
3829
  {
@@ -3599,6 +3908,12 @@ function getPopulateForRelation(attribute, model, attributeName, { countMany, co
3599
3908
  if (initialPopulate) {
3600
3909
  return initialPopulate;
3601
3910
  }
3911
+ if (attributeName === "localizations") {
3912
+ const validationPopulate = getPopulateForValidation(model.uid);
3913
+ return {
3914
+ populate: validationPopulate.populate
3915
+ };
3916
+ }
3602
3917
  if (!isVisibleAttribute$1(model, attributeName)) {
3603
3918
  return true;
3604
3919
  }
@@ -3658,6 +3973,9 @@ const getDeepPopulate = (uid2, {
3658
3973
  return {};
3659
3974
  }
3660
3975
  const model = strapi.getModel(uid2);
3976
+ if (!model) {
3977
+ return {};
3978
+ }
3661
3979
  return Object.keys(model.attributes).reduce(
3662
3980
  (populateAcc, attributeName) => merge(
3663
3981
  populateAcc,
@@ -3677,40 +3995,46 @@ const getDeepPopulate = (uid2, {
3677
3995
  {}
3678
3996
  );
3679
3997
  };
3680
- const getValidatableFieldsPopulate = (uid2, {
3681
- initialPopulate = {},
3682
- countMany = false,
3683
- countOne = false,
3684
- maxLevel = Infinity
3685
- } = {}, level = 1) => {
3686
- if (level > maxLevel) {
3998
+ const getPopulateForValidation = (uid2) => {
3999
+ const model = strapi.getModel(uid2);
4000
+ if (!model) {
3687
4001
  return {};
3688
4002
  }
3689
- const model = strapi.getModel(uid2);
3690
4003
  return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
3691
- if (!getDoesAttributeRequireValidation(attribute)) {
4004
+ if (isScalarAttribute(attribute)) {
4005
+ if (getDoesAttributeRequireValidation(attribute)) {
4006
+ populateAcc.fields = populateAcc.fields || [];
4007
+ populateAcc.fields.push(attributeName);
4008
+ }
3692
4009
  return populateAcc;
3693
4010
  }
3694
- if (isScalarAttribute(attribute)) {
3695
- return merge(populateAcc, {
3696
- [attributeName]: true
3697
- });
4011
+ if (isComponent(attribute)) {
4012
+ const component = attribute.component;
4013
+ const componentResult = getPopulateForValidation(component);
4014
+ if (Object.keys(componentResult).length > 0) {
4015
+ populateAcc.populate = populateAcc.populate || {};
4016
+ populateAcc.populate[attributeName] = componentResult;
4017
+ }
4018
+ return populateAcc;
3698
4019
  }
3699
- return merge(
3700
- populateAcc,
3701
- getPopulateFor(
3702
- attributeName,
3703
- model,
3704
- {
3705
- // @ts-expect-error - improve types
3706
- initialPopulate: initialPopulate?.[attributeName],
3707
- countMany,
3708
- countOne,
3709
- maxLevel
4020
+ if (isDynamicZone(attribute)) {
4021
+ const components2 = attribute.components;
4022
+ const componentsResult = (components2 || []).reduce(
4023
+ (acc, componentUID) => {
4024
+ const componentResult = getPopulateForValidation(componentUID);
4025
+ if (Object.keys(componentResult).length > 0) {
4026
+ acc[componentUID] = componentResult;
4027
+ }
4028
+ return acc;
3710
4029
  },
3711
- level
3712
- )
3713
- );
4030
+ {}
4031
+ );
4032
+ if (Object.keys(componentsResult).length > 0) {
4033
+ populateAcc.populate = populateAcc.populate || {};
4034
+ populateAcc.populate[attributeName] = { on: componentsResult };
4035
+ }
4036
+ }
4037
+ return populateAcc;
3714
4038
  }, {});
3715
4039
  };
3716
4040
  const getDeepPopulateDraftCount = (uid2) => {
@@ -3720,6 +4044,10 @@ const getDeepPopulateDraftCount = (uid2) => {
3720
4044
  const attribute = model.attributes[attributeName];
3721
4045
  switch (attribute.type) {
3722
4046
  case "relation": {
4047
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
4048
+ if (isMorphRelation) {
4049
+ break;
4050
+ }
3723
4051
  if (isVisibleAttribute$1(model, attributeName)) {
3724
4052
  populateAcc[attributeName] = {
3725
4053
  count: true,
@@ -3786,7 +4114,7 @@ const getQueryPopulate = async (uid2, query) => {
3786
4114
  return populateQuery;
3787
4115
  };
3788
4116
  const buildDeepPopulate = (uid2) => {
3789
- return getService$1("populate-builder")(uid2).populateDeep(Infinity).countRelations().build();
4117
+ return getService$2("populate-builder")(uid2).populateDeep(Infinity).countRelations().build();
3790
4118
  };
3791
4119
  const populateBuilder = (uid2) => {
3792
4120
  let getInitialPopulate = async () => {
@@ -3948,7 +4276,6 @@ const AVAILABLE_LOCALES_FIELDS = [
3948
4276
  "locale",
3949
4277
  "updatedAt",
3950
4278
  "createdAt",
3951
- "status",
3952
4279
  "publishedAt",
3953
4280
  "documentId"
3954
4281
  ];
@@ -3969,34 +4296,20 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3969
4296
  /**
3970
4297
  * Returns available locales of a document for the current status
3971
4298
  */
3972
- async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
4299
+ async getAvailableLocales(uid2, version, allVersions) {
3973
4300
  const versionsByLocale = groupBy("locale", allVersions);
3974
- delete versionsByLocale[version.locale];
4301
+ if (version.locale) {
4302
+ delete versionsByLocale[version.locale];
4303
+ }
3975
4304
  const model = strapi2.getModel(uid2);
3976
- const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
3977
- const traversalFunction = async (localeVersion) => traverseEntity(
3978
- ({ key }, { remove }) => {
3979
- if (keysToKeep.includes(key)) {
3980
- return;
3981
- }
3982
- remove(key);
3983
- },
3984
- { schema: model, getModel: strapi2.getModel.bind(strapi2) },
3985
- // @ts-expect-error fix types DocumentVersion incompatible with Data
3986
- localeVersion
3987
- );
3988
4305
  const mappingResult = await async.map(
3989
4306
  Object.values(versionsByLocale),
3990
4307
  async (localeVersions) => {
3991
- const mappedLocaleVersions = await async.map(
3992
- localeVersions,
3993
- traversalFunction
3994
- );
3995
4308
  if (!contentTypes$1.hasDraftAndPublish(model)) {
3996
- return mappedLocaleVersions[0];
4309
+ return localeVersions[0];
3997
4310
  }
3998
- const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
3999
- const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
4311
+ const draftVersion = localeVersions.find((v) => v.publishedAt === null);
4312
+ const otherVersions = localeVersions.filter((v) => v.id !== draftVersion?.id);
4000
4313
  if (!draftVersion) {
4001
4314
  return;
4002
4315
  }
@@ -4018,8 +4331,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4018
4331
  const matchStatus = status === "published" ? v.publishedAt !== null : v.publishedAt === null;
4019
4332
  return matchLocale && matchStatus;
4020
4333
  });
4021
- if (!availableStatus)
4022
- return availableStatus;
4334
+ if (!availableStatus) return availableStatus;
4023
4335
  return pick(AVAILABLE_STATUS_FIELDS, availableStatus);
4024
4336
  },
4025
4337
  /**
@@ -4029,18 +4341,19 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4029
4341
  * @returns
4030
4342
  */
4031
4343
  async getManyAvailableStatus(uid2, documents) {
4032
- if (!documents.length)
4033
- return [];
4344
+ if (!documents.length) return [];
4034
4345
  const status = documents[0].publishedAt !== null ? "published" : "draft";
4035
- const locale = documents[0]?.locale;
4036
- const otherStatus = status === "published" ? "draft" : "published";
4037
- return strapi2.documents(uid2).findMany({
4038
- filters: {
4039
- documentId: { $in: documents.map((d) => d.documentId).filter(Boolean) }
4040
- },
4041
- status: otherStatus,
4042
- locale,
4043
- fields: ["documentId", "locale", "updatedAt", "createdAt", "publishedAt"]
4346
+ const locales = documents.map((d) => d.locale).filter(Boolean);
4347
+ const where = {
4348
+ documentId: { $in: documents.map((d) => d.documentId).filter(Boolean) },
4349
+ publishedAt: { $null: status === "published" }
4350
+ };
4351
+ if (locales.length) {
4352
+ where.locale = { $in: locales };
4353
+ }
4354
+ return strapi2.query(uid2).findMany({
4355
+ where,
4356
+ select: ["id", "documentId", "locale", "updatedAt", "createdAt", "publishedAt"]
4044
4357
  });
4045
4358
  },
4046
4359
  getStatus(version, otherDocumentStatuses) {
@@ -4057,10 +4370,8 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4057
4370
  } else if (otherVersion) {
4058
4371
  draftVersion = otherVersion;
4059
4372
  }
4060
- if (!draftVersion)
4061
- return CONTENT_MANAGER_STATUS.PUBLISHED;
4062
- if (!publishedVersion)
4063
- return CONTENT_MANAGER_STATUS.DRAFT;
4373
+ if (!draftVersion) return CONTENT_MANAGER_STATUS.PUBLISHED;
4374
+ if (!publishedVersion) return CONTENT_MANAGER_STATUS.DRAFT;
4064
4375
  const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
4065
4376
  return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
4066
4377
  },
@@ -4068,11 +4379,9 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4068
4379
  // We could refactor this so the locales are only loaded when they're
4069
4380
  // needed. e.g. in the bulk locale action modal.
4070
4381
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4071
- const populate = getValidatableFieldsPopulate(uid2);
4072
- const versions = await strapi2.db.query(uid2).findMany({
4073
- where: { documentId: version.documentId },
4382
+ const { populate = {}, fields = [] } = getPopulateForValidation(uid2);
4383
+ const params = {
4074
4384
  populate: {
4075
- // Populate only fields that require validation for bulk locale actions
4076
4385
  ...populate,
4077
4386
  // NOTE: creator fields are selected in this way to avoid exposing sensitive data
4078
4387
  createdBy: {
@@ -4081,9 +4390,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4081
4390
  updatedBy: {
4082
4391
  select: ["id", "firstname", "lastname", "email"]
4083
4392
  }
4393
+ },
4394
+ fields: uniq([...AVAILABLE_LOCALES_FIELDS, ...fields]),
4395
+ filters: {
4396
+ documentId: version.documentId
4084
4397
  }
4085
- });
4086
- const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
4398
+ };
4399
+ const dbParams = strapi2.get("query-params").transform(uid2, params);
4400
+ const versions = await strapi2.db.query(uid2).findMany(dbParams);
4401
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions) : [];
4087
4402
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
4088
4403
  return {
4089
4404
  availableLocales: availableLocalesResult,
@@ -4097,13 +4412,29 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4097
4412
  */
4098
4413
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
4099
4414
  if (!document) {
4100
- return document;
4415
+ return {
4416
+ data: document,
4417
+ meta: {
4418
+ availableLocales: [],
4419
+ availableStatus: []
4420
+ }
4421
+ };
4101
4422
  }
4102
4423
  const hasDraftAndPublish = contentTypes$1.hasDraftAndPublish(strapi2.getModel(uid2));
4103
4424
  if (!hasDraftAndPublish) {
4104
4425
  opts.availableStatus = false;
4105
4426
  }
4106
4427
  const meta = await this.getMetadata(uid2, document, opts);
4428
+ if (document.localizations) {
4429
+ const otherStatus = await this.getManyAvailableStatus(uid2, document.localizations);
4430
+ document.localizations = document.localizations.map((d) => {
4431
+ const status = otherStatus.find((s) => s.documentId === d.documentId);
4432
+ return {
4433
+ ...d,
4434
+ status: this.getStatus(d, status ? [status] : [])
4435
+ };
4436
+ });
4437
+ }
4107
4438
  return {
4108
4439
  data: {
4109
4440
  ...document,
@@ -4205,10 +4536,7 @@ const documentManager = ({ strapi: strapi2 }) => {
4205
4536
  async clone(id, body, uid2) {
4206
4537
  const populate = await buildDeepPopulate(uid2);
4207
4538
  const params = {
4208
- data: {
4209
- ...omitIdField(body),
4210
- [PUBLISHED_AT_ATTRIBUTE]: null
4211
- },
4539
+ data: omitIdField(body),
4212
4540
  populate
4213
4541
  };
4214
4542
  return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
@@ -4324,7 +4652,8 @@ const services = {
4324
4652
  permission,
4325
4653
  "populate-builder": populateBuilder$1,
4326
4654
  uid,
4327
- ...history.services ? history.services : {}
4655
+ ...history.services ? history.services : {},
4656
+ ...preview.services ? preview.services : {}
4328
4657
  };
4329
4658
  const index = () => {
4330
4659
  return {