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