@strapi/content-releases 0.0.0-next.f8af92b375dc730ba47ed2117f25df893aae696c → 0.0.0-next.fc231041206e6f3999b094160cfa05db2892ad54
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-xAkiD42p.mjs → App-HVXzE3i3.mjs} +644 -623
- package/dist/_chunks/App-HVXzE3i3.mjs.map +1 -0
- package/dist/_chunks/{App-OK4Xac-O.js → App-l62gIUTX.js} +635 -614
- package/dist/_chunks/App-l62gIUTX.js.map +1 -0
- package/dist/_chunks/{en-veqvqeEr.mjs → en-RdapH-9X.mjs} +4 -4
- package/dist/_chunks/en-RdapH-9X.mjs.map +1 -0
- package/dist/_chunks/{en-r0otWaln.js → en-faJDuv3q.js} +4 -4
- package/dist/_chunks/en-faJDuv3q.js.map +1 -0
- package/dist/_chunks/{index-JvA2_26n.js → index-ML_b3php.js} +33 -14
- package/dist/_chunks/index-ML_b3php.js.map +1 -0
- package/dist/_chunks/{index-exoiSU3V.mjs → index-Ys87ROOe.mjs} +43 -24
- package/dist/_chunks/index-Ys87ROOe.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +554 -170
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +553 -170
- package/dist/server/index.mjs.map +1 -1
- package/package.json +11 -11
- package/dist/_chunks/App-OK4Xac-O.js.map +0 -1
- package/dist/_chunks/App-xAkiD42p.mjs.map +0 -1
- package/dist/_chunks/en-r0otWaln.js.map +0 -1
- package/dist/_chunks/en-veqvqeEr.mjs.map +0 -1
- package/dist/_chunks/index-JvA2_26n.js.map +0 -1
- package/dist/_chunks/index-exoiSU3V.mjs.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
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");
|
|
@@ -24,6 +25,7 @@ function _interopNamespace(e) {
|
|
|
24
25
|
n.default = e;
|
|
25
26
|
return Object.freeze(n);
|
|
26
27
|
}
|
|
28
|
+
const isEqual__default = /* @__PURE__ */ _interopDefault(isEqual);
|
|
27
29
|
const ___default = /* @__PURE__ */ _interopDefault(_);
|
|
28
30
|
const EE__default = /* @__PURE__ */ _interopDefault(EE);
|
|
29
31
|
const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
|
|
@@ -76,6 +78,29 @@ const ACTIONS = [
|
|
|
76
78
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
77
79
|
RELEASES_PUBLISH: "releases.publish"
|
|
78
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
|
+
};
|
|
79
104
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
80
105
|
oldContentTypes,
|
|
81
106
|
contentTypes: contentTypes2
|
|
@@ -102,31 +127,196 @@ async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes:
|
|
|
102
127
|
});
|
|
103
128
|
}
|
|
104
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
|
+
}
|
|
105
274
|
const { features: features$2 } = require("@strapi/strapi/dist/utils/ee");
|
|
106
275
|
const register = async ({ strapi: strapi2 }) => {
|
|
107
276
|
if (features$2.isEnabled("cms-content-releases")) {
|
|
108
277
|
await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
|
|
109
|
-
strapi2.hook("strapi::content-types.beforeSync").register(deleteActionsOnDisableDraftAndPublish);
|
|
110
|
-
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();
|
|
111
285
|
}
|
|
112
|
-
};
|
|
113
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
114
|
-
return strapi2.plugin("content-releases").service(name);
|
|
115
286
|
};
|
|
116
287
|
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
117
288
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
118
289
|
if (features$1.isEnabled("cms-content-releases")) {
|
|
290
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
291
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
292
|
+
);
|
|
119
293
|
strapi2.db.lifecycles.subscribe({
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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);
|
|
128
316
|
}
|
|
129
|
-
}
|
|
317
|
+
}
|
|
318
|
+
} catch (error) {
|
|
319
|
+
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
130
320
|
}
|
|
131
321
|
},
|
|
132
322
|
/**
|
|
@@ -146,41 +336,94 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
146
336
|
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
147
337
|
*/
|
|
148
338
|
async afterDeleteMany(event) {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
+
}
|
|
157
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
|
+
}
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
for (const release2 of releases) {
|
|
364
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
158
365
|
}
|
|
366
|
+
}
|
|
367
|
+
} catch (error) {
|
|
368
|
+
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
369
|
+
error
|
|
159
370
|
});
|
|
160
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
|
+
}
|
|
161
408
|
}
|
|
162
409
|
});
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
});
|
|
173
|
-
}
|
|
410
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
411
|
+
strapi2.log.error(
|
|
412
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
413
|
+
);
|
|
414
|
+
throw err;
|
|
415
|
+
});
|
|
416
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
417
|
+
strapi2.webhookStore.addAllowedEvent(key, value);
|
|
418
|
+
});
|
|
174
419
|
}
|
|
175
420
|
};
|
|
176
421
|
const destroy = async ({ strapi: strapi2 }) => {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
job.cancel();
|
|
183
|
-
}
|
|
422
|
+
const scheduledJobs = getService("scheduling", {
|
|
423
|
+
strapi: strapi2
|
|
424
|
+
}).getAll();
|
|
425
|
+
for (const [, job] of scheduledJobs) {
|
|
426
|
+
job.cancel();
|
|
184
427
|
}
|
|
185
428
|
};
|
|
186
429
|
const schema$1 = {
|
|
@@ -215,6 +458,11 @@ const schema$1 = {
|
|
|
215
458
|
timezone: {
|
|
216
459
|
type: "string"
|
|
217
460
|
},
|
|
461
|
+
status: {
|
|
462
|
+
type: "enumeration",
|
|
463
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
464
|
+
required: true
|
|
465
|
+
},
|
|
218
466
|
actions: {
|
|
219
467
|
type: "relation",
|
|
220
468
|
relation: "oneToMany",
|
|
@@ -267,6 +515,9 @@ const schema = {
|
|
|
267
515
|
relation: "manyToOne",
|
|
268
516
|
target: RELEASE_MODEL_UID,
|
|
269
517
|
inversedBy: "actions"
|
|
518
|
+
},
|
|
519
|
+
isEntryValid: {
|
|
520
|
+
type: "boolean"
|
|
270
521
|
}
|
|
271
522
|
}
|
|
272
523
|
};
|
|
@@ -297,6 +548,94 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
297
548
|
release: release2
|
|
298
549
|
});
|
|
299
550
|
};
|
|
551
|
+
const publishSingleTypeAction = async (uid, actionType, entryId) => {
|
|
552
|
+
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
553
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
554
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
555
|
+
const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
|
|
556
|
+
try {
|
|
557
|
+
if (actionType === "publish") {
|
|
558
|
+
await entityManagerService.publish(entry, uid);
|
|
559
|
+
} else {
|
|
560
|
+
await entityManagerService.unpublish(entry, uid);
|
|
561
|
+
}
|
|
562
|
+
} catch (error) {
|
|
563
|
+
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
|
|
564
|
+
;
|
|
565
|
+
else {
|
|
566
|
+
throw error;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
};
|
|
570
|
+
const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
|
|
571
|
+
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
572
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
573
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
574
|
+
const entriesToPublish = await strapi2.entityService.findMany(uid, {
|
|
575
|
+
filters: {
|
|
576
|
+
id: {
|
|
577
|
+
$in: entriesToPublishIds
|
|
578
|
+
}
|
|
579
|
+
},
|
|
580
|
+
populate
|
|
581
|
+
});
|
|
582
|
+
const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
|
|
583
|
+
filters: {
|
|
584
|
+
id: {
|
|
585
|
+
$in: entriestoUnpublishIds
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
populate
|
|
589
|
+
});
|
|
590
|
+
if (entriesToPublish.length > 0) {
|
|
591
|
+
await entityManagerService.publishMany(entriesToPublish, uid);
|
|
592
|
+
}
|
|
593
|
+
if (entriesToUnpublish.length > 0) {
|
|
594
|
+
await entityManagerService.unpublishMany(entriesToUnpublish, uid);
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
const getFormattedActions = async (releaseId) => {
|
|
598
|
+
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
599
|
+
where: {
|
|
600
|
+
release: {
|
|
601
|
+
id: releaseId
|
|
602
|
+
}
|
|
603
|
+
},
|
|
604
|
+
populate: {
|
|
605
|
+
entry: {
|
|
606
|
+
fields: ["id"]
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
if (actions.length === 0) {
|
|
611
|
+
throw new utils.errors.ValidationError("No entries to publish");
|
|
612
|
+
}
|
|
613
|
+
const collectionTypeActions = {};
|
|
614
|
+
const singleTypeActions = [];
|
|
615
|
+
for (const action of actions) {
|
|
616
|
+
const contentTypeUid = action.contentType;
|
|
617
|
+
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
618
|
+
if (!collectionTypeActions[contentTypeUid]) {
|
|
619
|
+
collectionTypeActions[contentTypeUid] = {
|
|
620
|
+
entriesToPublishIds: [],
|
|
621
|
+
entriesToUnpublishIds: []
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
if (action.type === "publish") {
|
|
625
|
+
collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
|
|
626
|
+
} else {
|
|
627
|
+
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
628
|
+
}
|
|
629
|
+
} else {
|
|
630
|
+
singleTypeActions.push({
|
|
631
|
+
uid: contentTypeUid,
|
|
632
|
+
action: action.type,
|
|
633
|
+
id: action.entry.id
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return { collectionTypeActions, singleTypeActions };
|
|
638
|
+
};
|
|
300
639
|
return {
|
|
301
640
|
async create(releaseData, { user }) {
|
|
302
641
|
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
@@ -311,9 +650,12 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
311
650
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
312
651
|
]);
|
|
313
652
|
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
314
|
-
data:
|
|
653
|
+
data: {
|
|
654
|
+
...releaseWithCreatorFields,
|
|
655
|
+
status: "empty"
|
|
656
|
+
}
|
|
315
657
|
});
|
|
316
|
-
if (
|
|
658
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
317
659
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
318
660
|
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
319
661
|
}
|
|
@@ -438,14 +780,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
438
780
|
// @ts-expect-error see above
|
|
439
781
|
data: releaseWithCreatorFields
|
|
440
782
|
});
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
schedulingService.cancel(id);
|
|
447
|
-
}
|
|
783
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
784
|
+
if (releaseData.scheduledAt) {
|
|
785
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
786
|
+
} else if (release2.scheduledAt) {
|
|
787
|
+
schedulingService.cancel(id);
|
|
448
788
|
}
|
|
789
|
+
this.updateReleaseStatus(id);
|
|
449
790
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
450
791
|
return updatedRelease;
|
|
451
792
|
},
|
|
@@ -465,11 +806,14 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
465
806
|
throw new utils.errors.ValidationError("Release already published");
|
|
466
807
|
}
|
|
467
808
|
const { entry, type } = action;
|
|
468
|
-
|
|
809
|
+
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
810
|
+
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
811
|
+
const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
469
812
|
data: {
|
|
470
813
|
type,
|
|
471
814
|
contentType: entry.contentType,
|
|
472
815
|
locale: entry.locale,
|
|
816
|
+
isEntryValid,
|
|
473
817
|
entry: {
|
|
474
818
|
id: entry.id,
|
|
475
819
|
__type: entry.contentType,
|
|
@@ -479,6 +823,8 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
479
823
|
},
|
|
480
824
|
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
481
825
|
});
|
|
826
|
+
this.updateReleaseStatus(releaseId);
|
|
827
|
+
return releaseAction2;
|
|
482
828
|
},
|
|
483
829
|
async findActions(releaseId, query) {
|
|
484
830
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -604,7 +950,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
604
950
|
});
|
|
605
951
|
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
606
952
|
});
|
|
607
|
-
if (
|
|
953
|
+
if (release2.scheduledAt) {
|
|
608
954
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
609
955
|
await schedulingService.cancel(release2.id);
|
|
610
956
|
}
|
|
@@ -612,139 +958,71 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
612
958
|
return release2;
|
|
613
959
|
},
|
|
614
960
|
async publish(releaseId) {
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
actions: {
|
|
622
|
-
populate: {
|
|
623
|
-
entry: {
|
|
624
|
-
fields: ["id"]
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
}
|
|
630
|
-
);
|
|
631
|
-
if (!releaseWithPopulatedActionEntries) {
|
|
961
|
+
const {
|
|
962
|
+
release: release2,
|
|
963
|
+
error
|
|
964
|
+
} = await strapi2.db.transaction(async ({ trx }) => {
|
|
965
|
+
const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
|
|
966
|
+
if (!lockedRelease) {
|
|
632
967
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
633
968
|
}
|
|
634
|
-
if (
|
|
969
|
+
if (lockedRelease.releasedAt) {
|
|
635
970
|
throw new utils.errors.ValidationError("Release already published");
|
|
636
971
|
}
|
|
637
|
-
if (
|
|
638
|
-
throw new utils.errors.ValidationError("
|
|
639
|
-
}
|
|
640
|
-
const collectionTypeActions = {};
|
|
641
|
-
const singleTypeActions = [];
|
|
642
|
-
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
643
|
-
const contentTypeUid = action.contentType;
|
|
644
|
-
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
645
|
-
if (!collectionTypeActions[contentTypeUid]) {
|
|
646
|
-
collectionTypeActions[contentTypeUid] = {
|
|
647
|
-
entriestoPublishIds: [],
|
|
648
|
-
entriesToUnpublishIds: []
|
|
649
|
-
};
|
|
650
|
-
}
|
|
651
|
-
if (action.type === "publish") {
|
|
652
|
-
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
653
|
-
} else {
|
|
654
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
655
|
-
}
|
|
656
|
-
} else {
|
|
657
|
-
singleTypeActions.push({
|
|
658
|
-
uid: contentTypeUid,
|
|
659
|
-
action: action.type,
|
|
660
|
-
id: action.entry.id
|
|
661
|
-
});
|
|
662
|
-
}
|
|
972
|
+
if (lockedRelease.status === "failed") {
|
|
973
|
+
throw new utils.errors.ValidationError("Release failed to publish");
|
|
663
974
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
await entityManagerService.publish(entry, uid);
|
|
673
|
-
} else {
|
|
674
|
-
await entityManagerService.unpublish(entry, uid);
|
|
675
|
-
}
|
|
676
|
-
} catch (error) {
|
|
677
|
-
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
|
|
678
|
-
} else {
|
|
679
|
-
throw error;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
684
|
-
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
685
|
-
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
686
|
-
const entriesToPublish = await strapi2.entityService.findMany(
|
|
687
|
-
contentTypeUid,
|
|
688
|
-
{
|
|
689
|
-
filters: {
|
|
690
|
-
id: {
|
|
691
|
-
$in: entriestoPublishIds
|
|
692
|
-
}
|
|
693
|
-
},
|
|
694
|
-
populate
|
|
695
|
-
}
|
|
696
|
-
);
|
|
697
|
-
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
698
|
-
contentTypeUid,
|
|
699
|
-
{
|
|
700
|
-
filters: {
|
|
701
|
-
id: {
|
|
702
|
-
$in: entriesToUnpublishIds
|
|
703
|
-
}
|
|
704
|
-
},
|
|
705
|
-
populate
|
|
706
|
-
}
|
|
707
|
-
);
|
|
708
|
-
if (entriesToPublish.length > 0) {
|
|
709
|
-
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
975
|
+
try {
|
|
976
|
+
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
977
|
+
const { collectionTypeActions, singleTypeActions } = await getFormattedActions(
|
|
978
|
+
releaseId
|
|
979
|
+
);
|
|
980
|
+
await strapi2.db.transaction(async () => {
|
|
981
|
+
for (const { uid, action, id } of singleTypeActions) {
|
|
982
|
+
await publishSingleTypeAction(uid, action, id);
|
|
710
983
|
}
|
|
711
|
-
|
|
712
|
-
|
|
984
|
+
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
985
|
+
const uid = contentTypeUid;
|
|
986
|
+
await publishCollectionTypeAction(
|
|
987
|
+
uid,
|
|
988
|
+
collectionTypeActions[uid].entriesToPublishIds,
|
|
989
|
+
collectionTypeActions[uid].entriesToUnpublishIds
|
|
990
|
+
);
|
|
713
991
|
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
releasedAt: /* @__PURE__ */ new Date()
|
|
723
|
-
},
|
|
724
|
-
populate: {
|
|
725
|
-
actions: {
|
|
726
|
-
// @ts-expect-error is not expecting count but it is working
|
|
727
|
-
count: true
|
|
992
|
+
});
|
|
993
|
+
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
994
|
+
where: {
|
|
995
|
+
id: releaseId
|
|
996
|
+
},
|
|
997
|
+
data: {
|
|
998
|
+
status: "done",
|
|
999
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
728
1000
|
}
|
|
729
|
-
}
|
|
730
|
-
});
|
|
731
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
1001
|
+
});
|
|
732
1002
|
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
733
1003
|
isPublished: true,
|
|
734
|
-
release:
|
|
1004
|
+
release: release22
|
|
735
1005
|
});
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
} catch (error) {
|
|
740
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
1006
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
1007
|
+
return { release: release22, error: null };
|
|
1008
|
+
} catch (error2) {
|
|
741
1009
|
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
742
1010
|
isPublished: false,
|
|
743
|
-
error
|
|
1011
|
+
error: error2
|
|
744
1012
|
});
|
|
1013
|
+
await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
|
|
1014
|
+
status: "failed"
|
|
1015
|
+
}).transacting(trx).execute();
|
|
1016
|
+
return {
|
|
1017
|
+
release: null,
|
|
1018
|
+
error: error2
|
|
1019
|
+
};
|
|
745
1020
|
}
|
|
1021
|
+
});
|
|
1022
|
+
if (error) {
|
|
746
1023
|
throw error;
|
|
747
1024
|
}
|
|
1025
|
+
return release2;
|
|
748
1026
|
},
|
|
749
1027
|
async updateAction(actionId, releaseId, update) {
|
|
750
1028
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
@@ -783,10 +1061,60 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
783
1061
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
784
1062
|
);
|
|
785
1063
|
}
|
|
1064
|
+
this.updateReleaseStatus(releaseId);
|
|
786
1065
|
return deletedAction;
|
|
1066
|
+
},
|
|
1067
|
+
async updateReleaseStatus(releaseId) {
|
|
1068
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
1069
|
+
this.countActions({
|
|
1070
|
+
filters: {
|
|
1071
|
+
release: releaseId
|
|
1072
|
+
}
|
|
1073
|
+
}),
|
|
1074
|
+
this.countActions({
|
|
1075
|
+
filters: {
|
|
1076
|
+
release: releaseId,
|
|
1077
|
+
isEntryValid: false
|
|
1078
|
+
}
|
|
1079
|
+
})
|
|
1080
|
+
]);
|
|
1081
|
+
if (totalActions > 0) {
|
|
1082
|
+
if (invalidActions > 0) {
|
|
1083
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1084
|
+
where: {
|
|
1085
|
+
id: releaseId
|
|
1086
|
+
},
|
|
1087
|
+
data: {
|
|
1088
|
+
status: "blocked"
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1093
|
+
where: {
|
|
1094
|
+
id: releaseId
|
|
1095
|
+
},
|
|
1096
|
+
data: {
|
|
1097
|
+
status: "ready"
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1102
|
+
where: {
|
|
1103
|
+
id: releaseId
|
|
1104
|
+
},
|
|
1105
|
+
data: {
|
|
1106
|
+
status: "empty"
|
|
1107
|
+
}
|
|
1108
|
+
});
|
|
787
1109
|
}
|
|
788
1110
|
};
|
|
789
1111
|
};
|
|
1112
|
+
class AlreadyOnReleaseError extends utils.errors.ApplicationError {
|
|
1113
|
+
constructor(message) {
|
|
1114
|
+
super(message);
|
|
1115
|
+
this.name = "AlreadyOnReleaseError";
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
790
1118
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
791
1119
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
792
1120
|
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
@@ -799,7 +1127,7 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
799
1127
|
(action) => Number(action.entry.id) === Number(releaseActionArgs.entry.id) && action.contentType === releaseActionArgs.entry.contentType
|
|
800
1128
|
);
|
|
801
1129
|
if (isEntryInRelease) {
|
|
802
|
-
throw new
|
|
1130
|
+
throw new AlreadyOnReleaseError(
|
|
803
1131
|
`Entry with id ${releaseActionArgs.entry.id} and contentType ${releaseActionArgs.entry.contentType} already exists in release with id ${releaseId}`
|
|
804
1132
|
);
|
|
805
1133
|
}
|
|
@@ -907,7 +1235,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
907
1235
|
const services = {
|
|
908
1236
|
release: createReleaseService,
|
|
909
1237
|
"release-validation": createReleaseValidationService,
|
|
910
|
-
|
|
1238
|
+
scheduling: createSchedulingService
|
|
911
1239
|
};
|
|
912
1240
|
const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
913
1241
|
name: yup__namespace.string().trim().required(),
|
|
@@ -960,7 +1288,12 @@ const releaseController = {
|
|
|
960
1288
|
}
|
|
961
1289
|
};
|
|
962
1290
|
});
|
|
963
|
-
|
|
1291
|
+
const pendingReleasesCount = await strapi.query(RELEASE_MODEL_UID).count({
|
|
1292
|
+
where: {
|
|
1293
|
+
releasedAt: null
|
|
1294
|
+
}
|
|
1295
|
+
});
|
|
1296
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
964
1297
|
}
|
|
965
1298
|
},
|
|
966
1299
|
async findOne(ctx) {
|
|
@@ -1078,6 +1411,38 @@ const releaseActionController = {
|
|
|
1078
1411
|
data: releaseAction2
|
|
1079
1412
|
};
|
|
1080
1413
|
},
|
|
1414
|
+
async createMany(ctx) {
|
|
1415
|
+
const releaseId = ctx.params.releaseId;
|
|
1416
|
+
const releaseActionsArgs = ctx.request.body;
|
|
1417
|
+
await Promise.all(
|
|
1418
|
+
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1419
|
+
);
|
|
1420
|
+
const releaseService = getService("release", { strapi });
|
|
1421
|
+
const releaseActions = await strapi.db.transaction(async () => {
|
|
1422
|
+
const releaseActions2 = await Promise.all(
|
|
1423
|
+
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1424
|
+
try {
|
|
1425
|
+
const action = await releaseService.createAction(releaseId, releaseActionArgs);
|
|
1426
|
+
return action;
|
|
1427
|
+
} catch (error) {
|
|
1428
|
+
if (error instanceof AlreadyOnReleaseError) {
|
|
1429
|
+
return null;
|
|
1430
|
+
}
|
|
1431
|
+
throw error;
|
|
1432
|
+
}
|
|
1433
|
+
})
|
|
1434
|
+
);
|
|
1435
|
+
return releaseActions2;
|
|
1436
|
+
});
|
|
1437
|
+
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1438
|
+
ctx.body = {
|
|
1439
|
+
data: newReleaseActions,
|
|
1440
|
+
meta: {
|
|
1441
|
+
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1442
|
+
totalEntries: releaseActions.length
|
|
1443
|
+
}
|
|
1444
|
+
};
|
|
1445
|
+
},
|
|
1081
1446
|
async findMany(ctx) {
|
|
1082
1447
|
const releaseId = ctx.params.releaseId;
|
|
1083
1448
|
const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
|
|
@@ -1263,6 +1628,22 @@ const releaseAction = {
|
|
|
1263
1628
|
]
|
|
1264
1629
|
}
|
|
1265
1630
|
},
|
|
1631
|
+
{
|
|
1632
|
+
method: "POST",
|
|
1633
|
+
path: "/:releaseId/actions/bulk",
|
|
1634
|
+
handler: "release-action.createMany",
|
|
1635
|
+
config: {
|
|
1636
|
+
policies: [
|
|
1637
|
+
"admin::isAuthenticatedAdmin",
|
|
1638
|
+
{
|
|
1639
|
+
name: "admin::hasPermissions",
|
|
1640
|
+
config: {
|
|
1641
|
+
actions: ["plugin::content-releases.create-action"]
|
|
1642
|
+
}
|
|
1643
|
+
}
|
|
1644
|
+
]
|
|
1645
|
+
}
|
|
1646
|
+
},
|
|
1266
1647
|
{
|
|
1267
1648
|
method: "GET",
|
|
1268
1649
|
path: "/:releaseId/actions",
|
|
@@ -1331,6 +1712,9 @@ const getPlugin = () => {
|
|
|
1331
1712
|
};
|
|
1332
1713
|
}
|
|
1333
1714
|
return {
|
|
1715
|
+
// Always return register, it handles its own feature check
|
|
1716
|
+
register,
|
|
1717
|
+
// Always return contentTypes to avoid losing data when the feature is disabled
|
|
1334
1718
|
contentTypes
|
|
1335
1719
|
};
|
|
1336
1720
|
};
|