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

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 (180) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{CardDragPreview-DSVYodBX.js → CardDragPreview-C0QyJgRA.js} +10 -14
  3. package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -0
  4. package/dist/_chunks/{CardDragPreview-ikSG4M46.mjs → CardDragPreview-DOxamsuj.mjs} +7 -9
  5. package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -0
  6. package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js → ComponentConfigurationPage-BWQv6yRj.js} +3 -3
  7. package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js.map → ComponentConfigurationPage-BWQv6yRj.js.map} +1 -1
  8. package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs → ComponentConfigurationPage-C7ImeKGM.mjs} +3 -3
  9. package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs.map → ComponentConfigurationPage-C7ImeKGM.mjs.map} +1 -1
  10. package/dist/_chunks/{ComponentIcon-BBQsYCVn.js → ComponentIcon-BXdiCGQp.js} +8 -2
  11. package/dist/_chunks/ComponentIcon-BXdiCGQp.js.map +1 -0
  12. package/dist/_chunks/{ComponentIcon-BOFnK76n.mjs → ComponentIcon-u4bIXTFY.mjs} +9 -3
  13. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -0
  14. package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js → EditConfigurationPage-CEGwxV-L.js} +3 -3
  15. package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js.map → EditConfigurationPage-CEGwxV-L.js.map} +1 -1
  16. package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs → EditConfigurationPage-MItFGzT9.mjs} +3 -3
  17. package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs.map → EditConfigurationPage-MItFGzT9.mjs.map} +1 -1
  18. package/dist/_chunks/{EditViewPage-CzOT5Kpj.js → EditViewPage-CmMi2Xsn.js} +46 -48
  19. package/dist/_chunks/EditViewPage-CmMi2Xsn.js.map +1 -0
  20. package/dist/_chunks/{EditViewPage-Bm8lgcm6.mjs → EditViewPage-DhmAg0NK.mjs} +47 -47
  21. package/dist/_chunks/EditViewPage-DhmAg0NK.mjs.map +1 -0
  22. package/dist/_chunks/{Field-Caef4JjM.js → Field-1DLtcLAI.js} +948 -777
  23. package/dist/_chunks/Field-1DLtcLAI.js.map +1 -0
  24. package/dist/_chunks/{Field-Dlh0uGnL.mjs → Field-Cs62u5pl.mjs} +896 -724
  25. package/dist/_chunks/Field-Cs62u5pl.mjs.map +1 -0
  26. package/dist/_chunks/{Form-BzuAjtRq.js → Form-CqFA7F_V.js} +42 -41
  27. package/dist/_chunks/Form-CqFA7F_V.js.map +1 -0
  28. package/dist/_chunks/{Form-EnaQL_6L.mjs → Form-zYHtzGUX.mjs} +43 -41
  29. package/dist/_chunks/Form-zYHtzGUX.mjs.map +1 -0
  30. package/dist/_chunks/{History-C17LiyRg.js → History-BblwXv7-.js} +149 -66
  31. package/dist/_chunks/History-BblwXv7-.js.map +1 -0
  32. package/dist/_chunks/{History-D6sbCJvo.mjs → History-DalgFQ3D.mjs} +149 -65
  33. package/dist/_chunks/History-DalgFQ3D.mjs.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-Dks5SX6f.js → ListConfigurationPage-Cpy4QqNd.js} +61 -62
  35. package/dist/_chunks/ListConfigurationPage-Cpy4QqNd.js.map +1 -0
  36. package/dist/_chunks/{ListConfigurationPage-Ce4qs7qE.mjs → ListConfigurationPage-DWy-vRzs.mjs} +58 -58
  37. package/dist/_chunks/ListConfigurationPage-DWy-vRzs.mjs.map +1 -0
  38. package/dist/_chunks/{ListViewPage-Be7S5aKL.mjs → ListViewPage-BkAwIW9s.mjs} +90 -106
  39. package/dist/_chunks/ListViewPage-BkAwIW9s.mjs.map +1 -0
  40. package/dist/_chunks/{ListViewPage-BwrZrPsh.js → ListViewPage-DFjn1DNW.js} +95 -111
  41. package/dist/_chunks/ListViewPage-DFjn1DNW.js.map +1 -0
  42. package/dist/_chunks/{NoContentTypePage-CIPmYQMm.mjs → NoContentTypePage-B9BCNNdL.mjs} +7 -7
  43. package/dist/_chunks/NoContentTypePage-B9BCNNdL.mjs.map +1 -0
  44. package/dist/_chunks/{NoContentTypePage-Cu5r1-JT.js → NoContentTypePage-C-3ykoxs.js} +5 -5
  45. package/dist/_chunks/NoContentTypePage-C-3ykoxs.js.map +1 -0
  46. package/dist/_chunks/{NoPermissionsPage-DhJ7LYrr.mjs → NoPermissionsPage-Bt_HWGat.mjs} +5 -6
  47. package/dist/_chunks/NoPermissionsPage-Bt_HWGat.mjs.map +1 -0
  48. package/dist/_chunks/{NoPermissionsPage-C-j6TEUF.js → NoPermissionsPage-DKLmDZnZ.js} +4 -5
  49. package/dist/_chunks/NoPermissionsPage-DKLmDZnZ.js.map +1 -0
  50. package/dist/_chunks/{Relations-CY7AtkDA.mjs → Relations-CJmTbZ8T.mjs} +66 -56
  51. package/dist/_chunks/Relations-CJmTbZ8T.mjs.map +1 -0
  52. package/dist/_chunks/{Relations-Czs-uZ-s.js → Relations-CrxfoH2n.js} +70 -61
  53. package/dist/_chunks/Relations-CrxfoH2n.js.map +1 -0
  54. package/dist/_chunks/{en-MBPul9Su.mjs → en-Ux26r5pl.mjs} +7 -1
  55. package/dist/_chunks/{en-MBPul9Su.mjs.map → en-Ux26r5pl.mjs.map} +1 -1
  56. package/dist/_chunks/{en-C-V1_90f.js → en-fbKQxLGn.js} +7 -1
  57. package/dist/_chunks/{en-C-V1_90f.js.map → en-fbKQxLGn.js.map} +1 -1
  58. package/dist/_chunks/{index-X_2tafck.js → index-Buwn78Rt.js} +1507 -868
  59. package/dist/_chunks/index-Buwn78Rt.js.map +1 -0
  60. package/dist/_chunks/{index-DNVx8ssZ.mjs → index-D1344xdw.mjs} +1476 -836
  61. package/dist/_chunks/index-D1344xdw.mjs.map +1 -0
  62. package/dist/_chunks/{layout-Dnh0PNp9.mjs → layout-ChVuUpa1.mjs} +32 -27
  63. package/dist/_chunks/layout-ChVuUpa1.mjs.map +1 -0
  64. package/dist/_chunks/{layout-dBc7wN7L.js → layout-DRuJUpas.js} +32 -29
  65. package/dist/_chunks/layout-DRuJUpas.js.map +1 -0
  66. package/dist/_chunks/{relations-Dx7tMKJN.mjs → relations-B-deMCy4.mjs} +2 -2
  67. package/dist/_chunks/{relations-Dx7tMKJN.mjs.map → relations-B-deMCy4.mjs.map} +1 -1
  68. package/dist/_chunks/{relations-4pHtBrHJ.js → relations-DuoUwyJr.js} +2 -2
  69. package/dist/_chunks/{relations-4pHtBrHJ.js.map → relations-DuoUwyJr.js.map} +1 -1
  70. package/dist/_chunks/useDragAndDrop-DdHgKsqq.mjs.map +1 -1
  71. package/dist/_chunks/useDragAndDrop-J0TUUbR6.js.map +1 -1
  72. package/dist/_chunks/usePrev-B9w_-eYc.js +15 -0
  73. package/dist/_chunks/usePrev-B9w_-eYc.js.map +1 -0
  74. package/dist/_chunks/usePrev-DH6iah0A.mjs +16 -0
  75. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +1 -0
  76. package/dist/admin/index.js +2 -1
  77. package/dist/admin/index.js.map +1 -1
  78. package/dist/admin/index.mjs +5 -4
  79. package/dist/admin/src/components/ComponentIcon.d.ts +6 -3
  80. package/dist/admin/src/content-manager.d.ts +3 -3
  81. package/dist/admin/src/exports.d.ts +1 -0
  82. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
  83. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  84. package/dist/admin/src/hooks/useDocument.d.ts +5 -8
  85. package/dist/admin/src/hooks/useDocumentActions.d.ts +24 -3
  86. package/dist/admin/src/hooks/useDocumentLayout.d.ts +2 -2
  87. package/dist/admin/src/hooks/useDragAndDrop.d.ts +4 -4
  88. package/dist/admin/src/hooks/useKeyboardDragAndDrop.d.ts +1 -1
  89. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +10 -4
  90. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksInput.d.ts +3 -3
  91. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +4 -0
  92. package/dist/admin/src/pages/EditView/components/FormInputs/Component/Input.d.ts +2 -2
  93. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +3 -5
  94. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
  95. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +10 -18
  96. package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
  97. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +3 -49
  98. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
  99. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +59 -52
  100. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
  101. package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
  102. package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
  103. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +9 -26
  104. package/dist/admin/src/services/api.d.ts +2 -3
  105. package/dist/admin/src/services/components.d.ts +2 -2
  106. package/dist/admin/src/services/contentTypes.d.ts +5 -5
  107. package/dist/admin/src/services/documents.d.ts +29 -17
  108. package/dist/admin/src/services/init.d.ts +2 -2
  109. package/dist/admin/src/services/relations.d.ts +3 -3
  110. package/dist/admin/src/services/uid.d.ts +3 -3
  111. package/dist/admin/src/utils/api.d.ts +4 -18
  112. package/dist/admin/src/utils/validation.d.ts +1 -6
  113. package/dist/server/index.js +547 -416
  114. package/dist/server/index.js.map +1 -1
  115. package/dist/server/index.mjs +555 -424
  116. package/dist/server/index.mjs.map +1 -1
  117. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  118. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  119. package/dist/server/src/controllers/utils/metadata.d.ts +8 -0
  120. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
  121. package/dist/server/src/controllers/validation/dimensions.d.ts +9 -0
  122. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
  123. package/dist/server/src/controllers/validation/index.d.ts +1 -1
  124. package/dist/server/src/history/services/history.d.ts +2 -4
  125. package/dist/server/src/history/services/history.d.ts.map +1 -1
  126. package/dist/server/src/history/services/index.d.ts +6 -2
  127. package/dist/server/src/history/services/index.d.ts.map +1 -1
  128. package/dist/server/src/history/services/lifecycles.d.ts +9 -0
  129. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -0
  130. package/dist/server/src/history/services/utils.d.ts +41 -9
  131. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  132. package/dist/server/src/history/utils.d.ts +6 -2
  133. package/dist/server/src/history/utils.d.ts.map +1 -1
  134. package/dist/server/src/index.d.ts +18 -39
  135. package/dist/server/src/index.d.ts.map +1 -1
  136. package/dist/server/src/services/document-manager.d.ts +13 -12
  137. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  138. package/dist/server/src/services/document-metadata.d.ts +8 -29
  139. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  140. package/dist/server/src/services/index.d.ts +18 -39
  141. package/dist/server/src/services/index.d.ts.map +1 -1
  142. package/dist/server/src/services/utils/populate.d.ts +8 -1
  143. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  144. package/dist/shared/contracts/collection-types.d.ts +14 -6
  145. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  146. package/dist/shared/contracts/relations.d.ts +2 -2
  147. package/dist/shared/contracts/relations.d.ts.map +1 -1
  148. package/package.json +13 -14
  149. package/dist/_chunks/CardDragPreview-DSVYodBX.js.map +0 -1
  150. package/dist/_chunks/CardDragPreview-ikSG4M46.mjs.map +0 -1
  151. package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
  152. package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
  153. package/dist/_chunks/EditViewPage-Bm8lgcm6.mjs.map +0 -1
  154. package/dist/_chunks/EditViewPage-CzOT5Kpj.js.map +0 -1
  155. package/dist/_chunks/Field-Caef4JjM.js.map +0 -1
  156. package/dist/_chunks/Field-Dlh0uGnL.mjs.map +0 -1
  157. package/dist/_chunks/Form-BzuAjtRq.js.map +0 -1
  158. package/dist/_chunks/Form-EnaQL_6L.mjs.map +0 -1
  159. package/dist/_chunks/History-C17LiyRg.js.map +0 -1
  160. package/dist/_chunks/History-D6sbCJvo.mjs.map +0 -1
  161. package/dist/_chunks/ListConfigurationPage-Ce4qs7qE.mjs.map +0 -1
  162. package/dist/_chunks/ListConfigurationPage-Dks5SX6f.js.map +0 -1
  163. package/dist/_chunks/ListViewPage-Be7S5aKL.mjs.map +0 -1
  164. package/dist/_chunks/ListViewPage-BwrZrPsh.js.map +0 -1
  165. package/dist/_chunks/NoContentTypePage-CIPmYQMm.mjs.map +0 -1
  166. package/dist/_chunks/NoContentTypePage-Cu5r1-JT.js.map +0 -1
  167. package/dist/_chunks/NoPermissionsPage-C-j6TEUF.js.map +0 -1
  168. package/dist/_chunks/NoPermissionsPage-DhJ7LYrr.mjs.map +0 -1
  169. package/dist/_chunks/Relations-CY7AtkDA.mjs.map +0 -1
  170. package/dist/_chunks/Relations-Czs-uZ-s.js.map +0 -1
  171. package/dist/_chunks/index-DNVx8ssZ.mjs.map +0 -1
  172. package/dist/_chunks/index-X_2tafck.js.map +0 -1
  173. package/dist/_chunks/layout-Dnh0PNp9.mjs.map +0 -1
  174. package/dist/_chunks/layout-dBc7wN7L.js.map +0 -1
  175. package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
  176. package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
  177. package/dist/_chunks/urls-DzZya_gm.js +0 -6
  178. package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
  179. package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
  180. package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
@@ -138,40 +138,65 @@ const FIELDS_TO_IGNORE = [
138
138
  "strapi_stage",
139
139
  "strapi_assignee"
140
140
  ];
141
- const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
142
- const sanitizedContentTypeSchemaAttributes = fp.omit(FIELDS_TO_IGNORE, contentTypeSchemaAttributes);
143
- const reduceDifferenceToAttributesObject = (diffKeys, source) => {
144
- return diffKeys.reduce((previousAttributesObject, diffKey) => {
145
- previousAttributesObject[diffKey] = source[diffKey];
146
- return previousAttributesObject;
147
- }, {});
148
- };
149
- const versionSchemaKeys = Object.keys(versionSchemaAttributes);
150
- const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
151
- const uniqueToContentType = fp.difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
152
- const added = reduceDifferenceToAttributesObject(
153
- uniqueToContentType,
154
- sanitizedContentTypeSchemaAttributes
155
- );
156
- const uniqueToVersion = fp.difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
157
- const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
158
- return { added, removed };
159
- };
160
141
  const DEFAULT_RETENTION_DAYS = 90;
161
- const createHistoryService = ({ strapi: strapi2 }) => {
162
- const state = {
163
- deleteExpiredJob: null,
164
- isInitialized: false
142
+ const createServiceUtils = ({ strapi: strapi2 }) => {
143
+ const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
144
+ const sanitizedContentTypeSchemaAttributes = fp.omit(
145
+ FIELDS_TO_IGNORE,
146
+ contentTypeSchemaAttributes
147
+ );
148
+ const reduceDifferenceToAttributesObject = (diffKeys, source) => {
149
+ return diffKeys.reduce(
150
+ (previousAttributesObject, diffKey) => {
151
+ previousAttributesObject[diffKey] = source[diffKey];
152
+ return previousAttributesObject;
153
+ },
154
+ {}
155
+ );
156
+ };
157
+ const versionSchemaKeys = Object.keys(versionSchemaAttributes);
158
+ const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
159
+ const uniqueToContentType = fp.difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
160
+ const added = reduceDifferenceToAttributesObject(
161
+ uniqueToContentType,
162
+ sanitizedContentTypeSchemaAttributes
163
+ );
164
+ const uniqueToVersion = fp.difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
165
+ const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
166
+ return { added, removed };
165
167
  };
166
- const query = strapi2.db.query(HISTORY_VERSION_UID);
167
- const getRetentionDays = (strapi22) => {
168
- const featureConfig = strapi22.ee.features.get("cms-content-history");
169
- const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
170
- const userRetentionDays = strapi22.config.get("admin.history.retentionDays");
171
- if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
172
- return userRetentionDays;
168
+ const getRelationRestoreValue = async (versionRelationData, attribute) => {
169
+ if (Array.isArray(versionRelationData)) {
170
+ if (versionRelationData.length === 0)
171
+ return versionRelationData;
172
+ const existingAndMissingRelations = await Promise.all(
173
+ versionRelationData.map((relation) => {
174
+ return strapi2.documents(attribute.target).findOne({
175
+ documentId: relation.documentId,
176
+ locale: relation.locale || void 0
177
+ });
178
+ })
179
+ );
180
+ return existingAndMissingRelations.filter(
181
+ (relation) => relation !== null
182
+ );
173
183
  }
174
- return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
184
+ return strapi2.documents(attribute.target).findOne({
185
+ documentId: versionRelationData.documentId,
186
+ locale: versionRelationData.locale || void 0
187
+ });
188
+ };
189
+ const getMediaRestoreValue = async (versionRelationData, attribute) => {
190
+ if (attribute.multiple) {
191
+ const existingAndMissingMedias = await Promise.all(
192
+ // @ts-expect-error Fix the type definitions so this isn't any
193
+ versionRelationData.map((media) => {
194
+ return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
195
+ })
196
+ );
197
+ return existingAndMissingMedias.filter((media) => media != null);
198
+ }
199
+ return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
175
200
  };
176
201
  const localesService = strapi2.plugin("i18n")?.service("locales");
177
202
  const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
@@ -187,6 +212,15 @@ const createHistoryService = ({ strapi: strapi2 }) => {
187
212
  {}
188
213
  );
189
214
  };
215
+ const getRetentionDays = () => {
216
+ const featureConfig = strapi2.ee.features.get("cms-content-history");
217
+ const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
218
+ const userRetentionDays = strapi2.config.get("admin.history.retentionDays");
219
+ if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
220
+ return userRetentionDays;
221
+ }
222
+ return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
223
+ };
190
224
  const getVersionStatus = async (contentTypeUid, document) => {
191
225
  const documentMetadataService = strapi2.plugin("content-manager").service("document-metadata");
192
226
  const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
@@ -198,6 +232,10 @@ const createHistoryService = ({ strapi: strapi2 }) => {
198
232
  return attributes.reduce((acc, [attributeName, attribute]) => {
199
233
  switch (attribute.type) {
200
234
  case "relation": {
235
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
236
+ if (isMorphRelation) {
237
+ break;
238
+ }
201
239
  const isVisible2 = strapiUtils.contentTypes.isVisibleAttribute(model, attributeName);
202
240
  if (isVisible2) {
203
241
  acc[attributeName] = { fields: ["documentId", "locale", "publishedAt"] };
@@ -228,80 +266,68 @@ const createHistoryService = ({ strapi: strapi2 }) => {
228
266
  return acc;
229
267
  }, {});
230
268
  };
231
- return {
232
- async bootstrap() {
233
- if (state.isInitialized) {
234
- return;
235
- }
236
- strapi2.documents.use(async (context, next) => {
237
- if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
238
- return next();
269
+ const buildMediaResponse = async (values) => {
270
+ return values.slice(0, 25).reduce(
271
+ async (currentRelationDataPromise, entry) => {
272
+ const currentRelationData = await currentRelationDataPromise;
273
+ if (!entry) {
274
+ return currentRelationData;
239
275
  }
240
- if (context.action !== "create" && context.action !== "update" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
241
- return next();
276
+ const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
277
+ if (relatedEntry) {
278
+ currentRelationData.results.push(relatedEntry);
279
+ } else {
280
+ currentRelationData.meta.missingCount += 1;
242
281
  }
243
- const contentTypeUid = context.contentType.uid;
244
- if (!contentTypeUid.startsWith("api::")) {
245
- return next();
282
+ return currentRelationData;
283
+ },
284
+ Promise.resolve({
285
+ results: [],
286
+ meta: { missingCount: 0 }
287
+ })
288
+ );
289
+ };
290
+ const buildRelationReponse = async (values, attributeSchema) => {
291
+ return values.slice(0, 25).reduce(
292
+ async (currentRelationDataPromise, entry) => {
293
+ const currentRelationData = await currentRelationDataPromise;
294
+ if (!entry) {
295
+ return currentRelationData;
246
296
  }
247
- const result = await next();
248
- const documentContext = context.action === "create" ? { documentId: result.documentId, locale: context.params?.locale } : { documentId: context.params.documentId, locale: context.params?.locale };
249
- const defaultLocale = await getDefaultLocale();
250
- const locale = documentContext.locale || defaultLocale;
251
- const document = await strapi2.documents(contentTypeUid).findOne({
252
- documentId: documentContext.documentId,
253
- locale,
254
- populate: getDeepPopulate2(contentTypeUid)
255
- });
256
- const status = await getVersionStatus(contentTypeUid, document);
257
- const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
258
- const componentsSchemas = Object.keys(
259
- attributesSchema
260
- ).reduce((currentComponentSchemas, key) => {
261
- const fieldSchema = attributesSchema[key];
262
- if (fieldSchema.type === "component") {
263
- const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
264
- return {
265
- ...currentComponentSchemas,
266
- [fieldSchema.component]: componentSchema
267
- };
268
- }
269
- return currentComponentSchemas;
270
- }, {});
271
- await strapi2.db.transaction(async ({ onCommit }) => {
272
- onCommit(() => {
273
- this.createVersion({
274
- contentType: contentTypeUid,
275
- data: fp.omit(FIELDS_TO_IGNORE, document),
276
- schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
277
- componentsSchemas,
278
- relatedDocumentId: documentContext.documentId,
279
- locale,
280
- status
281
- });
297
+ const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
298
+ if (relatedEntry) {
299
+ currentRelationData.results.push({
300
+ ...relatedEntry,
301
+ status: await getVersionStatus(attributeSchema.target, relatedEntry)
282
302
  });
283
- });
284
- return result;
285
- });
286
- const retentionDays = getRetentionDays(strapi2);
287
- state.deleteExpiredJob = nodeSchedule.scheduleJob("0 0 * * *", () => {
288
- const retentionDaysInMilliseconds = retentionDays * 24 * 60 * 60 * 1e3;
289
- const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
290
- query.deleteMany({
291
- where: {
292
- created_at: {
293
- $lt: expirationDate.toISOString()
294
- }
295
- }
296
- });
297
- });
298
- state.isInitialized = true;
299
- },
300
- async destroy() {
301
- if (state.deleteExpiredJob) {
302
- state.deleteExpiredJob.cancel();
303
- }
304
- },
303
+ } else {
304
+ currentRelationData.meta.missingCount += 1;
305
+ }
306
+ return currentRelationData;
307
+ },
308
+ Promise.resolve({
309
+ results: [],
310
+ meta: { missingCount: 0 }
311
+ })
312
+ );
313
+ };
314
+ return {
315
+ getSchemaAttributesDiff,
316
+ getRelationRestoreValue,
317
+ getMediaRestoreValue,
318
+ getDefaultLocale,
319
+ getLocaleDictionary,
320
+ getRetentionDays,
321
+ getVersionStatus,
322
+ getDeepPopulate: getDeepPopulate2,
323
+ buildMediaResponse,
324
+ buildRelationReponse
325
+ };
326
+ };
327
+ const createHistoryService = ({ strapi: strapi2 }) => {
328
+ const query = strapi2.db.query(HISTORY_VERSION_UID);
329
+ const serviceUtils = createServiceUtils({ strapi: strapi2 });
330
+ return {
305
331
  async createVersion(historyVersionData) {
306
332
  await query.create({
307
333
  data: {
@@ -312,7 +338,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
312
338
  });
313
339
  },
314
340
  async findVersionsPage(params) {
315
- const locale = params.query.locale || await getDefaultLocale();
341
+ const locale = params.query.locale || await serviceUtils.getDefaultLocale();
316
342
  const [{ results, pagination }, localeDictionary] = await Promise.all([
317
343
  query.findPage({
318
344
  ...params.query,
@@ -326,78 +352,34 @@ const createHistoryService = ({ strapi: strapi2 }) => {
326
352
  populate: ["createdBy"],
327
353
  orderBy: [{ createdAt: "desc" }]
328
354
  }),
329
- getLocaleDictionary()
355
+ serviceUtils.getLocaleDictionary()
330
356
  ]);
331
- const buildRelationReponse = async (values, attributeSchema) => {
332
- return values.slice(0, 25).reduce(
333
- async (currentRelationDataPromise, entry) => {
334
- const currentRelationData = await currentRelationDataPromise;
335
- if (!entry) {
336
- return currentRelationData;
337
- }
338
- const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
339
- const permissionChecker2 = getService$1("permission-checker").create({
340
- userAbility: params.state.userAbility,
341
- model: attributeSchema.target
342
- });
343
- const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
344
- if (sanitizedEntry) {
345
- currentRelationData.results.push({
346
- ...sanitizedEntry,
347
- status: await getVersionStatus(attributeSchema.target, sanitizedEntry)
348
- });
349
- } else {
350
- currentRelationData.meta.missingCount += 1;
351
- }
352
- return currentRelationData;
353
- },
354
- Promise.resolve({
355
- results: [],
356
- meta: { missingCount: 0 }
357
- })
358
- );
359
- };
360
- const buildMediaResponse = async (values) => {
361
- return values.slice(0, 25).reduce(
362
- async (currentRelationDataPromise, entry) => {
363
- const currentRelationData = await currentRelationDataPromise;
364
- if (!entry) {
365
- return currentRelationData;
366
- }
367
- const permissionChecker2 = getService$1("permission-checker").create({
368
- userAbility: params.state.userAbility,
369
- model: "plugin::upload.file"
370
- });
371
- const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
372
- const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
373
- if (sanitizedEntry) {
374
- currentRelationData.results.push(sanitizedEntry);
375
- } else {
376
- currentRelationData.meta.missingCount += 1;
377
- }
378
- return currentRelationData;
379
- },
380
- Promise.resolve({
381
- results: [],
382
- meta: { missingCount: 0 }
383
- })
384
- );
385
- };
386
357
  const populateEntryRelations = async (entry) => {
387
358
  const entryWithRelations = await Object.entries(entry.schema).reduce(
388
359
  async (currentDataWithRelations, [attributeKey, attributeSchema]) => {
389
360
  const attributeValue = entry.data[attributeKey];
390
361
  const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
391
362
  if (attributeSchema.type === "media") {
363
+ const permissionChecker2 = getService$1("permission-checker").create({
364
+ userAbility: params.state.userAbility,
365
+ model: "plugin::upload.file"
366
+ });
367
+ const response = await serviceUtils.buildMediaResponse(attributeValues);
368
+ const sanitizedResults = await Promise.all(
369
+ response.results.map((media) => permissionChecker2.sanitizeOutput(media))
370
+ );
392
371
  return {
393
372
  ...await currentDataWithRelations,
394
- [attributeKey]: await buildMediaResponse(attributeValues)
373
+ [attributeKey]: {
374
+ results: sanitizedResults,
375
+ meta: response.meta
376
+ }
395
377
  };
396
378
  }
397
379
  if (attributeSchema.type === "relation" && attributeSchema.relation !== "morphToOne" && attributeSchema.relation !== "morphToMany") {
398
380
  if (attributeSchema.target === "admin::user") {
399
381
  const adminUsers = await Promise.all(
400
- attributeValues.map(async (userToPopulate) => {
382
+ attributeValues.map((userToPopulate) => {
401
383
  if (userToPopulate == null) {
402
384
  return null;
403
385
  }
@@ -414,9 +396,23 @@ const createHistoryService = ({ strapi: strapi2 }) => {
414
396
  [attributeKey]: adminUsers
415
397
  };
416
398
  }
399
+ const permissionChecker2 = getService$1("permission-checker").create({
400
+ userAbility: params.state.userAbility,
401
+ model: attributeSchema.target
402
+ });
403
+ const response = await serviceUtils.buildRelationReponse(
404
+ attributeValues,
405
+ attributeSchema
406
+ );
407
+ const sanitizedResults = await Promise.all(
408
+ response.results.map((media) => permissionChecker2.sanitizeOutput(media))
409
+ );
417
410
  return {
418
411
  ...await currentDataWithRelations,
419
- [attributeKey]: await buildRelationReponse(attributeValues, attributeSchema)
412
+ [attributeKey]: {
413
+ results: sanitizedResults,
414
+ meta: response.meta
415
+ }
420
416
  };
421
417
  }
422
418
  return currentDataWithRelations;
@@ -431,7 +427,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
431
427
  ...result,
432
428
  data: await populateEntryRelations(result),
433
429
  meta: {
434
- unknownAttributes: getSchemaAttributesDiff(
430
+ unknownAttributes: serviceUtils.getSchemaAttributesDiff(
435
431
  result.schema,
436
432
  strapi2.getModel(params.query.contentType).attributes
437
433
  )
@@ -448,7 +444,10 @@ const createHistoryService = ({ strapi: strapi2 }) => {
448
444
  async restoreVersion(versionId) {
449
445
  const version = await query.findOne({ where: { id: versionId } });
450
446
  const contentTypeSchemaAttributes = strapi2.getModel(version.contentType).attributes;
451
- const schemaDiff = getSchemaAttributesDiff(version.schema, contentTypeSchemaAttributes);
447
+ const schemaDiff = serviceUtils.getSchemaAttributesDiff(
448
+ version.schema,
449
+ contentTypeSchemaAttributes
450
+ );
452
451
  const dataWithoutAddedAttributes = Object.keys(schemaDiff.added).reduce(
453
452
  (currentData, addedKey) => {
454
453
  currentData[addedKey] = null;
@@ -461,61 +460,26 @@ const createHistoryService = ({ strapi: strapi2 }) => {
461
460
  FIELDS_TO_IGNORE,
462
461
  contentTypeSchemaAttributes
463
462
  );
464
- const dataWithoutMissingRelations = await Object.entries(sanitizedSchemaAttributes).reduce(
465
- async (previousRelationAttributesPromise, [name, attribute]) => {
466
- const previousRelationAttributes = await previousRelationAttributesPromise;
467
- const relationData = version.data[name];
468
- if (relationData === null) {
463
+ const reducer = strapiUtils.async.reduce(Object.entries(sanitizedSchemaAttributes));
464
+ const dataWithoutMissingRelations = await reducer(
465
+ async (previousRelationAttributes, [name, attribute]) => {
466
+ const versionRelationData = version.data[name];
467
+ if (!versionRelationData) {
469
468
  return previousRelationAttributes;
470
469
  }
471
470
  if (attribute.type === "relation" && // TODO: handle polymorphic relations
472
471
  attribute.relation !== "morphToOne" && attribute.relation !== "morphToMany") {
473
- if (Array.isArray(relationData)) {
474
- if (relationData.length === 0)
475
- return previousRelationAttributes;
476
- const existingAndMissingRelations = await Promise.all(
477
- relationData.map((relation) => {
478
- return strapi2.documents(attribute.target).findOne({
479
- documentId: relation.documentId,
480
- locale: relation.locale || void 0
481
- });
482
- })
483
- );
484
- const existingRelations = existingAndMissingRelations.filter(
485
- (relation) => relation !== null
486
- );
487
- previousRelationAttributes[name] = existingRelations;
488
- } else {
489
- const existingRelation = await strapi2.documents(attribute.target).findOne({
490
- documentId: relationData.documentId,
491
- locale: relationData.locale || void 0
492
- });
493
- if (!existingRelation) {
494
- previousRelationAttributes[name] = null;
495
- }
496
- }
472
+ const data2 = await serviceUtils.getRelationRestoreValue(versionRelationData, attribute);
473
+ previousRelationAttributes[name] = data2;
497
474
  }
498
475
  if (attribute.type === "media") {
499
- if (attribute.multiple) {
500
- const existingAndMissingMedias = await Promise.all(
501
- // @ts-expect-error Fix the type definitions so this isn't any
502
- relationData.map((media) => {
503
- return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
504
- })
505
- );
506
- const existingMedias = existingAndMissingMedias.filter((media) => media != null);
507
- previousRelationAttributes[name] = existingMedias;
508
- } else {
509
- const existingMedia = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: version.data[name].id } });
510
- if (!existingMedia) {
511
- previousRelationAttributes[name] = null;
512
- }
513
- }
476
+ const data2 = await serviceUtils.getMediaRestoreValue(versionRelationData, attribute);
477
+ previousRelationAttributes[name] = data2;
514
478
  }
515
479
  return previousRelationAttributes;
516
480
  },
517
481
  // Clone to avoid mutating the original version data
518
- Promise.resolve(structuredClone(dataWithoutAddedAttributes))
482
+ structuredClone(dataWithoutAddedAttributes)
519
483
  );
520
484
  const data = fp.omit(["id", ...Object.keys(schemaDiff.removed)], dataWithoutMissingRelations);
521
485
  const restoredDocument = await strapi2.documents(version.contentType).update({
@@ -530,8 +494,102 @@ const createHistoryService = ({ strapi: strapi2 }) => {
530
494
  }
531
495
  };
532
496
  };
497
+ const createLifecyclesService = ({ strapi: strapi2 }) => {
498
+ const state = {
499
+ deleteExpiredJob: null,
500
+ isInitialized: false
501
+ };
502
+ const serviceUtils = createServiceUtils({ strapi: strapi2 });
503
+ return {
504
+ async bootstrap() {
505
+ if (state.isInitialized) {
506
+ return;
507
+ }
508
+ strapi2.documents.use(async (context, next) => {
509
+ if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
510
+ return next();
511
+ }
512
+ if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
513
+ return next();
514
+ }
515
+ if (context.action === "update" && strapi2.requestContext.get()?.request.url.endsWith("/actions/publish")) {
516
+ return next();
517
+ }
518
+ const contentTypeUid = context.contentType.uid;
519
+ if (!contentTypeUid.startsWith("api::")) {
520
+ return next();
521
+ }
522
+ const result = await next();
523
+ const documentContext = {
524
+ documentId: context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId,
525
+ locale: context.params?.locale
526
+ };
527
+ const defaultLocale = await serviceUtils.getDefaultLocale();
528
+ const locale = documentContext.locale || defaultLocale;
529
+ if (Array.isArray(locale)) {
530
+ strapi2.log.warn(
531
+ "[Content manager history middleware]: An array of locales was provided, but only a single locale is supported for the findOne operation."
532
+ );
533
+ return next();
534
+ }
535
+ const document = await strapi2.documents(contentTypeUid).findOne({
536
+ documentId: documentContext.documentId,
537
+ locale,
538
+ populate: serviceUtils.getDeepPopulate(contentTypeUid)
539
+ });
540
+ const status = await serviceUtils.getVersionStatus(contentTypeUid, document);
541
+ const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
542
+ const componentsSchemas = Object.keys(
543
+ attributesSchema
544
+ ).reduce((currentComponentSchemas, key) => {
545
+ const fieldSchema = attributesSchema[key];
546
+ if (fieldSchema.type === "component") {
547
+ const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
548
+ return {
549
+ ...currentComponentSchemas,
550
+ [fieldSchema.component]: componentSchema
551
+ };
552
+ }
553
+ return currentComponentSchemas;
554
+ }, {});
555
+ await strapi2.db.transaction(async ({ onCommit }) => {
556
+ onCommit(() => {
557
+ getService(strapi2, "history").createVersion({
558
+ contentType: contentTypeUid,
559
+ data: fp.omit(FIELDS_TO_IGNORE, document),
560
+ schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
561
+ componentsSchemas,
562
+ relatedDocumentId: documentContext.documentId,
563
+ locale,
564
+ status
565
+ });
566
+ });
567
+ });
568
+ return result;
569
+ });
570
+ state.deleteExpiredJob = nodeSchedule.scheduleJob("0 0 * * *", () => {
571
+ const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
572
+ const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
573
+ strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
574
+ where: {
575
+ created_at: {
576
+ $lt: expirationDate.toISOString()
577
+ }
578
+ }
579
+ });
580
+ });
581
+ state.isInitialized = true;
582
+ },
583
+ async destroy() {
584
+ if (state.deleteExpiredJob) {
585
+ state.deleteExpiredJob.cancel();
586
+ }
587
+ }
588
+ };
589
+ };
533
590
  const services$1 = {
534
- history: createHistoryService
591
+ history: createHistoryService,
592
+ lifecycles: createLifecyclesService
535
593
  };
536
594
  const info = { pluginName: "content-manager", type: "admin" };
537
595
  const historyVersionRouter = {
@@ -611,10 +669,10 @@ const getFeature = () => {
611
669
  strapi2.get("models").add(historyVersion);
612
670
  },
613
671
  bootstrap({ strapi: strapi2 }) {
614
- getService(strapi2, "history").bootstrap();
672
+ getService(strapi2, "lifecycles").bootstrap();
615
673
  },
616
674
  destroy({ strapi: strapi2 }) {
617
- getService(strapi2, "history").destroy();
675
+ getService(strapi2, "lifecycles").destroy();
618
676
  },
619
677
  controllers: controllers$1,
620
678
  services: services$1,
@@ -1433,7 +1491,7 @@ const { PaginationError, ValidationError } = strapiUtils.errors;
1433
1491
  const TYPES = ["singleType", "collectionType"];
1434
1492
  const kindSchema = strapiUtils.yup.string().oneOf(TYPES).nullable();
1435
1493
  const bulkActionInputSchema = strapiUtils.yup.object({
1436
- ids: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
1494
+ documentIds: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
1437
1495
  }).required();
1438
1496
  const generateUIDInputSchema = strapiUtils.yup.object({
1439
1497
  contentTypeUID: strapiUtils.yup.string().required(),
@@ -1532,15 +1590,47 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1532
1590
  }
1533
1591
  }, body);
1534
1592
  };
1535
- const getDocumentLocaleAndStatus = (request) => {
1593
+ const singleLocaleSchema = strapiUtils.yup.string().nullable();
1594
+ const multipleLocaleSchema = strapiUtils.yup.lazy(
1595
+ (value) => Array.isArray(value) ? strapiUtils.yup.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1596
+ );
1597
+ const statusSchema = strapiUtils.yup.mixed().oneOf(["draft", "published"], "Invalid status");
1598
+ const getDocumentLocaleAndStatus = async (request, opts = { allowMultipleLocales: false }) => {
1599
+ const { allowMultipleLocales } = opts;
1536
1600
  const { locale, status, ...rest } = request || {};
1537
- if (!fp.isNil(locale) && typeof locale !== "string") {
1538
- throw new strapiUtils.errors.ValidationError(`Invalid locale: ${locale}`);
1539
- }
1540
- if (!fp.isNil(status) && !["draft", "published"].includes(status)) {
1541
- throw new strapiUtils.errors.ValidationError(`Invalid status: ${status}`);
1601
+ const schema = strapiUtils.yup.object().shape({
1602
+ locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1603
+ status: statusSchema
1604
+ });
1605
+ try {
1606
+ await strapiUtils.validateYupSchema(schema, { strict: true, abortEarly: false })(request);
1607
+ return { locale, status, ...rest };
1608
+ } catch (error) {
1609
+ throw new strapiUtils.errors.ValidationError(`Validation error: ${error.message}`);
1542
1610
  }
1543
- return { locale, status, ...rest };
1611
+ };
1612
+ const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
1613
+ const documentMetadata2 = getService$1("document-metadata");
1614
+ const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
1615
+ let {
1616
+ meta: { availableLocales, availableStatus }
1617
+ } = serviceOutput;
1618
+ const metadataSanitizer = permissionChecker2.sanitizeOutput;
1619
+ availableLocales = await strapiUtils.async.map(
1620
+ availableLocales,
1621
+ async (localeDocument) => metadataSanitizer(localeDocument)
1622
+ );
1623
+ availableStatus = await strapiUtils.async.map(
1624
+ availableStatus,
1625
+ async (statusDocument) => metadataSanitizer(statusDocument)
1626
+ );
1627
+ return {
1628
+ ...serviceOutput,
1629
+ meta: {
1630
+ availableLocales,
1631
+ availableStatus
1632
+ }
1633
+ };
1544
1634
  };
1545
1635
  const createDocument = async (ctx, opts) => {
1546
1636
  const { userAbility, user } = ctx.state;
@@ -1555,7 +1645,7 @@ const createDocument = async (ctx, opts) => {
1555
1645
  const setCreator = strapiUtils.setCreatorFields({ user });
1556
1646
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1557
1647
  const sanitizedBody = await sanitizeFn(body);
1558
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(body);
1648
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(body);
1559
1649
  return documentManager2.create(model, {
1560
1650
  data: sanitizedBody,
1561
1651
  locale,
@@ -1574,7 +1664,7 @@ const updateDocument = async (ctx, opts) => {
1574
1664
  }
1575
1665
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1576
1666
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1577
- const { locale } = getDocumentLocaleAndStatus(body);
1667
+ const { locale } = await getDocumentLocaleAndStatus(body);
1578
1668
  const [documentVersion, documentExists] = await Promise.all([
1579
1669
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1580
1670
  documentManager2.exists(model, id)
@@ -1612,7 +1702,7 @@ const collectionTypes = {
1612
1702
  }
1613
1703
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1614
1704
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1615
- const { locale, status } = getDocumentLocaleAndStatus(query);
1705
+ const { locale, status } = await getDocumentLocaleAndStatus(query);
1616
1706
  const { results: documents, pagination } = await documentManager2.findPage(
1617
1707
  { ...permissionQuery, populate, locale, status },
1618
1708
  model
@@ -1641,14 +1731,13 @@ const collectionTypes = {
1641
1731
  const { userAbility } = ctx.state;
1642
1732
  const { model, id } = ctx.params;
1643
1733
  const documentManager2 = getService$1("document-manager");
1644
- const documentMetadata2 = getService$1("document-metadata");
1645
1734
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1646
1735
  if (permissionChecker2.cannot.read()) {
1647
1736
  return ctx.forbidden();
1648
1737
  }
1649
1738
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1650
1739
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1651
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
1740
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1652
1741
  const version = await documentManager2.findOne(id, model, {
1653
1742
  populate,
1654
1743
  locale,
@@ -1659,8 +1748,10 @@ const collectionTypes = {
1659
1748
  if (!exists) {
1660
1749
  return ctx.notFound();
1661
1750
  }
1662
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
1751
+ const { meta } = await formatDocumentWithMetadata(
1752
+ permissionChecker2,
1663
1753
  model,
1754
+ // @ts-expect-error TODO: fix
1664
1755
  { id, locale, publishedAt: null },
1665
1756
  { availableLocales: true, availableStatus: false }
1666
1757
  );
@@ -1671,12 +1762,11 @@ const collectionTypes = {
1671
1762
  return ctx.forbidden();
1672
1763
  }
1673
1764
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
1674
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1765
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1675
1766
  },
1676
1767
  async create(ctx) {
1677
1768
  const { userAbility } = ctx.state;
1678
1769
  const { model } = ctx.params;
1679
- const documentMetadata2 = getService$1("document-metadata");
1680
1770
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1681
1771
  const [totalEntries, document] = await Promise.all([
1682
1772
  strapi.db.query(model).count(),
@@ -1684,7 +1774,7 @@ const collectionTypes = {
1684
1774
  ]);
1685
1775
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
1686
1776
  ctx.status = 201;
1687
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1777
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1688
1778
  // Empty metadata as it's not relevant for a new document
1689
1779
  availableLocales: false,
1690
1780
  availableStatus: false
@@ -1698,25 +1788,23 @@ const collectionTypes = {
1698
1788
  async update(ctx) {
1699
1789
  const { userAbility } = ctx.state;
1700
1790
  const { model } = ctx.params;
1701
- const documentMetadata2 = getService$1("document-metadata");
1702
1791
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1703
1792
  const updatedVersion = await updateDocument(ctx);
1704
1793
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1705
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedVersion);
1794
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
1706
1795
  },
1707
1796
  async clone(ctx) {
1708
1797
  const { userAbility, user } = ctx.state;
1709
1798
  const { model, sourceId: id } = ctx.params;
1710
1799
  const { body } = ctx.request;
1711
1800
  const documentManager2 = getService$1("document-manager");
1712
- const documentMetadata2 = getService$1("document-metadata");
1713
1801
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1714
1802
  if (permissionChecker2.cannot.create()) {
1715
1803
  return ctx.forbidden();
1716
1804
  }
1717
1805
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1718
1806
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1719
- const { locale } = getDocumentLocaleAndStatus(body);
1807
+ const { locale } = await getDocumentLocaleAndStatus(body);
1720
1808
  const document = await documentManager2.findOne(id, model, {
1721
1809
  populate,
1722
1810
  locale,
@@ -1732,7 +1820,7 @@ const collectionTypes = {
1732
1820
  const sanitizedBody = await sanitizeFn(body);
1733
1821
  const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
1734
1822
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
1735
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1823
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1736
1824
  // Empty metadata as it's not relevant for a new document
1737
1825
  availableLocales: false,
1738
1826
  availableStatus: false
@@ -1761,7 +1849,7 @@ const collectionTypes = {
1761
1849
  }
1762
1850
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1763
1851
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1764
- const { locale } = getDocumentLocaleAndStatus(ctx.query);
1852
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query);
1765
1853
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1766
1854
  if (documentLocales.length === 0) {
1767
1855
  return ctx.notFound();
@@ -1783,7 +1871,6 @@ const collectionTypes = {
1783
1871
  const { id, model } = ctx.params;
1784
1872
  const { body } = ctx.request;
1785
1873
  const documentManager2 = getService$1("document-manager");
1786
- const documentMetadata2 = getService$1("document-metadata");
1787
1874
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1788
1875
  if (permissionChecker2.cannot.publish()) {
1789
1876
  return ctx.forbidden();
@@ -1795,21 +1882,25 @@ const collectionTypes = {
1795
1882
  if (permissionChecker2.cannot.publish(document)) {
1796
1883
  throw new strapiUtils.errors.ForbiddenError();
1797
1884
  }
1798
- const { locale } = getDocumentLocaleAndStatus(body);
1799
- return documentManager2.publish(document.documentId, model, {
1885
+ const { locale } = await getDocumentLocaleAndStatus(body);
1886
+ const publishResult = await documentManager2.publish(document.documentId, model, {
1800
1887
  locale
1801
1888
  // TODO: Allow setting creator fields on publish
1802
1889
  // data: setCreatorFields({ user, isEdition: true })({}),
1803
1890
  });
1891
+ if (!publishResult || publishResult.length === 0) {
1892
+ throw new strapiUtils.errors.NotFoundError("Document not found or already published.");
1893
+ }
1894
+ return publishResult[0];
1804
1895
  });
1805
1896
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
1806
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1897
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1807
1898
  },
1808
1899
  async bulkPublish(ctx) {
1809
1900
  const { userAbility } = ctx.state;
1810
1901
  const { model } = ctx.params;
1811
1902
  const { body } = ctx.request;
1812
- const { ids } = body;
1903
+ const { documentIds } = body;
1813
1904
  await validateBulkActionInput(body);
1814
1905
  const documentManager2 = getService$1("document-manager");
1815
1906
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1818,8 +1909,11 @@ const collectionTypes = {
1818
1909
  }
1819
1910
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1820
1911
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1821
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1822
- const entities = await Promise.all(entityPromises);
1912
+ const { locale } = await getDocumentLocaleAndStatus(body, { allowMultipleLocales: true });
1913
+ const entityPromises = documentIds.map(
1914
+ (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
1915
+ );
1916
+ const entities = (await Promise.all(entityPromises)).flat();
1823
1917
  for (const entity of entities) {
1824
1918
  if (!entity) {
1825
1919
  return ctx.notFound();
@@ -1828,24 +1922,25 @@ const collectionTypes = {
1828
1922
  return ctx.forbidden();
1829
1923
  }
1830
1924
  }
1831
- const { count } = await documentManager2.publishMany(entities, model);
1925
+ const count = await documentManager2.publishMany(model, documentIds, locale);
1832
1926
  ctx.body = { count };
1833
1927
  },
1834
1928
  async bulkUnpublish(ctx) {
1835
1929
  const { userAbility } = ctx.state;
1836
1930
  const { model } = ctx.params;
1837
1931
  const { body } = ctx.request;
1838
- const { ids } = body;
1932
+ const { documentIds } = body;
1839
1933
  await validateBulkActionInput(body);
1840
1934
  const documentManager2 = getService$1("document-manager");
1841
1935
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1842
1936
  if (permissionChecker2.cannot.unpublish()) {
1843
1937
  return ctx.forbidden();
1844
1938
  }
1845
- const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1846
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1847
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1848
- const entities = await Promise.all(entityPromises);
1939
+ const { locale } = await getDocumentLocaleAndStatus(body);
1940
+ const entityPromises = documentIds.map(
1941
+ (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
1942
+ );
1943
+ const entities = (await Promise.all(entityPromises)).flat();
1849
1944
  for (const entity of entities) {
1850
1945
  if (!entity) {
1851
1946
  return ctx.notFound();
@@ -1854,7 +1949,8 @@ const collectionTypes = {
1854
1949
  return ctx.forbidden();
1855
1950
  }
1856
1951
  }
1857
- const { count } = await documentManager2.unpublishMany(entities, model);
1952
+ const entitiesIds = entities.map((document) => document.documentId);
1953
+ const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
1858
1954
  ctx.body = { count };
1859
1955
  },
1860
1956
  async unpublish(ctx) {
@@ -1864,7 +1960,6 @@ const collectionTypes = {
1864
1960
  body: { discardDraft, ...body }
1865
1961
  } = ctx.request;
1866
1962
  const documentManager2 = getService$1("document-manager");
1867
- const documentMetadata2 = getService$1("document-metadata");
1868
1963
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1869
1964
  if (permissionChecker2.cannot.unpublish()) {
1870
1965
  return ctx.forbidden();
@@ -1874,7 +1969,7 @@ const collectionTypes = {
1874
1969
  }
1875
1970
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1876
1971
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1877
- const { locale } = getDocumentLocaleAndStatus(body);
1972
+ const { locale } = await getDocumentLocaleAndStatus(body);
1878
1973
  const document = await documentManager2.findOne(id, model, {
1879
1974
  populate,
1880
1975
  locale,
@@ -1896,7 +1991,7 @@ const collectionTypes = {
1896
1991
  ctx.body = await strapiUtils.async.pipe(
1897
1992
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
1898
1993
  permissionChecker2.sanitizeOutput,
1899
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
1994
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1900
1995
  )(document);
1901
1996
  });
1902
1997
  },
@@ -1905,14 +2000,13 @@ const collectionTypes = {
1905
2000
  const { id, model } = ctx.params;
1906
2001
  const { body } = ctx.request;
1907
2002
  const documentManager2 = getService$1("document-manager");
1908
- const documentMetadata2 = getService$1("document-metadata");
1909
2003
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1910
2004
  if (permissionChecker2.cannot.discard()) {
1911
2005
  return ctx.forbidden();
1912
2006
  }
1913
2007
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
1914
2008
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1915
- const { locale } = getDocumentLocaleAndStatus(body);
2009
+ const { locale } = await getDocumentLocaleAndStatus(body);
1916
2010
  const document = await documentManager2.findOne(id, model, {
1917
2011
  populate,
1918
2012
  locale,
@@ -1927,14 +2021,14 @@ const collectionTypes = {
1927
2021
  ctx.body = await strapiUtils.async.pipe(
1928
2022
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
1929
2023
  permissionChecker2.sanitizeOutput,
1930
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2024
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1931
2025
  )(document);
1932
2026
  },
1933
2027
  async bulkDelete(ctx) {
1934
2028
  const { userAbility } = ctx.state;
1935
2029
  const { model } = ctx.params;
1936
2030
  const { query, body } = ctx.request;
1937
- const { ids } = body;
2031
+ const { documentIds } = body;
1938
2032
  await validateBulkActionInput(body);
1939
2033
  const documentManager2 = getService$1("document-manager");
1940
2034
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1942,14 +2036,22 @@ const collectionTypes = {
1942
2036
  return ctx.forbidden();
1943
2037
  }
1944
2038
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
1945
- const idsWhereClause = { id: { $in: ids } };
1946
- const params = {
1947
- ...permissionQuery,
1948
- filters: {
1949
- $and: [idsWhereClause].concat(permissionQuery.filters || [])
2039
+ const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2040
+ const { locale } = await getDocumentLocaleAndStatus(body);
2041
+ const documentLocales = await documentManager2.findLocales(documentIds, model, {
2042
+ populate,
2043
+ locale
2044
+ });
2045
+ if (documentLocales.length === 0) {
2046
+ return ctx.notFound();
2047
+ }
2048
+ for (const document of documentLocales) {
2049
+ if (permissionChecker2.cannot.delete(document)) {
2050
+ return ctx.forbidden();
1950
2051
  }
1951
- };
1952
- const { count } = await documentManager2.deleteMany(params, model);
2052
+ }
2053
+ const localeDocumentsIds = documentLocales.map((document) => document.documentId);
2054
+ const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
1953
2055
  ctx.body = { count };
1954
2056
  },
1955
2057
  async countDraftRelations(ctx) {
@@ -1962,7 +2064,7 @@ const collectionTypes = {
1962
2064
  }
1963
2065
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1964
2066
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1965
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
2067
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1966
2068
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
1967
2069
  if (!entity) {
1968
2070
  return ctx.notFound();
@@ -1977,7 +2079,7 @@ const collectionTypes = {
1977
2079
  },
1978
2080
  async countManyEntriesDraftRelations(ctx) {
1979
2081
  const { userAbility } = ctx.state;
1980
- const ids = ctx.request.query.ids;
2082
+ const ids = ctx.request.query.documentIds;
1981
2083
  const locale = ctx.request.query.locale;
1982
2084
  const { model } = ctx.params;
1983
2085
  const documentManager2 = getService$1("document-manager");
@@ -1985,16 +2087,16 @@ const collectionTypes = {
1985
2087
  if (permissionChecker2.cannot.read()) {
1986
2088
  return ctx.forbidden();
1987
2089
  }
1988
- const entities = await documentManager2.findMany(
2090
+ const documents = await documentManager2.findMany(
1989
2091
  {
1990
2092
  filters: {
1991
- id: ids
2093
+ documentId: ids
1992
2094
  },
1993
2095
  locale
1994
2096
  },
1995
2097
  model
1996
2098
  );
1997
- if (!entities) {
2099
+ if (!documents) {
1998
2100
  return ctx.notFound();
1999
2101
  }
2000
2102
  const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
@@ -2490,7 +2592,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
2490
2592
  throw new strapiUtils.errors.ForbiddenError();
2491
2593
  }
2492
2594
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2493
- const { locale } = getDocumentLocaleAndStatus(body);
2595
+ const { locale } = await getDocumentLocaleAndStatus(body);
2494
2596
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2495
2597
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2496
2598
  // Find the first document to check if it exists
@@ -2527,12 +2629,11 @@ const singleTypes = {
2527
2629
  const { model } = ctx.params;
2528
2630
  const { query = {} } = ctx.request;
2529
2631
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2530
- const documentMetadata2 = getService$1("document-metadata");
2531
2632
  if (permissionChecker2.cannot.read()) {
2532
2633
  return ctx.forbidden();
2533
2634
  }
2534
2635
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2535
- const { locale, status } = getDocumentLocaleAndStatus(query);
2636
+ const { locale, status } = await getDocumentLocaleAndStatus(query);
2536
2637
  const version = await findDocument(permissionQuery, model, { locale, status });
2537
2638
  if (!version) {
2538
2639
  if (permissionChecker2.cannot.create()) {
@@ -2542,8 +2643,10 @@ const singleTypes = {
2542
2643
  if (!document) {
2543
2644
  return ctx.notFound();
2544
2645
  }
2545
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
2646
+ const { meta } = await formatDocumentWithMetadata(
2647
+ permissionChecker2,
2546
2648
  model,
2649
+ // @ts-expect-error - fix types
2547
2650
  { id: document.documentId, locale, publishedAt: null },
2548
2651
  { availableLocales: true, availableStatus: false }
2549
2652
  );
@@ -2554,16 +2657,15 @@ const singleTypes = {
2554
2657
  return ctx.forbidden();
2555
2658
  }
2556
2659
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
2557
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2660
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2558
2661
  },
2559
2662
  async createOrUpdate(ctx) {
2560
2663
  const { userAbility } = ctx.state;
2561
2664
  const { model } = ctx.params;
2562
- const documentMetadata2 = getService$1("document-metadata");
2563
2665
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2564
2666
  const document = await createOrUpdateDocument(ctx);
2565
2667
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2566
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2668
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2567
2669
  },
2568
2670
  async delete(ctx) {
2569
2671
  const { userAbility } = ctx.state;
@@ -2576,7 +2678,7 @@ const singleTypes = {
2576
2678
  }
2577
2679
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2578
2680
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2579
- const { locale } = getDocumentLocaleAndStatus(query);
2681
+ const { locale } = await getDocumentLocaleAndStatus(query);
2580
2682
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2581
2683
  populate,
2582
2684
  locale
@@ -2599,7 +2701,6 @@ const singleTypes = {
2599
2701
  const { model } = ctx.params;
2600
2702
  const { query = {} } = ctx.request;
2601
2703
  const documentManager2 = getService$1("document-manager");
2602
- const documentMetadata2 = getService$1("document-metadata");
2603
2704
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2604
2705
  if (permissionChecker2.cannot.publish()) {
2605
2706
  return ctx.forbidden();
@@ -2614,11 +2715,12 @@ const singleTypes = {
2614
2715
  if (permissionChecker2.cannot.publish(document)) {
2615
2716
  throw new strapiUtils.errors.ForbiddenError();
2616
2717
  }
2617
- const { locale } = getDocumentLocaleAndStatus(document);
2618
- return documentManager2.publish(document.documentId, model, { locale });
2718
+ const { locale } = await getDocumentLocaleAndStatus(document);
2719
+ const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2720
+ return publishResult.at(0);
2619
2721
  });
2620
2722
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
2621
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2723
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2622
2724
  },
2623
2725
  async unpublish(ctx) {
2624
2726
  const { userAbility } = ctx.state;
@@ -2628,7 +2730,6 @@ const singleTypes = {
2628
2730
  query = {}
2629
2731
  } = ctx.request;
2630
2732
  const documentManager2 = getService$1("document-manager");
2631
- const documentMetadata2 = getService$1("document-metadata");
2632
2733
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2633
2734
  if (permissionChecker2.cannot.unpublish()) {
2634
2735
  return ctx.forbidden();
@@ -2637,7 +2738,7 @@ const singleTypes = {
2637
2738
  return ctx.forbidden();
2638
2739
  }
2639
2740
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2640
- const { locale } = getDocumentLocaleAndStatus(body);
2741
+ const { locale } = await getDocumentLocaleAndStatus(body);
2641
2742
  const document = await findDocument(sanitizedQuery, model, { locale });
2642
2743
  if (!document) {
2643
2744
  return ctx.notFound();
@@ -2655,7 +2756,7 @@ const singleTypes = {
2655
2756
  ctx.body = await strapiUtils.async.pipe(
2656
2757
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
2657
2758
  permissionChecker2.sanitizeOutput,
2658
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2759
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2659
2760
  )(document);
2660
2761
  });
2661
2762
  },
@@ -2664,13 +2765,12 @@ const singleTypes = {
2664
2765
  const { model } = ctx.params;
2665
2766
  const { body, query = {} } = ctx.request;
2666
2767
  const documentManager2 = getService$1("document-manager");
2667
- const documentMetadata2 = getService$1("document-metadata");
2668
2768
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2669
2769
  if (permissionChecker2.cannot.discard()) {
2670
2770
  return ctx.forbidden();
2671
2771
  }
2672
2772
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2673
- const { locale } = getDocumentLocaleAndStatus(body);
2773
+ const { locale } = await getDocumentLocaleAndStatus(body);
2674
2774
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2675
2775
  if (!document) {
2676
2776
  return ctx.notFound();
@@ -2681,7 +2781,7 @@ const singleTypes = {
2681
2781
  ctx.body = await strapiUtils.async.pipe(
2682
2782
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
2683
2783
  permissionChecker2.sanitizeOutput,
2684
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2784
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2685
2785
  )(document);
2686
2786
  },
2687
2787
  async countDraftRelations(ctx) {
@@ -2690,7 +2790,7 @@ const singleTypes = {
2690
2790
  const { query } = ctx.request;
2691
2791
  const documentManager2 = getService$1("document-manager");
2692
2792
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2693
- const { locale } = getDocumentLocaleAndStatus(query);
2793
+ const { locale } = await getDocumentLocaleAndStatus(query);
2694
2794
  if (permissionChecker2.cannot.read()) {
2695
2795
  return ctx.forbidden();
2696
2796
  }
@@ -2711,7 +2811,7 @@ const uid$1 = {
2711
2811
  async generateUID(ctx) {
2712
2812
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2713
2813
  const { query = {} } = ctx.request;
2714
- const { locale } = getDocumentLocaleAndStatus(query);
2814
+ const { locale } = await getDocumentLocaleAndStatus(query);
2715
2815
  await validateUIDField(contentTypeUID, field);
2716
2816
  const uidService = getService$1("uid");
2717
2817
  ctx.body = {
@@ -2723,7 +2823,7 @@ const uid$1 = {
2723
2823
  ctx.request.body
2724
2824
  );
2725
2825
  const { query = {} } = ctx.request;
2726
- const { locale } = getDocumentLocaleAndStatus(query);
2826
+ const { locale } = await getDocumentLocaleAndStatus(query);
2727
2827
  await validateUIDField(contentTypeUID, field);
2728
2828
  const uidService = getService$1("uid");
2729
2829
  const isAvailable = await uidService.checkUIDAvailability({
@@ -3514,7 +3614,7 @@ const permission = ({ strapi: strapi2 }) => ({
3514
3614
  await strapi2.service("admin::permission").actionProvider.registerMany(actions);
3515
3615
  }
3516
3616
  });
3517
- const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils__default.default.contentTypes;
3617
+ const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils__default.default.contentTypes;
3518
3618
  const { isAnyToMany } = strapiUtils__default.default.relations;
3519
3619
  const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils__default.default.contentTypes.constants;
3520
3620
  const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
@@ -3605,6 +3705,42 @@ const getDeepPopulate = (uid2, {
3605
3705
  {}
3606
3706
  );
3607
3707
  };
3708
+ const getValidatableFieldsPopulate = (uid2, {
3709
+ initialPopulate = {},
3710
+ countMany = false,
3711
+ countOne = false,
3712
+ maxLevel = Infinity
3713
+ } = {}, level = 1) => {
3714
+ if (level > maxLevel) {
3715
+ return {};
3716
+ }
3717
+ const model = strapi.getModel(uid2);
3718
+ return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
3719
+ if (!getDoesAttributeRequireValidation(attribute)) {
3720
+ return populateAcc;
3721
+ }
3722
+ if (isScalarAttribute(attribute)) {
3723
+ return fp.merge(populateAcc, {
3724
+ [attributeName]: true
3725
+ });
3726
+ }
3727
+ return fp.merge(
3728
+ populateAcc,
3729
+ getPopulateFor(
3730
+ attributeName,
3731
+ model,
3732
+ {
3733
+ // @ts-expect-error - improve types
3734
+ initialPopulate: initialPopulate?.[attributeName],
3735
+ countMany,
3736
+ countOne,
3737
+ maxLevel
3738
+ },
3739
+ level
3740
+ )
3741
+ );
3742
+ }, {});
3743
+ };
3608
3744
  const getDeepPopulateDraftCount = (uid2) => {
3609
3745
  const model = strapi.getModel(uid2);
3610
3746
  let hasRelations = false;
@@ -3626,22 +3762,24 @@ const getDeepPopulateDraftCount = (uid2) => {
3626
3762
  attribute.component
3627
3763
  );
3628
3764
  if (childHasRelations) {
3629
- populateAcc[attributeName] = { populate: populate2 };
3765
+ populateAcc[attributeName] = {
3766
+ populate: populate2
3767
+ };
3630
3768
  hasRelations = true;
3631
3769
  }
3632
3770
  break;
3633
3771
  }
3634
3772
  case "dynamiczone": {
3635
- const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
3636
- const { populate: populate2, hasRelations: childHasRelations } = getDeepPopulateDraftCount(componentUID);
3637
- if (childHasRelations) {
3773
+ const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
3774
+ const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
3775
+ if (componentHasRelations) {
3638
3776
  hasRelations = true;
3639
- return fp.merge(acc, populate2);
3777
+ return { ...acc, [componentUID]: { populate: componentPopulate } };
3640
3778
  }
3641
3779
  return acc;
3642
3780
  }, {});
3643
- if (!fp.isEmpty(dzPopulate)) {
3644
- populateAcc[attributeName] = { populate: dzPopulate };
3781
+ if (!fp.isEmpty(dzPopulateFragment)) {
3782
+ populateAcc[attributeName] = { on: dzPopulateFragment };
3645
3783
  }
3646
3784
  break;
3647
3785
  }
@@ -3833,41 +3971,70 @@ const AVAILABLE_STATUS_FIELDS = [
3833
3971
  "updatedBy",
3834
3972
  "status"
3835
3973
  ];
3836
- const AVAILABLE_LOCALES_FIELDS = ["id", "locale", "updatedAt", "createdAt", "status"];
3974
+ const AVAILABLE_LOCALES_FIELDS = [
3975
+ "id",
3976
+ "locale",
3977
+ "updatedAt",
3978
+ "createdAt",
3979
+ "status",
3980
+ "publishedAt",
3981
+ "documentId"
3982
+ ];
3837
3983
  const CONTENT_MANAGER_STATUS = {
3838
3984
  PUBLISHED: "published",
3839
3985
  DRAFT: "draft",
3840
3986
  MODIFIED: "modified"
3841
3987
  };
3842
- const areDatesEqual = (date1, date2, threshold) => {
3843
- if (!date1 || !date2) {
3988
+ const getIsVersionLatestModification = (version, otherVersion) => {
3989
+ if (!version || !version.updatedAt) {
3844
3990
  return false;
3845
3991
  }
3846
- const time1 = new Date(date1).getTime();
3847
- const time2 = new Date(date2).getTime();
3848
- const difference = Math.abs(time1 - time2);
3849
- return difference <= threshold;
3992
+ const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
3993
+ const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
3994
+ return versionUpdatedAt > otherUpdatedAt;
3850
3995
  };
3851
3996
  const documentMetadata = ({ strapi: strapi2 }) => ({
3852
3997
  /**
3853
3998
  * Returns available locales of a document for the current status
3854
3999
  */
3855
- getAvailableLocales(uid2, version, allVersions) {
4000
+ async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
3856
4001
  const versionsByLocale = fp.groupBy("locale", allVersions);
3857
4002
  delete versionsByLocale[version.locale];
3858
- return Object.values(versionsByLocale).map((localeVersions) => {
3859
- if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2))) {
3860
- return fp.pick(AVAILABLE_LOCALES_FIELDS, localeVersions[0]);
4003
+ const model = strapi2.getModel(uid2);
4004
+ const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
4005
+ const traversalFunction = async (localeVersion) => strapiUtils.traverseEntity(
4006
+ ({ key }, { remove }) => {
4007
+ if (keysToKeep.includes(key)) {
4008
+ return;
4009
+ }
4010
+ remove(key);
4011
+ },
4012
+ { schema: model, getModel: strapi2.getModel.bind(strapi2) },
4013
+ // @ts-expect-error fix types DocumentVersion incompatible with Data
4014
+ localeVersion
4015
+ );
4016
+ const mappingResult = await strapiUtils.async.map(
4017
+ Object.values(versionsByLocale),
4018
+ async (localeVersions) => {
4019
+ const mappedLocaleVersions = await strapiUtils.async.map(
4020
+ localeVersions,
4021
+ traversalFunction
4022
+ );
4023
+ if (!strapiUtils.contentTypes.hasDraftAndPublish(model)) {
4024
+ return mappedLocaleVersions[0];
4025
+ }
4026
+ const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
4027
+ const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
4028
+ if (!draftVersion) {
4029
+ return;
4030
+ }
4031
+ return {
4032
+ ...draftVersion,
4033
+ status: this.getStatus(draftVersion, otherVersions)
4034
+ };
3861
4035
  }
3862
- const draftVersion = localeVersions.find((v) => v.publishedAt === null);
3863
- const otherVersions = localeVersions.filter((v) => v.id !== draftVersion?.id);
3864
- if (!draftVersion)
3865
- return;
3866
- return {
3867
- ...fp.pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
3868
- status: this.getStatus(draftVersion, otherVersions)
3869
- };
3870
- }).filter(Boolean);
4036
+ );
4037
+ return mappingResult.filter(Boolean);
3871
4038
  },
3872
4039
  /**
3873
4040
  * Returns available status of a document for the current locale
@@ -3905,26 +4072,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3905
4072
  });
3906
4073
  },
3907
4074
  getStatus(version, otherDocumentStatuses) {
3908
- const isDraft = version.publishedAt === null;
3909
- if (!otherDocumentStatuses?.length) {
3910
- return isDraft ? CONTENT_MANAGER_STATUS.DRAFT : CONTENT_MANAGER_STATUS.PUBLISHED;
4075
+ let draftVersion;
4076
+ let publishedVersion;
4077
+ if (version.publishedAt) {
4078
+ publishedVersion = version;
4079
+ } else {
4080
+ draftVersion = version;
3911
4081
  }
3912
- if (isDraft) {
3913
- const publishedVersion = otherDocumentStatuses?.find((d) => d.publishedAt !== null);
3914
- if (!publishedVersion) {
3915
- return CONTENT_MANAGER_STATUS.DRAFT;
3916
- }
4082
+ const otherVersion = otherDocumentStatuses?.at(0);
4083
+ if (otherVersion?.publishedAt) {
4084
+ publishedVersion = otherVersion;
4085
+ } else if (otherVersion) {
4086
+ draftVersion = otherVersion;
3917
4087
  }
3918
- if (areDatesEqual(version.updatedAt, otherDocumentStatuses.at(0)?.updatedAt, 500)) {
4088
+ if (!draftVersion)
3919
4089
  return CONTENT_MANAGER_STATUS.PUBLISHED;
3920
- }
3921
- return CONTENT_MANAGER_STATUS.MODIFIED;
4090
+ if (!publishedVersion)
4091
+ return CONTENT_MANAGER_STATUS.DRAFT;
4092
+ const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
4093
+ return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
3922
4094
  },
4095
+ // TODO is it necessary to return metadata on every page of the CM
4096
+ // We could refactor this so the locales are only loaded when they're
4097
+ // needed. e.g. in the bulk locale action modal.
3923
4098
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4099
+ const populate = getValidatableFieldsPopulate(uid2);
3924
4100
  const versions = await strapi2.db.query(uid2).findMany({
3925
4101
  where: { documentId: version.documentId },
3926
- select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
3927
4102
  populate: {
4103
+ // Populate only fields that require validation for bulk locale actions
4104
+ ...populate,
4105
+ // NOTE: creator fields are selected in this way to avoid exposing sensitive data
3928
4106
  createdBy: {
3929
4107
  select: ["id", "firstname", "lastname", "email"]
3930
4108
  },
@@ -3933,7 +4111,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3933
4111
  }
3934
4112
  }
3935
4113
  });
3936
- const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
4114
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
3937
4115
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
3938
4116
  return {
3939
4117
  availableLocales: availableLocalesResult,
@@ -3946,8 +4124,9 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3946
4124
  * - Available status of the document for the current locale
3947
4125
  */
3948
4126
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
3949
- if (!document)
4127
+ if (!document) {
3950
4128
  return document;
4129
+ }
3951
4130
  const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
3952
4131
  if (!hasDraftAndPublish) {
3953
4132
  opts.availableStatus = false;
@@ -3997,26 +4176,9 @@ const sumDraftCounts = (entity, uid2) => {
3997
4176
  }, 0);
3998
4177
  };
3999
4178
  const { ApplicationError } = strapiUtils.errors;
4000
- const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
4001
4179
  const { PUBLISHED_AT_ATTRIBUTE } = strapiUtils.contentTypes.constants;
4002
4180
  const omitPublishedAtField = fp.omit(PUBLISHED_AT_ATTRIBUTE);
4003
4181
  const omitIdField = fp.omit("id");
4004
- const emitEvent = async (uid2, event, document) => {
4005
- const modelDef = strapi.getModel(uid2);
4006
- const sanitizedDocument = await strapiUtils.sanitize.sanitizers.defaultSanitizeOutput(
4007
- {
4008
- schema: modelDef,
4009
- getModel(uid22) {
4010
- return strapi.getModel(uid22);
4011
- }
4012
- },
4013
- document
4014
- );
4015
- strapi.eventHub.emit(event, {
4016
- model: modelDef.modelName,
4017
- entry: sanitizedDocument
4018
- });
4019
- };
4020
4182
  const documentManager = ({ strapi: strapi2 }) => {
4021
4183
  return {
4022
4184
  async findOne(id, uid2, opts = {}) {
@@ -4035,6 +4197,9 @@ const documentManager = ({ strapi: strapi2 }) => {
4035
4197
  } else if (opts.locale && opts.locale !== "*") {
4036
4198
  where.locale = opts.locale;
4037
4199
  }
4200
+ if (typeof opts.isPublished === "boolean") {
4201
+ where.publishedAt = { $notNull: opts.isPublished };
4202
+ }
4038
4203
  return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
4039
4204
  },
4040
4205
  async findMany(opts, uid2) {
@@ -4042,20 +4207,16 @@ const documentManager = ({ strapi: strapi2 }) => {
4042
4207
  return strapi2.documents(uid2).findMany(params);
4043
4208
  },
4044
4209
  async findPage(opts, uid2) {
4045
- const page = Number(opts?.page) || 1;
4046
- const pageSize = Number(opts?.pageSize) || 10;
4210
+ const params = strapiUtils.pagination.withDefaultPagination(opts || {}, {
4211
+ maxLimit: 1e3
4212
+ });
4047
4213
  const [documents, total = 0] = await Promise.all([
4048
- strapi2.documents(uid2).findMany(opts),
4049
- strapi2.documents(uid2).count(opts)
4214
+ strapi2.documents(uid2).findMany(params),
4215
+ strapi2.documents(uid2).count(params)
4050
4216
  ]);
4051
4217
  return {
4052
4218
  results: documents,
4053
- pagination: {
4054
- page,
4055
- pageSize,
4056
- pageCount: Math.ceil(total / pageSize),
4057
- total
4058
- }
4219
+ pagination: strapiUtils.pagination.transformPagedPaginationInfo(params, total)
4059
4220
  };
4060
4221
  },
4061
4222
  async create(uid2, opts = {}) {
@@ -4101,70 +4262,36 @@ const documentManager = ({ strapi: strapi2 }) => {
4101
4262
  return {};
4102
4263
  },
4103
4264
  // FIXME: handle relations
4104
- async deleteMany(opts, uid2) {
4105
- const docs = await strapi2.documents(uid2).findMany(opts);
4106
- for (const doc of docs) {
4107
- await strapi2.documents(uid2).delete({ documentId: doc.documentId });
4108
- }
4109
- return { count: docs.length };
4265
+ async deleteMany(documentIds, uid2, opts = {}) {
4266
+ const deletedEntries = await strapi2.db.transaction(async () => {
4267
+ return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
4268
+ });
4269
+ return { count: deletedEntries.length };
4110
4270
  },
4111
4271
  async publish(id, uid2, opts = {}) {
4112
4272
  const populate = await buildDeepPopulate(uid2);
4113
4273
  const params = { ...opts, populate };
4114
- return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries.at(0));
4274
+ return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
4115
4275
  },
4116
- async publishMany(entities, uid2) {
4117
- if (!entities.length) {
4118
- return null;
4119
- }
4120
- await Promise.all(
4121
- entities.map((document) => {
4122
- return strapi2.entityValidator.validateEntityCreation(
4123
- strapi2.getModel(uid2),
4124
- document,
4125
- void 0,
4126
- // @ts-expect-error - FIXME: entity here is unnecessary
4127
- document
4128
- );
4129
- })
4130
- );
4131
- const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4132
- const filters = { id: { $in: entitiesToPublish } };
4133
- const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
4134
- const populate = await buildDeepPopulate(uid2);
4135
- const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4136
- where: filters,
4137
- data
4138
- });
4139
- const publishedEntities = await strapi2.db.query(uid2).findMany({
4140
- where: filters,
4141
- populate
4276
+ async publishMany(uid2, documentIds, locale) {
4277
+ return strapi2.db.transaction(async () => {
4278
+ const results = await Promise.all(
4279
+ documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
4280
+ );
4281
+ const publishedEntitiesCount = results.flat().filter(Boolean).length;
4282
+ return publishedEntitiesCount;
4142
4283
  });
4143
- await Promise.all(
4144
- publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
4145
- );
4146
- return publishedEntitiesCount;
4147
4284
  },
4148
- async unpublishMany(documents, uid2) {
4149
- if (!documents.length) {
4150
- return null;
4151
- }
4152
- const entitiesToUnpublish = documents.filter((doc) => doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4153
- const filters = { id: { $in: entitiesToUnpublish } };
4154
- const data = { [PUBLISHED_AT_ATTRIBUTE]: null };
4155
- const populate = await buildDeepPopulate(uid2);
4156
- const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4157
- where: filters,
4158
- data
4159
- });
4160
- const unpublishedEntities = await strapi2.db.query(uid2).findMany({
4161
- where: filters,
4162
- populate
4285
+ async unpublishMany(documentIds, uid2, opts = {}) {
4286
+ const unpublishedEntries = await strapi2.db.transaction(async () => {
4287
+ return Promise.all(
4288
+ documentIds.map(
4289
+ (id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
4290
+ )
4291
+ );
4163
4292
  });
4164
- await Promise.all(
4165
- unpublishedEntities.map((doc) => emitEvent(uid2, ENTRY_UNPUBLISH, doc))
4166
- );
4167
- return unpublishedEntitiesCount;
4293
+ const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
4294
+ return { count: unpublishedEntitiesCount };
4168
4295
  },
4169
4296
  async unpublish(id, uid2, opts = {}) {
4170
4297
  const populate = await buildDeepPopulate(uid2);
@@ -4189,16 +4316,20 @@ const documentManager = ({ strapi: strapi2 }) => {
4189
4316
  }
4190
4317
  return sumDraftCounts(document, uid2);
4191
4318
  },
4192
- async countManyEntriesDraftRelations(ids, uid2, locale) {
4319
+ async countManyEntriesDraftRelations(documentIds, uid2, locale) {
4193
4320
  const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
4194
4321
  if (!hasRelations) {
4195
4322
  return 0;
4196
4323
  }
4324
+ let localeFilter = {};
4325
+ if (locale) {
4326
+ localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
4327
+ }
4197
4328
  const entities = await strapi2.db.query(uid2).findMany({
4198
4329
  populate,
4199
4330
  where: {
4200
- id: { $in: ids },
4201
- ...locale ? { locale } : {}
4331
+ documentId: { $in: documentIds },
4332
+ ...localeFilter
4202
4333
  }
4203
4334
  });
4204
4335
  const totalNumberDraftRelations = entities.reduce(