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