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