@strapi/content-releases 0.0.0-experimental.f0d4afee92a0d386f80385590c87955656f995ce → 0.0.0-experimental.f28dba7c374eae9c02b95b4b77aba4c3ad41a841

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 (140) hide show
  1. package/dist/_chunks/App-dLXY5ei3.js +1353 -0
  2. package/dist/_chunks/App-dLXY5ei3.js.map +1 -0
  3. package/dist/_chunks/{App-Cne--1Z8.mjs → App-jrh58sXY.mjs} +558 -602
  4. package/dist/_chunks/App-jrh58sXY.mjs.map +1 -0
  5. package/dist/_chunks/{PurchaseContentReleases-_MxP6-Dt.mjs → PurchaseContentReleases-3tRbmbY3.mjs} +7 -8
  6. package/dist/_chunks/PurchaseContentReleases-3tRbmbY3.mjs.map +1 -0
  7. package/dist/_chunks/{PurchaseContentReleases-Be3acS2L.js → PurchaseContentReleases-bpIYXOfu.js} +6 -7
  8. package/dist/_chunks/PurchaseContentReleases-bpIYXOfu.js.map +1 -0
  9. package/dist/_chunks/{en-CmYoEnA7.js → en-HrREghh3.js} +2 -9
  10. package/dist/_chunks/en-HrREghh3.js.map +1 -0
  11. package/dist/_chunks/{en-D0yVZFqf.mjs → en-ltT1TlKQ.mjs} +2 -9
  12. package/dist/_chunks/en-ltT1TlKQ.mjs.map +1 -0
  13. package/dist/_chunks/index-CVO0Rqdm.js +1336 -0
  14. package/dist/_chunks/index-CVO0Rqdm.js.map +1 -0
  15. package/dist/_chunks/index-PiOGBETy.mjs +1315 -0
  16. package/dist/_chunks/index-PiOGBETy.mjs.map +1 -0
  17. package/dist/admin/index.js +15 -1
  18. package/dist/admin/index.js.map +1 -1
  19. package/dist/admin/index.mjs +16 -2
  20. package/dist/admin/index.mjs.map +1 -1
  21. package/dist/server/index.js +638 -834
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/server/index.mjs +638 -835
  24. package/dist/server/index.mjs.map +1 -1
  25. package/package.json +39 -33
  26. package/strapi-server.js +3 -0
  27. package/dist/_chunks/App-BKB1esYS.js +0 -1395
  28. package/dist/_chunks/App-BKB1esYS.js.map +0 -1
  29. package/dist/_chunks/App-Cne--1Z8.mjs.map +0 -1
  30. package/dist/_chunks/PurchaseContentReleases-Be3acS2L.js.map +0 -1
  31. package/dist/_chunks/PurchaseContentReleases-_MxP6-Dt.mjs.map +0 -1
  32. package/dist/_chunks/ReleasesSettingsPage-C1WwGWIH.mjs +0 -178
  33. package/dist/_chunks/ReleasesSettingsPage-C1WwGWIH.mjs.map +0 -1
  34. package/dist/_chunks/ReleasesSettingsPage-kuXIwpWp.js +0 -178
  35. package/dist/_chunks/ReleasesSettingsPage-kuXIwpWp.js.map +0 -1
  36. package/dist/_chunks/en-CmYoEnA7.js.map +0 -1
  37. package/dist/_chunks/en-D0yVZFqf.mjs.map +0 -1
  38. package/dist/_chunks/index-5Odi61vw.js +0 -1381
  39. package/dist/_chunks/index-5Odi61vw.js.map +0 -1
  40. package/dist/_chunks/index-Cy7qwpaU.mjs +0 -1362
  41. package/dist/_chunks/index-Cy7qwpaU.mjs.map +0 -1
  42. package/dist/_chunks/schemas-BE1LxE9J.js +0 -62
  43. package/dist/_chunks/schemas-BE1LxE9J.js.map +0 -1
  44. package/dist/_chunks/schemas-DdA2ic2U.mjs +0 -44
  45. package/dist/_chunks/schemas-DdA2ic2U.mjs.map +0 -1
  46. package/dist/admin/src/components/RelativeTime.d.ts +0 -28
  47. package/dist/admin/src/components/ReleaseAction.d.ts +0 -3
  48. package/dist/admin/src/components/ReleaseActionMenu.d.ts +0 -26
  49. package/dist/admin/src/components/ReleaseActionModal.d.ts +0 -24
  50. package/dist/admin/src/components/ReleaseActionOptions.d.ts +0 -9
  51. package/dist/admin/src/components/ReleaseListCell.d.ts +0 -28
  52. package/dist/admin/src/components/ReleaseModal.d.ts +0 -17
  53. package/dist/admin/src/components/ReleasesPanel.d.ts +0 -3
  54. package/dist/admin/src/constants.d.ts +0 -76
  55. package/dist/admin/src/index.d.ts +0 -3
  56. package/dist/admin/src/modules/hooks.d.ts +0 -7
  57. package/dist/admin/src/pages/App.d.ts +0 -1
  58. package/dist/admin/src/pages/PurchaseContentReleases.d.ts +0 -2
  59. package/dist/admin/src/pages/ReleaseDetailsPage.d.ts +0 -2
  60. package/dist/admin/src/pages/ReleasesPage.d.ts +0 -8
  61. package/dist/admin/src/pages/ReleasesSettingsPage.d.ts +0 -1
  62. package/dist/admin/src/pages/tests/mockReleaseDetailsPageData.d.ts +0 -181
  63. package/dist/admin/src/pages/tests/mockReleasesPageData.d.ts +0 -39
  64. package/dist/admin/src/pluginId.d.ts +0 -1
  65. package/dist/admin/src/services/release.d.ts +0 -112
  66. package/dist/admin/src/store/hooks.d.ts +0 -7
  67. package/dist/admin/src/utils/api.d.ts +0 -6
  68. package/dist/admin/src/utils/prefixPluginTranslations.d.ts +0 -3
  69. package/dist/admin/src/utils/time.d.ts +0 -10
  70. package/dist/admin/src/validation/schemas.d.ts +0 -6
  71. package/dist/server/src/bootstrap.d.ts +0 -5
  72. package/dist/server/src/bootstrap.d.ts.map +0 -1
  73. package/dist/server/src/constants.d.ts +0 -21
  74. package/dist/server/src/constants.d.ts.map +0 -1
  75. package/dist/server/src/content-types/index.d.ts +0 -97
  76. package/dist/server/src/content-types/index.d.ts.map +0 -1
  77. package/dist/server/src/content-types/release/index.d.ts +0 -48
  78. package/dist/server/src/content-types/release/index.d.ts.map +0 -1
  79. package/dist/server/src/content-types/release/schema.d.ts +0 -47
  80. package/dist/server/src/content-types/release/schema.d.ts.map +0 -1
  81. package/dist/server/src/content-types/release-action/index.d.ts +0 -48
  82. package/dist/server/src/content-types/release-action/index.d.ts.map +0 -1
  83. package/dist/server/src/content-types/release-action/schema.d.ts +0 -47
  84. package/dist/server/src/content-types/release-action/schema.d.ts.map +0 -1
  85. package/dist/server/src/controllers/index.d.ts +0 -25
  86. package/dist/server/src/controllers/index.d.ts.map +0 -1
  87. package/dist/server/src/controllers/release-action.d.ts +0 -10
  88. package/dist/server/src/controllers/release-action.d.ts.map +0 -1
  89. package/dist/server/src/controllers/release.d.ts +0 -18
  90. package/dist/server/src/controllers/release.d.ts.map +0 -1
  91. package/dist/server/src/controllers/settings.d.ts +0 -11
  92. package/dist/server/src/controllers/settings.d.ts.map +0 -1
  93. package/dist/server/src/controllers/validation/release-action.d.ts +0 -14
  94. package/dist/server/src/controllers/validation/release-action.d.ts.map +0 -1
  95. package/dist/server/src/controllers/validation/release.d.ts +0 -4
  96. package/dist/server/src/controllers/validation/release.d.ts.map +0 -1
  97. package/dist/server/src/controllers/validation/settings.d.ts +0 -3
  98. package/dist/server/src/controllers/validation/settings.d.ts.map +0 -1
  99. package/dist/server/src/destroy.d.ts +0 -5
  100. package/dist/server/src/destroy.d.ts.map +0 -1
  101. package/dist/server/src/index.d.ts +0 -2115
  102. package/dist/server/src/index.d.ts.map +0 -1
  103. package/dist/server/src/middlewares/documents.d.ts +0 -6
  104. package/dist/server/src/middlewares/documents.d.ts.map +0 -1
  105. package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts +0 -9
  106. package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts.map +0 -1
  107. package/dist/server/src/migrations/index.d.ts +0 -13
  108. package/dist/server/src/migrations/index.d.ts.map +0 -1
  109. package/dist/server/src/register.d.ts +0 -5
  110. package/dist/server/src/register.d.ts.map +0 -1
  111. package/dist/server/src/routes/index.d.ts +0 -51
  112. package/dist/server/src/routes/index.d.ts.map +0 -1
  113. package/dist/server/src/routes/release-action.d.ts +0 -18
  114. package/dist/server/src/routes/release-action.d.ts.map +0 -1
  115. package/dist/server/src/routes/release.d.ts +0 -18
  116. package/dist/server/src/routes/release.d.ts.map +0 -1
  117. package/dist/server/src/routes/settings.d.ts +0 -18
  118. package/dist/server/src/routes/settings.d.ts.map +0 -1
  119. package/dist/server/src/services/index.d.ts +0 -1828
  120. package/dist/server/src/services/index.d.ts.map +0 -1
  121. package/dist/server/src/services/release-action.d.ts +0 -38
  122. package/dist/server/src/services/release-action.d.ts.map +0 -1
  123. package/dist/server/src/services/release.d.ts +0 -31
  124. package/dist/server/src/services/release.d.ts.map +0 -1
  125. package/dist/server/src/services/scheduling.d.ts +0 -18
  126. package/dist/server/src/services/scheduling.d.ts.map +0 -1
  127. package/dist/server/src/services/settings.d.ts +0 -13
  128. package/dist/server/src/services/settings.d.ts.map +0 -1
  129. package/dist/server/src/services/validation.d.ts +0 -18
  130. package/dist/server/src/services/validation.d.ts.map +0 -1
  131. package/dist/server/src/utils/index.d.ts +0 -35
  132. package/dist/server/src/utils/index.d.ts.map +0 -1
  133. package/dist/shared/contracts/release-actions.d.ts +0 -130
  134. package/dist/shared/contracts/release-actions.d.ts.map +0 -1
  135. package/dist/shared/contracts/releases.d.ts +0 -184
  136. package/dist/shared/contracts/releases.d.ts.map +0 -1
  137. package/dist/shared/contracts/settings.d.ts +0 -39
  138. package/dist/shared/contracts/settings.d.ts.map +0 -1
  139. package/dist/shared/types.d.ts +0 -24
  140. package/dist/shared/types.d.ts.map +0 -1
@@ -3,6 +3,7 @@ const utils = require("@strapi/utils");
3
3
  const isEqual = require("lodash/isEqual");
4
4
  const lodash = require("lodash");
5
5
  const _ = require("lodash/fp");
6
+ const EE = require("@strapi/strapi/dist/utils/ee");
6
7
  const nodeSchedule = require("node-schedule");
7
8
  const yup = require("yup");
8
9
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
@@ -26,6 +27,7 @@ function _interopNamespace(e) {
26
27
  }
27
28
  const isEqual__default = /* @__PURE__ */ _interopDefault(isEqual);
28
29
  const ___default = /* @__PURE__ */ _interopDefault(_);
30
+ const EE__default = /* @__PURE__ */ _interopDefault(EE);
29
31
  const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
30
32
  const RELEASE_MODEL_UID = "plugin::content-releases.release";
31
33
  const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
@@ -71,38 +73,21 @@ const ACTIONS = [
71
73
  displayName: "Add an entry to a release",
72
74
  uid: "create-action",
73
75
  pluginName: "content-releases"
74
- },
75
- // Settings
76
- {
77
- uid: "settings.read",
78
- section: "settings",
79
- displayName: "Read",
80
- category: "content releases",
81
- subCategory: "options",
82
- pluginName: "content-releases"
83
- },
84
- {
85
- uid: "settings.update",
86
- section: "settings",
87
- displayName: "Edit",
88
- category: "content releases",
89
- subCategory: "options",
90
- pluginName: "content-releases"
91
76
  }
92
77
  ];
93
78
  const ALLOWED_WEBHOOK_EVENTS = {
94
79
  RELEASES_PUBLISH: "releases.publish"
95
80
  };
96
- const getService = (name, { strapi: strapi2 }) => {
81
+ const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
97
82
  return strapi2.plugin("content-releases").service(name);
98
83
  };
99
- const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi: strapi2 }) => {
84
+ const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 } = { strapi: global.strapi }) => {
100
85
  const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
101
- const populate = await populateBuilderService(contentType).populateDeep(Infinity).build();
102
- const entry = await getEntry({ contentType, documentId, locale, populate }, { strapi: strapi2 });
103
- return isEntryValid(contentType, entry, { strapi: strapi2 });
86
+ const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
87
+ const entry = await strapi2.entityService.findOne(contentTypeUid, entryId, { populate });
88
+ return entry;
104
89
  };
105
- const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
90
+ const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } = { strapi: global.strapi }) => {
106
91
  try {
107
92
  await strapi2.entityValidator.validateEntityCreation(
108
93
  strapi2.getModel(contentTypeUid),
@@ -116,38 +101,6 @@ const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
116
101
  return false;
117
102
  }
118
103
  };
119
- const getEntry = async ({
120
- contentType,
121
- documentId,
122
- locale,
123
- populate,
124
- status = "draft"
125
- }, { strapi: strapi2 }) => {
126
- if (documentId) {
127
- return strapi2.documents(contentType).findOne({ documentId, locale, populate, status });
128
- }
129
- return strapi2.documents(contentType).findFirst({ locale, populate, status });
130
- };
131
- const getEntryStatus = async (contentType, entry) => {
132
- if (entry.publishedAt) {
133
- return "published";
134
- }
135
- const publishedEntry = await strapi.documents(contentType).findOne({
136
- documentId: entry.documentId,
137
- locale: entry.locale,
138
- status: "published",
139
- fields: ["updatedAt"]
140
- });
141
- if (!publishedEntry) {
142
- return "draft";
143
- }
144
- const entryUpdatedAt = new Date(entry.updatedAt).getTime();
145
- const publishedEntryUpdatedAt = new Date(publishedEntry.updatedAt).getTime();
146
- if (entryUpdatedAt > publishedEntryUpdatedAt) {
147
- return "modified";
148
- }
149
- return "published";
150
- };
151
104
  async function deleteActionsOnDisableDraftAndPublish({
152
105
  oldContentTypes,
153
106
  contentTypes: contentTypes2
@@ -169,7 +122,7 @@ async function deleteActionsOnDisableDraftAndPublish({
169
122
  async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
170
123
  const deletedContentTypes = lodash.difference(lodash.keys(oldContentTypes), lodash.keys(contentTypes2)) ?? [];
171
124
  if (deletedContentTypes.length) {
172
- await utils.async.map(deletedContentTypes, async (deletedContentTypeUID) => {
125
+ await utils.mapAsync(deletedContentTypes, async (deletedContentTypeUID) => {
173
126
  return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
174
127
  });
175
128
  }
@@ -188,27 +141,25 @@ async function migrateIsValidAndStatusReleases() {
188
141
  }
189
142
  }
190
143
  });
191
- utils.async.map(releasesWithoutStatus, async (release2) => {
144
+ utils.mapAsync(releasesWithoutStatus, async (release2) => {
192
145
  const actions = release2.actions;
193
146
  const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
194
147
  for (const action of notValidatedActions) {
195
148
  if (action.entry) {
196
- const isEntryValid2 = getDraftEntryValidStatus(
197
- {
198
- contentType: action.contentType,
199
- documentId: action.entryDocumentId,
200
- locale: action.locale
201
- },
202
- { strapi }
203
- );
204
- await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
205
- where: {
206
- id: action.id
207
- },
208
- data: {
209
- isEntryValid: isEntryValid2
210
- }
149
+ const populatedEntry = await getPopulatedEntry(action.contentType, action.entry.id, {
150
+ strapi
211
151
  });
152
+ if (populatedEntry) {
153
+ const isEntryValid = getEntryValidStatus(action.contentType, populatedEntry, { strapi });
154
+ await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
155
+ where: {
156
+ id: action.id
157
+ },
158
+ data: {
159
+ isEntryValid
160
+ }
161
+ });
162
+ }
212
163
  }
213
164
  }
214
165
  return getService("release", { strapi }).updateReleaseStatus(release2.id);
@@ -221,7 +172,7 @@ async function migrateIsValidAndStatusReleases() {
221
172
  }
222
173
  }
223
174
  });
224
- utils.async.map(publishedReleases, async (release2) => {
175
+ utils.mapAsync(publishedReleases, async (release2) => {
225
176
  return strapi.db.query(RELEASE_MODEL_UID).update({
226
177
  where: {
227
178
  id: release2.id
@@ -238,7 +189,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
238
189
  (uid) => oldContentTypes[uid]?.options?.draftAndPublish
239
190
  );
240
191
  const releasesAffected = /* @__PURE__ */ new Set();
241
- utils.async.map(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
192
+ utils.mapAsync(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
242
193
  const oldContentType = oldContentTypes[contentTypeUID];
243
194
  const contentType = contentTypes2[contentTypeUID];
244
195
  if (!isEqual__default.default(oldContentType?.attributes, contentType?.attributes)) {
@@ -251,30 +202,30 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
251
202
  release: true
252
203
  }
253
204
  });
254
- await utils.async.map(actions, async (action) => {
255
- if (action.entry && action.release && action.type === "publish") {
256
- const isEntryValid2 = await getDraftEntryValidStatus(
257
- {
258
- contentType: contentTypeUID,
259
- documentId: action.entryDocumentId,
260
- locale: action.locale
261
- },
262
- { strapi }
263
- );
264
- releasesAffected.add(action.release.id);
265
- await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
266
- where: {
267
- id: action.id
268
- },
269
- data: {
270
- isEntryValid: isEntryValid2
271
- }
205
+ await utils.mapAsync(actions, async (action) => {
206
+ if (action.entry && action.release) {
207
+ const populatedEntry = await getPopulatedEntry(contentTypeUID, action.entry.id, {
208
+ strapi
272
209
  });
210
+ if (populatedEntry) {
211
+ const isEntryValid = await getEntryValidStatus(contentTypeUID, populatedEntry, {
212
+ strapi
213
+ });
214
+ releasesAffected.add(action.release.id);
215
+ await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
216
+ where: {
217
+ id: action.id
218
+ },
219
+ data: {
220
+ isEntryValid
221
+ }
222
+ });
223
+ }
273
224
  }
274
225
  });
275
226
  }
276
227
  }).then(() => {
277
- utils.async.map(releasesAffected, async (releaseId) => {
228
+ utils.mapAsync(releasesAffected, async (releaseId) => {
278
229
  return getService("release", { strapi }).updateReleaseStatus(releaseId);
279
230
  });
280
231
  });
@@ -326,43 +277,11 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
326
277
  }
327
278
  }
328
279
  }
329
- const addEntryDocumentToReleaseActions = {
330
- name: "content-releases::5.0.0-add-entry-document-id-to-release-actions",
331
- async up(trx, db) {
332
- const hasTable = await trx.schema.hasTable("strapi_release_actions");
333
- if (!hasTable) {
334
- return;
335
- }
336
- const hasPolymorphicColumn = await trx.schema.hasColumn("strapi_release_actions", "target_id");
337
- if (hasPolymorphicColumn) {
338
- const hasEntryDocumentIdColumn = await trx.schema.hasColumn(
339
- "strapi_release_actions",
340
- "entry_document_id"
341
- );
342
- if (!hasEntryDocumentIdColumn) {
343
- await trx.schema.alterTable("strapi_release_actions", (table) => {
344
- table.string("entry_document_id");
345
- });
346
- }
347
- const releaseActions = await trx.select("*").from("strapi_release_actions");
348
- utils.async.map(releaseActions, async (action) => {
349
- const { target_type, target_id } = action;
350
- const entry = await db.query(target_type).findOne({ where: { id: target_id } });
351
- if (entry) {
352
- await trx("strapi_release_actions").update({ entry_document_id: entry.documentId }).where("id", action.id);
353
- }
354
- });
355
- }
356
- },
357
- async down() {
358
- throw new Error("not implemented");
359
- }
360
- };
280
+ const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
361
281
  const register = async ({ strapi: strapi2 }) => {
362
- if (strapi2.ee.features.isEnabled("cms-content-releases")) {
363
- await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
364
- strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
365
- strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
282
+ if (features$2.isEnabled("cms-content-releases")) {
283
+ await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
284
+ strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish).register(disableContentTypeLocalized);
366
285
  strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
367
286
  }
368
287
  if (strapi2.plugin("graphql")) {
@@ -371,134 +290,129 @@ const register = async ({ strapi: strapi2 }) => {
371
290
  graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
372
291
  }
373
292
  };
374
- const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
375
- const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
376
- where: {
377
- actions: {
378
- contentType,
379
- entryDocumentId: entry.documentId,
380
- locale: entry.locale
381
- }
382
- }
383
- });
384
- const entryStatus = await isEntryValid(contentType, entry, { strapi });
385
- await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
386
- where: {
387
- contentType,
388
- entryDocumentId: entry.documentId,
389
- locale: entry.locale
390
- },
391
- data: {
392
- isEntryValid: entryStatus
393
- }
394
- });
395
- for (const release2 of releases) {
396
- getService("release", { strapi }).updateReleaseStatus(release2.id);
397
- }
398
- };
399
- const deleteActionsAndUpdateReleaseStatus = async (params) => {
400
- const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
401
- where: {
402
- actions: params
403
- }
404
- });
405
- await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
406
- where: params
407
- });
408
- for (const release2 of releases) {
409
- getService("release", { strapi }).updateReleaseStatus(release2.id);
410
- }
411
- };
412
- const deleteActionsOnDelete = async (ctx, next) => {
413
- if (ctx.action !== "delete") {
414
- return next();
415
- }
416
- if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
417
- return next();
418
- }
419
- const contentType = ctx.contentType.uid;
420
- const { documentId, locale } = ctx.params;
421
- const result = await next();
422
- if (!result) {
423
- return result;
424
- }
425
- try {
426
- deleteActionsAndUpdateReleaseStatus({
427
- contentType,
428
- entryDocumentId: documentId,
429
- ...locale !== "*" && { locale }
430
- });
431
- } catch (error) {
432
- strapi.log.error("Error while deleting release actions after delete", {
433
- error
434
- });
435
- }
436
- return result;
437
- };
438
- const updateActionsOnUpdate = async (ctx, next) => {
439
- if (ctx.action !== "update") {
440
- return next();
441
- }
442
- if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
443
- return next();
444
- }
445
- const contentType = ctx.contentType.uid;
446
- const result = await next();
447
- if (!result) {
448
- return result;
449
- }
450
- try {
451
- updateActionsStatusAndUpdateReleaseStatus(contentType, result);
452
- } catch (error) {
453
- strapi.log.error("Error while updating release actions after update", {
454
- error
455
- });
456
- }
457
- return result;
458
- };
459
- const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
460
- const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
461
- where: {
462
- actions: params
463
- }
464
- });
465
- await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
466
- where: params
467
- });
468
- for (const release2 of releases) {
469
- getService("release", { strapi }).updateReleaseStatus(release2.id);
470
- }
471
- };
293
+ const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
472
294
  const bootstrap = async ({ strapi: strapi2 }) => {
473
- if (strapi2.ee.features.isEnabled("cms-content-releases")) {
295
+ if (features$1.isEnabled("cms-content-releases")) {
474
296
  const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
475
297
  (uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
476
298
  );
477
299
  strapi2.db.lifecycles.subscribe({
478
300
  models: contentTypesWithDraftAndPublish,
301
+ async afterDelete(event) {
302
+ try {
303
+ const { model, result } = event;
304
+ if (model.kind === "collectionType" && model.options?.draftAndPublish) {
305
+ const { id } = result;
306
+ const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
307
+ where: {
308
+ actions: {
309
+ target_type: model.uid,
310
+ target_id: id
311
+ }
312
+ }
313
+ });
314
+ await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
315
+ where: {
316
+ target_type: model.uid,
317
+ target_id: id
318
+ }
319
+ });
320
+ for (const release2 of releases) {
321
+ getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
322
+ }
323
+ }
324
+ } catch (error) {
325
+ strapi2.log.error("Error while deleting release actions after entry delete", { error });
326
+ }
327
+ },
328
+ /**
329
+ * deleteMany hook doesn't return the deleted entries ids
330
+ * so we need to fetch them before deleting the entries to save the ids on our state
331
+ */
332
+ async beforeDeleteMany(event) {
333
+ const { model, params } = event;
334
+ if (model.kind === "collectionType" && model.options?.draftAndPublish) {
335
+ const { where } = params;
336
+ const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
337
+ event.state.entriesToDelete = entriesToDelete;
338
+ }
339
+ },
479
340
  /**
480
- * deleteMany is still used outside documents service, for example when deleting a locale
341
+ * We delete the release actions related to deleted entries
342
+ * We make this only after deleteMany is succesfully executed to avoid errors
481
343
  */
482
344
  async afterDeleteMany(event) {
483
345
  try {
484
- const model = strapi2.getModel(event.model.uid);
485
- if (model.kind === "collectionType" && model.options?.draftAndPublish) {
486
- const { where } = event.params;
487
- deleteReleasesActionsAndUpdateReleaseStatus({
488
- contentType: model.uid,
489
- locale: where.locale ?? null,
490
- ...where.documentId && { entryDocumentId: where.documentId }
346
+ const { model, state } = event;
347
+ const entriesToDelete = state.entriesToDelete;
348
+ if (entriesToDelete) {
349
+ const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
350
+ where: {
351
+ actions: {
352
+ target_type: model.uid,
353
+ target_id: {
354
+ $in: entriesToDelete.map(
355
+ (entry) => entry.id
356
+ )
357
+ }
358
+ }
359
+ }
360
+ });
361
+ await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
362
+ where: {
363
+ target_type: model.uid,
364
+ target_id: {
365
+ $in: entriesToDelete.map((entry) => entry.id)
366
+ }
367
+ }
491
368
  });
369
+ for (const release2 of releases) {
370
+ getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
371
+ }
492
372
  }
493
373
  } catch (error) {
494
374
  strapi2.log.error("Error while deleting release actions after entry deleteMany", {
495
375
  error
496
376
  });
497
377
  }
378
+ },
379
+ async afterUpdate(event) {
380
+ try {
381
+ const { model, result } = event;
382
+ if (model.kind === "collectionType" && model.options?.draftAndPublish) {
383
+ const isEntryValid = await getEntryValidStatus(
384
+ model.uid,
385
+ result,
386
+ {
387
+ strapi: strapi2
388
+ }
389
+ );
390
+ await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
391
+ where: {
392
+ target_type: model.uid,
393
+ target_id: result.id
394
+ },
395
+ data: {
396
+ isEntryValid
397
+ }
398
+ });
399
+ const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
400
+ where: {
401
+ actions: {
402
+ target_type: model.uid,
403
+ target_id: result.id
404
+ }
405
+ }
406
+ });
407
+ for (const release2 of releases) {
408
+ getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
409
+ }
410
+ }
411
+ } catch (error) {
412
+ strapi2.log.error("Error while updating release actions after entry update", { error });
413
+ }
498
414
  }
499
415
  });
500
- strapi2.documents.use(deleteActionsOnDelete);
501
- strapi2.documents.use(updateActionsOnUpdate);
502
416
  getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
503
417
  strapi2.log.error(
504
418
  "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
@@ -506,7 +420,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
506
420
  throw err;
507
421
  });
508
422
  Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
509
- strapi2.get("webhookStore").addAllowedEvent(key, value);
423
+ strapi2.webhookStore.addAllowedEvent(key, value);
510
424
  });
511
425
  }
512
426
  };
@@ -590,13 +504,15 @@ const schema = {
590
504
  enum: ["publish", "unpublish"],
591
505
  required: true
592
506
  },
507
+ entry: {
508
+ type: "relation",
509
+ relation: "morphToOne",
510
+ configurable: false
511
+ },
593
512
  contentType: {
594
513
  type: "string",
595
514
  required: true
596
515
  },
597
- entryDocumentId: {
598
- type: "string"
599
- },
600
516
  locale: {
601
517
  type: "string"
602
518
  },
@@ -618,6 +534,18 @@ const contentTypes = {
618
534
  release: release$1,
619
535
  "release-action": releaseAction$1
620
536
  };
537
+ const getGroupName = (queryValue) => {
538
+ switch (queryValue) {
539
+ case "contentType":
540
+ return "contentType.displayName";
541
+ case "action":
542
+ return "type";
543
+ case "locale":
544
+ return ___default.default.getOr("No locale", "locale.name");
545
+ default:
546
+ return "contentType.displayName";
547
+ }
548
+ };
621
549
  const createReleaseService = ({ strapi: strapi2 }) => {
622
550
  const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
623
551
  strapi2.eventHub.emit(event, {
@@ -626,32 +554,93 @@ const createReleaseService = ({ strapi: strapi2 }) => {
626
554
  release: release2
627
555
  });
628
556
  };
557
+ const publishSingleTypeAction = async (uid, actionType, entryId) => {
558
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
559
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
560
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
561
+ const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
562
+ try {
563
+ if (actionType === "publish") {
564
+ await entityManagerService.publish(entry, uid);
565
+ } else {
566
+ await entityManagerService.unpublish(entry, uid);
567
+ }
568
+ } catch (error) {
569
+ if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
570
+ ;
571
+ else {
572
+ throw error;
573
+ }
574
+ }
575
+ };
576
+ const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
577
+ const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
578
+ const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
579
+ const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
580
+ const entriesToPublish = await strapi2.entityService.findMany(uid, {
581
+ filters: {
582
+ id: {
583
+ $in: entriesToPublishIds
584
+ }
585
+ },
586
+ populate
587
+ });
588
+ const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
589
+ filters: {
590
+ id: {
591
+ $in: entriestoUnpublishIds
592
+ }
593
+ },
594
+ populate
595
+ });
596
+ if (entriesToPublish.length > 0) {
597
+ await entityManagerService.publishMany(entriesToPublish, uid);
598
+ }
599
+ if (entriesToUnpublish.length > 0) {
600
+ await entityManagerService.unpublishMany(entriesToUnpublish, uid);
601
+ }
602
+ };
629
603
  const getFormattedActions = async (releaseId) => {
630
604
  const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
631
605
  where: {
632
606
  release: {
633
607
  id: releaseId
634
608
  }
609
+ },
610
+ populate: {
611
+ entry: {
612
+ fields: ["id"]
613
+ }
635
614
  }
636
615
  });
637
616
  if (actions.length === 0) {
638
617
  throw new utils.errors.ValidationError("No entries to publish");
639
618
  }
640
- const formattedActions = {};
619
+ const collectionTypeActions = {};
620
+ const singleTypeActions = [];
641
621
  for (const action of actions) {
642
622
  const contentTypeUid = action.contentType;
643
- if (!formattedActions[contentTypeUid]) {
644
- formattedActions[contentTypeUid] = {
645
- publish: [],
646
- unpublish: []
647
- };
623
+ if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
624
+ if (!collectionTypeActions[contentTypeUid]) {
625
+ collectionTypeActions[contentTypeUid] = {
626
+ entriesToPublishIds: [],
627
+ entriesToUnpublishIds: []
628
+ };
629
+ }
630
+ if (action.type === "publish") {
631
+ collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
632
+ } else {
633
+ collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
634
+ }
635
+ } else {
636
+ singleTypeActions.push({
637
+ uid: contentTypeUid,
638
+ action: action.type,
639
+ id: action.entry.id
640
+ });
648
641
  }
649
- formattedActions[contentTypeUid][action.type].push({
650
- documentId: action.entryDocumentId,
651
- locale: action.locale
652
- });
653
642
  }
654
- return formattedActions;
643
+ return { collectionTypeActions, singleTypeActions };
655
644
  };
656
645
  return {
657
646
  async create(releaseData, { user }) {
@@ -666,7 +655,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
666
655
  validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
667
656
  validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
668
657
  ]);
669
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
658
+ const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
670
659
  data: {
671
660
  ...releaseWithCreatorFields,
672
661
  status: "empty"
@@ -680,28 +669,107 @@ const createReleaseService = ({ strapi: strapi2 }) => {
680
669
  return release2;
681
670
  },
682
671
  async findOne(id, query = {}) {
683
- const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query);
684
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
685
- ...dbQuery,
686
- where: { id }
672
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
673
+ ...query
687
674
  });
688
675
  return release2;
689
676
  },
690
677
  findPage(query) {
691
- const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
692
- return strapi2.db.query(RELEASE_MODEL_UID).findPage({
693
- ...dbQuery,
678
+ return strapi2.entityService.findPage(RELEASE_MODEL_UID, {
679
+ ...query,
694
680
  populate: {
695
681
  actions: {
682
+ // @ts-expect-error Ignore missing properties
696
683
  count: true
697
684
  }
698
685
  }
699
686
  });
700
687
  },
701
- findMany(query) {
702
- const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
703
- return strapi2.db.query(RELEASE_MODEL_UID).findMany({
704
- ...dbQuery
688
+ async findManyWithContentTypeEntryAttached(contentTypeUid, entriesIds) {
689
+ let entries = entriesIds;
690
+ if (!Array.isArray(entriesIds)) {
691
+ entries = [entriesIds];
692
+ }
693
+ const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
694
+ where: {
695
+ actions: {
696
+ target_type: contentTypeUid,
697
+ target_id: {
698
+ $in: entries
699
+ }
700
+ },
701
+ releasedAt: {
702
+ $null: true
703
+ }
704
+ },
705
+ populate: {
706
+ // Filter the action to get only the content type entry
707
+ actions: {
708
+ where: {
709
+ target_type: contentTypeUid,
710
+ target_id: {
711
+ $in: entries
712
+ }
713
+ },
714
+ populate: {
715
+ entry: {
716
+ select: ["id"]
717
+ }
718
+ }
719
+ }
720
+ }
721
+ });
722
+ return releases.map((release2) => {
723
+ if (release2.actions?.length) {
724
+ const actionsForEntry = release2.actions;
725
+ delete release2.actions;
726
+ return {
727
+ ...release2,
728
+ actions: actionsForEntry
729
+ };
730
+ }
731
+ return release2;
732
+ });
733
+ },
734
+ async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
735
+ const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
736
+ where: {
737
+ releasedAt: {
738
+ $null: true
739
+ },
740
+ actions: {
741
+ target_type: contentTypeUid,
742
+ target_id: entryId
743
+ }
744
+ }
745
+ });
746
+ const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
747
+ where: {
748
+ $or: [
749
+ {
750
+ id: {
751
+ $notIn: releasesRelated.map((release2) => release2.id)
752
+ }
753
+ },
754
+ {
755
+ actions: null
756
+ }
757
+ ],
758
+ releasedAt: {
759
+ $null: true
760
+ }
761
+ }
762
+ });
763
+ return releases.map((release2) => {
764
+ if (release2.actions?.length) {
765
+ const [actionForEntry] = release2.actions;
766
+ delete release2.actions;
767
+ return {
768
+ ...release2,
769
+ action: actionForEntry
770
+ };
771
+ }
772
+ return release2;
705
773
  });
706
774
  },
707
775
  async update(id, releaseData, { user }) {
@@ -716,15 +784,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
716
784
  validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
717
785
  validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
718
786
  ]);
719
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
787
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
720
788
  if (!release2) {
721
789
  throw new utils.errors.NotFoundError(`No release found for id ${id}`);
722
790
  }
723
791
  if (release2.releasedAt) {
724
792
  throw new utils.errors.ValidationError("Release already published");
725
793
  }
726
- const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
727
- where: { id },
794
+ const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
795
+ /*
796
+ * The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
797
+ * is not compatible with the type we are passing here: UpdateRelease.Request['body']
798
+ */
799
+ // @ts-expect-error see above
728
800
  data: releaseWithCreatorFields
729
801
  });
730
802
  const schedulingService = getService("scheduling", { strapi: strapi2 });
@@ -737,6 +809,132 @@ const createReleaseService = ({ strapi: strapi2 }) => {
737
809
  strapi2.telemetry.send("didUpdateContentRelease");
738
810
  return updatedRelease;
739
811
  },
812
+ async createAction(releaseId, action, { disableUpdateReleaseStatus = false } = {}) {
813
+ const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
814
+ strapi: strapi2
815
+ });
816
+ await Promise.all([
817
+ validateEntryContentType(action.entry.contentType),
818
+ validateUniqueEntry(releaseId, action)
819
+ ]);
820
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
821
+ if (!release2) {
822
+ throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
823
+ }
824
+ if (release2.releasedAt) {
825
+ throw new utils.errors.ValidationError("Release already published");
826
+ }
827
+ const { entry, type } = action;
828
+ const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
829
+ const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
830
+ const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
831
+ data: {
832
+ type,
833
+ contentType: entry.contentType,
834
+ locale: entry.locale,
835
+ isEntryValid,
836
+ entry: {
837
+ id: entry.id,
838
+ __type: entry.contentType,
839
+ __pivot: { field: "entry" }
840
+ },
841
+ release: releaseId
842
+ },
843
+ populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
844
+ });
845
+ if (!disableUpdateReleaseStatus) {
846
+ this.updateReleaseStatus(releaseId);
847
+ }
848
+ return releaseAction2;
849
+ },
850
+ async findActions(releaseId, query) {
851
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
852
+ fields: ["id"]
853
+ });
854
+ if (!release2) {
855
+ throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
856
+ }
857
+ return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
858
+ ...query,
859
+ populate: {
860
+ entry: {
861
+ populate: "*"
862
+ }
863
+ },
864
+ filters: {
865
+ release: releaseId
866
+ }
867
+ });
868
+ },
869
+ async countActions(query) {
870
+ return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
871
+ },
872
+ async groupActions(actions, groupBy) {
873
+ const contentTypeUids = actions.reduce((acc, action) => {
874
+ if (!acc.includes(action.contentType)) {
875
+ acc.push(action.contentType);
876
+ }
877
+ return acc;
878
+ }, []);
879
+ const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
880
+ contentTypeUids
881
+ );
882
+ const allLocalesDictionary = await this.getLocalesDataForActions();
883
+ const formattedData = actions.map((action) => {
884
+ const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
885
+ return {
886
+ ...action,
887
+ locale: action.locale ? allLocalesDictionary[action.locale] : null,
888
+ contentType: {
889
+ displayName,
890
+ mainFieldValue: action.entry[mainField],
891
+ uid: action.contentType
892
+ }
893
+ };
894
+ });
895
+ const groupName = getGroupName(groupBy);
896
+ return ___default.default.groupBy(groupName)(formattedData);
897
+ },
898
+ async getLocalesDataForActions() {
899
+ if (!strapi2.plugin("i18n")) {
900
+ return {};
901
+ }
902
+ const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
903
+ return allLocales.reduce((acc, locale) => {
904
+ acc[locale.code] = { name: locale.name, code: locale.code };
905
+ return acc;
906
+ }, {});
907
+ },
908
+ async getContentTypesDataForActions(contentTypesUids) {
909
+ const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
910
+ const contentTypesData = {};
911
+ for (const contentTypeUid of contentTypesUids) {
912
+ const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
913
+ uid: contentTypeUid
914
+ });
915
+ contentTypesData[contentTypeUid] = {
916
+ mainField: contentTypeConfig.settings.mainField,
917
+ displayName: strapi2.getModel(contentTypeUid).info.displayName
918
+ };
919
+ }
920
+ return contentTypesData;
921
+ },
922
+ getContentTypeModelsFromActions(actions) {
923
+ const contentTypeUids = actions.reduce((acc, action) => {
924
+ if (!acc.includes(action.contentType)) {
925
+ acc.push(action.contentType);
926
+ }
927
+ return acc;
928
+ }, []);
929
+ const contentTypeModelsMap = contentTypeUids.reduce(
930
+ (acc, contentTypeUid) => {
931
+ acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
932
+ return acc;
933
+ },
934
+ {}
935
+ );
936
+ return contentTypeModelsMap;
937
+ },
740
938
  async getAllComponents() {
741
939
  const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
742
940
  const components = await contentManagerComponentsService.findAllComponents();
@@ -750,11 +948,10 @@ const createReleaseService = ({ strapi: strapi2 }) => {
750
948
  return componentsMap;
751
949
  },
752
950
  async delete(releaseId) {
753
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
754
- where: { id: releaseId },
951
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
755
952
  populate: {
756
953
  actions: {
757
- select: ["id"]
954
+ fields: ["id"]
758
955
  }
759
956
  }
760
957
  });
@@ -772,11 +969,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
772
969
  }
773
970
  }
774
971
  });
775
- await strapi2.db.query(RELEASE_MODEL_UID).delete({
776
- where: {
777
- id: releaseId
778
- }
779
- });
972
+ await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
780
973
  });
781
974
  if (release2.scheduledAt) {
782
975
  const schedulingService = getService("scheduling", { strapi: strapi2 });
@@ -802,19 +995,22 @@ const createReleaseService = ({ strapi: strapi2 }) => {
802
995
  }
803
996
  try {
804
997
  strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
805
- const formattedActions = await getFormattedActions(releaseId);
806
- await strapi2.db.transaction(
807
- async () => Promise.all(
808
- Object.keys(formattedActions).map(async (contentTypeUid) => {
809
- const contentType = contentTypeUid;
810
- const { publish, unpublish } = formattedActions[contentType];
811
- return Promise.all([
812
- ...publish.map((params) => strapi2.documents(contentType).publish(params)),
813
- ...unpublish.map((params) => strapi2.documents(contentType).unpublish(params))
814
- ]);
815
- })
816
- )
998
+ const { collectionTypeActions, singleTypeActions } = await getFormattedActions(
999
+ releaseId
817
1000
  );
1001
+ await strapi2.db.transaction(async () => {
1002
+ for (const { uid, action, id } of singleTypeActions) {
1003
+ await publishSingleTypeAction(uid, action, id);
1004
+ }
1005
+ for (const contentTypeUid of Object.keys(collectionTypeActions)) {
1006
+ const uid = contentTypeUid;
1007
+ await publishCollectionTypeAction(
1008
+ uid,
1009
+ collectionTypeActions[uid].entriesToPublishIds,
1010
+ collectionTypeActions[uid].entriesToUnpublishIds
1011
+ );
1012
+ }
1013
+ });
818
1014
  const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
819
1015
  where: {
820
1016
  id: releaseId
@@ -844,226 +1040,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
844
1040
  };
845
1041
  }
846
1042
  });
847
- if (error instanceof Error) {
848
- throw error;
849
- }
850
- return release2;
851
- },
852
- async updateReleaseStatus(releaseId) {
853
- const releaseActionService = getService("release-action", { strapi: strapi2 });
854
- const [totalActions, invalidActions] = await Promise.all([
855
- releaseActionService.countActions({
856
- filters: {
857
- release: releaseId
858
- }
859
- }),
860
- releaseActionService.countActions({
861
- filters: {
862
- release: releaseId,
863
- isEntryValid: false
864
- }
865
- })
866
- ]);
867
- if (totalActions > 0) {
868
- if (invalidActions > 0) {
869
- return strapi2.db.query(RELEASE_MODEL_UID).update({
870
- where: {
871
- id: releaseId
872
- },
873
- data: {
874
- status: "blocked"
875
- }
876
- });
877
- }
878
- return strapi2.db.query(RELEASE_MODEL_UID).update({
879
- where: {
880
- id: releaseId
881
- },
882
- data: {
883
- status: "ready"
884
- }
885
- });
886
- }
887
- return strapi2.db.query(RELEASE_MODEL_UID).update({
888
- where: {
889
- id: releaseId
890
- },
891
- data: {
892
- status: "empty"
893
- }
894
- });
895
- }
896
- };
897
- };
898
- const getGroupName = (queryValue) => {
899
- switch (queryValue) {
900
- case "contentType":
901
- return "contentType.displayName";
902
- case "type":
903
- return "type";
904
- case "locale":
905
- return ___default.default.getOr("No locale", "locale.name");
906
- default:
907
- return "contentType.displayName";
908
- }
909
- };
910
- const createReleaseActionService = ({ strapi: strapi2 }) => {
911
- const getLocalesDataForActions = async () => {
912
- if (!strapi2.plugin("i18n")) {
913
- return {};
914
- }
915
- const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
916
- return allLocales.reduce((acc, locale) => {
917
- acc[locale.code] = { name: locale.name, code: locale.code };
918
- return acc;
919
- }, {});
920
- };
921
- const getContentTypesDataForActions = async (contentTypesUids) => {
922
- const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
923
- const contentTypesData = {};
924
- for (const contentTypeUid of contentTypesUids) {
925
- const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
926
- uid: contentTypeUid
927
- });
928
- contentTypesData[contentTypeUid] = {
929
- mainField: contentTypeConfig.settings.mainField,
930
- displayName: strapi2.getModel(contentTypeUid).info.displayName
931
- };
932
- }
933
- return contentTypesData;
934
- };
935
- return {
936
- async create(releaseId, action, { disableUpdateReleaseStatus = false } = {}) {
937
- const { validateEntryData, validateUniqueEntry } = getService("release-validation", {
938
- strapi: strapi2
939
- });
940
- await Promise.all([
941
- validateEntryData(action.contentType, action.entryDocumentId),
942
- validateUniqueEntry(releaseId, action)
943
- ]);
944
- const model = strapi2.contentType(action.contentType);
945
- if (model.kind === "singleType") {
946
- const document = await strapi2.db.query(model.uid).findOne({ select: ["documentId"] });
947
- if (!document) {
948
- throw new utils.errors.NotFoundError(`No entry found for contentType ${action.contentType}`);
949
- }
950
- action.entryDocumentId = document.documentId;
951
- }
952
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
953
- if (!release2) {
954
- throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
955
- }
956
- if (release2.releasedAt) {
957
- throw new utils.errors.ValidationError("Release already published");
958
- }
959
- const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
960
- {
961
- contentType: action.contentType,
962
- documentId: action.entryDocumentId,
963
- locale: action.locale
964
- },
965
- {
966
- strapi: strapi2
967
- }
968
- ) : true;
969
- const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
970
- data: {
971
- ...action,
972
- release: release2.id,
973
- isEntryValid: actionStatus
974
- },
975
- populate: { release: { select: ["id"] } }
976
- });
977
- if (!disableUpdateReleaseStatus) {
978
- getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
979
- }
980
- return releaseAction2;
981
- },
982
- async findPage(releaseId, query) {
983
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
984
- where: { id: releaseId },
985
- select: ["id"]
986
- });
987
- if (!release2) {
988
- throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
1043
+ if (error) {
1044
+ throw error;
989
1045
  }
990
- const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
991
- const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
992
- ...dbQuery,
993
- where: {
994
- release: releaseId
995
- }
996
- });
997
- const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
998
- const actionsWithEntry = await utils.async.map(actions, async (action) => {
999
- const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
1000
- const entry = await getEntry(
1001
- {
1002
- contentType: action.contentType,
1003
- documentId: action.entryDocumentId,
1004
- locale: action.locale,
1005
- populate,
1006
- status: action.type === "publish" ? "draft" : "published"
1007
- },
1008
- { strapi: strapi2 }
1009
- );
1010
- return {
1011
- ...action,
1012
- entry,
1013
- status: entry ? await getEntryStatus(action.contentType, entry) : null
1014
- };
1015
- });
1016
- return {
1017
- results: actionsWithEntry,
1018
- pagination
1019
- };
1020
- },
1021
- async groupActions(actions, groupBy) {
1022
- const contentTypeUids = actions.reduce((acc, action) => {
1023
- if (!acc.includes(action.contentType)) {
1024
- acc.push(action.contentType);
1025
- }
1026
- return acc;
1027
- }, []);
1028
- const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
1029
- const allLocalesDictionary = await getLocalesDataForActions();
1030
- const formattedData = actions.map((action) => {
1031
- const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
1032
- return {
1033
- ...action,
1034
- locale: action.locale ? allLocalesDictionary[action.locale] : null,
1035
- contentType: {
1036
- displayName,
1037
- mainFieldValue: action.entry[mainField],
1038
- uid: action.contentType
1039
- }
1040
- };
1041
- });
1042
- const groupName = getGroupName(groupBy);
1043
- return ___default.default.groupBy(groupName)(formattedData);
1044
- },
1045
- getContentTypeModelsFromActions(actions) {
1046
- const contentTypeUids = actions.reduce((acc, action) => {
1047
- if (!acc.includes(action.contentType)) {
1048
- acc.push(action.contentType);
1049
- }
1050
- return acc;
1051
- }, []);
1052
- const contentTypeModelsMap = contentTypeUids.reduce(
1053
- (acc, contentTypeUid) => {
1054
- acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
1055
- return acc;
1056
- },
1057
- {}
1058
- );
1059
- return contentTypeModelsMap;
1060
- },
1061
- async countActions(query) {
1062
- const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
1063
- return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
1046
+ return release2;
1064
1047
  },
1065
- async update(actionId, releaseId, update) {
1066
- const action = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findOne({
1048
+ async updateAction(actionId, releaseId, update) {
1049
+ const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
1067
1050
  where: {
1068
1051
  id: actionId,
1069
1052
  release: {
@@ -1072,42 +1055,17 @@ const createReleaseActionService = ({ strapi: strapi2 }) => {
1072
1055
  $null: true
1073
1056
  }
1074
1057
  }
1075
- }
1058
+ },
1059
+ data: update
1076
1060
  });
1077
- if (!action) {
1061
+ if (!updatedAction) {
1078
1062
  throw new utils.errors.NotFoundError(
1079
1063
  `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
1080
1064
  );
1081
1065
  }
1082
- const actionStatus = update.type === "publish" ? await getDraftEntryValidStatus(
1083
- {
1084
- contentType: action.contentType,
1085
- documentId: action.entryDocumentId,
1086
- locale: action.locale
1087
- },
1088
- {
1089
- strapi: strapi2
1090
- }
1091
- ) : true;
1092
- const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
1093
- where: {
1094
- id: actionId,
1095
- release: {
1096
- id: releaseId,
1097
- releasedAt: {
1098
- $null: true
1099
- }
1100
- }
1101
- },
1102
- data: {
1103
- ...update,
1104
- isEntryValid: actionStatus
1105
- }
1106
- });
1107
- getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
1108
1066
  return updatedAction;
1109
1067
  },
1110
- async delete(actionId, releaseId) {
1068
+ async deleteAction(actionId, releaseId) {
1111
1069
  const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
1112
1070
  where: {
1113
1071
  id: actionId,
@@ -1124,8 +1082,51 @@ const createReleaseActionService = ({ strapi: strapi2 }) => {
1124
1082
  `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
1125
1083
  );
1126
1084
  }
1127
- getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
1085
+ this.updateReleaseStatus(releaseId);
1128
1086
  return deletedAction;
1087
+ },
1088
+ async updateReleaseStatus(releaseId) {
1089
+ const [totalActions, invalidActions] = await Promise.all([
1090
+ this.countActions({
1091
+ filters: {
1092
+ release: releaseId
1093
+ }
1094
+ }),
1095
+ this.countActions({
1096
+ filters: {
1097
+ release: releaseId,
1098
+ isEntryValid: false
1099
+ }
1100
+ })
1101
+ ]);
1102
+ if (totalActions > 0) {
1103
+ if (invalidActions > 0) {
1104
+ return strapi2.db.query(RELEASE_MODEL_UID).update({
1105
+ where: {
1106
+ id: releaseId
1107
+ },
1108
+ data: {
1109
+ status: "blocked"
1110
+ }
1111
+ });
1112
+ }
1113
+ return strapi2.db.query(RELEASE_MODEL_UID).update({
1114
+ where: {
1115
+ id: releaseId
1116
+ },
1117
+ data: {
1118
+ status: "ready"
1119
+ }
1120
+ });
1121
+ }
1122
+ return strapi2.db.query(RELEASE_MODEL_UID).update({
1123
+ where: {
1124
+ id: releaseId
1125
+ },
1126
+ data: {
1127
+ status: "empty"
1128
+ }
1129
+ });
1129
1130
  }
1130
1131
  };
1131
1132
  };
@@ -1137,43 +1138,37 @@ class AlreadyOnReleaseError extends utils.errors.ApplicationError {
1137
1138
  }
1138
1139
  const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1139
1140
  async validateUniqueEntry(releaseId, releaseActionArgs) {
1140
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
1141
- where: {
1142
- id: releaseId
1143
- },
1144
- populate: {
1145
- actions: true
1146
- }
1141
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
1142
+ populate: { actions: { populate: { entry: { fields: ["id"] } } } }
1147
1143
  });
1148
1144
  if (!release2) {
1149
1145
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
1150
1146
  }
1151
1147
  const isEntryInRelease = release2.actions.some(
1152
- (action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
1148
+ (action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
1153
1149
  );
1154
1150
  if (isEntryInRelease) {
1155
1151
  throw new AlreadyOnReleaseError(
1156
- `Entry with documentId ${releaseActionArgs.entryDocumentId}${releaseActionArgs.locale ? `( ${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
1152
+ `Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
1157
1153
  );
1158
1154
  }
1159
1155
  },
1160
- validateEntryData(contentTypeUid, entryDocumentId) {
1156
+ validateEntryContentType(contentTypeUid) {
1161
1157
  const contentType = strapi2.contentType(contentTypeUid);
1162
1158
  if (!contentType) {
1163
1159
  throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
1164
1160
  }
1165
- if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
1161
+ if (!contentType.options?.draftAndPublish) {
1166
1162
  throw new utils.errors.ValidationError(
1167
1163
  `Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
1168
1164
  );
1169
1165
  }
1170
- if (contentType.kind === "collectionType" && !entryDocumentId) {
1171
- throw new utils.errors.ValidationError("Document id is required for collection type");
1172
- }
1173
1166
  },
1174
1167
  async validatePendingReleasesLimit() {
1175
- const featureCfg = strapi2.ee.features.get("cms-content-releases");
1176
- const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
1168
+ const maximumPendingReleases = (
1169
+ // @ts-expect-error - options is not typed into features
1170
+ EE__default.default.features.get("cms-content-releases")?.options?.maximumReleases || 3
1171
+ );
1177
1172
  const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
1178
1173
  filters: {
1179
1174
  releasedAt: {
@@ -1186,8 +1181,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1186
1181
  }
1187
1182
  },
1188
1183
  async validateUniqueNameForPendingRelease(name, id) {
1189
- const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
1190
- where: {
1184
+ const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
1185
+ filters: {
1191
1186
  releasedAt: {
1192
1187
  $null: true
1193
1188
  },
@@ -1216,7 +1211,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1216
1211
  }
1217
1212
  const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
1218
1213
  try {
1219
- await getService("release", { strapi: strapi2 }).publish(releaseId);
1214
+ await getService("release").publish(releaseId);
1220
1215
  } catch (error) {
1221
1216
  }
1222
1217
  this.cancel(releaseId);
@@ -1258,172 +1253,85 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1258
1253
  }
1259
1254
  };
1260
1255
  };
1261
- const DEFAULT_SETTINGS = {
1262
- defaultTimezone: null
1263
- };
1264
- const createSettingsService = ({ strapi: strapi2 }) => {
1265
- const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
1266
- return {
1267
- async update({ settings: settings2 }) {
1268
- const store = await getStore();
1269
- store.set({ key: "settings", value: settings2 });
1270
- return settings2;
1271
- },
1272
- async find() {
1273
- const store = await getStore();
1274
- const settings2 = await store.get({ key: "settings" });
1275
- return {
1276
- ...DEFAULT_SETTINGS,
1277
- ...settings2 || {}
1278
- };
1279
- }
1280
- };
1281
- };
1282
1256
  const services = {
1283
1257
  release: createReleaseService,
1284
- "release-action": createReleaseActionService,
1285
1258
  "release-validation": createReleaseValidationService,
1286
- scheduling: createSchedulingService,
1287
- settings: createSettingsService
1259
+ scheduling: createSchedulingService
1288
1260
  };
1289
- const RELEASE_SCHEMA = utils.yup.object().shape({
1290
- name: utils.yup.string().trim().required(),
1291
- scheduledAt: utils.yup.string().nullable(),
1292
- timezone: utils.yup.string().when("scheduledAt", {
1293
- is: (value) => value !== null && value !== void 0,
1294
- then: utils.yup.string().required(),
1295
- otherwise: utils.yup.string().nullable()
1261
+ const RELEASE_SCHEMA = yup__namespace.object().shape({
1262
+ name: yup__namespace.string().trim().required(),
1263
+ scheduledAt: yup__namespace.string().nullable(),
1264
+ isScheduled: yup__namespace.boolean().optional(),
1265
+ time: yup__namespace.string().when("isScheduled", {
1266
+ is: true,
1267
+ then: yup__namespace.string().trim().required(),
1268
+ otherwise: yup__namespace.string().nullable()
1269
+ }),
1270
+ timezone: yup__namespace.string().when("isScheduled", {
1271
+ is: true,
1272
+ then: yup__namespace.string().required().nullable(),
1273
+ otherwise: yup__namespace.string().nullable()
1274
+ }),
1275
+ date: yup__namespace.string().when("isScheduled", {
1276
+ is: true,
1277
+ then: yup__namespace.string().required().nullable(),
1278
+ otherwise: yup__namespace.string().nullable()
1296
1279
  })
1297
1280
  }).required().noUnknown();
1298
- const FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA = utils.yup.object().shape({
1299
- contentType: utils.yup.string().required(),
1300
- entryDocumentId: utils.yup.string().nullable(),
1301
- hasEntryAttached: utils.yup.string().nullable(),
1302
- locale: utils.yup.string().nullable()
1303
- }).required().noUnknown();
1304
1281
  const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
1305
- const validatefindByDocumentAttachedParams = utils.validateYupSchema(
1306
- FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
1307
- );
1308
1282
  const releaseController = {
1309
- /**
1310
- * Find releases based on documents attached or not to the release.
1311
- * If `hasEntryAttached` is true, it will return all releases that have the entry attached.
1312
- * If `hasEntryAttached` is false, it will return all releases that don't have the entry attached.
1313
- */
1314
- async findByDocumentAttached(ctx) {
1315
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1283
+ async findMany(ctx) {
1284
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1316
1285
  ability: ctx.state.userAbility,
1317
1286
  model: RELEASE_MODEL_UID
1318
1287
  });
1319
1288
  await permissionsManager.validateQuery(ctx.query);
1320
1289
  const releaseService = getService("release", { strapi });
1321
- const query = await permissionsManager.sanitizeQuery(ctx.query);
1322
- await validatefindByDocumentAttachedParams(query);
1323
- const model = strapi.getModel(query.contentType);
1324
- if (model.kind && model.kind === "singleType") {
1325
- const document = await strapi.db.query(model.uid).findOne({ select: ["documentId"] });
1326
- if (!document) {
1327
- throw new utils.errors.NotFoundError(`No entry found for contentType ${query.contentType}`);
1328
- }
1329
- query.entryDocumentId = document.documentId;
1330
- }
1331
- const { contentType, hasEntryAttached, entryDocumentId, locale } = query;
1332
- const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
1333
- if (isEntryAttached) {
1334
- const releases = await releaseService.findMany({
1335
- where: {
1336
- releasedAt: null,
1337
- actions: {
1338
- contentType,
1339
- entryDocumentId: entryDocumentId ?? null,
1340
- locale: locale ?? null
1341
- }
1342
- },
1343
- populate: {
1344
- actions: {
1345
- fields: ["type"],
1346
- filters: {
1347
- contentType,
1348
- entryDocumentId: entryDocumentId ?? null,
1349
- locale: locale ?? null
1350
- }
1351
- }
1352
- }
1353
- });
1354
- ctx.body = { data: releases };
1290
+ const isFindManyForContentTypeEntry = Boolean(ctx.query?.contentTypeUid && ctx.query?.entryId);
1291
+ if (isFindManyForContentTypeEntry) {
1292
+ const query = await permissionsManager.sanitizeQuery(ctx.query);
1293
+ const contentTypeUid = query.contentTypeUid;
1294
+ const entryId = query.entryId;
1295
+ const hasEntryAttached = typeof query.hasEntryAttached === "string" ? JSON.parse(query.hasEntryAttached) : false;
1296
+ const data = hasEntryAttached ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId) : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);
1297
+ ctx.body = { data };
1355
1298
  } else {
1356
- const relatedReleases = await releaseService.findMany({
1357
- where: {
1358
- releasedAt: null,
1299
+ const query = await permissionsManager.sanitizeQuery(ctx.query);
1300
+ const { results, pagination } = await releaseService.findPage(query);
1301
+ const data = results.map((release2) => {
1302
+ const { actions, ...releaseData } = release2;
1303
+ return {
1304
+ ...releaseData,
1359
1305
  actions: {
1360
- contentType,
1361
- entryDocumentId: entryDocumentId ?? null,
1362
- locale: locale ?? null
1306
+ meta: {
1307
+ count: actions.count
1308
+ }
1363
1309
  }
1364
- }
1310
+ };
1365
1311
  });
1366
- const releases = await releaseService.findMany({
1312
+ const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
1367
1313
  where: {
1368
- $or: [
1369
- {
1370
- id: {
1371
- $notIn: relatedReleases.map((release2) => release2.id)
1372
- }
1373
- },
1374
- {
1375
- actions: null
1376
- }
1377
- ],
1378
1314
  releasedAt: null
1379
1315
  }
1380
1316
  });
1381
- ctx.body = { data: releases };
1317
+ ctx.body = { data, meta: { pagination, pendingReleasesCount } };
1382
1318
  }
1383
1319
  },
1384
- async findPage(ctx) {
1385
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1386
- ability: ctx.state.userAbility,
1387
- model: RELEASE_MODEL_UID
1388
- });
1389
- await permissionsManager.validateQuery(ctx.query);
1390
- const releaseService = getService("release", { strapi });
1391
- const query = await permissionsManager.sanitizeQuery(ctx.query);
1392
- const { results, pagination } = await releaseService.findPage(query);
1393
- const data = results.map((release2) => {
1394
- const { actions, ...releaseData } = release2;
1395
- return {
1396
- ...releaseData,
1397
- actions: {
1398
- meta: {
1399
- count: actions.count
1400
- }
1401
- }
1402
- };
1403
- });
1404
- const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
1405
- where: {
1406
- releasedAt: null
1407
- }
1408
- });
1409
- ctx.body = { data, meta: { pagination, pendingReleasesCount } };
1410
- },
1411
1320
  async findOne(ctx) {
1412
1321
  const id = ctx.params.id;
1413
1322
  const releaseService = getService("release", { strapi });
1414
- const releaseActionService = getService("release-action", { strapi });
1415
1323
  const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
1416
1324
  if (!release2) {
1417
1325
  throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
1418
1326
  }
1419
- const count = await releaseActionService.countActions({
1327
+ const count = await releaseService.countActions({
1420
1328
  filters: {
1421
1329
  release: id
1422
1330
  }
1423
1331
  });
1424
1332
  const sanitizedRelease = {
1425
1333
  ...release2,
1426
- createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
1334
+ createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
1427
1335
  };
1428
1336
  const data = {
1429
1337
  ...sanitizedRelease,
@@ -1436,39 +1344,22 @@ const releaseController = {
1436
1344
  ctx.body = { data };
1437
1345
  },
1438
1346
  async mapEntriesToReleases(ctx) {
1439
- const { contentTypeUid, documentIds, locale } = ctx.query;
1440
- if (!contentTypeUid || !documentIds) {
1347
+ const { contentTypeUid, entriesIds } = ctx.query;
1348
+ if (!contentTypeUid || !entriesIds) {
1441
1349
  throw new utils.errors.ValidationError("Missing required query parameters");
1442
1350
  }
1443
1351
  const releaseService = getService("release", { strapi });
1444
- const releasesWithActions = await releaseService.findMany({
1445
- where: {
1446
- releasedAt: null,
1447
- actions: {
1448
- contentType: contentTypeUid,
1449
- entryDocumentId: {
1450
- $in: documentIds
1451
- },
1452
- locale
1453
- }
1454
- },
1455
- populate: {
1456
- actions: true
1457
- }
1458
- });
1352
+ const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
1353
+ contentTypeUid,
1354
+ entriesIds
1355
+ );
1459
1356
  const mappedEntriesInReleases = releasesWithActions.reduce(
1460
1357
  (acc, release2) => {
1461
1358
  release2.actions.forEach((action) => {
1462
- if (action.contentType !== contentTypeUid) {
1463
- return;
1464
- }
1465
- if (locale && action.locale !== locale) {
1466
- return;
1467
- }
1468
- if (!acc[action.entryDocumentId]) {
1469
- acc[action.entryDocumentId] = [{ id: release2.id, name: release2.name }];
1359
+ if (!acc[action.entry.id]) {
1360
+ acc[action.entry.id] = [{ id: release2.id, name: release2.name }];
1470
1361
  } else {
1471
- acc[action.entryDocumentId].push({ id: release2.id, name: release2.name });
1362
+ acc[action.entry.id].push({ id: release2.id, name: release2.name });
1472
1363
  }
1473
1364
  });
1474
1365
  return acc;
@@ -1485,13 +1376,13 @@ const releaseController = {
1485
1376
  await validateRelease(releaseArgs);
1486
1377
  const releaseService = getService("release", { strapi });
1487
1378
  const release2 = await releaseService.create(releaseArgs, { user });
1488
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1379
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1489
1380
  ability: ctx.state.userAbility,
1490
1381
  model: RELEASE_MODEL_UID
1491
1382
  });
1492
- ctx.created({
1383
+ ctx.body = {
1493
1384
  data: await permissionsManager.sanitizeOutput(release2)
1494
- });
1385
+ };
1495
1386
  },
1496
1387
  async update(ctx) {
1497
1388
  const user = ctx.state.user;
@@ -1500,7 +1391,7 @@ const releaseController = {
1500
1391
  await validateRelease(releaseArgs);
1501
1392
  const releaseService = getService("release", { strapi });
1502
1393
  const release2 = await releaseService.update(id, releaseArgs, { user });
1503
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1394
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1504
1395
  ability: ctx.state.userAbility,
1505
1396
  model: RELEASE_MODEL_UID
1506
1397
  });
@@ -1517,18 +1408,18 @@ const releaseController = {
1517
1408
  };
1518
1409
  },
1519
1410
  async publish(ctx) {
1411
+ const user = ctx.state.user;
1520
1412
  const id = ctx.params.id;
1521
1413
  const releaseService = getService("release", { strapi });
1522
- const releaseActionService = getService("release-action", { strapi });
1523
- const release2 = await releaseService.publish(id);
1414
+ const release2 = await releaseService.publish(id, { user });
1524
1415
  const [countPublishActions, countUnpublishActions] = await Promise.all([
1525
- releaseActionService.countActions({
1416
+ releaseService.countActions({
1526
1417
  filters: {
1527
1418
  release: id,
1528
1419
  type: "publish"
1529
1420
  }
1530
1421
  }),
1531
- releaseActionService.countActions({
1422
+ releaseService.countActions({
1532
1423
  filters: {
1533
1424
  release: id,
1534
1425
  type: "unpublish"
@@ -1546,30 +1437,27 @@ const releaseController = {
1546
1437
  }
1547
1438
  };
1548
1439
  const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
1549
- contentType: utils.yup.string().required(),
1550
- entryDocumentId: utils.yup.strapiID(),
1551
- locale: utils.yup.string(),
1440
+ entry: utils.yup.object().shape({
1441
+ id: utils.yup.strapiID().required(),
1442
+ contentType: utils.yup.string().required()
1443
+ }).required(),
1552
1444
  type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
1553
1445
  });
1554
1446
  const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
1555
1447
  type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
1556
1448
  });
1557
- const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
1558
- groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
1559
- });
1560
1449
  const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
1561
1450
  const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
1562
- const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
1563
1451
  const releaseActionController = {
1564
1452
  async create(ctx) {
1565
1453
  const releaseId = ctx.params.releaseId;
1566
1454
  const releaseActionArgs = ctx.request.body;
1567
1455
  await validateReleaseAction(releaseActionArgs);
1568
- const releaseActionService = getService("release-action", { strapi });
1569
- const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
1570
- ctx.created({
1456
+ const releaseService = getService("release", { strapi });
1457
+ const releaseAction2 = await releaseService.createAction(releaseId, releaseActionArgs);
1458
+ ctx.body = {
1571
1459
  data: releaseAction2
1572
- });
1460
+ };
1573
1461
  },
1574
1462
  async createMany(ctx) {
1575
1463
  const releaseId = ctx.params.releaseId;
@@ -1577,13 +1465,12 @@ const releaseActionController = {
1577
1465
  await Promise.all(
1578
1466
  releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
1579
1467
  );
1580
- const releaseActionService = getService("release-action", { strapi });
1581
1468
  const releaseService = getService("release", { strapi });
1582
1469
  const releaseActions = await strapi.db.transaction(async () => {
1583
1470
  const releaseActions2 = await Promise.all(
1584
1471
  releaseActionsArgs.map(async (releaseActionArgs) => {
1585
1472
  try {
1586
- const action = await releaseActionService.create(releaseId, releaseActionArgs, {
1473
+ const action = await releaseService.createAction(releaseId, releaseActionArgs, {
1587
1474
  disableUpdateReleaseStatus: true
1588
1475
  });
1589
1476
  return action;
@@ -1601,51 +1488,43 @@ const releaseActionController = {
1601
1488
  if (newReleaseActions.length > 0) {
1602
1489
  releaseService.updateReleaseStatus(releaseId);
1603
1490
  }
1604
- ctx.created({
1491
+ ctx.body = {
1605
1492
  data: newReleaseActions,
1606
1493
  meta: {
1607
1494
  entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
1608
1495
  totalEntries: releaseActions.length
1609
1496
  }
1610
- });
1497
+ };
1611
1498
  },
1612
1499
  async findMany(ctx) {
1613
1500
  const releaseId = ctx.params.releaseId;
1614
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1501
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1615
1502
  ability: ctx.state.userAbility,
1616
1503
  model: RELEASE_ACTION_MODEL_UID
1617
1504
  });
1618
- await validateFindManyActionsParams(ctx.query);
1619
- if (ctx.query.groupBy) {
1620
- if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
1621
- ctx.badRequest("Invalid groupBy parameter");
1622
- }
1623
- }
1624
- ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
1625
- delete ctx.query.groupBy;
1626
1505
  const query = await permissionsManager.sanitizeQuery(ctx.query);
1627
- const releaseActionService = getService("release-action", { strapi });
1628
- const { results, pagination } = await releaseActionService.findPage(releaseId, {
1506
+ const releaseService = getService("release", { strapi });
1507
+ const { results, pagination } = await releaseService.findActions(releaseId, {
1508
+ sort: query.groupBy === "action" ? "type" : query.groupBy,
1629
1509
  ...query
1630
1510
  });
1631
1511
  const contentTypeOutputSanitizers = results.reduce((acc, action) => {
1632
1512
  if (acc[action.contentType]) {
1633
1513
  return acc;
1634
1514
  }
1635
- const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
1515
+ const contentTypePermissionsManager = strapi.admin.services.permission.createPermissionsManager({
1636
1516
  ability: ctx.state.userAbility,
1637
1517
  model: action.contentType
1638
1518
  });
1639
1519
  acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
1640
1520
  return acc;
1641
1521
  }, {});
1642
- const sanitizedResults = await utils.async.map(results, async (action) => ({
1522
+ const sanitizedResults = await utils.mapAsync(results, async (action) => ({
1643
1523
  ...action,
1644
- entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
1524
+ entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
1645
1525
  }));
1646
- const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
1647
- const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
1648
- const releaseService = getService("release", { strapi });
1526
+ const groupedData = await releaseService.groupActions(sanitizedResults, query.groupBy);
1527
+ const contentTypes2 = releaseService.getContentTypeModelsFromActions(results);
1649
1528
  const components = await releaseService.getAllComponents();
1650
1529
  ctx.body = {
1651
1530
  data: groupedData,
@@ -1661,8 +1540,8 @@ const releaseActionController = {
1661
1540
  const releaseId = ctx.params.releaseId;
1662
1541
  const releaseActionUpdateArgs = ctx.request.body;
1663
1542
  await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
1664
- const releaseActionService = getService("release-action", { strapi });
1665
- const updatedAction = await releaseActionService.update(
1543
+ const releaseService = getService("release", { strapi });
1544
+ const updatedAction = await releaseService.updateAction(
1666
1545
  actionId,
1667
1546
  releaseId,
1668
1547
  releaseActionUpdateArgs
@@ -1674,36 +1553,14 @@ const releaseActionController = {
1674
1553
  async delete(ctx) {
1675
1554
  const actionId = ctx.params.actionId;
1676
1555
  const releaseId = ctx.params.releaseId;
1677
- const releaseActionService = getService("release-action", { strapi });
1678
- const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
1556
+ const releaseService = getService("release", { strapi });
1557
+ const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);
1679
1558
  ctx.body = {
1680
1559
  data: deletedReleaseAction
1681
1560
  };
1682
1561
  }
1683
1562
  };
1684
- const SETTINGS_SCHEMA = yup__namespace.object().shape({
1685
- defaultTimezone: yup__namespace.string().nullable().default(null)
1686
- }).required().noUnknown();
1687
- const validateSettings = utils.validateYupSchema(SETTINGS_SCHEMA);
1688
- const settingsController = {
1689
- async find(ctx) {
1690
- const settingsService = getService("settings", { strapi });
1691
- const settings2 = await settingsService.find();
1692
- ctx.body = { data: settings2 };
1693
- },
1694
- async update(ctx) {
1695
- const settingsBody = ctx.request.body;
1696
- const settings2 = await validateSettings(settingsBody);
1697
- const settingsService = getService("settings", { strapi });
1698
- const updatedSettings = await settingsService.update({ settings: settings2 });
1699
- ctx.body = { data: updatedSettings };
1700
- }
1701
- };
1702
- const controllers = {
1703
- release: releaseController,
1704
- "release-action": releaseActionController,
1705
- settings: settingsController
1706
- };
1563
+ const controllers = { release: releaseController, "release-action": releaseActionController };
1707
1564
  const release = {
1708
1565
  type: "admin",
1709
1566
  routes: [
@@ -1723,22 +1580,6 @@ const release = {
1723
1580
  ]
1724
1581
  }
1725
1582
  },
1726
- {
1727
- method: "GET",
1728
- path: "/getByDocumentAttached",
1729
- handler: "release.findByDocumentAttached",
1730
- config: {
1731
- policies: [
1732
- "admin::isAuthenticatedAdmin",
1733
- {
1734
- name: "admin::hasPermissions",
1735
- config: {
1736
- actions: ["plugin::content-releases.read"]
1737
- }
1738
- }
1739
- ]
1740
- }
1741
- },
1742
1583
  {
1743
1584
  method: "POST",
1744
1585
  path: "/",
@@ -1758,7 +1599,7 @@ const release = {
1758
1599
  {
1759
1600
  method: "GET",
1760
1601
  path: "/",
1761
- handler: "release.findPage",
1602
+ handler: "release.findMany",
1762
1603
  config: {
1763
1604
  policies: [
1764
1605
  "admin::isAuthenticatedAdmin",
@@ -1922,50 +1763,13 @@ const releaseAction = {
1922
1763
  }
1923
1764
  ]
1924
1765
  };
1925
- const settings = {
1926
- type: "admin",
1927
- routes: [
1928
- {
1929
- method: "GET",
1930
- path: "/settings",
1931
- handler: "settings.find",
1932
- config: {
1933
- policies: [
1934
- "admin::isAuthenticatedAdmin",
1935
- {
1936
- name: "admin::hasPermissions",
1937
- config: {
1938
- actions: ["plugin::content-releases.settings.read"]
1939
- }
1940
- }
1941
- ]
1942
- }
1943
- },
1944
- {
1945
- method: "PUT",
1946
- path: "/settings",
1947
- handler: "settings.update",
1948
- config: {
1949
- policies: [
1950
- "admin::isAuthenticatedAdmin",
1951
- {
1952
- name: "admin::hasPermissions",
1953
- config: {
1954
- actions: ["plugin::content-releases.settings.update"]
1955
- }
1956
- }
1957
- ]
1958
- }
1959
- }
1960
- ]
1961
- };
1962
1766
  const routes = {
1963
- settings,
1964
1767
  release,
1965
1768
  "release-action": releaseAction
1966
1769
  };
1770
+ const { features } = require("@strapi/strapi/dist/utils/ee");
1967
1771
  const getPlugin = () => {
1968
- if (strapi.ee.features.isEnabled("cms-content-releases")) {
1772
+ if (features.isEnabled("cms-content-releases")) {
1969
1773
  return {
1970
1774
  register,
1971
1775
  bootstrap,