@strapi/content-releases 4.18.1-beta.1 → 4.19.0
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-g2P5kbSm.mjs → App-L1jSxCiL.mjs} +88 -18
- package/dist/_chunks/App-L1jSxCiL.mjs.map +1 -0
- package/dist/_chunks/{App-o5_WfqR-.js → App-_20W9dYa.js} +85 -15
- package/dist/_chunks/App-_20W9dYa.js.map +1 -0
- package/dist/_chunks/{en-ngTk74JV.mjs → en-MyLPoISH.mjs} +2 -1
- package/dist/_chunks/en-MyLPoISH.mjs.map +1 -0
- package/dist/_chunks/{en-haKSQIo8.js → en-gYDqKYFd.js} +2 -1
- package/dist/_chunks/en-gYDqKYFd.js.map +1 -0
- package/dist/_chunks/{index-EdBmRHRU.js → index-KJa1Rb5F.js} +91 -32
- package/dist/_chunks/index-KJa1Rb5F.js.map +1 -0
- package/dist/_chunks/{index-XAQOX_IB.mjs → index-c4zRX_sg.mjs} +102 -43
- package/dist/_chunks/index-c4zRX_sg.mjs.map +1 -0
- package/dist/admin/index.js +2 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +3 -2
- package/dist/admin/index.mjs.map +1 -1
- package/dist/server/index.js +317 -85
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +317 -86
- package/dist/server/index.mjs.map +1 -1
- package/package.json +10 -9
- package/dist/_chunks/App-g2P5kbSm.mjs.map +0 -1
- package/dist/_chunks/App-o5_WfqR-.js.map +0 -1
- package/dist/_chunks/en-haKSQIo8.js.map +0 -1
- package/dist/_chunks/en-ngTk74JV.mjs.map +0 -1
- package/dist/_chunks/index-EdBmRHRU.js.map +0 -1
- package/dist/_chunks/index-XAQOX_IB.mjs.map +0 -1
package/dist/server/index.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { setCreatorFields, errors, validateYupSchema, yup as yup$1 } from "@strapi/utils";
|
|
1
|
+
import { contentTypes as contentTypes$1, mapAsync, setCreatorFields, errors, validateYupSchema, yup as yup$1 } from "@strapi/utils";
|
|
2
|
+
import { difference, keys } from "lodash";
|
|
2
3
|
import _ from "lodash/fp";
|
|
4
|
+
import EE from "@strapi/strapi/dist/utils/ee";
|
|
3
5
|
import * as yup from "yup";
|
|
4
6
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
5
7
|
const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
|
|
@@ -47,10 +49,87 @@ const ACTIONS = [
|
|
|
47
49
|
pluginName: "content-releases"
|
|
48
50
|
}
|
|
49
51
|
];
|
|
50
|
-
|
|
52
|
+
async function deleteActionsOnDisableDraftAndPublish({
|
|
53
|
+
oldContentTypes,
|
|
54
|
+
contentTypes: contentTypes2
|
|
55
|
+
}) {
|
|
56
|
+
if (!oldContentTypes) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
for (const uid in contentTypes2) {
|
|
60
|
+
if (!oldContentTypes[uid]) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const oldContentType = oldContentTypes[uid];
|
|
64
|
+
const contentType = contentTypes2[uid];
|
|
65
|
+
if (contentTypes$1.hasDraftAndPublish(oldContentType) && !contentTypes$1.hasDraftAndPublish(contentType)) {
|
|
66
|
+
await strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: uid }).execute();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
71
|
+
const deletedContentTypes = difference(keys(oldContentTypes), keys(contentTypes2)) ?? [];
|
|
72
|
+
if (deletedContentTypes.length) {
|
|
73
|
+
await mapAsync(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
74
|
+
return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
|
|
51
79
|
const register = async ({ strapi: strapi2 }) => {
|
|
52
|
-
if (features$
|
|
80
|
+
if (features$2.isEnabled("cms-content-releases")) {
|
|
53
81
|
await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
|
|
82
|
+
strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish);
|
|
83
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
87
|
+
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
88
|
+
if (features$1.isEnabled("cms-content-releases")) {
|
|
89
|
+
strapi2.db.lifecycles.subscribe({
|
|
90
|
+
afterDelete(event) {
|
|
91
|
+
const { model, result } = event;
|
|
92
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
93
|
+
const { id } = result;
|
|
94
|
+
strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
95
|
+
where: {
|
|
96
|
+
target_type: model.uid,
|
|
97
|
+
target_id: id
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
/**
|
|
103
|
+
* deleteMany hook doesn't return the deleted entries ids
|
|
104
|
+
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
105
|
+
*/
|
|
106
|
+
async beforeDeleteMany(event) {
|
|
107
|
+
const { model, params } = event;
|
|
108
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
109
|
+
const { where } = params;
|
|
110
|
+
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
111
|
+
event.state.entriesToDelete = entriesToDelete;
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
/**
|
|
115
|
+
* We delete the release actions related to deleted entries
|
|
116
|
+
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
117
|
+
*/
|
|
118
|
+
async afterDeleteMany(event) {
|
|
119
|
+
const { model, state } = event;
|
|
120
|
+
const entriesToDelete = state.entriesToDelete;
|
|
121
|
+
if (entriesToDelete) {
|
|
122
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
123
|
+
where: {
|
|
124
|
+
target_type: model.uid,
|
|
125
|
+
target_id: {
|
|
126
|
+
$in: entriesToDelete.map((entry) => entry.id)
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
});
|
|
54
133
|
}
|
|
55
134
|
};
|
|
56
135
|
const schema$1 = {
|
|
@@ -147,18 +226,26 @@ const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
|
147
226
|
const getGroupName = (queryValue) => {
|
|
148
227
|
switch (queryValue) {
|
|
149
228
|
case "contentType":
|
|
150
|
-
return "
|
|
229
|
+
return "contentType.displayName";
|
|
151
230
|
case "action":
|
|
152
231
|
return "type";
|
|
153
232
|
case "locale":
|
|
154
|
-
return _.getOr("No locale", "
|
|
233
|
+
return _.getOr("No locale", "locale.name");
|
|
155
234
|
default:
|
|
156
|
-
return "
|
|
235
|
+
return "contentType.displayName";
|
|
157
236
|
}
|
|
158
237
|
};
|
|
159
238
|
const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
160
239
|
async create(releaseData, { user }) {
|
|
161
240
|
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
|
241
|
+
const { validatePendingReleasesLimit, validateUniqueNameForPendingRelease } = getService(
|
|
242
|
+
"release-validation",
|
|
243
|
+
{ strapi: strapi2 }
|
|
244
|
+
);
|
|
245
|
+
await Promise.all([
|
|
246
|
+
validatePendingReleasesLimit(),
|
|
247
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name)
|
|
248
|
+
]);
|
|
162
249
|
return strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
163
250
|
data: releaseWithCreatorFields
|
|
164
251
|
});
|
|
@@ -180,51 +267,66 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
180
267
|
}
|
|
181
268
|
});
|
|
182
269
|
},
|
|
183
|
-
async
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
// Find all Releases where the content type entry is present
|
|
190
|
-
actions: {
|
|
191
|
-
target_type: contentTypeUid,
|
|
192
|
-
target_id: entryId
|
|
193
|
-
}
|
|
194
|
-
} : {
|
|
195
|
-
// Find all Releases where the content type entry is not present
|
|
196
|
-
$or: [
|
|
197
|
-
{
|
|
198
|
-
$not: {
|
|
199
|
-
actions: {
|
|
200
|
-
target_type: contentTypeUid,
|
|
201
|
-
target_id: entryId
|
|
202
|
-
}
|
|
203
|
-
}
|
|
270
|
+
async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
271
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
272
|
+
where: {
|
|
273
|
+
actions: {
|
|
274
|
+
target_type: contentTypeUid,
|
|
275
|
+
target_id: entryId
|
|
204
276
|
},
|
|
205
|
-
{
|
|
206
|
-
|
|
277
|
+
releasedAt: {
|
|
278
|
+
$null: true
|
|
207
279
|
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
280
|
+
},
|
|
281
|
+
populate: {
|
|
282
|
+
// Filter the action to get only the content type entry
|
|
283
|
+
actions: {
|
|
284
|
+
where: {
|
|
285
|
+
target_type: contentTypeUid,
|
|
286
|
+
target_id: entryId
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
return releases.map((release2) => {
|
|
292
|
+
if (release2.actions?.length) {
|
|
293
|
+
const [actionForEntry] = release2.actions;
|
|
294
|
+
delete release2.actions;
|
|
295
|
+
return {
|
|
296
|
+
...release2,
|
|
297
|
+
action: actionForEntry
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return release2;
|
|
301
|
+
});
|
|
302
|
+
},
|
|
303
|
+
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
304
|
+
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
305
|
+
where: {
|
|
306
|
+
releasedAt: {
|
|
307
|
+
$null: true
|
|
308
|
+
},
|
|
309
|
+
actions: {
|
|
214
310
|
target_type: contentTypeUid,
|
|
215
311
|
target_id: entryId
|
|
216
312
|
}
|
|
217
313
|
}
|
|
218
|
-
}
|
|
314
|
+
});
|
|
219
315
|
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
220
316
|
where: {
|
|
221
|
-
|
|
317
|
+
$or: [
|
|
318
|
+
{
|
|
319
|
+
id: {
|
|
320
|
+
$notIn: releasesRelated.map((release2) => release2.id)
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
actions: null
|
|
325
|
+
}
|
|
326
|
+
],
|
|
222
327
|
releasedAt: {
|
|
223
328
|
$null: true
|
|
224
329
|
}
|
|
225
|
-
},
|
|
226
|
-
populate: {
|
|
227
|
-
...populateAttachedAction
|
|
228
330
|
}
|
|
229
331
|
});
|
|
230
332
|
return releases.map((release2) => {
|
|
@@ -240,19 +342,23 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
240
342
|
});
|
|
241
343
|
},
|
|
242
344
|
async update(id, releaseData, { user }) {
|
|
243
|
-
const
|
|
244
|
-
const release2 = await strapi2.entityService.
|
|
345
|
+
const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(releaseData);
|
|
346
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
|
|
347
|
+
if (!release2) {
|
|
348
|
+
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
349
|
+
}
|
|
350
|
+
if (release2.releasedAt) {
|
|
351
|
+
throw new errors.ValidationError("Release already published");
|
|
352
|
+
}
|
|
353
|
+
const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
|
|
245
354
|
/*
|
|
246
355
|
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
247
356
|
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
248
357
|
*/
|
|
249
358
|
// @ts-expect-error see above
|
|
250
|
-
data:
|
|
359
|
+
data: releaseWithCreatorFields
|
|
251
360
|
});
|
|
252
|
-
|
|
253
|
-
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
254
|
-
}
|
|
255
|
-
return release2;
|
|
361
|
+
return updatedRelease;
|
|
256
362
|
},
|
|
257
363
|
async createAction(releaseId, action) {
|
|
258
364
|
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
@@ -262,6 +368,13 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
262
368
|
validateEntryContentType(action.entry.contentType),
|
|
263
369
|
validateUniqueEntry(releaseId, action)
|
|
264
370
|
]);
|
|
371
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
372
|
+
if (!release2) {
|
|
373
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
374
|
+
}
|
|
375
|
+
if (release2.releasedAt) {
|
|
376
|
+
throw new errors.ValidationError("Release already published");
|
|
377
|
+
}
|
|
265
378
|
const { entry, type } = action;
|
|
266
379
|
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
267
380
|
data: {
|
|
@@ -288,7 +401,9 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
288
401
|
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
289
402
|
...query,
|
|
290
403
|
populate: {
|
|
291
|
-
entry:
|
|
404
|
+
entry: {
|
|
405
|
+
populate: "*"
|
|
406
|
+
}
|
|
292
407
|
},
|
|
293
408
|
filters: {
|
|
294
409
|
release: releaseId
|
|
@@ -308,29 +423,32 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
308
423
|
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
309
424
|
contentTypeUids
|
|
310
425
|
);
|
|
311
|
-
const
|
|
312
|
-
const allLocalesDictionary = allLocales.reduce((acc, locale) => {
|
|
313
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
314
|
-
return acc;
|
|
315
|
-
}, {});
|
|
426
|
+
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
316
427
|
const formattedData = actions.map((action) => {
|
|
317
428
|
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
318
429
|
return {
|
|
319
430
|
...action,
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
},
|
|
326
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
327
|
-
status: action.entry.publishedAt ? "published" : "draft"
|
|
431
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
432
|
+
contentType: {
|
|
433
|
+
displayName,
|
|
434
|
+
mainFieldValue: action.entry[mainField],
|
|
435
|
+
uid: action.contentType
|
|
328
436
|
}
|
|
329
437
|
};
|
|
330
438
|
});
|
|
331
439
|
const groupName = getGroupName(groupBy);
|
|
332
440
|
return _.groupBy(groupName)(formattedData);
|
|
333
441
|
},
|
|
442
|
+
async getLocalesDataForActions() {
|
|
443
|
+
if (!strapi2.plugin("i18n")) {
|
|
444
|
+
return {};
|
|
445
|
+
}
|
|
446
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
447
|
+
return allLocales.reduce((acc, locale) => {
|
|
448
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
449
|
+
return acc;
|
|
450
|
+
}, {});
|
|
451
|
+
},
|
|
334
452
|
async getContentTypesDataForActions(contentTypesUids) {
|
|
335
453
|
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
336
454
|
const contentTypesData = {};
|
|
@@ -345,6 +463,34 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
345
463
|
}
|
|
346
464
|
return contentTypesData;
|
|
347
465
|
},
|
|
466
|
+
getContentTypeModelsFromActions(actions) {
|
|
467
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
468
|
+
if (!acc.includes(action.contentType)) {
|
|
469
|
+
acc.push(action.contentType);
|
|
470
|
+
}
|
|
471
|
+
return acc;
|
|
472
|
+
}, []);
|
|
473
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
474
|
+
(acc, contentTypeUid) => {
|
|
475
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
476
|
+
return acc;
|
|
477
|
+
},
|
|
478
|
+
{}
|
|
479
|
+
);
|
|
480
|
+
return contentTypeModelsMap;
|
|
481
|
+
},
|
|
482
|
+
async getAllComponents() {
|
|
483
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
484
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
485
|
+
const componentsMap = components.reduce(
|
|
486
|
+
(acc, component) => {
|
|
487
|
+
acc[component.uid] = component;
|
|
488
|
+
return acc;
|
|
489
|
+
},
|
|
490
|
+
{}
|
|
491
|
+
);
|
|
492
|
+
return componentsMap;
|
|
493
|
+
},
|
|
348
494
|
async delete(releaseId) {
|
|
349
495
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
350
496
|
populate: {
|
|
@@ -379,7 +525,9 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
379
525
|
populate: {
|
|
380
526
|
actions: {
|
|
381
527
|
populate: {
|
|
382
|
-
entry:
|
|
528
|
+
entry: {
|
|
529
|
+
fields: ["id"]
|
|
530
|
+
}
|
|
383
531
|
}
|
|
384
532
|
}
|
|
385
533
|
}
|
|
@@ -399,25 +547,49 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
399
547
|
const contentTypeUid = action.contentType;
|
|
400
548
|
if (!actions[contentTypeUid]) {
|
|
401
549
|
actions[contentTypeUid] = {
|
|
402
|
-
|
|
403
|
-
|
|
550
|
+
entriestoPublishIds: [],
|
|
551
|
+
entriesToUnpublishIds: []
|
|
404
552
|
};
|
|
405
553
|
}
|
|
406
554
|
if (action.type === "publish") {
|
|
407
|
-
actions[contentTypeUid].
|
|
555
|
+
actions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
408
556
|
} else {
|
|
409
|
-
actions[contentTypeUid].
|
|
557
|
+
actions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
410
558
|
}
|
|
411
559
|
}
|
|
412
560
|
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
561
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
413
562
|
await strapi2.db.transaction(async () => {
|
|
414
563
|
for (const contentTypeUid of Object.keys(actions)) {
|
|
415
|
-
const
|
|
416
|
-
|
|
417
|
-
|
|
564
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
565
|
+
const { entriestoPublishIds, entriesToUnpublishIds } = actions[contentTypeUid];
|
|
566
|
+
const entriesToPublish = await strapi2.entityService.findMany(
|
|
567
|
+
contentTypeUid,
|
|
568
|
+
{
|
|
569
|
+
filters: {
|
|
570
|
+
id: {
|
|
571
|
+
$in: entriestoPublishIds
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
populate
|
|
575
|
+
}
|
|
576
|
+
);
|
|
577
|
+
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
578
|
+
contentTypeUid,
|
|
579
|
+
{
|
|
580
|
+
filters: {
|
|
581
|
+
id: {
|
|
582
|
+
$in: entriesToUnpublishIds
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
populate
|
|
586
|
+
}
|
|
587
|
+
);
|
|
588
|
+
if (entriesToPublish.length > 0) {
|
|
589
|
+
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
418
590
|
}
|
|
419
|
-
if (
|
|
420
|
-
await entityManagerService.unpublishMany(
|
|
591
|
+
if (entriesToUnpublish.length > 0) {
|
|
592
|
+
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
421
593
|
}
|
|
422
594
|
}
|
|
423
595
|
});
|
|
@@ -436,13 +608,18 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
436
608
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
437
609
|
where: {
|
|
438
610
|
id: actionId,
|
|
439
|
-
release:
|
|
611
|
+
release: {
|
|
612
|
+
id: releaseId,
|
|
613
|
+
releasedAt: {
|
|
614
|
+
$null: true
|
|
615
|
+
}
|
|
616
|
+
}
|
|
440
617
|
},
|
|
441
618
|
data: update
|
|
442
619
|
});
|
|
443
620
|
if (!updatedAction) {
|
|
444
621
|
throw new errors.NotFoundError(
|
|
445
|
-
`Action with id ${actionId} not found in release with id ${releaseId}`
|
|
622
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
446
623
|
);
|
|
447
624
|
}
|
|
448
625
|
return updatedAction;
|
|
@@ -451,12 +628,17 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
451
628
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
452
629
|
where: {
|
|
453
630
|
id: actionId,
|
|
454
|
-
release:
|
|
631
|
+
release: {
|
|
632
|
+
id: releaseId,
|
|
633
|
+
releasedAt: {
|
|
634
|
+
$null: true
|
|
635
|
+
}
|
|
636
|
+
}
|
|
455
637
|
}
|
|
456
638
|
});
|
|
457
639
|
if (!deletedAction) {
|
|
458
640
|
throw new errors.NotFoundError(
|
|
459
|
-
`Action with id ${actionId} not found in release with id ${releaseId}`
|
|
641
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
460
642
|
);
|
|
461
643
|
}
|
|
462
644
|
return deletedAction;
|
|
@@ -489,9 +671,42 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
489
671
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
490
672
|
);
|
|
491
673
|
}
|
|
674
|
+
},
|
|
675
|
+
async validatePendingReleasesLimit() {
|
|
676
|
+
const maximumPendingReleases = (
|
|
677
|
+
// @ts-expect-error - options is not typed into features
|
|
678
|
+
EE.features.get("cms-content-releases")?.options?.maximumReleases || 3
|
|
679
|
+
);
|
|
680
|
+
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
681
|
+
filters: {
|
|
682
|
+
releasedAt: {
|
|
683
|
+
$null: true
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
});
|
|
687
|
+
if (pendingReleasesCount >= maximumPendingReleases) {
|
|
688
|
+
throw new errors.ValidationError("You have reached the maximum number of pending releases");
|
|
689
|
+
}
|
|
690
|
+
},
|
|
691
|
+
async validateUniqueNameForPendingRelease(name) {
|
|
692
|
+
const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
|
|
693
|
+
filters: {
|
|
694
|
+
releasedAt: {
|
|
695
|
+
$null: true
|
|
696
|
+
},
|
|
697
|
+
name
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
const isNameUnique = pendingReleases.length === 0;
|
|
701
|
+
if (!isNameUnique) {
|
|
702
|
+
throw new errors.ValidationError(`Release with name ${name} already exists`);
|
|
703
|
+
}
|
|
492
704
|
}
|
|
493
705
|
});
|
|
494
|
-
const services = {
|
|
706
|
+
const services = {
|
|
707
|
+
release: createReleaseService,
|
|
708
|
+
"release-validation": createReleaseValidationService
|
|
709
|
+
};
|
|
495
710
|
const RELEASE_SCHEMA = yup.object().shape({
|
|
496
711
|
name: yup.string().trim().required()
|
|
497
712
|
}).required().noUnknown();
|
|
@@ -510,9 +725,7 @@ const releaseController = {
|
|
|
510
725
|
const contentTypeUid = query.contentTypeUid;
|
|
511
726
|
const entryId = query.entryId;
|
|
512
727
|
const hasEntryAttached = typeof query.hasEntryAttached === "string" ? JSON.parse(query.hasEntryAttached) : false;
|
|
513
|
-
const data = await releaseService.
|
|
514
|
-
hasEntryAttached
|
|
515
|
-
});
|
|
728
|
+
const data = hasEntryAttached ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId) : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);
|
|
516
729
|
ctx.body = { data };
|
|
517
730
|
} else {
|
|
518
731
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
@@ -640,11 +853,30 @@ const releaseActionController = {
|
|
|
640
853
|
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
641
854
|
...query
|
|
642
855
|
});
|
|
643
|
-
const
|
|
856
|
+
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
857
|
+
if (acc[action.contentType]) {
|
|
858
|
+
return acc;
|
|
859
|
+
}
|
|
860
|
+
const contentTypePermissionsManager = strapi.admin.services.permission.createPermissionsManager({
|
|
861
|
+
ability: ctx.state.userAbility,
|
|
862
|
+
model: action.contentType
|
|
863
|
+
});
|
|
864
|
+
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
865
|
+
return acc;
|
|
866
|
+
}, {});
|
|
867
|
+
const sanitizedResults = await mapAsync(results, async (action) => ({
|
|
868
|
+
...action,
|
|
869
|
+
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
870
|
+
}));
|
|
871
|
+
const groupedData = await releaseService.groupActions(sanitizedResults, query.groupBy);
|
|
872
|
+
const contentTypes2 = releaseService.getContentTypeModelsFromActions(results);
|
|
873
|
+
const components = await releaseService.getAllComponents();
|
|
644
874
|
ctx.body = {
|
|
645
875
|
data: groupedData,
|
|
646
876
|
meta: {
|
|
647
|
-
pagination
|
|
877
|
+
pagination,
|
|
878
|
+
contentTypes: contentTypes2,
|
|
879
|
+
components
|
|
648
880
|
}
|
|
649
881
|
};
|
|
650
882
|
},
|
|
@@ -666,10 +898,8 @@ const releaseActionController = {
|
|
|
666
898
|
async delete(ctx) {
|
|
667
899
|
const actionId = ctx.params.actionId;
|
|
668
900
|
const releaseId = ctx.params.releaseId;
|
|
669
|
-
const
|
|
670
|
-
|
|
671
|
-
releaseId
|
|
672
|
-
);
|
|
901
|
+
const releaseService = getService("release", { strapi });
|
|
902
|
+
const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);
|
|
673
903
|
ctx.body = {
|
|
674
904
|
data: deletedReleaseAction
|
|
675
905
|
};
|
|
@@ -852,9 +1082,10 @@ const routes = {
|
|
|
852
1082
|
};
|
|
853
1083
|
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
854
1084
|
const getPlugin = () => {
|
|
855
|
-
if (features.isEnabled("cms-content-releases")
|
|
1085
|
+
if (features.isEnabled("cms-content-releases")) {
|
|
856
1086
|
return {
|
|
857
1087
|
register,
|
|
1088
|
+
bootstrap,
|
|
858
1089
|
contentTypes,
|
|
859
1090
|
services,
|
|
860
1091
|
controllers,
|