@strapi/content-releases 0.0.0-next.60335b11eca9587b558035fe9ecca90e05db036b → 0.0.0-next.64bd4f3d1efcc9420d27c4f4d2013677ded62360
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-BKB1esYS.js +1395 -0
- package/dist/_chunks/App-BKB1esYS.js.map +1 -0
- package/dist/_chunks/App-Cne--1Z8.mjs +1374 -0
- package/dist/_chunks/App-Cne--1Z8.mjs.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-Be3acS2L.js +52 -0
- package/dist/_chunks/PurchaseContentReleases-Be3acS2L.js.map +1 -0
- package/dist/_chunks/PurchaseContentReleases-_MxP6-Dt.mjs +52 -0
- package/dist/_chunks/PurchaseContentReleases-_MxP6-Dt.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-C1WwGWIH.mjs +178 -0
- package/dist/_chunks/ReleasesSettingsPage-C1WwGWIH.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-kuXIwpWp.js +178 -0
- package/dist/_chunks/ReleasesSettingsPage-kuXIwpWp.js.map +1 -0
- package/dist/_chunks/{en-r9YocBH0.js → en-CmYoEnA7.js} +31 -5
- package/dist/_chunks/en-CmYoEnA7.js.map +1 -0
- package/dist/_chunks/{en-m9eTk4UF.mjs → en-D0yVZFqf.mjs} +31 -5
- package/dist/_chunks/en-D0yVZFqf.mjs.map +1 -0
- package/dist/_chunks/index-5Odi61vw.js +1381 -0
- package/dist/_chunks/index-5Odi61vw.js.map +1 -0
- package/dist/_chunks/index-Cy7qwpaU.mjs +1362 -0
- package/dist/_chunks/index-Cy7qwpaU.mjs.map +1 -0
- package/dist/_chunks/schemas-BE1LxE9J.js +62 -0
- package/dist/_chunks/schemas-BE1LxE9J.js.map +1 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs +44 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs.map +1 -0
- package/dist/admin/index.js +1 -15
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +2 -16
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/src/components/RelativeTime.d.ts +28 -0
- package/dist/admin/src/components/ReleaseAction.d.ts +3 -0
- package/dist/admin/src/components/ReleaseActionMenu.d.ts +26 -0
- package/dist/admin/src/components/ReleaseActionModal.d.ts +24 -0
- package/dist/admin/src/components/ReleaseActionOptions.d.ts +9 -0
- package/dist/admin/src/components/ReleaseListCell.d.ts +28 -0
- package/dist/admin/src/components/ReleaseModal.d.ts +17 -0
- package/dist/admin/src/components/ReleasesPanel.d.ts +3 -0
- package/dist/admin/src/constants.d.ts +76 -0
- package/dist/admin/src/index.d.ts +3 -0
- package/dist/admin/src/modules/hooks.d.ts +7 -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/ReleasesSettingsPage.d.ts +1 -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/release.d.ts +112 -0
- package/dist/admin/src/store/hooks.d.ts +7 -0
- package/dist/admin/src/utils/api.d.ts +6 -0
- package/dist/admin/src/utils/prefixPluginTranslations.d.ts +3 -0
- package/dist/admin/src/utils/time.d.ts +10 -0
- package/dist/admin/src/validation/schemas.d.ts +6 -0
- package/dist/server/index.js +1292 -525
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1292 -525
- 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 +21 -0
- package/dist/server/src/constants.d.ts.map +1 -0
- package/dist/server/src/content-types/index.d.ts +97 -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 +48 -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 +47 -0
- package/dist/server/src/content-types/release-action/schema.d.ts.map +1 -0
- package/dist/server/src/controllers/index.d.ts +25 -0
- package/dist/server/src/controllers/index.d.ts.map +1 -0
- package/dist/server/src/controllers/release-action.d.ts +10 -0
- package/dist/server/src/controllers/release-action.d.ts.map +1 -0
- package/dist/server/src/controllers/release.d.ts +18 -0
- package/dist/server/src/controllers/release.d.ts.map +1 -0
- 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 +14 -0
- package/dist/server/src/controllers/validation/release-action.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/release.d.ts +4 -0
- package/dist/server/src/controllers/validation/release.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/settings.d.ts +3 -0
- package/dist/server/src/controllers/validation/settings.d.ts.map +1 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/destroy.d.ts.map +1 -0
- package/dist/server/src/index.d.ts +2115 -0
- package/dist/server/src/index.d.ts.map +1 -0
- 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 +13 -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 +51 -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/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 +1828 -0
- package/dist/server/src/services/index.d.ts.map +1 -0
- package/dist/server/src/services/release-action.d.ts +38 -0
- package/dist/server/src/services/release-action.d.ts.map +1 -0
- package/dist/server/src/services/release.d.ts +31 -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/settings.d.ts +13 -0
- package/dist/server/src/services/settings.d.ts.map +1 -0
- package/dist/server/src/services/validation.d.ts +18 -0
- package/dist/server/src/services/validation.d.ts.map +1 -0
- package/dist/server/src/utils/index.d.ts +35 -0
- package/dist/server/src/utils/index.d.ts.map +1 -0
- package/dist/shared/contracts/release-actions.d.ts +130 -0
- package/dist/shared/contracts/release-actions.d.ts.map +1 -0
- package/dist/shared/contracts/releases.d.ts +184 -0
- package/dist/shared/contracts/releases.d.ts.map +1 -0
- package/dist/shared/contracts/settings.d.ts +39 -0
- package/dist/shared/contracts/settings.d.ts.map +1 -0
- package/dist/shared/types.d.ts +24 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/package.json +34 -37
- package/dist/_chunks/App-BFMg2T7b.mjs +0 -1080
- package/dist/_chunks/App-BFMg2T7b.mjs.map +0 -1
- package/dist/_chunks/App-HSsvlR7P.js +0 -1102
- package/dist/_chunks/App-HSsvlR7P.js.map +0 -1
- package/dist/_chunks/en-m9eTk4UF.mjs.map +0 -1
- package/dist/_chunks/en-r9YocBH0.js.map +0 -1
- package/dist/_chunks/index-GAnmm0Zl.js +0 -957
- package/dist/_chunks/index-GAnmm0Zl.js.map +0 -1
- package/dist/_chunks/index-y1lk-7GR.mjs +0 -936
- package/dist/_chunks/index-y1lk-7GR.mjs.map +0 -1
- package/strapi-server.js +0 -3
package/dist/server/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const utils = require("@strapi/utils");
|
|
3
|
+
const isEqual = require("lodash/isEqual");
|
|
3
4
|
const lodash = require("lodash");
|
|
4
5
|
const _ = require("lodash/fp");
|
|
5
|
-
const EE = require("@strapi/strapi/dist/utils/ee");
|
|
6
6
|
const nodeSchedule = require("node-schedule");
|
|
7
7
|
const yup = require("yup");
|
|
8
8
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
@@ -24,8 +24,8 @@ function _interopNamespace(e) {
|
|
|
24
24
|
n.default = e;
|
|
25
25
|
return Object.freeze(n);
|
|
26
26
|
}
|
|
27
|
+
const isEqual__default = /* @__PURE__ */ _interopDefault(isEqual);
|
|
27
28
|
const ___default = /* @__PURE__ */ _interopDefault(_);
|
|
28
|
-
const EE__default = /* @__PURE__ */ _interopDefault(EE);
|
|
29
29
|
const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
|
|
30
30
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
31
31
|
const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
|
|
@@ -71,8 +71,83 @@ 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
|
];
|
|
93
|
+
const ALLOWED_WEBHOOK_EVENTS = {
|
|
94
|
+
RELEASES_PUBLISH: "releases.publish"
|
|
95
|
+
};
|
|
96
|
+
const getService = (name, { strapi: strapi2 }) => {
|
|
97
|
+
return strapi2.plugin("content-releases").service(name);
|
|
98
|
+
};
|
|
99
|
+
const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi: strapi2 }) => {
|
|
100
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
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 });
|
|
104
|
+
};
|
|
105
|
+
const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
|
|
106
|
+
try {
|
|
107
|
+
await strapi2.entityValidator.validateEntityCreation(
|
|
108
|
+
strapi2.getModel(contentTypeUid),
|
|
109
|
+
entry,
|
|
110
|
+
void 0,
|
|
111
|
+
// @ts-expect-error - FIXME: entity here is unnecessary
|
|
112
|
+
entry
|
|
113
|
+
);
|
|
114
|
+
return true;
|
|
115
|
+
} catch {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
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
|
+
};
|
|
76
151
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
77
152
|
oldContentTypes,
|
|
78
153
|
contentTypes: contentTypes2
|
|
@@ -94,66 +169,349 @@ async function deleteActionsOnDisableDraftAndPublish({
|
|
|
94
169
|
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
95
170
|
const deletedContentTypes = lodash.difference(lodash.keys(oldContentTypes), lodash.keys(contentTypes2)) ?? [];
|
|
96
171
|
if (deletedContentTypes.length) {
|
|
97
|
-
await utils.
|
|
172
|
+
await utils.async.map(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
98
173
|
return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
|
|
99
174
|
});
|
|
100
175
|
}
|
|
101
176
|
}
|
|
102
|
-
|
|
177
|
+
async function migrateIsValidAndStatusReleases() {
|
|
178
|
+
const releasesWithoutStatus = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
179
|
+
where: {
|
|
180
|
+
status: null,
|
|
181
|
+
releasedAt: null
|
|
182
|
+
},
|
|
183
|
+
populate: {
|
|
184
|
+
actions: {
|
|
185
|
+
populate: {
|
|
186
|
+
entry: true
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
utils.async.map(releasesWithoutStatus, async (release2) => {
|
|
192
|
+
const actions = release2.actions;
|
|
193
|
+
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
194
|
+
for (const action of notValidatedActions) {
|
|
195
|
+
if (action.entry) {
|
|
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
|
+
}
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
215
|
+
});
|
|
216
|
+
const publishedReleases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
217
|
+
where: {
|
|
218
|
+
status: null,
|
|
219
|
+
releasedAt: {
|
|
220
|
+
$notNull: true
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
utils.async.map(publishedReleases, async (release2) => {
|
|
225
|
+
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
226
|
+
where: {
|
|
227
|
+
id: release2.id
|
|
228
|
+
},
|
|
229
|
+
data: {
|
|
230
|
+
status: "done"
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
236
|
+
if (oldContentTypes !== void 0 && contentTypes2 !== void 0) {
|
|
237
|
+
const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes).filter(
|
|
238
|
+
(uid) => oldContentTypes[uid]?.options?.draftAndPublish
|
|
239
|
+
);
|
|
240
|
+
const releasesAffected = /* @__PURE__ */ new Set();
|
|
241
|
+
utils.async.map(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
|
|
242
|
+
const oldContentType = oldContentTypes[contentTypeUID];
|
|
243
|
+
const contentType = contentTypes2[contentTypeUID];
|
|
244
|
+
if (!isEqual__default.default(oldContentType?.attributes, contentType?.attributes)) {
|
|
245
|
+
const actions = await strapi.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
246
|
+
where: {
|
|
247
|
+
contentType: contentTypeUID
|
|
248
|
+
},
|
|
249
|
+
populate: {
|
|
250
|
+
entry: true,
|
|
251
|
+
release: true
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
await utils.async.map(actions, async (action) => {
|
|
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
|
+
}
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}).then(() => {
|
|
277
|
+
utils.async.map(releasesAffected, async (releaseId) => {
|
|
278
|
+
return getService("release", { strapi }).updateReleaseStatus(releaseId);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async function disableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
284
|
+
if (!oldContentTypes) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
288
|
+
if (!i18nPlugin) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
for (const uid in contentTypes2) {
|
|
292
|
+
if (!oldContentTypes[uid]) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const oldContentType = oldContentTypes[uid];
|
|
296
|
+
const contentType = contentTypes2[uid];
|
|
297
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
298
|
+
if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
|
|
299
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
300
|
+
locale: null
|
|
301
|
+
}).where({ contentType: uid }).execute();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async function enableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
306
|
+
if (!oldContentTypes) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
310
|
+
if (!i18nPlugin) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
for (const uid in contentTypes2) {
|
|
314
|
+
if (!oldContentTypes[uid]) {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const oldContentType = oldContentTypes[uid];
|
|
318
|
+
const contentType = contentTypes2[uid];
|
|
319
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
320
|
+
const { getDefaultLocale } = i18nPlugin.service("locales");
|
|
321
|
+
if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
|
|
322
|
+
const defaultLocale = await getDefaultLocale();
|
|
323
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
324
|
+
locale: defaultLocale
|
|
325
|
+
}).where({ contentType: uid }).execute();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
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
|
+
};
|
|
103
357
|
const register = async ({ strapi: strapi2 }) => {
|
|
104
|
-
if (features
|
|
105
|
-
await strapi2.admin
|
|
106
|
-
strapi2.
|
|
107
|
-
strapi2.hook("strapi::content-types.
|
|
358
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
359
|
+
await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
|
|
360
|
+
strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
|
|
361
|
+
strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
|
|
362
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
363
|
+
}
|
|
364
|
+
if (strapi2.plugin("graphql")) {
|
|
365
|
+
const graphqlExtensionService = strapi2.plugin("graphql").service("extension");
|
|
366
|
+
graphqlExtensionService.shadowCRUD(RELEASE_MODEL_UID).disable();
|
|
367
|
+
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
368
|
+
}
|
|
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);
|
|
108
466
|
}
|
|
109
467
|
};
|
|
110
|
-
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
111
468
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
112
|
-
if (features
|
|
469
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
470
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
471
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
472
|
+
);
|
|
113
473
|
strapi2.db.lifecycles.subscribe({
|
|
114
|
-
|
|
115
|
-
const { model, result } = event;
|
|
116
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
117
|
-
const { id } = result;
|
|
118
|
-
strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
119
|
-
where: {
|
|
120
|
-
target_type: model.uid,
|
|
121
|
-
target_id: id
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
},
|
|
474
|
+
models: contentTypesWithDraftAndPublish,
|
|
126
475
|
/**
|
|
127
|
-
* deleteMany
|
|
128
|
-
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
129
|
-
*/
|
|
130
|
-
async beforeDeleteMany(event) {
|
|
131
|
-
const { model, params } = event;
|
|
132
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
133
|
-
const { where } = params;
|
|
134
|
-
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
135
|
-
event.state.entriesToDelete = entriesToDelete;
|
|
136
|
-
}
|
|
137
|
-
},
|
|
138
|
-
/**
|
|
139
|
-
* We delete the release actions related to deleted entries
|
|
140
|
-
* 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
|
|
141
477
|
*/
|
|
142
478
|
async afterDeleteMany(event) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
479
|
+
try {
|
|
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 }
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
} catch (error) {
|
|
490
|
+
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
491
|
+
error
|
|
153
492
|
});
|
|
154
493
|
}
|
|
155
494
|
}
|
|
156
495
|
});
|
|
496
|
+
strapi2.documents.use(deleteActionsOnDelete);
|
|
497
|
+
strapi2.documents.use(updateActionsOnUpdate);
|
|
498
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
499
|
+
strapi2.log.error(
|
|
500
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
501
|
+
);
|
|
502
|
+
throw err;
|
|
503
|
+
});
|
|
504
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
505
|
+
strapi2.get("webhookStore").addAllowedEvent(key, value);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
const destroy = async ({ strapi: strapi2 }) => {
|
|
510
|
+
const scheduledJobs = getService("scheduling", {
|
|
511
|
+
strapi: strapi2
|
|
512
|
+
}).getAll();
|
|
513
|
+
for (const [, job] of scheduledJobs) {
|
|
514
|
+
job.cancel();
|
|
157
515
|
}
|
|
158
516
|
};
|
|
159
517
|
const schema$1 = {
|
|
@@ -185,6 +543,14 @@ const schema$1 = {
|
|
|
185
543
|
scheduledAt: {
|
|
186
544
|
type: "datetime"
|
|
187
545
|
},
|
|
546
|
+
timezone: {
|
|
547
|
+
type: "string"
|
|
548
|
+
},
|
|
549
|
+
status: {
|
|
550
|
+
type: "enumeration",
|
|
551
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
552
|
+
required: true
|
|
553
|
+
},
|
|
188
554
|
actions: {
|
|
189
555
|
type: "relation",
|
|
190
556
|
relation: "oneToMany",
|
|
@@ -220,15 +586,13 @@ const schema = {
|
|
|
220
586
|
enum: ["publish", "unpublish"],
|
|
221
587
|
required: true
|
|
222
588
|
},
|
|
223
|
-
entry: {
|
|
224
|
-
type: "relation",
|
|
225
|
-
relation: "morphToOne",
|
|
226
|
-
configurable: false
|
|
227
|
-
},
|
|
228
589
|
contentType: {
|
|
229
590
|
type: "string",
|
|
230
591
|
required: true
|
|
231
592
|
},
|
|
593
|
+
entryDocumentId: {
|
|
594
|
+
type: "string"
|
|
595
|
+
},
|
|
232
596
|
locale: {
|
|
233
597
|
type: "string"
|
|
234
598
|
},
|
|
@@ -237,6 +601,9 @@ const schema = {
|
|
|
237
601
|
relation: "manyToOne",
|
|
238
602
|
target: RELEASE_MODEL_UID,
|
|
239
603
|
inversedBy: "actions"
|
|
604
|
+
},
|
|
605
|
+
isEntryValid: {
|
|
606
|
+
type: "boolean"
|
|
240
607
|
}
|
|
241
608
|
}
|
|
242
609
|
};
|
|
@@ -247,233 +614,297 @@ const contentTypes = {
|
|
|
247
614
|
release: release$1,
|
|
248
615
|
"release-action": releaseAction$1
|
|
249
616
|
};
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
return "contentType.displayName";
|
|
257
|
-
case "action":
|
|
258
|
-
return "type";
|
|
259
|
-
case "locale":
|
|
260
|
-
return ___default.default.getOr("No locale", "locale.name");
|
|
261
|
-
default:
|
|
262
|
-
return "contentType.displayName";
|
|
263
|
-
}
|
|
264
|
-
};
|
|
265
|
-
const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
266
|
-
async create(releaseData, { user }) {
|
|
267
|
-
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
268
|
-
const {
|
|
269
|
-
validatePendingReleasesLimit,
|
|
270
|
-
validateUniqueNameForPendingRelease,
|
|
271
|
-
validateScheduledAtIsLaterThanNow
|
|
272
|
-
} = getService("release-validation", { strapi: strapi2 });
|
|
273
|
-
await Promise.all([
|
|
274
|
-
validatePendingReleasesLimit(),
|
|
275
|
-
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
276
|
-
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
277
|
-
]);
|
|
278
|
-
const release2 = await strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
279
|
-
data: releaseWithCreatorFields
|
|
617
|
+
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
618
|
+
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
619
|
+
strapi2.eventHub.emit(event, {
|
|
620
|
+
isPublished,
|
|
621
|
+
error,
|
|
622
|
+
release: release2
|
|
280
623
|
});
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
async findOne(id, query = {}) {
|
|
288
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
|
|
289
|
-
...query
|
|
290
|
-
});
|
|
291
|
-
return release2;
|
|
292
|
-
},
|
|
293
|
-
findPage(query) {
|
|
294
|
-
return strapi2.entityService.findPage(RELEASE_MODEL_UID, {
|
|
295
|
-
...query,
|
|
296
|
-
populate: {
|
|
297
|
-
actions: {
|
|
298
|
-
// @ts-expect-error Ignore missing properties
|
|
299
|
-
count: true
|
|
624
|
+
};
|
|
625
|
+
const getFormattedActions = async (releaseId) => {
|
|
626
|
+
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
627
|
+
where: {
|
|
628
|
+
release: {
|
|
629
|
+
id: releaseId
|
|
300
630
|
}
|
|
301
631
|
}
|
|
302
632
|
});
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
633
|
+
if (actions.length === 0) {
|
|
634
|
+
throw new utils.errors.ValidationError("No entries to publish");
|
|
635
|
+
}
|
|
636
|
+
const formattedActions = {};
|
|
637
|
+
for (const action of actions) {
|
|
638
|
+
const contentTypeUid = action.contentType;
|
|
639
|
+
if (!formattedActions[contentTypeUid]) {
|
|
640
|
+
formattedActions[contentTypeUid] = {
|
|
641
|
+
publish: [],
|
|
642
|
+
unpublish: []
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
formattedActions[contentTypeUid][action.type].push({
|
|
646
|
+
documentId: action.entryDocumentId,
|
|
647
|
+
locale: action.locale
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
return formattedActions;
|
|
651
|
+
};
|
|
652
|
+
return {
|
|
653
|
+
async create(releaseData, { user }) {
|
|
654
|
+
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
655
|
+
const {
|
|
656
|
+
validatePendingReleasesLimit,
|
|
657
|
+
validateUniqueNameForPendingRelease,
|
|
658
|
+
validateScheduledAtIsLaterThanNow
|
|
659
|
+
} = getService("release-validation", { strapi: strapi2 });
|
|
660
|
+
await Promise.all([
|
|
661
|
+
validatePendingReleasesLimit(),
|
|
662
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
663
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
664
|
+
]);
|
|
665
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
|
|
666
|
+
data: {
|
|
667
|
+
...releaseWithCreatorFields,
|
|
668
|
+
status: "empty"
|
|
313
669
|
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
670
|
+
});
|
|
671
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
672
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
673
|
+
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
674
|
+
}
|
|
675
|
+
strapi2.telemetry.send("didCreateContentRelease");
|
|
676
|
+
return release2;
|
|
677
|
+
},
|
|
678
|
+
async findOne(id, query = {}) {
|
|
679
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query);
|
|
680
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
681
|
+
...dbQuery,
|
|
682
|
+
where: { id }
|
|
683
|
+
});
|
|
684
|
+
return release2;
|
|
685
|
+
},
|
|
686
|
+
findPage(query) {
|
|
687
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
688
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findPage({
|
|
689
|
+
...dbQuery,
|
|
690
|
+
populate: {
|
|
691
|
+
actions: {
|
|
692
|
+
count: true
|
|
321
693
|
}
|
|
322
694
|
}
|
|
695
|
+
});
|
|
696
|
+
},
|
|
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
|
|
701
|
+
});
|
|
702
|
+
},
|
|
703
|
+
async update(id, releaseData, { user }) {
|
|
704
|
+
const releaseWithCreatorFields = await utils.setCreatorFields({ user, isEdition: true })(
|
|
705
|
+
releaseData
|
|
706
|
+
);
|
|
707
|
+
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
|
708
|
+
"release-validation",
|
|
709
|
+
{ strapi: strapi2 }
|
|
710
|
+
);
|
|
711
|
+
await Promise.all([
|
|
712
|
+
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
713
|
+
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
714
|
+
]);
|
|
715
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
|
|
716
|
+
if (!release2) {
|
|
717
|
+
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
323
718
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
if (release2.actions?.length) {
|
|
327
|
-
const [actionForEntry] = release2.actions;
|
|
328
|
-
delete release2.actions;
|
|
329
|
-
return {
|
|
330
|
-
...release2,
|
|
331
|
-
action: actionForEntry
|
|
332
|
-
};
|
|
719
|
+
if (release2.releasedAt) {
|
|
720
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
333
721
|
}
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
722
|
+
const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
723
|
+
where: { id },
|
|
724
|
+
data: releaseWithCreatorFields
|
|
725
|
+
});
|
|
726
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
727
|
+
if (releaseData.scheduledAt) {
|
|
728
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
729
|
+
} else if (release2.scheduledAt) {
|
|
730
|
+
schedulingService.cancel(id);
|
|
731
|
+
}
|
|
732
|
+
this.updateReleaseStatus(id);
|
|
733
|
+
strapi2.telemetry.send("didUpdateContentRelease");
|
|
734
|
+
return updatedRelease;
|
|
735
|
+
},
|
|
736
|
+
async getAllComponents() {
|
|
737
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
738
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
739
|
+
const componentsMap = components.reduce(
|
|
740
|
+
(acc, component) => {
|
|
741
|
+
acc[component.uid] = component;
|
|
742
|
+
return acc;
|
|
342
743
|
},
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
744
|
+
{}
|
|
745
|
+
);
|
|
746
|
+
return componentsMap;
|
|
747
|
+
},
|
|
748
|
+
async delete(releaseId) {
|
|
749
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
750
|
+
where: { id: releaseId },
|
|
751
|
+
populate: {
|
|
752
|
+
actions: {
|
|
753
|
+
select: ["id"]
|
|
754
|
+
}
|
|
346
755
|
}
|
|
756
|
+
});
|
|
757
|
+
if (!release2) {
|
|
758
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
347
759
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
760
|
+
if (release2.releasedAt) {
|
|
761
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
762
|
+
}
|
|
763
|
+
await strapi2.db.transaction(async () => {
|
|
764
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
765
|
+
where: {
|
|
353
766
|
id: {
|
|
354
|
-
$
|
|
767
|
+
$in: release2.actions.map((action) => action.id)
|
|
355
768
|
}
|
|
356
|
-
},
|
|
357
|
-
{
|
|
358
|
-
actions: null
|
|
359
769
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
if (release2.
|
|
368
|
-
const
|
|
369
|
-
|
|
370
|
-
return {
|
|
371
|
-
...release2,
|
|
372
|
-
action: actionForEntry
|
|
373
|
-
};
|
|
770
|
+
});
|
|
771
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
772
|
+
where: {
|
|
773
|
+
id: releaseId
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
if (release2.scheduledAt) {
|
|
778
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
779
|
+
await schedulingService.cancel(release2.id);
|
|
374
780
|
}
|
|
781
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
375
782
|
return release2;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
386
|
-
}
|
|
387
|
-
const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
|
|
388
|
-
/*
|
|
389
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
390
|
-
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
391
|
-
*/
|
|
392
|
-
// @ts-expect-error see above
|
|
393
|
-
data: releaseWithCreatorFields
|
|
394
|
-
});
|
|
395
|
-
return updatedRelease;
|
|
396
|
-
},
|
|
397
|
-
async createAction(releaseId, action) {
|
|
398
|
-
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
399
|
-
strapi: strapi2
|
|
400
|
-
});
|
|
401
|
-
await Promise.all([
|
|
402
|
-
validateEntryContentType(action.entry.contentType),
|
|
403
|
-
validateUniqueEntry(releaseId, action)
|
|
404
|
-
]);
|
|
405
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
406
|
-
if (!release2) {
|
|
407
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
408
|
-
}
|
|
409
|
-
if (release2.releasedAt) {
|
|
410
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
411
|
-
}
|
|
412
|
-
const { entry, type } = action;
|
|
413
|
-
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
414
|
-
data: {
|
|
415
|
-
type,
|
|
416
|
-
contentType: entry.contentType,
|
|
417
|
-
locale: entry.locale,
|
|
418
|
-
entry: {
|
|
419
|
-
id: entry.id,
|
|
420
|
-
__type: entry.contentType,
|
|
421
|
-
__pivot: { field: "entry" }
|
|
422
|
-
},
|
|
423
|
-
release: releaseId
|
|
424
|
-
},
|
|
425
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
426
|
-
});
|
|
427
|
-
},
|
|
428
|
-
async findActions(releaseId, query) {
|
|
429
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
430
|
-
fields: ["id"]
|
|
431
|
-
});
|
|
432
|
-
if (!release2) {
|
|
433
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
434
|
-
}
|
|
435
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
436
|
-
...query,
|
|
437
|
-
populate: {
|
|
438
|
-
entry: {
|
|
439
|
-
populate: "*"
|
|
783
|
+
},
|
|
784
|
+
async publish(releaseId) {
|
|
785
|
+
const {
|
|
786
|
+
release: release2,
|
|
787
|
+
error
|
|
788
|
+
} = await strapi2.db.transaction(async ({ trx }) => {
|
|
789
|
+
const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
|
|
790
|
+
if (!lockedRelease) {
|
|
791
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
440
792
|
}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
793
|
+
if (lockedRelease.releasedAt) {
|
|
794
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
795
|
+
}
|
|
796
|
+
if (lockedRelease.status === "failed") {
|
|
797
|
+
throw new utils.errors.ValidationError("Release failed to publish");
|
|
798
|
+
}
|
|
799
|
+
try {
|
|
800
|
+
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
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
|
+
);
|
|
814
|
+
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
815
|
+
where: {
|
|
816
|
+
id: releaseId
|
|
817
|
+
},
|
|
818
|
+
data: {
|
|
819
|
+
status: "done",
|
|
820
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
824
|
+
isPublished: true,
|
|
825
|
+
release: release22
|
|
826
|
+
});
|
|
827
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
828
|
+
return { release: release22, error: null };
|
|
829
|
+
} catch (error2) {
|
|
830
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
831
|
+
isPublished: false,
|
|
832
|
+
error: error2
|
|
833
|
+
});
|
|
834
|
+
await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
|
|
835
|
+
status: "failed"
|
|
836
|
+
}).transacting(trx).execute();
|
|
837
|
+
return {
|
|
838
|
+
release: null,
|
|
839
|
+
error: error2
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
if (error instanceof Error) {
|
|
844
|
+
throw error;
|
|
444
845
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
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
|
+
});
|
|
454
882
|
}
|
|
455
|
-
return
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
const formattedData = actions.map((action) => {
|
|
462
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
463
|
-
return {
|
|
464
|
-
...action,
|
|
465
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
466
|
-
contentType: {
|
|
467
|
-
displayName,
|
|
468
|
-
mainFieldValue: action.entry[mainField],
|
|
469
|
-
uid: action.contentType
|
|
883
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
884
|
+
where: {
|
|
885
|
+
id: releaseId
|
|
886
|
+
},
|
|
887
|
+
data: {
|
|
888
|
+
status: "empty"
|
|
470
889
|
}
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
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 () => {
|
|
477
908
|
if (!strapi2.plugin("i18n")) {
|
|
478
909
|
return {};
|
|
479
910
|
}
|
|
@@ -482,8 +913,8 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
482
913
|
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
483
914
|
return acc;
|
|
484
915
|
}, {});
|
|
485
|
-
}
|
|
486
|
-
async
|
|
916
|
+
};
|
|
917
|
+
const getContentTypesDataForActions = async (contentTypesUids) => {
|
|
487
918
|
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
488
919
|
const contentTypesData = {};
|
|
489
920
|
for (const contentTypeUid of contentTypesUids) {
|
|
@@ -496,247 +927,249 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
496
927
|
};
|
|
497
928
|
}
|
|
498
929
|
return contentTypesData;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
return contentTypeModelsMap;
|
|
515
|
-
},
|
|
516
|
-
async getAllComponents() {
|
|
517
|
-
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
518
|
-
const components = await contentManagerComponentsService.findAllComponents();
|
|
519
|
-
const componentsMap = components.reduce(
|
|
520
|
-
(acc, component) => {
|
|
521
|
-
acc[component.uid] = component;
|
|
522
|
-
return acc;
|
|
523
|
-
},
|
|
524
|
-
{}
|
|
525
|
-
);
|
|
526
|
-
return componentsMap;
|
|
527
|
-
},
|
|
528
|
-
async delete(releaseId) {
|
|
529
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
530
|
-
populate: {
|
|
531
|
-
actions: {
|
|
532
|
-
fields: ["id"]
|
|
930
|
+
};
|
|
931
|
+
return {
|
|
932
|
+
async create(releaseId, action, { disableUpdateReleaseStatus = false } = {}) {
|
|
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 model = strapi2.contentType(action.contentType);
|
|
941
|
+
if (model.kind === "singleType") {
|
|
942
|
+
const document = await strapi2.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
943
|
+
if (!document) {
|
|
944
|
+
throw new utils.errors.NotFoundError(`No entry found for contentType ${action.contentType}`);
|
|
533
945
|
}
|
|
946
|
+
action.entryDocumentId = document.documentId;
|
|
534
947
|
}
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
948
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
|
|
949
|
+
if (!release2) {
|
|
950
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
951
|
+
}
|
|
952
|
+
if (release2.releasedAt) {
|
|
953
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
954
|
+
}
|
|
955
|
+
const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
|
|
956
|
+
{
|
|
957
|
+
contentType: action.contentType,
|
|
958
|
+
documentId: action.entryDocumentId,
|
|
959
|
+
locale: action.locale
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
strapi: strapi2
|
|
548
963
|
}
|
|
964
|
+
) : true;
|
|
965
|
+
const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
966
|
+
data: {
|
|
967
|
+
...action,
|
|
968
|
+
release: release2.id,
|
|
969
|
+
isEntryValid: actionStatus
|
|
970
|
+
},
|
|
971
|
+
populate: { release: { select: ["id"] } }
|
|
549
972
|
});
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
return release2;
|
|
553
|
-
},
|
|
554
|
-
async publish(releaseId) {
|
|
555
|
-
const releaseWithPopulatedActionEntries = await strapi2.entityService.findOne(
|
|
556
|
-
RELEASE_MODEL_UID,
|
|
557
|
-
releaseId,
|
|
558
|
-
{
|
|
559
|
-
populate: {
|
|
560
|
-
actions: {
|
|
561
|
-
populate: {
|
|
562
|
-
entry: {
|
|
563
|
-
fields: ["id"]
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
}
|
|
973
|
+
if (!disableUpdateReleaseStatus) {
|
|
974
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
568
975
|
}
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
if (!collectionTypeActions[contentTypeUid]) {
|
|
585
|
-
collectionTypeActions[contentTypeUid] = {
|
|
586
|
-
entriestoPublishIds: [],
|
|
587
|
-
entriesToUnpublishIds: []
|
|
588
|
-
};
|
|
976
|
+
return releaseAction2;
|
|
977
|
+
},
|
|
978
|
+
async findPage(releaseId, query) {
|
|
979
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
980
|
+
where: { id: releaseId },
|
|
981
|
+
select: ["id"]
|
|
982
|
+
});
|
|
983
|
+
if (!release2) {
|
|
984
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
985
|
+
}
|
|
986
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
987
|
+
const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
988
|
+
...dbQuery,
|
|
989
|
+
where: {
|
|
990
|
+
release: releaseId
|
|
589
991
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
992
|
+
});
|
|
993
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
994
|
+
const actionsWithEntry = await utils.async.map(actions, async (action) => {
|
|
995
|
+
const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
|
|
996
|
+
const entry = await getEntry(
|
|
997
|
+
{
|
|
998
|
+
contentType: action.contentType,
|
|
999
|
+
documentId: action.entryDocumentId,
|
|
1000
|
+
locale: action.locale,
|
|
1001
|
+
populate,
|
|
1002
|
+
status: action.type === "publish" ? "draft" : "published"
|
|
1003
|
+
},
|
|
1004
|
+
{ strapi: strapi2 }
|
|
1005
|
+
);
|
|
1006
|
+
return {
|
|
1007
|
+
...action,
|
|
1008
|
+
entry,
|
|
1009
|
+
status: entry ? await getEntryStatus(action.contentType, entry) : null
|
|
1010
|
+
};
|
|
1011
|
+
});
|
|
1012
|
+
return {
|
|
1013
|
+
results: actionsWithEntry,
|
|
1014
|
+
pagination
|
|
1015
|
+
};
|
|
1016
|
+
},
|
|
1017
|
+
async groupActions(actions, groupBy) {
|
|
1018
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
1019
|
+
if (!acc.includes(action.contentType)) {
|
|
1020
|
+
acc.push(action.contentType);
|
|
594
1021
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
609
|
-
try {
|
|
610
|
-
if (action === "publish") {
|
|
611
|
-
await entityManagerService.publish(entry, uid);
|
|
612
|
-
} else {
|
|
613
|
-
await entityManagerService.unpublish(entry, uid);
|
|
614
|
-
}
|
|
615
|
-
} catch (error) {
|
|
616
|
-
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
|
|
617
|
-
;
|
|
618
|
-
else {
|
|
619
|
-
throw error;
|
|
1022
|
+
return acc;
|
|
1023
|
+
}, []);
|
|
1024
|
+
const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
|
|
1025
|
+
const allLocalesDictionary = await getLocalesDataForActions();
|
|
1026
|
+
const formattedData = actions.map((action) => {
|
|
1027
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
1028
|
+
return {
|
|
1029
|
+
...action,
|
|
1030
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
1031
|
+
contentType: {
|
|
1032
|
+
displayName,
|
|
1033
|
+
mainFieldValue: action.entry[mainField],
|
|
1034
|
+
uid: action.contentType
|
|
620
1035
|
}
|
|
1036
|
+
};
|
|
1037
|
+
});
|
|
1038
|
+
const groupName = getGroupName(groupBy);
|
|
1039
|
+
return ___default.default.groupBy(groupName)(formattedData);
|
|
1040
|
+
},
|
|
1041
|
+
getContentTypeModelsFromActions(actions) {
|
|
1042
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
1043
|
+
if (!acc.includes(action.contentType)) {
|
|
1044
|
+
acc.push(action.contentType);
|
|
621
1045
|
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
1046
|
+
return acc;
|
|
1047
|
+
}, []);
|
|
1048
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
1049
|
+
(acc, contentTypeUid) => {
|
|
1050
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
1051
|
+
return acc;
|
|
1052
|
+
},
|
|
1053
|
+
{}
|
|
1054
|
+
);
|
|
1055
|
+
return contentTypeModelsMap;
|
|
1056
|
+
},
|
|
1057
|
+
async countActions(query) {
|
|
1058
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
1059
|
+
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
1060
|
+
},
|
|
1061
|
+
async update(actionId, releaseId, update) {
|
|
1062
|
+
const action = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findOne({
|
|
1063
|
+
where: {
|
|
1064
|
+
id: actionId,
|
|
1065
|
+
release: {
|
|
1066
|
+
id: releaseId,
|
|
1067
|
+
releasedAt: {
|
|
1068
|
+
$null: true
|
|
1069
|
+
}
|
|
646
1070
|
}
|
|
647
|
-
);
|
|
648
|
-
if (entriesToPublish.length > 0) {
|
|
649
|
-
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
650
1071
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, releaseId, {
|
|
657
|
-
data: {
|
|
658
|
-
/*
|
|
659
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
|
|
660
|
-
*/
|
|
661
|
-
// @ts-expect-error see above
|
|
662
|
-
releasedAt: /* @__PURE__ */ new Date()
|
|
1072
|
+
});
|
|
1073
|
+
if (!action) {
|
|
1074
|
+
throw new utils.errors.NotFoundError(
|
|
1075
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1076
|
+
);
|
|
663
1077
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
1078
|
+
const actionStatus = update.type === "publish" ? await getDraftEntryValidStatus(
|
|
1079
|
+
{
|
|
1080
|
+
contentType: action.contentType,
|
|
1081
|
+
documentId: action.entryDocumentId,
|
|
1082
|
+
locale: action.locale
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
strapi: strapi2
|
|
1086
|
+
}
|
|
1087
|
+
) : true;
|
|
1088
|
+
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
1089
|
+
where: {
|
|
1090
|
+
id: actionId,
|
|
1091
|
+
release: {
|
|
1092
|
+
id: releaseId,
|
|
1093
|
+
releasedAt: {
|
|
1094
|
+
$null: true
|
|
1095
|
+
}
|
|
675
1096
|
}
|
|
1097
|
+
},
|
|
1098
|
+
data: {
|
|
1099
|
+
...update,
|
|
1100
|
+
isEntryValid: actionStatus
|
|
676
1101
|
}
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
id: actionId,
|
|
691
|
-
release: {
|
|
692
|
-
id: releaseId,
|
|
693
|
-
releasedAt: {
|
|
694
|
-
$null: true
|
|
1102
|
+
});
|
|
1103
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1104
|
+
return updatedAction;
|
|
1105
|
+
},
|
|
1106
|
+
async delete(actionId, releaseId) {
|
|
1107
|
+
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
1108
|
+
where: {
|
|
1109
|
+
id: actionId,
|
|
1110
|
+
release: {
|
|
1111
|
+
id: releaseId,
|
|
1112
|
+
releasedAt: {
|
|
1113
|
+
$null: true
|
|
1114
|
+
}
|
|
695
1115
|
}
|
|
696
1116
|
}
|
|
1117
|
+
});
|
|
1118
|
+
if (!deletedAction) {
|
|
1119
|
+
throw new utils.errors.NotFoundError(
|
|
1120
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1121
|
+
);
|
|
697
1122
|
}
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
throw new utils.errors.NotFoundError(
|
|
701
|
-
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
702
|
-
);
|
|
1123
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1124
|
+
return deletedAction;
|
|
703
1125
|
}
|
|
704
|
-
|
|
1126
|
+
};
|
|
1127
|
+
};
|
|
1128
|
+
class AlreadyOnReleaseError extends utils.errors.ApplicationError {
|
|
1129
|
+
constructor(message) {
|
|
1130
|
+
super(message);
|
|
1131
|
+
this.name = "AlreadyOnReleaseError";
|
|
705
1132
|
}
|
|
706
|
-
}
|
|
1133
|
+
}
|
|
707
1134
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
708
1135
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
709
|
-
const release2 = await strapi2.
|
|
710
|
-
|
|
1136
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1137
|
+
where: {
|
|
1138
|
+
id: releaseId
|
|
1139
|
+
},
|
|
1140
|
+
populate: {
|
|
1141
|
+
actions: true
|
|
1142
|
+
}
|
|
711
1143
|
});
|
|
712
1144
|
if (!release2) {
|
|
713
1145
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
714
1146
|
}
|
|
715
1147
|
const isEntryInRelease = release2.actions.some(
|
|
716
|
-
(action) =>
|
|
1148
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
717
1149
|
);
|
|
718
1150
|
if (isEntryInRelease) {
|
|
719
|
-
throw new
|
|
720
|
-
`Entry with
|
|
1151
|
+
throw new AlreadyOnReleaseError(
|
|
1152
|
+
`Entry with documentId ${releaseActionArgs.entryDocumentId}${releaseActionArgs.locale ? `( ${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
|
|
721
1153
|
);
|
|
722
1154
|
}
|
|
723
1155
|
},
|
|
724
|
-
|
|
1156
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
725
1157
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
726
1158
|
if (!contentType) {
|
|
727
1159
|
throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
728
1160
|
}
|
|
729
|
-
if (!contentType
|
|
1161
|
+
if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
|
|
730
1162
|
throw new utils.errors.ValidationError(
|
|
731
1163
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
732
1164
|
);
|
|
733
1165
|
}
|
|
1166
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1167
|
+
throw new utils.errors.ValidationError("Document id is required for collection type");
|
|
1168
|
+
}
|
|
734
1169
|
},
|
|
735
1170
|
async validatePendingReleasesLimit() {
|
|
736
|
-
const
|
|
737
|
-
|
|
738
|
-
EE__default.default.features.get("cms-content-releases")?.options?.maximumReleases || 3
|
|
739
|
-
);
|
|
1171
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1172
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
740
1173
|
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
741
1174
|
filters: {
|
|
742
1175
|
releasedAt: {
|
|
@@ -748,13 +1181,14 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
748
1181
|
throw new utils.errors.ValidationError("You have reached the maximum number of pending releases");
|
|
749
1182
|
}
|
|
750
1183
|
},
|
|
751
|
-
async validateUniqueNameForPendingRelease(name) {
|
|
752
|
-
const pendingReleases = await strapi2.
|
|
753
|
-
|
|
1184
|
+
async validateUniqueNameForPendingRelease(name, id) {
|
|
1185
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1186
|
+
where: {
|
|
754
1187
|
releasedAt: {
|
|
755
1188
|
$null: true
|
|
756
1189
|
},
|
|
757
|
-
name
|
|
1190
|
+
name,
|
|
1191
|
+
...id && { id: { $ne: id } }
|
|
758
1192
|
}
|
|
759
1193
|
});
|
|
760
1194
|
const isNameUnique = pendingReleases.length === 0;
|
|
@@ -778,7 +1212,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
778
1212
|
}
|
|
779
1213
|
const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
|
|
780
1214
|
try {
|
|
781
|
-
await getService("release").publish(releaseId);
|
|
1215
|
+
await getService("release", { strapi: strapi2 }).publish(releaseId);
|
|
782
1216
|
} catch (error) {
|
|
783
1217
|
}
|
|
784
1218
|
this.cancel(releaseId);
|
|
@@ -795,68 +1229,197 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
795
1229
|
scheduledJobs.delete(releaseId);
|
|
796
1230
|
}
|
|
797
1231
|
return scheduledJobs;
|
|
1232
|
+
},
|
|
1233
|
+
getAll() {
|
|
1234
|
+
return scheduledJobs;
|
|
1235
|
+
},
|
|
1236
|
+
/**
|
|
1237
|
+
* On bootstrap, we can use this function to make sure to sync the scheduled jobs from the database that are not yet released
|
|
1238
|
+
* This is useful in case the server was restarted and the scheduled jobs were lost
|
|
1239
|
+
* This also could be used to sync different Strapi instances in case of a cluster
|
|
1240
|
+
*/
|
|
1241
|
+
async syncFromDatabase() {
|
|
1242
|
+
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1243
|
+
where: {
|
|
1244
|
+
scheduledAt: {
|
|
1245
|
+
$gte: /* @__PURE__ */ new Date()
|
|
1246
|
+
},
|
|
1247
|
+
releasedAt: null
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
for (const release2 of releases) {
|
|
1251
|
+
this.set(release2.id, release2.scheduledAt);
|
|
1252
|
+
}
|
|
1253
|
+
return scheduledJobs;
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
};
|
|
1257
|
+
const DEFAULT_SETTINGS = {
|
|
1258
|
+
defaultTimezone: null
|
|
1259
|
+
};
|
|
1260
|
+
const createSettingsService = ({ strapi: strapi2 }) => {
|
|
1261
|
+
const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
|
|
1262
|
+
return {
|
|
1263
|
+
async update({ settings: settings2 }) {
|
|
1264
|
+
const store = await getStore();
|
|
1265
|
+
store.set({ key: "settings", value: settings2 });
|
|
1266
|
+
return settings2;
|
|
1267
|
+
},
|
|
1268
|
+
async find() {
|
|
1269
|
+
const store = await getStore();
|
|
1270
|
+
const settings2 = await store.get({ key: "settings" });
|
|
1271
|
+
return {
|
|
1272
|
+
...DEFAULT_SETTINGS,
|
|
1273
|
+
...settings2 || {}
|
|
1274
|
+
};
|
|
798
1275
|
}
|
|
799
1276
|
};
|
|
800
1277
|
};
|
|
801
1278
|
const services = {
|
|
802
1279
|
release: createReleaseService,
|
|
1280
|
+
"release-action": createReleaseActionService,
|
|
803
1281
|
"release-validation": createReleaseValidationService,
|
|
804
|
-
|
|
1282
|
+
scheduling: createSchedulingService,
|
|
1283
|
+
settings: createSettingsService
|
|
805
1284
|
};
|
|
806
|
-
const RELEASE_SCHEMA =
|
|
807
|
-
name:
|
|
808
|
-
|
|
809
|
-
|
|
1285
|
+
const RELEASE_SCHEMA = utils.yup.object().shape({
|
|
1286
|
+
name: utils.yup.string().trim().required(),
|
|
1287
|
+
scheduledAt: utils.yup.string().nullable(),
|
|
1288
|
+
timezone: utils.yup.string().when("scheduledAt", {
|
|
1289
|
+
is: (value) => value !== null && value !== void 0,
|
|
1290
|
+
then: utils.yup.string().required(),
|
|
1291
|
+
otherwise: utils.yup.string().nullable()
|
|
1292
|
+
})
|
|
1293
|
+
}).required().noUnknown();
|
|
1294
|
+
const FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA = utils.yup.object().shape({
|
|
1295
|
+
contentType: utils.yup.string().required(),
|
|
1296
|
+
entryDocumentId: utils.yup.string().nullable(),
|
|
1297
|
+
hasEntryAttached: utils.yup.string().nullable(),
|
|
1298
|
+
locale: utils.yup.string().nullable()
|
|
810
1299
|
}).required().noUnknown();
|
|
811
1300
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
1301
|
+
const validatefindByDocumentAttachedParams = utils.validateYupSchema(
|
|
1302
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1303
|
+
);
|
|
812
1304
|
const releaseController = {
|
|
813
|
-
|
|
814
|
-
|
|
1305
|
+
/**
|
|
1306
|
+
* Find releases based on documents attached or not to the release.
|
|
1307
|
+
* If `hasEntryAttached` is true, it will return all releases that have the entry attached.
|
|
1308
|
+
* If `hasEntryAttached` is false, it will return all releases that don't have the entry attached.
|
|
1309
|
+
*/
|
|
1310
|
+
async findByDocumentAttached(ctx) {
|
|
1311
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
815
1312
|
ability: ctx.state.userAbility,
|
|
816
1313
|
model: RELEASE_MODEL_UID
|
|
817
1314
|
});
|
|
818
1315
|
await permissionsManager.validateQuery(ctx.query);
|
|
819
1316
|
const releaseService = getService("release", { strapi });
|
|
820
|
-
const
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
1317
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1318
|
+
await validatefindByDocumentAttachedParams(query);
|
|
1319
|
+
const model = strapi.getModel(query.contentType);
|
|
1320
|
+
if (model.kind && model.kind === "singleType") {
|
|
1321
|
+
const document = await strapi.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
1322
|
+
if (!document) {
|
|
1323
|
+
throw new utils.errors.NotFoundError(`No entry found for contentType ${query.contentType}`);
|
|
1324
|
+
}
|
|
1325
|
+
query.entryDocumentId = document.documentId;
|
|
1326
|
+
}
|
|
1327
|
+
const { contentType, hasEntryAttached, entryDocumentId, locale } = query;
|
|
1328
|
+
const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
|
|
1329
|
+
if (isEntryAttached) {
|
|
1330
|
+
const releases = await releaseService.findMany({
|
|
1331
|
+
where: {
|
|
1332
|
+
releasedAt: null,
|
|
1333
|
+
actions: {
|
|
1334
|
+
contentType,
|
|
1335
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1336
|
+
locale: locale ?? null
|
|
1337
|
+
}
|
|
1338
|
+
},
|
|
1339
|
+
populate: {
|
|
835
1340
|
actions: {
|
|
836
|
-
|
|
837
|
-
|
|
1341
|
+
fields: ["type"],
|
|
1342
|
+
filters: {
|
|
1343
|
+
contentType,
|
|
1344
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1345
|
+
locale: locale ?? null
|
|
838
1346
|
}
|
|
839
1347
|
}
|
|
840
|
-
}
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
ctx.body = { data: releases };
|
|
1351
|
+
} else {
|
|
1352
|
+
const relatedReleases = await releaseService.findMany({
|
|
1353
|
+
where: {
|
|
1354
|
+
releasedAt: null,
|
|
1355
|
+
actions: {
|
|
1356
|
+
contentType,
|
|
1357
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1358
|
+
locale: locale ?? null
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
});
|
|
1362
|
+
const releases = await releaseService.findMany({
|
|
1363
|
+
where: {
|
|
1364
|
+
$or: [
|
|
1365
|
+
{
|
|
1366
|
+
id: {
|
|
1367
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1368
|
+
}
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
actions: null
|
|
1372
|
+
}
|
|
1373
|
+
],
|
|
1374
|
+
releasedAt: null
|
|
1375
|
+
}
|
|
841
1376
|
});
|
|
842
|
-
ctx.body = { data
|
|
1377
|
+
ctx.body = { data: releases };
|
|
843
1378
|
}
|
|
844
1379
|
},
|
|
1380
|
+
async findPage(ctx) {
|
|
1381
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1382
|
+
ability: ctx.state.userAbility,
|
|
1383
|
+
model: RELEASE_MODEL_UID
|
|
1384
|
+
});
|
|
1385
|
+
await permissionsManager.validateQuery(ctx.query);
|
|
1386
|
+
const releaseService = getService("release", { strapi });
|
|
1387
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1388
|
+
const { results, pagination } = await releaseService.findPage(query);
|
|
1389
|
+
const data = results.map((release2) => {
|
|
1390
|
+
const { actions, ...releaseData } = release2;
|
|
1391
|
+
return {
|
|
1392
|
+
...releaseData,
|
|
1393
|
+
actions: {
|
|
1394
|
+
meta: {
|
|
1395
|
+
count: actions.count
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
};
|
|
1399
|
+
});
|
|
1400
|
+
const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
|
|
1401
|
+
where: {
|
|
1402
|
+
releasedAt: null
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1405
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
1406
|
+
},
|
|
845
1407
|
async findOne(ctx) {
|
|
846
1408
|
const id = ctx.params.id;
|
|
847
1409
|
const releaseService = getService("release", { strapi });
|
|
1410
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
848
1411
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
849
1412
|
if (!release2) {
|
|
850
1413
|
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
851
1414
|
}
|
|
852
|
-
const count = await
|
|
1415
|
+
const count = await releaseActionService.countActions({
|
|
853
1416
|
filters: {
|
|
854
1417
|
release: id
|
|
855
1418
|
}
|
|
856
1419
|
});
|
|
857
1420
|
const sanitizedRelease = {
|
|
858
1421
|
...release2,
|
|
859
|
-
createdBy: release2.createdBy ? strapi.admin
|
|
1422
|
+
createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
|
|
860
1423
|
};
|
|
861
1424
|
const data = {
|
|
862
1425
|
...sanitizedRelease,
|
|
@@ -868,19 +1431,63 @@ const releaseController = {
|
|
|
868
1431
|
};
|
|
869
1432
|
ctx.body = { data };
|
|
870
1433
|
},
|
|
1434
|
+
async mapEntriesToReleases(ctx) {
|
|
1435
|
+
const { contentTypeUid, documentIds, locale } = ctx.query;
|
|
1436
|
+
if (!contentTypeUid || !documentIds) {
|
|
1437
|
+
throw new utils.errors.ValidationError("Missing required query parameters");
|
|
1438
|
+
}
|
|
1439
|
+
const releaseService = getService("release", { strapi });
|
|
1440
|
+
const releasesWithActions = await releaseService.findMany({
|
|
1441
|
+
where: {
|
|
1442
|
+
releasedAt: null,
|
|
1443
|
+
actions: {
|
|
1444
|
+
contentType: contentTypeUid,
|
|
1445
|
+
entryDocumentId: {
|
|
1446
|
+
$in: documentIds
|
|
1447
|
+
},
|
|
1448
|
+
locale
|
|
1449
|
+
}
|
|
1450
|
+
},
|
|
1451
|
+
populate: {
|
|
1452
|
+
actions: true
|
|
1453
|
+
}
|
|
1454
|
+
});
|
|
1455
|
+
const mappedEntriesInReleases = releasesWithActions.reduce(
|
|
1456
|
+
(acc, release2) => {
|
|
1457
|
+
release2.actions.forEach((action) => {
|
|
1458
|
+
if (action.contentType !== contentTypeUid) {
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
if (locale && action.locale !== locale) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
if (!acc[action.entryDocumentId]) {
|
|
1465
|
+
acc[action.entryDocumentId] = [{ id: release2.id, name: release2.name }];
|
|
1466
|
+
} else {
|
|
1467
|
+
acc[action.entryDocumentId].push({ id: release2.id, name: release2.name });
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
return acc;
|
|
1471
|
+
},
|
|
1472
|
+
{}
|
|
1473
|
+
);
|
|
1474
|
+
ctx.body = {
|
|
1475
|
+
data: mappedEntriesInReleases
|
|
1476
|
+
};
|
|
1477
|
+
},
|
|
871
1478
|
async create(ctx) {
|
|
872
1479
|
const user = ctx.state.user;
|
|
873
1480
|
const releaseArgs = ctx.request.body;
|
|
874
1481
|
await validateRelease(releaseArgs);
|
|
875
1482
|
const releaseService = getService("release", { strapi });
|
|
876
1483
|
const release2 = await releaseService.create(releaseArgs, { user });
|
|
877
|
-
const permissionsManager = strapi.admin
|
|
1484
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
878
1485
|
ability: ctx.state.userAbility,
|
|
879
1486
|
model: RELEASE_MODEL_UID
|
|
880
1487
|
});
|
|
881
|
-
ctx.
|
|
1488
|
+
ctx.created({
|
|
882
1489
|
data: await permissionsManager.sanitizeOutput(release2)
|
|
883
|
-
};
|
|
1490
|
+
});
|
|
884
1491
|
},
|
|
885
1492
|
async update(ctx) {
|
|
886
1493
|
const user = ctx.state.user;
|
|
@@ -889,7 +1496,7 @@ const releaseController = {
|
|
|
889
1496
|
await validateRelease(releaseArgs);
|
|
890
1497
|
const releaseService = getService("release", { strapi });
|
|
891
1498
|
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
892
|
-
const permissionsManager = strapi.admin
|
|
1499
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
893
1500
|
ability: ctx.state.userAbility,
|
|
894
1501
|
model: RELEASE_MODEL_UID
|
|
895
1502
|
});
|
|
@@ -906,18 +1513,18 @@ const releaseController = {
|
|
|
906
1513
|
};
|
|
907
1514
|
},
|
|
908
1515
|
async publish(ctx) {
|
|
909
|
-
const user = ctx.state.user;
|
|
910
1516
|
const id = ctx.params.id;
|
|
911
1517
|
const releaseService = getService("release", { strapi });
|
|
912
|
-
const
|
|
1518
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1519
|
+
const release2 = await releaseService.publish(id);
|
|
913
1520
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
914
|
-
|
|
1521
|
+
releaseActionService.countActions({
|
|
915
1522
|
filters: {
|
|
916
1523
|
release: id,
|
|
917
1524
|
type: "publish"
|
|
918
1525
|
}
|
|
919
1526
|
}),
|
|
920
|
-
|
|
1527
|
+
releaseActionService.countActions({
|
|
921
1528
|
filters: {
|
|
922
1529
|
release: id,
|
|
923
1530
|
type: "unpublish"
|
|
@@ -935,57 +1542,106 @@ const releaseController = {
|
|
|
935
1542
|
}
|
|
936
1543
|
};
|
|
937
1544
|
const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
}).required(),
|
|
1545
|
+
contentType: utils.yup.string().required(),
|
|
1546
|
+
entryDocumentId: utils.yup.strapiID(),
|
|
1547
|
+
locale: utils.yup.string(),
|
|
942
1548
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
943
1549
|
});
|
|
944
1550
|
const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
|
|
945
1551
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
946
1552
|
});
|
|
1553
|
+
const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
|
|
1554
|
+
groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
|
|
1555
|
+
});
|
|
947
1556
|
const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
948
1557
|
const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1558
|
+
const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
949
1559
|
const releaseActionController = {
|
|
950
1560
|
async create(ctx) {
|
|
951
1561
|
const releaseId = ctx.params.releaseId;
|
|
952
1562
|
const releaseActionArgs = ctx.request.body;
|
|
953
1563
|
await validateReleaseAction(releaseActionArgs);
|
|
954
|
-
const
|
|
955
|
-
const releaseAction2 = await
|
|
956
|
-
ctx.
|
|
1564
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1565
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1566
|
+
ctx.created({
|
|
957
1567
|
data: releaseAction2
|
|
958
|
-
};
|
|
1568
|
+
});
|
|
1569
|
+
},
|
|
1570
|
+
async createMany(ctx) {
|
|
1571
|
+
const releaseId = ctx.params.releaseId;
|
|
1572
|
+
const releaseActionsArgs = ctx.request.body;
|
|
1573
|
+
await Promise.all(
|
|
1574
|
+
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1575
|
+
);
|
|
1576
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1577
|
+
const releaseService = getService("release", { strapi });
|
|
1578
|
+
const releaseActions = await strapi.db.transaction(async () => {
|
|
1579
|
+
const releaseActions2 = await Promise.all(
|
|
1580
|
+
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1581
|
+
try {
|
|
1582
|
+
const action = await releaseActionService.create(releaseId, releaseActionArgs, {
|
|
1583
|
+
disableUpdateReleaseStatus: true
|
|
1584
|
+
});
|
|
1585
|
+
return action;
|
|
1586
|
+
} catch (error) {
|
|
1587
|
+
if (error instanceof AlreadyOnReleaseError) {
|
|
1588
|
+
return null;
|
|
1589
|
+
}
|
|
1590
|
+
throw error;
|
|
1591
|
+
}
|
|
1592
|
+
})
|
|
1593
|
+
);
|
|
1594
|
+
return releaseActions2;
|
|
1595
|
+
});
|
|
1596
|
+
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1597
|
+
if (newReleaseActions.length > 0) {
|
|
1598
|
+
releaseService.updateReleaseStatus(releaseId);
|
|
1599
|
+
}
|
|
1600
|
+
ctx.created({
|
|
1601
|
+
data: newReleaseActions,
|
|
1602
|
+
meta: {
|
|
1603
|
+
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1604
|
+
totalEntries: releaseActions.length
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
959
1607
|
},
|
|
960
1608
|
async findMany(ctx) {
|
|
961
1609
|
const releaseId = ctx.params.releaseId;
|
|
962
|
-
const permissionsManager = strapi.admin
|
|
1610
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
963
1611
|
ability: ctx.state.userAbility,
|
|
964
1612
|
model: RELEASE_ACTION_MODEL_UID
|
|
965
1613
|
});
|
|
1614
|
+
await validateFindManyActionsParams(ctx.query);
|
|
1615
|
+
if (ctx.query.groupBy) {
|
|
1616
|
+
if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
|
|
1617
|
+
ctx.badRequest("Invalid groupBy parameter");
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
|
|
1621
|
+
delete ctx.query.groupBy;
|
|
966
1622
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
967
|
-
const
|
|
968
|
-
const { results, pagination } = await
|
|
969
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1623
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1624
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
970
1625
|
...query
|
|
971
1626
|
});
|
|
972
1627
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
973
1628
|
if (acc[action.contentType]) {
|
|
974
1629
|
return acc;
|
|
975
1630
|
}
|
|
976
|
-
const contentTypePermissionsManager = strapi.admin
|
|
1631
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
977
1632
|
ability: ctx.state.userAbility,
|
|
978
1633
|
model: action.contentType
|
|
979
1634
|
});
|
|
980
1635
|
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
981
1636
|
return acc;
|
|
982
1637
|
}, {});
|
|
983
|
-
const sanitizedResults = await utils.
|
|
1638
|
+
const sanitizedResults = await utils.async.map(results, async (action) => ({
|
|
984
1639
|
...action,
|
|
985
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1640
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
986
1641
|
}));
|
|
987
|
-
const groupedData = await
|
|
988
|
-
const contentTypes2 =
|
|
1642
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1643
|
+
const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
|
|
1644
|
+
const releaseService = getService("release", { strapi });
|
|
989
1645
|
const components = await releaseService.getAllComponents();
|
|
990
1646
|
ctx.body = {
|
|
991
1647
|
data: groupedData,
|
|
@@ -1001,8 +1657,8 @@ const releaseActionController = {
|
|
|
1001
1657
|
const releaseId = ctx.params.releaseId;
|
|
1002
1658
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1003
1659
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1004
|
-
const
|
|
1005
|
-
const updatedAction = await
|
|
1660
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1661
|
+
const updatedAction = await releaseActionService.update(
|
|
1006
1662
|
actionId,
|
|
1007
1663
|
releaseId,
|
|
1008
1664
|
releaseActionUpdateArgs
|
|
@@ -1014,17 +1670,71 @@ const releaseActionController = {
|
|
|
1014
1670
|
async delete(ctx) {
|
|
1015
1671
|
const actionId = ctx.params.actionId;
|
|
1016
1672
|
const releaseId = ctx.params.releaseId;
|
|
1017
|
-
const
|
|
1018
|
-
const deletedReleaseAction = await
|
|
1673
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1674
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1019
1675
|
ctx.body = {
|
|
1020
1676
|
data: deletedReleaseAction
|
|
1021
1677
|
};
|
|
1022
1678
|
}
|
|
1023
1679
|
};
|
|
1024
|
-
const
|
|
1680
|
+
const SETTINGS_SCHEMA = yup__namespace.object().shape({
|
|
1681
|
+
defaultTimezone: yup__namespace.string().nullable().default(null)
|
|
1682
|
+
}).required().noUnknown();
|
|
1683
|
+
const validateSettings = utils.validateYupSchema(SETTINGS_SCHEMA);
|
|
1684
|
+
const settingsController = {
|
|
1685
|
+
async find(ctx) {
|
|
1686
|
+
const settingsService = getService("settings", { strapi });
|
|
1687
|
+
const settings2 = await settingsService.find();
|
|
1688
|
+
ctx.body = { data: settings2 };
|
|
1689
|
+
},
|
|
1690
|
+
async update(ctx) {
|
|
1691
|
+
const settingsBody = ctx.request.body;
|
|
1692
|
+
const settings2 = await validateSettings(settingsBody);
|
|
1693
|
+
const settingsService = getService("settings", { strapi });
|
|
1694
|
+
const updatedSettings = await settingsService.update({ settings: settings2 });
|
|
1695
|
+
ctx.body = { data: updatedSettings };
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
const controllers = {
|
|
1699
|
+
release: releaseController,
|
|
1700
|
+
"release-action": releaseActionController,
|
|
1701
|
+
settings: settingsController
|
|
1702
|
+
};
|
|
1025
1703
|
const release = {
|
|
1026
1704
|
type: "admin",
|
|
1027
1705
|
routes: [
|
|
1706
|
+
{
|
|
1707
|
+
method: "GET",
|
|
1708
|
+
path: "/mapEntriesToReleases",
|
|
1709
|
+
handler: "release.mapEntriesToReleases",
|
|
1710
|
+
config: {
|
|
1711
|
+
policies: [
|
|
1712
|
+
"admin::isAuthenticatedAdmin",
|
|
1713
|
+
{
|
|
1714
|
+
name: "admin::hasPermissions",
|
|
1715
|
+
config: {
|
|
1716
|
+
actions: ["plugin::content-releases.read"]
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
]
|
|
1720
|
+
}
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
method: "GET",
|
|
1724
|
+
path: "/getByDocumentAttached",
|
|
1725
|
+
handler: "release.findByDocumentAttached",
|
|
1726
|
+
config: {
|
|
1727
|
+
policies: [
|
|
1728
|
+
"admin::isAuthenticatedAdmin",
|
|
1729
|
+
{
|
|
1730
|
+
name: "admin::hasPermissions",
|
|
1731
|
+
config: {
|
|
1732
|
+
actions: ["plugin::content-releases.read"]
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
]
|
|
1736
|
+
}
|
|
1737
|
+
},
|
|
1028
1738
|
{
|
|
1029
1739
|
method: "POST",
|
|
1030
1740
|
path: "/",
|
|
@@ -1044,7 +1754,7 @@ const release = {
|
|
|
1044
1754
|
{
|
|
1045
1755
|
method: "GET",
|
|
1046
1756
|
path: "/",
|
|
1047
|
-
handler: "release.
|
|
1757
|
+
handler: "release.findPage",
|
|
1048
1758
|
config: {
|
|
1049
1759
|
policies: [
|
|
1050
1760
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1142,6 +1852,22 @@ const releaseAction = {
|
|
|
1142
1852
|
]
|
|
1143
1853
|
}
|
|
1144
1854
|
},
|
|
1855
|
+
{
|
|
1856
|
+
method: "POST",
|
|
1857
|
+
path: "/:releaseId/actions/bulk",
|
|
1858
|
+
handler: "release-action.createMany",
|
|
1859
|
+
config: {
|
|
1860
|
+
policies: [
|
|
1861
|
+
"admin::isAuthenticatedAdmin",
|
|
1862
|
+
{
|
|
1863
|
+
name: "admin::hasPermissions",
|
|
1864
|
+
config: {
|
|
1865
|
+
actions: ["plugin::content-releases.create-action"]
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
]
|
|
1869
|
+
}
|
|
1870
|
+
},
|
|
1145
1871
|
{
|
|
1146
1872
|
method: "GET",
|
|
1147
1873
|
path: "/:releaseId/actions",
|
|
@@ -1192,16 +1918,54 @@ const releaseAction = {
|
|
|
1192
1918
|
}
|
|
1193
1919
|
]
|
|
1194
1920
|
};
|
|
1921
|
+
const settings = {
|
|
1922
|
+
type: "admin",
|
|
1923
|
+
routes: [
|
|
1924
|
+
{
|
|
1925
|
+
method: "GET",
|
|
1926
|
+
path: "/settings",
|
|
1927
|
+
handler: "settings.find",
|
|
1928
|
+
config: {
|
|
1929
|
+
policies: [
|
|
1930
|
+
"admin::isAuthenticatedAdmin",
|
|
1931
|
+
{
|
|
1932
|
+
name: "admin::hasPermissions",
|
|
1933
|
+
config: {
|
|
1934
|
+
actions: ["plugin::content-releases.settings.read"]
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
]
|
|
1938
|
+
}
|
|
1939
|
+
},
|
|
1940
|
+
{
|
|
1941
|
+
method: "PUT",
|
|
1942
|
+
path: "/settings",
|
|
1943
|
+
handler: "settings.update",
|
|
1944
|
+
config: {
|
|
1945
|
+
policies: [
|
|
1946
|
+
"admin::isAuthenticatedAdmin",
|
|
1947
|
+
{
|
|
1948
|
+
name: "admin::hasPermissions",
|
|
1949
|
+
config: {
|
|
1950
|
+
actions: ["plugin::content-releases.settings.update"]
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
]
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
]
|
|
1957
|
+
};
|
|
1195
1958
|
const routes = {
|
|
1959
|
+
settings,
|
|
1196
1960
|
release,
|
|
1197
1961
|
"release-action": releaseAction
|
|
1198
1962
|
};
|
|
1199
|
-
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
1200
1963
|
const getPlugin = () => {
|
|
1201
|
-
if (features.isEnabled("cms-content-releases")) {
|
|
1964
|
+
if (strapi.ee.features.isEnabled("cms-content-releases")) {
|
|
1202
1965
|
return {
|
|
1203
1966
|
register,
|
|
1204
1967
|
bootstrap,
|
|
1968
|
+
destroy,
|
|
1205
1969
|
contentTypes,
|
|
1206
1970
|
services,
|
|
1207
1971
|
controllers,
|
|
@@ -1209,6 +1973,9 @@ const getPlugin = () => {
|
|
|
1209
1973
|
};
|
|
1210
1974
|
}
|
|
1211
1975
|
return {
|
|
1976
|
+
// Always return register, it handles its own feature check
|
|
1977
|
+
register,
|
|
1978
|
+
// Always return contentTypes to avoid losing data when the feature is disabled
|
|
1212
1979
|
contentTypes
|
|
1213
1980
|
};
|
|
1214
1981
|
};
|