@strapi/content-releases 0.0.0-experimental.fc1ac2acd58c8a5a858679956b6d102ac5ee4011 → 0.0.0-experimental.fd379e4937e431407d784eaa5fe7f93cf2a53386
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 +1057 -646
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1058 -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,242 @@ 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 hasTable = await trx.schema.hasTable("strapi_release_actions");
|
|
310
|
+
if (!hasTable) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
const hasPolymorphicColumn = await trx.schema.hasColumn("strapi_release_actions", "target_id");
|
|
314
|
+
if (hasPolymorphicColumn) {
|
|
315
|
+
const hasEntryDocumentIdColumn = await trx.schema.hasColumn(
|
|
316
|
+
"strapi_release_actions",
|
|
317
|
+
"entry_document_id"
|
|
318
|
+
);
|
|
319
|
+
if (!hasEntryDocumentIdColumn) {
|
|
320
|
+
await trx.schema.alterTable("strapi_release_actions", (table) => {
|
|
321
|
+
table.string("entry_document_id");
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
const releaseActions = await trx.select("*").from("strapi_release_actions");
|
|
325
|
+
async.map(releaseActions, async (action) => {
|
|
326
|
+
const { target_type, target_id } = action;
|
|
327
|
+
const entry = await db.query(target_type).findOne({ where: { id: target_id } });
|
|
328
|
+
if (entry) {
|
|
329
|
+
await trx("strapi_release_actions").update({ entry_document_id: entry.documentId }).where("id", action.id);
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
},
|
|
334
|
+
async down() {
|
|
335
|
+
throw new Error("not implemented");
|
|
336
|
+
}
|
|
337
|
+
};
|
|
189
338
|
const register = async ({ strapi: strapi2 }) => {
|
|
190
339
|
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
191
|
-
await strapi2.admin
|
|
192
|
-
strapi2.
|
|
340
|
+
await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
|
|
341
|
+
strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
|
|
342
|
+
strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
|
|
343
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
344
|
+
}
|
|
345
|
+
if (strapi2.plugin("graphql")) {
|
|
346
|
+
const graphqlExtensionService = strapi2.plugin("graphql").service("extension");
|
|
347
|
+
graphqlExtensionService.shadowCRUD(RELEASE_MODEL_UID).disable();
|
|
348
|
+
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
|
|
352
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
353
|
+
where: {
|
|
354
|
+
actions: {
|
|
355
|
+
contentType,
|
|
356
|
+
entryDocumentId: entry.documentId,
|
|
357
|
+
locale: entry.locale
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
const entryStatus = await isEntryValid(contentType, entry, { strapi });
|
|
362
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
363
|
+
where: {
|
|
364
|
+
contentType,
|
|
365
|
+
entryDocumentId: entry.documentId,
|
|
366
|
+
locale: entry.locale
|
|
367
|
+
},
|
|
368
|
+
data: {
|
|
369
|
+
isEntryValid: entryStatus
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
for (const release2 of releases) {
|
|
373
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
const deleteActionsAndUpdateReleaseStatus = async (params) => {
|
|
377
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
378
|
+
where: {
|
|
379
|
+
actions: params
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
383
|
+
where: params
|
|
384
|
+
});
|
|
385
|
+
for (const release2 of releases) {
|
|
386
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
const deleteActionsOnDelete = async (ctx, next) => {
|
|
390
|
+
if (ctx.action !== "delete") {
|
|
391
|
+
return next();
|
|
392
|
+
}
|
|
393
|
+
if (!contentTypes$1.hasDraftAndPublish(ctx.contentType)) {
|
|
394
|
+
return next();
|
|
395
|
+
}
|
|
396
|
+
const contentType = ctx.contentType.uid;
|
|
397
|
+
const { documentId, locale } = ctx.params;
|
|
398
|
+
const result = await next();
|
|
399
|
+
if (!result) {
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
deleteActionsAndUpdateReleaseStatus({
|
|
404
|
+
contentType,
|
|
405
|
+
entryDocumentId: documentId,
|
|
406
|
+
...locale !== "*" && { locale }
|
|
407
|
+
});
|
|
408
|
+
} catch (error) {
|
|
409
|
+
strapi.log.error("Error while deleting release actions after delete", {
|
|
410
|
+
error
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return result;
|
|
414
|
+
};
|
|
415
|
+
const updateActionsOnUpdate = async (ctx, next) => {
|
|
416
|
+
if (ctx.action !== "update") {
|
|
417
|
+
return next();
|
|
418
|
+
}
|
|
419
|
+
if (!contentTypes$1.hasDraftAndPublish(ctx.contentType)) {
|
|
420
|
+
return next();
|
|
421
|
+
}
|
|
422
|
+
const contentType = ctx.contentType.uid;
|
|
423
|
+
const result = await next();
|
|
424
|
+
if (!result) {
|
|
425
|
+
return result;
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
updateActionsStatusAndUpdateReleaseStatus(contentType, result);
|
|
429
|
+
} catch (error) {
|
|
430
|
+
strapi.log.error("Error while updating release actions after update", {
|
|
431
|
+
error
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
return result;
|
|
435
|
+
};
|
|
436
|
+
const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
|
|
437
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
438
|
+
where: {
|
|
439
|
+
actions: params
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
443
|
+
where: params
|
|
444
|
+
});
|
|
445
|
+
for (const release2 of releases) {
|
|
446
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
193
447
|
}
|
|
194
448
|
};
|
|
195
449
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
196
450
|
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
197
|
-
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes)
|
|
451
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
452
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
453
|
+
);
|
|
198
454
|
strapi2.db.lifecycles.subscribe({
|
|
199
455
|
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
|
-
/**
|
|
228
|
-
* deleteMany hook doesn't return the deleted entries ids
|
|
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
456
|
/**
|
|
240
|
-
*
|
|
241
|
-
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
457
|
+
* deleteMany is still used outside documents service, for example when deleting a locale
|
|
242
458
|
*/
|
|
243
459
|
async afterDeleteMany(event) {
|
|
244
460
|
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
|
-
}
|
|
461
|
+
const model = strapi2.getModel(event.model.uid);
|
|
462
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
463
|
+
const { where } = event.params;
|
|
464
|
+
deleteReleasesActionsAndUpdateReleaseStatus({
|
|
465
|
+
contentType: model.uid,
|
|
466
|
+
locale: where.locale ?? null,
|
|
467
|
+
...where.documentId && { entryDocumentId: where.documentId }
|
|
267
468
|
});
|
|
268
|
-
for (const release2 of releases) {
|
|
269
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
270
|
-
}
|
|
271
469
|
}
|
|
272
470
|
} catch (error) {
|
|
273
471
|
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
274
472
|
error
|
|
275
473
|
});
|
|
276
474
|
}
|
|
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
475
|
}
|
|
314
476
|
});
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
}
|
|
477
|
+
strapi2.documents.use(deleteActionsOnDelete);
|
|
478
|
+
strapi2.documents.use(updateActionsOnUpdate);
|
|
479
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
480
|
+
strapi2.log.error(
|
|
481
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
482
|
+
);
|
|
483
|
+
throw err;
|
|
484
|
+
});
|
|
485
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
486
|
+
strapi2.get("webhookStore").addAllowedEvent(key, value);
|
|
487
|
+
});
|
|
326
488
|
}
|
|
327
489
|
};
|
|
328
490
|
const destroy = async ({ strapi: strapi2 }) => {
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
job.cancel();
|
|
335
|
-
}
|
|
491
|
+
const scheduledJobs = getService("scheduling", {
|
|
492
|
+
strapi: strapi2
|
|
493
|
+
}).getAll();
|
|
494
|
+
for (const [, job] of scheduledJobs) {
|
|
495
|
+
job.cancel();
|
|
336
496
|
}
|
|
337
497
|
};
|
|
338
498
|
const schema$1 = {
|
|
@@ -407,15 +567,13 @@ const schema = {
|
|
|
407
567
|
enum: ["publish", "unpublish"],
|
|
408
568
|
required: true
|
|
409
569
|
},
|
|
410
|
-
entry: {
|
|
411
|
-
type: "relation",
|
|
412
|
-
relation: "morphToOne",
|
|
413
|
-
configurable: false
|
|
414
|
-
},
|
|
415
570
|
contentType: {
|
|
416
571
|
type: "string",
|
|
417
572
|
required: true
|
|
418
573
|
},
|
|
574
|
+
entryDocumentId: {
|
|
575
|
+
type: "string"
|
|
576
|
+
},
|
|
419
577
|
locale: {
|
|
420
578
|
type: "string"
|
|
421
579
|
},
|
|
@@ -437,18 +595,6 @@ const contentTypes = {
|
|
|
437
595
|
release: release$1,
|
|
438
596
|
"release-action": releaseAction$1
|
|
439
597
|
};
|
|
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
598
|
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
453
599
|
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
454
600
|
strapi2.eventHub.emit(event, {
|
|
@@ -457,6 +603,33 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
457
603
|
release: release2
|
|
458
604
|
});
|
|
459
605
|
};
|
|
606
|
+
const getFormattedActions = async (releaseId) => {
|
|
607
|
+
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
608
|
+
where: {
|
|
609
|
+
release: {
|
|
610
|
+
id: releaseId
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
});
|
|
614
|
+
if (actions.length === 0) {
|
|
615
|
+
throw new errors.ValidationError("No entries to publish");
|
|
616
|
+
}
|
|
617
|
+
const formattedActions = {};
|
|
618
|
+
for (const action of actions) {
|
|
619
|
+
const contentTypeUid = action.contentType;
|
|
620
|
+
if (!formattedActions[contentTypeUid]) {
|
|
621
|
+
formattedActions[contentTypeUid] = {
|
|
622
|
+
publish: [],
|
|
623
|
+
unpublish: []
|
|
624
|
+
};
|
|
625
|
+
}
|
|
626
|
+
formattedActions[contentTypeUid][action.type].push({
|
|
627
|
+
documentId: action.entryDocumentId,
|
|
628
|
+
locale: action.locale
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return formattedActions;
|
|
632
|
+
};
|
|
460
633
|
return {
|
|
461
634
|
async create(releaseData, { user }) {
|
|
462
635
|
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
|
@@ -470,13 +643,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
470
643
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
471
644
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
472
645
|
]);
|
|
473
|
-
const release2 = await strapi2.
|
|
646
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
|
|
474
647
|
data: {
|
|
475
648
|
...releaseWithCreatorFields,
|
|
476
649
|
status: "empty"
|
|
477
650
|
}
|
|
478
651
|
});
|
|
479
|
-
if (
|
|
652
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
480
653
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
481
654
|
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
482
655
|
}
|
|
@@ -484,94 +657,28 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
484
657
|
return release2;
|
|
485
658
|
},
|
|
486
659
|
async findOne(id, query = {}) {
|
|
487
|
-
const
|
|
488
|
-
|
|
660
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query);
|
|
661
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
662
|
+
...dbQuery,
|
|
663
|
+
where: { id }
|
|
489
664
|
});
|
|
490
665
|
return release2;
|
|
491
666
|
},
|
|
492
667
|
findPage(query) {
|
|
493
|
-
|
|
494
|
-
|
|
668
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
669
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findPage({
|
|
670
|
+
...dbQuery,
|
|
495
671
|
populate: {
|
|
496
672
|
actions: {
|
|
497
|
-
// @ts-expect-error Ignore missing properties
|
|
498
673
|
count: true
|
|
499
674
|
}
|
|
500
675
|
}
|
|
501
676
|
});
|
|
502
677
|
},
|
|
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;
|
|
678
|
+
findMany(query) {
|
|
679
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
680
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
681
|
+
...dbQuery
|
|
575
682
|
});
|
|
576
683
|
},
|
|
577
684
|
async update(id, releaseData, { user }) {
|
|
@@ -586,90 +693,307 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
586
693
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
587
694
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
588
695
|
]);
|
|
589
|
-
const release2 = await strapi2.
|
|
696
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
|
|
590
697
|
if (!release2) {
|
|
591
698
|
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
592
699
|
}
|
|
593
700
|
if (release2.releasedAt) {
|
|
594
701
|
throw new errors.ValidationError("Release already published");
|
|
595
702
|
}
|
|
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
|
|
703
|
+
const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
704
|
+
where: { id },
|
|
602
705
|
data: releaseWithCreatorFields
|
|
603
706
|
});
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
schedulingService.cancel(id);
|
|
610
|
-
}
|
|
707
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
708
|
+
if (releaseData.scheduledAt) {
|
|
709
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
710
|
+
} else if (release2.scheduledAt) {
|
|
711
|
+
schedulingService.cancel(id);
|
|
611
712
|
}
|
|
612
713
|
this.updateReleaseStatus(id);
|
|
613
714
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
614
715
|
return updatedRelease;
|
|
615
716
|
},
|
|
616
|
-
async
|
|
617
|
-
const
|
|
717
|
+
async getAllComponents() {
|
|
718
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
719
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
720
|
+
const componentsMap = components.reduce(
|
|
721
|
+
(acc, component) => {
|
|
722
|
+
acc[component.uid] = component;
|
|
723
|
+
return acc;
|
|
724
|
+
},
|
|
725
|
+
{}
|
|
726
|
+
);
|
|
727
|
+
return componentsMap;
|
|
728
|
+
},
|
|
729
|
+
async delete(releaseId) {
|
|
730
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
731
|
+
where: { id: releaseId },
|
|
732
|
+
populate: {
|
|
733
|
+
actions: {
|
|
734
|
+
select: ["id"]
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
});
|
|
738
|
+
if (!release2) {
|
|
739
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
740
|
+
}
|
|
741
|
+
if (release2.releasedAt) {
|
|
742
|
+
throw new errors.ValidationError("Release already published");
|
|
743
|
+
}
|
|
744
|
+
await strapi2.db.transaction(async () => {
|
|
745
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
746
|
+
where: {
|
|
747
|
+
id: {
|
|
748
|
+
$in: release2.actions.map((action) => action.id)
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
});
|
|
752
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
753
|
+
where: {
|
|
754
|
+
id: releaseId
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
});
|
|
758
|
+
if (release2.scheduledAt) {
|
|
759
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
760
|
+
await schedulingService.cancel(release2.id);
|
|
761
|
+
}
|
|
762
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
763
|
+
return release2;
|
|
764
|
+
},
|
|
765
|
+
async publish(releaseId) {
|
|
766
|
+
const {
|
|
767
|
+
release: release2,
|
|
768
|
+
error
|
|
769
|
+
} = await strapi2.db.transaction(async ({ trx }) => {
|
|
770
|
+
const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
|
|
771
|
+
if (!lockedRelease) {
|
|
772
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
773
|
+
}
|
|
774
|
+
if (lockedRelease.releasedAt) {
|
|
775
|
+
throw new errors.ValidationError("Release already published");
|
|
776
|
+
}
|
|
777
|
+
if (lockedRelease.status === "failed") {
|
|
778
|
+
throw new errors.ValidationError("Release failed to publish");
|
|
779
|
+
}
|
|
780
|
+
try {
|
|
781
|
+
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
782
|
+
const formattedActions = await getFormattedActions(releaseId);
|
|
783
|
+
await strapi2.db.transaction(
|
|
784
|
+
async () => Promise.all(
|
|
785
|
+
Object.keys(formattedActions).map(async (contentTypeUid) => {
|
|
786
|
+
const contentType = contentTypeUid;
|
|
787
|
+
const { publish, unpublish } = formattedActions[contentType];
|
|
788
|
+
return Promise.all([
|
|
789
|
+
...publish.map((params) => strapi2.documents(contentType).publish(params)),
|
|
790
|
+
...unpublish.map((params) => strapi2.documents(contentType).unpublish(params))
|
|
791
|
+
]);
|
|
792
|
+
})
|
|
793
|
+
)
|
|
794
|
+
);
|
|
795
|
+
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
796
|
+
where: {
|
|
797
|
+
id: releaseId
|
|
798
|
+
},
|
|
799
|
+
data: {
|
|
800
|
+
status: "done",
|
|
801
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
802
|
+
}
|
|
803
|
+
});
|
|
804
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
805
|
+
isPublished: true,
|
|
806
|
+
release: release22
|
|
807
|
+
});
|
|
808
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
809
|
+
return { release: release22, error: null };
|
|
810
|
+
} catch (error2) {
|
|
811
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
812
|
+
isPublished: false,
|
|
813
|
+
error: error2
|
|
814
|
+
});
|
|
815
|
+
await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
|
|
816
|
+
status: "failed"
|
|
817
|
+
}).transacting(trx).execute();
|
|
818
|
+
return {
|
|
819
|
+
release: null,
|
|
820
|
+
error: error2
|
|
821
|
+
};
|
|
822
|
+
}
|
|
823
|
+
});
|
|
824
|
+
if (error instanceof Error) {
|
|
825
|
+
throw error;
|
|
826
|
+
}
|
|
827
|
+
return release2;
|
|
828
|
+
},
|
|
829
|
+
async updateReleaseStatus(releaseId) {
|
|
830
|
+
const releaseActionService = getService("release-action", { strapi: strapi2 });
|
|
831
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
832
|
+
releaseActionService.countActions({
|
|
833
|
+
filters: {
|
|
834
|
+
release: releaseId
|
|
835
|
+
}
|
|
836
|
+
}),
|
|
837
|
+
releaseActionService.countActions({
|
|
838
|
+
filters: {
|
|
839
|
+
release: releaseId,
|
|
840
|
+
isEntryValid: false
|
|
841
|
+
}
|
|
842
|
+
})
|
|
843
|
+
]);
|
|
844
|
+
if (totalActions > 0) {
|
|
845
|
+
if (invalidActions > 0) {
|
|
846
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
847
|
+
where: {
|
|
848
|
+
id: releaseId
|
|
849
|
+
},
|
|
850
|
+
data: {
|
|
851
|
+
status: "blocked"
|
|
852
|
+
}
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
856
|
+
where: {
|
|
857
|
+
id: releaseId
|
|
858
|
+
},
|
|
859
|
+
data: {
|
|
860
|
+
status: "ready"
|
|
861
|
+
}
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
865
|
+
where: {
|
|
866
|
+
id: releaseId
|
|
867
|
+
},
|
|
868
|
+
data: {
|
|
869
|
+
status: "empty"
|
|
870
|
+
}
|
|
871
|
+
});
|
|
872
|
+
}
|
|
873
|
+
};
|
|
874
|
+
};
|
|
875
|
+
const getGroupName = (queryValue) => {
|
|
876
|
+
switch (queryValue) {
|
|
877
|
+
case "contentType":
|
|
878
|
+
return "contentType.displayName";
|
|
879
|
+
case "type":
|
|
880
|
+
return "type";
|
|
881
|
+
case "locale":
|
|
882
|
+
return _.getOr("No locale", "locale.name");
|
|
883
|
+
default:
|
|
884
|
+
return "contentType.displayName";
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
const createReleaseActionService = ({ strapi: strapi2 }) => {
|
|
888
|
+
const getLocalesDataForActions = async () => {
|
|
889
|
+
if (!strapi2.plugin("i18n")) {
|
|
890
|
+
return {};
|
|
891
|
+
}
|
|
892
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
893
|
+
return allLocales.reduce((acc, locale) => {
|
|
894
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
895
|
+
return acc;
|
|
896
|
+
}, {});
|
|
897
|
+
};
|
|
898
|
+
const getContentTypesDataForActions = async (contentTypesUids) => {
|
|
899
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
900
|
+
const contentTypesData = {};
|
|
901
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
902
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
903
|
+
uid: contentTypeUid
|
|
904
|
+
});
|
|
905
|
+
contentTypesData[contentTypeUid] = {
|
|
906
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
907
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
return contentTypesData;
|
|
911
|
+
};
|
|
912
|
+
return {
|
|
913
|
+
async create(releaseId, action, { disableUpdateReleaseStatus = false } = {}) {
|
|
914
|
+
const { validateEntryData, validateUniqueEntry } = getService("release-validation", {
|
|
618
915
|
strapi: strapi2
|
|
619
916
|
});
|
|
620
917
|
await Promise.all([
|
|
621
|
-
|
|
918
|
+
validateEntryData(action.contentType, action.entryDocumentId),
|
|
622
919
|
validateUniqueEntry(releaseId, action)
|
|
623
920
|
]);
|
|
624
|
-
const
|
|
921
|
+
const model = strapi2.contentType(action.contentType);
|
|
922
|
+
if (model.kind === "singleType") {
|
|
923
|
+
const document = await strapi2.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
924
|
+
if (!document) {
|
|
925
|
+
throw new errors.NotFoundError(`No entry found for contentType ${action.contentType}`);
|
|
926
|
+
}
|
|
927
|
+
action.entryDocumentId = document.documentId;
|
|
928
|
+
}
|
|
929
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
|
|
625
930
|
if (!release2) {
|
|
626
931
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
627
932
|
}
|
|
628
933
|
if (release2.releasedAt) {
|
|
629
934
|
throw new errors.ValidationError("Release already published");
|
|
630
935
|
}
|
|
631
|
-
const
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
936
|
+
const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
|
|
937
|
+
{
|
|
938
|
+
contentType: action.contentType,
|
|
939
|
+
documentId: action.entryDocumentId,
|
|
940
|
+
locale: action.locale
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
strapi: strapi2
|
|
944
|
+
}
|
|
945
|
+
) : true;
|
|
946
|
+
const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
635
947
|
data: {
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
isEntryValid,
|
|
640
|
-
entry: {
|
|
641
|
-
id: entry.id,
|
|
642
|
-
__type: entry.contentType,
|
|
643
|
-
__pivot: { field: "entry" }
|
|
644
|
-
},
|
|
645
|
-
release: releaseId
|
|
948
|
+
...action,
|
|
949
|
+
release: release2.id,
|
|
950
|
+
isEntryValid: actionStatus
|
|
646
951
|
},
|
|
647
|
-
populate: { release: {
|
|
952
|
+
populate: { release: { select: ["id"] } }
|
|
648
953
|
});
|
|
649
|
-
|
|
954
|
+
if (!disableUpdateReleaseStatus) {
|
|
955
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
956
|
+
}
|
|
650
957
|
return releaseAction2;
|
|
651
958
|
},
|
|
652
|
-
async
|
|
653
|
-
const release2 = await strapi2.
|
|
654
|
-
|
|
959
|
+
async findPage(releaseId, query) {
|
|
960
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
961
|
+
where: { id: releaseId },
|
|
962
|
+
select: ["id"]
|
|
655
963
|
});
|
|
656
964
|
if (!release2) {
|
|
657
965
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
658
966
|
}
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
populate: "*"
|
|
664
|
-
}
|
|
665
|
-
},
|
|
666
|
-
filters: {
|
|
967
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
968
|
+
const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
969
|
+
...dbQuery,
|
|
970
|
+
where: {
|
|
667
971
|
release: releaseId
|
|
668
972
|
}
|
|
669
973
|
});
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
974
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
975
|
+
const actionsWithEntry = await async.map(actions, async (action) => {
|
|
976
|
+
const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
|
|
977
|
+
const entry = await getEntry(
|
|
978
|
+
{
|
|
979
|
+
contentType: action.contentType,
|
|
980
|
+
documentId: action.entryDocumentId,
|
|
981
|
+
locale: action.locale,
|
|
982
|
+
populate,
|
|
983
|
+
status: action.type === "publish" ? "draft" : "published"
|
|
984
|
+
},
|
|
985
|
+
{ strapi: strapi2 }
|
|
986
|
+
);
|
|
987
|
+
return {
|
|
988
|
+
...action,
|
|
989
|
+
entry,
|
|
990
|
+
status: entry ? await getEntryStatus(action.contentType, entry) : null
|
|
991
|
+
};
|
|
992
|
+
});
|
|
993
|
+
return {
|
|
994
|
+
results: actionsWithEntry,
|
|
995
|
+
pagination
|
|
996
|
+
};
|
|
673
997
|
},
|
|
674
998
|
async groupActions(actions, groupBy) {
|
|
675
999
|
const contentTypeUids = actions.reduce((acc, action) => {
|
|
@@ -678,10 +1002,8 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
678
1002
|
}
|
|
679
1003
|
return acc;
|
|
680
1004
|
}, []);
|
|
681
|
-
const allReleaseContentTypesDictionary = await
|
|
682
|
-
|
|
683
|
-
);
|
|
684
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
1005
|
+
const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
|
|
1006
|
+
const allLocalesDictionary = await getLocalesDataForActions();
|
|
685
1007
|
const formattedData = actions.map((action) => {
|
|
686
1008
|
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
687
1009
|
return {
|
|
@@ -697,30 +1019,6 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
697
1019
|
const groupName = getGroupName(groupBy);
|
|
698
1020
|
return _.groupBy(groupName)(formattedData);
|
|
699
1021
|
},
|
|
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
1022
|
getContentTypeModelsFromActions(actions) {
|
|
725
1023
|
const contentTypeUids = actions.reduce((acc, action) => {
|
|
726
1024
|
if (!acc.includes(action.contentType)) {
|
|
@@ -737,191 +1035,37 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
737
1035
|
);
|
|
738
1036
|
return contentTypeModelsMap;
|
|
739
1037
|
},
|
|
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;
|
|
1038
|
+
async countActions(query) {
|
|
1039
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
1040
|
+
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
782
1041
|
},
|
|
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
|
|
1042
|
+
async update(actionId, releaseId, update) {
|
|
1043
|
+
const action = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findOne({
|
|
1044
|
+
where: {
|
|
1045
|
+
id: actionId,
|
|
1046
|
+
release: {
|
|
1047
|
+
id: releaseId,
|
|
1048
|
+
releasedAt: {
|
|
1049
|
+
$null: true
|
|
897
1050
|
}
|
|
898
1051
|
}
|
|
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
1052
|
}
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
});
|
|
921
|
-
throw error;
|
|
1053
|
+
});
|
|
1054
|
+
if (!action) {
|
|
1055
|
+
throw new errors.NotFoundError(
|
|
1056
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1057
|
+
);
|
|
922
1058
|
}
|
|
923
|
-
|
|
924
|
-
|
|
1059
|
+
const actionStatus = update.type === "publish" ? await getDraftEntryValidStatus(
|
|
1060
|
+
{
|
|
1061
|
+
contentType: action.contentType,
|
|
1062
|
+
documentId: action.entryDocumentId,
|
|
1063
|
+
locale: action.locale
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
strapi: strapi2
|
|
1067
|
+
}
|
|
1068
|
+
) : true;
|
|
925
1069
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
926
1070
|
where: {
|
|
927
1071
|
id: actionId,
|
|
@@ -932,16 +1076,15 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
932
1076
|
}
|
|
933
1077
|
}
|
|
934
1078
|
},
|
|
935
|
-
data:
|
|
1079
|
+
data: {
|
|
1080
|
+
...update,
|
|
1081
|
+
isEntryValid: actionStatus
|
|
1082
|
+
}
|
|
936
1083
|
});
|
|
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
|
-
}
|
|
1084
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
942
1085
|
return updatedAction;
|
|
943
1086
|
},
|
|
944
|
-
async
|
|
1087
|
+
async delete(actionId, releaseId) {
|
|
945
1088
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
946
1089
|
where: {
|
|
947
1090
|
id: actionId,
|
|
@@ -958,79 +1101,56 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
958
1101
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
959
1102
|
);
|
|
960
1103
|
}
|
|
961
|
-
|
|
1104
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
962
1105
|
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
1106
|
}
|
|
1007
1107
|
};
|
|
1008
1108
|
};
|
|
1109
|
+
class AlreadyOnReleaseError extends errors.ApplicationError {
|
|
1110
|
+
constructor(message) {
|
|
1111
|
+
super(message);
|
|
1112
|
+
this.name = "AlreadyOnReleaseError";
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1009
1115
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
1010
1116
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
1011
|
-
const release2 = await strapi2.
|
|
1012
|
-
|
|
1117
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1118
|
+
where: {
|
|
1119
|
+
id: releaseId
|
|
1120
|
+
},
|
|
1121
|
+
populate: {
|
|
1122
|
+
actions: true
|
|
1123
|
+
}
|
|
1013
1124
|
});
|
|
1014
1125
|
if (!release2) {
|
|
1015
1126
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1016
1127
|
}
|
|
1017
1128
|
const isEntryInRelease = release2.actions.some(
|
|
1018
|
-
(action) =>
|
|
1129
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
1019
1130
|
);
|
|
1020
1131
|
if (isEntryInRelease) {
|
|
1021
|
-
throw new
|
|
1022
|
-
`Entry with
|
|
1132
|
+
throw new AlreadyOnReleaseError(
|
|
1133
|
+
`Entry with documentId ${releaseActionArgs.entryDocumentId}${releaseActionArgs.locale ? `( ${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
|
|
1023
1134
|
);
|
|
1024
1135
|
}
|
|
1025
1136
|
},
|
|
1026
|
-
|
|
1137
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
1027
1138
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
1028
1139
|
if (!contentType) {
|
|
1029
1140
|
throw new errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
1030
1141
|
}
|
|
1142
|
+
if (!contentTypes$1.hasDraftAndPublish(contentType)) {
|
|
1143
|
+
throw new errors.ValidationError(
|
|
1144
|
+
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
1145
|
+
);
|
|
1146
|
+
}
|
|
1147
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1148
|
+
throw new errors.ValidationError("Document id is required for collection type");
|
|
1149
|
+
}
|
|
1031
1150
|
},
|
|
1032
1151
|
async validatePendingReleasesLimit() {
|
|
1033
|
-
const
|
|
1152
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1153
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
1034
1154
|
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
1035
1155
|
filters: {
|
|
1036
1156
|
releasedAt: {
|
|
@@ -1043,8 +1163,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
1043
1163
|
}
|
|
1044
1164
|
},
|
|
1045
1165
|
async validateUniqueNameForPendingRelease(name, id) {
|
|
1046
|
-
const pendingReleases = await strapi2.
|
|
1047
|
-
|
|
1166
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1167
|
+
where: {
|
|
1048
1168
|
releasedAt: {
|
|
1049
1169
|
$null: true
|
|
1050
1170
|
},
|
|
@@ -1073,7 +1193,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1073
1193
|
}
|
|
1074
1194
|
const job = scheduleJob(scheduleDate, async () => {
|
|
1075
1195
|
try {
|
|
1076
|
-
await getService("release").publish(releaseId);
|
|
1196
|
+
await getService("release", { strapi: strapi2 }).publish(releaseId);
|
|
1077
1197
|
} catch (error) {
|
|
1078
1198
|
}
|
|
1079
1199
|
this.cancel(releaseId);
|
|
@@ -1115,85 +1235,172 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1115
1235
|
}
|
|
1116
1236
|
};
|
|
1117
1237
|
};
|
|
1238
|
+
const DEFAULT_SETTINGS = {
|
|
1239
|
+
defaultTimezone: null
|
|
1240
|
+
};
|
|
1241
|
+
const createSettingsService = ({ strapi: strapi2 }) => {
|
|
1242
|
+
const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
|
|
1243
|
+
return {
|
|
1244
|
+
async update({ settings: settings2 }) {
|
|
1245
|
+
const store = await getStore();
|
|
1246
|
+
store.set({ key: "settings", value: settings2 });
|
|
1247
|
+
return settings2;
|
|
1248
|
+
},
|
|
1249
|
+
async find() {
|
|
1250
|
+
const store = await getStore();
|
|
1251
|
+
const settings2 = await store.get({ key: "settings" });
|
|
1252
|
+
return {
|
|
1253
|
+
...DEFAULT_SETTINGS,
|
|
1254
|
+
...settings2 || {}
|
|
1255
|
+
};
|
|
1256
|
+
}
|
|
1257
|
+
};
|
|
1258
|
+
};
|
|
1118
1259
|
const services = {
|
|
1119
1260
|
release: createReleaseService,
|
|
1261
|
+
"release-action": createReleaseActionService,
|
|
1120
1262
|
"release-validation": createReleaseValidationService,
|
|
1121
|
-
|
|
1263
|
+
scheduling: createSchedulingService,
|
|
1264
|
+
settings: createSettingsService
|
|
1122
1265
|
};
|
|
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()
|
|
1266
|
+
const RELEASE_SCHEMA = yup$1.object().shape({
|
|
1267
|
+
name: yup$1.string().trim().required(),
|
|
1268
|
+
scheduledAt: yup$1.string().nullable(),
|
|
1269
|
+
timezone: yup$1.string().when("scheduledAt", {
|
|
1270
|
+
is: (value) => value !== null && value !== void 0,
|
|
1271
|
+
then: yup$1.string().required(),
|
|
1272
|
+
otherwise: yup$1.string().nullable()
|
|
1141
1273
|
})
|
|
1142
1274
|
}).required().noUnknown();
|
|
1275
|
+
const FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA = yup$1.object().shape({
|
|
1276
|
+
contentType: yup$1.string().required(),
|
|
1277
|
+
entryDocumentId: yup$1.string().nullable(),
|
|
1278
|
+
hasEntryAttached: yup$1.string().nullable(),
|
|
1279
|
+
locale: yup$1.string().nullable()
|
|
1280
|
+
}).required().noUnknown();
|
|
1143
1281
|
const validateRelease = validateYupSchema(RELEASE_SCHEMA);
|
|
1282
|
+
const validatefindByDocumentAttachedParams = validateYupSchema(
|
|
1283
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1284
|
+
);
|
|
1144
1285
|
const releaseController = {
|
|
1145
|
-
|
|
1146
|
-
|
|
1286
|
+
/**
|
|
1287
|
+
* Find releases based on documents attached or not to the release.
|
|
1288
|
+
* If `hasEntryAttached` is true, it will return all releases that have the entry attached.
|
|
1289
|
+
* If `hasEntryAttached` is false, it will return all releases that don't have the entry attached.
|
|
1290
|
+
*/
|
|
1291
|
+
async findByDocumentAttached(ctx) {
|
|
1292
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1147
1293
|
ability: ctx.state.userAbility,
|
|
1148
1294
|
model: RELEASE_MODEL_UID
|
|
1149
1295
|
});
|
|
1150
1296
|
await permissionsManager.validateQuery(ctx.query);
|
|
1151
1297
|
const releaseService = getService("release", { strapi });
|
|
1152
|
-
const
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
const
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1298
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1299
|
+
await validatefindByDocumentAttachedParams(query);
|
|
1300
|
+
const model = strapi.getModel(query.contentType);
|
|
1301
|
+
if (model.kind && model.kind === "singleType") {
|
|
1302
|
+
const document = await strapi.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
1303
|
+
if (!document) {
|
|
1304
|
+
throw new errors.NotFoundError(`No entry found for contentType ${query.contentType}`);
|
|
1305
|
+
}
|
|
1306
|
+
query.entryDocumentId = document.documentId;
|
|
1307
|
+
}
|
|
1308
|
+
const { contentType, hasEntryAttached, entryDocumentId, locale } = query;
|
|
1309
|
+
const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
|
|
1310
|
+
if (isEntryAttached) {
|
|
1311
|
+
const releases = await releaseService.findMany({
|
|
1312
|
+
where: {
|
|
1313
|
+
releasedAt: null,
|
|
1167
1314
|
actions: {
|
|
1168
|
-
|
|
1169
|
-
|
|
1315
|
+
contentType,
|
|
1316
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1317
|
+
locale: locale ?? null
|
|
1318
|
+
}
|
|
1319
|
+
},
|
|
1320
|
+
populate: {
|
|
1321
|
+
actions: {
|
|
1322
|
+
fields: ["type"],
|
|
1323
|
+
filters: {
|
|
1324
|
+
contentType,
|
|
1325
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1326
|
+
locale: locale ?? null
|
|
1170
1327
|
}
|
|
1171
1328
|
}
|
|
1172
|
-
}
|
|
1329
|
+
}
|
|
1330
|
+
});
|
|
1331
|
+
ctx.body = { data: releases };
|
|
1332
|
+
} else {
|
|
1333
|
+
const relatedReleases = await releaseService.findMany({
|
|
1334
|
+
where: {
|
|
1335
|
+
releasedAt: null,
|
|
1336
|
+
actions: {
|
|
1337
|
+
contentType,
|
|
1338
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1339
|
+
locale: locale ?? null
|
|
1340
|
+
}
|
|
1341
|
+
}
|
|
1173
1342
|
});
|
|
1174
|
-
const
|
|
1343
|
+
const releases = await releaseService.findMany({
|
|
1175
1344
|
where: {
|
|
1345
|
+
$or: [
|
|
1346
|
+
{
|
|
1347
|
+
id: {
|
|
1348
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1349
|
+
}
|
|
1350
|
+
},
|
|
1351
|
+
{
|
|
1352
|
+
actions: null
|
|
1353
|
+
}
|
|
1354
|
+
],
|
|
1176
1355
|
releasedAt: null
|
|
1177
1356
|
}
|
|
1178
1357
|
});
|
|
1179
|
-
ctx.body = { data
|
|
1358
|
+
ctx.body = { data: releases };
|
|
1180
1359
|
}
|
|
1181
1360
|
},
|
|
1361
|
+
async findPage(ctx) {
|
|
1362
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1363
|
+
ability: ctx.state.userAbility,
|
|
1364
|
+
model: RELEASE_MODEL_UID
|
|
1365
|
+
});
|
|
1366
|
+
await permissionsManager.validateQuery(ctx.query);
|
|
1367
|
+
const releaseService = getService("release", { strapi });
|
|
1368
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1369
|
+
const { results, pagination } = await releaseService.findPage(query);
|
|
1370
|
+
const data = results.map((release2) => {
|
|
1371
|
+
const { actions, ...releaseData } = release2;
|
|
1372
|
+
return {
|
|
1373
|
+
...releaseData,
|
|
1374
|
+
actions: {
|
|
1375
|
+
meta: {
|
|
1376
|
+
count: actions.count
|
|
1377
|
+
}
|
|
1378
|
+
}
|
|
1379
|
+
};
|
|
1380
|
+
});
|
|
1381
|
+
const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
|
|
1382
|
+
where: {
|
|
1383
|
+
releasedAt: null
|
|
1384
|
+
}
|
|
1385
|
+
});
|
|
1386
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
1387
|
+
},
|
|
1182
1388
|
async findOne(ctx) {
|
|
1183
1389
|
const id = ctx.params.id;
|
|
1184
1390
|
const releaseService = getService("release", { strapi });
|
|
1391
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1185
1392
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
1186
1393
|
if (!release2) {
|
|
1187
1394
|
throw new errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1188
1395
|
}
|
|
1189
|
-
const count = await
|
|
1396
|
+
const count = await releaseActionService.countActions({
|
|
1190
1397
|
filters: {
|
|
1191
1398
|
release: id
|
|
1192
1399
|
}
|
|
1193
1400
|
});
|
|
1194
1401
|
const sanitizedRelease = {
|
|
1195
1402
|
...release2,
|
|
1196
|
-
createdBy: release2.createdBy ? strapi.admin
|
|
1403
|
+
createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
|
|
1197
1404
|
};
|
|
1198
1405
|
const data = {
|
|
1199
1406
|
...sanitizedRelease,
|
|
@@ -1205,19 +1412,63 @@ const releaseController = {
|
|
|
1205
1412
|
};
|
|
1206
1413
|
ctx.body = { data };
|
|
1207
1414
|
},
|
|
1415
|
+
async mapEntriesToReleases(ctx) {
|
|
1416
|
+
const { contentTypeUid, documentIds, locale } = ctx.query;
|
|
1417
|
+
if (!contentTypeUid || !documentIds) {
|
|
1418
|
+
throw new errors.ValidationError("Missing required query parameters");
|
|
1419
|
+
}
|
|
1420
|
+
const releaseService = getService("release", { strapi });
|
|
1421
|
+
const releasesWithActions = await releaseService.findMany({
|
|
1422
|
+
where: {
|
|
1423
|
+
releasedAt: null,
|
|
1424
|
+
actions: {
|
|
1425
|
+
contentType: contentTypeUid,
|
|
1426
|
+
entryDocumentId: {
|
|
1427
|
+
$in: documentIds
|
|
1428
|
+
},
|
|
1429
|
+
locale
|
|
1430
|
+
}
|
|
1431
|
+
},
|
|
1432
|
+
populate: {
|
|
1433
|
+
actions: true
|
|
1434
|
+
}
|
|
1435
|
+
});
|
|
1436
|
+
const mappedEntriesInReleases = releasesWithActions.reduce(
|
|
1437
|
+
(acc, release2) => {
|
|
1438
|
+
release2.actions.forEach((action) => {
|
|
1439
|
+
if (action.contentType !== contentTypeUid) {
|
|
1440
|
+
return;
|
|
1441
|
+
}
|
|
1442
|
+
if (locale && action.locale !== locale) {
|
|
1443
|
+
return;
|
|
1444
|
+
}
|
|
1445
|
+
if (!acc[action.entryDocumentId]) {
|
|
1446
|
+
acc[action.entryDocumentId] = [{ id: release2.id, name: release2.name }];
|
|
1447
|
+
} else {
|
|
1448
|
+
acc[action.entryDocumentId].push({ id: release2.id, name: release2.name });
|
|
1449
|
+
}
|
|
1450
|
+
});
|
|
1451
|
+
return acc;
|
|
1452
|
+
},
|
|
1453
|
+
{}
|
|
1454
|
+
);
|
|
1455
|
+
ctx.body = {
|
|
1456
|
+
data: mappedEntriesInReleases
|
|
1457
|
+
};
|
|
1458
|
+
},
|
|
1208
1459
|
async create(ctx) {
|
|
1209
1460
|
const user = ctx.state.user;
|
|
1210
1461
|
const releaseArgs = ctx.request.body;
|
|
1211
1462
|
await validateRelease(releaseArgs);
|
|
1212
1463
|
const releaseService = getService("release", { strapi });
|
|
1213
1464
|
const release2 = await releaseService.create(releaseArgs, { user });
|
|
1214
|
-
const permissionsManager = strapi.admin
|
|
1465
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1215
1466
|
ability: ctx.state.userAbility,
|
|
1216
1467
|
model: RELEASE_MODEL_UID
|
|
1217
1468
|
});
|
|
1218
|
-
ctx.
|
|
1469
|
+
ctx.created({
|
|
1219
1470
|
data: await permissionsManager.sanitizeOutput(release2)
|
|
1220
|
-
};
|
|
1471
|
+
});
|
|
1221
1472
|
},
|
|
1222
1473
|
async update(ctx) {
|
|
1223
1474
|
const user = ctx.state.user;
|
|
@@ -1226,7 +1477,7 @@ const releaseController = {
|
|
|
1226
1477
|
await validateRelease(releaseArgs);
|
|
1227
1478
|
const releaseService = getService("release", { strapi });
|
|
1228
1479
|
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
1229
|
-
const permissionsManager = strapi.admin
|
|
1480
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1230
1481
|
ability: ctx.state.userAbility,
|
|
1231
1482
|
model: RELEASE_MODEL_UID
|
|
1232
1483
|
});
|
|
@@ -1243,18 +1494,18 @@ const releaseController = {
|
|
|
1243
1494
|
};
|
|
1244
1495
|
},
|
|
1245
1496
|
async publish(ctx) {
|
|
1246
|
-
const user = ctx.state.user;
|
|
1247
1497
|
const id = ctx.params.id;
|
|
1248
1498
|
const releaseService = getService("release", { strapi });
|
|
1249
|
-
const
|
|
1499
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1500
|
+
const release2 = await releaseService.publish(id);
|
|
1250
1501
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1251
|
-
|
|
1502
|
+
releaseActionService.countActions({
|
|
1252
1503
|
filters: {
|
|
1253
1504
|
release: id,
|
|
1254
1505
|
type: "publish"
|
|
1255
1506
|
}
|
|
1256
1507
|
}),
|
|
1257
|
-
|
|
1508
|
+
releaseActionService.countActions({
|
|
1258
1509
|
filters: {
|
|
1259
1510
|
release: id,
|
|
1260
1511
|
type: "unpublish"
|
|
@@ -1272,45 +1523,93 @@ const releaseController = {
|
|
|
1272
1523
|
}
|
|
1273
1524
|
};
|
|
1274
1525
|
const RELEASE_ACTION_SCHEMA = yup$1.object().shape({
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
}).required(),
|
|
1526
|
+
contentType: yup$1.string().required(),
|
|
1527
|
+
entryDocumentId: yup$1.strapiID(),
|
|
1528
|
+
locale: yup$1.string(),
|
|
1279
1529
|
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1280
1530
|
});
|
|
1281
1531
|
const RELEASE_ACTION_UPDATE_SCHEMA = yup$1.object().shape({
|
|
1282
1532
|
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1283
1533
|
});
|
|
1534
|
+
const FIND_MANY_ACTIONS_PARAMS = yup$1.object().shape({
|
|
1535
|
+
groupBy: yup$1.string().oneOf(["action", "contentType", "locale"])
|
|
1536
|
+
});
|
|
1284
1537
|
const validateReleaseAction = validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1285
1538
|
const validateReleaseActionUpdateSchema = validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1539
|
+
const validateFindManyActionsParams = validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1286
1540
|
const releaseActionController = {
|
|
1287
1541
|
async create(ctx) {
|
|
1288
1542
|
const releaseId = ctx.params.releaseId;
|
|
1289
1543
|
const releaseActionArgs = ctx.request.body;
|
|
1290
1544
|
await validateReleaseAction(releaseActionArgs);
|
|
1291
|
-
const
|
|
1292
|
-
const releaseAction2 = await
|
|
1293
|
-
ctx.
|
|
1545
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1546
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1547
|
+
ctx.created({
|
|
1294
1548
|
data: releaseAction2
|
|
1295
|
-
};
|
|
1549
|
+
});
|
|
1550
|
+
},
|
|
1551
|
+
async createMany(ctx) {
|
|
1552
|
+
const releaseId = ctx.params.releaseId;
|
|
1553
|
+
const releaseActionsArgs = ctx.request.body;
|
|
1554
|
+
await Promise.all(
|
|
1555
|
+
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1556
|
+
);
|
|
1557
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1558
|
+
const releaseService = getService("release", { strapi });
|
|
1559
|
+
const releaseActions = await strapi.db.transaction(async () => {
|
|
1560
|
+
const releaseActions2 = await Promise.all(
|
|
1561
|
+
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1562
|
+
try {
|
|
1563
|
+
const action = await releaseActionService.create(releaseId, releaseActionArgs, {
|
|
1564
|
+
disableUpdateReleaseStatus: true
|
|
1565
|
+
});
|
|
1566
|
+
return action;
|
|
1567
|
+
} catch (error) {
|
|
1568
|
+
if (error instanceof AlreadyOnReleaseError) {
|
|
1569
|
+
return null;
|
|
1570
|
+
}
|
|
1571
|
+
throw error;
|
|
1572
|
+
}
|
|
1573
|
+
})
|
|
1574
|
+
);
|
|
1575
|
+
return releaseActions2;
|
|
1576
|
+
});
|
|
1577
|
+
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1578
|
+
if (newReleaseActions.length > 0) {
|
|
1579
|
+
releaseService.updateReleaseStatus(releaseId);
|
|
1580
|
+
}
|
|
1581
|
+
ctx.created({
|
|
1582
|
+
data: newReleaseActions,
|
|
1583
|
+
meta: {
|
|
1584
|
+
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1585
|
+
totalEntries: releaseActions.length
|
|
1586
|
+
}
|
|
1587
|
+
});
|
|
1296
1588
|
},
|
|
1297
1589
|
async findMany(ctx) {
|
|
1298
1590
|
const releaseId = ctx.params.releaseId;
|
|
1299
|
-
const permissionsManager = strapi.admin
|
|
1591
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1300
1592
|
ability: ctx.state.userAbility,
|
|
1301
1593
|
model: RELEASE_ACTION_MODEL_UID
|
|
1302
1594
|
});
|
|
1595
|
+
await validateFindManyActionsParams(ctx.query);
|
|
1596
|
+
if (ctx.query.groupBy) {
|
|
1597
|
+
if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
|
|
1598
|
+
ctx.badRequest("Invalid groupBy parameter");
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
|
|
1602
|
+
delete ctx.query.groupBy;
|
|
1303
1603
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1304
|
-
const
|
|
1305
|
-
const { results, pagination } = await
|
|
1306
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1604
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1605
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1307
1606
|
...query
|
|
1308
1607
|
});
|
|
1309
1608
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
1310
1609
|
if (acc[action.contentType]) {
|
|
1311
1610
|
return acc;
|
|
1312
1611
|
}
|
|
1313
|
-
const contentTypePermissionsManager = strapi.admin
|
|
1612
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1314
1613
|
ability: ctx.state.userAbility,
|
|
1315
1614
|
model: action.contentType
|
|
1316
1615
|
});
|
|
@@ -1319,10 +1618,11 @@ const releaseActionController = {
|
|
|
1319
1618
|
}, {});
|
|
1320
1619
|
const sanitizedResults = await async.map(results, async (action) => ({
|
|
1321
1620
|
...action,
|
|
1322
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1621
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1323
1622
|
}));
|
|
1324
|
-
const groupedData = await
|
|
1325
|
-
const contentTypes2 =
|
|
1623
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1624
|
+
const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
|
|
1625
|
+
const releaseService = getService("release", { strapi });
|
|
1326
1626
|
const components = await releaseService.getAllComponents();
|
|
1327
1627
|
ctx.body = {
|
|
1328
1628
|
data: groupedData,
|
|
@@ -1338,8 +1638,8 @@ const releaseActionController = {
|
|
|
1338
1638
|
const releaseId = ctx.params.releaseId;
|
|
1339
1639
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1340
1640
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1341
|
-
const
|
|
1342
|
-
const updatedAction = await
|
|
1641
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1642
|
+
const updatedAction = await releaseActionService.update(
|
|
1343
1643
|
actionId,
|
|
1344
1644
|
releaseId,
|
|
1345
1645
|
releaseActionUpdateArgs
|
|
@@ -1351,17 +1651,71 @@ const releaseActionController = {
|
|
|
1351
1651
|
async delete(ctx) {
|
|
1352
1652
|
const actionId = ctx.params.actionId;
|
|
1353
1653
|
const releaseId = ctx.params.releaseId;
|
|
1354
|
-
const
|
|
1355
|
-
const deletedReleaseAction = await
|
|
1654
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1655
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1356
1656
|
ctx.body = {
|
|
1357
1657
|
data: deletedReleaseAction
|
|
1358
1658
|
};
|
|
1359
1659
|
}
|
|
1360
1660
|
};
|
|
1361
|
-
const
|
|
1661
|
+
const SETTINGS_SCHEMA = yup.object().shape({
|
|
1662
|
+
defaultTimezone: yup.string().nullable().default(null)
|
|
1663
|
+
}).required().noUnknown();
|
|
1664
|
+
const validateSettings = validateYupSchema(SETTINGS_SCHEMA);
|
|
1665
|
+
const settingsController = {
|
|
1666
|
+
async find(ctx) {
|
|
1667
|
+
const settingsService = getService("settings", { strapi });
|
|
1668
|
+
const settings2 = await settingsService.find();
|
|
1669
|
+
ctx.body = { data: settings2 };
|
|
1670
|
+
},
|
|
1671
|
+
async update(ctx) {
|
|
1672
|
+
const settingsBody = ctx.request.body;
|
|
1673
|
+
const settings2 = await validateSettings(settingsBody);
|
|
1674
|
+
const settingsService = getService("settings", { strapi });
|
|
1675
|
+
const updatedSettings = await settingsService.update({ settings: settings2 });
|
|
1676
|
+
ctx.body = { data: updatedSettings };
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
const controllers = {
|
|
1680
|
+
release: releaseController,
|
|
1681
|
+
"release-action": releaseActionController,
|
|
1682
|
+
settings: settingsController
|
|
1683
|
+
};
|
|
1362
1684
|
const release = {
|
|
1363
1685
|
type: "admin",
|
|
1364
1686
|
routes: [
|
|
1687
|
+
{
|
|
1688
|
+
method: "GET",
|
|
1689
|
+
path: "/mapEntriesToReleases",
|
|
1690
|
+
handler: "release.mapEntriesToReleases",
|
|
1691
|
+
config: {
|
|
1692
|
+
policies: [
|
|
1693
|
+
"admin::isAuthenticatedAdmin",
|
|
1694
|
+
{
|
|
1695
|
+
name: "admin::hasPermissions",
|
|
1696
|
+
config: {
|
|
1697
|
+
actions: ["plugin::content-releases.read"]
|
|
1698
|
+
}
|
|
1699
|
+
}
|
|
1700
|
+
]
|
|
1701
|
+
}
|
|
1702
|
+
},
|
|
1703
|
+
{
|
|
1704
|
+
method: "GET",
|
|
1705
|
+
path: "/getByDocumentAttached",
|
|
1706
|
+
handler: "release.findByDocumentAttached",
|
|
1707
|
+
config: {
|
|
1708
|
+
policies: [
|
|
1709
|
+
"admin::isAuthenticatedAdmin",
|
|
1710
|
+
{
|
|
1711
|
+
name: "admin::hasPermissions",
|
|
1712
|
+
config: {
|
|
1713
|
+
actions: ["plugin::content-releases.read"]
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
]
|
|
1717
|
+
}
|
|
1718
|
+
},
|
|
1365
1719
|
{
|
|
1366
1720
|
method: "POST",
|
|
1367
1721
|
path: "/",
|
|
@@ -1381,7 +1735,7 @@ const release = {
|
|
|
1381
1735
|
{
|
|
1382
1736
|
method: "GET",
|
|
1383
1737
|
path: "/",
|
|
1384
|
-
handler: "release.
|
|
1738
|
+
handler: "release.findPage",
|
|
1385
1739
|
config: {
|
|
1386
1740
|
policies: [
|
|
1387
1741
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1479,6 +1833,22 @@ const releaseAction = {
|
|
|
1479
1833
|
]
|
|
1480
1834
|
}
|
|
1481
1835
|
},
|
|
1836
|
+
{
|
|
1837
|
+
method: "POST",
|
|
1838
|
+
path: "/:releaseId/actions/bulk",
|
|
1839
|
+
handler: "release-action.createMany",
|
|
1840
|
+
config: {
|
|
1841
|
+
policies: [
|
|
1842
|
+
"admin::isAuthenticatedAdmin",
|
|
1843
|
+
{
|
|
1844
|
+
name: "admin::hasPermissions",
|
|
1845
|
+
config: {
|
|
1846
|
+
actions: ["plugin::content-releases.create-action"]
|
|
1847
|
+
}
|
|
1848
|
+
}
|
|
1849
|
+
]
|
|
1850
|
+
}
|
|
1851
|
+
},
|
|
1482
1852
|
{
|
|
1483
1853
|
method: "GET",
|
|
1484
1854
|
path: "/:releaseId/actions",
|
|
@@ -1529,7 +1899,45 @@ const releaseAction = {
|
|
|
1529
1899
|
}
|
|
1530
1900
|
]
|
|
1531
1901
|
};
|
|
1902
|
+
const settings = {
|
|
1903
|
+
type: "admin",
|
|
1904
|
+
routes: [
|
|
1905
|
+
{
|
|
1906
|
+
method: "GET",
|
|
1907
|
+
path: "/settings",
|
|
1908
|
+
handler: "settings.find",
|
|
1909
|
+
config: {
|
|
1910
|
+
policies: [
|
|
1911
|
+
"admin::isAuthenticatedAdmin",
|
|
1912
|
+
{
|
|
1913
|
+
name: "admin::hasPermissions",
|
|
1914
|
+
config: {
|
|
1915
|
+
actions: ["plugin::content-releases.settings.read"]
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
]
|
|
1919
|
+
}
|
|
1920
|
+
},
|
|
1921
|
+
{
|
|
1922
|
+
method: "PUT",
|
|
1923
|
+
path: "/settings",
|
|
1924
|
+
handler: "settings.update",
|
|
1925
|
+
config: {
|
|
1926
|
+
policies: [
|
|
1927
|
+
"admin::isAuthenticatedAdmin",
|
|
1928
|
+
{
|
|
1929
|
+
name: "admin::hasPermissions",
|
|
1930
|
+
config: {
|
|
1931
|
+
actions: ["plugin::content-releases.settings.update"]
|
|
1932
|
+
}
|
|
1933
|
+
}
|
|
1934
|
+
]
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
]
|
|
1938
|
+
};
|
|
1532
1939
|
const routes = {
|
|
1940
|
+
settings,
|
|
1533
1941
|
release,
|
|
1534
1942
|
"release-action": releaseAction
|
|
1535
1943
|
};
|
|
@@ -1546,6 +1954,9 @@ const getPlugin = () => {
|
|
|
1546
1954
|
};
|
|
1547
1955
|
}
|
|
1548
1956
|
return {
|
|
1957
|
+
// Always return register, it handles its own feature check
|
|
1958
|
+
register,
|
|
1959
|
+
// Always return contentTypes to avoid losing data when the feature is disabled
|
|
1549
1960
|
contentTypes
|
|
1550
1961
|
};
|
|
1551
1962
|
};
|