@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
@@ -1,5 +1,5 @@
1
- import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1, pagination, sanitize } from "@strapi/utils";
2
- import { pick, omit, difference, intersection, pipe, propOr, isEqual, isEmpty, set, isNil as isNil$1, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, getOr, propEq, merge, groupBy, castArray } from "lodash/fp";
1
+ import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1, traverseEntity, pagination } from "@strapi/utils";
2
+ import { pick, omit, difference, intersection, pipe, propOr, isEqual, isEmpty, set, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, isNil as isNil$1, getOr, propEq, merge, groupBy, castArray } from "lodash/fp";
3
3
  import "@strapi/types";
4
4
  import * as yup from "yup";
5
5
  import { scheduleJob } from "node-schedule";
@@ -481,7 +481,7 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
481
481
  if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
482
482
  return next();
483
483
  }
484
- if (context.action !== "create" && context.action !== "update" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
484
+ if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
485
485
  return next();
486
486
  }
487
487
  const contentTypeUid = context.contentType.uid;
@@ -489,9 +489,18 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
489
489
  return next();
490
490
  }
491
491
  const result = await next();
492
- const documentContext = context.action === "create" ? { documentId: result.documentId, locale: context.params?.locale } : { documentId: context.params.documentId, locale: context.params?.locale };
492
+ const documentContext = {
493
+ documentId: context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId,
494
+ locale: context.params?.locale
495
+ };
493
496
  const defaultLocale = await serviceUtils.getDefaultLocale();
494
497
  const locale = documentContext.locale || defaultLocale;
498
+ if (Array.isArray(locale)) {
499
+ strapi2.log.warn(
500
+ "[Content manager history middleware]: An array of locales was provided, but only a single locale is supported for the findOne operation."
501
+ );
502
+ return next();
503
+ }
495
504
  const document = await strapi2.documents(contentTypeUid).findOne({
496
505
  documentId: documentContext.documentId,
497
506
  locale,
@@ -527,9 +536,8 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
527
536
  });
528
537
  return result;
529
538
  });
530
- const retentionDays = serviceUtils.getRetentionDays();
531
539
  state.deleteExpiredJob = scheduleJob("0 0 * * *", () => {
532
- const retentionDaysInMilliseconds = retentionDays * 24 * 60 * 60 * 1e3;
540
+ const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
533
541
  const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
534
542
  query.deleteMany({
535
543
  where: {
@@ -1452,7 +1460,7 @@ const { PaginationError, ValidationError } = errors;
1452
1460
  const TYPES = ["singleType", "collectionType"];
1453
1461
  const kindSchema = yup$1.string().oneOf(TYPES).nullable();
1454
1462
  const bulkActionInputSchema = yup$1.object({
1455
- ids: yup$1.array().of(yup$1.strapiID()).min(1).required()
1463
+ documentIds: yup$1.array().of(yup$1.strapiID()).min(1).required()
1456
1464
  }).required();
1457
1465
  const generateUIDInputSchema = yup$1.object({
1458
1466
  contentTypeUID: yup$1.string().required(),
@@ -1551,15 +1559,47 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1551
1559
  }
1552
1560
  }, body);
1553
1561
  };
1554
- const getDocumentLocaleAndStatus = (request) => {
1562
+ const singleLocaleSchema = yup$1.string().nullable();
1563
+ const multipleLocaleSchema = yup$1.lazy(
1564
+ (value) => Array.isArray(value) ? yup$1.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1565
+ );
1566
+ const statusSchema = yup$1.mixed().oneOf(["draft", "published"], "Invalid status");
1567
+ const getDocumentLocaleAndStatus = async (request, opts = { allowMultipleLocales: false }) => {
1568
+ const { allowMultipleLocales } = opts;
1555
1569
  const { locale, status, ...rest } = request || {};
1556
- if (!isNil$1(locale) && typeof locale !== "string") {
1557
- throw new errors.ValidationError(`Invalid locale: ${locale}`);
1558
- }
1559
- if (!isNil$1(status) && !["draft", "published"].includes(status)) {
1560
- throw new errors.ValidationError(`Invalid status: ${status}`);
1570
+ const schema = yup$1.object().shape({
1571
+ locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1572
+ status: statusSchema
1573
+ });
1574
+ try {
1575
+ await validateYupSchema(schema, { strict: true, abortEarly: false })(request);
1576
+ return { locale, status, ...rest };
1577
+ } catch (error) {
1578
+ throw new errors.ValidationError(`Validation error: ${error.message}`);
1561
1579
  }
1562
- return { locale, status, ...rest };
1580
+ };
1581
+ const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
1582
+ const documentMetadata2 = getService$1("document-metadata");
1583
+ const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
1584
+ let {
1585
+ meta: { availableLocales, availableStatus }
1586
+ } = serviceOutput;
1587
+ const metadataSanitizer = permissionChecker2.sanitizeOutput;
1588
+ availableLocales = await async.map(
1589
+ availableLocales,
1590
+ async (localeDocument) => metadataSanitizer(localeDocument)
1591
+ );
1592
+ availableStatus = await async.map(
1593
+ availableStatus,
1594
+ async (statusDocument) => metadataSanitizer(statusDocument)
1595
+ );
1596
+ return {
1597
+ ...serviceOutput,
1598
+ meta: {
1599
+ availableLocales,
1600
+ availableStatus
1601
+ }
1602
+ };
1563
1603
  };
1564
1604
  const createDocument = async (ctx, opts) => {
1565
1605
  const { userAbility, user } = ctx.state;
@@ -1574,7 +1614,7 @@ const createDocument = async (ctx, opts) => {
1574
1614
  const setCreator = setCreatorFields({ user });
1575
1615
  const sanitizeFn = async.pipe(pickPermittedFields, setCreator);
1576
1616
  const sanitizedBody = await sanitizeFn(body);
1577
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(body);
1617
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(body);
1578
1618
  return documentManager2.create(model, {
1579
1619
  data: sanitizedBody,
1580
1620
  locale,
@@ -1593,7 +1633,7 @@ const updateDocument = async (ctx, opts) => {
1593
1633
  }
1594
1634
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1595
1635
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1596
- const { locale } = getDocumentLocaleAndStatus(body);
1636
+ const { locale } = await getDocumentLocaleAndStatus(body);
1597
1637
  const [documentVersion, documentExists] = await Promise.all([
1598
1638
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1599
1639
  documentManager2.exists(model, id)
@@ -1631,7 +1671,7 @@ const collectionTypes = {
1631
1671
  }
1632
1672
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1633
1673
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1634
- const { locale, status } = getDocumentLocaleAndStatus(query);
1674
+ const { locale, status } = await getDocumentLocaleAndStatus(query);
1635
1675
  const { results: documents, pagination: pagination2 } = await documentManager2.findPage(
1636
1676
  { ...permissionQuery, populate, locale, status },
1637
1677
  model
@@ -1660,14 +1700,13 @@ const collectionTypes = {
1660
1700
  const { userAbility } = ctx.state;
1661
1701
  const { model, id } = ctx.params;
1662
1702
  const documentManager2 = getService$1("document-manager");
1663
- const documentMetadata2 = getService$1("document-metadata");
1664
1703
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1665
1704
  if (permissionChecker2.cannot.read()) {
1666
1705
  return ctx.forbidden();
1667
1706
  }
1668
1707
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1669
1708
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1670
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
1709
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1671
1710
  const version = await documentManager2.findOne(id, model, {
1672
1711
  populate,
1673
1712
  locale,
@@ -1678,8 +1717,10 @@ const collectionTypes = {
1678
1717
  if (!exists) {
1679
1718
  return ctx.notFound();
1680
1719
  }
1681
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
1720
+ const { meta } = await formatDocumentWithMetadata(
1721
+ permissionChecker2,
1682
1722
  model,
1723
+ // @ts-expect-error TODO: fix
1683
1724
  { id, locale, publishedAt: null },
1684
1725
  { availableLocales: true, availableStatus: false }
1685
1726
  );
@@ -1690,12 +1731,11 @@ const collectionTypes = {
1690
1731
  return ctx.forbidden();
1691
1732
  }
1692
1733
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
1693
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1734
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1694
1735
  },
1695
1736
  async create(ctx) {
1696
1737
  const { userAbility } = ctx.state;
1697
1738
  const { model } = ctx.params;
1698
- const documentMetadata2 = getService$1("document-metadata");
1699
1739
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1700
1740
  const [totalEntries, document] = await Promise.all([
1701
1741
  strapi.db.query(model).count(),
@@ -1703,7 +1743,7 @@ const collectionTypes = {
1703
1743
  ]);
1704
1744
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
1705
1745
  ctx.status = 201;
1706
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1746
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1707
1747
  // Empty metadata as it's not relevant for a new document
1708
1748
  availableLocales: false,
1709
1749
  availableStatus: false
@@ -1717,25 +1757,23 @@ const collectionTypes = {
1717
1757
  async update(ctx) {
1718
1758
  const { userAbility } = ctx.state;
1719
1759
  const { model } = ctx.params;
1720
- const documentMetadata2 = getService$1("document-metadata");
1721
1760
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1722
1761
  const updatedVersion = await updateDocument(ctx);
1723
1762
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1724
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedVersion);
1763
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
1725
1764
  },
1726
1765
  async clone(ctx) {
1727
1766
  const { userAbility, user } = ctx.state;
1728
1767
  const { model, sourceId: id } = ctx.params;
1729
1768
  const { body } = ctx.request;
1730
1769
  const documentManager2 = getService$1("document-manager");
1731
- const documentMetadata2 = getService$1("document-metadata");
1732
1770
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1733
1771
  if (permissionChecker2.cannot.create()) {
1734
1772
  return ctx.forbidden();
1735
1773
  }
1736
1774
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1737
1775
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1738
- const { locale } = getDocumentLocaleAndStatus(body);
1776
+ const { locale } = await getDocumentLocaleAndStatus(body);
1739
1777
  const document = await documentManager2.findOne(id, model, {
1740
1778
  populate,
1741
1779
  locale,
@@ -1751,7 +1789,7 @@ const collectionTypes = {
1751
1789
  const sanitizedBody = await sanitizeFn(body);
1752
1790
  const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
1753
1791
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
1754
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1792
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1755
1793
  // Empty metadata as it's not relevant for a new document
1756
1794
  availableLocales: false,
1757
1795
  availableStatus: false
@@ -1780,7 +1818,7 @@ const collectionTypes = {
1780
1818
  }
1781
1819
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1782
1820
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1783
- const { locale } = getDocumentLocaleAndStatus(ctx.query);
1821
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query);
1784
1822
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1785
1823
  if (documentLocales.length === 0) {
1786
1824
  return ctx.notFound();
@@ -1802,7 +1840,6 @@ const collectionTypes = {
1802
1840
  const { id, model } = ctx.params;
1803
1841
  const { body } = ctx.request;
1804
1842
  const documentManager2 = getService$1("document-manager");
1805
- const documentMetadata2 = getService$1("document-metadata");
1806
1843
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1807
1844
  if (permissionChecker2.cannot.publish()) {
1808
1845
  return ctx.forbidden();
@@ -1814,21 +1851,25 @@ const collectionTypes = {
1814
1851
  if (permissionChecker2.cannot.publish(document)) {
1815
1852
  throw new errors.ForbiddenError();
1816
1853
  }
1817
- const { locale } = getDocumentLocaleAndStatus(body);
1818
- return documentManager2.publish(document.documentId, model, {
1854
+ const { locale } = await getDocumentLocaleAndStatus(body);
1855
+ const publishResult = await documentManager2.publish(document.documentId, model, {
1819
1856
  locale
1820
1857
  // TODO: Allow setting creator fields on publish
1821
1858
  // data: setCreatorFields({ user, isEdition: true })({}),
1822
1859
  });
1860
+ if (!publishResult || publishResult.length === 0) {
1861
+ throw new errors.NotFoundError("Document not found or already published.");
1862
+ }
1863
+ return publishResult[0];
1823
1864
  });
1824
1865
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
1825
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1866
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1826
1867
  },
1827
1868
  async bulkPublish(ctx) {
1828
1869
  const { userAbility } = ctx.state;
1829
1870
  const { model } = ctx.params;
1830
1871
  const { body } = ctx.request;
1831
- const { ids } = body;
1872
+ const { documentIds } = body;
1832
1873
  await validateBulkActionInput(body);
1833
1874
  const documentManager2 = getService$1("document-manager");
1834
1875
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1837,8 +1878,11 @@ const collectionTypes = {
1837
1878
  }
1838
1879
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1839
1880
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1840
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1841
- const entities = await Promise.all(entityPromises);
1881
+ const { locale } = await getDocumentLocaleAndStatus(body, { allowMultipleLocales: true });
1882
+ const entityPromises = documentIds.map(
1883
+ (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
1884
+ );
1885
+ const entities = (await Promise.all(entityPromises)).flat();
1842
1886
  for (const entity of entities) {
1843
1887
  if (!entity) {
1844
1888
  return ctx.notFound();
@@ -1847,24 +1891,25 @@ const collectionTypes = {
1847
1891
  return ctx.forbidden();
1848
1892
  }
1849
1893
  }
1850
- const { count } = await documentManager2.publishMany(entities, model);
1894
+ const count = await documentManager2.publishMany(model, documentIds, locale);
1851
1895
  ctx.body = { count };
1852
1896
  },
1853
1897
  async bulkUnpublish(ctx) {
1854
1898
  const { userAbility } = ctx.state;
1855
1899
  const { model } = ctx.params;
1856
1900
  const { body } = ctx.request;
1857
- const { ids } = body;
1901
+ const { documentIds } = body;
1858
1902
  await validateBulkActionInput(body);
1859
1903
  const documentManager2 = getService$1("document-manager");
1860
1904
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1861
1905
  if (permissionChecker2.cannot.unpublish()) {
1862
1906
  return ctx.forbidden();
1863
1907
  }
1864
- const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1865
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1866
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1867
- const entities = await Promise.all(entityPromises);
1908
+ const { locale } = await getDocumentLocaleAndStatus(body);
1909
+ const entityPromises = documentIds.map(
1910
+ (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
1911
+ );
1912
+ const entities = (await Promise.all(entityPromises)).flat();
1868
1913
  for (const entity of entities) {
1869
1914
  if (!entity) {
1870
1915
  return ctx.notFound();
@@ -1873,7 +1918,8 @@ const collectionTypes = {
1873
1918
  return ctx.forbidden();
1874
1919
  }
1875
1920
  }
1876
- const { count } = await documentManager2.unpublishMany(entities, model);
1921
+ const entitiesIds = entities.map((document) => document.documentId);
1922
+ const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
1877
1923
  ctx.body = { count };
1878
1924
  },
1879
1925
  async unpublish(ctx) {
@@ -1883,7 +1929,6 @@ const collectionTypes = {
1883
1929
  body: { discardDraft, ...body }
1884
1930
  } = ctx.request;
1885
1931
  const documentManager2 = getService$1("document-manager");
1886
- const documentMetadata2 = getService$1("document-metadata");
1887
1932
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1888
1933
  if (permissionChecker2.cannot.unpublish()) {
1889
1934
  return ctx.forbidden();
@@ -1893,7 +1938,7 @@ const collectionTypes = {
1893
1938
  }
1894
1939
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1895
1940
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1896
- const { locale } = getDocumentLocaleAndStatus(body);
1941
+ const { locale } = await getDocumentLocaleAndStatus(body);
1897
1942
  const document = await documentManager2.findOne(id, model, {
1898
1943
  populate,
1899
1944
  locale,
@@ -1915,7 +1960,7 @@ const collectionTypes = {
1915
1960
  ctx.body = await async.pipe(
1916
1961
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
1917
1962
  permissionChecker2.sanitizeOutput,
1918
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
1963
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1919
1964
  )(document);
1920
1965
  });
1921
1966
  },
@@ -1924,14 +1969,13 @@ const collectionTypes = {
1924
1969
  const { id, model } = ctx.params;
1925
1970
  const { body } = ctx.request;
1926
1971
  const documentManager2 = getService$1("document-manager");
1927
- const documentMetadata2 = getService$1("document-metadata");
1928
1972
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1929
1973
  if (permissionChecker2.cannot.discard()) {
1930
1974
  return ctx.forbidden();
1931
1975
  }
1932
1976
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
1933
1977
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1934
- const { locale } = getDocumentLocaleAndStatus(body);
1978
+ const { locale } = await getDocumentLocaleAndStatus(body);
1935
1979
  const document = await documentManager2.findOne(id, model, {
1936
1980
  populate,
1937
1981
  locale,
@@ -1946,14 +1990,14 @@ const collectionTypes = {
1946
1990
  ctx.body = await async.pipe(
1947
1991
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
1948
1992
  permissionChecker2.sanitizeOutput,
1949
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
1993
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1950
1994
  )(document);
1951
1995
  },
1952
1996
  async bulkDelete(ctx) {
1953
1997
  const { userAbility } = ctx.state;
1954
1998
  const { model } = ctx.params;
1955
1999
  const { query, body } = ctx.request;
1956
- const { ids } = body;
2000
+ const { documentIds } = body;
1957
2001
  await validateBulkActionInput(body);
1958
2002
  const documentManager2 = getService$1("document-manager");
1959
2003
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1961,14 +2005,22 @@ const collectionTypes = {
1961
2005
  return ctx.forbidden();
1962
2006
  }
1963
2007
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
1964
- const idsWhereClause = { id: { $in: ids } };
1965
- const params = {
1966
- ...permissionQuery,
1967
- filters: {
1968
- $and: [idsWhereClause].concat(permissionQuery.filters || [])
2008
+ const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2009
+ const { locale } = await getDocumentLocaleAndStatus(body);
2010
+ const documentLocales = await documentManager2.findLocales(documentIds, model, {
2011
+ populate,
2012
+ locale
2013
+ });
2014
+ if (documentLocales.length === 0) {
2015
+ return ctx.notFound();
2016
+ }
2017
+ for (const document of documentLocales) {
2018
+ if (permissionChecker2.cannot.delete(document)) {
2019
+ return ctx.forbidden();
1969
2020
  }
1970
- };
1971
- const { count } = await documentManager2.deleteMany(params, model);
2021
+ }
2022
+ const localeDocumentsIds = documentLocales.map((document) => document.documentId);
2023
+ const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
1972
2024
  ctx.body = { count };
1973
2025
  },
1974
2026
  async countDraftRelations(ctx) {
@@ -1981,7 +2033,7 @@ const collectionTypes = {
1981
2033
  }
1982
2034
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1983
2035
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1984
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
2036
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1985
2037
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
1986
2038
  if (!entity) {
1987
2039
  return ctx.notFound();
@@ -1996,7 +2048,7 @@ const collectionTypes = {
1996
2048
  },
1997
2049
  async countManyEntriesDraftRelations(ctx) {
1998
2050
  const { userAbility } = ctx.state;
1999
- const ids = ctx.request.query.ids;
2051
+ const ids = ctx.request.query.documentIds;
2000
2052
  const locale = ctx.request.query.locale;
2001
2053
  const { model } = ctx.params;
2002
2054
  const documentManager2 = getService$1("document-manager");
@@ -2007,7 +2059,7 @@ const collectionTypes = {
2007
2059
  const entities = await documentManager2.findMany(
2008
2060
  {
2009
2061
  filters: {
2010
- id: ids
2062
+ documentId: ids
2011
2063
  },
2012
2064
  locale
2013
2065
  },
@@ -2509,7 +2561,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
2509
2561
  throw new errors.ForbiddenError();
2510
2562
  }
2511
2563
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2512
- const { locale } = getDocumentLocaleAndStatus(body);
2564
+ const { locale } = await getDocumentLocaleAndStatus(body);
2513
2565
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2514
2566
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2515
2567
  // Find the first document to check if it exists
@@ -2546,12 +2598,11 @@ const singleTypes = {
2546
2598
  const { model } = ctx.params;
2547
2599
  const { query = {} } = ctx.request;
2548
2600
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2549
- const documentMetadata2 = getService$1("document-metadata");
2550
2601
  if (permissionChecker2.cannot.read()) {
2551
2602
  return ctx.forbidden();
2552
2603
  }
2553
2604
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2554
- const { locale, status } = getDocumentLocaleAndStatus(query);
2605
+ const { locale, status } = await getDocumentLocaleAndStatus(query);
2555
2606
  const version = await findDocument(permissionQuery, model, { locale, status });
2556
2607
  if (!version) {
2557
2608
  if (permissionChecker2.cannot.create()) {
@@ -2561,8 +2612,10 @@ const singleTypes = {
2561
2612
  if (!document) {
2562
2613
  return ctx.notFound();
2563
2614
  }
2564
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
2615
+ const { meta } = await formatDocumentWithMetadata(
2616
+ permissionChecker2,
2565
2617
  model,
2618
+ // @ts-expect-error - fix types
2566
2619
  { id: document.documentId, locale, publishedAt: null },
2567
2620
  { availableLocales: true, availableStatus: false }
2568
2621
  );
@@ -2573,16 +2626,15 @@ const singleTypes = {
2573
2626
  return ctx.forbidden();
2574
2627
  }
2575
2628
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
2576
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2629
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2577
2630
  },
2578
2631
  async createOrUpdate(ctx) {
2579
2632
  const { userAbility } = ctx.state;
2580
2633
  const { model } = ctx.params;
2581
- const documentMetadata2 = getService$1("document-metadata");
2582
2634
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2583
2635
  const document = await createOrUpdateDocument(ctx);
2584
2636
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2585
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2637
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2586
2638
  },
2587
2639
  async delete(ctx) {
2588
2640
  const { userAbility } = ctx.state;
@@ -2595,7 +2647,7 @@ const singleTypes = {
2595
2647
  }
2596
2648
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2597
2649
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2598
- const { locale } = getDocumentLocaleAndStatus(query);
2650
+ const { locale } = await getDocumentLocaleAndStatus(query);
2599
2651
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2600
2652
  populate,
2601
2653
  locale
@@ -2618,7 +2670,6 @@ const singleTypes = {
2618
2670
  const { model } = ctx.params;
2619
2671
  const { query = {} } = ctx.request;
2620
2672
  const documentManager2 = getService$1("document-manager");
2621
- const documentMetadata2 = getService$1("document-metadata");
2622
2673
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2623
2674
  if (permissionChecker2.cannot.publish()) {
2624
2675
  return ctx.forbidden();
@@ -2633,11 +2684,12 @@ const singleTypes = {
2633
2684
  if (permissionChecker2.cannot.publish(document)) {
2634
2685
  throw new errors.ForbiddenError();
2635
2686
  }
2636
- const { locale } = getDocumentLocaleAndStatus(document);
2637
- return documentManager2.publish(document.documentId, model, { locale });
2687
+ const { locale } = await getDocumentLocaleAndStatus(document);
2688
+ const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2689
+ return publishResult.at(0);
2638
2690
  });
2639
2691
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
2640
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2692
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2641
2693
  },
2642
2694
  async unpublish(ctx) {
2643
2695
  const { userAbility } = ctx.state;
@@ -2647,7 +2699,6 @@ const singleTypes = {
2647
2699
  query = {}
2648
2700
  } = ctx.request;
2649
2701
  const documentManager2 = getService$1("document-manager");
2650
- const documentMetadata2 = getService$1("document-metadata");
2651
2702
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2652
2703
  if (permissionChecker2.cannot.unpublish()) {
2653
2704
  return ctx.forbidden();
@@ -2656,7 +2707,7 @@ const singleTypes = {
2656
2707
  return ctx.forbidden();
2657
2708
  }
2658
2709
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2659
- const { locale } = getDocumentLocaleAndStatus(body);
2710
+ const { locale } = await getDocumentLocaleAndStatus(body);
2660
2711
  const document = await findDocument(sanitizedQuery, model, { locale });
2661
2712
  if (!document) {
2662
2713
  return ctx.notFound();
@@ -2674,7 +2725,7 @@ const singleTypes = {
2674
2725
  ctx.body = await async.pipe(
2675
2726
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
2676
2727
  permissionChecker2.sanitizeOutput,
2677
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2728
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2678
2729
  )(document);
2679
2730
  });
2680
2731
  },
@@ -2683,13 +2734,12 @@ const singleTypes = {
2683
2734
  const { model } = ctx.params;
2684
2735
  const { body, query = {} } = ctx.request;
2685
2736
  const documentManager2 = getService$1("document-manager");
2686
- const documentMetadata2 = getService$1("document-metadata");
2687
2737
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2688
2738
  if (permissionChecker2.cannot.discard()) {
2689
2739
  return ctx.forbidden();
2690
2740
  }
2691
2741
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2692
- const { locale } = getDocumentLocaleAndStatus(body);
2742
+ const { locale } = await getDocumentLocaleAndStatus(body);
2693
2743
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2694
2744
  if (!document) {
2695
2745
  return ctx.notFound();
@@ -2700,7 +2750,7 @@ const singleTypes = {
2700
2750
  ctx.body = await async.pipe(
2701
2751
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
2702
2752
  permissionChecker2.sanitizeOutput,
2703
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2753
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2704
2754
  )(document);
2705
2755
  },
2706
2756
  async countDraftRelations(ctx) {
@@ -2709,7 +2759,7 @@ const singleTypes = {
2709
2759
  const { query } = ctx.request;
2710
2760
  const documentManager2 = getService$1("document-manager");
2711
2761
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2712
- const { locale } = getDocumentLocaleAndStatus(query);
2762
+ const { locale } = await getDocumentLocaleAndStatus(query);
2713
2763
  if (permissionChecker2.cannot.read()) {
2714
2764
  return ctx.forbidden();
2715
2765
  }
@@ -2730,7 +2780,7 @@ const uid$1 = {
2730
2780
  async generateUID(ctx) {
2731
2781
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2732
2782
  const { query = {} } = ctx.request;
2733
- const { locale } = getDocumentLocaleAndStatus(query);
2783
+ const { locale } = await getDocumentLocaleAndStatus(query);
2734
2784
  await validateUIDField(contentTypeUID, field);
2735
2785
  const uidService = getService$1("uid");
2736
2786
  ctx.body = {
@@ -2742,7 +2792,7 @@ const uid$1 = {
2742
2792
  ctx.request.body
2743
2793
  );
2744
2794
  const { query = {} } = ctx.request;
2745
- const { locale } = getDocumentLocaleAndStatus(query);
2795
+ const { locale } = await getDocumentLocaleAndStatus(query);
2746
2796
  await validateUIDField(contentTypeUID, field);
2747
2797
  const uidService = getService$1("uid");
2748
2798
  const isAvailable = await uidService.checkUIDAvailability({
@@ -3533,7 +3583,7 @@ const permission = ({ strapi: strapi2 }) => ({
3533
3583
  await strapi2.service("admin::permission").actionProvider.registerMany(actions);
3534
3584
  }
3535
3585
  });
3536
- const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils.contentTypes;
3586
+ const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils.contentTypes;
3537
3587
  const { isAnyToMany } = strapiUtils.relations;
3538
3588
  const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils.contentTypes.constants;
3539
3589
  const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
@@ -3624,6 +3674,42 @@ const getDeepPopulate = (uid2, {
3624
3674
  {}
3625
3675
  );
3626
3676
  };
3677
+ const getValidatableFieldsPopulate = (uid2, {
3678
+ initialPopulate = {},
3679
+ countMany = false,
3680
+ countOne = false,
3681
+ maxLevel = Infinity
3682
+ } = {}, level = 1) => {
3683
+ if (level > maxLevel) {
3684
+ return {};
3685
+ }
3686
+ const model = strapi.getModel(uid2);
3687
+ return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
3688
+ if (!getDoesAttributeRequireValidation(attribute)) {
3689
+ return populateAcc;
3690
+ }
3691
+ if (isScalarAttribute(attribute)) {
3692
+ return merge(populateAcc, {
3693
+ [attributeName]: true
3694
+ });
3695
+ }
3696
+ return merge(
3697
+ populateAcc,
3698
+ getPopulateFor(
3699
+ attributeName,
3700
+ model,
3701
+ {
3702
+ // @ts-expect-error - improve types
3703
+ initialPopulate: initialPopulate?.[attributeName],
3704
+ countMany,
3705
+ countOne,
3706
+ maxLevel
3707
+ },
3708
+ level
3709
+ )
3710
+ );
3711
+ }, {});
3712
+ };
3627
3713
  const getDeepPopulateDraftCount = (uid2) => {
3628
3714
  const model = strapi.getModel(uid2);
3629
3715
  let hasRelations = false;
@@ -3645,22 +3731,24 @@ const getDeepPopulateDraftCount = (uid2) => {
3645
3731
  attribute.component
3646
3732
  );
3647
3733
  if (childHasRelations) {
3648
- populateAcc[attributeName] = { populate: populate2 };
3734
+ populateAcc[attributeName] = {
3735
+ populate: populate2
3736
+ };
3649
3737
  hasRelations = true;
3650
3738
  }
3651
3739
  break;
3652
3740
  }
3653
3741
  case "dynamiczone": {
3654
- const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
3655
- const { populate: populate2, hasRelations: childHasRelations } = getDeepPopulateDraftCount(componentUID);
3656
- if (childHasRelations) {
3742
+ const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
3743
+ const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
3744
+ if (componentHasRelations) {
3657
3745
  hasRelations = true;
3658
- return merge(acc, populate2);
3746
+ return { ...acc, [componentUID]: { populate: componentPopulate } };
3659
3747
  }
3660
3748
  return acc;
3661
3749
  }, {});
3662
- if (!isEmpty(dzPopulate)) {
3663
- populateAcc[attributeName] = { populate: dzPopulate };
3750
+ if (!isEmpty(dzPopulateFragment)) {
3751
+ populateAcc[attributeName] = { on: dzPopulateFragment };
3664
3752
  }
3665
3753
  break;
3666
3754
  }
@@ -3852,41 +3940,70 @@ const AVAILABLE_STATUS_FIELDS = [
3852
3940
  "updatedBy",
3853
3941
  "status"
3854
3942
  ];
3855
- const AVAILABLE_LOCALES_FIELDS = ["id", "locale", "updatedAt", "createdAt", "status"];
3943
+ const AVAILABLE_LOCALES_FIELDS = [
3944
+ "id",
3945
+ "locale",
3946
+ "updatedAt",
3947
+ "createdAt",
3948
+ "status",
3949
+ "publishedAt",
3950
+ "documentId"
3951
+ ];
3856
3952
  const CONTENT_MANAGER_STATUS = {
3857
3953
  PUBLISHED: "published",
3858
3954
  DRAFT: "draft",
3859
3955
  MODIFIED: "modified"
3860
3956
  };
3861
- const areDatesEqual = (date1, date2, threshold) => {
3862
- if (!date1 || !date2) {
3957
+ const getIsVersionLatestModification = (version, otherVersion) => {
3958
+ if (!version || !version.updatedAt) {
3863
3959
  return false;
3864
3960
  }
3865
- const time1 = new Date(date1).getTime();
3866
- const time2 = new Date(date2).getTime();
3867
- const difference2 = Math.abs(time1 - time2);
3868
- return difference2 <= threshold;
3961
+ const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
3962
+ const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
3963
+ return versionUpdatedAt > otherUpdatedAt;
3869
3964
  };
3870
3965
  const documentMetadata = ({ strapi: strapi2 }) => ({
3871
3966
  /**
3872
3967
  * Returns available locales of a document for the current status
3873
3968
  */
3874
- getAvailableLocales(uid2, version, allVersions) {
3969
+ async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
3875
3970
  const versionsByLocale = groupBy("locale", allVersions);
3876
3971
  delete versionsByLocale[version.locale];
3877
- return Object.values(versionsByLocale).map((localeVersions) => {
3878
- if (!contentTypes$1.hasDraftAndPublish(strapi2.getModel(uid2))) {
3879
- return pick(AVAILABLE_LOCALES_FIELDS, localeVersions[0]);
3972
+ const model = strapi2.getModel(uid2);
3973
+ const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
3974
+ const traversalFunction = async (localeVersion) => traverseEntity(
3975
+ ({ key }, { remove }) => {
3976
+ if (keysToKeep.includes(key)) {
3977
+ return;
3978
+ }
3979
+ remove(key);
3980
+ },
3981
+ { schema: model, getModel: strapi2.getModel.bind(strapi2) },
3982
+ // @ts-expect-error fix types DocumentVersion incompatible with Data
3983
+ localeVersion
3984
+ );
3985
+ const mappingResult = await async.map(
3986
+ Object.values(versionsByLocale),
3987
+ async (localeVersions) => {
3988
+ const mappedLocaleVersions = await async.map(
3989
+ localeVersions,
3990
+ traversalFunction
3991
+ );
3992
+ if (!contentTypes$1.hasDraftAndPublish(model)) {
3993
+ return mappedLocaleVersions[0];
3994
+ }
3995
+ const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
3996
+ const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
3997
+ if (!draftVersion) {
3998
+ return;
3999
+ }
4000
+ return {
4001
+ ...draftVersion,
4002
+ status: this.getStatus(draftVersion, otherVersions)
4003
+ };
3880
4004
  }
3881
- const draftVersion = localeVersions.find((v) => v.publishedAt === null);
3882
- const otherVersions = localeVersions.filter((v) => v.id !== draftVersion?.id);
3883
- if (!draftVersion)
3884
- return;
3885
- return {
3886
- ...pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
3887
- status: this.getStatus(draftVersion, otherVersions)
3888
- };
3889
- }).filter(Boolean);
4005
+ );
4006
+ return mappingResult.filter(Boolean);
3890
4007
  },
3891
4008
  /**
3892
4009
  * Returns available status of a document for the current locale
@@ -3924,26 +4041,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3924
4041
  });
3925
4042
  },
3926
4043
  getStatus(version, otherDocumentStatuses) {
3927
- const isDraft = version.publishedAt === null;
3928
- if (!otherDocumentStatuses?.length) {
3929
- return isDraft ? CONTENT_MANAGER_STATUS.DRAFT : CONTENT_MANAGER_STATUS.PUBLISHED;
4044
+ let draftVersion;
4045
+ let publishedVersion;
4046
+ if (version.publishedAt) {
4047
+ publishedVersion = version;
4048
+ } else {
4049
+ draftVersion = version;
3930
4050
  }
3931
- if (isDraft) {
3932
- const publishedVersion = otherDocumentStatuses?.find((d) => d.publishedAt !== null);
3933
- if (!publishedVersion) {
3934
- return CONTENT_MANAGER_STATUS.DRAFT;
3935
- }
4051
+ const otherVersion = otherDocumentStatuses?.at(0);
4052
+ if (otherVersion?.publishedAt) {
4053
+ publishedVersion = otherVersion;
4054
+ } else if (otherVersion) {
4055
+ draftVersion = otherVersion;
3936
4056
  }
3937
- if (areDatesEqual(version.updatedAt, otherDocumentStatuses.at(0)?.updatedAt, 500)) {
4057
+ if (!draftVersion)
3938
4058
  return CONTENT_MANAGER_STATUS.PUBLISHED;
3939
- }
3940
- return CONTENT_MANAGER_STATUS.MODIFIED;
4059
+ if (!publishedVersion)
4060
+ return CONTENT_MANAGER_STATUS.DRAFT;
4061
+ const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
4062
+ return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
3941
4063
  },
4064
+ // TODO is it necessary to return metadata on every page of the CM
4065
+ // We could refactor this so the locales are only loaded when they're
4066
+ // needed. e.g. in the bulk locale action modal.
3942
4067
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4068
+ const populate = getValidatableFieldsPopulate(uid2);
3943
4069
  const versions = await strapi2.db.query(uid2).findMany({
3944
4070
  where: { documentId: version.documentId },
3945
- select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
3946
4071
  populate: {
4072
+ // Populate only fields that require validation for bulk locale actions
4073
+ ...populate,
4074
+ // NOTE: creator fields are selected in this way to avoid exposing sensitive data
3947
4075
  createdBy: {
3948
4076
  select: ["id", "firstname", "lastname", "email"]
3949
4077
  },
@@ -3952,7 +4080,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3952
4080
  }
3953
4081
  }
3954
4082
  });
3955
- const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
4083
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
3956
4084
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
3957
4085
  return {
3958
4086
  availableLocales: availableLocalesResult,
@@ -3965,8 +4093,9 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3965
4093
  * - Available status of the document for the current locale
3966
4094
  */
3967
4095
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
3968
- if (!document)
4096
+ if (!document) {
3969
4097
  return document;
4098
+ }
3970
4099
  const hasDraftAndPublish = contentTypes$1.hasDraftAndPublish(strapi2.getModel(uid2));
3971
4100
  if (!hasDraftAndPublish) {
3972
4101
  opts.availableStatus = false;
@@ -4016,26 +4145,9 @@ const sumDraftCounts = (entity, uid2) => {
4016
4145
  }, 0);
4017
4146
  };
4018
4147
  const { ApplicationError } = errors;
4019
- const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
4020
4148
  const { PUBLISHED_AT_ATTRIBUTE } = contentTypes$1.constants;
4021
4149
  const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE);
4022
4150
  const omitIdField = omit("id");
4023
- const emitEvent = async (uid2, event, document) => {
4024
- const modelDef = strapi.getModel(uid2);
4025
- const sanitizedDocument = await sanitize.sanitizers.defaultSanitizeOutput(
4026
- {
4027
- schema: modelDef,
4028
- getModel(uid22) {
4029
- return strapi.getModel(uid22);
4030
- }
4031
- },
4032
- document
4033
- );
4034
- strapi.eventHub.emit(event, {
4035
- model: modelDef.modelName,
4036
- entry: sanitizedDocument
4037
- });
4038
- };
4039
4151
  const documentManager = ({ strapi: strapi2 }) => {
4040
4152
  return {
4041
4153
  async findOne(id, uid2, opts = {}) {
@@ -4054,6 +4166,9 @@ const documentManager = ({ strapi: strapi2 }) => {
4054
4166
  } else if (opts.locale && opts.locale !== "*") {
4055
4167
  where.locale = opts.locale;
4056
4168
  }
4169
+ if (typeof opts.isPublished === "boolean") {
4170
+ where.publishedAt = { $notNull: opts.isPublished };
4171
+ }
4057
4172
  return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
4058
4173
  },
4059
4174
  async findMany(opts, uid2) {
@@ -4116,70 +4231,36 @@ const documentManager = ({ strapi: strapi2 }) => {
4116
4231
  return {};
4117
4232
  },
4118
4233
  // FIXME: handle relations
4119
- async deleteMany(opts, uid2) {
4120
- const docs = await strapi2.documents(uid2).findMany(opts);
4121
- for (const doc of docs) {
4122
- await strapi2.documents(uid2).delete({ documentId: doc.documentId });
4123
- }
4124
- return { count: docs.length };
4234
+ async deleteMany(documentIds, uid2, opts = {}) {
4235
+ const deletedEntries = await strapi2.db.transaction(async () => {
4236
+ return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
4237
+ });
4238
+ return { count: deletedEntries.length };
4125
4239
  },
4126
4240
  async publish(id, uid2, opts = {}) {
4127
4241
  const populate = await buildDeepPopulate(uid2);
4128
4242
  const params = { ...opts, populate };
4129
- return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries.at(0));
4243
+ return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
4130
4244
  },
4131
- async publishMany(entities, uid2) {
4132
- if (!entities.length) {
4133
- return null;
4134
- }
4135
- await Promise.all(
4136
- entities.map((document) => {
4137
- return strapi2.entityValidator.validateEntityCreation(
4138
- strapi2.getModel(uid2),
4139
- document,
4140
- void 0,
4141
- // @ts-expect-error - FIXME: entity here is unnecessary
4142
- document
4143
- );
4144
- })
4145
- );
4146
- const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4147
- const filters = { id: { $in: entitiesToPublish } };
4148
- const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
4149
- const populate = await buildDeepPopulate(uid2);
4150
- const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4151
- where: filters,
4152
- data
4153
- });
4154
- const publishedEntities = await strapi2.db.query(uid2).findMany({
4155
- where: filters,
4156
- populate
4245
+ async publishMany(uid2, documentIds, locale) {
4246
+ return strapi2.db.transaction(async () => {
4247
+ const results = await Promise.all(
4248
+ documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
4249
+ );
4250
+ const publishedEntitiesCount = results.flat().filter(Boolean).length;
4251
+ return publishedEntitiesCount;
4157
4252
  });
4158
- await Promise.all(
4159
- publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
4160
- );
4161
- return publishedEntitiesCount;
4162
4253
  },
4163
- async unpublishMany(documents, uid2) {
4164
- if (!documents.length) {
4165
- return null;
4166
- }
4167
- const entitiesToUnpublish = documents.filter((doc) => doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4168
- const filters = { id: { $in: entitiesToUnpublish } };
4169
- const data = { [PUBLISHED_AT_ATTRIBUTE]: null };
4170
- const populate = await buildDeepPopulate(uid2);
4171
- const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4172
- where: filters,
4173
- data
4174
- });
4175
- const unpublishedEntities = await strapi2.db.query(uid2).findMany({
4176
- where: filters,
4177
- populate
4254
+ async unpublishMany(documentIds, uid2, opts = {}) {
4255
+ const unpublishedEntries = await strapi2.db.transaction(async () => {
4256
+ return Promise.all(
4257
+ documentIds.map(
4258
+ (id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
4259
+ )
4260
+ );
4178
4261
  });
4179
- await Promise.all(
4180
- unpublishedEntities.map((doc) => emitEvent(uid2, ENTRY_UNPUBLISH, doc))
4181
- );
4182
- return unpublishedEntitiesCount;
4262
+ const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
4263
+ return { count: unpublishedEntitiesCount };
4183
4264
  },
4184
4265
  async unpublish(id, uid2, opts = {}) {
4185
4266
  const populate = await buildDeepPopulate(uid2);
@@ -4204,16 +4285,20 @@ const documentManager = ({ strapi: strapi2 }) => {
4204
4285
  }
4205
4286
  return sumDraftCounts(document, uid2);
4206
4287
  },
4207
- async countManyEntriesDraftRelations(ids, uid2, locale) {
4288
+ async countManyEntriesDraftRelations(documentIds, uid2, locale) {
4208
4289
  const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
4209
4290
  if (!hasRelations) {
4210
4291
  return 0;
4211
4292
  }
4293
+ let localeFilter = {};
4294
+ if (locale) {
4295
+ localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
4296
+ }
4212
4297
  const entities = await strapi2.db.query(uid2).findMany({
4213
4298
  populate,
4214
4299
  where: {
4215
- id: { $in: ids },
4216
- ...locale ? { locale } : {}
4300
+ documentId: { $in: documentIds },
4301
+ ...localeFilter
4217
4302
  }
4218
4303
  });
4219
4304
  const totalNumberDraftRelations = entities.reduce(