@strapi/content-releases 0.0.0-next.504ae21185714e6995d2bdd6458efe2e20371a84 → 0.0.0-next.51dcef446efe662f00da6d85d6bea2218c4c538e
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-2DuPv5k0.js → en-BWPPsSH-.js} +43 -7
- package/dist/_chunks/en-BWPPsSH-.js.map +1 -0
- package/dist/_chunks/{en-SOqjCdyh.mjs → en-D9Q4YW03.mjs} +43 -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 +1409 -520
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1409 -520
- 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-3ycH2d3s.mjs +0 -1062
- package/dist/_chunks/App-3ycH2d3s.mjs.map +0 -1
- package/dist/_chunks/App-5PsAyVt2.js +0 -1084
- package/dist/_chunks/App-5PsAyVt2.js.map +0 -1
- package/dist/_chunks/en-2DuPv5k0.js.map +0 -1
- package/dist/_chunks/en-SOqjCdyh.mjs.map +0 -1
- package/dist/_chunks/index-4gUWuCQV.mjs +0 -909
- package/dist/_chunks/index-4gUWuCQV.mjs.map +0 -1
- package/dist/_chunks/index-D57Rztnc.js +0 -930
- package/dist/_chunks/index-D57Rztnc.js.map +0 -1
- package/strapi-server.js +0 -3
package/dist/server/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
const utils = require("@strapi/utils");
|
|
3
|
+
const isEqual = require("lodash/isEqual");
|
|
3
4
|
const lodash = require("lodash");
|
|
4
5
|
const _ = require("lodash/fp");
|
|
5
|
-
const
|
|
6
|
+
const nodeSchedule = require("node-schedule");
|
|
6
7
|
const yup = require("yup");
|
|
7
8
|
const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
|
|
8
9
|
function _interopNamespace(e) {
|
|
@@ -23,8 +24,8 @@ function _interopNamespace(e) {
|
|
|
23
24
|
n.default = e;
|
|
24
25
|
return Object.freeze(n);
|
|
25
26
|
}
|
|
27
|
+
const isEqual__default = /* @__PURE__ */ _interopDefault(isEqual);
|
|
26
28
|
const ___default = /* @__PURE__ */ _interopDefault(_);
|
|
27
|
-
const EE__default = /* @__PURE__ */ _interopDefault(EE);
|
|
28
29
|
const yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
|
|
29
30
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
30
31
|
const RELEASE_ACTION_MODEL_UID = "plugin::content-releases.release-action";
|
|
@@ -70,8 +71,94 @@ const ACTIONS = [
|
|
|
70
71
|
displayName: "Add an entry to a release",
|
|
71
72
|
uid: "create-action",
|
|
72
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"
|
|
73
91
|
}
|
|
74
92
|
];
|
|
93
|
+
const ALLOWED_WEBHOOK_EVENTS = {
|
|
94
|
+
RELEASES_PUBLISH: "releases.publish"
|
|
95
|
+
};
|
|
96
|
+
const getService = (name, { strapi: strapi2 }) => {
|
|
97
|
+
return strapi2.plugin("content-releases").service(name);
|
|
98
|
+
};
|
|
99
|
+
const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi: strapi2 }) => {
|
|
100
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
101
|
+
const populate = await populateBuilderService(contentType).populateDeep(Infinity).build();
|
|
102
|
+
const entry = await getEntry({ contentType, documentId, locale, populate }, { strapi: strapi2 });
|
|
103
|
+
return isEntryValid(contentType, entry, { strapi: strapi2 });
|
|
104
|
+
};
|
|
105
|
+
const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
|
|
106
|
+
try {
|
|
107
|
+
await strapi2.entityValidator.validateEntityCreation(
|
|
108
|
+
strapi2.getModel(contentTypeUid),
|
|
109
|
+
entry,
|
|
110
|
+
void 0,
|
|
111
|
+
// @ts-expect-error - FIXME: entity here is unnecessary
|
|
112
|
+
entry
|
|
113
|
+
);
|
|
114
|
+
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
|
+
};
|
|
75
162
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
76
163
|
oldContentTypes,
|
|
77
164
|
contentTypes: contentTypes2
|
|
@@ -93,66 +180,354 @@ async function deleteActionsOnDisableDraftAndPublish({
|
|
|
93
180
|
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
94
181
|
const deletedContentTypes = lodash.difference(lodash.keys(oldContentTypes), lodash.keys(contentTypes2)) ?? [];
|
|
95
182
|
if (deletedContentTypes.length) {
|
|
96
|
-
await utils.
|
|
183
|
+
await utils.async.map(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
97
184
|
return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
|
|
98
185
|
});
|
|
99
186
|
}
|
|
100
187
|
}
|
|
101
|
-
|
|
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
|
|
198
|
+
}
|
|
199
|
+
}
|
|
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"
|
|
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
|
|
250
|
+
);
|
|
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
|
+
};
|
|
102
372
|
const register = async ({ strapi: strapi2 }) => {
|
|
103
|
-
if (features
|
|
104
|
-
await strapi2.admin
|
|
105
|
-
strapi2.
|
|
106
|
-
strapi2.hook("strapi::content-types.
|
|
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);
|
|
107
482
|
}
|
|
108
483
|
};
|
|
109
|
-
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
110
484
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
111
|
-
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
|
+
);
|
|
112
489
|
strapi2.db.lifecycles.subscribe({
|
|
113
|
-
|
|
114
|
-
const { model, result } = event;
|
|
115
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
116
|
-
const { id } = result;
|
|
117
|
-
strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
118
|
-
where: {
|
|
119
|
-
target_type: model.uid,
|
|
120
|
-
target_id: id
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
/**
|
|
126
|
-
* deleteMany hook doesn't return the deleted entries ids
|
|
127
|
-
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
128
|
-
*/
|
|
129
|
-
async beforeDeleteMany(event) {
|
|
130
|
-
const { model, params } = event;
|
|
131
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
132
|
-
const { where } = params;
|
|
133
|
-
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
134
|
-
event.state.entriesToDelete = entriesToDelete;
|
|
135
|
-
}
|
|
136
|
-
},
|
|
490
|
+
models: contentTypesWithDraftAndPublish,
|
|
137
491
|
/**
|
|
138
|
-
*
|
|
139
|
-
* 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
|
|
140
493
|
*/
|
|
141
494
|
async afterDeleteMany(event) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
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
|
|
152
508
|
});
|
|
153
509
|
}
|
|
154
510
|
}
|
|
155
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();
|
|
156
531
|
}
|
|
157
532
|
};
|
|
158
533
|
const schema$1 = {
|
|
@@ -181,6 +556,17 @@ const schema$1 = {
|
|
|
181
556
|
releasedAt: {
|
|
182
557
|
type: "datetime"
|
|
183
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
|
+
},
|
|
184
570
|
actions: {
|
|
185
571
|
type: "relation",
|
|
186
572
|
relation: "oneToMany",
|
|
@@ -216,15 +602,13 @@ const schema = {
|
|
|
216
602
|
enum: ["publish", "unpublish"],
|
|
217
603
|
required: true
|
|
218
604
|
},
|
|
219
|
-
entry: {
|
|
220
|
-
type: "relation",
|
|
221
|
-
relation: "morphToOne",
|
|
222
|
-
configurable: false
|
|
223
|
-
},
|
|
224
605
|
contentType: {
|
|
225
606
|
type: "string",
|
|
226
607
|
required: true
|
|
227
608
|
},
|
|
609
|
+
entryDocumentId: {
|
|
610
|
+
type: "string"
|
|
611
|
+
},
|
|
228
612
|
locale: {
|
|
229
613
|
type: "string"
|
|
230
614
|
},
|
|
@@ -233,6 +617,9 @@ const schema = {
|
|
|
233
617
|
relation: "manyToOne",
|
|
234
618
|
target: RELEASE_MODEL_UID,
|
|
235
619
|
inversedBy: "actions"
|
|
620
|
+
},
|
|
621
|
+
isEntryValid: {
|
|
622
|
+
type: "boolean"
|
|
236
623
|
}
|
|
237
624
|
}
|
|
238
625
|
};
|
|
@@ -243,226 +630,297 @@ const contentTypes = {
|
|
|
243
630
|
release: release$1,
|
|
244
631
|
"release-action": releaseAction$1
|
|
245
632
|
};
|
|
246
|
-
const
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
return "contentType.displayName";
|
|
253
|
-
case "action":
|
|
254
|
-
return "type";
|
|
255
|
-
case "locale":
|
|
256
|
-
return ___default.default.getOr("No locale", "locale.name");
|
|
257
|
-
default:
|
|
258
|
-
return "contentType.displayName";
|
|
259
|
-
}
|
|
260
|
-
};
|
|
261
|
-
const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
262
|
-
async create(releaseData, { user }) {
|
|
263
|
-
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
264
|
-
const { validatePendingReleasesLimit, validateUniqueNameForPendingRelease } = getService(
|
|
265
|
-
"release-validation",
|
|
266
|
-
{ strapi: strapi2 }
|
|
267
|
-
);
|
|
268
|
-
await Promise.all([
|
|
269
|
-
validatePendingReleasesLimit(),
|
|
270
|
-
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name)
|
|
271
|
-
]);
|
|
272
|
-
return strapi2.entityService.create(RELEASE_MODEL_UID, {
|
|
273
|
-
data: releaseWithCreatorFields
|
|
274
|
-
});
|
|
275
|
-
},
|
|
276
|
-
async findOne(id, query = {}) {
|
|
277
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, id, {
|
|
278
|
-
...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
|
|
279
639
|
});
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
actions: {
|
|
287
|
-
// @ts-expect-error Ignore missing properties
|
|
288
|
-
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
|
|
289
646
|
}
|
|
290
647
|
}
|
|
291
648
|
});
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
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
|
+
};
|
|
660
|
+
}
|
|
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"
|
|
302
685
|
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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
|
|
310
709
|
}
|
|
311
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}`);
|
|
312
734
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (release2.actions?.length) {
|
|
316
|
-
const [actionForEntry] = release2.actions;
|
|
317
|
-
delete release2.actions;
|
|
318
|
-
return {
|
|
319
|
-
...release2,
|
|
320
|
-
action: actionForEntry
|
|
321
|
-
};
|
|
735
|
+
if (release2.releasedAt) {
|
|
736
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
322
737
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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;
|
|
331
759
|
},
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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
|
+
}
|
|
335
771
|
}
|
|
772
|
+
});
|
|
773
|
+
if (!release2) {
|
|
774
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
336
775
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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: {
|
|
342
782
|
id: {
|
|
343
|
-
$
|
|
783
|
+
$in: release2.actions.map((action) => action.id)
|
|
344
784
|
}
|
|
345
|
-
},
|
|
346
|
-
{
|
|
347
|
-
actions: null
|
|
348
785
|
}
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
if (release2.
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
return {
|
|
360
|
-
...release2,
|
|
361
|
-
action: actionForEntry
|
|
362
|
-
};
|
|
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);
|
|
363
796
|
}
|
|
797
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
364
798
|
return release2;
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
375
|
-
}
|
|
376
|
-
const updatedRelease = await strapi2.entityService.update(RELEASE_MODEL_UID, id, {
|
|
377
|
-
/*
|
|
378
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
379
|
-
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
380
|
-
*/
|
|
381
|
-
// @ts-expect-error see above
|
|
382
|
-
data: releaseWithCreatorFields
|
|
383
|
-
});
|
|
384
|
-
return updatedRelease;
|
|
385
|
-
},
|
|
386
|
-
async createAction(releaseId, action) {
|
|
387
|
-
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
388
|
-
strapi: strapi2
|
|
389
|
-
});
|
|
390
|
-
await Promise.all([
|
|
391
|
-
validateEntryContentType(action.entry.contentType),
|
|
392
|
-
validateUniqueEntry(releaseId, action)
|
|
393
|
-
]);
|
|
394
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
395
|
-
if (!release2) {
|
|
396
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
397
|
-
}
|
|
398
|
-
if (release2.releasedAt) {
|
|
399
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
400
|
-
}
|
|
401
|
-
const { entry, type } = action;
|
|
402
|
-
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
403
|
-
data: {
|
|
404
|
-
type,
|
|
405
|
-
contentType: entry.contentType,
|
|
406
|
-
locale: entry.locale,
|
|
407
|
-
entry: {
|
|
408
|
-
id: entry.id,
|
|
409
|
-
__type: entry.contentType,
|
|
410
|
-
__pivot: { field: "entry" }
|
|
411
|
-
},
|
|
412
|
-
release: releaseId
|
|
413
|
-
},
|
|
414
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
415
|
-
});
|
|
416
|
-
},
|
|
417
|
-
async findActions(releaseId, query) {
|
|
418
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
419
|
-
fields: ["id"]
|
|
420
|
-
});
|
|
421
|
-
if (!release2) {
|
|
422
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
423
|
-
}
|
|
424
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
425
|
-
...query,
|
|
426
|
-
populate: {
|
|
427
|
-
entry: {
|
|
428
|
-
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}`);
|
|
429
808
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
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;
|
|
433
861
|
}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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
|
+
});
|
|
443
898
|
}
|
|
444
|
-
return
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const formattedData = actions.map((action) => {
|
|
451
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
452
|
-
return {
|
|
453
|
-
...action,
|
|
454
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
455
|
-
contentType: {
|
|
456
|
-
displayName,
|
|
457
|
-
mainFieldValue: action.entry[mainField],
|
|
458
|
-
uid: action.contentType
|
|
899
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
900
|
+
where: {
|
|
901
|
+
id: releaseId
|
|
902
|
+
},
|
|
903
|
+
data: {
|
|
904
|
+
status: "empty"
|
|
459
905
|
}
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
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 () => {
|
|
466
924
|
if (!strapi2.plugin("i18n")) {
|
|
467
925
|
return {};
|
|
468
926
|
}
|
|
@@ -471,8 +929,8 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
471
929
|
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
472
930
|
return acc;
|
|
473
931
|
}, {});
|
|
474
|
-
}
|
|
475
|
-
async
|
|
932
|
+
};
|
|
933
|
+
const getContentTypesDataForActions = async (contentTypesUids) => {
|
|
476
934
|
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
477
935
|
const contentTypesData = {};
|
|
478
936
|
for (const contentTypeUid of contentTypesUids) {
|
|
@@ -485,247 +943,307 @@ const createReleaseService = ({ strapi: strapi2 }) => ({
|
|
|
485
943
|
};
|
|
486
944
|
}
|
|
487
945
|
return contentTypesData;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
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;
|
|
493
963
|
}
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
(acc, component) => {
|
|
510
|
-
acc[component.uid] = component;
|
|
511
|
-
return acc;
|
|
512
|
-
},
|
|
513
|
-
{}
|
|
514
|
-
);
|
|
515
|
-
return componentsMap;
|
|
516
|
-
},
|
|
517
|
-
async delete(releaseId) {
|
|
518
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
519
|
-
populate: {
|
|
520
|
-
actions: {
|
|
521
|
-
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
|
|
522
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);
|
|
523
991
|
}
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
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,
|
|
533
1005
|
where: {
|
|
534
|
-
|
|
535
|
-
$in: release2.actions.map((action) => action.id)
|
|
536
|
-
}
|
|
1006
|
+
release: releaseId
|
|
537
1007
|
}
|
|
538
1008
|
});
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
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
|
|
555
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);
|
|
556
1061
|
}
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const contentTypeUid = action.contentType;
|
|
572
|
-
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
573
|
-
if (!collectionTypeActions[contentTypeUid]) {
|
|
574
|
-
collectionTypeActions[contentTypeUid] = {
|
|
575
|
-
entriestoPublishIds: [],
|
|
576
|
-
entriesToUnpublishIds: []
|
|
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
|
|
577
1076
|
};
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
598
|
-
try {
|
|
599
|
-
if (action === "publish") {
|
|
600
|
-
await entityManagerService.publish(entry, uid);
|
|
601
|
-
} else {
|
|
602
|
-
await entityManagerService.unpublish(entry, uid);
|
|
603
|
-
}
|
|
604
|
-
} catch (error) {
|
|
605
|
-
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
|
|
606
|
-
;
|
|
607
|
-
else {
|
|
608
|
-
throw error;
|
|
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
|
|
1095
|
+
}
|
|
609
1096
|
}
|
|
610
1097
|
}
|
|
611
|
-
}
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const entriesToPublish = await strapi2.entityService.findMany(
|
|
616
|
-
contentTypeUid,
|
|
617
|
-
{
|
|
618
|
-
filters: {
|
|
619
|
-
id: {
|
|
620
|
-
$in: entriestoPublishIds
|
|
621
|
-
}
|
|
622
|
-
},
|
|
623
|
-
populate
|
|
624
|
-
}
|
|
625
|
-
);
|
|
626
|
-
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
627
|
-
contentTypeUid,
|
|
628
|
-
{
|
|
629
|
-
filters: {
|
|
630
|
-
id: {
|
|
631
|
-
$in: entriesToUnpublishIds
|
|
632
|
-
}
|
|
633
|
-
},
|
|
634
|
-
populate
|
|
635
|
-
}
|
|
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`
|
|
636
1102
|
);
|
|
637
|
-
|
|
638
|
-
|
|
1103
|
+
}
|
|
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
|
|
639
1112
|
}
|
|
640
|
-
|
|
641
|
-
|
|
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
|
|
642
1127
|
}
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
async updateAction(actionId, releaseId, update) {
|
|
657
|
-
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
658
|
-
where: {
|
|
659
|
-
id: actionId,
|
|
660
|
-
release: {
|
|
661
|
-
id: releaseId,
|
|
662
|
-
releasedAt: {
|
|
663
|
-
$null: true
|
|
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
|
+
}
|
|
664
1141
|
}
|
|
665
1142
|
}
|
|
666
|
-
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
);
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
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
|
+
);
|
|
1148
|
+
}
|
|
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
|
+
}
|
|
1164
|
+
}
|
|
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
|
|
684
1184
|
}
|
|
1185
|
+
});
|
|
1186
|
+
if (!releasesUpdated.includes(action.release.id)) {
|
|
1187
|
+
releasesUpdated.push(action.release.id);
|
|
685
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
|
+
});
|
|
686
1198
|
}
|
|
687
|
-
});
|
|
688
|
-
if (!deletedAction) {
|
|
689
|
-
throw new utils.errors.NotFoundError(
|
|
690
|
-
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
691
|
-
);
|
|
692
1199
|
}
|
|
693
|
-
|
|
1200
|
+
};
|
|
1201
|
+
};
|
|
1202
|
+
class AlreadyOnReleaseError extends utils.errors.ApplicationError {
|
|
1203
|
+
constructor(message) {
|
|
1204
|
+
super(message);
|
|
1205
|
+
this.name = "AlreadyOnReleaseError";
|
|
694
1206
|
}
|
|
695
|
-
}
|
|
1207
|
+
}
|
|
696
1208
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
697
1209
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
698
|
-
const release2 = await strapi2.
|
|
699
|
-
|
|
1210
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1211
|
+
where: {
|
|
1212
|
+
id: releaseId
|
|
1213
|
+
},
|
|
1214
|
+
populate: {
|
|
1215
|
+
actions: true
|
|
1216
|
+
}
|
|
700
1217
|
});
|
|
701
1218
|
if (!release2) {
|
|
702
1219
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
703
1220
|
}
|
|
704
1221
|
const isEntryInRelease = release2.actions.some(
|
|
705
|
-
(action) =>
|
|
1222
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
706
1223
|
);
|
|
707
1224
|
if (isEntryInRelease) {
|
|
708
|
-
throw new
|
|
709
|
-
`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}`
|
|
710
1227
|
);
|
|
711
1228
|
}
|
|
712
1229
|
},
|
|
713
|
-
|
|
1230
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
714
1231
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
715
1232
|
if (!contentType) {
|
|
716
1233
|
throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
717
1234
|
}
|
|
718
|
-
if (!contentType
|
|
1235
|
+
if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
|
|
719
1236
|
throw new utils.errors.ValidationError(
|
|
720
1237
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
721
1238
|
);
|
|
722
1239
|
}
|
|
1240
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1241
|
+
throw new utils.errors.ValidationError("Document id is required for collection type");
|
|
1242
|
+
}
|
|
723
1243
|
},
|
|
724
1244
|
async validatePendingReleasesLimit() {
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
EE__default.default.features.get("cms-content-releases")?.options?.maximumReleases || 3
|
|
728
|
-
);
|
|
1245
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1246
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
729
1247
|
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
730
1248
|
filters: {
|
|
731
1249
|
releasedAt: {
|
|
@@ -737,79 +1255,246 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
737
1255
|
throw new utils.errors.ValidationError("You have reached the maximum number of pending releases");
|
|
738
1256
|
}
|
|
739
1257
|
},
|
|
740
|
-
async validateUniqueNameForPendingRelease(name) {
|
|
741
|
-
const pendingReleases = await strapi2.
|
|
742
|
-
|
|
1258
|
+
async validateUniqueNameForPendingRelease(name, id) {
|
|
1259
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1260
|
+
where: {
|
|
743
1261
|
releasedAt: {
|
|
744
1262
|
$null: true
|
|
745
1263
|
},
|
|
746
|
-
name
|
|
1264
|
+
name,
|
|
1265
|
+
...id && { id: { $ne: id } }
|
|
747
1266
|
}
|
|
748
1267
|
});
|
|
749
1268
|
const isNameUnique = pendingReleases.length === 0;
|
|
750
1269
|
if (!isNameUnique) {
|
|
751
1270
|
throw new utils.errors.ValidationError(`Release with name ${name} already exists`);
|
|
752
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
|
+
}
|
|
753
1277
|
}
|
|
754
1278
|
});
|
|
1279
|
+
const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
1280
|
+
const scheduledJobs = /* @__PURE__ */ new Map();
|
|
1281
|
+
return {
|
|
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;
|
|
1299
|
+
},
|
|
1300
|
+
cancel(releaseId) {
|
|
1301
|
+
if (scheduledJobs.has(releaseId)) {
|
|
1302
|
+
scheduledJobs.get(releaseId).cancel();
|
|
1303
|
+
scheduledJobs.delete(releaseId);
|
|
1304
|
+
}
|
|
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
|
+
}
|
|
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
|
+
};
|
|
1349
|
+
}
|
|
1350
|
+
};
|
|
1351
|
+
};
|
|
755
1352
|
const services = {
|
|
756
1353
|
release: createReleaseService,
|
|
757
|
-
"release-
|
|
1354
|
+
"release-action": createReleaseActionService,
|
|
1355
|
+
"release-validation": createReleaseValidationService,
|
|
1356
|
+
scheduling: createSchedulingService,
|
|
1357
|
+
settings: createSettingsService
|
|
758
1358
|
};
|
|
759
|
-
const RELEASE_SCHEMA =
|
|
760
|
-
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()
|
|
761
1373
|
}).required().noUnknown();
|
|
762
1374
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
1375
|
+
const validatefindByDocumentAttachedParams = utils.validateYupSchema(
|
|
1376
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1377
|
+
);
|
|
763
1378
|
const releaseController = {
|
|
764
|
-
|
|
765
|
-
|
|
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({
|
|
766
1386
|
ability: ctx.state.userAbility,
|
|
767
1387
|
model: RELEASE_MODEL_UID
|
|
768
1388
|
});
|
|
769
1389
|
await permissionsManager.validateQuery(ctx.query);
|
|
770
1390
|
const releaseService = getService("release", { strapi });
|
|
771
|
-
const
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
const
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
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: {
|
|
786
1414
|
actions: {
|
|
787
|
-
|
|
788
|
-
|
|
1415
|
+
fields: ["type"],
|
|
1416
|
+
filters: {
|
|
1417
|
+
contentType,
|
|
1418
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1419
|
+
locale: locale ?? null
|
|
789
1420
|
}
|
|
790
1421
|
}
|
|
791
|
-
}
|
|
1422
|
+
}
|
|
1423
|
+
});
|
|
1424
|
+
ctx.body = { data: releases };
|
|
1425
|
+
} else {
|
|
1426
|
+
const relatedReleases = await releaseService.findMany({
|
|
1427
|
+
where: {
|
|
1428
|
+
releasedAt: null,
|
|
1429
|
+
actions: {
|
|
1430
|
+
contentType,
|
|
1431
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1432
|
+
locale: locale ?? null
|
|
1433
|
+
}
|
|
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
|
+
}
|
|
792
1450
|
});
|
|
793
|
-
ctx.body = { data
|
|
1451
|
+
ctx.body = { data: releases };
|
|
794
1452
|
}
|
|
795
1453
|
},
|
|
796
|
-
async
|
|
797
|
-
const
|
|
798
|
-
const releaseService = getService("release", { strapi });
|
|
799
|
-
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
800
|
-
const permissionsManager = strapi.admin.services.permission.createPermissionsManager({
|
|
1454
|
+
async findPage(ctx) {
|
|
1455
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
801
1456
|
ability: ctx.state.userAbility,
|
|
802
1457
|
model: RELEASE_MODEL_UID
|
|
803
1458
|
});
|
|
804
|
-
|
|
805
|
-
const
|
|
806
|
-
|
|
807
|
-
|
|
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
|
|
808
1477
|
}
|
|
809
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"] });
|
|
810
1486
|
if (!release2) {
|
|
811
1487
|
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
812
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
|
+
};
|
|
813
1498
|
const data = {
|
|
814
1499
|
...sanitizedRelease,
|
|
815
1500
|
actions: {
|
|
@@ -820,19 +1505,63 @@ const releaseController = {
|
|
|
820
1505
|
};
|
|
821
1506
|
ctx.body = { data };
|
|
822
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
|
+
},
|
|
823
1552
|
async create(ctx) {
|
|
824
1553
|
const user = ctx.state.user;
|
|
825
1554
|
const releaseArgs = ctx.request.body;
|
|
826
1555
|
await validateRelease(releaseArgs);
|
|
827
1556
|
const releaseService = getService("release", { strapi });
|
|
828
1557
|
const release2 = await releaseService.create(releaseArgs, { user });
|
|
829
|
-
const permissionsManager = strapi.admin
|
|
1558
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
830
1559
|
ability: ctx.state.userAbility,
|
|
831
1560
|
model: RELEASE_MODEL_UID
|
|
832
1561
|
});
|
|
833
|
-
ctx.
|
|
1562
|
+
ctx.created({
|
|
834
1563
|
data: await permissionsManager.sanitizeOutput(release2)
|
|
835
|
-
};
|
|
1564
|
+
});
|
|
836
1565
|
},
|
|
837
1566
|
async update(ctx) {
|
|
838
1567
|
const user = ctx.state.user;
|
|
@@ -841,7 +1570,7 @@ const releaseController = {
|
|
|
841
1570
|
await validateRelease(releaseArgs);
|
|
842
1571
|
const releaseService = getService("release", { strapi });
|
|
843
1572
|
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
844
|
-
const permissionsManager = strapi.admin
|
|
1573
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
845
1574
|
ability: ctx.state.userAbility,
|
|
846
1575
|
model: RELEASE_MODEL_UID
|
|
847
1576
|
});
|
|
@@ -858,18 +1587,18 @@ const releaseController = {
|
|
|
858
1587
|
};
|
|
859
1588
|
},
|
|
860
1589
|
async publish(ctx) {
|
|
861
|
-
const user = ctx.state.user;
|
|
862
1590
|
const id = ctx.params.id;
|
|
863
1591
|
const releaseService = getService("release", { strapi });
|
|
864
|
-
const
|
|
1592
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1593
|
+
const release2 = await releaseService.publish(id);
|
|
865
1594
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
866
|
-
|
|
1595
|
+
releaseActionService.countActions({
|
|
867
1596
|
filters: {
|
|
868
1597
|
release: id,
|
|
869
1598
|
type: "publish"
|
|
870
1599
|
}
|
|
871
1600
|
}),
|
|
872
|
-
|
|
1601
|
+
releaseActionService.countActions({
|
|
873
1602
|
filters: {
|
|
874
1603
|
release: id,
|
|
875
1604
|
type: "unpublish"
|
|
@@ -887,57 +1616,106 @@ const releaseController = {
|
|
|
887
1616
|
}
|
|
888
1617
|
};
|
|
889
1618
|
const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
}).required(),
|
|
1619
|
+
contentType: utils.yup.string().required(),
|
|
1620
|
+
entryDocumentId: utils.yup.strapiID(),
|
|
1621
|
+
locale: utils.yup.string(),
|
|
894
1622
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
895
1623
|
});
|
|
896
1624
|
const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
|
|
897
1625
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
898
1626
|
});
|
|
1627
|
+
const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
|
|
1628
|
+
groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
|
|
1629
|
+
});
|
|
899
1630
|
const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
900
1631
|
const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1632
|
+
const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
901
1633
|
const releaseActionController = {
|
|
902
1634
|
async create(ctx) {
|
|
903
1635
|
const releaseId = ctx.params.releaseId;
|
|
904
1636
|
const releaseActionArgs = ctx.request.body;
|
|
905
1637
|
await validateReleaseAction(releaseActionArgs);
|
|
906
|
-
const
|
|
907
|
-
const releaseAction2 = await
|
|
908
|
-
ctx.
|
|
1638
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1639
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1640
|
+
ctx.created({
|
|
909
1641
|
data: releaseAction2
|
|
910
|
-
};
|
|
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
|
+
});
|
|
911
1681
|
},
|
|
912
1682
|
async findMany(ctx) {
|
|
913
1683
|
const releaseId = ctx.params.releaseId;
|
|
914
|
-
const permissionsManager = strapi.admin
|
|
1684
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
915
1685
|
ability: ctx.state.userAbility,
|
|
916
1686
|
model: RELEASE_ACTION_MODEL_UID
|
|
917
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;
|
|
918
1696
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
919
|
-
const
|
|
920
|
-
const { results, pagination } = await
|
|
921
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1697
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1698
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
922
1699
|
...query
|
|
923
1700
|
});
|
|
924
1701
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
925
1702
|
if (acc[action.contentType]) {
|
|
926
1703
|
return acc;
|
|
927
1704
|
}
|
|
928
|
-
const contentTypePermissionsManager = strapi.admin
|
|
1705
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
929
1706
|
ability: ctx.state.userAbility,
|
|
930
1707
|
model: action.contentType
|
|
931
1708
|
});
|
|
932
1709
|
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
933
1710
|
return acc;
|
|
934
1711
|
}, {});
|
|
935
|
-
const sanitizedResults = await utils.
|
|
1712
|
+
const sanitizedResults = await utils.async.map(results, async (action) => ({
|
|
936
1713
|
...action,
|
|
937
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1714
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
938
1715
|
}));
|
|
939
|
-
const groupedData = await
|
|
940
|
-
const contentTypes2 =
|
|
1716
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1717
|
+
const contentTypes2 = await releaseActionService.getContentTypeModelsFromActions(results);
|
|
1718
|
+
const releaseService = getService("release", { strapi });
|
|
941
1719
|
const components = await releaseService.getAllComponents();
|
|
942
1720
|
ctx.body = {
|
|
943
1721
|
data: groupedData,
|
|
@@ -953,8 +1731,8 @@ const releaseActionController = {
|
|
|
953
1731
|
const releaseId = ctx.params.releaseId;
|
|
954
1732
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
955
1733
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
956
|
-
const
|
|
957
|
-
const updatedAction = await
|
|
1734
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1735
|
+
const updatedAction = await releaseActionService.update(
|
|
958
1736
|
actionId,
|
|
959
1737
|
releaseId,
|
|
960
1738
|
releaseActionUpdateArgs
|
|
@@ -966,17 +1744,71 @@ const releaseActionController = {
|
|
|
966
1744
|
async delete(ctx) {
|
|
967
1745
|
const actionId = ctx.params.actionId;
|
|
968
1746
|
const releaseId = ctx.params.releaseId;
|
|
969
|
-
const
|
|
970
|
-
const deletedReleaseAction = await
|
|
1747
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1748
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
971
1749
|
ctx.body = {
|
|
972
1750
|
data: deletedReleaseAction
|
|
973
1751
|
};
|
|
974
1752
|
}
|
|
975
1753
|
};
|
|
976
|
-
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
|
+
};
|
|
977
1777
|
const release = {
|
|
978
1778
|
type: "admin",
|
|
979
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
|
+
},
|
|
980
1812
|
{
|
|
981
1813
|
method: "POST",
|
|
982
1814
|
path: "/",
|
|
@@ -996,7 +1828,7 @@ const release = {
|
|
|
996
1828
|
{
|
|
997
1829
|
method: "GET",
|
|
998
1830
|
path: "/",
|
|
999
|
-
handler: "release.
|
|
1831
|
+
handler: "release.findPage",
|
|
1000
1832
|
config: {
|
|
1001
1833
|
policies: [
|
|
1002
1834
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1094,6 +1926,22 @@ const releaseAction = {
|
|
|
1094
1926
|
]
|
|
1095
1927
|
}
|
|
1096
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
|
+
},
|
|
1097
1945
|
{
|
|
1098
1946
|
method: "GET",
|
|
1099
1947
|
path: "/:releaseId/actions",
|
|
@@ -1144,16 +1992,54 @@ const releaseAction = {
|
|
|
1144
1992
|
}
|
|
1145
1993
|
]
|
|
1146
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
|
+
};
|
|
1147
2032
|
const routes = {
|
|
2033
|
+
settings,
|
|
1148
2034
|
release,
|
|
1149
2035
|
"release-action": releaseAction
|
|
1150
2036
|
};
|
|
1151
|
-
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
1152
2037
|
const getPlugin = () => {
|
|
1153
|
-
if (features.isEnabled("cms-content-releases")) {
|
|
2038
|
+
if (strapi.ee.features.isEnabled("cms-content-releases")) {
|
|
1154
2039
|
return {
|
|
1155
2040
|
register,
|
|
1156
2041
|
bootstrap,
|
|
2042
|
+
destroy,
|
|
1157
2043
|
contentTypes,
|
|
1158
2044
|
services,
|
|
1159
2045
|
controllers,
|
|
@@ -1161,6 +2047,9 @@ const getPlugin = () => {
|
|
|
1161
2047
|
};
|
|
1162
2048
|
}
|
|
1163
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
|
|
1164
2053
|
contentTypes
|
|
1165
2054
|
};
|
|
1166
2055
|
};
|