@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
@@ -1,5 +1,5 @@
1
- import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1, sanitize } from "@strapi/utils";
2
- import { pick, omit, difference, intersection, pipe, propOr, isEqual, isEmpty, set, isNil as isNil$1, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, getOr, propEq, merge, groupBy, castArray } from "lodash/fp";
1
+ import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1, traverseEntity, pagination } from "@strapi/utils";
2
+ import { pick, omit, difference, castArray, intersection, pipe, propOr, isEqual, isEmpty, set, isNil as isNil$1, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, getOr, propEq, merge, groupBy } from "lodash/fp";
3
3
  import "@strapi/types";
4
4
  import * as yup from "yup";
5
5
  import { scheduleJob } from "node-schedule";
@@ -54,7 +54,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
54
54
  return ctx.forbidden();
55
55
  }
56
56
  const query = await permissionChecker2.sanitizeQuery(ctx.query);
57
- const { results, pagination } = await getService(strapi2, "history").findVersionsPage({
57
+ const { results, pagination: pagination2 } = await getService(strapi2, "history").findVersionsPage({
58
58
  query: {
59
59
  ...query,
60
60
  ...getValidPagination({ page: query.page, pageSize: query.pageSize })
@@ -73,7 +73,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
73
73
  );
74
74
  return {
75
75
  data: sanitizedResults,
76
- meta: { pagination }
76
+ meta: { pagination: pagination2 }
77
77
  };
78
78
  },
79
79
  async restoreVersion(ctx) {
@@ -200,20 +200,25 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
200
200
  const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
201
201
  return documentMetadataService.getStatus(document, meta.availableStatus);
202
202
  };
203
- const getDeepPopulate2 = (uid2) => {
203
+ const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
204
204
  const model = strapi2.getModel(uid2);
205
205
  const attributes = Object.entries(model.attributes);
206
+ const fieldSelector = useDatabaseSyntax ? "select" : "fields";
206
207
  return attributes.reduce((acc, [attributeName, attribute]) => {
207
208
  switch (attribute.type) {
208
209
  case "relation": {
210
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
211
+ if (isMorphRelation) {
212
+ break;
213
+ }
209
214
  const isVisible2 = contentTypes$1.isVisibleAttribute(model, attributeName);
210
215
  if (isVisible2) {
211
- acc[attributeName] = { fields: ["documentId", "locale", "publishedAt"] };
216
+ acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
212
217
  }
213
218
  break;
214
219
  }
215
220
  case "media": {
216
- acc[attributeName] = { fields: ["id"] };
221
+ acc[attributeName] = { [fieldSelector]: ["id"] };
217
222
  break;
218
223
  }
219
224
  case "component": {
@@ -309,7 +314,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
309
314
  },
310
315
  async findVersionsPage(params) {
311
316
  const locale = params.query.locale || await serviceUtils.getDefaultLocale();
312
- const [{ results, pagination }, localeDictionary] = await Promise.all([
317
+ const [{ results, pagination: pagination2 }, localeDictionary] = await Promise.all([
313
318
  query.findPage({
314
319
  ...params.query,
315
320
  where: {
@@ -408,7 +413,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
408
413
  );
409
414
  return {
410
415
  results: formattedResults,
411
- pagination
416
+ pagination: pagination2
412
417
  };
413
418
  },
414
419
  async restoreVersion(versionId) {
@@ -464,13 +469,47 @@ const createHistoryService = ({ strapi: strapi2 }) => {
464
469
  }
465
470
  };
466
471
  };
472
+ const shouldCreateHistoryVersion = (context) => {
473
+ if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
474
+ return false;
475
+ }
476
+ if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
477
+ return false;
478
+ }
479
+ if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
480
+ return false;
481
+ }
482
+ if (!context.contentType.uid.startsWith("api::")) {
483
+ return false;
484
+ }
485
+ return true;
486
+ };
487
+ const getSchemas = (uid2) => {
488
+ const attributesSchema = strapi.getModel(uid2).attributes;
489
+ const componentsSchemas = Object.keys(attributesSchema).reduce(
490
+ (currentComponentSchemas, key) => {
491
+ const fieldSchema = attributesSchema[key];
492
+ if (fieldSchema.type === "component") {
493
+ const componentSchema = strapi.getModel(fieldSchema.component).attributes;
494
+ return {
495
+ ...currentComponentSchemas,
496
+ [fieldSchema.component]: componentSchema
497
+ };
498
+ }
499
+ return currentComponentSchemas;
500
+ },
501
+ {}
502
+ );
503
+ return {
504
+ schema: omit(FIELDS_TO_IGNORE, attributesSchema),
505
+ componentsSchemas
506
+ };
507
+ };
467
508
  const createLifecyclesService = ({ strapi: strapi2 }) => {
468
509
  const state = {
469
510
  deleteExpiredJob: null,
470
511
  isInitialized: false
471
512
  };
472
- const query = strapi2.db.query(HISTORY_VERSION_UID);
473
- const historyService = getService(strapi2, "history");
474
513
  const serviceUtils = createServiceUtils({ strapi: strapi2 });
475
514
  return {
476
515
  async bootstrap() {
@@ -478,60 +517,51 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
478
517
  return;
479
518
  }
480
519
  strapi2.documents.use(async (context, next) => {
481
- if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
482
- return next();
483
- }
484
- if (context.action !== "create" && context.action !== "update" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
485
- return next();
486
- }
487
- const contentTypeUid = context.contentType.uid;
488
- if (!contentTypeUid.startsWith("api::")) {
489
- return next();
490
- }
491
520
  const result = await next();
492
- const documentContext = context.action === "create" ? { documentId: result.documentId, locale: context.params?.locale } : { documentId: context.params.documentId, locale: context.params?.locale };
521
+ if (!shouldCreateHistoryVersion(context)) {
522
+ return result;
523
+ }
524
+ const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
493
525
  const defaultLocale = await serviceUtils.getDefaultLocale();
494
- const locale = documentContext.locale || defaultLocale;
495
- const document = await strapi2.documents(contentTypeUid).findOne({
496
- documentId: documentContext.documentId,
497
- locale,
498
- populate: serviceUtils.getDeepPopulate(contentTypeUid)
526
+ const locales = castArray(context.params?.locale || defaultLocale);
527
+ if (!locales.length) {
528
+ return result;
529
+ }
530
+ const uid2 = context.contentType.uid;
531
+ const schemas = getSchemas(uid2);
532
+ const localeEntries = await strapi2.db.query(uid2).findMany({
533
+ where: {
534
+ documentId,
535
+ locale: { $in: locales },
536
+ publishedAt: null
537
+ },
538
+ populate: serviceUtils.getDeepPopulate(
539
+ uid2,
540
+ true
541
+ /* use database syntax */
542
+ )
499
543
  });
500
- const status = await serviceUtils.getVersionStatus(contentTypeUid, document);
501
- const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
502
- const componentsSchemas = Object.keys(
503
- attributesSchema
504
- ).reduce((currentComponentSchemas, key) => {
505
- const fieldSchema = attributesSchema[key];
506
- if (fieldSchema.type === "component") {
507
- const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
508
- return {
509
- ...currentComponentSchemas,
510
- [fieldSchema.component]: componentSchema
511
- };
512
- }
513
- return currentComponentSchemas;
514
- }, {});
515
544
  await strapi2.db.transaction(async ({ onCommit }) => {
516
- onCommit(() => {
517
- historyService.createVersion({
518
- contentType: contentTypeUid,
519
- data: omit(FIELDS_TO_IGNORE, document),
520
- schema: omit(FIELDS_TO_IGNORE, attributesSchema),
521
- componentsSchemas,
522
- relatedDocumentId: documentContext.documentId,
523
- locale,
524
- status
525
- });
545
+ onCommit(async () => {
546
+ for (const entry of localeEntries) {
547
+ const status = await serviceUtils.getVersionStatus(uid2, entry);
548
+ await getService(strapi2, "history").createVersion({
549
+ contentType: uid2,
550
+ data: omit(FIELDS_TO_IGNORE, entry),
551
+ relatedDocumentId: documentId,
552
+ locale: entry.locale,
553
+ status,
554
+ ...schemas
555
+ });
556
+ }
526
557
  });
527
558
  });
528
559
  return result;
529
560
  });
530
- const retentionDays = serviceUtils.getRetentionDays();
531
561
  state.deleteExpiredJob = scheduleJob("0 0 * * *", () => {
532
- const retentionDaysInMilliseconds = retentionDays * 24 * 60 * 60 * 1e3;
562
+ const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
533
563
  const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
534
- query.deleteMany({
564
+ strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
535
565
  where: {
536
566
  created_at: {
537
567
  $lt: expirationDate.toISOString()
@@ -1452,7 +1482,7 @@ const { PaginationError, ValidationError } = errors;
1452
1482
  const TYPES = ["singleType", "collectionType"];
1453
1483
  const kindSchema = yup$1.string().oneOf(TYPES).nullable();
1454
1484
  const bulkActionInputSchema = yup$1.object({
1455
- ids: yup$1.array().of(yup$1.strapiID()).min(1).required()
1485
+ documentIds: yup$1.array().of(yup$1.strapiID()).min(1).required()
1456
1486
  }).required();
1457
1487
  const generateUIDInputSchema = yup$1.object({
1458
1488
  contentTypeUID: yup$1.string().required(),
@@ -1551,15 +1581,49 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1551
1581
  }
1552
1582
  }, body);
1553
1583
  };
1554
- const getDocumentLocaleAndStatus = (request) => {
1555
- const { locale, status, ...rest } = request || {};
1556
- if (!isNil$1(locale) && typeof locale !== "string") {
1557
- throw new errors.ValidationError(`Invalid locale: ${locale}`);
1558
- }
1559
- if (!isNil$1(status) && !["draft", "published"].includes(status)) {
1560
- throw new errors.ValidationError(`Invalid status: ${status}`);
1584
+ const singleLocaleSchema = yup$1.string().nullable();
1585
+ const multipleLocaleSchema = yup$1.lazy(
1586
+ (value) => Array.isArray(value) ? yup$1.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1587
+ );
1588
+ const statusSchema = yup$1.mixed().oneOf(["draft", "published"], "Invalid status");
1589
+ const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
1590
+ const { allowMultipleLocales } = opts;
1591
+ const { locale, status: providedStatus, ...rest } = request || {};
1592
+ const defaultStatus = contentTypes$1.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
1593
+ const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
1594
+ const schema = yup$1.object().shape({
1595
+ locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1596
+ status: statusSchema
1597
+ });
1598
+ try {
1599
+ await validateYupSchema(schema, { strict: true, abortEarly: false })(request);
1600
+ return { locale, status, ...rest };
1601
+ } catch (error) {
1602
+ throw new errors.ValidationError(`Validation error: ${error.message}`);
1561
1603
  }
1562
- return { locale, status, ...rest };
1604
+ };
1605
+ const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
1606
+ const documentMetadata2 = getService$1("document-metadata");
1607
+ const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
1608
+ let {
1609
+ meta: { availableLocales, availableStatus }
1610
+ } = serviceOutput;
1611
+ const metadataSanitizer = permissionChecker2.sanitizeOutput;
1612
+ availableLocales = await async.map(
1613
+ availableLocales,
1614
+ async (localeDocument) => metadataSanitizer(localeDocument)
1615
+ );
1616
+ availableStatus = await async.map(
1617
+ availableStatus,
1618
+ async (statusDocument) => metadataSanitizer(statusDocument)
1619
+ );
1620
+ return {
1621
+ ...serviceOutput,
1622
+ meta: {
1623
+ availableLocales,
1624
+ availableStatus
1625
+ }
1626
+ };
1563
1627
  };
1564
1628
  const createDocument = async (ctx, opts) => {
1565
1629
  const { userAbility, user } = ctx.state;
@@ -1574,7 +1638,7 @@ const createDocument = async (ctx, opts) => {
1574
1638
  const setCreator = setCreatorFields({ user });
1575
1639
  const sanitizeFn = async.pipe(pickPermittedFields, setCreator);
1576
1640
  const sanitizedBody = await sanitizeFn(body);
1577
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(body);
1641
+ const { locale, status } = await getDocumentLocaleAndStatus(body, model);
1578
1642
  return documentManager2.create(model, {
1579
1643
  data: sanitizedBody,
1580
1644
  locale,
@@ -1593,7 +1657,7 @@ const updateDocument = async (ctx, opts) => {
1593
1657
  }
1594
1658
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1595
1659
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1596
- const { locale } = getDocumentLocaleAndStatus(body);
1660
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1597
1661
  const [documentVersion, documentExists] = await Promise.all([
1598
1662
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1599
1663
  documentManager2.exists(model, id)
@@ -1631,8 +1695,8 @@ const collectionTypes = {
1631
1695
  }
1632
1696
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1633
1697
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1634
- const { locale, status } = getDocumentLocaleAndStatus(query);
1635
- const { results: documents, pagination } = await documentManager2.findPage(
1698
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
1699
+ const { results: documents, pagination: pagination2 } = await documentManager2.findPage(
1636
1700
  { ...permissionQuery, populate, locale, status },
1637
1701
  model
1638
1702
  );
@@ -1653,21 +1717,20 @@ const collectionTypes = {
1653
1717
  );
1654
1718
  ctx.body = {
1655
1719
  results,
1656
- pagination
1720
+ pagination: pagination2
1657
1721
  };
1658
1722
  },
1659
1723
  async findOne(ctx) {
1660
1724
  const { userAbility } = ctx.state;
1661
1725
  const { model, id } = ctx.params;
1662
1726
  const documentManager2 = getService$1("document-manager");
1663
- const documentMetadata2 = getService$1("document-metadata");
1664
1727
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1665
1728
  if (permissionChecker2.cannot.read()) {
1666
1729
  return ctx.forbidden();
1667
1730
  }
1668
1731
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1669
1732
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1670
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
1733
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
1671
1734
  const version = await documentManager2.findOne(id, model, {
1672
1735
  populate,
1673
1736
  locale,
@@ -1678,8 +1741,10 @@ const collectionTypes = {
1678
1741
  if (!exists) {
1679
1742
  return ctx.notFound();
1680
1743
  }
1681
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
1744
+ const { meta } = await formatDocumentWithMetadata(
1745
+ permissionChecker2,
1682
1746
  model,
1747
+ // @ts-expect-error TODO: fix
1683
1748
  { id, locale, publishedAt: null },
1684
1749
  { availableLocales: true, availableStatus: false }
1685
1750
  );
@@ -1690,12 +1755,11 @@ const collectionTypes = {
1690
1755
  return ctx.forbidden();
1691
1756
  }
1692
1757
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
1693
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1758
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1694
1759
  },
1695
1760
  async create(ctx) {
1696
1761
  const { userAbility } = ctx.state;
1697
1762
  const { model } = ctx.params;
1698
- const documentMetadata2 = getService$1("document-metadata");
1699
1763
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1700
1764
  const [totalEntries, document] = await Promise.all([
1701
1765
  strapi.db.query(model).count(),
@@ -1703,7 +1767,7 @@ const collectionTypes = {
1703
1767
  ]);
1704
1768
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
1705
1769
  ctx.status = 201;
1706
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1770
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1707
1771
  // Empty metadata as it's not relevant for a new document
1708
1772
  availableLocales: false,
1709
1773
  availableStatus: false
@@ -1717,25 +1781,23 @@ const collectionTypes = {
1717
1781
  async update(ctx) {
1718
1782
  const { userAbility } = ctx.state;
1719
1783
  const { model } = ctx.params;
1720
- const documentMetadata2 = getService$1("document-metadata");
1721
1784
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1722
1785
  const updatedVersion = await updateDocument(ctx);
1723
1786
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1724
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedVersion);
1787
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
1725
1788
  },
1726
1789
  async clone(ctx) {
1727
1790
  const { userAbility, user } = ctx.state;
1728
1791
  const { model, sourceId: id } = ctx.params;
1729
1792
  const { body } = ctx.request;
1730
1793
  const documentManager2 = getService$1("document-manager");
1731
- const documentMetadata2 = getService$1("document-metadata");
1732
1794
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1733
1795
  if (permissionChecker2.cannot.create()) {
1734
1796
  return ctx.forbidden();
1735
1797
  }
1736
1798
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1737
1799
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1738
- const { locale } = getDocumentLocaleAndStatus(body);
1800
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1739
1801
  const document = await documentManager2.findOne(id, model, {
1740
1802
  populate,
1741
1803
  locale,
@@ -1751,7 +1813,7 @@ const collectionTypes = {
1751
1813
  const sanitizedBody = await sanitizeFn(body);
1752
1814
  const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
1753
1815
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
1754
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1816
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1755
1817
  // Empty metadata as it's not relevant for a new document
1756
1818
  availableLocales: false,
1757
1819
  availableStatus: false
@@ -1780,7 +1842,7 @@ const collectionTypes = {
1780
1842
  }
1781
1843
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1782
1844
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1783
- const { locale } = getDocumentLocaleAndStatus(ctx.query);
1845
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
1784
1846
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1785
1847
  if (documentLocales.length === 0) {
1786
1848
  return ctx.notFound();
@@ -1802,7 +1864,6 @@ const collectionTypes = {
1802
1864
  const { id, model } = ctx.params;
1803
1865
  const { body } = ctx.request;
1804
1866
  const documentManager2 = getService$1("document-manager");
1805
- const documentMetadata2 = getService$1("document-metadata");
1806
1867
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1807
1868
  if (permissionChecker2.cannot.publish()) {
1808
1869
  return ctx.forbidden();
@@ -1810,25 +1871,46 @@ const collectionTypes = {
1810
1871
  const publishedDocument = await strapi.db.transaction(async () => {
1811
1872
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1812
1873
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1813
- const document = id ? await updateDocument(ctx, { populate }) : await createDocument(ctx, { populate });
1874
+ let document;
1875
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1876
+ const isCreate = isNil$1(id);
1877
+ if (isCreate) {
1878
+ if (permissionChecker2.cannot.create()) {
1879
+ throw new errors.ForbiddenError();
1880
+ }
1881
+ document = await createDocument(ctx, { populate });
1882
+ }
1883
+ const isUpdate = !isCreate;
1884
+ if (isUpdate) {
1885
+ document = await documentManager2.findOne(id, model, { populate, locale });
1886
+ if (!document) {
1887
+ throw new errors.NotFoundError("Document not found");
1888
+ }
1889
+ if (permissionChecker2.can.update(document)) {
1890
+ await updateDocument(ctx);
1891
+ }
1892
+ }
1814
1893
  if (permissionChecker2.cannot.publish(document)) {
1815
1894
  throw new errors.ForbiddenError();
1816
1895
  }
1817
- const { locale } = getDocumentLocaleAndStatus(body);
1818
- return documentManager2.publish(document.documentId, model, {
1896
+ const publishResult = await documentManager2.publish(document.documentId, model, {
1819
1897
  locale
1820
1898
  // TODO: Allow setting creator fields on publish
1821
1899
  // data: setCreatorFields({ user, isEdition: true })({}),
1822
1900
  });
1901
+ if (!publishResult || publishResult.length === 0) {
1902
+ throw new errors.NotFoundError("Document not found or already published.");
1903
+ }
1904
+ return publishResult[0];
1823
1905
  });
1824
1906
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
1825
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1907
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1826
1908
  },
1827
1909
  async bulkPublish(ctx) {
1828
1910
  const { userAbility } = ctx.state;
1829
1911
  const { model } = ctx.params;
1830
1912
  const { body } = ctx.request;
1831
- const { ids } = body;
1913
+ const { documentIds } = body;
1832
1914
  await validateBulkActionInput(body);
1833
1915
  const documentManager2 = getService$1("document-manager");
1834
1916
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1837,8 +1919,13 @@ const collectionTypes = {
1837
1919
  }
1838
1920
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1839
1921
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1840
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1841
- const entities = await Promise.all(entityPromises);
1922
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
1923
+ allowMultipleLocales: true
1924
+ });
1925
+ const entityPromises = documentIds.map(
1926
+ (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
1927
+ );
1928
+ const entities = (await Promise.all(entityPromises)).flat();
1842
1929
  for (const entity of entities) {
1843
1930
  if (!entity) {
1844
1931
  return ctx.notFound();
@@ -1847,24 +1934,25 @@ const collectionTypes = {
1847
1934
  return ctx.forbidden();
1848
1935
  }
1849
1936
  }
1850
- const { count } = await documentManager2.publishMany(entities, model);
1937
+ const count = await documentManager2.publishMany(model, documentIds, locale);
1851
1938
  ctx.body = { count };
1852
1939
  },
1853
1940
  async bulkUnpublish(ctx) {
1854
1941
  const { userAbility } = ctx.state;
1855
1942
  const { model } = ctx.params;
1856
1943
  const { body } = ctx.request;
1857
- const { ids } = body;
1944
+ const { documentIds } = body;
1858
1945
  await validateBulkActionInput(body);
1859
1946
  const documentManager2 = getService$1("document-manager");
1860
1947
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1861
1948
  if (permissionChecker2.cannot.unpublish()) {
1862
1949
  return ctx.forbidden();
1863
1950
  }
1864
- const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1865
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1866
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1867
- const entities = await Promise.all(entityPromises);
1951
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1952
+ const entityPromises = documentIds.map(
1953
+ (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
1954
+ );
1955
+ const entities = (await Promise.all(entityPromises)).flat();
1868
1956
  for (const entity of entities) {
1869
1957
  if (!entity) {
1870
1958
  return ctx.notFound();
@@ -1873,7 +1961,8 @@ const collectionTypes = {
1873
1961
  return ctx.forbidden();
1874
1962
  }
1875
1963
  }
1876
- const { count } = await documentManager2.unpublishMany(entities, model);
1964
+ const entitiesIds = entities.map((document) => document.documentId);
1965
+ const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
1877
1966
  ctx.body = { count };
1878
1967
  },
1879
1968
  async unpublish(ctx) {
@@ -1883,7 +1972,6 @@ const collectionTypes = {
1883
1972
  body: { discardDraft, ...body }
1884
1973
  } = ctx.request;
1885
1974
  const documentManager2 = getService$1("document-manager");
1886
- const documentMetadata2 = getService$1("document-metadata");
1887
1975
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1888
1976
  if (permissionChecker2.cannot.unpublish()) {
1889
1977
  return ctx.forbidden();
@@ -1893,7 +1981,7 @@ const collectionTypes = {
1893
1981
  }
1894
1982
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1895
1983
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1896
- const { locale } = getDocumentLocaleAndStatus(body);
1984
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1897
1985
  const document = await documentManager2.findOne(id, model, {
1898
1986
  populate,
1899
1987
  locale,
@@ -1915,7 +2003,7 @@ const collectionTypes = {
1915
2003
  ctx.body = await async.pipe(
1916
2004
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
1917
2005
  permissionChecker2.sanitizeOutput,
1918
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2006
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1919
2007
  )(document);
1920
2008
  });
1921
2009
  },
@@ -1924,14 +2012,13 @@ const collectionTypes = {
1924
2012
  const { id, model } = ctx.params;
1925
2013
  const { body } = ctx.request;
1926
2014
  const documentManager2 = getService$1("document-manager");
1927
- const documentMetadata2 = getService$1("document-metadata");
1928
2015
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1929
2016
  if (permissionChecker2.cannot.discard()) {
1930
2017
  return ctx.forbidden();
1931
2018
  }
1932
2019
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
1933
2020
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1934
- const { locale } = getDocumentLocaleAndStatus(body);
2021
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1935
2022
  const document = await documentManager2.findOne(id, model, {
1936
2023
  populate,
1937
2024
  locale,
@@ -1946,14 +2033,14 @@ const collectionTypes = {
1946
2033
  ctx.body = await async.pipe(
1947
2034
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
1948
2035
  permissionChecker2.sanitizeOutput,
1949
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2036
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1950
2037
  )(document);
1951
2038
  },
1952
2039
  async bulkDelete(ctx) {
1953
2040
  const { userAbility } = ctx.state;
1954
2041
  const { model } = ctx.params;
1955
2042
  const { query, body } = ctx.request;
1956
- const { ids } = body;
2043
+ const { documentIds } = body;
1957
2044
  await validateBulkActionInput(body);
1958
2045
  const documentManager2 = getService$1("document-manager");
1959
2046
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1961,14 +2048,22 @@ const collectionTypes = {
1961
2048
  return ctx.forbidden();
1962
2049
  }
1963
2050
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
1964
- const idsWhereClause = { id: { $in: ids } };
1965
- const params = {
1966
- ...permissionQuery,
1967
- filters: {
1968
- $and: [idsWhereClause].concat(permissionQuery.filters || [])
2051
+ const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2052
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2053
+ const documentLocales = await documentManager2.findLocales(documentIds, model, {
2054
+ populate,
2055
+ locale
2056
+ });
2057
+ if (documentLocales.length === 0) {
2058
+ return ctx.notFound();
2059
+ }
2060
+ for (const document of documentLocales) {
2061
+ if (permissionChecker2.cannot.delete(document)) {
2062
+ return ctx.forbidden();
1969
2063
  }
1970
- };
1971
- const { count } = await documentManager2.deleteMany(params, model);
2064
+ }
2065
+ const localeDocumentsIds = documentLocales.map((document) => document.documentId);
2066
+ const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
1972
2067
  ctx.body = { count };
1973
2068
  },
1974
2069
  async countDraftRelations(ctx) {
@@ -1981,7 +2076,7 @@ const collectionTypes = {
1981
2076
  }
1982
2077
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1983
2078
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1984
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
2079
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
1985
2080
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
1986
2081
  if (!entity) {
1987
2082
  return ctx.notFound();
@@ -1996,7 +2091,7 @@ const collectionTypes = {
1996
2091
  },
1997
2092
  async countManyEntriesDraftRelations(ctx) {
1998
2093
  const { userAbility } = ctx.state;
1999
- const ids = ctx.request.query.ids;
2094
+ const ids = ctx.request.query.documentIds;
2000
2095
  const locale = ctx.request.query.locale;
2001
2096
  const { model } = ctx.params;
2002
2097
  const documentManager2 = getService$1("document-manager");
@@ -2004,16 +2099,16 @@ const collectionTypes = {
2004
2099
  if (permissionChecker2.cannot.read()) {
2005
2100
  return ctx.forbidden();
2006
2101
  }
2007
- const entities = await documentManager2.findMany(
2102
+ const documents = await documentManager2.findMany(
2008
2103
  {
2009
2104
  filters: {
2010
- id: ids
2105
+ documentId: ids
2011
2106
  },
2012
2107
  locale
2013
2108
  },
2014
2109
  model
2015
2110
  );
2016
- if (!entities) {
2111
+ if (!documents) {
2017
2112
  return ctx.notFound();
2018
2113
  }
2019
2114
  const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
@@ -2509,7 +2604,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
2509
2604
  throw new errors.ForbiddenError();
2510
2605
  }
2511
2606
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2512
- const { locale } = getDocumentLocaleAndStatus(body);
2607
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2513
2608
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2514
2609
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2515
2610
  // Find the first document to check if it exists
@@ -2546,12 +2641,11 @@ const singleTypes = {
2546
2641
  const { model } = ctx.params;
2547
2642
  const { query = {} } = ctx.request;
2548
2643
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2549
- const documentMetadata2 = getService$1("document-metadata");
2550
2644
  if (permissionChecker2.cannot.read()) {
2551
2645
  return ctx.forbidden();
2552
2646
  }
2553
2647
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2554
- const { locale, status } = getDocumentLocaleAndStatus(query);
2648
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
2555
2649
  const version = await findDocument(permissionQuery, model, { locale, status });
2556
2650
  if (!version) {
2557
2651
  if (permissionChecker2.cannot.create()) {
@@ -2561,8 +2655,10 @@ const singleTypes = {
2561
2655
  if (!document) {
2562
2656
  return ctx.notFound();
2563
2657
  }
2564
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
2658
+ const { meta } = await formatDocumentWithMetadata(
2659
+ permissionChecker2,
2565
2660
  model,
2661
+ // @ts-expect-error - fix types
2566
2662
  { id: document.documentId, locale, publishedAt: null },
2567
2663
  { availableLocales: true, availableStatus: false }
2568
2664
  );
@@ -2573,16 +2669,15 @@ const singleTypes = {
2573
2669
  return ctx.forbidden();
2574
2670
  }
2575
2671
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
2576
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2672
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2577
2673
  },
2578
2674
  async createOrUpdate(ctx) {
2579
2675
  const { userAbility } = ctx.state;
2580
2676
  const { model } = ctx.params;
2581
- const documentMetadata2 = getService$1("document-metadata");
2582
2677
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2583
2678
  const document = await createOrUpdateDocument(ctx);
2584
2679
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2585
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2680
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2586
2681
  },
2587
2682
  async delete(ctx) {
2588
2683
  const { userAbility } = ctx.state;
@@ -2595,7 +2690,7 @@ const singleTypes = {
2595
2690
  }
2596
2691
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2597
2692
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2598
- const { locale } = getDocumentLocaleAndStatus(query);
2693
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2599
2694
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2600
2695
  populate,
2601
2696
  locale
@@ -2618,7 +2713,6 @@ const singleTypes = {
2618
2713
  const { model } = ctx.params;
2619
2714
  const { query = {} } = ctx.request;
2620
2715
  const documentManager2 = getService$1("document-manager");
2621
- const documentMetadata2 = getService$1("document-metadata");
2622
2716
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2623
2717
  if (permissionChecker2.cannot.publish()) {
2624
2718
  return ctx.forbidden();
@@ -2633,11 +2727,12 @@ const singleTypes = {
2633
2727
  if (permissionChecker2.cannot.publish(document)) {
2634
2728
  throw new errors.ForbiddenError();
2635
2729
  }
2636
- const { locale } = getDocumentLocaleAndStatus(document);
2637
- return documentManager2.publish(document.documentId, model, { locale });
2730
+ const { locale } = await getDocumentLocaleAndStatus(document, model);
2731
+ const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2732
+ return publishResult.at(0);
2638
2733
  });
2639
2734
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
2640
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2735
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2641
2736
  },
2642
2737
  async unpublish(ctx) {
2643
2738
  const { userAbility } = ctx.state;
@@ -2647,7 +2742,6 @@ const singleTypes = {
2647
2742
  query = {}
2648
2743
  } = ctx.request;
2649
2744
  const documentManager2 = getService$1("document-manager");
2650
- const documentMetadata2 = getService$1("document-metadata");
2651
2745
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2652
2746
  if (permissionChecker2.cannot.unpublish()) {
2653
2747
  return ctx.forbidden();
@@ -2656,7 +2750,7 @@ const singleTypes = {
2656
2750
  return ctx.forbidden();
2657
2751
  }
2658
2752
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2659
- const { locale } = getDocumentLocaleAndStatus(body);
2753
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2660
2754
  const document = await findDocument(sanitizedQuery, model, { locale });
2661
2755
  if (!document) {
2662
2756
  return ctx.notFound();
@@ -2674,7 +2768,7 @@ const singleTypes = {
2674
2768
  ctx.body = await async.pipe(
2675
2769
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
2676
2770
  permissionChecker2.sanitizeOutput,
2677
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2771
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2678
2772
  )(document);
2679
2773
  });
2680
2774
  },
@@ -2683,13 +2777,12 @@ const singleTypes = {
2683
2777
  const { model } = ctx.params;
2684
2778
  const { body, query = {} } = ctx.request;
2685
2779
  const documentManager2 = getService$1("document-manager");
2686
- const documentMetadata2 = getService$1("document-metadata");
2687
2780
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2688
2781
  if (permissionChecker2.cannot.discard()) {
2689
2782
  return ctx.forbidden();
2690
2783
  }
2691
2784
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2692
- const { locale } = getDocumentLocaleAndStatus(body);
2785
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2693
2786
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2694
2787
  if (!document) {
2695
2788
  return ctx.notFound();
@@ -2700,7 +2793,7 @@ const singleTypes = {
2700
2793
  ctx.body = await async.pipe(
2701
2794
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
2702
2795
  permissionChecker2.sanitizeOutput,
2703
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2796
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2704
2797
  )(document);
2705
2798
  },
2706
2799
  async countDraftRelations(ctx) {
@@ -2709,7 +2802,7 @@ const singleTypes = {
2709
2802
  const { query } = ctx.request;
2710
2803
  const documentManager2 = getService$1("document-manager");
2711
2804
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2712
- const { locale } = getDocumentLocaleAndStatus(query);
2805
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2713
2806
  if (permissionChecker2.cannot.read()) {
2714
2807
  return ctx.forbidden();
2715
2808
  }
@@ -2730,7 +2823,7 @@ const uid$1 = {
2730
2823
  async generateUID(ctx) {
2731
2824
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2732
2825
  const { query = {} } = ctx.request;
2733
- const { locale } = getDocumentLocaleAndStatus(query);
2826
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2734
2827
  await validateUIDField(contentTypeUID, field);
2735
2828
  const uidService = getService$1("uid");
2736
2829
  ctx.body = {
@@ -2742,7 +2835,7 @@ const uid$1 = {
2742
2835
  ctx.request.body
2743
2836
  );
2744
2837
  const { query = {} } = ctx.request;
2745
- const { locale } = getDocumentLocaleAndStatus(query);
2838
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2746
2839
  await validateUIDField(contentTypeUID, field);
2747
2840
  const uidService = getService$1("uid");
2748
2841
  const isAvailable = await uidService.checkUIDAvailability({
@@ -3533,7 +3626,7 @@ const permission = ({ strapi: strapi2 }) => ({
3533
3626
  await strapi2.service("admin::permission").actionProvider.registerMany(actions);
3534
3627
  }
3535
3628
  });
3536
- const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils.contentTypes;
3629
+ const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils.contentTypes;
3537
3630
  const { isAnyToMany } = strapiUtils.relations;
3538
3631
  const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils.contentTypes.constants;
3539
3632
  const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
@@ -3624,6 +3717,42 @@ const getDeepPopulate = (uid2, {
3624
3717
  {}
3625
3718
  );
3626
3719
  };
3720
+ const getValidatableFieldsPopulate = (uid2, {
3721
+ initialPopulate = {},
3722
+ countMany = false,
3723
+ countOne = false,
3724
+ maxLevel = Infinity
3725
+ } = {}, level = 1) => {
3726
+ if (level > maxLevel) {
3727
+ return {};
3728
+ }
3729
+ const model = strapi.getModel(uid2);
3730
+ return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
3731
+ if (!getDoesAttributeRequireValidation(attribute)) {
3732
+ return populateAcc;
3733
+ }
3734
+ if (isScalarAttribute(attribute)) {
3735
+ return merge(populateAcc, {
3736
+ [attributeName]: true
3737
+ });
3738
+ }
3739
+ return merge(
3740
+ populateAcc,
3741
+ getPopulateFor(
3742
+ attributeName,
3743
+ model,
3744
+ {
3745
+ // @ts-expect-error - improve types
3746
+ initialPopulate: initialPopulate?.[attributeName],
3747
+ countMany,
3748
+ countOne,
3749
+ maxLevel
3750
+ },
3751
+ level
3752
+ )
3753
+ );
3754
+ }, {});
3755
+ };
3627
3756
  const getDeepPopulateDraftCount = (uid2) => {
3628
3757
  const model = strapi.getModel(uid2);
3629
3758
  let hasRelations = false;
@@ -3631,6 +3760,10 @@ const getDeepPopulateDraftCount = (uid2) => {
3631
3760
  const attribute = model.attributes[attributeName];
3632
3761
  switch (attribute.type) {
3633
3762
  case "relation": {
3763
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
3764
+ if (isMorphRelation) {
3765
+ break;
3766
+ }
3634
3767
  if (isVisibleAttribute$1(model, attributeName)) {
3635
3768
  populateAcc[attributeName] = {
3636
3769
  count: true,
@@ -3645,22 +3778,24 @@ const getDeepPopulateDraftCount = (uid2) => {
3645
3778
  attribute.component
3646
3779
  );
3647
3780
  if (childHasRelations) {
3648
- populateAcc[attributeName] = { populate: populate2 };
3781
+ populateAcc[attributeName] = {
3782
+ populate: populate2
3783
+ };
3649
3784
  hasRelations = true;
3650
3785
  }
3651
3786
  break;
3652
3787
  }
3653
3788
  case "dynamiczone": {
3654
- const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
3655
- const { populate: populate2, hasRelations: childHasRelations } = getDeepPopulateDraftCount(componentUID);
3656
- if (childHasRelations) {
3789
+ const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
3790
+ const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
3791
+ if (componentHasRelations) {
3657
3792
  hasRelations = true;
3658
- return merge(acc, populate2);
3793
+ return { ...acc, [componentUID]: { populate: componentPopulate } };
3659
3794
  }
3660
3795
  return acc;
3661
3796
  }, {});
3662
- if (!isEmpty(dzPopulate)) {
3663
- populateAcc[attributeName] = { populate: dzPopulate };
3797
+ if (!isEmpty(dzPopulateFragment)) {
3798
+ populateAcc[attributeName] = { on: dzPopulateFragment };
3664
3799
  }
3665
3800
  break;
3666
3801
  }
@@ -3852,41 +3987,70 @@ const AVAILABLE_STATUS_FIELDS = [
3852
3987
  "updatedBy",
3853
3988
  "status"
3854
3989
  ];
3855
- const AVAILABLE_LOCALES_FIELDS = ["id", "locale", "updatedAt", "createdAt", "status"];
3990
+ const AVAILABLE_LOCALES_FIELDS = [
3991
+ "id",
3992
+ "locale",
3993
+ "updatedAt",
3994
+ "createdAt",
3995
+ "status",
3996
+ "publishedAt",
3997
+ "documentId"
3998
+ ];
3856
3999
  const CONTENT_MANAGER_STATUS = {
3857
4000
  PUBLISHED: "published",
3858
4001
  DRAFT: "draft",
3859
4002
  MODIFIED: "modified"
3860
4003
  };
3861
- const areDatesEqual = (date1, date2, threshold) => {
3862
- if (!date1 || !date2) {
4004
+ const getIsVersionLatestModification = (version, otherVersion) => {
4005
+ if (!version || !version.updatedAt) {
3863
4006
  return false;
3864
4007
  }
3865
- const time1 = new Date(date1).getTime();
3866
- const time2 = new Date(date2).getTime();
3867
- const difference2 = Math.abs(time1 - time2);
3868
- return difference2 <= threshold;
4008
+ const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
4009
+ const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
4010
+ return versionUpdatedAt > otherUpdatedAt;
3869
4011
  };
3870
4012
  const documentMetadata = ({ strapi: strapi2 }) => ({
3871
4013
  /**
3872
4014
  * Returns available locales of a document for the current status
3873
4015
  */
3874
- getAvailableLocales(uid2, version, allVersions) {
4016
+ async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
3875
4017
  const versionsByLocale = groupBy("locale", allVersions);
3876
4018
  delete versionsByLocale[version.locale];
3877
- return Object.values(versionsByLocale).map((localeVersions) => {
3878
- if (!contentTypes$1.hasDraftAndPublish(strapi2.getModel(uid2))) {
3879
- return pick(AVAILABLE_LOCALES_FIELDS, localeVersions[0]);
4019
+ const model = strapi2.getModel(uid2);
4020
+ const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
4021
+ const traversalFunction = async (localeVersion) => traverseEntity(
4022
+ ({ key }, { remove }) => {
4023
+ if (keysToKeep.includes(key)) {
4024
+ return;
4025
+ }
4026
+ remove(key);
4027
+ },
4028
+ { schema: model, getModel: strapi2.getModel.bind(strapi2) },
4029
+ // @ts-expect-error fix types DocumentVersion incompatible with Data
4030
+ localeVersion
4031
+ );
4032
+ const mappingResult = await async.map(
4033
+ Object.values(versionsByLocale),
4034
+ async (localeVersions) => {
4035
+ const mappedLocaleVersions = await async.map(
4036
+ localeVersions,
4037
+ traversalFunction
4038
+ );
4039
+ if (!contentTypes$1.hasDraftAndPublish(model)) {
4040
+ return mappedLocaleVersions[0];
4041
+ }
4042
+ const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
4043
+ const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
4044
+ if (!draftVersion) {
4045
+ return;
4046
+ }
4047
+ return {
4048
+ ...draftVersion,
4049
+ status: this.getStatus(draftVersion, otherVersions)
4050
+ };
3880
4051
  }
3881
- const draftVersion = localeVersions.find((v) => v.publishedAt === null);
3882
- const otherVersions = localeVersions.filter((v) => v.id !== draftVersion?.id);
3883
- if (!draftVersion)
3884
- return;
3885
- return {
3886
- ...pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
3887
- status: this.getStatus(draftVersion, otherVersions)
3888
- };
3889
- }).filter(Boolean);
4052
+ );
4053
+ return mappingResult.filter(Boolean);
3890
4054
  },
3891
4055
  /**
3892
4056
  * Returns available status of a document for the current locale
@@ -3924,26 +4088,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3924
4088
  });
3925
4089
  },
3926
4090
  getStatus(version, otherDocumentStatuses) {
3927
- const isDraft = version.publishedAt === null;
3928
- if (!otherDocumentStatuses?.length) {
3929
- return isDraft ? CONTENT_MANAGER_STATUS.DRAFT : CONTENT_MANAGER_STATUS.PUBLISHED;
4091
+ let draftVersion;
4092
+ let publishedVersion;
4093
+ if (version.publishedAt) {
4094
+ publishedVersion = version;
4095
+ } else {
4096
+ draftVersion = version;
3930
4097
  }
3931
- if (isDraft) {
3932
- const publishedVersion = otherDocumentStatuses?.find((d) => d.publishedAt !== null);
3933
- if (!publishedVersion) {
3934
- return CONTENT_MANAGER_STATUS.DRAFT;
3935
- }
4098
+ const otherVersion = otherDocumentStatuses?.at(0);
4099
+ if (otherVersion?.publishedAt) {
4100
+ publishedVersion = otherVersion;
4101
+ } else if (otherVersion) {
4102
+ draftVersion = otherVersion;
3936
4103
  }
3937
- if (areDatesEqual(version.updatedAt, otherDocumentStatuses.at(0)?.updatedAt, 500)) {
4104
+ if (!draftVersion)
3938
4105
  return CONTENT_MANAGER_STATUS.PUBLISHED;
3939
- }
3940
- return CONTENT_MANAGER_STATUS.MODIFIED;
4106
+ if (!publishedVersion)
4107
+ return CONTENT_MANAGER_STATUS.DRAFT;
4108
+ const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
4109
+ return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
3941
4110
  },
4111
+ // TODO is it necessary to return metadata on every page of the CM
4112
+ // We could refactor this so the locales are only loaded when they're
4113
+ // needed. e.g. in the bulk locale action modal.
3942
4114
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4115
+ const populate = getValidatableFieldsPopulate(uid2);
3943
4116
  const versions = await strapi2.db.query(uid2).findMany({
3944
4117
  where: { documentId: version.documentId },
3945
- select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
3946
4118
  populate: {
4119
+ // Populate only fields that require validation for bulk locale actions
4120
+ ...populate,
4121
+ // NOTE: creator fields are selected in this way to avoid exposing sensitive data
3947
4122
  createdBy: {
3948
4123
  select: ["id", "firstname", "lastname", "email"]
3949
4124
  },
@@ -3952,7 +4127,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3952
4127
  }
3953
4128
  }
3954
4129
  });
3955
- const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
4130
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
3956
4131
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
3957
4132
  return {
3958
4133
  availableLocales: availableLocalesResult,
@@ -3965,8 +4140,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3965
4140
  * - Available status of the document for the current locale
3966
4141
  */
3967
4142
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
3968
- if (!document)
3969
- return document;
4143
+ if (!document) {
4144
+ return {
4145
+ data: document,
4146
+ meta: {
4147
+ availableLocales: [],
4148
+ availableStatus: []
4149
+ }
4150
+ };
4151
+ }
3970
4152
  const hasDraftAndPublish = contentTypes$1.hasDraftAndPublish(strapi2.getModel(uid2));
3971
4153
  if (!hasDraftAndPublish) {
3972
4154
  opts.availableStatus = false;
@@ -4016,26 +4198,9 @@ const sumDraftCounts = (entity, uid2) => {
4016
4198
  }, 0);
4017
4199
  };
4018
4200
  const { ApplicationError } = errors;
4019
- const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
4020
4201
  const { PUBLISHED_AT_ATTRIBUTE } = contentTypes$1.constants;
4021
4202
  const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE);
4022
4203
  const omitIdField = omit("id");
4023
- const emitEvent = async (uid2, event, document) => {
4024
- const modelDef = strapi.getModel(uid2);
4025
- const sanitizedDocument = await sanitize.sanitizers.defaultSanitizeOutput(
4026
- {
4027
- schema: modelDef,
4028
- getModel(uid22) {
4029
- return strapi.getModel(uid22);
4030
- }
4031
- },
4032
- document
4033
- );
4034
- strapi.eventHub.emit(event, {
4035
- model: modelDef.modelName,
4036
- entry: sanitizedDocument
4037
- });
4038
- };
4039
4204
  const documentManager = ({ strapi: strapi2 }) => {
4040
4205
  return {
4041
4206
  async findOne(id, uid2, opts = {}) {
@@ -4054,6 +4219,9 @@ const documentManager = ({ strapi: strapi2 }) => {
4054
4219
  } else if (opts.locale && opts.locale !== "*") {
4055
4220
  where.locale = opts.locale;
4056
4221
  }
4222
+ if (typeof opts.isPublished === "boolean") {
4223
+ where.publishedAt = { $notNull: opts.isPublished };
4224
+ }
4057
4225
  return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
4058
4226
  },
4059
4227
  async findMany(opts, uid2) {
@@ -4061,20 +4229,16 @@ const documentManager = ({ strapi: strapi2 }) => {
4061
4229
  return strapi2.documents(uid2).findMany(params);
4062
4230
  },
4063
4231
  async findPage(opts, uid2) {
4064
- const page = Number(opts?.page) || 1;
4065
- const pageSize = Number(opts?.pageSize) || 10;
4232
+ const params = pagination.withDefaultPagination(opts || {}, {
4233
+ maxLimit: 1e3
4234
+ });
4066
4235
  const [documents, total = 0] = await Promise.all([
4067
- strapi2.documents(uid2).findMany(opts),
4068
- strapi2.documents(uid2).count(opts)
4236
+ strapi2.documents(uid2).findMany(params),
4237
+ strapi2.documents(uid2).count(params)
4069
4238
  ]);
4070
4239
  return {
4071
4240
  results: documents,
4072
- pagination: {
4073
- page,
4074
- pageSize,
4075
- pageCount: Math.ceil(total / pageSize),
4076
- total
4077
- }
4241
+ pagination: pagination.transformPagedPaginationInfo(params, total)
4078
4242
  };
4079
4243
  },
4080
4244
  async create(uid2, opts = {}) {
@@ -4091,10 +4255,7 @@ const documentManager = ({ strapi: strapi2 }) => {
4091
4255
  async clone(id, body, uid2) {
4092
4256
  const populate = await buildDeepPopulate(uid2);
4093
4257
  const params = {
4094
- data: {
4095
- ...omitIdField(body),
4096
- [PUBLISHED_AT_ATTRIBUTE]: null
4097
- },
4258
+ data: omitIdField(body),
4098
4259
  populate
4099
4260
  };
4100
4261
  return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
@@ -4120,70 +4281,36 @@ const documentManager = ({ strapi: strapi2 }) => {
4120
4281
  return {};
4121
4282
  },
4122
4283
  // FIXME: handle relations
4123
- async deleteMany(opts, uid2) {
4124
- const docs = await strapi2.documents(uid2).findMany(opts);
4125
- for (const doc of docs) {
4126
- await strapi2.documents(uid2).delete({ documentId: doc.documentId });
4127
- }
4128
- return { count: docs.length };
4284
+ async deleteMany(documentIds, uid2, opts = {}) {
4285
+ const deletedEntries = await strapi2.db.transaction(async () => {
4286
+ return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
4287
+ });
4288
+ return { count: deletedEntries.length };
4129
4289
  },
4130
4290
  async publish(id, uid2, opts = {}) {
4131
4291
  const populate = await buildDeepPopulate(uid2);
4132
4292
  const params = { ...opts, populate };
4133
- return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries.at(0));
4293
+ return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
4134
4294
  },
4135
- async publishMany(entities, uid2) {
4136
- if (!entities.length) {
4137
- return null;
4138
- }
4139
- await Promise.all(
4140
- entities.map((document) => {
4141
- return strapi2.entityValidator.validateEntityCreation(
4142
- strapi2.getModel(uid2),
4143
- document,
4144
- void 0,
4145
- // @ts-expect-error - FIXME: entity here is unnecessary
4146
- document
4147
- );
4148
- })
4149
- );
4150
- const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4151
- const filters = { id: { $in: entitiesToPublish } };
4152
- const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
4153
- const populate = await buildDeepPopulate(uid2);
4154
- const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4155
- where: filters,
4156
- data
4157
- });
4158
- const publishedEntities = await strapi2.db.query(uid2).findMany({
4159
- where: filters,
4160
- populate
4295
+ async publishMany(uid2, documentIds, locale) {
4296
+ return strapi2.db.transaction(async () => {
4297
+ const results = await Promise.all(
4298
+ documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
4299
+ );
4300
+ const publishedEntitiesCount = results.flat().filter(Boolean).length;
4301
+ return publishedEntitiesCount;
4161
4302
  });
4162
- await Promise.all(
4163
- publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
4164
- );
4165
- return publishedEntitiesCount;
4166
4303
  },
4167
- async unpublishMany(documents, uid2) {
4168
- if (!documents.length) {
4169
- return null;
4170
- }
4171
- const entitiesToUnpublish = documents.filter((doc) => doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4172
- const filters = { id: { $in: entitiesToUnpublish } };
4173
- const data = { [PUBLISHED_AT_ATTRIBUTE]: null };
4174
- const populate = await buildDeepPopulate(uid2);
4175
- const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4176
- where: filters,
4177
- data
4178
- });
4179
- const unpublishedEntities = await strapi2.db.query(uid2).findMany({
4180
- where: filters,
4181
- populate
4304
+ async unpublishMany(documentIds, uid2, opts = {}) {
4305
+ const unpublishedEntries = await strapi2.db.transaction(async () => {
4306
+ return Promise.all(
4307
+ documentIds.map(
4308
+ (id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
4309
+ )
4310
+ );
4182
4311
  });
4183
- await Promise.all(
4184
- unpublishedEntities.map((doc) => emitEvent(uid2, ENTRY_UNPUBLISH, doc))
4185
- );
4186
- return unpublishedEntitiesCount;
4312
+ const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
4313
+ return { count: unpublishedEntitiesCount };
4187
4314
  },
4188
4315
  async unpublish(id, uid2, opts = {}) {
4189
4316
  const populate = await buildDeepPopulate(uid2);
@@ -4208,16 +4335,20 @@ const documentManager = ({ strapi: strapi2 }) => {
4208
4335
  }
4209
4336
  return sumDraftCounts(document, uid2);
4210
4337
  },
4211
- async countManyEntriesDraftRelations(ids, uid2, locale) {
4338
+ async countManyEntriesDraftRelations(documentIds, uid2, locale) {
4212
4339
  const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
4213
4340
  if (!hasRelations) {
4214
4341
  return 0;
4215
4342
  }
4343
+ let localeFilter = {};
4344
+ if (locale) {
4345
+ localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
4346
+ }
4216
4347
  const entities = await strapi2.db.query(uid2).findMany({
4217
4348
  populate,
4218
4349
  where: {
4219
- id: { $in: ids },
4220
- ...locale ? { locale } : {}
4350
+ documentId: { $in: documentIds },
4351
+ ...localeFilter
4221
4352
  }
4222
4353
  });
4223
4354
  const totalNumberDraftRelations = entities.reduce(