@strapi/content-releases 0.0.0-experimental.f2351bcfa3965c60f063a492da51faa2c636eee8 → 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 (139) 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-JwN_xBnA.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 +646 -809
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/server/index.mjs +646 -810
  24. package/dist/server/index.mjs.map +1 -1
  25. package/package.json +37 -31
  26. package/dist/_chunks/App-BFo3ibui.js +0 -1395
  27. package/dist/_chunks/App-BFo3ibui.js.map +0 -1
  28. package/dist/_chunks/App-JwN_xBnA.mjs.map +0 -1
  29. package/dist/_chunks/PurchaseContentReleases-Be3acS2L.js.map +0 -1
  30. package/dist/_chunks/PurchaseContentReleases-_MxP6-Dt.mjs.map +0 -1
  31. package/dist/_chunks/ReleasesSettingsPage-BanjZwEc.js +0 -178
  32. package/dist/_chunks/ReleasesSettingsPage-BanjZwEc.js.map +0 -1
  33. package/dist/_chunks/ReleasesSettingsPage-CNMXGcZC.mjs +0 -178
  34. package/dist/_chunks/ReleasesSettingsPage-CNMXGcZC.mjs.map +0 -1
  35. package/dist/_chunks/en-CmYoEnA7.js.map +0 -1
  36. package/dist/_chunks/en-D0yVZFqf.mjs.map +0 -1
  37. package/dist/_chunks/index-C_e6DQb0.mjs +0 -1342
  38. package/dist/_chunks/index-C_e6DQb0.mjs.map +0 -1
  39. package/dist/_chunks/index-Em3KctMx.js +0 -1361
  40. package/dist/_chunks/index-Em3KctMx.js.map +0 -1
  41. package/dist/_chunks/schemas-63pFihNF.mjs +0 -44
  42. package/dist/_chunks/schemas-63pFihNF.mjs.map +0 -1
  43. package/dist/_chunks/schemas-z5zp-_Gd.js +0 -62
  44. package/dist/_chunks/schemas-z5zp-_Gd.js.map +0 -1
  45. package/dist/admin/src/components/RelativeTime.d.ts +0 -28
  46. package/dist/admin/src/components/ReleaseAction.d.ts +0 -3
  47. package/dist/admin/src/components/ReleaseActionMenu.d.ts +0 -26
  48. package/dist/admin/src/components/ReleaseActionModal.d.ts +0 -24
  49. package/dist/admin/src/components/ReleaseActionOptions.d.ts +0 -9
  50. package/dist/admin/src/components/ReleaseListCell.d.ts +0 -28
  51. package/dist/admin/src/components/ReleaseModal.d.ts +0 -17
  52. package/dist/admin/src/components/ReleasesPanel.d.ts +0 -3
  53. package/dist/admin/src/constants.d.ts +0 -76
  54. package/dist/admin/src/index.d.ts +0 -3
  55. package/dist/admin/src/modules/hooks.d.ts +0 -7
  56. package/dist/admin/src/pages/App.d.ts +0 -1
  57. package/dist/admin/src/pages/PurchaseContentReleases.d.ts +0 -2
  58. package/dist/admin/src/pages/ReleaseDetailsPage.d.ts +0 -2
  59. package/dist/admin/src/pages/ReleasesPage.d.ts +0 -8
  60. package/dist/admin/src/pages/ReleasesSettingsPage.d.ts +0 -1
  61. package/dist/admin/src/pages/tests/mockReleaseDetailsPageData.d.ts +0 -181
  62. package/dist/admin/src/pages/tests/mockReleasesPageData.d.ts +0 -39
  63. package/dist/admin/src/pluginId.d.ts +0 -1
  64. package/dist/admin/src/services/release.d.ts +0 -112
  65. package/dist/admin/src/store/hooks.d.ts +0 -7
  66. package/dist/admin/src/utils/api.d.ts +0 -6
  67. package/dist/admin/src/utils/prefixPluginTranslations.d.ts +0 -3
  68. package/dist/admin/src/utils/time.d.ts +0 -10
  69. package/dist/admin/src/validation/schemas.d.ts +0 -6
  70. package/dist/server/src/bootstrap.d.ts +0 -5
  71. package/dist/server/src/bootstrap.d.ts.map +0 -1
  72. package/dist/server/src/constants.d.ts +0 -21
  73. package/dist/server/src/constants.d.ts.map +0 -1
  74. package/dist/server/src/content-types/index.d.ts +0 -97
  75. package/dist/server/src/content-types/index.d.ts.map +0 -1
  76. package/dist/server/src/content-types/release/index.d.ts +0 -48
  77. package/dist/server/src/content-types/release/index.d.ts.map +0 -1
  78. package/dist/server/src/content-types/release/schema.d.ts +0 -47
  79. package/dist/server/src/content-types/release/schema.d.ts.map +0 -1
  80. package/dist/server/src/content-types/release-action/index.d.ts +0 -48
  81. package/dist/server/src/content-types/release-action/index.d.ts.map +0 -1
  82. package/dist/server/src/content-types/release-action/schema.d.ts +0 -47
  83. package/dist/server/src/content-types/release-action/schema.d.ts.map +0 -1
  84. package/dist/server/src/controllers/index.d.ts +0 -25
  85. package/dist/server/src/controllers/index.d.ts.map +0 -1
  86. package/dist/server/src/controllers/release-action.d.ts +0 -10
  87. package/dist/server/src/controllers/release-action.d.ts.map +0 -1
  88. package/dist/server/src/controllers/release.d.ts +0 -18
  89. package/dist/server/src/controllers/release.d.ts.map +0 -1
  90. package/dist/server/src/controllers/settings.d.ts +0 -11
  91. package/dist/server/src/controllers/settings.d.ts.map +0 -1
  92. package/dist/server/src/controllers/validation/release-action.d.ts +0 -14
  93. package/dist/server/src/controllers/validation/release-action.d.ts.map +0 -1
  94. package/dist/server/src/controllers/validation/release.d.ts +0 -4
  95. package/dist/server/src/controllers/validation/release.d.ts.map +0 -1
  96. package/dist/server/src/controllers/validation/settings.d.ts +0 -3
  97. package/dist/server/src/controllers/validation/settings.d.ts.map +0 -1
  98. package/dist/server/src/destroy.d.ts +0 -5
  99. package/dist/server/src/destroy.d.ts.map +0 -1
  100. package/dist/server/src/index.d.ts +0 -2113
  101. package/dist/server/src/index.d.ts.map +0 -1
  102. package/dist/server/src/middlewares/documents.d.ts +0 -6
  103. package/dist/server/src/middlewares/documents.d.ts.map +0 -1
  104. package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts +0 -9
  105. package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts.map +0 -1
  106. package/dist/server/src/migrations/index.d.ts +0 -13
  107. package/dist/server/src/migrations/index.d.ts.map +0 -1
  108. package/dist/server/src/register.d.ts +0 -5
  109. package/dist/server/src/register.d.ts.map +0 -1
  110. package/dist/server/src/routes/index.d.ts +0 -51
  111. package/dist/server/src/routes/index.d.ts.map +0 -1
  112. package/dist/server/src/routes/release-action.d.ts +0 -18
  113. package/dist/server/src/routes/release-action.d.ts.map +0 -1
  114. package/dist/server/src/routes/release.d.ts +0 -18
  115. package/dist/server/src/routes/release.d.ts.map +0 -1
  116. package/dist/server/src/routes/settings.d.ts +0 -18
  117. package/dist/server/src/routes/settings.d.ts.map +0 -1
  118. package/dist/server/src/services/index.d.ts +0 -1826
  119. package/dist/server/src/services/index.d.ts.map +0 -1
  120. package/dist/server/src/services/release-action.d.ts +0 -36
  121. package/dist/server/src/services/release-action.d.ts.map +0 -1
  122. package/dist/server/src/services/release.d.ts +0 -31
  123. package/dist/server/src/services/release.d.ts.map +0 -1
  124. package/dist/server/src/services/scheduling.d.ts +0 -18
  125. package/dist/server/src/services/scheduling.d.ts.map +0 -1
  126. package/dist/server/src/services/settings.d.ts +0 -13
  127. package/dist/server/src/services/settings.d.ts.map +0 -1
  128. package/dist/server/src/services/validation.d.ts +0 -18
  129. package/dist/server/src/services/validation.d.ts.map +0 -1
  130. package/dist/server/src/utils/index.d.ts +0 -35
  131. package/dist/server/src/utils/index.d.ts.map +0 -1
  132. package/dist/shared/contracts/release-actions.d.ts +0 -130
  133. package/dist/shared/contracts/release-actions.d.ts.map +0 -1
  134. package/dist/shared/contracts/releases.d.ts +0 -184
  135. package/dist/shared/contracts/releases.d.ts.map +0 -1
  136. package/dist/shared/contracts/settings.d.ts +0 -39
  137. package/dist/shared/contracts/settings.d.ts.map +0 -1
  138. package/dist/shared/types.d.ts +0 -24
  139. 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,39 +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 hasPolymorphicColumn = await trx.schema.hasColumn("strapi_release_actions", "target_id");
333
- if (hasPolymorphicColumn) {
334
- const hasEntryDocumentIdColumn = await trx.schema.hasColumn(
335
- "strapi_release_actions",
336
- "entry_document_id"
337
- );
338
- if (!hasEntryDocumentIdColumn) {
339
- await trx.schema.alterTable("strapi_release_actions", (table) => {
340
- table.string("entry_document_id");
341
- });
342
- }
343
- const releaseActions = await trx.select("*").from("strapi_release_actions");
344
- utils.async.map(releaseActions, async (action) => {
345
- const { target_type, target_id } = action;
346
- const entry = await db.query(target_type).findOne({ where: { id: target_id } });
347
- if (entry) {
348
- await trx("strapi_release_actions").update({ entry_document_id: entry.documentId }).where("id", action.id);
349
- }
350
- });
351
- }
352
- },
353
- async down() {
354
- throw new Error("not implemented");
355
- }
356
- };
280
+ const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
357
281
  const register = async ({ strapi: strapi2 }) => {
358
- if (strapi2.ee.features.isEnabled("cms-content-releases")) {
359
- await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
360
- strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
361
- 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);
362
285
  strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
363
286
  }
364
287
  if (strapi2.plugin("graphql")) {
@@ -367,134 +290,129 @@ const register = async ({ strapi: strapi2 }) => {
367
290
  graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
368
291
  }
369
292
  };
370
- const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
371
- const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
372
- where: {
373
- actions: {
374
- contentType,
375
- entryDocumentId: entry.documentId,
376
- locale: entry.locale
377
- }
378
- }
379
- });
380
- const entryStatus = await isEntryValid(contentType, entry, { strapi });
381
- await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
382
- where: {
383
- contentType,
384
- entryDocumentId: entry.documentId,
385
- locale: entry.locale
386
- },
387
- data: {
388
- isEntryValid: entryStatus
389
- }
390
- });
391
- for (const release2 of releases) {
392
- getService("release", { strapi }).updateReleaseStatus(release2.id);
393
- }
394
- };
395
- const deleteActionsAndUpdateReleaseStatus = async (params) => {
396
- const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
397
- where: {
398
- actions: params
399
- }
400
- });
401
- await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
402
- where: params
403
- });
404
- for (const release2 of releases) {
405
- getService("release", { strapi }).updateReleaseStatus(release2.id);
406
- }
407
- };
408
- const deleteActionsOnDelete = async (ctx, next) => {
409
- if (ctx.action !== "delete") {
410
- return next();
411
- }
412
- if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
413
- return next();
414
- }
415
- const contentType = ctx.contentType.uid;
416
- const { documentId, locale } = ctx.params;
417
- const result = await next();
418
- if (!result) {
419
- return result;
420
- }
421
- try {
422
- deleteActionsAndUpdateReleaseStatus({
423
- contentType,
424
- entryDocumentId: documentId,
425
- ...locale !== "*" && { locale }
426
- });
427
- } catch (error) {
428
- strapi.log.error("Error while deleting release actions after delete", {
429
- error
430
- });
431
- }
432
- return result;
433
- };
434
- const updateActionsOnUpdate = async (ctx, next) => {
435
- if (ctx.action !== "update") {
436
- return next();
437
- }
438
- if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
439
- return next();
440
- }
441
- const contentType = ctx.contentType.uid;
442
- const result = await next();
443
- if (!result) {
444
- return result;
445
- }
446
- try {
447
- updateActionsStatusAndUpdateReleaseStatus(contentType, result);
448
- } catch (error) {
449
- strapi.log.error("Error while updating release actions after update", {
450
- error
451
- });
452
- }
453
- return result;
454
- };
455
- const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
456
- const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
457
- where: {
458
- actions: params
459
- }
460
- });
461
- await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
462
- where: params
463
- });
464
- for (const release2 of releases) {
465
- getService("release", { strapi }).updateReleaseStatus(release2.id);
466
- }
467
- };
293
+ const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
468
294
  const bootstrap = async ({ strapi: strapi2 }) => {
469
- if (strapi2.ee.features.isEnabled("cms-content-releases")) {
295
+ if (features$1.isEnabled("cms-content-releases")) {
470
296
  const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
471
297
  (uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
472
298
  );
473
299
  strapi2.db.lifecycles.subscribe({
474
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
+ },
475
340
  /**
476
- * 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
477
343
  */
478
344
  async afterDeleteMany(event) {
479
345
  try {
480
- const model = strapi2.getModel(event.model.uid);
481
- if (model.kind === "collectionType" && model.options?.draftAndPublish) {
482
- const { where } = event.params;
483
- deleteReleasesActionsAndUpdateReleaseStatus({
484
- contentType: model.uid,
485
- locale: where.locale ?? null,
486
- ...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
+ }
487
368
  });
369
+ for (const release2 of releases) {
370
+ getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
371
+ }
488
372
  }
489
373
  } catch (error) {
490
374
  strapi2.log.error("Error while deleting release actions after entry deleteMany", {
491
375
  error
492
376
  });
493
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
+ }
494
414
  }
495
415
  });
496
- strapi2.documents.use(deleteActionsOnDelete);
497
- strapi2.documents.use(updateActionsOnUpdate);
498
416
  getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
499
417
  strapi2.log.error(
500
418
  "Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
@@ -502,7 +420,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
502
420
  throw err;
503
421
  });
504
422
  Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
505
- strapi2.get("webhookStore").addAllowedEvent(key, value);
423
+ strapi2.webhookStore.addAllowedEvent(key, value);
506
424
  });
507
425
  }
508
426
  };
@@ -586,13 +504,15 @@ const schema = {
586
504
  enum: ["publish", "unpublish"],
587
505
  required: true
588
506
  },
507
+ entry: {
508
+ type: "relation",
509
+ relation: "morphToOne",
510
+ configurable: false
511
+ },
589
512
  contentType: {
590
513
  type: "string",
591
514
  required: true
592
515
  },
593
- entryDocumentId: {
594
- type: "string"
595
- },
596
516
  locale: {
597
517
  type: "string"
598
518
  },
@@ -614,6 +534,18 @@ const contentTypes = {
614
534
  release: release$1,
615
535
  "release-action": releaseAction$1
616
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
+ };
617
549
  const createReleaseService = ({ strapi: strapi2 }) => {
618
550
  const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
619
551
  strapi2.eventHub.emit(event, {
@@ -622,32 +554,93 @@ const createReleaseService = ({ strapi: strapi2 }) => {
622
554
  release: release2
623
555
  });
624
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
+ };
625
603
  const getFormattedActions = async (releaseId) => {
626
604
  const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
627
605
  where: {
628
606
  release: {
629
607
  id: releaseId
630
608
  }
609
+ },
610
+ populate: {
611
+ entry: {
612
+ fields: ["id"]
613
+ }
631
614
  }
632
615
  });
633
616
  if (actions.length === 0) {
634
617
  throw new utils.errors.ValidationError("No entries to publish");
635
618
  }
636
- const formattedActions = {};
619
+ const collectionTypeActions = {};
620
+ const singleTypeActions = [];
637
621
  for (const action of actions) {
638
622
  const contentTypeUid = action.contentType;
639
- if (!formattedActions[contentTypeUid]) {
640
- formattedActions[contentTypeUid] = {
641
- publish: [],
642
- unpublish: []
643
- };
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
+ });
644
641
  }
645
- formattedActions[contentTypeUid][action.type].push({
646
- documentId: action.entryDocumentId,
647
- locale: action.locale
648
- });
649
642
  }
650
- return formattedActions;
643
+ return { collectionTypeActions, singleTypeActions };
651
644
  };
652
645
  return {
653
646
  async create(releaseData, { user }) {
@@ -662,7 +655,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
662
655
  validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
663
656
  validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
664
657
  ]);
665
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
658
+ const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
666
659
  data: {
667
660
  ...releaseWithCreatorFields,
668
661
  status: "empty"
@@ -676,28 +669,107 @@ const createReleaseService = ({ strapi: strapi2 }) => {
676
669
  return release2;
677
670
  },
678
671
  async findOne(id, query = {}) {
679
- const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query);
680
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
681
- ...dbQuery,
682
- where: { id }
672
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
673
+ ...query
683
674
  });
684
675
  return release2;
685
676
  },
686
677
  findPage(query) {
687
- const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
688
- return strapi2.db.query(RELEASE_MODEL_UID).findPage({
689
- ...dbQuery,
678
+ return strapi2.entityService.findPage(RELEASE_MODEL_UID, {
679
+ ...query,
690
680
  populate: {
691
681
  actions: {
682
+ // @ts-expect-error Ignore missing properties
692
683
  count: true
693
684
  }
694
685
  }
695
686
  });
696
687
  },
697
- findMany(query) {
698
- const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
699
- return strapi2.db.query(RELEASE_MODEL_UID).findMany({
700
- ...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;
701
773
  });
702
774
  },
703
775
  async update(id, releaseData, { user }) {
@@ -712,15 +784,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
712
784
  validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
713
785
  validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
714
786
  ]);
715
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
787
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
716
788
  if (!release2) {
717
789
  throw new utils.errors.NotFoundError(`No release found for id ${id}`);
718
790
  }
719
791
  if (release2.releasedAt) {
720
792
  throw new utils.errors.ValidationError("Release already published");
721
793
  }
722
- const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
723
- 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
724
800
  data: releaseWithCreatorFields
725
801
  });
726
802
  const schedulingService = getService("scheduling", { strapi: strapi2 });
@@ -733,6 +809,132 @@ const createReleaseService = ({ strapi: strapi2 }) => {
733
809
  strapi2.telemetry.send("didUpdateContentRelease");
734
810
  return updatedRelease;
735
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
+ },
736
938
  async getAllComponents() {
737
939
  const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
738
940
  const components = await contentManagerComponentsService.findAllComponents();
@@ -746,11 +948,10 @@ const createReleaseService = ({ strapi: strapi2 }) => {
746
948
  return componentsMap;
747
949
  },
748
950
  async delete(releaseId) {
749
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
750
- where: { id: releaseId },
951
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
751
952
  populate: {
752
953
  actions: {
753
- select: ["id"]
954
+ fields: ["id"]
754
955
  }
755
956
  }
756
957
  });
@@ -768,11 +969,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
768
969
  }
769
970
  }
770
971
  });
771
- await strapi2.db.query(RELEASE_MODEL_UID).delete({
772
- where: {
773
- id: releaseId
774
- }
775
- });
972
+ await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
776
973
  });
777
974
  if (release2.scheduledAt) {
778
975
  const schedulingService = getService("scheduling", { strapi: strapi2 });
@@ -798,19 +995,22 @@ const createReleaseService = ({ strapi: strapi2 }) => {
798
995
  }
799
996
  try {
800
997
  strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
801
- const formattedActions = await getFormattedActions(releaseId);
802
- await strapi2.db.transaction(
803
- async () => Promise.all(
804
- Object.keys(formattedActions).map(async (contentTypeUid) => {
805
- const contentType = contentTypeUid;
806
- const { publish, unpublish } = formattedActions[contentType];
807
- return Promise.all([
808
- ...publish.map((params) => strapi2.documents(contentType).publish(params)),
809
- ...unpublish.map((params) => strapi2.documents(contentType).unpublish(params))
810
- ]);
811
- })
812
- )
998
+ const { collectionTypeActions, singleTypeActions } = await getFormattedActions(
999
+ releaseId
813
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
+ });
814
1014
  const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
815
1015
  where: {
816
1016
  id: releaseId
@@ -836,220 +1036,17 @@ const createReleaseService = ({ strapi: strapi2 }) => {
836
1036
  }).transacting(trx).execute();
837
1037
  return {
838
1038
  release: null,
839
- error: error2
840
- };
841
- }
842
- });
843
- if (error instanceof Error) {
844
- throw error;
845
- }
846
- return release2;
847
- },
848
- async updateReleaseStatus(releaseId) {
849
- const releaseActionService = getService("release-action", { strapi: strapi2 });
850
- const [totalActions, invalidActions] = await Promise.all([
851
- releaseActionService.countActions({
852
- filters: {
853
- release: releaseId
854
- }
855
- }),
856
- releaseActionService.countActions({
857
- filters: {
858
- release: releaseId,
859
- isEntryValid: false
860
- }
861
- })
862
- ]);
863
- if (totalActions > 0) {
864
- if (invalidActions > 0) {
865
- return strapi2.db.query(RELEASE_MODEL_UID).update({
866
- where: {
867
- id: releaseId
868
- },
869
- data: {
870
- status: "blocked"
871
- }
872
- });
873
- }
874
- return strapi2.db.query(RELEASE_MODEL_UID).update({
875
- where: {
876
- id: releaseId
877
- },
878
- data: {
879
- status: "ready"
880
- }
881
- });
882
- }
883
- return strapi2.db.query(RELEASE_MODEL_UID).update({
884
- where: {
885
- id: releaseId
886
- },
887
- data: {
888
- status: "empty"
889
- }
890
- });
891
- }
892
- };
893
- };
894
- const getGroupName = (queryValue) => {
895
- switch (queryValue) {
896
- case "contentType":
897
- return "contentType.displayName";
898
- case "type":
899
- return "type";
900
- case "locale":
901
- return ___default.default.getOr("No locale", "locale.name");
902
- default:
903
- return "contentType.displayName";
904
- }
905
- };
906
- const createReleaseActionService = ({ strapi: strapi2 }) => {
907
- const getLocalesDataForActions = async () => {
908
- if (!strapi2.plugin("i18n")) {
909
- return {};
910
- }
911
- const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
912
- return allLocales.reduce((acc, locale) => {
913
- acc[locale.code] = { name: locale.name, code: locale.code };
914
- return acc;
915
- }, {});
916
- };
917
- const getContentTypesDataForActions = async (contentTypesUids) => {
918
- const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
919
- const contentTypesData = {};
920
- for (const contentTypeUid of contentTypesUids) {
921
- const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
922
- uid: contentTypeUid
923
- });
924
- contentTypesData[contentTypeUid] = {
925
- mainField: contentTypeConfig.settings.mainField,
926
- displayName: strapi2.getModel(contentTypeUid).info.displayName
927
- };
928
- }
929
- return contentTypesData;
930
- };
931
- return {
932
- async create(releaseId, action) {
933
- const { validateEntryData, validateUniqueEntry } = getService("release-validation", {
934
- strapi: strapi2
935
- });
936
- await Promise.all([
937
- validateEntryData(action.contentType, action.entryDocumentId),
938
- validateUniqueEntry(releaseId, action)
939
- ]);
940
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
941
- if (!release2) {
942
- throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
943
- }
944
- if (release2.releasedAt) {
945
- throw new utils.errors.ValidationError("Release already published");
946
- }
947
- const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
948
- {
949
- contentType: action.contentType,
950
- documentId: action.entryDocumentId,
951
- locale: action.locale
952
- },
953
- {
954
- strapi: strapi2
1039
+ error: error2
1040
+ };
955
1041
  }
956
- ) : true;
957
- const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
958
- data: {
959
- ...action,
960
- release: release2.id,
961
- isEntryValid: actionStatus
962
- },
963
- populate: { release: { select: ["id"] } }
964
- });
965
- getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
966
- return releaseAction2;
967
- },
968
- async findPage(releaseId, query) {
969
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
970
- where: { id: releaseId },
971
- select: ["id"]
972
1042
  });
973
- if (!release2) {
974
- throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
1043
+ if (error) {
1044
+ throw error;
975
1045
  }
976
- const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
977
- const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
978
- ...dbQuery,
979
- where: {
980
- release: releaseId
981
- }
982
- });
983
- const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
984
- const actionsWithEntry = await utils.async.map(actions, async (action) => {
985
- const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
986
- const entry = await getEntry(
987
- {
988
- contentType: action.contentType,
989
- documentId: action.entryDocumentId,
990
- locale: action.locale,
991
- populate,
992
- status: action.type === "publish" ? "draft" : "published"
993
- },
994
- { strapi: strapi2 }
995
- );
996
- return {
997
- ...action,
998
- entry,
999
- status: entry ? await getEntryStatus(action.contentType, entry) : null
1000
- };
1001
- });
1002
- return {
1003
- results: actionsWithEntry,
1004
- pagination
1005
- };
1006
- },
1007
- async groupActions(actions, groupBy) {
1008
- const contentTypeUids = actions.reduce((acc, action) => {
1009
- if (!acc.includes(action.contentType)) {
1010
- acc.push(action.contentType);
1011
- }
1012
- return acc;
1013
- }, []);
1014
- const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
1015
- const allLocalesDictionary = await getLocalesDataForActions();
1016
- const formattedData = actions.map((action) => {
1017
- const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
1018
- return {
1019
- ...action,
1020
- locale: action.locale ? allLocalesDictionary[action.locale] : null,
1021
- contentType: {
1022
- displayName,
1023
- mainFieldValue: action.entry[mainField],
1024
- uid: action.contentType
1025
- }
1026
- };
1027
- });
1028
- const groupName = getGroupName(groupBy);
1029
- return ___default.default.groupBy(groupName)(formattedData);
1030
- },
1031
- getContentTypeModelsFromActions(actions) {
1032
- const contentTypeUids = actions.reduce((acc, action) => {
1033
- if (!acc.includes(action.contentType)) {
1034
- acc.push(action.contentType);
1035
- }
1036
- return acc;
1037
- }, []);
1038
- const contentTypeModelsMap = contentTypeUids.reduce(
1039
- (acc, contentTypeUid) => {
1040
- acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
1041
- return acc;
1042
- },
1043
- {}
1044
- );
1045
- return contentTypeModelsMap;
1046
- },
1047
- async countActions(query) {
1048
- const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
1049
- return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
1046
+ return release2;
1050
1047
  },
1051
- async update(actionId, releaseId, update) {
1052
- 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({
1053
1050
  where: {
1054
1051
  id: actionId,
1055
1052
  release: {
@@ -1058,42 +1055,17 @@ const createReleaseActionService = ({ strapi: strapi2 }) => {
1058
1055
  $null: true
1059
1056
  }
1060
1057
  }
1061
- }
1058
+ },
1059
+ data: update
1062
1060
  });
1063
- if (!action) {
1061
+ if (!updatedAction) {
1064
1062
  throw new utils.errors.NotFoundError(
1065
1063
  `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
1066
1064
  );
1067
1065
  }
1068
- const actionStatus = update.type === "publish" ? getDraftEntryValidStatus(
1069
- {
1070
- contentType: action.contentType,
1071
- documentId: action.entryDocumentId,
1072
- locale: action.locale
1073
- },
1074
- {
1075
- strapi: strapi2
1076
- }
1077
- ) : true;
1078
- const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
1079
- where: {
1080
- id: actionId,
1081
- release: {
1082
- id: releaseId,
1083
- releasedAt: {
1084
- $null: true
1085
- }
1086
- }
1087
- },
1088
- data: {
1089
- ...update,
1090
- isEntryValid: actionStatus
1091
- }
1092
- });
1093
- getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
1094
1066
  return updatedAction;
1095
1067
  },
1096
- async delete(actionId, releaseId) {
1068
+ async deleteAction(actionId, releaseId) {
1097
1069
  const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
1098
1070
  where: {
1099
1071
  id: actionId,
@@ -1110,8 +1082,51 @@ const createReleaseActionService = ({ strapi: strapi2 }) => {
1110
1082
  `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
1111
1083
  );
1112
1084
  }
1113
- getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
1085
+ this.updateReleaseStatus(releaseId);
1114
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
+ });
1115
1130
  }
1116
1131
  };
1117
1132
  };
@@ -1123,43 +1138,37 @@ class AlreadyOnReleaseError extends utils.errors.ApplicationError {
1123
1138
  }
1124
1139
  const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1125
1140
  async validateUniqueEntry(releaseId, releaseActionArgs) {
1126
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
1127
- where: {
1128
- id: releaseId
1129
- },
1130
- populate: {
1131
- actions: true
1132
- }
1141
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
1142
+ populate: { actions: { populate: { entry: { fields: ["id"] } } } }
1133
1143
  });
1134
1144
  if (!release2) {
1135
1145
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
1136
1146
  }
1137
1147
  const isEntryInRelease = release2.actions.some(
1138
- (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
1139
1149
  );
1140
1150
  if (isEntryInRelease) {
1141
1151
  throw new AlreadyOnReleaseError(
1142
- `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}`
1143
1153
  );
1144
1154
  }
1145
1155
  },
1146
- validateEntryData(contentTypeUid, entryDocumentId) {
1156
+ validateEntryContentType(contentTypeUid) {
1147
1157
  const contentType = strapi2.contentType(contentTypeUid);
1148
1158
  if (!contentType) {
1149
1159
  throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
1150
1160
  }
1151
- if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
1161
+ if (!contentType.options?.draftAndPublish) {
1152
1162
  throw new utils.errors.ValidationError(
1153
1163
  `Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
1154
1164
  );
1155
1165
  }
1156
- if (contentType.kind === "collectionType" && !entryDocumentId) {
1157
- throw new utils.errors.ValidationError("Document id is required for collection type");
1158
- }
1159
1166
  },
1160
1167
  async validatePendingReleasesLimit() {
1161
- const featureCfg = strapi2.ee.features.get("cms-content-releases");
1162
- 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
+ );
1163
1172
  const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
1164
1173
  filters: {
1165
1174
  releasedAt: {
@@ -1172,8 +1181,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1172
1181
  }
1173
1182
  },
1174
1183
  async validateUniqueNameForPendingRelease(name, id) {
1175
- const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
1176
- where: {
1184
+ const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
1185
+ filters: {
1177
1186
  releasedAt: {
1178
1187
  $null: true
1179
1188
  },
@@ -1202,7 +1211,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1202
1211
  }
1203
1212
  const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
1204
1213
  try {
1205
- await getService("release", { strapi: strapi2 }).publish(releaseId);
1214
+ await getService("release").publish(releaseId);
1206
1215
  } catch (error) {
1207
1216
  }
1208
1217
  this.cancel(releaseId);
@@ -1244,159 +1253,85 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1244
1253
  }
1245
1254
  };
1246
1255
  };
1247
- const DEFAULT_SETTINGS = {
1248
- defaultTimezone: null
1249
- };
1250
- const createSettingsService = ({ strapi: strapi2 }) => {
1251
- const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
1252
- return {
1253
- async update({ settings: settings2 }) {
1254
- const store = await getStore();
1255
- store.set({ key: "settings", value: settings2 });
1256
- return settings2;
1257
- },
1258
- async find() {
1259
- const store = await getStore();
1260
- const settings2 = await store.get({ key: "settings" });
1261
- return {
1262
- ...DEFAULT_SETTINGS,
1263
- ...settings2 || {}
1264
- };
1265
- }
1266
- };
1267
- };
1268
1256
  const services = {
1269
1257
  release: createReleaseService,
1270
- "release-action": createReleaseActionService,
1271
1258
  "release-validation": createReleaseValidationService,
1272
- scheduling: createSchedulingService,
1273
- settings: createSettingsService
1259
+ scheduling: createSchedulingService
1274
1260
  };
1275
- const RELEASE_SCHEMA = utils.yup.object().shape({
1276
- name: utils.yup.string().trim().required(),
1277
- scheduledAt: utils.yup.string().nullable(),
1278
- timezone: utils.yup.string().when("scheduledAt", {
1279
- is: (value) => value !== null && value !== void 0,
1280
- then: utils.yup.string().required(),
1281
- 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()
1282
1279
  })
1283
1280
  }).required().noUnknown();
1284
- const FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA = utils.yup.object().shape({
1285
- contentType: utils.yup.string().required(),
1286
- entryDocumentId: utils.yup.string().nullable(),
1287
- hasEntryAttached: utils.yup.string().nullable(),
1288
- locale: utils.yup.string().nullable()
1289
- }).required().noUnknown();
1290
1281
  const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
1291
- const validatefindByDocumentAttachedParams = utils.validateYupSchema(
1292
- FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
1293
- );
1294
1282
  const releaseController = {
1295
- /**
1296
- * Find releases based on documents attached or not to the release.
1297
- * If `hasEntryAttached` is true, it will return all releases that have the entry attached.
1298
- * If `hasEntryAttached` is false, it will return all releases that don't have the entry attached.
1299
- */
1300
- async findByDocumentAttached(ctx) {
1301
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1283
+ async findMany(ctx) {
1284
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1302
1285
  ability: ctx.state.userAbility,
1303
1286
  model: RELEASE_MODEL_UID
1304
1287
  });
1305
1288
  await permissionsManager.validateQuery(ctx.query);
1306
1289
  const releaseService = getService("release", { strapi });
1307
- const query = await permissionsManager.sanitizeQuery(ctx.query);
1308
- await validatefindByDocumentAttachedParams(query);
1309
- const { contentType, entryDocumentId, hasEntryAttached, locale } = query;
1310
- const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
1311
- if (isEntryAttached) {
1312
- const releases = await releaseService.findMany({
1313
- where: {
1314
- releasedAt: null,
1315
- actions: {
1316
- contentType,
1317
- entryDocumentId: entryDocumentId ?? null,
1318
- locale: locale ?? null
1319
- }
1320
- },
1321
- populate: {
1322
- actions: {
1323
- fields: ["type"]
1324
- }
1325
- }
1326
- });
1327
- 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 };
1328
1298
  } else {
1329
- const relatedReleases = await releaseService.findMany({
1330
- where: {
1331
- 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,
1332
1305
  actions: {
1333
- contentType,
1334
- entryDocumentId: entryDocumentId ?? null,
1335
- locale: locale ?? null
1306
+ meta: {
1307
+ count: actions.count
1308
+ }
1336
1309
  }
1337
- }
1310
+ };
1338
1311
  });
1339
- const releases = await releaseService.findMany({
1312
+ const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
1340
1313
  where: {
1341
- $or: [
1342
- {
1343
- id: {
1344
- $notIn: relatedReleases.map((release2) => release2.id)
1345
- }
1346
- },
1347
- {
1348
- actions: null
1349
- }
1350
- ],
1351
1314
  releasedAt: null
1352
1315
  }
1353
1316
  });
1354
- ctx.body = { data: releases };
1317
+ ctx.body = { data, meta: { pagination, pendingReleasesCount } };
1355
1318
  }
1356
1319
  },
1357
- async findPage(ctx) {
1358
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1359
- ability: ctx.state.userAbility,
1360
- model: RELEASE_MODEL_UID
1361
- });
1362
- await permissionsManager.validateQuery(ctx.query);
1363
- const releaseService = getService("release", { strapi });
1364
- const query = await permissionsManager.sanitizeQuery(ctx.query);
1365
- const { results, pagination } = await releaseService.findPage(query);
1366
- const data = results.map((release2) => {
1367
- const { actions, ...releaseData } = release2;
1368
- return {
1369
- ...releaseData,
1370
- actions: {
1371
- meta: {
1372
- count: actions.count
1373
- }
1374
- }
1375
- };
1376
- });
1377
- const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
1378
- where: {
1379
- releasedAt: null
1380
- }
1381
- });
1382
- ctx.body = { data, meta: { pagination, pendingReleasesCount } };
1383
- },
1384
1320
  async findOne(ctx) {
1385
1321
  const id = ctx.params.id;
1386
1322
  const releaseService = getService("release", { strapi });
1387
- const releaseActionService = getService("release-action", { strapi });
1388
1323
  const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
1389
1324
  if (!release2) {
1390
1325
  throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
1391
1326
  }
1392
- const count = await releaseActionService.countActions({
1327
+ const count = await releaseService.countActions({
1393
1328
  filters: {
1394
1329
  release: id
1395
1330
  }
1396
1331
  });
1397
1332
  const sanitizedRelease = {
1398
1333
  ...release2,
1399
- createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
1334
+ createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
1400
1335
  };
1401
1336
  const data = {
1402
1337
  ...sanitizedRelease,
@@ -1409,39 +1344,22 @@ const releaseController = {
1409
1344
  ctx.body = { data };
1410
1345
  },
1411
1346
  async mapEntriesToReleases(ctx) {
1412
- const { contentTypeUid, documentIds, locale } = ctx.query;
1413
- if (!contentTypeUid || !documentIds) {
1347
+ const { contentTypeUid, entriesIds } = ctx.query;
1348
+ if (!contentTypeUid || !entriesIds) {
1414
1349
  throw new utils.errors.ValidationError("Missing required query parameters");
1415
1350
  }
1416
1351
  const releaseService = getService("release", { strapi });
1417
- const releasesWithActions = await releaseService.findMany({
1418
- where: {
1419
- releasedAt: null,
1420
- actions: {
1421
- contentType: contentTypeUid,
1422
- entryDocumentId: {
1423
- $in: documentIds
1424
- },
1425
- locale
1426
- }
1427
- },
1428
- populate: {
1429
- actions: true
1430
- }
1431
- });
1352
+ const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
1353
+ contentTypeUid,
1354
+ entriesIds
1355
+ );
1432
1356
  const mappedEntriesInReleases = releasesWithActions.reduce(
1433
1357
  (acc, release2) => {
1434
1358
  release2.actions.forEach((action) => {
1435
- if (action.contentType !== contentTypeUid) {
1436
- return;
1437
- }
1438
- if (locale && action.locale !== locale) {
1439
- return;
1440
- }
1441
- if (!acc[action.entryDocumentId]) {
1442
- 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 }];
1443
1361
  } else {
1444
- acc[action.entryDocumentId].push({ id: release2.id, name: release2.name });
1362
+ acc[action.entry.id].push({ id: release2.id, name: release2.name });
1445
1363
  }
1446
1364
  });
1447
1365
  return acc;
@@ -1458,13 +1376,13 @@ const releaseController = {
1458
1376
  await validateRelease(releaseArgs);
1459
1377
  const releaseService = getService("release", { strapi });
1460
1378
  const release2 = await releaseService.create(releaseArgs, { user });
1461
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1379
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1462
1380
  ability: ctx.state.userAbility,
1463
1381
  model: RELEASE_MODEL_UID
1464
1382
  });
1465
- ctx.created({
1383
+ ctx.body = {
1466
1384
  data: await permissionsManager.sanitizeOutput(release2)
1467
- });
1385
+ };
1468
1386
  },
1469
1387
  async update(ctx) {
1470
1388
  const user = ctx.state.user;
@@ -1473,7 +1391,7 @@ const releaseController = {
1473
1391
  await validateRelease(releaseArgs);
1474
1392
  const releaseService = getService("release", { strapi });
1475
1393
  const release2 = await releaseService.update(id, releaseArgs, { user });
1476
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1394
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1477
1395
  ability: ctx.state.userAbility,
1478
1396
  model: RELEASE_MODEL_UID
1479
1397
  });
@@ -1490,18 +1408,18 @@ const releaseController = {
1490
1408
  };
1491
1409
  },
1492
1410
  async publish(ctx) {
1411
+ const user = ctx.state.user;
1493
1412
  const id = ctx.params.id;
1494
1413
  const releaseService = getService("release", { strapi });
1495
- const releaseActionService = getService("release-action", { strapi });
1496
- const release2 = await releaseService.publish(id);
1414
+ const release2 = await releaseService.publish(id, { user });
1497
1415
  const [countPublishActions, countUnpublishActions] = await Promise.all([
1498
- releaseActionService.countActions({
1416
+ releaseService.countActions({
1499
1417
  filters: {
1500
1418
  release: id,
1501
1419
  type: "publish"
1502
1420
  }
1503
1421
  }),
1504
- releaseActionService.countActions({
1422
+ releaseService.countActions({
1505
1423
  filters: {
1506
1424
  release: id,
1507
1425
  type: "unpublish"
@@ -1519,30 +1437,27 @@ const releaseController = {
1519
1437
  }
1520
1438
  };
1521
1439
  const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
1522
- contentType: utils.yup.string().required(),
1523
- entryDocumentId: utils.yup.strapiID(),
1524
- locale: utils.yup.string(),
1440
+ entry: utils.yup.object().shape({
1441
+ id: utils.yup.strapiID().required(),
1442
+ contentType: utils.yup.string().required()
1443
+ }).required(),
1525
1444
  type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
1526
1445
  });
1527
1446
  const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
1528
1447
  type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
1529
1448
  });
1530
- const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
1531
- groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
1532
- });
1533
1449
  const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
1534
1450
  const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
1535
- const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
1536
1451
  const releaseActionController = {
1537
1452
  async create(ctx) {
1538
1453
  const releaseId = ctx.params.releaseId;
1539
1454
  const releaseActionArgs = ctx.request.body;
1540
1455
  await validateReleaseAction(releaseActionArgs);
1541
- const releaseActionService = getService("release-action", { strapi });
1542
- const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
1543
- ctx.created({
1456
+ const releaseService = getService("release", { strapi });
1457
+ const releaseAction2 = await releaseService.createAction(releaseId, releaseActionArgs);
1458
+ ctx.body = {
1544
1459
  data: releaseAction2
1545
- });
1460
+ };
1546
1461
  },
1547
1462
  async createMany(ctx) {
1548
1463
  const releaseId = ctx.params.releaseId;
@@ -1550,12 +1465,14 @@ const releaseActionController = {
1550
1465
  await Promise.all(
1551
1466
  releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
1552
1467
  );
1553
- const releaseActionService = getService("release-action", { strapi });
1468
+ const releaseService = getService("release", { strapi });
1554
1469
  const releaseActions = await strapi.db.transaction(async () => {
1555
1470
  const releaseActions2 = await Promise.all(
1556
1471
  releaseActionsArgs.map(async (releaseActionArgs) => {
1557
1472
  try {
1558
- const action = await releaseActionService.create(releaseId, releaseActionArgs);
1473
+ const action = await releaseService.createAction(releaseId, releaseActionArgs, {
1474
+ disableUpdateReleaseStatus: true
1475
+ });
1559
1476
  return action;
1560
1477
  } catch (error) {
1561
1478
  if (error instanceof AlreadyOnReleaseError) {
@@ -1568,51 +1485,46 @@ const releaseActionController = {
1568
1485
  return releaseActions2;
1569
1486
  });
1570
1487
  const newReleaseActions = releaseActions.filter((action) => action !== null);
1571
- ctx.created({
1488
+ if (newReleaseActions.length > 0) {
1489
+ releaseService.updateReleaseStatus(releaseId);
1490
+ }
1491
+ ctx.body = {
1572
1492
  data: newReleaseActions,
1573
1493
  meta: {
1574
1494
  entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
1575
1495
  totalEntries: releaseActions.length
1576
1496
  }
1577
- });
1497
+ };
1578
1498
  },
1579
1499
  async findMany(ctx) {
1580
1500
  const releaseId = ctx.params.releaseId;
1581
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1501
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1582
1502
  ability: ctx.state.userAbility,
1583
1503
  model: RELEASE_ACTION_MODEL_UID
1584
1504
  });
1585
- await validateFindManyActionsParams(ctx.query);
1586
- if (ctx.query.groupBy) {
1587
- if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
1588
- ctx.badRequest("Invalid groupBy parameter");
1589
- }
1590
- }
1591
- ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
1592
- delete ctx.query.groupBy;
1593
1505
  const query = await permissionsManager.sanitizeQuery(ctx.query);
1594
- const releaseActionService = getService("release-action", { strapi });
1595
- 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,
1596
1509
  ...query
1597
1510
  });
1598
1511
  const contentTypeOutputSanitizers = results.reduce((acc, action) => {
1599
1512
  if (acc[action.contentType]) {
1600
1513
  return acc;
1601
1514
  }
1602
- const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
1515
+ const contentTypePermissionsManager = strapi.admin.services.permission.createPermissionsManager({
1603
1516
  ability: ctx.state.userAbility,
1604
1517
  model: action.contentType
1605
1518
  });
1606
1519
  acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
1607
1520
  return acc;
1608
1521
  }, {});
1609
- const sanitizedResults = await utils.async.map(results, async (action) => ({
1522
+ const sanitizedResults = await utils.mapAsync(results, async (action) => ({
1610
1523
  ...action,
1611
- entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
1524
+ entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
1612
1525
  }));
1613
- const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
1614
- const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
1615
- const releaseService = getService("release", { strapi });
1526
+ const groupedData = await releaseService.groupActions(sanitizedResults, query.groupBy);
1527
+ const contentTypes2 = releaseService.getContentTypeModelsFromActions(results);
1616
1528
  const components = await releaseService.getAllComponents();
1617
1529
  ctx.body = {
1618
1530
  data: groupedData,
@@ -1628,8 +1540,8 @@ const releaseActionController = {
1628
1540
  const releaseId = ctx.params.releaseId;
1629
1541
  const releaseActionUpdateArgs = ctx.request.body;
1630
1542
  await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
1631
- const releaseActionService = getService("release-action", { strapi });
1632
- const updatedAction = await releaseActionService.update(
1543
+ const releaseService = getService("release", { strapi });
1544
+ const updatedAction = await releaseService.updateAction(
1633
1545
  actionId,
1634
1546
  releaseId,
1635
1547
  releaseActionUpdateArgs
@@ -1641,36 +1553,14 @@ const releaseActionController = {
1641
1553
  async delete(ctx) {
1642
1554
  const actionId = ctx.params.actionId;
1643
1555
  const releaseId = ctx.params.releaseId;
1644
- const releaseActionService = getService("release-action", { strapi });
1645
- const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
1556
+ const releaseService = getService("release", { strapi });
1557
+ const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);
1646
1558
  ctx.body = {
1647
1559
  data: deletedReleaseAction
1648
1560
  };
1649
1561
  }
1650
1562
  };
1651
- const SETTINGS_SCHEMA = yup__namespace.object().shape({
1652
- defaultTimezone: yup__namespace.string().nullable().default(null)
1653
- }).required().noUnknown();
1654
- const validateSettings = utils.validateYupSchema(SETTINGS_SCHEMA);
1655
- const settingsController = {
1656
- async find(ctx) {
1657
- const settingsService = getService("settings", { strapi });
1658
- const settings2 = await settingsService.find();
1659
- ctx.body = { data: settings2 };
1660
- },
1661
- async update(ctx) {
1662
- const settingsBody = ctx.request.body;
1663
- const settings2 = await validateSettings(settingsBody);
1664
- const settingsService = getService("settings", { strapi });
1665
- const updatedSettings = await settingsService.update({ settings: settings2 });
1666
- ctx.body = { data: updatedSettings };
1667
- }
1668
- };
1669
- const controllers = {
1670
- release: releaseController,
1671
- "release-action": releaseActionController,
1672
- settings: settingsController
1673
- };
1563
+ const controllers = { release: releaseController, "release-action": releaseActionController };
1674
1564
  const release = {
1675
1565
  type: "admin",
1676
1566
  routes: [
@@ -1690,22 +1580,6 @@ const release = {
1690
1580
  ]
1691
1581
  }
1692
1582
  },
1693
- {
1694
- method: "GET",
1695
- path: "/getByDocumentAttached",
1696
- handler: "release.findByDocumentAttached",
1697
- config: {
1698
- policies: [
1699
- "admin::isAuthenticatedAdmin",
1700
- {
1701
- name: "admin::hasPermissions",
1702
- config: {
1703
- actions: ["plugin::content-releases.read"]
1704
- }
1705
- }
1706
- ]
1707
- }
1708
- },
1709
1583
  {
1710
1584
  method: "POST",
1711
1585
  path: "/",
@@ -1725,7 +1599,7 @@ const release = {
1725
1599
  {
1726
1600
  method: "GET",
1727
1601
  path: "/",
1728
- handler: "release.findPage",
1602
+ handler: "release.findMany",
1729
1603
  config: {
1730
1604
  policies: [
1731
1605
  "admin::isAuthenticatedAdmin",
@@ -1889,50 +1763,13 @@ const releaseAction = {
1889
1763
  }
1890
1764
  ]
1891
1765
  };
1892
- const settings = {
1893
- type: "admin",
1894
- routes: [
1895
- {
1896
- method: "GET",
1897
- path: "/settings",
1898
- handler: "settings.find",
1899
- config: {
1900
- policies: [
1901
- "admin::isAuthenticatedAdmin",
1902
- {
1903
- name: "admin::hasPermissions",
1904
- config: {
1905
- actions: ["plugin::content-releases.settings.read"]
1906
- }
1907
- }
1908
- ]
1909
- }
1910
- },
1911
- {
1912
- method: "PUT",
1913
- path: "/settings",
1914
- handler: "settings.update",
1915
- config: {
1916
- policies: [
1917
- "admin::isAuthenticatedAdmin",
1918
- {
1919
- name: "admin::hasPermissions",
1920
- config: {
1921
- actions: ["plugin::content-releases.settings.update"]
1922
- }
1923
- }
1924
- ]
1925
- }
1926
- }
1927
- ]
1928
- };
1929
1766
  const routes = {
1930
- settings,
1931
1767
  release,
1932
1768
  "release-action": releaseAction
1933
1769
  };
1770
+ const { features } = require("@strapi/strapi/dist/utils/ee");
1934
1771
  const getPlugin = () => {
1935
- if (strapi.ee.features.isEnabled("cms-content-releases")) {
1772
+ if (features.isEnabled("cms-content-releases")) {
1936
1773
  return {
1937
1774
  register,
1938
1775
  bootstrap,