@strapi/content-manager 0.0.0-experimental.f75e3c6d67cc47c64ab37479efdbb7b43be50b78 → 0.0.0-next.ce84fada19d58a7dfbdd553035e6558f8befcba4

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-DfFSZQxe.mjs} +3 -3
  3. package/dist/_chunks/{ComponentConfigurationPage-CuWgXugY.mjs.map → ComponentConfigurationPage-DfFSZQxe.mjs.map} +1 -1
  4. package/dist/_chunks/{ComponentConfigurationPage-by0e_kNd.js → ComponentConfigurationPage-FqfsxQ1j.js} +3 -3
  5. package/dist/_chunks/{ComponentConfigurationPage-by0e_kNd.js.map → ComponentConfigurationPage-FqfsxQ1j.js.map} +1 -1
  6. package/dist/_chunks/{EditConfigurationPage-CqBeCPGH.js → EditConfigurationPage-Cn0e8t3I.js} +3 -3
  7. package/dist/_chunks/{EditConfigurationPage-CqBeCPGH.js.map → EditConfigurationPage-Cn0e8t3I.js.map} +1 -1
  8. package/dist/_chunks/{EditConfigurationPage-DbI4KMyz.mjs → EditConfigurationPage-DdPNAbl3.mjs} +3 -3
  9. package/dist/_chunks/{EditConfigurationPage-DbI4KMyz.mjs.map → EditConfigurationPage-DdPNAbl3.mjs.map} +1 -1
  10. package/dist/_chunks/{EditViewPage-dFPBya9U.mjs → EditViewPage-B82x_x1b.mjs} +69 -48
  11. package/dist/_chunks/EditViewPage-B82x_x1b.mjs.map +1 -0
  12. package/dist/_chunks/{EditViewPage-ChgloMyO.js → EditViewPage-DlxEHhUt.js} +68 -47
  13. package/dist/_chunks/EditViewPage-DlxEHhUt.js.map +1 -0
  14. package/dist/_chunks/{Field-dLk-vgLL.js → Field-COL25JiC.js} +581 -229
  15. package/dist/_chunks/Field-COL25JiC.js.map +1 -0
  16. package/dist/_chunks/{Field-C1nUKcdS.mjs → Field-DufHXW17.mjs} +579 -227
  17. package/dist/_chunks/Field-DufHXW17.mjs.map +1 -0
  18. package/dist/_chunks/{Form-CbXtmHC_.js → Form-BssUwrTO.js} +52 -34
  19. package/dist/_chunks/Form-BssUwrTO.js.map +1 -0
  20. package/dist/_chunks/{Form-DOlpi7Js.mjs → Form-u_kAOhwB.mjs} +54 -36
  21. package/dist/_chunks/Form-u_kAOhwB.mjs.map +1 -0
  22. package/dist/_chunks/{History-BjDfohBr.js → History-C9t9UqpO.js} +158 -40
  23. package/dist/_chunks/History-C9t9UqpO.js.map +1 -0
  24. package/dist/_chunks/{History-BFNUAiGc.mjs → History-DRwA3oMM.mjs} +159 -41
  25. package/dist/_chunks/History-DRwA3oMM.mjs.map +1 -0
  26. package/dist/_chunks/{ListConfigurationPage-IQBgWTaa.js → ListConfigurationPage-BXYPohh-.js} +57 -46
  27. package/dist/_chunks/ListConfigurationPage-BXYPohh-.js.map +1 -0
  28. package/dist/_chunks/{ListConfigurationPage-DDi0KqFm.mjs → ListConfigurationPage-BxfQJzPk.mjs} +58 -48
  29. package/dist/_chunks/ListConfigurationPage-BxfQJzPk.mjs.map +1 -0
  30. package/dist/_chunks/{ListViewPage-BPjljUsH.mjs → ListViewPage-CELx2ysp.mjs} +116 -103
  31. package/dist/_chunks/ListViewPage-CELx2ysp.mjs.map +1 -0
  32. package/dist/_chunks/{ListViewPage-CZYGqlvF.js → ListViewPage-D2VD8Szg.js} +117 -104
  33. package/dist/_chunks/ListViewPage-D2VD8Szg.js.map +1 -0
  34. package/dist/_chunks/{NoContentTypePage-BOAI6VZ1.js → NoContentTypePage-BV9IjJSM.js} +2 -2
  35. package/dist/_chunks/{NoContentTypePage-BOAI6VZ1.js.map → NoContentTypePage-BV9IjJSM.js.map} +1 -1
  36. package/dist/_chunks/{NoContentTypePage-DaWw67K-.mjs → NoContentTypePage-DtJ9jcfk.mjs} +2 -2
  37. package/dist/_chunks/{NoContentTypePage-DaWw67K-.mjs.map → NoContentTypePage-DtJ9jcfk.mjs.map} +1 -1
  38. package/dist/_chunks/{NoPermissionsPage-CZrJH00p.mjs → NoPermissionsPage-DWleVYK7.mjs} +2 -2
  39. package/dist/_chunks/{NoPermissionsPage-CZrJH00p.mjs.map → NoPermissionsPage-DWleVYK7.mjs.map} +1 -1
  40. package/dist/_chunks/{NoPermissionsPage-cYEtLc_e.js → NoPermissionsPage-Dp8NpF9I.js} +2 -2
  41. package/dist/_chunks/{NoPermissionsPage-cYEtLc_e.js.map → NoPermissionsPage-Dp8NpF9I.js.map} +1 -1
  42. package/dist/_chunks/{Relations-DTowyge2.mjs → Relations-BTcf5xaw.mjs} +34 -25
  43. package/dist/_chunks/Relations-BTcf5xaw.mjs.map +1 -0
  44. package/dist/_chunks/{Relations-DU6B7irU.js → Relations-DR7EUgyC.js} +34 -25
  45. package/dist/_chunks/Relations-DR7EUgyC.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-CCJeB7Rw.js → index-BdMf2lfT.js} +1357 -747
  51. package/dist/_chunks/index-BdMf2lfT.js.map +1 -0
  52. package/dist/_chunks/{index-BaGHmIir.mjs → index-wnqzm4Q8.mjs} +1390 -780
  53. package/dist/_chunks/index-wnqzm4Q8.mjs.map +1 -0
  54. package/dist/_chunks/{layout-BinjszSQ.mjs → layout-2CfjL0T9.mjs} +41 -23
  55. package/dist/_chunks/layout-2CfjL0T9.mjs.map +1 -0
  56. package/dist/_chunks/{layout-ni_L9kT1.js → layout-B2MyZU-_.js} +39 -21
  57. package/dist/_chunks/layout-B2MyZU-_.js.map +1 -0
  58. package/dist/_chunks/{relations-CeJAJc5I.js → relations-BH7JJGGe.js} +2 -2
  59. package/dist/_chunks/{relations-CeJAJc5I.js.map → relations-BH7JJGGe.js.map} +1 -1
  60. package/dist/_chunks/{relations-c91ji5eR.mjs → relations-C0w0GcXi.mjs} +2 -2
  61. package/dist/_chunks/{relations-c91ji5eR.mjs.map → relations-C0w0GcXi.mjs.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 +218 -126
  95. package/dist/server/index.js.map +1 -1
  96. package/dist/server/index.mjs +219 -127
  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,9 +2621,7 @@ 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
2626
  const relationsUnion = fp.uniqBy("id", fp.concat(sanitizedRes.results, res.results));
2559
2627
  ctx.body = {
@@ -2585,7 +2653,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
2585
2653
  throw new strapiUtils.errors.ForbiddenError();
2586
2654
  }
2587
2655
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
2588
- const { locale } = await getDocumentLocaleAndStatus(body);
2656
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2589
2657
  const [documentVersion, otherDocumentVersion] = await Promise.all([
2590
2658
  findDocument(sanitizedQuery, model, { locale, status: "draft" }),
2591
2659
  // Find the first document to check if it exists
@@ -2626,7 +2694,7 @@ const singleTypes = {
2626
2694
  return ctx.forbidden();
2627
2695
  }
2628
2696
  const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
2629
- const { locale, status } = await getDocumentLocaleAndStatus(query);
2697
+ const { locale, status } = await getDocumentLocaleAndStatus(query, model);
2630
2698
  const version = await findDocument(permissionQuery, model, { locale, status });
2631
2699
  if (!version) {
2632
2700
  if (permissionChecker2.cannot.create()) {
@@ -2640,7 +2708,7 @@ const singleTypes = {
2640
2708
  permissionChecker2,
2641
2709
  model,
2642
2710
  // @ts-expect-error - fix types
2643
- { id: document.documentId, locale, publishedAt: null },
2711
+ { documentId: document.documentId, locale, publishedAt: null },
2644
2712
  { availableLocales: true, availableStatus: false }
2645
2713
  );
2646
2714
  ctx.body = { data: {}, meta };
@@ -2671,7 +2739,7 @@ const singleTypes = {
2671
2739
  }
2672
2740
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
2673
2741
  const populate = await buildPopulateFromQuery(sanitizedQuery, model);
2674
- const { locale } = await getDocumentLocaleAndStatus(query);
2742
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2675
2743
  const documentLocales = await documentManager2.findLocales(void 0, model, {
2676
2744
  populate,
2677
2745
  locale
@@ -2708,7 +2776,7 @@ const singleTypes = {
2708
2776
  if (permissionChecker2.cannot.publish(document)) {
2709
2777
  throw new strapiUtils.errors.ForbiddenError();
2710
2778
  }
2711
- const { locale } = await getDocumentLocaleAndStatus(document);
2779
+ const { locale } = await getDocumentLocaleAndStatus(document, model);
2712
2780
  const publishResult = await documentManager2.publish(document.documentId, model, { locale });
2713
2781
  return publishResult.at(0);
2714
2782
  });
@@ -2731,7 +2799,7 @@ const singleTypes = {
2731
2799
  return ctx.forbidden();
2732
2800
  }
2733
2801
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
2734
- const { locale } = await getDocumentLocaleAndStatus(body);
2802
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2735
2803
  const document = await findDocument(sanitizedQuery, model, { locale });
2736
2804
  if (!document) {
2737
2805
  return ctx.notFound();
@@ -2763,7 +2831,7 @@ const singleTypes = {
2763
2831
  return ctx.forbidden();
2764
2832
  }
2765
2833
  const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
2766
- const { locale } = await getDocumentLocaleAndStatus(body);
2834
+ const { locale } = await getDocumentLocaleAndStatus(body, model);
2767
2835
  const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
2768
2836
  if (!document) {
2769
2837
  return ctx.notFound();
@@ -2783,7 +2851,7 @@ const singleTypes = {
2783
2851
  const { query } = ctx.request;
2784
2852
  const documentManager2 = getService$1("document-manager");
2785
2853
  const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
2786
- const { locale } = await getDocumentLocaleAndStatus(query);
2854
+ const { locale } = await getDocumentLocaleAndStatus(query, model);
2787
2855
  if (permissionChecker2.cannot.read()) {
2788
2856
  return ctx.forbidden();
2789
2857
  }
@@ -2804,7 +2872,7 @@ const uid$1 = {
2804
2872
  async generateUID(ctx) {
2805
2873
  const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
2806
2874
  const { query = {} } = ctx.request;
2807
- const { locale } = await getDocumentLocaleAndStatus(query);
2875
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2808
2876
  await validateUIDField(contentTypeUID, field);
2809
2877
  const uidService = getService$1("uid");
2810
2878
  ctx.body = {
@@ -2816,7 +2884,7 @@ const uid$1 = {
2816
2884
  ctx.request.body
2817
2885
  );
2818
2886
  const { query = {} } = ctx.request;
2819
- const { locale } = await getDocumentLocaleAndStatus(query);
2887
+ const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
2820
2888
  await validateUIDField(contentTypeUID, field);
2821
2889
  const uidService = getService$1("uid");
2822
2890
  const isAvailable = await uidService.checkUIDAvailability({
@@ -3459,12 +3527,27 @@ const createPermissionChecker = (strapi2) => ({ userAbility, model }) => {
3459
3527
  ability: userAbility,
3460
3528
  model
3461
3529
  });
3462
- const toSubject = (entity) => entity ? permissionsManager.toSubject(entity, model) : model;
3530
+ const { actionProvider } = strapi2.service("admin::permission");
3531
+ const toSubject = (entity) => {
3532
+ return entity ? permissionsManager.toSubject(entity, model) : model;
3533
+ };
3463
3534
  const can = (action, entity, field) => {
3464
- return userAbility.can(action, toSubject(entity), field);
3535
+ const subject = toSubject(entity);
3536
+ const aliases = actionProvider.unstable_aliases(action, model);
3537
+ return (
3538
+ // Test the original action to see if it passes
3539
+ userAbility.can(action, subject, field) || // Else try every known alias if at least one of them succeed, then the user "can"
3540
+ aliases.some((alias) => userAbility.can(alias, subject, field))
3541
+ );
3465
3542
  };
3466
3543
  const cannot = (action, entity, field) => {
3467
- return userAbility.cannot(action, toSubject(entity), field);
3544
+ const subject = toSubject(entity);
3545
+ const aliases = actionProvider.unstable_aliases(action, model);
3546
+ return (
3547
+ // Test both the original action
3548
+ userAbility.cannot(action, subject, field) && // and every known alias, if all of them fail (cannot), then the user truly "cannot"
3549
+ aliases.every((alias) => userAbility.cannot(alias, subject, field))
3550
+ );
3468
3551
  };
3469
3552
  const sanitizeOutput = (data, { action = ACTIONS.read } = {}) => {
3470
3553
  return permissionsManager.sanitizeOutput(data, { subject: toSubject(data), action });
@@ -3741,6 +3824,10 @@ const getDeepPopulateDraftCount = (uid2) => {
3741
3824
  const attribute = model.attributes[attributeName];
3742
3825
  switch (attribute.type) {
3743
3826
  case "relation": {
3827
+ const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
3828
+ if (isMorphRelation) {
3829
+ break;
3830
+ }
3744
3831
  if (isVisibleAttribute$1(model, attributeName)) {
3745
3832
  populateAcc[attributeName] = {
3746
3833
  count: true,
@@ -3755,22 +3842,24 @@ const getDeepPopulateDraftCount = (uid2) => {
3755
3842
  attribute.component
3756
3843
  );
3757
3844
  if (childHasRelations) {
3758
- populateAcc[attributeName] = { populate: populate2 };
3845
+ populateAcc[attributeName] = {
3846
+ populate: populate2
3847
+ };
3759
3848
  hasRelations = true;
3760
3849
  }
3761
3850
  break;
3762
3851
  }
3763
3852
  case "dynamiczone": {
3764
- const dzPopulate = (attribute.components || []).reduce((acc, componentUID) => {
3765
- const { populate: populate2, hasRelations: childHasRelations } = getDeepPopulateDraftCount(componentUID);
3766
- if (childHasRelations) {
3853
+ const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
3854
+ const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
3855
+ if (componentHasRelations) {
3767
3856
  hasRelations = true;
3768
- return fp.merge(acc, populate2);
3857
+ return { ...acc, [componentUID]: { populate: componentPopulate } };
3769
3858
  }
3770
3859
  return acc;
3771
3860
  }, {});
3772
- if (!fp.isEmpty(dzPopulate)) {
3773
- populateAcc[attributeName] = { populate: dzPopulate };
3861
+ if (!fp.isEmpty(dzPopulateFragment)) {
3862
+ populateAcc[attributeName] = { on: dzPopulateFragment };
3774
3863
  }
3775
3864
  break;
3776
3865
  }
@@ -4116,7 +4205,13 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
4116
4205
  */
4117
4206
  async formatDocumentWithMetadata(uid2, document, opts = {}) {
4118
4207
  if (!document) {
4119
- return document;
4208
+ return {
4209
+ data: document,
4210
+ meta: {
4211
+ availableLocales: [],
4212
+ availableStatus: []
4213
+ }
4214
+ };
4120
4215
  }
4121
4216
  const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
4122
4217
  if (!hasDraftAndPublish) {
@@ -4224,10 +4319,7 @@ const documentManager = ({ strapi: strapi2 }) => {
4224
4319
  async clone(id, body, uid2) {
4225
4320
  const populate = await buildDeepPopulate(uid2);
4226
4321
  const params = {
4227
- data: {
4228
- ...omitIdField(body),
4229
- [PUBLISHED_AT_ATTRIBUTE]: null
4230
- },
4322
+ data: omitIdField(body),
4231
4323
  populate
4232
4324
  };
4233
4325
  return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));