@strapi/content-manager 0.0.0-experimental.17b4116f461a49b8ce5386f7c8d79c511d40fb3b → 0.0.0-experimental.25e22c6cc9bc6b35392bb55d09f641a0a65e7403

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 (168) hide show
  1. package/dist/_chunks/{CardDragPreview-DSVYodBX.js → CardDragPreview-C0QyJgRA.js} +10 -14
  2. package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -0
  3. package/dist/_chunks/{CardDragPreview-ikSG4M46.mjs → CardDragPreview-DOxamsuj.mjs} +7 -9
  4. package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -0
  5. package/dist/_chunks/{ComponentConfigurationPage-2iOVVhqV.js → ComponentConfigurationPage-WRPUXGd6.js} +3 -3
  6. package/dist/_chunks/{ComponentConfigurationPage-2iOVVhqV.js.map → ComponentConfigurationPage-WRPUXGd6.js.map} +1 -1
  7. package/dist/_chunks/{ComponentConfigurationPage-DjQBdcKF.mjs → ComponentConfigurationPage-gdUj_t-O.mjs} +3 -3
  8. package/dist/_chunks/{ComponentConfigurationPage-DjQBdcKF.mjs.map → ComponentConfigurationPage-gdUj_t-O.mjs.map} +1 -1
  9. package/dist/_chunks/{ComponentIcon-BBQsYCVn.js → ComponentIcon-BXdiCGQp.js} +8 -2
  10. package/dist/_chunks/ComponentIcon-BXdiCGQp.js.map +1 -0
  11. package/dist/_chunks/{ComponentIcon-BOFnK76n.mjs → ComponentIcon-u4bIXTFY.mjs} +9 -3
  12. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -0
  13. package/dist/_chunks/{EditConfigurationPage-BoBb-DLH.mjs → EditConfigurationPage-BwuIPOJG.mjs} +3 -3
  14. package/dist/_chunks/{EditConfigurationPage-BoBb-DLH.mjs.map → EditConfigurationPage-BwuIPOJG.mjs.map} +1 -1
  15. package/dist/_chunks/{EditConfigurationPage-B7dw5_cS.js → EditConfigurationPage-C1vjMBgy.js} +3 -3
  16. package/dist/_chunks/{EditConfigurationPage-B7dw5_cS.js.map → EditConfigurationPage-C1vjMBgy.js.map} +1 -1
  17. package/dist/_chunks/{EditViewPage-aUnqL-63.mjs → EditViewPage-0MiFkXa8.mjs} +47 -47
  18. package/dist/_chunks/EditViewPage-0MiFkXa8.mjs.map +1 -0
  19. package/dist/_chunks/{EditViewPage-KRG56aCq.js → EditViewPage-DbcGfyqK.js} +46 -48
  20. package/dist/_chunks/EditViewPage-DbcGfyqK.js.map +1 -0
  21. package/dist/_chunks/{Field-kVFO4ZKB.mjs → Field-BDMSCcy5.mjs} +585 -705
  22. package/dist/_chunks/Field-BDMSCcy5.mjs.map +1 -0
  23. package/dist/_chunks/{Field-kq1c2TF1.js → Field-BG1xu38N.js} +637 -758
  24. package/dist/_chunks/Field-BG1xu38N.js.map +1 -0
  25. package/dist/_chunks/{Form-CQ67ZifP.js → Form-9BnFyUjy.js} +35 -37
  26. package/dist/_chunks/Form-9BnFyUjy.js.map +1 -0
  27. package/dist/_chunks/{Form-Jgh5hGTu.mjs → Form-CPVWavB8.mjs} +35 -36
  28. package/dist/_chunks/Form-CPVWavB8.mjs.map +1 -0
  29. package/dist/_chunks/{History-DKhZAPcK.mjs → History-BVpd8LP3.mjs} +121 -48
  30. package/dist/_chunks/History-BVpd8LP3.mjs.map +1 -0
  31. package/dist/_chunks/{History-BLEnudTX.js → History-BWWxLt2Z.js} +122 -50
  32. package/dist/_chunks/History-BWWxLt2Z.js.map +1 -0
  33. package/dist/_chunks/{ListConfigurationPage-Zso_LUjn.js → ListConfigurationPage-6swzjdAZ.js} +53 -56
  34. package/dist/_chunks/ListConfigurationPage-6swzjdAZ.js.map +1 -0
  35. package/dist/_chunks/{ListConfigurationPage-nrXcxNYi.mjs → ListConfigurationPage-DozVMKcR.mjs} +49 -51
  36. package/dist/_chunks/ListConfigurationPage-DozVMKcR.mjs.map +1 -0
  37. package/dist/_chunks/{ListViewPage-DsaOakWQ.js → ListViewPage-BlzfjS2Q.js} +80 -105
  38. package/dist/_chunks/ListViewPage-BlzfjS2Q.js.map +1 -0
  39. package/dist/_chunks/{ListViewPage-ChhYmA-L.mjs → ListViewPage-Ds0ulgfG.mjs} +75 -100
  40. package/dist/_chunks/ListViewPage-Ds0ulgfG.mjs.map +1 -0
  41. package/dist/_chunks/{NoContentTypePage-BrdFcN33.mjs → NoContentTypePage-BH11kaKt.mjs} +3 -3
  42. package/dist/_chunks/NoContentTypePage-BH11kaKt.mjs.map +1 -0
  43. package/dist/_chunks/{NoContentTypePage-DPCuS9Y1.js → NoContentTypePage-D2nCCWEl.js} +3 -3
  44. package/dist/_chunks/NoContentTypePage-D2nCCWEl.js.map +1 -0
  45. package/dist/_chunks/{NoPermissionsPage-B9dqrtTy.mjs → NoPermissionsPage-BT2Tn0D_.mjs} +2 -2
  46. package/dist/_chunks/{NoPermissionsPage-B9dqrtTy.mjs.map → NoPermissionsPage-BT2Tn0D_.mjs.map} +1 -1
  47. package/dist/_chunks/{NoPermissionsPage-DdyOfdKb.js → NoPermissionsPage-DN_JlsU2.js} +2 -2
  48. package/dist/_chunks/{NoPermissionsPage-DdyOfdKb.js.map → NoPermissionsPage-DN_JlsU2.js.map} +1 -1
  49. package/dist/_chunks/{Relations-CY8Isqdu.js → Relations-CcgFTcWo.js} +70 -61
  50. package/dist/_chunks/Relations-CcgFTcWo.js.map +1 -0
  51. package/dist/_chunks/{Relations-DjFiYd7-.mjs → Relations-Dnag3fhV.mjs} +66 -56
  52. package/dist/_chunks/Relations-Dnag3fhV.mjs.map +1 -0
  53. package/dist/_chunks/{en-MBPul9Su.mjs → en-Ux26r5pl.mjs} +7 -1
  54. package/dist/_chunks/{en-MBPul9Su.mjs.map → en-Ux26r5pl.mjs.map} +1 -1
  55. package/dist/_chunks/{en-C-V1_90f.js → en-fbKQxLGn.js} +7 -1
  56. package/dist/_chunks/{en-C-V1_90f.js.map → en-fbKQxLGn.js.map} +1 -1
  57. package/dist/_chunks/{index-DNa1J4HE.js → index-CWpLBSt0.js} +1391 -808
  58. package/dist/_chunks/index-CWpLBSt0.js.map +1 -0
  59. package/dist/_chunks/{index-CAc9yTnx.mjs → index-JNNNKUHs.mjs} +1465 -881
  60. package/dist/_chunks/index-JNNNKUHs.mjs.map +1 -0
  61. package/dist/_chunks/{layout-BqtLA6Lb.js → layout--iHdZzRk.js} +26 -23
  62. package/dist/_chunks/layout--iHdZzRk.js.map +1 -0
  63. package/dist/_chunks/{layout-CXsHbc3E.mjs → layout-DC503LnF.mjs} +24 -19
  64. package/dist/_chunks/layout-DC503LnF.mjs.map +1 -0
  65. package/dist/_chunks/{relations-BHY_KDJ_.js → relations-BbHizA5K.js} +2 -2
  66. package/dist/_chunks/{relations-BHY_KDJ_.js.map → relations-BbHizA5K.js.map} +1 -1
  67. package/dist/_chunks/{relations-mMFEcZRq.mjs → relations-CTje5t-a.mjs} +2 -2
  68. package/dist/_chunks/{relations-mMFEcZRq.mjs.map → relations-CTje5t-a.mjs.map} +1 -1
  69. package/dist/_chunks/useDragAndDrop-DdHgKsqq.mjs.map +1 -1
  70. package/dist/_chunks/useDragAndDrop-J0TUUbR6.js.map +1 -1
  71. package/dist/_chunks/usePrev-B9w_-eYc.js +15 -0
  72. package/dist/_chunks/usePrev-B9w_-eYc.js.map +1 -0
  73. package/dist/_chunks/usePrev-DH6iah0A.mjs +16 -0
  74. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +1 -0
  75. package/dist/admin/index.js +2 -1
  76. package/dist/admin/index.js.map +1 -1
  77. package/dist/admin/index.mjs +8 -7
  78. package/dist/admin/src/components/ComponentIcon.d.ts +6 -3
  79. package/dist/admin/src/content-manager.d.ts +3 -3
  80. package/dist/admin/src/exports.d.ts +1 -0
  81. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
  82. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  83. package/dist/admin/src/hooks/useDocument.d.ts +5 -8
  84. package/dist/admin/src/hooks/useDocumentActions.d.ts +24 -3
  85. package/dist/admin/src/hooks/useDocumentLayout.d.ts +2 -2
  86. package/dist/admin/src/hooks/useDragAndDrop.d.ts +4 -4
  87. package/dist/admin/src/hooks/useKeyboardDragAndDrop.d.ts +1 -1
  88. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +10 -4
  89. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksInput.d.ts +3 -3
  90. package/dist/admin/src/pages/EditView/components/FormInputs/Component/Input.d.ts +2 -2
  91. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +3 -5
  92. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
  93. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +10 -18
  94. package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
  95. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +3 -49
  96. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
  97. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +59 -52
  98. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
  99. package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
  100. package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
  101. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +9 -26
  102. package/dist/admin/src/services/api.d.ts +2 -3
  103. package/dist/admin/src/services/components.d.ts +2 -2
  104. package/dist/admin/src/services/contentTypes.d.ts +5 -5
  105. package/dist/admin/src/services/documents.d.ts +29 -17
  106. package/dist/admin/src/services/init.d.ts +2 -2
  107. package/dist/admin/src/services/relations.d.ts +3 -3
  108. package/dist/admin/src/services/uid.d.ts +3 -3
  109. package/dist/admin/src/utils/api.d.ts +4 -18
  110. package/dist/admin/src/utils/validation.d.ts +1 -6
  111. package/dist/server/index.js +285 -197
  112. package/dist/server/index.js.map +1 -1
  113. package/dist/server/index.mjs +287 -199
  114. package/dist/server/index.mjs.map +1 -1
  115. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  116. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  117. package/dist/server/src/controllers/utils/metadata.d.ts +8 -0
  118. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
  119. package/dist/server/src/controllers/validation/dimensions.d.ts +9 -0
  120. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
  121. package/dist/server/src/controllers/validation/index.d.ts +1 -1
  122. package/dist/server/src/history/services/history.d.ts.map +1 -1
  123. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  124. package/dist/server/src/index.d.ts +17 -33
  125. package/dist/server/src/index.d.ts.map +1 -1
  126. package/dist/server/src/services/document-manager.d.ts +11 -6
  127. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  128. package/dist/server/src/services/document-metadata.d.ts +8 -29
  129. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  130. package/dist/server/src/services/index.d.ts +17 -33
  131. package/dist/server/src/services/index.d.ts.map +1 -1
  132. package/dist/server/src/services/utils/populate.d.ts +8 -1
  133. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  134. package/dist/shared/contracts/collection-types.d.ts +14 -6
  135. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  136. package/dist/shared/contracts/relations.d.ts +2 -2
  137. package/dist/shared/contracts/relations.d.ts.map +1 -1
  138. package/package.json +13 -14
  139. package/dist/_chunks/CardDragPreview-DSVYodBX.js.map +0 -1
  140. package/dist/_chunks/CardDragPreview-ikSG4M46.mjs.map +0 -1
  141. package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
  142. package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
  143. package/dist/_chunks/EditViewPage-KRG56aCq.js.map +0 -1
  144. package/dist/_chunks/EditViewPage-aUnqL-63.mjs.map +0 -1
  145. package/dist/_chunks/Field-kVFO4ZKB.mjs.map +0 -1
  146. package/dist/_chunks/Field-kq1c2TF1.js.map +0 -1
  147. package/dist/_chunks/Form-CQ67ZifP.js.map +0 -1
  148. package/dist/_chunks/Form-Jgh5hGTu.mjs.map +0 -1
  149. package/dist/_chunks/History-BLEnudTX.js.map +0 -1
  150. package/dist/_chunks/History-DKhZAPcK.mjs.map +0 -1
  151. package/dist/_chunks/ListConfigurationPage-Zso_LUjn.js.map +0 -1
  152. package/dist/_chunks/ListConfigurationPage-nrXcxNYi.mjs.map +0 -1
  153. package/dist/_chunks/ListViewPage-ChhYmA-L.mjs.map +0 -1
  154. package/dist/_chunks/ListViewPage-DsaOakWQ.js.map +0 -1
  155. package/dist/_chunks/NoContentTypePage-BrdFcN33.mjs.map +0 -1
  156. package/dist/_chunks/NoContentTypePage-DPCuS9Y1.js.map +0 -1
  157. package/dist/_chunks/Relations-CY8Isqdu.js.map +0 -1
  158. package/dist/_chunks/Relations-DjFiYd7-.mjs.map +0 -1
  159. package/dist/_chunks/index-CAc9yTnx.mjs.map +0 -1
  160. package/dist/_chunks/index-DNa1J4HE.js.map +0 -1
  161. package/dist/_chunks/layout-BqtLA6Lb.js.map +0 -1
  162. package/dist/_chunks/layout-CXsHbc3E.mjs.map +0 -1
  163. package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
  164. package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
  165. package/dist/_chunks/urls-DzZya_gm.js +0 -6
  166. package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
  167. package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
  168. package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
@@ -507,7 +507,10 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
507
507
  if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
508
508
  return next();
509
509
  }
510
- if (context.action !== "create" && context.action !== "update" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
510
+ if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
511
+ return next();
512
+ }
513
+ if (context.action === "update" && strapi2.requestContext.get()?.request.url.endsWith("/actions/publish")) {
511
514
  return next();
512
515
  }
513
516
  const contentTypeUid = context.contentType.uid;
@@ -515,9 +518,18 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
515
518
  return next();
516
519
  }
517
520
  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 };
521
+ const documentContext = {
522
+ documentId: context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId,
523
+ locale: context.params?.locale
524
+ };
519
525
  const defaultLocale = await serviceUtils.getDefaultLocale();
520
526
  const locale = documentContext.locale || defaultLocale;
527
+ if (Array.isArray(locale)) {
528
+ strapi2.log.warn(
529
+ "[Content manager history middleware]: An array of locales was provided, but only a single locale is supported for the findOne operation."
530
+ );
531
+ return next();
532
+ }
521
533
  const document = await strapi2.documents(contentTypeUid).findOne({
522
534
  documentId: documentContext.documentId,
523
535
  locale,
@@ -553,9 +565,8 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
553
565
  });
554
566
  return result;
555
567
  });
556
- const retentionDays = serviceUtils.getRetentionDays();
557
568
  state.deleteExpiredJob = nodeSchedule.scheduleJob("0 0 * * *", () => {
558
- const retentionDaysInMilliseconds = retentionDays * 24 * 60 * 60 * 1e3;
569
+ const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
559
570
  const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
560
571
  query.deleteMany({
561
572
  where: {
@@ -1478,7 +1489,7 @@ const { PaginationError, ValidationError } = strapiUtils.errors;
1478
1489
  const TYPES = ["singleType", "collectionType"];
1479
1490
  const kindSchema = strapiUtils.yup.string().oneOf(TYPES).nullable();
1480
1491
  const bulkActionInputSchema = strapiUtils.yup.object({
1481
- ids: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
1492
+ documentIds: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
1482
1493
  }).required();
1483
1494
  const generateUIDInputSchema = strapiUtils.yup.object({
1484
1495
  contentTypeUID: strapiUtils.yup.string().required(),
@@ -1577,15 +1588,47 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1577
1588
  }
1578
1589
  }, body);
1579
1590
  };
1580
- const getDocumentLocaleAndStatus = (request) => {
1591
+ const singleLocaleSchema = strapiUtils.yup.string().nullable();
1592
+ const multipleLocaleSchema = strapiUtils.yup.lazy(
1593
+ (value) => Array.isArray(value) ? strapiUtils.yup.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1594
+ );
1595
+ const statusSchema = strapiUtils.yup.mixed().oneOf(["draft", "published"], "Invalid status");
1596
+ const getDocumentLocaleAndStatus = async (request, opts = { allowMultipleLocales: false }) => {
1597
+ const { allowMultipleLocales } = opts;
1581
1598
  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}`);
1599
+ const schema = strapiUtils.yup.object().shape({
1600
+ locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1601
+ status: statusSchema
1602
+ });
1603
+ try {
1604
+ await strapiUtils.validateYupSchema(schema, { strict: true, abortEarly: false })(request);
1605
+ return { locale, status, ...rest };
1606
+ } catch (error) {
1607
+ throw new strapiUtils.errors.ValidationError(`Validation error: ${error.message}`);
1587
1608
  }
1588
- return { locale, status, ...rest };
1609
+ };
1610
+ const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
1611
+ const documentMetadata2 = getService$1("document-metadata");
1612
+ const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
1613
+ let {
1614
+ meta: { availableLocales, availableStatus }
1615
+ } = serviceOutput;
1616
+ const metadataSanitizer = permissionChecker2.sanitizeOutput;
1617
+ availableLocales = await strapiUtils.async.map(
1618
+ availableLocales,
1619
+ async (localeDocument) => metadataSanitizer(localeDocument)
1620
+ );
1621
+ availableStatus = await strapiUtils.async.map(
1622
+ availableStatus,
1623
+ async (statusDocument) => metadataSanitizer(statusDocument)
1624
+ );
1625
+ return {
1626
+ ...serviceOutput,
1627
+ meta: {
1628
+ availableLocales,
1629
+ availableStatus
1630
+ }
1631
+ };
1589
1632
  };
1590
1633
  const createDocument = async (ctx, opts) => {
1591
1634
  const { userAbility, user } = ctx.state;
@@ -1600,7 +1643,7 @@ const createDocument = async (ctx, opts) => {
1600
1643
  const setCreator = strapiUtils.setCreatorFields({ user });
1601
1644
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1602
1645
  const sanitizedBody = await sanitizeFn(body);
1603
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(body);
1646
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(body);
1604
1647
  return documentManager2.create(model, {
1605
1648
  data: sanitizedBody,
1606
1649
  locale,
@@ -1619,7 +1662,7 @@ const updateDocument = async (ctx, opts) => {
1619
1662
  }
1620
1663
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1621
1664
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1622
- const { locale } = getDocumentLocaleAndStatus(body);
1665
+ const { locale } = await getDocumentLocaleAndStatus(body);
1623
1666
  const [documentVersion, documentExists] = await Promise.all([
1624
1667
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1625
1668
  documentManager2.exists(model, id)
@@ -1657,7 +1700,7 @@ const collectionTypes = {
1657
1700
  }
1658
1701
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1659
1702
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1660
- const { locale, status } = getDocumentLocaleAndStatus(query);
1703
+ const { locale, status } = await getDocumentLocaleAndStatus(query);
1661
1704
  const { results: documents, pagination } = await documentManager2.findPage(
1662
1705
  { ...permissionQuery, populate, locale, status },
1663
1706
  model
@@ -1686,14 +1729,13 @@ const collectionTypes = {
1686
1729
  const { userAbility } = ctx.state;
1687
1730
  const { model, id } = ctx.params;
1688
1731
  const documentManager2 = getService$1("document-manager");
1689
- const documentMetadata2 = getService$1("document-metadata");
1690
1732
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1691
1733
  if (permissionChecker2.cannot.read()) {
1692
1734
  return ctx.forbidden();
1693
1735
  }
1694
1736
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1695
1737
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1696
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
1738
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1697
1739
  const version = await documentManager2.findOne(id, model, {
1698
1740
  populate,
1699
1741
  locale,
@@ -1704,8 +1746,10 @@ const collectionTypes = {
1704
1746
  if (!exists) {
1705
1747
  return ctx.notFound();
1706
1748
  }
1707
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
1749
+ const { meta } = await formatDocumentWithMetadata(
1750
+ permissionChecker2,
1708
1751
  model,
1752
+ // @ts-expect-error TODO: fix
1709
1753
  { id, locale, publishedAt: null },
1710
1754
  { availableLocales: true, availableStatus: false }
1711
1755
  );
@@ -1716,12 +1760,11 @@ const collectionTypes = {
1716
1760
  return ctx.forbidden();
1717
1761
  }
1718
1762
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
1719
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1763
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1720
1764
  },
1721
1765
  async create(ctx) {
1722
1766
  const { userAbility } = ctx.state;
1723
1767
  const { model } = ctx.params;
1724
- const documentMetadata2 = getService$1("document-metadata");
1725
1768
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1726
1769
  const [totalEntries, document] = await Promise.all([
1727
1770
  strapi.db.query(model).count(),
@@ -1729,7 +1772,7 @@ const collectionTypes = {
1729
1772
  ]);
1730
1773
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
1731
1774
  ctx.status = 201;
1732
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1775
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1733
1776
  // Empty metadata as it's not relevant for a new document
1734
1777
  availableLocales: false,
1735
1778
  availableStatus: false
@@ -1743,25 +1786,23 @@ const collectionTypes = {
1743
1786
  async update(ctx) {
1744
1787
  const { userAbility } = ctx.state;
1745
1788
  const { model } = ctx.params;
1746
- const documentMetadata2 = getService$1("document-metadata");
1747
1789
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1748
1790
  const updatedVersion = await updateDocument(ctx);
1749
1791
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1750
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedVersion);
1792
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
1751
1793
  },
1752
1794
  async clone(ctx) {
1753
1795
  const { userAbility, user } = ctx.state;
1754
1796
  const { model, sourceId: id } = ctx.params;
1755
1797
  const { body } = ctx.request;
1756
1798
  const documentManager2 = getService$1("document-manager");
1757
- const documentMetadata2 = getService$1("document-metadata");
1758
1799
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1759
1800
  if (permissionChecker2.cannot.create()) {
1760
1801
  return ctx.forbidden();
1761
1802
  }
1762
1803
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1763
1804
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1764
- const { locale } = getDocumentLocaleAndStatus(body);
1805
+ const { locale } = await getDocumentLocaleAndStatus(body);
1765
1806
  const document = await documentManager2.findOne(id, model, {
1766
1807
  populate,
1767
1808
  locale,
@@ -1777,7 +1818,7 @@ const collectionTypes = {
1777
1818
  const sanitizedBody = await sanitizeFn(body);
1778
1819
  const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
1779
1820
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
1780
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1821
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1781
1822
  // Empty metadata as it's not relevant for a new document
1782
1823
  availableLocales: false,
1783
1824
  availableStatus: false
@@ -1806,7 +1847,7 @@ const collectionTypes = {
1806
1847
  }
1807
1848
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1808
1849
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1809
- const { locale } = getDocumentLocaleAndStatus(ctx.query);
1850
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query);
1810
1851
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1811
1852
  if (documentLocales.length === 0) {
1812
1853
  return ctx.notFound();
@@ -1828,7 +1869,6 @@ const collectionTypes = {
1828
1869
  const { id, model } = ctx.params;
1829
1870
  const { body } = ctx.request;
1830
1871
  const documentManager2 = getService$1("document-manager");
1831
- const documentMetadata2 = getService$1("document-metadata");
1832
1872
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1833
1873
  if (permissionChecker2.cannot.publish()) {
1834
1874
  return ctx.forbidden();
@@ -1840,21 +1880,25 @@ const collectionTypes = {
1840
1880
  if (permissionChecker2.cannot.publish(document)) {
1841
1881
  throw new strapiUtils.errors.ForbiddenError();
1842
1882
  }
1843
- const { locale } = getDocumentLocaleAndStatus(body);
1844
- return documentManager2.publish(document.documentId, model, {
1883
+ const { locale } = await getDocumentLocaleAndStatus(body);
1884
+ const publishResult = await documentManager2.publish(document.documentId, model, {
1845
1885
  locale
1846
1886
  // TODO: Allow setting creator fields on publish
1847
1887
  // data: setCreatorFields({ user, isEdition: true })({}),
1848
1888
  });
1889
+ if (!publishResult || publishResult.length === 0) {
1890
+ throw new strapiUtils.errors.NotFoundError("Document not found or already published.");
1891
+ }
1892
+ return publishResult[0];
1849
1893
  });
1850
1894
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
1851
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1895
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1852
1896
  },
1853
1897
  async bulkPublish(ctx) {
1854
1898
  const { userAbility } = ctx.state;
1855
1899
  const { model } = ctx.params;
1856
1900
  const { body } = ctx.request;
1857
- const { ids } = body;
1901
+ const { documentIds } = body;
1858
1902
  await validateBulkActionInput(body);
1859
1903
  const documentManager2 = getService$1("document-manager");
1860
1904
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1863,8 +1907,11 @@ const collectionTypes = {
1863
1907
  }
1864
1908
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1865
1909
  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);
1910
+ const { locale } = await getDocumentLocaleAndStatus(body, { allowMultipleLocales: true });
1911
+ const entityPromises = documentIds.map(
1912
+ (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
1913
+ );
1914
+ const entities = (await Promise.all(entityPromises)).flat();
1868
1915
  for (const entity of entities) {
1869
1916
  if (!entity) {
1870
1917
  return ctx.notFound();
@@ -1873,24 +1920,25 @@ const collectionTypes = {
1873
1920
  return ctx.forbidden();
1874
1921
  }
1875
1922
  }
1876
- const { count } = await documentManager2.publishMany(entities, model);
1923
+ const count = await documentManager2.publishMany(model, documentIds, locale);
1877
1924
  ctx.body = { count };
1878
1925
  },
1879
1926
  async bulkUnpublish(ctx) {
1880
1927
  const { userAbility } = ctx.state;
1881
1928
  const { model } = ctx.params;
1882
1929
  const { body } = ctx.request;
1883
- const { ids } = body;
1930
+ const { documentIds } = body;
1884
1931
  await validateBulkActionInput(body);
1885
1932
  const documentManager2 = getService$1("document-manager");
1886
1933
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1887
1934
  if (permissionChecker2.cannot.unpublish()) {
1888
1935
  return ctx.forbidden();
1889
1936
  }
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);
1937
+ const { locale } = await getDocumentLocaleAndStatus(body);
1938
+ const entityPromises = documentIds.map(
1939
+ (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
1940
+ );
1941
+ const entities = (await Promise.all(entityPromises)).flat();
1894
1942
  for (const entity of entities) {
1895
1943
  if (!entity) {
1896
1944
  return ctx.notFound();
@@ -1899,7 +1947,8 @@ const collectionTypes = {
1899
1947
  return ctx.forbidden();
1900
1948
  }
1901
1949
  }
1902
- const { count } = await documentManager2.unpublishMany(entities, model);
1950
+ const entitiesIds = entities.map((document) => document.documentId);
1951
+ const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
1903
1952
  ctx.body = { count };
1904
1953
  },
1905
1954
  async unpublish(ctx) {
@@ -1909,7 +1958,6 @@ const collectionTypes = {
1909
1958
  body: { discardDraft, ...body }
1910
1959
  } = ctx.request;
1911
1960
  const documentManager2 = getService$1("document-manager");
1912
- const documentMetadata2 = getService$1("document-metadata");
1913
1961
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1914
1962
  if (permissionChecker2.cannot.unpublish()) {
1915
1963
  return ctx.forbidden();
@@ -1919,7 +1967,7 @@ const collectionTypes = {
1919
1967
  }
1920
1968
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1921
1969
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1922
- const { locale } = getDocumentLocaleAndStatus(body);
1970
+ const { locale } = await getDocumentLocaleAndStatus(body);
1923
1971
  const document = await documentManager2.findOne(id, model, {
1924
1972
  populate,
1925
1973
  locale,
@@ -1941,7 +1989,7 @@ const collectionTypes = {
1941
1989
  ctx.body = await strapiUtils.async.pipe(
1942
1990
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
1943
1991
  permissionChecker2.sanitizeOutput,
1944
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
1992
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1945
1993
  )(document);
1946
1994
  });
1947
1995
  },
@@ -1950,14 +1998,13 @@ const collectionTypes = {
1950
1998
  const { id, model } = ctx.params;
1951
1999
  const { body } = ctx.request;
1952
2000
  const documentManager2 = getService$1("document-manager");
1953
- const documentMetadata2 = getService$1("document-metadata");
1954
2001
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1955
2002
  if (permissionChecker2.cannot.discard()) {
1956
2003
  return ctx.forbidden();
1957
2004
  }
1958
2005
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
1959
2006
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1960
- const { locale } = getDocumentLocaleAndStatus(body);
2007
+ const { locale } = await getDocumentLocaleAndStatus(body);
1961
2008
  const document = await documentManager2.findOne(id, model, {
1962
2009
  populate,
1963
2010
  locale,
@@ -1972,14 +2019,14 @@ const collectionTypes = {
1972
2019
  ctx.body = await strapiUtils.async.pipe(
1973
2020
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
1974
2021
  permissionChecker2.sanitizeOutput,
1975
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2022
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1976
2023
  )(document);
1977
2024
  },
1978
2025
  async bulkDelete(ctx) {
1979
2026
  const { userAbility } = ctx.state;
1980
2027
  const { model } = ctx.params;
1981
2028
  const { query, body } = ctx.request;
1982
- const { ids } = body;
2029
+ const { documentIds } = body;
1983
2030
  await validateBulkActionInput(body);
1984
2031
  const documentManager2 = getService$1("document-manager");
1985
2032
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1987,14 +2034,22 @@ const collectionTypes = {
1987
2034
  return ctx.forbidden();
1988
2035
  }
1989
2036
  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 || [])
2037
+ const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2038
+ const { locale } = await getDocumentLocaleAndStatus(body);
2039
+ const documentLocales = await documentManager2.findLocales(documentIds, model, {
2040
+ populate,
2041
+ locale
2042
+ });
2043
+ if (documentLocales.length === 0) {
2044
+ return ctx.notFound();
2045
+ }
2046
+ for (const document of documentLocales) {
2047
+ if (permissionChecker2.cannot.delete(document)) {
2048
+ return ctx.forbidden();
1995
2049
  }
1996
- };
1997
- const { count } = await documentManager2.deleteMany(params, model);
2050
+ }
2051
+ const localeDocumentsIds = documentLocales.map((document) => document.documentId);
2052
+ const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
1998
2053
  ctx.body = { count };
1999
2054
  },
2000
2055
  async countDraftRelations(ctx) {
@@ -2007,7 +2062,7 @@ const collectionTypes = {
2007
2062
  }
2008
2063
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2009
2064
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2010
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
2065
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
2011
2066
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
2012
2067
  if (!entity) {
2013
2068
  return ctx.notFound();
@@ -2022,7 +2077,7 @@ const collectionTypes = {
2022
2077
  },
2023
2078
  async countManyEntriesDraftRelations(ctx) {
2024
2079
  const { userAbility } = ctx.state;
2025
- const ids = ctx.request.query.ids;
2080
+ const ids = ctx.request.query.documentIds;
2026
2081
  const locale = ctx.request.query.locale;
2027
2082
  const { model } = ctx.params;
2028
2083
  const documentManager2 = getService$1("document-manager");
@@ -2033,7 +2088,7 @@ const collectionTypes = {
2033
2088
  const entities = await documentManager2.findMany(
2034
2089
  {
2035
2090
  filters: {
2036
- id: ids
2091
+ documentId: ids
2037
2092
  },
2038
2093
  locale
2039
2094
  },
@@ -2535,7 +2590,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
2535
2590
  throw new strapiUtils.errors.ForbiddenError();
2536
2591
  }
2537
2592
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2538
- const { locale } = getDocumentLocaleAndStatus(body);
2593
+ const { locale } = await getDocumentLocaleAndStatus(body);
2539
2594
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2540
2595
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2541
2596
  // Find the first document to check if it exists
@@ -2572,12 +2627,11 @@ const singleTypes = {
2572
2627
  const { model } = ctx.params;
2573
2628
  const { query = {} } = ctx.request;
2574
2629
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2575
- const documentMetadata2 = getService$1("document-metadata");
2576
2630
  if (permissionChecker2.cannot.read()) {
2577
2631
  return ctx.forbidden();
2578
2632
  }
2579
2633
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2580
- const { locale, status } = getDocumentLocaleAndStatus(query);
2634
+ const { locale, status } = await getDocumentLocaleAndStatus(query);
2581
2635
  const version = await findDocument(permissionQuery, model, { locale, status });
2582
2636
  if (!version) {
2583
2637
  if (permissionChecker2.cannot.create()) {
@@ -2587,8 +2641,10 @@ const singleTypes = {
2587
2641
  if (!document) {
2588
2642
  return ctx.notFound();
2589
2643
  }
2590
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
2644
+ const { meta } = await formatDocumentWithMetadata(
2645
+ permissionChecker2,
2591
2646
  model,
2647
+ // @ts-expect-error - fix types
2592
2648
  { id: document.documentId, locale, publishedAt: null },
2593
2649
  { availableLocales: true, availableStatus: false }
2594
2650
  );
@@ -2599,16 +2655,15 @@ const singleTypes = {
2599
2655
  return ctx.forbidden();
2600
2656
  }
2601
2657
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
2602
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2658
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2603
2659
  },
2604
2660
  async createOrUpdate(ctx) {
2605
2661
  const { userAbility } = ctx.state;
2606
2662
  const { model } = ctx.params;
2607
- const documentMetadata2 = getService$1("document-metadata");
2608
2663
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2609
2664
  const document = await createOrUpdateDocument(ctx);
2610
2665
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2611
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2666
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2612
2667
  },
2613
2668
  async delete(ctx) {
2614
2669
  const { userAbility } = ctx.state;
@@ -2621,7 +2676,7 @@ const singleTypes = {
2621
2676
  }
2622
2677
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2623
2678
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2624
- const { locale } = getDocumentLocaleAndStatus(query);
2679
+ const { locale } = await getDocumentLocaleAndStatus(query);
2625
2680
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2626
2681
  populate,
2627
2682
  locale
@@ -2644,7 +2699,6 @@ const singleTypes = {
2644
2699
  const { model } = ctx.params;
2645
2700
  const { query = {} } = ctx.request;
2646
2701
  const documentManager2 = getService$1("document-manager");
2647
- const documentMetadata2 = getService$1("document-metadata");
2648
2702
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2649
2703
  if (permissionChecker2.cannot.publish()) {
2650
2704
  return ctx.forbidden();
@@ -2659,11 +2713,12 @@ const singleTypes = {
2659
2713
  if (permissionChecker2.cannot.publish(document)) {
2660
2714
  throw new strapiUtils.errors.ForbiddenError();
2661
2715
  }
2662
- const { locale } = getDocumentLocaleAndStatus(document);
2663
- return documentManager2.publish(document.documentId, model, { locale });
2716
+ const { locale } = await getDocumentLocaleAndStatus(document);
2717
+ const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2718
+ return publishResult.at(0);
2664
2719
  });
2665
2720
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
2666
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2721
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2667
2722
  },
2668
2723
  async unpublish(ctx) {
2669
2724
  const { userAbility } = ctx.state;
@@ -2673,7 +2728,6 @@ const singleTypes = {
2673
2728
  query = {}
2674
2729
  } = ctx.request;
2675
2730
  const documentManager2 = getService$1("document-manager");
2676
- const documentMetadata2 = getService$1("document-metadata");
2677
2731
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2678
2732
  if (permissionChecker2.cannot.unpublish()) {
2679
2733
  return ctx.forbidden();
@@ -2682,7 +2736,7 @@ const singleTypes = {
2682
2736
  return ctx.forbidden();
2683
2737
  }
2684
2738
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2685
- const { locale } = getDocumentLocaleAndStatus(body);
2739
+ const { locale } = await getDocumentLocaleAndStatus(body);
2686
2740
  const document = await findDocument(sanitizedQuery, model, { locale });
2687
2741
  if (!document) {
2688
2742
  return ctx.notFound();
@@ -2700,7 +2754,7 @@ const singleTypes = {
2700
2754
  ctx.body = await strapiUtils.async.pipe(
2701
2755
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
2702
2756
  permissionChecker2.sanitizeOutput,
2703
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2757
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2704
2758
  )(document);
2705
2759
  });
2706
2760
  },
@@ -2709,13 +2763,12 @@ const singleTypes = {
2709
2763
  const { model } = ctx.params;
2710
2764
  const { body, query = {} } = ctx.request;
2711
2765
  const documentManager2 = getService$1("document-manager");
2712
- const documentMetadata2 = getService$1("document-metadata");
2713
2766
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2714
2767
  if (permissionChecker2.cannot.discard()) {
2715
2768
  return ctx.forbidden();
2716
2769
  }
2717
2770
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2718
- const { locale } = getDocumentLocaleAndStatus(body);
2771
+ const { locale } = await getDocumentLocaleAndStatus(body);
2719
2772
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2720
2773
  if (!document) {
2721
2774
  return ctx.notFound();
@@ -2726,7 +2779,7 @@ const singleTypes = {
2726
2779
  ctx.body = await strapiUtils.async.pipe(
2727
2780
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
2728
2781
  permissionChecker2.sanitizeOutput,
2729
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2782
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2730
2783
  )(document);
2731
2784
  },
2732
2785
  async countDraftRelations(ctx) {
@@ -2735,7 +2788,7 @@ const singleTypes = {
2735
2788
  const { query } = ctx.request;
2736
2789
  const documentManager2 = getService$1("document-manager");
2737
2790
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2738
- const { locale } = getDocumentLocaleAndStatus(query);
2791
+ const { locale } = await getDocumentLocaleAndStatus(query);
2739
2792
  if (permissionChecker2.cannot.read()) {
2740
2793
  return ctx.forbidden();
2741
2794
  }
@@ -2756,7 +2809,7 @@ const uid$1 = {
2756
2809
  async generateUID(ctx) {
2757
2810
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2758
2811
  const { query = {} } = ctx.request;
2759
- const { locale } = getDocumentLocaleAndStatus(query);
2812
+ const { locale } = await getDocumentLocaleAndStatus(query);
2760
2813
  await validateUIDField(contentTypeUID, field);
2761
2814
  const uidService = getService$1("uid");
2762
2815
  ctx.body = {
@@ -2768,7 +2821,7 @@ const uid$1 = {
2768
2821
  ctx.request.body
2769
2822
  );
2770
2823
  const { query = {} } = ctx.request;
2771
- const { locale } = getDocumentLocaleAndStatus(query);
2824
+ const { locale } = await getDocumentLocaleAndStatus(query);
2772
2825
  await validateUIDField(contentTypeUID, field);
2773
2826
  const uidService = getService$1("uid");
2774
2827
  const isAvailable = await uidService.checkUIDAvailability({
@@ -3559,7 +3612,7 @@ const permission = ({ strapi: strapi2 }) => ({
3559
3612
  await strapi2.service("admin::permission").actionProvider.registerMany(actions);
3560
3613
  }
3561
3614
  });
3562
- const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils__default.default.contentTypes;
3615
+ const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils__default.default.contentTypes;
3563
3616
  const { isAnyToMany } = strapiUtils__default.default.relations;
3564
3617
  const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils__default.default.contentTypes.constants;
3565
3618
  const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
@@ -3650,6 +3703,42 @@ const getDeepPopulate = (uid2, {
3650
3703
  {}
3651
3704
  );
3652
3705
  };
3706
+ const getValidatableFieldsPopulate = (uid2, {
3707
+ initialPopulate = {},
3708
+ countMany = false,
3709
+ countOne = false,
3710
+ maxLevel = Infinity
3711
+ } = {}, level = 1) => {
3712
+ if (level > maxLevel) {
3713
+ return {};
3714
+ }
3715
+ const model = strapi.getModel(uid2);
3716
+ return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
3717
+ if (!getDoesAttributeRequireValidation(attribute)) {
3718
+ return populateAcc;
3719
+ }
3720
+ if (isScalarAttribute(attribute)) {
3721
+ return fp.merge(populateAcc, {
3722
+ [attributeName]: true
3723
+ });
3724
+ }
3725
+ return fp.merge(
3726
+ populateAcc,
3727
+ getPopulateFor(
3728
+ attributeName,
3729
+ model,
3730
+ {
3731
+ // @ts-expect-error - improve types
3732
+ initialPopulate: initialPopulate?.[attributeName],
3733
+ countMany,
3734
+ countOne,
3735
+ maxLevel
3736
+ },
3737
+ level
3738
+ )
3739
+ );
3740
+ }, {});
3741
+ };
3653
3742
  const getDeepPopulateDraftCount = (uid2) => {
3654
3743
  const model = strapi.getModel(uid2);
3655
3744
  let hasRelations = false;
@@ -3671,22 +3760,24 @@ const getDeepPopulateDraftCount = (uid2) => {
3671
3760
  attribute.component
3672
3761
  );
3673
3762
  if (childHasRelations) {
3674
- populateAcc[attributeName] = { populate: populate2 };
3763
+ populateAcc[attributeName] = {
3764
+ populate: populate2
3765
+ };
3675
3766
  hasRelations = true;
3676
3767
  }
3677
3768
  break;
3678
3769
  }
3679
3770
  case "dynamiczone": {
3680
- const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
3681
- const { populate: populate2, hasRelations: childHasRelations } = getDeepPopulateDraftCount(componentUID);
3682
- if (childHasRelations) {
3771
+ const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
3772
+ const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
3773
+ if (componentHasRelations) {
3683
3774
  hasRelations = true;
3684
- return fp.merge(acc, populate2);
3775
+ return { ...acc, [componentUID]: { populate: componentPopulate } };
3685
3776
  }
3686
3777
  return acc;
3687
3778
  }, {});
3688
- if (!fp.isEmpty(dzPopulate)) {
3689
- populateAcc[attributeName] = { populate: dzPopulate };
3779
+ if (!fp.isEmpty(dzPopulateFragment)) {
3780
+ populateAcc[attributeName] = { on: dzPopulateFragment };
3690
3781
  }
3691
3782
  break;
3692
3783
  }
@@ -3878,41 +3969,70 @@ const AVAILABLE_STATUS_FIELDS = [
3878
3969
  "updatedBy",
3879
3970
  "status"
3880
3971
  ];
3881
- const AVAILABLE_LOCALES_FIELDS = ["id", "locale", "updatedAt", "createdAt", "status"];
3972
+ const AVAILABLE_LOCALES_FIELDS = [
3973
+ "id",
3974
+ "locale",
3975
+ "updatedAt",
3976
+ "createdAt",
3977
+ "status",
3978
+ "publishedAt",
3979
+ "documentId"
3980
+ ];
3882
3981
  const CONTENT_MANAGER_STATUS = {
3883
3982
  PUBLISHED: "published",
3884
3983
  DRAFT: "draft",
3885
3984
  MODIFIED: "modified"
3886
3985
  };
3887
- const areDatesEqual = (date1, date2, threshold) => {
3888
- if (!date1 || !date2) {
3986
+ const getIsVersionLatestModification = (version, otherVersion) => {
3987
+ if (!version || !version.updatedAt) {
3889
3988
  return false;
3890
3989
  }
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;
3990
+ const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
3991
+ const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
3992
+ return versionUpdatedAt > otherUpdatedAt;
3895
3993
  };
3896
3994
  const documentMetadata = ({ strapi: strapi2 }) => ({
3897
3995
  /**
3898
3996
  * Returns available locales of a document for the current status
3899
3997
  */
3900
- getAvailableLocales(uid2, version, allVersions) {
3998
+ async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
3901
3999
  const versionsByLocale = fp.groupBy("locale", allVersions);
3902
4000
  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]);
4001
+ const model = strapi2.getModel(uid2);
4002
+ const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
4003
+ const traversalFunction = async (localeVersion) => strapiUtils.traverseEntity(
4004
+ ({ key }, { remove }) => {
4005
+ if (keysToKeep.includes(key)) {
4006
+ return;
4007
+ }
4008
+ remove(key);
4009
+ },
4010
+ { schema: model, getModel: strapi2.getModel.bind(strapi2) },
4011
+ // @ts-expect-error fix types DocumentVersion incompatible with Data
4012
+ localeVersion
4013
+ );
4014
+ const mappingResult = await strapiUtils.async.map(
4015
+ Object.values(versionsByLocale),
4016
+ async (localeVersions) => {
4017
+ const mappedLocaleVersions = await strapiUtils.async.map(
4018
+ localeVersions,
4019
+ traversalFunction
4020
+ );
4021
+ if (!strapiUtils.contentTypes.hasDraftAndPublish(model)) {
4022
+ return mappedLocaleVersions[0];
4023
+ }
4024
+ const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
4025
+ const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
4026
+ if (!draftVersion) {
4027
+ return;
4028
+ }
4029
+ return {
4030
+ ...draftVersion,
4031
+ status: this.getStatus(draftVersion, otherVersions)
4032
+ };
3906
4033
  }
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);
4034
+ );
4035
+ return mappingResult.filter(Boolean);
3916
4036
  },
3917
4037
  /**
3918
4038
  * Returns available status of a document for the current locale
@@ -3950,26 +4070,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3950
4070
  });
3951
4071
  },
3952
4072
  getStatus(version, otherDocumentStatuses) {
3953
- const isDraft = version.publishedAt === null;
3954
- if (!otherDocumentStatuses?.length) {
3955
- return isDraft ? CONTENT_MANAGER_STATUS.DRAFT : CONTENT_MANAGER_STATUS.PUBLISHED;
4073
+ let draftVersion;
4074
+ let publishedVersion;
4075
+ if (version.publishedAt) {
4076
+ publishedVersion = version;
4077
+ } else {
4078
+ draftVersion = version;
3956
4079
  }
3957
- if (isDraft) {
3958
- const publishedVersion = otherDocumentStatuses?.find((d) => d.publishedAt !== null);
3959
- if (!publishedVersion) {
3960
- return CONTENT_MANAGER_STATUS.DRAFT;
3961
- }
4080
+ const otherVersion = otherDocumentStatuses?.at(0);
4081
+ if (otherVersion?.publishedAt) {
4082
+ publishedVersion = otherVersion;
4083
+ } else if (otherVersion) {
4084
+ draftVersion = otherVersion;
3962
4085
  }
3963
- if (areDatesEqual(version.updatedAt, otherDocumentStatuses.at(0)?.updatedAt, 500)) {
4086
+ if (!draftVersion)
3964
4087
  return CONTENT_MANAGER_STATUS.PUBLISHED;
3965
- }
3966
- return CONTENT_MANAGER_STATUS.MODIFIED;
4088
+ if (!publishedVersion)
4089
+ return CONTENT_MANAGER_STATUS.DRAFT;
4090
+ const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
4091
+ return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
3967
4092
  },
4093
+ // TODO is it necessary to return metadata on every page of the CM
4094
+ // We could refactor this so the locales are only loaded when they're
4095
+ // needed. e.g. in the bulk locale action modal.
3968
4096
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4097
+ const populate = getValidatableFieldsPopulate(uid2);
3969
4098
  const versions = await strapi2.db.query(uid2).findMany({
3970
4099
  where: { documentId: version.documentId },
3971
- select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
3972
4100
  populate: {
4101
+ // Populate only fields that require validation for bulk locale actions
4102
+ ...populate,
4103
+ // NOTE: creator fields are selected in this way to avoid exposing sensitive data
3973
4104
  createdBy: {
3974
4105
  select: ["id", "firstname", "lastname", "email"]
3975
4106
  },
@@ -3978,7 +4109,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3978
4109
  }
3979
4110
  }
3980
4111
  });
3981
- const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
4112
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
3982
4113
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
3983
4114
  return {
3984
4115
  availableLocales: availableLocalesResult,
@@ -3991,8 +4122,9 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3991
4122
  * - Available status of the document for the current locale
3992
4123
  */
3993
4124
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
3994
- if (!document)
4125
+ if (!document) {
3995
4126
  return document;
4127
+ }
3996
4128
  const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
3997
4129
  if (!hasDraftAndPublish) {
3998
4130
  opts.availableStatus = false;
@@ -4042,26 +4174,9 @@ const sumDraftCounts = (entity, uid2) => {
4042
4174
  }, 0);
4043
4175
  };
4044
4176
  const { ApplicationError } = strapiUtils.errors;
4045
- const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
4046
4177
  const { PUBLISHED_AT_ATTRIBUTE } = strapiUtils.contentTypes.constants;
4047
4178
  const omitPublishedAtField = fp.omit(PUBLISHED_AT_ATTRIBUTE);
4048
4179
  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
4180
  const documentManager = ({ strapi: strapi2 }) => {
4066
4181
  return {
4067
4182
  async findOne(id, uid2, opts = {}) {
@@ -4080,6 +4195,9 @@ const documentManager = ({ strapi: strapi2 }) => {
4080
4195
  } else if (opts.locale && opts.locale !== "*") {
4081
4196
  where.locale = opts.locale;
4082
4197
  }
4198
+ if (typeof opts.isPublished === "boolean") {
4199
+ where.publishedAt = { $notNull: opts.isPublished };
4200
+ }
4083
4201
  return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
4084
4202
  },
4085
4203
  async findMany(opts, uid2) {
@@ -4142,70 +4260,36 @@ const documentManager = ({ strapi: strapi2 }) => {
4142
4260
  return {};
4143
4261
  },
4144
4262
  // FIXME: handle relations
4145
- async deleteMany(opts, uid2) {
4146
- const docs = await strapi2.documents(uid2).findMany(opts);
4147
- for (const doc of docs) {
4148
- await strapi2.documents(uid2).delete({ documentId: doc.documentId });
4149
- }
4150
- return { count: docs.length };
4263
+ async deleteMany(documentIds, uid2, opts = {}) {
4264
+ const deletedEntries = await strapi2.db.transaction(async () => {
4265
+ return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
4266
+ });
4267
+ return { count: deletedEntries.length };
4151
4268
  },
4152
4269
  async publish(id, uid2, opts = {}) {
4153
4270
  const populate = await buildDeepPopulate(uid2);
4154
4271
  const params = { ...opts, populate };
4155
- return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries.at(0));
4272
+ return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
4156
4273
  },
4157
- async publishMany(entities, uid2) {
4158
- if (!entities.length) {
4159
- return null;
4160
- }
4161
- await Promise.all(
4162
- entities.map((document) => {
4163
- return strapi2.entityValidator.validateEntityCreation(
4164
- strapi2.getModel(uid2),
4165
- document,
4166
- void 0,
4167
- // @ts-expect-error - FIXME: entity here is unnecessary
4168
- document
4169
- );
4170
- })
4171
- );
4172
- const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4173
- const filters = { id: { $in: entitiesToPublish } };
4174
- const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
4175
- const populate = await buildDeepPopulate(uid2);
4176
- const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4177
- where: filters,
4178
- data
4179
- });
4180
- const publishedEntities = await strapi2.db.query(uid2).findMany({
4181
- where: filters,
4182
- populate
4274
+ async publishMany(uid2, documentIds, locale) {
4275
+ return strapi2.db.transaction(async () => {
4276
+ const results = await Promise.all(
4277
+ documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
4278
+ );
4279
+ const publishedEntitiesCount = results.flat().filter(Boolean).length;
4280
+ return publishedEntitiesCount;
4183
4281
  });
4184
- await Promise.all(
4185
- publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
4186
- );
4187
- return publishedEntitiesCount;
4188
4282
  },
4189
- async unpublishMany(documents, uid2) {
4190
- if (!documents.length) {
4191
- return null;
4192
- }
4193
- const entitiesToUnpublish = documents.filter((doc) => doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4194
- const filters = { id: { $in: entitiesToUnpublish } };
4195
- const data = { [PUBLISHED_AT_ATTRIBUTE]: null };
4196
- const populate = await buildDeepPopulate(uid2);
4197
- const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4198
- where: filters,
4199
- data
4200
- });
4201
- const unpublishedEntities = await strapi2.db.query(uid2).findMany({
4202
- where: filters,
4203
- populate
4283
+ async unpublishMany(documentIds, uid2, opts = {}) {
4284
+ const unpublishedEntries = await strapi2.db.transaction(async () => {
4285
+ return Promise.all(
4286
+ documentIds.map(
4287
+ (id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
4288
+ )
4289
+ );
4204
4290
  });
4205
- await Promise.all(
4206
- unpublishedEntities.map((doc) => emitEvent(uid2, ENTRY_UNPUBLISH, doc))
4207
- );
4208
- return unpublishedEntitiesCount;
4291
+ const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
4292
+ return { count: unpublishedEntitiesCount };
4209
4293
  },
4210
4294
  async unpublish(id, uid2, opts = {}) {
4211
4295
  const populate = await buildDeepPopulate(uid2);
@@ -4230,16 +4314,20 @@ const documentManager = ({ strapi: strapi2 }) => {
4230
4314
  }
4231
4315
  return sumDraftCounts(document, uid2);
4232
4316
  },
4233
- async countManyEntriesDraftRelations(ids, uid2, locale) {
4317
+ async countManyEntriesDraftRelations(documentIds, uid2, locale) {
4234
4318
  const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
4235
4319
  if (!hasRelations) {
4236
4320
  return 0;
4237
4321
  }
4322
+ let localeFilter = {};
4323
+ if (locale) {
4324
+ localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
4325
+ }
4238
4326
  const entities = await strapi2.db.query(uid2).findMany({
4239
4327
  populate,
4240
4328
  where: {
4241
- id: { $in: ids },
4242
- ...locale ? { locale } : {}
4329
+ documentId: { $in: documentIds },
4330
+ ...localeFilter
4243
4331
  }
4244
4332
  });
4245
4333
  const totalNumberDraftRelations = entities.reduce(