@strapi/content-releases 0.0.0-next.56199ab7a5f3320e0debcbe4a24fe0b8cd599e21 → 0.0.0-next.583e758623dc82206a4b2758d01dd5948b6e3f6a
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.
- package/dist/_chunks/{App-8J9a-MD5.mjs → App-6ugQxqYE.mjs} +758 -452
- package/dist/_chunks/App-6ugQxqYE.mjs.map +1 -0
- package/dist/_chunks/{App-YFvVMqB8.js → App-P1kyM3gT.js} +750 -443
- package/dist/_chunks/App-P1kyM3gT.js.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs +51 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js +51 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +1 -0
- package/dist/_chunks/{en-MyLPoISH.mjs → en-WuuhP6Bn.mjs} +21 -6
- package/dist/_chunks/en-WuuhP6Bn.mjs.map +1 -0
- package/dist/_chunks/{en-gYDqKYFd.js → en-gcJJ5htG.js} +21 -6
- package/dist/_chunks/en-gcJJ5htG.js.map +1 -0
- package/dist/_chunks/{index-vxli-E-l.js → index-2xzbhaQP.js} +159 -33
- package/dist/_chunks/index-2xzbhaQP.js.map +1 -0
- package/dist/_chunks/{index-ej8MzbQl.mjs → index-_eBuegHN.mjs} +171 -45
- package/dist/_chunks/index-_eBuegHN.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +2 -2
- package/dist/server/index.js +928 -402
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +928 -403
- package/dist/server/index.mjs.map +1 -1
- package/package.json +14 -11
- package/dist/_chunks/App-8J9a-MD5.mjs.map +0 -1
- package/dist/_chunks/App-YFvVMqB8.js.map +0 -1
- package/dist/_chunks/en-MyLPoISH.mjs.map +0 -1
- package/dist/_chunks/en-gYDqKYFd.js.map +0 -1
- package/dist/_chunks/index-ej8MzbQl.mjs.map +0 -1
- package/dist/_chunks/index-vxli-E-l.js.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const utils = require("@strapi/utils");
|
|
3
|
+
const isEqual = require("lodash/isEqual");
|
|
4
|
+
const lodash = require("lodash");
|
|
3
5
|
const _ = require("lodash/fp");
|
|
4
6
|
const EE = require("@strapi/strapi/dist/utils/ee");
|
|
7
|
+
const nodeSchedule = require("node-schedule");
|
|
5
8
|
const yup = require("yup");
|
|
6
9
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
7
10
|
function _interopNamespace(e) {
|
|
@@ -22,6 +25,7 @@ function _interopNamespace(e) {
|
|
|
22
25
|
n.default = e;
|
|
23
26
|
return Object.freeze(n);
|
|
24
27
|
}
|
|
28
|
+
const isEqual__default = /* @__PURE__ */ _interopDefault(isEqual);
|
|
25
29
|
const ___default = /* @__PURE__ */ _interopDefault(_);
|
|
26
30
|
const EE__default = /* @__PURE__ */ _interopDefault(EE);
|
|
27
31
|
const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
|
|
@@ -71,47 +75,203 @@ const ACTIONS = [
|
|
|
71
75
|
pluginName: "content-releases"
|
|
72
76
|
}
|
|
73
77
|
];
|
|
78
|
+
const ALLOWED_WEBHOOK_EVENTS = {
|
|
79
|
+
RELEASES_PUBLISH: "releases.publish"
|
|
80
|
+
};
|
|
74
81
|
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
75
82
|
return strapi2.plugin("content-releases").service(name);
|
|
76
83
|
};
|
|
77
|
-
const {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
85
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
86
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
87
|
+
const entry = await strapi2.entityService.findOne(contentTypeUid, entryId, { populate });
|
|
88
|
+
return entry;
|
|
89
|
+
};
|
|
90
|
+
const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
91
|
+
try {
|
|
92
|
+
await strapi2.entityValidator.validateEntityCreation(
|
|
93
|
+
strapi2.getModel(contentTypeUid),
|
|
94
|
+
entry,
|
|
95
|
+
void 0,
|
|
96
|
+
// @ts-expect-error - FIXME: entity here is unnecessary
|
|
97
|
+
entry
|
|
98
|
+
);
|
|
99
|
+
return true;
|
|
100
|
+
} catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
async function deleteActionsOnDisableDraftAndPublish({
|
|
105
|
+
oldContentTypes,
|
|
106
|
+
contentTypes: contentTypes2
|
|
107
|
+
}) {
|
|
108
|
+
if (!oldContentTypes) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
for (const uid in contentTypes2) {
|
|
112
|
+
if (!oldContentTypes[uid]) {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
const oldContentType = oldContentTypes[uid];
|
|
116
|
+
const contentType = contentTypes2[uid];
|
|
117
|
+
if (utils.contentTypes.hasDraftAndPublish(oldContentType) && !utils.contentTypes.hasDraftAndPublish(contentType)) {
|
|
118
|
+
await strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: uid }).execute();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
123
|
+
const deletedContentTypes = lodash.difference(lodash.keys(oldContentTypes), lodash.keys(contentTypes2)) ?? [];
|
|
124
|
+
if (deletedContentTypes.length) {
|
|
125
|
+
await utils.mapAsync(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
126
|
+
return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
async function migrateIsValidAndStatusReleases() {
|
|
131
|
+
const releasesWithoutStatus = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
132
|
+
where: {
|
|
133
|
+
status: null,
|
|
134
|
+
releasedAt: null
|
|
135
|
+
},
|
|
136
|
+
populate: {
|
|
137
|
+
actions: {
|
|
138
|
+
populate: {
|
|
139
|
+
entry: true
|
|
88
140
|
}
|
|
89
141
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
utils.mapAsync(releasesWithoutStatus, async (release2) => {
|
|
145
|
+
const actions = release2.actions;
|
|
146
|
+
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
147
|
+
for (const action of notValidatedActions) {
|
|
148
|
+
if (action.entry) {
|
|
149
|
+
const populatedEntry = await getPopulatedEntry(action.contentType, action.entry.id, {
|
|
150
|
+
strapi
|
|
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
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
166
|
+
});
|
|
167
|
+
const publishedReleases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
168
|
+
where: {
|
|
169
|
+
status: null,
|
|
170
|
+
releasedAt: {
|
|
171
|
+
$notNull: true
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
utils.mapAsync(publishedReleases, async (release2) => {
|
|
176
|
+
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
177
|
+
where: {
|
|
178
|
+
id: release2.id
|
|
179
|
+
},
|
|
180
|
+
data: {
|
|
181
|
+
status: "done"
|
|
96
182
|
}
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
187
|
+
if (oldContentTypes !== void 0 && contentTypes2 !== void 0) {
|
|
188
|
+
const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes).filter(
|
|
189
|
+
(uid) => oldContentTypes[uid]?.options?.draftAndPublish
|
|
97
190
|
);
|
|
98
|
-
|
|
191
|
+
const releasesAffected = /* @__PURE__ */ new Set();
|
|
192
|
+
utils.mapAsync(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
|
|
193
|
+
const oldContentType = oldContentTypes[contentTypeUID];
|
|
194
|
+
const contentType = contentTypes2[contentTypeUID];
|
|
195
|
+
if (!isEqual__default.default(oldContentType?.attributes, contentType?.attributes)) {
|
|
196
|
+
const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
197
|
+
where: {
|
|
198
|
+
contentType: contentTypeUID
|
|
199
|
+
},
|
|
200
|
+
populate: {
|
|
201
|
+
entry: true,
|
|
202
|
+
release: true
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
await utils.mapAsync(actions, async (action) => {
|
|
206
|
+
if (action.entry) {
|
|
207
|
+
const populatedEntry = await getPopulatedEntry(contentTypeUID, action.entry.id, {
|
|
208
|
+
strapi
|
|
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
|
+
}
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}).then(() => {
|
|
228
|
+
utils.mapAsync(releasesAffected, async (releaseId) => {
|
|
229
|
+
return getService("release", { strapi }).updateReleaseStatus(releaseId);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
|
|
235
|
+
const register = async ({ strapi: strapi2 }) => {
|
|
236
|
+
if (features$2.isEnabled("cms-content-releases")) {
|
|
237
|
+
await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
|
|
238
|
+
strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish);
|
|
239
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
99
240
|
}
|
|
100
241
|
};
|
|
101
242
|
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
102
243
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
103
|
-
if (features$1.isEnabled("cms-content-releases")
|
|
244
|
+
if (features$1.isEnabled("cms-content-releases")) {
|
|
245
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
246
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
247
|
+
);
|
|
104
248
|
strapi2.db.lifecycles.subscribe({
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
249
|
+
models: contentTypesWithDraftAndPublish,
|
|
250
|
+
async afterDelete(event) {
|
|
251
|
+
try {
|
|
252
|
+
const { model, result } = event;
|
|
253
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
254
|
+
const { id } = result;
|
|
255
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
256
|
+
where: {
|
|
257
|
+
actions: {
|
|
258
|
+
target_type: model.uid,
|
|
259
|
+
target_id: id
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
264
|
+
where: {
|
|
265
|
+
target_type: model.uid,
|
|
266
|
+
target_id: id
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
for (const release2 of releases) {
|
|
270
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
113
271
|
}
|
|
114
|
-
}
|
|
272
|
+
}
|
|
273
|
+
} catch (error) {
|
|
274
|
+
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
115
275
|
}
|
|
116
276
|
},
|
|
117
277
|
/**
|
|
@@ -120,7 +280,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
120
280
|
*/
|
|
121
281
|
async beforeDeleteMany(event) {
|
|
122
282
|
const { model, params } = event;
|
|
123
|
-
if (model.kind === "collectionType" && model.options
|
|
283
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
124
284
|
const { where } = params;
|
|
125
285
|
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
126
286
|
event.state.entriesToDelete = entriesToDelete;
|
|
@@ -131,20 +291,98 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
131
291
|
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
132
292
|
*/
|
|
133
293
|
async afterDeleteMany(event) {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
294
|
+
try {
|
|
295
|
+
const { model, state } = event;
|
|
296
|
+
const entriesToDelete = state.entriesToDelete;
|
|
297
|
+
if (entriesToDelete) {
|
|
298
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
299
|
+
where: {
|
|
300
|
+
actions: {
|
|
301
|
+
target_type: model.uid,
|
|
302
|
+
target_id: {
|
|
303
|
+
$in: entriesToDelete.map(
|
|
304
|
+
(entry) => entry.id
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
311
|
+
where: {
|
|
312
|
+
target_type: model.uid,
|
|
313
|
+
target_id: {
|
|
314
|
+
$in: entriesToDelete.map((entry) => entry.id)
|
|
315
|
+
}
|
|
142
316
|
}
|
|
317
|
+
});
|
|
318
|
+
for (const release2 of releases) {
|
|
319
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
143
320
|
}
|
|
321
|
+
}
|
|
322
|
+
} catch (error) {
|
|
323
|
+
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
324
|
+
error
|
|
144
325
|
});
|
|
145
326
|
}
|
|
327
|
+
},
|
|
328
|
+
async afterUpdate(event) {
|
|
329
|
+
try {
|
|
330
|
+
const { model, result } = event;
|
|
331
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
332
|
+
const isEntryValid = await getEntryValidStatus(
|
|
333
|
+
model.uid,
|
|
334
|
+
result,
|
|
335
|
+
{
|
|
336
|
+
strapi: strapi2
|
|
337
|
+
}
|
|
338
|
+
);
|
|
339
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
340
|
+
where: {
|
|
341
|
+
target_type: model.uid,
|
|
342
|
+
target_id: result.id
|
|
343
|
+
},
|
|
344
|
+
data: {
|
|
345
|
+
isEntryValid
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
349
|
+
where: {
|
|
350
|
+
actions: {
|
|
351
|
+
target_type: model.uid,
|
|
352
|
+
target_id: result.id
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
});
|
|
356
|
+
for (const release2 of releases) {
|
|
357
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
} catch (error) {
|
|
361
|
+
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
362
|
+
}
|
|
146
363
|
}
|
|
147
364
|
});
|
|
365
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
366
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
367
|
+
strapi2.log.error(
|
|
368
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
369
|
+
);
|
|
370
|
+
throw err;
|
|
371
|
+
});
|
|
372
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
373
|
+
strapi2.webhookStore.addAllowedEvent(key, value);
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
const destroy = async ({ strapi: strapi2 }) => {
|
|
379
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
380
|
+
const scheduledJobs = getService("scheduling", {
|
|
381
|
+
strapi: strapi2
|
|
382
|
+
}).getAll();
|
|
383
|
+
for (const [, job] of scheduledJobs) {
|
|
384
|
+
job.cancel();
|
|
385
|
+
}
|
|
148
386
|
}
|
|
149
387
|
};
|
|
150
388
|
const schema$1 = {
|
|
@@ -173,6 +411,17 @@ const schema$1 = {
|
|
|
173
411
|
releasedAt: {
|
|
174
412
|
type: "datetime"
|
|
175
413
|
},
|
|
414
|
+
scheduledAt: {
|
|
415
|
+
type: "datetime"
|
|
416
|
+
},
|
|
417
|
+
timezone: {
|
|
418
|
+
type: "string"
|
|
419
|
+
},
|
|
420
|
+
status: {
|
|
421
|
+
type: "enumeration",
|
|
422
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
423
|
+
required: true
|
|
424
|
+
},
|
|
176
425
|
actions: {
|
|
177
426
|
type: "relation",
|
|
178
427
|
relation: "oneToMany",
|
|
@@ -225,6 +474,9 @@ const schema = {
|
|
|
225
474
|
relation: "manyToOne",
|
|
226
475
|
target: RELEASE_MODEL_UID,
|
|
227
476
|
inversedBy: "actions"
|
|
477
|
+
},
|
|
478
|
+
isEntryValid: {
|
|
479
|
+
type: "boolean"
|
|
228
480
|
}
|
|
229
481
|
}
|
|
230
482
|
};
|
|
@@ -235,15 +487,6 @@ const contentTypes = {
|
|
|
235
487
|
release: release$1,
|
|
236
488
|
"release-action": releaseAction$1
|
|
237
489
|
};
|
|
238
|
-
const createReleaseActionService = ({ strapi: strapi2 }) => ({
|
|
239
|
-
async deleteManyForContentType(contentTypeUid) {
|
|
240
|
-
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
241
|
-
where: {
|
|
242
|
-
target_type: contentTypeUid
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
});
|
|
247
490
|
const getGroupName = (queryValue) => {
|
|
248
491
|
switch (queryValue) {
|
|
249
492
|
case "contentType":
|
|
@@ -256,367 +499,563 @@ const getGroupName = (queryValue) => {
|
|
|
256
499
|
return "contentType.displayName";
|
|
257
500
|
}
|
|
258
501
|
};
|
|
259
|
-
const createReleaseService = ({ strapi: strapi2 }) =>
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
502
|
+
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
503
|
+
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
504
|
+
strapi2.eventHub.emit(event, {
|
|
505
|
+
isPublished,
|
|
506
|
+
error,
|
|
507
|
+
release: release2
|
|
265
508
|
});
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
509
|
+
};
|
|
510
|
+
return {
|
|
511
|
+
async create(releaseData, { user }) {
|
|
512
|
+
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
513
|
+
const {
|
|
514
|
+
validatePendingReleasesLimit,
|
|
515
|
+
validateUniqueNameForPendingRelease,
|
|
516
|
+
validateScheduledAtIsLaterThanNow
|
|
517
|
+
} = getService("release-validation", { strapi: strapi2 });
|
|
518
|
+
await Promise.all([
|
|
519
|
+
validatePendingReleasesLimit(),
|
|
520
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
521
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
522
|
+
]);
|
|
523
|
+
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
524
|
+
data: {
|
|
525
|
+
...releaseWithCreatorFields,
|
|
526
|
+
status: "empty"
|
|
280
527
|
}
|
|
528
|
+
});
|
|
529
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
|
|
530
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
531
|
+
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
281
532
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
533
|
+
strapi2.telemetry.send("didCreateContentRelease");
|
|
534
|
+
return release2;
|
|
535
|
+
},
|
|
536
|
+
async findOne(id, query = {}) {
|
|
537
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
|
|
538
|
+
...query
|
|
539
|
+
});
|
|
540
|
+
return release2;
|
|
541
|
+
},
|
|
542
|
+
findPage(query) {
|
|
543
|
+
return strapi2.entityService.findPage(RELEASE_MODEL_UID, {
|
|
544
|
+
...query,
|
|
545
|
+
populate: {
|
|
546
|
+
actions: {
|
|
547
|
+
// @ts-expect-error Ignore missing properties
|
|
548
|
+
count: true
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
},
|
|
553
|
+
async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
554
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
555
|
+
where: {
|
|
556
|
+
actions: {
|
|
557
|
+
target_type: contentTypeUid,
|
|
558
|
+
target_id: entryId
|
|
559
|
+
},
|
|
560
|
+
releasedAt: {
|
|
561
|
+
$null: true
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
populate: {
|
|
565
|
+
// Filter the action to get only the content type entry
|
|
566
|
+
actions: {
|
|
567
|
+
where: {
|
|
301
568
|
target_type: contentTypeUid,
|
|
302
569
|
target_id: entryId
|
|
303
570
|
}
|
|
304
571
|
}
|
|
305
|
-
},
|
|
306
|
-
{
|
|
307
|
-
actions: null
|
|
308
572
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
573
|
+
});
|
|
574
|
+
return releases.map((release2) => {
|
|
575
|
+
if (release2.actions?.length) {
|
|
576
|
+
const [actionForEntry] = release2.actions;
|
|
577
|
+
delete release2.actions;
|
|
578
|
+
return {
|
|
579
|
+
...release2,
|
|
580
|
+
action: actionForEntry
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
return release2;
|
|
584
|
+
});
|
|
585
|
+
},
|
|
586
|
+
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
587
|
+
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
314
588
|
where: {
|
|
315
|
-
|
|
316
|
-
|
|
589
|
+
releasedAt: {
|
|
590
|
+
$null: true
|
|
591
|
+
},
|
|
592
|
+
actions: {
|
|
593
|
+
target_type: contentTypeUid,
|
|
594
|
+
target_id: entryId
|
|
595
|
+
}
|
|
317
596
|
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
597
|
+
});
|
|
598
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
599
|
+
where: {
|
|
600
|
+
$or: [
|
|
601
|
+
{
|
|
602
|
+
id: {
|
|
603
|
+
$notIn: releasesRelated.map((release2) => release2.id)
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
actions: null
|
|
608
|
+
}
|
|
609
|
+
],
|
|
610
|
+
releasedAt: {
|
|
611
|
+
$null: true
|
|
612
|
+
}
|
|
325
613
|
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
|
|
614
|
+
});
|
|
615
|
+
return releases.map((release2) => {
|
|
616
|
+
if (release2.actions?.length) {
|
|
617
|
+
const [actionForEntry] = release2.actions;
|
|
618
|
+
delete release2.actions;
|
|
619
|
+
return {
|
|
620
|
+
...release2,
|
|
621
|
+
action: actionForEntry
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
return release2;
|
|
625
|
+
});
|
|
626
|
+
},
|
|
627
|
+
async update(id, releaseData, { user }) {
|
|
628
|
+
const releaseWithCreatorFields = await utils.setCreatorFields({ user, isEdition: true })(
|
|
629
|
+
releaseData
|
|
630
|
+
);
|
|
631
|
+
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
|
632
|
+
"release-validation",
|
|
633
|
+
{ strapi: strapi2 }
|
|
634
|
+
);
|
|
635
|
+
await Promise.all([
|
|
636
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
637
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
638
|
+
]);
|
|
639
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
|
|
640
|
+
if (!release2) {
|
|
641
|
+
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
329
642
|
}
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
if (release2.actions?.length) {
|
|
333
|
-
const [actionForEntry] = release2.actions;
|
|
334
|
-
delete release2.actions;
|
|
335
|
-
return {
|
|
336
|
-
...release2,
|
|
337
|
-
action: actionForEntry
|
|
338
|
-
};
|
|
643
|
+
if (release2.releasedAt) {
|
|
644
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
339
645
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
355
|
-
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
356
|
-
*/
|
|
357
|
-
// @ts-expect-error see above
|
|
358
|
-
data: releaseWithCreatorFields
|
|
359
|
-
});
|
|
360
|
-
return updatedRelease;
|
|
361
|
-
},
|
|
362
|
-
async createAction(releaseId, action) {
|
|
363
|
-
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
364
|
-
strapi: strapi2
|
|
365
|
-
});
|
|
366
|
-
await Promise.all([
|
|
367
|
-
validateEntryContentType(action.entry.contentType),
|
|
368
|
-
validateUniqueEntry(releaseId, action)
|
|
369
|
-
]);
|
|
370
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
371
|
-
if (!release2) {
|
|
372
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
373
|
-
}
|
|
374
|
-
if (release2.releasedAt) {
|
|
375
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
376
|
-
}
|
|
377
|
-
const { entry, type } = action;
|
|
378
|
-
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
379
|
-
data: {
|
|
380
|
-
type,
|
|
381
|
-
contentType: entry.contentType,
|
|
382
|
-
locale: entry.locale,
|
|
383
|
-
entry: {
|
|
384
|
-
id: entry.id,
|
|
385
|
-
__type: entry.contentType,
|
|
386
|
-
__pivot: { field: "entry" }
|
|
387
|
-
},
|
|
388
|
-
release: releaseId
|
|
389
|
-
},
|
|
390
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
391
|
-
});
|
|
392
|
-
},
|
|
393
|
-
async findActions(releaseId, query) {
|
|
394
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
395
|
-
fields: ["id"]
|
|
396
|
-
});
|
|
397
|
-
if (!release2) {
|
|
398
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
399
|
-
}
|
|
400
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
401
|
-
...query,
|
|
402
|
-
populate: {
|
|
403
|
-
entry: {
|
|
404
|
-
populate: "*"
|
|
646
|
+
const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
|
|
647
|
+
/*
|
|
648
|
+
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
649
|
+
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
650
|
+
*/
|
|
651
|
+
// @ts-expect-error see above
|
|
652
|
+
data: releaseWithCreatorFields
|
|
653
|
+
});
|
|
654
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
655
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
656
|
+
if (releaseData.scheduledAt) {
|
|
657
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
658
|
+
} else if (release2.scheduledAt) {
|
|
659
|
+
schedulingService.cancel(id);
|
|
405
660
|
}
|
|
406
|
-
},
|
|
407
|
-
filters: {
|
|
408
|
-
release: releaseId
|
|
409
661
|
}
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
662
|
+
this.updateReleaseStatus(id);
|
|
663
|
+
strapi2.telemetry.send("didUpdateContentRelease");
|
|
664
|
+
return updatedRelease;
|
|
665
|
+
},
|
|
666
|
+
async createAction(releaseId, action) {
|
|
667
|
+
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
668
|
+
strapi: strapi2
|
|
669
|
+
});
|
|
670
|
+
await Promise.all([
|
|
671
|
+
validateEntryContentType(action.entry.contentType),
|
|
672
|
+
validateUniqueEntry(releaseId, action)
|
|
673
|
+
]);
|
|
674
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
675
|
+
if (!release2) {
|
|
676
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
419
677
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
},
|
|
441
|
-
async getLocalesDataForActions() {
|
|
442
|
-
if (!strapi2.plugin("i18n")) {
|
|
443
|
-
return {};
|
|
444
|
-
}
|
|
445
|
-
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
446
|
-
return allLocales.reduce((acc, locale) => {
|
|
447
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
448
|
-
return acc;
|
|
449
|
-
}, {});
|
|
450
|
-
},
|
|
451
|
-
async getContentTypesDataForActions(contentTypesUids) {
|
|
452
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
453
|
-
const contentTypesData = {};
|
|
454
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
455
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
456
|
-
uid: contentTypeUid
|
|
678
|
+
if (release2.releasedAt) {
|
|
679
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
680
|
+
}
|
|
681
|
+
const { entry, type } = action;
|
|
682
|
+
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
683
|
+
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
684
|
+
const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
685
|
+
data: {
|
|
686
|
+
type,
|
|
687
|
+
contentType: entry.contentType,
|
|
688
|
+
locale: entry.locale,
|
|
689
|
+
isEntryValid,
|
|
690
|
+
entry: {
|
|
691
|
+
id: entry.id,
|
|
692
|
+
__type: entry.contentType,
|
|
693
|
+
__pivot: { field: "entry" }
|
|
694
|
+
},
|
|
695
|
+
release: releaseId
|
|
696
|
+
},
|
|
697
|
+
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
457
698
|
});
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
if (!acc.includes(action.contentType)) {
|
|
468
|
-
acc.push(action.contentType);
|
|
699
|
+
this.updateReleaseStatus(releaseId);
|
|
700
|
+
return releaseAction2;
|
|
701
|
+
},
|
|
702
|
+
async findActions(releaseId, query) {
|
|
703
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
704
|
+
fields: ["id"]
|
|
705
|
+
});
|
|
706
|
+
if (!release2) {
|
|
707
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
469
708
|
}
|
|
470
|
-
return
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
709
|
+
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
710
|
+
...query,
|
|
711
|
+
populate: {
|
|
712
|
+
entry: {
|
|
713
|
+
populate: "*"
|
|
714
|
+
}
|
|
715
|
+
},
|
|
716
|
+
filters: {
|
|
717
|
+
release: releaseId
|
|
718
|
+
}
|
|
719
|
+
});
|
|
720
|
+
},
|
|
721
|
+
async countActions(query) {
|
|
722
|
+
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
723
|
+
},
|
|
724
|
+
async groupActions(actions, groupBy) {
|
|
725
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
726
|
+
if (!acc.includes(action.contentType)) {
|
|
727
|
+
acc.push(action.contentType);
|
|
728
|
+
}
|
|
475
729
|
return acc;
|
|
476
|
-
},
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
730
|
+
}, []);
|
|
731
|
+
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
732
|
+
contentTypeUids
|
|
733
|
+
);
|
|
734
|
+
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
735
|
+
const formattedData = actions.map((action) => {
|
|
736
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
737
|
+
return {
|
|
738
|
+
...action,
|
|
739
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
740
|
+
contentType: {
|
|
741
|
+
displayName,
|
|
742
|
+
mainFieldValue: action.entry[mainField],
|
|
743
|
+
uid: action.contentType
|
|
744
|
+
}
|
|
745
|
+
};
|
|
746
|
+
});
|
|
747
|
+
const groupName = getGroupName(groupBy);
|
|
748
|
+
return ___default.default.groupBy(groupName)(formattedData);
|
|
749
|
+
},
|
|
750
|
+
async getLocalesDataForActions() {
|
|
751
|
+
if (!strapi2.plugin("i18n")) {
|
|
752
|
+
return {};
|
|
753
|
+
}
|
|
754
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
755
|
+
return allLocales.reduce((acc, locale) => {
|
|
756
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
487
757
|
return acc;
|
|
488
|
-
},
|
|
489
|
-
|
|
490
|
-
)
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
758
|
+
}, {});
|
|
759
|
+
},
|
|
760
|
+
async getContentTypesDataForActions(contentTypesUids) {
|
|
761
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
762
|
+
const contentTypesData = {};
|
|
763
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
764
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
765
|
+
uid: contentTypeUid
|
|
766
|
+
});
|
|
767
|
+
contentTypesData[contentTypeUid] = {
|
|
768
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
769
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
770
|
+
};
|
|
499
771
|
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
}
|
|
507
|
-
await strapi2.db.transaction(async () => {
|
|
508
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
509
|
-
where: {
|
|
510
|
-
id: {
|
|
511
|
-
$in: release2.actions.map((action) => action.id)
|
|
512
|
-
}
|
|
772
|
+
return contentTypesData;
|
|
773
|
+
},
|
|
774
|
+
getContentTypeModelsFromActions(actions) {
|
|
775
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
776
|
+
if (!acc.includes(action.contentType)) {
|
|
777
|
+
acc.push(action.contentType);
|
|
513
778
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
779
|
+
return acc;
|
|
780
|
+
}, []);
|
|
781
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
782
|
+
(acc, contentTypeUid) => {
|
|
783
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
784
|
+
return acc;
|
|
785
|
+
},
|
|
786
|
+
{}
|
|
787
|
+
);
|
|
788
|
+
return contentTypeModelsMap;
|
|
789
|
+
},
|
|
790
|
+
async getAllComponents() {
|
|
791
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
792
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
793
|
+
const componentsMap = components.reduce(
|
|
794
|
+
(acc, component) => {
|
|
795
|
+
acc[component.uid] = component;
|
|
796
|
+
return acc;
|
|
797
|
+
},
|
|
798
|
+
{}
|
|
799
|
+
);
|
|
800
|
+
return componentsMap;
|
|
801
|
+
},
|
|
802
|
+
async delete(releaseId) {
|
|
803
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
524
804
|
populate: {
|
|
525
805
|
actions: {
|
|
526
|
-
|
|
527
|
-
entry: true
|
|
528
|
-
}
|
|
806
|
+
fields: ["id"]
|
|
529
807
|
}
|
|
530
808
|
}
|
|
809
|
+
});
|
|
810
|
+
if (!release2) {
|
|
811
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
531
812
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
535
|
-
}
|
|
536
|
-
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
537
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
538
|
-
}
|
|
539
|
-
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
540
|
-
throw new utils.errors.ValidationError("No entries to publish");
|
|
541
|
-
}
|
|
542
|
-
const actions = {};
|
|
543
|
-
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
544
|
-
const contentTypeUid = action.contentType;
|
|
545
|
-
if (!actions[contentTypeUid]) {
|
|
546
|
-
actions[contentTypeUid] = {
|
|
547
|
-
publish: [],
|
|
548
|
-
unpublish: []
|
|
549
|
-
};
|
|
813
|
+
if (release2.releasedAt) {
|
|
814
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
550
815
|
}
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
816
|
+
await strapi2.db.transaction(async () => {
|
|
817
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
818
|
+
where: {
|
|
819
|
+
id: {
|
|
820
|
+
$in: release2.actions.map((action) => action.id)
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
825
|
+
});
|
|
826
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
|
|
827
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
828
|
+
await schedulingService.cancel(release2.id);
|
|
555
829
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
830
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
831
|
+
return release2;
|
|
832
|
+
},
|
|
833
|
+
async publish(releaseId) {
|
|
834
|
+
try {
|
|
835
|
+
const releaseWithPopulatedActionEntries = await strapi2.entityService.findOne(
|
|
836
|
+
RELEASE_MODEL_UID,
|
|
837
|
+
releaseId,
|
|
838
|
+
{
|
|
839
|
+
populate: {
|
|
840
|
+
actions: {
|
|
841
|
+
populate: {
|
|
842
|
+
entry: {
|
|
843
|
+
fields: ["id"]
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
);
|
|
850
|
+
if (!releaseWithPopulatedActionEntries) {
|
|
851
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
852
|
+
}
|
|
853
|
+
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
854
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
855
|
+
}
|
|
856
|
+
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
857
|
+
throw new utils.errors.ValidationError("No entries to publish");
|
|
858
|
+
}
|
|
859
|
+
const collectionTypeActions = {};
|
|
860
|
+
const singleTypeActions = [];
|
|
861
|
+
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
862
|
+
const contentTypeUid = action.contentType;
|
|
863
|
+
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
864
|
+
if (!collectionTypeActions[contentTypeUid]) {
|
|
865
|
+
collectionTypeActions[contentTypeUid] = {
|
|
866
|
+
entriestoPublishIds: [],
|
|
867
|
+
entriesToUnpublishIds: []
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
if (action.type === "publish") {
|
|
871
|
+
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
872
|
+
} else {
|
|
873
|
+
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
874
|
+
}
|
|
875
|
+
} else {
|
|
876
|
+
singleTypeActions.push({
|
|
877
|
+
uid: contentTypeUid,
|
|
878
|
+
action: action.type,
|
|
879
|
+
id: action.entry.id
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
884
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
885
|
+
await strapi2.db.transaction(async () => {
|
|
886
|
+
for (const { uid, action, id } of singleTypeActions) {
|
|
887
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
888
|
+
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
889
|
+
try {
|
|
890
|
+
if (action === "publish") {
|
|
891
|
+
await entityManagerService.publish(entry, uid);
|
|
892
|
+
} else {
|
|
893
|
+
await entityManagerService.unpublish(entry, uid);
|
|
894
|
+
}
|
|
895
|
+
} catch (error) {
|
|
896
|
+
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
|
|
897
|
+
} else {
|
|
898
|
+
throw error;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
903
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
904
|
+
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
905
|
+
const entriesToPublish = await strapi2.entityService.findMany(
|
|
906
|
+
contentTypeUid,
|
|
907
|
+
{
|
|
908
|
+
filters: {
|
|
909
|
+
id: {
|
|
910
|
+
$in: entriestoPublishIds
|
|
911
|
+
}
|
|
912
|
+
},
|
|
913
|
+
populate
|
|
914
|
+
}
|
|
915
|
+
);
|
|
916
|
+
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
917
|
+
contentTypeUid,
|
|
918
|
+
{
|
|
919
|
+
filters: {
|
|
920
|
+
id: {
|
|
921
|
+
$in: entriesToUnpublishIds
|
|
922
|
+
}
|
|
923
|
+
},
|
|
924
|
+
populate
|
|
925
|
+
}
|
|
926
|
+
);
|
|
927
|
+
if (entriesToPublish.length > 0) {
|
|
928
|
+
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
929
|
+
}
|
|
930
|
+
if (entriesToUnpublish.length > 0) {
|
|
931
|
+
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
});
|
|
935
|
+
const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, releaseId, {
|
|
936
|
+
data: {
|
|
937
|
+
/*
|
|
938
|
+
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
|
|
939
|
+
*/
|
|
940
|
+
// @ts-expect-error see above
|
|
941
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
942
|
+
},
|
|
943
|
+
populate: {
|
|
944
|
+
actions: {
|
|
945
|
+
// @ts-expect-error is not expecting count but it is working
|
|
946
|
+
count: true
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
});
|
|
950
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
951
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
952
|
+
isPublished: true,
|
|
953
|
+
release: release2
|
|
954
|
+
});
|
|
563
955
|
}
|
|
564
|
-
|
|
565
|
-
|
|
956
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
957
|
+
return release2;
|
|
958
|
+
} catch (error) {
|
|
959
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
960
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
961
|
+
isPublished: false,
|
|
962
|
+
error
|
|
963
|
+
});
|
|
566
964
|
}
|
|
965
|
+
strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
966
|
+
where: { id: releaseId },
|
|
967
|
+
data: {
|
|
968
|
+
status: "failed"
|
|
969
|
+
}
|
|
970
|
+
});
|
|
971
|
+
throw error;
|
|
567
972
|
}
|
|
568
|
-
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
973
|
+
},
|
|
974
|
+
async updateAction(actionId, releaseId, update) {
|
|
975
|
+
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
976
|
+
where: {
|
|
977
|
+
id: actionId,
|
|
978
|
+
release: {
|
|
979
|
+
id: releaseId,
|
|
980
|
+
releasedAt: {
|
|
981
|
+
$null: true
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
},
|
|
985
|
+
data: update
|
|
986
|
+
});
|
|
987
|
+
if (!updatedAction) {
|
|
988
|
+
throw new utils.errors.NotFoundError(
|
|
989
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
990
|
+
);
|
|
576
991
|
}
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
992
|
+
return updatedAction;
|
|
993
|
+
},
|
|
994
|
+
async deleteAction(actionId, releaseId) {
|
|
995
|
+
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
996
|
+
where: {
|
|
997
|
+
id: actionId,
|
|
998
|
+
release: {
|
|
999
|
+
id: releaseId,
|
|
1000
|
+
releasedAt: {
|
|
1001
|
+
$null: true
|
|
1002
|
+
}
|
|
588
1003
|
}
|
|
589
1004
|
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
);
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
release: {
|
|
605
|
-
id: releaseId,
|
|
606
|
-
releasedAt: {
|
|
607
|
-
$null: true
|
|
1005
|
+
});
|
|
1006
|
+
if (!deletedAction) {
|
|
1007
|
+
throw new utils.errors.NotFoundError(
|
|
1008
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1009
|
+
);
|
|
1010
|
+
}
|
|
1011
|
+
this.updateReleaseStatus(releaseId);
|
|
1012
|
+
return deletedAction;
|
|
1013
|
+
},
|
|
1014
|
+
async updateReleaseStatus(releaseId) {
|
|
1015
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
1016
|
+
this.countActions({
|
|
1017
|
+
filters: {
|
|
1018
|
+
release: releaseId
|
|
608
1019
|
}
|
|
1020
|
+
}),
|
|
1021
|
+
this.countActions({
|
|
1022
|
+
filters: {
|
|
1023
|
+
release: releaseId,
|
|
1024
|
+
isEntryValid: false
|
|
1025
|
+
}
|
|
1026
|
+
})
|
|
1027
|
+
]);
|
|
1028
|
+
if (totalActions > 0) {
|
|
1029
|
+
if (invalidActions > 0) {
|
|
1030
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1031
|
+
where: {
|
|
1032
|
+
id: releaseId
|
|
1033
|
+
},
|
|
1034
|
+
data: {
|
|
1035
|
+
status: "blocked"
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
609
1038
|
}
|
|
1039
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1040
|
+
where: {
|
|
1041
|
+
id: releaseId
|
|
1042
|
+
},
|
|
1043
|
+
data: {
|
|
1044
|
+
status: "ready"
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
610
1047
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
1048
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1049
|
+
where: {
|
|
1050
|
+
id: releaseId
|
|
1051
|
+
},
|
|
1052
|
+
data: {
|
|
1053
|
+
status: "empty"
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
616
1056
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
});
|
|
1057
|
+
};
|
|
1058
|
+
};
|
|
620
1059
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
621
1060
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
622
1061
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -660,34 +1099,104 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
660
1099
|
if (pendingReleasesCount >= maximumPendingReleases) {
|
|
661
1100
|
throw new utils.errors.ValidationError("You have reached the maximum number of pending releases");
|
|
662
1101
|
}
|
|
1102
|
+
},
|
|
1103
|
+
async validateUniqueNameForPendingRelease(name, id) {
|
|
1104
|
+
const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
|
|
1105
|
+
filters: {
|
|
1106
|
+
releasedAt: {
|
|
1107
|
+
$null: true
|
|
1108
|
+
},
|
|
1109
|
+
name,
|
|
1110
|
+
...id && { id: { $ne: id } }
|
|
1111
|
+
}
|
|
1112
|
+
});
|
|
1113
|
+
const isNameUnique = pendingReleases.length === 0;
|
|
1114
|
+
if (!isNameUnique) {
|
|
1115
|
+
throw new utils.errors.ValidationError(`Release with name ${name} already exists`);
|
|
1116
|
+
}
|
|
1117
|
+
},
|
|
1118
|
+
async validateScheduledAtIsLaterThanNow(scheduledAt) {
|
|
1119
|
+
if (scheduledAt && new Date(scheduledAt) <= /* @__PURE__ */ new Date()) {
|
|
1120
|
+
throw new utils.errors.ValidationError("Scheduled at must be later than now");
|
|
1121
|
+
}
|
|
663
1122
|
}
|
|
664
1123
|
});
|
|
665
|
-
const
|
|
666
|
-
const
|
|
667
|
-
destroyListenerCallbacks: []
|
|
668
|
-
};
|
|
1124
|
+
const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
1125
|
+
const scheduledJobs = /* @__PURE__ */ new Map();
|
|
669
1126
|
return {
|
|
670
|
-
|
|
671
|
-
|
|
1127
|
+
async set(releaseId, scheduleDate) {
|
|
1128
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId, releasedAt: null } });
|
|
1129
|
+
if (!release2) {
|
|
1130
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1131
|
+
}
|
|
1132
|
+
const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
|
|
1133
|
+
try {
|
|
1134
|
+
await getService("release").publish(releaseId);
|
|
1135
|
+
} catch (error) {
|
|
1136
|
+
}
|
|
1137
|
+
this.cancel(releaseId);
|
|
1138
|
+
});
|
|
1139
|
+
if (scheduledJobs.has(releaseId)) {
|
|
1140
|
+
this.cancel(releaseId);
|
|
1141
|
+
}
|
|
1142
|
+
scheduledJobs.set(releaseId, job);
|
|
1143
|
+
return scheduledJobs;
|
|
672
1144
|
},
|
|
673
|
-
|
|
674
|
-
if (
|
|
675
|
-
|
|
1145
|
+
cancel(releaseId) {
|
|
1146
|
+
if (scheduledJobs.has(releaseId)) {
|
|
1147
|
+
scheduledJobs.get(releaseId).cancel();
|
|
1148
|
+
scheduledJobs.delete(releaseId);
|
|
676
1149
|
}
|
|
677
|
-
|
|
678
|
-
|
|
1150
|
+
return scheduledJobs;
|
|
1151
|
+
},
|
|
1152
|
+
getAll() {
|
|
1153
|
+
return scheduledJobs;
|
|
1154
|
+
},
|
|
1155
|
+
/**
|
|
1156
|
+
* On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
|
|
1157
|
+
* This is useful in case the server was restarted and the scheduled jobs were lost
|
|
1158
|
+
* This also could be used to sync different Strapi instances in case of a cluster
|
|
1159
|
+
*/
|
|
1160
|
+
async syncFromDatabase() {
|
|
1161
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1162
|
+
where: {
|
|
1163
|
+
scheduledAt: {
|
|
1164
|
+
$gte: /* @__PURE__ */ new Date()
|
|
1165
|
+
},
|
|
1166
|
+
releasedAt: null
|
|
1167
|
+
}
|
|
679
1168
|
});
|
|
1169
|
+
for (const release2 of releases) {
|
|
1170
|
+
this.set(release2.id, release2.scheduledAt);
|
|
1171
|
+
}
|
|
1172
|
+
return scheduledJobs;
|
|
680
1173
|
}
|
|
681
1174
|
};
|
|
682
1175
|
};
|
|
683
1176
|
const services = {
|
|
684
1177
|
release: createReleaseService,
|
|
685
|
-
"release-action": createReleaseActionService,
|
|
686
1178
|
"release-validation": createReleaseValidationService,
|
|
687
|
-
"
|
|
1179
|
+
...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
|
|
688
1180
|
};
|
|
689
1181
|
const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
690
|
-
name: yup__namespace.string().trim().required()
|
|
1182
|
+
name: yup__namespace.string().trim().required(),
|
|
1183
|
+
scheduledAt: yup__namespace.string().nullable(),
|
|
1184
|
+
isScheduled: yup__namespace.boolean().optional(),
|
|
1185
|
+
time: yup__namespace.string().when("isScheduled", {
|
|
1186
|
+
is: true,
|
|
1187
|
+
then: yup__namespace.string().trim().required(),
|
|
1188
|
+
otherwise: yup__namespace.string().nullable()
|
|
1189
|
+
}),
|
|
1190
|
+
timezone: yup__namespace.string().when("isScheduled", {
|
|
1191
|
+
is: true,
|
|
1192
|
+
then: yup__namespace.string().required().nullable(),
|
|
1193
|
+
otherwise: yup__namespace.string().nullable()
|
|
1194
|
+
}),
|
|
1195
|
+
date: yup__namespace.string().when("isScheduled", {
|
|
1196
|
+
is: true,
|
|
1197
|
+
then: yup__namespace.string().required().nullable(),
|
|
1198
|
+
otherwise: yup__namespace.string().nullable()
|
|
1199
|
+
})
|
|
691
1200
|
}).required().noUnknown();
|
|
692
1201
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
693
1202
|
const releaseController = {
|
|
@@ -704,9 +1213,7 @@ const releaseController = {
|
|
|
704
1213
|
const contentTypeUid = query.contentTypeUid;
|
|
705
1214
|
const entryId = query.entryId;
|
|
706
1215
|
const hasEntryAttached = typeof query.hasEntryAttached === "string" ? JSON.parse(query.hasEntryAttached) : false;
|
|
707
|
-
const data = await releaseService.
|
|
708
|
-
hasEntryAttached
|
|
709
|
-
});
|
|
1216
|
+
const data = hasEntryAttached ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId) : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);
|
|
710
1217
|
ctx.body = { data };
|
|
711
1218
|
} else {
|
|
712
1219
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
@@ -722,26 +1229,30 @@ const releaseController = {
|
|
|
722
1229
|
}
|
|
723
1230
|
};
|
|
724
1231
|
});
|
|
725
|
-
|
|
1232
|
+
const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
|
|
1233
|
+
where: {
|
|
1234
|
+
releasedAt: null
|
|
1235
|
+
}
|
|
1236
|
+
});
|
|
1237
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
726
1238
|
}
|
|
727
1239
|
},
|
|
728
1240
|
async findOne(ctx) {
|
|
729
1241
|
const id = ctx.params.id;
|
|
730
1242
|
const releaseService = getService("release", { strapi });
|
|
731
1243
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
});
|
|
736
|
-
const sanitizedRelease = await permissionsManager.sanitizeOutput(release2);
|
|
1244
|
+
if (!release2) {
|
|
1245
|
+
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1246
|
+
}
|
|
737
1247
|
const count = await releaseService.countActions({
|
|
738
1248
|
filters: {
|
|
739
1249
|
release: id
|
|
740
1250
|
}
|
|
741
1251
|
});
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
1252
|
+
const sanitizedRelease = {
|
|
1253
|
+
...release2,
|
|
1254
|
+
createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
|
|
1255
|
+
};
|
|
745
1256
|
const data = {
|
|
746
1257
|
...sanitizedRelease,
|
|
747
1258
|
actions: {
|
|
@@ -794,8 +1305,27 @@ const releaseController = {
|
|
|
794
1305
|
const id = ctx.params.id;
|
|
795
1306
|
const releaseService = getService("release", { strapi });
|
|
796
1307
|
const release2 = await releaseService.publish(id, { user });
|
|
1308
|
+
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1309
|
+
releaseService.countActions({
|
|
1310
|
+
filters: {
|
|
1311
|
+
release: id,
|
|
1312
|
+
type: "publish"
|
|
1313
|
+
}
|
|
1314
|
+
}),
|
|
1315
|
+
releaseService.countActions({
|
|
1316
|
+
filters: {
|
|
1317
|
+
release: id,
|
|
1318
|
+
type: "unpublish"
|
|
1319
|
+
}
|
|
1320
|
+
})
|
|
1321
|
+
]);
|
|
797
1322
|
ctx.body = {
|
|
798
|
-
data: release2
|
|
1323
|
+
data: release2,
|
|
1324
|
+
meta: {
|
|
1325
|
+
totalEntries: countPublishActions + countUnpublishActions,
|
|
1326
|
+
totalPublishedEntries: countPublishActions,
|
|
1327
|
+
totalUnpublishedEntries: countUnpublishActions
|
|
1328
|
+
}
|
|
799
1329
|
};
|
|
800
1330
|
}
|
|
801
1331
|
};
|
|
@@ -1063,19 +1593,15 @@ const routes = {
|
|
|
1063
1593
|
};
|
|
1064
1594
|
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
1065
1595
|
const getPlugin = () => {
|
|
1066
|
-
if (features.isEnabled("cms-content-releases")
|
|
1596
|
+
if (features.isEnabled("cms-content-releases")) {
|
|
1067
1597
|
return {
|
|
1068
1598
|
register,
|
|
1069
1599
|
bootstrap,
|
|
1600
|
+
destroy,
|
|
1070
1601
|
contentTypes,
|
|
1071
1602
|
services,
|
|
1072
1603
|
controllers,
|
|
1073
|
-
routes
|
|
1074
|
-
destroy() {
|
|
1075
|
-
if (features.isEnabled("cms-content-releases") && strapi.features.future.isEnabled("contentReleases")) {
|
|
1076
|
-
getService("event-manager").destroyAllListeners();
|
|
1077
|
-
}
|
|
1078
|
-
}
|
|
1604
|
+
routes
|
|
1079
1605
|
};
|
|
1080
1606
|
}
|
|
1081
1607
|
return {
|