@strapi/content-releases 0.0.0-next.e6eaa3d0563c85f80fd88b258df70a55c057096e → 0.0.0-next.e8d8fc824d0f6a695b2a9ebaa4680ed21c3645ca
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-CiZCkScI.mjs +1558 -0
- package/dist/_chunks/App-CiZCkScI.mjs.map +1 -0
- package/dist/_chunks/App-SGjO5UPV.js +1578 -0
- package/dist/_chunks/App-SGjO5UPV.js.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases--qQepXpP.js} +9 -8
- package/dist/_chunks/PurchaseContentReleases--qQepXpP.js.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-D-n-w-st.mjs} +10 -9
- package/dist/_chunks/PurchaseContentReleases-D-n-w-st.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-Cto_NLUd.js +178 -0
- package/dist/_chunks/ReleasesSettingsPage-Cto_NLUd.js.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-DQT8N3A-.mjs +178 -0
- package/dist/_chunks/ReleasesSettingsPage-DQT8N3A-.mjs.map +1 -0
- package/dist/_chunks/{en-faJDuv3q.js → en-BWPPsSH-.js} +28 -3
- package/dist/_chunks/en-BWPPsSH-.js.map +1 -0
- package/dist/_chunks/{en-RdapH-9X.mjs → en-D9Q4YW03.mjs} +28 -3
- package/dist/_chunks/en-D9Q4YW03.mjs.map +1 -0
- package/dist/_chunks/index-BjvFfTtA.mjs +1386 -0
- package/dist/_chunks/index-BjvFfTtA.mjs.map +1 -0
- package/dist/_chunks/index-CyU534vL.js +1404 -0
- package/dist/_chunks/index-CyU534vL.js.map +1 -0
- package/dist/_chunks/schemas-DBYv9gK8.js +61 -0
- package/dist/_chunks/schemas-DBYv9gK8.js.map +1 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs +44 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs.map +1 -0
- package/dist/admin/index.js +1 -15
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +2 -16
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/src/components/EntryValidationPopover.d.ts +13 -0
- package/dist/admin/src/components/RelativeTime.d.ts +28 -0
- package/dist/admin/src/components/ReleaseAction.d.ts +3 -0
- package/dist/admin/src/components/ReleaseActionMenu.d.ts +26 -0
- package/dist/admin/src/components/ReleaseActionModal.d.ts +24 -0
- package/dist/admin/src/components/ReleaseActionOptions.d.ts +9 -0
- package/dist/admin/src/components/ReleaseListCell.d.ts +28 -0
- package/dist/admin/src/components/ReleaseModal.d.ts +17 -0
- package/dist/admin/src/components/ReleasesPanel.d.ts +3 -0
- package/dist/admin/src/constants.d.ts +76 -0
- package/dist/admin/src/index.d.ts +3 -0
- package/dist/admin/src/modules/hooks.d.ts +7 -0
- package/dist/admin/src/pages/App.d.ts +1 -0
- package/dist/admin/src/pages/PurchaseContentReleases.d.ts +2 -0
- package/dist/admin/src/pages/ReleaseDetailsPage.d.ts +2 -0
- package/dist/admin/src/pages/ReleasesPage.d.ts +8 -0
- package/dist/admin/src/pages/ReleasesSettingsPage.d.ts +1 -0
- package/dist/admin/src/pages/tests/mockReleaseDetailsPageData.d.ts +181 -0
- package/dist/admin/src/pages/tests/mockReleasesPageData.d.ts +39 -0
- package/dist/admin/src/pluginId.d.ts +1 -0
- package/dist/admin/src/services/release.d.ts +112 -0
- package/dist/admin/src/store/hooks.d.ts +7 -0
- package/dist/admin/src/utils/api.d.ts +6 -0
- package/dist/admin/src/utils/prefixPluginTranslations.d.ts +3 -0
- package/dist/admin/src/utils/time.d.ts +10 -0
- package/dist/admin/src/validation/schemas.d.ts +6 -0
- package/dist/server/index.js +952 -618
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +952 -616
- 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-R-kksTW7.mjs +0 -1308
- package/dist/_chunks/App-R-kksTW7.mjs.map +0 -1
- package/dist/_chunks/App-WZHc_d3m.js +0 -1331
- package/dist/_chunks/App-WZHc_d3m.js.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
- package/dist/_chunks/en-RdapH-9X.mjs.map +0 -1
- package/dist/_chunks/en-faJDuv3q.js.map +0 -1
- package/dist/_chunks/index-k6fw6RYi.js +0 -1033
- package/dist/_chunks/index-k6fw6RYi.js.map +0 -1
- package/dist/_chunks/index-vpSczx8v.mjs +0 -1012
- package/dist/_chunks/index-vpSczx8v.mjs.map +0 -1
- package/strapi-server.js +0 -3
package/dist/server/index.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { contentTypes as contentTypes$1,
|
|
1
|
+
import { contentTypes as contentTypes$1, async, setCreatorFields, errors, yup as yup$1, validateYupSchema } from "@strapi/utils";
|
|
2
2
|
import isEqual from "lodash/isEqual";
|
|
3
3
|
import { difference, keys } from "lodash";
|
|
4
4
|
import _ from "lodash/fp";
|
|
5
|
-
import EE from "@strapi/strapi/dist/utils/ee";
|
|
6
5
|
import { scheduleJob } from "node-schedule";
|
|
7
6
|
import * as yup from "yup";
|
|
8
7
|
const RELEASE_MODEL_UID = "plugin::content-releases.release";
|
|
@@ -49,21 +48,38 @@ const ACTIONS = [
|
|
|
49
48
|
displayName: "Add an entry to a release",
|
|
50
49
|
uid: "create-action",
|
|
51
50
|
pluginName: "content-releases"
|
|
51
|
+
},
|
|
52
|
+
// Settings
|
|
53
|
+
{
|
|
54
|
+
uid: "settings.read",
|
|
55
|
+
section: "settings",
|
|
56
|
+
displayName: "Read",
|
|
57
|
+
category: "content releases",
|
|
58
|
+
subCategory: "options",
|
|
59
|
+
pluginName: "content-releases"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
uid: "settings.update",
|
|
63
|
+
section: "settings",
|
|
64
|
+
displayName: "Edit",
|
|
65
|
+
category: "content releases",
|
|
66
|
+
subCategory: "options",
|
|
67
|
+
pluginName: "content-releases"
|
|
52
68
|
}
|
|
53
69
|
];
|
|
54
70
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
55
71
|
RELEASES_PUBLISH: "releases.publish"
|
|
56
72
|
};
|
|
57
|
-
const getService = (name, { strapi: strapi2 }
|
|
73
|
+
const getService = (name, { strapi: strapi2 }) => {
|
|
58
74
|
return strapi2.plugin("content-releases").service(name);
|
|
59
75
|
};
|
|
60
|
-
const
|
|
76
|
+
const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi: strapi2 }) => {
|
|
61
77
|
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
62
|
-
const populate = await populateBuilderService(
|
|
63
|
-
const entry = await
|
|
64
|
-
return entry;
|
|
78
|
+
const populate = await populateBuilderService(contentType).populateDeep(Infinity).build();
|
|
79
|
+
const entry = await getEntry({ contentType, documentId, locale, populate }, { strapi: strapi2 });
|
|
80
|
+
return isEntryValid(contentType, entry, { strapi: strapi2 });
|
|
65
81
|
};
|
|
66
|
-
const
|
|
82
|
+
const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
|
|
67
83
|
try {
|
|
68
84
|
await strapi2.entityValidator.validateEntityCreation(
|
|
69
85
|
strapi2.getModel(contentTypeUid),
|
|
@@ -72,11 +88,54 @@ const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } =
|
|
|
72
88
|
// @ts-expect-error - FIXME: entity here is unnecessary
|
|
73
89
|
entry
|
|
74
90
|
);
|
|
91
|
+
const workflowsService = strapi2.plugin("review-workflows").service("workflows");
|
|
92
|
+
const workflow = await workflowsService.getAssignedWorkflow(contentTypeUid, {
|
|
93
|
+
populate: "stageRequiredToPublish"
|
|
94
|
+
});
|
|
95
|
+
if (workflow?.stageRequiredToPublish) {
|
|
96
|
+
return entry.strapi_stage.id === workflow.stageRequiredToPublish.id;
|
|
97
|
+
}
|
|
75
98
|
return true;
|
|
76
99
|
} catch {
|
|
77
100
|
return false;
|
|
78
101
|
}
|
|
79
102
|
};
|
|
103
|
+
const getEntry = async ({
|
|
104
|
+
contentType,
|
|
105
|
+
documentId,
|
|
106
|
+
locale,
|
|
107
|
+
populate,
|
|
108
|
+
status = "draft"
|
|
109
|
+
}, { strapi: strapi2 }) => {
|
|
110
|
+
if (documentId) {
|
|
111
|
+
const entry = await strapi2.documents(contentType).findOne({ documentId, locale, populate, status });
|
|
112
|
+
if (status === "published" && !entry) {
|
|
113
|
+
return strapi2.documents(contentType).findOne({ documentId, locale, populate, status: "draft" });
|
|
114
|
+
}
|
|
115
|
+
return entry;
|
|
116
|
+
}
|
|
117
|
+
return strapi2.documents(contentType).findFirst({ locale, populate, status });
|
|
118
|
+
};
|
|
119
|
+
const getEntryStatus = async (contentType, entry) => {
|
|
120
|
+
if (entry.publishedAt) {
|
|
121
|
+
return "published";
|
|
122
|
+
}
|
|
123
|
+
const publishedEntry = await strapi.documents(contentType).findOne({
|
|
124
|
+
documentId: entry.documentId,
|
|
125
|
+
locale: entry.locale,
|
|
126
|
+
status: "published",
|
|
127
|
+
fields: ["updatedAt"]
|
|
128
|
+
});
|
|
129
|
+
if (!publishedEntry) {
|
|
130
|
+
return "draft";
|
|
131
|
+
}
|
|
132
|
+
const entryUpdatedAt = new Date(entry.updatedAt).getTime();
|
|
133
|
+
const publishedEntryUpdatedAt = new Date(publishedEntry.updatedAt).getTime();
|
|
134
|
+
if (entryUpdatedAt > publishedEntryUpdatedAt) {
|
|
135
|
+
return "modified";
|
|
136
|
+
}
|
|
137
|
+
return "published";
|
|
138
|
+
};
|
|
80
139
|
async function deleteActionsOnDisableDraftAndPublish({
|
|
81
140
|
oldContentTypes,
|
|
82
141
|
contentTypes: contentTypes2
|
|
@@ -98,7 +157,7 @@ async function deleteActionsOnDisableDraftAndPublish({
|
|
|
98
157
|
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
99
158
|
const deletedContentTypes = difference(keys(oldContentTypes), keys(contentTypes2)) ?? [];
|
|
100
159
|
if (deletedContentTypes.length) {
|
|
101
|
-
await
|
|
160
|
+
await async.map(deletedContentTypes, async (deletedContentTypeUID) => {
|
|
102
161
|
return strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: deletedContentTypeUID }).execute();
|
|
103
162
|
});
|
|
104
163
|
}
|
|
@@ -117,25 +176,27 @@ async function migrateIsValidAndStatusReleases() {
|
|
|
117
176
|
}
|
|
118
177
|
}
|
|
119
178
|
});
|
|
120
|
-
|
|
179
|
+
async.map(releasesWithoutStatus, async (release2) => {
|
|
121
180
|
const actions = release2.actions;
|
|
122
181
|
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
123
182
|
for (const action of notValidatedActions) {
|
|
124
183
|
if (action.entry) {
|
|
125
|
-
const
|
|
126
|
-
|
|
184
|
+
const isEntryValid2 = getDraftEntryValidStatus(
|
|
185
|
+
{
|
|
186
|
+
contentType: action.contentType,
|
|
187
|
+
documentId: action.entryDocumentId,
|
|
188
|
+
locale: action.locale
|
|
189
|
+
},
|
|
190
|
+
{ strapi }
|
|
191
|
+
);
|
|
192
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
193
|
+
where: {
|
|
194
|
+
id: action.id
|
|
195
|
+
},
|
|
196
|
+
data: {
|
|
197
|
+
isEntryValid: isEntryValid2
|
|
198
|
+
}
|
|
127
199
|
});
|
|
128
|
-
if (populatedEntry) {
|
|
129
|
-
const isEntryValid = getEntryValidStatus(action.contentType, populatedEntry, { strapi });
|
|
130
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
131
|
-
where: {
|
|
132
|
-
id: action.id
|
|
133
|
-
},
|
|
134
|
-
data: {
|
|
135
|
-
isEntryValid
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
200
|
}
|
|
140
201
|
}
|
|
141
202
|
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
@@ -148,7 +209,7 @@ async function migrateIsValidAndStatusReleases() {
|
|
|
148
209
|
}
|
|
149
210
|
}
|
|
150
211
|
});
|
|
151
|
-
|
|
212
|
+
async.map(publishedReleases, async (release2) => {
|
|
152
213
|
return strapi.db.query(RELEASE_MODEL_UID).update({
|
|
153
214
|
where: {
|
|
154
215
|
id: release2.id
|
|
@@ -165,7 +226,7 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
|
|
|
165
226
|
(uid) => oldContentTypes[uid]?.options?.draftAndPublish
|
|
166
227
|
);
|
|
167
228
|
const releasesAffected = /* @__PURE__ */ new Set();
|
|
168
|
-
|
|
229
|
+
async.map(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
|
|
169
230
|
const oldContentType = oldContentTypes[contentTypeUID];
|
|
170
231
|
const contentType = contentTypes2[contentTypeUID];
|
|
171
232
|
if (!isEqual(oldContentType?.attributes, contentType?.attributes)) {
|
|
@@ -178,30 +239,30 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
|
|
|
178
239
|
release: true
|
|
179
240
|
}
|
|
180
241
|
});
|
|
181
|
-
await
|
|
182
|
-
if (action.entry && action.release) {
|
|
183
|
-
const
|
|
184
|
-
|
|
242
|
+
await async.map(actions, async (action) => {
|
|
243
|
+
if (action.entry && action.release && action.type === "publish") {
|
|
244
|
+
const isEntryValid2 = await getDraftEntryValidStatus(
|
|
245
|
+
{
|
|
246
|
+
contentType: contentTypeUID,
|
|
247
|
+
documentId: action.entryDocumentId,
|
|
248
|
+
locale: action.locale
|
|
249
|
+
},
|
|
250
|
+
{ strapi }
|
|
251
|
+
);
|
|
252
|
+
releasesAffected.add(action.release.id);
|
|
253
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
254
|
+
where: {
|
|
255
|
+
id: action.id
|
|
256
|
+
},
|
|
257
|
+
data: {
|
|
258
|
+
isEntryValid: isEntryValid2
|
|
259
|
+
}
|
|
185
260
|
});
|
|
186
|
-
if (populatedEntry) {
|
|
187
|
-
const isEntryValid = await getEntryValidStatus(contentTypeUID, populatedEntry, {
|
|
188
|
-
strapi
|
|
189
|
-
});
|
|
190
|
-
releasesAffected.add(action.release.id);
|
|
191
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
192
|
-
where: {
|
|
193
|
-
id: action.id
|
|
194
|
-
},
|
|
195
|
-
data: {
|
|
196
|
-
isEntryValid
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
261
|
}
|
|
201
262
|
});
|
|
202
263
|
}
|
|
203
264
|
}).then(() => {
|
|
204
|
-
|
|
265
|
+
async.map(releasesAffected, async (releaseId) => {
|
|
205
266
|
return getService("release", { strapi }).updateReleaseStatus(releaseId);
|
|
206
267
|
});
|
|
207
268
|
});
|
|
@@ -211,13 +272,16 @@ async function disableContentTypeLocalized({ oldContentTypes, contentTypes: cont
|
|
|
211
272
|
if (!oldContentTypes) {
|
|
212
273
|
return;
|
|
213
274
|
}
|
|
275
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
276
|
+
if (!i18nPlugin) {
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
214
279
|
for (const uid in contentTypes2) {
|
|
215
280
|
if (!oldContentTypes[uid]) {
|
|
216
281
|
continue;
|
|
217
282
|
}
|
|
218
283
|
const oldContentType = oldContentTypes[uid];
|
|
219
284
|
const contentType = contentTypes2[uid];
|
|
220
|
-
const i18nPlugin = strapi.plugin("i18n");
|
|
221
285
|
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
222
286
|
if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
|
|
223
287
|
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
@@ -230,13 +294,16 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
|
|
|
230
294
|
if (!oldContentTypes) {
|
|
231
295
|
return;
|
|
232
296
|
}
|
|
297
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
298
|
+
if (!i18nPlugin) {
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
233
301
|
for (const uid in contentTypes2) {
|
|
234
302
|
if (!oldContentTypes[uid]) {
|
|
235
303
|
continue;
|
|
236
304
|
}
|
|
237
305
|
const oldContentType = oldContentTypes[uid];
|
|
238
306
|
const contentType = contentTypes2[uid];
|
|
239
|
-
const i18nPlugin = strapi.plugin("i18n");
|
|
240
307
|
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
241
308
|
const { getDefaultLocale } = i18nPlugin.service("locales");
|
|
242
309
|
if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
|
|
@@ -247,11 +314,43 @@ async function enableContentTypeLocalized({ oldContentTypes, contentTypes: conte
|
|
|
247
314
|
}
|
|
248
315
|
}
|
|
249
316
|
}
|
|
250
|
-
const
|
|
317
|
+
const addEntryDocumentToReleaseActions = {
|
|
318
|
+
name: "content-releases::5.0.0-add-entry-document-id-to-release-actions",
|
|
319
|
+
async up(trx, db) {
|
|
320
|
+
const hasTable = await trx.schema.hasTable("strapi_release_actions");
|
|
321
|
+
if (!hasTable) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
const hasPolymorphicColumn = await trx.schema.hasColumn("strapi_release_actions", "target_id");
|
|
325
|
+
if (hasPolymorphicColumn) {
|
|
326
|
+
const hasEntryDocumentIdColumn = await trx.schema.hasColumn(
|
|
327
|
+
"strapi_release_actions",
|
|
328
|
+
"entry_document_id"
|
|
329
|
+
);
|
|
330
|
+
if (!hasEntryDocumentIdColumn) {
|
|
331
|
+
await trx.schema.alterTable("strapi_release_actions", (table) => {
|
|
332
|
+
table.string("entry_document_id");
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
const releaseActions = await trx.select("*").from("strapi_release_actions");
|
|
336
|
+
async.map(releaseActions, async (action) => {
|
|
337
|
+
const { target_type, target_id } = action;
|
|
338
|
+
const entry = await db.query(target_type).findOne({ where: { id: target_id } });
|
|
339
|
+
if (entry) {
|
|
340
|
+
await trx("strapi_release_actions").update({ entry_document_id: entry.documentId }).where("id", action.id);
|
|
341
|
+
}
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
},
|
|
345
|
+
async down() {
|
|
346
|
+
throw new Error("not implemented");
|
|
347
|
+
}
|
|
348
|
+
};
|
|
251
349
|
const register = async ({ strapi: strapi2 }) => {
|
|
252
|
-
if (features
|
|
253
|
-
await strapi2.admin
|
|
254
|
-
strapi2.
|
|
350
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
351
|
+
await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
|
|
352
|
+
strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
|
|
353
|
+
strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
|
|
255
354
|
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
256
355
|
}
|
|
257
356
|
if (strapi2.plugin("graphql")) {
|
|
@@ -260,129 +359,135 @@ const register = async ({ strapi: strapi2 }) => {
|
|
|
260
359
|
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
261
360
|
}
|
|
262
361
|
};
|
|
263
|
-
const
|
|
362
|
+
const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
|
|
363
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
364
|
+
where: {
|
|
365
|
+
releasedAt: null,
|
|
366
|
+
actions: {
|
|
367
|
+
contentType,
|
|
368
|
+
entryDocumentId: entry.documentId,
|
|
369
|
+
locale: entry.locale
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
const entryStatus = await isEntryValid(contentType, entry, { strapi });
|
|
374
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).updateMany({
|
|
375
|
+
where: {
|
|
376
|
+
contentType,
|
|
377
|
+
entryDocumentId: entry.documentId,
|
|
378
|
+
locale: entry.locale
|
|
379
|
+
},
|
|
380
|
+
data: {
|
|
381
|
+
isEntryValid: entryStatus
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
for (const release2 of releases) {
|
|
385
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
const deleteActionsAndUpdateReleaseStatus = async (params) => {
|
|
389
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
390
|
+
where: {
|
|
391
|
+
actions: params
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
395
|
+
where: params
|
|
396
|
+
});
|
|
397
|
+
for (const release2 of releases) {
|
|
398
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
const deleteActionsOnDelete = async (ctx, next) => {
|
|
402
|
+
if (ctx.action !== "delete") {
|
|
403
|
+
return next();
|
|
404
|
+
}
|
|
405
|
+
if (!contentTypes$1.hasDraftAndPublish(ctx.contentType)) {
|
|
406
|
+
return next();
|
|
407
|
+
}
|
|
408
|
+
const contentType = ctx.contentType.uid;
|
|
409
|
+
const { documentId, locale } = ctx.params;
|
|
410
|
+
const result = await next();
|
|
411
|
+
if (!result) {
|
|
412
|
+
return result;
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
deleteActionsAndUpdateReleaseStatus({
|
|
416
|
+
contentType,
|
|
417
|
+
entryDocumentId: documentId,
|
|
418
|
+
...locale !== "*" && { locale }
|
|
419
|
+
});
|
|
420
|
+
} catch (error) {
|
|
421
|
+
strapi.log.error("Error while deleting release actions after delete", {
|
|
422
|
+
error
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
return result;
|
|
426
|
+
};
|
|
427
|
+
const updateActionsOnUpdate = async (ctx, next) => {
|
|
428
|
+
if (ctx.action !== "update") {
|
|
429
|
+
return next();
|
|
430
|
+
}
|
|
431
|
+
if (!contentTypes$1.hasDraftAndPublish(ctx.contentType)) {
|
|
432
|
+
return next();
|
|
433
|
+
}
|
|
434
|
+
const contentType = ctx.contentType.uid;
|
|
435
|
+
const result = await next();
|
|
436
|
+
if (!result) {
|
|
437
|
+
return result;
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
updateActionsStatusAndUpdateReleaseStatus(contentType, result);
|
|
441
|
+
} catch (error) {
|
|
442
|
+
strapi.log.error("Error while updating release actions after update", {
|
|
443
|
+
error
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return result;
|
|
447
|
+
};
|
|
448
|
+
const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
|
|
449
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
450
|
+
where: {
|
|
451
|
+
actions: params
|
|
452
|
+
}
|
|
453
|
+
});
|
|
454
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
455
|
+
where: params
|
|
456
|
+
});
|
|
457
|
+
for (const release2 of releases) {
|
|
458
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
459
|
+
}
|
|
460
|
+
};
|
|
264
461
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
265
|
-
if (features
|
|
462
|
+
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
266
463
|
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
267
464
|
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
268
465
|
);
|
|
269
466
|
strapi2.db.lifecycles.subscribe({
|
|
270
467
|
models: contentTypesWithDraftAndPublish,
|
|
271
|
-
async afterDelete(event) {
|
|
272
|
-
try {
|
|
273
|
-
const { model, result } = event;
|
|
274
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
275
|
-
const { id } = result;
|
|
276
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
277
|
-
where: {
|
|
278
|
-
actions: {
|
|
279
|
-
target_type: model.uid,
|
|
280
|
-
target_id: id
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
});
|
|
284
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
285
|
-
where: {
|
|
286
|
-
target_type: model.uid,
|
|
287
|
-
target_id: id
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
for (const release2 of releases) {
|
|
291
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
} catch (error) {
|
|
295
|
-
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
296
|
-
}
|
|
297
|
-
},
|
|
298
|
-
/**
|
|
299
|
-
* deleteMany hook doesn't return the deleted entries ids
|
|
300
|
-
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
301
|
-
*/
|
|
302
|
-
async beforeDeleteMany(event) {
|
|
303
|
-
const { model, params } = event;
|
|
304
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
305
|
-
const { where } = params;
|
|
306
|
-
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
307
|
-
event.state.entriesToDelete = entriesToDelete;
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
468
|
/**
|
|
311
|
-
*
|
|
312
|
-
* 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
|
|
313
470
|
*/
|
|
314
471
|
async afterDeleteMany(event) {
|
|
315
472
|
try {
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
target_id: {
|
|
324
|
-
$in: entriesToDelete.map(
|
|
325
|
-
(entry) => entry.id
|
|
326
|
-
)
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
});
|
|
331
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
332
|
-
where: {
|
|
333
|
-
target_type: model.uid,
|
|
334
|
-
target_id: {
|
|
335
|
-
$in: entriesToDelete.map((entry) => entry.id)
|
|
336
|
-
}
|
|
337
|
-
}
|
|
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 }
|
|
338
480
|
});
|
|
339
|
-
for (const release2 of releases) {
|
|
340
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
341
|
-
}
|
|
342
481
|
}
|
|
343
482
|
} catch (error) {
|
|
344
483
|
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
345
484
|
error
|
|
346
485
|
});
|
|
347
486
|
}
|
|
348
|
-
},
|
|
349
|
-
async afterUpdate(event) {
|
|
350
|
-
try {
|
|
351
|
-
const { model, result } = event;
|
|
352
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
353
|
-
const isEntryValid = await getEntryValidStatus(
|
|
354
|
-
model.uid,
|
|
355
|
-
result,
|
|
356
|
-
{
|
|
357
|
-
strapi: strapi2
|
|
358
|
-
}
|
|
359
|
-
);
|
|
360
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
361
|
-
where: {
|
|
362
|
-
target_type: model.uid,
|
|
363
|
-
target_id: result.id
|
|
364
|
-
},
|
|
365
|
-
data: {
|
|
366
|
-
isEntryValid
|
|
367
|
-
}
|
|
368
|
-
});
|
|
369
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
370
|
-
where: {
|
|
371
|
-
actions: {
|
|
372
|
-
target_type: model.uid,
|
|
373
|
-
target_id: result.id
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
});
|
|
377
|
-
for (const release2 of releases) {
|
|
378
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
} catch (error) {
|
|
382
|
-
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
383
|
-
}
|
|
384
487
|
}
|
|
385
488
|
});
|
|
489
|
+
strapi2.documents.use(deleteActionsOnDelete);
|
|
490
|
+
strapi2.documents.use(updateActionsOnUpdate);
|
|
386
491
|
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
387
492
|
strapi2.log.error(
|
|
388
493
|
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
@@ -390,7 +495,7 @@ const bootstrap = async ({ strapi: strapi2 }) => {
|
|
|
390
495
|
throw err;
|
|
391
496
|
});
|
|
392
497
|
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
393
|
-
strapi2.webhookStore.addAllowedEvent(key, value);
|
|
498
|
+
strapi2.get("webhookStore").addAllowedEvent(key, value);
|
|
394
499
|
});
|
|
395
500
|
}
|
|
396
501
|
};
|
|
@@ -474,15 +579,13 @@ const schema = {
|
|
|
474
579
|
enum: ["publish", "unpublish"],
|
|
475
580
|
required: true
|
|
476
581
|
},
|
|
477
|
-
entry: {
|
|
478
|
-
type: "relation",
|
|
479
|
-
relation: "morphToOne",
|
|
480
|
-
configurable: false
|
|
481
|
-
},
|
|
482
582
|
contentType: {
|
|
483
583
|
type: "string",
|
|
484
584
|
required: true
|
|
485
585
|
},
|
|
586
|
+
entryDocumentId: {
|
|
587
|
+
type: "string"
|
|
588
|
+
},
|
|
486
589
|
locale: {
|
|
487
590
|
type: "string"
|
|
488
591
|
},
|
|
@@ -504,18 +607,6 @@ const contentTypes = {
|
|
|
504
607
|
release: release$1,
|
|
505
608
|
"release-action": releaseAction$1
|
|
506
609
|
};
|
|
507
|
-
const getGroupName = (queryValue) => {
|
|
508
|
-
switch (queryValue) {
|
|
509
|
-
case "contentType":
|
|
510
|
-
return "contentType.displayName";
|
|
511
|
-
case "action":
|
|
512
|
-
return "type";
|
|
513
|
-
case "locale":
|
|
514
|
-
return _.getOr("No locale", "locale.name");
|
|
515
|
-
default:
|
|
516
|
-
return "contentType.displayName";
|
|
517
|
-
}
|
|
518
|
-
};
|
|
519
610
|
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
520
611
|
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
521
612
|
strapi2.eventHub.emit(event, {
|
|
@@ -524,93 +615,32 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
524
615
|
release: release2
|
|
525
616
|
});
|
|
526
617
|
};
|
|
527
|
-
const publishSingleTypeAction = async (uid, actionType, entryId) => {
|
|
528
|
-
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
529
|
-
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
530
|
-
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
531
|
-
const entry = await strapi2.entityService.findOne(uid, entryId, { populate });
|
|
532
|
-
try {
|
|
533
|
-
if (actionType === "publish") {
|
|
534
|
-
await entityManagerService.publish(entry, uid);
|
|
535
|
-
} else {
|
|
536
|
-
await entityManagerService.unpublish(entry, uid);
|
|
537
|
-
}
|
|
538
|
-
} catch (error) {
|
|
539
|
-
if (error instanceof errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft"))
|
|
540
|
-
;
|
|
541
|
-
else {
|
|
542
|
-
throw error;
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
const publishCollectionTypeAction = async (uid, entriesToPublishIds, entriestoUnpublishIds) => {
|
|
547
|
-
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
548
|
-
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
549
|
-
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
550
|
-
const entriesToPublish = await strapi2.entityService.findMany(uid, {
|
|
551
|
-
filters: {
|
|
552
|
-
id: {
|
|
553
|
-
$in: entriesToPublishIds
|
|
554
|
-
}
|
|
555
|
-
},
|
|
556
|
-
populate
|
|
557
|
-
});
|
|
558
|
-
const entriesToUnpublish = await strapi2.entityService.findMany(uid, {
|
|
559
|
-
filters: {
|
|
560
|
-
id: {
|
|
561
|
-
$in: entriestoUnpublishIds
|
|
562
|
-
}
|
|
563
|
-
},
|
|
564
|
-
populate
|
|
565
|
-
});
|
|
566
|
-
if (entriesToPublish.length > 0) {
|
|
567
|
-
await entityManagerService.publishMany(entriesToPublish, uid);
|
|
568
|
-
}
|
|
569
|
-
if (entriesToUnpublish.length > 0) {
|
|
570
|
-
await entityManagerService.unpublishMany(entriesToUnpublish, uid);
|
|
571
|
-
}
|
|
572
|
-
};
|
|
573
618
|
const getFormattedActions = async (releaseId) => {
|
|
574
619
|
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
575
620
|
where: {
|
|
576
621
|
release: {
|
|
577
622
|
id: releaseId
|
|
578
623
|
}
|
|
579
|
-
},
|
|
580
|
-
populate: {
|
|
581
|
-
entry: {
|
|
582
|
-
fields: ["id"]
|
|
583
|
-
}
|
|
584
624
|
}
|
|
585
625
|
});
|
|
586
626
|
if (actions.length === 0) {
|
|
587
627
|
throw new errors.ValidationError("No entries to publish");
|
|
588
628
|
}
|
|
589
|
-
const
|
|
590
|
-
const singleTypeActions = [];
|
|
629
|
+
const formattedActions = {};
|
|
591
630
|
for (const action of actions) {
|
|
592
631
|
const contentTypeUid = action.contentType;
|
|
593
|
-
if (
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
};
|
|
599
|
-
}
|
|
600
|
-
if (action.type === "publish") {
|
|
601
|
-
collectionTypeActions[contentTypeUid].entriesToPublishIds.push(action.entry.id);
|
|
602
|
-
} else {
|
|
603
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
604
|
-
}
|
|
605
|
-
} else {
|
|
606
|
-
singleTypeActions.push({
|
|
607
|
-
uid: contentTypeUid,
|
|
608
|
-
action: action.type,
|
|
609
|
-
id: action.entry.id
|
|
610
|
-
});
|
|
632
|
+
if (!formattedActions[contentTypeUid]) {
|
|
633
|
+
formattedActions[contentTypeUid] = {
|
|
634
|
+
publish: [],
|
|
635
|
+
unpublish: []
|
|
636
|
+
};
|
|
611
637
|
}
|
|
638
|
+
formattedActions[contentTypeUid][action.type].push({
|
|
639
|
+
documentId: action.entryDocumentId,
|
|
640
|
+
locale: action.locale
|
|
641
|
+
});
|
|
612
642
|
}
|
|
613
|
-
return
|
|
643
|
+
return formattedActions;
|
|
614
644
|
};
|
|
615
645
|
return {
|
|
616
646
|
async create(releaseData, { user }) {
|
|
@@ -625,7 +655,7 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
625
655
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
626
656
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
627
657
|
]);
|
|
628
|
-
const release2 = await strapi2.
|
|
658
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
|
|
629
659
|
data: {
|
|
630
660
|
...releaseWithCreatorFields,
|
|
631
661
|
status: "empty"
|
|
@@ -639,94 +669,28 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
639
669
|
return release2;
|
|
640
670
|
},
|
|
641
671
|
async findOne(id, query = {}) {
|
|
642
|
-
const
|
|
643
|
-
|
|
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 }
|
|
644
676
|
});
|
|
645
677
|
return release2;
|
|
646
678
|
},
|
|
647
679
|
findPage(query) {
|
|
648
|
-
|
|
649
|
-
|
|
680
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
681
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findPage({
|
|
682
|
+
...dbQuery,
|
|
650
683
|
populate: {
|
|
651
684
|
actions: {
|
|
652
|
-
// @ts-expect-error Ignore missing properties
|
|
653
685
|
count: true
|
|
654
686
|
}
|
|
655
687
|
}
|
|
656
688
|
});
|
|
657
689
|
},
|
|
658
|
-
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
target_type: contentTypeUid,
|
|
663
|
-
target_id: entryId
|
|
664
|
-
},
|
|
665
|
-
releasedAt: {
|
|
666
|
-
$null: true
|
|
667
|
-
}
|
|
668
|
-
},
|
|
669
|
-
populate: {
|
|
670
|
-
// Filter the action to get only the content type entry
|
|
671
|
-
actions: {
|
|
672
|
-
where: {
|
|
673
|
-
target_type: contentTypeUid,
|
|
674
|
-
target_id: entryId
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
});
|
|
679
|
-
return releases.map((release2) => {
|
|
680
|
-
if (release2.actions?.length) {
|
|
681
|
-
const [actionForEntry] = release2.actions;
|
|
682
|
-
delete release2.actions;
|
|
683
|
-
return {
|
|
684
|
-
...release2,
|
|
685
|
-
action: actionForEntry
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
|
-
return release2;
|
|
689
|
-
});
|
|
690
|
-
},
|
|
691
|
-
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
692
|
-
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
693
|
-
where: {
|
|
694
|
-
releasedAt: {
|
|
695
|
-
$null: true
|
|
696
|
-
},
|
|
697
|
-
actions: {
|
|
698
|
-
target_type: contentTypeUid,
|
|
699
|
-
target_id: entryId
|
|
700
|
-
}
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
704
|
-
where: {
|
|
705
|
-
$or: [
|
|
706
|
-
{
|
|
707
|
-
id: {
|
|
708
|
-
$notIn: releasesRelated.map((release2) => release2.id)
|
|
709
|
-
}
|
|
710
|
-
},
|
|
711
|
-
{
|
|
712
|
-
actions: null
|
|
713
|
-
}
|
|
714
|
-
],
|
|
715
|
-
releasedAt: {
|
|
716
|
-
$null: true
|
|
717
|
-
}
|
|
718
|
-
}
|
|
719
|
-
});
|
|
720
|
-
return releases.map((release2) => {
|
|
721
|
-
if (release2.actions?.length) {
|
|
722
|
-
const [actionForEntry] = release2.actions;
|
|
723
|
-
delete release2.actions;
|
|
724
|
-
return {
|
|
725
|
-
...release2,
|
|
726
|
-
action: actionForEntry
|
|
727
|
-
};
|
|
728
|
-
}
|
|
729
|
-
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
|
|
730
694
|
});
|
|
731
695
|
},
|
|
732
696
|
async update(id, releaseData, { user }) {
|
|
@@ -741,19 +705,15 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
741
705
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
742
706
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
743
707
|
]);
|
|
744
|
-
const release2 = await strapi2.
|
|
708
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
|
|
745
709
|
if (!release2) {
|
|
746
710
|
throw new errors.NotFoundError(`No release found for id ${id}`);
|
|
747
711
|
}
|
|
748
712
|
if (release2.releasedAt) {
|
|
749
713
|
throw new errors.ValidationError("Release already published");
|
|
750
714
|
}
|
|
751
|
-
const updatedRelease = await strapi2.
|
|
752
|
-
|
|
753
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
754
|
-
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
755
|
-
*/
|
|
756
|
-
// @ts-expect-error see above
|
|
715
|
+
const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
716
|
+
where: { id },
|
|
757
717
|
data: releaseWithCreatorFields
|
|
758
718
|
});
|
|
759
719
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
@@ -766,130 +726,6 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
766
726
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
767
727
|
return updatedRelease;
|
|
768
728
|
},
|
|
769
|
-
async createAction(releaseId, action) {
|
|
770
|
-
const { validateEntryContentType, validateUniqueEntry } = getService("release-validation", {
|
|
771
|
-
strapi: strapi2
|
|
772
|
-
});
|
|
773
|
-
await Promise.all([
|
|
774
|
-
validateEntryContentType(action.entry.contentType),
|
|
775
|
-
validateUniqueEntry(releaseId, action)
|
|
776
|
-
]);
|
|
777
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId);
|
|
778
|
-
if (!release2) {
|
|
779
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
780
|
-
}
|
|
781
|
-
if (release2.releasedAt) {
|
|
782
|
-
throw new errors.ValidationError("Release already published");
|
|
783
|
-
}
|
|
784
|
-
const { entry, type } = action;
|
|
785
|
-
const populatedEntry = await getPopulatedEntry(entry.contentType, entry.id, { strapi: strapi2 });
|
|
786
|
-
const isEntryValid = await getEntryValidStatus(entry.contentType, populatedEntry, { strapi: strapi2 });
|
|
787
|
-
const releaseAction2 = await strapi2.entityService.create(RELEASE_ACTION_MODEL_UID, {
|
|
788
|
-
data: {
|
|
789
|
-
type,
|
|
790
|
-
contentType: entry.contentType,
|
|
791
|
-
locale: entry.locale,
|
|
792
|
-
isEntryValid,
|
|
793
|
-
entry: {
|
|
794
|
-
id: entry.id,
|
|
795
|
-
__type: entry.contentType,
|
|
796
|
-
__pivot: { field: "entry" }
|
|
797
|
-
},
|
|
798
|
-
release: releaseId
|
|
799
|
-
},
|
|
800
|
-
populate: { release: { fields: ["id"] }, entry: { fields: ["id"] } }
|
|
801
|
-
});
|
|
802
|
-
this.updateReleaseStatus(releaseId);
|
|
803
|
-
return releaseAction2;
|
|
804
|
-
},
|
|
805
|
-
async findActions(releaseId, query) {
|
|
806
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
807
|
-
fields: ["id"]
|
|
808
|
-
});
|
|
809
|
-
if (!release2) {
|
|
810
|
-
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
811
|
-
}
|
|
812
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
813
|
-
...query,
|
|
814
|
-
populate: {
|
|
815
|
-
entry: {
|
|
816
|
-
populate: "*"
|
|
817
|
-
}
|
|
818
|
-
},
|
|
819
|
-
filters: {
|
|
820
|
-
release: releaseId
|
|
821
|
-
}
|
|
822
|
-
});
|
|
823
|
-
},
|
|
824
|
-
async countActions(query) {
|
|
825
|
-
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
826
|
-
},
|
|
827
|
-
async groupActions(actions, groupBy) {
|
|
828
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
829
|
-
if (!acc.includes(action.contentType)) {
|
|
830
|
-
acc.push(action.contentType);
|
|
831
|
-
}
|
|
832
|
-
return acc;
|
|
833
|
-
}, []);
|
|
834
|
-
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
835
|
-
contentTypeUids
|
|
836
|
-
);
|
|
837
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
838
|
-
const formattedData = actions.map((action) => {
|
|
839
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
840
|
-
return {
|
|
841
|
-
...action,
|
|
842
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
843
|
-
contentType: {
|
|
844
|
-
displayName,
|
|
845
|
-
mainFieldValue: action.entry[mainField],
|
|
846
|
-
uid: action.contentType
|
|
847
|
-
}
|
|
848
|
-
};
|
|
849
|
-
});
|
|
850
|
-
const groupName = getGroupName(groupBy);
|
|
851
|
-
return _.groupBy(groupName)(formattedData);
|
|
852
|
-
},
|
|
853
|
-
async getLocalesDataForActions() {
|
|
854
|
-
if (!strapi2.plugin("i18n")) {
|
|
855
|
-
return {};
|
|
856
|
-
}
|
|
857
|
-
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
858
|
-
return allLocales.reduce((acc, locale) => {
|
|
859
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
860
|
-
return acc;
|
|
861
|
-
}, {});
|
|
862
|
-
},
|
|
863
|
-
async getContentTypesDataForActions(contentTypesUids) {
|
|
864
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
865
|
-
const contentTypesData = {};
|
|
866
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
867
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
868
|
-
uid: contentTypeUid
|
|
869
|
-
});
|
|
870
|
-
contentTypesData[contentTypeUid] = {
|
|
871
|
-
mainField: contentTypeConfig.settings.mainField,
|
|
872
|
-
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
873
|
-
};
|
|
874
|
-
}
|
|
875
|
-
return contentTypesData;
|
|
876
|
-
},
|
|
877
|
-
getContentTypeModelsFromActions(actions) {
|
|
878
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
879
|
-
if (!acc.includes(action.contentType)) {
|
|
880
|
-
acc.push(action.contentType);
|
|
881
|
-
}
|
|
882
|
-
return acc;
|
|
883
|
-
}, []);
|
|
884
|
-
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
885
|
-
(acc, contentTypeUid) => {
|
|
886
|
-
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
887
|
-
return acc;
|
|
888
|
-
},
|
|
889
|
-
{}
|
|
890
|
-
);
|
|
891
|
-
return contentTypeModelsMap;
|
|
892
|
-
},
|
|
893
729
|
async getAllComponents() {
|
|
894
730
|
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
895
731
|
const components = await contentManagerComponentsService.findAllComponents();
|
|
@@ -903,10 +739,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
903
739
|
return componentsMap;
|
|
904
740
|
},
|
|
905
741
|
async delete(releaseId) {
|
|
906
|
-
const release2 = await strapi2.
|
|
742
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
743
|
+
where: { id: releaseId },
|
|
907
744
|
populate: {
|
|
908
745
|
actions: {
|
|
909
|
-
|
|
746
|
+
select: ["id"]
|
|
910
747
|
}
|
|
911
748
|
}
|
|
912
749
|
});
|
|
@@ -924,7 +761,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
924
761
|
}
|
|
925
762
|
}
|
|
926
763
|
});
|
|
927
|
-
await strapi2.
|
|
764
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
765
|
+
where: {
|
|
766
|
+
id: releaseId
|
|
767
|
+
}
|
|
768
|
+
});
|
|
928
769
|
});
|
|
929
770
|
if (release2.scheduledAt) {
|
|
930
771
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
@@ -950,22 +791,19 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
950
791
|
}
|
|
951
792
|
try {
|
|
952
793
|
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
953
|
-
const
|
|
954
|
-
|
|
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
|
+
)
|
|
955
806
|
);
|
|
956
|
-
await strapi2.db.transaction(async () => {
|
|
957
|
-
for (const { uid, action, id } of singleTypeActions) {
|
|
958
|
-
await publishSingleTypeAction(uid, action, id);
|
|
959
|
-
}
|
|
960
|
-
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
961
|
-
const uid = contentTypeUid;
|
|
962
|
-
await publishCollectionTypeAction(
|
|
963
|
-
uid,
|
|
964
|
-
collectionTypeActions[uid].entriesToPublishIds,
|
|
965
|
-
collectionTypeActions[uid].entriesToUnpublishIds
|
|
966
|
-
);
|
|
967
|
-
}
|
|
968
|
-
});
|
|
969
807
|
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
970
808
|
where: {
|
|
971
809
|
id: releaseId
|
|
@@ -986,22 +824,245 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
986
824
|
isPublished: false,
|
|
987
825
|
error: error2
|
|
988
826
|
});
|
|
989
|
-
await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
|
|
990
|
-
status: "failed"
|
|
991
|
-
}).transacting(trx).execute();
|
|
992
|
-
return {
|
|
993
|
-
release: null,
|
|
994
|
-
error: error2
|
|
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
|
+
}
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
868
|
+
where: {
|
|
869
|
+
id: releaseId
|
|
870
|
+
},
|
|
871
|
+
data: {
|
|
872
|
+
status: "ready"
|
|
873
|
+
}
|
|
874
|
+
});
|
|
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
|
|
995
1053
|
};
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
return release2;
|
|
1054
|
+
return acc;
|
|
1055
|
+
},
|
|
1056
|
+
{}
|
|
1057
|
+
);
|
|
1058
|
+
return contentTypeModelsMap;
|
|
1002
1059
|
},
|
|
1003
|
-
async
|
|
1004
|
-
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({
|
|
1005
1066
|
where: {
|
|
1006
1067
|
id: actionId,
|
|
1007
1068
|
release: {
|
|
@@ -1010,17 +1071,42 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1010
1071
|
$null: true
|
|
1011
1072
|
}
|
|
1012
1073
|
}
|
|
1013
|
-
}
|
|
1014
|
-
data: update
|
|
1074
|
+
}
|
|
1015
1075
|
});
|
|
1016
|
-
if (!
|
|
1076
|
+
if (!action) {
|
|
1017
1077
|
throw new errors.NotFoundError(
|
|
1018
1078
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1019
1079
|
);
|
|
1020
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);
|
|
1021
1107
|
return updatedAction;
|
|
1022
1108
|
},
|
|
1023
|
-
async
|
|
1109
|
+
async delete(actionId, releaseId) {
|
|
1024
1110
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
1025
1111
|
where: {
|
|
1026
1112
|
id: actionId,
|
|
@@ -1037,51 +1123,56 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1037
1123
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1038
1124
|
);
|
|
1039
1125
|
}
|
|
1040
|
-
|
|
1126
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1041
1127
|
return deletedAction;
|
|
1042
1128
|
},
|
|
1043
|
-
async
|
|
1044
|
-
const
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
}
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
}
|
|
1055
|
-
})
|
|
1056
|
-
]);
|
|
1057
|
-
if (totalActions > 0) {
|
|
1058
|
-
if (invalidActions > 0) {
|
|
1059
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1060
|
-
where: {
|
|
1061
|
-
id: releaseId
|
|
1062
|
-
},
|
|
1063
|
-
data: {
|
|
1064
|
-
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
|
|
1065
1140
|
}
|
|
1066
|
-
}
|
|
1067
|
-
}
|
|
1068
|
-
|
|
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({
|
|
1069
1156
|
where: {
|
|
1070
|
-
id:
|
|
1157
|
+
id: action.id
|
|
1071
1158
|
},
|
|
1072
1159
|
data: {
|
|
1073
|
-
|
|
1160
|
+
isEntryValid: isValid
|
|
1074
1161
|
}
|
|
1075
1162
|
});
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
where: {
|
|
1079
|
-
id: releaseId
|
|
1080
|
-
},
|
|
1081
|
-
data: {
|
|
1082
|
-
status: "empty"
|
|
1163
|
+
if (!releasesUpdated.includes(action.release.id)) {
|
|
1164
|
+
releasesUpdated.push(action.release.id);
|
|
1083
1165
|
}
|
|
1166
|
+
return {
|
|
1167
|
+
id: action.id,
|
|
1168
|
+
isEntryValid: isValid
|
|
1169
|
+
};
|
|
1084
1170
|
});
|
|
1171
|
+
if (releasesUpdated.length > 0) {
|
|
1172
|
+
await async.map(releasesUpdated, async (releaseId) => {
|
|
1173
|
+
await getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1085
1176
|
}
|
|
1086
1177
|
};
|
|
1087
1178
|
};
|
|
@@ -1093,37 +1184,43 @@ class AlreadyOnReleaseError extends errors.ApplicationError {
|
|
|
1093
1184
|
}
|
|
1094
1185
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
1095
1186
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
1096
|
-
const release2 = await strapi2.
|
|
1097
|
-
|
|
1187
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1188
|
+
where: {
|
|
1189
|
+
id: releaseId
|
|
1190
|
+
},
|
|
1191
|
+
populate: {
|
|
1192
|
+
actions: true
|
|
1193
|
+
}
|
|
1098
1194
|
});
|
|
1099
1195
|
if (!release2) {
|
|
1100
1196
|
throw new errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1101
1197
|
}
|
|
1102
1198
|
const isEntryInRelease = release2.actions.some(
|
|
1103
|
-
(action) =>
|
|
1199
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
1104
1200
|
);
|
|
1105
1201
|
if (isEntryInRelease) {
|
|
1106
1202
|
throw new AlreadyOnReleaseError(
|
|
1107
|
-
`Entry with
|
|
1203
|
+
`Entry with documentId ${releaseActionArgs.entryDocumentId}${releaseActionArgs.locale ? `( ${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
|
|
1108
1204
|
);
|
|
1109
1205
|
}
|
|
1110
1206
|
},
|
|
1111
|
-
|
|
1207
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
1112
1208
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
1113
1209
|
if (!contentType) {
|
|
1114
1210
|
throw new errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
1115
1211
|
}
|
|
1116
|
-
if (!contentType
|
|
1212
|
+
if (!contentTypes$1.hasDraftAndPublish(contentType)) {
|
|
1117
1213
|
throw new errors.ValidationError(
|
|
1118
1214
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
1119
1215
|
);
|
|
1120
1216
|
}
|
|
1217
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1218
|
+
throw new errors.ValidationError("Document id is required for collection type");
|
|
1219
|
+
}
|
|
1121
1220
|
},
|
|
1122
1221
|
async validatePendingReleasesLimit() {
|
|
1123
|
-
const
|
|
1124
|
-
|
|
1125
|
-
EE.features.get("cms-content-releases")?.options?.maximumReleases || 3
|
|
1126
|
-
);
|
|
1222
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1223
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
1127
1224
|
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
1128
1225
|
filters: {
|
|
1129
1226
|
releasedAt: {
|
|
@@ -1136,8 +1233,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
1136
1233
|
}
|
|
1137
1234
|
},
|
|
1138
1235
|
async validateUniqueNameForPendingRelease(name, id) {
|
|
1139
|
-
const pendingReleases = await strapi2.
|
|
1140
|
-
|
|
1236
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1237
|
+
where: {
|
|
1141
1238
|
releasedAt: {
|
|
1142
1239
|
$null: true
|
|
1143
1240
|
},
|
|
@@ -1166,7 +1263,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1166
1263
|
}
|
|
1167
1264
|
const job = scheduleJob(scheduleDate, async () => {
|
|
1168
1265
|
try {
|
|
1169
|
-
await getService("release").publish(releaseId);
|
|
1266
|
+
await getService("release", { strapi: strapi2 }).publish(releaseId);
|
|
1170
1267
|
} catch (error) {
|
|
1171
1268
|
}
|
|
1172
1269
|
this.cancel(releaseId);
|
|
@@ -1208,85 +1305,172 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1208
1305
|
}
|
|
1209
1306
|
};
|
|
1210
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
|
+
};
|
|
1211
1329
|
const services = {
|
|
1212
1330
|
release: createReleaseService,
|
|
1331
|
+
"release-action": createReleaseActionService,
|
|
1213
1332
|
"release-validation": createReleaseValidationService,
|
|
1214
|
-
scheduling: createSchedulingService
|
|
1333
|
+
scheduling: createSchedulingService,
|
|
1334
|
+
settings: createSettingsService
|
|
1215
1335
|
};
|
|
1216
|
-
const RELEASE_SCHEMA = yup.object().shape({
|
|
1217
|
-
name: yup.string().trim().required(),
|
|
1218
|
-
scheduledAt: yup.string().nullable(),
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
otherwise: yup.string().nullable()
|
|
1224
|
-
}),
|
|
1225
|
-
timezone: yup.string().when("isScheduled", {
|
|
1226
|
-
is: true,
|
|
1227
|
-
then: yup.string().required().nullable(),
|
|
1228
|
-
otherwise: yup.string().nullable()
|
|
1229
|
-
}),
|
|
1230
|
-
date: yup.string().when("isScheduled", {
|
|
1231
|
-
is: true,
|
|
1232
|
-
then: yup.string().required().nullable(),
|
|
1233
|
-
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()
|
|
1234
1343
|
})
|
|
1235
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();
|
|
1236
1351
|
const validateRelease = validateYupSchema(RELEASE_SCHEMA);
|
|
1352
|
+
const validatefindByDocumentAttachedParams = validateYupSchema(
|
|
1353
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1354
|
+
);
|
|
1237
1355
|
const releaseController = {
|
|
1238
|
-
|
|
1239
|
-
|
|
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({
|
|
1240
1363
|
ability: ctx.state.userAbility,
|
|
1241
1364
|
model: RELEASE_MODEL_UID
|
|
1242
1365
|
});
|
|
1243
1366
|
await permissionsManager.validateQuery(ctx.query);
|
|
1244
1367
|
const releaseService = getService("release", { strapi });
|
|
1245
|
-
const
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
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,
|
|
1384
|
+
actions: {
|
|
1385
|
+
contentType,
|
|
1386
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1387
|
+
locale: locale ?? null
|
|
1388
|
+
}
|
|
1389
|
+
},
|
|
1390
|
+
populate: {
|
|
1260
1391
|
actions: {
|
|
1261
|
-
|
|
1262
|
-
|
|
1392
|
+
fields: ["type"],
|
|
1393
|
+
filters: {
|
|
1394
|
+
contentType,
|
|
1395
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1396
|
+
locale: locale ?? null
|
|
1263
1397
|
}
|
|
1264
1398
|
}
|
|
1265
|
-
}
|
|
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
|
+
}
|
|
1266
1412
|
});
|
|
1267
|
-
const
|
|
1413
|
+
const releases = await releaseService.findMany({
|
|
1268
1414
|
where: {
|
|
1415
|
+
$or: [
|
|
1416
|
+
{
|
|
1417
|
+
id: {
|
|
1418
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1419
|
+
}
|
|
1420
|
+
},
|
|
1421
|
+
{
|
|
1422
|
+
actions: null
|
|
1423
|
+
}
|
|
1424
|
+
],
|
|
1269
1425
|
releasedAt: null
|
|
1270
1426
|
}
|
|
1271
1427
|
});
|
|
1272
|
-
ctx.body = { data
|
|
1428
|
+
ctx.body = { data: releases };
|
|
1273
1429
|
}
|
|
1274
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
|
+
},
|
|
1275
1458
|
async findOne(ctx) {
|
|
1276
1459
|
const id = ctx.params.id;
|
|
1277
1460
|
const releaseService = getService("release", { strapi });
|
|
1461
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1278
1462
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
1279
1463
|
if (!release2) {
|
|
1280
1464
|
throw new errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1281
1465
|
}
|
|
1282
|
-
const count = await
|
|
1466
|
+
const count = await releaseActionService.countActions({
|
|
1283
1467
|
filters: {
|
|
1284
1468
|
release: id
|
|
1285
1469
|
}
|
|
1286
1470
|
});
|
|
1287
1471
|
const sanitizedRelease = {
|
|
1288
1472
|
...release2,
|
|
1289
|
-
createdBy: release2.createdBy ? strapi.admin
|
|
1473
|
+
createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
|
|
1290
1474
|
};
|
|
1291
1475
|
const data = {
|
|
1292
1476
|
...sanitizedRelease,
|
|
@@ -1298,19 +1482,63 @@ const releaseController = {
|
|
|
1298
1482
|
};
|
|
1299
1483
|
ctx.body = { data };
|
|
1300
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
|
+
},
|
|
1301
1529
|
async create(ctx) {
|
|
1302
1530
|
const user = ctx.state.user;
|
|
1303
1531
|
const releaseArgs = ctx.request.body;
|
|
1304
1532
|
await validateRelease(releaseArgs);
|
|
1305
1533
|
const releaseService = getService("release", { strapi });
|
|
1306
1534
|
const release2 = await releaseService.create(releaseArgs, { user });
|
|
1307
|
-
const permissionsManager = strapi.admin
|
|
1535
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1308
1536
|
ability: ctx.state.userAbility,
|
|
1309
1537
|
model: RELEASE_MODEL_UID
|
|
1310
1538
|
});
|
|
1311
|
-
ctx.
|
|
1539
|
+
ctx.created({
|
|
1312
1540
|
data: await permissionsManager.sanitizeOutput(release2)
|
|
1313
|
-
};
|
|
1541
|
+
});
|
|
1314
1542
|
},
|
|
1315
1543
|
async update(ctx) {
|
|
1316
1544
|
const user = ctx.state.user;
|
|
@@ -1319,7 +1547,7 @@ const releaseController = {
|
|
|
1319
1547
|
await validateRelease(releaseArgs);
|
|
1320
1548
|
const releaseService = getService("release", { strapi });
|
|
1321
1549
|
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
1322
|
-
const permissionsManager = strapi.admin
|
|
1550
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1323
1551
|
ability: ctx.state.userAbility,
|
|
1324
1552
|
model: RELEASE_MODEL_UID
|
|
1325
1553
|
});
|
|
@@ -1336,18 +1564,18 @@ const releaseController = {
|
|
|
1336
1564
|
};
|
|
1337
1565
|
},
|
|
1338
1566
|
async publish(ctx) {
|
|
1339
|
-
const user = ctx.state.user;
|
|
1340
1567
|
const id = ctx.params.id;
|
|
1341
1568
|
const releaseService = getService("release", { strapi });
|
|
1342
|
-
const
|
|
1569
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1570
|
+
const release2 = await releaseService.publish(id);
|
|
1343
1571
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1344
|
-
|
|
1572
|
+
releaseActionService.countActions({
|
|
1345
1573
|
filters: {
|
|
1346
1574
|
release: id,
|
|
1347
1575
|
type: "publish"
|
|
1348
1576
|
}
|
|
1349
1577
|
}),
|
|
1350
|
-
|
|
1578
|
+
releaseActionService.countActions({
|
|
1351
1579
|
filters: {
|
|
1352
1580
|
release: id,
|
|
1353
1581
|
type: "unpublish"
|
|
@@ -1365,27 +1593,30 @@ const releaseController = {
|
|
|
1365
1593
|
}
|
|
1366
1594
|
};
|
|
1367
1595
|
const RELEASE_ACTION_SCHEMA = yup$1.object().shape({
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
}).required(),
|
|
1596
|
+
contentType: yup$1.string().required(),
|
|
1597
|
+
entryDocumentId: yup$1.strapiID(),
|
|
1598
|
+
locale: yup$1.string(),
|
|
1372
1599
|
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1373
1600
|
});
|
|
1374
1601
|
const RELEASE_ACTION_UPDATE_SCHEMA = yup$1.object().shape({
|
|
1375
1602
|
type: yup$1.string().oneOf(["publish", "unpublish"]).required()
|
|
1376
1603
|
});
|
|
1604
|
+
const FIND_MANY_ACTIONS_PARAMS = yup$1.object().shape({
|
|
1605
|
+
groupBy: yup$1.string().oneOf(["action", "contentType", "locale"])
|
|
1606
|
+
});
|
|
1377
1607
|
const validateReleaseAction = validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1378
1608
|
const validateReleaseActionUpdateSchema = validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1609
|
+
const validateFindManyActionsParams = validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1379
1610
|
const releaseActionController = {
|
|
1380
1611
|
async create(ctx) {
|
|
1381
1612
|
const releaseId = ctx.params.releaseId;
|
|
1382
1613
|
const releaseActionArgs = ctx.request.body;
|
|
1383
1614
|
await validateReleaseAction(releaseActionArgs);
|
|
1384
|
-
const
|
|
1385
|
-
const releaseAction2 = await
|
|
1386
|
-
ctx.
|
|
1615
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1616
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1617
|
+
ctx.created({
|
|
1387
1618
|
data: releaseAction2
|
|
1388
|
-
};
|
|
1619
|
+
});
|
|
1389
1620
|
},
|
|
1390
1621
|
async createMany(ctx) {
|
|
1391
1622
|
const releaseId = ctx.params.releaseId;
|
|
@@ -1393,12 +1624,15 @@ const releaseActionController = {
|
|
|
1393
1624
|
await Promise.all(
|
|
1394
1625
|
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1395
1626
|
);
|
|
1627
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1396
1628
|
const releaseService = getService("release", { strapi });
|
|
1397
1629
|
const releaseActions = await strapi.db.transaction(async () => {
|
|
1398
1630
|
const releaseActions2 = await Promise.all(
|
|
1399
1631
|
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1400
1632
|
try {
|
|
1401
|
-
const action = await
|
|
1633
|
+
const action = await releaseActionService.create(releaseId, releaseActionArgs, {
|
|
1634
|
+
disableUpdateReleaseStatus: true
|
|
1635
|
+
});
|
|
1402
1636
|
return action;
|
|
1403
1637
|
} catch (error) {
|
|
1404
1638
|
if (error instanceof AlreadyOnReleaseError) {
|
|
@@ -1411,43 +1645,54 @@ const releaseActionController = {
|
|
|
1411
1645
|
return releaseActions2;
|
|
1412
1646
|
});
|
|
1413
1647
|
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1414
|
-
|
|
1648
|
+
if (newReleaseActions.length > 0) {
|
|
1649
|
+
releaseService.updateReleaseStatus(releaseId);
|
|
1650
|
+
}
|
|
1651
|
+
ctx.created({
|
|
1415
1652
|
data: newReleaseActions,
|
|
1416
1653
|
meta: {
|
|
1417
1654
|
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1418
1655
|
totalEntries: releaseActions.length
|
|
1419
1656
|
}
|
|
1420
|
-
};
|
|
1657
|
+
});
|
|
1421
1658
|
},
|
|
1422
1659
|
async findMany(ctx) {
|
|
1423
1660
|
const releaseId = ctx.params.releaseId;
|
|
1424
|
-
const permissionsManager = strapi.admin
|
|
1661
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1425
1662
|
ability: ctx.state.userAbility,
|
|
1426
1663
|
model: RELEASE_ACTION_MODEL_UID
|
|
1427
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;
|
|
1428
1673
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1429
|
-
const
|
|
1430
|
-
const { results, pagination } = await
|
|
1431
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1674
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1675
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1432
1676
|
...query
|
|
1433
1677
|
});
|
|
1434
1678
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
1435
1679
|
if (acc[action.contentType]) {
|
|
1436
1680
|
return acc;
|
|
1437
1681
|
}
|
|
1438
|
-
const contentTypePermissionsManager = strapi.admin
|
|
1682
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1439
1683
|
ability: ctx.state.userAbility,
|
|
1440
1684
|
model: action.contentType
|
|
1441
1685
|
});
|
|
1442
1686
|
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
1443
1687
|
return acc;
|
|
1444
1688
|
}, {});
|
|
1445
|
-
const sanitizedResults = await
|
|
1689
|
+
const sanitizedResults = await async.map(results, async (action) => ({
|
|
1446
1690
|
...action,
|
|
1447
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1691
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1448
1692
|
}));
|
|
1449
|
-
const groupedData = await
|
|
1450
|
-
const contentTypes2 =
|
|
1693
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1694
|
+
const contentTypes2 = await releaseActionService.getContentTypeModelsFromActions(results);
|
|
1695
|
+
const releaseService = getService("release", { strapi });
|
|
1451
1696
|
const components = await releaseService.getAllComponents();
|
|
1452
1697
|
ctx.body = {
|
|
1453
1698
|
data: groupedData,
|
|
@@ -1463,8 +1708,8 @@ const releaseActionController = {
|
|
|
1463
1708
|
const releaseId = ctx.params.releaseId;
|
|
1464
1709
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1465
1710
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1466
|
-
const
|
|
1467
|
-
const updatedAction = await
|
|
1711
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1712
|
+
const updatedAction = await releaseActionService.update(
|
|
1468
1713
|
actionId,
|
|
1469
1714
|
releaseId,
|
|
1470
1715
|
releaseActionUpdateArgs
|
|
@@ -1476,17 +1721,71 @@ const releaseActionController = {
|
|
|
1476
1721
|
async delete(ctx) {
|
|
1477
1722
|
const actionId = ctx.params.actionId;
|
|
1478
1723
|
const releaseId = ctx.params.releaseId;
|
|
1479
|
-
const
|
|
1480
|
-
const deletedReleaseAction = await
|
|
1724
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1725
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1481
1726
|
ctx.body = {
|
|
1482
1727
|
data: deletedReleaseAction
|
|
1483
1728
|
};
|
|
1484
1729
|
}
|
|
1485
1730
|
};
|
|
1486
|
-
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
|
+
};
|
|
1487
1754
|
const release = {
|
|
1488
1755
|
type: "admin",
|
|
1489
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
|
+
},
|
|
1490
1789
|
{
|
|
1491
1790
|
method: "POST",
|
|
1492
1791
|
path: "/",
|
|
@@ -1506,7 +1805,7 @@ const release = {
|
|
|
1506
1805
|
{
|
|
1507
1806
|
method: "GET",
|
|
1508
1807
|
path: "/",
|
|
1509
|
-
handler: "release.
|
|
1808
|
+
handler: "release.findPage",
|
|
1510
1809
|
config: {
|
|
1511
1810
|
policies: [
|
|
1512
1811
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1670,13 +1969,50 @@ const releaseAction = {
|
|
|
1670
1969
|
}
|
|
1671
1970
|
]
|
|
1672
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
|
+
};
|
|
1673
2009
|
const routes = {
|
|
2010
|
+
settings,
|
|
1674
2011
|
release,
|
|
1675
2012
|
"release-action": releaseAction
|
|
1676
2013
|
};
|
|
1677
|
-
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
1678
2014
|
const getPlugin = () => {
|
|
1679
|
-
if (features.isEnabled("cms-content-releases")) {
|
|
2015
|
+
if (strapi.ee.features.isEnabled("cms-content-releases")) {
|
|
1680
2016
|
return {
|
|
1681
2017
|
register,
|
|
1682
2018
|
bootstrap,
|