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

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 (175) 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-DjWJdz6Y.js → ComponentConfigurationPage-BNxtMIfV.js} +3 -3
  7. package/dist/_chunks/{ComponentConfigurationPage-DjWJdz6Y.js.map → ComponentConfigurationPage-BNxtMIfV.js.map} +1 -1
  8. package/dist/_chunks/{ComponentConfigurationPage-BPvzFjM7.mjs → ComponentConfigurationPage-BWOQWCv2.mjs} +3 -3
  9. package/dist/_chunks/{ComponentConfigurationPage-BPvzFjM7.mjs.map → ComponentConfigurationPage-BWOQWCv2.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-Dmv83RlS.js → EditConfigurationPage-D340bYlT.js} +3 -3
  15. package/dist/_chunks/{EditConfigurationPage-Dmv83RlS.js.map → EditConfigurationPage-D340bYlT.js.map} +1 -1
  16. package/dist/_chunks/{EditConfigurationPage-DacbqQ_f.mjs → EditConfigurationPage-GTp-Ucnw.mjs} +3 -3
  17. package/dist/_chunks/{EditConfigurationPage-DacbqQ_f.mjs.map → EditConfigurationPage-GTp-Ucnw.mjs.map} +1 -1
  18. package/dist/_chunks/{EditViewPage-DDS6H9HO.mjs → EditViewPage-BVMS5hT-.mjs} +47 -47
  19. package/dist/_chunks/EditViewPage-BVMS5hT-.mjs.map +1 -0
  20. package/dist/_chunks/{EditViewPage-DvNpQkam.js → EditViewPage-CXkmnAvI.js} +46 -48
  21. package/dist/_chunks/EditViewPage-CXkmnAvI.js.map +1 -0
  22. package/dist/_chunks/{Field-DmVKIAOo.js → Field-Ibi32diw.js} +953 -782
  23. package/dist/_chunks/Field-Ibi32diw.js.map +1 -0
  24. package/dist/_chunks/{Field-6gvGdPBV.mjs → Field-nNgv5bpd.mjs} +901 -729
  25. package/dist/_chunks/Field-nNgv5bpd.mjs.map +1 -0
  26. package/dist/_chunks/{Form-CPZC9vWa.js → Form-Dhnh34ym.js} +39 -38
  27. package/dist/_chunks/Form-Dhnh34ym.js.map +1 -0
  28. package/dist/_chunks/{Form-DW6K1IH-.mjs → Form-DodJsI2A.mjs} +39 -37
  29. package/dist/_chunks/Form-DodJsI2A.mjs.map +1 -0
  30. package/dist/_chunks/{History-DeAPlvtv.js → History-C9auUkDi.js} +149 -56
  31. package/dist/_chunks/History-C9auUkDi.js.map +1 -0
  32. package/dist/_chunks/{History-Dmr9fmUA.mjs → History-CKCSQXz_.mjs} +148 -54
  33. package/dist/_chunks/History-CKCSQXz_.mjs.map +1 -0
  34. package/dist/_chunks/{ListConfigurationPage-DPCwW5Vr.js → ListConfigurationPage-Bg4rWUjX.js} +58 -59
  35. package/dist/_chunks/ListConfigurationPage-Bg4rWUjX.js.map +1 -0
  36. package/dist/_chunks/{ListConfigurationPage-DhwvYcNv.mjs → ListConfigurationPage-CKEC4ttG.mjs} +54 -54
  37. package/dist/_chunks/ListConfigurationPage-CKEC4ttG.mjs.map +1 -0
  38. package/dist/_chunks/{ListViewPage-BtAwuYLE.mjs → ListViewPage-B7_WJUjG.mjs} +93 -104
  39. package/dist/_chunks/ListViewPage-B7_WJUjG.mjs.map +1 -0
  40. package/dist/_chunks/{ListViewPage-5ySZ-VUs.js → ListViewPage-C2gIeYHG.js} +98 -109
  41. package/dist/_chunks/ListViewPage-C2gIeYHG.js.map +1 -0
  42. package/dist/_chunks/{NoContentTypePage-DSPxnxxp.mjs → NoContentTypePage-Ckem6Ll6.mjs} +3 -3
  43. package/dist/_chunks/NoContentTypePage-Ckem6Ll6.mjs.map +1 -0
  44. package/dist/_chunks/{NoContentTypePage-DOC_yWOf.js → NoContentTypePage-DqgdUfyn.js} +3 -3
  45. package/dist/_chunks/NoContentTypePage-DqgdUfyn.js.map +1 -0
  46. package/dist/_chunks/{NoPermissionsPage-UWDC-1Tw.mjs → NoPermissionsPage-BO-GEjA4.mjs} +2 -2
  47. package/dist/_chunks/{NoPermissionsPage-UWDC-1Tw.mjs.map → NoPermissionsPage-BO-GEjA4.mjs.map} +1 -1
  48. package/dist/_chunks/{NoPermissionsPage-Dwu8rRJu.js → NoPermissionsPage-CF29Q-sW.js} +2 -2
  49. package/dist/_chunks/{NoPermissionsPage-Dwu8rRJu.js.map → NoPermissionsPage-CF29Q-sW.js.map} +1 -1
  50. package/dist/_chunks/{Relations-CgWtgnPe.js → Relations-C0uC9J4f.js} +70 -61
  51. package/dist/_chunks/Relations-C0uC9J4f.js.map +1 -0
  52. package/dist/_chunks/{Relations-J8cscLlR.mjs → Relations-DItV5eow.mjs} +66 -56
  53. package/dist/_chunks/Relations-DItV5eow.mjs.map +1 -0
  54. package/dist/_chunks/{en-MBPul9Su.mjs → en-BrCTWlZv.mjs} +11 -4
  55. package/dist/_chunks/{en-MBPul9Su.mjs.map → en-BrCTWlZv.mjs.map} +1 -1
  56. package/dist/_chunks/{en-C-V1_90f.js → en-uOUIxfcQ.js} +11 -4
  57. package/dist/_chunks/{en-C-V1_90f.js.map → en-uOUIxfcQ.js.map} +1 -1
  58. package/dist/_chunks/{index-C6AH2hEl.js → index-Dd0nXyJF.js} +1649 -905
  59. package/dist/_chunks/index-Dd0nXyJF.js.map +1 -0
  60. package/dist/_chunks/{index-CwRRo1V9.mjs → index-DrNe6ctw.mjs} +1671 -926
  61. package/dist/_chunks/index-DrNe6ctw.mjs.map +1 -0
  62. package/dist/_chunks/{layout-jIDzX0Fp.mjs → layout-B3ez7kvr.mjs} +43 -26
  63. package/dist/_chunks/layout-B3ez7kvr.mjs.map +1 -0
  64. package/dist/_chunks/{layout-B_SXLhqf.js → layout-CLLtt_5O.js} +43 -28
  65. package/dist/_chunks/layout-CLLtt_5O.js.map +1 -0
  66. package/dist/_chunks/{relations-CuvIgCqI.mjs → relations-B0hlsUU_.mjs} +2 -2
  67. package/dist/_chunks/{relations-CuvIgCqI.mjs.map → relations-B0hlsUU_.mjs.map} +1 -1
  68. package/dist/_chunks/{relations-iBMa_OFG.js → relations-bRxcNv1q.js} +2 -2
  69. package/dist/_chunks/{relations-iBMa_OFG.js.map → relations-bRxcNv1q.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/index.d.ts +3 -0
  84. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  85. package/dist/admin/src/hooks/useDocument.d.ts +5 -8
  86. package/dist/admin/src/hooks/useDocumentActions.d.ts +24 -3
  87. package/dist/admin/src/hooks/useDocumentLayout.d.ts +2 -2
  88. package/dist/admin/src/hooks/useDragAndDrop.d.ts +4 -4
  89. package/dist/admin/src/hooks/useKeyboardDragAndDrop.d.ts +1 -1
  90. package/dist/admin/src/index.d.ts +1 -0
  91. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +11 -4
  92. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksInput.d.ts +3 -3
  93. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +4 -0
  94. package/dist/admin/src/pages/EditView/components/FormInputs/Component/Input.d.ts +2 -2
  95. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +3 -5
  96. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
  97. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +30 -18
  98. package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
  99. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +3 -49
  100. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
  101. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +59 -52
  102. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
  103. package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
  104. package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
  105. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +9 -26
  106. package/dist/admin/src/services/api.d.ts +2 -3
  107. package/dist/admin/src/services/components.d.ts +2 -2
  108. package/dist/admin/src/services/contentTypes.d.ts +5 -5
  109. package/dist/admin/src/services/documents.d.ts +29 -17
  110. package/dist/admin/src/services/init.d.ts +2 -2
  111. package/dist/admin/src/services/relations.d.ts +3 -3
  112. package/dist/admin/src/services/uid.d.ts +3 -3
  113. package/dist/admin/src/utils/api.d.ts +4 -18
  114. package/dist/admin/src/utils/validation.d.ts +1 -6
  115. package/dist/server/index.js +392 -261
  116. package/dist/server/index.js.map +1 -1
  117. package/dist/server/index.mjs +400 -269
  118. package/dist/server/index.mjs.map +1 -1
  119. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  120. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  121. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  122. package/dist/server/src/controllers/utils/metadata.d.ts +8 -0
  123. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
  124. package/dist/server/src/controllers/validation/dimensions.d.ts +11 -0
  125. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
  126. package/dist/server/src/controllers/validation/index.d.ts +1 -1
  127. package/dist/server/src/history/services/history.d.ts.map +1 -1
  128. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  129. package/dist/server/src/history/services/utils.d.ts +1 -1
  130. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  131. package/dist/server/src/index.d.ts +18 -39
  132. package/dist/server/src/index.d.ts.map +1 -1
  133. package/dist/server/src/services/document-manager.d.ts +13 -12
  134. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  135. package/dist/server/src/services/document-metadata.d.ts +8 -29
  136. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  137. package/dist/server/src/services/index.d.ts +18 -39
  138. package/dist/server/src/services/index.d.ts.map +1 -1
  139. package/dist/server/src/services/utils/populate.d.ts +8 -1
  140. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  141. package/dist/shared/contracts/collection-types.d.ts +14 -6
  142. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  143. package/dist/shared/contracts/relations.d.ts +2 -2
  144. package/dist/shared/contracts/relations.d.ts.map +1 -1
  145. package/package.json +13 -14
  146. package/dist/_chunks/CardDragPreview-DSVYodBX.js.map +0 -1
  147. package/dist/_chunks/CardDragPreview-ikSG4M46.mjs.map +0 -1
  148. package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
  149. package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
  150. package/dist/_chunks/EditViewPage-DDS6H9HO.mjs.map +0 -1
  151. package/dist/_chunks/EditViewPage-DvNpQkam.js.map +0 -1
  152. package/dist/_chunks/Field-6gvGdPBV.mjs.map +0 -1
  153. package/dist/_chunks/Field-DmVKIAOo.js.map +0 -1
  154. package/dist/_chunks/Form-CPZC9vWa.js.map +0 -1
  155. package/dist/_chunks/Form-DW6K1IH-.mjs.map +0 -1
  156. package/dist/_chunks/History-DeAPlvtv.js.map +0 -1
  157. package/dist/_chunks/History-Dmr9fmUA.mjs.map +0 -1
  158. package/dist/_chunks/ListConfigurationPage-DPCwW5Vr.js.map +0 -1
  159. package/dist/_chunks/ListConfigurationPage-DhwvYcNv.mjs.map +0 -1
  160. package/dist/_chunks/ListViewPage-5ySZ-VUs.js.map +0 -1
  161. package/dist/_chunks/ListViewPage-BtAwuYLE.mjs.map +0 -1
  162. package/dist/_chunks/NoContentTypePage-DOC_yWOf.js.map +0 -1
  163. package/dist/_chunks/NoContentTypePage-DSPxnxxp.mjs.map +0 -1
  164. package/dist/_chunks/Relations-CgWtgnPe.js.map +0 -1
  165. package/dist/_chunks/Relations-J8cscLlR.mjs.map +0 -1
  166. package/dist/_chunks/index-C6AH2hEl.js.map +0 -1
  167. package/dist/_chunks/index-CwRRo1V9.mjs.map +0 -1
  168. package/dist/_chunks/layout-B_SXLhqf.js.map +0 -1
  169. package/dist/_chunks/layout-jIDzX0Fp.mjs.map +0 -1
  170. package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
  171. package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
  172. package/dist/_chunks/urls-DzZya_gm.js +0 -6
  173. package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
  174. package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
  175. package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
@@ -226,20 +226,25 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
226
226
  const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
227
227
  return documentMetadataService.getStatus(document, meta.availableStatus);
228
228
  };
229
- const getDeepPopulate2 = (uid2) => {
229
+ const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
230
230
  const model = strapi2.getModel(uid2);
231
231
  const attributes = Object.entries(model.attributes);
232
+ const fieldSelector = useDatabaseSyntax ? "select" : "fields";
232
233
  return attributes.reduce((acc, [attributeName, attribute]) => {
233
234
  switch (attribute.type) {
234
235
  case "relation": {
236
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
237
+ if (isMorphRelation) {
238
+ break;
239
+ }
235
240
  const isVisible2 = strapiUtils.contentTypes.isVisibleAttribute(model, attributeName);
236
241
  if (isVisible2) {
237
- acc[attributeName] = { fields: ["documentId", "locale", "publishedAt"] };
242
+ acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
238
243
  }
239
244
  break;
240
245
  }
241
246
  case "media": {
242
- acc[attributeName] = { fields: ["id"] };
247
+ acc[attributeName] = { [fieldSelector]: ["id"] };
243
248
  break;
244
249
  }
245
250
  case "component": {
@@ -490,13 +495,47 @@ const createHistoryService = ({ strapi: strapi2 }) => {
490
495
  }
491
496
  };
492
497
  };
498
+ const shouldCreateHistoryVersion = (context) => {
499
+ if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
500
+ return false;
501
+ }
502
+ if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
503
+ return false;
504
+ }
505
+ if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
506
+ return false;
507
+ }
508
+ if (!context.contentType.uid.startsWith("api::")) {
509
+ return false;
510
+ }
511
+ return true;
512
+ };
513
+ const getSchemas = (uid2) => {
514
+ const attributesSchema = strapi.getModel(uid2).attributes;
515
+ const componentsSchemas = Object.keys(attributesSchema).reduce(
516
+ (currentComponentSchemas, key) => {
517
+ const fieldSchema = attributesSchema[key];
518
+ if (fieldSchema.type === "component") {
519
+ const componentSchema = strapi.getModel(fieldSchema.component).attributes;
520
+ return {
521
+ ...currentComponentSchemas,
522
+ [fieldSchema.component]: componentSchema
523
+ };
524
+ }
525
+ return currentComponentSchemas;
526
+ },
527
+ {}
528
+ );
529
+ return {
530
+ schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
531
+ componentsSchemas
532
+ };
533
+ };
493
534
  const createLifecyclesService = ({ strapi: strapi2 }) => {
494
535
  const state = {
495
536
  deleteExpiredJob: null,
496
537
  isInitialized: false
497
538
  };
498
- const query = strapi2.db.query(HISTORY_VERSION_UID);
499
- const historyService = getService(strapi2, "history");
500
539
  const serviceUtils = createServiceUtils({ strapi: strapi2 });
501
540
  return {
502
541
  async bootstrap() {
@@ -504,60 +543,51 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
504
543
  return;
505
544
  }
506
545
  strapi2.documents.use(async (context, next) => {
507
- if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
508
- return next();
509
- }
510
- if (context.action !== "create" && context.action !== "update" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
511
- return next();
512
- }
513
- const contentTypeUid = context.contentType.uid;
514
- if (!contentTypeUid.startsWith("api::")) {
515
- return next();
516
- }
517
546
  const result = await next();
518
- const documentContext = context.action === "create" ? { documentId: result.documentId, locale: context.params?.locale } : { documentId: context.params.documentId, locale: context.params?.locale };
547
+ if (!shouldCreateHistoryVersion(context)) {
548
+ return result;
549
+ }
550
+ const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
519
551
  const defaultLocale = await serviceUtils.getDefaultLocale();
520
- const locale = documentContext.locale || defaultLocale;
521
- const document = await strapi2.documents(contentTypeUid).findOne({
522
- documentId: documentContext.documentId,
523
- locale,
524
- populate: serviceUtils.getDeepPopulate(contentTypeUid)
552
+ const locales = fp.castArray(context.params?.locale || defaultLocale);
553
+ if (!locales.length) {
554
+ return result;
555
+ }
556
+ const uid2 = context.contentType.uid;
557
+ const schemas = getSchemas(uid2);
558
+ const localeEntries = await strapi2.db.query(uid2).findMany({
559
+ where: {
560
+ documentId,
561
+ locale: { $in: locales },
562
+ publishedAt: null
563
+ },
564
+ populate: serviceUtils.getDeepPopulate(
565
+ uid2,
566
+ true
567
+ /* use database syntax */
568
+ )
525
569
  });
526
- const status = await serviceUtils.getVersionStatus(contentTypeUid, document);
527
- const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
528
- const componentsSchemas = Object.keys(
529
- attributesSchema
530
- ).reduce((currentComponentSchemas, key) => {
531
- const fieldSchema = attributesSchema[key];
532
- if (fieldSchema.type === "component") {
533
- const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
534
- return {
535
- ...currentComponentSchemas,
536
- [fieldSchema.component]: componentSchema
537
- };
538
- }
539
- return currentComponentSchemas;
540
- }, {});
541
570
  await strapi2.db.transaction(async ({ onCommit }) => {
542
- onCommit(() => {
543
- historyService.createVersion({
544
- contentType: contentTypeUid,
545
- data: fp.omit(FIELDS_TO_IGNORE, document),
546
- schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
547
- componentsSchemas,
548
- relatedDocumentId: documentContext.documentId,
549
- locale,
550
- status
551
- });
571
+ onCommit(async () => {
572
+ for (const entry of localeEntries) {
573
+ const status = await serviceUtils.getVersionStatus(uid2, entry);
574
+ await getService(strapi2, "history").createVersion({
575
+ contentType: uid2,
576
+ data: fp.omit(FIELDS_TO_IGNORE, entry),
577
+ relatedDocumentId: documentId,
578
+ locale: entry.locale,
579
+ status,
580
+ ...schemas
581
+ });
582
+ }
552
583
  });
553
584
  });
554
585
  return result;
555
586
  });
556
- const retentionDays = serviceUtils.getRetentionDays();
557
587
  state.deleteExpiredJob = nodeSchedule.scheduleJob("0 0 * * *", () => {
558
- const retentionDaysInMilliseconds = retentionDays * 24 * 60 * 60 * 1e3;
588
+ const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
559
589
  const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
560
- query.deleteMany({
590
+ strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
561
591
  where: {
562
592
  created_at: {
563
593
  $lt: expirationDate.toISOString()
@@ -1478,7 +1508,7 @@ const { PaginationError, ValidationError } = strapiUtils.errors;
1478
1508
  const TYPES = ["singleType", "collectionType"];
1479
1509
  const kindSchema = strapiUtils.yup.string().oneOf(TYPES).nullable();
1480
1510
  const bulkActionInputSchema = strapiUtils.yup.object({
1481
- ids: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
1511
+ documentIds: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
1482
1512
  }).required();
1483
1513
  const generateUIDInputSchema = strapiUtils.yup.object({
1484
1514
  contentTypeUID: strapiUtils.yup.string().required(),
@@ -1577,15 +1607,49 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1577
1607
  }
1578
1608
  }, body);
1579
1609
  };
1580
- const getDocumentLocaleAndStatus = (request) => {
1581
- const { locale, status, ...rest } = request || {};
1582
- if (!fp.isNil(locale) && typeof locale !== "string") {
1583
- throw new strapiUtils.errors.ValidationError(`Invalid locale: ${locale}`);
1584
- }
1585
- if (!fp.isNil(status) && !["draft", "published"].includes(status)) {
1586
- throw new strapiUtils.errors.ValidationError(`Invalid status: ${status}`);
1610
+ const singleLocaleSchema = strapiUtils.yup.string().nullable();
1611
+ const multipleLocaleSchema = strapiUtils.yup.lazy(
1612
+ (value) => Array.isArray(value) ? strapiUtils.yup.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1613
+ );
1614
+ const statusSchema = strapiUtils.yup.mixed().oneOf(["draft", "published"], "Invalid status");
1615
+ const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
1616
+ const { allowMultipleLocales } = opts;
1617
+ const { locale, status: providedStatus, ...rest } = request || {};
1618
+ const defaultStatus = strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
1619
+ const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
1620
+ const schema = strapiUtils.yup.object().shape({
1621
+ locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1622
+ status: statusSchema
1623
+ });
1624
+ try {
1625
+ await strapiUtils.validateYupSchema(schema, { strict: true, abortEarly: false })(request);
1626
+ return { locale, status, ...rest };
1627
+ } catch (error) {
1628
+ throw new strapiUtils.errors.ValidationError(`Validation error: ${error.message}`);
1587
1629
  }
1588
- return { locale, status, ...rest };
1630
+ };
1631
+ const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
1632
+ const documentMetadata2 = getService$1("document-metadata");
1633
+ const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
1634
+ let {
1635
+ meta: { availableLocales, availableStatus }
1636
+ } = serviceOutput;
1637
+ const metadataSanitizer = permissionChecker2.sanitizeOutput;
1638
+ availableLocales = await strapiUtils.async.map(
1639
+ availableLocales,
1640
+ async (localeDocument) => metadataSanitizer(localeDocument)
1641
+ );
1642
+ availableStatus = await strapiUtils.async.map(
1643
+ availableStatus,
1644
+ async (statusDocument) => metadataSanitizer(statusDocument)
1645
+ );
1646
+ return {
1647
+ ...serviceOutput,
1648
+ meta: {
1649
+ availableLocales,
1650
+ availableStatus
1651
+ }
1652
+ };
1589
1653
  };
1590
1654
  const createDocument = async (ctx, opts) => {
1591
1655
  const { userAbility, user } = ctx.state;
@@ -1600,7 +1664,7 @@ const createDocument = async (ctx, opts) => {
1600
1664
  const setCreator = strapiUtils.setCreatorFields({ user });
1601
1665
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1602
1666
  const sanitizedBody = await sanitizeFn(body);
1603
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(body);
1667
+ const { locale, status } = await getDocumentLocaleAndStatus(body, model);
1604
1668
  return documentManager2.create(model, {
1605
1669
  data: sanitizedBody,
1606
1670
  locale,
@@ -1619,7 +1683,7 @@ const updateDocument = async (ctx, opts) => {
1619
1683
  }
1620
1684
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1621
1685
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1622
- const { locale } = getDocumentLocaleAndStatus(body);
1686
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1623
1687
  const [documentVersion, documentExists] = await Promise.all([
1624
1688
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1625
1689
  documentManager2.exists(model, id)
@@ -1657,7 +1721,7 @@ const collectionTypes = {
1657
1721
  }
1658
1722
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1659
1723
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1660
- const { locale, status } = getDocumentLocaleAndStatus(query);
1724
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
1661
1725
  const { results: documents, pagination } = await documentManager2.findPage(
1662
1726
  { ...permissionQuery, populate, locale, status },
1663
1727
  model
@@ -1686,14 +1750,13 @@ const collectionTypes = {
1686
1750
  const { userAbility } = ctx.state;
1687
1751
  const { model, id } = ctx.params;
1688
1752
  const documentManager2 = getService$1("document-manager");
1689
- const documentMetadata2 = getService$1("document-metadata");
1690
1753
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1691
1754
  if (permissionChecker2.cannot.read()) {
1692
1755
  return ctx.forbidden();
1693
1756
  }
1694
1757
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1695
1758
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1696
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
1759
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
1697
1760
  const version = await documentManager2.findOne(id, model, {
1698
1761
  populate,
1699
1762
  locale,
@@ -1704,8 +1767,10 @@ const collectionTypes = {
1704
1767
  if (!exists) {
1705
1768
  return ctx.notFound();
1706
1769
  }
1707
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
1770
+ const { meta } = await formatDocumentWithMetadata(
1771
+ permissionChecker2,
1708
1772
  model,
1773
+ // @ts-expect-error TODO: fix
1709
1774
  { id, locale, publishedAt: null },
1710
1775
  { availableLocales: true, availableStatus: false }
1711
1776
  );
@@ -1716,12 +1781,11 @@ const collectionTypes = {
1716
1781
  return ctx.forbidden();
1717
1782
  }
1718
1783
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
1719
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1784
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1720
1785
  },
1721
1786
  async create(ctx) {
1722
1787
  const { userAbility } = ctx.state;
1723
1788
  const { model } = ctx.params;
1724
- const documentMetadata2 = getService$1("document-metadata");
1725
1789
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1726
1790
  const [totalEntries, document] = await Promise.all([
1727
1791
  strapi.db.query(model).count(),
@@ -1729,7 +1793,7 @@ const collectionTypes = {
1729
1793
  ]);
1730
1794
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
1731
1795
  ctx.status = 201;
1732
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1796
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1733
1797
  // Empty metadata as it's not relevant for a new document
1734
1798
  availableLocales: false,
1735
1799
  availableStatus: false
@@ -1743,25 +1807,23 @@ const collectionTypes = {
1743
1807
  async update(ctx) {
1744
1808
  const { userAbility } = ctx.state;
1745
1809
  const { model } = ctx.params;
1746
- const documentMetadata2 = getService$1("document-metadata");
1747
1810
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1748
1811
  const updatedVersion = await updateDocument(ctx);
1749
1812
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1750
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedVersion);
1813
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
1751
1814
  },
1752
1815
  async clone(ctx) {
1753
1816
  const { userAbility, user } = ctx.state;
1754
1817
  const { model, sourceId: id } = ctx.params;
1755
1818
  const { body } = ctx.request;
1756
1819
  const documentManager2 = getService$1("document-manager");
1757
- const documentMetadata2 = getService$1("document-metadata");
1758
1820
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1759
1821
  if (permissionChecker2.cannot.create()) {
1760
1822
  return ctx.forbidden();
1761
1823
  }
1762
1824
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1763
1825
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1764
- const { locale } = getDocumentLocaleAndStatus(body);
1826
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1765
1827
  const document = await documentManager2.findOne(id, model, {
1766
1828
  populate,
1767
1829
  locale,
@@ -1777,7 +1839,7 @@ const collectionTypes = {
1777
1839
  const sanitizedBody = await sanitizeFn(body);
1778
1840
  const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
1779
1841
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
1780
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1842
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1781
1843
  // Empty metadata as it's not relevant for a new document
1782
1844
  availableLocales: false,
1783
1845
  availableStatus: false
@@ -1806,7 +1868,7 @@ const collectionTypes = {
1806
1868
  }
1807
1869
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1808
1870
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1809
- const { locale } = getDocumentLocaleAndStatus(ctx.query);
1871
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
1810
1872
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1811
1873
  if (documentLocales.length === 0) {
1812
1874
  return ctx.notFound();
@@ -1828,7 +1890,6 @@ const collectionTypes = {
1828
1890
  const { id, model } = ctx.params;
1829
1891
  const { body } = ctx.request;
1830
1892
  const documentManager2 = getService$1("document-manager");
1831
- const documentMetadata2 = getService$1("document-metadata");
1832
1893
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1833
1894
  if (permissionChecker2.cannot.publish()) {
1834
1895
  return ctx.forbidden();
@@ -1836,25 +1897,46 @@ const collectionTypes = {
1836
1897
  const publishedDocument = await strapi.db.transaction(async () => {
1837
1898
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1838
1899
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1839
- const document = id ? await updateDocument(ctx, { populate }) : await createDocument(ctx, { populate });
1900
+ let document;
1901
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1902
+ const isCreate = fp.isNil(id);
1903
+ if (isCreate) {
1904
+ if (permissionChecker2.cannot.create()) {
1905
+ throw new strapiUtils.errors.ForbiddenError();
1906
+ }
1907
+ document = await createDocument(ctx, { populate });
1908
+ }
1909
+ const isUpdate = !isCreate;
1910
+ if (isUpdate) {
1911
+ document = await documentManager2.findOne(id, model, { populate, locale });
1912
+ if (!document) {
1913
+ throw new strapiUtils.errors.NotFoundError("Document not found");
1914
+ }
1915
+ if (permissionChecker2.can.update(document)) {
1916
+ await updateDocument(ctx);
1917
+ }
1918
+ }
1840
1919
  if (permissionChecker2.cannot.publish(document)) {
1841
1920
  throw new strapiUtils.errors.ForbiddenError();
1842
1921
  }
1843
- const { locale } = getDocumentLocaleAndStatus(body);
1844
- return documentManager2.publish(document.documentId, model, {
1922
+ const publishResult = await documentManager2.publish(document.documentId, model, {
1845
1923
  locale
1846
1924
  // TODO: Allow setting creator fields on publish
1847
1925
  // data: setCreatorFields({ user, isEdition: true })({}),
1848
1926
  });
1927
+ if (!publishResult || publishResult.length === 0) {
1928
+ throw new strapiUtils.errors.NotFoundError("Document not found or already published.");
1929
+ }
1930
+ return publishResult[0];
1849
1931
  });
1850
1932
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
1851
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1933
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1852
1934
  },
1853
1935
  async bulkPublish(ctx) {
1854
1936
  const { userAbility } = ctx.state;
1855
1937
  const { model } = ctx.params;
1856
1938
  const { body } = ctx.request;
1857
- const { ids } = body;
1939
+ const { documentIds } = body;
1858
1940
  await validateBulkActionInput(body);
1859
1941
  const documentManager2 = getService$1("document-manager");
1860
1942
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1863,8 +1945,13 @@ const collectionTypes = {
1863
1945
  }
1864
1946
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1865
1947
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1866
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1867
- const entities = await Promise.all(entityPromises);
1948
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
1949
+ allowMultipleLocales: true
1950
+ });
1951
+ const entityPromises = documentIds.map(
1952
+ (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
1953
+ );
1954
+ const entities = (await Promise.all(entityPromises)).flat();
1868
1955
  for (const entity of entities) {
1869
1956
  if (!entity) {
1870
1957
  return ctx.notFound();
@@ -1873,24 +1960,25 @@ const collectionTypes = {
1873
1960
  return ctx.forbidden();
1874
1961
  }
1875
1962
  }
1876
- const { count } = await documentManager2.publishMany(entities, model);
1963
+ const count = await documentManager2.publishMany(model, documentIds, locale);
1877
1964
  ctx.body = { count };
1878
1965
  },
1879
1966
  async bulkUnpublish(ctx) {
1880
1967
  const { userAbility } = ctx.state;
1881
1968
  const { model } = ctx.params;
1882
1969
  const { body } = ctx.request;
1883
- const { ids } = body;
1970
+ const { documentIds } = body;
1884
1971
  await validateBulkActionInput(body);
1885
1972
  const documentManager2 = getService$1("document-manager");
1886
1973
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1887
1974
  if (permissionChecker2.cannot.unpublish()) {
1888
1975
  return ctx.forbidden();
1889
1976
  }
1890
- const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1891
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1892
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1893
- const entities = await Promise.all(entityPromises);
1977
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1978
+ const entityPromises = documentIds.map(
1979
+ (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
1980
+ );
1981
+ const entities = (await Promise.all(entityPromises)).flat();
1894
1982
  for (const entity of entities) {
1895
1983
  if (!entity) {
1896
1984
  return ctx.notFound();
@@ -1899,7 +1987,8 @@ const collectionTypes = {
1899
1987
  return ctx.forbidden();
1900
1988
  }
1901
1989
  }
1902
- const { count } = await documentManager2.unpublishMany(entities, model);
1990
+ const entitiesIds = entities.map((document) => document.documentId);
1991
+ const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
1903
1992
  ctx.body = { count };
1904
1993
  },
1905
1994
  async unpublish(ctx) {
@@ -1909,7 +1998,6 @@ const collectionTypes = {
1909
1998
  body: { discardDraft, ...body }
1910
1999
  } = ctx.request;
1911
2000
  const documentManager2 = getService$1("document-manager");
1912
- const documentMetadata2 = getService$1("document-metadata");
1913
2001
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1914
2002
  if (permissionChecker2.cannot.unpublish()) {
1915
2003
  return ctx.forbidden();
@@ -1919,7 +2007,7 @@ const collectionTypes = {
1919
2007
  }
1920
2008
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1921
2009
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1922
- const { locale } = getDocumentLocaleAndStatus(body);
2010
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1923
2011
  const document = await documentManager2.findOne(id, model, {
1924
2012
  populate,
1925
2013
  locale,
@@ -1941,7 +2029,7 @@ const collectionTypes = {
1941
2029
  ctx.body = await strapiUtils.async.pipe(
1942
2030
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
1943
2031
  permissionChecker2.sanitizeOutput,
1944
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2032
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1945
2033
  )(document);
1946
2034
  });
1947
2035
  },
@@ -1950,14 +2038,13 @@ const collectionTypes = {
1950
2038
  const { id, model } = ctx.params;
1951
2039
  const { body } = ctx.request;
1952
2040
  const documentManager2 = getService$1("document-manager");
1953
- const documentMetadata2 = getService$1("document-metadata");
1954
2041
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1955
2042
  if (permissionChecker2.cannot.discard()) {
1956
2043
  return ctx.forbidden();
1957
2044
  }
1958
2045
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
1959
2046
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1960
- const { locale } = getDocumentLocaleAndStatus(body);
2047
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1961
2048
  const document = await documentManager2.findOne(id, model, {
1962
2049
  populate,
1963
2050
  locale,
@@ -1972,14 +2059,14 @@ const collectionTypes = {
1972
2059
  ctx.body = await strapiUtils.async.pipe(
1973
2060
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
1974
2061
  permissionChecker2.sanitizeOutput,
1975
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2062
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1976
2063
  )(document);
1977
2064
  },
1978
2065
  async bulkDelete(ctx) {
1979
2066
  const { userAbility } = ctx.state;
1980
2067
  const { model } = ctx.params;
1981
2068
  const { query, body } = ctx.request;
1982
- const { ids } = body;
2069
+ const { documentIds } = body;
1983
2070
  await validateBulkActionInput(body);
1984
2071
  const documentManager2 = getService$1("document-manager");
1985
2072
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1987,14 +2074,22 @@ const collectionTypes = {
1987
2074
  return ctx.forbidden();
1988
2075
  }
1989
2076
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
1990
- const idsWhereClause = { id: { $in: ids } };
1991
- const params = {
1992
- ...permissionQuery,
1993
- filters: {
1994
- $and: [idsWhereClause].concat(permissionQuery.filters || [])
2077
+ const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2078
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2079
+ const documentLocales = await documentManager2.findLocales(documentIds, model, {
2080
+ populate,
2081
+ locale
2082
+ });
2083
+ if (documentLocales.length === 0) {
2084
+ return ctx.notFound();
2085
+ }
2086
+ for (const document of documentLocales) {
2087
+ if (permissionChecker2.cannot.delete(document)) {
2088
+ return ctx.forbidden();
1995
2089
  }
1996
- };
1997
- const { count } = await documentManager2.deleteMany(params, model);
2090
+ }
2091
+ const localeDocumentsIds = documentLocales.map((document) => document.documentId);
2092
+ const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
1998
2093
  ctx.body = { count };
1999
2094
  },
2000
2095
  async countDraftRelations(ctx) {
@@ -2007,7 +2102,7 @@ const collectionTypes = {
2007
2102
  }
2008
2103
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2009
2104
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2010
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
2105
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
2011
2106
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
2012
2107
  if (!entity) {
2013
2108
  return ctx.notFound();
@@ -2022,7 +2117,7 @@ const collectionTypes = {
2022
2117
  },
2023
2118
  async countManyEntriesDraftRelations(ctx) {
2024
2119
  const { userAbility } = ctx.state;
2025
- const ids = ctx.request.query.ids;
2120
+ const ids = ctx.request.query.documentIds;
2026
2121
  const locale = ctx.request.query.locale;
2027
2122
  const { model } = ctx.params;
2028
2123
  const documentManager2 = getService$1("document-manager");
@@ -2030,16 +2125,16 @@ const collectionTypes = {
2030
2125
  if (permissionChecker2.cannot.read()) {
2031
2126
  return ctx.forbidden();
2032
2127
  }
2033
- const entities = await documentManager2.findMany(
2128
+ const documents = await documentManager2.findMany(
2034
2129
  {
2035
2130
  filters: {
2036
- id: ids
2131
+ documentId: ids
2037
2132
  },
2038
2133
  locale
2039
2134
  },
2040
2135
  model
2041
2136
  );
2042
- if (!entities) {
2137
+ if (!documents) {
2043
2138
  return ctx.notFound();
2044
2139
  }
2045
2140
  const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
@@ -2535,7 +2630,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
2535
2630
  throw new strapiUtils.errors.ForbiddenError();
2536
2631
  }
2537
2632
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2538
- const { locale } = getDocumentLocaleAndStatus(body);
2633
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2539
2634
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2540
2635
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2541
2636
  // Find the first document to check if it exists
@@ -2572,12 +2667,11 @@ const singleTypes = {
2572
2667
  const { model } = ctx.params;
2573
2668
  const { query = {} } = ctx.request;
2574
2669
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2575
- const documentMetadata2 = getService$1("document-metadata");
2576
2670
  if (permissionChecker2.cannot.read()) {
2577
2671
  return ctx.forbidden();
2578
2672
  }
2579
2673
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2580
- const { locale, status } = getDocumentLocaleAndStatus(query);
2674
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
2581
2675
  const version = await findDocument(permissionQuery, model, { locale, status });
2582
2676
  if (!version) {
2583
2677
  if (permissionChecker2.cannot.create()) {
@@ -2587,8 +2681,10 @@ const singleTypes = {
2587
2681
  if (!document) {
2588
2682
  return ctx.notFound();
2589
2683
  }
2590
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
2684
+ const { meta } = await formatDocumentWithMetadata(
2685
+ permissionChecker2,
2591
2686
  model,
2687
+ // @ts-expect-error - fix types
2592
2688
  { id: document.documentId, locale, publishedAt: null },
2593
2689
  { availableLocales: true, availableStatus: false }
2594
2690
  );
@@ -2599,16 +2695,15 @@ const singleTypes = {
2599
2695
  return ctx.forbidden();
2600
2696
  }
2601
2697
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
2602
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2698
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2603
2699
  },
2604
2700
  async createOrUpdate(ctx) {
2605
2701
  const { userAbility } = ctx.state;
2606
2702
  const { model } = ctx.params;
2607
- const documentMetadata2 = getService$1("document-metadata");
2608
2703
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2609
2704
  const document = await createOrUpdateDocument(ctx);
2610
2705
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2611
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2706
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2612
2707
  },
2613
2708
  async delete(ctx) {
2614
2709
  const { userAbility } = ctx.state;
@@ -2621,7 +2716,7 @@ const singleTypes = {
2621
2716
  }
2622
2717
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2623
2718
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2624
- const { locale } = getDocumentLocaleAndStatus(query);
2719
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2625
2720
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2626
2721
  populate,
2627
2722
  locale
@@ -2644,7 +2739,6 @@ const singleTypes = {
2644
2739
  const { model } = ctx.params;
2645
2740
  const { query = {} } = ctx.request;
2646
2741
  const documentManager2 = getService$1("document-manager");
2647
- const documentMetadata2 = getService$1("document-metadata");
2648
2742
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2649
2743
  if (permissionChecker2.cannot.publish()) {
2650
2744
  return ctx.forbidden();
@@ -2659,11 +2753,12 @@ const singleTypes = {
2659
2753
  if (permissionChecker2.cannot.publish(document)) {
2660
2754
  throw new strapiUtils.errors.ForbiddenError();
2661
2755
  }
2662
- const { locale } = getDocumentLocaleAndStatus(document);
2663
- return documentManager2.publish(document.documentId, model, { locale });
2756
+ const { locale } = await getDocumentLocaleAndStatus(document, model);
2757
+ const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2758
+ return publishResult.at(0);
2664
2759
  });
2665
2760
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
2666
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2761
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2667
2762
  },
2668
2763
  async unpublish(ctx) {
2669
2764
  const { userAbility } = ctx.state;
@@ -2673,7 +2768,6 @@ const singleTypes = {
2673
2768
  query = {}
2674
2769
  } = ctx.request;
2675
2770
  const documentManager2 = getService$1("document-manager");
2676
- const documentMetadata2 = getService$1("document-metadata");
2677
2771
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2678
2772
  if (permissionChecker2.cannot.unpublish()) {
2679
2773
  return ctx.forbidden();
@@ -2682,7 +2776,7 @@ const singleTypes = {
2682
2776
  return ctx.forbidden();
2683
2777
  }
2684
2778
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2685
- const { locale } = getDocumentLocaleAndStatus(body);
2779
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2686
2780
  const document = await findDocument(sanitizedQuery, model, { locale });
2687
2781
  if (!document) {
2688
2782
  return ctx.notFound();
@@ -2700,7 +2794,7 @@ const singleTypes = {
2700
2794
  ctx.body = await strapiUtils.async.pipe(
2701
2795
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
2702
2796
  permissionChecker2.sanitizeOutput,
2703
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2797
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2704
2798
  )(document);
2705
2799
  });
2706
2800
  },
@@ -2709,13 +2803,12 @@ const singleTypes = {
2709
2803
  const { model } = ctx.params;
2710
2804
  const { body, query = {} } = ctx.request;
2711
2805
  const documentManager2 = getService$1("document-manager");
2712
- const documentMetadata2 = getService$1("document-metadata");
2713
2806
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2714
2807
  if (permissionChecker2.cannot.discard()) {
2715
2808
  return ctx.forbidden();
2716
2809
  }
2717
2810
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2718
- const { locale } = getDocumentLocaleAndStatus(body);
2811
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2719
2812
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2720
2813
  if (!document) {
2721
2814
  return ctx.notFound();
@@ -2726,7 +2819,7 @@ const singleTypes = {
2726
2819
  ctx.body = await strapiUtils.async.pipe(
2727
2820
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
2728
2821
  permissionChecker2.sanitizeOutput,
2729
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2822
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2730
2823
  )(document);
2731
2824
  },
2732
2825
  async countDraftRelations(ctx) {
@@ -2735,7 +2828,7 @@ const singleTypes = {
2735
2828
  const { query } = ctx.request;
2736
2829
  const documentManager2 = getService$1("document-manager");
2737
2830
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2738
- const { locale } = getDocumentLocaleAndStatus(query);
2831
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2739
2832
  if (permissionChecker2.cannot.read()) {
2740
2833
  return ctx.forbidden();
2741
2834
  }
@@ -2756,7 +2849,7 @@ const uid$1 = {
2756
2849
  async generateUID(ctx) {
2757
2850
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2758
2851
  const { query = {} } = ctx.request;
2759
- const { locale } = getDocumentLocaleAndStatus(query);
2852
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2760
2853
  await validateUIDField(contentTypeUID, field);
2761
2854
  const uidService = getService$1("uid");
2762
2855
  ctx.body = {
@@ -2768,7 +2861,7 @@ const uid$1 = {
2768
2861
  ctx.request.body
2769
2862
  );
2770
2863
  const { query = {} } = ctx.request;
2771
- const { locale } = getDocumentLocaleAndStatus(query);
2864
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2772
2865
  await validateUIDField(contentTypeUID, field);
2773
2866
  const uidService = getService$1("uid");
2774
2867
  const isAvailable = await uidService.checkUIDAvailability({
@@ -3559,7 +3652,7 @@ const permission = ({ strapi: strapi2 }) => ({
3559
3652
  await strapi2.service("admin::permission").actionProvider.registerMany(actions);
3560
3653
  }
3561
3654
  });
3562
- const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils__default.default.contentTypes;
3655
+ const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils__default.default.contentTypes;
3563
3656
  const { isAnyToMany } = strapiUtils__default.default.relations;
3564
3657
  const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils__default.default.contentTypes.constants;
3565
3658
  const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
@@ -3650,6 +3743,42 @@ const getDeepPopulate = (uid2, {
3650
3743
  {}
3651
3744
  );
3652
3745
  };
3746
+ const getValidatableFieldsPopulate = (uid2, {
3747
+ initialPopulate = {},
3748
+ countMany = false,
3749
+ countOne = false,
3750
+ maxLevel = Infinity
3751
+ } = {}, level = 1) => {
3752
+ if (level > maxLevel) {
3753
+ return {};
3754
+ }
3755
+ const model = strapi.getModel(uid2);
3756
+ return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
3757
+ if (!getDoesAttributeRequireValidation(attribute)) {
3758
+ return populateAcc;
3759
+ }
3760
+ if (isScalarAttribute(attribute)) {
3761
+ return fp.merge(populateAcc, {
3762
+ [attributeName]: true
3763
+ });
3764
+ }
3765
+ return fp.merge(
3766
+ populateAcc,
3767
+ getPopulateFor(
3768
+ attributeName,
3769
+ model,
3770
+ {
3771
+ // @ts-expect-error - improve types
3772
+ initialPopulate: initialPopulate?.[attributeName],
3773
+ countMany,
3774
+ countOne,
3775
+ maxLevel
3776
+ },
3777
+ level
3778
+ )
3779
+ );
3780
+ }, {});
3781
+ };
3653
3782
  const getDeepPopulateDraftCount = (uid2) => {
3654
3783
  const model = strapi.getModel(uid2);
3655
3784
  let hasRelations = false;
@@ -3657,6 +3786,10 @@ const getDeepPopulateDraftCount = (uid2) => {
3657
3786
  const attribute = model.attributes[attributeName];
3658
3787
  switch (attribute.type) {
3659
3788
  case "relation": {
3789
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
3790
+ if (isMorphRelation) {
3791
+ break;
3792
+ }
3660
3793
  if (isVisibleAttribute$1(model, attributeName)) {
3661
3794
  populateAcc[attributeName] = {
3662
3795
  count: true,
@@ -3671,22 +3804,24 @@ const getDeepPopulateDraftCount = (uid2) => {
3671
3804
  attribute.component
3672
3805
  );
3673
3806
  if (childHasRelations) {
3674
- populateAcc[attributeName] = { populate: populate2 };
3807
+ populateAcc[attributeName] = {
3808
+ populate: populate2
3809
+ };
3675
3810
  hasRelations = true;
3676
3811
  }
3677
3812
  break;
3678
3813
  }
3679
3814
  case "dynamiczone": {
3680
- const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
3681
- const { populate: populate2, hasRelations: childHasRelations } = getDeepPopulateDraftCount(componentUID);
3682
- if (childHasRelations) {
3815
+ const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
3816
+ const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
3817
+ if (componentHasRelations) {
3683
3818
  hasRelations = true;
3684
- return fp.merge(acc, populate2);
3819
+ return { ...acc, [componentUID]: { populate: componentPopulate } };
3685
3820
  }
3686
3821
  return acc;
3687
3822
  }, {});
3688
- if (!fp.isEmpty(dzPopulate)) {
3689
- populateAcc[attributeName] = { populate: dzPopulate };
3823
+ if (!fp.isEmpty(dzPopulateFragment)) {
3824
+ populateAcc[attributeName] = { on: dzPopulateFragment };
3690
3825
  }
3691
3826
  break;
3692
3827
  }
@@ -3878,41 +4013,70 @@ const AVAILABLE_STATUS_FIELDS = [
3878
4013
  "updatedBy",
3879
4014
  "status"
3880
4015
  ];
3881
- const AVAILABLE_LOCALES_FIELDS = ["id", "locale", "updatedAt", "createdAt", "status"];
4016
+ const AVAILABLE_LOCALES_FIELDS = [
4017
+ "id",
4018
+ "locale",
4019
+ "updatedAt",
4020
+ "createdAt",
4021
+ "status",
4022
+ "publishedAt",
4023
+ "documentId"
4024
+ ];
3882
4025
  const CONTENT_MANAGER_STATUS = {
3883
4026
  PUBLISHED: "published",
3884
4027
  DRAFT: "draft",
3885
4028
  MODIFIED: "modified"
3886
4029
  };
3887
- const areDatesEqual = (date1, date2, threshold) => {
3888
- if (!date1 || !date2) {
4030
+ const getIsVersionLatestModification = (version, otherVersion) => {
4031
+ if (!version || !version.updatedAt) {
3889
4032
  return false;
3890
4033
  }
3891
- const time1 = new Date(date1).getTime();
3892
- const time2 = new Date(date2).getTime();
3893
- const difference = Math.abs(time1 - time2);
3894
- return difference <= threshold;
4034
+ const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
4035
+ const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
4036
+ return versionUpdatedAt > otherUpdatedAt;
3895
4037
  };
3896
4038
  const documentMetadata = ({ strapi: strapi2 }) => ({
3897
4039
  /**
3898
4040
  * Returns available locales of a document for the current status
3899
4041
  */
3900
- getAvailableLocales(uid2, version, allVersions) {
4042
+ async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
3901
4043
  const versionsByLocale = fp.groupBy("locale", allVersions);
3902
4044
  delete versionsByLocale[version.locale];
3903
- return Object.values(versionsByLocale).map((localeVersions) => {
3904
- if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2))) {
3905
- return fp.pick(AVAILABLE_LOCALES_FIELDS, localeVersions[0]);
4045
+ const model = strapi2.getModel(uid2);
4046
+ const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
4047
+ const traversalFunction = async (localeVersion) => strapiUtils.traverseEntity(
4048
+ ({ key }, { remove }) => {
4049
+ if (keysToKeep.includes(key)) {
4050
+ return;
4051
+ }
4052
+ remove(key);
4053
+ },
4054
+ { schema: model, getModel: strapi2.getModel.bind(strapi2) },
4055
+ // @ts-expect-error fix types DocumentVersion incompatible with Data
4056
+ localeVersion
4057
+ );
4058
+ const mappingResult = await strapiUtils.async.map(
4059
+ Object.values(versionsByLocale),
4060
+ async (localeVersions) => {
4061
+ const mappedLocaleVersions = await strapiUtils.async.map(
4062
+ localeVersions,
4063
+ traversalFunction
4064
+ );
4065
+ if (!strapiUtils.contentTypes.hasDraftAndPublish(model)) {
4066
+ return mappedLocaleVersions[0];
4067
+ }
4068
+ const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
4069
+ const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
4070
+ if (!draftVersion) {
4071
+ return;
4072
+ }
4073
+ return {
4074
+ ...draftVersion,
4075
+ status: this.getStatus(draftVersion, otherVersions)
4076
+ };
3906
4077
  }
3907
- const draftVersion = localeVersions.find((v) => v.publishedAt === null);
3908
- const otherVersions = localeVersions.filter((v) => v.id !== draftVersion?.id);
3909
- if (!draftVersion)
3910
- return;
3911
- return {
3912
- ...fp.pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
3913
- status: this.getStatus(draftVersion, otherVersions)
3914
- };
3915
- }).filter(Boolean);
4078
+ );
4079
+ return mappingResult.filter(Boolean);
3916
4080
  },
3917
4081
  /**
3918
4082
  * Returns available status of a document for the current locale
@@ -3950,26 +4114,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3950
4114
  });
3951
4115
  },
3952
4116
  getStatus(version, otherDocumentStatuses) {
3953
- const isDraft = version.publishedAt === null;
3954
- if (!otherDocumentStatuses?.length) {
3955
- return isDraft ? CONTENT_MANAGER_STATUS.DRAFT : CONTENT_MANAGER_STATUS.PUBLISHED;
4117
+ let draftVersion;
4118
+ let publishedVersion;
4119
+ if (version.publishedAt) {
4120
+ publishedVersion = version;
4121
+ } else {
4122
+ draftVersion = version;
3956
4123
  }
3957
- if (isDraft) {
3958
- const publishedVersion = otherDocumentStatuses?.find((d) => d.publishedAt !== null);
3959
- if (!publishedVersion) {
3960
- return CONTENT_MANAGER_STATUS.DRAFT;
3961
- }
4124
+ const otherVersion = otherDocumentStatuses?.at(0);
4125
+ if (otherVersion?.publishedAt) {
4126
+ publishedVersion = otherVersion;
4127
+ } else if (otherVersion) {
4128
+ draftVersion = otherVersion;
3962
4129
  }
3963
- if (areDatesEqual(version.updatedAt, otherDocumentStatuses.at(0)?.updatedAt, 500)) {
4130
+ if (!draftVersion)
3964
4131
  return CONTENT_MANAGER_STATUS.PUBLISHED;
3965
- }
3966
- return CONTENT_MANAGER_STATUS.MODIFIED;
4132
+ if (!publishedVersion)
4133
+ return CONTENT_MANAGER_STATUS.DRAFT;
4134
+ const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
4135
+ return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
3967
4136
  },
4137
+ // TODO is it necessary to return metadata on every page of the CM
4138
+ // We could refactor this so the locales are only loaded when they're
4139
+ // needed. e.g. in the bulk locale action modal.
3968
4140
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4141
+ const populate = getValidatableFieldsPopulate(uid2);
3969
4142
  const versions = await strapi2.db.query(uid2).findMany({
3970
4143
  where: { documentId: version.documentId },
3971
- select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
3972
4144
  populate: {
4145
+ // Populate only fields that require validation for bulk locale actions
4146
+ ...populate,
4147
+ // NOTE: creator fields are selected in this way to avoid exposing sensitive data
3973
4148
  createdBy: {
3974
4149
  select: ["id", "firstname", "lastname", "email"]
3975
4150
  },
@@ -3978,7 +4153,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3978
4153
  }
3979
4154
  }
3980
4155
  });
3981
- const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
4156
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
3982
4157
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
3983
4158
  return {
3984
4159
  availableLocales: availableLocalesResult,
@@ -3991,8 +4166,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3991
4166
  * - Available status of the document for the current locale
3992
4167
  */
3993
4168
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
3994
- if (!document)
3995
- return document;
4169
+ if (!document) {
4170
+ return {
4171
+ data: document,
4172
+ meta: {
4173
+ availableLocales: [],
4174
+ availableStatus: []
4175
+ }
4176
+ };
4177
+ }
3996
4178
  const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
3997
4179
  if (!hasDraftAndPublish) {
3998
4180
  opts.availableStatus = false;
@@ -4042,26 +4224,9 @@ const sumDraftCounts = (entity, uid2) => {
4042
4224
  }, 0);
4043
4225
  };
4044
4226
  const { ApplicationError } = strapiUtils.errors;
4045
- const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
4046
4227
  const { PUBLISHED_AT_ATTRIBUTE } = strapiUtils.contentTypes.constants;
4047
4228
  const omitPublishedAtField = fp.omit(PUBLISHED_AT_ATTRIBUTE);
4048
4229
  const omitIdField = fp.omit("id");
4049
- const emitEvent = async (uid2, event, document) => {
4050
- const modelDef = strapi.getModel(uid2);
4051
- const sanitizedDocument = await strapiUtils.sanitize.sanitizers.defaultSanitizeOutput(
4052
- {
4053
- schema: modelDef,
4054
- getModel(uid22) {
4055
- return strapi.getModel(uid22);
4056
- }
4057
- },
4058
- document
4059
- );
4060
- strapi.eventHub.emit(event, {
4061
- model: modelDef.modelName,
4062
- entry: sanitizedDocument
4063
- });
4064
- };
4065
4230
  const documentManager = ({ strapi: strapi2 }) => {
4066
4231
  return {
4067
4232
  async findOne(id, uid2, opts = {}) {
@@ -4080,6 +4245,9 @@ const documentManager = ({ strapi: strapi2 }) => {
4080
4245
  } else if (opts.locale && opts.locale !== "*") {
4081
4246
  where.locale = opts.locale;
4082
4247
  }
4248
+ if (typeof opts.isPublished === "boolean") {
4249
+ where.publishedAt = { $notNull: opts.isPublished };
4250
+ }
4083
4251
  return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
4084
4252
  },
4085
4253
  async findMany(opts, uid2) {
@@ -4087,20 +4255,16 @@ const documentManager = ({ strapi: strapi2 }) => {
4087
4255
  return strapi2.documents(uid2).findMany(params);
4088
4256
  },
4089
4257
  async findPage(opts, uid2) {
4090
- const page = Number(opts?.page) || 1;
4091
- const pageSize = Number(opts?.pageSize) || 10;
4258
+ const params = strapiUtils.pagination.withDefaultPagination(opts || {}, {
4259
+ maxLimit: 1e3
4260
+ });
4092
4261
  const [documents, total = 0] = await Promise.all([
4093
- strapi2.documents(uid2).findMany(opts),
4094
- strapi2.documents(uid2).count(opts)
4262
+ strapi2.documents(uid2).findMany(params),
4263
+ strapi2.documents(uid2).count(params)
4095
4264
  ]);
4096
4265
  return {
4097
4266
  results: documents,
4098
- pagination: {
4099
- page,
4100
- pageSize,
4101
- pageCount: Math.ceil(total / pageSize),
4102
- total
4103
- }
4267
+ pagination: strapiUtils.pagination.transformPagedPaginationInfo(params, total)
4104
4268
  };
4105
4269
  },
4106
4270
  async create(uid2, opts = {}) {
@@ -4117,10 +4281,7 @@ const documentManager = ({ strapi: strapi2 }) => {
4117
4281
  async clone(id, body, uid2) {
4118
4282
  const populate = await buildDeepPopulate(uid2);
4119
4283
  const params = {
4120
- data: {
4121
- ...omitIdField(body),
4122
- [PUBLISHED_AT_ATTRIBUTE]: null
4123
- },
4284
+ data: omitIdField(body),
4124
4285
  populate
4125
4286
  };
4126
4287
  return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
@@ -4146,70 +4307,36 @@ const documentManager = ({ strapi: strapi2 }) => {
4146
4307
  return {};
4147
4308
  },
4148
4309
  // FIXME: handle relations
4149
- async deleteMany(opts, uid2) {
4150
- const docs = await strapi2.documents(uid2).findMany(opts);
4151
- for (const doc of docs) {
4152
- await strapi2.documents(uid2).delete({ documentId: doc.documentId });
4153
- }
4154
- return { count: docs.length };
4310
+ async deleteMany(documentIds, uid2, opts = {}) {
4311
+ const deletedEntries = await strapi2.db.transaction(async () => {
4312
+ return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
4313
+ });
4314
+ return { count: deletedEntries.length };
4155
4315
  },
4156
4316
  async publish(id, uid2, opts = {}) {
4157
4317
  const populate = await buildDeepPopulate(uid2);
4158
4318
  const params = { ...opts, populate };
4159
- return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries.at(0));
4319
+ return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
4160
4320
  },
4161
- async publishMany(entities, uid2) {
4162
- if (!entities.length) {
4163
- return null;
4164
- }
4165
- await Promise.all(
4166
- entities.map((document) => {
4167
- return strapi2.entityValidator.validateEntityCreation(
4168
- strapi2.getModel(uid2),
4169
- document,
4170
- void 0,
4171
- // @ts-expect-error - FIXME: entity here is unnecessary
4172
- document
4173
- );
4174
- })
4175
- );
4176
- const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4177
- const filters = { id: { $in: entitiesToPublish } };
4178
- const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
4179
- const populate = await buildDeepPopulate(uid2);
4180
- const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4181
- where: filters,
4182
- data
4183
- });
4184
- const publishedEntities = await strapi2.db.query(uid2).findMany({
4185
- where: filters,
4186
- populate
4321
+ async publishMany(uid2, documentIds, locale) {
4322
+ return strapi2.db.transaction(async () => {
4323
+ const results = await Promise.all(
4324
+ documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
4325
+ );
4326
+ const publishedEntitiesCount = results.flat().filter(Boolean).length;
4327
+ return publishedEntitiesCount;
4187
4328
  });
4188
- await Promise.all(
4189
- publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
4190
- );
4191
- return publishedEntitiesCount;
4192
4329
  },
4193
- async unpublishMany(documents, uid2) {
4194
- if (!documents.length) {
4195
- return null;
4196
- }
4197
- const entitiesToUnpublish = documents.filter((doc) => doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4198
- const filters = { id: { $in: entitiesToUnpublish } };
4199
- const data = { [PUBLISHED_AT_ATTRIBUTE]: null };
4200
- const populate = await buildDeepPopulate(uid2);
4201
- const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4202
- where: filters,
4203
- data
4204
- });
4205
- const unpublishedEntities = await strapi2.db.query(uid2).findMany({
4206
- where: filters,
4207
- populate
4330
+ async unpublishMany(documentIds, uid2, opts = {}) {
4331
+ const unpublishedEntries = await strapi2.db.transaction(async () => {
4332
+ return Promise.all(
4333
+ documentIds.map(
4334
+ (id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
4335
+ )
4336
+ );
4208
4337
  });
4209
- await Promise.all(
4210
- unpublishedEntities.map((doc) => emitEvent(uid2, ENTRY_UNPUBLISH, doc))
4211
- );
4212
- return unpublishedEntitiesCount;
4338
+ const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
4339
+ return { count: unpublishedEntitiesCount };
4213
4340
  },
4214
4341
  async unpublish(id, uid2, opts = {}) {
4215
4342
  const populate = await buildDeepPopulate(uid2);
@@ -4234,16 +4361,20 @@ const documentManager = ({ strapi: strapi2 }) => {
4234
4361
  }
4235
4362
  return sumDraftCounts(document, uid2);
4236
4363
  },
4237
- async countManyEntriesDraftRelations(ids, uid2, locale) {
4364
+ async countManyEntriesDraftRelations(documentIds, uid2, locale) {
4238
4365
  const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
4239
4366
  if (!hasRelations) {
4240
4367
  return 0;
4241
4368
  }
4369
+ let localeFilter = {};
4370
+ if (locale) {
4371
+ localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
4372
+ }
4242
4373
  const entities = await strapi2.db.query(uid2).findMany({
4243
4374
  populate,
4244
4375
  where: {
4245
- id: { $in: ids },
4246
- ...locale ? { locale } : {}
4376
+ documentId: { $in: documentIds },
4377
+ ...localeFilter
4247
4378
  }
4248
4379
  });
4249
4380
  const totalNumberDraftRelations = entities.reduce(