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