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