@strapi/content-releases 0.0.0-next.78ea7925e0dad75936ae2e937a041a0666e3d65a → 0.0.0-next.7f1333f1967e625c57ab16648c057aea08c9dddb
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-p8aKBitd.js → App-BKB1esYS.js} +600 -551
- package/dist/_chunks/App-BKB1esYS.js.map +1 -0
- package/dist/_chunks/{App-bpzO2Ljh.mjs → App-Cne--1Z8.mjs} +582 -531
- 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/RelativeTime.d.ts +28 -0
- package/dist/admin/src/components/ReleaseAction.d.ts +3 -0
- package/dist/admin/src/components/ReleaseActionMenu.d.ts +26 -0
- package/dist/admin/src/components/ReleaseActionModal.d.ts +24 -0
- package/dist/admin/src/components/ReleaseActionOptions.d.ts +9 -0
- package/dist/admin/src/components/ReleaseListCell.d.ts +28 -0
- package/dist/admin/src/components/ReleaseModal.d.ts +17 -0
- package/dist/admin/src/components/ReleasesPanel.d.ts +3 -0
- package/dist/admin/src/constants.d.ts +76 -0
- package/dist/admin/src/index.d.ts +3 -0
- package/dist/admin/src/modules/hooks.d.ts +7 -0
- package/dist/admin/src/pages/App.d.ts +1 -0
- package/dist/admin/src/pages/PurchaseContentReleases.d.ts +2 -0
- package/dist/admin/src/pages/ReleaseDetailsPage.d.ts +2 -0
- package/dist/admin/src/pages/ReleasesPage.d.ts +8 -0
- package/dist/admin/src/pages/ReleasesSettingsPage.d.ts +1 -0
- package/dist/admin/src/pages/tests/mockReleaseDetailsPageData.d.ts +181 -0
- package/dist/admin/src/pages/tests/mockReleasesPageData.d.ts +39 -0
- package/dist/admin/src/pluginId.d.ts +1 -0
- package/dist/admin/src/services/release.d.ts +112 -0
- package/dist/admin/src/store/hooks.d.ts +7 -0
- package/dist/admin/src/utils/api.d.ts +6 -0
- package/dist/admin/src/utils/prefixPluginTranslations.d.ts +3 -0
- package/dist/admin/src/utils/time.d.ts +10 -0
- package/dist/admin/src/validation/schemas.d.ts +6 -0
- package/dist/server/index.js +974 -665
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +975 -665
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts +5 -0
- package/dist/server/src/bootstrap.d.ts.map +1 -0
- package/dist/server/src/constants.d.ts +21 -0
- package/dist/server/src/constants.d.ts.map +1 -0
- package/dist/server/src/content-types/index.d.ts +97 -0
- package/dist/server/src/content-types/index.d.ts.map +1 -0
- package/dist/server/src/content-types/release/index.d.ts +48 -0
- package/dist/server/src/content-types/release/index.d.ts.map +1 -0
- package/dist/server/src/content-types/release/schema.d.ts +47 -0
- package/dist/server/src/content-types/release/schema.d.ts.map +1 -0
- package/dist/server/src/content-types/release-action/index.d.ts +48 -0
- package/dist/server/src/content-types/release-action/index.d.ts.map +1 -0
- package/dist/server/src/content-types/release-action/schema.d.ts +47 -0
- package/dist/server/src/content-types/release-action/schema.d.ts.map +1 -0
- package/dist/server/src/controllers/index.d.ts +25 -0
- package/dist/server/src/controllers/index.d.ts.map +1 -0
- package/dist/server/src/controllers/release-action.d.ts +10 -0
- package/dist/server/src/controllers/release-action.d.ts.map +1 -0
- package/dist/server/src/controllers/release.d.ts +18 -0
- package/dist/server/src/controllers/release.d.ts.map +1 -0
- package/dist/server/src/controllers/settings.d.ts +11 -0
- package/dist/server/src/controllers/settings.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/release-action.d.ts +14 -0
- package/dist/server/src/controllers/validation/release-action.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/release.d.ts +4 -0
- package/dist/server/src/controllers/validation/release.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/settings.d.ts +3 -0
- package/dist/server/src/controllers/validation/settings.d.ts.map +1 -0
- package/dist/server/src/destroy.d.ts +5 -0
- package/dist/server/src/destroy.d.ts.map +1 -0
- package/dist/server/src/index.d.ts +2115 -0
- package/dist/server/src/index.d.ts.map +1 -0
- package/dist/server/src/middlewares/documents.d.ts +6 -0
- package/dist/server/src/middlewares/documents.d.ts.map +1 -0
- package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts +9 -0
- package/dist/server/src/migrations/database/5.0.0-document-id-in-actions.d.ts.map +1 -0
- package/dist/server/src/migrations/index.d.ts +13 -0
- package/dist/server/src/migrations/index.d.ts.map +1 -0
- package/dist/server/src/register.d.ts +5 -0
- package/dist/server/src/register.d.ts.map +1 -0
- package/dist/server/src/routes/index.d.ts +51 -0
- package/dist/server/src/routes/index.d.ts.map +1 -0
- package/dist/server/src/routes/release-action.d.ts +18 -0
- package/dist/server/src/routes/release-action.d.ts.map +1 -0
- package/dist/server/src/routes/release.d.ts +18 -0
- package/dist/server/src/routes/release.d.ts.map +1 -0
- package/dist/server/src/routes/settings.d.ts +18 -0
- package/dist/server/src/routes/settings.d.ts.map +1 -0
- package/dist/server/src/services/index.d.ts +1828 -0
- package/dist/server/src/services/index.d.ts.map +1 -0
- package/dist/server/src/services/release-action.d.ts +38 -0
- package/dist/server/src/services/release-action.d.ts.map +1 -0
- package/dist/server/src/services/release.d.ts +31 -0
- package/dist/server/src/services/release.d.ts.map +1 -0
- package/dist/server/src/services/scheduling.d.ts +18 -0
- package/dist/server/src/services/scheduling.d.ts.map +1 -0
- package/dist/server/src/services/settings.d.ts +13 -0
- package/dist/server/src/services/settings.d.ts.map +1 -0
- package/dist/server/src/services/validation.d.ts +18 -0
- package/dist/server/src/services/validation.d.ts.map +1 -0
- package/dist/server/src/utils/index.d.ts +35 -0
- package/dist/server/src/utils/index.d.ts.map +1 -0
- package/dist/shared/contracts/release-actions.d.ts +130 -0
- package/dist/shared/contracts/release-actions.d.ts.map +1 -0
- package/dist/shared/contracts/releases.d.ts +184 -0
- package/dist/shared/contracts/releases.d.ts.map +1 -0
- package/dist/shared/contracts/settings.d.ts +39 -0
- package/dist/shared/contracts/settings.d.ts.map +1 -0
- package/dist/shared/types.d.ts +24 -0
- package/dist/shared/types.d.ts.map +1 -0
- package/package.json +33 -38
- package/dist/_chunks/App-bpzO2Ljh.mjs.map +0 -1
- package/dist/_chunks/App-p8aKBitd.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-AECgcaDa.mjs +0 -1013
- package/dist/_chunks/index-AECgcaDa.mjs.map +0 -1
- package/dist/_chunks/index-fP3qoWZ4.js +0 -1034
- package/dist/_chunks/index-fP3qoWZ4.js.map +0 -1
- package/strapi-server.js +0 -3
package/dist/server/index.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { contentTypes as contentTypes$1,
|
|
1
|
+
import { contentTypes as contentTypes$1, async, setCreatorFields, errors, yup as yup$1, validateYupSchema } from "@strapi/utils";
|
|
2
2
|
import isEqual from "lodash/isEqual";
|
|
3
3
|
import { difference, keys } from "lodash";
|
|
4
4
|
import _ from "lodash/fp";
|
|
5
|
-
import EE from "@strapi/strapi/dist/utils/ee";
|
|
6
5
|
import { scheduleJob } from "node-schedule";
|
|
7
6
|
import * as yup from "yup";
|
|
8
7
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
@@ -49,21 +48,38 @@ const ACTIONS = [
|
|
|
49
48
|
displayName: "Add an entry to a release",
|
|
50
49
|
uid: "create-action",
|
|
51
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"
|
|
52
68
|
}
|
|
53
69
|
];
|
|
54
70
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
55
71
|
RELEASES_PUBLISH: "releases.publish"
|
|
56
72
|
};
|
|
57
|
-
const getService = (name, { strapi: strapi2 }
|
|
73
|
+
const getService = (name, { strapi: strapi2 }) => {
|
|
58
74
|
return strapi2.plugin("content-releases").service(name);
|
|
59
75
|
};
|
|
60
|
-
const
|
|
76
|
+
const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi: strapi2 }) => {
|
|
61
77
|
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
62
|
-
const populate = await populateBuilderService(
|
|
63
|
-
const entry = await
|
|
64
|
-
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 });
|
|
65
81
|
};
|
|
66
|
-
const
|
|
82
|
+
const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
|
|
67
83
|
try {
|
|
68
84
|
await strapi2.entityValidator.validateEntityCreation(
|
|
69
85
|
strapi2.getModel(contentTypeUid),
|
|
@@ -77,6 +93,38 @@ const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } =
|
|
|
77
93
|
return false;
|
|
78
94
|
}
|
|
79
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
|
+
};
|
|
80
128
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
81
129
|
oldContentTypes,
|
|
82
130
|
contentTypes: contentTypes2
|
|
@@ -98,7 +146,7 @@ async function deleteActionsOnDisableDraftAndPublish({
|
|
|
98
146
|
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
99
147
|
const deletedContentTypes = difference(keys(oldContentTypes), keys(contentTypes2)) ?? [];
|
|
100
148
|
if (deletedContentTypes.length) {
|
|
101
|
-
await
|
|
149
|
+
await async.map(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
102
150
|
return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
|
|
103
151
|
});
|
|
104
152
|
}
|
|
@@ -117,25 +165,27 @@ async function migrateIsValidAndStatusReleases() {
|
|
|
117
165
|
}
|
|
118
166
|
}
|
|
119
167
|
});
|
|
120
|
-
|
|
168
|
+
async.map(releasesWithoutStatus, async (release2) => {
|
|
121
169
|
const actions = release2.actions;
|
|
122
170
|
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
123
171
|
for (const action of notValidatedActions) {
|
|
124
172
|
if (action.entry) {
|
|
125
|
-
const
|
|
126
|
-
|
|
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
|
+
}
|
|
127
188
|
});
|
|
128
|
-
if (populatedEntry) {
|
|
129
|
-
const isEntryValid = getEntryValidStatus(action.contentType, populatedEntry, { strapi });
|
|
130
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
131
|
-
where: {
|
|
132
|
-
id: action.id
|
|
133
|
-
},
|
|
134
|
-
data: {
|
|
135
|
-
isEntryValid
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
189
|
}
|
|
140
190
|
}
|
|
141
191
|
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
@@ -148,7 +198,7 @@ async function migrateIsValidAndStatusReleases() {
|
|
|
148
198
|
}
|
|
149
199
|
}
|
|
150
200
|
});
|
|
151
|
-
|
|
201
|
+
async.map(publishedReleases, async (release2) => {
|
|
152
202
|
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
153
203
|
where: {
|
|
154
204
|
id: release2.id
|
|
@@ -165,7 +215,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
|
|
|
165
215
|
(uid) => oldContentTypes[uid]?.options?.draftAndPublish
|
|
166
216
|
);
|
|
167
217
|
const releasesAffected = /* @__PURE__ */ new Set();
|
|
168
|
-
|
|
218
|
+
async.map(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
|
|
169
219
|
const oldContentType = oldContentTypes[contentTypeUID];
|
|
170
220
|
const contentType = contentTypes2[contentTypeUID];
|
|
171
221
|
if (!isEqual(oldContentType?.attributes, contentType?.attributes)) {
|
|
@@ -178,30 +228,30 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
|
|
|
178
228
|
release: true
|
|
179
229
|
}
|
|
180
230
|
});
|
|
181
|
-
await
|
|
182
|
-
if (action.entry && action.release) {
|
|
183
|
-
const
|
|
184
|
-
|
|
231
|
+
await async.map(actions, async (action) => {
|
|
232
|
+
if (action.entry && action.release && action.type === "publish") {
|
|
233
|
+
const isEntryValid2 = await getDraftEntryValidStatus(
|
|
234
|
+
{
|
|
235
|
+
contentType: contentTypeUID,
|
|
236
|
+
documentId: action.entryDocumentId,
|
|
237
|
+
locale: action.locale
|
|
238
|
+
},
|
|
239
|
+
{ strapi }
|
|
240
|
+
);
|
|
241
|
+
releasesAffected.add(action.release.id);
|
|
242
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
243
|
+
where: {
|
|
244
|
+
id: action.id
|
|
245
|
+
},
|
|
246
|
+
data: {
|
|
247
|
+
isEntryValid: isEntryValid2
|
|
248
|
+
}
|
|
185
249
|
});
|
|
186
|
-
if (populatedEntry) {
|
|
187
|
-
const isEntryValid = await getEntryValidStatus(contentTypeUID, populatedEntry, {
|
|
188
|
-
strapi
|
|
189
|
-
});
|
|
190
|
-
releasesAffected.add(action.release.id);
|
|
191
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
192
|
-
where: {
|
|
193
|
-
id: action.id
|
|
194
|
-
},
|
|
195
|
-
data: {
|
|
196
|
-
isEntryValid
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
250
|
}
|
|
201
251
|
});
|
|
202
252
|
}
|
|
203
253
|
}).then(() => {
|
|
204
|
-
|
|
254
|
+
async.map(releasesAffected, async (releaseId) => {
|
|
205
255
|
return getService("release", { strapi }).updateReleaseStatus(releaseId);
|
|
206
256
|
});
|
|
207
257
|
});
|
|
@@ -211,13 +261,16 @@ async function disableContentTypeLocalized({ oldContentTypes, contentTypes: cont
|
|
|
211
261
|
if (!oldContentTypes) {
|
|
212
262
|
return;
|
|
213
263
|
}
|
|
264
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
265
|
+
if (!i18nPlugin) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
214
268
|
for (const uid in contentTypes2) {
|
|
215
269
|
if (!oldContentTypes[uid]) {
|
|
216
270
|
continue;
|
|
217
271
|
}
|
|
218
272
|
const oldContentType = oldContentTypes[uid];
|
|
219
273
|
const contentType = contentTypes2[uid];
|
|
220
|
-
const i18nPlugin = strapi.plugin("i18n");
|
|
221
274
|
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
222
275
|
if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
|
|
223
276
|
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
@@ -230,13 +283,16 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
|
|
|
230
283
|
if (!oldContentTypes) {
|
|
231
284
|
return;
|
|
232
285
|
}
|
|
286
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
287
|
+
if (!i18nPlugin) {
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
233
290
|
for (const uid in contentTypes2) {
|
|
234
291
|
if (!oldContentTypes[uid]) {
|
|
235
292
|
continue;
|
|
236
293
|
}
|
|
237
294
|
const oldContentType = oldContentTypes[uid];
|
|
238
295
|
const contentType = contentTypes2[uid];
|
|
239
|
-
const i18nPlugin = strapi.plugin("i18n");
|
|
240
296
|
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
241
297
|
const { getDefaultLocale } = i18nPlugin.service("locales");
|
|
242
298
|
if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
|
|
@@ -247,11 +303,43 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
|
|
|
247
303
|
}
|
|
248
304
|
}
|
|
249
305
|
}
|
|
250
|
-
const
|
|
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
|
+
};
|
|
251
338
|
const register = async ({ strapi: strapi2 }) => {
|
|
252
|
-
if (features
|
|
253
|
-
await strapi2.admin
|
|
254
|
-
strapi2.
|
|
339
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
340
|
+
await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
|
|
341
|
+
strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
|
|
342
|
+
strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
|
|
255
343
|
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
256
344
|
}
|
|
257
345
|
if (strapi2.plugin("graphql")) {
|
|
@@ -260,150 +348,151 @@ const register = async ({ strapi: strapi2 }) => {
|
|
|
260
348
|
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
261
349
|
}
|
|
262
350
|
};
|
|
263
|
-
const
|
|
351
|
+
const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
|
|
352
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
353
|
+
where: {
|
|
354
|
+
actions: {
|
|
355
|
+
contentType,
|
|
356
|
+
entryDocumentId: entry.documentId,
|
|
357
|
+
locale: entry.locale
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
const entryStatus = await isEntryValid(contentType, entry, { strapi });
|
|
362
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
363
|
+
where: {
|
|
364
|
+
contentType,
|
|
365
|
+
entryDocumentId: entry.documentId,
|
|
366
|
+
locale: entry.locale
|
|
367
|
+
},
|
|
368
|
+
data: {
|
|
369
|
+
isEntryValid: entryStatus
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
for (const release2 of releases) {
|
|
373
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
const deleteActionsAndUpdateReleaseStatus = async (params) => {
|
|
377
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
378
|
+
where: {
|
|
379
|
+
actions: params
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
383
|
+
where: params
|
|
384
|
+
});
|
|
385
|
+
for (const release2 of releases) {
|
|
386
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
const deleteActionsOnDelete = async (ctx, next) => {
|
|
390
|
+
if (ctx.action !== "delete") {
|
|
391
|
+
return next();
|
|
392
|
+
}
|
|
393
|
+
if (!contentTypes$1.hasDraftAndPublish(ctx.contentType)) {
|
|
394
|
+
return next();
|
|
395
|
+
}
|
|
396
|
+
const contentType = ctx.contentType.uid;
|
|
397
|
+
const { documentId, locale } = ctx.params;
|
|
398
|
+
const result = await next();
|
|
399
|
+
if (!result) {
|
|
400
|
+
return result;
|
|
401
|
+
}
|
|
402
|
+
try {
|
|
403
|
+
deleteActionsAndUpdateReleaseStatus({
|
|
404
|
+
contentType,
|
|
405
|
+
entryDocumentId: documentId,
|
|
406
|
+
...locale !== "*" && { locale }
|
|
407
|
+
});
|
|
408
|
+
} catch (error) {
|
|
409
|
+
strapi.log.error("Error while deleting release actions after delete", {
|
|
410
|
+
error
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
return result;
|
|
414
|
+
};
|
|
415
|
+
const updateActionsOnUpdate = async (ctx, next) => {
|
|
416
|
+
if (ctx.action !== "update") {
|
|
417
|
+
return next();
|
|
418
|
+
}
|
|
419
|
+
if (!contentTypes$1.hasDraftAndPublish(ctx.contentType)) {
|
|
420
|
+
return next();
|
|
421
|
+
}
|
|
422
|
+
const contentType = ctx.contentType.uid;
|
|
423
|
+
const result = await next();
|
|
424
|
+
if (!result) {
|
|
425
|
+
return result;
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
updateActionsStatusAndUpdateReleaseStatus(contentType, result);
|
|
429
|
+
} catch (error) {
|
|
430
|
+
strapi.log.error("Error while updating release actions after update", {
|
|
431
|
+
error
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
return result;
|
|
435
|
+
};
|
|
436
|
+
const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
|
|
437
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
438
|
+
where: {
|
|
439
|
+
actions: params
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
443
|
+
where: params
|
|
444
|
+
});
|
|
445
|
+
for (const release2 of releases) {
|
|
446
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
447
|
+
}
|
|
448
|
+
};
|
|
264
449
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
265
|
-
if (features
|
|
450
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
266
451
|
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
267
452
|
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
268
453
|
);
|
|
269
454
|
strapi2.db.lifecycles.subscribe({
|
|
270
455
|
models: contentTypesWithDraftAndPublish,
|
|
271
|
-
async afterDelete(event) {
|
|
272
|
-
try {
|
|
273
|
-
const { model, result } = event;
|
|
274
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
275
|
-
const { id } = result;
|
|
276
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
277
|
-
where: {
|
|
278
|
-
actions: {
|
|
279
|
-
target_type: model.uid,
|
|
280
|
-
target_id: id
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
285
|
-
where: {
|
|
286
|
-
target_type: model.uid,
|
|
287
|
-
target_id: id
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
for (const release2 of releases) {
|
|
291
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
} catch (error) {
|
|
295
|
-
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
296
|
-
}
|
|
297
|
-
},
|
|
298
456
|
/**
|
|
299
|
-
* deleteMany
|
|
300
|
-
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
301
|
-
*/
|
|
302
|
-
async beforeDeleteMany(event) {
|
|
303
|
-
const { model, params } = event;
|
|
304
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
305
|
-
const { where } = params;
|
|
306
|
-
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
307
|
-
event.state.entriesToDelete = entriesToDelete;
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
/**
|
|
311
|
-
* We delete the release actions related to deleted entries
|
|
312
|
-
* 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
|
|
313
458
|
*/
|
|
314
459
|
async afterDeleteMany(event) {
|
|
315
460
|
try {
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
target_id: {
|
|
324
|
-
$in: entriesToDelete.map(
|
|
325
|
-
(entry) => entry.id
|
|
326
|
-
)
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
332
|
-
where: {
|
|
333
|
-
target_type: model.uid,
|
|
334
|
-
target_id: {
|
|
335
|
-
$in: entriesToDelete.map((entry) => entry.id)
|
|
336
|
-
}
|
|
337
|
-
}
|
|
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 }
|
|
338
468
|
});
|
|
339
|
-
for (const release2 of releases) {
|
|
340
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
341
|
-
}
|
|
342
469
|
}
|
|
343
470
|
} catch (error) {
|
|
344
471
|
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
345
472
|
error
|
|
346
473
|
});
|
|
347
474
|
}
|
|
348
|
-
},
|
|
349
|
-
async afterUpdate(event) {
|
|
350
|
-
try {
|
|
351
|
-
const { model, result } = event;
|
|
352
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
353
|
-
const isEntryValid = await getEntryValidStatus(
|
|
354
|
-
model.uid,
|
|
355
|
-
result,
|
|
356
|
-
{
|
|
357
|
-
strapi: strapi2
|
|
358
|
-
}
|
|
359
|
-
);
|
|
360
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
361
|
-
where: {
|
|
362
|
-
target_type: model.uid,
|
|
363
|
-
target_id: result.id
|
|
364
|
-
},
|
|
365
|
-
data: {
|
|
366
|
-
isEntryValid
|
|
367
|
-
}
|
|
368
|
-
});
|
|
369
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
370
|
-
where: {
|
|
371
|
-
actions: {
|
|
372
|
-
target_type: model.uid,
|
|
373
|
-
target_id: result.id
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
});
|
|
377
|
-
for (const release2 of releases) {
|
|
378
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
} catch (error) {
|
|
382
|
-
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
383
|
-
}
|
|
384
475
|
}
|
|
385
476
|
});
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
}
|
|
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
|
+
});
|
|
397
488
|
}
|
|
398
489
|
};
|
|
399
490
|
const destroy = async ({ strapi: strapi2 }) => {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
job.cancel();
|
|
406
|
-
}
|
|
491
|
+
const scheduledJobs = getService("scheduling", {
|
|
492
|
+
strapi: strapi2
|
|
493
|
+
}).getAll();
|
|
494
|
+
for (const [, job] of scheduledJobs) {
|
|
495
|
+
job.cancel();
|
|
407
496
|
}
|
|
408
497
|
};
|
|
409
498
|
const schema$1 = {
|
|
@@ -478,15 +567,13 @@ const schema = {
|
|
|
478
567
|
enum: ["publish", "unpublish"],
|
|
479
568
|
required: true
|
|
480
569
|
},
|
|
481
|
-
entry: {
|
|
482
|
-
type: "relation",
|
|
483
|
-
relation: "morphToOne",
|
|
484
|
-
configurable: false
|
|
485
|
-
},
|
|
486
570
|
contentType: {
|
|
487
571
|
type: "string",
|
|
488
572
|
required: true
|
|
489
573
|
},
|
|
574
|
+
entryDocumentId: {
|
|
575
|
+
type: "string"
|
|
576
|
+
},
|
|
490
577
|
locale: {
|
|
491
578
|
type: "string"
|
|
492
579
|
},
|
|
@@ -508,18 +595,6 @@ const contentTypes = {
|
|
|
508
595
|
release: release$1,
|
|
509
596
|
"release-action": releaseAction$1
|
|
510
597
|
};
|
|
511
|
-
const getGroupName = (queryValue) => {
|
|
512
|
-
switch (queryValue) {
|
|
513
|
-
case "contentType":
|
|
514
|
-
return "contentType.displayName";
|
|
515
|
-
case "action":
|
|
516
|
-
return "type";
|
|
517
|
-
case "locale":
|
|
518
|
-
return _.getOr("No locale", "locale.name");
|
|
519
|
-
default:
|
|
520
|
-
return "contentType.displayName";
|
|
521
|
-
}
|
|
522
|
-
};
|
|
523
598
|
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
524
599
|
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
525
600
|
strapi2.eventHub.emit(event, {
|
|
@@ -528,93 +603,32 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
528
603
|
release: release2
|
|
529
604
|
});
|
|
530
605
|
};
|
|
531
|
-
const publishSingleTypeAction = async (uid, actionType, entryId) => {
|
|
532
|
-
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
533
|
-
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
534
|
-
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
535
|
-
const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
|
|
536
|
-
try {
|
|
537
|
-
if (actionType === "publish") {
|
|
538
|
-
await entityManagerService.publish(entry, uid);
|
|
539
|
-
} else {
|
|
540
|
-
await entityManagerService.unpublish(entry, uid);
|
|
541
|
-
}
|
|
542
|
-
} catch (error) {
|
|
543
|
-
if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
|
|
544
|
-
;
|
|
545
|
-
else {
|
|
546
|
-
throw error;
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
};
|
|
550
|
-
const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
|
|
551
|
-
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
552
|
-
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
553
|
-
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
554
|
-
const entriesToPublish = await strapi2.entityService.findMany(uid, {
|
|
555
|
-
filters: {
|
|
556
|
-
id: {
|
|
557
|
-
$in: entriesToPublishIds
|
|
558
|
-
}
|
|
559
|
-
},
|
|
560
|
-
populate
|
|
561
|
-
});
|
|
562
|
-
const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
|
|
563
|
-
filters: {
|
|
564
|
-
id: {
|
|
565
|
-
$in: entriestoUnpublishIds
|
|
566
|
-
}
|
|
567
|
-
},
|
|
568
|
-
populate
|
|
569
|
-
});
|
|
570
|
-
if (entriesToPublish.length > 0) {
|
|
571
|
-
await entityManagerService.publishMany(entriesToPublish, uid);
|
|
572
|
-
}
|
|
573
|
-
if (entriesToUnpublish.length > 0) {
|
|
574
|
-
await entityManagerService.unpublishMany(entriesToUnpublish, uid);
|
|
575
|
-
}
|
|
576
|
-
};
|
|
577
606
|
const getFormattedActions = async (releaseId) => {
|
|
578
607
|
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
579
608
|
where: {
|
|
580
609
|
release: {
|
|
581
610
|
id: releaseId
|
|
582
611
|
}
|
|
583
|
-
},
|
|
584
|
-
populate: {
|
|
585
|
-
entry: {
|
|
586
|
-
fields: ["id"]
|
|
587
|
-
}
|
|
588
612
|
}
|
|
589
613
|
});
|
|
590
614
|
if (actions.length === 0) {
|
|
591
615
|
throw new errors.ValidationError("No entries to publish");
|
|
592
616
|
}
|
|
593
|
-
const
|
|
594
|
-
const singleTypeActions = [];
|
|
617
|
+
const formattedActions = {};
|
|
595
618
|
for (const action of actions) {
|
|
596
619
|
const contentTypeUid = action.contentType;
|
|
597
|
-
if (
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
};
|
|
603
|
-
}
|
|
604
|
-
if (action.type === "publish") {
|
|
605
|
-
collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
|
|
606
|
-
} else {
|
|
607
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
608
|
-
}
|
|
609
|
-
} else {
|
|
610
|
-
singleTypeActions.push({
|
|
611
|
-
uid: contentTypeUid,
|
|
612
|
-
action: action.type,
|
|
613
|
-
id: action.entry.id
|
|
614
|
-
});
|
|
620
|
+
if (!formattedActions[contentTypeUid]) {
|
|
621
|
+
formattedActions[contentTypeUid] = {
|
|
622
|
+
publish: [],
|
|
623
|
+
unpublish: []
|
|
624
|
+
};
|
|
615
625
|
}
|
|
626
|
+
formattedActions[contentTypeUid][action.type].push({
|
|
627
|
+
documentId: action.entryDocumentId,
|
|
628
|
+
locale: action.locale
|
|
629
|
+
});
|
|
616
630
|
}
|
|
617
|
-
return
|
|
631
|
+
return formattedActions;
|
|
618
632
|
};
|
|
619
633
|
return {
|
|
620
634
|
async create(releaseData, { user }) {
|
|
@@ -629,13 +643,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
629
643
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
630
644
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
631
645
|
]);
|
|
632
|
-
const release2 = await strapi2.
|
|
646
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
|
|
633
647
|
data: {
|
|
634
648
|
...releaseWithCreatorFields,
|
|
635
649
|
status: "empty"
|
|
636
650
|
}
|
|
637
651
|
});
|
|
638
|
-
if (
|
|
652
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
639
653
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
640
654
|
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
641
655
|
}
|
|
@@ -643,94 +657,28 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
643
657
|
return release2;
|
|
644
658
|
},
|
|
645
659
|
async findOne(id, query = {}) {
|
|
646
|
-
const
|
|
647
|
-
|
|
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 }
|
|
648
664
|
});
|
|
649
665
|
return release2;
|
|
650
666
|
},
|
|
651
667
|
findPage(query) {
|
|
652
|
-
|
|
653
|
-
|
|
668
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
669
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findPage({
|
|
670
|
+
...dbQuery,
|
|
654
671
|
populate: {
|
|
655
672
|
actions: {
|
|
656
|
-
// @ts-expect-error Ignore missing properties
|
|
657
673
|
count: true
|
|
658
674
|
}
|
|
659
675
|
}
|
|
660
676
|
});
|
|
661
677
|
},
|
|
662
|
-
|
|
663
|
-
const
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
target_type: contentTypeUid,
|
|
667
|
-
target_id: entryId
|
|
668
|
-
},
|
|
669
|
-
releasedAt: {
|
|
670
|
-
$null: true
|
|
671
|
-
}
|
|
672
|
-
},
|
|
673
|
-
populate: {
|
|
674
|
-
// Filter the action to get only the content type entry
|
|
675
|
-
actions: {
|
|
676
|
-
where: {
|
|
677
|
-
target_type: contentTypeUid,
|
|
678
|
-
target_id: entryId
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
});
|
|
683
|
-
return releases.map((release2) => {
|
|
684
|
-
if (release2.actions?.length) {
|
|
685
|
-
const [actionForEntry] = release2.actions;
|
|
686
|
-
delete release2.actions;
|
|
687
|
-
return {
|
|
688
|
-
...release2,
|
|
689
|
-
action: actionForEntry
|
|
690
|
-
};
|
|
691
|
-
}
|
|
692
|
-
return release2;
|
|
693
|
-
});
|
|
694
|
-
},
|
|
695
|
-
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
696
|
-
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
697
|
-
where: {
|
|
698
|
-
releasedAt: {
|
|
699
|
-
$null: true
|
|
700
|
-
},
|
|
701
|
-
actions: {
|
|
702
|
-
target_type: contentTypeUid,
|
|
703
|
-
target_id: entryId
|
|
704
|
-
}
|
|
705
|
-
}
|
|
706
|
-
});
|
|
707
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
708
|
-
where: {
|
|
709
|
-
$or: [
|
|
710
|
-
{
|
|
711
|
-
id: {
|
|
712
|
-
$notIn: releasesRelated.map((release2) => release2.id)
|
|
713
|
-
}
|
|
714
|
-
},
|
|
715
|
-
{
|
|
716
|
-
actions: null
|
|
717
|
-
}
|
|
718
|
-
],
|
|
719
|
-
releasedAt: {
|
|
720
|
-
$null: true
|
|
721
|
-
}
|
|
722
|
-
}
|
|
723
|
-
});
|
|
724
|
-
return releases.map((release2) => {
|
|
725
|
-
if (release2.actions?.length) {
|
|
726
|
-
const [actionForEntry] = release2.actions;
|
|
727
|
-
delete release2.actions;
|
|
728
|
-
return {
|
|
729
|
-
...release2,
|
|
730
|
-
action: actionForEntry
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
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
|
|
734
682
|
});
|
|
735
683
|
},
|
|
736
684
|
async update(id, releaseData, { user }) {
|
|
@@ -745,157 +693,27 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
745
693
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
746
694
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
747
695
|
]);
|
|
748
|
-
const release2 = await strapi2.
|
|
696
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
|
|
749
697
|
if (!release2) {
|
|
750
698
|
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
751
699
|
}
|
|
752
700
|
if (release2.releasedAt) {
|
|
753
701
|
throw new errors.ValidationError("Release already published");
|
|
754
702
|
}
|
|
755
|
-
const updatedRelease = await strapi2.
|
|
756
|
-
|
|
757
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
758
|
-
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
759
|
-
*/
|
|
760
|
-
// @ts-expect-error see above
|
|
703
|
+
const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
704
|
+
where: { id },
|
|
761
705
|
data: releaseWithCreatorFields
|
|
762
706
|
});
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
schedulingService.cancel(id);
|
|
769
|
-
}
|
|
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);
|
|
770
712
|
}
|
|
771
713
|
this.updateReleaseStatus(id);
|
|
772
714
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
773
715
|
return updatedRelease;
|
|
774
716
|
},
|
|
775
|
-
async createAction(releaseId, action) {
|
|
776
|
-
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
777
|
-
strapi: strapi2
|
|
778
|
-
});
|
|
779
|
-
await Promise.all([
|
|
780
|
-
validateEntryContentType(action.entry.contentType),
|
|
781
|
-
validateUniqueEntry(releaseId, action)
|
|
782
|
-
]);
|
|
783
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
784
|
-
if (!release2) {
|
|
785
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
786
|
-
}
|
|
787
|
-
if (release2.releasedAt) {
|
|
788
|
-
throw new errors.ValidationError("Release already published");
|
|
789
|
-
}
|
|
790
|
-
const { entry, type } = action;
|
|
791
|
-
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
792
|
-
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
793
|
-
const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
794
|
-
data: {
|
|
795
|
-
type,
|
|
796
|
-
contentType: entry.contentType,
|
|
797
|
-
locale: entry.locale,
|
|
798
|
-
isEntryValid,
|
|
799
|
-
entry: {
|
|
800
|
-
id: entry.id,
|
|
801
|
-
__type: entry.contentType,
|
|
802
|
-
__pivot: { field: "entry" }
|
|
803
|
-
},
|
|
804
|
-
release: releaseId
|
|
805
|
-
},
|
|
806
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
807
|
-
});
|
|
808
|
-
this.updateReleaseStatus(releaseId);
|
|
809
|
-
return releaseAction2;
|
|
810
|
-
},
|
|
811
|
-
async findActions(releaseId, query) {
|
|
812
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
813
|
-
fields: ["id"]
|
|
814
|
-
});
|
|
815
|
-
if (!release2) {
|
|
816
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
817
|
-
}
|
|
818
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
819
|
-
...query,
|
|
820
|
-
populate: {
|
|
821
|
-
entry: {
|
|
822
|
-
populate: "*"
|
|
823
|
-
}
|
|
824
|
-
},
|
|
825
|
-
filters: {
|
|
826
|
-
release: releaseId
|
|
827
|
-
}
|
|
828
|
-
});
|
|
829
|
-
},
|
|
830
|
-
async countActions(query) {
|
|
831
|
-
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
832
|
-
},
|
|
833
|
-
async groupActions(actions, groupBy) {
|
|
834
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
835
|
-
if (!acc.includes(action.contentType)) {
|
|
836
|
-
acc.push(action.contentType);
|
|
837
|
-
}
|
|
838
|
-
return acc;
|
|
839
|
-
}, []);
|
|
840
|
-
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
841
|
-
contentTypeUids
|
|
842
|
-
);
|
|
843
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
844
|
-
const formattedData = actions.map((action) => {
|
|
845
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
846
|
-
return {
|
|
847
|
-
...action,
|
|
848
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
849
|
-
contentType: {
|
|
850
|
-
displayName,
|
|
851
|
-
mainFieldValue: action.entry[mainField],
|
|
852
|
-
uid: action.contentType
|
|
853
|
-
}
|
|
854
|
-
};
|
|
855
|
-
});
|
|
856
|
-
const groupName = getGroupName(groupBy);
|
|
857
|
-
return _.groupBy(groupName)(formattedData);
|
|
858
|
-
},
|
|
859
|
-
async getLocalesDataForActions() {
|
|
860
|
-
if (!strapi2.plugin("i18n")) {
|
|
861
|
-
return {};
|
|
862
|
-
}
|
|
863
|
-
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
864
|
-
return allLocales.reduce((acc, locale) => {
|
|
865
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
866
|
-
return acc;
|
|
867
|
-
}, {});
|
|
868
|
-
},
|
|
869
|
-
async getContentTypesDataForActions(contentTypesUids) {
|
|
870
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
871
|
-
const contentTypesData = {};
|
|
872
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
873
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
874
|
-
uid: contentTypeUid
|
|
875
|
-
});
|
|
876
|
-
contentTypesData[contentTypeUid] = {
|
|
877
|
-
mainField: contentTypeConfig.settings.mainField,
|
|
878
|
-
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
879
|
-
};
|
|
880
|
-
}
|
|
881
|
-
return contentTypesData;
|
|
882
|
-
},
|
|
883
|
-
getContentTypeModelsFromActions(actions) {
|
|
884
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
885
|
-
if (!acc.includes(action.contentType)) {
|
|
886
|
-
acc.push(action.contentType);
|
|
887
|
-
}
|
|
888
|
-
return acc;
|
|
889
|
-
}, []);
|
|
890
|
-
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
891
|
-
(acc, contentTypeUid) => {
|
|
892
|
-
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
893
|
-
return acc;
|
|
894
|
-
},
|
|
895
|
-
{}
|
|
896
|
-
);
|
|
897
|
-
return contentTypeModelsMap;
|
|
898
|
-
},
|
|
899
717
|
async getAllComponents() {
|
|
900
718
|
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
901
719
|
const components = await contentManagerComponentsService.findAllComponents();
|
|
@@ -909,10 +727,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
909
727
|
return componentsMap;
|
|
910
728
|
},
|
|
911
729
|
async delete(releaseId) {
|
|
912
|
-
const release2 = await strapi2.
|
|
730
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
731
|
+
where: { id: releaseId },
|
|
913
732
|
populate: {
|
|
914
733
|
actions: {
|
|
915
|
-
|
|
734
|
+
select: ["id"]
|
|
916
735
|
}
|
|
917
736
|
}
|
|
918
737
|
});
|
|
@@ -930,9 +749,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
930
749
|
}
|
|
931
750
|
}
|
|
932
751
|
});
|
|
933
|
-
await strapi2.
|
|
752
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
753
|
+
where: {
|
|
754
|
+
id: releaseId
|
|
755
|
+
}
|
|
756
|
+
});
|
|
934
757
|
});
|
|
935
|
-
if (
|
|
758
|
+
if (release2.scheduledAt) {
|
|
936
759
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
937
760
|
await schedulingService.cancel(release2.id);
|
|
938
761
|
}
|
|
@@ -956,22 +779,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
956
779
|
}
|
|
957
780
|
try {
|
|
958
781
|
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
959
|
-
const
|
|
960
|
-
|
|
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
|
+
)
|
|
961
794
|
);
|
|
962
|
-
await strapi2.db.transaction(async () => {
|
|
963
|
-
for (const { uid, action, id } of singleTypeActions) {
|
|
964
|
-
await publishSingleTypeAction(uid, action, id);
|
|
965
|
-
}
|
|
966
|
-
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
967
|
-
const uid = contentTypeUid;
|
|
968
|
-
await publishCollectionTypeAction(
|
|
969
|
-
uid,
|
|
970
|
-
collectionTypeActions[uid].entriesToPublishIds,
|
|
971
|
-
collectionTypeActions[uid].entriesToUnpublishIds
|
|
972
|
-
);
|
|
973
|
-
}
|
|
974
|
-
});
|
|
975
795
|
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
976
796
|
where: {
|
|
977
797
|
id: releaseId
|
|
@@ -980,37 +800,272 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
980
800
|
status: "done",
|
|
981
801
|
releasedAt: /* @__PURE__ */ new Date()
|
|
982
802
|
}
|
|
983
|
-
});
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
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", {
|
|
915
|
+
strapi: strapi2
|
|
916
|
+
});
|
|
917
|
+
await Promise.all([
|
|
918
|
+
validateEntryData(action.contentType, action.entryDocumentId),
|
|
919
|
+
validateUniqueEntry(releaseId, action)
|
|
920
|
+
]);
|
|
921
|
+
const model = strapi2.contentType(action.contentType);
|
|
922
|
+
if (model.kind === "singleType") {
|
|
923
|
+
const document = await strapi2.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
924
|
+
if (!document) {
|
|
925
|
+
throw new errors.NotFoundError(`No entry found for contentType ${action.contentType}`);
|
|
926
|
+
}
|
|
927
|
+
action.entryDocumentId = document.documentId;
|
|
928
|
+
}
|
|
929
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
|
|
930
|
+
if (!release2) {
|
|
931
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
932
|
+
}
|
|
933
|
+
if (release2.releasedAt) {
|
|
934
|
+
throw new errors.ValidationError("Release already published");
|
|
935
|
+
}
|
|
936
|
+
const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
|
|
937
|
+
{
|
|
938
|
+
contentType: action.contentType,
|
|
939
|
+
documentId: action.entryDocumentId,
|
|
940
|
+
locale: action.locale
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
strapi: strapi2
|
|
944
|
+
}
|
|
945
|
+
) : true;
|
|
946
|
+
const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
947
|
+
data: {
|
|
948
|
+
...action,
|
|
949
|
+
release: release2.id,
|
|
950
|
+
isEntryValid: actionStatus
|
|
951
|
+
},
|
|
952
|
+
populate: { release: { select: ["id"] } }
|
|
953
|
+
});
|
|
954
|
+
if (!disableUpdateReleaseStatus) {
|
|
955
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
956
|
+
}
|
|
957
|
+
return releaseAction2;
|
|
958
|
+
},
|
|
959
|
+
async findPage(releaseId, query) {
|
|
960
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
961
|
+
where: { id: releaseId },
|
|
962
|
+
select: ["id"]
|
|
963
|
+
});
|
|
964
|
+
if (!release2) {
|
|
965
|
+
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
966
|
+
}
|
|
967
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
968
|
+
const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
969
|
+
...dbQuery,
|
|
970
|
+
where: {
|
|
971
|
+
release: releaseId
|
|
972
|
+
}
|
|
973
|
+
});
|
|
974
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
975
|
+
const actionsWithEntry = await async.map(actions, async (action) => {
|
|
976
|
+
const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
|
|
977
|
+
const entry = await getEntry(
|
|
978
|
+
{
|
|
979
|
+
contentType: action.contentType,
|
|
980
|
+
documentId: action.entryDocumentId,
|
|
981
|
+
locale: action.locale,
|
|
982
|
+
populate,
|
|
983
|
+
status: action.type === "publish" ? "draft" : "published"
|
|
984
|
+
},
|
|
985
|
+
{ strapi: strapi2 }
|
|
986
|
+
);
|
|
987
|
+
return {
|
|
988
|
+
...action,
|
|
989
|
+
entry,
|
|
990
|
+
status: entry ? await getEntryStatus(action.contentType, entry) : null
|
|
991
|
+
};
|
|
992
|
+
});
|
|
993
|
+
return {
|
|
994
|
+
results: actionsWithEntry,
|
|
995
|
+
pagination
|
|
996
|
+
};
|
|
997
|
+
},
|
|
998
|
+
async groupActions(actions, groupBy) {
|
|
999
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
1000
|
+
if (!acc.includes(action.contentType)) {
|
|
1001
|
+
acc.push(action.contentType);
|
|
1002
|
+
}
|
|
1003
|
+
return acc;
|
|
1004
|
+
}, []);
|
|
1005
|
+
const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
|
|
1006
|
+
const allLocalesDictionary = await getLocalesDataForActions();
|
|
1007
|
+
const formattedData = actions.map((action) => {
|
|
1008
|
+
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
1009
|
+
return {
|
|
1010
|
+
...action,
|
|
1011
|
+
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
1012
|
+
contentType: {
|
|
1013
|
+
displayName,
|
|
1014
|
+
mainFieldValue: action.entry[mainField],
|
|
1015
|
+
uid: action.contentType
|
|
1016
|
+
}
|
|
1017
|
+
};
|
|
1018
|
+
});
|
|
1019
|
+
const groupName = getGroupName(groupBy);
|
|
1020
|
+
return _.groupBy(groupName)(formattedData);
|
|
1021
|
+
},
|
|
1022
|
+
getContentTypeModelsFromActions(actions) {
|
|
1023
|
+
const contentTypeUids = actions.reduce((acc, action) => {
|
|
1024
|
+
if (!acc.includes(action.contentType)) {
|
|
1025
|
+
acc.push(action.contentType);
|
|
1026
|
+
}
|
|
1027
|
+
return acc;
|
|
1028
|
+
}, []);
|
|
1029
|
+
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
1030
|
+
(acc, contentTypeUid) => {
|
|
1031
|
+
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
1032
|
+
return acc;
|
|
1033
|
+
},
|
|
1034
|
+
{}
|
|
1035
|
+
);
|
|
1036
|
+
return contentTypeModelsMap;
|
|
1037
|
+
},
|
|
1038
|
+
async countActions(query) {
|
|
1039
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
1040
|
+
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
1041
|
+
},
|
|
1042
|
+
async update(actionId, releaseId, update) {
|
|
1043
|
+
const action = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findOne({
|
|
1044
|
+
where: {
|
|
1045
|
+
id: actionId,
|
|
1046
|
+
release: {
|
|
1047
|
+
id: releaseId,
|
|
1048
|
+
releasedAt: {
|
|
1049
|
+
$null: true
|
|
1050
|
+
}
|
|
998
1051
|
}
|
|
999
|
-
await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
|
|
1000
|
-
status: "failed"
|
|
1001
|
-
}).transacting(trx).execute();
|
|
1002
|
-
return {
|
|
1003
|
-
release: null,
|
|
1004
|
-
error: error2
|
|
1005
|
-
};
|
|
1006
1052
|
}
|
|
1007
1053
|
});
|
|
1008
|
-
if (
|
|
1009
|
-
throw
|
|
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
|
+
);
|
|
1010
1058
|
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
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;
|
|
1014
1069
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
1015
1070
|
where: {
|
|
1016
1071
|
id: actionId,
|
|
@@ -1021,16 +1076,15 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1021
1076
|
}
|
|
1022
1077
|
}
|
|
1023
1078
|
},
|
|
1024
|
-
data:
|
|
1079
|
+
data: {
|
|
1080
|
+
...update,
|
|
1081
|
+
isEntryValid: actionStatus
|
|
1082
|
+
}
|
|
1025
1083
|
});
|
|
1026
|
-
|
|
1027
|
-
throw new errors.NotFoundError(
|
|
1028
|
-
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1029
|
-
);
|
|
1030
|
-
}
|
|
1084
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1031
1085
|
return updatedAction;
|
|
1032
1086
|
},
|
|
1033
|
-
async
|
|
1087
|
+
async delete(actionId, releaseId) {
|
|
1034
1088
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
1035
1089
|
where: {
|
|
1036
1090
|
id: actionId,
|
|
@@ -1047,87 +1101,56 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1047
1101
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1048
1102
|
);
|
|
1049
1103
|
}
|
|
1050
|
-
|
|
1104
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1051
1105
|
return deletedAction;
|
|
1052
|
-
},
|
|
1053
|
-
async updateReleaseStatus(releaseId) {
|
|
1054
|
-
const [totalActions, invalidActions] = await Promise.all([
|
|
1055
|
-
this.countActions({
|
|
1056
|
-
filters: {
|
|
1057
|
-
release: releaseId
|
|
1058
|
-
}
|
|
1059
|
-
}),
|
|
1060
|
-
this.countActions({
|
|
1061
|
-
filters: {
|
|
1062
|
-
release: releaseId,
|
|
1063
|
-
isEntryValid: false
|
|
1064
|
-
}
|
|
1065
|
-
})
|
|
1066
|
-
]);
|
|
1067
|
-
if (totalActions > 0) {
|
|
1068
|
-
if (invalidActions > 0) {
|
|
1069
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1070
|
-
where: {
|
|
1071
|
-
id: releaseId
|
|
1072
|
-
},
|
|
1073
|
-
data: {
|
|
1074
|
-
status: "blocked"
|
|
1075
|
-
}
|
|
1076
|
-
});
|
|
1077
|
-
}
|
|
1078
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1079
|
-
where: {
|
|
1080
|
-
id: releaseId
|
|
1081
|
-
},
|
|
1082
|
-
data: {
|
|
1083
|
-
status: "ready"
|
|
1084
|
-
}
|
|
1085
|
-
});
|
|
1086
|
-
}
|
|
1087
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1088
|
-
where: {
|
|
1089
|
-
id: releaseId
|
|
1090
|
-
},
|
|
1091
|
-
data: {
|
|
1092
|
-
status: "empty"
|
|
1093
|
-
}
|
|
1094
|
-
});
|
|
1095
1106
|
}
|
|
1096
1107
|
};
|
|
1097
1108
|
};
|
|
1109
|
+
class AlreadyOnReleaseError extends errors.ApplicationError {
|
|
1110
|
+
constructor(message) {
|
|
1111
|
+
super(message);
|
|
1112
|
+
this.name = "AlreadyOnReleaseError";
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1098
1115
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
1099
1116
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
1100
|
-
const release2 = await strapi2.
|
|
1101
|
-
|
|
1117
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1118
|
+
where: {
|
|
1119
|
+
id: releaseId
|
|
1120
|
+
},
|
|
1121
|
+
populate: {
|
|
1122
|
+
actions: true
|
|
1123
|
+
}
|
|
1102
1124
|
});
|
|
1103
1125
|
if (!release2) {
|
|
1104
1126
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1105
1127
|
}
|
|
1106
1128
|
const isEntryInRelease = release2.actions.some(
|
|
1107
|
-
(action) =>
|
|
1129
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
1108
1130
|
);
|
|
1109
1131
|
if (isEntryInRelease) {
|
|
1110
|
-
throw new
|
|
1111
|
-
`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}`
|
|
1112
1134
|
);
|
|
1113
1135
|
}
|
|
1114
1136
|
},
|
|
1115
|
-
|
|
1137
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
1116
1138
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
1117
1139
|
if (!contentType) {
|
|
1118
1140
|
throw new errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
1119
1141
|
}
|
|
1120
|
-
if (!contentType
|
|
1142
|
+
if (!contentTypes$1.hasDraftAndPublish(contentType)) {
|
|
1121
1143
|
throw new errors.ValidationError(
|
|
1122
1144
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
1123
1145
|
);
|
|
1124
1146
|
}
|
|
1147
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1148
|
+
throw new errors.ValidationError("Document id is required for collection type");
|
|
1149
|
+
}
|
|
1125
1150
|
},
|
|
1126
1151
|
async validatePendingReleasesLimit() {
|
|
1127
|
-
const
|
|
1128
|
-
|
|
1129
|
-
EE.features.get("cms-content-releases")?.options?.maximumReleases || 3
|
|
1130
|
-
);
|
|
1152
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1153
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
1131
1154
|
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
1132
1155
|
filters: {
|
|
1133
1156
|
releasedAt: {
|
|
@@ -1140,8 +1163,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
1140
1163
|
}
|
|
1141
1164
|
},
|
|
1142
1165
|
async validateUniqueNameForPendingRelease(name, id) {
|
|
1143
|
-
const pendingReleases = await strapi2.
|
|
1144
|
-
|
|
1166
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1167
|
+
where: {
|
|
1145
1168
|
releasedAt: {
|
|
1146
1169
|
$null: true
|
|
1147
1170
|
},
|
|
@@ -1170,7 +1193,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1170
1193
|
}
|
|
1171
1194
|
const job = scheduleJob(scheduleDate, async () => {
|
|
1172
1195
|
try {
|
|
1173
|
-
await getService("release").publish(releaseId);
|
|
1196
|
+
await getService("release", { strapi: strapi2 }).publish(releaseId);
|
|
1174
1197
|
} catch (error) {
|
|
1175
1198
|
}
|
|
1176
1199
|
this.cancel(releaseId);
|
|
@@ -1212,85 +1235,172 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1212
1235
|
}
|
|
1213
1236
|
};
|
|
1214
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
|
+
};
|
|
1215
1259
|
const services = {
|
|
1216
1260
|
release: createReleaseService,
|
|
1261
|
+
"release-action": createReleaseActionService,
|
|
1217
1262
|
"release-validation": createReleaseValidationService,
|
|
1218
|
-
|
|
1263
|
+
scheduling: createSchedulingService,
|
|
1264
|
+
settings: createSettingsService
|
|
1219
1265
|
};
|
|
1220
|
-
const RELEASE_SCHEMA = yup.object().shape({
|
|
1221
|
-
name: yup.string().trim().required(),
|
|
1222
|
-
scheduledAt: yup.string().nullable(),
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
otherwise: yup.string().nullable()
|
|
1228
|
-
}),
|
|
1229
|
-
timezone: yup.string().when("isScheduled", {
|
|
1230
|
-
is: true,
|
|
1231
|
-
then: yup.string().required().nullable(),
|
|
1232
|
-
otherwise: yup.string().nullable()
|
|
1233
|
-
}),
|
|
1234
|
-
date: yup.string().when("isScheduled", {
|
|
1235
|
-
is: true,
|
|
1236
|
-
then: yup.string().required().nullable(),
|
|
1237
|
-
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()
|
|
1238
1273
|
})
|
|
1239
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();
|
|
1240
1281
|
const validateRelease = validateYupSchema(RELEASE_SCHEMA);
|
|
1282
|
+
const validatefindByDocumentAttachedParams = validateYupSchema(
|
|
1283
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1284
|
+
);
|
|
1241
1285
|
const releaseController = {
|
|
1242
|
-
|
|
1243
|
-
|
|
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({
|
|
1244
1293
|
ability: ctx.state.userAbility,
|
|
1245
1294
|
model: RELEASE_MODEL_UID
|
|
1246
1295
|
});
|
|
1247
1296
|
await permissionsManager.validateQuery(ctx.query);
|
|
1248
1297
|
const releaseService = getService("release", { strapi });
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
const
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
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,
|
|
1264
1314
|
actions: {
|
|
1265
|
-
|
|
1266
|
-
|
|
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
|
|
1267
1327
|
}
|
|
1268
1328
|
}
|
|
1269
|
-
}
|
|
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
|
+
}
|
|
1270
1342
|
});
|
|
1271
|
-
const
|
|
1343
|
+
const releases = await releaseService.findMany({
|
|
1272
1344
|
where: {
|
|
1345
|
+
$or: [
|
|
1346
|
+
{
|
|
1347
|
+
id: {
|
|
1348
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1349
|
+
}
|
|
1350
|
+
},
|
|
1351
|
+
{
|
|
1352
|
+
actions: null
|
|
1353
|
+
}
|
|
1354
|
+
],
|
|
1273
1355
|
releasedAt: null
|
|
1274
1356
|
}
|
|
1275
1357
|
});
|
|
1276
|
-
ctx.body = { data
|
|
1358
|
+
ctx.body = { data: releases };
|
|
1277
1359
|
}
|
|
1278
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
|
+
},
|
|
1279
1388
|
async findOne(ctx) {
|
|
1280
1389
|
const id = ctx.params.id;
|
|
1281
1390
|
const releaseService = getService("release", { strapi });
|
|
1391
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1282
1392
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
1283
1393
|
if (!release2) {
|
|
1284
1394
|
throw new errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1285
1395
|
}
|
|
1286
|
-
const count = await
|
|
1396
|
+
const count = await releaseActionService.countActions({
|
|
1287
1397
|
filters: {
|
|
1288
1398
|
release: id
|
|
1289
1399
|
}
|
|
1290
1400
|
});
|
|
1291
1401
|
const sanitizedRelease = {
|
|
1292
1402
|
...release2,
|
|
1293
|
-
createdBy: release2.createdBy ? strapi.admin
|
|
1403
|
+
createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
|
|
1294
1404
|
};
|
|
1295
1405
|
const data = {
|
|
1296
1406
|
...sanitizedRelease,
|
|
@@ -1302,19 +1412,63 @@ const releaseController = {
|
|
|
1302
1412
|
};
|
|
1303
1413
|
ctx.body = { data };
|
|
1304
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
|
+
},
|
|
1305
1459
|
async create(ctx) {
|
|
1306
1460
|
const user = ctx.state.user;
|
|
1307
1461
|
const releaseArgs = ctx.request.body;
|
|
1308
1462
|
await validateRelease(releaseArgs);
|
|
1309
1463
|
const releaseService = getService("release", { strapi });
|
|
1310
1464
|
const release2 = await releaseService.create(releaseArgs, { user });
|
|
1311
|
-
const permissionsManager = strapi.admin
|
|
1465
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1312
1466
|
ability: ctx.state.userAbility,
|
|
1313
1467
|
model: RELEASE_MODEL_UID
|
|
1314
1468
|
});
|
|
1315
|
-
ctx.
|
|
1469
|
+
ctx.created({
|
|
1316
1470
|
data: await permissionsManager.sanitizeOutput(release2)
|
|
1317
|
-
};
|
|
1471
|
+
});
|
|
1318
1472
|
},
|
|
1319
1473
|
async update(ctx) {
|
|
1320
1474
|
const user = ctx.state.user;
|
|
@@ -1323,7 +1477,7 @@ const releaseController = {
|
|
|
1323
1477
|
await validateRelease(releaseArgs);
|
|
1324
1478
|
const releaseService = getService("release", { strapi });
|
|
1325
1479
|
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
1326
|
-
const permissionsManager = strapi.admin
|
|
1480
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1327
1481
|
ability: ctx.state.userAbility,
|
|
1328
1482
|
model: RELEASE_MODEL_UID
|
|
1329
1483
|
});
|
|
@@ -1340,18 +1494,18 @@ const releaseController = {
|
|
|
1340
1494
|
};
|
|
1341
1495
|
},
|
|
1342
1496
|
async publish(ctx) {
|
|
1343
|
-
const user = ctx.state.user;
|
|
1344
1497
|
const id = ctx.params.id;
|
|
1345
1498
|
const releaseService = getService("release", { strapi });
|
|
1346
|
-
const
|
|
1499
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1500
|
+
const release2 = await releaseService.publish(id);
|
|
1347
1501
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1348
|
-
|
|
1502
|
+
releaseActionService.countActions({
|
|
1349
1503
|
filters: {
|
|
1350
1504
|
release: id,
|
|
1351
1505
|
type: "publish"
|
|
1352
1506
|
}
|
|
1353
1507
|
}),
|
|
1354
|
-
|
|
1508
|
+
releaseActionService.countActions({
|
|
1355
1509
|
filters: {
|
|
1356
1510
|
release: id,
|
|
1357
1511
|
type: "unpublish"
|
|
@@ -1369,57 +1523,106 @@ const releaseController = {
|
|
|
1369
1523
|
}
|
|
1370
1524
|
};
|
|
1371
1525
|
const RELEASE_ACTION_SCHEMA = yup$1.object().shape({
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
}).required(),
|
|
1526
|
+
contentType: yup$1.string().required(),
|
|
1527
|
+
entryDocumentId: yup$1.strapiID(),
|
|
1528
|
+
locale: yup$1.string(),
|
|
1376
1529
|
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1377
1530
|
});
|
|
1378
1531
|
const RELEASE_ACTION_UPDATE_SCHEMA = yup$1.object().shape({
|
|
1379
1532
|
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1380
1533
|
});
|
|
1534
|
+
const FIND_MANY_ACTIONS_PARAMS = yup$1.object().shape({
|
|
1535
|
+
groupBy: yup$1.string().oneOf(["action", "contentType", "locale"])
|
|
1536
|
+
});
|
|
1381
1537
|
const validateReleaseAction = validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1382
1538
|
const validateReleaseActionUpdateSchema = validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1539
|
+
const validateFindManyActionsParams = validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1383
1540
|
const releaseActionController = {
|
|
1384
1541
|
async create(ctx) {
|
|
1385
1542
|
const releaseId = ctx.params.releaseId;
|
|
1386
1543
|
const releaseActionArgs = ctx.request.body;
|
|
1387
1544
|
await validateReleaseAction(releaseActionArgs);
|
|
1388
|
-
const
|
|
1389
|
-
const releaseAction2 = await
|
|
1390
|
-
ctx.
|
|
1545
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1546
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1547
|
+
ctx.created({
|
|
1391
1548
|
data: releaseAction2
|
|
1392
|
-
};
|
|
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
|
+
});
|
|
1393
1588
|
},
|
|
1394
1589
|
async findMany(ctx) {
|
|
1395
1590
|
const releaseId = ctx.params.releaseId;
|
|
1396
|
-
const permissionsManager = strapi.admin
|
|
1591
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1397
1592
|
ability: ctx.state.userAbility,
|
|
1398
1593
|
model: RELEASE_ACTION_MODEL_UID
|
|
1399
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;
|
|
1400
1603
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1401
|
-
const
|
|
1402
|
-
const { results, pagination } = await
|
|
1403
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1604
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1605
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1404
1606
|
...query
|
|
1405
1607
|
});
|
|
1406
1608
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
1407
1609
|
if (acc[action.contentType]) {
|
|
1408
1610
|
return acc;
|
|
1409
1611
|
}
|
|
1410
|
-
const contentTypePermissionsManager = strapi.admin
|
|
1612
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1411
1613
|
ability: ctx.state.userAbility,
|
|
1412
1614
|
model: action.contentType
|
|
1413
1615
|
});
|
|
1414
1616
|
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
1415
1617
|
return acc;
|
|
1416
1618
|
}, {});
|
|
1417
|
-
const sanitizedResults = await
|
|
1619
|
+
const sanitizedResults = await async.map(results, async (action) => ({
|
|
1418
1620
|
...action,
|
|
1419
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1621
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1420
1622
|
}));
|
|
1421
|
-
const groupedData = await
|
|
1422
|
-
const contentTypes2 =
|
|
1623
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1624
|
+
const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
|
|
1625
|
+
const releaseService = getService("release", { strapi });
|
|
1423
1626
|
const components = await releaseService.getAllComponents();
|
|
1424
1627
|
ctx.body = {
|
|
1425
1628
|
data: groupedData,
|
|
@@ -1435,8 +1638,8 @@ const releaseActionController = {
|
|
|
1435
1638
|
const releaseId = ctx.params.releaseId;
|
|
1436
1639
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1437
1640
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1438
|
-
const
|
|
1439
|
-
const updatedAction = await
|
|
1641
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1642
|
+
const updatedAction = await releaseActionService.update(
|
|
1440
1643
|
actionId,
|
|
1441
1644
|
releaseId,
|
|
1442
1645
|
releaseActionUpdateArgs
|
|
@@ -1448,17 +1651,71 @@ const releaseActionController = {
|
|
|
1448
1651
|
async delete(ctx) {
|
|
1449
1652
|
const actionId = ctx.params.actionId;
|
|
1450
1653
|
const releaseId = ctx.params.releaseId;
|
|
1451
|
-
const
|
|
1452
|
-
const deletedReleaseAction = await
|
|
1654
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1655
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1453
1656
|
ctx.body = {
|
|
1454
1657
|
data: deletedReleaseAction
|
|
1455
1658
|
};
|
|
1456
1659
|
}
|
|
1457
1660
|
};
|
|
1458
|
-
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
|
+
};
|
|
1459
1684
|
const release = {
|
|
1460
1685
|
type: "admin",
|
|
1461
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
|
+
},
|
|
1462
1719
|
{
|
|
1463
1720
|
method: "POST",
|
|
1464
1721
|
path: "/",
|
|
@@ -1478,7 +1735,7 @@ const release = {
|
|
|
1478
1735
|
{
|
|
1479
1736
|
method: "GET",
|
|
1480
1737
|
path: "/",
|
|
1481
|
-
handler: "release.
|
|
1738
|
+
handler: "release.findPage",
|
|
1482
1739
|
config: {
|
|
1483
1740
|
policies: [
|
|
1484
1741
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1576,6 +1833,22 @@ const releaseAction = {
|
|
|
1576
1833
|
]
|
|
1577
1834
|
}
|
|
1578
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
|
+
},
|
|
1579
1852
|
{
|
|
1580
1853
|
method: "GET",
|
|
1581
1854
|
path: "/:releaseId/actions",
|
|
@@ -1626,13 +1899,50 @@ const releaseAction = {
|
|
|
1626
1899
|
}
|
|
1627
1900
|
]
|
|
1628
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
|
+
};
|
|
1629
1939
|
const routes = {
|
|
1940
|
+
settings,
|
|
1630
1941
|
release,
|
|
1631
1942
|
"release-action": releaseAction
|
|
1632
1943
|
};
|
|
1633
|
-
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
1634
1944
|
const getPlugin = () => {
|
|
1635
|
-
if (features.isEnabled("cms-content-releases")) {
|
|
1945
|
+
if (strapi.ee.features.isEnabled("cms-content-releases")) {
|
|
1636
1946
|
return {
|
|
1637
1947
|
register,
|
|
1638
1948
|
bootstrap,
|