@strapi/content-releases 0.0.0-experimental.defd8568ae03ef8d52f86e1f3541979f953c3941 → 0.0.0-experimental.df298029ec6478763dcca7d59fafe8d2ae4ed60a
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/LICENSE +17 -1
- package/dist/_chunks/{App-C0DlH0im.js → App-BKB1esYS.js} +434 -405
- package/dist/_chunks/App-BKB1esYS.js.map +1 -0
- package/dist/_chunks/{App-O0ZO-S35.mjs → App-Cne--1Z8.mjs} +431 -400
- package/dist/_chunks/App-Cne--1Z8.mjs.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-DAHdUpAA.js → PurchaseContentReleases-Be3acS2L.js} +4 -3
- package/dist/_chunks/PurchaseContentReleases-Be3acS2L.js.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-Ex09YpKR.mjs → PurchaseContentReleases-_MxP6-Dt.mjs} +5 -4
- package/dist/_chunks/PurchaseContentReleases-_MxP6-Dt.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-C1WwGWIH.mjs +178 -0
- package/dist/_chunks/ReleasesSettingsPage-C1WwGWIH.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-kuXIwpWp.js +178 -0
- package/dist/_chunks/ReleasesSettingsPage-kuXIwpWp.js.map +1 -0
- package/dist/_chunks/{en-DtFJ5ViE.js → en-CmYoEnA7.js} +9 -2
- package/dist/_chunks/en-CmYoEnA7.js.map +1 -0
- package/dist/_chunks/{en-B9Ur3VsE.mjs → en-D0yVZFqf.mjs} +9 -2
- package/dist/_chunks/en-D0yVZFqf.mjs.map +1 -0
- package/dist/_chunks/{index-DoZNNtsb.js → index-5Odi61vw.js} +714 -595
- package/dist/_chunks/index-5Odi61vw.js.map +1 -0
- package/dist/_chunks/{index-DjDPK8kb.mjs → index-Cy7qwpaU.mjs} +723 -602
- package/dist/_chunks/index-Cy7qwpaU.mjs.map +1 -0
- package/dist/_chunks/schemas-BE1LxE9J.js +62 -0
- package/dist/_chunks/schemas-BE1LxE9J.js.map +1 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs +44 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +2 -2
- package/dist/admin/src/components/ReleaseAction.d.ts +1 -1
- package/dist/admin/src/components/ReleaseActionMenu.d.ts +3 -3
- package/dist/admin/src/components/{CMReleasesContainer.d.ts → ReleaseActionModal.d.ts} +3 -1
- package/dist/admin/src/components/ReleaseListCell.d.ts +28 -0
- package/dist/admin/src/components/ReleaseModal.d.ts +3 -2
- package/dist/admin/src/components/ReleasesPanel.d.ts +3 -0
- package/dist/admin/src/constants.d.ts +18 -0
- package/dist/admin/src/modules/hooks.d.ts +7 -0
- package/dist/admin/src/pages/ReleasesSettingsPage.d.ts +1 -0
- package/dist/admin/src/services/release.d.ts +53 -370
- package/dist/admin/src/utils/api.d.ts +6 -0
- package/dist/admin/src/utils/time.d.ts +9 -0
- package/dist/admin/src/validation/schemas.d.ts +6 -0
- package/dist/server/index.js +786 -580
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +787 -581
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/constants.d.ts +11 -2
- package/dist/server/src/constants.d.ts.map +1 -1
- package/dist/server/src/content-types/index.d.ts +3 -5
- package/dist/server/src/content-types/index.d.ts.map +1 -1
- package/dist/server/src/content-types/release-action/index.d.ts +3 -5
- package/dist/server/src/content-types/release-action/index.d.ts.map +1 -1
- package/dist/server/src/content-types/release-action/schema.d.ts +3 -5
- package/dist/server/src/content-types/release-action/schema.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +6 -1
- package/dist/server/src/controllers/index.d.ts.map +1 -1
- package/dist/server/src/controllers/release-action.d.ts.map +1 -1
- package/dist/server/src/controllers/release.d.ts +7 -1
- package/dist/server/src/controllers/release.d.ts.map +1 -1
- package/dist/server/src/controllers/settings.d.ts +11 -0
- package/dist/server/src/controllers/settings.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/release-action.d.ts +7 -1
- package/dist/server/src/controllers/validation/release-action.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/release.d.ts +2 -0
- package/dist/server/src/controllers/validation/release.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/settings.d.ts +3 -0
- package/dist/server/src/controllers/validation/settings.d.ts.map +1 -0
- package/dist/server/src/index.d.ts +68 -49
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/middlewares/documents.d.ts +6 -0
- package/dist/server/src/middlewares/documents.d.ts.map +1 -0
- package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts +9 -0
- package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts.map +1 -0
- package/dist/server/src/migrations/index.d.ts.map +1 -1
- package/dist/server/src/register.d.ts.map +1 -1
- package/dist/server/src/routes/index.d.ts +16 -0
- package/dist/server/src/routes/index.d.ts.map +1 -1
- package/dist/server/src/routes/release.d.ts.map +1 -1
- package/dist/server/src/routes/settings.d.ts +18 -0
- package/dist/server/src/routes/settings.d.ts.map +1 -0
- package/dist/server/src/services/index.d.ts +40 -38
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/release-action.d.ts +38 -0
- package/dist/server/src/services/release-action.d.ts.map +1 -0
- package/dist/server/src/services/release.d.ts +6 -41
- package/dist/server/src/services/release.d.ts.map +1 -1
- package/dist/server/src/services/settings.d.ts +13 -0
- package/dist/server/src/services/settings.d.ts.map +1 -0
- package/dist/server/src/services/validation.d.ts +1 -1
- package/dist/server/src/services/validation.d.ts.map +1 -1
- package/dist/server/src/utils/index.d.ts +29 -8
- package/dist/server/src/utils/index.d.ts.map +1 -1
- package/dist/shared/contracts/release-actions.d.ts +9 -10
- package/dist/shared/contracts/release-actions.d.ts.map +1 -1
- package/dist/shared/contracts/releases.d.ts +9 -7
- package/dist/shared/contracts/releases.d.ts.map +1 -1
- package/dist/shared/contracts/settings.d.ts +39 -0
- package/dist/shared/contracts/settings.d.ts.map +1 -0
- package/package.json +22 -22
- package/dist/_chunks/App-C0DlH0im.js.map +0 -1
- package/dist/_chunks/App-O0ZO-S35.mjs.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-DAHdUpAA.js.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-Ex09YpKR.mjs.map +0 -1
- package/dist/_chunks/en-B9Ur3VsE.mjs.map +0 -1
- package/dist/_chunks/en-DtFJ5ViE.js.map +0 -1
- package/dist/_chunks/index-DjDPK8kb.mjs.map +0 -1
- package/dist/_chunks/index-DoZNNtsb.js.map +0 -1
- package/dist/admin/src/services/axios.d.ts +0 -29
- package/dist/shared/validation-schemas.d.ts +0 -2
- package/dist/shared/validation-schemas.d.ts.map +0 -1
- package/strapi-server.js +0 -3
package/dist/server/index.js
CHANGED
|
@@ -71,6 +71,23 @@ const ACTIONS = [
|
|
|
71
71
|
displayName: "Add an entry to a release",
|
|
72
72
|
uid: "create-action",
|
|
73
73
|
pluginName: "content-releases"
|
|
74
|
+
},
|
|
75
|
+
// Settings
|
|
76
|
+
{
|
|
77
|
+
uid: "settings.read",
|
|
78
|
+
section: "settings",
|
|
79
|
+
displayName: "Read",
|
|
80
|
+
category: "content releases",
|
|
81
|
+
subCategory: "options",
|
|
82
|
+
pluginName: "content-releases"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
uid: "settings.update",
|
|
86
|
+
section: "settings",
|
|
87
|
+
displayName: "Edit",
|
|
88
|
+
category: "content releases",
|
|
89
|
+
subCategory: "options",
|
|
90
|
+
pluginName: "content-releases"
|
|
74
91
|
}
|
|
75
92
|
];
|
|
76
93
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
@@ -79,16 +96,13 @@ const ALLOWED_WEBHOOK_EVENTS = {
|
|
|
79
96
|
const getService = (name, { strapi: strapi2 }) => {
|
|
80
97
|
return strapi2.plugin("content-releases").service(name);
|
|
81
98
|
};
|
|
82
|
-
const
|
|
99
|
+
const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi: strapi2 }) => {
|
|
83
100
|
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
84
|
-
const populate = await populateBuilderService(
|
|
85
|
-
const entry = await strapi2
|
|
86
|
-
|
|
87
|
-
populate
|
|
88
|
-
});
|
|
89
|
-
return entry;
|
|
101
|
+
const populate = await populateBuilderService(contentType).populateDeep(Infinity).build();
|
|
102
|
+
const entry = await getEntry({ contentType, documentId, locale, populate }, { strapi: strapi2 });
|
|
103
|
+
return isEntryValid(contentType, entry, { strapi: strapi2 });
|
|
90
104
|
};
|
|
91
|
-
const
|
|
105
|
+
const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
|
|
92
106
|
try {
|
|
93
107
|
await strapi2.entityValidator.validateEntityCreation(
|
|
94
108
|
strapi2.getModel(contentTypeUid),
|
|
@@ -102,6 +116,38 @@ const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 }) =
|
|
|
102
116
|
return false;
|
|
103
117
|
}
|
|
104
118
|
};
|
|
119
|
+
const getEntry = async ({
|
|
120
|
+
contentType,
|
|
121
|
+
documentId,
|
|
122
|
+
locale,
|
|
123
|
+
populate,
|
|
124
|
+
status = "draft"
|
|
125
|
+
}, { strapi: strapi2 }) => {
|
|
126
|
+
if (documentId) {
|
|
127
|
+
return strapi2.documents(contentType).findOne({ documentId, locale, populate, status });
|
|
128
|
+
}
|
|
129
|
+
return strapi2.documents(contentType).findFirst({ locale, populate, status });
|
|
130
|
+
};
|
|
131
|
+
const getEntryStatus = async (contentType, entry) => {
|
|
132
|
+
if (entry.publishedAt) {
|
|
133
|
+
return "published";
|
|
134
|
+
}
|
|
135
|
+
const publishedEntry = await strapi.documents(contentType).findOne({
|
|
136
|
+
documentId: entry.documentId,
|
|
137
|
+
locale: entry.locale,
|
|
138
|
+
status: "published",
|
|
139
|
+
fields: ["updatedAt"]
|
|
140
|
+
});
|
|
141
|
+
if (!publishedEntry) {
|
|
142
|
+
return "draft";
|
|
143
|
+
}
|
|
144
|
+
const entryUpdatedAt = new Date(entry.updatedAt).getTime();
|
|
145
|
+
const publishedEntryUpdatedAt = new Date(publishedEntry.updatedAt).getTime();
|
|
146
|
+
if (entryUpdatedAt > publishedEntryUpdatedAt) {
|
|
147
|
+
return "modified";
|
|
148
|
+
}
|
|
149
|
+
return "published";
|
|
150
|
+
};
|
|
105
151
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
106
152
|
oldContentTypes,
|
|
107
153
|
contentTypes: contentTypes2
|
|
@@ -147,20 +193,22 @@ async function migrateIsValidAndStatusReleases() {
|
|
|
147
193
|
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
148
194
|
for (const action of notValidatedActions) {
|
|
149
195
|
if (action.entry) {
|
|
150
|
-
const
|
|
151
|
-
|
|
196
|
+
const isEntryValid2 = getDraftEntryValidStatus(
|
|
197
|
+
{
|
|
198
|
+
contentType: action.contentType,
|
|
199
|
+
documentId: action.entryDocumentId,
|
|
200
|
+
locale: action.locale
|
|
201
|
+
},
|
|
202
|
+
{ strapi }
|
|
203
|
+
);
|
|
204
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
205
|
+
where: {
|
|
206
|
+
id: action.id
|
|
207
|
+
},
|
|
208
|
+
data: {
|
|
209
|
+
isEntryValid: isEntryValid2
|
|
210
|
+
}
|
|
152
211
|
});
|
|
153
|
-
if (populatedEntry) {
|
|
154
|
-
const isEntryValid = getEntryValidStatus(action.contentType, populatedEntry, { strapi });
|
|
155
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
156
|
-
where: {
|
|
157
|
-
id: action.id
|
|
158
|
-
},
|
|
159
|
-
data: {
|
|
160
|
-
isEntryValid
|
|
161
|
-
}
|
|
162
|
-
});
|
|
163
|
-
}
|
|
164
212
|
}
|
|
165
213
|
}
|
|
166
214
|
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
@@ -204,24 +252,24 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
|
|
|
204
252
|
}
|
|
205
253
|
});
|
|
206
254
|
await utils.async.map(actions, async (action) => {
|
|
207
|
-
if (action.entry && action.release) {
|
|
208
|
-
const
|
|
209
|
-
|
|
255
|
+
if (action.entry && action.release && action.type === "publish") {
|
|
256
|
+
const isEntryValid2 = await getDraftEntryValidStatus(
|
|
257
|
+
{
|
|
258
|
+
contentType: contentTypeUID,
|
|
259
|
+
documentId: action.entryDocumentId,
|
|
260
|
+
locale: action.locale
|
|
261
|
+
},
|
|
262
|
+
{ strapi }
|
|
263
|
+
);
|
|
264
|
+
releasesAffected.add(action.release.id);
|
|
265
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
266
|
+
where: {
|
|
267
|
+
id: action.id
|
|
268
|
+
},
|
|
269
|
+
data: {
|
|
270
|
+
isEntryValid: isEntryValid2
|
|
271
|
+
}
|
|
210
272
|
});
|
|
211
|
-
if (populatedEntry) {
|
|
212
|
-
const isEntryValid = await getEntryValidStatus(contentTypeUID, populatedEntry, {
|
|
213
|
-
strapi
|
|
214
|
-
});
|
|
215
|
-
releasesAffected.add(action.release.id);
|
|
216
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
217
|
-
where: {
|
|
218
|
-
id: action.id
|
|
219
|
-
},
|
|
220
|
-
data: {
|
|
221
|
-
isEntryValid
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
273
|
}
|
|
226
274
|
});
|
|
227
275
|
}
|
|
@@ -278,9 +326,42 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
|
|
|
278
326
|
}
|
|
279
327
|
}
|
|
280
328
|
}
|
|
329
|
+
const addEntryDocumentToReleaseActions = {
|
|
330
|
+
name: "content-releases::5.0.0-add-entry-document-id-to-release-actions",
|
|
331
|
+
async up(trx, db) {
|
|
332
|
+
const hasTable = await trx.schema.hasTable("strapi_release_actions");
|
|
333
|
+
if (!hasTable) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const hasPolymorphicColumn = await trx.schema.hasColumn("strapi_release_actions", "target_id");
|
|
337
|
+
if (hasPolymorphicColumn) {
|
|
338
|
+
const hasEntryDocumentIdColumn = await trx.schema.hasColumn(
|
|
339
|
+
"strapi_release_actions",
|
|
340
|
+
"entry_document_id"
|
|
341
|
+
);
|
|
342
|
+
if (!hasEntryDocumentIdColumn) {
|
|
343
|
+
await trx.schema.alterTable("strapi_release_actions", (table) => {
|
|
344
|
+
table.string("entry_document_id");
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
const releaseActions = await trx.select("*").from("strapi_release_actions");
|
|
348
|
+
utils.async.map(releaseActions, async (action) => {
|
|
349
|
+
const { target_type, target_id } = action;
|
|
350
|
+
const entry = await db.query(target_type).findOne({ where: { id: target_id } });
|
|
351
|
+
if (entry) {
|
|
352
|
+
await trx("strapi_release_actions").update({ entry_document_id: entry.documentId }).where("id", action.id);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
async down() {
|
|
358
|
+
throw new Error("not implemented");
|
|
359
|
+
}
|
|
360
|
+
};
|
|
281
361
|
const register = async ({ strapi: strapi2 }) => {
|
|
282
362
|
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
283
363
|
await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
|
|
364
|
+
strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
|
|
284
365
|
strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
|
|
285
366
|
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
286
367
|
}
|
|
@@ -290,6 +371,104 @@ const register = async ({ strapi: strapi2 }) => {
|
|
|
290
371
|
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
291
372
|
}
|
|
292
373
|
};
|
|
374
|
+
const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
|
|
375
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
376
|
+
where: {
|
|
377
|
+
actions: {
|
|
378
|
+
contentType,
|
|
379
|
+
entryDocumentId: entry.documentId,
|
|
380
|
+
locale: entry.locale
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
const entryStatus = await isEntryValid(contentType, entry, { strapi });
|
|
385
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
386
|
+
where: {
|
|
387
|
+
contentType,
|
|
388
|
+
entryDocumentId: entry.documentId,
|
|
389
|
+
locale: entry.locale
|
|
390
|
+
},
|
|
391
|
+
data: {
|
|
392
|
+
isEntryValid: entryStatus
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
for (const release2 of releases) {
|
|
396
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const deleteActionsAndUpdateReleaseStatus = async (params) => {
|
|
400
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
401
|
+
where: {
|
|
402
|
+
actions: params
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
406
|
+
where: params
|
|
407
|
+
});
|
|
408
|
+
for (const release2 of releases) {
|
|
409
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
const deleteActionsOnDelete = async (ctx, next) => {
|
|
413
|
+
if (ctx.action !== "delete") {
|
|
414
|
+
return next();
|
|
415
|
+
}
|
|
416
|
+
if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
|
|
417
|
+
return next();
|
|
418
|
+
}
|
|
419
|
+
const contentType = ctx.contentType.uid;
|
|
420
|
+
const { documentId, locale } = ctx.params;
|
|
421
|
+
const result = await next();
|
|
422
|
+
if (!result) {
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
deleteActionsAndUpdateReleaseStatus({
|
|
427
|
+
contentType,
|
|
428
|
+
entryDocumentId: documentId,
|
|
429
|
+
...locale !== "*" && { locale }
|
|
430
|
+
});
|
|
431
|
+
} catch (error) {
|
|
432
|
+
strapi.log.error("Error while deleting release actions after delete", {
|
|
433
|
+
error
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
return result;
|
|
437
|
+
};
|
|
438
|
+
const updateActionsOnUpdate = async (ctx, next) => {
|
|
439
|
+
if (ctx.action !== "update") {
|
|
440
|
+
return next();
|
|
441
|
+
}
|
|
442
|
+
if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
|
|
443
|
+
return next();
|
|
444
|
+
}
|
|
445
|
+
const contentType = ctx.contentType.uid;
|
|
446
|
+
const result = await next();
|
|
447
|
+
if (!result) {
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
try {
|
|
451
|
+
updateActionsStatusAndUpdateReleaseStatus(contentType, result);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
strapi.log.error("Error while updating release actions after update", {
|
|
454
|
+
error
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
return result;
|
|
458
|
+
};
|
|
459
|
+
const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
|
|
460
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
461
|
+
where: {
|
|
462
|
+
actions: params
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
466
|
+
where: params
|
|
467
|
+
});
|
|
468
|
+
for (const release2 of releases) {
|
|
469
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
293
472
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
294
473
|
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
295
474
|
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
@@ -297,115 +476,29 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
297
476
|
);
|
|
298
477
|
strapi2.db.lifecycles.subscribe({
|
|
299
478
|
models: contentTypesWithDraftAndPublish,
|
|
300
|
-
async afterDelete(event) {
|
|
301
|
-
try {
|
|
302
|
-
const { model, result } = event;
|
|
303
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
304
|
-
const { id } = result;
|
|
305
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
306
|
-
where: {
|
|
307
|
-
actions: {
|
|
308
|
-
target_type: model.uid,
|
|
309
|
-
target_id: id
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
314
|
-
where: {
|
|
315
|
-
target_type: model.uid,
|
|
316
|
-
target_id: id
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
for (const release2 of releases) {
|
|
320
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
} catch (error) {
|
|
324
|
-
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
325
|
-
}
|
|
326
|
-
},
|
|
327
|
-
/**
|
|
328
|
-
* deleteMany hook doesn't return the deleted entries ids
|
|
329
|
-
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
330
|
-
*/
|
|
331
|
-
async beforeDeleteMany(event) {
|
|
332
|
-
const { model, params } = event;
|
|
333
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
334
|
-
const { where } = params;
|
|
335
|
-
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
336
|
-
event.state.entriesToDelete = entriesToDelete;
|
|
337
|
-
}
|
|
338
|
-
},
|
|
339
479
|
/**
|
|
340
|
-
*
|
|
341
|
-
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
480
|
+
* deleteMany is still used outside documents service, for example when deleting a locale
|
|
342
481
|
*/
|
|
343
482
|
async afterDeleteMany(event) {
|
|
344
483
|
try {
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
target_id: {
|
|
353
|
-
$in: entriesToDelete.map((entry) => entry.id)
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
359
|
-
where: {
|
|
360
|
-
target_type: model.uid,
|
|
361
|
-
target_id: {
|
|
362
|
-
$in: entriesToDelete.map((entry) => entry.id)
|
|
363
|
-
}
|
|
364
|
-
}
|
|
484
|
+
const model = strapi2.getModel(event.model.uid);
|
|
485
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
486
|
+
const { where } = event.params;
|
|
487
|
+
deleteReleasesActionsAndUpdateReleaseStatus({
|
|
488
|
+
contentType: model.uid,
|
|
489
|
+
locale: where.locale ?? null,
|
|
490
|
+
...where.documentId && { entryDocumentId: where.documentId }
|
|
365
491
|
});
|
|
366
|
-
for (const release2 of releases) {
|
|
367
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
368
|
-
}
|
|
369
492
|
}
|
|
370
493
|
} catch (error) {
|
|
371
494
|
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
372
495
|
error
|
|
373
496
|
});
|
|
374
497
|
}
|
|
375
|
-
},
|
|
376
|
-
async afterUpdate(event) {
|
|
377
|
-
try {
|
|
378
|
-
const { model, result } = event;
|
|
379
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
380
|
-
const isEntryValid = await getEntryValidStatus(model.uid, result, {
|
|
381
|
-
strapi: strapi2
|
|
382
|
-
});
|
|
383
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
384
|
-
where: {
|
|
385
|
-
target_type: model.uid,
|
|
386
|
-
target_id: result.id
|
|
387
|
-
},
|
|
388
|
-
data: {
|
|
389
|
-
isEntryValid
|
|
390
|
-
}
|
|
391
|
-
});
|
|
392
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
393
|
-
where: {
|
|
394
|
-
actions: {
|
|
395
|
-
target_type: model.uid,
|
|
396
|
-
target_id: result.id
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
});
|
|
400
|
-
for (const release2 of releases) {
|
|
401
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
} catch (error) {
|
|
405
|
-
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
406
|
-
}
|
|
407
498
|
}
|
|
408
499
|
});
|
|
500
|
+
strapi2.documents.use(deleteActionsOnDelete);
|
|
501
|
+
strapi2.documents.use(updateActionsOnUpdate);
|
|
409
502
|
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
410
503
|
strapi2.log.error(
|
|
411
504
|
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
@@ -497,15 +590,13 @@ const schema = {
|
|
|
497
590
|
enum: ["publish", "unpublish"],
|
|
498
591
|
required: true
|
|
499
592
|
},
|
|
500
|
-
entry: {
|
|
501
|
-
type: "relation",
|
|
502
|
-
relation: "morphToOne",
|
|
503
|
-
configurable: false
|
|
504
|
-
},
|
|
505
593
|
contentType: {
|
|
506
594
|
type: "string",
|
|
507
595
|
required: true
|
|
508
596
|
},
|
|
597
|
+
entryDocumentId: {
|
|
598
|
+
type: "string"
|
|
599
|
+
},
|
|
509
600
|
locale: {
|
|
510
601
|
type: "string"
|
|
511
602
|
},
|
|
@@ -527,18 +618,6 @@ const contentTypes = {
|
|
|
527
618
|
release: release$1,
|
|
528
619
|
"release-action": releaseAction$1
|
|
529
620
|
};
|
|
530
|
-
const getGroupName = (queryValue) => {
|
|
531
|
-
switch (queryValue) {
|
|
532
|
-
case "contentType":
|
|
533
|
-
return "contentType.displayName";
|
|
534
|
-
case "action":
|
|
535
|
-
return "type";
|
|
536
|
-
case "locale":
|
|
537
|
-
return ___default.default.getOr("No locale", "locale.name");
|
|
538
|
-
default:
|
|
539
|
-
return "contentType.displayName";
|
|
540
|
-
}
|
|
541
|
-
};
|
|
542
621
|
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
543
622
|
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
544
623
|
strapi2.eventHub.emit(event, {
|
|
@@ -547,93 +626,32 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
547
626
|
release: release2
|
|
548
627
|
});
|
|
549
628
|
};
|
|
550
|
-
const publishSingleTypeAction = async (uid, actionType, entryId) => {
|
|
551
|
-
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
552
|
-
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
553
|
-
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
554
|
-
const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
|
|
555
|
-
try {
|
|
556
|
-
if (actionType === "publish") {
|
|
557
|
-
await entityManagerService.publish(entry, uid);
|
|
558
|
-
} else {
|
|
559
|
-
await entityManagerService.unpublish(entry, uid);
|
|
560
|
-
}
|
|
561
|
-
} catch (error) {
|
|
562
|
-
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
|
|
563
|
-
;
|
|
564
|
-
else {
|
|
565
|
-
throw error;
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
};
|
|
569
|
-
const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
|
|
570
|
-
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
571
|
-
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
572
|
-
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
573
|
-
const entriesToPublish = await strapi2.entityService.findMany(uid, {
|
|
574
|
-
filters: {
|
|
575
|
-
id: {
|
|
576
|
-
$in: entriesToPublishIds
|
|
577
|
-
}
|
|
578
|
-
},
|
|
579
|
-
populate
|
|
580
|
-
});
|
|
581
|
-
const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
|
|
582
|
-
filters: {
|
|
583
|
-
id: {
|
|
584
|
-
$in: entriestoUnpublishIds
|
|
585
|
-
}
|
|
586
|
-
},
|
|
587
|
-
populate
|
|
588
|
-
});
|
|
589
|
-
if (entriesToPublish.length > 0) {
|
|
590
|
-
await entityManagerService.publishMany(entriesToPublish, uid);
|
|
591
|
-
}
|
|
592
|
-
if (entriesToUnpublish.length > 0) {
|
|
593
|
-
await entityManagerService.unpublishMany(entriesToUnpublish, uid);
|
|
594
|
-
}
|
|
595
|
-
};
|
|
596
629
|
const getFormattedActions = async (releaseId) => {
|
|
597
630
|
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
598
631
|
where: {
|
|
599
632
|
release: {
|
|
600
633
|
id: releaseId
|
|
601
634
|
}
|
|
602
|
-
},
|
|
603
|
-
populate: {
|
|
604
|
-
entry: {
|
|
605
|
-
fields: ["id"]
|
|
606
|
-
}
|
|
607
635
|
}
|
|
608
636
|
});
|
|
609
637
|
if (actions.length === 0) {
|
|
610
638
|
throw new utils.errors.ValidationError("No entries to publish");
|
|
611
639
|
}
|
|
612
|
-
const
|
|
613
|
-
const singleTypeActions = [];
|
|
640
|
+
const formattedActions = {};
|
|
614
641
|
for (const action of actions) {
|
|
615
642
|
const contentTypeUid = action.contentType;
|
|
616
|
-
if (
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
if (action.type === "publish") {
|
|
624
|
-
collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
|
|
625
|
-
} else {
|
|
626
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
627
|
-
}
|
|
628
|
-
} else {
|
|
629
|
-
singleTypeActions.push({
|
|
630
|
-
uid: contentTypeUid,
|
|
631
|
-
action: action.type,
|
|
632
|
-
id: action.entry.id
|
|
633
|
-
});
|
|
643
|
+
if (!formattedActions[contentTypeUid]) {
|
|
644
|
+
formattedActions[contentTypeUid] = {
|
|
645
|
+
publish: [],
|
|
646
|
+
unpublish: []
|
|
647
|
+
};
|
|
634
648
|
}
|
|
649
|
+
formattedActions[contentTypeUid][action.type].push({
|
|
650
|
+
documentId: action.entryDocumentId,
|
|
651
|
+
locale: action.locale
|
|
652
|
+
});
|
|
635
653
|
}
|
|
636
|
-
return
|
|
654
|
+
return formattedActions;
|
|
637
655
|
};
|
|
638
656
|
return {
|
|
639
657
|
async create(releaseData, { user }) {
|
|
@@ -680,91 +698,10 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
680
698
|
}
|
|
681
699
|
});
|
|
682
700
|
},
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
}
|
|
688
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
689
|
-
where: {
|
|
690
|
-
actions: {
|
|
691
|
-
target_type: contentTypeUid,
|
|
692
|
-
target_id: {
|
|
693
|
-
$in: entries
|
|
694
|
-
}
|
|
695
|
-
},
|
|
696
|
-
releasedAt: {
|
|
697
|
-
$null: true
|
|
698
|
-
}
|
|
699
|
-
},
|
|
700
|
-
populate: {
|
|
701
|
-
// Filter the action to get only the content type entry
|
|
702
|
-
actions: {
|
|
703
|
-
where: {
|
|
704
|
-
target_type: contentTypeUid,
|
|
705
|
-
target_id: {
|
|
706
|
-
$in: entries
|
|
707
|
-
}
|
|
708
|
-
},
|
|
709
|
-
populate: {
|
|
710
|
-
entry: {
|
|
711
|
-
select: ["id"]
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
}
|
|
716
|
-
});
|
|
717
|
-
return releases.map((release2) => {
|
|
718
|
-
if (release2.actions?.length) {
|
|
719
|
-
const actionsForEntry = release2.actions;
|
|
720
|
-
delete release2.actions;
|
|
721
|
-
return {
|
|
722
|
-
...release2,
|
|
723
|
-
actions: actionsForEntry
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
return release2;
|
|
727
|
-
});
|
|
728
|
-
},
|
|
729
|
-
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
730
|
-
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
731
|
-
where: {
|
|
732
|
-
releasedAt: {
|
|
733
|
-
$null: true
|
|
734
|
-
},
|
|
735
|
-
actions: {
|
|
736
|
-
target_type: contentTypeUid,
|
|
737
|
-
target_id: entryId
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
});
|
|
741
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
742
|
-
where: {
|
|
743
|
-
$or: [
|
|
744
|
-
{
|
|
745
|
-
id: {
|
|
746
|
-
$notIn: releasesRelated.map((release2) => release2.id)
|
|
747
|
-
}
|
|
748
|
-
},
|
|
749
|
-
{
|
|
750
|
-
actions: null
|
|
751
|
-
}
|
|
752
|
-
],
|
|
753
|
-
releasedAt: {
|
|
754
|
-
$null: true
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
});
|
|
758
|
-
return releases.map((release2) => {
|
|
759
|
-
if (release2.actions?.length) {
|
|
760
|
-
const [actionForEntry] = release2.actions;
|
|
761
|
-
delete release2.actions;
|
|
762
|
-
return {
|
|
763
|
-
...release2,
|
|
764
|
-
action: actionForEntry
|
|
765
|
-
};
|
|
766
|
-
}
|
|
767
|
-
return release2;
|
|
701
|
+
findMany(query) {
|
|
702
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
703
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
704
|
+
...dbQuery
|
|
768
705
|
});
|
|
769
706
|
},
|
|
770
707
|
async update(id, releaseData, { user }) {
|
|
@@ -800,131 +737,6 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
800
737
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
801
738
|
return updatedRelease;
|
|
802
739
|
},
|
|
803
|
-
async createAction(releaseId, action) {
|
|
804
|
-
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
805
|
-
strapi: strapi2
|
|
806
|
-
});
|
|
807
|
-
await Promise.all([
|
|
808
|
-
validateEntryContentType(action.entry.contentType),
|
|
809
|
-
validateUniqueEntry(releaseId, action)
|
|
810
|
-
]);
|
|
811
|
-
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
|
|
812
|
-
if (!release2) {
|
|
813
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
814
|
-
}
|
|
815
|
-
if (release2.releasedAt) {
|
|
816
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
817
|
-
}
|
|
818
|
-
const { entry, type } = action;
|
|
819
|
-
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
820
|
-
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
821
|
-
const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
822
|
-
data: {
|
|
823
|
-
type,
|
|
824
|
-
contentType: entry.contentType,
|
|
825
|
-
locale: entry.locale,
|
|
826
|
-
isEntryValid,
|
|
827
|
-
entry: {
|
|
828
|
-
id: entry.id,
|
|
829
|
-
__type: entry.contentType,
|
|
830
|
-
__pivot: { field: "entry" }
|
|
831
|
-
},
|
|
832
|
-
release: releaseId
|
|
833
|
-
},
|
|
834
|
-
populate: { release: { select: ["id"] }, entry: { select: ["id"] } }
|
|
835
|
-
});
|
|
836
|
-
this.updateReleaseStatus(releaseId);
|
|
837
|
-
return releaseAction2;
|
|
838
|
-
},
|
|
839
|
-
async findActions(releaseId, query) {
|
|
840
|
-
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
841
|
-
where: { id: releaseId },
|
|
842
|
-
select: ["id"]
|
|
843
|
-
});
|
|
844
|
-
if (!release2) {
|
|
845
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
846
|
-
}
|
|
847
|
-
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
848
|
-
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
849
|
-
...dbQuery,
|
|
850
|
-
populate: {
|
|
851
|
-
entry: {
|
|
852
|
-
populate: "*"
|
|
853
|
-
}
|
|
854
|
-
},
|
|
855
|
-
where: {
|
|
856
|
-
release: releaseId
|
|
857
|
-
}
|
|
858
|
-
});
|
|
859
|
-
},
|
|
860
|
-
async countActions(query) {
|
|
861
|
-
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
862
|
-
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
863
|
-
},
|
|
864
|
-
async groupActions(actions, groupBy) {
|
|
865
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
866
|
-
if (!acc.includes(action.contentType)) {
|
|
867
|
-
acc.push(action.contentType);
|
|
868
|
-
}
|
|
869
|
-
return acc;
|
|
870
|
-
}, []);
|
|
871
|
-
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(contentTypeUids);
|
|
872
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
873
|
-
const formattedData = actions.map((action) => {
|
|
874
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
875
|
-
return {
|
|
876
|
-
...action,
|
|
877
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
878
|
-
contentType: {
|
|
879
|
-
displayName,
|
|
880
|
-
mainFieldValue: action.entry[mainField],
|
|
881
|
-
uid: action.contentType
|
|
882
|
-
}
|
|
883
|
-
};
|
|
884
|
-
});
|
|
885
|
-
const groupName = getGroupName(groupBy);
|
|
886
|
-
return ___default.default.groupBy(groupName)(formattedData);
|
|
887
|
-
},
|
|
888
|
-
async getLocalesDataForActions() {
|
|
889
|
-
if (!strapi2.plugin("i18n")) {
|
|
890
|
-
return {};
|
|
891
|
-
}
|
|
892
|
-
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
893
|
-
return allLocales.reduce((acc, locale) => {
|
|
894
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
895
|
-
return acc;
|
|
896
|
-
}, {});
|
|
897
|
-
},
|
|
898
|
-
async getContentTypesDataForActions(contentTypesUids) {
|
|
899
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
900
|
-
const contentTypesData = {};
|
|
901
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
902
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
903
|
-
uid: contentTypeUid
|
|
904
|
-
});
|
|
905
|
-
contentTypesData[contentTypeUid] = {
|
|
906
|
-
mainField: contentTypeConfig.settings.mainField,
|
|
907
|
-
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
908
|
-
};
|
|
909
|
-
}
|
|
910
|
-
return contentTypesData;
|
|
911
|
-
},
|
|
912
|
-
getContentTypeModelsFromActions(actions) {
|
|
913
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
914
|
-
if (!acc.includes(action.contentType)) {
|
|
915
|
-
acc.push(action.contentType);
|
|
916
|
-
}
|
|
917
|
-
return acc;
|
|
918
|
-
}, []);
|
|
919
|
-
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
920
|
-
(acc, contentTypeUid) => {
|
|
921
|
-
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
922
|
-
return acc;
|
|
923
|
-
},
|
|
924
|
-
{}
|
|
925
|
-
);
|
|
926
|
-
return contentTypeModelsMap;
|
|
927
|
-
},
|
|
928
740
|
async getAllComponents() {
|
|
929
741
|
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
930
742
|
const components = await contentManagerComponentsService.findAllComponents();
|
|
@@ -990,20 +802,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
990
802
|
}
|
|
991
803
|
try {
|
|
992
804
|
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
993
|
-
const
|
|
994
|
-
await strapi2.db.transaction(
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
});
|
|
805
|
+
const formattedActions = await getFormattedActions(releaseId);
|
|
806
|
+
await strapi2.db.transaction(
|
|
807
|
+
async () => Promise.all(
|
|
808
|
+
Object.keys(formattedActions).map(async (contentTypeUid) => {
|
|
809
|
+
const contentType = contentTypeUid;
|
|
810
|
+
const { publish, unpublish } = formattedActions[contentType];
|
|
811
|
+
return Promise.all([
|
|
812
|
+
...publish.map((params) => strapi2.documents(contentType).publish(params)),
|
|
813
|
+
...unpublish.map((params) => strapi2.documents(contentType).unpublish(params))
|
|
814
|
+
]);
|
|
815
|
+
})
|
|
816
|
+
)
|
|
817
|
+
);
|
|
1007
818
|
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1008
819
|
where: {
|
|
1009
820
|
id: releaseId
|
|
@@ -1033,13 +844,226 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1033
844
|
};
|
|
1034
845
|
}
|
|
1035
846
|
});
|
|
1036
|
-
if (error instanceof Error) {
|
|
1037
|
-
throw error;
|
|
847
|
+
if (error instanceof Error) {
|
|
848
|
+
throw error;
|
|
849
|
+
}
|
|
850
|
+
return release2;
|
|
851
|
+
},
|
|
852
|
+
async updateReleaseStatus(releaseId) {
|
|
853
|
+
const releaseActionService = getService("release-action", { strapi: strapi2 });
|
|
854
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
855
|
+
releaseActionService.countActions({
|
|
856
|
+
filters: {
|
|
857
|
+
release: releaseId
|
|
858
|
+
}
|
|
859
|
+
}),
|
|
860
|
+
releaseActionService.countActions({
|
|
861
|
+
filters: {
|
|
862
|
+
release: releaseId,
|
|
863
|
+
isEntryValid: false
|
|
864
|
+
}
|
|
865
|
+
})
|
|
866
|
+
]);
|
|
867
|
+
if (totalActions > 0) {
|
|
868
|
+
if (invalidActions > 0) {
|
|
869
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
870
|
+
where: {
|
|
871
|
+
id: releaseId
|
|
872
|
+
},
|
|
873
|
+
data: {
|
|
874
|
+
status: "blocked"
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
879
|
+
where: {
|
|
880
|
+
id: releaseId
|
|
881
|
+
},
|
|
882
|
+
data: {
|
|
883
|
+
status: "ready"
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
888
|
+
where: {
|
|
889
|
+
id: releaseId
|
|
890
|
+
},
|
|
891
|
+
data: {
|
|
892
|
+
status: "empty"
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
};
|
|
898
|
+
const getGroupName = (queryValue) => {
|
|
899
|
+
switch (queryValue) {
|
|
900
|
+
case "contentType":
|
|
901
|
+
return "contentType.displayName";
|
|
902
|
+
case "type":
|
|
903
|
+
return "type";
|
|
904
|
+
case "locale":
|
|
905
|
+
return ___default.default.getOr("No locale", "locale.name");
|
|
906
|
+
default:
|
|
907
|
+
return "contentType.displayName";
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
const createReleaseActionService = ({ strapi: strapi2 }) => {
|
|
911
|
+
const getLocalesDataForActions = async () => {
|
|
912
|
+
if (!strapi2.plugin("i18n")) {
|
|
913
|
+
return {};
|
|
914
|
+
}
|
|
915
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
916
|
+
return allLocales.reduce((acc, locale) => {
|
|
917
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
918
|
+
return acc;
|
|
919
|
+
}, {});
|
|
920
|
+
};
|
|
921
|
+
const getContentTypesDataForActions = async (contentTypesUids) => {
|
|
922
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
923
|
+
const contentTypesData = {};
|
|
924
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
925
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
926
|
+
uid: contentTypeUid
|
|
927
|
+
});
|
|
928
|
+
contentTypesData[contentTypeUid] = {
|
|
929
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
930
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
return contentTypesData;
|
|
934
|
+
};
|
|
935
|
+
return {
|
|
936
|
+
async create(releaseId, action, { disableUpdateReleaseStatus = false } = {}) {
|
|
937
|
+
const { validateEntryData, validateUniqueEntry } = getService("release-validation", {
|
|
938
|
+
strapi: strapi2
|
|
939
|
+
});
|
|
940
|
+
await Promise.all([
|
|
941
|
+
validateEntryData(action.contentType, action.entryDocumentId),
|
|
942
|
+
validateUniqueEntry(releaseId, action)
|
|
943
|
+
]);
|
|
944
|
+
const model = strapi2.contentType(action.contentType);
|
|
945
|
+
if (model.kind === "singleType") {
|
|
946
|
+
const document = await strapi2.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
947
|
+
if (!document) {
|
|
948
|
+
throw new utils.errors.NotFoundError(`No entry found for contentType ${action.contentType}`);
|
|
949
|
+
}
|
|
950
|
+
action.entryDocumentId = document.documentId;
|
|
951
|
+
}
|
|
952
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
|
|
953
|
+
if (!release2) {
|
|
954
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
955
|
+
}
|
|
956
|
+
if (release2.releasedAt) {
|
|
957
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
958
|
+
}
|
|
959
|
+
const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
|
|
960
|
+
{
|
|
961
|
+
contentType: action.contentType,
|
|
962
|
+
documentId: action.entryDocumentId,
|
|
963
|
+
locale: action.locale
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
strapi: strapi2
|
|
967
|
+
}
|
|
968
|
+
) : true;
|
|
969
|
+
const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
970
|
+
data: {
|
|
971
|
+
...action,
|
|
972
|
+
release: release2.id,
|
|
973
|
+
isEntryValid: actionStatus
|
|
974
|
+
},
|
|
975
|
+
populate: { release: { select: ["id"] } }
|
|
976
|
+
});
|
|
977
|
+
if (!disableUpdateReleaseStatus) {
|
|
978
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
1038
979
|
}
|
|
1039
|
-
return
|
|
980
|
+
return releaseAction2;
|
|
1040
981
|
},
|
|
1041
|
-
async
|
|
1042
|
-
const
|
|
982
|
+
async findPage(releaseId, query) {
|
|
983
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
984
|
+
where: { id: releaseId },
|
|
985
|
+
select: ["id"]
|
|
986
|
+
});
|
|
987
|
+
if (!release2) {
|
|
988
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
989
|
+
}
|
|
990
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
991
|
+
const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
992
|
+
...dbQuery,
|
|
993
|
+
where: {
|
|
994
|
+
release: releaseId
|
|
995
|
+
}
|
|
996
|
+
});
|
|
997
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
998
|
+
const actionsWithEntry = await utils.async.map(actions, async (action) => {
|
|
999
|
+
const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
|
|
1000
|
+
const entry = await getEntry(
|
|
1001
|
+
{
|
|
1002
|
+
contentType: action.contentType,
|
|
1003
|
+
documentId: action.entryDocumentId,
|
|
1004
|
+
locale: action.locale,
|
|
1005
|
+
populate,
|
|
1006
|
+
status: action.type === "publish" ? "draft" : "published"
|
|
1007
|
+
},
|
|
1008
|
+
{ strapi: strapi2 }
|
|
1009
|
+
);
|
|
1010
|
+
return {
|
|
1011
|
+
...action,
|
|
1012
|
+
entry,
|
|
1013
|
+
status: entry ? await getEntryStatus(action.contentType, entry) : null
|
|
1014
|
+
};
|
|
1015
|
+
});
|
|
1016
|
+
return {
|
|
1017
|
+
results: actionsWithEntry,
|
|
1018
|
+
pagination
|
|
1019
|
+
};
|
|
1020
|
+
},
|
|
1021
|
+
async groupActions(actions, groupBy) {
|
|
1022
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
1023
|
+
if (!acc.includes(action.contentType)) {
|
|
1024
|
+
acc.push(action.contentType);
|
|
1025
|
+
}
|
|
1026
|
+
return acc;
|
|
1027
|
+
}, []);
|
|
1028
|
+
const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
|
|
1029
|
+
const allLocalesDictionary = await getLocalesDataForActions();
|
|
1030
|
+
const formattedData = actions.map((action) => {
|
|
1031
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
1032
|
+
return {
|
|
1033
|
+
...action,
|
|
1034
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
1035
|
+
contentType: {
|
|
1036
|
+
displayName,
|
|
1037
|
+
mainFieldValue: action.entry[mainField],
|
|
1038
|
+
uid: action.contentType
|
|
1039
|
+
}
|
|
1040
|
+
};
|
|
1041
|
+
});
|
|
1042
|
+
const groupName = getGroupName(groupBy);
|
|
1043
|
+
return ___default.default.groupBy(groupName)(formattedData);
|
|
1044
|
+
},
|
|
1045
|
+
getContentTypeModelsFromActions(actions) {
|
|
1046
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
1047
|
+
if (!acc.includes(action.contentType)) {
|
|
1048
|
+
acc.push(action.contentType);
|
|
1049
|
+
}
|
|
1050
|
+
return acc;
|
|
1051
|
+
}, []);
|
|
1052
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
1053
|
+
(acc, contentTypeUid) => {
|
|
1054
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
1055
|
+
return acc;
|
|
1056
|
+
},
|
|
1057
|
+
{}
|
|
1058
|
+
);
|
|
1059
|
+
return contentTypeModelsMap;
|
|
1060
|
+
},
|
|
1061
|
+
async countActions(query) {
|
|
1062
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
1063
|
+
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
1064
|
+
},
|
|
1065
|
+
async update(actionId, releaseId, update) {
|
|
1066
|
+
const action = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findOne({
|
|
1043
1067
|
where: {
|
|
1044
1068
|
id: actionId,
|
|
1045
1069
|
release: {
|
|
@@ -1048,17 +1072,42 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1048
1072
|
$null: true
|
|
1049
1073
|
}
|
|
1050
1074
|
}
|
|
1051
|
-
}
|
|
1052
|
-
data: update
|
|
1075
|
+
}
|
|
1053
1076
|
});
|
|
1054
|
-
if (!
|
|
1077
|
+
if (!action) {
|
|
1055
1078
|
throw new utils.errors.NotFoundError(
|
|
1056
1079
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1057
1080
|
);
|
|
1058
1081
|
}
|
|
1082
|
+
const actionStatus = update.type === "publish" ? await getDraftEntryValidStatus(
|
|
1083
|
+
{
|
|
1084
|
+
contentType: action.contentType,
|
|
1085
|
+
documentId: action.entryDocumentId,
|
|
1086
|
+
locale: action.locale
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
strapi: strapi2
|
|
1090
|
+
}
|
|
1091
|
+
) : true;
|
|
1092
|
+
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
1093
|
+
where: {
|
|
1094
|
+
id: actionId,
|
|
1095
|
+
release: {
|
|
1096
|
+
id: releaseId,
|
|
1097
|
+
releasedAt: {
|
|
1098
|
+
$null: true
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
data: {
|
|
1103
|
+
...update,
|
|
1104
|
+
isEntryValid: actionStatus
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1107
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1059
1108
|
return updatedAction;
|
|
1060
1109
|
},
|
|
1061
|
-
async
|
|
1110
|
+
async delete(actionId, releaseId) {
|
|
1062
1111
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
1063
1112
|
where: {
|
|
1064
1113
|
id: actionId,
|
|
@@ -1075,51 +1124,8 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1075
1124
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1076
1125
|
);
|
|
1077
1126
|
}
|
|
1078
|
-
|
|
1127
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1079
1128
|
return deletedAction;
|
|
1080
|
-
},
|
|
1081
|
-
async updateReleaseStatus(releaseId) {
|
|
1082
|
-
const [totalActions, invalidActions] = await Promise.all([
|
|
1083
|
-
this.countActions({
|
|
1084
|
-
filters: {
|
|
1085
|
-
release: releaseId
|
|
1086
|
-
}
|
|
1087
|
-
}),
|
|
1088
|
-
this.countActions({
|
|
1089
|
-
filters: {
|
|
1090
|
-
release: releaseId,
|
|
1091
|
-
isEntryValid: false
|
|
1092
|
-
}
|
|
1093
|
-
})
|
|
1094
|
-
]);
|
|
1095
|
-
if (totalActions > 0) {
|
|
1096
|
-
if (invalidActions > 0) {
|
|
1097
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1098
|
-
where: {
|
|
1099
|
-
id: releaseId
|
|
1100
|
-
},
|
|
1101
|
-
data: {
|
|
1102
|
-
status: "blocked"
|
|
1103
|
-
}
|
|
1104
|
-
});
|
|
1105
|
-
}
|
|
1106
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1107
|
-
where: {
|
|
1108
|
-
id: releaseId
|
|
1109
|
-
},
|
|
1110
|
-
data: {
|
|
1111
|
-
status: "ready"
|
|
1112
|
-
}
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1115
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1116
|
-
where: {
|
|
1117
|
-
id: releaseId
|
|
1118
|
-
},
|
|
1119
|
-
data: {
|
|
1120
|
-
status: "empty"
|
|
1121
|
-
}
|
|
1122
|
-
});
|
|
1123
1129
|
}
|
|
1124
1130
|
};
|
|
1125
1131
|
};
|
|
@@ -1135,30 +1141,35 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
1135
1141
|
where: {
|
|
1136
1142
|
id: releaseId
|
|
1137
1143
|
},
|
|
1138
|
-
populate: {
|
|
1144
|
+
populate: {
|
|
1145
|
+
actions: true
|
|
1146
|
+
}
|
|
1139
1147
|
});
|
|
1140
1148
|
if (!release2) {
|
|
1141
1149
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1142
1150
|
}
|
|
1143
1151
|
const isEntryInRelease = release2.actions.some(
|
|
1144
|
-
(action) =>
|
|
1152
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
1145
1153
|
);
|
|
1146
1154
|
if (isEntryInRelease) {
|
|
1147
1155
|
throw new AlreadyOnReleaseError(
|
|
1148
|
-
`Entry with
|
|
1156
|
+
`Entry with documentId ${releaseActionArgs.entryDocumentId}${releaseActionArgs.locale ? `( ${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
|
|
1149
1157
|
);
|
|
1150
1158
|
}
|
|
1151
1159
|
},
|
|
1152
|
-
|
|
1160
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
1153
1161
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
1154
1162
|
if (!contentType) {
|
|
1155
1163
|
throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
1156
1164
|
}
|
|
1157
|
-
if (!contentType
|
|
1165
|
+
if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
|
|
1158
1166
|
throw new utils.errors.ValidationError(
|
|
1159
1167
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
1160
1168
|
);
|
|
1161
1169
|
}
|
|
1170
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1171
|
+
throw new utils.errors.ValidationError("Document id is required for collection type");
|
|
1172
|
+
}
|
|
1162
1173
|
},
|
|
1163
1174
|
async validatePendingReleasesLimit() {
|
|
1164
1175
|
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
@@ -1247,78 +1258,165 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1247
1258
|
}
|
|
1248
1259
|
};
|
|
1249
1260
|
};
|
|
1261
|
+
const DEFAULT_SETTINGS = {
|
|
1262
|
+
defaultTimezone: null
|
|
1263
|
+
};
|
|
1264
|
+
const createSettingsService = ({ strapi: strapi2 }) => {
|
|
1265
|
+
const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
|
|
1266
|
+
return {
|
|
1267
|
+
async update({ settings: settings2 }) {
|
|
1268
|
+
const store = await getStore();
|
|
1269
|
+
store.set({ key: "settings", value: settings2 });
|
|
1270
|
+
return settings2;
|
|
1271
|
+
},
|
|
1272
|
+
async find() {
|
|
1273
|
+
const store = await getStore();
|
|
1274
|
+
const settings2 = await store.get({ key: "settings" });
|
|
1275
|
+
return {
|
|
1276
|
+
...DEFAULT_SETTINGS,
|
|
1277
|
+
...settings2 || {}
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
};
|
|
1281
|
+
};
|
|
1250
1282
|
const services = {
|
|
1251
1283
|
release: createReleaseService,
|
|
1284
|
+
"release-action": createReleaseActionService,
|
|
1252
1285
|
"release-validation": createReleaseValidationService,
|
|
1253
|
-
scheduling: createSchedulingService
|
|
1286
|
+
scheduling: createSchedulingService,
|
|
1287
|
+
settings: createSettingsService
|
|
1254
1288
|
};
|
|
1255
|
-
const RELEASE_SCHEMA =
|
|
1256
|
-
name:
|
|
1257
|
-
scheduledAt:
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
otherwise: yup__namespace.string().nullable()
|
|
1263
|
-
}),
|
|
1264
|
-
timezone: yup__namespace.string().when("isScheduled", {
|
|
1265
|
-
is: true,
|
|
1266
|
-
then: yup__namespace.string().required().nullable(),
|
|
1267
|
-
otherwise: yup__namespace.string().nullable()
|
|
1268
|
-
}),
|
|
1269
|
-
date: yup__namespace.string().when("isScheduled", {
|
|
1270
|
-
is: true,
|
|
1271
|
-
then: yup__namespace.string().required().nullable(),
|
|
1272
|
-
otherwise: yup__namespace.string().nullable()
|
|
1289
|
+
const RELEASE_SCHEMA = utils.yup.object().shape({
|
|
1290
|
+
name: utils.yup.string().trim().required(),
|
|
1291
|
+
scheduledAt: utils.yup.string().nullable(),
|
|
1292
|
+
timezone: utils.yup.string().when("scheduledAt", {
|
|
1293
|
+
is: (value) => value !== null && value !== void 0,
|
|
1294
|
+
then: utils.yup.string().required(),
|
|
1295
|
+
otherwise: utils.yup.string().nullable()
|
|
1273
1296
|
})
|
|
1274
1297
|
}).required().noUnknown();
|
|
1298
|
+
const FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA = utils.yup.object().shape({
|
|
1299
|
+
contentType: utils.yup.string().required(),
|
|
1300
|
+
entryDocumentId: utils.yup.string().nullable(),
|
|
1301
|
+
hasEntryAttached: utils.yup.string().nullable(),
|
|
1302
|
+
locale: utils.yup.string().nullable()
|
|
1303
|
+
}).required().noUnknown();
|
|
1275
1304
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
1305
|
+
const validatefindByDocumentAttachedParams = utils.validateYupSchema(
|
|
1306
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1307
|
+
);
|
|
1276
1308
|
const releaseController = {
|
|
1277
|
-
|
|
1309
|
+
/**
|
|
1310
|
+
* Find releases based on documents attached or not to the release.
|
|
1311
|
+
* If `hasEntryAttached` is true, it will return all releases that have the entry attached.
|
|
1312
|
+
* If `hasEntryAttached` is false, it will return all releases that don't have the entry attached.
|
|
1313
|
+
*/
|
|
1314
|
+
async findByDocumentAttached(ctx) {
|
|
1278
1315
|
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1279
1316
|
ability: ctx.state.userAbility,
|
|
1280
1317
|
model: RELEASE_MODEL_UID
|
|
1281
1318
|
});
|
|
1282
1319
|
await permissionsManager.validateQuery(ctx.query);
|
|
1283
1320
|
const releaseService = getService("release", { strapi });
|
|
1284
|
-
const
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
const
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1321
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1322
|
+
await validatefindByDocumentAttachedParams(query);
|
|
1323
|
+
const model = strapi.getModel(query.contentType);
|
|
1324
|
+
if (model.kind && model.kind === "singleType") {
|
|
1325
|
+
const document = await strapi.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
1326
|
+
if (!document) {
|
|
1327
|
+
throw new utils.errors.NotFoundError(`No entry found for contentType ${query.contentType}`);
|
|
1328
|
+
}
|
|
1329
|
+
query.entryDocumentId = document.documentId;
|
|
1330
|
+
}
|
|
1331
|
+
const { contentType, hasEntryAttached, entryDocumentId, locale } = query;
|
|
1332
|
+
const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
|
|
1333
|
+
if (isEntryAttached) {
|
|
1334
|
+
const releases = await releaseService.findMany({
|
|
1335
|
+
where: {
|
|
1336
|
+
releasedAt: null,
|
|
1337
|
+
actions: {
|
|
1338
|
+
contentType,
|
|
1339
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1340
|
+
locale: locale ?? null
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
populate: {
|
|
1299
1344
|
actions: {
|
|
1300
|
-
|
|
1301
|
-
|
|
1345
|
+
fields: ["type"],
|
|
1346
|
+
filters: {
|
|
1347
|
+
contentType,
|
|
1348
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1349
|
+
locale: locale ?? null
|
|
1302
1350
|
}
|
|
1303
1351
|
}
|
|
1304
|
-
}
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
ctx.body = { data: releases };
|
|
1355
|
+
} else {
|
|
1356
|
+
const relatedReleases = await releaseService.findMany({
|
|
1357
|
+
where: {
|
|
1358
|
+
releasedAt: null,
|
|
1359
|
+
actions: {
|
|
1360
|
+
contentType,
|
|
1361
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1362
|
+
locale: locale ?? null
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1305
1365
|
});
|
|
1306
|
-
const
|
|
1366
|
+
const releases = await releaseService.findMany({
|
|
1307
1367
|
where: {
|
|
1368
|
+
$or: [
|
|
1369
|
+
{
|
|
1370
|
+
id: {
|
|
1371
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
{
|
|
1375
|
+
actions: null
|
|
1376
|
+
}
|
|
1377
|
+
],
|
|
1308
1378
|
releasedAt: null
|
|
1309
1379
|
}
|
|
1310
1380
|
});
|
|
1311
|
-
ctx.body = { data
|
|
1381
|
+
ctx.body = { data: releases };
|
|
1312
1382
|
}
|
|
1313
1383
|
},
|
|
1384
|
+
async findPage(ctx) {
|
|
1385
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1386
|
+
ability: ctx.state.userAbility,
|
|
1387
|
+
model: RELEASE_MODEL_UID
|
|
1388
|
+
});
|
|
1389
|
+
await permissionsManager.validateQuery(ctx.query);
|
|
1390
|
+
const releaseService = getService("release", { strapi });
|
|
1391
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1392
|
+
const { results, pagination } = await releaseService.findPage(query);
|
|
1393
|
+
const data = results.map((release2) => {
|
|
1394
|
+
const { actions, ...releaseData } = release2;
|
|
1395
|
+
return {
|
|
1396
|
+
...releaseData,
|
|
1397
|
+
actions: {
|
|
1398
|
+
meta: {
|
|
1399
|
+
count: actions.count
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
});
|
|
1404
|
+
const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
|
|
1405
|
+
where: {
|
|
1406
|
+
releasedAt: null
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1409
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
1410
|
+
},
|
|
1314
1411
|
async findOne(ctx) {
|
|
1315
1412
|
const id = ctx.params.id;
|
|
1316
1413
|
const releaseService = getService("release", { strapi });
|
|
1414
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1317
1415
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
1318
1416
|
if (!release2) {
|
|
1319
1417
|
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1320
1418
|
}
|
|
1321
|
-
const count = await
|
|
1419
|
+
const count = await releaseActionService.countActions({
|
|
1322
1420
|
filters: {
|
|
1323
1421
|
release: id
|
|
1324
1422
|
}
|
|
@@ -1338,28 +1436,43 @@ const releaseController = {
|
|
|
1338
1436
|
ctx.body = { data };
|
|
1339
1437
|
},
|
|
1340
1438
|
async mapEntriesToReleases(ctx) {
|
|
1341
|
-
const { contentTypeUid,
|
|
1342
|
-
if (!contentTypeUid || !
|
|
1439
|
+
const { contentTypeUid, documentIds, locale } = ctx.query;
|
|
1440
|
+
if (!contentTypeUid || !documentIds) {
|
|
1343
1441
|
throw new utils.errors.ValidationError("Missing required query parameters");
|
|
1344
1442
|
}
|
|
1345
1443
|
const releaseService = getService("release", { strapi });
|
|
1346
|
-
const releasesWithActions = await releaseService.
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1444
|
+
const releasesWithActions = await releaseService.findMany({
|
|
1445
|
+
where: {
|
|
1446
|
+
releasedAt: null,
|
|
1447
|
+
actions: {
|
|
1448
|
+
contentType: contentTypeUid,
|
|
1449
|
+
entryDocumentId: {
|
|
1450
|
+
$in: documentIds
|
|
1451
|
+
},
|
|
1452
|
+
locale
|
|
1453
|
+
}
|
|
1454
|
+
},
|
|
1455
|
+
populate: {
|
|
1456
|
+
actions: true
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1350
1459
|
const mappedEntriesInReleases = releasesWithActions.reduce(
|
|
1351
|
-
// TODO: Fix for v5 removed mappedEntriedToRelease
|
|
1352
1460
|
(acc, release2) => {
|
|
1353
1461
|
release2.actions.forEach((action) => {
|
|
1354
|
-
if (
|
|
1355
|
-
|
|
1462
|
+
if (action.contentType !== contentTypeUid) {
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
if (locale && action.locale !== locale) {
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
if (!acc[action.entryDocumentId]) {
|
|
1469
|
+
acc[action.entryDocumentId] = [{ id: release2.id, name: release2.name }];
|
|
1356
1470
|
} else {
|
|
1357
|
-
acc[action.
|
|
1471
|
+
acc[action.entryDocumentId].push({ id: release2.id, name: release2.name });
|
|
1358
1472
|
}
|
|
1359
1473
|
});
|
|
1360
1474
|
return acc;
|
|
1361
1475
|
},
|
|
1362
|
-
// TODO: Fix for v5 removed mappedEntriedToRelease
|
|
1363
1476
|
{}
|
|
1364
1477
|
);
|
|
1365
1478
|
ctx.body = {
|
|
@@ -1404,18 +1517,18 @@ const releaseController = {
|
|
|
1404
1517
|
};
|
|
1405
1518
|
},
|
|
1406
1519
|
async publish(ctx) {
|
|
1407
|
-
const user = ctx.state.user;
|
|
1408
1520
|
const id = ctx.params.id;
|
|
1409
1521
|
const releaseService = getService("release", { strapi });
|
|
1410
|
-
const
|
|
1522
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1523
|
+
const release2 = await releaseService.publish(id);
|
|
1411
1524
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1412
|
-
|
|
1525
|
+
releaseActionService.countActions({
|
|
1413
1526
|
filters: {
|
|
1414
1527
|
release: id,
|
|
1415
1528
|
type: "publish"
|
|
1416
1529
|
}
|
|
1417
1530
|
}),
|
|
1418
|
-
|
|
1531
|
+
releaseActionService.countActions({
|
|
1419
1532
|
filters: {
|
|
1420
1533
|
release: id,
|
|
1421
1534
|
type: "unpublish"
|
|
@@ -1433,24 +1546,27 @@ const releaseController = {
|
|
|
1433
1546
|
}
|
|
1434
1547
|
};
|
|
1435
1548
|
const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
}).required(),
|
|
1549
|
+
contentType: utils.yup.string().required(),
|
|
1550
|
+
entryDocumentId: utils.yup.strapiID(),
|
|
1551
|
+
locale: utils.yup.string(),
|
|
1440
1552
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
1441
1553
|
});
|
|
1442
1554
|
const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
|
|
1443
1555
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
1444
1556
|
});
|
|
1557
|
+
const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
|
|
1558
|
+
groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
|
|
1559
|
+
});
|
|
1445
1560
|
const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1446
1561
|
const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1562
|
+
const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1447
1563
|
const releaseActionController = {
|
|
1448
1564
|
async create(ctx) {
|
|
1449
1565
|
const releaseId = ctx.params.releaseId;
|
|
1450
1566
|
const releaseActionArgs = ctx.request.body;
|
|
1451
1567
|
await validateReleaseAction(releaseActionArgs);
|
|
1452
|
-
const
|
|
1453
|
-
const releaseAction2 = await
|
|
1568
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1569
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1454
1570
|
ctx.created({
|
|
1455
1571
|
data: releaseAction2
|
|
1456
1572
|
});
|
|
@@ -1461,12 +1577,15 @@ const releaseActionController = {
|
|
|
1461
1577
|
await Promise.all(
|
|
1462
1578
|
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1463
1579
|
);
|
|
1580
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1464
1581
|
const releaseService = getService("release", { strapi });
|
|
1465
1582
|
const releaseActions = await strapi.db.transaction(async () => {
|
|
1466
1583
|
const releaseActions2 = await Promise.all(
|
|
1467
1584
|
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1468
1585
|
try {
|
|
1469
|
-
const action = await
|
|
1586
|
+
const action = await releaseActionService.create(releaseId, releaseActionArgs, {
|
|
1587
|
+
disableUpdateReleaseStatus: true
|
|
1588
|
+
});
|
|
1470
1589
|
return action;
|
|
1471
1590
|
} catch (error) {
|
|
1472
1591
|
if (error instanceof AlreadyOnReleaseError) {
|
|
@@ -1479,6 +1598,9 @@ const releaseActionController = {
|
|
|
1479
1598
|
return releaseActions2;
|
|
1480
1599
|
});
|
|
1481
1600
|
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1601
|
+
if (newReleaseActions.length > 0) {
|
|
1602
|
+
releaseService.updateReleaseStatus(releaseId);
|
|
1603
|
+
}
|
|
1482
1604
|
ctx.created({
|
|
1483
1605
|
data: newReleaseActions,
|
|
1484
1606
|
meta: {
|
|
@@ -1493,10 +1615,17 @@ const releaseActionController = {
|
|
|
1493
1615
|
ability: ctx.state.userAbility,
|
|
1494
1616
|
model: RELEASE_ACTION_MODEL_UID
|
|
1495
1617
|
});
|
|
1618
|
+
await validateFindManyActionsParams(ctx.query);
|
|
1619
|
+
if (ctx.query.groupBy) {
|
|
1620
|
+
if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
|
|
1621
|
+
ctx.badRequest("Invalid groupBy parameter");
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
|
|
1625
|
+
delete ctx.query.groupBy;
|
|
1496
1626
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1497
|
-
const
|
|
1498
|
-
const { results, pagination } = await
|
|
1499
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1627
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1628
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1500
1629
|
...query
|
|
1501
1630
|
});
|
|
1502
1631
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
@@ -1512,10 +1641,11 @@ const releaseActionController = {
|
|
|
1512
1641
|
}, {});
|
|
1513
1642
|
const sanitizedResults = await utils.async.map(results, async (action) => ({
|
|
1514
1643
|
...action,
|
|
1515
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1644
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1516
1645
|
}));
|
|
1517
|
-
const groupedData = await
|
|
1518
|
-
const contentTypes2 =
|
|
1646
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1647
|
+
const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
|
|
1648
|
+
const releaseService = getService("release", { strapi });
|
|
1519
1649
|
const components = await releaseService.getAllComponents();
|
|
1520
1650
|
ctx.body = {
|
|
1521
1651
|
data: groupedData,
|
|
@@ -1531,8 +1661,8 @@ const releaseActionController = {
|
|
|
1531
1661
|
const releaseId = ctx.params.releaseId;
|
|
1532
1662
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1533
1663
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1534
|
-
const
|
|
1535
|
-
const updatedAction = await
|
|
1664
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1665
|
+
const updatedAction = await releaseActionService.update(
|
|
1536
1666
|
actionId,
|
|
1537
1667
|
releaseId,
|
|
1538
1668
|
releaseActionUpdateArgs
|
|
@@ -1544,14 +1674,36 @@ const releaseActionController = {
|
|
|
1544
1674
|
async delete(ctx) {
|
|
1545
1675
|
const actionId = ctx.params.actionId;
|
|
1546
1676
|
const releaseId = ctx.params.releaseId;
|
|
1547
|
-
const
|
|
1548
|
-
const deletedReleaseAction = await
|
|
1677
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1678
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1549
1679
|
ctx.body = {
|
|
1550
1680
|
data: deletedReleaseAction
|
|
1551
1681
|
};
|
|
1552
1682
|
}
|
|
1553
1683
|
};
|
|
1554
|
-
const
|
|
1684
|
+
const SETTINGS_SCHEMA = yup__namespace.object().shape({
|
|
1685
|
+
defaultTimezone: yup__namespace.string().nullable().default(null)
|
|
1686
|
+
}).required().noUnknown();
|
|
1687
|
+
const validateSettings = utils.validateYupSchema(SETTINGS_SCHEMA);
|
|
1688
|
+
const settingsController = {
|
|
1689
|
+
async find(ctx) {
|
|
1690
|
+
const settingsService = getService("settings", { strapi });
|
|
1691
|
+
const settings2 = await settingsService.find();
|
|
1692
|
+
ctx.body = { data: settings2 };
|
|
1693
|
+
},
|
|
1694
|
+
async update(ctx) {
|
|
1695
|
+
const settingsBody = ctx.request.body;
|
|
1696
|
+
const settings2 = await validateSettings(settingsBody);
|
|
1697
|
+
const settingsService = getService("settings", { strapi });
|
|
1698
|
+
const updatedSettings = await settingsService.update({ settings: settings2 });
|
|
1699
|
+
ctx.body = { data: updatedSettings };
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1702
|
+
const controllers = {
|
|
1703
|
+
release: releaseController,
|
|
1704
|
+
"release-action": releaseActionController,
|
|
1705
|
+
settings: settingsController
|
|
1706
|
+
};
|
|
1555
1707
|
const release = {
|
|
1556
1708
|
type: "admin",
|
|
1557
1709
|
routes: [
|
|
@@ -1571,6 +1723,22 @@ const release = {
|
|
|
1571
1723
|
]
|
|
1572
1724
|
}
|
|
1573
1725
|
},
|
|
1726
|
+
{
|
|
1727
|
+
method: "GET",
|
|
1728
|
+
path: "/getByDocumentAttached",
|
|
1729
|
+
handler: "release.findByDocumentAttached",
|
|
1730
|
+
config: {
|
|
1731
|
+
policies: [
|
|
1732
|
+
"admin::isAuthenticatedAdmin",
|
|
1733
|
+
{
|
|
1734
|
+
name: "admin::hasPermissions",
|
|
1735
|
+
config: {
|
|
1736
|
+
actions: ["plugin::content-releases.read"]
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
]
|
|
1740
|
+
}
|
|
1741
|
+
},
|
|
1574
1742
|
{
|
|
1575
1743
|
method: "POST",
|
|
1576
1744
|
path: "/",
|
|
@@ -1590,7 +1758,7 @@ const release = {
|
|
|
1590
1758
|
{
|
|
1591
1759
|
method: "GET",
|
|
1592
1760
|
path: "/",
|
|
1593
|
-
handler: "release.
|
|
1761
|
+
handler: "release.findPage",
|
|
1594
1762
|
config: {
|
|
1595
1763
|
policies: [
|
|
1596
1764
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1754,7 +1922,45 @@ const releaseAction = {
|
|
|
1754
1922
|
}
|
|
1755
1923
|
]
|
|
1756
1924
|
};
|
|
1925
|
+
const settings = {
|
|
1926
|
+
type: "admin",
|
|
1927
|
+
routes: [
|
|
1928
|
+
{
|
|
1929
|
+
method: "GET",
|
|
1930
|
+
path: "/settings",
|
|
1931
|
+
handler: "settings.find",
|
|
1932
|
+
config: {
|
|
1933
|
+
policies: [
|
|
1934
|
+
"admin::isAuthenticatedAdmin",
|
|
1935
|
+
{
|
|
1936
|
+
name: "admin::hasPermissions",
|
|
1937
|
+
config: {
|
|
1938
|
+
actions: ["plugin::content-releases.settings.read"]
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
]
|
|
1942
|
+
}
|
|
1943
|
+
},
|
|
1944
|
+
{
|
|
1945
|
+
method: "PUT",
|
|
1946
|
+
path: "/settings",
|
|
1947
|
+
handler: "settings.update",
|
|
1948
|
+
config: {
|
|
1949
|
+
policies: [
|
|
1950
|
+
"admin::isAuthenticatedAdmin",
|
|
1951
|
+
{
|
|
1952
|
+
name: "admin::hasPermissions",
|
|
1953
|
+
config: {
|
|
1954
|
+
actions: ["plugin::content-releases.settings.update"]
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
]
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
]
|
|
1961
|
+
};
|
|
1757
1962
|
const routes = {
|
|
1963
|
+
settings,
|
|
1758
1964
|
release,
|
|
1759
1965
|
"release-action": releaseAction
|
|
1760
1966
|
};
|