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