@strapi/content-releases 4.18.1-experimental.0 → 4.19.1
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.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const utils = require("@strapi/utils");
|
|
3
|
+
const lodash = require("lodash");
|
|
3
4
|
const _ = require("lodash/fp");
|
|
5
|
+
const EE = require("@strapi/strapi/dist/utils/ee");
|
|
4
6
|
const yup = require("yup");
|
|
5
7
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
6
8
|
function _interopNamespace(e) {
|
|
@@ -22,6 +24,7 @@ function _interopNamespace(e) {
|
|
|
22
24
|
return Object.freeze(n);
|
|
23
25
|
}
|
|
24
26
|
const ___default = /* @__PURE__ */ _interopDefault(_);
|
|
27
|
+
const EE__default = /* @__PURE__ */ _interopDefault(EE);
|
|
25
28
|
const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
|
|
26
29
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
27
30
|
const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
|
|
@@ -69,10 +72,87 @@ const ACTIONS = [
|
|
|
69
72
|
pluginName: "content-releases"
|
|
70
73
|
}
|
|
71
74
|
];
|
|
72
|
-
|
|
75
|
+
async function deleteActionsOnDisableDraftAndPublish({
|
|
76
|
+
oldContentTypes,
|
|
77
|
+
contentTypes: contentTypes2
|
|
78
|
+
}) {
|
|
79
|
+
if (!oldContentTypes) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
for (const uid in contentTypes2) {
|
|
83
|
+
if (!oldContentTypes[uid]) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const oldContentType = oldContentTypes[uid];
|
|
87
|
+
const contentType = contentTypes2[uid];
|
|
88
|
+
if (utils.contentTypes.hasDraftAndPublish(oldContentType) && !utils.contentTypes.hasDraftAndPublish(contentType)) {
|
|
89
|
+
await strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: uid }).execute();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
94
|
+
const deletedContentTypes = lodash.difference(lodash.keys(oldContentTypes), lodash.keys(contentTypes2)) ?? [];
|
|
95
|
+
if (deletedContentTypes.length) {
|
|
96
|
+
await utils.mapAsync(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
97
|
+
return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
|
|
73
102
|
const register = async ({ strapi: strapi2 }) => {
|
|
74
|
-
if (features$
|
|
103
|
+
if (features$2.isEnabled("cms-content-releases")) {
|
|
75
104
|
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);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
110
|
+
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
111
|
+
if (features$1.isEnabled("cms-content-releases")) {
|
|
112
|
+
strapi2.db.lifecycles.subscribe({
|
|
113
|
+
afterDelete(event) {
|
|
114
|
+
const { model, result } = event;
|
|
115
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
116
|
+
const { id } = result;
|
|
117
|
+
strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
118
|
+
where: {
|
|
119
|
+
target_type: model.uid,
|
|
120
|
+
target_id: id
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
/**
|
|
126
|
+
* deleteMany hook doesn't return the deleted entries ids
|
|
127
|
+
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
128
|
+
*/
|
|
129
|
+
async beforeDeleteMany(event) {
|
|
130
|
+
const { model, params } = event;
|
|
131
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
132
|
+
const { where } = params;
|
|
133
|
+
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
134
|
+
event.state.entriesToDelete = entriesToDelete;
|
|
135
|
+
}
|
|
136
|
+
},
|
|
137
|
+
/**
|
|
138
|
+
* We delete the release actions related to deleted entries
|
|
139
|
+
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
140
|
+
*/
|
|
141
|
+
async afterDeleteMany(event) {
|
|
142
|
+
const { model, state } = event;
|
|
143
|
+
const entriesToDelete = state.entriesToDelete;
|
|
144
|
+
if (entriesToDelete) {
|
|
145
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
146
|
+
where: {
|
|
147
|
+
target_type: model.uid,
|
|
148
|
+
target_id: {
|
|
149
|
+
$in: entriesToDelete.map((entry) => entry.id)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
76
156
|
}
|
|
77
157
|
};
|
|
78
158
|
const schema$1 = {
|
|
@@ -169,18 +249,26 @@ const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
|
169
249
|
const getGroupName = (queryValue) => {
|
|
170
250
|
switch (queryValue) {
|
|
171
251
|
case "contentType":
|
|
172
|
-
return "
|
|
252
|
+
return "contentType.displayName";
|
|
173
253
|
case "action":
|
|
174
254
|
return "type";
|
|
175
255
|
case "locale":
|
|
176
|
-
return ___default.default.getOr("No locale", "
|
|
256
|
+
return ___default.default.getOr("No locale", "locale.name");
|
|
177
257
|
default:
|
|
178
|
-
return "
|
|
258
|
+
return "contentType.displayName";
|
|
179
259
|
}
|
|
180
260
|
};
|
|
181
261
|
const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
182
262
|
async create(releaseData, { user }) {
|
|
183
263
|
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
264
|
+
const { validatePendingReleasesLimit, validateUniqueNameForPendingRelease } = getService(
|
|
265
|
+
"release-validation",
|
|
266
|
+
{ strapi: strapi2 }
|
|
267
|
+
);
|
|
268
|
+
await Promise.all([
|
|
269
|
+
validatePendingReleasesLimit(),
|
|
270
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name)
|
|
271
|
+
]);
|
|
184
272
|
return strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
185
273
|
data: releaseWithCreatorFields
|
|
186
274
|
});
|
|
@@ -202,51 +290,66 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
202
290
|
}
|
|
203
291
|
});
|
|
204
292
|
},
|
|
205
|
-
async
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// Find all Releases where the content type entry is present
|
|
212
|
-
actions: {
|
|
213
|
-
target_type: contentTypeUid,
|
|
214
|
-
target_id: entryId
|
|
215
|
-
}
|
|
216
|
-
} : {
|
|
217
|
-
// Find all Releases where the content type entry is not present
|
|
218
|
-
$or: [
|
|
219
|
-
{
|
|
220
|
-
$not: {
|
|
221
|
-
actions: {
|
|
222
|
-
target_type: contentTypeUid,
|
|
223
|
-
target_id: entryId
|
|
224
|
-
}
|
|
225
|
-
}
|
|
293
|
+
async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
294
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
295
|
+
where: {
|
|
296
|
+
actions: {
|
|
297
|
+
target_type: contentTypeUid,
|
|
298
|
+
target_id: entryId
|
|
226
299
|
},
|
|
227
|
-
{
|
|
228
|
-
|
|
300
|
+
releasedAt: {
|
|
301
|
+
$null: true
|
|
229
302
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
303
|
+
},
|
|
304
|
+
populate: {
|
|
305
|
+
// Filter the action to get only the content type entry
|
|
306
|
+
actions: {
|
|
307
|
+
where: {
|
|
308
|
+
target_type: contentTypeUid,
|
|
309
|
+
target_id: entryId
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
});
|
|
314
|
+
return releases.map((release2) => {
|
|
315
|
+
if (release2.actions?.length) {
|
|
316
|
+
const [actionForEntry] = release2.actions;
|
|
317
|
+
delete release2.actions;
|
|
318
|
+
return {
|
|
319
|
+
...release2,
|
|
320
|
+
action: actionForEntry
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
return release2;
|
|
324
|
+
});
|
|
325
|
+
},
|
|
326
|
+
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
327
|
+
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
328
|
+
where: {
|
|
329
|
+
releasedAt: {
|
|
330
|
+
$null: true
|
|
331
|
+
},
|
|
332
|
+
actions: {
|
|
236
333
|
target_type: contentTypeUid,
|
|
237
334
|
target_id: entryId
|
|
238
335
|
}
|
|
239
336
|
}
|
|
240
|
-
}
|
|
337
|
+
});
|
|
241
338
|
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
242
339
|
where: {
|
|
243
|
-
|
|
340
|
+
$or: [
|
|
341
|
+
{
|
|
342
|
+
id: {
|
|
343
|
+
$notIn: releasesRelated.map((release2) => release2.id)
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
actions: null
|
|
348
|
+
}
|
|
349
|
+
],
|
|
244
350
|
releasedAt: {
|
|
245
351
|
$null: true
|
|
246
352
|
}
|
|
247
|
-
},
|
|
248
|
-
populate: {
|
|
249
|
-
...populateAttachedAction
|
|
250
353
|
}
|
|
251
354
|
});
|
|
252
355
|
return releases.map((release2) => {
|
|
@@ -262,19 +365,23 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
262
365
|
});
|
|
263
366
|
},
|
|
264
367
|
async update(id, releaseData, { user }) {
|
|
265
|
-
const
|
|
266
|
-
const release2 = await strapi2.entityService.
|
|
368
|
+
const releaseWithCreatorFields = await utils.setCreatorFields({ user, isEdition: true })(releaseData);
|
|
369
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id);
|
|
370
|
+
if (!release2) {
|
|
371
|
+
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
372
|
+
}
|
|
373
|
+
if (release2.releasedAt) {
|
|
374
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
375
|
+
}
|
|
376
|
+
const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
|
|
267
377
|
/*
|
|
268
378
|
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
269
379
|
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
270
380
|
*/
|
|
271
381
|
// @ts-expect-error see above
|
|
272
|
-
data:
|
|
382
|
+
data: releaseWithCreatorFields
|
|
273
383
|
});
|
|
274
|
-
|
|
275
|
-
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
276
|
-
}
|
|
277
|
-
return release2;
|
|
384
|
+
return updatedRelease;
|
|
278
385
|
},
|
|
279
386
|
async createAction(releaseId, action) {
|
|
280
387
|
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
@@ -284,6 +391,13 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
284
391
|
validateEntryContentType(action.entry.contentType),
|
|
285
392
|
validateUniqueEntry(releaseId, action)
|
|
286
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
|
+
}
|
|
287
401
|
const { entry, type } = action;
|
|
288
402
|
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
289
403
|
data: {
|
|
@@ -310,7 +424,9 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
310
424
|
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
311
425
|
...query,
|
|
312
426
|
populate: {
|
|
313
|
-
entry:
|
|
427
|
+
entry: {
|
|
428
|
+
populate: "*"
|
|
429
|
+
}
|
|
314
430
|
},
|
|
315
431
|
filters: {
|
|
316
432
|
release: releaseId
|
|
@@ -330,29 +446,32 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
330
446
|
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
331
447
|
contentTypeUids
|
|
332
448
|
);
|
|
333
|
-
const
|
|
334
|
-
const allLocalesDictionary = allLocales.reduce((acc, locale) => {
|
|
335
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
336
|
-
return acc;
|
|
337
|
-
}, {});
|
|
449
|
+
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
338
450
|
const formattedData = actions.map((action) => {
|
|
339
451
|
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
340
452
|
return {
|
|
341
453
|
...action,
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
},
|
|
348
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
349
|
-
status: action.entry.publishedAt ? "published" : "draft"
|
|
454
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
455
|
+
contentType: {
|
|
456
|
+
displayName,
|
|
457
|
+
mainFieldValue: action.entry[mainField],
|
|
458
|
+
uid: action.contentType
|
|
350
459
|
}
|
|
351
460
|
};
|
|
352
461
|
});
|
|
353
462
|
const groupName = getGroupName(groupBy);
|
|
354
463
|
return ___default.default.groupBy(groupName)(formattedData);
|
|
355
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
|
+
},
|
|
356
475
|
async getContentTypesDataForActions(contentTypesUids) {
|
|
357
476
|
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
358
477
|
const contentTypesData = {};
|
|
@@ -367,6 +486,34 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
367
486
|
}
|
|
368
487
|
return contentTypesData;
|
|
369
488
|
},
|
|
489
|
+
getContentTypeModelsFromActions(actions) {
|
|
490
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
491
|
+
if (!acc.includes(action.contentType)) {
|
|
492
|
+
acc.push(action.contentType);
|
|
493
|
+
}
|
|
494
|
+
return acc;
|
|
495
|
+
}, []);
|
|
496
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
497
|
+
(acc, contentTypeUid) => {
|
|
498
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
499
|
+
return acc;
|
|
500
|
+
},
|
|
501
|
+
{}
|
|
502
|
+
);
|
|
503
|
+
return contentTypeModelsMap;
|
|
504
|
+
},
|
|
505
|
+
async getAllComponents() {
|
|
506
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
507
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
508
|
+
const componentsMap = components.reduce(
|
|
509
|
+
(acc, component) => {
|
|
510
|
+
acc[component.uid] = component;
|
|
511
|
+
return acc;
|
|
512
|
+
},
|
|
513
|
+
{}
|
|
514
|
+
);
|
|
515
|
+
return componentsMap;
|
|
516
|
+
},
|
|
370
517
|
async delete(releaseId) {
|
|
371
518
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
372
519
|
populate: {
|
|
@@ -401,7 +548,9 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
401
548
|
populate: {
|
|
402
549
|
actions: {
|
|
403
550
|
populate: {
|
|
404
|
-
entry:
|
|
551
|
+
entry: {
|
|
552
|
+
fields: ["id"]
|
|
553
|
+
}
|
|
405
554
|
}
|
|
406
555
|
}
|
|
407
556
|
}
|
|
@@ -421,25 +570,49 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
421
570
|
const contentTypeUid = action.contentType;
|
|
422
571
|
if (!actions[contentTypeUid]) {
|
|
423
572
|
actions[contentTypeUid] = {
|
|
424
|
-
|
|
425
|
-
|
|
573
|
+
entriestoPublishIds: [],
|
|
574
|
+
entriesToUnpublishIds: []
|
|
426
575
|
};
|
|
427
576
|
}
|
|
428
577
|
if (action.type === "publish") {
|
|
429
|
-
actions[contentTypeUid].
|
|
578
|
+
actions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
430
579
|
} else {
|
|
431
|
-
actions[contentTypeUid].
|
|
580
|
+
actions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
432
581
|
}
|
|
433
582
|
}
|
|
434
583
|
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
584
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
435
585
|
await strapi2.db.transaction(async () => {
|
|
436
586
|
for (const contentTypeUid of Object.keys(actions)) {
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
587
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
588
|
+
const { entriestoPublishIds, entriesToUnpublishIds } = actions[contentTypeUid];
|
|
589
|
+
const entriesToPublish = await strapi2.entityService.findMany(
|
|
590
|
+
contentTypeUid,
|
|
591
|
+
{
|
|
592
|
+
filters: {
|
|
593
|
+
id: {
|
|
594
|
+
$in: entriestoPublishIds
|
|
595
|
+
}
|
|
596
|
+
},
|
|
597
|
+
populate
|
|
598
|
+
}
|
|
599
|
+
);
|
|
600
|
+
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
601
|
+
contentTypeUid,
|
|
602
|
+
{
|
|
603
|
+
filters: {
|
|
604
|
+
id: {
|
|
605
|
+
$in: entriesToUnpublishIds
|
|
606
|
+
}
|
|
607
|
+
},
|
|
608
|
+
populate
|
|
609
|
+
}
|
|
610
|
+
);
|
|
611
|
+
if (entriesToPublish.length > 0) {
|
|
612
|
+
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
440
613
|
}
|
|
441
|
-
if (
|
|
442
|
-
await entityManagerService.unpublishMany(
|
|
614
|
+
if (entriesToUnpublish.length > 0) {
|
|
615
|
+
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
443
616
|
}
|
|
444
617
|
}
|
|
445
618
|
});
|
|
@@ -458,13 +631,18 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
458
631
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
459
632
|
where: {
|
|
460
633
|
id: actionId,
|
|
461
|
-
release:
|
|
634
|
+
release: {
|
|
635
|
+
id: releaseId,
|
|
636
|
+
releasedAt: {
|
|
637
|
+
$null: true
|
|
638
|
+
}
|
|
639
|
+
}
|
|
462
640
|
},
|
|
463
641
|
data: update
|
|
464
642
|
});
|
|
465
643
|
if (!updatedAction) {
|
|
466
644
|
throw new utils.errors.NotFoundError(
|
|
467
|
-
`Action with id ${actionId} not found in release with id ${releaseId}`
|
|
645
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
468
646
|
);
|
|
469
647
|
}
|
|
470
648
|
return updatedAction;
|
|
@@ -473,12 +651,17 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
473
651
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
474
652
|
where: {
|
|
475
653
|
id: actionId,
|
|
476
|
-
release:
|
|
654
|
+
release: {
|
|
655
|
+
id: releaseId,
|
|
656
|
+
releasedAt: {
|
|
657
|
+
$null: true
|
|
658
|
+
}
|
|
659
|
+
}
|
|
477
660
|
}
|
|
478
661
|
});
|
|
479
662
|
if (!deletedAction) {
|
|
480
663
|
throw new utils.errors.NotFoundError(
|
|
481
|
-
`Action with id ${actionId} not found in release with id ${releaseId}`
|
|
664
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
482
665
|
);
|
|
483
666
|
}
|
|
484
667
|
return deletedAction;
|
|
@@ -511,9 +694,42 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
511
694
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
512
695
|
);
|
|
513
696
|
}
|
|
697
|
+
},
|
|
698
|
+
async validatePendingReleasesLimit() {
|
|
699
|
+
const maximumPendingReleases = (
|
|
700
|
+
// @ts-expect-error - options is not typed into features
|
|
701
|
+
EE__default.default.features.get("cms-content-releases")?.options?.maximumReleases || 3
|
|
702
|
+
);
|
|
703
|
+
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
704
|
+
filters: {
|
|
705
|
+
releasedAt: {
|
|
706
|
+
$null: true
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
if (pendingReleasesCount >= maximumPendingReleases) {
|
|
711
|
+
throw new utils.errors.ValidationError("You have reached the maximum number of pending releases");
|
|
712
|
+
}
|
|
713
|
+
},
|
|
714
|
+
async validateUniqueNameForPendingRelease(name) {
|
|
715
|
+
const pendingReleases = await strapi2.entityService.findMany(RELEASE_MODEL_UID, {
|
|
716
|
+
filters: {
|
|
717
|
+
releasedAt: {
|
|
718
|
+
$null: true
|
|
719
|
+
},
|
|
720
|
+
name
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
const isNameUnique = pendingReleases.length === 0;
|
|
724
|
+
if (!isNameUnique) {
|
|
725
|
+
throw new utils.errors.ValidationError(`Release with name ${name} already exists`);
|
|
726
|
+
}
|
|
514
727
|
}
|
|
515
728
|
});
|
|
516
|
-
const services = {
|
|
729
|
+
const services = {
|
|
730
|
+
release: createReleaseService,
|
|
731
|
+
"release-validation": createReleaseValidationService
|
|
732
|
+
};
|
|
517
733
|
const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
518
734
|
name: yup__namespace.string().trim().required()
|
|
519
735
|
}).required().noUnknown();
|
|
@@ -532,9 +748,7 @@ const releaseController = {
|
|
|
532
748
|
const contentTypeUid = query.contentTypeUid;
|
|
533
749
|
const entryId = query.entryId;
|
|
534
750
|
const hasEntryAttached = typeof query.hasEntryAttached === "string" ? JSON.parse(query.hasEntryAttached) : false;
|
|
535
|
-
const data = await releaseService.
|
|
536
|
-
hasEntryAttached
|
|
537
|
-
});
|
|
751
|
+
const data = hasEntryAttached ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId) : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);
|
|
538
752
|
ctx.body = { data };
|
|
539
753
|
} else {
|
|
540
754
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
@@ -662,11 +876,30 @@ const releaseActionController = {
|
|
|
662
876
|
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
663
877
|
...query
|
|
664
878
|
});
|
|
665
|
-
const
|
|
879
|
+
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
880
|
+
if (acc[action.contentType]) {
|
|
881
|
+
return acc;
|
|
882
|
+
}
|
|
883
|
+
const contentTypePermissionsManager = strapi.admin.services.permission.createPermissionsManager({
|
|
884
|
+
ability: ctx.state.userAbility,
|
|
885
|
+
model: action.contentType
|
|
886
|
+
});
|
|
887
|
+
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
888
|
+
return acc;
|
|
889
|
+
}, {});
|
|
890
|
+
const sanitizedResults = await utils.mapAsync(results, async (action) => ({
|
|
891
|
+
...action,
|
|
892
|
+
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
893
|
+
}));
|
|
894
|
+
const groupedData = await releaseService.groupActions(sanitizedResults, query.groupBy);
|
|
895
|
+
const contentTypes2 = releaseService.getContentTypeModelsFromActions(results);
|
|
896
|
+
const components = await releaseService.getAllComponents();
|
|
666
897
|
ctx.body = {
|
|
667
898
|
data: groupedData,
|
|
668
899
|
meta: {
|
|
669
|
-
pagination
|
|
900
|
+
pagination,
|
|
901
|
+
contentTypes: contentTypes2,
|
|
902
|
+
components
|
|
670
903
|
}
|
|
671
904
|
};
|
|
672
905
|
},
|
|
@@ -688,10 +921,8 @@ const releaseActionController = {
|
|
|
688
921
|
async delete(ctx) {
|
|
689
922
|
const actionId = ctx.params.actionId;
|
|
690
923
|
const releaseId = ctx.params.releaseId;
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
releaseId
|
|
694
|
-
);
|
|
924
|
+
const releaseService = getService("release", { strapi });
|
|
925
|
+
const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);
|
|
695
926
|
ctx.body = {
|
|
696
927
|
data: deletedReleaseAction
|
|
697
928
|
};
|
|
@@ -874,9 +1105,10 @@ const routes = {
|
|
|
874
1105
|
};
|
|
875
1106
|
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
876
1107
|
const getPlugin = () => {
|
|
877
|
-
if (features.isEnabled("cms-content-releases")
|
|
1108
|
+
if (features.isEnabled("cms-content-releases")) {
|
|
878
1109
|
return {
|
|
879
1110
|
register,
|
|
1111
|
+
bootstrap,
|
|
880
1112
|
contentTypes,
|
|
881
1113
|
services,
|
|
882
1114
|
controllers,
|