@strapi/content-releases 0.0.0-experimental.e02b4637b3906c6d31048d00600d09a23a0edc3d → 0.0.0-experimental.e033e9b9c89837331a60b1b6a2c21a779fffc801

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-BA2xDdy0.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 -830
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/server/index.mjs +638 -831
  24. package/dist/server/index.mjs.map +1 -1
  25. package/package.json +37 -31
  26. package/strapi-server.js +3 -0
  27. package/dist/_chunks/App-BA2xDdy0.mjs.map +0 -1
  28. package/dist/_chunks/App-D4Wira1X.js +0 -1395
  29. package/dist/_chunks/App-D4Wira1X.js.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-BAlbMWpw.mjs +0 -178
  33. package/dist/_chunks/ReleasesSettingsPage-BAlbMWpw.mjs.map +0 -1
  34. package/dist/_chunks/ReleasesSettingsPage-xhFyRXCM.js +0 -178
  35. package/dist/_chunks/ReleasesSettingsPage-xhFyRXCM.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-CCFFG3Zs.mjs +0 -1365
  39. package/dist/_chunks/index-CCFFG3Zs.mjs.map +0 -1
  40. package/dist/_chunks/index-DxkQGp4N.js +0 -1384
  41. package/dist/_chunks/index-DxkQGp4N.js.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,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
@@ -840,226 +1040,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
840
1040
  };
841
1041
  }
842
1042
  });
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, { disableUpdateReleaseStatus = false } = {}) {
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 model = strapi2.contentType(action.contentType);
941
- if (model.kind === "singleType") {
942
- const document = await strapi2.db.query(model.uid).findOne({ select: ["documentId"] });
943
- if (!document) {
944
- throw new utils.errors.NotFoundError(`No entry found for contentType ${action.contentType}`);
945
- }
946
- action.entryDocumentId = document.documentId;
947
- }
948
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
949
- if (!release2) {
950
- throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
951
- }
952
- if (release2.releasedAt) {
953
- throw new utils.errors.ValidationError("Release already published");
954
- }
955
- const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
956
- {
957
- contentType: action.contentType,
958
- documentId: action.entryDocumentId,
959
- locale: action.locale
960
- },
961
- {
962
- strapi: strapi2
963
- }
964
- ) : true;
965
- const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
966
- data: {
967
- ...action,
968
- release: release2.id,
969
- isEntryValid: actionStatus
970
- },
971
- populate: { release: { select: ["id"] } }
972
- });
973
- if (!disableUpdateReleaseStatus) {
974
- getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
975
- }
976
- return releaseAction2;
977
- },
978
- async findPage(releaseId, query) {
979
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
980
- where: { id: releaseId },
981
- select: ["id"]
982
- });
983
- if (!release2) {
984
- throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
1043
+ if (error) {
1044
+ throw error;
985
1045
  }
986
- const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
987
- const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
988
- ...dbQuery,
989
- where: {
990
- release: releaseId
991
- }
992
- });
993
- const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
994
- const actionsWithEntry = await utils.async.map(actions, async (action) => {
995
- const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
996
- const entry = await getEntry(
997
- {
998
- contentType: action.contentType,
999
- documentId: action.entryDocumentId,
1000
- locale: action.locale,
1001
- populate,
1002
- status: action.type === "publish" ? "draft" : "published"
1003
- },
1004
- { strapi: strapi2 }
1005
- );
1006
- return {
1007
- ...action,
1008
- entry,
1009
- status: entry ? await getEntryStatus(action.contentType, entry) : null
1010
- };
1011
- });
1012
- return {
1013
- results: actionsWithEntry,
1014
- pagination
1015
- };
1016
- },
1017
- async groupActions(actions, groupBy) {
1018
- const contentTypeUids = actions.reduce((acc, action) => {
1019
- if (!acc.includes(action.contentType)) {
1020
- acc.push(action.contentType);
1021
- }
1022
- return acc;
1023
- }, []);
1024
- const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
1025
- const allLocalesDictionary = await getLocalesDataForActions();
1026
- const formattedData = actions.map((action) => {
1027
- const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
1028
- return {
1029
- ...action,
1030
- locale: action.locale ? allLocalesDictionary[action.locale] : null,
1031
- contentType: {
1032
- displayName,
1033
- mainFieldValue: action.entry[mainField],
1034
- uid: action.contentType
1035
- }
1036
- };
1037
- });
1038
- const groupName = getGroupName(groupBy);
1039
- return ___default.default.groupBy(groupName)(formattedData);
1040
- },
1041
- getContentTypeModelsFromActions(actions) {
1042
- const contentTypeUids = actions.reduce((acc, action) => {
1043
- if (!acc.includes(action.contentType)) {
1044
- acc.push(action.contentType);
1045
- }
1046
- return acc;
1047
- }, []);
1048
- const contentTypeModelsMap = contentTypeUids.reduce(
1049
- (acc, contentTypeUid) => {
1050
- acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
1051
- return acc;
1052
- },
1053
- {}
1054
- );
1055
- return contentTypeModelsMap;
1056
- },
1057
- async countActions(query) {
1058
- const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
1059
- return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
1046
+ return release2;
1060
1047
  },
1061
- async update(actionId, releaseId, update) {
1062
- 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({
1063
1050
  where: {
1064
1051
  id: actionId,
1065
1052
  release: {
@@ -1068,42 +1055,17 @@ const createReleaseActionService = ({ strapi: strapi2 }) => {
1068
1055
  $null: true
1069
1056
  }
1070
1057
  }
1071
- }
1058
+ },
1059
+ data: update
1072
1060
  });
1073
- if (!action) {
1061
+ if (!updatedAction) {
1074
1062
  throw new utils.errors.NotFoundError(
1075
1063
  `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
1076
1064
  );
1077
1065
  }
1078
- const actionStatus = update.type === "publish" ? await getDraftEntryValidStatus(
1079
- {
1080
- contentType: action.contentType,
1081
- documentId: action.entryDocumentId,
1082
- locale: action.locale
1083
- },
1084
- {
1085
- strapi: strapi2
1086
- }
1087
- ) : true;
1088
- const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
1089
- where: {
1090
- id: actionId,
1091
- release: {
1092
- id: releaseId,
1093
- releasedAt: {
1094
- $null: true
1095
- }
1096
- }
1097
- },
1098
- data: {
1099
- ...update,
1100
- isEntryValid: actionStatus
1101
- }
1102
- });
1103
- getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
1104
1066
  return updatedAction;
1105
1067
  },
1106
- async delete(actionId, releaseId) {
1068
+ async deleteAction(actionId, releaseId) {
1107
1069
  const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
1108
1070
  where: {
1109
1071
  id: actionId,
@@ -1120,8 +1082,51 @@ const createReleaseActionService = ({ strapi: strapi2 }) => {
1120
1082
  `Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
1121
1083
  );
1122
1084
  }
1123
- getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
1085
+ this.updateReleaseStatus(releaseId);
1124
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
+ });
1125
1130
  }
1126
1131
  };
1127
1132
  };
@@ -1133,43 +1138,37 @@ class AlreadyOnReleaseError extends utils.errors.ApplicationError {
1133
1138
  }
1134
1139
  const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1135
1140
  async validateUniqueEntry(releaseId, releaseActionArgs) {
1136
- const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
1137
- where: {
1138
- id: releaseId
1139
- },
1140
- populate: {
1141
- actions: true
1142
- }
1141
+ const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
1142
+ populate: { actions: { populate: { entry: { fields: ["id"] } } } }
1143
1143
  });
1144
1144
  if (!release2) {
1145
1145
  throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
1146
1146
  }
1147
1147
  const isEntryInRelease = release2.actions.some(
1148
- (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
1149
1149
  );
1150
1150
  if (isEntryInRelease) {
1151
1151
  throw new AlreadyOnReleaseError(
1152
- `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}`
1153
1153
  );
1154
1154
  }
1155
1155
  },
1156
- validateEntryData(contentTypeUid, entryDocumentId) {
1156
+ validateEntryContentType(contentTypeUid) {
1157
1157
  const contentType = strapi2.contentType(contentTypeUid);
1158
1158
  if (!contentType) {
1159
1159
  throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
1160
1160
  }
1161
- if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
1161
+ if (!contentType.options?.draftAndPublish) {
1162
1162
  throw new utils.errors.ValidationError(
1163
1163
  `Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
1164
1164
  );
1165
1165
  }
1166
- if (contentType.kind === "collectionType" && !entryDocumentId) {
1167
- throw new utils.errors.ValidationError("Document id is required for collection type");
1168
- }
1169
1166
  },
1170
1167
  async validatePendingReleasesLimit() {
1171
- const featureCfg = strapi2.ee.features.get("cms-content-releases");
1172
- 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
+ );
1173
1172
  const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
1174
1173
  filters: {
1175
1174
  releasedAt: {
@@ -1182,8 +1181,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
1182
1181
  }
1183
1182
  },
1184
1183
  async validateUniqueNameForPendingRelease(name, id) {
1185
- const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
1186
- where: {
1184
+ const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
1185
+ filters: {
1187
1186
  releasedAt: {
1188
1187
  $null: true
1189
1188
  },
@@ -1212,7 +1211,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1212
1211
  }
1213
1212
  const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
1214
1213
  try {
1215
- await getService("release", { strapi: strapi2 }).publish(releaseId);
1214
+ await getService("release").publish(releaseId);
1216
1215
  } catch (error) {
1217
1216
  }
1218
1217
  this.cancel(releaseId);
@@ -1254,172 +1253,85 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
1254
1253
  }
1255
1254
  };
1256
1255
  };
1257
- const DEFAULT_SETTINGS = {
1258
- defaultTimezone: null
1259
- };
1260
- const createSettingsService = ({ strapi: strapi2 }) => {
1261
- const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
1262
- return {
1263
- async update({ settings: settings2 }) {
1264
- const store = await getStore();
1265
- store.set({ key: "settings", value: settings2 });
1266
- return settings2;
1267
- },
1268
- async find() {
1269
- const store = await getStore();
1270
- const settings2 = await store.get({ key: "settings" });
1271
- return {
1272
- ...DEFAULT_SETTINGS,
1273
- ...settings2 || {}
1274
- };
1275
- }
1276
- };
1277
- };
1278
1256
  const services = {
1279
1257
  release: createReleaseService,
1280
- "release-action": createReleaseActionService,
1281
1258
  "release-validation": createReleaseValidationService,
1282
- scheduling: createSchedulingService,
1283
- settings: createSettingsService
1259
+ scheduling: createSchedulingService
1284
1260
  };
1285
- const RELEASE_SCHEMA = utils.yup.object().shape({
1286
- name: utils.yup.string().trim().required(),
1287
- scheduledAt: utils.yup.string().nullable(),
1288
- timezone: utils.yup.string().when("scheduledAt", {
1289
- is: (value) => value !== null && value !== void 0,
1290
- then: utils.yup.string().required(),
1291
- 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()
1292
1279
  })
1293
1280
  }).required().noUnknown();
1294
- const FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA = utils.yup.object().shape({
1295
- contentType: utils.yup.string().required(),
1296
- entryDocumentId: utils.yup.string().nullable(),
1297
- hasEntryAttached: utils.yup.string().nullable(),
1298
- locale: utils.yup.string().nullable()
1299
- }).required().noUnknown();
1300
1281
  const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
1301
- const validatefindByDocumentAttachedParams = utils.validateYupSchema(
1302
- FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
1303
- );
1304
1282
  const releaseController = {
1305
- /**
1306
- * Find releases based on documents attached or not to the release.
1307
- * If `hasEntryAttached` is true, it will return all releases that have the entry attached.
1308
- * If `hasEntryAttached` is false, it will return all releases that don't have the entry attached.
1309
- */
1310
- async findByDocumentAttached(ctx) {
1311
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1283
+ async findMany(ctx) {
1284
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1312
1285
  ability: ctx.state.userAbility,
1313
1286
  model: RELEASE_MODEL_UID
1314
1287
  });
1315
1288
  await permissionsManager.validateQuery(ctx.query);
1316
1289
  const releaseService = getService("release", { strapi });
1317
- const query = await permissionsManager.sanitizeQuery(ctx.query);
1318
- await validatefindByDocumentAttachedParams(query);
1319
- const model = strapi.getModel(query.contentType);
1320
- if (model.kind && model.kind === "singleType") {
1321
- const document = await strapi.db.query(model.uid).findOne({ select: ["documentId"] });
1322
- if (!document) {
1323
- throw new utils.errors.NotFoundError(`No entry found for contentType ${query.contentType}`);
1324
- }
1325
- query.entryDocumentId = document.documentId;
1326
- }
1327
- const { contentType, hasEntryAttached, entryDocumentId, locale } = query;
1328
- const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
1329
- if (isEntryAttached) {
1330
- const releases = await releaseService.findMany({
1331
- where: {
1332
- releasedAt: null,
1333
- actions: {
1334
- contentType,
1335
- entryDocumentId: entryDocumentId ?? null,
1336
- locale: locale ?? null
1337
- }
1338
- },
1339
- populate: {
1340
- actions: {
1341
- fields: ["type"],
1342
- filters: {
1343
- contentType,
1344
- entryDocumentId: entryDocumentId ?? null,
1345
- locale: locale ?? null
1346
- }
1347
- }
1348
- }
1349
- });
1350
- 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 };
1351
1298
  } else {
1352
- const relatedReleases = await releaseService.findMany({
1353
- where: {
1354
- 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,
1355
1305
  actions: {
1356
- contentType,
1357
- entryDocumentId: entryDocumentId ?? null,
1358
- locale: locale ?? null
1306
+ meta: {
1307
+ count: actions.count
1308
+ }
1359
1309
  }
1360
- }
1310
+ };
1361
1311
  });
1362
- const releases = await releaseService.findMany({
1312
+ const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
1363
1313
  where: {
1364
- $or: [
1365
- {
1366
- id: {
1367
- $notIn: relatedReleases.map((release2) => release2.id)
1368
- }
1369
- },
1370
- {
1371
- actions: null
1372
- }
1373
- ],
1374
1314
  releasedAt: null
1375
1315
  }
1376
1316
  });
1377
- ctx.body = { data: releases };
1317
+ ctx.body = { data, meta: { pagination, pendingReleasesCount } };
1378
1318
  }
1379
1319
  },
1380
- async findPage(ctx) {
1381
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1382
- ability: ctx.state.userAbility,
1383
- model: RELEASE_MODEL_UID
1384
- });
1385
- await permissionsManager.validateQuery(ctx.query);
1386
- const releaseService = getService("release", { strapi });
1387
- const query = await permissionsManager.sanitizeQuery(ctx.query);
1388
- const { results, pagination } = await releaseService.findPage(query);
1389
- const data = results.map((release2) => {
1390
- const { actions, ...releaseData } = release2;
1391
- return {
1392
- ...releaseData,
1393
- actions: {
1394
- meta: {
1395
- count: actions.count
1396
- }
1397
- }
1398
- };
1399
- });
1400
- const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
1401
- where: {
1402
- releasedAt: null
1403
- }
1404
- });
1405
- ctx.body = { data, meta: { pagination, pendingReleasesCount } };
1406
- },
1407
1320
  async findOne(ctx) {
1408
1321
  const id = ctx.params.id;
1409
1322
  const releaseService = getService("release", { strapi });
1410
- const releaseActionService = getService("release-action", { strapi });
1411
1323
  const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
1412
1324
  if (!release2) {
1413
1325
  throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
1414
1326
  }
1415
- const count = await releaseActionService.countActions({
1327
+ const count = await releaseService.countActions({
1416
1328
  filters: {
1417
1329
  release: id
1418
1330
  }
1419
1331
  });
1420
1332
  const sanitizedRelease = {
1421
1333
  ...release2,
1422
- createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
1334
+ createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
1423
1335
  };
1424
1336
  const data = {
1425
1337
  ...sanitizedRelease,
@@ -1432,39 +1344,22 @@ const releaseController = {
1432
1344
  ctx.body = { data };
1433
1345
  },
1434
1346
  async mapEntriesToReleases(ctx) {
1435
- const { contentTypeUid, documentIds, locale } = ctx.query;
1436
- if (!contentTypeUid || !documentIds) {
1347
+ const { contentTypeUid, entriesIds } = ctx.query;
1348
+ if (!contentTypeUid || !entriesIds) {
1437
1349
  throw new utils.errors.ValidationError("Missing required query parameters");
1438
1350
  }
1439
1351
  const releaseService = getService("release", { strapi });
1440
- const releasesWithActions = await releaseService.findMany({
1441
- where: {
1442
- releasedAt: null,
1443
- actions: {
1444
- contentType: contentTypeUid,
1445
- entryDocumentId: {
1446
- $in: documentIds
1447
- },
1448
- locale
1449
- }
1450
- },
1451
- populate: {
1452
- actions: true
1453
- }
1454
- });
1352
+ const releasesWithActions = await releaseService.findManyWithContentTypeEntryAttached(
1353
+ contentTypeUid,
1354
+ entriesIds
1355
+ );
1455
1356
  const mappedEntriesInReleases = releasesWithActions.reduce(
1456
1357
  (acc, release2) => {
1457
1358
  release2.actions.forEach((action) => {
1458
- if (action.contentType !== contentTypeUid) {
1459
- return;
1460
- }
1461
- if (locale && action.locale !== locale) {
1462
- return;
1463
- }
1464
- if (!acc[action.entryDocumentId]) {
1465
- 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 }];
1466
1361
  } else {
1467
- acc[action.entryDocumentId].push({ id: release2.id, name: release2.name });
1362
+ acc[action.entry.id].push({ id: release2.id, name: release2.name });
1468
1363
  }
1469
1364
  });
1470
1365
  return acc;
@@ -1481,13 +1376,13 @@ const releaseController = {
1481
1376
  await validateRelease(releaseArgs);
1482
1377
  const releaseService = getService("release", { strapi });
1483
1378
  const release2 = await releaseService.create(releaseArgs, { user });
1484
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1379
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1485
1380
  ability: ctx.state.userAbility,
1486
1381
  model: RELEASE_MODEL_UID
1487
1382
  });
1488
- ctx.created({
1383
+ ctx.body = {
1489
1384
  data: await permissionsManager.sanitizeOutput(release2)
1490
- });
1385
+ };
1491
1386
  },
1492
1387
  async update(ctx) {
1493
1388
  const user = ctx.state.user;
@@ -1496,7 +1391,7 @@ const releaseController = {
1496
1391
  await validateRelease(releaseArgs);
1497
1392
  const releaseService = getService("release", { strapi });
1498
1393
  const release2 = await releaseService.update(id, releaseArgs, { user });
1499
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1394
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1500
1395
  ability: ctx.state.userAbility,
1501
1396
  model: RELEASE_MODEL_UID
1502
1397
  });
@@ -1513,18 +1408,18 @@ const releaseController = {
1513
1408
  };
1514
1409
  },
1515
1410
  async publish(ctx) {
1411
+ const user = ctx.state.user;
1516
1412
  const id = ctx.params.id;
1517
1413
  const releaseService = getService("release", { strapi });
1518
- const releaseActionService = getService("release-action", { strapi });
1519
- const release2 = await releaseService.publish(id);
1414
+ const release2 = await releaseService.publish(id, { user });
1520
1415
  const [countPublishActions, countUnpublishActions] = await Promise.all([
1521
- releaseActionService.countActions({
1416
+ releaseService.countActions({
1522
1417
  filters: {
1523
1418
  release: id,
1524
1419
  type: "publish"
1525
1420
  }
1526
1421
  }),
1527
- releaseActionService.countActions({
1422
+ releaseService.countActions({
1528
1423
  filters: {
1529
1424
  release: id,
1530
1425
  type: "unpublish"
@@ -1542,30 +1437,27 @@ const releaseController = {
1542
1437
  }
1543
1438
  };
1544
1439
  const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
1545
- contentType: utils.yup.string().required(),
1546
- entryDocumentId: utils.yup.strapiID(),
1547
- locale: utils.yup.string(),
1440
+ entry: utils.yup.object().shape({
1441
+ id: utils.yup.strapiID().required(),
1442
+ contentType: utils.yup.string().required()
1443
+ }).required(),
1548
1444
  type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
1549
1445
  });
1550
1446
  const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
1551
1447
  type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
1552
1448
  });
1553
- const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
1554
- groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
1555
- });
1556
1449
  const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
1557
1450
  const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
1558
- const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
1559
1451
  const releaseActionController = {
1560
1452
  async create(ctx) {
1561
1453
  const releaseId = ctx.params.releaseId;
1562
1454
  const releaseActionArgs = ctx.request.body;
1563
1455
  await validateReleaseAction(releaseActionArgs);
1564
- const releaseActionService = getService("release-action", { strapi });
1565
- const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
1566
- ctx.created({
1456
+ const releaseService = getService("release", { strapi });
1457
+ const releaseAction2 = await releaseService.createAction(releaseId, releaseActionArgs);
1458
+ ctx.body = {
1567
1459
  data: releaseAction2
1568
- });
1460
+ };
1569
1461
  },
1570
1462
  async createMany(ctx) {
1571
1463
  const releaseId = ctx.params.releaseId;
@@ -1573,13 +1465,12 @@ const releaseActionController = {
1573
1465
  await Promise.all(
1574
1466
  releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
1575
1467
  );
1576
- const releaseActionService = getService("release-action", { strapi });
1577
1468
  const releaseService = getService("release", { strapi });
1578
1469
  const releaseActions = await strapi.db.transaction(async () => {
1579
1470
  const releaseActions2 = await Promise.all(
1580
1471
  releaseActionsArgs.map(async (releaseActionArgs) => {
1581
1472
  try {
1582
- const action = await releaseActionService.create(releaseId, releaseActionArgs, {
1473
+ const action = await releaseService.createAction(releaseId, releaseActionArgs, {
1583
1474
  disableUpdateReleaseStatus: true
1584
1475
  });
1585
1476
  return action;
@@ -1597,51 +1488,43 @@ const releaseActionController = {
1597
1488
  if (newReleaseActions.length > 0) {
1598
1489
  releaseService.updateReleaseStatus(releaseId);
1599
1490
  }
1600
- ctx.created({
1491
+ ctx.body = {
1601
1492
  data: newReleaseActions,
1602
1493
  meta: {
1603
1494
  entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
1604
1495
  totalEntries: releaseActions.length
1605
1496
  }
1606
- });
1497
+ };
1607
1498
  },
1608
1499
  async findMany(ctx) {
1609
1500
  const releaseId = ctx.params.releaseId;
1610
- const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
1501
+ const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
1611
1502
  ability: ctx.state.userAbility,
1612
1503
  model: RELEASE_ACTION_MODEL_UID
1613
1504
  });
1614
- await validateFindManyActionsParams(ctx.query);
1615
- if (ctx.query.groupBy) {
1616
- if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
1617
- ctx.badRequest("Invalid groupBy parameter");
1618
- }
1619
- }
1620
- ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
1621
- delete ctx.query.groupBy;
1622
1505
  const query = await permissionsManager.sanitizeQuery(ctx.query);
1623
- const releaseActionService = getService("release-action", { strapi });
1624
- 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,
1625
1509
  ...query
1626
1510
  });
1627
1511
  const contentTypeOutputSanitizers = results.reduce((acc, action) => {
1628
1512
  if (acc[action.contentType]) {
1629
1513
  return acc;
1630
1514
  }
1631
- const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
1515
+ const contentTypePermissionsManager = strapi.admin.services.permission.createPermissionsManager({
1632
1516
  ability: ctx.state.userAbility,
1633
1517
  model: action.contentType
1634
1518
  });
1635
1519
  acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
1636
1520
  return acc;
1637
1521
  }, {});
1638
- const sanitizedResults = await utils.async.map(results, async (action) => ({
1522
+ const sanitizedResults = await utils.mapAsync(results, async (action) => ({
1639
1523
  ...action,
1640
- entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
1524
+ entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
1641
1525
  }));
1642
- const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
1643
- const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
1644
- const releaseService = getService("release", { strapi });
1526
+ const groupedData = await releaseService.groupActions(sanitizedResults, query.groupBy);
1527
+ const contentTypes2 = releaseService.getContentTypeModelsFromActions(results);
1645
1528
  const components = await releaseService.getAllComponents();
1646
1529
  ctx.body = {
1647
1530
  data: groupedData,
@@ -1657,8 +1540,8 @@ const releaseActionController = {
1657
1540
  const releaseId = ctx.params.releaseId;
1658
1541
  const releaseActionUpdateArgs = ctx.request.body;
1659
1542
  await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
1660
- const releaseActionService = getService("release-action", { strapi });
1661
- const updatedAction = await releaseActionService.update(
1543
+ const releaseService = getService("release", { strapi });
1544
+ const updatedAction = await releaseService.updateAction(
1662
1545
  actionId,
1663
1546
  releaseId,
1664
1547
  releaseActionUpdateArgs
@@ -1670,36 +1553,14 @@ const releaseActionController = {
1670
1553
  async delete(ctx) {
1671
1554
  const actionId = ctx.params.actionId;
1672
1555
  const releaseId = ctx.params.releaseId;
1673
- const releaseActionService = getService("release-action", { strapi });
1674
- const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
1556
+ const releaseService = getService("release", { strapi });
1557
+ const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);
1675
1558
  ctx.body = {
1676
1559
  data: deletedReleaseAction
1677
1560
  };
1678
1561
  }
1679
1562
  };
1680
- const SETTINGS_SCHEMA = yup__namespace.object().shape({
1681
- defaultTimezone: yup__namespace.string().nullable().default(null)
1682
- }).required().noUnknown();
1683
- const validateSettings = utils.validateYupSchema(SETTINGS_SCHEMA);
1684
- const settingsController = {
1685
- async find(ctx) {
1686
- const settingsService = getService("settings", { strapi });
1687
- const settings2 = await settingsService.find();
1688
- ctx.body = { data: settings2 };
1689
- },
1690
- async update(ctx) {
1691
- const settingsBody = ctx.request.body;
1692
- const settings2 = await validateSettings(settingsBody);
1693
- const settingsService = getService("settings", { strapi });
1694
- const updatedSettings = await settingsService.update({ settings: settings2 });
1695
- ctx.body = { data: updatedSettings };
1696
- }
1697
- };
1698
- const controllers = {
1699
- release: releaseController,
1700
- "release-action": releaseActionController,
1701
- settings: settingsController
1702
- };
1563
+ const controllers = { release: releaseController, "release-action": releaseActionController };
1703
1564
  const release = {
1704
1565
  type: "admin",
1705
1566
  routes: [
@@ -1719,22 +1580,6 @@ const release = {
1719
1580
  ]
1720
1581
  }
1721
1582
  },
1722
- {
1723
- method: "GET",
1724
- path: "/getByDocumentAttached",
1725
- handler: "release.findByDocumentAttached",
1726
- config: {
1727
- policies: [
1728
- "admin::isAuthenticatedAdmin",
1729
- {
1730
- name: "admin::hasPermissions",
1731
- config: {
1732
- actions: ["plugin::content-releases.read"]
1733
- }
1734
- }
1735
- ]
1736
- }
1737
- },
1738
1583
  {
1739
1584
  method: "POST",
1740
1585
  path: "/",
@@ -1754,7 +1599,7 @@ const release = {
1754
1599
  {
1755
1600
  method: "GET",
1756
1601
  path: "/",
1757
- handler: "release.findPage",
1602
+ handler: "release.findMany",
1758
1603
  config: {
1759
1604
  policies: [
1760
1605
  "admin::isAuthenticatedAdmin",
@@ -1918,50 +1763,13 @@ const releaseAction = {
1918
1763
  }
1919
1764
  ]
1920
1765
  };
1921
- const settings = {
1922
- type: "admin",
1923
- routes: [
1924
- {
1925
- method: "GET",
1926
- path: "/settings",
1927
- handler: "settings.find",
1928
- config: {
1929
- policies: [
1930
- "admin::isAuthenticatedAdmin",
1931
- {
1932
- name: "admin::hasPermissions",
1933
- config: {
1934
- actions: ["plugin::content-releases.settings.read"]
1935
- }
1936
- }
1937
- ]
1938
- }
1939
- },
1940
- {
1941
- method: "PUT",
1942
- path: "/settings",
1943
- handler: "settings.update",
1944
- config: {
1945
- policies: [
1946
- "admin::isAuthenticatedAdmin",
1947
- {
1948
- name: "admin::hasPermissions",
1949
- config: {
1950
- actions: ["plugin::content-releases.settings.update"]
1951
- }
1952
- }
1953
- ]
1954
- }
1955
- }
1956
- ]
1957
- };
1958
1766
  const routes = {
1959
- settings,
1960
1767
  release,
1961
1768
  "release-action": releaseAction
1962
1769
  };
1770
+ const { features } = require("@strapi/strapi/dist/utils/ee");
1963
1771
  const getPlugin = () => {
1964
- if (strapi.ee.features.isEnabled("cms-content-releases")) {
1772
+ if (features.isEnabled("cms-content-releases")) {
1965
1773
  return {
1966
1774
  register,
1967
1775
  bootstrap,