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