@strapi/content-releases 0.0.0-next.e9b6852d1c05518ff6e37d599321f7aa7aa0683b → 0.0.0-next.ec9b1b708d4d319f2b8b39d9397bd752d250d541
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 +931 -658
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +932 -658
- 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 -38
- 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,130 +749,6 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
809
749
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
810
750
|
return updatedRelease;
|
|
811
751
|
},
|
|
812
|
-
async createAction(releaseId, action) {
|
|
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
|
-
this.updateReleaseStatus(releaseId);
|
|
846
|
-
return releaseAction2;
|
|
847
|
-
},
|
|
848
|
-
async findActions(releaseId, query) {
|
|
849
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
850
|
-
fields: ["id"]
|
|
851
|
-
});
|
|
852
|
-
if (!release2) {
|
|
853
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
854
|
-
}
|
|
855
|
-
return strapi2.entityService.findPage(RELEASE_ACTION_MODEL_UID, {
|
|
856
|
-
...query,
|
|
857
|
-
populate: {
|
|
858
|
-
entry: {
|
|
859
|
-
populate: "*"
|
|
860
|
-
}
|
|
861
|
-
},
|
|
862
|
-
filters: {
|
|
863
|
-
release: releaseId
|
|
864
|
-
}
|
|
865
|
-
});
|
|
866
|
-
},
|
|
867
|
-
async countActions(query) {
|
|
868
|
-
return strapi2.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
|
869
|
-
},
|
|
870
|
-
async groupActions(actions, groupBy) {
|
|
871
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
872
|
-
if (!acc.includes(action.contentType)) {
|
|
873
|
-
acc.push(action.contentType);
|
|
874
|
-
}
|
|
875
|
-
return acc;
|
|
876
|
-
}, []);
|
|
877
|
-
const allReleaseContentTypesDictionary = await this.getContentTypesDataForActions(
|
|
878
|
-
contentTypeUids
|
|
879
|
-
);
|
|
880
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
881
|
-
const formattedData = actions.map((action) => {
|
|
882
|
-
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
883
|
-
return {
|
|
884
|
-
...action,
|
|
885
|
-
locale: action.locale ? allLocalesDictionary[action.locale] : null,
|
|
886
|
-
contentType: {
|
|
887
|
-
displayName,
|
|
888
|
-
mainFieldValue: action.entry[mainField],
|
|
889
|
-
uid: action.contentType
|
|
890
|
-
}
|
|
891
|
-
};
|
|
892
|
-
});
|
|
893
|
-
const groupName = getGroupName(groupBy);
|
|
894
|
-
return ___default.default.groupBy(groupName)(formattedData);
|
|
895
|
-
},
|
|
896
|
-
async getLocalesDataForActions() {
|
|
897
|
-
if (!strapi2.plugin("i18n")) {
|
|
898
|
-
return {};
|
|
899
|
-
}
|
|
900
|
-
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
901
|
-
return allLocales.reduce((acc, locale) => {
|
|
902
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
903
|
-
return acc;
|
|
904
|
-
}, {});
|
|
905
|
-
},
|
|
906
|
-
async getContentTypesDataForActions(contentTypesUids) {
|
|
907
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
908
|
-
const contentTypesData = {};
|
|
909
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
910
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
911
|
-
uid: contentTypeUid
|
|
912
|
-
});
|
|
913
|
-
contentTypesData[contentTypeUid] = {
|
|
914
|
-
mainField: contentTypeConfig.settings.mainField,
|
|
915
|
-
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
916
|
-
};
|
|
917
|
-
}
|
|
918
|
-
return contentTypesData;
|
|
919
|
-
},
|
|
920
|
-
getContentTypeModelsFromActions(actions) {
|
|
921
|
-
const contentTypeUids = actions.reduce((acc, action) => {
|
|
922
|
-
if (!acc.includes(action.contentType)) {
|
|
923
|
-
acc.push(action.contentType);
|
|
924
|
-
}
|
|
925
|
-
return acc;
|
|
926
|
-
}, []);
|
|
927
|
-
const contentTypeModelsMap = contentTypeUids.reduce(
|
|
928
|
-
(acc, contentTypeUid) => {
|
|
929
|
-
acc[contentTypeUid] = strapi2.getModel(contentTypeUid);
|
|
930
|
-
return acc;
|
|
931
|
-
},
|
|
932
|
-
{}
|
|
933
|
-
);
|
|
934
|
-
return contentTypeModelsMap;
|
|
935
|
-
},
|
|
936
752
|
async getAllComponents() {
|
|
937
753
|
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
938
754
|
const components = await contentManagerComponentsService.findAllComponents();
|
|
@@ -946,10 +762,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
946
762
|
return componentsMap;
|
|
947
763
|
},
|
|
948
764
|
async delete(releaseId) {
|
|
949
|
-
const release2 = await strapi2.
|
|
765
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
766
|
+
where: { id: releaseId },
|
|
950
767
|
populate: {
|
|
951
768
|
actions: {
|
|
952
|
-
|
|
769
|
+
select: ["id"]
|
|
953
770
|
}
|
|
954
771
|
}
|
|
955
772
|
});
|
|
@@ -967,7 +784,11 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
967
784
|
}
|
|
968
785
|
}
|
|
969
786
|
});
|
|
970
|
-
await strapi2.
|
|
787
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
788
|
+
where: {
|
|
789
|
+
id: releaseId
|
|
790
|
+
}
|
|
791
|
+
});
|
|
971
792
|
});
|
|
972
793
|
if (release2.scheduledAt) {
|
|
973
794
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
@@ -991,60 +812,280 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
991
812
|
if (lockedRelease.status === "failed") {
|
|
992
813
|
throw new utils.errors.ValidationError("Release failed to publish");
|
|
993
814
|
}
|
|
994
|
-
try {
|
|
995
|
-
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
996
|
-
const
|
|
997
|
-
|
|
998
|
-
|
|
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
|
-
|
|
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"
|
|
1031
1071
|
});
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
release: null,
|
|
1037
|
-
error: error2
|
|
1072
|
+
acc[contentTypeUid] = {
|
|
1073
|
+
...contentTypeModel,
|
|
1074
|
+
hasReviewWorkflow: !!workflow,
|
|
1075
|
+
stageRequiredToPublish: workflow?.stageRequiredToPublish
|
|
1038
1076
|
};
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
return release2;
|
|
1077
|
+
return acc;
|
|
1078
|
+
},
|
|
1079
|
+
{}
|
|
1080
|
+
);
|
|
1081
|
+
return contentTypeModelsMap;
|
|
1045
1082
|
},
|
|
1046
|
-
async
|
|
1047
|
-
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({
|
|
1048
1089
|
where: {
|
|
1049
1090
|
id: actionId,
|
|
1050
1091
|
release: {
|
|
@@ -1053,17 +1094,42 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1053
1094
|
$null: true
|
|
1054
1095
|
}
|
|
1055
1096
|
}
|
|
1056
|
-
}
|
|
1057
|
-
data: update
|
|
1097
|
+
}
|
|
1058
1098
|
});
|
|
1059
|
-
if (!
|
|
1099
|
+
if (!action) {
|
|
1060
1100
|
throw new utils.errors.NotFoundError(
|
|
1061
1101
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1062
1102
|
);
|
|
1063
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);
|
|
1064
1130
|
return updatedAction;
|
|
1065
1131
|
},
|
|
1066
|
-
async
|
|
1132
|
+
async delete(actionId, releaseId) {
|
|
1067
1133
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
1068
1134
|
where: {
|
|
1069
1135
|
id: actionId,
|
|
@@ -1080,51 +1146,56 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
1080
1146
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1081
1147
|
);
|
|
1082
1148
|
}
|
|
1083
|
-
|
|
1149
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1084
1150
|
return deletedAction;
|
|
1085
1151
|
},
|
|
1086
|
-
async
|
|
1087
|
-
const
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
}
|
|
1098
|
-
})
|
|
1099
|
-
]);
|
|
1100
|
-
if (totalActions > 0) {
|
|
1101
|
-
if (invalidActions > 0) {
|
|
1102
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1103
|
-
where: {
|
|
1104
|
-
id: releaseId
|
|
1105
|
-
},
|
|
1106
|
-
data: {
|
|
1107
|
-
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
|
|
1108
1163
|
}
|
|
1109
|
-
}
|
|
1110
|
-
}
|
|
1111
|
-
|
|
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({
|
|
1112
1179
|
where: {
|
|
1113
|
-
id:
|
|
1180
|
+
id: action.id
|
|
1114
1181
|
},
|
|
1115
1182
|
data: {
|
|
1116
|
-
|
|
1183
|
+
isEntryValid: isValid
|
|
1117
1184
|
}
|
|
1118
1185
|
});
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
where: {
|
|
1122
|
-
id: releaseId
|
|
1123
|
-
},
|
|
1124
|
-
data: {
|
|
1125
|
-
status: "empty"
|
|
1186
|
+
if (!releasesUpdated.includes(action.release.id)) {
|
|
1187
|
+
releasesUpdated.push(action.release.id);
|
|
1126
1188
|
}
|
|
1189
|
+
return {
|
|
1190
|
+
id: action.id,
|
|
1191
|
+
isEntryValid: isValid
|
|
1192
|
+
};
|
|
1127
1193
|
});
|
|
1194
|
+
if (releasesUpdated.length > 0) {
|
|
1195
|
+
await utils.async.map(releasesUpdated, async (releaseId) => {
|
|
1196
|
+
await getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1128
1199
|
}
|
|
1129
1200
|
};
|
|
1130
1201
|
};
|
|
@@ -1136,37 +1207,43 @@ class AlreadyOnReleaseError extends utils.errors.ApplicationError {
|
|
|
1136
1207
|
}
|
|
1137
1208
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
1138
1209
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
1139
|
-
const release2 = await strapi2.
|
|
1140
|
-
|
|
1210
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1211
|
+
where: {
|
|
1212
|
+
id: releaseId
|
|
1213
|
+
},
|
|
1214
|
+
populate: {
|
|
1215
|
+
actions: true
|
|
1216
|
+
}
|
|
1141
1217
|
});
|
|
1142
1218
|
if (!release2) {
|
|
1143
1219
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1144
1220
|
}
|
|
1145
1221
|
const isEntryInRelease = release2.actions.some(
|
|
1146
|
-
(action) =>
|
|
1222
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
1147
1223
|
);
|
|
1148
1224
|
if (isEntryInRelease) {
|
|
1149
1225
|
throw new AlreadyOnReleaseError(
|
|
1150
|
-
`Entry with
|
|
1226
|
+
`Entry with documentId ${releaseActionArgs.entryDocumentId}${releaseActionArgs.locale ? `( ${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
|
|
1151
1227
|
);
|
|
1152
1228
|
}
|
|
1153
1229
|
},
|
|
1154
|
-
|
|
1230
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
1155
1231
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
1156
1232
|
if (!contentType) {
|
|
1157
1233
|
throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
1158
1234
|
}
|
|
1159
|
-
if (!contentType
|
|
1235
|
+
if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
|
|
1160
1236
|
throw new utils.errors.ValidationError(
|
|
1161
1237
|
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
1162
1238
|
);
|
|
1163
1239
|
}
|
|
1240
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1241
|
+
throw new utils.errors.ValidationError("Document id is required for collection type");
|
|
1242
|
+
}
|
|
1164
1243
|
},
|
|
1165
1244
|
async validatePendingReleasesLimit() {
|
|
1166
|
-
const
|
|
1167
|
-
|
|
1168
|
-
EE__default.default.features.get("cms-content-releases")?.options?.maximumReleases || 3
|
|
1169
|
-
);
|
|
1245
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1246
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
1170
1247
|
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
1171
1248
|
filters: {
|
|
1172
1249
|
releasedAt: {
|
|
@@ -1179,8 +1256,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
1179
1256
|
}
|
|
1180
1257
|
},
|
|
1181
1258
|
async validateUniqueNameForPendingRelease(name, id) {
|
|
1182
|
-
const pendingReleases = await strapi2.
|
|
1183
|
-
|
|
1259
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1260
|
+
where: {
|
|
1184
1261
|
releasedAt: {
|
|
1185
1262
|
$null: true
|
|
1186
1263
|
},
|
|
@@ -1209,7 +1286,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1209
1286
|
}
|
|
1210
1287
|
const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
|
|
1211
1288
|
try {
|
|
1212
|
-
await getService("release").publish(releaseId);
|
|
1289
|
+
await getService("release", { strapi: strapi2 }).publish(releaseId);
|
|
1213
1290
|
} catch (error) {
|
|
1214
1291
|
}
|
|
1215
1292
|
this.cancel(releaseId);
|
|
@@ -1251,85 +1328,172 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1251
1328
|
}
|
|
1252
1329
|
};
|
|
1253
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
|
+
};
|
|
1254
1352
|
const services = {
|
|
1255
1353
|
release: createReleaseService,
|
|
1354
|
+
"release-action": createReleaseActionService,
|
|
1256
1355
|
"release-validation": createReleaseValidationService,
|
|
1257
|
-
scheduling: createSchedulingService
|
|
1356
|
+
scheduling: createSchedulingService,
|
|
1357
|
+
settings: createSettingsService
|
|
1258
1358
|
};
|
|
1259
|
-
const RELEASE_SCHEMA =
|
|
1260
|
-
name:
|
|
1261
|
-
scheduledAt:
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
otherwise: yup__namespace.string().nullable()
|
|
1267
|
-
}),
|
|
1268
|
-
timezone: yup__namespace.string().when("isScheduled", {
|
|
1269
|
-
is: true,
|
|
1270
|
-
then: yup__namespace.string().required().nullable(),
|
|
1271
|
-
otherwise: yup__namespace.string().nullable()
|
|
1272
|
-
}),
|
|
1273
|
-
date: yup__namespace.string().when("isScheduled", {
|
|
1274
|
-
is: true,
|
|
1275
|
-
then: yup__namespace.string().required().nullable(),
|
|
1276
|
-
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()
|
|
1277
1366
|
})
|
|
1278
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();
|
|
1279
1374
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
1375
|
+
const validatefindByDocumentAttachedParams = utils.validateYupSchema(
|
|
1376
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1377
|
+
);
|
|
1280
1378
|
const releaseController = {
|
|
1281
|
-
|
|
1282
|
-
|
|
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({
|
|
1283
1386
|
ability: ctx.state.userAbility,
|
|
1284
1387
|
model: RELEASE_MODEL_UID
|
|
1285
1388
|
});
|
|
1286
1389
|
await permissionsManager.validateQuery(ctx.query);
|
|
1287
1390
|
const releaseService = getService("release", { strapi });
|
|
1288
|
-
const
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
const
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
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,
|
|
1407
|
+
actions: {
|
|
1408
|
+
contentType,
|
|
1409
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1410
|
+
locale: locale ?? null
|
|
1411
|
+
}
|
|
1412
|
+
},
|
|
1413
|
+
populate: {
|
|
1303
1414
|
actions: {
|
|
1304
|
-
|
|
1305
|
-
|
|
1415
|
+
fields: ["type"],
|
|
1416
|
+
filters: {
|
|
1417
|
+
contentType,
|
|
1418
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1419
|
+
locale: locale ?? null
|
|
1306
1420
|
}
|
|
1307
1421
|
}
|
|
1308
|
-
}
|
|
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
|
+
}
|
|
1309
1435
|
});
|
|
1310
|
-
const
|
|
1436
|
+
const releases = await releaseService.findMany({
|
|
1311
1437
|
where: {
|
|
1438
|
+
$or: [
|
|
1439
|
+
{
|
|
1440
|
+
id: {
|
|
1441
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1442
|
+
}
|
|
1443
|
+
},
|
|
1444
|
+
{
|
|
1445
|
+
actions: null
|
|
1446
|
+
}
|
|
1447
|
+
],
|
|
1312
1448
|
releasedAt: null
|
|
1313
1449
|
}
|
|
1314
1450
|
});
|
|
1315
|
-
ctx.body = { data
|
|
1451
|
+
ctx.body = { data: releases };
|
|
1316
1452
|
}
|
|
1317
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
|
+
},
|
|
1318
1481
|
async findOne(ctx) {
|
|
1319
1482
|
const id = ctx.params.id;
|
|
1320
1483
|
const releaseService = getService("release", { strapi });
|
|
1484
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1321
1485
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
1322
1486
|
if (!release2) {
|
|
1323
1487
|
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1324
1488
|
}
|
|
1325
|
-
const count = await
|
|
1489
|
+
const count = await releaseActionService.countActions({
|
|
1326
1490
|
filters: {
|
|
1327
1491
|
release: id
|
|
1328
1492
|
}
|
|
1329
1493
|
});
|
|
1330
1494
|
const sanitizedRelease = {
|
|
1331
1495
|
...release2,
|
|
1332
|
-
createdBy: release2.createdBy ? strapi.admin
|
|
1496
|
+
createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
|
|
1333
1497
|
};
|
|
1334
1498
|
const data = {
|
|
1335
1499
|
...sanitizedRelease,
|
|
@@ -1342,22 +1506,39 @@ const releaseController = {
|
|
|
1342
1506
|
ctx.body = { data };
|
|
1343
1507
|
},
|
|
1344
1508
|
async mapEntriesToReleases(ctx) {
|
|
1345
|
-
const { contentTypeUid,
|
|
1346
|
-
if (!contentTypeUid || !
|
|
1509
|
+
const { contentTypeUid, documentIds, locale } = ctx.query;
|
|
1510
|
+
if (!contentTypeUid || !documentIds) {
|
|
1347
1511
|
throw new utils.errors.ValidationError("Missing required query parameters");
|
|
1348
1512
|
}
|
|
1349
1513
|
const releaseService = getService("release", { strapi });
|
|
1350
|
-
const releasesWithActions = await releaseService.
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
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
|
+
});
|
|
1354
1529
|
const mappedEntriesInReleases = releasesWithActions.reduce(
|
|
1355
1530
|
(acc, release2) => {
|
|
1356
1531
|
release2.actions.forEach((action) => {
|
|
1357
|
-
if (
|
|
1358
|
-
|
|
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 }];
|
|
1359
1540
|
} else {
|
|
1360
|
-
acc[action.
|
|
1541
|
+
acc[action.entryDocumentId].push({ id: release2.id, name: release2.name });
|
|
1361
1542
|
}
|
|
1362
1543
|
});
|
|
1363
1544
|
return acc;
|
|
@@ -1374,13 +1555,13 @@ const releaseController = {
|
|
|
1374
1555
|
await validateRelease(releaseArgs);
|
|
1375
1556
|
const releaseService = getService("release", { strapi });
|
|
1376
1557
|
const release2 = await releaseService.create(releaseArgs, { user });
|
|
1377
|
-
const permissionsManager = strapi.admin
|
|
1558
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1378
1559
|
ability: ctx.state.userAbility,
|
|
1379
1560
|
model: RELEASE_MODEL_UID
|
|
1380
1561
|
});
|
|
1381
|
-
ctx.
|
|
1562
|
+
ctx.created({
|
|
1382
1563
|
data: await permissionsManager.sanitizeOutput(release2)
|
|
1383
|
-
};
|
|
1564
|
+
});
|
|
1384
1565
|
},
|
|
1385
1566
|
async update(ctx) {
|
|
1386
1567
|
const user = ctx.state.user;
|
|
@@ -1389,7 +1570,7 @@ const releaseController = {
|
|
|
1389
1570
|
await validateRelease(releaseArgs);
|
|
1390
1571
|
const releaseService = getService("release", { strapi });
|
|
1391
1572
|
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
1392
|
-
const permissionsManager = strapi.admin
|
|
1573
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1393
1574
|
ability: ctx.state.userAbility,
|
|
1394
1575
|
model: RELEASE_MODEL_UID
|
|
1395
1576
|
});
|
|
@@ -1406,18 +1587,18 @@ const releaseController = {
|
|
|
1406
1587
|
};
|
|
1407
1588
|
},
|
|
1408
1589
|
async publish(ctx) {
|
|
1409
|
-
const user = ctx.state.user;
|
|
1410
1590
|
const id = ctx.params.id;
|
|
1411
1591
|
const releaseService = getService("release", { strapi });
|
|
1412
|
-
const
|
|
1592
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1593
|
+
const release2 = await releaseService.publish(id);
|
|
1413
1594
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1414
|
-
|
|
1595
|
+
releaseActionService.countActions({
|
|
1415
1596
|
filters: {
|
|
1416
1597
|
release: id,
|
|
1417
1598
|
type: "publish"
|
|
1418
1599
|
}
|
|
1419
1600
|
}),
|
|
1420
|
-
|
|
1601
|
+
releaseActionService.countActions({
|
|
1421
1602
|
filters: {
|
|
1422
1603
|
release: id,
|
|
1423
1604
|
type: "unpublish"
|
|
@@ -1435,27 +1616,30 @@ const releaseController = {
|
|
|
1435
1616
|
}
|
|
1436
1617
|
};
|
|
1437
1618
|
const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
}).required(),
|
|
1619
|
+
contentType: utils.yup.string().required(),
|
|
1620
|
+
entryDocumentId: utils.yup.strapiID(),
|
|
1621
|
+
locale: utils.yup.string(),
|
|
1442
1622
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
1443
1623
|
});
|
|
1444
1624
|
const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
|
|
1445
1625
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
1446
1626
|
});
|
|
1627
|
+
const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
|
|
1628
|
+
groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
|
|
1629
|
+
});
|
|
1447
1630
|
const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1448
1631
|
const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1632
|
+
const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1449
1633
|
const releaseActionController = {
|
|
1450
1634
|
async create(ctx) {
|
|
1451
1635
|
const releaseId = ctx.params.releaseId;
|
|
1452
1636
|
const releaseActionArgs = ctx.request.body;
|
|
1453
1637
|
await validateReleaseAction(releaseActionArgs);
|
|
1454
|
-
const
|
|
1455
|
-
const releaseAction2 = await
|
|
1456
|
-
ctx.
|
|
1638
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1639
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1640
|
+
ctx.created({
|
|
1457
1641
|
data: releaseAction2
|
|
1458
|
-
};
|
|
1642
|
+
});
|
|
1459
1643
|
},
|
|
1460
1644
|
async createMany(ctx) {
|
|
1461
1645
|
const releaseId = ctx.params.releaseId;
|
|
@@ -1463,12 +1647,15 @@ const releaseActionController = {
|
|
|
1463
1647
|
await Promise.all(
|
|
1464
1648
|
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1465
1649
|
);
|
|
1650
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1466
1651
|
const releaseService = getService("release", { strapi });
|
|
1467
1652
|
const releaseActions = await strapi.db.transaction(async () => {
|
|
1468
1653
|
const releaseActions2 = await Promise.all(
|
|
1469
1654
|
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1470
1655
|
try {
|
|
1471
|
-
const action = await
|
|
1656
|
+
const action = await releaseActionService.create(releaseId, releaseActionArgs, {
|
|
1657
|
+
disableUpdateReleaseStatus: true
|
|
1658
|
+
});
|
|
1472
1659
|
return action;
|
|
1473
1660
|
} catch (error) {
|
|
1474
1661
|
if (error instanceof AlreadyOnReleaseError) {
|
|
@@ -1481,43 +1668,54 @@ const releaseActionController = {
|
|
|
1481
1668
|
return releaseActions2;
|
|
1482
1669
|
});
|
|
1483
1670
|
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1484
|
-
|
|
1671
|
+
if (newReleaseActions.length > 0) {
|
|
1672
|
+
releaseService.updateReleaseStatus(releaseId);
|
|
1673
|
+
}
|
|
1674
|
+
ctx.created({
|
|
1485
1675
|
data: newReleaseActions,
|
|
1486
1676
|
meta: {
|
|
1487
1677
|
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1488
1678
|
totalEntries: releaseActions.length
|
|
1489
1679
|
}
|
|
1490
|
-
};
|
|
1680
|
+
});
|
|
1491
1681
|
},
|
|
1492
1682
|
async findMany(ctx) {
|
|
1493
1683
|
const releaseId = ctx.params.releaseId;
|
|
1494
|
-
const permissionsManager = strapi.admin
|
|
1684
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1495
1685
|
ability: ctx.state.userAbility,
|
|
1496
1686
|
model: RELEASE_ACTION_MODEL_UID
|
|
1497
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;
|
|
1498
1696
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1499
|
-
const
|
|
1500
|
-
const { results, pagination } = await
|
|
1501
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1697
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1698
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1502
1699
|
...query
|
|
1503
1700
|
});
|
|
1504
1701
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
1505
1702
|
if (acc[action.contentType]) {
|
|
1506
1703
|
return acc;
|
|
1507
1704
|
}
|
|
1508
|
-
const contentTypePermissionsManager = strapi.admin
|
|
1705
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1509
1706
|
ability: ctx.state.userAbility,
|
|
1510
1707
|
model: action.contentType
|
|
1511
1708
|
});
|
|
1512
1709
|
acc[action.contentType] = contentTypePermissionsManager.sanitizeOutput;
|
|
1513
1710
|
return acc;
|
|
1514
1711
|
}, {});
|
|
1515
|
-
const sanitizedResults = await utils.
|
|
1712
|
+
const sanitizedResults = await utils.async.map(results, async (action) => ({
|
|
1516
1713
|
...action,
|
|
1517
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1714
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1518
1715
|
}));
|
|
1519
|
-
const groupedData = await
|
|
1520
|
-
const contentTypes2 =
|
|
1716
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1717
|
+
const contentTypes2 = await releaseActionService.getContentTypeModelsFromActions(results);
|
|
1718
|
+
const releaseService = getService("release", { strapi });
|
|
1521
1719
|
const components = await releaseService.getAllComponents();
|
|
1522
1720
|
ctx.body = {
|
|
1523
1721
|
data: groupedData,
|
|
@@ -1533,8 +1731,8 @@ const releaseActionController = {
|
|
|
1533
1731
|
const releaseId = ctx.params.releaseId;
|
|
1534
1732
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1535
1733
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1536
|
-
const
|
|
1537
|
-
const updatedAction = await
|
|
1734
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1735
|
+
const updatedAction = await releaseActionService.update(
|
|
1538
1736
|
actionId,
|
|
1539
1737
|
releaseId,
|
|
1540
1738
|
releaseActionUpdateArgs
|
|
@@ -1546,14 +1744,36 @@ const releaseActionController = {
|
|
|
1546
1744
|
async delete(ctx) {
|
|
1547
1745
|
const actionId = ctx.params.actionId;
|
|
1548
1746
|
const releaseId = ctx.params.releaseId;
|
|
1549
|
-
const
|
|
1550
|
-
const deletedReleaseAction = await
|
|
1747
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1748
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1551
1749
|
ctx.body = {
|
|
1552
1750
|
data: deletedReleaseAction
|
|
1553
1751
|
};
|
|
1554
1752
|
}
|
|
1555
1753
|
};
|
|
1556
|
-
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
|
+
};
|
|
1557
1777
|
const release = {
|
|
1558
1778
|
type: "admin",
|
|
1559
1779
|
routes: [
|
|
@@ -1573,6 +1793,22 @@ const release = {
|
|
|
1573
1793
|
]
|
|
1574
1794
|
}
|
|
1575
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
|
+
},
|
|
1576
1812
|
{
|
|
1577
1813
|
method: "POST",
|
|
1578
1814
|
path: "/",
|
|
@@ -1592,7 +1828,7 @@ const release = {
|
|
|
1592
1828
|
{
|
|
1593
1829
|
method: "GET",
|
|
1594
1830
|
path: "/",
|
|
1595
|
-
handler: "release.
|
|
1831
|
+
handler: "release.findPage",
|
|
1596
1832
|
config: {
|
|
1597
1833
|
policies: [
|
|
1598
1834
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1756,13 +1992,50 @@ const releaseAction = {
|
|
|
1756
1992
|
}
|
|
1757
1993
|
]
|
|
1758
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
|
+
};
|
|
1759
2032
|
const routes = {
|
|
2033
|
+
settings,
|
|
1760
2034
|
release,
|
|
1761
2035
|
"release-action": releaseAction
|
|
1762
2036
|
};
|
|
1763
|
-
const { features } = require("@strapi/strapi/dist/utils/ee");
|
|
1764
2037
|
const getPlugin = () => {
|
|
1765
|
-
if (features.isEnabled("cms-content-releases")) {
|
|
2038
|
+
if (strapi.ee.features.isEnabled("cms-content-releases")) {
|
|
1766
2039
|
return {
|
|
1767
2040
|
register,
|
|
1768
2041
|
bootstrap,
|