@strapi/content-manager 0.0.0-experimental.f75e3c6d67cc47c64ab37479efdbb7b43be50b78 → 0.0.0-experimental.f8a68bc03b751aa0a66c4dcfaf83553c9b6adf2b

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 (137) hide show
  1. package/LICENSE +18 -3
  2. package/dist/_chunks/{ComponentConfigurationPage-CuWgXugY.mjs → ComponentConfigurationPage-B2g3icXu.mjs} +3 -3
  3. package/dist/_chunks/{ComponentConfigurationPage-CuWgXugY.mjs.map → ComponentConfigurationPage-B2g3icXu.mjs.map} +1 -1
  4. package/dist/_chunks/{ComponentConfigurationPage-by0e_kNd.js → ComponentConfigurationPage-CzDn8Uxh.js} +3 -3
  5. package/dist/_chunks/{ComponentConfigurationPage-by0e_kNd.js.map → ComponentConfigurationPage-CzDn8Uxh.js.map} +1 -1
  6. package/dist/_chunks/{EditConfigurationPage-CqBeCPGH.js → EditConfigurationPage-Bcbi6r5y.js} +3 -3
  7. package/dist/_chunks/{EditConfigurationPage-CqBeCPGH.js.map → EditConfigurationPage-Bcbi6r5y.js.map} +1 -1
  8. package/dist/_chunks/{EditConfigurationPage-DbI4KMyz.mjs → EditConfigurationPage-DSA1zONV.mjs} +3 -3
  9. package/dist/_chunks/{EditConfigurationPage-DbI4KMyz.mjs.map → EditConfigurationPage-DSA1zONV.mjs.map} +1 -1
  10. package/dist/_chunks/{EditViewPage-ChgloMyO.js → EditViewPage-BvcMXaP2.js} +68 -47
  11. package/dist/_chunks/EditViewPage-BvcMXaP2.js.map +1 -0
  12. package/dist/_chunks/{EditViewPage-dFPBya9U.mjs → EditViewPage-Ca2EmJpb.mjs} +69 -48
  13. package/dist/_chunks/EditViewPage-Ca2EmJpb.mjs.map +1 -0
  14. package/dist/_chunks/{Field-C1nUKcdS.mjs → Field-BEYkgCZ0.mjs} +579 -227
  15. package/dist/_chunks/Field-BEYkgCZ0.mjs.map +1 -0
  16. package/dist/_chunks/{Field-dLk-vgLL.js → Field-CmaWxdpf.js} +581 -229
  17. package/dist/_chunks/Field-CmaWxdpf.js.map +1 -0
  18. package/dist/_chunks/{Form-DOlpi7Js.mjs → Form-BJVbU6w4.mjs} +54 -36
  19. package/dist/_chunks/Form-BJVbU6w4.mjs.map +1 -0
  20. package/dist/_chunks/{Form-CbXtmHC_.js → Form-DAUpPBVM.js} +52 -34
  21. package/dist/_chunks/Form-DAUpPBVM.js.map +1 -0
  22. package/dist/_chunks/{History-BjDfohBr.js → History-Bw9vfT1k.js} +158 -40
  23. package/dist/_chunks/History-Bw9vfT1k.js.map +1 -0
  24. package/dist/_chunks/{History-BFNUAiGc.mjs → History-M6Pk9CoY.mjs} +159 -41
  25. package/dist/_chunks/History-M6Pk9CoY.mjs.map +1 -0
  26. package/dist/_chunks/{ListConfigurationPage-DDi0KqFm.mjs → ListConfigurationPage-D7ior2zq.mjs} +58 -48
  27. package/dist/_chunks/ListConfigurationPage-D7ior2zq.mjs.map +1 -0
  28. package/dist/_chunks/{ListConfigurationPage-IQBgWTaa.js → ListConfigurationPage-DBVqF5fc.js} +57 -46
  29. package/dist/_chunks/ListConfigurationPage-DBVqF5fc.js.map +1 -0
  30. package/dist/_chunks/{ListViewPage-BPjljUsH.mjs → ListViewPage-BH37wONY.mjs} +116 -103
  31. package/dist/_chunks/ListViewPage-BH37wONY.mjs.map +1 -0
  32. package/dist/_chunks/{ListViewPage-CZYGqlvF.js → ListViewPage-MZu-8OKX.js} +117 -104
  33. package/dist/_chunks/ListViewPage-MZu-8OKX.js.map +1 -0
  34. package/dist/_chunks/{NoContentTypePage-BOAI6VZ1.js → NoContentTypePage-C9lRMTCa.js} +2 -2
  35. package/dist/_chunks/{NoContentTypePage-BOAI6VZ1.js.map → NoContentTypePage-C9lRMTCa.js.map} +1 -1
  36. package/dist/_chunks/{NoContentTypePage-DaWw67K-.mjs → NoContentTypePage-DRp7Aem_.mjs} +2 -2
  37. package/dist/_chunks/{NoContentTypePage-DaWw67K-.mjs.map → NoContentTypePage-DRp7Aem_.mjs.map} +1 -1
  38. package/dist/_chunks/{NoPermissionsPage-cYEtLc_e.js → NoPermissionsPage-COs61PpB.js} +2 -2
  39. package/dist/_chunks/{NoPermissionsPage-cYEtLc_e.js.map → NoPermissionsPage-COs61PpB.js.map} +1 -1
  40. package/dist/_chunks/{NoPermissionsPage-CZrJH00p.mjs → NoPermissionsPage-CzhRt5CA.mjs} +2 -2
  41. package/dist/_chunks/{NoPermissionsPage-CZrJH00p.mjs.map → NoPermissionsPage-CzhRt5CA.mjs.map} +1 -1
  42. package/dist/_chunks/{Relations-DTowyge2.mjs → Relations-68XxIzPI.mjs} +51 -30
  43. package/dist/_chunks/Relations-68XxIzPI.mjs.map +1 -0
  44. package/dist/_chunks/{Relations-DU6B7irU.js → Relations-CsGlulU_.js} +51 -30
  45. package/dist/_chunks/Relations-CsGlulU_.js.map +1 -0
  46. package/dist/_chunks/{en-DTULi5-d.js → en-Bm0D0IWz.js} +21 -15
  47. package/dist/_chunks/{en-DTULi5-d.js.map → en-Bm0D0IWz.js.map} +1 -1
  48. package/dist/_chunks/{en-GCOTL6jR.mjs → en-DKV44jRb.mjs} +21 -15
  49. package/dist/_chunks/{en-GCOTL6jR.mjs.map → en-DKV44jRb.mjs.map} +1 -1
  50. package/dist/_chunks/{index-BaGHmIir.mjs → index-CsfwpRfc.mjs} +1406 -783
  51. package/dist/_chunks/index-CsfwpRfc.mjs.map +1 -0
  52. package/dist/_chunks/{index-CCJeB7Rw.js → index-oDq3VO9j.js} +1382 -758
  53. package/dist/_chunks/index-oDq3VO9j.js.map +1 -0
  54. package/dist/_chunks/{layout-BinjszSQ.mjs → layout-C5uSVTqi.mjs} +41 -23
  55. package/dist/_chunks/layout-C5uSVTqi.mjs.map +1 -0
  56. package/dist/_chunks/{layout-ni_L9kT1.js → layout-Euka-kfv.js} +39 -21
  57. package/dist/_chunks/layout-Euka-kfv.js.map +1 -0
  58. package/dist/_chunks/{relations-c91ji5eR.mjs → relations-BpHVgFuV.mjs} +2 -2
  59. package/dist/_chunks/{relations-c91ji5eR.mjs.map → relations-BpHVgFuV.mjs.map} +1 -1
  60. package/dist/_chunks/{relations-CeJAJc5I.js → relations-DG7kmxa0.js} +2 -2
  61. package/dist/_chunks/{relations-CeJAJc5I.js.map → relations-DG7kmxa0.js.map} +1 -1
  62. package/dist/_chunks/{usePrev-B9w_-eYc.js → useDebounce-CtcjDB3L.js} +14 -1
  63. package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
  64. package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
  65. package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
  66. package/dist/admin/index.js +2 -1
  67. package/dist/admin/index.js.map +1 -1
  68. package/dist/admin/index.mjs +7 -6
  69. package/dist/admin/src/exports.d.ts +1 -1
  70. package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
  71. package/dist/admin/src/history/index.d.ts +3 -0
  72. package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
  73. package/dist/admin/src/hooks/useDocument.d.ts +32 -1
  74. package/dist/admin/src/hooks/useDocumentActions.d.ts +1 -1
  75. package/dist/admin/src/index.d.ts +1 -0
  76. package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +8 -3
  77. package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +4 -0
  78. package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +20 -0
  79. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +2 -2
  80. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
  81. package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +6 -58
  82. package/dist/admin/src/pages/EditView/components/Header.d.ts +11 -11
  83. package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
  84. package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
  85. package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +14 -0
  86. package/dist/admin/src/services/api.d.ts +1 -1
  87. package/dist/admin/src/services/components.d.ts +2 -2
  88. package/dist/admin/src/services/contentTypes.d.ts +3 -3
  89. package/dist/admin/src/services/documents.d.ts +19 -17
  90. package/dist/admin/src/services/init.d.ts +1 -1
  91. package/dist/admin/src/services/relations.d.ts +2 -2
  92. package/dist/admin/src/services/uid.d.ts +3 -3
  93. package/dist/admin/src/utils/validation.d.ts +4 -1
  94. package/dist/server/index.js +222 -127
  95. package/dist/server/index.js.map +1 -1
  96. package/dist/server/index.mjs +223 -128
  97. package/dist/server/index.mjs.map +1 -1
  98. package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
  99. package/dist/server/src/controllers/relations.d.ts.map +1 -1
  100. package/dist/server/src/controllers/single-types.d.ts.map +1 -1
  101. package/dist/server/src/controllers/uid.d.ts.map +1 -1
  102. package/dist/server/src/controllers/validation/dimensions.d.ts +4 -2
  103. package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -1
  104. package/dist/server/src/history/services/history.d.ts.map +1 -1
  105. package/dist/server/src/history/services/lifecycles.d.ts.map +1 -1
  106. package/dist/server/src/history/services/utils.d.ts +2 -1
  107. package/dist/server/src/history/services/utils.d.ts.map +1 -1
  108. package/dist/server/src/policies/hasPermissions.d.ts.map +1 -1
  109. package/dist/server/src/services/document-manager.d.ts.map +1 -1
  110. package/dist/server/src/services/document-metadata.d.ts.map +1 -1
  111. package/dist/server/src/services/permission-checker.d.ts.map +1 -1
  112. package/dist/server/src/services/utils/populate.d.ts.map +1 -1
  113. package/dist/shared/contracts/collection-types.d.ts +3 -1
  114. package/dist/shared/contracts/collection-types.d.ts.map +1 -1
  115. package/package.json +12 -12
  116. package/dist/_chunks/EditViewPage-ChgloMyO.js.map +0 -1
  117. package/dist/_chunks/EditViewPage-dFPBya9U.mjs.map +0 -1
  118. package/dist/_chunks/Field-C1nUKcdS.mjs.map +0 -1
  119. package/dist/_chunks/Field-dLk-vgLL.js.map +0 -1
  120. package/dist/_chunks/Form-CbXtmHC_.js.map +0 -1
  121. package/dist/_chunks/Form-DOlpi7Js.mjs.map +0 -1
  122. package/dist/_chunks/History-BFNUAiGc.mjs.map +0 -1
  123. package/dist/_chunks/History-BjDfohBr.js.map +0 -1
  124. package/dist/_chunks/ListConfigurationPage-DDi0KqFm.mjs.map +0 -1
  125. package/dist/_chunks/ListConfigurationPage-IQBgWTaa.js.map +0 -1
  126. package/dist/_chunks/ListViewPage-BPjljUsH.mjs.map +0 -1
  127. package/dist/_chunks/ListViewPage-CZYGqlvF.js.map +0 -1
  128. package/dist/_chunks/Relations-DTowyge2.mjs.map +0 -1
  129. package/dist/_chunks/Relations-DU6B7irU.js.map +0 -1
  130. package/dist/_chunks/index-BaGHmIir.mjs.map +0 -1
  131. package/dist/_chunks/index-CCJeB7Rw.js.map +0 -1
  132. package/dist/_chunks/layout-BinjszSQ.mjs.map +0 -1
  133. package/dist/_chunks/layout-ni_L9kT1.js.map +0 -1
  134. package/dist/_chunks/usePrev-B9w_-eYc.js.map +0 -1
  135. package/dist/_chunks/usePrev-DH6iah0A.mjs +0 -16
  136. package/dist/_chunks/usePrev-DH6iah0A.mjs.map +0 -1
  137. package/strapi-server.js +0 -3
@@ -199,7 +199,9 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
199
199
  return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
200
200
  };
201
201
  const localesService = strapi2.plugin("i18n")?.service("locales");
202
+ const i18nContentTypeService = strapi2.plugin("i18n")?.service("content-types");
202
203
  const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
204
+ const isLocalizedContentType = (model) => i18nContentTypeService ? i18nContentTypeService.isLocalizedContentType(model) : false;
203
205
  const getLocaleDictionary = async () => {
204
206
  if (!localesService)
205
207
  return {};
@@ -226,20 +228,25 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
226
228
  const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
227
229
  return documentMetadataService.getStatus(document, meta.availableStatus);
228
230
  };
229
- const getDeepPopulate2 = (uid2) => {
231
+ const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
230
232
  const model = strapi2.getModel(uid2);
231
233
  const attributes = Object.entries(model.attributes);
234
+ const fieldSelector = useDatabaseSyntax ? "select" : "fields";
232
235
  return attributes.reduce((acc, [attributeName, attribute]) => {
233
236
  switch (attribute.type) {
234
237
  case "relation": {
238
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
239
+ if (isMorphRelation) {
240
+ break;
241
+ }
235
242
  const isVisible2 = strapiUtils.contentTypes.isVisibleAttribute(model, attributeName);
236
243
  if (isVisible2) {
237
- acc[attributeName] = { fields: ["documentId", "locale", "publishedAt"] };
244
+ acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
238
245
  }
239
246
  break;
240
247
  }
241
248
  case "media": {
242
- acc[attributeName] = { fields: ["id"] };
249
+ acc[attributeName] = { [fieldSelector]: ["id"] };
243
250
  break;
244
251
  }
245
252
  case "component": {
@@ -312,6 +319,7 @@ const createServiceUtils = ({ strapi: strapi2 }) => {
312
319
  getRelationRestoreValue,
313
320
  getMediaRestoreValue,
314
321
  getDefaultLocale,
322
+ isLocalizedContentType,
315
323
  getLocaleDictionary,
316
324
  getRetentionDays,
317
325
  getVersionStatus,
@@ -334,7 +342,13 @@ const createHistoryService = ({ strapi: strapi2 }) => {
334
342
  });
335
343
  },
336
344
  async findVersionsPage(params) {
337
- const locale = params.query.locale || await serviceUtils.getDefaultLocale();
345
+ const model = strapi2.getModel(params.query.contentType);
346
+ const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
347
+ const defaultLocale = await serviceUtils.getDefaultLocale();
348
+ let locale = null;
349
+ if (isLocalizedContentType) {
350
+ locale = params.query.locale || defaultLocale;
351
+ }
338
352
  const [{ results, pagination }, localeDictionary] = await Promise.all([
339
353
  query.findPage({
340
354
  ...params.query,
@@ -379,7 +393,12 @@ const createHistoryService = ({ strapi: strapi2 }) => {
379
393
  if (userToPopulate == null) {
380
394
  return null;
381
395
  }
382
- return strapi2.query("admin::user").findOne({ where: { id: userToPopulate.id } });
396
+ return strapi2.query("admin::user").findOne({
397
+ where: {
398
+ ...userToPopulate.id ? { id: userToPopulate.id } : {},
399
+ ...userToPopulate.documentId ? { documentId: userToPopulate.documentId } : {}
400
+ }
401
+ });
383
402
  })
384
403
  );
385
404
  return {
@@ -490,13 +509,47 @@ const createHistoryService = ({ strapi: strapi2 }) => {
490
509
  }
491
510
  };
492
511
  };
512
+ const shouldCreateHistoryVersion = (context) => {
513
+ if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
514
+ return false;
515
+ }
516
+ if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
517
+ return false;
518
+ }
519
+ if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
520
+ return false;
521
+ }
522
+ if (!context.contentType.uid.startsWith("api::")) {
523
+ return false;
524
+ }
525
+ return true;
526
+ };
527
+ const getSchemas = (uid2) => {
528
+ const attributesSchema = strapi.getModel(uid2).attributes;
529
+ const componentsSchemas = Object.keys(attributesSchema).reduce(
530
+ (currentComponentSchemas, key) => {
531
+ const fieldSchema = attributesSchema[key];
532
+ if (fieldSchema.type === "component") {
533
+ const componentSchema = strapi.getModel(fieldSchema.component).attributes;
534
+ return {
535
+ ...currentComponentSchemas,
536
+ [fieldSchema.component]: componentSchema
537
+ };
538
+ }
539
+ return currentComponentSchemas;
540
+ },
541
+ {}
542
+ );
543
+ return {
544
+ schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
545
+ componentsSchemas
546
+ };
547
+ };
493
548
  const createLifecyclesService = ({ strapi: strapi2 }) => {
494
549
  const state = {
495
550
  deleteExpiredJob: null,
496
551
  isInitialized: false
497
552
  };
498
- const query = strapi2.db.query(HISTORY_VERSION_UID);
499
- const historyService = getService(strapi2, "history");
500
553
  const serviceUtils = createServiceUtils({ strapi: strapi2 });
501
554
  return {
502
555
  async bootstrap() {
@@ -504,66 +557,53 @@ const createLifecyclesService = ({ strapi: strapi2 }) => {
504
557
  return;
505
558
  }
506
559
  strapi2.documents.use(async (context, next) => {
507
- if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
508
- return next();
509
- }
510
- if (context.action !== "create" && context.action !== "update" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
511
- return next();
512
- }
513
- const contentTypeUid = context.contentType.uid;
514
- if (!contentTypeUid.startsWith("api::")) {
515
- return next();
516
- }
517
560
  const result = await next();
518
- const documentContext = context.action === "create" ? { documentId: result.documentId, locale: context.params?.locale } : { documentId: context.params.documentId, locale: context.params?.locale };
561
+ if (!shouldCreateHistoryVersion(context)) {
562
+ return result;
563
+ }
564
+ const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
519
565
  const defaultLocale = await serviceUtils.getDefaultLocale();
520
- const locale = documentContext.locale || defaultLocale;
521
- if (Array.isArray(locale)) {
522
- strapi2.log.warn(
523
- "[Content manager history middleware]: An array of locales was provided, but only a single locale is supported for the findOne operation."
524
- );
525
- return next();
566
+ const locales = fp.castArray(context.params?.locale || defaultLocale);
567
+ if (!locales.length) {
568
+ return result;
526
569
  }
527
- const document = await strapi2.documents(contentTypeUid).findOne({
528
- documentId: documentContext.documentId,
529
- locale,
530
- populate: serviceUtils.getDeepPopulate(contentTypeUid)
570
+ const uid2 = context.contentType.uid;
571
+ const schemas = getSchemas(uid2);
572
+ const model = strapi2.getModel(uid2);
573
+ const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
574
+ const localeEntries = await strapi2.db.query(uid2).findMany({
575
+ where: {
576
+ documentId,
577
+ ...isLocalizedContentType ? { locale: { $in: locales } } : {},
578
+ ...strapiUtils.contentTypes.hasDraftAndPublish(strapi2.contentTypes[uid2]) ? { publishedAt: null } : {}
579
+ },
580
+ populate: serviceUtils.getDeepPopulate(
581
+ uid2,
582
+ true
583
+ /* use database syntax */
584
+ )
531
585
  });
532
- const status = await serviceUtils.getVersionStatus(contentTypeUid, document);
533
- const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
534
- const componentsSchemas = Object.keys(
535
- attributesSchema
536
- ).reduce((currentComponentSchemas, key) => {
537
- const fieldSchema = attributesSchema[key];
538
- if (fieldSchema.type === "component") {
539
- const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
540
- return {
541
- ...currentComponentSchemas,
542
- [fieldSchema.component]: componentSchema
543
- };
544
- }
545
- return currentComponentSchemas;
546
- }, {});
547
586
  await strapi2.db.transaction(async ({ onCommit }) => {
548
- onCommit(() => {
549
- historyService.createVersion({
550
- contentType: contentTypeUid,
551
- data: fp.omit(FIELDS_TO_IGNORE, document),
552
- schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
553
- componentsSchemas,
554
- relatedDocumentId: documentContext.documentId,
555
- locale,
556
- status
557
- });
587
+ onCommit(async () => {
588
+ for (const entry of localeEntries) {
589
+ const status = await serviceUtils.getVersionStatus(uid2, entry);
590
+ await getService(strapi2, "history").createVersion({
591
+ contentType: uid2,
592
+ data: fp.omit(FIELDS_TO_IGNORE, entry),
593
+ relatedDocumentId: documentId,
594
+ locale: entry.locale,
595
+ status,
596
+ ...schemas
597
+ });
598
+ }
558
599
  });
559
600
  });
560
601
  return result;
561
602
  });
562
- const retentionDays = serviceUtils.getRetentionDays();
563
603
  state.deleteExpiredJob = nodeSchedule.scheduleJob("0 0 * * *", () => {
564
- const retentionDaysInMilliseconds = retentionDays * 24 * 60 * 60 * 1e3;
604
+ const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
565
605
  const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
566
- query.deleteMany({
606
+ strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
567
607
  where: {
568
608
  created_at: {
569
609
  $lt: expirationDate.toISOString()
@@ -1195,6 +1235,11 @@ const { createPolicy } = strapiUtils.policy;
1195
1235
  const hasPermissions = createPolicy({
1196
1236
  name: "plugin::content-manager.hasPermissions",
1197
1237
  validator: validateHasPermissionsInput,
1238
+ /**
1239
+ * NOTE: Action aliases are currently not checked at this level (policy).
1240
+ * This is currently the intended behavior to avoid changing the behavior of API related permissions.
1241
+ * If you want to add support for it, please create a dedicated RFC with a list of potential side effect this could have.
1242
+ */
1198
1243
  handler(ctx, config = {}) {
1199
1244
  const { actions = [], hasAtLeastOne = false } = config;
1200
1245
  const { userAbility } = ctx.state;
@@ -1588,9 +1633,11 @@ const multipleLocaleSchema = strapiUtils.yup.lazy(
1588
1633
  (value) => Array.isArray(value) ? strapiUtils.yup.array().of(singleLocaleSchema.required()) : singleLocaleSchema
1589
1634
  );
1590
1635
  const statusSchema = strapiUtils.yup.mixed().oneOf(["draft", "published"], "Invalid status");
1591
- const getDocumentLocaleAndStatus = async (request, opts = { allowMultipleLocales: false }) => {
1636
+ const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
1592
1637
  const { allowMultipleLocales } = opts;
1593
- const { locale, status, ...rest } = request || {};
1638
+ const { locale, status: providedStatus, ...rest } = request || {};
1639
+ const defaultStatus = strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
1640
+ const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
1594
1641
  const schema = strapiUtils.yup.object().shape({
1595
1642
  locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
1596
1643
  status: statusSchema
@@ -1638,7 +1685,7 @@ const createDocument = async (ctx, opts) => {
1638
1685
  const setCreator = strapiUtils.setCreatorFields({ user });
1639
1686
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1640
1687
  const sanitizedBody = await sanitizeFn(body);
1641
- const { locale, status = "draft" } = await getDocumentLocaleAndStatus(body);
1688
+ const { locale, status } = await getDocumentLocaleAndStatus(body, model);
1642
1689
  return documentManager2.create(model, {
1643
1690
  data: sanitizedBody,
1644
1691
  locale,
@@ -1657,7 +1704,7 @@ const updateDocument = async (ctx, opts) => {
1657
1704
  }
1658
1705
  const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
1659
1706
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1660
- const { locale } = await getDocumentLocaleAndStatus(body);
1707
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1661
1708
  const [documentVersion, documentExists] = await Promise.all([
1662
1709
  documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
1663
1710
  documentManager2.exists(model, id)
@@ -1673,7 +1720,7 @@ const updateDocument = async (ctx, opts) => {
1673
1720
  throw new strapiUtils.errors.ForbiddenError();
1674
1721
  }
1675
1722
  const pickPermittedFields = documentVersion ? permissionChecker2.sanitizeUpdateInput(documentVersion) : permissionChecker2.sanitizeCreateInput;
1676
- const setCreator = strapiUtils.setCreatorFields({ user, isEdition: true });
1723
+ const setCreator = documentVersion ? strapiUtils.setCreatorFields({ user, isEdition: true }) : strapiUtils.setCreatorFields({ user });
1677
1724
  const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
1678
1725
  const sanitizedBody = await sanitizeFn(body);
1679
1726
  return documentManager2.update(documentVersion?.documentId || id, model, {
@@ -1695,7 +1742,7 @@ const collectionTypes = {
1695
1742
  }
1696
1743
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
1697
1744
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
1698
- const { locale, status } = await getDocumentLocaleAndStatus(query);
1745
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
1699
1746
  const { results: documents, pagination } = await documentManager2.findPage(
1700
1747
  { ...permissionQuery, populate, locale, status },
1701
1748
  model
@@ -1730,7 +1777,7 @@ const collectionTypes = {
1730
1777
  }
1731
1778
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
1732
1779
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1733
- const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
1780
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
1734
1781
  const version = await documentManager2.findOne(id, model, {
1735
1782
  populate,
1736
1783
  locale,
@@ -1745,7 +1792,7 @@ const collectionTypes = {
1745
1792
  permissionChecker2,
1746
1793
  model,
1747
1794
  // @ts-expect-error TODO: fix
1748
- { id, locale, publishedAt: null },
1795
+ { documentId: id, locale, publishedAt: null },
1749
1796
  { availableLocales: true, availableStatus: false }
1750
1797
  );
1751
1798
  ctx.body = { data: {}, meta };
@@ -1797,7 +1844,7 @@ const collectionTypes = {
1797
1844
  }
1798
1845
  const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
1799
1846
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1800
- const { locale } = await getDocumentLocaleAndStatus(body);
1847
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1801
1848
  const document = await documentManager2.findOne(id, model, {
1802
1849
  populate,
1803
1850
  locale,
@@ -1842,7 +1889,7 @@ const collectionTypes = {
1842
1889
  }
1843
1890
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
1844
1891
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1845
- const { locale } = await getDocumentLocaleAndStatus(ctx.query);
1892
+ const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
1846
1893
  const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
1847
1894
  if (documentLocales.length === 0) {
1848
1895
  return ctx.notFound();
@@ -1871,11 +1918,34 @@ const collectionTypes = {
1871
1918
  const publishedDocument = await strapi.db.transaction(async () => {
1872
1919
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1873
1920
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1874
- const document = id ? await updateDocument(ctx, { populate }) : await createDocument(ctx, { populate });
1921
+ let document;
1922
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1923
+ const isCreate = fp.isNil(id);
1924
+ if (isCreate) {
1925
+ if (permissionChecker2.cannot.create()) {
1926
+ throw new strapiUtils.errors.ForbiddenError();
1927
+ }
1928
+ document = await createDocument(ctx, { populate });
1929
+ }
1930
+ const isUpdate = !isCreate;
1931
+ if (isUpdate) {
1932
+ const documentExists = documentManager2.exists(model, id);
1933
+ if (!documentExists) {
1934
+ throw new strapiUtils.errors.NotFoundError("Document not found");
1935
+ }
1936
+ document = await documentManager2.findOne(id, model, { populate, locale });
1937
+ if (!document) {
1938
+ if (permissionChecker2.cannot.create({ locale }) || permissionChecker2.cannot.publish({ locale })) {
1939
+ throw new strapiUtils.errors.ForbiddenError();
1940
+ }
1941
+ document = await updateDocument(ctx);
1942
+ } else if (permissionChecker2.can.update(document)) {
1943
+ await updateDocument(ctx);
1944
+ }
1945
+ }
1875
1946
  if (permissionChecker2.cannot.publish(document)) {
1876
1947
  throw new strapiUtils.errors.ForbiddenError();
1877
1948
  }
1878
- const { locale } = await getDocumentLocaleAndStatus(body);
1879
1949
  const publishResult = await documentManager2.publish(document.documentId, model, {
1880
1950
  locale
1881
1951
  // TODO: Allow setting creator fields on publish
@@ -1902,7 +1972,9 @@ const collectionTypes = {
1902
1972
  }
1903
1973
  const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
1904
1974
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
1905
- const { locale } = await getDocumentLocaleAndStatus(body, { allowMultipleLocales: true });
1975
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
1976
+ allowMultipleLocales: true
1977
+ });
1906
1978
  const entityPromises = documentIds.map(
1907
1979
  (documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
1908
1980
  );
@@ -1929,7 +2001,9 @@ const collectionTypes = {
1929
2001
  if (permissionChecker2.cannot.unpublish()) {
1930
2002
  return ctx.forbidden();
1931
2003
  }
1932
- const { locale } = await getDocumentLocaleAndStatus(body);
2004
+ const { locale } = await getDocumentLocaleAndStatus(body, model, {
2005
+ allowMultipleLocales: true
2006
+ });
1933
2007
  const entityPromises = documentIds.map(
1934
2008
  (documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
1935
2009
  );
@@ -1962,7 +2036,7 @@ const collectionTypes = {
1962
2036
  }
1963
2037
  const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
1964
2038
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
1965
- const { locale } = await getDocumentLocaleAndStatus(body);
2039
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
1966
2040
  const document = await documentManager2.findOne(id, model, {
1967
2041
  populate,
1968
2042
  locale,
@@ -1999,7 +2073,7 @@ const collectionTypes = {
1999
2073
  }
2000
2074
  const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
2001
2075
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2002
- const { locale } = await getDocumentLocaleAndStatus(body);
2076
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2003
2077
  const document = await documentManager2.findOne(id, model, {
2004
2078
  populate,
2005
2079
  locale,
@@ -2030,7 +2104,7 @@ const collectionTypes = {
2030
2104
  }
2031
2105
  const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
2032
2106
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2033
- const { locale } = await getDocumentLocaleAndStatus(body);
2107
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2034
2108
  const documentLocales = await documentManager2.findLocales(documentIds, model, {
2035
2109
  populate,
2036
2110
  locale
@@ -2057,7 +2131,7 @@ const collectionTypes = {
2057
2131
  }
2058
2132
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
2059
2133
  const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
2060
- const { locale, status = "draft" } = await getDocumentLocaleAndStatus(ctx.query);
2134
+ const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
2061
2135
  const entity = await documentManager2.findOne(id, model, { populate, locale, status });
2062
2136
  if (!entity) {
2063
2137
  return ctx.notFound();
@@ -2080,7 +2154,7 @@ const collectionTypes = {
2080
2154
  if (permissionChecker2.cannot.read()) {
2081
2155
  return ctx.forbidden();
2082
2156
  }
2083
- const entities = await documentManager2.findMany(
2157
+ const documents = await documentManager2.findMany(
2084
2158
  {
2085
2159
  filters: {
2086
2160
  documentId: ids
@@ -2089,7 +2163,7 @@ const collectionTypes = {
2089
2163
  },
2090
2164
  model
2091
2165
  );
2092
- if (!entities) {
2166
+ if (!documents) {
2093
2167
  return ctx.notFound();
2094
2168
  }
2095
2169
  const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
@@ -2280,20 +2354,13 @@ const sanitizeMainField = (model, mainField, userAbility) => {
2280
2354
  userAbility,
2281
2355
  model: model.uid
2282
2356
  });
2283
- if (!isListable(model, mainField)) {
2357
+ const isMainFieldListable = isListable(model, mainField);
2358
+ const canReadMainField = permissionChecker2.can.read(null, mainField);
2359
+ if (!isMainFieldListable || !canReadMainField) {
2284
2360
  return "id";
2285
2361
  }
2286
- if (permissionChecker2.cannot.read(null, mainField)) {
2287
- if (model.uid === "plugin::users-permissions.role") {
2288
- const userPermissionChecker = getService$1("permission-checker").create({
2289
- userAbility,
2290
- model: "plugin::users-permissions.user"
2291
- });
2292
- if (userPermissionChecker.can.read()) {
2293
- return "name";
2294
- }
2295
- }
2296
- return "id";
2362
+ if (model.uid === "plugin::users-permissions.role") {
2363
+ return "name";
2297
2364
  }
2298
2365
  return mainField;
2299
2366
  };
@@ -2326,11 +2393,8 @@ const validateLocale = (sourceUid, targetUid, locale) => {
2326
2393
  const isLocalized = strapi.plugin("i18n").service("content-types").isLocalizedContentType;
2327
2394
  const isSourceLocalized = isLocalized(sourceModel);
2328
2395
  const isTargetLocalized = isLocalized(targetModel);
2329
- let validatedLocale = locale;
2330
- if (!targetModel || !isTargetLocalized)
2331
- validatedLocale = void 0;
2332
2396
  return {
2333
- locale: validatedLocale,
2397
+ locale,
2334
2398
  isSourceLocalized,
2335
2399
  isTargetLocalized
2336
2400
  };
@@ -2433,7 +2497,7 @@ const relations = {
2433
2497
  attribute,
2434
2498
  fieldsToSelect,
2435
2499
  mainField,
2436
- source: { schema: sourceSchema },
2500
+ source: { schema: sourceSchema, isLocalized: isSourceLocalized },
2437
2501
  target: { schema: targetSchema, isLocalized: isTargetLocalized },
2438
2502
  sourceSchema,
2439
2503
  targetSchema,
@@ -2455,7 +2519,8 @@ const relations = {
2455
2519
  fieldsToSelect,
2456
2520
  mainField,
2457
2521
  source: {
2458
- schema: { uid: sourceUid, modelType: sourceModelType }
2522
+ schema: { uid: sourceUid, modelType: sourceModelType },
2523
+ isLocalized: isSourceLocalized
2459
2524
  },
2460
2525
  target: {
2461
2526
  schema: { uid: targetUid },
@@ -2493,12 +2558,16 @@ const relations = {
2493
2558
  } else {
2494
2559
  where.id = id;
2495
2560
  }
2496
- if (status) {
2497
- where[`${alias}.published_at`] = getPublishedAtClause(status, targetUid);
2561
+ const publishedAt = getPublishedAtClause(status, targetUid);
2562
+ if (!fp.isEmpty(publishedAt)) {
2563
+ where[`${alias}.published_at`] = publishedAt;
2498
2564
  }
2499
- if (filterByLocale) {
2565
+ if (isTargetLocalized && locale) {
2500
2566
  where[`${alias}.locale`] = locale;
2501
2567
  }
2568
+ if (isSourceLocalized && locale) {
2569
+ where.locale = locale;
2570
+ }
2502
2571
  if ((idsToInclude?.length ?? 0) !== 0) {
2503
2572
  where[`${alias}.id`].$notIn = idsToInclude;
2504
2573
  }
@@ -2516,7 +2585,8 @@ const relations = {
2516
2585
  id: { $notIn: fp.uniq(idsToOmit) }
2517
2586
  });
2518
2587
  }
2519
- const res = await strapi.db.query(targetUid).findPage(strapi.get("query-params").transform(targetUid, queryParams));
2588
+ const dbQuery = strapi.get("query-params").transform(targetUid, queryParams);
2589
+ const res = await strapi.db.query(targetUid).findPage(dbQuery);
2520
2590
  ctx.body = {
2521
2591
  ...res,
2522
2592
  results: await addStatusToRelations(targetUid, res.results)
@@ -2551,11 +2621,12 @@ const relations = {
2551
2621
  addFiltersClause(permissionQuery, { id: { $in: loadedIds } });
2552
2622
  const sanitizedRes = await loadRelations({ id: entryId }, targetField, {
2553
2623
  ...strapi.get("query-params").transform(targetUid, permissionQuery),
2554
- ordering: "desc",
2555
- page: ctx.request.query.page,
2556
- pageSize: ctx.request.query.pageSize
2624
+ ordering: "desc"
2557
2625
  });
2558
- const relationsUnion = fp.uniqBy("id", fp.concat(sanitizedRes.results, res.results));
2626
+ const relationsUnion = fp.uniqBy(
2627
+ (res2) => `${res2.documentId}-${res2.locale}`,
2628
+ fp.concat(sanitizedRes.results, res.results)
2629
+ );
2559
2630
  ctx.body = {
2560
2631
  pagination: res.pagination || {
2561
2632
  page: 1,
@@ -2585,7 +2656,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
2585
2656
  throw new strapiUtils.errors.ForbiddenError();
2586
2657
  }
2587
2658
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2588
- const { locale } = await getDocumentLocaleAndStatus(body);
2659
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2589
2660
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2590
2661
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2591
2662
  // Find the first document to check if it exists
@@ -2626,7 +2697,7 @@ const singleTypes = {
2626
2697
  return ctx.forbidden();
2627
2698
  }
2628
2699
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2629
- const { locale, status } = await getDocumentLocaleAndStatus(query);
2700
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
2630
2701
  const version = await findDocument(permissionQuery, model, { locale, status });
2631
2702
  if (!version) {
2632
2703
  if (permissionChecker2.cannot.create()) {
@@ -2640,7 +2711,7 @@ const singleTypes = {
2640
2711
  permissionChecker2,
2641
2712
  model,
2642
2713
  // @ts-expect-error - fix types
2643
- { id: document.documentId, locale, publishedAt: null },
2714
+ { documentId: document.documentId, locale, publishedAt: null },
2644
2715
  { availableLocales: true, availableStatus: false }
2645
2716
  );
2646
2717
  ctx.body = { data: {}, meta };
@@ -2671,7 +2742,7 @@ const singleTypes = {
2671
2742
  }
2672
2743
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2673
2744
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2674
- const { locale } = await getDocumentLocaleAndStatus(query);
2745
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2675
2746
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2676
2747
  populate,
2677
2748
  locale
@@ -2708,7 +2779,7 @@ const singleTypes = {
2708
2779
  if (permissionChecker2.cannot.publish(document)) {
2709
2780
  throw new strapiUtils.errors.ForbiddenError();
2710
2781
  }
2711
- const { locale } = await getDocumentLocaleAndStatus(document);
2782
+ const { locale } = await getDocumentLocaleAndStatus(document, model);
2712
2783
  const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2713
2784
  return publishResult.at(0);
2714
2785
  });
@@ -2731,7 +2802,7 @@ const singleTypes = {
2731
2802
  return ctx.forbidden();
2732
2803
  }
2733
2804
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2734
- const { locale } = await getDocumentLocaleAndStatus(body);
2805
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2735
2806
  const document = await findDocument(sanitizedQuery, model, { locale });
2736
2807
  if (!document) {
2737
2808
  return ctx.notFound();
@@ -2763,7 +2834,7 @@ const singleTypes = {
2763
2834
  return ctx.forbidden();
2764
2835
  }
2765
2836
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2766
- const { locale } = await getDocumentLocaleAndStatus(body);
2837
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2767
2838
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2768
2839
  if (!document) {
2769
2840
  return ctx.notFound();
@@ -2783,7 +2854,7 @@ const singleTypes = {
2783
2854
  const { query } = ctx.request;
2784
2855
  const documentManager2 = getService$1("document-manager");
2785
2856
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2786
- const { locale } = await getDocumentLocaleAndStatus(query);
2857
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2787
2858
  if (permissionChecker2.cannot.read()) {
2788
2859
  return ctx.forbidden();
2789
2860
  }
@@ -2804,7 +2875,7 @@ const uid$1 = {
2804
2875
  async generateUID(ctx) {
2805
2876
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2806
2877
  const { query = {} } = ctx.request;
2807
- const { locale } = await getDocumentLocaleAndStatus(query);
2878
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2808
2879
  await validateUIDField(contentTypeUID, field);
2809
2880
  const uidService = getService$1("uid");
2810
2881
  ctx.body = {
@@ -2816,7 +2887,7 @@ const uid$1 = {
2816
2887
  ctx.request.body
2817
2888
  );
2818
2889
  const { query = {} } = ctx.request;
2819
- const { locale } = await getDocumentLocaleAndStatus(query);
2890
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2820
2891
  await validateUIDField(contentTypeUID, field);
2821
2892
  const uidService = getService$1("uid");
2822
2893
  const isAvailable = await uidService.checkUIDAvailability({
@@ -3459,12 +3530,27 @@ const createPermissionChecker = (strapi2) => ({ userAbility, model }) => {
3459
3530
  ability: userAbility,
3460
3531
  model
3461
3532
  });
3462
- const toSubject = (entity) => entity ? permissionsManager.toSubject(entity, model) : model;
3533
+ const { actionProvider } = strapi2.service("admin::permission");
3534
+ const toSubject = (entity) => {
3535
+ return entity ? permissionsManager.toSubject(entity, model) : model;
3536
+ };
3463
3537
  const can = (action, entity, field) => {
3464
- return userAbility.can(action, toSubject(entity), field);
3538
+ const subject = toSubject(entity);
3539
+ const aliases = actionProvider.unstable_aliases(action, model);
3540
+ return (
3541
+ // Test the original action to see if it passes
3542
+ userAbility.can(action, subject, field) || // Else try every known alias if at least one of them succeed, then the user "can"
3543
+ aliases.some((alias) => userAbility.can(alias, subject, field))
3544
+ );
3465
3545
  };
3466
3546
  const cannot = (action, entity, field) => {
3467
- return userAbility.cannot(action, toSubject(entity), field);
3547
+ const subject = toSubject(entity);
3548
+ const aliases = actionProvider.unstable_aliases(action, model);
3549
+ return (
3550
+ // Test both the original action
3551
+ userAbility.cannot(action, subject, field) && // and every known alias, if all of them fail (cannot), then the user truly "cannot"
3552
+ aliases.every((alias) => userAbility.cannot(alias, subject, field))
3553
+ );
3468
3554
  };
3469
3555
  const sanitizeOutput = (data, { action = ACTIONS.read } = {}) => {
3470
3556
  return permissionsManager.sanitizeOutput(data, { subject: toSubject(data), action });
@@ -3741,6 +3827,10 @@ const getDeepPopulateDraftCount = (uid2) => {
3741
3827
  const attribute = model.attributes[attributeName];
3742
3828
  switch (attribute.type) {
3743
3829
  case "relation": {
3830
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
3831
+ if (isMorphRelation) {
3832
+ break;
3833
+ }
3744
3834
  if (isVisibleAttribute$1(model, attributeName)) {
3745
3835
  populateAcc[attributeName] = {
3746
3836
  count: true,
@@ -3755,22 +3845,24 @@ const getDeepPopulateDraftCount = (uid2) => {
3755
3845
  attribute.component
3756
3846
  );
3757
3847
  if (childHasRelations) {
3758
- populateAcc[attributeName] = { populate: populate2 };
3848
+ populateAcc[attributeName] = {
3849
+ populate: populate2
3850
+ };
3759
3851
  hasRelations = true;
3760
3852
  }
3761
3853
  break;
3762
3854
  }
3763
3855
  case "dynamiczone": {
3764
- const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
3765
- const { populate: populate2, hasRelations: childHasRelations } = getDeepPopulateDraftCount(componentUID);
3766
- if (childHasRelations) {
3856
+ const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
3857
+ const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
3858
+ if (componentHasRelations) {
3767
3859
  hasRelations = true;
3768
- return fp.merge(acc, populate2);
3860
+ return { ...acc, [componentUID]: { populate: componentPopulate } };
3769
3861
  }
3770
3862
  return acc;
3771
3863
  }, {});
3772
- if (!fp.isEmpty(dzPopulate)) {
3773
- populateAcc[attributeName] = { populate: dzPopulate };
3864
+ if (!fp.isEmpty(dzPopulateFragment)) {
3865
+ populateAcc[attributeName] = { on: dzPopulateFragment };
3774
3866
  }
3775
3867
  break;
3776
3868
  }
@@ -4116,7 +4208,13 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4116
4208
  */
4117
4209
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
4118
4210
  if (!document) {
4119
- return document;
4211
+ return {
4212
+ data: document,
4213
+ meta: {
4214
+ availableLocales: [],
4215
+ availableStatus: []
4216
+ }
4217
+ };
4120
4218
  }
4121
4219
  const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
4122
4220
  if (!hasDraftAndPublish) {
@@ -4224,10 +4322,7 @@ const documentManager = ({ strapi: strapi2 }) => {
4224
4322
  async clone(id, body, uid2) {
4225
4323
  const populate = await buildDeepPopulate(uid2);
4226
4324
  const params = {
4227
- data: {
4228
- ...omitIdField(body),
4229
- [PUBLISHED_AT_ATTRIBUTE]: null
4230
- },
4325
+ data: omitIdField(body),
4231
4326
  populate
4232
4327
  };
4233
4328
  return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));