@strapi/content-releases 0.0.0-next.b6d552f6e63dec5627cb8611ab2adcb8244359be → 0.0.0-next.bb6ff32f5168f3e380d3d9acba90a9d53bfcfb89
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-BX6_LcmS.mjs +1559 -0
- package/dist/_chunks/App-BX6_LcmS.mjs.map +1 -0
- package/dist/_chunks/App-DITZWWqI.js +1580 -0
- package/dist/_chunks/App-DITZWWqI.js.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases-Be3acS2L.js} +8 -7
- package/dist/_chunks/PurchaseContentReleases-Be3acS2L.js.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-_MxP6-Dt.mjs} +9 -8
- package/dist/_chunks/PurchaseContentReleases-_MxP6-Dt.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-BMgLwqci.mjs +178 -0
- package/dist/_chunks/ReleasesSettingsPage-BMgLwqci.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-DZcRvN_O.js +178 -0
- package/dist/_chunks/ReleasesSettingsPage-DZcRvN_O.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-CBsSVKTv.mjs +1380 -0
- package/dist/_chunks/index-CBsSVKTv.mjs.map +1 -0
- package/dist/_chunks/index-TfMp19WL.js +1399 -0
- package/dist/_chunks/index-TfMp19WL.js.map +1 -0
- package/dist/_chunks/schemas-BE1LxE9J.js +62 -0
- package/dist/_chunks/schemas-BE1LxE9J.js.map +1 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs +44 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs.map +1 -0
- package/dist/admin/index.js +1 -15
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +2 -16
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/src/components/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 +1107 -662
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1108 -662
- 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-5G7GEzBM.js +0 -1317
- package/dist/_chunks/App-5G7GEzBM.js.map +0 -1
- package/dist/_chunks/App-WMxox0mk.mjs +0 -1294
- package/dist/_chunks/App-WMxox0mk.mjs.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
- package/dist/_chunks/en-WuuhP6Bn.mjs.map +0 -1
- package/dist/_chunks/en-gcJJ5htG.js.map +0 -1
- package/dist/_chunks/index-BZ8RPGiV.mjs +0 -1013
- package/dist/_chunks/index-BZ8RPGiV.mjs.map +0 -1
- package/dist/_chunks/index-pQ3hnZJy.js +0 -1034
- package/dist/_chunks/index-pQ3hnZJy.js.map +0 -1
- package/strapi-server.js +0 -3
package/dist/server/index.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { contentTypes as contentTypes$1,
|
|
1
|
+
import { contentTypes as contentTypes$1, async, setCreatorFields, errors, yup as yup$1, validateYupSchema } from "@strapi/utils";
|
|
2
2
|
import isEqual from "lodash/isEqual";
|
|
3
3
|
import { difference, keys } from "lodash";
|
|
4
4
|
import _ from "lodash/fp";
|
|
5
|
-
import EE from "@strapi/strapi/dist/utils/ee";
|
|
6
5
|
import { scheduleJob } from "node-schedule";
|
|
7
6
|
import * as yup from "yup";
|
|
8
7
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
@@ -49,21 +48,38 @@ const ACTIONS = [
|
|
|
49
48
|
displayName: "Add an entry to a release",
|
|
50
49
|
uid: "create-action",
|
|
51
50
|
pluginName: "content-releases"
|
|
51
|
+
},
|
|
52
|
+
// Settings
|
|
53
|
+
{
|
|
54
|
+
uid: "settings.read",
|
|
55
|
+
section: "settings",
|
|
56
|
+
displayName: "Read",
|
|
57
|
+
category: "content releases",
|
|
58
|
+
subCategory: "options",
|
|
59
|
+
pluginName: "content-releases"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
uid: "settings.update",
|
|
63
|
+
section: "settings",
|
|
64
|
+
displayName: "Edit",
|
|
65
|
+
category: "content releases",
|
|
66
|
+
subCategory: "options",
|
|
67
|
+
pluginName: "content-releases"
|
|
52
68
|
}
|
|
53
69
|
];
|
|
54
70
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
55
71
|
RELEASES_PUBLISH: "releases.publish"
|
|
56
72
|
};
|
|
57
|
-
const getService = (name, { strapi: strapi2 }
|
|
73
|
+
const getService = (name, { strapi: strapi2 }) => {
|
|
58
74
|
return strapi2.plugin("content-releases").service(name);
|
|
59
75
|
};
|
|
60
|
-
const
|
|
76
|
+
const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi: strapi2 }) => {
|
|
61
77
|
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
62
|
-
const populate = await populateBuilderService(
|
|
63
|
-
const entry = await
|
|
64
|
-
return entry;
|
|
78
|
+
const populate = await populateBuilderService(contentType).populateDeep(Infinity).build();
|
|
79
|
+
const entry = await getEntry({ contentType, documentId, locale, populate }, { strapi: strapi2 });
|
|
80
|
+
return isEntryValid(contentType, entry, { strapi: strapi2 });
|
|
65
81
|
};
|
|
66
|
-
const
|
|
82
|
+
const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
|
|
67
83
|
try {
|
|
68
84
|
await strapi2.entityValidator.validateEntityCreation(
|
|
69
85
|
strapi2.getModel(contentTypeUid),
|
|
@@ -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,187 +239,272 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
|
|
|
178
239
|
release: true
|
|
179
240
|
}
|
|
180
241
|
});
|
|
181
|
-
await
|
|
182
|
-
if (action.entry) {
|
|
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
|
});
|
|
208
269
|
}
|
|
209
270
|
}
|
|
210
|
-
|
|
271
|
+
async function disableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
272
|
+
if (!oldContentTypes) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
276
|
+
if (!i18nPlugin) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
for (const uid in contentTypes2) {
|
|
280
|
+
if (!oldContentTypes[uid]) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const oldContentType = oldContentTypes[uid];
|
|
284
|
+
const contentType = contentTypes2[uid];
|
|
285
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
286
|
+
if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
|
|
287
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
288
|
+
locale: null
|
|
289
|
+
}).where({ contentType: uid }).execute();
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
async function enableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
294
|
+
if (!oldContentTypes) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
298
|
+
if (!i18nPlugin) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
for (const uid in contentTypes2) {
|
|
302
|
+
if (!oldContentTypes[uid]) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const oldContentType = oldContentTypes[uid];
|
|
306
|
+
const contentType = contentTypes2[uid];
|
|
307
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
308
|
+
const { getDefaultLocale } = i18nPlugin.service("locales");
|
|
309
|
+
if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
|
|
310
|
+
const defaultLocale = await getDefaultLocale();
|
|
311
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
312
|
+
locale: defaultLocale
|
|
313
|
+
}).where({ contentType: uid }).execute();
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
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
|
+
};
|
|
211
349
|
const register = async ({ strapi: strapi2 }) => {
|
|
212
|
-
if (features
|
|
213
|
-
await strapi2.admin
|
|
214
|
-
strapi2.
|
|
215
|
-
strapi2.hook("strapi::content-types.
|
|
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);
|
|
354
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
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);
|
|
216
459
|
}
|
|
217
460
|
};
|
|
218
|
-
const { features: features$1 } = require("@strapi/strapi/dist/utils/ee");
|
|
219
461
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
220
|
-
if (features
|
|
462
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
221
463
|
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
222
464
|
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
223
465
|
);
|
|
224
466
|
strapi2.db.lifecycles.subscribe({
|
|
225
467
|
models: contentTypesWithDraftAndPublish,
|
|
226
|
-
async afterDelete(event) {
|
|
227
|
-
try {
|
|
228
|
-
const { model, result } = event;
|
|
229
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
230
|
-
const { id } = result;
|
|
231
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
232
|
-
where: {
|
|
233
|
-
actions: {
|
|
234
|
-
target_type: model.uid,
|
|
235
|
-
target_id: id
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
240
|
-
where: {
|
|
241
|
-
target_type: model.uid,
|
|
242
|
-
target_id: id
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
for (const release2 of releases) {
|
|
246
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
} catch (error) {
|
|
250
|
-
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
251
|
-
}
|
|
252
|
-
},
|
|
253
|
-
/**
|
|
254
|
-
* deleteMany hook doesn't return the deleted entries ids
|
|
255
|
-
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
256
|
-
*/
|
|
257
|
-
async beforeDeleteMany(event) {
|
|
258
|
-
const { model, params } = event;
|
|
259
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
260
|
-
const { where } = params;
|
|
261
|
-
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
262
|
-
event.state.entriesToDelete = entriesToDelete;
|
|
263
|
-
}
|
|
264
|
-
},
|
|
265
468
|
/**
|
|
266
|
-
*
|
|
267
|
-
* 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
|
|
268
470
|
*/
|
|
269
471
|
async afterDeleteMany(event) {
|
|
270
472
|
try {
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
target_id: {
|
|
279
|
-
$in: entriesToDelete.map(
|
|
280
|
-
(entry) => entry.id
|
|
281
|
-
)
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
});
|
|
286
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
287
|
-
where: {
|
|
288
|
-
target_type: model.uid,
|
|
289
|
-
target_id: {
|
|
290
|
-
$in: entriesToDelete.map((entry) => entry.id)
|
|
291
|
-
}
|
|
292
|
-
}
|
|
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 }
|
|
293
480
|
});
|
|
294
|
-
for (const release2 of releases) {
|
|
295
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
296
|
-
}
|
|
297
481
|
}
|
|
298
482
|
} catch (error) {
|
|
299
483
|
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
300
484
|
error
|
|
301
485
|
});
|
|
302
486
|
}
|
|
303
|
-
},
|
|
304
|
-
async afterUpdate(event) {
|
|
305
|
-
try {
|
|
306
|
-
const { model, result } = event;
|
|
307
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
308
|
-
const isEntryValid = await getEntryValidStatus(
|
|
309
|
-
model.uid,
|
|
310
|
-
result,
|
|
311
|
-
{
|
|
312
|
-
strapi: strapi2
|
|
313
|
-
}
|
|
314
|
-
);
|
|
315
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
316
|
-
where: {
|
|
317
|
-
target_type: model.uid,
|
|
318
|
-
target_id: result.id
|
|
319
|
-
},
|
|
320
|
-
data: {
|
|
321
|
-
isEntryValid
|
|
322
|
-
}
|
|
323
|
-
});
|
|
324
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
325
|
-
where: {
|
|
326
|
-
actions: {
|
|
327
|
-
target_type: model.uid,
|
|
328
|
-
target_id: result.id
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
for (const release2 of releases) {
|
|
333
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
} catch (error) {
|
|
337
|
-
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
338
|
-
}
|
|
339
487
|
}
|
|
340
488
|
});
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
}
|
|
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
|
+
});
|
|
352
500
|
}
|
|
353
501
|
};
|
|
354
502
|
const destroy = async ({ strapi: strapi2 }) => {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
job.cancel();
|
|
361
|
-
}
|
|
503
|
+
const scheduledJobs = getService("scheduling", {
|
|
504
|
+
strapi: strapi2
|
|
505
|
+
}).getAll();
|
|
506
|
+
for (const [, job] of scheduledJobs) {
|
|
507
|
+
job.cancel();
|
|
362
508
|
}
|
|
363
509
|
};
|
|
364
510
|
const schema$1 = {
|
|
@@ -433,15 +579,13 @@ const schema = {
|
|
|
433
579
|
enum: ["publish", "unpublish"],
|
|
434
580
|
required: true
|
|
435
581
|
},
|
|
436
|
-
entry: {
|
|
437
|
-
type: "relation",
|
|
438
|
-
relation: "morphToOne",
|
|
439
|
-
configurable: false
|
|
440
|
-
},
|
|
441
582
|
contentType: {
|
|
442
583
|
type: "string",
|
|
443
584
|
required: true
|
|
444
585
|
},
|
|
586
|
+
entryDocumentId: {
|
|
587
|
+
type: "string"
|
|
588
|
+
},
|
|
445
589
|
locale: {
|
|
446
590
|
type: "string"
|
|
447
591
|
},
|
|
@@ -463,18 +607,6 @@ const contentTypes = {
|
|
|
463
607
|
release: release$1,
|
|
464
608
|
"release-action": releaseAction$1
|
|
465
609
|
};
|
|
466
|
-
const getGroupName = (queryValue) => {
|
|
467
|
-
switch (queryValue) {
|
|
468
|
-
case "contentType":
|
|
469
|
-
return "contentType.displayName";
|
|
470
|
-
case "action":
|
|
471
|
-
return "type";
|
|
472
|
-
case "locale":
|
|
473
|
-
return _.getOr("No locale", "locale.name");
|
|
474
|
-
default:
|
|
475
|
-
return "contentType.displayName";
|
|
476
|
-
}
|
|
477
|
-
};
|
|
478
610
|
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
479
611
|
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
480
612
|
strapi2.eventHub.emit(event, {
|
|
@@ -483,6 +615,33 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
483
615
|
release: release2
|
|
484
616
|
});
|
|
485
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
|
+
};
|
|
486
645
|
return {
|
|
487
646
|
async create(releaseData, { user }) {
|
|
488
647
|
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
|
@@ -496,13 +655,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
496
655
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
497
656
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
498
657
|
]);
|
|
499
|
-
const release2 = await strapi2.
|
|
658
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
|
|
500
659
|
data: {
|
|
501
660
|
...releaseWithCreatorFields,
|
|
502
661
|
status: "empty"
|
|
503
662
|
}
|
|
504
663
|
});
|
|
505
|
-
if (
|
|
664
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
506
665
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
507
666
|
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
508
667
|
}
|
|
@@ -510,94 +669,28 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
510
669
|
return release2;
|
|
511
670
|
},
|
|
512
671
|
async findOne(id, query = {}) {
|
|
513
|
-
const
|
|
514
|
-
|
|
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 }
|
|
515
676
|
});
|
|
516
677
|
return release2;
|
|
517
678
|
},
|
|
518
679
|
findPage(query) {
|
|
519
|
-
|
|
520
|
-
|
|
680
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
681
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findPage({
|
|
682
|
+
...dbQuery,
|
|
521
683
|
populate: {
|
|
522
684
|
actions: {
|
|
523
|
-
// @ts-expect-error Ignore missing properties
|
|
524
685
|
count: true
|
|
525
686
|
}
|
|
526
687
|
}
|
|
527
688
|
});
|
|
528
689
|
},
|
|
529
|
-
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
target_type: contentTypeUid,
|
|
534
|
-
target_id: entryId
|
|
535
|
-
},
|
|
536
|
-
releasedAt: {
|
|
537
|
-
$null: true
|
|
538
|
-
}
|
|
539
|
-
},
|
|
540
|
-
populate: {
|
|
541
|
-
// Filter the action to get only the content type entry
|
|
542
|
-
actions: {
|
|
543
|
-
where: {
|
|
544
|
-
target_type: contentTypeUid,
|
|
545
|
-
target_id: entryId
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
});
|
|
550
|
-
return releases.map((release2) => {
|
|
551
|
-
if (release2.actions?.length) {
|
|
552
|
-
const [actionForEntry] = release2.actions;
|
|
553
|
-
delete release2.actions;
|
|
554
|
-
return {
|
|
555
|
-
...release2,
|
|
556
|
-
action: actionForEntry
|
|
557
|
-
};
|
|
558
|
-
}
|
|
559
|
-
return release2;
|
|
560
|
-
});
|
|
561
|
-
},
|
|
562
|
-
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
563
|
-
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
564
|
-
where: {
|
|
565
|
-
releasedAt: {
|
|
566
|
-
$null: true
|
|
567
|
-
},
|
|
568
|
-
actions: {
|
|
569
|
-
target_type: contentTypeUid,
|
|
570
|
-
target_id: entryId
|
|
571
|
-
}
|
|
572
|
-
}
|
|
573
|
-
});
|
|
574
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
575
|
-
where: {
|
|
576
|
-
$or: [
|
|
577
|
-
{
|
|
578
|
-
id: {
|
|
579
|
-
$notIn: releasesRelated.map((release2) => release2.id)
|
|
580
|
-
}
|
|
581
|
-
},
|
|
582
|
-
{
|
|
583
|
-
actions: null
|
|
584
|
-
}
|
|
585
|
-
],
|
|
586
|
-
releasedAt: {
|
|
587
|
-
$null: true
|
|
588
|
-
}
|
|
589
|
-
}
|
|
590
|
-
});
|
|
591
|
-
return releases.map((release2) => {
|
|
592
|
-
if (release2.actions?.length) {
|
|
593
|
-
const [actionForEntry] = release2.actions;
|
|
594
|
-
delete release2.actions;
|
|
595
|
-
return {
|
|
596
|
-
...release2,
|
|
597
|
-
action: actionForEntry
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
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
|
|
601
694
|
});
|
|
602
695
|
},
|
|
603
696
|
async update(id, releaseData, { user }) {
|
|
@@ -612,157 +705,27 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
612
705
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
613
706
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
614
707
|
]);
|
|
615
|
-
const release2 = await strapi2.
|
|
708
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
|
|
616
709
|
if (!release2) {
|
|
617
710
|
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
618
711
|
}
|
|
619
712
|
if (release2.releasedAt) {
|
|
620
713
|
throw new errors.ValidationError("Release already published");
|
|
621
714
|
}
|
|
622
|
-
const updatedRelease = await strapi2.
|
|
623
|
-
|
|
624
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
625
|
-
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
626
|
-
*/
|
|
627
|
-
// @ts-expect-error see above
|
|
715
|
+
const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
716
|
+
where: { id },
|
|
628
717
|
data: releaseWithCreatorFields
|
|
629
718
|
});
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
schedulingService.cancel(id);
|
|
636
|
-
}
|
|
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);
|
|
637
724
|
}
|
|
638
725
|
this.updateReleaseStatus(id);
|
|
639
726
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
640
727
|
return updatedRelease;
|
|
641
728
|
},
|
|
642
|
-
async createAction(releaseId, action) {
|
|
643
|
-
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
644
|
-
strapi: strapi2
|
|
645
|
-
});
|
|
646
|
-
await Promise.all([
|
|
647
|
-
validateEntryContentType(action.entry.contentType),
|
|
648
|
-
validateUniqueEntry(releaseId, action)
|
|
649
|
-
]);
|
|
650
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
651
|
-
if (!release2) {
|
|
652
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
653
|
-
}
|
|
654
|
-
if (release2.releasedAt) {
|
|
655
|
-
throw new errors.ValidationError("Release already published");
|
|
656
|
-
}
|
|
657
|
-
const { entry, type } = action;
|
|
658
|
-
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
659
|
-
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
660
|
-
const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
661
|
-
data: {
|
|
662
|
-
type,
|
|
663
|
-
contentType: entry.contentType,
|
|
664
|
-
locale: entry.locale,
|
|
665
|
-
isEntryValid,
|
|
666
|
-
entry: {
|
|
667
|
-
id: entry.id,
|
|
668
|
-
__type: entry.contentType,
|
|
669
|
-
__pivot: { field: "entry" }
|
|
670
|
-
},
|
|
671
|
-
release: releaseId
|
|
672
|
-
},
|
|
673
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
674
|
-
});
|
|
675
|
-
this.updateReleaseStatus(releaseId);
|
|
676
|
-
return releaseAction2;
|
|
677
|
-
},
|
|
678
|
-
async findActions(releaseId, query) {
|
|
679
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
680
|
-
fields: ["id"]
|
|
681
|
-
});
|
|
682
|
-
if (!release2) {
|
|
683
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
684
|
-
}
|
|
685
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
686
|
-
...query,
|
|
687
|
-
populate: {
|
|
688
|
-
entry: {
|
|
689
|
-
populate: "*"
|
|
690
|
-
}
|
|
691
|
-
},
|
|
692
|
-
filters: {
|
|
693
|
-
release: releaseId
|
|
694
|
-
}
|
|
695
|
-
});
|
|
696
|
-
},
|
|
697
|
-
async countActions(query) {
|
|
698
|
-
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
699
|
-
},
|
|
700
|
-
async groupActions(actions, groupBy) {
|
|
701
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
702
|
-
if (!acc.includes(action.contentType)) {
|
|
703
|
-
acc.push(action.contentType);
|
|
704
|
-
}
|
|
705
|
-
return acc;
|
|
706
|
-
}, []);
|
|
707
|
-
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
708
|
-
contentTypeUids
|
|
709
|
-
);
|
|
710
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
711
|
-
const formattedData = actions.map((action) => {
|
|
712
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
713
|
-
return {
|
|
714
|
-
...action,
|
|
715
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
716
|
-
contentType: {
|
|
717
|
-
displayName,
|
|
718
|
-
mainFieldValue: action.entry[mainField],
|
|
719
|
-
uid: action.contentType
|
|
720
|
-
}
|
|
721
|
-
};
|
|
722
|
-
});
|
|
723
|
-
const groupName = getGroupName(groupBy);
|
|
724
|
-
return _.groupBy(groupName)(formattedData);
|
|
725
|
-
},
|
|
726
|
-
async getLocalesDataForActions() {
|
|
727
|
-
if (!strapi2.plugin("i18n")) {
|
|
728
|
-
return {};
|
|
729
|
-
}
|
|
730
|
-
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
731
|
-
return allLocales.reduce((acc, locale) => {
|
|
732
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
733
|
-
return acc;
|
|
734
|
-
}, {});
|
|
735
|
-
},
|
|
736
|
-
async getContentTypesDataForActions(contentTypesUids) {
|
|
737
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
738
|
-
const contentTypesData = {};
|
|
739
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
740
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
741
|
-
uid: contentTypeUid
|
|
742
|
-
});
|
|
743
|
-
contentTypesData[contentTypeUid] = {
|
|
744
|
-
mainField: contentTypeConfig.settings.mainField,
|
|
745
|
-
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
746
|
-
};
|
|
747
|
-
}
|
|
748
|
-
return contentTypesData;
|
|
749
|
-
},
|
|
750
|
-
getContentTypeModelsFromActions(actions) {
|
|
751
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
752
|
-
if (!acc.includes(action.contentType)) {
|
|
753
|
-
acc.push(action.contentType);
|
|
754
|
-
}
|
|
755
|
-
return acc;
|
|
756
|
-
}, []);
|
|
757
|
-
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
758
|
-
(acc, contentTypeUid) => {
|
|
759
|
-
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
760
|
-
return acc;
|
|
761
|
-
},
|
|
762
|
-
{}
|
|
763
|
-
);
|
|
764
|
-
return contentTypeModelsMap;
|
|
765
|
-
},
|
|
766
729
|
async getAllComponents() {
|
|
767
730
|
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
768
731
|
const components = await contentManagerComponentsService.findAllComponents();
|
|
@@ -776,10 +739,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
776
739
|
return componentsMap;
|
|
777
740
|
},
|
|
778
741
|
async delete(releaseId) {
|
|
779
|
-
const release2 = await strapi2.
|
|
742
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
743
|
+
where: { id: releaseId },
|
|
780
744
|
populate: {
|
|
781
745
|
actions: {
|
|
782
|
-
|
|
746
|
+
select: ["id"]
|
|
783
747
|
}
|
|
784
748
|
}
|
|
785
749
|
});
|
|
@@ -797,9 +761,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
797
761
|
}
|
|
798
762
|
}
|
|
799
763
|
});
|
|
800
|
-
await strapi2.
|
|
764
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
765
|
+
where: {
|
|
766
|
+
id: releaseId
|
|
767
|
+
}
|
|
768
|
+
});
|
|
801
769
|
});
|
|
802
|
-
if (
|
|
770
|
+
if (release2.scheduledAt) {
|
|
803
771
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
804
772
|
await schedulingService.cancel(release2.id);
|
|
805
773
|
}
|
|
@@ -807,148 +775,294 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
807
775
|
return release2;
|
|
808
776
|
},
|
|
809
777
|
async publish(releaseId) {
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
actions: {
|
|
817
|
-
populate: {
|
|
818
|
-
entry: {
|
|
819
|
-
fields: ["id"]
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
}
|
|
823
|
-
}
|
|
824
|
-
}
|
|
825
|
-
);
|
|
826
|
-
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) {
|
|
827
784
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
828
785
|
}
|
|
829
|
-
if (
|
|
786
|
+
if (lockedRelease.releasedAt) {
|
|
830
787
|
throw new errors.ValidationError("Release already published");
|
|
831
788
|
}
|
|
832
|
-
if (
|
|
833
|
-
throw new errors.ValidationError("
|
|
789
|
+
if (lockedRelease.status === "failed") {
|
|
790
|
+
throw new errors.ValidationError("Release failed to publish");
|
|
834
791
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
860
|
-
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
861
|
-
await strapi2.db.transaction(async () => {
|
|
862
|
-
for (const { uid, action, id } of singleTypeActions) {
|
|
863
|
-
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
864
|
-
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
865
|
-
try {
|
|
866
|
-
if (action === "publish") {
|
|
867
|
-
await entityManagerService.publish(entry, uid);
|
|
868
|
-
} else {
|
|
869
|
-
await entityManagerService.unpublish(entry, uid);
|
|
870
|
-
}
|
|
871
|
-
} catch (error) {
|
|
872
|
-
if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
|
|
873
|
-
} else {
|
|
874
|
-
throw error;
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
}
|
|
878
|
-
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
879
|
-
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
880
|
-
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
881
|
-
const entriesToPublish = await strapi2.entityService.findMany(
|
|
882
|
-
contentTypeUid,
|
|
883
|
-
{
|
|
884
|
-
filters: {
|
|
885
|
-
id: {
|
|
886
|
-
$in: entriestoPublishIds
|
|
887
|
-
}
|
|
888
|
-
},
|
|
889
|
-
populate
|
|
890
|
-
}
|
|
891
|
-
);
|
|
892
|
-
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
893
|
-
contentTypeUid,
|
|
894
|
-
{
|
|
895
|
-
filters: {
|
|
896
|
-
id: {
|
|
897
|
-
$in: entriesToUnpublishIds
|
|
898
|
-
}
|
|
899
|
-
},
|
|
900
|
-
populate
|
|
901
|
-
}
|
|
902
|
-
);
|
|
903
|
-
if (entriesToPublish.length > 0) {
|
|
904
|
-
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
905
|
-
}
|
|
906
|
-
if (entriesToUnpublish.length > 0) {
|
|
907
|
-
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
908
|
-
}
|
|
909
|
-
}
|
|
910
|
-
});
|
|
911
|
-
const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, releaseId, {
|
|
912
|
-
data: {
|
|
913
|
-
/*
|
|
914
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
|
|
915
|
-
*/
|
|
916
|
-
// @ts-expect-error see above
|
|
917
|
-
releasedAt: /* @__PURE__ */ new Date()
|
|
918
|
-
},
|
|
919
|
-
populate: {
|
|
920
|
-
actions: {
|
|
921
|
-
// @ts-expect-error is not expecting count but it is working
|
|
922
|
-
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()
|
|
923
814
|
}
|
|
924
|
-
}
|
|
925
|
-
});
|
|
926
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
815
|
+
});
|
|
927
816
|
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
928
817
|
isPublished: true,
|
|
929
|
-
release:
|
|
818
|
+
release: release22
|
|
930
819
|
});
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
} catch (error) {
|
|
935
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
820
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
821
|
+
return { release: release22, error: null };
|
|
822
|
+
} catch (error2) {
|
|
936
823
|
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
937
824
|
isPublished: false,
|
|
938
|
-
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
|
+
}
|
|
939
865
|
});
|
|
940
866
|
}
|
|
941
|
-
strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
942
|
-
where: {
|
|
867
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
868
|
+
where: {
|
|
869
|
+
id: releaseId
|
|
870
|
+
},
|
|
943
871
|
data: {
|
|
944
|
-
status: "
|
|
872
|
+
status: "ready"
|
|
945
873
|
}
|
|
946
874
|
});
|
|
947
|
-
throw error;
|
|
948
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;
|
|
949
1059
|
},
|
|
950
|
-
async
|
|
951
|
-
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({
|
|
952
1066
|
where: {
|
|
953
1067
|
id: actionId,
|
|
954
1068
|
release: {
|
|
@@ -957,17 +1071,42 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
957
1071
|
$null: true
|
|
958
1072
|
}
|
|
959
1073
|
}
|
|
960
|
-
}
|
|
961
|
-
data: update
|
|
1074
|
+
}
|
|
962
1075
|
});
|
|
963
|
-
if (!
|
|
1076
|
+
if (!action) {
|
|
964
1077
|
throw new errors.NotFoundError(
|
|
965
1078
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
966
1079
|
);
|
|
967
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);
|
|
968
1107
|
return updatedAction;
|
|
969
1108
|
},
|
|
970
|
-
async
|
|
1109
|
+
async delete(actionId, releaseId) {
|
|
971
1110
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
972
1111
|
where: {
|
|
973
1112
|
id: actionId,
|
|
@@ -984,87 +1123,104 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
984
1123
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
985
1124
|
);
|
|
986
1125
|
}
|
|
987
|
-
|
|
1126
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
988
1127
|
return deletedAction;
|
|
989
1128
|
},
|
|
990
|
-
async
|
|
991
|
-
const
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
}
|
|
1002
|
-
})
|
|
1003
|
-
]);
|
|
1004
|
-
if (totalActions > 0) {
|
|
1005
|
-
if (invalidActions > 0) {
|
|
1006
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1007
|
-
where: {
|
|
1008
|
-
id: releaseId
|
|
1009
|
-
},
|
|
1010
|
-
data: {
|
|
1011
|
-
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
|
|
1012
1140
|
}
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
|
|
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({
|
|
1016
1156
|
where: {
|
|
1017
|
-
id:
|
|
1157
|
+
id: action.id
|
|
1018
1158
|
},
|
|
1019
1159
|
data: {
|
|
1020
|
-
|
|
1160
|
+
isEntryValid: isValid
|
|
1021
1161
|
}
|
|
1022
1162
|
});
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
where: {
|
|
1026
|
-
id: releaseId
|
|
1027
|
-
},
|
|
1028
|
-
data: {
|
|
1029
|
-
status: "empty"
|
|
1163
|
+
if (!releasesUpdated.includes(action.release.id)) {
|
|
1164
|
+
releasesUpdated.push(action.release.id);
|
|
1030
1165
|
}
|
|
1166
|
+
return {
|
|
1167
|
+
id: action.id,
|
|
1168
|
+
isEntryValid: isValid
|
|
1169
|
+
};
|
|
1031
1170
|
});
|
|
1171
|
+
if (releasesUpdated.length > 0) {
|
|
1172
|
+
await async.map(releasesUpdated, async (releaseId) => {
|
|
1173
|
+
await getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1032
1176
|
}
|
|
1033
1177
|
};
|
|
1034
1178
|
};
|
|
1179
|
+
class AlreadyOnReleaseError extends errors.ApplicationError {
|
|
1180
|
+
constructor(message) {
|
|
1181
|
+
super(message);
|
|
1182
|
+
this.name = "AlreadyOnReleaseError";
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1035
1185
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
1036
1186
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
1037
|
-
const release2 = await strapi2.
|
|
1038
|
-
|
|
1187
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1188
|
+
where: {
|
|
1189
|
+
id: releaseId
|
|
1190
|
+
},
|
|
1191
|
+
populate: {
|
|
1192
|
+
actions: true
|
|
1193
|
+
}
|
|
1039
1194
|
});
|
|
1040
1195
|
if (!release2) {
|
|
1041
1196
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1042
1197
|
}
|
|
1043
1198
|
const isEntryInRelease = release2.actions.some(
|
|
1044
|
-
(action) =>
|
|
1199
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
1045
1200
|
);
|
|
1046
1201
|
if (isEntryInRelease) {
|
|
1047
|
-
throw new
|
|
1048
|
-
`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}`
|
|
1049
1204
|
);
|
|
1050
1205
|
}
|
|
1051
1206
|
},
|
|
1052
|
-
|
|
1207
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
1053
1208
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
1054
1209
|
if (!contentType) {
|
|
1055
1210
|
throw new errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
1056
1211
|
}
|
|
1057
|
-
if (!contentType
|
|
1212
|
+
if (!contentTypes$1.hasDraftAndPublish(contentType)) {
|
|
1058
1213
|
throw new errors.ValidationError(
|
|
1059
1214
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
1060
1215
|
);
|
|
1061
1216
|
}
|
|
1217
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1218
|
+
throw new errors.ValidationError("Document id is required for collection type");
|
|
1219
|
+
}
|
|
1062
1220
|
},
|
|
1063
1221
|
async validatePendingReleasesLimit() {
|
|
1064
|
-
const
|
|
1065
|
-
|
|
1066
|
-
EE.features.get("cms-content-releases")?.options?.maximumReleases || 3
|
|
1067
|
-
);
|
|
1222
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1223
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
1068
1224
|
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
1069
1225
|
filters: {
|
|
1070
1226
|
releasedAt: {
|
|
@@ -1077,8 +1233,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
1077
1233
|
}
|
|
1078
1234
|
},
|
|
1079
1235
|
async validateUniqueNameForPendingRelease(name, id) {
|
|
1080
|
-
const pendingReleases = await strapi2.
|
|
1081
|
-
|
|
1236
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1237
|
+
where: {
|
|
1082
1238
|
releasedAt: {
|
|
1083
1239
|
$null: true
|
|
1084
1240
|
},
|
|
@@ -1107,7 +1263,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1107
1263
|
}
|
|
1108
1264
|
const job = scheduleJob(scheduleDate, async () => {
|
|
1109
1265
|
try {
|
|
1110
|
-
await getService("release").publish(releaseId);
|
|
1266
|
+
await getService("release", { strapi: strapi2 }).publish(releaseId);
|
|
1111
1267
|
} catch (error) {
|
|
1112
1268
|
}
|
|
1113
1269
|
this.cancel(releaseId);
|
|
@@ -1149,85 +1305,172 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1149
1305
|
}
|
|
1150
1306
|
};
|
|
1151
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
|
+
};
|
|
1152
1329
|
const services = {
|
|
1153
1330
|
release: createReleaseService,
|
|
1331
|
+
"release-action": createReleaseActionService,
|
|
1154
1332
|
"release-validation": createReleaseValidationService,
|
|
1155
|
-
|
|
1333
|
+
scheduling: createSchedulingService,
|
|
1334
|
+
settings: createSettingsService
|
|
1156
1335
|
};
|
|
1157
|
-
const RELEASE_SCHEMA = yup.object().shape({
|
|
1158
|
-
name: yup.string().trim().required(),
|
|
1159
|
-
scheduledAt: yup.string().nullable(),
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
otherwise: yup.string().nullable()
|
|
1165
|
-
}),
|
|
1166
|
-
timezone: yup.string().when("isScheduled", {
|
|
1167
|
-
is: true,
|
|
1168
|
-
then: yup.string().required().nullable(),
|
|
1169
|
-
otherwise: yup.string().nullable()
|
|
1170
|
-
}),
|
|
1171
|
-
date: yup.string().when("isScheduled", {
|
|
1172
|
-
is: true,
|
|
1173
|
-
then: yup.string().required().nullable(),
|
|
1174
|
-
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()
|
|
1175
1343
|
})
|
|
1176
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();
|
|
1177
1351
|
const validateRelease = validateYupSchema(RELEASE_SCHEMA);
|
|
1352
|
+
const validatefindByDocumentAttachedParams = validateYupSchema(
|
|
1353
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1354
|
+
);
|
|
1178
1355
|
const releaseController = {
|
|
1179
|
-
|
|
1180
|
-
|
|
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({
|
|
1181
1363
|
ability: ctx.state.userAbility,
|
|
1182
1364
|
model: RELEASE_MODEL_UID
|
|
1183
1365
|
});
|
|
1184
1366
|
await permissionsManager.validateQuery(ctx.query);
|
|
1185
1367
|
const releaseService = getService("release", { strapi });
|
|
1186
|
-
const
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
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,
|
|
1201
1384
|
actions: {
|
|
1202
|
-
|
|
1203
|
-
|
|
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
|
|
1204
1397
|
}
|
|
1205
1398
|
}
|
|
1206
|
-
}
|
|
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
|
+
}
|
|
1207
1412
|
});
|
|
1208
|
-
const
|
|
1413
|
+
const releases = await releaseService.findMany({
|
|
1209
1414
|
where: {
|
|
1415
|
+
$or: [
|
|
1416
|
+
{
|
|
1417
|
+
id: {
|
|
1418
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1419
|
+
}
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
actions: null
|
|
1423
|
+
}
|
|
1424
|
+
],
|
|
1210
1425
|
releasedAt: null
|
|
1211
1426
|
}
|
|
1212
1427
|
});
|
|
1213
|
-
ctx.body = { data
|
|
1428
|
+
ctx.body = { data: releases };
|
|
1214
1429
|
}
|
|
1215
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
|
+
},
|
|
1216
1458
|
async findOne(ctx) {
|
|
1217
1459
|
const id = ctx.params.id;
|
|
1218
1460
|
const releaseService = getService("release", { strapi });
|
|
1461
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1219
1462
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
1220
1463
|
if (!release2) {
|
|
1221
1464
|
throw new errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1222
1465
|
}
|
|
1223
|
-
const count = await
|
|
1466
|
+
const count = await releaseActionService.countActions({
|
|
1224
1467
|
filters: {
|
|
1225
1468
|
release: id
|
|
1226
1469
|
}
|
|
1227
1470
|
});
|
|
1228
1471
|
const sanitizedRelease = {
|
|
1229
1472
|
...release2,
|
|
1230
|
-
createdBy: release2.createdBy ? strapi.admin
|
|
1473
|
+
createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
|
|
1231
1474
|
};
|
|
1232
1475
|
const data = {
|
|
1233
1476
|
...sanitizedRelease,
|
|
@@ -1239,19 +1482,63 @@ const releaseController = {
|
|
|
1239
1482
|
};
|
|
1240
1483
|
ctx.body = { data };
|
|
1241
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
|
+
},
|
|
1242
1529
|
async create(ctx) {
|
|
1243
1530
|
const user = ctx.state.user;
|
|
1244
1531
|
const releaseArgs = ctx.request.body;
|
|
1245
1532
|
await validateRelease(releaseArgs);
|
|
1246
1533
|
const releaseService = getService("release", { strapi });
|
|
1247
1534
|
const release2 = await releaseService.create(releaseArgs, { user });
|
|
1248
|
-
const permissionsManager = strapi.admin
|
|
1535
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1249
1536
|
ability: ctx.state.userAbility,
|
|
1250
1537
|
model: RELEASE_MODEL_UID
|
|
1251
1538
|
});
|
|
1252
|
-
ctx.
|
|
1539
|
+
ctx.created({
|
|
1253
1540
|
data: await permissionsManager.sanitizeOutput(release2)
|
|
1254
|
-
};
|
|
1541
|
+
});
|
|
1255
1542
|
},
|
|
1256
1543
|
async update(ctx) {
|
|
1257
1544
|
const user = ctx.state.user;
|
|
@@ -1260,7 +1547,7 @@ const releaseController = {
|
|
|
1260
1547
|
await validateRelease(releaseArgs);
|
|
1261
1548
|
const releaseService = getService("release", { strapi });
|
|
1262
1549
|
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
1263
|
-
const permissionsManager = strapi.admin
|
|
1550
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1264
1551
|
ability: ctx.state.userAbility,
|
|
1265
1552
|
model: RELEASE_MODEL_UID
|
|
1266
1553
|
});
|
|
@@ -1277,18 +1564,18 @@ const releaseController = {
|
|
|
1277
1564
|
};
|
|
1278
1565
|
},
|
|
1279
1566
|
async publish(ctx) {
|
|
1280
|
-
const user = ctx.state.user;
|
|
1281
1567
|
const id = ctx.params.id;
|
|
1282
1568
|
const releaseService = getService("release", { strapi });
|
|
1283
|
-
const
|
|
1569
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1570
|
+
const release2 = await releaseService.publish(id);
|
|
1284
1571
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1285
|
-
|
|
1572
|
+
releaseActionService.countActions({
|
|
1286
1573
|
filters: {
|
|
1287
1574
|
release: id,
|
|
1288
1575
|
type: "publish"
|
|
1289
1576
|
}
|
|
1290
1577
|
}),
|
|
1291
|
-
|
|
1578
|
+
releaseActionService.countActions({
|
|
1292
1579
|
filters: {
|
|
1293
1580
|
release: id,
|
|
1294
1581
|
type: "unpublish"
|
|
@@ -1306,57 +1593,106 @@ const releaseController = {
|
|
|
1306
1593
|
}
|
|
1307
1594
|
};
|
|
1308
1595
|
const RELEASE_ACTION_SCHEMA = yup$1.object().shape({
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
}).required(),
|
|
1596
|
+
contentType: yup$1.string().required(),
|
|
1597
|
+
entryDocumentId: yup$1.strapiID(),
|
|
1598
|
+
locale: yup$1.string(),
|
|
1313
1599
|
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1314
1600
|
});
|
|
1315
1601
|
const RELEASE_ACTION_UPDATE_SCHEMA = yup$1.object().shape({
|
|
1316
1602
|
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1317
1603
|
});
|
|
1604
|
+
const FIND_MANY_ACTIONS_PARAMS = yup$1.object().shape({
|
|
1605
|
+
groupBy: yup$1.string().oneOf(["action", "contentType", "locale"])
|
|
1606
|
+
});
|
|
1318
1607
|
const validateReleaseAction = validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1319
1608
|
const validateReleaseActionUpdateSchema = validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1609
|
+
const validateFindManyActionsParams = validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1320
1610
|
const releaseActionController = {
|
|
1321
1611
|
async create(ctx) {
|
|
1322
1612
|
const releaseId = ctx.params.releaseId;
|
|
1323
1613
|
const releaseActionArgs = ctx.request.body;
|
|
1324
1614
|
await validateReleaseAction(releaseActionArgs);
|
|
1325
|
-
const
|
|
1326
|
-
const releaseAction2 = await
|
|
1327
|
-
ctx.
|
|
1615
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1616
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1617
|
+
ctx.created({
|
|
1328
1618
|
data: releaseAction2
|
|
1329
|
-
};
|
|
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
|
+
});
|
|
1330
1658
|
},
|
|
1331
1659
|
async findMany(ctx) {
|
|
1332
1660
|
const releaseId = ctx.params.releaseId;
|
|
1333
|
-
const permissionsManager = strapi.admin
|
|
1661
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1334
1662
|
ability: ctx.state.userAbility,
|
|
1335
1663
|
model: RELEASE_ACTION_MODEL_UID
|
|
1336
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;
|
|
1337
1673
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1338
|
-
const
|
|
1339
|
-
const { results, pagination } = await
|
|
1340
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1674
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1675
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1341
1676
|
...query
|
|
1342
1677
|
});
|
|
1343
1678
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
1344
1679
|
if (acc[action.contentType]) {
|
|
1345
1680
|
return acc;
|
|
1346
1681
|
}
|
|
1347
|
-
const contentTypePermissionsManager = strapi.admin
|
|
1682
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1348
1683
|
ability: ctx.state.userAbility,
|
|
1349
1684
|
model: action.contentType
|
|
1350
1685
|
});
|
|
1351
1686
|
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
1352
1687
|
return acc;
|
|
1353
1688
|
}, {});
|
|
1354
|
-
const sanitizedResults = await
|
|
1689
|
+
const sanitizedResults = await async.map(results, async (action) => ({
|
|
1355
1690
|
...action,
|
|
1356
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1691
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1357
1692
|
}));
|
|
1358
|
-
const groupedData = await
|
|
1359
|
-
const contentTypes2 =
|
|
1693
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1694
|
+
const contentTypes2 = await releaseActionService.getContentTypeModelsFromActions(results);
|
|
1695
|
+
const releaseService = getService("release", { strapi });
|
|
1360
1696
|
const components = await releaseService.getAllComponents();
|
|
1361
1697
|
ctx.body = {
|
|
1362
1698
|
data: groupedData,
|
|
@@ -1372,8 +1708,8 @@ const releaseActionController = {
|
|
|
1372
1708
|
const releaseId = ctx.params.releaseId;
|
|
1373
1709
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1374
1710
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1375
|
-
const
|
|
1376
|
-
const updatedAction = await
|
|
1711
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1712
|
+
const updatedAction = await releaseActionService.update(
|
|
1377
1713
|
actionId,
|
|
1378
1714
|
releaseId,
|
|
1379
1715
|
releaseActionUpdateArgs
|
|
@@ -1385,17 +1721,71 @@ const releaseActionController = {
|
|
|
1385
1721
|
async delete(ctx) {
|
|
1386
1722
|
const actionId = ctx.params.actionId;
|
|
1387
1723
|
const releaseId = ctx.params.releaseId;
|
|
1388
|
-
const
|
|
1389
|
-
const deletedReleaseAction = await
|
|
1724
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1725
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1390
1726
|
ctx.body = {
|
|
1391
1727
|
data: deletedReleaseAction
|
|
1392
1728
|
};
|
|
1393
1729
|
}
|
|
1394
1730
|
};
|
|
1395
|
-
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
|
+
};
|
|
1396
1754
|
const release = {
|
|
1397
1755
|
type: "admin",
|
|
1398
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
|
+
},
|
|
1399
1789
|
{
|
|
1400
1790
|
method: "POST",
|
|
1401
1791
|
path: "/",
|
|
@@ -1415,7 +1805,7 @@ const release = {
|
|
|
1415
1805
|
{
|
|
1416
1806
|
method: "GET",
|
|
1417
1807
|
path: "/",
|
|
1418
|
-
handler: "release.
|
|
1808
|
+
handler: "release.findPage",
|
|
1419
1809
|
config: {
|
|
1420
1810
|
policies: [
|
|
1421
1811
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1513,6 +1903,22 @@ const releaseAction = {
|
|
|
1513
1903
|
]
|
|
1514
1904
|
}
|
|
1515
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
|
+
},
|
|
1516
1922
|
{
|
|
1517
1923
|
method: "GET",
|
|
1518
1924
|
path: "/:releaseId/actions",
|
|
@@ -1563,13 +1969,50 @@ const releaseAction = {
|
|
|
1563
1969
|
}
|
|
1564
1970
|
]
|
|
1565
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
|
+
};
|
|
1566
2009
|
const routes = {
|
|
2010
|
+
settings,
|
|
1567
2011
|
release,
|
|
1568
2012
|
"release-action": releaseAction
|
|
1569
2013
|
};
|
|
1570
|
-
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
1571
2014
|
const getPlugin = () => {
|
|
1572
|
-
if (features.isEnabled("cms-content-releases")) {
|
|
2015
|
+
if (strapi.ee.features.isEnabled("cms-content-releases")) {
|
|
1573
2016
|
return {
|
|
1574
2017
|
register,
|
|
1575
2018
|
bootstrap,
|
|
@@ -1581,6 +2024,9 @@ const getPlugin = () => {
|
|
|
1581
2024
|
};
|
|
1582
2025
|
}
|
|
1583
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
|
|
1584
2030
|
contentTypes
|
|
1585
2031
|
};
|
|
1586
2032
|
};
|