@strapi/content-manager 5.0.0-beta.7 → 5.0.0-beta.8

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 (143) hide show
  1. package/dist/_chunks/{ComponentConfigurationPage-uTMkLI60.mjs → ComponentConfigurationPage-CuWgXugY.mjs} +3 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-uTMkLI60.mjs.map → ComponentConfigurationPage-CuWgXugY.mjs.map} +1 -1
  3. package/dist/_chunks/{ComponentConfigurationPage-DMq0wvcL.js → ComponentConfigurationPage-by0e_kNd.js} +3 -3
  4. package/dist/_chunks/{ComponentConfigurationPage-DMq0wvcL.js.map → ComponentConfigurationPage-by0e_kNd.js.map} +1 -1
  5. package/dist/_chunks/{ComponentIcon-BBQsYCVn.js → ComponentIcon-BXdiCGQp.js} +8 -2
  6. package/dist/_chunks/ComponentIcon-BXdiCGQp.js.map +1 -0
  7. package/dist/_chunks/{ComponentIcon-BOFnK76n.mjs → ComponentIcon-u4bIXTFY.mjs} +9 -3
  8. package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -0
  9. package/dist/_chunks/{EditConfigurationPage-BFpQwwbc.js → EditConfigurationPage-CqBeCPGH.js} +3 -3
  10. package/dist/_chunks/{EditConfigurationPage-BFpQwwbc.js.map → EditConfigurationPage-CqBeCPGH.js.map} +1 -1
  11. package/dist/_chunks/{EditConfigurationPage-B2HhCh-b.mjs → EditConfigurationPage-DbI4KMyz.mjs} +3 -3
  12. package/dist/_chunks/{EditConfigurationPage-B2HhCh-b.mjs.map → EditConfigurationPage-DbI4KMyz.mjs.map} +1 -1
  13. package/dist/_chunks/{EditViewPage-CXXue16T.js → EditViewPage-ChgloMyO.js} +5 -5
  14. package/dist/_chunks/{EditViewPage-CXXue16T.js.map → EditViewPage-ChgloMyO.js.map} +1 -1
  15. package/dist/_chunks/{EditViewPage-BVIrgjyG.mjs → EditViewPage-dFPBya9U.mjs} +5 -5
  16. package/dist/_chunks/{EditViewPage-BVIrgjyG.mjs.map → EditViewPage-dFPBya9U.mjs.map} +1 -1
  17. package/dist/_chunks/{Field-0_2h1vuK.mjs → Field-C1nUKcdS.mjs} +240 -357
  18. package/dist/_chunks/Field-C1nUKcdS.mjs.map +1 -0
  19. package/dist/_chunks/{Field-ZgzKlgxR.js → Field-dLk-vgLL.js} +240 -357
  20. package/dist/_chunks/Field-dLk-vgLL.js.map +1 -0
  21. package/dist/_chunks/{Form-DgTc2qkx.js → Form-CbXtmHC_.js} +9 -6
  22. package/dist/_chunks/Form-CbXtmHC_.js.map +1 -0
  23. package/dist/_chunks/{Form-B7TUnQDd.mjs → Form-DOlpi7Js.mjs} +9 -6
  24. package/dist/_chunks/Form-DOlpi7Js.mjs.map +1 -0
  25. package/dist/_chunks/{History-Dug_4HIA.mjs → History-BFNUAiGc.mjs} +8 -8
  26. package/dist/_chunks/{History-Dug_4HIA.mjs.map → History-BFNUAiGc.mjs.map} +1 -1
  27. package/dist/_chunks/{History-DtHjQuqM.js → History-BjDfohBr.js} +8 -8
  28. package/dist/_chunks/{History-DtHjQuqM.js.map → History-BjDfohBr.js.map} +1 -1
  29. package/dist/_chunks/{ListConfigurationPage-CmEeNg6T.mjs → ListConfigurationPage-DDi0KqFm.mjs} +2 -2
  30. package/dist/_chunks/{ListConfigurationPage-CmEeNg6T.mjs.map → ListConfigurationPage-DDi0KqFm.mjs.map} +1 -1
  31. package/dist/_chunks/{ListConfigurationPage-BuSdTjfa.js → ListConfigurationPage-IQBgWTaa.js} +2 -2
  32. package/dist/_chunks/{ListConfigurationPage-BuSdTjfa.js.map → ListConfigurationPage-IQBgWTaa.js.map} +1 -1
  33. package/dist/_chunks/{ListViewPage-Dsoa3wEA.mjs → ListViewPage-BPjljUsH.mjs} +8 -16
  34. package/dist/_chunks/ListViewPage-BPjljUsH.mjs.map +1 -0
  35. package/dist/_chunks/{ListViewPage-CExWwa4S.js → ListViewPage-CZYGqlvF.js} +11 -18
  36. package/dist/_chunks/ListViewPage-CZYGqlvF.js.map +1 -0
  37. package/dist/_chunks/{NoContentTypePage-DCUL8gVi.js → NoContentTypePage-BOAI6VZ1.js} +2 -2
  38. package/dist/_chunks/{NoContentTypePage-DCUL8gVi.js.map → NoContentTypePage-BOAI6VZ1.js.map} +1 -1
  39. package/dist/_chunks/{NoContentTypePage-Dh38hBXB.mjs → NoContentTypePage-DaWw67K-.mjs} +2 -2
  40. package/dist/_chunks/{NoContentTypePage-Dh38hBXB.mjs.map → NoContentTypePage-DaWw67K-.mjs.map} +1 -1
  41. package/dist/_chunks/{NoPermissionsPage-Dt2O40ey.mjs → NoPermissionsPage-CZrJH00p.mjs} +2 -2
  42. package/dist/_chunks/{NoPermissionsPage-Dt2O40ey.mjs.map → NoPermissionsPage-CZrJH00p.mjs.map} +1 -1
  43. package/dist/_chunks/{NoPermissionsPage-BK-XCpIy.js → NoPermissionsPage-cYEtLc_e.js} +2 -2
  44. package/dist/_chunks/{NoPermissionsPage-BK-XCpIy.js.map → NoPermissionsPage-cYEtLc_e.js.map} +1 -1
  45. package/dist/_chunks/{Relations-DZyjWZHl.mjs → Relations-DTowyge2.mjs} +7 -5
  46. package/dist/_chunks/{Relations-DZyjWZHl.mjs.map → Relations-DTowyge2.mjs.map} +1 -1
  47. package/dist/_chunks/{Relations-CNypkp-g.js → Relations-DU6B7irU.js} +7 -5
  48. package/dist/_chunks/{Relations-CNypkp-g.js.map → Relations-DU6B7irU.js.map} +1 -1
  49. package/dist/_chunks/{en-C-V1_90f.js → en-DTULi5-d.js} +3 -1
  50. package/dist/_chunks/{en-C-V1_90f.js.map → en-DTULi5-d.js.map} +1 -1
  51. package/dist/_chunks/{en-MBPul9Su.mjs → en-GCOTL6jR.mjs} +3 -1
  52. package/dist/_chunks/{en-MBPul9Su.mjs.map → en-GCOTL6jR.mjs.map} +1 -1
  53. package/dist/_chunks/{index-B3c-4it4.mjs → index-BaGHmIir.mjs} +488 -196
  54. package/dist/_chunks/index-BaGHmIir.mjs.map +1 -0
  55. package/dist/_chunks/{index-DFK7LwDW.js → index-CCJeB7Rw.js} +480 -188
  56. package/dist/_chunks/index-CCJeB7Rw.js.map +1 -0
  57. package/dist/_chunks/{layout-B5cm7cZj.mjs → layout-BinjszSQ.mjs} +6 -6
  58. package/dist/_chunks/layout-BinjszSQ.mjs.map +1 -0
  59. package/dist/_chunks/{layout-DLih5-_W.js → layout-ni_L9kT1.js} +6 -6
  60. package/dist/_chunks/layout-ni_L9kT1.js.map +1 -0
  61. package/dist/_chunks/{relations-CTvkuINQ.js → relations-CeJAJc5I.js} +2 -2
  62. package/dist/_chunks/{relations-CTvkuINQ.js.map → relations-CeJAJc5I.js.map} +1 -1
  63. package/dist/_chunks/{relations-BZkrMa2z.mjs → relations-c91ji5eR.mjs} +2 -2
  64. package/dist/_chunks/{relations-BZkrMa2z.mjs.map → relations-c91ji5eR.mjs.map} +1 -1
  65. package/dist/_chunks/usePrev-B9w_-eYc.js +15 -0
  66. package/dist/_chunks/usePrev-B9w_-eYc.js.map +1 -0
  67. package/dist/_chunks/usePrev-DH6iah0A.mjs +16 -0
  68. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +1 -0
  69. package/dist/admin/index.js +2 -1
  70. package/dist/admin/index.js.map +1 -1
  71. package/dist/admin/index.mjs +5 -4
  72. package/dist/admin/src/components/ComponentIcon.d.ts +6 -3
  73. package/dist/admin/src/content-manager.d.ts +3 -3
  74. package/dist/admin/src/exports.d.ts +1 -0
  75. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  76. package/dist/admin/src/hooks/useDocument.d.ts +5 -8
  77. package/dist/admin/src/hooks/useDocumentActions.d.ts +24 -3
  78. package/dist/admin/src/hooks/useDocumentLayout.d.ts +1 -1
  79. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +3 -1
  80. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksInput.d.ts +3 -3
  81. package/dist/admin/src/pages/EditView/components/FormInputs/Component/Input.d.ts +2 -2
  82. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +3 -5
  83. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
  84. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +2 -15
  85. package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
  86. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
  87. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +56 -35
  88. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
  89. package/dist/admin/src/services/api.d.ts +2 -3
  90. package/dist/admin/src/services/components.d.ts +2 -2
  91. package/dist/admin/src/services/contentTypes.d.ts +5 -5
  92. package/dist/admin/src/services/documents.d.ts +29 -17
  93. package/dist/admin/src/services/init.d.ts +2 -2
  94. package/dist/admin/src/services/relations.d.ts +3 -3
  95. package/dist/admin/src/services/uid.d.ts +3 -3
  96. package/dist/admin/src/utils/api.d.ts +4 -17
  97. package/dist/admin/src/utils/validation.d.ts +1 -6
  98. package/dist/server/index.js +232 -116
  99. package/dist/server/index.js.map +1 -1
  100. package/dist/server/index.mjs +234 -118
  101. package/dist/server/index.mjs.map +1 -1
  102. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  103. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  104. package/dist/server/src/controllers/utils/metadata.d.ts +8 -0
  105. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
  106. package/dist/server/src/controllers/validation/dimensions.d.ts +9 -0
  107. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
  108. package/dist/server/src/history/services/history.d.ts.map +1 -1
  109. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  110. package/dist/server/src/index.d.ts +12 -33
  111. package/dist/server/src/index.d.ts.map +1 -1
  112. package/dist/server/src/services/document-manager.d.ts +6 -6
  113. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  114. package/dist/server/src/services/document-metadata.d.ts +8 -29
  115. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  116. package/dist/server/src/services/index.d.ts +12 -33
  117. package/dist/server/src/services/index.d.ts.map +1 -1
  118. package/dist/server/src/services/utils/populate.d.ts +8 -1
  119. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  120. package/dist/shared/contracts/collection-types.d.ts +11 -5
  121. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  122. package/dist/shared/contracts/relations.d.ts +2 -2
  123. package/dist/shared/contracts/relations.d.ts.map +1 -1
  124. package/package.json +11 -11
  125. package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
  126. package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
  127. package/dist/_chunks/Field-0_2h1vuK.mjs.map +0 -1
  128. package/dist/_chunks/Field-ZgzKlgxR.js.map +0 -1
  129. package/dist/_chunks/Form-B7TUnQDd.mjs.map +0 -1
  130. package/dist/_chunks/Form-DgTc2qkx.js.map +0 -1
  131. package/dist/_chunks/ListViewPage-CExWwa4S.js.map +0 -1
  132. package/dist/_chunks/ListViewPage-Dsoa3wEA.mjs.map +0 -1
  133. package/dist/_chunks/index-B3c-4it4.mjs.map +0 -1
  134. package/dist/_chunks/index-DFK7LwDW.js.map +0 -1
  135. package/dist/_chunks/layout-B5cm7cZj.mjs.map +0 -1
  136. package/dist/_chunks/layout-DLih5-_W.js.map +0 -1
  137. package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
  138. package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
  139. package/dist/_chunks/urls-DzZya_gm.js +0 -6
  140. package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
  141. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +0 -31
  142. package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
  143. package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
@@ -518,6 +518,12 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
518
518
  const documentContext = context.action === "create" ? { documentId: result.documentId, locale: context.params?.locale } : { documentId: context.params.documentId, locale: context.params?.locale };
519
519
  const defaultLocale = await serviceUtils.getDefaultLocale();
520
520
  const locale = documentContext.locale || defaultLocale;
521
+ if (Array.isArray(locale)) {
522
+ strapi2.log.warn(
523
+ "[Content manager history middleware]: An array of locales was provided, but only a single locale is supported for the findOne operation."
524
+ );
525
+ return next();
526
+ }
521
527
  const document = await strapi2.documents(contentTypeUid).findOne({
522
528
  documentId: documentContext.documentId,
523
529
  locale,
@@ -1577,15 +1583,47 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1577
1583
  }
1578
1584
  }, body);
1579
1585
  };
1580
- const getDocumentLocaleAndStatus = (request) => {
1586
+ const singleLocaleSchema = strapiUtils.yup.string().nullable();
1587
+ const multipleLocaleSchema = strapiUtils.yup.lazy(
1588
+ (value) => Array.isArray(value) ? strapiUtils.yup.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1589
+ );
1590
+ const statusSchema = strapiUtils.yup.mixed().oneOf(["draft", "published"], "Invalid status");
1591
+ const getDocumentLocaleAndStatus = async (request, opts = { allowMultipleLocales: false }) => {
1592
+ const { allowMultipleLocales } = opts;
1581
1593
  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}`);
1594
+ const schema = strapiUtils.yup.object().shape({
1595
+ locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1596
+ status: statusSchema
1597
+ });
1598
+ try {
1599
+ await strapiUtils.validateYupSchema(schema, { strict: true, abortEarly: false })(request);
1600
+ return { locale, status, ...rest };
1601
+ } catch (error) {
1602
+ throw new strapiUtils.errors.ValidationError(`Validation error: ${error.message}`);
1587
1603
  }
1588
- 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 strapiUtils.async.map(
1613
+ availableLocales,
1614
+ async (localeDocument) => metadataSanitizer(localeDocument)
1615
+ );
1616
+ availableStatus = await strapiUtils.async.map(
1617
+ availableStatus,
1618
+ async (statusDocument) => metadataSanitizer(statusDocument)
1619
+ );
1620
+ return {
1621
+ ...serviceOutput,
1622
+ meta: {
1623
+ availableLocales,
1624
+ availableStatus
1625
+ }
1626
+ };
1589
1627
  };
1590
1628
  const createDocument = async (ctx, opts) => {
1591
1629
  const { userAbility, user } = ctx.state;
@@ -1600,7 +1638,7 @@ const createDocument = async (ctx, opts) => {
1600
1638
  const setCreator = strapiUtils.setCreatorFields({ user });
1601
1639
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1602
1640
  const sanitizedBody = await sanitizeFn(body);
1603
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(body);
1641
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(body);
1604
1642
  return documentManager2.create(model, {
1605
1643
  data: sanitizedBody,
1606
1644
  locale,
@@ -1619,7 +1657,7 @@ const updateDocument = async (ctx, opts) => {
1619
1657
  }
1620
1658
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1621
1659
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1622
- const { locale } = getDocumentLocaleAndStatus(body);
1660
+ const { locale } = await getDocumentLocaleAndStatus(body);
1623
1661
  const [documentVersion, documentExists] = await Promise.all([
1624
1662
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1625
1663
  documentManager2.exists(model, id)
@@ -1657,7 +1695,7 @@ const collectionTypes = {
1657
1695
  }
1658
1696
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1659
1697
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1660
- const { locale, status } = getDocumentLocaleAndStatus(query);
1698
+ const { locale, status } = await getDocumentLocaleAndStatus(query);
1661
1699
  const { results: documents, pagination } = await documentManager2.findPage(
1662
1700
  { ...permissionQuery, populate, locale, status },
1663
1701
  model
@@ -1686,14 +1724,13 @@ const collectionTypes = {
1686
1724
  const { userAbility } = ctx.state;
1687
1725
  const { model, id } = ctx.params;
1688
1726
  const documentManager2 = getService$1("document-manager");
1689
- const documentMetadata2 = getService$1("document-metadata");
1690
1727
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1691
1728
  if (permissionChecker2.cannot.read()) {
1692
1729
  return ctx.forbidden();
1693
1730
  }
1694
1731
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1695
1732
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1696
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
1733
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1697
1734
  const version = await documentManager2.findOne(id, model, {
1698
1735
  populate,
1699
1736
  locale,
@@ -1704,8 +1741,10 @@ const collectionTypes = {
1704
1741
  if (!exists) {
1705
1742
  return ctx.notFound();
1706
1743
  }
1707
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
1744
+ const { meta } = await formatDocumentWithMetadata(
1745
+ permissionChecker2,
1708
1746
  model,
1747
+ // @ts-expect-error TODO: fix
1709
1748
  { id, locale, publishedAt: null },
1710
1749
  { availableLocales: true, availableStatus: false }
1711
1750
  );
@@ -1716,12 +1755,11 @@ const collectionTypes = {
1716
1755
  return ctx.forbidden();
1717
1756
  }
1718
1757
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
1719
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1758
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1720
1759
  },
1721
1760
  async create(ctx) {
1722
1761
  const { userAbility } = ctx.state;
1723
1762
  const { model } = ctx.params;
1724
- const documentMetadata2 = getService$1("document-metadata");
1725
1763
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1726
1764
  const [totalEntries, document] = await Promise.all([
1727
1765
  strapi.db.query(model).count(),
@@ -1729,7 +1767,7 @@ const collectionTypes = {
1729
1767
  ]);
1730
1768
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
1731
1769
  ctx.status = 201;
1732
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1770
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1733
1771
  // Empty metadata as it's not relevant for a new document
1734
1772
  availableLocales: false,
1735
1773
  availableStatus: false
@@ -1743,25 +1781,23 @@ const collectionTypes = {
1743
1781
  async update(ctx) {
1744
1782
  const { userAbility } = ctx.state;
1745
1783
  const { model } = ctx.params;
1746
- const documentMetadata2 = getService$1("document-metadata");
1747
1784
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1748
1785
  const updatedVersion = await updateDocument(ctx);
1749
1786
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1750
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedVersion);
1787
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
1751
1788
  },
1752
1789
  async clone(ctx) {
1753
1790
  const { userAbility, user } = ctx.state;
1754
1791
  const { model, sourceId: id } = ctx.params;
1755
1792
  const { body } = ctx.request;
1756
1793
  const documentManager2 = getService$1("document-manager");
1757
- const documentMetadata2 = getService$1("document-metadata");
1758
1794
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1759
1795
  if (permissionChecker2.cannot.create()) {
1760
1796
  return ctx.forbidden();
1761
1797
  }
1762
1798
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1763
1799
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1764
- const { locale } = getDocumentLocaleAndStatus(body);
1800
+ const { locale } = await getDocumentLocaleAndStatus(body);
1765
1801
  const document = await documentManager2.findOne(id, model, {
1766
1802
  populate,
1767
1803
  locale,
@@ -1777,7 +1813,7 @@ const collectionTypes = {
1777
1813
  const sanitizedBody = await sanitizeFn(body);
1778
1814
  const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
1779
1815
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
1780
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1816
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1781
1817
  // Empty metadata as it's not relevant for a new document
1782
1818
  availableLocales: false,
1783
1819
  availableStatus: false
@@ -1806,7 +1842,7 @@ const collectionTypes = {
1806
1842
  }
1807
1843
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1808
1844
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1809
- const { locale } = getDocumentLocaleAndStatus(ctx.query);
1845
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query);
1810
1846
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1811
1847
  if (documentLocales.length === 0) {
1812
1848
  return ctx.notFound();
@@ -1828,7 +1864,6 @@ const collectionTypes = {
1828
1864
  const { id, model } = ctx.params;
1829
1865
  const { body } = ctx.request;
1830
1866
  const documentManager2 = getService$1("document-manager");
1831
- const documentMetadata2 = getService$1("document-metadata");
1832
1867
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1833
1868
  if (permissionChecker2.cannot.publish()) {
1834
1869
  return ctx.forbidden();
@@ -1840,15 +1875,19 @@ const collectionTypes = {
1840
1875
  if (permissionChecker2.cannot.publish(document)) {
1841
1876
  throw new strapiUtils.errors.ForbiddenError();
1842
1877
  }
1843
- const { locale } = getDocumentLocaleAndStatus(body);
1844
- return documentManager2.publish(document.documentId, model, {
1878
+ const { locale } = await getDocumentLocaleAndStatus(body);
1879
+ const publishResult = await documentManager2.publish(document.documentId, model, {
1845
1880
  locale
1846
1881
  // TODO: Allow setting creator fields on publish
1847
1882
  // data: setCreatorFields({ user, isEdition: true })({}),
1848
1883
  });
1884
+ if (!publishResult || publishResult.length === 0) {
1885
+ throw new strapiUtils.errors.NotFoundError("Document not found or already published.");
1886
+ }
1887
+ return publishResult[0];
1849
1888
  });
1850
1889
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
1851
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1890
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1852
1891
  },
1853
1892
  async bulkPublish(ctx) {
1854
1893
  const { userAbility } = ctx.state;
@@ -1861,9 +1900,11 @@ const collectionTypes = {
1861
1900
  if (permissionChecker2.cannot.publish()) {
1862
1901
  return ctx.forbidden();
1863
1902
  }
1864
- const { locale } = getDocumentLocaleAndStatus(body);
1903
+ const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1904
+ const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1905
+ const { locale } = await getDocumentLocaleAndStatus(body, { allowMultipleLocales: true });
1865
1906
  const entityPromises = documentIds.map(
1866
- (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: false })
1907
+ (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
1867
1908
  );
1868
1909
  const entities = (await Promise.all(entityPromises)).flat();
1869
1910
  for (const entity of entities) {
@@ -1874,8 +1915,7 @@ const collectionTypes = {
1874
1915
  return ctx.forbidden();
1875
1916
  }
1876
1917
  }
1877
- const entitiesIds = entities.map((document) => document.documentId);
1878
- const { count } = await documentManager2.publishMany(entitiesIds, model, { locale });
1918
+ const count = await documentManager2.publishMany(model, documentIds, locale);
1879
1919
  ctx.body = { count };
1880
1920
  },
1881
1921
  async bulkUnpublish(ctx) {
@@ -1889,7 +1929,7 @@ const collectionTypes = {
1889
1929
  if (permissionChecker2.cannot.unpublish()) {
1890
1930
  return ctx.forbidden();
1891
1931
  }
1892
- const { locale } = getDocumentLocaleAndStatus(body);
1932
+ const { locale } = await getDocumentLocaleAndStatus(body);
1893
1933
  const entityPromises = documentIds.map(
1894
1934
  (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
1895
1935
  );
@@ -1913,7 +1953,6 @@ const collectionTypes = {
1913
1953
  body: { discardDraft, ...body }
1914
1954
  } = ctx.request;
1915
1955
  const documentManager2 = getService$1("document-manager");
1916
- const documentMetadata2 = getService$1("document-metadata");
1917
1956
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1918
1957
  if (permissionChecker2.cannot.unpublish()) {
1919
1958
  return ctx.forbidden();
@@ -1923,7 +1962,7 @@ const collectionTypes = {
1923
1962
  }
1924
1963
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1925
1964
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1926
- const { locale } = getDocumentLocaleAndStatus(body);
1965
+ const { locale } = await getDocumentLocaleAndStatus(body);
1927
1966
  const document = await documentManager2.findOne(id, model, {
1928
1967
  populate,
1929
1968
  locale,
@@ -1945,7 +1984,7 @@ const collectionTypes = {
1945
1984
  ctx.body = await strapiUtils.async.pipe(
1946
1985
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
1947
1986
  permissionChecker2.sanitizeOutput,
1948
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
1987
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1949
1988
  )(document);
1950
1989
  });
1951
1990
  },
@@ -1954,14 +1993,13 @@ const collectionTypes = {
1954
1993
  const { id, model } = ctx.params;
1955
1994
  const { body } = ctx.request;
1956
1995
  const documentManager2 = getService$1("document-manager");
1957
- const documentMetadata2 = getService$1("document-metadata");
1958
1996
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1959
1997
  if (permissionChecker2.cannot.discard()) {
1960
1998
  return ctx.forbidden();
1961
1999
  }
1962
2000
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
1963
2001
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1964
- const { locale } = getDocumentLocaleAndStatus(body);
2002
+ const { locale } = await getDocumentLocaleAndStatus(body);
1965
2003
  const document = await documentManager2.findOne(id, model, {
1966
2004
  populate,
1967
2005
  locale,
@@ -1976,7 +2014,7 @@ const collectionTypes = {
1976
2014
  ctx.body = await strapiUtils.async.pipe(
1977
2015
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
1978
2016
  permissionChecker2.sanitizeOutput,
1979
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2017
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1980
2018
  )(document);
1981
2019
  },
1982
2020
  async bulkDelete(ctx) {
@@ -1992,7 +2030,7 @@ const collectionTypes = {
1992
2030
  }
1993
2031
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
1994
2032
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1995
- const { locale } = getDocumentLocaleAndStatus(body);
2033
+ const { locale } = await getDocumentLocaleAndStatus(body);
1996
2034
  const documentLocales = await documentManager2.findLocales(documentIds, model, {
1997
2035
  populate,
1998
2036
  locale
@@ -2019,7 +2057,7 @@ const collectionTypes = {
2019
2057
  }
2020
2058
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2021
2059
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2022
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
2060
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
2023
2061
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
2024
2062
  if (!entity) {
2025
2063
  return ctx.notFound();
@@ -2034,26 +2072,27 @@ const collectionTypes = {
2034
2072
  },
2035
2073
  async countManyEntriesDraftRelations(ctx) {
2036
2074
  const { userAbility } = ctx.state;
2037
- const { documentIds, locale } = ctx.request.query;
2075
+ const ids = ctx.request.query.documentIds;
2076
+ const locale = ctx.request.query.locale;
2038
2077
  const { model } = ctx.params;
2039
2078
  const documentManager2 = getService$1("document-manager");
2040
2079
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2041
2080
  if (permissionChecker2.cannot.read()) {
2042
2081
  return ctx.forbidden();
2043
2082
  }
2044
- const documents = await documentManager2.findMany(
2083
+ const entities = await documentManager2.findMany(
2045
2084
  {
2046
2085
  filters: {
2047
- documentId: { $in: documentIds }
2086
+ documentId: ids
2048
2087
  },
2049
2088
  locale
2050
2089
  },
2051
2090
  model
2052
2091
  );
2053
- if (!documents) {
2092
+ if (!entities) {
2054
2093
  return ctx.notFound();
2055
2094
  }
2056
- const number = await documentManager2.countManyEntriesDraftRelations(documentIds, model, locale);
2095
+ const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
2057
2096
  return {
2058
2097
  data: number
2059
2098
  };
@@ -2546,7 +2585,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
2546
2585
  throw new strapiUtils.errors.ForbiddenError();
2547
2586
  }
2548
2587
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2549
- const { locale } = getDocumentLocaleAndStatus(body);
2588
+ const { locale } = await getDocumentLocaleAndStatus(body);
2550
2589
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2551
2590
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2552
2591
  // Find the first document to check if it exists
@@ -2583,12 +2622,11 @@ const singleTypes = {
2583
2622
  const { model } = ctx.params;
2584
2623
  const { query = {} } = ctx.request;
2585
2624
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2586
- const documentMetadata2 = getService$1("document-metadata");
2587
2625
  if (permissionChecker2.cannot.read()) {
2588
2626
  return ctx.forbidden();
2589
2627
  }
2590
2628
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2591
- const { locale, status } = getDocumentLocaleAndStatus(query);
2629
+ const { locale, status } = await getDocumentLocaleAndStatus(query);
2592
2630
  const version = await findDocument(permissionQuery, model, { locale, status });
2593
2631
  if (!version) {
2594
2632
  if (permissionChecker2.cannot.create()) {
@@ -2598,8 +2636,10 @@ const singleTypes = {
2598
2636
  if (!document) {
2599
2637
  return ctx.notFound();
2600
2638
  }
2601
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
2639
+ const { meta } = await formatDocumentWithMetadata(
2640
+ permissionChecker2,
2602
2641
  model,
2642
+ // @ts-expect-error - fix types
2603
2643
  { id: document.documentId, locale, publishedAt: null },
2604
2644
  { availableLocales: true, availableStatus: false }
2605
2645
  );
@@ -2610,16 +2650,15 @@ const singleTypes = {
2610
2650
  return ctx.forbidden();
2611
2651
  }
2612
2652
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
2613
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2653
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2614
2654
  },
2615
2655
  async createOrUpdate(ctx) {
2616
2656
  const { userAbility } = ctx.state;
2617
2657
  const { model } = ctx.params;
2618
- const documentMetadata2 = getService$1("document-metadata");
2619
2658
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2620
2659
  const document = await createOrUpdateDocument(ctx);
2621
2660
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2622
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2661
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2623
2662
  },
2624
2663
  async delete(ctx) {
2625
2664
  const { userAbility } = ctx.state;
@@ -2632,7 +2671,7 @@ const singleTypes = {
2632
2671
  }
2633
2672
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2634
2673
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2635
- const { locale } = getDocumentLocaleAndStatus(query);
2674
+ const { locale } = await getDocumentLocaleAndStatus(query);
2636
2675
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2637
2676
  populate,
2638
2677
  locale
@@ -2655,7 +2694,6 @@ const singleTypes = {
2655
2694
  const { model } = ctx.params;
2656
2695
  const { query = {} } = ctx.request;
2657
2696
  const documentManager2 = getService$1("document-manager");
2658
- const documentMetadata2 = getService$1("document-metadata");
2659
2697
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2660
2698
  if (permissionChecker2.cannot.publish()) {
2661
2699
  return ctx.forbidden();
@@ -2670,11 +2708,12 @@ const singleTypes = {
2670
2708
  if (permissionChecker2.cannot.publish(document)) {
2671
2709
  throw new strapiUtils.errors.ForbiddenError();
2672
2710
  }
2673
- const { locale } = getDocumentLocaleAndStatus(document);
2674
- return documentManager2.publish(document.documentId, model, { locale });
2711
+ const { locale } = await getDocumentLocaleAndStatus(document);
2712
+ const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2713
+ return publishResult.at(0);
2675
2714
  });
2676
2715
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
2677
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2716
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2678
2717
  },
2679
2718
  async unpublish(ctx) {
2680
2719
  const { userAbility } = ctx.state;
@@ -2684,7 +2723,6 @@ const singleTypes = {
2684
2723
  query = {}
2685
2724
  } = ctx.request;
2686
2725
  const documentManager2 = getService$1("document-manager");
2687
- const documentMetadata2 = getService$1("document-metadata");
2688
2726
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2689
2727
  if (permissionChecker2.cannot.unpublish()) {
2690
2728
  return ctx.forbidden();
@@ -2693,7 +2731,7 @@ const singleTypes = {
2693
2731
  return ctx.forbidden();
2694
2732
  }
2695
2733
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2696
- const { locale } = getDocumentLocaleAndStatus(body);
2734
+ const { locale } = await getDocumentLocaleAndStatus(body);
2697
2735
  const document = await findDocument(sanitizedQuery, model, { locale });
2698
2736
  if (!document) {
2699
2737
  return ctx.notFound();
@@ -2711,7 +2749,7 @@ const singleTypes = {
2711
2749
  ctx.body = await strapiUtils.async.pipe(
2712
2750
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
2713
2751
  permissionChecker2.sanitizeOutput,
2714
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2752
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2715
2753
  )(document);
2716
2754
  });
2717
2755
  },
@@ -2720,13 +2758,12 @@ const singleTypes = {
2720
2758
  const { model } = ctx.params;
2721
2759
  const { body, query = {} } = ctx.request;
2722
2760
  const documentManager2 = getService$1("document-manager");
2723
- const documentMetadata2 = getService$1("document-metadata");
2724
2761
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2725
2762
  if (permissionChecker2.cannot.discard()) {
2726
2763
  return ctx.forbidden();
2727
2764
  }
2728
2765
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2729
- const { locale } = getDocumentLocaleAndStatus(body);
2766
+ const { locale } = await getDocumentLocaleAndStatus(body);
2730
2767
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2731
2768
  if (!document) {
2732
2769
  return ctx.notFound();
@@ -2737,7 +2774,7 @@ const singleTypes = {
2737
2774
  ctx.body = await strapiUtils.async.pipe(
2738
2775
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
2739
2776
  permissionChecker2.sanitizeOutput,
2740
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2777
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2741
2778
  )(document);
2742
2779
  },
2743
2780
  async countDraftRelations(ctx) {
@@ -2746,7 +2783,7 @@ const singleTypes = {
2746
2783
  const { query } = ctx.request;
2747
2784
  const documentManager2 = getService$1("document-manager");
2748
2785
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2749
- const { locale } = getDocumentLocaleAndStatus(query);
2786
+ const { locale } = await getDocumentLocaleAndStatus(query);
2750
2787
  if (permissionChecker2.cannot.read()) {
2751
2788
  return ctx.forbidden();
2752
2789
  }
@@ -2767,7 +2804,7 @@ const uid$1 = {
2767
2804
  async generateUID(ctx) {
2768
2805
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2769
2806
  const { query = {} } = ctx.request;
2770
- const { locale } = getDocumentLocaleAndStatus(query);
2807
+ const { locale } = await getDocumentLocaleAndStatus(query);
2771
2808
  await validateUIDField(contentTypeUID, field);
2772
2809
  const uidService = getService$1("uid");
2773
2810
  ctx.body = {
@@ -2779,7 +2816,7 @@ const uid$1 = {
2779
2816
  ctx.request.body
2780
2817
  );
2781
2818
  const { query = {} } = ctx.request;
2782
- const { locale } = getDocumentLocaleAndStatus(query);
2819
+ const { locale } = await getDocumentLocaleAndStatus(query);
2783
2820
  await validateUIDField(contentTypeUID, field);
2784
2821
  const uidService = getService$1("uid");
2785
2822
  const isAvailable = await uidService.checkUIDAvailability({
@@ -3570,7 +3607,7 @@ const permission = ({ strapi: strapi2 }) => ({
3570
3607
  await strapi2.service("admin::permission").actionProvider.registerMany(actions);
3571
3608
  }
3572
3609
  });
3573
- const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils__default.default.contentTypes;
3610
+ const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils__default.default.contentTypes;
3574
3611
  const { isAnyToMany } = strapiUtils__default.default.relations;
3575
3612
  const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils__default.default.contentTypes.constants;
3576
3613
  const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
@@ -3661,6 +3698,42 @@ const getDeepPopulate = (uid2, {
3661
3698
  {}
3662
3699
  );
3663
3700
  };
3701
+ const getValidatableFieldsPopulate = (uid2, {
3702
+ initialPopulate = {},
3703
+ countMany = false,
3704
+ countOne = false,
3705
+ maxLevel = Infinity
3706
+ } = {}, level = 1) => {
3707
+ if (level > maxLevel) {
3708
+ return {};
3709
+ }
3710
+ const model = strapi.getModel(uid2);
3711
+ return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
3712
+ if (!getDoesAttributeRequireValidation(attribute)) {
3713
+ return populateAcc;
3714
+ }
3715
+ if (isScalarAttribute(attribute)) {
3716
+ return fp.merge(populateAcc, {
3717
+ [attributeName]: true
3718
+ });
3719
+ }
3720
+ return fp.merge(
3721
+ populateAcc,
3722
+ getPopulateFor(
3723
+ attributeName,
3724
+ model,
3725
+ {
3726
+ // @ts-expect-error - improve types
3727
+ initialPopulate: initialPopulate?.[attributeName],
3728
+ countMany,
3729
+ countOne,
3730
+ maxLevel
3731
+ },
3732
+ level
3733
+ )
3734
+ );
3735
+ }, {});
3736
+ };
3664
3737
  const getDeepPopulateDraftCount = (uid2) => {
3665
3738
  const model = strapi.getModel(uid2);
3666
3739
  let hasRelations = false;
@@ -3889,41 +3962,70 @@ const AVAILABLE_STATUS_FIELDS = [
3889
3962
  "updatedBy",
3890
3963
  "status"
3891
3964
  ];
3892
- const AVAILABLE_LOCALES_FIELDS = ["id", "locale", "updatedAt", "createdAt", "status"];
3965
+ const AVAILABLE_LOCALES_FIELDS = [
3966
+ "id",
3967
+ "locale",
3968
+ "updatedAt",
3969
+ "createdAt",
3970
+ "status",
3971
+ "publishedAt",
3972
+ "documentId"
3973
+ ];
3893
3974
  const CONTENT_MANAGER_STATUS = {
3894
3975
  PUBLISHED: "published",
3895
3976
  DRAFT: "draft",
3896
3977
  MODIFIED: "modified"
3897
3978
  };
3898
- const areDatesEqual = (date1, date2, threshold) => {
3899
- if (!date1 || !date2) {
3979
+ const getIsVersionLatestModification = (version, otherVersion) => {
3980
+ if (!version || !version.updatedAt) {
3900
3981
  return false;
3901
3982
  }
3902
- const time1 = new Date(date1).getTime();
3903
- const time2 = new Date(date2).getTime();
3904
- const difference = Math.abs(time1 - time2);
3905
- return difference <= threshold;
3983
+ const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
3984
+ const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
3985
+ return versionUpdatedAt > otherUpdatedAt;
3906
3986
  };
3907
3987
  const documentMetadata = ({ strapi: strapi2 }) => ({
3908
3988
  /**
3909
3989
  * Returns available locales of a document for the current status
3910
3990
  */
3911
- getAvailableLocales(uid2, version, allVersions) {
3991
+ async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
3912
3992
  const versionsByLocale = fp.groupBy("locale", allVersions);
3913
3993
  delete versionsByLocale[version.locale];
3914
- return Object.values(versionsByLocale).map((localeVersions) => {
3915
- if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2))) {
3916
- return fp.pick(AVAILABLE_LOCALES_FIELDS, localeVersions[0]);
3994
+ const model = strapi2.getModel(uid2);
3995
+ const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
3996
+ const traversalFunction = async (localeVersion) => strapiUtils.traverseEntity(
3997
+ ({ key }, { remove }) => {
3998
+ if (keysToKeep.includes(key)) {
3999
+ return;
4000
+ }
4001
+ remove(key);
4002
+ },
4003
+ { schema: model, getModel: strapi2.getModel.bind(strapi2) },
4004
+ // @ts-expect-error fix types DocumentVersion incompatible with Data
4005
+ localeVersion
4006
+ );
4007
+ const mappingResult = await strapiUtils.async.map(
4008
+ Object.values(versionsByLocale),
4009
+ async (localeVersions) => {
4010
+ const mappedLocaleVersions = await strapiUtils.async.map(
4011
+ localeVersions,
4012
+ traversalFunction
4013
+ );
4014
+ if (!strapiUtils.contentTypes.hasDraftAndPublish(model)) {
4015
+ return mappedLocaleVersions[0];
4016
+ }
4017
+ const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
4018
+ const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
4019
+ if (!draftVersion) {
4020
+ return;
4021
+ }
4022
+ return {
4023
+ ...draftVersion,
4024
+ status: this.getStatus(draftVersion, otherVersions)
4025
+ };
3917
4026
  }
3918
- const draftVersion = localeVersions.find((v) => v.publishedAt === null);
3919
- const otherVersions = localeVersions.filter((v) => v.id !== draftVersion?.id);
3920
- if (!draftVersion)
3921
- return;
3922
- return {
3923
- ...fp.pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
3924
- status: this.getStatus(draftVersion, otherVersions)
3925
- };
3926
- }).filter(Boolean);
4027
+ );
4028
+ return mappingResult.filter(Boolean);
3927
4029
  },
3928
4030
  /**
3929
4031
  * Returns available status of a document for the current locale
@@ -3961,26 +4063,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3961
4063
  });
3962
4064
  },
3963
4065
  getStatus(version, otherDocumentStatuses) {
3964
- const isDraft = version.publishedAt === null;
3965
- if (!otherDocumentStatuses?.length) {
3966
- return isDraft ? CONTENT_MANAGER_STATUS.DRAFT : CONTENT_MANAGER_STATUS.PUBLISHED;
4066
+ let draftVersion;
4067
+ let publishedVersion;
4068
+ if (version.publishedAt) {
4069
+ publishedVersion = version;
4070
+ } else {
4071
+ draftVersion = version;
3967
4072
  }
3968
- if (isDraft) {
3969
- const publishedVersion = otherDocumentStatuses?.find((d) => d.publishedAt !== null);
3970
- if (!publishedVersion) {
3971
- return CONTENT_MANAGER_STATUS.DRAFT;
3972
- }
4073
+ const otherVersion = otherDocumentStatuses?.at(0);
4074
+ if (otherVersion?.publishedAt) {
4075
+ publishedVersion = otherVersion;
4076
+ } else if (otherVersion) {
4077
+ draftVersion = otherVersion;
3973
4078
  }
3974
- if (areDatesEqual(version.updatedAt, otherDocumentStatuses.at(0)?.updatedAt, 500)) {
4079
+ if (!draftVersion)
3975
4080
  return CONTENT_MANAGER_STATUS.PUBLISHED;
3976
- }
3977
- return CONTENT_MANAGER_STATUS.MODIFIED;
4081
+ if (!publishedVersion)
4082
+ return CONTENT_MANAGER_STATUS.DRAFT;
4083
+ const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
4084
+ return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
3978
4085
  },
4086
+ // TODO is it necessary to return metadata on every page of the CM
4087
+ // We could refactor this so the locales are only loaded when they're
4088
+ // needed. e.g. in the bulk locale action modal.
3979
4089
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4090
+ const populate = getValidatableFieldsPopulate(uid2);
3980
4091
  const versions = await strapi2.db.query(uid2).findMany({
3981
4092
  where: { documentId: version.documentId },
3982
- select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
3983
4093
  populate: {
4094
+ // Populate only fields that require validation for bulk locale actions
4095
+ ...populate,
4096
+ // NOTE: creator fields are selected in this way to avoid exposing sensitive data
3984
4097
  createdBy: {
3985
4098
  select: ["id", "firstname", "lastname", "email"]
3986
4099
  },
@@ -3989,7 +4102,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3989
4102
  }
3990
4103
  }
3991
4104
  });
3992
- const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
4105
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
3993
4106
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
3994
4107
  return {
3995
4108
  availableLocales: availableLocalesResult,
@@ -4002,8 +4115,9 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4002
4115
  * - Available status of the document for the current locale
4003
4116
  */
4004
4117
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
4005
- if (!document)
4118
+ if (!document) {
4006
4119
  return document;
4120
+ }
4007
4121
  const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
4008
4122
  if (!hasDraftAndPublish) {
4009
4123
  opts.availableStatus = false;
@@ -4139,7 +4253,7 @@ const documentManager = ({ strapi: strapi2 }) => {
4139
4253
  return {};
4140
4254
  },
4141
4255
  // FIXME: handle relations
4142
- async deleteMany(documentIds, uid2, opts) {
4256
+ async deleteMany(documentIds, uid2, opts = {}) {
4143
4257
  const deletedEntries = await strapi2.db.transaction(async () => {
4144
4258
  return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
4145
4259
  });
@@ -4148,18 +4262,16 @@ const documentManager = ({ strapi: strapi2 }) => {
4148
4262
  async publish(id, uid2, opts = {}) {
4149
4263
  const populate = await buildDeepPopulate(uid2);
4150
4264
  const params = { ...opts, populate };
4151
- return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries.at(0));
4265
+ return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
4152
4266
  },
4153
- async publishMany(documentIds, uid2, opts = {}) {
4154
- const publishedEntries = await strapi2.db.transaction(async () => {
4155
- return Promise.all(
4156
- documentIds.map(
4157
- (id) => strapi2.documents(uid2).publish({ ...opts, documentId: id }).then((result) => result?.entries)
4158
- )
4267
+ async publishMany(uid2, documentIds, locale) {
4268
+ return strapi2.db.transaction(async () => {
4269
+ const results = await Promise.all(
4270
+ documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
4159
4271
  );
4272
+ const publishedEntitiesCount = results.flat().filter(Boolean).length;
4273
+ return publishedEntitiesCount;
4160
4274
  });
4161
- const publishedEntitiesCount = publishedEntries.flat().filter(Boolean).length;
4162
- return { count: publishedEntitiesCount };
4163
4275
  },
4164
4276
  async unpublishMany(documentIds, uid2, opts = {}) {
4165
4277
  const unpublishedEntries = await strapi2.db.transaction(async () => {
@@ -4200,14 +4312,18 @@ const documentManager = ({ strapi: strapi2 }) => {
4200
4312
  if (!hasRelations) {
4201
4313
  return 0;
4202
4314
  }
4203
- const documents = await strapi2.documents(uid2).findMany({
4315
+ let localeFilter = {};
4316
+ if (locale) {
4317
+ localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
4318
+ }
4319
+ const entities = await strapi2.db.query(uid2).findMany({
4204
4320
  populate,
4205
- filters: {
4206
- documentId: documentIds
4207
- },
4208
- locale
4321
+ where: {
4322
+ documentId: { $in: documentIds },
4323
+ ...localeFilter
4324
+ }
4209
4325
  });
4210
- const totalNumberDraftRelations = documents.reduce(
4326
+ const totalNumberDraftRelations = entities.reduce(
4211
4327
  (count, entity) => sumDraftCounts(entity, uid2) + count,
4212
4328
  0
4213
4329
  );