@strapi/content-releases 5.0.0-beta.9 → 5.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -1
- package/dist/_chunks/{App-DUmziQ17.js → App-CatMj4_9.js} +216 -257
- package/dist/_chunks/App-CatMj4_9.js.map +1 -0
- package/dist/_chunks/{App-D_6Y9N2F.mjs → App-CdTs45Xr.mjs} +215 -255
- package/dist/_chunks/App-CdTs45Xr.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-C2L6_E3J.mjs +178 -0
- package/dist/_chunks/ReleasesSettingsPage-C2L6_E3J.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-hHmY2fiE.js +178 -0
- package/dist/_chunks/ReleasesSettingsPage-hHmY2fiE.js.map +1 -0
- package/dist/_chunks/{en-DtFJ5ViE.js → en-1_1re1mc.js} +7 -2
- package/dist/_chunks/en-1_1re1mc.js.map +1 -0
- package/dist/_chunks/{en-B9Ur3VsE.mjs → en-C3sq5KNP.mjs} +7 -2
- package/dist/_chunks/en-C3sq5KNP.mjs.map +1 -0
- package/dist/_chunks/{index-BomF0-yY.mjs → index-CJpYVXMt.mjs} +525 -444
- package/dist/_chunks/index-CJpYVXMt.mjs.map +1 -0
- package/dist/_chunks/{index-C5Hc767q.js → index-DYXEZrUA.js} +518 -437
- package/dist/_chunks/index-DYXEZrUA.js.map +1 -0
- package/dist/_chunks/validation-schemas-C7P2rhPu.mjs +29 -0
- package/dist/_chunks/validation-schemas-C7P2rhPu.mjs.map +1 -0
- package/dist/_chunks/validation-schemas-bib1fBc7.js +47 -0
- package/dist/_chunks/validation-schemas-bib1fBc7.js.map +1 -0
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +2 -2
- package/dist/admin/src/components/ReleaseActionMenu.d.ts +2 -2
- package/dist/admin/src/components/{CMReleasesContainer.d.ts → ReleaseActionModal.d.ts} +3 -1
- package/dist/admin/src/components/ReleaseModal.d.ts +2 -1
- 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 +37 -32
- package/dist/admin/src/utils/time.d.ts +9 -0
- package/dist/server/index.js +784 -600
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +785 -601
- 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 -3
- package/dist/server/src/controllers/index.d.ts.map +1 -1
- package/dist/server/src/controllers/release-action.d.ts +0 -1
- package/dist/server/src/controllers/release-action.d.ts.map +1 -1
- package/dist/server/src/controllers/release.d.ts +7 -2
- 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 +1 -0
- package/dist/server/src/controllers/validation/release.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/settings.d.ts +2 -0
- package/dist/server/src/controllers/validation/settings.d.ts.map +1 -0
- package/dist/server/src/index.d.ts +66 -51
- 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-action.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 +38 -38
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/release-action.d.ts +36 -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 +6 -5
- package/dist/shared/contracts/release-actions.d.ts.map +1 -1
- package/dist/shared/contracts/releases.d.ts +6 -5
- 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/dist/shared/validation-schemas.d.ts +1 -0
- package/dist/shared/validation-schemas.d.ts.map +1 -1
- package/package.json +10 -9
- package/dist/_chunks/App-DUmziQ17.js.map +0 -1
- package/dist/_chunks/App-D_6Y9N2F.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-BomF0-yY.mjs.map +0 -1
- package/dist/_chunks/index-C5Hc767q.js.map +0 -1
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,38 @@ 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 hasPolymorphicColumn = await trx.schema.hasColumn("strapi_release_actions", "target_id");
|
|
333
|
+
if (hasPolymorphicColumn) {
|
|
334
|
+
const hasEntryDocumentIdColumn = await trx.schema.hasColumn(
|
|
335
|
+
"strapi_release_actions",
|
|
336
|
+
"entry_document_id"
|
|
337
|
+
);
|
|
338
|
+
if (!hasEntryDocumentIdColumn) {
|
|
339
|
+
await trx.schema.alterTable("strapi_release_actions", (table) => {
|
|
340
|
+
table.string("entry_document_id");
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const releaseActions = await trx.select("*").from("strapi_release_actions");
|
|
344
|
+
utils.async.map(releaseActions, async (action) => {
|
|
345
|
+
const { target_type, target_id } = action;
|
|
346
|
+
const entry = await db.query(target_type).findOne({ where: { id: target_id } });
|
|
347
|
+
if (entry) {
|
|
348
|
+
await trx("strapi_release_actions").update({ entry_document_id: entry.documentId }).where("id", action.id);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
async down() {
|
|
354
|
+
throw new Error("not implemented");
|
|
355
|
+
}
|
|
356
|
+
};
|
|
281
357
|
const register = async ({ strapi: strapi2 }) => {
|
|
282
358
|
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
283
359
|
await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
|
|
360
|
+
strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
|
|
284
361
|
strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
|
|
285
362
|
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
286
363
|
}
|
|
@@ -290,6 +367,104 @@ const register = async ({ strapi: strapi2 }) => {
|
|
|
290
367
|
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
291
368
|
}
|
|
292
369
|
};
|
|
370
|
+
const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
|
|
371
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
372
|
+
where: {
|
|
373
|
+
actions: {
|
|
374
|
+
contentType,
|
|
375
|
+
entryDocumentId: entry.documentId,
|
|
376
|
+
locale: entry.locale
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
const entryStatus = await isEntryValid(contentType, entry, { strapi });
|
|
381
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
382
|
+
where: {
|
|
383
|
+
contentType,
|
|
384
|
+
entryDocumentId: entry.documentId,
|
|
385
|
+
locale: entry.locale
|
|
386
|
+
},
|
|
387
|
+
data: {
|
|
388
|
+
isEntryValid: entryStatus
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
for (const release2 of releases) {
|
|
392
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const deleteActionsAndUpdateReleaseStatus = async (params) => {
|
|
396
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
397
|
+
where: {
|
|
398
|
+
actions: params
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
402
|
+
where: params
|
|
403
|
+
});
|
|
404
|
+
for (const release2 of releases) {
|
|
405
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
const deleteActionsOnDelete = async (ctx, next) => {
|
|
409
|
+
if (ctx.action !== "delete") {
|
|
410
|
+
return next();
|
|
411
|
+
}
|
|
412
|
+
if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
|
|
413
|
+
return next();
|
|
414
|
+
}
|
|
415
|
+
const contentType = ctx.contentType.uid;
|
|
416
|
+
const { documentId, locale } = ctx.params;
|
|
417
|
+
const result = await next();
|
|
418
|
+
if (!result) {
|
|
419
|
+
return result;
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
deleteActionsAndUpdateReleaseStatus({
|
|
423
|
+
contentType,
|
|
424
|
+
entryDocumentId: documentId,
|
|
425
|
+
...locale !== "*" && { locale }
|
|
426
|
+
});
|
|
427
|
+
} catch (error) {
|
|
428
|
+
strapi.log.error("Error while deleting release actions after delete", {
|
|
429
|
+
error
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return result;
|
|
433
|
+
};
|
|
434
|
+
const updateActionsOnUpdate = async (ctx, next) => {
|
|
435
|
+
if (ctx.action !== "update") {
|
|
436
|
+
return next();
|
|
437
|
+
}
|
|
438
|
+
if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
|
|
439
|
+
return next();
|
|
440
|
+
}
|
|
441
|
+
const contentType = ctx.contentType.uid;
|
|
442
|
+
const result = await next();
|
|
443
|
+
if (!result) {
|
|
444
|
+
return result;
|
|
445
|
+
}
|
|
446
|
+
try {
|
|
447
|
+
updateActionsStatusAndUpdateReleaseStatus(contentType, result);
|
|
448
|
+
} catch (error) {
|
|
449
|
+
strapi.log.error("Error while updating release actions after update", {
|
|
450
|
+
error
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return result;
|
|
454
|
+
};
|
|
455
|
+
const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
|
|
456
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
457
|
+
where: {
|
|
458
|
+
actions: params
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
462
|
+
where: params
|
|
463
|
+
});
|
|
464
|
+
for (const release2 of releases) {
|
|
465
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
466
|
+
}
|
|
467
|
+
};
|
|
293
468
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
294
469
|
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
295
470
|
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
@@ -297,115 +472,29 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
297
472
|
);
|
|
298
473
|
strapi2.db.lifecycles.subscribe({
|
|
299
474
|
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
475
|
/**
|
|
328
|
-
* deleteMany
|
|
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
|
-
/**
|
|
340
|
-
* We delete the release actions related to deleted entries
|
|
341
|
-
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
476
|
+
* deleteMany is still used outside documents service, for example when deleting a locale
|
|
342
477
|
*/
|
|
343
478
|
async afterDeleteMany(event) {
|
|
344
479
|
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
|
-
}
|
|
480
|
+
const model = strapi2.getModel(event.model.uid);
|
|
481
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
482
|
+
const { where } = event.params;
|
|
483
|
+
deleteReleasesActionsAndUpdateReleaseStatus({
|
|
484
|
+
contentType: model.uid,
|
|
485
|
+
locale: where.locale ?? null,
|
|
486
|
+
...where.documentId && { entryDocumentId: where.documentId }
|
|
365
487
|
});
|
|
366
|
-
for (const release2 of releases) {
|
|
367
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
368
|
-
}
|
|
369
488
|
}
|
|
370
489
|
} catch (error) {
|
|
371
490
|
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
372
491
|
error
|
|
373
492
|
});
|
|
374
493
|
}
|
|
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
494
|
}
|
|
408
495
|
});
|
|
496
|
+
strapi2.documents.use(deleteActionsOnDelete);
|
|
497
|
+
strapi2.documents.use(updateActionsOnUpdate);
|
|
409
498
|
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
410
499
|
strapi2.log.error(
|
|
411
500
|
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
@@ -497,15 +586,13 @@ const schema = {
|
|
|
497
586
|
enum: ["publish", "unpublish"],
|
|
498
587
|
required: true
|
|
499
588
|
},
|
|
500
|
-
entry: {
|
|
501
|
-
type: "relation",
|
|
502
|
-
relation: "morphToOne",
|
|
503
|
-
configurable: false
|
|
504
|
-
},
|
|
505
589
|
contentType: {
|
|
506
590
|
type: "string",
|
|
507
591
|
required: true
|
|
508
592
|
},
|
|
593
|
+
entryDocumentId: {
|
|
594
|
+
type: "string"
|
|
595
|
+
},
|
|
509
596
|
locale: {
|
|
510
597
|
type: "string"
|
|
511
598
|
},
|
|
@@ -527,18 +614,6 @@ const contentTypes = {
|
|
|
527
614
|
release: release$1,
|
|
528
615
|
"release-action": releaseAction$1
|
|
529
616
|
};
|
|
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
617
|
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
543
618
|
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
544
619
|
strapi2.eventHub.emit(event, {
|
|
@@ -547,93 +622,32 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
547
622
|
release: release2
|
|
548
623
|
});
|
|
549
624
|
};
|
|
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
625
|
const getFormattedActions = async (releaseId) => {
|
|
597
626
|
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
598
627
|
where: {
|
|
599
628
|
release: {
|
|
600
629
|
id: releaseId
|
|
601
630
|
}
|
|
602
|
-
},
|
|
603
|
-
populate: {
|
|
604
|
-
entry: {
|
|
605
|
-
fields: ["id"]
|
|
606
|
-
}
|
|
607
631
|
}
|
|
608
632
|
});
|
|
609
633
|
if (actions.length === 0) {
|
|
610
634
|
throw new utils.errors.ValidationError("No entries to publish");
|
|
611
635
|
}
|
|
612
|
-
const
|
|
613
|
-
const singleTypeActions = [];
|
|
636
|
+
const formattedActions = {};
|
|
614
637
|
for (const action of actions) {
|
|
615
638
|
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
|
-
});
|
|
639
|
+
if (!formattedActions[contentTypeUid]) {
|
|
640
|
+
formattedActions[contentTypeUid] = {
|
|
641
|
+
publish: [],
|
|
642
|
+
unpublish: []
|
|
643
|
+
};
|
|
634
644
|
}
|
|
645
|
+
formattedActions[contentTypeUid][action.type].push({
|
|
646
|
+
documentId: action.entryDocumentId,
|
|
647
|
+
locale: action.locale
|
|
648
|
+
});
|
|
635
649
|
}
|
|
636
|
-
return
|
|
650
|
+
return formattedActions;
|
|
637
651
|
};
|
|
638
652
|
return {
|
|
639
653
|
async create(releaseData, { user }) {
|
|
@@ -680,91 +694,10 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
680
694
|
}
|
|
681
695
|
});
|
|
682
696
|
},
|
|
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;
|
|
697
|
+
findMany(query) {
|
|
698
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
699
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
700
|
+
...dbQuery
|
|
768
701
|
});
|
|
769
702
|
},
|
|
770
703
|
async update(id, releaseData, { user }) {
|
|
@@ -800,131 +733,6 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
800
733
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
801
734
|
return updatedRelease;
|
|
802
735
|
},
|
|
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
736
|
async getAllComponents() {
|
|
929
737
|
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
930
738
|
const components = await contentManagerComponentsService.findAllComponents();
|
|
@@ -990,20 +798,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
990
798
|
}
|
|
991
799
|
try {
|
|
992
800
|
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
|
-
});
|
|
801
|
+
const formattedActions = await getFormattedActions(releaseId);
|
|
802
|
+
await strapi2.db.transaction(
|
|
803
|
+
async () => Promise.all(
|
|
804
|
+
Object.keys(formattedActions).map(async (contentTypeUid) => {
|
|
805
|
+
const contentType = contentTypeUid;
|
|
806
|
+
const { publish, unpublish } = formattedActions[contentType];
|
|
807
|
+
return Promise.all([
|
|
808
|
+
...publish.map((params) => strapi2.documents(contentType).publish(params)),
|
|
809
|
+
...unpublish.map((params) => strapi2.documents(contentType).unpublish(params))
|
|
810
|
+
]);
|
|
811
|
+
})
|
|
812
|
+
)
|
|
813
|
+
);
|
|
1007
814
|
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1008
815
|
where: {
|
|
1009
816
|
id: releaseId
|
|
@@ -1033,13 +840,216 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1033
840
|
};
|
|
1034
841
|
}
|
|
1035
842
|
});
|
|
1036
|
-
if (error instanceof Error) {
|
|
1037
|
-
throw error;
|
|
843
|
+
if (error instanceof Error) {
|
|
844
|
+
throw error;
|
|
845
|
+
}
|
|
846
|
+
return release2;
|
|
847
|
+
},
|
|
848
|
+
async updateReleaseStatus(releaseId) {
|
|
849
|
+
const releaseActionService = getService("release-action", { strapi: strapi2 });
|
|
850
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
851
|
+
releaseActionService.countActions({
|
|
852
|
+
filters: {
|
|
853
|
+
release: releaseId
|
|
854
|
+
}
|
|
855
|
+
}),
|
|
856
|
+
releaseActionService.countActions({
|
|
857
|
+
filters: {
|
|
858
|
+
release: releaseId,
|
|
859
|
+
isEntryValid: false
|
|
860
|
+
}
|
|
861
|
+
})
|
|
862
|
+
]);
|
|
863
|
+
if (totalActions > 0) {
|
|
864
|
+
if (invalidActions > 0) {
|
|
865
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
866
|
+
where: {
|
|
867
|
+
id: releaseId
|
|
868
|
+
},
|
|
869
|
+
data: {
|
|
870
|
+
status: "blocked"
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
875
|
+
where: {
|
|
876
|
+
id: releaseId
|
|
877
|
+
},
|
|
878
|
+
data: {
|
|
879
|
+
status: "ready"
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
884
|
+
where: {
|
|
885
|
+
id: releaseId
|
|
886
|
+
},
|
|
887
|
+
data: {
|
|
888
|
+
status: "empty"
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
};
|
|
894
|
+
const getGroupName = (queryValue) => {
|
|
895
|
+
switch (queryValue) {
|
|
896
|
+
case "contentType":
|
|
897
|
+
return "contentType.displayName";
|
|
898
|
+
case "type":
|
|
899
|
+
return "type";
|
|
900
|
+
case "locale":
|
|
901
|
+
return ___default.default.getOr("No locale", "locale.name");
|
|
902
|
+
default:
|
|
903
|
+
return "contentType.displayName";
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
const createReleaseActionService = ({ strapi: strapi2 }) => {
|
|
907
|
+
const getLocalesDataForActions = async () => {
|
|
908
|
+
if (!strapi2.plugin("i18n")) {
|
|
909
|
+
return {};
|
|
910
|
+
}
|
|
911
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
912
|
+
return allLocales.reduce((acc, locale) => {
|
|
913
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
914
|
+
return acc;
|
|
915
|
+
}, {});
|
|
916
|
+
};
|
|
917
|
+
const getContentTypesDataForActions = async (contentTypesUids) => {
|
|
918
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
919
|
+
const contentTypesData = {};
|
|
920
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
921
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
922
|
+
uid: contentTypeUid
|
|
923
|
+
});
|
|
924
|
+
contentTypesData[contentTypeUid] = {
|
|
925
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
926
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
return contentTypesData;
|
|
930
|
+
};
|
|
931
|
+
return {
|
|
932
|
+
async create(releaseId, action) {
|
|
933
|
+
const { validateEntryData, validateUniqueEntry } = getService("release-validation", {
|
|
934
|
+
strapi: strapi2
|
|
935
|
+
});
|
|
936
|
+
await Promise.all([
|
|
937
|
+
validateEntryData(action.contentType, action.entryDocumentId),
|
|
938
|
+
validateUniqueEntry(releaseId, action)
|
|
939
|
+
]);
|
|
940
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
|
|
941
|
+
if (!release2) {
|
|
942
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
943
|
+
}
|
|
944
|
+
if (release2.releasedAt) {
|
|
945
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
946
|
+
}
|
|
947
|
+
const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
|
|
948
|
+
{
|
|
949
|
+
contentType: action.contentType,
|
|
950
|
+
documentId: action.entryDocumentId,
|
|
951
|
+
locale: action.locale
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
strapi: strapi2
|
|
955
|
+
}
|
|
956
|
+
) : true;
|
|
957
|
+
const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
958
|
+
data: {
|
|
959
|
+
...action,
|
|
960
|
+
release: release2.id,
|
|
961
|
+
isEntryValid: actionStatus
|
|
962
|
+
},
|
|
963
|
+
populate: { release: { select: ["id"] } }
|
|
964
|
+
});
|
|
965
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
966
|
+
return releaseAction2;
|
|
967
|
+
},
|
|
968
|
+
async findPage(releaseId, query) {
|
|
969
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
970
|
+
where: { id: releaseId },
|
|
971
|
+
select: ["id"]
|
|
972
|
+
});
|
|
973
|
+
if (!release2) {
|
|
974
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1038
975
|
}
|
|
1039
|
-
|
|
976
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
977
|
+
const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
978
|
+
...dbQuery,
|
|
979
|
+
where: {
|
|
980
|
+
release: releaseId
|
|
981
|
+
}
|
|
982
|
+
});
|
|
983
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
984
|
+
const actionsWithEntry = await utils.async.map(actions, async (action) => {
|
|
985
|
+
const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
|
|
986
|
+
const entry = await getEntry(
|
|
987
|
+
{
|
|
988
|
+
contentType: action.contentType,
|
|
989
|
+
documentId: action.entryDocumentId,
|
|
990
|
+
locale: action.locale,
|
|
991
|
+
populate,
|
|
992
|
+
status: action.type === "publish" ? "draft" : "published"
|
|
993
|
+
},
|
|
994
|
+
{ strapi: strapi2 }
|
|
995
|
+
);
|
|
996
|
+
return {
|
|
997
|
+
...action,
|
|
998
|
+
entry,
|
|
999
|
+
status: entry ? await getEntryStatus(action.contentType, entry) : null
|
|
1000
|
+
};
|
|
1001
|
+
});
|
|
1002
|
+
return {
|
|
1003
|
+
results: actionsWithEntry,
|
|
1004
|
+
pagination
|
|
1005
|
+
};
|
|
1040
1006
|
},
|
|
1041
|
-
async
|
|
1042
|
-
const
|
|
1007
|
+
async groupActions(actions, groupBy) {
|
|
1008
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
1009
|
+
if (!acc.includes(action.contentType)) {
|
|
1010
|
+
acc.push(action.contentType);
|
|
1011
|
+
}
|
|
1012
|
+
return acc;
|
|
1013
|
+
}, []);
|
|
1014
|
+
const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
|
|
1015
|
+
const allLocalesDictionary = await getLocalesDataForActions();
|
|
1016
|
+
const formattedData = actions.map((action) => {
|
|
1017
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
1018
|
+
return {
|
|
1019
|
+
...action,
|
|
1020
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
1021
|
+
contentType: {
|
|
1022
|
+
displayName,
|
|
1023
|
+
mainFieldValue: action.entry[mainField],
|
|
1024
|
+
uid: action.contentType
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
});
|
|
1028
|
+
const groupName = getGroupName(groupBy);
|
|
1029
|
+
return ___default.default.groupBy(groupName)(formattedData);
|
|
1030
|
+
},
|
|
1031
|
+
getContentTypeModelsFromActions(actions) {
|
|
1032
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
1033
|
+
if (!acc.includes(action.contentType)) {
|
|
1034
|
+
acc.push(action.contentType);
|
|
1035
|
+
}
|
|
1036
|
+
return acc;
|
|
1037
|
+
}, []);
|
|
1038
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
1039
|
+
(acc, contentTypeUid) => {
|
|
1040
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
1041
|
+
return acc;
|
|
1042
|
+
},
|
|
1043
|
+
{}
|
|
1044
|
+
);
|
|
1045
|
+
return contentTypeModelsMap;
|
|
1046
|
+
},
|
|
1047
|
+
async countActions(query) {
|
|
1048
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
1049
|
+
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
1050
|
+
},
|
|
1051
|
+
async update(actionId, releaseId, update) {
|
|
1052
|
+
const action = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findOne({
|
|
1043
1053
|
where: {
|
|
1044
1054
|
id: actionId,
|
|
1045
1055
|
release: {
|
|
@@ -1048,17 +1058,42 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1048
1058
|
$null: true
|
|
1049
1059
|
}
|
|
1050
1060
|
}
|
|
1051
|
-
}
|
|
1052
|
-
data: update
|
|
1061
|
+
}
|
|
1053
1062
|
});
|
|
1054
|
-
if (!
|
|
1063
|
+
if (!action) {
|
|
1055
1064
|
throw new utils.errors.NotFoundError(
|
|
1056
1065
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1057
1066
|
);
|
|
1058
1067
|
}
|
|
1068
|
+
const actionStatus = update.type === "publish" ? getDraftEntryValidStatus(
|
|
1069
|
+
{
|
|
1070
|
+
contentType: action.contentType,
|
|
1071
|
+
documentId: action.entryDocumentId,
|
|
1072
|
+
locale: action.locale
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
strapi: strapi2
|
|
1076
|
+
}
|
|
1077
|
+
) : true;
|
|
1078
|
+
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
1079
|
+
where: {
|
|
1080
|
+
id: actionId,
|
|
1081
|
+
release: {
|
|
1082
|
+
id: releaseId,
|
|
1083
|
+
releasedAt: {
|
|
1084
|
+
$null: true
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
},
|
|
1088
|
+
data: {
|
|
1089
|
+
...update,
|
|
1090
|
+
isEntryValid: actionStatus
|
|
1091
|
+
}
|
|
1092
|
+
});
|
|
1093
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1059
1094
|
return updatedAction;
|
|
1060
1095
|
},
|
|
1061
|
-
async
|
|
1096
|
+
async delete(actionId, releaseId) {
|
|
1062
1097
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
1063
1098
|
where: {
|
|
1064
1099
|
id: actionId,
|
|
@@ -1075,51 +1110,8 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1075
1110
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1076
1111
|
);
|
|
1077
1112
|
}
|
|
1078
|
-
|
|
1113
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1079
1114
|
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
1115
|
}
|
|
1124
1116
|
};
|
|
1125
1117
|
};
|
|
@@ -1135,30 +1127,35 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
1135
1127
|
where: {
|
|
1136
1128
|
id: releaseId
|
|
1137
1129
|
},
|
|
1138
|
-
populate: {
|
|
1130
|
+
populate: {
|
|
1131
|
+
actions: true
|
|
1132
|
+
}
|
|
1139
1133
|
});
|
|
1140
1134
|
if (!release2) {
|
|
1141
1135
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1142
1136
|
}
|
|
1143
1137
|
const isEntryInRelease = release2.actions.some(
|
|
1144
|
-
(action) => Number(action.
|
|
1138
|
+
(action) => Number(action.entryDocumentId) === Number(releaseActionArgs.entryDocumentId) && action.contentType === releaseActionArgs.contentType && action.locale === releaseActionArgs.locale
|
|
1145
1139
|
);
|
|
1146
1140
|
if (isEntryInRelease) {
|
|
1147
1141
|
throw new AlreadyOnReleaseError(
|
|
1148
|
-
`Entry with
|
|
1142
|
+
`Entry with documentId ${releaseActionArgs.entryDocumentId} ${releaseActionArgs.locale ? `(${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
|
|
1149
1143
|
);
|
|
1150
1144
|
}
|
|
1151
1145
|
},
|
|
1152
|
-
|
|
1146
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
1153
1147
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
1154
1148
|
if (!contentType) {
|
|
1155
1149
|
throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
1156
1150
|
}
|
|
1157
|
-
if (!contentType
|
|
1151
|
+
if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
|
|
1158
1152
|
throw new utils.errors.ValidationError(
|
|
1159
1153
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
1160
1154
|
);
|
|
1161
1155
|
}
|
|
1156
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1157
|
+
throw new utils.errors.ValidationError("Document id is required for collection type");
|
|
1158
|
+
}
|
|
1162
1159
|
},
|
|
1163
1160
|
async validatePendingReleasesLimit() {
|
|
1164
1161
|
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
@@ -1247,10 +1244,33 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1247
1244
|
}
|
|
1248
1245
|
};
|
|
1249
1246
|
};
|
|
1247
|
+
const DEFAULT_SETTINGS = {
|
|
1248
|
+
defaultTimezone: null
|
|
1249
|
+
};
|
|
1250
|
+
const createSettingsService = ({ strapi: strapi2 }) => {
|
|
1251
|
+
const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
|
|
1252
|
+
return {
|
|
1253
|
+
async update({ settings: settings2 }) {
|
|
1254
|
+
const store = await getStore();
|
|
1255
|
+
store.set({ key: "settings", value: settings2 });
|
|
1256
|
+
return settings2;
|
|
1257
|
+
},
|
|
1258
|
+
async find() {
|
|
1259
|
+
const store = await getStore();
|
|
1260
|
+
const settings2 = await store.get({ key: "settings" });
|
|
1261
|
+
return {
|
|
1262
|
+
...DEFAULT_SETTINGS,
|
|
1263
|
+
...settings2 || {}
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
};
|
|
1267
|
+
};
|
|
1250
1268
|
const services = {
|
|
1251
1269
|
release: createReleaseService,
|
|
1270
|
+
"release-action": createReleaseActionService,
|
|
1252
1271
|
"release-validation": createReleaseValidationService,
|
|
1253
|
-
scheduling: createSchedulingService
|
|
1272
|
+
scheduling: createSchedulingService,
|
|
1273
|
+
settings: createSettingsService
|
|
1254
1274
|
};
|
|
1255
1275
|
const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
1256
1276
|
name: yup__namespace.string().trim().required(),
|
|
@@ -1272,53 +1292,118 @@ const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
|
1272
1292
|
otherwise: yup__namespace.string().nullable()
|
|
1273
1293
|
})
|
|
1274
1294
|
}).required().noUnknown();
|
|
1295
|
+
const SETTINGS_SCHEMA = yup__namespace.object().shape({
|
|
1296
|
+
defaultTimezone: yup__namespace.string().nullable().default(null)
|
|
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
|
-
|
|
1289
|
-
const
|
|
1290
|
-
|
|
1291
|
-
|
|
1321
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1322
|
+
await validatefindByDocumentAttachedParams(query);
|
|
1323
|
+
const { contentType, entryDocumentId, hasEntryAttached, locale } = query;
|
|
1324
|
+
const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
|
|
1325
|
+
if (isEntryAttached) {
|
|
1326
|
+
const releases = await releaseService.findMany({
|
|
1327
|
+
where: {
|
|
1328
|
+
releasedAt: null,
|
|
1329
|
+
actions: {
|
|
1330
|
+
contentType,
|
|
1331
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1332
|
+
locale: locale ?? null
|
|
1333
|
+
}
|
|
1334
|
+
},
|
|
1335
|
+
populate: {
|
|
1336
|
+
actions: {
|
|
1337
|
+
fields: ["type"]
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
});
|
|
1341
|
+
ctx.body = { data: releases };
|
|
1292
1342
|
} else {
|
|
1293
|
-
const
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
const { actions, ...releaseData } = release2;
|
|
1297
|
-
return {
|
|
1298
|
-
...releaseData,
|
|
1343
|
+
const relatedReleases = await releaseService.findMany({
|
|
1344
|
+
where: {
|
|
1345
|
+
releasedAt: null,
|
|
1299
1346
|
actions: {
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1347
|
+
contentType,
|
|
1348
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1349
|
+
locale: locale ?? null
|
|
1303
1350
|
}
|
|
1304
|
-
}
|
|
1351
|
+
}
|
|
1305
1352
|
});
|
|
1306
|
-
const
|
|
1353
|
+
const releases = await releaseService.findMany({
|
|
1307
1354
|
where: {
|
|
1355
|
+
$or: [
|
|
1356
|
+
{
|
|
1357
|
+
id: {
|
|
1358
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1359
|
+
}
|
|
1360
|
+
},
|
|
1361
|
+
{
|
|
1362
|
+
actions: null
|
|
1363
|
+
}
|
|
1364
|
+
],
|
|
1308
1365
|
releasedAt: null
|
|
1309
1366
|
}
|
|
1310
1367
|
});
|
|
1311
|
-
ctx.body = { data
|
|
1368
|
+
ctx.body = { data: releases };
|
|
1312
1369
|
}
|
|
1313
1370
|
},
|
|
1371
|
+
async findPage(ctx) {
|
|
1372
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1373
|
+
ability: ctx.state.userAbility,
|
|
1374
|
+
model: RELEASE_MODEL_UID
|
|
1375
|
+
});
|
|
1376
|
+
await permissionsManager.validateQuery(ctx.query);
|
|
1377
|
+
const releaseService = getService("release", { strapi });
|
|
1378
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1379
|
+
const { results, pagination } = await releaseService.findPage(query);
|
|
1380
|
+
const data = results.map((release2) => {
|
|
1381
|
+
const { actions, ...releaseData } = release2;
|
|
1382
|
+
return {
|
|
1383
|
+
...releaseData,
|
|
1384
|
+
actions: {
|
|
1385
|
+
meta: {
|
|
1386
|
+
count: actions.count
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
};
|
|
1390
|
+
});
|
|
1391
|
+
const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
|
|
1392
|
+
where: {
|
|
1393
|
+
releasedAt: null
|
|
1394
|
+
}
|
|
1395
|
+
});
|
|
1396
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
1397
|
+
},
|
|
1314
1398
|
async findOne(ctx) {
|
|
1315
1399
|
const id = ctx.params.id;
|
|
1316
1400
|
const releaseService = getService("release", { strapi });
|
|
1401
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1317
1402
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
1318
1403
|
if (!release2) {
|
|
1319
1404
|
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1320
1405
|
}
|
|
1321
|
-
const count = await
|
|
1406
|
+
const count = await releaseActionService.countActions({
|
|
1322
1407
|
filters: {
|
|
1323
1408
|
release: id
|
|
1324
1409
|
}
|
|
@@ -1337,35 +1422,43 @@ const releaseController = {
|
|
|
1337
1422
|
};
|
|
1338
1423
|
ctx.body = { data };
|
|
1339
1424
|
},
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1425
|
+
/* @TODO: Migrate to new api
|
|
1426
|
+
async mapEntriesToReleases(ctx: Koa.Context) {
|
|
1427
|
+
const { contentTypeUid, entriesIds } = ctx.query;
|
|
1428
|
+
|
|
1429
|
+
if (!contentTypeUid || !entriesIds) {
|
|
1430
|
+
throw new errors.ValidationError('Missing required query parameters');
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
const releaseService = getService('release', { strapi });
|
|
1434
|
+
|
|
1435
|
+
const releasesWithActions = await releaseService.findMany(
|
|
1436
|
+
contentTypeUid,
|
|
1437
|
+
entriesIds
|
|
1438
|
+
);
|
|
1439
|
+
|
|
1440
|
+
const mappedEntriesInReleases = releasesWithActions.reduce(
|
|
1441
|
+
// TODO: Fix for v5 removed mappedEntriedToRelease
|
|
1442
|
+
(acc: MapEntriesToReleases.Response['data'], release: Release) => {
|
|
1443
|
+
release.actions.forEach((action) => {
|
|
1444
|
+
if (!acc[action.entry.id]) {
|
|
1445
|
+
acc[action.entry.id] = [{ id: release.id, name: release.name }];
|
|
1446
|
+
} else {
|
|
1447
|
+
acc[action.entry.id].push({ id: release.id, name: release.name });
|
|
1448
|
+
}
|
|
1449
|
+
});
|
|
1450
|
+
|
|
1451
|
+
return acc;
|
|
1452
|
+
},
|
|
1453
|
+
// TODO: Fix for v5 removed mappedEntriedToRelease
|
|
1454
|
+
{} as MapEntriesToReleases.Response['data']
|
|
1455
|
+
);
|
|
1456
|
+
|
|
1457
|
+
ctx.body = {
|
|
1458
|
+
data: mappedEntriesInReleases,
|
|
1459
|
+
};
|
|
1460
|
+
},
|
|
1461
|
+
*/
|
|
1369
1462
|
async create(ctx) {
|
|
1370
1463
|
const user = ctx.state.user;
|
|
1371
1464
|
const releaseArgs = ctx.request.body;
|
|
@@ -1404,18 +1497,18 @@ const releaseController = {
|
|
|
1404
1497
|
};
|
|
1405
1498
|
},
|
|
1406
1499
|
async publish(ctx) {
|
|
1407
|
-
const user = ctx.state.user;
|
|
1408
1500
|
const id = ctx.params.id;
|
|
1409
1501
|
const releaseService = getService("release", { strapi });
|
|
1410
|
-
const
|
|
1502
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1503
|
+
const release2 = await releaseService.publish(id);
|
|
1411
1504
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1412
|
-
|
|
1505
|
+
releaseActionService.countActions({
|
|
1413
1506
|
filters: {
|
|
1414
1507
|
release: id,
|
|
1415
1508
|
type: "publish"
|
|
1416
1509
|
}
|
|
1417
1510
|
}),
|
|
1418
|
-
|
|
1511
|
+
releaseActionService.countActions({
|
|
1419
1512
|
filters: {
|
|
1420
1513
|
release: id,
|
|
1421
1514
|
type: "unpublish"
|
|
@@ -1433,42 +1526,47 @@ const releaseController = {
|
|
|
1433
1526
|
}
|
|
1434
1527
|
};
|
|
1435
1528
|
const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
}).required(),
|
|
1529
|
+
contentType: utils.yup.string().required(),
|
|
1530
|
+
entryDocumentId: utils.yup.strapiID(),
|
|
1531
|
+
locale: utils.yup.string(),
|
|
1440
1532
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
1441
1533
|
});
|
|
1442
1534
|
const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
|
|
1443
1535
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
1444
1536
|
});
|
|
1537
|
+
const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
|
|
1538
|
+
groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
|
|
1539
|
+
});
|
|
1445
1540
|
const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1446
1541
|
const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1542
|
+
const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1447
1543
|
const releaseActionController = {
|
|
1448
1544
|
async create(ctx) {
|
|
1449
1545
|
const releaseId = ctx.params.releaseId;
|
|
1450
1546
|
const releaseActionArgs = ctx.request.body;
|
|
1451
1547
|
await validateReleaseAction(releaseActionArgs);
|
|
1452
|
-
const
|
|
1453
|
-
const releaseAction2 = await
|
|
1548
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1549
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1454
1550
|
ctx.created({
|
|
1455
1551
|
data: releaseAction2
|
|
1456
1552
|
});
|
|
1457
1553
|
},
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
const
|
|
1554
|
+
/*
|
|
1555
|
+
async createMany(ctx: Koa.Context) {
|
|
1556
|
+
const releaseId: CreateManyReleaseActions.Request['params']['releaseId'] = ctx.params.releaseId;
|
|
1557
|
+
const releaseActionsArgs = ctx.request.body as CreateManyReleaseActions.Request['body'];
|
|
1461
1558
|
await Promise.all(
|
|
1462
1559
|
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1463
1560
|
);
|
|
1464
|
-
const
|
|
1561
|
+
const releaseActionService = getService('release-action', { strapi });
|
|
1465
1562
|
const releaseActions = await strapi.db.transaction(async () => {
|
|
1466
|
-
const
|
|
1563
|
+
const releaseActions = await Promise.all(
|
|
1467
1564
|
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1468
1565
|
try {
|
|
1469
|
-
const action = await
|
|
1566
|
+
const action = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1470
1567
|
return action;
|
|
1471
1568
|
} catch (error) {
|
|
1569
|
+
// If the entry is already in the release, we don't want to throw an error, so we catch and ignore it
|
|
1472
1570
|
if (error instanceof AlreadyOnReleaseError) {
|
|
1473
1571
|
return null;
|
|
1474
1572
|
}
|
|
@@ -1476,27 +1574,35 @@ const releaseActionController = {
|
|
|
1476
1574
|
}
|
|
1477
1575
|
})
|
|
1478
1576
|
);
|
|
1479
|
-
return
|
|
1577
|
+
return releaseActions;
|
|
1480
1578
|
});
|
|
1481
1579
|
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1482
1580
|
ctx.created({
|
|
1483
1581
|
data: newReleaseActions,
|
|
1484
1582
|
meta: {
|
|
1485
1583
|
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1486
|
-
totalEntries: releaseActions.length
|
|
1487
|
-
}
|
|
1584
|
+
totalEntries: releaseActions.length,
|
|
1585
|
+
},
|
|
1488
1586
|
});
|
|
1489
1587
|
},
|
|
1588
|
+
*/
|
|
1490
1589
|
async findMany(ctx) {
|
|
1491
1590
|
const releaseId = ctx.params.releaseId;
|
|
1492
1591
|
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1493
1592
|
ability: ctx.state.userAbility,
|
|
1494
1593
|
model: RELEASE_ACTION_MODEL_UID
|
|
1495
1594
|
});
|
|
1595
|
+
await validateFindManyActionsParams(ctx.query);
|
|
1596
|
+
if (ctx.query.groupBy) {
|
|
1597
|
+
if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
|
|
1598
|
+
ctx.badRequest("Invalid groupBy parameter");
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
|
|
1602
|
+
delete ctx.query.groupBy;
|
|
1496
1603
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1497
|
-
const
|
|
1498
|
-
const { results, pagination } = await
|
|
1499
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1604
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1605
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1500
1606
|
...query
|
|
1501
1607
|
});
|
|
1502
1608
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
@@ -1512,10 +1618,11 @@ const releaseActionController = {
|
|
|
1512
1618
|
}, {});
|
|
1513
1619
|
const sanitizedResults = await utils.async.map(results, async (action) => ({
|
|
1514
1620
|
...action,
|
|
1515
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1621
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1516
1622
|
}));
|
|
1517
|
-
const groupedData = await
|
|
1518
|
-
const contentTypes2 =
|
|
1623
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1624
|
+
const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
|
|
1625
|
+
const releaseService = getService("release", { strapi });
|
|
1519
1626
|
const components = await releaseService.getAllComponents();
|
|
1520
1627
|
ctx.body = {
|
|
1521
1628
|
data: groupedData,
|
|
@@ -1531,8 +1638,8 @@ const releaseActionController = {
|
|
|
1531
1638
|
const releaseId = ctx.params.releaseId;
|
|
1532
1639
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1533
1640
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1534
|
-
const
|
|
1535
|
-
const updatedAction = await
|
|
1641
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1642
|
+
const updatedAction = await releaseActionService.update(
|
|
1536
1643
|
actionId,
|
|
1537
1644
|
releaseId,
|
|
1538
1645
|
releaseActionUpdateArgs
|
|
@@ -1544,21 +1651,58 @@ const releaseActionController = {
|
|
|
1544
1651
|
async delete(ctx) {
|
|
1545
1652
|
const actionId = ctx.params.actionId;
|
|
1546
1653
|
const releaseId = ctx.params.releaseId;
|
|
1547
|
-
const
|
|
1548
|
-
const deletedReleaseAction = await
|
|
1654
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1655
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1549
1656
|
ctx.body = {
|
|
1550
1657
|
data: deletedReleaseAction
|
|
1551
1658
|
};
|
|
1552
1659
|
}
|
|
1553
1660
|
};
|
|
1554
|
-
const
|
|
1661
|
+
const validateSettings = utils.validateYupSchema(SETTINGS_SCHEMA);
|
|
1662
|
+
const settingsController = {
|
|
1663
|
+
async find(ctx) {
|
|
1664
|
+
const settingsService = getService("settings", { strapi });
|
|
1665
|
+
const settings2 = await settingsService.find();
|
|
1666
|
+
ctx.body = { data: settings2 };
|
|
1667
|
+
},
|
|
1668
|
+
async update(ctx) {
|
|
1669
|
+
const settingsBody = ctx.request.body;
|
|
1670
|
+
const settings2 = await validateSettings(settingsBody);
|
|
1671
|
+
const settingsService = getService("settings", { strapi });
|
|
1672
|
+
const updatedSettings = await settingsService.update({ settings: settings2 });
|
|
1673
|
+
ctx.body = { data: updatedSettings };
|
|
1674
|
+
}
|
|
1675
|
+
};
|
|
1676
|
+
const controllers = {
|
|
1677
|
+
release: releaseController,
|
|
1678
|
+
"release-action": releaseActionController,
|
|
1679
|
+
settings: settingsController
|
|
1680
|
+
};
|
|
1555
1681
|
const release = {
|
|
1556
1682
|
type: "admin",
|
|
1557
1683
|
routes: [
|
|
1684
|
+
/*
|
|
1685
|
+
{
|
|
1686
|
+
method: 'GET',
|
|
1687
|
+
path: '/mapEntriesToReleases',
|
|
1688
|
+
handler: 'release.mapEntriesToReleases',
|
|
1689
|
+
config: {
|
|
1690
|
+
policies: [
|
|
1691
|
+
'admin::isAuthenticatedAdmin',
|
|
1692
|
+
{
|
|
1693
|
+
name: 'admin::hasPermissions',
|
|
1694
|
+
config: {
|
|
1695
|
+
actions: ['plugin::content-releases.read'],
|
|
1696
|
+
},
|
|
1697
|
+
},
|
|
1698
|
+
],
|
|
1699
|
+
},
|
|
1700
|
+
},
|
|
1701
|
+
*/
|
|
1558
1702
|
{
|
|
1559
1703
|
method: "GET",
|
|
1560
|
-
path: "/
|
|
1561
|
-
handler: "release.
|
|
1704
|
+
path: "/getByDocumentAttached",
|
|
1705
|
+
handler: "release.findByDocumentAttached",
|
|
1562
1706
|
config: {
|
|
1563
1707
|
policies: [
|
|
1564
1708
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1590,7 +1734,7 @@ const release = {
|
|
|
1590
1734
|
{
|
|
1591
1735
|
method: "GET",
|
|
1592
1736
|
path: "/",
|
|
1593
|
-
handler: "release.
|
|
1737
|
+
handler: "release.findPage",
|
|
1594
1738
|
config: {
|
|
1595
1739
|
policies: [
|
|
1596
1740
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1688,22 +1832,24 @@ const releaseAction = {
|
|
|
1688
1832
|
]
|
|
1689
1833
|
}
|
|
1690
1834
|
},
|
|
1835
|
+
/*
|
|
1691
1836
|
{
|
|
1692
|
-
method:
|
|
1693
|
-
path:
|
|
1694
|
-
handler:
|
|
1837
|
+
method: 'POST',
|
|
1838
|
+
path: '/:releaseId/actions/bulk',
|
|
1839
|
+
handler: 'release-action.createMany',
|
|
1695
1840
|
config: {
|
|
1696
1841
|
policies: [
|
|
1697
|
-
|
|
1842
|
+
'admin::isAuthenticatedAdmin',
|
|
1698
1843
|
{
|
|
1699
|
-
name:
|
|
1844
|
+
name: 'admin::hasPermissions',
|
|
1700
1845
|
config: {
|
|
1701
|
-
actions: [
|
|
1702
|
-
}
|
|
1703
|
-
}
|
|
1704
|
-
]
|
|
1705
|
-
}
|
|
1846
|
+
actions: ['plugin::content-releases.create-action'],
|
|
1847
|
+
},
|
|
1848
|
+
},
|
|
1849
|
+
],
|
|
1850
|
+
},
|
|
1706
1851
|
},
|
|
1852
|
+
*/
|
|
1707
1853
|
{
|
|
1708
1854
|
method: "GET",
|
|
1709
1855
|
path: "/:releaseId/actions",
|
|
@@ -1754,7 +1900,45 @@ const releaseAction = {
|
|
|
1754
1900
|
}
|
|
1755
1901
|
]
|
|
1756
1902
|
};
|
|
1903
|
+
const settings = {
|
|
1904
|
+
type: "admin",
|
|
1905
|
+
routes: [
|
|
1906
|
+
{
|
|
1907
|
+
method: "GET",
|
|
1908
|
+
path: "/settings",
|
|
1909
|
+
handler: "settings.find",
|
|
1910
|
+
config: {
|
|
1911
|
+
policies: [
|
|
1912
|
+
"admin::isAuthenticatedAdmin",
|
|
1913
|
+
{
|
|
1914
|
+
name: "admin::hasPermissions",
|
|
1915
|
+
config: {
|
|
1916
|
+
actions: ["plugin::content-releases.settings.read"]
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
]
|
|
1920
|
+
}
|
|
1921
|
+
},
|
|
1922
|
+
{
|
|
1923
|
+
method: "PUT",
|
|
1924
|
+
path: "/settings",
|
|
1925
|
+
handler: "settings.update",
|
|
1926
|
+
config: {
|
|
1927
|
+
policies: [
|
|
1928
|
+
"admin::isAuthenticatedAdmin",
|
|
1929
|
+
{
|
|
1930
|
+
name: "admin::hasPermissions",
|
|
1931
|
+
config: {
|
|
1932
|
+
actions: ["plugin::content-releases.settings.update"]
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
]
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
]
|
|
1939
|
+
};
|
|
1757
1940
|
const routes = {
|
|
1941
|
+
settings,
|
|
1758
1942
|
release,
|
|
1759
1943
|
"release-action": releaseAction
|
|
1760
1944
|
};
|