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