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