@strapi/content-manager 0.0.0-experimental.17b4116f461a49b8ce5386f7c8d79c511d40fb3b → 0.0.0-experimental.826f263c58b6886b849d3f03b81f7a530bc51c91

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