@strapi/content-releases 0.0.0-experimental.f7b9b47085e387e97f990d8695971b51d7f7149a → 0.0.0-experimental.fb8e9fec2e10d6b55e3aee59c4eb76bb7a11432a
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-a4843fda.mjs → App-L1jSxCiL.mjs} +277 -117
- package/dist/_chunks/App-L1jSxCiL.mjs.map +1 -0
- package/dist/_chunks/{App-f2cafd81.js → App-_20W9dYa.js} +274 -114
- package/dist/_chunks/App-_20W9dYa.js.map +1 -0
- package/dist/_chunks/{en-e98d8b57.mjs → en-MyLPoISH.mjs} +14 -4
- package/dist/_chunks/en-MyLPoISH.mjs.map +1 -0
- package/dist/_chunks/{en-13576ce2.js → en-gYDqKYFd.js} +14 -4
- package/dist/_chunks/en-gYDqKYFd.js.map +1 -0
- package/dist/_chunks/{index-66d129ac.js → index-KJa1Rb5F.js} +107 -37
- package/dist/_chunks/index-KJa1Rb5F.js.map +1 -0
- package/dist/_chunks/{index-937f8179.mjs → index-c4zRX_sg.mjs} +117 -47
- 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 +359 -102
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +357 -103
- package/dist/server/index.mjs.map +1 -1
- package/package.json +11 -9
- package/dist/_chunks/App-a4843fda.mjs.map +0 -1
- package/dist/_chunks/App-f2cafd81.js.map +0 -1
- package/dist/_chunks/en-13576ce2.js.map +0 -1
- package/dist/_chunks/en-e98d8b57.mjs.map +0 -1
- package/dist/_chunks/index-66d129ac.js.map +0 -1
- package/dist/_chunks/index-937f8179.mjs.map +0 -1
package/dist/server/index.mjs
CHANGED
|
@@ -1,4 +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";
|
|
3
|
+
import _ from "lodash/fp";
|
|
4
|
+
import EE from "@strapi/strapi/dist/utils/ee";
|
|
2
5
|
import * as yup from "yup";
|
|
3
6
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
4
7
|
const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
|
|
@@ -46,10 +49,87 @@ const ACTIONS = [
|
|
|
46
49
|
pluginName: "content-releases"
|
|
47
50
|
}
|
|
48
51
|
];
|
|
49
|
-
|
|
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");
|
|
50
79
|
const register = async ({ strapi: strapi2 }) => {
|
|
51
|
-
if (features$
|
|
80
|
+
if (features$2.isEnabled("cms-content-releases")) {
|
|
52
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
|
+
});
|
|
53
133
|
}
|
|
54
134
|
};
|
|
55
135
|
const schema$1 = {
|
|
@@ -122,6 +202,9 @@ const schema = {
|
|
|
122
202
|
type: "string",
|
|
123
203
|
required: true
|
|
124
204
|
},
|
|
205
|
+
locale: {
|
|
206
|
+
type: "string"
|
|
207
|
+
},
|
|
125
208
|
release: {
|
|
126
209
|
type: "relation",
|
|
127
210
|
relation: "manyToOne",
|
|
@@ -140,9 +223,29 @@ const contentTypes = {
|
|
|
140
223
|
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
141
224
|
return strapi2.plugin("content-releases").service(name);
|
|
142
225
|
};
|
|
226
|
+
const getGroupName = (queryValue) => {
|
|
227
|
+
switch (queryValue) {
|
|
228
|
+
case "contentType":
|
|
229
|
+
return "contentType.displayName";
|
|
230
|
+
case "action":
|
|
231
|
+
return "type";
|
|
232
|
+
case "locale":
|
|
233
|
+
return _.getOr("No locale", "locale.name");
|
|
234
|
+
default:
|
|
235
|
+
return "contentType.displayName";
|
|
236
|
+
}
|
|
237
|
+
};
|
|
143
238
|
const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
144
239
|
async create(releaseData, { user }) {
|
|
145
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
|
+
]);
|
|
146
249
|
return strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
147
250
|
data: releaseWithCreatorFields
|
|
148
251
|
});
|
|
@@ -164,51 +267,66 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
164
267
|
}
|
|
165
268
|
});
|
|
166
269
|
},
|
|
167
|
-
async
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
// Find all Releases where the content type entry is present
|
|
174
|
-
actions: {
|
|
175
|
-
target_type: contentTypeUid,
|
|
176
|
-
target_id: entryId
|
|
177
|
-
}
|
|
178
|
-
} : {
|
|
179
|
-
// Find all Releases where the content type entry is not present
|
|
180
|
-
$or: [
|
|
181
|
-
{
|
|
182
|
-
$not: {
|
|
183
|
-
actions: {
|
|
184
|
-
target_type: contentTypeUid,
|
|
185
|
-
target_id: entryId
|
|
186
|
-
}
|
|
187
|
-
}
|
|
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
|
|
188
276
|
},
|
|
189
|
-
{
|
|
190
|
-
|
|
277
|
+
releasedAt: {
|
|
278
|
+
$null: true
|
|
191
279
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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: {
|
|
198
310
|
target_type: contentTypeUid,
|
|
199
311
|
target_id: entryId
|
|
200
312
|
}
|
|
201
313
|
}
|
|
202
|
-
}
|
|
314
|
+
});
|
|
203
315
|
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
204
316
|
where: {
|
|
205
|
-
|
|
317
|
+
$or: [
|
|
318
|
+
{
|
|
319
|
+
id: {
|
|
320
|
+
$notIn: releasesRelated.map((release2) => release2.id)
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
actions: null
|
|
325
|
+
}
|
|
326
|
+
],
|
|
206
327
|
releasedAt: {
|
|
207
328
|
$null: true
|
|
208
329
|
}
|
|
209
|
-
},
|
|
210
|
-
populate: {
|
|
211
|
-
...populateAttachedAction
|
|
212
330
|
}
|
|
213
331
|
});
|
|
214
332
|
return releases.map((release2) => {
|
|
@@ -224,19 +342,23 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
224
342
|
});
|
|
225
343
|
},
|
|
226
344
|
async update(id, releaseData, { user }) {
|
|
227
|
-
const
|
|
228
|
-
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, {
|
|
229
354
|
/*
|
|
230
355
|
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
231
356
|
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
232
357
|
*/
|
|
233
358
|
// @ts-expect-error see above
|
|
234
|
-
data:
|
|
359
|
+
data: releaseWithCreatorFields
|
|
235
360
|
});
|
|
236
|
-
|
|
237
|
-
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
238
|
-
}
|
|
239
|
-
return release2;
|
|
361
|
+
return updatedRelease;
|
|
240
362
|
},
|
|
241
363
|
async createAction(releaseId, action) {
|
|
242
364
|
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
@@ -246,11 +368,19 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
246
368
|
validateEntryContentType(action.entry.contentType),
|
|
247
369
|
validateUniqueEntry(releaseId, action)
|
|
248
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
|
+
}
|
|
249
378
|
const { entry, type } = action;
|
|
250
379
|
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
251
380
|
data: {
|
|
252
381
|
type,
|
|
253
382
|
contentType: entry.contentType,
|
|
383
|
+
locale: entry.locale,
|
|
254
384
|
entry: {
|
|
255
385
|
id: entry.id,
|
|
256
386
|
__type: entry.contentType,
|
|
@@ -262,14 +392,18 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
262
392
|
});
|
|
263
393
|
},
|
|
264
394
|
async findActions(releaseId, query) {
|
|
265
|
-
const
|
|
266
|
-
|
|
395
|
+
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
396
|
+
fields: ["id"]
|
|
397
|
+
});
|
|
398
|
+
if (!release2) {
|
|
267
399
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
268
400
|
}
|
|
269
401
|
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
270
402
|
...query,
|
|
271
403
|
populate: {
|
|
272
|
-
entry:
|
|
404
|
+
entry: {
|
|
405
|
+
populate: "*"
|
|
406
|
+
}
|
|
273
407
|
},
|
|
274
408
|
filters: {
|
|
275
409
|
release: releaseId
|
|
@@ -279,18 +413,43 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
279
413
|
async countActions(query) {
|
|
280
414
|
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
281
415
|
},
|
|
282
|
-
async
|
|
283
|
-
const
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
416
|
+
async groupActions(actions, groupBy) {
|
|
417
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
418
|
+
if (!acc.includes(action.contentType)) {
|
|
419
|
+
acc.push(action.contentType);
|
|
420
|
+
}
|
|
421
|
+
return acc;
|
|
422
|
+
}, []);
|
|
423
|
+
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
424
|
+
contentTypeUids
|
|
425
|
+
);
|
|
426
|
+
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
427
|
+
const formattedData = actions.map((action) => {
|
|
428
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
429
|
+
return {
|
|
430
|
+
...action,
|
|
431
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
432
|
+
contentType: {
|
|
433
|
+
displayName,
|
|
434
|
+
mainFieldValue: action.entry[mainField],
|
|
435
|
+
uid: action.contentType
|
|
287
436
|
}
|
|
288
|
-
|
|
289
|
-
})
|
|
290
|
-
|
|
437
|
+
};
|
|
438
|
+
});
|
|
439
|
+
const groupName = getGroupName(groupBy);
|
|
440
|
+
return _.groupBy(groupName)(formattedData);
|
|
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
|
+
}, {});
|
|
291
451
|
},
|
|
292
|
-
async getContentTypesDataForActions(
|
|
293
|
-
const contentTypesUids = await this.getAllContentTypeUids(releaseId);
|
|
452
|
+
async getContentTypesDataForActions(contentTypesUids) {
|
|
294
453
|
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
295
454
|
const contentTypesData = {};
|
|
296
455
|
for (const contentTypeUid of contentTypesUids) {
|
|
@@ -304,6 +463,34 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
304
463
|
}
|
|
305
464
|
return contentTypesData;
|
|
306
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
|
+
},
|
|
307
494
|
async delete(releaseId) {
|
|
308
495
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
309
496
|
populate: {
|
|
@@ -338,7 +525,9 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
338
525
|
populate: {
|
|
339
526
|
actions: {
|
|
340
527
|
populate: {
|
|
341
|
-
entry:
|
|
528
|
+
entry: {
|
|
529
|
+
fields: ["id"]
|
|
530
|
+
}
|
|
342
531
|
}
|
|
343
532
|
}
|
|
344
533
|
}
|
|
@@ -358,25 +547,49 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
358
547
|
const contentTypeUid = action.contentType;
|
|
359
548
|
if (!actions[contentTypeUid]) {
|
|
360
549
|
actions[contentTypeUid] = {
|
|
361
|
-
|
|
362
|
-
|
|
550
|
+
entriestoPublishIds: [],
|
|
551
|
+
entriesToUnpublishIds: []
|
|
363
552
|
};
|
|
364
553
|
}
|
|
365
554
|
if (action.type === "publish") {
|
|
366
|
-
actions[contentTypeUid].
|
|
555
|
+
actions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
367
556
|
} else {
|
|
368
|
-
actions[contentTypeUid].
|
|
557
|
+
actions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
369
558
|
}
|
|
370
559
|
}
|
|
371
560
|
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
561
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
372
562
|
await strapi2.db.transaction(async () => {
|
|
373
563
|
for (const contentTypeUid of Object.keys(actions)) {
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
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);
|
|
377
590
|
}
|
|
378
|
-
if (
|
|
379
|
-
await entityManagerService.unpublishMany(
|
|
591
|
+
if (entriesToUnpublish.length > 0) {
|
|
592
|
+
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
380
593
|
}
|
|
381
594
|
}
|
|
382
595
|
});
|
|
@@ -395,13 +608,18 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
395
608
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
396
609
|
where: {
|
|
397
610
|
id: actionId,
|
|
398
|
-
release:
|
|
611
|
+
release: {
|
|
612
|
+
id: releaseId,
|
|
613
|
+
releasedAt: {
|
|
614
|
+
$null: true
|
|
615
|
+
}
|
|
616
|
+
}
|
|
399
617
|
},
|
|
400
618
|
data: update
|
|
401
619
|
});
|
|
402
620
|
if (!updatedAction) {
|
|
403
621
|
throw new errors.NotFoundError(
|
|
404
|
-
`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`
|
|
405
623
|
);
|
|
406
624
|
}
|
|
407
625
|
return updatedAction;
|
|
@@ -410,12 +628,17 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
410
628
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
411
629
|
where: {
|
|
412
630
|
id: actionId,
|
|
413
|
-
release:
|
|
631
|
+
release: {
|
|
632
|
+
id: releaseId,
|
|
633
|
+
releasedAt: {
|
|
634
|
+
$null: true
|
|
635
|
+
}
|
|
636
|
+
}
|
|
414
637
|
}
|
|
415
638
|
});
|
|
416
639
|
if (!deletedAction) {
|
|
417
640
|
throw new errors.NotFoundError(
|
|
418
|
-
`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`
|
|
419
642
|
);
|
|
420
643
|
}
|
|
421
644
|
return deletedAction;
|
|
@@ -448,9 +671,42 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
448
671
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
449
672
|
);
|
|
450
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
|
+
}
|
|
451
704
|
}
|
|
452
705
|
});
|
|
453
|
-
const services = {
|
|
706
|
+
const services = {
|
|
707
|
+
release: createReleaseService,
|
|
708
|
+
"release-validation": createReleaseValidationService
|
|
709
|
+
};
|
|
454
710
|
const RELEASE_SCHEMA = yup.object().shape({
|
|
455
711
|
name: yup.string().trim().required()
|
|
456
712
|
}).required().noUnknown();
|
|
@@ -469,9 +725,7 @@ const releaseController = {
|
|
|
469
725
|
const contentTypeUid = query.contentTypeUid;
|
|
470
726
|
const entryId = query.entryId;
|
|
471
727
|
const hasEntryAttached = typeof query.hasEntryAttached === "string" ? JSON.parse(query.hasEntryAttached) : false;
|
|
472
|
-
const data = await releaseService.
|
|
473
|
-
hasEntryAttached
|
|
474
|
-
});
|
|
728
|
+
const data = hasEntryAttached ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId) : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);
|
|
475
729
|
ctx.body = { data };
|
|
476
730
|
} else {
|
|
477
731
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
@@ -595,33 +849,34 @@ const releaseActionController = {
|
|
|
595
849
|
});
|
|
596
850
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
597
851
|
const releaseService = getService("release", { strapi });
|
|
598
|
-
const { results, pagination } = await releaseService.findActions(releaseId,
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
);
|
|
602
|
-
const
|
|
603
|
-
|
|
604
|
-
|
|
852
|
+
const { results, pagination } = await releaseService.findActions(releaseId, {
|
|
853
|
+
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
854
|
+
...query
|
|
855
|
+
});
|
|
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;
|
|
605
865
|
return acc;
|
|
606
866
|
}, {});
|
|
607
|
-
const
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
displayName,
|
|
615
|
-
mainFieldValue: action.entry[mainField]
|
|
616
|
-
},
|
|
617
|
-
locale: allLocalesDictionary[action.entry.locale]
|
|
618
|
-
}
|
|
619
|
-
};
|
|
620
|
-
});
|
|
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();
|
|
621
874
|
ctx.body = {
|
|
622
|
-
data,
|
|
875
|
+
data: groupedData,
|
|
623
876
|
meta: {
|
|
624
|
-
pagination
|
|
877
|
+
pagination,
|
|
878
|
+
contentTypes: contentTypes2,
|
|
879
|
+
components
|
|
625
880
|
}
|
|
626
881
|
};
|
|
627
882
|
},
|
|
@@ -643,10 +898,8 @@ const releaseActionController = {
|
|
|
643
898
|
async delete(ctx) {
|
|
644
899
|
const actionId = ctx.params.actionId;
|
|
645
900
|
const releaseId = ctx.params.releaseId;
|
|
646
|
-
const
|
|
647
|
-
|
|
648
|
-
releaseId
|
|
649
|
-
);
|
|
901
|
+
const releaseService = getService("release", { strapi });
|
|
902
|
+
const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);
|
|
650
903
|
ctx.body = {
|
|
651
904
|
data: deletedReleaseAction
|
|
652
905
|
};
|
|
@@ -829,9 +1082,10 @@ const routes = {
|
|
|
829
1082
|
};
|
|
830
1083
|
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
831
1084
|
const getPlugin = () => {
|
|
832
|
-
if (features.isEnabled("cms-content-releases")
|
|
1085
|
+
if (features.isEnabled("cms-content-releases")) {
|
|
833
1086
|
return {
|
|
834
1087
|
register,
|
|
1088
|
+
bootstrap,
|
|
835
1089
|
contentTypes,
|
|
836
1090
|
services,
|
|
837
1091
|
controllers,
|