@strapi/content-manager 0.0.0-experimental.a65a85fdea97faae8679d3ffc5f9d79af61abd26 → 0.0.0-experimental.c3e9d4b26f9fd3d9eb530b5c11f9baa1d09b13ad

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 (176) 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--2aLCv-G.mjs → ComponentConfigurationPage-BMajAl1u.mjs} +3 -3
  6. package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs.map → ComponentConfigurationPage-BMajAl1u.mjs.map} +1 -1
  7. package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js → ComponentConfigurationPage-y_7iLdmB.js} +3 -3
  8. package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js.map → ComponentConfigurationPage-y_7iLdmB.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-BfFzJ4Br.js → EditConfigurationPage-CPVB8Uqc.js} +3 -3
  14. package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js.map → EditConfigurationPage-CPVB8Uqc.js.map} +1 -1
  15. package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs → EditConfigurationPage-CcOoD26O.mjs} +3 -3
  16. package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs.map → EditConfigurationPage-CcOoD26O.mjs.map} +1 -1
  17. package/dist/_chunks/{EditViewPage-CzOT5Kpj.js → EditViewPage-CTTDHKkQ.js} +7 -9
  18. package/dist/_chunks/EditViewPage-CTTDHKkQ.js.map +1 -0
  19. package/dist/_chunks/{EditViewPage-Bm8lgcm6.mjs → EditViewPage-DWb0DE7R.mjs} +6 -6
  20. package/dist/_chunks/EditViewPage-DWb0DE7R.mjs.map +1 -0
  21. package/dist/_chunks/{Field-Caef4JjM.js → Field-C5Z1Ivdv.js} +552 -661
  22. package/dist/_chunks/Field-C5Z1Ivdv.js.map +1 -0
  23. package/dist/_chunks/{Field-Dlh0uGnL.mjs → Field-DnStdvQw.mjs} +500 -608
  24. package/dist/_chunks/Field-DnStdvQw.mjs.map +1 -0
  25. package/dist/_chunks/{Form-BzuAjtRq.js → Form-B81OtW-k.js} +21 -19
  26. package/dist/_chunks/Form-B81OtW-k.js.map +1 -0
  27. package/dist/_chunks/{Form-EnaQL_6L.mjs → Form-DqGgE55Q.mjs} +21 -18
  28. package/dist/_chunks/Form-DqGgE55Q.mjs.map +1 -0
  29. package/dist/_chunks/{History-C17LiyRg.js → History-4NbOq2dX.js} +119 -49
  30. package/dist/_chunks/History-4NbOq2dX.js.map +1 -0
  31. package/dist/_chunks/{History-D6sbCJvo.mjs → History-DS6-HCYX.mjs} +119 -48
  32. package/dist/_chunks/History-DS6-HCYX.mjs.map +1 -0
  33. package/dist/_chunks/{ListConfigurationPage-Dks5SX6f.js → ListConfigurationPage-CpfstlYY.js} +17 -19
  34. package/dist/_chunks/ListConfigurationPage-CpfstlYY.js.map +1 -0
  35. package/dist/_chunks/{ListConfigurationPage-Ce4qs7qE.mjs → ListConfigurationPage-DQJJltko.mjs} +14 -14
  36. package/dist/_chunks/ListConfigurationPage-DQJJltko.mjs.map +1 -0
  37. package/dist/_chunks/{ListViewPage-BwrZrPsh.js → ListViewPage-CA3I75m5.js} +43 -51
  38. package/dist/_chunks/ListViewPage-CA3I75m5.js.map +1 -0
  39. package/dist/_chunks/{ListViewPage-Be7S5aKL.mjs → ListViewPage-nQrOQuVo.mjs} +37 -45
  40. package/dist/_chunks/ListViewPage-nQrOQuVo.mjs.map +1 -0
  41. package/dist/_chunks/{NoContentTypePage-CIPmYQMm.mjs → NoContentTypePage-DbnHE22g.mjs} +7 -7
  42. package/dist/_chunks/NoContentTypePage-DbnHE22g.mjs.map +1 -0
  43. package/dist/_chunks/{NoContentTypePage-Cu5r1-JT.js → NoContentTypePage-Dldu-_Mx.js} +5 -5
  44. package/dist/_chunks/NoContentTypePage-Dldu-_Mx.js.map +1 -0
  45. package/dist/_chunks/{NoPermissionsPage-C-j6TEUF.js → NoPermissionsPage-CO2MK200.js} +4 -5
  46. package/dist/_chunks/NoPermissionsPage-CO2MK200.js.map +1 -0
  47. package/dist/_chunks/{NoPermissionsPage-DhJ7LYrr.mjs → NoPermissionsPage-fOIkQM0v.mjs} +5 -6
  48. package/dist/_chunks/NoPermissionsPage-fOIkQM0v.mjs.map +1 -0
  49. package/dist/_chunks/{Relations-CY7AtkDA.mjs → Relations-BDRl99Ux.mjs} +66 -56
  50. package/dist/_chunks/Relations-BDRl99Ux.mjs.map +1 -0
  51. package/dist/_chunks/{Relations-Czs-uZ-s.js → Relations-DG2jnOcr.js} +70 -61
  52. package/dist/_chunks/Relations-DG2jnOcr.js.map +1 -0
  53. package/dist/_chunks/{en-MBPul9Su.mjs → en-Ux26r5pl.mjs} +7 -1
  54. package/dist/_chunks/{en-MBPul9Su.mjs.map → en-Ux26r5pl.mjs.map} +1 -1
  55. package/dist/_chunks/{en-C-V1_90f.js → en-fbKQxLGn.js} +7 -1
  56. package/dist/_chunks/{en-C-V1_90f.js.map → en-fbKQxLGn.js.map} +1 -1
  57. package/dist/_chunks/{index-X_2tafck.js → index-BZoNZMXL.js} +1556 -795
  58. package/dist/_chunks/index-BZoNZMXL.js.map +1 -0
  59. package/dist/_chunks/{index-DNVx8ssZ.mjs → index-Drt2DN7v.mjs} +1569 -807
  60. package/dist/_chunks/index-Drt2DN7v.mjs.map +1 -0
  61. package/dist/_chunks/{layout-Dnh0PNp9.mjs → layout-BzAbmoO6.mjs} +27 -22
  62. package/dist/_chunks/layout-BzAbmoO6.mjs.map +1 -0
  63. package/dist/_chunks/{layout-dBc7wN7L.js → layout-DEYBqgF1.js} +28 -25
  64. package/dist/_chunks/layout-DEYBqgF1.js.map +1 -0
  65. package/dist/_chunks/{relations-4pHtBrHJ.js → relations-D0eZ4VWw.js} +2 -2
  66. package/dist/_chunks/{relations-4pHtBrHJ.js.map → relations-D0eZ4VWw.js.map} +1 -1
  67. package/dist/_chunks/{relations-Dx7tMKJN.mjs → relations-D26zVRdi.mjs} +2 -2
  68. package/dist/_chunks/{relations-Dx7tMKJN.mjs.map → relations-D26zVRdi.mjs.map} +1 -1
  69. package/dist/_chunks/useDragAndDrop-DdHgKsqq.mjs.map +1 -1
  70. package/dist/_chunks/useDragAndDrop-J0TUUbR6.js.map +1 -1
  71. package/dist/_chunks/usePrev-B9w_-eYc.js +15 -0
  72. package/dist/_chunks/usePrev-B9w_-eYc.js.map +1 -0
  73. package/dist/_chunks/usePrev-DH6iah0A.mjs +16 -0
  74. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +1 -0
  75. package/dist/admin/index.js +2 -1
  76. package/dist/admin/index.js.map +1 -1
  77. package/dist/admin/index.mjs +8 -7
  78. package/dist/admin/src/components/ComponentIcon.d.ts +6 -3
  79. package/dist/admin/src/content-manager.d.ts +3 -3
  80. package/dist/admin/src/exports.d.ts +1 -0
  81. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
  82. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  83. package/dist/admin/src/hooks/useDocument.d.ts +5 -8
  84. package/dist/admin/src/hooks/useDocumentActions.d.ts +24 -3
  85. package/dist/admin/src/hooks/useDocumentLayout.d.ts +2 -2
  86. package/dist/admin/src/hooks/useDragAndDrop.d.ts +4 -4
  87. package/dist/admin/src/hooks/useKeyboardDragAndDrop.d.ts +1 -1
  88. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +3 -1
  89. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksInput.d.ts +3 -3
  90. package/dist/admin/src/pages/EditView/components/FormInputs/Component/Input.d.ts +2 -2
  91. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +3 -5
  92. package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
  93. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +10 -18
  94. package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
  95. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +3 -49
  96. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
  97. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +67 -52
  98. package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
  99. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +9 -26
  100. package/dist/admin/src/services/api.d.ts +2 -3
  101. package/dist/admin/src/services/components.d.ts +2 -2
  102. package/dist/admin/src/services/contentTypes.d.ts +5 -5
  103. package/dist/admin/src/services/documents.d.ts +29 -17
  104. package/dist/admin/src/services/init.d.ts +2 -2
  105. package/dist/admin/src/services/relations.d.ts +3 -3
  106. package/dist/admin/src/services/uid.d.ts +3 -3
  107. package/dist/admin/src/utils/api.d.ts +4 -18
  108. package/dist/admin/src/utils/validation.d.ts +1 -6
  109. package/dist/server/index.js +540 -414
  110. package/dist/server/index.js.map +1 -1
  111. package/dist/server/index.mjs +548 -422
  112. package/dist/server/index.mjs.map +1 -1
  113. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  114. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  115. package/dist/server/src/controllers/utils/metadata.d.ts +8 -0
  116. package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
  117. package/dist/server/src/controllers/validation/dimensions.d.ts +9 -0
  118. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
  119. package/dist/server/src/controllers/validation/index.d.ts +1 -1
  120. package/dist/server/src/history/services/history.d.ts +2 -4
  121. package/dist/server/src/history/services/history.d.ts.map +1 -1
  122. package/dist/server/src/history/services/index.d.ts +6 -2
  123. package/dist/server/src/history/services/index.d.ts.map +1 -1
  124. package/dist/server/src/history/services/lifecycles.d.ts +9 -0
  125. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -0
  126. package/dist/server/src/history/services/utils.d.ts +41 -9
  127. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  128. package/dist/server/src/history/utils.d.ts +6 -2
  129. package/dist/server/src/history/utils.d.ts.map +1 -1
  130. package/dist/server/src/index.d.ts +18 -39
  131. package/dist/server/src/index.d.ts.map +1 -1
  132. package/dist/server/src/services/document-manager.d.ts +13 -12
  133. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  134. package/dist/server/src/services/document-metadata.d.ts +8 -29
  135. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  136. package/dist/server/src/services/index.d.ts +18 -39
  137. package/dist/server/src/services/index.d.ts.map +1 -1
  138. package/dist/server/src/services/utils/populate.d.ts +8 -1
  139. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  140. package/dist/shared/contracts/collection-types.d.ts +14 -6
  141. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  142. package/dist/shared/contracts/relations.d.ts +2 -2
  143. package/dist/shared/contracts/relations.d.ts.map +1 -1
  144. package/package.json +13 -14
  145. package/dist/_chunks/CardDragPreview-DSVYodBX.js.map +0 -1
  146. package/dist/_chunks/CardDragPreview-ikSG4M46.mjs.map +0 -1
  147. package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
  148. package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
  149. package/dist/_chunks/EditViewPage-Bm8lgcm6.mjs.map +0 -1
  150. package/dist/_chunks/EditViewPage-CzOT5Kpj.js.map +0 -1
  151. package/dist/_chunks/Field-Caef4JjM.js.map +0 -1
  152. package/dist/_chunks/Field-Dlh0uGnL.mjs.map +0 -1
  153. package/dist/_chunks/Form-BzuAjtRq.js.map +0 -1
  154. package/dist/_chunks/Form-EnaQL_6L.mjs.map +0 -1
  155. package/dist/_chunks/History-C17LiyRg.js.map +0 -1
  156. package/dist/_chunks/History-D6sbCJvo.mjs.map +0 -1
  157. package/dist/_chunks/ListConfigurationPage-Ce4qs7qE.mjs.map +0 -1
  158. package/dist/_chunks/ListConfigurationPage-Dks5SX6f.js.map +0 -1
  159. package/dist/_chunks/ListViewPage-Be7S5aKL.mjs.map +0 -1
  160. package/dist/_chunks/ListViewPage-BwrZrPsh.js.map +0 -1
  161. package/dist/_chunks/NoContentTypePage-CIPmYQMm.mjs.map +0 -1
  162. package/dist/_chunks/NoContentTypePage-Cu5r1-JT.js.map +0 -1
  163. package/dist/_chunks/NoPermissionsPage-C-j6TEUF.js.map +0 -1
  164. package/dist/_chunks/NoPermissionsPage-DhJ7LYrr.mjs.map +0 -1
  165. package/dist/_chunks/Relations-CY7AtkDA.mjs.map +0 -1
  166. package/dist/_chunks/Relations-Czs-uZ-s.js.map +0 -1
  167. package/dist/_chunks/index-DNVx8ssZ.mjs.map +0 -1
  168. package/dist/_chunks/index-X_2tafck.js.map +0 -1
  169. package/dist/_chunks/layout-Dnh0PNp9.mjs.map +0 -1
  170. package/dist/_chunks/layout-dBc7wN7L.js.map +0 -1
  171. package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
  172. package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
  173. package/dist/_chunks/urls-DzZya_gm.js +0 -6
  174. package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
  175. package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
  176. package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
- import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1, sanitize } from "@strapi/utils";
2
- import { pick, omit, difference, intersection, pipe, propOr, isEqual, isEmpty, set, isNil as isNil$1, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, getOr, propEq, merge, groupBy, castArray } from "lodash/fp";
1
+ import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1, traverseEntity, pagination } from "@strapi/utils";
2
+ import { pick, omit, difference, 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";
@@ -54,7 +54,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
54
54
  return ctx.forbidden();
55
55
  }
56
56
  const query = await permissionChecker2.sanitizeQuery(ctx.query);
57
- const { results, pagination } = await getService(strapi2, "history").findVersionsPage({
57
+ const { results, pagination: pagination2 } = await getService(strapi2, "history").findVersionsPage({
58
58
  query: {
59
59
  ...query,
60
60
  ...getValidPagination({ page: query.page, pageSize: query.pageSize })
@@ -73,7 +73,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
73
73
  );
74
74
  return {
75
75
  data: sanitizedResults,
76
- meta: { pagination }
76
+ meta: { pagination: pagination2 }
77
77
  };
78
78
  },
79
79
  async restoreVersion(ctx) {
@@ -112,40 +112,65 @@ const FIELDS_TO_IGNORE = [
112
112
  "strapi_stage",
113
113
  "strapi_assignee"
114
114
  ];
115
- const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
116
- const sanitizedContentTypeSchemaAttributes = omit(FIELDS_TO_IGNORE, contentTypeSchemaAttributes);
117
- const reduceDifferenceToAttributesObject = (diffKeys, source) => {
118
- return diffKeys.reduce((previousAttributesObject, diffKey) => {
119
- previousAttributesObject[diffKey] = source[diffKey];
120
- return previousAttributesObject;
121
- }, {});
122
- };
123
- const versionSchemaKeys = Object.keys(versionSchemaAttributes);
124
- const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
125
- const uniqueToContentType = difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
126
- const added = reduceDifferenceToAttributesObject(
127
- uniqueToContentType,
128
- sanitizedContentTypeSchemaAttributes
129
- );
130
- const uniqueToVersion = difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
131
- const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
132
- return { added, removed };
133
- };
134
115
  const DEFAULT_RETENTION_DAYS = 90;
135
- const createHistoryService = ({ strapi: strapi2 }) => {
136
- const state = {
137
- deleteExpiredJob: null,
138
- isInitialized: false
116
+ const createServiceUtils = ({ strapi: strapi2 }) => {
117
+ const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
118
+ const sanitizedContentTypeSchemaAttributes = omit(
119
+ FIELDS_TO_IGNORE,
120
+ contentTypeSchemaAttributes
121
+ );
122
+ const reduceDifferenceToAttributesObject = (diffKeys, source) => {
123
+ return diffKeys.reduce(
124
+ (previousAttributesObject, diffKey) => {
125
+ previousAttributesObject[diffKey] = source[diffKey];
126
+ return previousAttributesObject;
127
+ },
128
+ {}
129
+ );
130
+ };
131
+ const versionSchemaKeys = Object.keys(versionSchemaAttributes);
132
+ const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
133
+ const uniqueToContentType = difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
134
+ const added = reduceDifferenceToAttributesObject(
135
+ uniqueToContentType,
136
+ sanitizedContentTypeSchemaAttributes
137
+ );
138
+ const uniqueToVersion = difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
139
+ const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
140
+ return { added, removed };
139
141
  };
140
- const query = strapi2.db.query(HISTORY_VERSION_UID);
141
- const getRetentionDays = (strapi22) => {
142
- const featureConfig = strapi22.ee.features.get("cms-content-history");
143
- const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
144
- const userRetentionDays = strapi22.config.get("admin.history.retentionDays");
145
- if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
146
- return userRetentionDays;
142
+ const getRelationRestoreValue = async (versionRelationData, attribute) => {
143
+ if (Array.isArray(versionRelationData)) {
144
+ if (versionRelationData.length === 0)
145
+ return versionRelationData;
146
+ const existingAndMissingRelations = await Promise.all(
147
+ versionRelationData.map((relation) => {
148
+ return strapi2.documents(attribute.target).findOne({
149
+ documentId: relation.documentId,
150
+ locale: relation.locale || void 0
151
+ });
152
+ })
153
+ );
154
+ return existingAndMissingRelations.filter(
155
+ (relation) => relation !== null
156
+ );
147
157
  }
148
- return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
158
+ return strapi2.documents(attribute.target).findOne({
159
+ documentId: versionRelationData.documentId,
160
+ locale: versionRelationData.locale || void 0
161
+ });
162
+ };
163
+ const getMediaRestoreValue = async (versionRelationData, attribute) => {
164
+ if (attribute.multiple) {
165
+ const existingAndMissingMedias = await Promise.all(
166
+ // @ts-expect-error Fix the type definitions so this isn't any
167
+ versionRelationData.map((media) => {
168
+ return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
169
+ })
170
+ );
171
+ return existingAndMissingMedias.filter((media) => media != null);
172
+ }
173
+ return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
149
174
  };
150
175
  const localesService = strapi2.plugin("i18n")?.service("locales");
151
176
  const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
@@ -161,6 +186,15 @@ const createHistoryService = ({ strapi: strapi2 }) => {
161
186
  {}
162
187
  );
163
188
  };
189
+ const getRetentionDays = () => {
190
+ const featureConfig = strapi2.ee.features.get("cms-content-history");
191
+ const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
192
+ const userRetentionDays = strapi2.config.get("admin.history.retentionDays");
193
+ if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
194
+ return userRetentionDays;
195
+ }
196
+ return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
197
+ };
164
198
  const getVersionStatus = async (contentTypeUid, document) => {
165
199
  const documentMetadataService = strapi2.plugin("content-manager").service("document-metadata");
166
200
  const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
@@ -202,80 +236,68 @@ const createHistoryService = ({ strapi: strapi2 }) => {
202
236
  return acc;
203
237
  }, {});
204
238
  };
205
- return {
206
- async bootstrap() {
207
- if (state.isInitialized) {
208
- return;
209
- }
210
- strapi2.documents.use(async (context, next) => {
211
- if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
212
- return next();
239
+ const buildMediaResponse = async (values) => {
240
+ return values.slice(0, 25).reduce(
241
+ async (currentRelationDataPromise, entry) => {
242
+ const currentRelationData = await currentRelationDataPromise;
243
+ if (!entry) {
244
+ return currentRelationData;
213
245
  }
214
- if (context.action !== "create" && context.action !== "update" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
215
- return next();
246
+ const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
247
+ if (relatedEntry) {
248
+ currentRelationData.results.push(relatedEntry);
249
+ } else {
250
+ currentRelationData.meta.missingCount += 1;
216
251
  }
217
- const contentTypeUid = context.contentType.uid;
218
- if (!contentTypeUid.startsWith("api::")) {
219
- return next();
252
+ return currentRelationData;
253
+ },
254
+ Promise.resolve({
255
+ results: [],
256
+ meta: { missingCount: 0 }
257
+ })
258
+ );
259
+ };
260
+ const buildRelationReponse = async (values, attributeSchema) => {
261
+ return values.slice(0, 25).reduce(
262
+ async (currentRelationDataPromise, entry) => {
263
+ const currentRelationData = await currentRelationDataPromise;
264
+ if (!entry) {
265
+ return currentRelationData;
220
266
  }
221
- const result = await next();
222
- const documentContext = context.action === "create" ? { documentId: result.documentId, locale: context.params?.locale } : { documentId: context.params.documentId, locale: context.params?.locale };
223
- const defaultLocale = await getDefaultLocale();
224
- const locale = documentContext.locale || defaultLocale;
225
- const document = await strapi2.documents(contentTypeUid).findOne({
226
- documentId: documentContext.documentId,
227
- locale,
228
- populate: getDeepPopulate2(contentTypeUid)
229
- });
230
- const status = await getVersionStatus(contentTypeUid, document);
231
- const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
232
- const componentsSchemas = Object.keys(
233
- attributesSchema
234
- ).reduce((currentComponentSchemas, key) => {
235
- const fieldSchema = attributesSchema[key];
236
- if (fieldSchema.type === "component") {
237
- const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
238
- return {
239
- ...currentComponentSchemas,
240
- [fieldSchema.component]: componentSchema
241
- };
242
- }
243
- return currentComponentSchemas;
244
- }, {});
245
- await strapi2.db.transaction(async ({ onCommit }) => {
246
- onCommit(() => {
247
- this.createVersion({
248
- contentType: contentTypeUid,
249
- data: omit(FIELDS_TO_IGNORE, document),
250
- schema: omit(FIELDS_TO_IGNORE, attributesSchema),
251
- componentsSchemas,
252
- relatedDocumentId: documentContext.documentId,
253
- locale,
254
- status
255
- });
267
+ const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
268
+ if (relatedEntry) {
269
+ currentRelationData.results.push({
270
+ ...relatedEntry,
271
+ status: await getVersionStatus(attributeSchema.target, relatedEntry)
256
272
  });
257
- });
258
- return result;
259
- });
260
- const retentionDays = getRetentionDays(strapi2);
261
- state.deleteExpiredJob = scheduleJob("0 0 * * *", () => {
262
- const retentionDaysInMilliseconds = retentionDays * 24 * 60 * 60 * 1e3;
263
- const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
264
- query.deleteMany({
265
- where: {
266
- created_at: {
267
- $lt: expirationDate.toISOString()
268
- }
269
- }
270
- });
271
- });
272
- state.isInitialized = true;
273
- },
274
- async destroy() {
275
- if (state.deleteExpiredJob) {
276
- state.deleteExpiredJob.cancel();
277
- }
278
- },
273
+ } else {
274
+ currentRelationData.meta.missingCount += 1;
275
+ }
276
+ return currentRelationData;
277
+ },
278
+ Promise.resolve({
279
+ results: [],
280
+ meta: { missingCount: 0 }
281
+ })
282
+ );
283
+ };
284
+ return {
285
+ getSchemaAttributesDiff,
286
+ getRelationRestoreValue,
287
+ getMediaRestoreValue,
288
+ getDefaultLocale,
289
+ getLocaleDictionary,
290
+ getRetentionDays,
291
+ getVersionStatus,
292
+ getDeepPopulate: getDeepPopulate2,
293
+ buildMediaResponse,
294
+ buildRelationReponse
295
+ };
296
+ };
297
+ const createHistoryService = ({ strapi: strapi2 }) => {
298
+ const query = strapi2.db.query(HISTORY_VERSION_UID);
299
+ const serviceUtils = createServiceUtils({ strapi: strapi2 });
300
+ return {
279
301
  async createVersion(historyVersionData) {
280
302
  await query.create({
281
303
  data: {
@@ -286,8 +308,8 @@ const createHistoryService = ({ strapi: strapi2 }) => {
286
308
  });
287
309
  },
288
310
  async findVersionsPage(params) {
289
- const locale = params.query.locale || await getDefaultLocale();
290
- const [{ results, pagination }, localeDictionary] = await Promise.all([
311
+ const locale = params.query.locale || await serviceUtils.getDefaultLocale();
312
+ const [{ results, pagination: pagination2 }, localeDictionary] = await Promise.all([
291
313
  query.findPage({
292
314
  ...params.query,
293
315
  where: {
@@ -300,78 +322,34 @@ const createHistoryService = ({ strapi: strapi2 }) => {
300
322
  populate: ["createdBy"],
301
323
  orderBy: [{ createdAt: "desc" }]
302
324
  }),
303
- getLocaleDictionary()
325
+ serviceUtils.getLocaleDictionary()
304
326
  ]);
305
- const buildRelationReponse = async (values, attributeSchema) => {
306
- return values.slice(0, 25).reduce(
307
- async (currentRelationDataPromise, entry) => {
308
- const currentRelationData = await currentRelationDataPromise;
309
- if (!entry) {
310
- return currentRelationData;
311
- }
312
- const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
313
- const permissionChecker2 = getService$1("permission-checker").create({
314
- userAbility: params.state.userAbility,
315
- model: attributeSchema.target
316
- });
317
- const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
318
- if (sanitizedEntry) {
319
- currentRelationData.results.push({
320
- ...sanitizedEntry,
321
- status: await getVersionStatus(attributeSchema.target, sanitizedEntry)
322
- });
323
- } else {
324
- currentRelationData.meta.missingCount += 1;
325
- }
326
- return currentRelationData;
327
- },
328
- Promise.resolve({
329
- results: [],
330
- meta: { missingCount: 0 }
331
- })
332
- );
333
- };
334
- const buildMediaResponse = async (values) => {
335
- return values.slice(0, 25).reduce(
336
- async (currentRelationDataPromise, entry) => {
337
- const currentRelationData = await currentRelationDataPromise;
338
- if (!entry) {
339
- return currentRelationData;
340
- }
341
- const permissionChecker2 = getService$1("permission-checker").create({
342
- userAbility: params.state.userAbility,
343
- model: "plugin::upload.file"
344
- });
345
- const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
346
- const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
347
- if (sanitizedEntry) {
348
- currentRelationData.results.push(sanitizedEntry);
349
- } else {
350
- currentRelationData.meta.missingCount += 1;
351
- }
352
- return currentRelationData;
353
- },
354
- Promise.resolve({
355
- results: [],
356
- meta: { missingCount: 0 }
357
- })
358
- );
359
- };
360
327
  const populateEntryRelations = async (entry) => {
361
328
  const entryWithRelations = await Object.entries(entry.schema).reduce(
362
329
  async (currentDataWithRelations, [attributeKey, attributeSchema]) => {
363
330
  const attributeValue = entry.data[attributeKey];
364
331
  const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
365
332
  if (attributeSchema.type === "media") {
333
+ const permissionChecker2 = getService$1("permission-checker").create({
334
+ userAbility: params.state.userAbility,
335
+ model: "plugin::upload.file"
336
+ });
337
+ const response = await serviceUtils.buildMediaResponse(attributeValues);
338
+ const sanitizedResults = await Promise.all(
339
+ response.results.map((media) => permissionChecker2.sanitizeOutput(media))
340
+ );
366
341
  return {
367
342
  ...await currentDataWithRelations,
368
- [attributeKey]: await buildMediaResponse(attributeValues)
343
+ [attributeKey]: {
344
+ results: sanitizedResults,
345
+ meta: response.meta
346
+ }
369
347
  };
370
348
  }
371
349
  if (attributeSchema.type === "relation" && attributeSchema.relation !== "morphToOne" && attributeSchema.relation !== "morphToMany") {
372
350
  if (attributeSchema.target === "admin::user") {
373
351
  const adminUsers = await Promise.all(
374
- attributeValues.map(async (userToPopulate) => {
352
+ attributeValues.map((userToPopulate) => {
375
353
  if (userToPopulate == null) {
376
354
  return null;
377
355
  }
@@ -388,9 +366,23 @@ const createHistoryService = ({ strapi: strapi2 }) => {
388
366
  [attributeKey]: adminUsers
389
367
  };
390
368
  }
369
+ const permissionChecker2 = getService$1("permission-checker").create({
370
+ userAbility: params.state.userAbility,
371
+ model: attributeSchema.target
372
+ });
373
+ const response = await serviceUtils.buildRelationReponse(
374
+ attributeValues,
375
+ attributeSchema
376
+ );
377
+ const sanitizedResults = await Promise.all(
378
+ response.results.map((media) => permissionChecker2.sanitizeOutput(media))
379
+ );
391
380
  return {
392
381
  ...await currentDataWithRelations,
393
- [attributeKey]: await buildRelationReponse(attributeValues, attributeSchema)
382
+ [attributeKey]: {
383
+ results: sanitizedResults,
384
+ meta: response.meta
385
+ }
394
386
  };
395
387
  }
396
388
  return currentDataWithRelations;
@@ -405,7 +397,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
405
397
  ...result,
406
398
  data: await populateEntryRelations(result),
407
399
  meta: {
408
- unknownAttributes: getSchemaAttributesDiff(
400
+ unknownAttributes: serviceUtils.getSchemaAttributesDiff(
409
401
  result.schema,
410
402
  strapi2.getModel(params.query.contentType).attributes
411
403
  )
@@ -416,13 +408,16 @@ const createHistoryService = ({ strapi: strapi2 }) => {
416
408
  );
417
409
  return {
418
410
  results: formattedResults,
419
- pagination
411
+ pagination: pagination2
420
412
  };
421
413
  },
422
414
  async restoreVersion(versionId) {
423
415
  const version = await query.findOne({ where: { id: versionId } });
424
416
  const contentTypeSchemaAttributes = strapi2.getModel(version.contentType).attributes;
425
- const schemaDiff = getSchemaAttributesDiff(version.schema, contentTypeSchemaAttributes);
417
+ const schemaDiff = serviceUtils.getSchemaAttributesDiff(
418
+ version.schema,
419
+ contentTypeSchemaAttributes
420
+ );
426
421
  const dataWithoutAddedAttributes = Object.keys(schemaDiff.added).reduce(
427
422
  (currentData, addedKey) => {
428
423
  currentData[addedKey] = null;
@@ -435,61 +430,26 @@ const createHistoryService = ({ strapi: strapi2 }) => {
435
430
  FIELDS_TO_IGNORE,
436
431
  contentTypeSchemaAttributes
437
432
  );
438
- const dataWithoutMissingRelations = await Object.entries(sanitizedSchemaAttributes).reduce(
439
- async (previousRelationAttributesPromise, [name, attribute]) => {
440
- const previousRelationAttributes = await previousRelationAttributesPromise;
441
- const relationData = version.data[name];
442
- if (relationData === null) {
433
+ const reducer = async.reduce(Object.entries(sanitizedSchemaAttributes));
434
+ const dataWithoutMissingRelations = await reducer(
435
+ async (previousRelationAttributes, [name, attribute]) => {
436
+ const versionRelationData = version.data[name];
437
+ if (!versionRelationData) {
443
438
  return previousRelationAttributes;
444
439
  }
445
440
  if (attribute.type === "relation" && // TODO: handle polymorphic relations
446
441
  attribute.relation !== "morphToOne" && attribute.relation !== "morphToMany") {
447
- if (Array.isArray(relationData)) {
448
- if (relationData.length === 0)
449
- return previousRelationAttributes;
450
- const existingAndMissingRelations = await Promise.all(
451
- relationData.map((relation) => {
452
- return strapi2.documents(attribute.target).findOne({
453
- documentId: relation.documentId,
454
- locale: relation.locale || void 0
455
- });
456
- })
457
- );
458
- const existingRelations = existingAndMissingRelations.filter(
459
- (relation) => relation !== null
460
- );
461
- previousRelationAttributes[name] = existingRelations;
462
- } else {
463
- const existingRelation = await strapi2.documents(attribute.target).findOne({
464
- documentId: relationData.documentId,
465
- locale: relationData.locale || void 0
466
- });
467
- if (!existingRelation) {
468
- previousRelationAttributes[name] = null;
469
- }
470
- }
442
+ const data2 = await serviceUtils.getRelationRestoreValue(versionRelationData, attribute);
443
+ previousRelationAttributes[name] = data2;
471
444
  }
472
445
  if (attribute.type === "media") {
473
- if (attribute.multiple) {
474
- const existingAndMissingMedias = await Promise.all(
475
- // @ts-expect-error Fix the type definitions so this isn't any
476
- relationData.map((media) => {
477
- return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
478
- })
479
- );
480
- const existingMedias = existingAndMissingMedias.filter((media) => media != null);
481
- previousRelationAttributes[name] = existingMedias;
482
- } else {
483
- const existingMedia = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: version.data[name].id } });
484
- if (!existingMedia) {
485
- previousRelationAttributes[name] = null;
486
- }
487
- }
446
+ const data2 = await serviceUtils.getMediaRestoreValue(versionRelationData, attribute);
447
+ previousRelationAttributes[name] = data2;
488
448
  }
489
449
  return previousRelationAttributes;
490
450
  },
491
451
  // Clone to avoid mutating the original version data
492
- Promise.resolve(structuredClone(dataWithoutAddedAttributes))
452
+ structuredClone(dataWithoutAddedAttributes)
493
453
  );
494
454
  const data = omit(["id", ...Object.keys(schemaDiff.removed)], dataWithoutMissingRelations);
495
455
  const restoredDocument = await strapi2.documents(version.contentType).update({
@@ -504,8 +464,101 @@ const createHistoryService = ({ strapi: strapi2 }) => {
504
464
  }
505
465
  };
506
466
  };
467
+ const createLifecyclesService = ({ strapi: strapi2 }) => {
468
+ const state = {
469
+ deleteExpiredJob: null,
470
+ isInitialized: false
471
+ };
472
+ const query = strapi2.db.query(HISTORY_VERSION_UID);
473
+ const historyService = getService(strapi2, "history");
474
+ const serviceUtils = createServiceUtils({ strapi: strapi2 });
475
+ return {
476
+ async bootstrap() {
477
+ if (state.isInitialized) {
478
+ return;
479
+ }
480
+ strapi2.documents.use(async (context, next) => {
481
+ if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
482
+ return next();
483
+ }
484
+ if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
485
+ return next();
486
+ }
487
+ const contentTypeUid = context.contentType.uid;
488
+ if (!contentTypeUid.startsWith("api::")) {
489
+ return next();
490
+ }
491
+ const result = await next();
492
+ const documentContext = {
493
+ documentId: context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId,
494
+ locale: context.params?.locale
495
+ };
496
+ const defaultLocale = await serviceUtils.getDefaultLocale();
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
+ }
504
+ const document = await strapi2.documents(contentTypeUid).findOne({
505
+ documentId: documentContext.documentId,
506
+ locale,
507
+ populate: serviceUtils.getDeepPopulate(contentTypeUid)
508
+ });
509
+ const status = await serviceUtils.getVersionStatus(contentTypeUid, document);
510
+ const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
511
+ const componentsSchemas = Object.keys(
512
+ attributesSchema
513
+ ).reduce((currentComponentSchemas, key) => {
514
+ const fieldSchema = attributesSchema[key];
515
+ if (fieldSchema.type === "component") {
516
+ const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
517
+ return {
518
+ ...currentComponentSchemas,
519
+ [fieldSchema.component]: componentSchema
520
+ };
521
+ }
522
+ return currentComponentSchemas;
523
+ }, {});
524
+ await strapi2.db.transaction(async ({ onCommit }) => {
525
+ onCommit(() => {
526
+ historyService.createVersion({
527
+ contentType: contentTypeUid,
528
+ data: omit(FIELDS_TO_IGNORE, document),
529
+ schema: omit(FIELDS_TO_IGNORE, attributesSchema),
530
+ componentsSchemas,
531
+ relatedDocumentId: documentContext.documentId,
532
+ locale,
533
+ status
534
+ });
535
+ });
536
+ });
537
+ return result;
538
+ });
539
+ state.deleteExpiredJob = scheduleJob("0 0 * * *", () => {
540
+ const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
541
+ const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
542
+ query.deleteMany({
543
+ where: {
544
+ created_at: {
545
+ $lt: expirationDate.toISOString()
546
+ }
547
+ }
548
+ });
549
+ });
550
+ state.isInitialized = true;
551
+ },
552
+ async destroy() {
553
+ if (state.deleteExpiredJob) {
554
+ state.deleteExpiredJob.cancel();
555
+ }
556
+ }
557
+ };
558
+ };
507
559
  const services$1 = {
508
- history: createHistoryService
560
+ history: createHistoryService,
561
+ lifecycles: createLifecyclesService
509
562
  };
510
563
  const info = { pluginName: "content-manager", type: "admin" };
511
564
  const historyVersionRouter = {
@@ -585,10 +638,10 @@ const getFeature = () => {
585
638
  strapi2.get("models").add(historyVersion);
586
639
  },
587
640
  bootstrap({ strapi: strapi2 }) {
588
- getService(strapi2, "history").bootstrap();
641
+ getService(strapi2, "lifecycles").bootstrap();
589
642
  },
590
643
  destroy({ strapi: strapi2 }) {
591
- getService(strapi2, "history").destroy();
644
+ getService(strapi2, "lifecycles").destroy();
592
645
  },
593
646
  controllers: controllers$1,
594
647
  services: services$1,
@@ -1407,7 +1460,7 @@ const { PaginationError, ValidationError } = errors;
1407
1460
  const TYPES = ["singleType", "collectionType"];
1408
1461
  const kindSchema = yup$1.string().oneOf(TYPES).nullable();
1409
1462
  const bulkActionInputSchema = yup$1.object({
1410
- ids: yup$1.array().of(yup$1.strapiID()).min(1).required()
1463
+ documentIds: yup$1.array().of(yup$1.strapiID()).min(1).required()
1411
1464
  }).required();
1412
1465
  const generateUIDInputSchema = yup$1.object({
1413
1466
  contentTypeUID: yup$1.string().required(),
@@ -1506,15 +1559,47 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
1506
1559
  }
1507
1560
  }, body);
1508
1561
  };
1509
- 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;
1510
1569
  const { locale, status, ...rest } = request || {};
1511
- if (!isNil$1(locale) && typeof locale !== "string") {
1512
- throw new errors.ValidationError(`Invalid locale: ${locale}`);
1513
- }
1514
- if (!isNil$1(status) && !["draft", "published"].includes(status)) {
1515
- 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}`);
1516
1579
  }
1517
- 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
+ };
1518
1603
  };
1519
1604
  const createDocument = async (ctx, opts) => {
1520
1605
  const { userAbility, user } = ctx.state;
@@ -1529,7 +1614,7 @@ const createDocument = async (ctx, opts) => {
1529
1614
  const setCreator = setCreatorFields({ user });
1530
1615
  const sanitizeFn = async.pipe(pickPermittedFields, setCreator);
1531
1616
  const sanitizedBody = await sanitizeFn(body);
1532
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(body);
1617
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(body);
1533
1618
  return documentManager2.create(model, {
1534
1619
  data: sanitizedBody,
1535
1620
  locale,
@@ -1548,7 +1633,7 @@ const updateDocument = async (ctx, opts) => {
1548
1633
  }
1549
1634
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1550
1635
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1551
- const { locale } = getDocumentLocaleAndStatus(body);
1636
+ const { locale } = await getDocumentLocaleAndStatus(body);
1552
1637
  const [documentVersion, documentExists] = await Promise.all([
1553
1638
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1554
1639
  documentManager2.exists(model, id)
@@ -1586,8 +1671,8 @@ const collectionTypes = {
1586
1671
  }
1587
1672
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1588
1673
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1589
- const { locale, status } = getDocumentLocaleAndStatus(query);
1590
- const { results: documents, pagination } = await documentManager2.findPage(
1674
+ const { locale, status } = await getDocumentLocaleAndStatus(query);
1675
+ const { results: documents, pagination: pagination2 } = await documentManager2.findPage(
1591
1676
  { ...permissionQuery, populate, locale, status },
1592
1677
  model
1593
1678
  );
@@ -1608,21 +1693,20 @@ const collectionTypes = {
1608
1693
  );
1609
1694
  ctx.body = {
1610
1695
  results,
1611
- pagination
1696
+ pagination: pagination2
1612
1697
  };
1613
1698
  },
1614
1699
  async findOne(ctx) {
1615
1700
  const { userAbility } = ctx.state;
1616
1701
  const { model, id } = ctx.params;
1617
1702
  const documentManager2 = getService$1("document-manager");
1618
- const documentMetadata2 = getService$1("document-metadata");
1619
1703
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1620
1704
  if (permissionChecker2.cannot.read()) {
1621
1705
  return ctx.forbidden();
1622
1706
  }
1623
1707
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1624
1708
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1625
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
1709
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1626
1710
  const version = await documentManager2.findOne(id, model, {
1627
1711
  populate,
1628
1712
  locale,
@@ -1633,8 +1717,10 @@ const collectionTypes = {
1633
1717
  if (!exists) {
1634
1718
  return ctx.notFound();
1635
1719
  }
1636
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
1720
+ const { meta } = await formatDocumentWithMetadata(
1721
+ permissionChecker2,
1637
1722
  model,
1723
+ // @ts-expect-error TODO: fix
1638
1724
  { id, locale, publishedAt: null },
1639
1725
  { availableLocales: true, availableStatus: false }
1640
1726
  );
@@ -1645,12 +1731,11 @@ const collectionTypes = {
1645
1731
  return ctx.forbidden();
1646
1732
  }
1647
1733
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
1648
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1734
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1649
1735
  },
1650
1736
  async create(ctx) {
1651
1737
  const { userAbility } = ctx.state;
1652
1738
  const { model } = ctx.params;
1653
- const documentMetadata2 = getService$1("document-metadata");
1654
1739
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1655
1740
  const [totalEntries, document] = await Promise.all([
1656
1741
  strapi.db.query(model).count(),
@@ -1658,7 +1743,7 @@ const collectionTypes = {
1658
1743
  ]);
1659
1744
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
1660
1745
  ctx.status = 201;
1661
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1746
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1662
1747
  // Empty metadata as it's not relevant for a new document
1663
1748
  availableLocales: false,
1664
1749
  availableStatus: false
@@ -1672,25 +1757,23 @@ const collectionTypes = {
1672
1757
  async update(ctx) {
1673
1758
  const { userAbility } = ctx.state;
1674
1759
  const { model } = ctx.params;
1675
- const documentMetadata2 = getService$1("document-metadata");
1676
1760
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1677
1761
  const updatedVersion = await updateDocument(ctx);
1678
1762
  const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
1679
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedVersion);
1763
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
1680
1764
  },
1681
1765
  async clone(ctx) {
1682
1766
  const { userAbility, user } = ctx.state;
1683
1767
  const { model, sourceId: id } = ctx.params;
1684
1768
  const { body } = ctx.request;
1685
1769
  const documentManager2 = getService$1("document-manager");
1686
- const documentMetadata2 = getService$1("document-metadata");
1687
1770
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1688
1771
  if (permissionChecker2.cannot.create()) {
1689
1772
  return ctx.forbidden();
1690
1773
  }
1691
1774
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1692
1775
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1693
- const { locale } = getDocumentLocaleAndStatus(body);
1776
+ const { locale } = await getDocumentLocaleAndStatus(body);
1694
1777
  const document = await documentManager2.findOne(id, model, {
1695
1778
  populate,
1696
1779
  locale,
@@ -1706,7 +1789,7 @@ const collectionTypes = {
1706
1789
  const sanitizedBody = await sanitizeFn(body);
1707
1790
  const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
1708
1791
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
1709
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument, {
1792
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
1710
1793
  // Empty metadata as it's not relevant for a new document
1711
1794
  availableLocales: false,
1712
1795
  availableStatus: false
@@ -1735,7 +1818,7 @@ const collectionTypes = {
1735
1818
  }
1736
1819
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1737
1820
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1738
- const { locale } = getDocumentLocaleAndStatus(ctx.query);
1821
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query);
1739
1822
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1740
1823
  if (documentLocales.length === 0) {
1741
1824
  return ctx.notFound();
@@ -1757,7 +1840,6 @@ const collectionTypes = {
1757
1840
  const { id, model } = ctx.params;
1758
1841
  const { body } = ctx.request;
1759
1842
  const documentManager2 = getService$1("document-manager");
1760
- const documentMetadata2 = getService$1("document-metadata");
1761
1843
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1762
1844
  if (permissionChecker2.cannot.publish()) {
1763
1845
  return ctx.forbidden();
@@ -1769,21 +1851,25 @@ const collectionTypes = {
1769
1851
  if (permissionChecker2.cannot.publish(document)) {
1770
1852
  throw new errors.ForbiddenError();
1771
1853
  }
1772
- const { locale } = getDocumentLocaleAndStatus(body);
1773
- return documentManager2.publish(document.documentId, model, {
1854
+ const { locale } = await getDocumentLocaleAndStatus(body);
1855
+ const publishResult = await documentManager2.publish(document.documentId, model, {
1774
1856
  locale
1775
1857
  // TODO: Allow setting creator fields on publish
1776
1858
  // data: setCreatorFields({ user, isEdition: true })({}),
1777
1859
  });
1860
+ if (!publishResult || publishResult.length === 0) {
1861
+ throw new errors.NotFoundError("Document not found or already published.");
1862
+ }
1863
+ return publishResult[0];
1778
1864
  });
1779
1865
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
1780
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
1866
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
1781
1867
  },
1782
1868
  async bulkPublish(ctx) {
1783
1869
  const { userAbility } = ctx.state;
1784
1870
  const { model } = ctx.params;
1785
1871
  const { body } = ctx.request;
1786
- const { ids } = body;
1872
+ const { documentIds } = body;
1787
1873
  await validateBulkActionInput(body);
1788
1874
  const documentManager2 = getService$1("document-manager");
1789
1875
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1792,8 +1878,11 @@ const collectionTypes = {
1792
1878
  }
1793
1879
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1794
1880
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1795
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1796
- 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();
1797
1886
  for (const entity of entities) {
1798
1887
  if (!entity) {
1799
1888
  return ctx.notFound();
@@ -1802,24 +1891,25 @@ const collectionTypes = {
1802
1891
  return ctx.forbidden();
1803
1892
  }
1804
1893
  }
1805
- const { count } = await documentManager2.publishMany(entities, model);
1894
+ const count = await documentManager2.publishMany(model, documentIds, locale);
1806
1895
  ctx.body = { count };
1807
1896
  },
1808
1897
  async bulkUnpublish(ctx) {
1809
1898
  const { userAbility } = ctx.state;
1810
1899
  const { model } = ctx.params;
1811
1900
  const { body } = ctx.request;
1812
- const { ids } = body;
1901
+ const { documentIds } = body;
1813
1902
  await validateBulkActionInput(body);
1814
1903
  const documentManager2 = getService$1("document-manager");
1815
1904
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1816
1905
  if (permissionChecker2.cannot.unpublish()) {
1817
1906
  return ctx.forbidden();
1818
1907
  }
1819
- const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1820
- const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1821
- const entityPromises = ids.map((id) => documentManager2.findOne(id, model, { populate }));
1822
- 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();
1823
1913
  for (const entity of entities) {
1824
1914
  if (!entity) {
1825
1915
  return ctx.notFound();
@@ -1828,7 +1918,8 @@ const collectionTypes = {
1828
1918
  return ctx.forbidden();
1829
1919
  }
1830
1920
  }
1831
- const { count } = await documentManager2.unpublishMany(entities, model);
1921
+ const entitiesIds = entities.map((document) => document.documentId);
1922
+ const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
1832
1923
  ctx.body = { count };
1833
1924
  },
1834
1925
  async unpublish(ctx) {
@@ -1838,7 +1929,6 @@ const collectionTypes = {
1838
1929
  body: { discardDraft, ...body }
1839
1930
  } = ctx.request;
1840
1931
  const documentManager2 = getService$1("document-manager");
1841
- const documentMetadata2 = getService$1("document-metadata");
1842
1932
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1843
1933
  if (permissionChecker2.cannot.unpublish()) {
1844
1934
  return ctx.forbidden();
@@ -1848,7 +1938,7 @@ const collectionTypes = {
1848
1938
  }
1849
1939
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1850
1940
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1851
- const { locale } = getDocumentLocaleAndStatus(body);
1941
+ const { locale } = await getDocumentLocaleAndStatus(body);
1852
1942
  const document = await documentManager2.findOne(id, model, {
1853
1943
  populate,
1854
1944
  locale,
@@ -1870,7 +1960,7 @@ const collectionTypes = {
1870
1960
  ctx.body = await async.pipe(
1871
1961
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
1872
1962
  permissionChecker2.sanitizeOutput,
1873
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
1963
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1874
1964
  )(document);
1875
1965
  });
1876
1966
  },
@@ -1879,14 +1969,13 @@ const collectionTypes = {
1879
1969
  const { id, model } = ctx.params;
1880
1970
  const { body } = ctx.request;
1881
1971
  const documentManager2 = getService$1("document-manager");
1882
- const documentMetadata2 = getService$1("document-metadata");
1883
1972
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
1884
1973
  if (permissionChecker2.cannot.discard()) {
1885
1974
  return ctx.forbidden();
1886
1975
  }
1887
1976
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
1888
1977
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1889
- const { locale } = getDocumentLocaleAndStatus(body);
1978
+ const { locale } = await getDocumentLocaleAndStatus(body);
1890
1979
  const document = await documentManager2.findOne(id, model, {
1891
1980
  populate,
1892
1981
  locale,
@@ -1901,14 +1990,14 @@ const collectionTypes = {
1901
1990
  ctx.body = await async.pipe(
1902
1991
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
1903
1992
  permissionChecker2.sanitizeOutput,
1904
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
1993
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
1905
1994
  )(document);
1906
1995
  },
1907
1996
  async bulkDelete(ctx) {
1908
1997
  const { userAbility } = ctx.state;
1909
1998
  const { model } = ctx.params;
1910
1999
  const { query, body } = ctx.request;
1911
- const { ids } = body;
2000
+ const { documentIds } = body;
1912
2001
  await validateBulkActionInput(body);
1913
2002
  const documentManager2 = getService$1("document-manager");
1914
2003
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
@@ -1916,14 +2005,22 @@ const collectionTypes = {
1916
2005
  return ctx.forbidden();
1917
2006
  }
1918
2007
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
1919
- const idsWhereClause = { id: { $in: ids } };
1920
- const params = {
1921
- ...permissionQuery,
1922
- filters: {
1923
- $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();
1924
2020
  }
1925
- };
1926
- 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 });
1927
2024
  ctx.body = { count };
1928
2025
  },
1929
2026
  async countDraftRelations(ctx) {
@@ -1936,7 +2033,7 @@ const collectionTypes = {
1936
2033
  }
1937
2034
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1938
2035
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1939
- const { locale, status = "draft" } = getDocumentLocaleAndStatus(ctx.query);
2036
+ const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1940
2037
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
1941
2038
  if (!entity) {
1942
2039
  return ctx.notFound();
@@ -1951,7 +2048,7 @@ const collectionTypes = {
1951
2048
  },
1952
2049
  async countManyEntriesDraftRelations(ctx) {
1953
2050
  const { userAbility } = ctx.state;
1954
- const ids = ctx.request.query.ids;
2051
+ const ids = ctx.request.query.documentIds;
1955
2052
  const locale = ctx.request.query.locale;
1956
2053
  const { model } = ctx.params;
1957
2054
  const documentManager2 = getService$1("document-manager");
@@ -1962,7 +2059,7 @@ const collectionTypes = {
1962
2059
  const entities = await documentManager2.findMany(
1963
2060
  {
1964
2061
  filters: {
1965
- id: ids
2062
+ documentId: ids
1966
2063
  },
1967
2064
  locale
1968
2065
  },
@@ -2464,7 +2561,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
2464
2561
  throw new errors.ForbiddenError();
2465
2562
  }
2466
2563
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2467
- const { locale } = getDocumentLocaleAndStatus(body);
2564
+ const { locale } = await getDocumentLocaleAndStatus(body);
2468
2565
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2469
2566
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2470
2567
  // Find the first document to check if it exists
@@ -2501,12 +2598,11 @@ const singleTypes = {
2501
2598
  const { model } = ctx.params;
2502
2599
  const { query = {} } = ctx.request;
2503
2600
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2504
- const documentMetadata2 = getService$1("document-metadata");
2505
2601
  if (permissionChecker2.cannot.read()) {
2506
2602
  return ctx.forbidden();
2507
2603
  }
2508
2604
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2509
- const { locale, status } = getDocumentLocaleAndStatus(query);
2605
+ const { locale, status } = await getDocumentLocaleAndStatus(query);
2510
2606
  const version = await findDocument(permissionQuery, model, { locale, status });
2511
2607
  if (!version) {
2512
2608
  if (permissionChecker2.cannot.create()) {
@@ -2516,8 +2612,10 @@ const singleTypes = {
2516
2612
  if (!document) {
2517
2613
  return ctx.notFound();
2518
2614
  }
2519
- const { meta } = await documentMetadata2.formatDocumentWithMetadata(
2615
+ const { meta } = await formatDocumentWithMetadata(
2616
+ permissionChecker2,
2520
2617
  model,
2618
+ // @ts-expect-error - fix types
2521
2619
  { id: document.documentId, locale, publishedAt: null },
2522
2620
  { availableLocales: true, availableStatus: false }
2523
2621
  );
@@ -2528,16 +2626,15 @@ const singleTypes = {
2528
2626
  return ctx.forbidden();
2529
2627
  }
2530
2628
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
2531
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2629
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2532
2630
  },
2533
2631
  async createOrUpdate(ctx) {
2534
2632
  const { userAbility } = ctx.state;
2535
2633
  const { model } = ctx.params;
2536
- const documentMetadata2 = getService$1("document-metadata");
2537
2634
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2538
2635
  const document = await createOrUpdateDocument(ctx);
2539
2636
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
2540
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2637
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2541
2638
  },
2542
2639
  async delete(ctx) {
2543
2640
  const { userAbility } = ctx.state;
@@ -2550,7 +2647,7 @@ const singleTypes = {
2550
2647
  }
2551
2648
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2552
2649
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2553
- const { locale } = getDocumentLocaleAndStatus(query);
2650
+ const { locale } = await getDocumentLocaleAndStatus(query);
2554
2651
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2555
2652
  populate,
2556
2653
  locale
@@ -2573,7 +2670,6 @@ const singleTypes = {
2573
2670
  const { model } = ctx.params;
2574
2671
  const { query = {} } = ctx.request;
2575
2672
  const documentManager2 = getService$1("document-manager");
2576
- const documentMetadata2 = getService$1("document-metadata");
2577
2673
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2578
2674
  if (permissionChecker2.cannot.publish()) {
2579
2675
  return ctx.forbidden();
@@ -2588,11 +2684,12 @@ const singleTypes = {
2588
2684
  if (permissionChecker2.cannot.publish(document)) {
2589
2685
  throw new errors.ForbiddenError();
2590
2686
  }
2591
- const { locale } = getDocumentLocaleAndStatus(document);
2592
- 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);
2593
2690
  });
2594
2691
  const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
2595
- ctx.body = await documentMetadata2.formatDocumentWithMetadata(model, sanitizedDocument);
2692
+ ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
2596
2693
  },
2597
2694
  async unpublish(ctx) {
2598
2695
  const { userAbility } = ctx.state;
@@ -2602,7 +2699,6 @@ const singleTypes = {
2602
2699
  query = {}
2603
2700
  } = ctx.request;
2604
2701
  const documentManager2 = getService$1("document-manager");
2605
- const documentMetadata2 = getService$1("document-metadata");
2606
2702
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2607
2703
  if (permissionChecker2.cannot.unpublish()) {
2608
2704
  return ctx.forbidden();
@@ -2611,7 +2707,7 @@ const singleTypes = {
2611
2707
  return ctx.forbidden();
2612
2708
  }
2613
2709
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2614
- const { locale } = getDocumentLocaleAndStatus(body);
2710
+ const { locale } = await getDocumentLocaleAndStatus(body);
2615
2711
  const document = await findDocument(sanitizedQuery, model, { locale });
2616
2712
  if (!document) {
2617
2713
  return ctx.notFound();
@@ -2629,7 +2725,7 @@ const singleTypes = {
2629
2725
  ctx.body = await async.pipe(
2630
2726
  (document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
2631
2727
  permissionChecker2.sanitizeOutput,
2632
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2728
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2633
2729
  )(document);
2634
2730
  });
2635
2731
  },
@@ -2638,13 +2734,12 @@ const singleTypes = {
2638
2734
  const { model } = ctx.params;
2639
2735
  const { body, query = {} } = ctx.request;
2640
2736
  const documentManager2 = getService$1("document-manager");
2641
- const documentMetadata2 = getService$1("document-metadata");
2642
2737
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2643
2738
  if (permissionChecker2.cannot.discard()) {
2644
2739
  return ctx.forbidden();
2645
2740
  }
2646
2741
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2647
- const { locale } = getDocumentLocaleAndStatus(body);
2742
+ const { locale } = await getDocumentLocaleAndStatus(body);
2648
2743
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2649
2744
  if (!document) {
2650
2745
  return ctx.notFound();
@@ -2655,7 +2750,7 @@ const singleTypes = {
2655
2750
  ctx.body = await async.pipe(
2656
2751
  (document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
2657
2752
  permissionChecker2.sanitizeOutput,
2658
- (document2) => documentMetadata2.formatDocumentWithMetadata(model, document2)
2753
+ (document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
2659
2754
  )(document);
2660
2755
  },
2661
2756
  async countDraftRelations(ctx) {
@@ -2664,7 +2759,7 @@ const singleTypes = {
2664
2759
  const { query } = ctx.request;
2665
2760
  const documentManager2 = getService$1("document-manager");
2666
2761
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2667
- const { locale } = getDocumentLocaleAndStatus(query);
2762
+ const { locale } = await getDocumentLocaleAndStatus(query);
2668
2763
  if (permissionChecker2.cannot.read()) {
2669
2764
  return ctx.forbidden();
2670
2765
  }
@@ -2685,7 +2780,7 @@ const uid$1 = {
2685
2780
  async generateUID(ctx) {
2686
2781
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2687
2782
  const { query = {} } = ctx.request;
2688
- const { locale } = getDocumentLocaleAndStatus(query);
2783
+ const { locale } = await getDocumentLocaleAndStatus(query);
2689
2784
  await validateUIDField(contentTypeUID, field);
2690
2785
  const uidService = getService$1("uid");
2691
2786
  ctx.body = {
@@ -2697,7 +2792,7 @@ const uid$1 = {
2697
2792
  ctx.request.body
2698
2793
  );
2699
2794
  const { query = {} } = ctx.request;
2700
- const { locale } = getDocumentLocaleAndStatus(query);
2795
+ const { locale } = await getDocumentLocaleAndStatus(query);
2701
2796
  await validateUIDField(contentTypeUID, field);
2702
2797
  const uidService = getService$1("uid");
2703
2798
  const isAvailable = await uidService.checkUIDAvailability({
@@ -3488,7 +3583,7 @@ const permission = ({ strapi: strapi2 }) => ({
3488
3583
  await strapi2.service("admin::permission").actionProvider.registerMany(actions);
3489
3584
  }
3490
3585
  });
3491
- const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils.contentTypes;
3586
+ const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils.contentTypes;
3492
3587
  const { isAnyToMany } = strapiUtils.relations;
3493
3588
  const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils.contentTypes.constants;
3494
3589
  const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
@@ -3579,6 +3674,42 @@ const getDeepPopulate = (uid2, {
3579
3674
  {}
3580
3675
  );
3581
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
+ };
3582
3713
  const getDeepPopulateDraftCount = (uid2) => {
3583
3714
  const model = strapi.getModel(uid2);
3584
3715
  let hasRelations = false;
@@ -3600,22 +3731,24 @@ const getDeepPopulateDraftCount = (uid2) => {
3600
3731
  attribute.component
3601
3732
  );
3602
3733
  if (childHasRelations) {
3603
- populateAcc[attributeName] = { populate: populate2 };
3734
+ populateAcc[attributeName] = {
3735
+ populate: populate2
3736
+ };
3604
3737
  hasRelations = true;
3605
3738
  }
3606
3739
  break;
3607
3740
  }
3608
3741
  case "dynamiczone": {
3609
- const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
3610
- const { populate: populate2, hasRelations: childHasRelations } = getDeepPopulateDraftCount(componentUID);
3611
- if (childHasRelations) {
3742
+ const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
3743
+ const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
3744
+ if (componentHasRelations) {
3612
3745
  hasRelations = true;
3613
- return merge(acc, populate2);
3746
+ return { ...acc, [componentUID]: { populate: componentPopulate } };
3614
3747
  }
3615
3748
  return acc;
3616
3749
  }, {});
3617
- if (!isEmpty(dzPopulate)) {
3618
- populateAcc[attributeName] = { populate: dzPopulate };
3750
+ if (!isEmpty(dzPopulateFragment)) {
3751
+ populateAcc[attributeName] = { on: dzPopulateFragment };
3619
3752
  }
3620
3753
  break;
3621
3754
  }
@@ -3807,41 +3940,70 @@ const AVAILABLE_STATUS_FIELDS = [
3807
3940
  "updatedBy",
3808
3941
  "status"
3809
3942
  ];
3810
- 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
+ ];
3811
3952
  const CONTENT_MANAGER_STATUS = {
3812
3953
  PUBLISHED: "published",
3813
3954
  DRAFT: "draft",
3814
3955
  MODIFIED: "modified"
3815
3956
  };
3816
- const areDatesEqual = (date1, date2, threshold) => {
3817
- if (!date1 || !date2) {
3957
+ const getIsVersionLatestModification = (version, otherVersion) => {
3958
+ if (!version || !version.updatedAt) {
3818
3959
  return false;
3819
3960
  }
3820
- const time1 = new Date(date1).getTime();
3821
- const time2 = new Date(date2).getTime();
3822
- const difference2 = Math.abs(time1 - time2);
3823
- 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;
3824
3964
  };
3825
3965
  const documentMetadata = ({ strapi: strapi2 }) => ({
3826
3966
  /**
3827
3967
  * Returns available locales of a document for the current status
3828
3968
  */
3829
- getAvailableLocales(uid2, version, allVersions) {
3969
+ async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
3830
3970
  const versionsByLocale = groupBy("locale", allVersions);
3831
3971
  delete versionsByLocale[version.locale];
3832
- return Object.values(versionsByLocale).map((localeVersions) => {
3833
- if (!contentTypes$1.hasDraftAndPublish(strapi2.getModel(uid2))) {
3834
- 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
+ };
3835
4004
  }
3836
- const draftVersion = localeVersions.find((v) => v.publishedAt === null);
3837
- const otherVersions = localeVersions.filter((v) => v.id !== draftVersion?.id);
3838
- if (!draftVersion)
3839
- return;
3840
- return {
3841
- ...pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
3842
- status: this.getStatus(draftVersion, otherVersions)
3843
- };
3844
- }).filter(Boolean);
4005
+ );
4006
+ return mappingResult.filter(Boolean);
3845
4007
  },
3846
4008
  /**
3847
4009
  * Returns available status of a document for the current locale
@@ -3879,26 +4041,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3879
4041
  });
3880
4042
  },
3881
4043
  getStatus(version, otherDocumentStatuses) {
3882
- const isDraft = version.publishedAt === null;
3883
- if (!otherDocumentStatuses?.length) {
3884
- 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;
3885
4050
  }
3886
- if (isDraft) {
3887
- const publishedVersion = otherDocumentStatuses?.find((d) => d.publishedAt !== null);
3888
- if (!publishedVersion) {
3889
- return CONTENT_MANAGER_STATUS.DRAFT;
3890
- }
4051
+ const otherVersion = otherDocumentStatuses?.at(0);
4052
+ if (otherVersion?.publishedAt) {
4053
+ publishedVersion = otherVersion;
4054
+ } else if (otherVersion) {
4055
+ draftVersion = otherVersion;
3891
4056
  }
3892
- if (areDatesEqual(version.updatedAt, otherDocumentStatuses.at(0)?.updatedAt, 500)) {
4057
+ if (!draftVersion)
3893
4058
  return CONTENT_MANAGER_STATUS.PUBLISHED;
3894
- }
3895
- 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;
3896
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.
3897
4067
  async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
4068
+ const populate = getValidatableFieldsPopulate(uid2);
3898
4069
  const versions = await strapi2.db.query(uid2).findMany({
3899
4070
  where: { documentId: version.documentId },
3900
- select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
3901
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
3902
4075
  createdBy: {
3903
4076
  select: ["id", "firstname", "lastname", "email"]
3904
4077
  },
@@ -3907,7 +4080,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3907
4080
  }
3908
4081
  }
3909
4082
  });
3910
- const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
4083
+ const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
3911
4084
  const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
3912
4085
  return {
3913
4086
  availableLocales: availableLocalesResult,
@@ -3920,8 +4093,9 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
3920
4093
  * - Available status of the document for the current locale
3921
4094
  */
3922
4095
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
3923
- if (!document)
4096
+ if (!document) {
3924
4097
  return document;
4098
+ }
3925
4099
  const hasDraftAndPublish = contentTypes$1.hasDraftAndPublish(strapi2.getModel(uid2));
3926
4100
  if (!hasDraftAndPublish) {
3927
4101
  opts.availableStatus = false;
@@ -3971,26 +4145,9 @@ const sumDraftCounts = (entity, uid2) => {
3971
4145
  }, 0);
3972
4146
  };
3973
4147
  const { ApplicationError } = errors;
3974
- const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
3975
4148
  const { PUBLISHED_AT_ATTRIBUTE } = contentTypes$1.constants;
3976
4149
  const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE);
3977
4150
  const omitIdField = omit("id");
3978
- const emitEvent = async (uid2, event, document) => {
3979
- const modelDef = strapi.getModel(uid2);
3980
- const sanitizedDocument = await sanitize.sanitizers.defaultSanitizeOutput(
3981
- {
3982
- schema: modelDef,
3983
- getModel(uid22) {
3984
- return strapi.getModel(uid22);
3985
- }
3986
- },
3987
- document
3988
- );
3989
- strapi.eventHub.emit(event, {
3990
- model: modelDef.modelName,
3991
- entry: sanitizedDocument
3992
- });
3993
- };
3994
4151
  const documentManager = ({ strapi: strapi2 }) => {
3995
4152
  return {
3996
4153
  async findOne(id, uid2, opts = {}) {
@@ -4009,6 +4166,9 @@ const documentManager = ({ strapi: strapi2 }) => {
4009
4166
  } else if (opts.locale && opts.locale !== "*") {
4010
4167
  where.locale = opts.locale;
4011
4168
  }
4169
+ if (typeof opts.isPublished === "boolean") {
4170
+ where.publishedAt = { $notNull: opts.isPublished };
4171
+ }
4012
4172
  return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
4013
4173
  },
4014
4174
  async findMany(opts, uid2) {
@@ -4016,20 +4176,16 @@ const documentManager = ({ strapi: strapi2 }) => {
4016
4176
  return strapi2.documents(uid2).findMany(params);
4017
4177
  },
4018
4178
  async findPage(opts, uid2) {
4019
- const page = Number(opts?.page) || 1;
4020
- const pageSize = Number(opts?.pageSize) || 10;
4179
+ const params = pagination.withDefaultPagination(opts || {}, {
4180
+ maxLimit: 1e3
4181
+ });
4021
4182
  const [documents, total = 0] = await Promise.all([
4022
- strapi2.documents(uid2).findMany(opts),
4023
- strapi2.documents(uid2).count(opts)
4183
+ strapi2.documents(uid2).findMany(params),
4184
+ strapi2.documents(uid2).count(params)
4024
4185
  ]);
4025
4186
  return {
4026
4187
  results: documents,
4027
- pagination: {
4028
- page,
4029
- pageSize,
4030
- pageCount: Math.ceil(total / pageSize),
4031
- total
4032
- }
4188
+ pagination: pagination.transformPagedPaginationInfo(params, total)
4033
4189
  };
4034
4190
  },
4035
4191
  async create(uid2, opts = {}) {
@@ -4075,70 +4231,36 @@ const documentManager = ({ strapi: strapi2 }) => {
4075
4231
  return {};
4076
4232
  },
4077
4233
  // FIXME: handle relations
4078
- async deleteMany(opts, uid2) {
4079
- const docs = await strapi2.documents(uid2).findMany(opts);
4080
- for (const doc of docs) {
4081
- await strapi2.documents(uid2).delete({ documentId: doc.documentId });
4082
- }
4083
- 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 };
4084
4239
  },
4085
4240
  async publish(id, uid2, opts = {}) {
4086
4241
  const populate = await buildDeepPopulate(uid2);
4087
4242
  const params = { ...opts, populate };
4088
- 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);
4089
4244
  },
4090
- async publishMany(entities, uid2) {
4091
- if (!entities.length) {
4092
- return null;
4093
- }
4094
- await Promise.all(
4095
- entities.map((document) => {
4096
- return strapi2.entityValidator.validateEntityCreation(
4097
- strapi2.getModel(uid2),
4098
- document,
4099
- void 0,
4100
- // @ts-expect-error - FIXME: entity here is unnecessary
4101
- document
4102
- );
4103
- })
4104
- );
4105
- const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4106
- const filters = { id: { $in: entitiesToPublish } };
4107
- const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
4108
- const populate = await buildDeepPopulate(uid2);
4109
- const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4110
- where: filters,
4111
- data
4112
- });
4113
- const publishedEntities = await strapi2.db.query(uid2).findMany({
4114
- where: filters,
4115
- 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;
4116
4252
  });
4117
- await Promise.all(
4118
- publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
4119
- );
4120
- return publishedEntitiesCount;
4121
4253
  },
4122
- async unpublishMany(documents, uid2) {
4123
- if (!documents.length) {
4124
- return null;
4125
- }
4126
- const entitiesToUnpublish = documents.filter((doc) => doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
4127
- const filters = { id: { $in: entitiesToUnpublish } };
4128
- const data = { [PUBLISHED_AT_ATTRIBUTE]: null };
4129
- const populate = await buildDeepPopulate(uid2);
4130
- const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
4131
- where: filters,
4132
- data
4133
- });
4134
- const unpublishedEntities = await strapi2.db.query(uid2).findMany({
4135
- where: filters,
4136
- 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
+ );
4137
4261
  });
4138
- await Promise.all(
4139
- unpublishedEntities.map((doc) => emitEvent(uid2, ENTRY_UNPUBLISH, doc))
4140
- );
4141
- return unpublishedEntitiesCount;
4262
+ const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
4263
+ return { count: unpublishedEntitiesCount };
4142
4264
  },
4143
4265
  async unpublish(id, uid2, opts = {}) {
4144
4266
  const populate = await buildDeepPopulate(uid2);
@@ -4163,16 +4285,20 @@ const documentManager = ({ strapi: strapi2 }) => {
4163
4285
  }
4164
4286
  return sumDraftCounts(document, uid2);
4165
4287
  },
4166
- async countManyEntriesDraftRelations(ids, uid2, locale) {
4288
+ async countManyEntriesDraftRelations(documentIds, uid2, locale) {
4167
4289
  const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
4168
4290
  if (!hasRelations) {
4169
4291
  return 0;
4170
4292
  }
4293
+ let localeFilter = {};
4294
+ if (locale) {
4295
+ localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
4296
+ }
4171
4297
  const entities = await strapi2.db.query(uid2).findMany({
4172
4298
  populate,
4173
4299
  where: {
4174
- id: { $in: ids },
4175
- ...locale ? { locale } : {}
4300
+ documentId: { $in: documentIds },
4301
+ ...localeFilter
4176
4302
  }
4177
4303
  });
4178
4304
  const totalNumberDraftRelations = entities.reduce(