@strapi/content-releases 0.0.0-next.4af8963f6880c5fb9fae32ecd580f5cd33eaddda → 0.0.0-next.4cf36024d8e6011c265fdc01924d063589432f8f
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-YhAPgpG9.js → PurchaseContentReleases-Be3acS2L.js} +8 -7
- package/dist/_chunks/PurchaseContentReleases-Be3acS2L.js.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-_MxP6-Dt.mjs} +9 -8
- 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-7P4i1cWH.js → en-CmYoEnA7.js} +25 -6
- package/dist/_chunks/en-CmYoEnA7.js.map +1 -0
- package/dist/_chunks/{en-pb1wUzhy.mjs → en-D0yVZFqf.mjs} +25 -6
- 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 +1167 -518
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1167 -518
- 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 -39
- package/dist/_chunks/App-ise7GunC.mjs +0 -1273
- package/dist/_chunks/App-ise7GunC.mjs.map +0 -1
- package/dist/_chunks/App-w2Zq-wj5.js +0 -1296
- package/dist/_chunks/App-w2Zq-wj5.js.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
- package/dist/_chunks/en-7P4i1cWH.js.map +0 -1
- package/dist/_chunks/en-pb1wUzhy.mjs.map +0 -1
- package/dist/_chunks/index-D-Yjf60c.mjs +0 -950
- package/dist/_chunks/index-D-Yjf60c.mjs.map +0 -1
- package/dist/_chunks/index-Q8Pv7enO.js +0 -971
- package/dist/_chunks/index-Q8Pv7enO.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,11 +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
|
];
|
|
53
70
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
54
71
|
RELEASES_PUBLISH: "releases.publish"
|
|
55
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
|
+
};
|
|
56
128
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
57
129
|
oldContentTypes,
|
|
58
130
|
contentTypes: contentTypes2
|
|
@@ -74,90 +146,353 @@ async function deleteActionsOnDisableDraftAndPublish({
|
|
|
74
146
|
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
75
147
|
const deletedContentTypes = difference(keys(oldContentTypes), keys(contentTypes2)) ?? [];
|
|
76
148
|
if (deletedContentTypes.length) {
|
|
77
|
-
await
|
|
149
|
+
await async.map(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
78
150
|
return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
|
|
79
151
|
});
|
|
80
152
|
}
|
|
81
153
|
}
|
|
82
|
-
|
|
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
|
+
};
|
|
83
338
|
const register = async ({ strapi: strapi2 }) => {
|
|
84
|
-
if (features
|
|
85
|
-
await strapi2.admin
|
|
86
|
-
strapi2.
|
|
87
|
-
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();
|
|
88
349
|
}
|
|
89
350
|
};
|
|
90
|
-
const
|
|
91
|
-
|
|
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
|
+
}
|
|
92
448
|
};
|
|
93
|
-
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
94
449
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
95
|
-
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
|
+
);
|
|
96
454
|
strapi2.db.lifecycles.subscribe({
|
|
97
|
-
|
|
98
|
-
const { model, result } = event;
|
|
99
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
100
|
-
const { id } = result;
|
|
101
|
-
strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
102
|
-
where: {
|
|
103
|
-
target_type: model.uid,
|
|
104
|
-
target_id: id
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
},
|
|
109
|
-
/**
|
|
110
|
-
* deleteMany hook doesn't return the deleted entries ids
|
|
111
|
-
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
112
|
-
*/
|
|
113
|
-
async beforeDeleteMany(event) {
|
|
114
|
-
const { model, params } = event;
|
|
115
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
116
|
-
const { where } = params;
|
|
117
|
-
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
118
|
-
event.state.entriesToDelete = entriesToDelete;
|
|
119
|
-
}
|
|
120
|
-
},
|
|
455
|
+
models: contentTypesWithDraftAndPublish,
|
|
121
456
|
/**
|
|
122
|
-
*
|
|
123
|
-
* 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
|
|
124
458
|
*/
|
|
125
459
|
async afterDeleteMany(event) {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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
|
|
136
473
|
});
|
|
137
474
|
}
|
|
138
475
|
}
|
|
139
476
|
});
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
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
|
+
});
|
|
151
488
|
}
|
|
152
489
|
};
|
|
153
490
|
const destroy = async ({ strapi: strapi2 }) => {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
job.cancel();
|
|
160
|
-
}
|
|
491
|
+
const scheduledJobs = getService("scheduling", {
|
|
492
|
+
strapi: strapi2
|
|
493
|
+
}).getAll();
|
|
494
|
+
for (const [, job] of scheduledJobs) {
|
|
495
|
+
job.cancel();
|
|
161
496
|
}
|
|
162
497
|
};
|
|
163
498
|
const schema$1 = {
|
|
@@ -192,6 +527,11 @@ const schema$1 = {
|
|
|
192
527
|
timezone: {
|
|
193
528
|
type: "string"
|
|
194
529
|
},
|
|
530
|
+
status: {
|
|
531
|
+
type: "enumeration",
|
|
532
|
+
enum: ["ready", "blocked", "failed", "done", "empty"],
|
|
533
|
+
required: true
|
|
534
|
+
},
|
|
195
535
|
actions: {
|
|
196
536
|
type: "relation",
|
|
197
537
|
relation: "oneToMany",
|
|
@@ -227,15 +567,13 @@ const schema = {
|
|
|
227
567
|
enum: ["publish", "unpublish"],
|
|
228
568
|
required: true
|
|
229
569
|
},
|
|
230
|
-
entry: {
|
|
231
|
-
type: "relation",
|
|
232
|
-
relation: "morphToOne",
|
|
233
|
-
configurable: false
|
|
234
|
-
},
|
|
235
570
|
contentType: {
|
|
236
571
|
type: "string",
|
|
237
572
|
required: true
|
|
238
573
|
},
|
|
574
|
+
entryDocumentId: {
|
|
575
|
+
type: "string"
|
|
576
|
+
},
|
|
239
577
|
locale: {
|
|
240
578
|
type: "string"
|
|
241
579
|
},
|
|
@@ -244,6 +582,9 @@ const schema = {
|
|
|
244
582
|
relation: "manyToOne",
|
|
245
583
|
target: RELEASE_MODEL_UID,
|
|
246
584
|
inversedBy: "actions"
|
|
585
|
+
},
|
|
586
|
+
isEntryValid: {
|
|
587
|
+
type: "boolean"
|
|
247
588
|
}
|
|
248
589
|
}
|
|
249
590
|
};
|
|
@@ -254,18 +595,6 @@ const contentTypes = {
|
|
|
254
595
|
release: release$1,
|
|
255
596
|
"release-action": releaseAction$1
|
|
256
597
|
};
|
|
257
|
-
const getGroupName = (queryValue) => {
|
|
258
|
-
switch (queryValue) {
|
|
259
|
-
case "contentType":
|
|
260
|
-
return "contentType.displayName";
|
|
261
|
-
case "action":
|
|
262
|
-
return "type";
|
|
263
|
-
case "locale":
|
|
264
|
-
return _.getOr("No locale", "locale.name");
|
|
265
|
-
default:
|
|
266
|
-
return "contentType.displayName";
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
598
|
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
270
599
|
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
271
600
|
strapi2.eventHub.emit(event, {
|
|
@@ -274,6 +603,33 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
274
603
|
release: release2
|
|
275
604
|
});
|
|
276
605
|
};
|
|
606
|
+
const getFormattedActions = async (releaseId) => {
|
|
607
|
+
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
608
|
+
where: {
|
|
609
|
+
release: {
|
|
610
|
+
id: releaseId
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
});
|
|
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: []
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
formattedActions[contentTypeUid][action.type].push({
|
|
627
|
+
documentId: action.entryDocumentId,
|
|
628
|
+
locale: action.locale
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return formattedActions;
|
|
632
|
+
};
|
|
277
633
|
return {
|
|
278
634
|
async create(releaseData, { user }) {
|
|
279
635
|
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
|
@@ -287,10 +643,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
287
643
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
288
644
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
289
645
|
]);
|
|
290
|
-
const release2 = await strapi2.
|
|
291
|
-
data:
|
|
646
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
|
|
647
|
+
data: {
|
|
648
|
+
...releaseWithCreatorFields,
|
|
649
|
+
status: "empty"
|
|
650
|
+
}
|
|
292
651
|
});
|
|
293
|
-
if (
|
|
652
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
294
653
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
295
654
|
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
296
655
|
}
|
|
@@ -298,94 +657,28 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
298
657
|
return release2;
|
|
299
658
|
},
|
|
300
659
|
async findOne(id, query = {}) {
|
|
301
|
-
const
|
|
302
|
-
|
|
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 }
|
|
303
664
|
});
|
|
304
665
|
return release2;
|
|
305
666
|
},
|
|
306
667
|
findPage(query) {
|
|
307
|
-
|
|
308
|
-
|
|
668
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
669
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findPage({
|
|
670
|
+
...dbQuery,
|
|
309
671
|
populate: {
|
|
310
672
|
actions: {
|
|
311
|
-
// @ts-expect-error Ignore missing properties
|
|
312
673
|
count: true
|
|
313
674
|
}
|
|
314
675
|
}
|
|
315
676
|
});
|
|
316
677
|
},
|
|
317
|
-
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
target_type: contentTypeUid,
|
|
322
|
-
target_id: entryId
|
|
323
|
-
},
|
|
324
|
-
releasedAt: {
|
|
325
|
-
$null: true
|
|
326
|
-
}
|
|
327
|
-
},
|
|
328
|
-
populate: {
|
|
329
|
-
// Filter the action to get only the content type entry
|
|
330
|
-
actions: {
|
|
331
|
-
where: {
|
|
332
|
-
target_type: contentTypeUid,
|
|
333
|
-
target_id: entryId
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
});
|
|
338
|
-
return releases.map((release2) => {
|
|
339
|
-
if (release2.actions?.length) {
|
|
340
|
-
const [actionForEntry] = release2.actions;
|
|
341
|
-
delete release2.actions;
|
|
342
|
-
return {
|
|
343
|
-
...release2,
|
|
344
|
-
action: actionForEntry
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
return release2;
|
|
348
|
-
});
|
|
349
|
-
},
|
|
350
|
-
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
351
|
-
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
352
|
-
where: {
|
|
353
|
-
releasedAt: {
|
|
354
|
-
$null: true
|
|
355
|
-
},
|
|
356
|
-
actions: {
|
|
357
|
-
target_type: contentTypeUid,
|
|
358
|
-
target_id: entryId
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
});
|
|
362
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
363
|
-
where: {
|
|
364
|
-
$or: [
|
|
365
|
-
{
|
|
366
|
-
id: {
|
|
367
|
-
$notIn: releasesRelated.map((release2) => release2.id)
|
|
368
|
-
}
|
|
369
|
-
},
|
|
370
|
-
{
|
|
371
|
-
actions: null
|
|
372
|
-
}
|
|
373
|
-
],
|
|
374
|
-
releasedAt: {
|
|
375
|
-
$null: true
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
});
|
|
379
|
-
return releases.map((release2) => {
|
|
380
|
-
if (release2.actions?.length) {
|
|
381
|
-
const [actionForEntry] = release2.actions;
|
|
382
|
-
delete release2.actions;
|
|
383
|
-
return {
|
|
384
|
-
...release2,
|
|
385
|
-
action: actionForEntry
|
|
386
|
-
};
|
|
387
|
-
}
|
|
388
|
-
return release2;
|
|
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
|
|
389
682
|
});
|
|
390
683
|
},
|
|
391
684
|
async update(id, releaseData, { user }) {
|
|
@@ -400,151 +693,27 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
400
693
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
401
694
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
402
695
|
]);
|
|
403
|
-
const release2 = await strapi2.
|
|
696
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
|
|
404
697
|
if (!release2) {
|
|
405
698
|
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
406
699
|
}
|
|
407
700
|
if (release2.releasedAt) {
|
|
408
701
|
throw new errors.ValidationError("Release already published");
|
|
409
702
|
}
|
|
410
|
-
const updatedRelease = await strapi2.
|
|
411
|
-
|
|
412
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
413
|
-
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
414
|
-
*/
|
|
415
|
-
// @ts-expect-error see above
|
|
703
|
+
const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
704
|
+
where: { id },
|
|
416
705
|
data: releaseWithCreatorFields
|
|
417
706
|
});
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
schedulingService.cancel(id);
|
|
424
|
-
}
|
|
707
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
708
|
+
if (releaseData.scheduledAt) {
|
|
709
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
710
|
+
} else if (release2.scheduledAt) {
|
|
711
|
+
schedulingService.cancel(id);
|
|
425
712
|
}
|
|
713
|
+
this.updateReleaseStatus(id);
|
|
426
714
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
427
715
|
return updatedRelease;
|
|
428
716
|
},
|
|
429
|
-
async createAction(releaseId, action) {
|
|
430
|
-
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
431
|
-
strapi: strapi2
|
|
432
|
-
});
|
|
433
|
-
await Promise.all([
|
|
434
|
-
validateEntryContentType(action.entry.contentType),
|
|
435
|
-
validateUniqueEntry(releaseId, action)
|
|
436
|
-
]);
|
|
437
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
438
|
-
if (!release2) {
|
|
439
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
440
|
-
}
|
|
441
|
-
if (release2.releasedAt) {
|
|
442
|
-
throw new errors.ValidationError("Release already published");
|
|
443
|
-
}
|
|
444
|
-
const { entry, type } = action;
|
|
445
|
-
return strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
446
|
-
data: {
|
|
447
|
-
type,
|
|
448
|
-
contentType: entry.contentType,
|
|
449
|
-
locale: entry.locale,
|
|
450
|
-
entry: {
|
|
451
|
-
id: entry.id,
|
|
452
|
-
__type: entry.contentType,
|
|
453
|
-
__pivot: { field: "entry" }
|
|
454
|
-
},
|
|
455
|
-
release: releaseId
|
|
456
|
-
},
|
|
457
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
458
|
-
});
|
|
459
|
-
},
|
|
460
|
-
async findActions(releaseId, query) {
|
|
461
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
462
|
-
fields: ["id"]
|
|
463
|
-
});
|
|
464
|
-
if (!release2) {
|
|
465
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
466
|
-
}
|
|
467
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
468
|
-
...query,
|
|
469
|
-
populate: {
|
|
470
|
-
entry: {
|
|
471
|
-
populate: "*"
|
|
472
|
-
}
|
|
473
|
-
},
|
|
474
|
-
filters: {
|
|
475
|
-
release: releaseId
|
|
476
|
-
}
|
|
477
|
-
});
|
|
478
|
-
},
|
|
479
|
-
async countActions(query) {
|
|
480
|
-
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
481
|
-
},
|
|
482
|
-
async groupActions(actions, groupBy) {
|
|
483
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
484
|
-
if (!acc.includes(action.contentType)) {
|
|
485
|
-
acc.push(action.contentType);
|
|
486
|
-
}
|
|
487
|
-
return acc;
|
|
488
|
-
}, []);
|
|
489
|
-
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
490
|
-
contentTypeUids
|
|
491
|
-
);
|
|
492
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
493
|
-
const formattedData = actions.map((action) => {
|
|
494
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
495
|
-
return {
|
|
496
|
-
...action,
|
|
497
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
498
|
-
contentType: {
|
|
499
|
-
displayName,
|
|
500
|
-
mainFieldValue: action.entry[mainField],
|
|
501
|
-
uid: action.contentType
|
|
502
|
-
}
|
|
503
|
-
};
|
|
504
|
-
});
|
|
505
|
-
const groupName = getGroupName(groupBy);
|
|
506
|
-
return _.groupBy(groupName)(formattedData);
|
|
507
|
-
},
|
|
508
|
-
async getLocalesDataForActions() {
|
|
509
|
-
if (!strapi2.plugin("i18n")) {
|
|
510
|
-
return {};
|
|
511
|
-
}
|
|
512
|
-
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
513
|
-
return allLocales.reduce((acc, locale) => {
|
|
514
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
515
|
-
return acc;
|
|
516
|
-
}, {});
|
|
517
|
-
},
|
|
518
|
-
async getContentTypesDataForActions(contentTypesUids) {
|
|
519
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
520
|
-
const contentTypesData = {};
|
|
521
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
522
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
523
|
-
uid: contentTypeUid
|
|
524
|
-
});
|
|
525
|
-
contentTypesData[contentTypeUid] = {
|
|
526
|
-
mainField: contentTypeConfig.settings.mainField,
|
|
527
|
-
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
528
|
-
};
|
|
529
|
-
}
|
|
530
|
-
return contentTypesData;
|
|
531
|
-
},
|
|
532
|
-
getContentTypeModelsFromActions(actions) {
|
|
533
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
534
|
-
if (!acc.includes(action.contentType)) {
|
|
535
|
-
acc.push(action.contentType);
|
|
536
|
-
}
|
|
537
|
-
return acc;
|
|
538
|
-
}, []);
|
|
539
|
-
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
540
|
-
(acc, contentTypeUid) => {
|
|
541
|
-
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
542
|
-
return acc;
|
|
543
|
-
},
|
|
544
|
-
{}
|
|
545
|
-
);
|
|
546
|
-
return contentTypeModelsMap;
|
|
547
|
-
},
|
|
548
717
|
async getAllComponents() {
|
|
549
718
|
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
550
719
|
const components = await contentManagerComponentsService.findAllComponents();
|
|
@@ -558,10 +727,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
558
727
|
return componentsMap;
|
|
559
728
|
},
|
|
560
729
|
async delete(releaseId) {
|
|
561
|
-
const release2 = await strapi2.
|
|
730
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
731
|
+
where: { id: releaseId },
|
|
562
732
|
populate: {
|
|
563
733
|
actions: {
|
|
564
|
-
|
|
734
|
+
select: ["id"]
|
|
565
735
|
}
|
|
566
736
|
}
|
|
567
737
|
});
|
|
@@ -579,9 +749,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
579
749
|
}
|
|
580
750
|
}
|
|
581
751
|
});
|
|
582
|
-
await strapi2.
|
|
752
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
753
|
+
where: {
|
|
754
|
+
id: releaseId
|
|
755
|
+
}
|
|
756
|
+
});
|
|
583
757
|
});
|
|
584
|
-
if (
|
|
758
|
+
if (release2.scheduledAt) {
|
|
585
759
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
586
760
|
await schedulingService.cancel(release2.id);
|
|
587
761
|
}
|
|
@@ -589,141 +763,309 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
589
763
|
return release2;
|
|
590
764
|
},
|
|
591
765
|
async publish(releaseId) {
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
actions: {
|
|
599
|
-
populate: {
|
|
600
|
-
entry: {
|
|
601
|
-
fields: ["id"]
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
);
|
|
608
|
-
if (!releaseWithPopulatedActionEntries) {
|
|
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) {
|
|
609
772
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
610
773
|
}
|
|
611
|
-
if (
|
|
774
|
+
if (lockedRelease.releasedAt) {
|
|
612
775
|
throw new errors.ValidationError("Release already published");
|
|
613
776
|
}
|
|
614
|
-
if (
|
|
615
|
-
throw new errors.ValidationError("
|
|
777
|
+
if (lockedRelease.status === "failed") {
|
|
778
|
+
throw new errors.ValidationError("Release failed to publish");
|
|
616
779
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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()
|
|
627
802
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
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"
|
|
632
852
|
}
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
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 () => {
|
|
889
|
+
if (!strapi2.plugin("i18n")) {
|
|
890
|
+
return {};
|
|
891
|
+
}
|
|
892
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
893
|
+
return allLocales.reduce((acc, locale) => {
|
|
894
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
895
|
+
return acc;
|
|
896
|
+
}, {});
|
|
897
|
+
};
|
|
898
|
+
const getContentTypesDataForActions = async (contentTypesUids) => {
|
|
899
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
900
|
+
const contentTypesData = {};
|
|
901
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
902
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
903
|
+
uid: contentTypeUid
|
|
904
|
+
});
|
|
905
|
+
contentTypesData[contentTypeUid] = {
|
|
906
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
907
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
return contentTypesData;
|
|
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}`);
|
|
926
|
+
}
|
|
927
|
+
action.entryDocumentId = document.documentId;
|
|
928
|
+
}
|
|
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
|
|
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"] } }
|
|
953
|
+
});
|
|
954
|
+
if (!disableUpdateReleaseStatus) {
|
|
955
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
956
|
+
}
|
|
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}`);
|
|
966
|
+
}
|
|
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
|
|
972
|
+
}
|
|
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(
|
|
978
|
+
{
|
|
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 }
|
|
986
|
+
);
|
|
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
|
|
639
1016
|
}
|
|
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);
|
|
640
1026
|
}
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
contentTypeUid,
|
|
665
|
-
{
|
|
666
|
-
filters: {
|
|
667
|
-
id: {
|
|
668
|
-
$in: entriestoPublishIds
|
|
669
|
-
}
|
|
670
|
-
},
|
|
671
|
-
populate
|
|
672
|
-
}
|
|
673
|
-
);
|
|
674
|
-
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
675
|
-
contentTypeUid,
|
|
676
|
-
{
|
|
677
|
-
filters: {
|
|
678
|
-
id: {
|
|
679
|
-
$in: entriesToUnpublishIds
|
|
680
|
-
}
|
|
681
|
-
},
|
|
682
|
-
populate
|
|
683
|
-
}
|
|
684
|
-
);
|
|
685
|
-
if (entriesToPublish.length > 0) {
|
|
686
|
-
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
687
|
-
}
|
|
688
|
-
if (entriesToUnpublish.length > 0) {
|
|
689
|
-
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
690
|
-
}
|
|
691
|
-
}
|
|
692
|
-
});
|
|
693
|
-
const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, releaseId, {
|
|
694
|
-
data: {
|
|
695
|
-
/*
|
|
696
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
|
|
697
|
-
*/
|
|
698
|
-
// @ts-expect-error see above
|
|
699
|
-
releasedAt: /* @__PURE__ */ new Date()
|
|
700
|
-
},
|
|
701
|
-
populate: {
|
|
702
|
-
actions: {
|
|
703
|
-
// @ts-expect-error is not expecting count but it is working
|
|
704
|
-
count: true
|
|
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
|
|
705
1050
|
}
|
|
706
1051
|
}
|
|
707
|
-
});
|
|
708
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
709
|
-
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
710
|
-
isPublished: true,
|
|
711
|
-
release: release2
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
strapi2.telemetry.send("didPublishContentRelease");
|
|
715
|
-
return release2;
|
|
716
|
-
} catch (error) {
|
|
717
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
718
|
-
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
719
|
-
isPublished: false,
|
|
720
|
-
error
|
|
721
|
-
});
|
|
722
1052
|
}
|
|
723
|
-
|
|
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
|
+
);
|
|
724
1058
|
}
|
|
725
|
-
|
|
726
|
-
|
|
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;
|
|
727
1069
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
728
1070
|
where: {
|
|
729
1071
|
id: actionId,
|
|
@@ -734,16 +1076,15 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
734
1076
|
}
|
|
735
1077
|
}
|
|
736
1078
|
},
|
|
737
|
-
data:
|
|
1079
|
+
data: {
|
|
1080
|
+
...update,
|
|
1081
|
+
isEntryValid: actionStatus
|
|
1082
|
+
}
|
|
738
1083
|
});
|
|
739
|
-
|
|
740
|
-
throw new errors.NotFoundError(
|
|
741
|
-
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
742
|
-
);
|
|
743
|
-
}
|
|
1084
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
744
1085
|
return updatedAction;
|
|
745
1086
|
},
|
|
746
|
-
async
|
|
1087
|
+
async delete(actionId, releaseId) {
|
|
747
1088
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
748
1089
|
where: {
|
|
749
1090
|
id: actionId,
|
|
@@ -760,43 +1101,56 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
760
1101
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
761
1102
|
);
|
|
762
1103
|
}
|
|
1104
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
763
1105
|
return deletedAction;
|
|
764
1106
|
}
|
|
765
1107
|
};
|
|
766
1108
|
};
|
|
1109
|
+
class AlreadyOnReleaseError extends errors.ApplicationError {
|
|
1110
|
+
constructor(message) {
|
|
1111
|
+
super(message);
|
|
1112
|
+
this.name = "AlreadyOnReleaseError";
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
767
1115
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
768
1116
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
769
|
-
const release2 = await strapi2.
|
|
770
|
-
|
|
1117
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1118
|
+
where: {
|
|
1119
|
+
id: releaseId
|
|
1120
|
+
},
|
|
1121
|
+
populate: {
|
|
1122
|
+
actions: true
|
|
1123
|
+
}
|
|
771
1124
|
});
|
|
772
1125
|
if (!release2) {
|
|
773
1126
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
774
1127
|
}
|
|
775
1128
|
const isEntryInRelease = release2.actions.some(
|
|
776
|
-
(action) =>
|
|
1129
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
777
1130
|
);
|
|
778
1131
|
if (isEntryInRelease) {
|
|
779
|
-
throw new
|
|
780
|
-
`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}`
|
|
781
1134
|
);
|
|
782
1135
|
}
|
|
783
1136
|
},
|
|
784
|
-
|
|
1137
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
785
1138
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
786
1139
|
if (!contentType) {
|
|
787
1140
|
throw new errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
788
1141
|
}
|
|
789
|
-
if (!contentType
|
|
1142
|
+
if (!contentTypes$1.hasDraftAndPublish(contentType)) {
|
|
790
1143
|
throw new errors.ValidationError(
|
|
791
1144
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
792
1145
|
);
|
|
793
1146
|
}
|
|
1147
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1148
|
+
throw new errors.ValidationError("Document id is required for collection type");
|
|
1149
|
+
}
|
|
794
1150
|
},
|
|
795
1151
|
async validatePendingReleasesLimit() {
|
|
796
|
-
const
|
|
797
|
-
|
|
798
|
-
EE.features.get("cms-content-releases")?.options?.maximumReleases || 3
|
|
799
|
-
);
|
|
1152
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1153
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
800
1154
|
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
801
1155
|
filters: {
|
|
802
1156
|
releasedAt: {
|
|
@@ -809,8 +1163,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
809
1163
|
}
|
|
810
1164
|
},
|
|
811
1165
|
async validateUniqueNameForPendingRelease(name, id) {
|
|
812
|
-
const pendingReleases = await strapi2.
|
|
813
|
-
|
|
1166
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1167
|
+
where: {
|
|
814
1168
|
releasedAt: {
|
|
815
1169
|
$null: true
|
|
816
1170
|
},
|
|
@@ -839,7 +1193,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
839
1193
|
}
|
|
840
1194
|
const job = scheduleJob(scheduleDate, async () => {
|
|
841
1195
|
try {
|
|
842
|
-
await getService("release").publish(releaseId);
|
|
1196
|
+
await getService("release", { strapi: strapi2 }).publish(releaseId);
|
|
843
1197
|
} catch (error) {
|
|
844
1198
|
}
|
|
845
1199
|
this.cancel(releaseId);
|
|
@@ -881,80 +1235,172 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
881
1235
|
}
|
|
882
1236
|
};
|
|
883
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
|
+
};
|
|
884
1259
|
const services = {
|
|
885
1260
|
release: createReleaseService,
|
|
1261
|
+
"release-action": createReleaseActionService,
|
|
886
1262
|
"release-validation": createReleaseValidationService,
|
|
887
|
-
|
|
1263
|
+
scheduling: createSchedulingService,
|
|
1264
|
+
settings: createSettingsService
|
|
888
1265
|
};
|
|
889
|
-
const RELEASE_SCHEMA = yup.object().shape({
|
|
890
|
-
name: yup.string().trim().required(),
|
|
891
|
-
scheduledAt: yup.string().nullable(),
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
otherwise: yup.string().nullable()
|
|
897
|
-
}),
|
|
898
|
-
timezone: yup.string().when("isScheduled", {
|
|
899
|
-
is: true,
|
|
900
|
-
then: yup.string().required().nullable(),
|
|
901
|
-
otherwise: yup.string().nullable()
|
|
902
|
-
}),
|
|
903
|
-
date: yup.string().when("isScheduled", {
|
|
904
|
-
is: true,
|
|
905
|
-
then: yup.string().required().nullable(),
|
|
906
|
-
otherwise: yup.string().nullable()
|
|
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()
|
|
907
1273
|
})
|
|
908
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()
|
|
1280
|
+
}).required().noUnknown();
|
|
909
1281
|
const validateRelease = validateYupSchema(RELEASE_SCHEMA);
|
|
1282
|
+
const validatefindByDocumentAttachedParams = validateYupSchema(
|
|
1283
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1284
|
+
);
|
|
910
1285
|
const releaseController = {
|
|
911
|
-
|
|
912
|
-
|
|
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({
|
|
913
1293
|
ability: ctx.state.userAbility,
|
|
914
1294
|
model: RELEASE_MODEL_UID
|
|
915
1295
|
});
|
|
916
1296
|
await permissionsManager.validateQuery(ctx.query);
|
|
917
1297
|
const releaseService = getService("release", { strapi });
|
|
918
|
-
const
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
const
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
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: {
|
|
933
1321
|
actions: {
|
|
934
|
-
|
|
935
|
-
|
|
1322
|
+
fields: ["type"],
|
|
1323
|
+
filters: {
|
|
1324
|
+
contentType,
|
|
1325
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1326
|
+
locale: locale ?? null
|
|
936
1327
|
}
|
|
937
1328
|
}
|
|
938
|
-
}
|
|
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
|
+
}
|
|
939
1357
|
});
|
|
940
|
-
ctx.body = { data
|
|
1358
|
+
ctx.body = { data: releases };
|
|
941
1359
|
}
|
|
942
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
|
+
},
|
|
943
1388
|
async findOne(ctx) {
|
|
944
1389
|
const id = ctx.params.id;
|
|
945
1390
|
const releaseService = getService("release", { strapi });
|
|
1391
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
946
1392
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
947
1393
|
if (!release2) {
|
|
948
1394
|
throw new errors.NotFoundError(`Release not found for id: ${id}`);
|
|
949
1395
|
}
|
|
950
|
-
const count = await
|
|
1396
|
+
const count = await releaseActionService.countActions({
|
|
951
1397
|
filters: {
|
|
952
1398
|
release: id
|
|
953
1399
|
}
|
|
954
1400
|
});
|
|
955
1401
|
const sanitizedRelease = {
|
|
956
1402
|
...release2,
|
|
957
|
-
createdBy: release2.createdBy ? strapi.admin
|
|
1403
|
+
createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
|
|
958
1404
|
};
|
|
959
1405
|
const data = {
|
|
960
1406
|
...sanitizedRelease,
|
|
@@ -966,19 +1412,63 @@ const releaseController = {
|
|
|
966
1412
|
};
|
|
967
1413
|
ctx.body = { data };
|
|
968
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
|
+
},
|
|
969
1459
|
async create(ctx) {
|
|
970
1460
|
const user = ctx.state.user;
|
|
971
1461
|
const releaseArgs = ctx.request.body;
|
|
972
1462
|
await validateRelease(releaseArgs);
|
|
973
1463
|
const releaseService = getService("release", { strapi });
|
|
974
1464
|
const release2 = await releaseService.create(releaseArgs, { user });
|
|
975
|
-
const permissionsManager = strapi.admin
|
|
1465
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
976
1466
|
ability: ctx.state.userAbility,
|
|
977
1467
|
model: RELEASE_MODEL_UID
|
|
978
1468
|
});
|
|
979
|
-
ctx.
|
|
1469
|
+
ctx.created({
|
|
980
1470
|
data: await permissionsManager.sanitizeOutput(release2)
|
|
981
|
-
};
|
|
1471
|
+
});
|
|
982
1472
|
},
|
|
983
1473
|
async update(ctx) {
|
|
984
1474
|
const user = ctx.state.user;
|
|
@@ -987,7 +1477,7 @@ const releaseController = {
|
|
|
987
1477
|
await validateRelease(releaseArgs);
|
|
988
1478
|
const releaseService = getService("release", { strapi });
|
|
989
1479
|
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
990
|
-
const permissionsManager = strapi.admin
|
|
1480
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
991
1481
|
ability: ctx.state.userAbility,
|
|
992
1482
|
model: RELEASE_MODEL_UID
|
|
993
1483
|
});
|
|
@@ -1004,18 +1494,18 @@ const releaseController = {
|
|
|
1004
1494
|
};
|
|
1005
1495
|
},
|
|
1006
1496
|
async publish(ctx) {
|
|
1007
|
-
const user = ctx.state.user;
|
|
1008
1497
|
const id = ctx.params.id;
|
|
1009
1498
|
const releaseService = getService("release", { strapi });
|
|
1010
|
-
const
|
|
1499
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1500
|
+
const release2 = await releaseService.publish(id);
|
|
1011
1501
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1012
|
-
|
|
1502
|
+
releaseActionService.countActions({
|
|
1013
1503
|
filters: {
|
|
1014
1504
|
release: id,
|
|
1015
1505
|
type: "publish"
|
|
1016
1506
|
}
|
|
1017
1507
|
}),
|
|
1018
|
-
|
|
1508
|
+
releaseActionService.countActions({
|
|
1019
1509
|
filters: {
|
|
1020
1510
|
release: id,
|
|
1021
1511
|
type: "unpublish"
|
|
@@ -1033,57 +1523,106 @@ const releaseController = {
|
|
|
1033
1523
|
}
|
|
1034
1524
|
};
|
|
1035
1525
|
const RELEASE_ACTION_SCHEMA = yup$1.object().shape({
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
}).required(),
|
|
1526
|
+
contentType: yup$1.string().required(),
|
|
1527
|
+
entryDocumentId: yup$1.strapiID(),
|
|
1528
|
+
locale: yup$1.string(),
|
|
1040
1529
|
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1041
1530
|
});
|
|
1042
1531
|
const RELEASE_ACTION_UPDATE_SCHEMA = yup$1.object().shape({
|
|
1043
1532
|
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1044
1533
|
});
|
|
1534
|
+
const FIND_MANY_ACTIONS_PARAMS = yup$1.object().shape({
|
|
1535
|
+
groupBy: yup$1.string().oneOf(["action", "contentType", "locale"])
|
|
1536
|
+
});
|
|
1045
1537
|
const validateReleaseAction = validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1046
1538
|
const validateReleaseActionUpdateSchema = validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1539
|
+
const validateFindManyActionsParams = validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1047
1540
|
const releaseActionController = {
|
|
1048
1541
|
async create(ctx) {
|
|
1049
1542
|
const releaseId = ctx.params.releaseId;
|
|
1050
1543
|
const releaseActionArgs = ctx.request.body;
|
|
1051
1544
|
await validateReleaseAction(releaseActionArgs);
|
|
1052
|
-
const
|
|
1053
|
-
const releaseAction2 = await
|
|
1054
|
-
ctx.
|
|
1545
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1546
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1547
|
+
ctx.created({
|
|
1055
1548
|
data: releaseAction2
|
|
1056
|
-
};
|
|
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
|
+
});
|
|
1057
1588
|
},
|
|
1058
1589
|
async findMany(ctx) {
|
|
1059
1590
|
const releaseId = ctx.params.releaseId;
|
|
1060
|
-
const permissionsManager = strapi.admin
|
|
1591
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1061
1592
|
ability: ctx.state.userAbility,
|
|
1062
1593
|
model: RELEASE_ACTION_MODEL_UID
|
|
1063
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;
|
|
1064
1603
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1065
|
-
const
|
|
1066
|
-
const { results, pagination } = await
|
|
1067
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1604
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1605
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1068
1606
|
...query
|
|
1069
1607
|
});
|
|
1070
1608
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
1071
1609
|
if (acc[action.contentType]) {
|
|
1072
1610
|
return acc;
|
|
1073
1611
|
}
|
|
1074
|
-
const contentTypePermissionsManager = strapi.admin
|
|
1612
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1075
1613
|
ability: ctx.state.userAbility,
|
|
1076
1614
|
model: action.contentType
|
|
1077
1615
|
});
|
|
1078
1616
|
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
1079
1617
|
return acc;
|
|
1080
1618
|
}, {});
|
|
1081
|
-
const sanitizedResults = await
|
|
1619
|
+
const sanitizedResults = await async.map(results, async (action) => ({
|
|
1082
1620
|
...action,
|
|
1083
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1621
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1084
1622
|
}));
|
|
1085
|
-
const groupedData = await
|
|
1086
|
-
const contentTypes2 =
|
|
1623
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1624
|
+
const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
|
|
1625
|
+
const releaseService = getService("release", { strapi });
|
|
1087
1626
|
const components = await releaseService.getAllComponents();
|
|
1088
1627
|
ctx.body = {
|
|
1089
1628
|
data: groupedData,
|
|
@@ -1099,8 +1638,8 @@ const releaseActionController = {
|
|
|
1099
1638
|
const releaseId = ctx.params.releaseId;
|
|
1100
1639
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1101
1640
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1102
|
-
const
|
|
1103
|
-
const updatedAction = await
|
|
1641
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1642
|
+
const updatedAction = await releaseActionService.update(
|
|
1104
1643
|
actionId,
|
|
1105
1644
|
releaseId,
|
|
1106
1645
|
releaseActionUpdateArgs
|
|
@@ -1112,17 +1651,71 @@ const releaseActionController = {
|
|
|
1112
1651
|
async delete(ctx) {
|
|
1113
1652
|
const actionId = ctx.params.actionId;
|
|
1114
1653
|
const releaseId = ctx.params.releaseId;
|
|
1115
|
-
const
|
|
1116
|
-
const deletedReleaseAction = await
|
|
1654
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1655
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1117
1656
|
ctx.body = {
|
|
1118
1657
|
data: deletedReleaseAction
|
|
1119
1658
|
};
|
|
1120
1659
|
}
|
|
1121
1660
|
};
|
|
1122
|
-
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
|
+
};
|
|
1123
1684
|
const release = {
|
|
1124
1685
|
type: "admin",
|
|
1125
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
|
+
},
|
|
1126
1719
|
{
|
|
1127
1720
|
method: "POST",
|
|
1128
1721
|
path: "/",
|
|
@@ -1142,7 +1735,7 @@ const release = {
|
|
|
1142
1735
|
{
|
|
1143
1736
|
method: "GET",
|
|
1144
1737
|
path: "/",
|
|
1145
|
-
handler: "release.
|
|
1738
|
+
handler: "release.findPage",
|
|
1146
1739
|
config: {
|
|
1147
1740
|
policies: [
|
|
1148
1741
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1240,6 +1833,22 @@ const releaseAction = {
|
|
|
1240
1833
|
]
|
|
1241
1834
|
}
|
|
1242
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
|
+
},
|
|
1243
1852
|
{
|
|
1244
1853
|
method: "GET",
|
|
1245
1854
|
path: "/:releaseId/actions",
|
|
@@ -1290,13 +1899,50 @@ const releaseAction = {
|
|
|
1290
1899
|
}
|
|
1291
1900
|
]
|
|
1292
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
|
+
};
|
|
1293
1939
|
const routes = {
|
|
1940
|
+
settings,
|
|
1294
1941
|
release,
|
|
1295
1942
|
"release-action": releaseAction
|
|
1296
1943
|
};
|
|
1297
|
-
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
1298
1944
|
const getPlugin = () => {
|
|
1299
|
-
if (features.isEnabled("cms-content-releases")) {
|
|
1945
|
+
if (strapi.ee.features.isEnabled("cms-content-releases")) {
|
|
1300
1946
|
return {
|
|
1301
1947
|
register,
|
|
1302
1948
|
bootstrap,
|
|
@@ -1308,6 +1954,9 @@ const getPlugin = () => {
|
|
|
1308
1954
|
};
|
|
1309
1955
|
}
|
|
1310
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
|
|
1311
1960
|
contentTypes
|
|
1312
1961
|
};
|
|
1313
1962
|
};
|