@strapi/content-releases 0.0.0-experimental.e1ede8c55a0e1e22ce20137bf238fc374bd5dd51 → 0.0.0-experimental.e3e48deb89bd0a1b6cc69b698696566fa7854a95
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_chunks/App-PQlYzNfb.mjs +1337 -0
- package/dist/_chunks/App-PQlYzNfb.mjs.map +1 -0
- package/dist/_chunks/App-lzeJz92X.js +1360 -0
- package/dist/_chunks/App-lzeJz92X.js.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs +51 -0
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js +51 -0
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +1 -0
- package/dist/_chunks/{en-ngTk74JV.mjs → en-WuuhP6Bn.mjs} +22 -6
- package/dist/_chunks/en-WuuhP6Bn.mjs.map +1 -0
- package/dist/_chunks/{en-haKSQIo8.js → en-gcJJ5htG.js} +22 -6
- package/dist/_chunks/en-gcJJ5htG.js.map +1 -0
- package/dist/_chunks/{index-XAQOX_IB.mjs → index--4AgLDzb.mjs} +262 -83
- package/dist/_chunks/index--4AgLDzb.mjs.map +1 -0
- package/dist/_chunks/{index-EdBmRHRU.js → index-Nf1JfI-m.js} +246 -67
- package/dist/_chunks/index-Nf1JfI-m.js.map +1 -0
- package/dist/admin/index.js +2 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +3 -2
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/src/components/CMReleasesContainer.d.ts +1 -0
- package/dist/admin/src/components/RelativeTime.d.ts +28 -0
- package/dist/admin/src/components/ReleaseActionMenu.d.ts +26 -0
- package/dist/admin/src/components/ReleaseActionOptions.d.ts +9 -0
- package/dist/admin/src/components/ReleaseModal.d.ts +16 -0
- package/dist/admin/src/constants.d.ts +58 -0
- package/dist/admin/src/index.d.ts +3 -0
- package/dist/admin/src/pages/App.d.ts +1 -0
- package/dist/admin/src/pages/PurchaseContentReleases.d.ts +2 -0
- package/dist/admin/src/pages/ReleaseDetailsPage.d.ts +2 -0
- package/dist/admin/src/pages/ReleasesPage.d.ts +8 -0
- package/dist/admin/src/pages/tests/mockReleaseDetailsPageData.d.ts +181 -0
- package/dist/admin/src/pages/tests/mockReleasesPageData.d.ts +39 -0
- package/dist/admin/src/pluginId.d.ts +1 -0
- package/dist/admin/src/services/axios.d.ts +29 -0
- package/dist/admin/src/services/release.d.ts +369 -0
- package/dist/admin/src/store/hooks.d.ts +7 -0
- package/dist/admin/src/utils/time.d.ts +1 -0
- package/dist/server/index.js +999 -309
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +999 -310
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/bootstrap.d.ts.map +1 -0
- package/dist/server/src/constants.d.ts +12 -0
- package/dist/server/src/constants.d.ts.map +1 -0
- package/dist/server/src/content-types/index.d.ts +99 -0
- package/dist/server/src/content-types/index.d.ts.map +1 -0
- package/dist/server/src/content-types/release/index.d.ts +48 -0
- package/dist/server/src/content-types/release/index.d.ts.map +1 -0
- package/dist/server/src/content-types/release/schema.d.ts +47 -0
- package/dist/server/src/content-types/release/schema.d.ts.map +1 -0
- package/dist/server/src/content-types/release-action/index.d.ts +50 -0
- package/dist/server/src/content-types/release-action/index.d.ts.map +1 -0
- package/dist/server/src/content-types/release-action/schema.d.ts +49 -0
- package/dist/server/src/content-types/release-action/schema.d.ts.map +1 -0
- package/dist/server/src/controllers/index.d.ts +18 -0
- package/dist/server/src/controllers/index.d.ts.map +1 -0
- package/dist/server/src/controllers/release-action.d.ts +9 -0
- package/dist/server/src/controllers/release-action.d.ts.map +1 -0
- package/dist/server/src/controllers/release.d.ts +11 -0
- package/dist/server/src/controllers/release.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/release-action.d.ts +3 -0
- package/dist/server/src/controllers/validation/release-action.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/release.d.ts +2 -0
- package/dist/server/src/controllers/validation/release.d.ts.map +1 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/destroy.d.ts.map +1 -0
- package/dist/server/src/index.d.ts +2092 -0
- package/dist/server/src/index.d.ts.map +1 -0
- package/dist/server/src/migrations/index.d.ts +10 -0
- package/dist/server/src/migrations/index.d.ts.map +1 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/register.d.ts.map +1 -0
- package/dist/server/src/routes/index.d.ts +35 -0
- package/dist/server/src/routes/index.d.ts.map +1 -0
- package/dist/server/src/routes/release-action.d.ts +18 -0
- package/dist/server/src/routes/release-action.d.ts.map +1 -0
- package/dist/server/src/routes/release.d.ts +18 -0
- package/dist/server/src/routes/release.d.ts.map +1 -0
- package/dist/server/src/services/index.d.ts +1826 -0
- package/dist/server/src/services/index.d.ts.map +1 -0
- package/dist/server/src/services/release.d.ts +66 -0
- package/dist/server/src/services/release.d.ts.map +1 -0
- package/dist/server/src/services/scheduling.d.ts +18 -0
- package/dist/server/src/services/scheduling.d.ts.map +1 -0
- package/dist/server/src/services/validation.d.ts +14 -0
- package/dist/server/src/services/validation.d.ts.map +1 -0
- package/dist/server/src/utils/index.d.ts +14 -0
- package/dist/server/src/utils/index.d.ts.map +1 -0
- package/dist/shared/contracts/release-actions.d.ts +105 -0
- package/dist/shared/contracts/release-actions.d.ts.map +1 -0
- package/dist/shared/contracts/releases.d.ts +166 -0
- package/dist/shared/contracts/releases.d.ts.map +1 -0
- package/dist/shared/types.d.ts +24 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/dist/shared/validation-schemas.d.ts +2 -0
- package/dist/shared/validation-schemas.d.ts.map +1 -0
- package/package.json +19 -22
- package/dist/_chunks/App-g2P5kbSm.mjs +0 -945
- package/dist/_chunks/App-g2P5kbSm.mjs.map +0 -1
- package/dist/_chunks/App-o5_WfqR-.js +0 -967
- package/dist/_chunks/App-o5_WfqR-.js.map +0 -1
- package/dist/_chunks/en-haKSQIo8.js.map +0 -1
- package/dist/_chunks/en-ngTk74JV.mjs.map +0 -1
- package/dist/_chunks/index-EdBmRHRU.js.map +0 -1
- package/dist/_chunks/index-XAQOX_IB.mjs.map +0 -1
package/dist/server/index.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const utils = require("@strapi/utils");
|
|
3
|
+
const isEqual = require("lodash/isEqual");
|
|
4
|
+
const lodash = require("lodash");
|
|
3
5
|
const _ = require("lodash/fp");
|
|
6
|
+
const nodeSchedule = require("node-schedule");
|
|
4
7
|
const yup = require("yup");
|
|
5
8
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
6
9
|
function _interopNamespace(e) {
|
|
@@ -21,6 +24,7 @@ function _interopNamespace(e) {
|
|
|
21
24
|
n.default = e;
|
|
22
25
|
return Object.freeze(n);
|
|
23
26
|
}
|
|
27
|
+
const isEqual__default = /* @__PURE__ */ _interopDefault(isEqual);
|
|
24
28
|
const ___default = /* @__PURE__ */ _interopDefault(_);
|
|
25
29
|
const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
|
|
26
30
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
@@ -69,10 +73,292 @@ const ACTIONS = [
|
|
|
69
73
|
pluginName: "content-releases"
|
|
70
74
|
}
|
|
71
75
|
];
|
|
72
|
-
const
|
|
76
|
+
const ALLOWED_WEBHOOK_EVENTS = {
|
|
77
|
+
RELEASES_PUBLISH: "releases.publish"
|
|
78
|
+
};
|
|
79
|
+
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
80
|
+
return strapi2.plugin("content-releases").service(name);
|
|
81
|
+
};
|
|
82
|
+
const getPopulatedEntry = async (contentTypeUid, entryId, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
83
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
84
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
85
|
+
const entry = await strapi2.db.query(contentTypeUid).findOne({
|
|
86
|
+
where: { id: entryId },
|
|
87
|
+
populate
|
|
88
|
+
});
|
|
89
|
+
return entry;
|
|
90
|
+
};
|
|
91
|
+
const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
92
|
+
try {
|
|
93
|
+
await strapi2.entityValidator.validateEntityCreation(
|
|
94
|
+
strapi2.getModel(contentTypeUid),
|
|
95
|
+
entry,
|
|
96
|
+
void 0,
|
|
97
|
+
// @ts-expect-error - FIXME: entity here is unnecessary
|
|
98
|
+
entry
|
|
99
|
+
);
|
|
100
|
+
return true;
|
|
101
|
+
} catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
106
|
+
const deletedContentTypes = lodash.difference(lodash.keys(oldContentTypes), lodash.keys(contentTypes2)) ?? [];
|
|
107
|
+
if (deletedContentTypes.length) {
|
|
108
|
+
await utils.async.map(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
109
|
+
return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
async function migrateIsValidAndStatusReleases() {
|
|
114
|
+
const releasesWithoutStatus = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
115
|
+
where: {
|
|
116
|
+
status: null,
|
|
117
|
+
releasedAt: null
|
|
118
|
+
},
|
|
119
|
+
populate: {
|
|
120
|
+
actions: {
|
|
121
|
+
populate: {
|
|
122
|
+
entry: true
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
utils.async.map(releasesWithoutStatus, async (release2) => {
|
|
128
|
+
const actions = release2.actions;
|
|
129
|
+
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
130
|
+
for (const action of notValidatedActions) {
|
|
131
|
+
if (action.entry) {
|
|
132
|
+
const populatedEntry = await getPopulatedEntry(action.contentType, action.entry.id, {
|
|
133
|
+
strapi
|
|
134
|
+
});
|
|
135
|
+
if (populatedEntry) {
|
|
136
|
+
const isEntryValid = getEntryValidStatus(action.contentType, populatedEntry, { strapi });
|
|
137
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
138
|
+
where: {
|
|
139
|
+
id: action.id
|
|
140
|
+
},
|
|
141
|
+
data: {
|
|
142
|
+
isEntryValid
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
149
|
+
});
|
|
150
|
+
const publishedReleases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
151
|
+
where: {
|
|
152
|
+
status: null,
|
|
153
|
+
releasedAt: {
|
|
154
|
+
$notNull: true
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
utils.async.map(publishedReleases, async (release2) => {
|
|
159
|
+
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
160
|
+
where: {
|
|
161
|
+
id: release2.id
|
|
162
|
+
},
|
|
163
|
+
data: {
|
|
164
|
+
status: "done"
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
170
|
+
if (oldContentTypes !== void 0 && contentTypes2 !== void 0) {
|
|
171
|
+
const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes);
|
|
172
|
+
const releasesAffected = /* @__PURE__ */ new Set();
|
|
173
|
+
utils.async.map(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
|
|
174
|
+
const oldContentType = oldContentTypes[contentTypeUID];
|
|
175
|
+
const contentType = contentTypes2[contentTypeUID];
|
|
176
|
+
if (!isEqual__default.default(oldContentType?.attributes, contentType?.attributes)) {
|
|
177
|
+
const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
178
|
+
where: {
|
|
179
|
+
contentType: contentTypeUID
|
|
180
|
+
},
|
|
181
|
+
populate: {
|
|
182
|
+
entry: true,
|
|
183
|
+
release: true
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
await utils.async.map(actions, async (action) => {
|
|
187
|
+
if (action.entry) {
|
|
188
|
+
const populatedEntry = await getPopulatedEntry(contentTypeUID, action.entry.id, {
|
|
189
|
+
strapi
|
|
190
|
+
});
|
|
191
|
+
if (populatedEntry) {
|
|
192
|
+
const isEntryValid = await getEntryValidStatus(contentTypeUID, populatedEntry, {
|
|
193
|
+
strapi
|
|
194
|
+
});
|
|
195
|
+
releasesAffected.add(action.release.id);
|
|
196
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
197
|
+
where: {
|
|
198
|
+
id: action.id
|
|
199
|
+
},
|
|
200
|
+
data: {
|
|
201
|
+
isEntryValid
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
}).then(() => {
|
|
209
|
+
utils.async.map(releasesAffected, async (releaseId) => {
|
|
210
|
+
return getService("release", { strapi }).updateReleaseStatus(releaseId);
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
73
215
|
const register = async ({ strapi: strapi2 }) => {
|
|
74
|
-
if (features
|
|
216
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
75
217
|
await strapi2.admin.services.permission.actionProvider.registerMany(ACTIONS);
|
|
218
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
222
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
223
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes);
|
|
224
|
+
strapi2.db.lifecycles.subscribe({
|
|
225
|
+
models: contentTypesWithDraftAndPublish,
|
|
226
|
+
async afterDelete(event) {
|
|
227
|
+
try {
|
|
228
|
+
const { model, result } = event;
|
|
229
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
230
|
+
const { id } = result;
|
|
231
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
232
|
+
where: {
|
|
233
|
+
actions: {
|
|
234
|
+
target_type: model.uid,
|
|
235
|
+
target_id: id
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
240
|
+
where: {
|
|
241
|
+
target_type: model.uid,
|
|
242
|
+
target_id: id
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
for (const release2 of releases) {
|
|
246
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
/**
|
|
254
|
+
* deleteMany hook doesn't return the deleted entries ids
|
|
255
|
+
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
256
|
+
*/
|
|
257
|
+
async beforeDeleteMany(event) {
|
|
258
|
+
const { model, params } = event;
|
|
259
|
+
if (model.kind === "collectionType") {
|
|
260
|
+
const { where } = params;
|
|
261
|
+
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
262
|
+
event.state.entriesToDelete = entriesToDelete;
|
|
263
|
+
}
|
|
264
|
+
},
|
|
265
|
+
/**
|
|
266
|
+
* We delete the release actions related to deleted entries
|
|
267
|
+
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
268
|
+
*/
|
|
269
|
+
async afterDeleteMany(event) {
|
|
270
|
+
try {
|
|
271
|
+
const { model, state } = event;
|
|
272
|
+
const entriesToDelete = state.entriesToDelete;
|
|
273
|
+
if (entriesToDelete) {
|
|
274
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
275
|
+
where: {
|
|
276
|
+
actions: {
|
|
277
|
+
target_type: model.uid,
|
|
278
|
+
target_id: {
|
|
279
|
+
$in: entriesToDelete.map(
|
|
280
|
+
(entry) => entry.id
|
|
281
|
+
)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
287
|
+
where: {
|
|
288
|
+
target_type: model.uid,
|
|
289
|
+
target_id: {
|
|
290
|
+
$in: entriesToDelete.map((entry) => entry.id)
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
for (const release2 of releases) {
|
|
295
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
} catch (error) {
|
|
299
|
+
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
300
|
+
error
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
},
|
|
304
|
+
async afterUpdate(event) {
|
|
305
|
+
try {
|
|
306
|
+
const { model, result } = event;
|
|
307
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
308
|
+
const isEntryValid = await getEntryValidStatus(
|
|
309
|
+
model.uid,
|
|
310
|
+
result,
|
|
311
|
+
{
|
|
312
|
+
strapi: strapi2
|
|
313
|
+
}
|
|
314
|
+
);
|
|
315
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
316
|
+
where: {
|
|
317
|
+
target_type: model.uid,
|
|
318
|
+
target_id: result.id
|
|
319
|
+
},
|
|
320
|
+
data: {
|
|
321
|
+
isEntryValid
|
|
322
|
+
}
|
|
323
|
+
});
|
|
324
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
325
|
+
where: {
|
|
326
|
+
actions: {
|
|
327
|
+
target_type: model.uid,
|
|
328
|
+
target_id: result.id
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
for (const release2 of releases) {
|
|
333
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} catch (error) {
|
|
337
|
+
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
});
|
|
341
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
342
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
343
|
+
strapi2.log.error(
|
|
344
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
345
|
+
);
|
|
346
|
+
throw err;
|
|
347
|
+
});
|
|
348
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
349
|
+
strapi2.webhookStore.addAllowedEvent(key, value);
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
const destroy = async ({ strapi: strapi2 }) => {
|
|
355
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
356
|
+
const scheduledJobs = getService("scheduling", {
|
|
357
|
+
strapi: strapi2
|
|
358
|
+
}).getAll();
|
|
359
|
+
for (const [, job] of scheduledJobs) {
|
|
360
|
+
job.cancel();
|
|
361
|
+
}
|
|
76
362
|
}
|
|
77
363
|
};
|
|
78
364
|
const schema$1 = {
|
|
@@ -101,6 +387,17 @@ const schema$1 = {
|
|
|
101
387
|
releasedAt: {
|
|
102
388
|
type: "datetime"
|
|
103
389
|
},
|
|
390
|
+
scheduledAt: {
|
|
391
|
+
type: "datetime"
|
|
392
|
+
},
|
|
393
|
+
timezone: {
|
|
394
|
+
type: "string"
|
|
395
|
+
},
|
|
396
|
+
status: {
|
|
397
|
+
type: "enumeration",
|
|
398
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
399
|
+
required: true
|
|
400
|
+
},
|
|
104
401
|
actions: {
|
|
105
402
|
type: "relation",
|
|
106
403
|
relation: "oneToMany",
|
|
@@ -153,6 +450,9 @@ const schema = {
|
|
|
153
450
|
relation: "manyToOne",
|
|
154
451
|
target: RELEASE_MODEL_UID,
|
|
155
452
|
inversedBy: "actions"
|
|
453
|
+
},
|
|
454
|
+
isEntryValid: {
|
|
455
|
+
type: "boolean"
|
|
156
456
|
}
|
|
157
457
|
}
|
|
158
458
|
};
|
|
@@ -163,331 +463,581 @@ const contentTypes = {
|
|
|
163
463
|
release: release$1,
|
|
164
464
|
"release-action": releaseAction$1
|
|
165
465
|
};
|
|
166
|
-
const getService = (name, { strapi: strapi2 } = { strapi: global.strapi }) => {
|
|
167
|
-
return strapi2.plugin("content-releases").service(name);
|
|
168
|
-
};
|
|
169
466
|
const getGroupName = (queryValue) => {
|
|
170
467
|
switch (queryValue) {
|
|
171
468
|
case "contentType":
|
|
172
|
-
return "
|
|
469
|
+
return "contentType.displayName";
|
|
173
470
|
case "action":
|
|
174
471
|
return "type";
|
|
175
472
|
case "locale":
|
|
176
|
-
return ___default.default.getOr("No locale", "
|
|
473
|
+
return ___default.default.getOr("No locale", "locale.name");
|
|
177
474
|
default:
|
|
178
|
-
return "
|
|
475
|
+
return "contentType.displayName";
|
|
179
476
|
}
|
|
180
477
|
};
|
|
181
|
-
const createReleaseService = ({ strapi: strapi2 }) =>
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
},
|
|
188
|
-
async findOne(id, query = {}) {
|
|
189
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
|
|
190
|
-
...query
|
|
478
|
+
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
479
|
+
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
480
|
+
strapi2.eventHub.emit(event, {
|
|
481
|
+
isPublished,
|
|
482
|
+
error,
|
|
483
|
+
release: release2
|
|
191
484
|
});
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
485
|
+
};
|
|
486
|
+
return {
|
|
487
|
+
async create(releaseData, { user }) {
|
|
488
|
+
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
489
|
+
const {
|
|
490
|
+
validatePendingReleasesLimit,
|
|
491
|
+
validateUniqueNameForPendingRelease,
|
|
492
|
+
validateScheduledAtIsLaterThanNow
|
|
493
|
+
} = getService("release-validation", { strapi: strapi2 });
|
|
494
|
+
await Promise.all([
|
|
495
|
+
validatePendingReleasesLimit(),
|
|
496
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
497
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
498
|
+
]);
|
|
499
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
|
|
500
|
+
data: {
|
|
501
|
+
...releaseWithCreatorFields,
|
|
502
|
+
status: "empty"
|
|
201
503
|
}
|
|
504
|
+
});
|
|
505
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && releaseWithCreatorFields.scheduledAt) {
|
|
506
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
507
|
+
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
202
508
|
}
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
509
|
+
strapi2.telemetry.send("didCreateContentRelease");
|
|
510
|
+
return release2;
|
|
511
|
+
},
|
|
512
|
+
async findOne(id, query = {}) {
|
|
513
|
+
const dbQuery = utils.convertQueryParams.transformParamsToQuery(RELEASE_MODEL_UID, query);
|
|
514
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
515
|
+
...dbQuery,
|
|
516
|
+
where: { id }
|
|
517
|
+
});
|
|
518
|
+
return release2;
|
|
519
|
+
},
|
|
520
|
+
findPage(query) {
|
|
521
|
+
const dbQuery = utils.convertQueryParams.transformParamsToQuery(RELEASE_MODEL_UID, query ?? {});
|
|
522
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findPage({
|
|
523
|
+
...dbQuery,
|
|
524
|
+
populate: {
|
|
525
|
+
actions: {
|
|
526
|
+
count: true
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
},
|
|
531
|
+
async findManyWithContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
532
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
533
|
+
where: {
|
|
534
|
+
actions: {
|
|
535
|
+
target_type: contentTypeUid,
|
|
536
|
+
target_id: entryId
|
|
537
|
+
},
|
|
538
|
+
releasedAt: {
|
|
539
|
+
$null: true
|
|
540
|
+
}
|
|
541
|
+
},
|
|
542
|
+
populate: {
|
|
543
|
+
// Filter the action to get only the content type entry
|
|
544
|
+
actions: {
|
|
545
|
+
where: {
|
|
222
546
|
target_type: contentTypeUid,
|
|
223
547
|
target_id: entryId
|
|
224
548
|
}
|
|
225
549
|
}
|
|
226
|
-
},
|
|
227
|
-
{
|
|
228
|
-
actions: null
|
|
229
550
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
551
|
+
});
|
|
552
|
+
return releases.map((release2) => {
|
|
553
|
+
if (release2.actions?.length) {
|
|
554
|
+
const [actionForEntry] = release2.actions;
|
|
555
|
+
delete release2.actions;
|
|
556
|
+
return {
|
|
557
|
+
...release2,
|
|
558
|
+
action: actionForEntry
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
return release2;
|
|
562
|
+
});
|
|
563
|
+
},
|
|
564
|
+
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
565
|
+
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
566
|
+
where: {
|
|
567
|
+
releasedAt: {
|
|
568
|
+
$null: true
|
|
569
|
+
},
|
|
570
|
+
actions: {
|
|
571
|
+
target_type: contentTypeUid,
|
|
572
|
+
target_id: entryId
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
235
577
|
where: {
|
|
236
|
-
|
|
237
|
-
|
|
578
|
+
$or: [
|
|
579
|
+
{
|
|
580
|
+
id: {
|
|
581
|
+
$notIn: releasesRelated.map((release2) => release2.id)
|
|
582
|
+
}
|
|
583
|
+
},
|
|
584
|
+
{
|
|
585
|
+
actions: null
|
|
586
|
+
}
|
|
587
|
+
],
|
|
588
|
+
releasedAt: {
|
|
589
|
+
$null: true
|
|
590
|
+
}
|
|
238
591
|
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
592
|
+
});
|
|
593
|
+
return releases.map((release2) => {
|
|
594
|
+
if (release2.actions?.length) {
|
|
595
|
+
const [actionForEntry] = release2.actions;
|
|
596
|
+
delete release2.actions;
|
|
597
|
+
return {
|
|
598
|
+
...release2,
|
|
599
|
+
action: actionForEntry
|
|
600
|
+
};
|
|
246
601
|
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
602
|
+
return release2;
|
|
603
|
+
});
|
|
604
|
+
},
|
|
605
|
+
async update(id, releaseData, { user }) {
|
|
606
|
+
const releaseWithCreatorFields = await utils.setCreatorFields({ user, isEdition: true })(
|
|
607
|
+
releaseData
|
|
608
|
+
);
|
|
609
|
+
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
|
610
|
+
"release-validation",
|
|
611
|
+
{ strapi: strapi2 }
|
|
612
|
+
);
|
|
613
|
+
await Promise.all([
|
|
614
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
615
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
616
|
+
]);
|
|
617
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
|
|
618
|
+
if (!release2) {
|
|
619
|
+
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
250
620
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (release2.actions?.length) {
|
|
254
|
-
const [actionForEntry] = release2.actions;
|
|
255
|
-
delete release2.actions;
|
|
256
|
-
return {
|
|
257
|
-
...release2,
|
|
258
|
-
action: actionForEntry
|
|
259
|
-
};
|
|
621
|
+
if (release2.releasedAt) {
|
|
622
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
260
623
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
data: updatedRelease
|
|
273
|
-
});
|
|
274
|
-
if (!release2) {
|
|
275
|
-
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
276
|
-
}
|
|
277
|
-
return release2;
|
|
278
|
-
},
|
|
279
|
-
async createAction(releaseId, action) {
|
|
280
|
-
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
281
|
-
strapi: strapi2
|
|
282
|
-
});
|
|
283
|
-
await Promise.all([
|
|
284
|
-
validateEntryContentType(action.entry.contentType),
|
|
285
|
-
validateUniqueEntry(releaseId, action)
|
|
286
|
-
]);
|
|
287
|
-
const { entry, type } = action;
|
|
288
|
-
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
289
|
-
data: {
|
|
290
|
-
type,
|
|
291
|
-
contentType: entry.contentType,
|
|
292
|
-
locale: entry.locale,
|
|
293
|
-
entry: {
|
|
294
|
-
id: entry.id,
|
|
295
|
-
__type: entry.contentType,
|
|
296
|
-
__pivot: { field: "entry" }
|
|
297
|
-
},
|
|
298
|
-
release: releaseId
|
|
299
|
-
},
|
|
300
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
301
|
-
});
|
|
302
|
-
},
|
|
303
|
-
async findActions(releaseId, query) {
|
|
304
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
305
|
-
fields: ["id"]
|
|
306
|
-
});
|
|
307
|
-
if (!release2) {
|
|
308
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
309
|
-
}
|
|
310
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
311
|
-
...query,
|
|
312
|
-
populate: {
|
|
313
|
-
entry: true
|
|
314
|
-
},
|
|
315
|
-
filters: {
|
|
316
|
-
release: releaseId
|
|
624
|
+
const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
625
|
+
where: { id },
|
|
626
|
+
data: releaseWithCreatorFields
|
|
627
|
+
});
|
|
628
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
629
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
630
|
+
if (releaseData.scheduledAt) {
|
|
631
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
632
|
+
} else if (release2.scheduledAt) {
|
|
633
|
+
schedulingService.cancel(id);
|
|
634
|
+
}
|
|
317
635
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
636
|
+
this.updateReleaseStatus(id);
|
|
637
|
+
strapi2.telemetry.send("didUpdateContentRelease");
|
|
638
|
+
return updatedRelease;
|
|
639
|
+
},
|
|
640
|
+
async createAction(releaseId, action) {
|
|
641
|
+
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
642
|
+
strapi: strapi2
|
|
643
|
+
});
|
|
644
|
+
await Promise.all([
|
|
645
|
+
validateEntryContentType(action.entry.contentType),
|
|
646
|
+
validateUniqueEntry(releaseId, action)
|
|
647
|
+
]);
|
|
648
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
|
|
649
|
+
if (!release2) {
|
|
650
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
327
651
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
contentType: {
|
|
345
|
-
displayName,
|
|
346
|
-
mainFieldValue: action.entry[mainField]
|
|
652
|
+
if (release2.releasedAt) {
|
|
653
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
654
|
+
}
|
|
655
|
+
const { entry, type } = action;
|
|
656
|
+
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
657
|
+
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
658
|
+
const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
659
|
+
data: {
|
|
660
|
+
type,
|
|
661
|
+
contentType: entry.contentType,
|
|
662
|
+
locale: entry.locale,
|
|
663
|
+
isEntryValid,
|
|
664
|
+
entry: {
|
|
665
|
+
id: entry.id,
|
|
666
|
+
__type: entry.contentType,
|
|
667
|
+
__pivot: { field: "entry" }
|
|
347
668
|
},
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
}
|
|
351
|
-
};
|
|
352
|
-
});
|
|
353
|
-
const groupName = getGroupName(groupBy);
|
|
354
|
-
return ___default.default.groupBy(groupName)(formattedData);
|
|
355
|
-
},
|
|
356
|
-
async getContentTypesDataForActions(contentTypesUids) {
|
|
357
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
358
|
-
const contentTypesData = {};
|
|
359
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
360
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
361
|
-
uid: contentTypeUid
|
|
669
|
+
release: releaseId
|
|
670
|
+
},
|
|
671
|
+
populate: { release: { select: ["id"] }, entry: { select: ["id"] } }
|
|
362
672
|
});
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
actions: {
|
|
374
|
-
fields: ["id"]
|
|
375
|
-
}
|
|
673
|
+
this.updateReleaseStatus(releaseId);
|
|
674
|
+
return releaseAction2;
|
|
675
|
+
},
|
|
676
|
+
async findActions(releaseId, query) {
|
|
677
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
678
|
+
where: { id: releaseId },
|
|
679
|
+
select: ["id"]
|
|
680
|
+
});
|
|
681
|
+
if (!release2) {
|
|
682
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
376
683
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
where: {
|
|
387
|
-
id: {
|
|
388
|
-
$in: release2.actions.map((action) => action.id)
|
|
684
|
+
const dbQuery = utils.convertQueryParams.transformParamsToQuery(
|
|
685
|
+
RELEASE_ACTION_MODEL_UID,
|
|
686
|
+
query ?? {}
|
|
687
|
+
);
|
|
688
|
+
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
689
|
+
...dbQuery,
|
|
690
|
+
populate: {
|
|
691
|
+
entry: {
|
|
692
|
+
populate: "*"
|
|
389
693
|
}
|
|
694
|
+
},
|
|
695
|
+
where: {
|
|
696
|
+
release: releaseId
|
|
390
697
|
}
|
|
391
698
|
});
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
699
|
+
},
|
|
700
|
+
async countActions(query) {
|
|
701
|
+
const dbQuery = utils.convertQueryParams.transformParamsToQuery(
|
|
702
|
+
RELEASE_ACTION_MODEL_UID,
|
|
703
|
+
query ?? {}
|
|
704
|
+
);
|
|
705
|
+
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
706
|
+
},
|
|
707
|
+
async groupActions(actions, groupBy) {
|
|
708
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
709
|
+
if (!acc.includes(action.contentType)) {
|
|
710
|
+
acc.push(action.contentType);
|
|
711
|
+
}
|
|
712
|
+
return acc;
|
|
713
|
+
}, []);
|
|
714
|
+
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
715
|
+
contentTypeUids
|
|
716
|
+
);
|
|
717
|
+
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
718
|
+
const formattedData = actions.map((action) => {
|
|
719
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
720
|
+
return {
|
|
721
|
+
...action,
|
|
722
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
723
|
+
contentType: {
|
|
724
|
+
displayName,
|
|
725
|
+
mainFieldValue: action.entry[mainField],
|
|
726
|
+
uid: action.contentType
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
});
|
|
730
|
+
const groupName = getGroupName(groupBy);
|
|
731
|
+
return ___default.default.groupBy(groupName)(formattedData);
|
|
732
|
+
},
|
|
733
|
+
async getLocalesDataForActions() {
|
|
734
|
+
if (!strapi2.plugin("i18n")) {
|
|
735
|
+
return {};
|
|
736
|
+
}
|
|
737
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
738
|
+
return allLocales.reduce((acc, locale) => {
|
|
739
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
740
|
+
return acc;
|
|
741
|
+
}, {});
|
|
742
|
+
},
|
|
743
|
+
async getContentTypesDataForActions(contentTypesUids) {
|
|
744
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
745
|
+
const contentTypesData = {};
|
|
746
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
747
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
748
|
+
uid: contentTypeUid
|
|
749
|
+
});
|
|
750
|
+
contentTypesData[contentTypeUid] = {
|
|
751
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
752
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
return contentTypesData;
|
|
756
|
+
},
|
|
757
|
+
getContentTypeModelsFromActions(actions) {
|
|
758
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
759
|
+
if (!acc.includes(action.contentType)) {
|
|
760
|
+
acc.push(action.contentType);
|
|
761
|
+
}
|
|
762
|
+
return acc;
|
|
763
|
+
}, []);
|
|
764
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
765
|
+
(acc, contentTypeUid) => {
|
|
766
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
767
|
+
return acc;
|
|
768
|
+
},
|
|
769
|
+
{}
|
|
770
|
+
);
|
|
771
|
+
return contentTypeModelsMap;
|
|
772
|
+
},
|
|
773
|
+
async getAllComponents() {
|
|
774
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
775
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
776
|
+
const componentsMap = components.reduce(
|
|
777
|
+
(acc, component) => {
|
|
778
|
+
acc[component.uid] = component;
|
|
779
|
+
return acc;
|
|
780
|
+
},
|
|
781
|
+
{}
|
|
782
|
+
);
|
|
783
|
+
return componentsMap;
|
|
784
|
+
},
|
|
785
|
+
async delete(releaseId) {
|
|
786
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
787
|
+
where: { id: releaseId },
|
|
401
788
|
populate: {
|
|
402
789
|
actions: {
|
|
403
|
-
|
|
404
|
-
entry: true
|
|
405
|
-
}
|
|
790
|
+
select: ["id"]
|
|
406
791
|
}
|
|
407
792
|
}
|
|
793
|
+
});
|
|
794
|
+
if (!release2) {
|
|
795
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
408
796
|
}
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
412
|
-
}
|
|
413
|
-
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
414
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
415
|
-
}
|
|
416
|
-
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
417
|
-
throw new utils.errors.ValidationError("No entries to publish");
|
|
418
|
-
}
|
|
419
|
-
const actions = {};
|
|
420
|
-
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
421
|
-
const contentTypeUid = action.contentType;
|
|
422
|
-
if (!actions[contentTypeUid]) {
|
|
423
|
-
actions[contentTypeUid] = {
|
|
424
|
-
publish: [],
|
|
425
|
-
unpublish: []
|
|
426
|
-
};
|
|
797
|
+
if (release2.releasedAt) {
|
|
798
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
427
799
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
800
|
+
await strapi2.db.transaction(async () => {
|
|
801
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
802
|
+
where: {
|
|
803
|
+
id: {
|
|
804
|
+
$in: release2.actions.map((action) => action.id)
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
});
|
|
808
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
809
|
+
where: {
|
|
810
|
+
id: releaseId
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
});
|
|
814
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
|
|
815
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
816
|
+
await schedulingService.cancel(release2.id);
|
|
432
817
|
}
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
818
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
819
|
+
return release2;
|
|
820
|
+
},
|
|
821
|
+
async publish(releaseId) {
|
|
822
|
+
try {
|
|
823
|
+
const releaseWithPopulatedActionEntries = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
824
|
+
where: { id: releaseId },
|
|
825
|
+
populate: {
|
|
826
|
+
actions: {
|
|
827
|
+
populate: {
|
|
828
|
+
entry: {
|
|
829
|
+
select: ["id"]
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
});
|
|
835
|
+
if (!releaseWithPopulatedActionEntries) {
|
|
836
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
837
|
+
}
|
|
838
|
+
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
839
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
840
|
+
}
|
|
841
|
+
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
842
|
+
throw new utils.errors.ValidationError("No entries to publish");
|
|
843
|
+
}
|
|
844
|
+
const collectionTypeActions = {};
|
|
845
|
+
const singleTypeActions = [];
|
|
846
|
+
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
847
|
+
const contentTypeUid = action.contentType;
|
|
848
|
+
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
849
|
+
if (!collectionTypeActions[contentTypeUid]) {
|
|
850
|
+
collectionTypeActions[contentTypeUid] = {
|
|
851
|
+
entriestoPublishIds: [],
|
|
852
|
+
entriesToUnpublishIds: []
|
|
853
|
+
};
|
|
854
|
+
}
|
|
855
|
+
if (action.type === "publish") {
|
|
856
|
+
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
857
|
+
} else {
|
|
858
|
+
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
859
|
+
}
|
|
860
|
+
} else {
|
|
861
|
+
singleTypeActions.push({
|
|
862
|
+
uid: contentTypeUid,
|
|
863
|
+
action: action.type,
|
|
864
|
+
id: action.entry.id
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
869
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
870
|
+
await strapi2.db.transaction(async () => {
|
|
871
|
+
for (const { uid, action, id } of singleTypeActions) {
|
|
872
|
+
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
873
|
+
const entry = await strapi2.db.query(uid).findOne({ where: { id }, populate });
|
|
874
|
+
try {
|
|
875
|
+
if (action === "publish") {
|
|
876
|
+
await entityManagerService.publish(entry, uid);
|
|
877
|
+
} else {
|
|
878
|
+
await entityManagerService.unpublish(entry, uid);
|
|
879
|
+
}
|
|
880
|
+
} catch (error) {
|
|
881
|
+
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
|
|
882
|
+
} else {
|
|
883
|
+
throw error;
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
888
|
+
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
889
|
+
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
890
|
+
const entriesToPublish = await strapi2.db.query(contentTypeUid).findMany({
|
|
891
|
+
where: {
|
|
892
|
+
id: {
|
|
893
|
+
$in: entriestoPublishIds
|
|
894
|
+
}
|
|
895
|
+
},
|
|
896
|
+
populate
|
|
897
|
+
});
|
|
898
|
+
const entriesToUnpublish = await strapi2.db.query(contentTypeUid).findMany({
|
|
899
|
+
where: {
|
|
900
|
+
id: {
|
|
901
|
+
$in: entriesToUnpublishIds
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
populate
|
|
905
|
+
});
|
|
906
|
+
if (entriesToPublish.length > 0) {
|
|
907
|
+
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
908
|
+
}
|
|
909
|
+
if (entriesToUnpublish.length > 0) {
|
|
910
|
+
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
});
|
|
914
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
915
|
+
where: { id: releaseId },
|
|
916
|
+
data: {
|
|
917
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
918
|
+
},
|
|
919
|
+
populate: {
|
|
920
|
+
actions: {
|
|
921
|
+
count: true
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
});
|
|
925
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
926
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
927
|
+
isPublished: true,
|
|
928
|
+
release: release2
|
|
929
|
+
});
|
|
440
930
|
}
|
|
441
|
-
|
|
442
|
-
|
|
931
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
932
|
+
return release2;
|
|
933
|
+
} catch (error) {
|
|
934
|
+
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
935
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
936
|
+
isPublished: false,
|
|
937
|
+
error
|
|
938
|
+
});
|
|
443
939
|
}
|
|
940
|
+
strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
941
|
+
where: { id: releaseId },
|
|
942
|
+
data: {
|
|
943
|
+
status: "failed"
|
|
944
|
+
}
|
|
945
|
+
});
|
|
946
|
+
throw error;
|
|
444
947
|
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
948
|
+
},
|
|
949
|
+
async updateAction(actionId, releaseId, update) {
|
|
950
|
+
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
951
|
+
where: {
|
|
952
|
+
id: actionId,
|
|
953
|
+
release: {
|
|
954
|
+
id: releaseId,
|
|
955
|
+
releasedAt: {
|
|
956
|
+
$null: true
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
},
|
|
960
|
+
data: update
|
|
961
|
+
});
|
|
962
|
+
if (!updatedAction) {
|
|
963
|
+
throw new utils.errors.NotFoundError(
|
|
964
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
965
|
+
);
|
|
453
966
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
)
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
async deleteAction(actionId, releaseId) {
|
|
473
|
-
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
474
|
-
where: {
|
|
475
|
-
id: actionId,
|
|
476
|
-
release: releaseId
|
|
967
|
+
return updatedAction;
|
|
968
|
+
},
|
|
969
|
+
async deleteAction(actionId, releaseId) {
|
|
970
|
+
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
971
|
+
where: {
|
|
972
|
+
id: actionId,
|
|
973
|
+
release: {
|
|
974
|
+
id: releaseId,
|
|
975
|
+
releasedAt: {
|
|
976
|
+
$null: true
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
if (!deletedAction) {
|
|
982
|
+
throw new utils.errors.NotFoundError(
|
|
983
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
984
|
+
);
|
|
477
985
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
986
|
+
this.updateReleaseStatus(releaseId);
|
|
987
|
+
return deletedAction;
|
|
988
|
+
},
|
|
989
|
+
async updateReleaseStatus(releaseId) {
|
|
990
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
991
|
+
this.countActions({
|
|
992
|
+
filters: {
|
|
993
|
+
release: releaseId
|
|
994
|
+
}
|
|
995
|
+
}),
|
|
996
|
+
this.countActions({
|
|
997
|
+
filters: {
|
|
998
|
+
release: releaseId,
|
|
999
|
+
isEntryValid: false
|
|
1000
|
+
}
|
|
1001
|
+
})
|
|
1002
|
+
]);
|
|
1003
|
+
if (totalActions > 0) {
|
|
1004
|
+
if (invalidActions > 0) {
|
|
1005
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1006
|
+
where: {
|
|
1007
|
+
id: releaseId
|
|
1008
|
+
},
|
|
1009
|
+
data: {
|
|
1010
|
+
status: "blocked"
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1015
|
+
where: {
|
|
1016
|
+
id: releaseId
|
|
1017
|
+
},
|
|
1018
|
+
data: {
|
|
1019
|
+
status: "ready"
|
|
1020
|
+
}
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
1023
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1024
|
+
where: {
|
|
1025
|
+
id: releaseId
|
|
1026
|
+
},
|
|
1027
|
+
data: {
|
|
1028
|
+
status: "empty"
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
483
1031
|
}
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
});
|
|
1032
|
+
};
|
|
1033
|
+
};
|
|
487
1034
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
488
1035
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
489
|
-
const release2 = await strapi2.
|
|
490
|
-
|
|
1036
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1037
|
+
where: {
|
|
1038
|
+
id: releaseId
|
|
1039
|
+
},
|
|
1040
|
+
populate: { actions: { populate: { entry: { select: ["id"] } } } }
|
|
491
1041
|
});
|
|
492
1042
|
if (!release2) {
|
|
493
1043
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
@@ -506,16 +1056,117 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
506
1056
|
if (!contentType) {
|
|
507
1057
|
throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
508
1058
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
1059
|
+
},
|
|
1060
|
+
async validatePendingReleasesLimit() {
|
|
1061
|
+
const maximumPendingReleases = strapi2.ee.features.get("cms-content-releases")?.options?.maximumReleases || 3;
|
|
1062
|
+
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
1063
|
+
filters: {
|
|
1064
|
+
releasedAt: {
|
|
1065
|
+
$null: true
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
});
|
|
1069
|
+
if (pendingReleasesCount >= maximumPendingReleases) {
|
|
1070
|
+
throw new utils.errors.ValidationError("You have reached the maximum number of pending releases");
|
|
1071
|
+
}
|
|
1072
|
+
},
|
|
1073
|
+
async validateUniqueNameForPendingRelease(name, id) {
|
|
1074
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1075
|
+
where: {
|
|
1076
|
+
releasedAt: {
|
|
1077
|
+
$null: true
|
|
1078
|
+
},
|
|
1079
|
+
name,
|
|
1080
|
+
...id && { id: { $ne: id } }
|
|
1081
|
+
}
|
|
1082
|
+
});
|
|
1083
|
+
const isNameUnique = pendingReleases.length === 0;
|
|
1084
|
+
if (!isNameUnique) {
|
|
1085
|
+
throw new utils.errors.ValidationError(`Release with name ${name} already exists`);
|
|
1086
|
+
}
|
|
1087
|
+
},
|
|
1088
|
+
async validateScheduledAtIsLaterThanNow(scheduledAt) {
|
|
1089
|
+
if (scheduledAt && new Date(scheduledAt) <= /* @__PURE__ */ new Date()) {
|
|
1090
|
+
throw new utils.errors.ValidationError("Scheduled at must be later than now");
|
|
513
1091
|
}
|
|
514
1092
|
}
|
|
515
1093
|
});
|
|
516
|
-
const
|
|
1094
|
+
const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
1095
|
+
const scheduledJobs = /* @__PURE__ */ new Map();
|
|
1096
|
+
return {
|
|
1097
|
+
async set(releaseId, scheduleDate) {
|
|
1098
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId, releasedAt: null } });
|
|
1099
|
+
if (!release2) {
|
|
1100
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1101
|
+
}
|
|
1102
|
+
const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
|
|
1103
|
+
try {
|
|
1104
|
+
await getService("release").publish(releaseId);
|
|
1105
|
+
} catch (error) {
|
|
1106
|
+
}
|
|
1107
|
+
this.cancel(releaseId);
|
|
1108
|
+
});
|
|
1109
|
+
if (scheduledJobs.has(releaseId)) {
|
|
1110
|
+
this.cancel(releaseId);
|
|
1111
|
+
}
|
|
1112
|
+
scheduledJobs.set(releaseId, job);
|
|
1113
|
+
return scheduledJobs;
|
|
1114
|
+
},
|
|
1115
|
+
cancel(releaseId) {
|
|
1116
|
+
if (scheduledJobs.has(releaseId)) {
|
|
1117
|
+
scheduledJobs.get(releaseId).cancel();
|
|
1118
|
+
scheduledJobs.delete(releaseId);
|
|
1119
|
+
}
|
|
1120
|
+
return scheduledJobs;
|
|
1121
|
+
},
|
|
1122
|
+
getAll() {
|
|
1123
|
+
return scheduledJobs;
|
|
1124
|
+
},
|
|
1125
|
+
/**
|
|
1126
|
+
* On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
|
|
1127
|
+
* This is useful in case the server was restarted and the scheduled jobs were lost
|
|
1128
|
+
* This also could be used to sync different Strapi instances in case of a cluster
|
|
1129
|
+
*/
|
|
1130
|
+
async syncFromDatabase() {
|
|
1131
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1132
|
+
where: {
|
|
1133
|
+
scheduledAt: {
|
|
1134
|
+
$gte: /* @__PURE__ */ new Date()
|
|
1135
|
+
},
|
|
1136
|
+
releasedAt: null
|
|
1137
|
+
}
|
|
1138
|
+
});
|
|
1139
|
+
for (const release2 of releases) {
|
|
1140
|
+
this.set(release2.id, release2.scheduledAt);
|
|
1141
|
+
}
|
|
1142
|
+
return scheduledJobs;
|
|
1143
|
+
}
|
|
1144
|
+
};
|
|
1145
|
+
};
|
|
1146
|
+
const services = {
|
|
1147
|
+
release: createReleaseService,
|
|
1148
|
+
"release-validation": createReleaseValidationService,
|
|
1149
|
+
...strapi.features.future.isEnabled("contentReleasesScheduling") ? { scheduling: createSchedulingService } : {}
|
|
1150
|
+
};
|
|
517
1151
|
const RELEASE_SCHEMA = yup__namespace.object().shape({
|
|
518
|
-
name: yup__namespace.string().trim().required()
|
|
1152
|
+
name: yup__namespace.string().trim().required(),
|
|
1153
|
+
scheduledAt: yup__namespace.string().nullable(),
|
|
1154
|
+
isScheduled: yup__namespace.boolean().optional(),
|
|
1155
|
+
time: yup__namespace.string().when("isScheduled", {
|
|
1156
|
+
is: true,
|
|
1157
|
+
then: yup__namespace.string().trim().required(),
|
|
1158
|
+
otherwise: yup__namespace.string().nullable()
|
|
1159
|
+
}),
|
|
1160
|
+
timezone: yup__namespace.string().when("isScheduled", {
|
|
1161
|
+
is: true,
|
|
1162
|
+
then: yup__namespace.string().required().nullable(),
|
|
1163
|
+
otherwise: yup__namespace.string().nullable()
|
|
1164
|
+
}),
|
|
1165
|
+
date: yup__namespace.string().when("isScheduled", {
|
|
1166
|
+
is: true,
|
|
1167
|
+
then: yup__namespace.string().required().nullable(),
|
|
1168
|
+
otherwise: yup__namespace.string().nullable()
|
|
1169
|
+
})
|
|
519
1170
|
}).required().noUnknown();
|
|
520
1171
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
521
1172
|
const releaseController = {
|
|
@@ -532,9 +1183,7 @@ const releaseController = {
|
|
|
532
1183
|
const contentTypeUid = query.contentTypeUid;
|
|
533
1184
|
const entryId = query.entryId;
|
|
534
1185
|
const hasEntryAttached = typeof query.hasEntryAttached === "string" ? JSON.parse(query.hasEntryAttached) : false;
|
|
535
|
-
const data = await releaseService.
|
|
536
|
-
hasEntryAttached
|
|
537
|
-
});
|
|
1186
|
+
const data = hasEntryAttached ? await releaseService.findManyWithContentTypeEntryAttached(contentTypeUid, entryId) : await releaseService.findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId);
|
|
538
1187
|
ctx.body = { data };
|
|
539
1188
|
} else {
|
|
540
1189
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
@@ -550,26 +1199,30 @@ const releaseController = {
|
|
|
550
1199
|
}
|
|
551
1200
|
};
|
|
552
1201
|
});
|
|
553
|
-
|
|
1202
|
+
const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
|
|
1203
|
+
where: {
|
|
1204
|
+
releasedAt: null
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
554
1208
|
}
|
|
555
1209
|
},
|
|
556
1210
|
async findOne(ctx) {
|
|
557
1211
|
const id = ctx.params.id;
|
|
558
1212
|
const releaseService = getService("release", { strapi });
|
|
559
1213
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
});
|
|
564
|
-
const sanitizedRelease = await permissionsManager.sanitizeOutput(release2);
|
|
1214
|
+
if (!release2) {
|
|
1215
|
+
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1216
|
+
}
|
|
565
1217
|
const count = await releaseService.countActions({
|
|
566
1218
|
filters: {
|
|
567
1219
|
release: id
|
|
568
1220
|
}
|
|
569
1221
|
});
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
1222
|
+
const sanitizedRelease = {
|
|
1223
|
+
...release2,
|
|
1224
|
+
createdBy: release2.createdBy ? strapi.admin.services.user.sanitizeUser(release2.createdBy) : null
|
|
1225
|
+
};
|
|
573
1226
|
const data = {
|
|
574
1227
|
...sanitizedRelease,
|
|
575
1228
|
actions: {
|
|
@@ -622,8 +1275,27 @@ const releaseController = {
|
|
|
622
1275
|
const id = ctx.params.id;
|
|
623
1276
|
const releaseService = getService("release", { strapi });
|
|
624
1277
|
const release2 = await releaseService.publish(id, { user });
|
|
1278
|
+
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1279
|
+
releaseService.countActions({
|
|
1280
|
+
filters: {
|
|
1281
|
+
release: id,
|
|
1282
|
+
type: "publish"
|
|
1283
|
+
}
|
|
1284
|
+
}),
|
|
1285
|
+
releaseService.countActions({
|
|
1286
|
+
filters: {
|
|
1287
|
+
release: id,
|
|
1288
|
+
type: "unpublish"
|
|
1289
|
+
}
|
|
1290
|
+
})
|
|
1291
|
+
]);
|
|
625
1292
|
ctx.body = {
|
|
626
|
-
data: release2
|
|
1293
|
+
data: release2,
|
|
1294
|
+
meta: {
|
|
1295
|
+
totalEntries: countPublishActions + countUnpublishActions,
|
|
1296
|
+
totalPublishedEntries: countPublishActions,
|
|
1297
|
+
totalUnpublishedEntries: countUnpublishActions
|
|
1298
|
+
}
|
|
627
1299
|
};
|
|
628
1300
|
}
|
|
629
1301
|
};
|
|
@@ -662,11 +1334,30 @@ const releaseActionController = {
|
|
|
662
1334
|
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
663
1335
|
...query
|
|
664
1336
|
});
|
|
665
|
-
const
|
|
1337
|
+
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
1338
|
+
if (acc[action.contentType]) {
|
|
1339
|
+
return acc;
|
|
1340
|
+
}
|
|
1341
|
+
const contentTypePermissionsManager = strapi.admin.services.permission.createPermissionsManager({
|
|
1342
|
+
ability: ctx.state.userAbility,
|
|
1343
|
+
model: action.contentType
|
|
1344
|
+
});
|
|
1345
|
+
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
1346
|
+
return acc;
|
|
1347
|
+
}, {});
|
|
1348
|
+
const sanitizedResults = await utils.async.map(results, async (action) => ({
|
|
1349
|
+
...action,
|
|
1350
|
+
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1351
|
+
}));
|
|
1352
|
+
const groupedData = await releaseService.groupActions(sanitizedResults, query.groupBy);
|
|
1353
|
+
const contentTypes2 = releaseService.getContentTypeModelsFromActions(results);
|
|
1354
|
+
const components = await releaseService.getAllComponents();
|
|
666
1355
|
ctx.body = {
|
|
667
1356
|
data: groupedData,
|
|
668
1357
|
meta: {
|
|
669
|
-
pagination
|
|
1358
|
+
pagination,
|
|
1359
|
+
contentTypes: contentTypes2,
|
|
1360
|
+
components
|
|
670
1361
|
}
|
|
671
1362
|
};
|
|
672
1363
|
},
|
|
@@ -688,10 +1379,8 @@ const releaseActionController = {
|
|
|
688
1379
|
async delete(ctx) {
|
|
689
1380
|
const actionId = ctx.params.actionId;
|
|
690
1381
|
const releaseId = ctx.params.releaseId;
|
|
691
|
-
const
|
|
692
|
-
|
|
693
|
-
releaseId
|
|
694
|
-
);
|
|
1382
|
+
const releaseService = getService("release", { strapi });
|
|
1383
|
+
const deletedReleaseAction = await releaseService.deleteAction(actionId, releaseId);
|
|
695
1384
|
ctx.body = {
|
|
696
1385
|
data: deletedReleaseAction
|
|
697
1386
|
};
|
|
@@ -872,11 +1561,12 @@ const routes = {
|
|
|
872
1561
|
release,
|
|
873
1562
|
"release-action": releaseAction
|
|
874
1563
|
};
|
|
875
|
-
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
876
1564
|
const getPlugin = () => {
|
|
877
|
-
if (features.isEnabled("cms-content-releases")
|
|
1565
|
+
if (strapi.ee.features.isEnabled("cms-content-releases")) {
|
|
878
1566
|
return {
|
|
879
1567
|
register,
|
|
1568
|
+
bootstrap,
|
|
1569
|
+
destroy,
|
|
880
1570
|
contentTypes,
|
|
881
1571
|
services,
|
|
882
1572
|
controllers,
|