@strapi/content-releases 0.0.0-experimental.fc1ac2acd58c8a5a858679956b6d102ac5ee4011 → 0.0.0-experimental.fccfa141baa6df7776ec7cb83f7fe923626ba6f5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +17 -1
- package/dist/_chunks/{App-C768ulk4.js → App-BKB1esYS.js} +497 -483
- package/dist/_chunks/App-BKB1esYS.js.map +1 -0
- package/dist/_chunks/{App-0Er6xxcq.mjs → App-Cne--1Z8.mjs} +480 -464
- package/dist/_chunks/App-Cne--1Z8.mjs.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-YhAPgpG9.js → PurchaseContentReleases-Be3acS2L.js} +8 -7
- package/dist/_chunks/PurchaseContentReleases-Be3acS2L.js.map +1 -0
- package/dist/_chunks/{PurchaseContentReleases-Clm0iACO.mjs → PurchaseContentReleases-_MxP6-Dt.mjs} +9 -8
- package/dist/_chunks/PurchaseContentReleases-_MxP6-Dt.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-C1WwGWIH.mjs +178 -0
- package/dist/_chunks/ReleasesSettingsPage-C1WwGWIH.mjs.map +1 -0
- package/dist/_chunks/ReleasesSettingsPage-kuXIwpWp.js +178 -0
- package/dist/_chunks/ReleasesSettingsPage-kuXIwpWp.js.map +1 -0
- package/dist/_chunks/{en-gcJJ5htG.js → en-CmYoEnA7.js} +19 -4
- package/dist/_chunks/en-CmYoEnA7.js.map +1 -0
- package/dist/_chunks/{en-WuuhP6Bn.mjs → en-D0yVZFqf.mjs} +19 -4
- package/dist/_chunks/en-D0yVZFqf.mjs.map +1 -0
- package/dist/_chunks/index-5Odi61vw.js +1381 -0
- package/dist/_chunks/index-5Odi61vw.js.map +1 -0
- package/dist/_chunks/index-Cy7qwpaU.mjs +1362 -0
- package/dist/_chunks/index-Cy7qwpaU.mjs.map +1 -0
- package/dist/_chunks/schemas-BE1LxE9J.js +62 -0
- package/dist/_chunks/schemas-BE1LxE9J.js.map +1 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs +44 -0
- package/dist/_chunks/schemas-DdA2ic2U.mjs.map +1 -0
- package/dist/admin/index.js +1 -15
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +2 -16
- package/dist/admin/index.mjs.map +1 -1
- package/dist/admin/src/components/ReleaseAction.d.ts +3 -0
- package/dist/admin/src/components/ReleaseActionMenu.d.ts +4 -4
- package/dist/admin/src/components/ReleaseActionModal.d.ts +24 -0
- package/dist/admin/src/components/ReleaseListCell.d.ts +28 -0
- package/dist/admin/src/components/ReleaseModal.d.ts +3 -2
- package/dist/admin/src/components/ReleasesPanel.d.ts +3 -0
- package/dist/admin/src/constants.d.ts +18 -0
- package/dist/admin/src/modules/hooks.d.ts +7 -0
- package/dist/admin/src/pages/ReleasesSettingsPage.d.ts +1 -0
- package/dist/admin/src/services/release.d.ts +66 -323
- 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 +9 -0
- package/dist/admin/src/validation/schemas.d.ts +6 -0
- package/dist/server/index.js +1053 -646
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1054 -647
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts +2 -2
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/constants.d.ts +11 -2
- package/dist/server/src/constants.d.ts.map +1 -1
- package/dist/server/src/content-types/index.d.ts +3 -5
- package/dist/server/src/content-types/index.d.ts.map +1 -1
- package/dist/server/src/content-types/release-action/index.d.ts +3 -5
- package/dist/server/src/content-types/release-action/index.d.ts.map +1 -1
- package/dist/server/src/content-types/release-action/schema.d.ts +3 -5
- package/dist/server/src/content-types/release-action/schema.d.ts.map +1 -1
- package/dist/server/src/controllers/index.d.ts +8 -1
- package/dist/server/src/controllers/index.d.ts.map +1 -1
- package/dist/server/src/controllers/release-action.d.ts +1 -0
- package/dist/server/src/controllers/release-action.d.ts.map +1 -1
- package/dist/server/src/controllers/release.d.ts +8 -1
- package/dist/server/src/controllers/release.d.ts.map +1 -1
- 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 +13 -2
- package/dist/server/src/controllers/validation/release-action.d.ts.map +1 -1
- package/dist/server/src/controllers/validation/release.d.ts +2 -0
- package/dist/server/src/controllers/validation/release.d.ts.map +1 -1
- 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 +2 -2
- package/dist/server/src/destroy.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +1518 -3241
- package/dist/server/src/index.d.ts.map +1 -1
- 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 +3 -0
- package/dist/server/src/migrations/index.d.ts.map +1 -1
- package/dist/server/src/register.d.ts +2 -2
- package/dist/server/src/register.d.ts.map +1 -1
- package/dist/server/src/routes/index.d.ts +16 -0
- package/dist/server/src/routes/index.d.ts.map +1 -1
- package/dist/server/src/routes/release-action.d.ts.map +1 -1
- package/dist/server/src/routes/release.d.ts.map +1 -1
- 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 +1482 -3226
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/release-action.d.ts +38 -0
- package/dist/server/src/services/release-action.d.ts.map +1 -0
- package/dist/server/src/services/release.d.ts +19 -1800
- package/dist/server/src/services/release.d.ts.map +1 -1
- package/dist/server/src/services/scheduling.d.ts +6 -6
- package/dist/server/src/services/scheduling.d.ts.map +1 -1
- 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 +7 -3
- package/dist/server/src/services/validation.d.ts.map +1 -1
- package/dist/server/src/utils/index.d.ts +33 -16
- package/dist/server/src/utils/index.d.ts.map +1 -1
- package/dist/shared/contracts/release-actions.d.ts +37 -12
- package/dist/shared/contracts/release-actions.d.ts.map +1 -1
- package/dist/shared/contracts/releases.d.ts +24 -6
- package/dist/shared/contracts/releases.d.ts.map +1 -1
- 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 +2 -2
- package/dist/shared/types.d.ts.map +1 -1
- package/package.json +29 -27
- package/dist/_chunks/App-0Er6xxcq.mjs.map +0 -1
- package/dist/_chunks/App-C768ulk4.js.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-Clm0iACO.mjs.map +0 -1
- package/dist/_chunks/PurchaseContentReleases-YhAPgpG9.js.map +0 -1
- package/dist/_chunks/en-WuuhP6Bn.mjs.map +0 -1
- package/dist/_chunks/en-gcJJ5htG.js.map +0 -1
- package/dist/_chunks/index-BLSMpbpZ.js +0 -1028
- package/dist/_chunks/index-BLSMpbpZ.js.map +0 -1
- package/dist/_chunks/index-fJx1up7m.mjs +0 -1007
- package/dist/_chunks/index-fJx1up7m.mjs.map +0 -1
- package/dist/admin/src/components/CMReleasesContainer.d.ts +0 -1
- package/dist/admin/src/services/axios.d.ts +0 -29
- package/dist/shared/validation-schemas.d.ts +0 -2
- package/dist/shared/validation-schemas.d.ts.map +0 -1
- package/strapi-server.js +0 -3
package/dist/server/index.js
CHANGED
|
@@ -71,21 +71,38 @@ const ACTIONS = [
|
|
|
71
71
|
displayName: "Add an entry to a release",
|
|
72
72
|
uid: "create-action",
|
|
73
73
|
pluginName: "content-releases"
|
|
74
|
+
},
|
|
75
|
+
// Settings
|
|
76
|
+
{
|
|
77
|
+
uid: "settings.read",
|
|
78
|
+
section: "settings",
|
|
79
|
+
displayName: "Read",
|
|
80
|
+
category: "content releases",
|
|
81
|
+
subCategory: "options",
|
|
82
|
+
pluginName: "content-releases"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
uid: "settings.update",
|
|
86
|
+
section: "settings",
|
|
87
|
+
displayName: "Edit",
|
|
88
|
+
category: "content releases",
|
|
89
|
+
subCategory: "options",
|
|
90
|
+
pluginName: "content-releases"
|
|
74
91
|
}
|
|
75
92
|
];
|
|
76
93
|
const ALLOWED_WEBHOOK_EVENTS = {
|
|
77
94
|
RELEASES_PUBLISH: "releases.publish"
|
|
78
95
|
};
|
|
79
|
-
const getService = (name, { strapi: strapi2 }
|
|
96
|
+
const getService = (name, { strapi: strapi2 }) => {
|
|
80
97
|
return strapi2.plugin("content-releases").service(name);
|
|
81
98
|
};
|
|
82
|
-
const
|
|
99
|
+
const getDraftEntryValidStatus = async ({ contentType, documentId, locale }, { strapi: strapi2 }) => {
|
|
83
100
|
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
84
|
-
const populate = await populateBuilderService(
|
|
85
|
-
const entry = await
|
|
86
|
-
return entry;
|
|
101
|
+
const populate = await populateBuilderService(contentType).populateDeep(Infinity).build();
|
|
102
|
+
const entry = await getEntry({ contentType, documentId, locale, populate }, { strapi: strapi2 });
|
|
103
|
+
return isEntryValid(contentType, entry, { strapi: strapi2 });
|
|
87
104
|
};
|
|
88
|
-
const
|
|
105
|
+
const isEntryValid = async (contentTypeUid, entry, { strapi: strapi2 }) => {
|
|
89
106
|
try {
|
|
90
107
|
await strapi2.entityValidator.validateEntityCreation(
|
|
91
108
|
strapi2.getModel(contentTypeUid),
|
|
@@ -99,6 +116,56 @@ const getEntryValidStatus = async (contentTypeUid, entry, { strapi: strapi2 } =
|
|
|
99
116
|
return false;
|
|
100
117
|
}
|
|
101
118
|
};
|
|
119
|
+
const getEntry = async ({
|
|
120
|
+
contentType,
|
|
121
|
+
documentId,
|
|
122
|
+
locale,
|
|
123
|
+
populate,
|
|
124
|
+
status = "draft"
|
|
125
|
+
}, { strapi: strapi2 }) => {
|
|
126
|
+
if (documentId) {
|
|
127
|
+
return strapi2.documents(contentType).findOne({ documentId, locale, populate, status });
|
|
128
|
+
}
|
|
129
|
+
return strapi2.documents(contentType).findFirst({ locale, populate, status });
|
|
130
|
+
};
|
|
131
|
+
const getEntryStatus = async (contentType, entry) => {
|
|
132
|
+
if (entry.publishedAt) {
|
|
133
|
+
return "published";
|
|
134
|
+
}
|
|
135
|
+
const publishedEntry = await strapi.documents(contentType).findOne({
|
|
136
|
+
documentId: entry.documentId,
|
|
137
|
+
locale: entry.locale,
|
|
138
|
+
status: "published",
|
|
139
|
+
fields: ["updatedAt"]
|
|
140
|
+
});
|
|
141
|
+
if (!publishedEntry) {
|
|
142
|
+
return "draft";
|
|
143
|
+
}
|
|
144
|
+
const entryUpdatedAt = new Date(entry.updatedAt).getTime();
|
|
145
|
+
const publishedEntryUpdatedAt = new Date(publishedEntry.updatedAt).getTime();
|
|
146
|
+
if (entryUpdatedAt > publishedEntryUpdatedAt) {
|
|
147
|
+
return "modified";
|
|
148
|
+
}
|
|
149
|
+
return "published";
|
|
150
|
+
};
|
|
151
|
+
async function deleteActionsOnDisableDraftAndPublish({
|
|
152
|
+
oldContentTypes,
|
|
153
|
+
contentTypes: contentTypes2
|
|
154
|
+
}) {
|
|
155
|
+
if (!oldContentTypes) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
for (const uid in contentTypes2) {
|
|
159
|
+
if (!oldContentTypes[uid]) {
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
const oldContentType = oldContentTypes[uid];
|
|
163
|
+
const contentType = contentTypes2[uid];
|
|
164
|
+
if (utils.contentTypes.hasDraftAndPublish(oldContentType) && !utils.contentTypes.hasDraftAndPublish(contentType)) {
|
|
165
|
+
await strapi.db?.queryBuilder(RELEASE_ACTION_MODEL_UID).delete().where({ contentType: uid }).execute();
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
102
169
|
async function deleteActionsOnDeleteContentType({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
103
170
|
const deletedContentTypes = lodash.difference(lodash.keys(oldContentTypes), lodash.keys(contentTypes2)) ?? [];
|
|
104
171
|
if (deletedContentTypes.length) {
|
|
@@ -126,20 +193,22 @@ async function migrateIsValidAndStatusReleases() {
|
|
|
126
193
|
const notValidatedActions = actions.filter((action) => action.isEntryValid === null);
|
|
127
194
|
for (const action of notValidatedActions) {
|
|
128
195
|
if (action.entry) {
|
|
129
|
-
const
|
|
130
|
-
|
|
196
|
+
const isEntryValid2 = getDraftEntryValidStatus(
|
|
197
|
+
{
|
|
198
|
+
contentType: action.contentType,
|
|
199
|
+
documentId: action.entryDocumentId,
|
|
200
|
+
locale: action.locale
|
|
201
|
+
},
|
|
202
|
+
{ strapi }
|
|
203
|
+
);
|
|
204
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
205
|
+
where: {
|
|
206
|
+
id: action.id
|
|
207
|
+
},
|
|
208
|
+
data: {
|
|
209
|
+
isEntryValid: isEntryValid2
|
|
210
|
+
}
|
|
131
211
|
});
|
|
132
|
-
if (populatedEntry) {
|
|
133
|
-
const isEntryValid = getEntryValidStatus(action.contentType, populatedEntry, { strapi });
|
|
134
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
135
|
-
where: {
|
|
136
|
-
id: action.id
|
|
137
|
-
},
|
|
138
|
-
data: {
|
|
139
|
-
isEntryValid
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
212
|
}
|
|
144
213
|
}
|
|
145
214
|
return getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
@@ -165,7 +234,9 @@ async function migrateIsValidAndStatusReleases() {
|
|
|
165
234
|
}
|
|
166
235
|
async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
167
236
|
if (oldContentTypes !== void 0 && contentTypes2 !== void 0) {
|
|
168
|
-
const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes)
|
|
237
|
+
const contentTypesWithDraftAndPublish = Object.keys(oldContentTypes).filter(
|
|
238
|
+
(uid) => oldContentTypes[uid]?.options?.draftAndPublish
|
|
239
|
+
);
|
|
169
240
|
const releasesAffected = /* @__PURE__ */ new Set();
|
|
170
241
|
utils.async.map(contentTypesWithDraftAndPublish, async (contentTypeUID) => {
|
|
171
242
|
const oldContentType = oldContentTypes[contentTypeUID];
|
|
@@ -181,24 +252,24 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
|
|
|
181
252
|
}
|
|
182
253
|
});
|
|
183
254
|
await utils.async.map(actions, async (action) => {
|
|
184
|
-
if (action.entry) {
|
|
185
|
-
const
|
|
186
|
-
|
|
255
|
+
if (action.entry && action.release && action.type === "publish") {
|
|
256
|
+
const isEntryValid2 = await getDraftEntryValidStatus(
|
|
257
|
+
{
|
|
258
|
+
contentType: contentTypeUID,
|
|
259
|
+
documentId: action.entryDocumentId,
|
|
260
|
+
locale: action.locale
|
|
261
|
+
},
|
|
262
|
+
{ strapi }
|
|
263
|
+
);
|
|
264
|
+
releasesAffected.add(action.release.id);
|
|
265
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
266
|
+
where: {
|
|
267
|
+
id: action.id
|
|
268
|
+
},
|
|
269
|
+
data: {
|
|
270
|
+
isEntryValid: isEntryValid2
|
|
271
|
+
}
|
|
187
272
|
});
|
|
188
|
-
if (populatedEntry) {
|
|
189
|
-
const isEntryValid = await getEntryValidStatus(contentTypeUID, populatedEntry, {
|
|
190
|
-
strapi
|
|
191
|
-
});
|
|
192
|
-
releasesAffected.add(action.release.id);
|
|
193
|
-
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
194
|
-
where: {
|
|
195
|
-
id: action.id
|
|
196
|
-
},
|
|
197
|
-
data: {
|
|
198
|
-
isEntryValid
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
273
|
}
|
|
203
274
|
});
|
|
204
275
|
}
|
|
@@ -209,153 +280,238 @@ async function revalidateChangedContentTypes({ oldContentTypes, contentTypes: co
|
|
|
209
280
|
});
|
|
210
281
|
}
|
|
211
282
|
}
|
|
283
|
+
async function disableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
284
|
+
if (!oldContentTypes) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
288
|
+
if (!i18nPlugin) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
for (const uid in contentTypes2) {
|
|
292
|
+
if (!oldContentTypes[uid]) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
const oldContentType = oldContentTypes[uid];
|
|
296
|
+
const contentType = contentTypes2[uid];
|
|
297
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
298
|
+
if (isLocalizedContentType(oldContentType) && !isLocalizedContentType(contentType)) {
|
|
299
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
300
|
+
locale: null
|
|
301
|
+
}).where({ contentType: uid }).execute();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
async function enableContentTypeLocalized({ oldContentTypes, contentTypes: contentTypes2 }) {
|
|
306
|
+
if (!oldContentTypes) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const i18nPlugin = strapi.plugin("i18n");
|
|
310
|
+
if (!i18nPlugin) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
for (const uid in contentTypes2) {
|
|
314
|
+
if (!oldContentTypes[uid]) {
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
const oldContentType = oldContentTypes[uid];
|
|
318
|
+
const contentType = contentTypes2[uid];
|
|
319
|
+
const { isLocalizedContentType } = i18nPlugin.service("content-types");
|
|
320
|
+
const { getDefaultLocale } = i18nPlugin.service("locales");
|
|
321
|
+
if (!isLocalizedContentType(oldContentType) && isLocalizedContentType(contentType)) {
|
|
322
|
+
const defaultLocale = await getDefaultLocale();
|
|
323
|
+
await strapi.db.queryBuilder(RELEASE_ACTION_MODEL_UID).update({
|
|
324
|
+
locale: defaultLocale
|
|
325
|
+
}).where({ contentType: uid }).execute();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
const addEntryDocumentToReleaseActions = {
|
|
330
|
+
name: "content-releases::5.0.0-add-entry-document-id-to-release-actions",
|
|
331
|
+
async up(trx, db) {
|
|
332
|
+
const hasPolymorphicColumn = await trx.schema.hasColumn("strapi_release_actions", "target_id");
|
|
333
|
+
if (hasPolymorphicColumn) {
|
|
334
|
+
const hasEntryDocumentIdColumn = await trx.schema.hasColumn(
|
|
335
|
+
"strapi_release_actions",
|
|
336
|
+
"entry_document_id"
|
|
337
|
+
);
|
|
338
|
+
if (!hasEntryDocumentIdColumn) {
|
|
339
|
+
await trx.schema.alterTable("strapi_release_actions", (table) => {
|
|
340
|
+
table.string("entry_document_id");
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
const releaseActions = await trx.select("*").from("strapi_release_actions");
|
|
344
|
+
utils.async.map(releaseActions, async (action) => {
|
|
345
|
+
const { target_type, target_id } = action;
|
|
346
|
+
const entry = await db.query(target_type).findOne({ where: { id: target_id } });
|
|
347
|
+
if (entry) {
|
|
348
|
+
await trx("strapi_release_actions").update({ entry_document_id: entry.documentId }).where("id", action.id);
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
},
|
|
353
|
+
async down() {
|
|
354
|
+
throw new Error("not implemented");
|
|
355
|
+
}
|
|
356
|
+
};
|
|
212
357
|
const register = async ({ strapi: strapi2 }) => {
|
|
213
358
|
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
214
|
-
await strapi2.admin
|
|
215
|
-
strapi2.
|
|
359
|
+
await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
|
|
360
|
+
strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
|
|
361
|
+
strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
|
|
362
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
363
|
+
}
|
|
364
|
+
if (strapi2.plugin("graphql")) {
|
|
365
|
+
const graphqlExtensionService = strapi2.plugin("graphql").service("extension");
|
|
366
|
+
graphqlExtensionService.shadowCRUD(RELEASE_MODEL_UID).disable();
|
|
367
|
+
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
|
|
371
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
372
|
+
where: {
|
|
373
|
+
actions: {
|
|
374
|
+
contentType,
|
|
375
|
+
entryDocumentId: entry.documentId,
|
|
376
|
+
locale: entry.locale
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
});
|
|
380
|
+
const entryStatus = await isEntryValid(contentType, entry, { strapi });
|
|
381
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
382
|
+
where: {
|
|
383
|
+
contentType,
|
|
384
|
+
entryDocumentId: entry.documentId,
|
|
385
|
+
locale: entry.locale
|
|
386
|
+
},
|
|
387
|
+
data: {
|
|
388
|
+
isEntryValid: entryStatus
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
for (const release2 of releases) {
|
|
392
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
const deleteActionsAndUpdateReleaseStatus = async (params) => {
|
|
396
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
397
|
+
where: {
|
|
398
|
+
actions: params
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
402
|
+
where: params
|
|
403
|
+
});
|
|
404
|
+
for (const release2 of releases) {
|
|
405
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
const deleteActionsOnDelete = async (ctx, next) => {
|
|
409
|
+
if (ctx.action !== "delete") {
|
|
410
|
+
return next();
|
|
411
|
+
}
|
|
412
|
+
if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
|
|
413
|
+
return next();
|
|
414
|
+
}
|
|
415
|
+
const contentType = ctx.contentType.uid;
|
|
416
|
+
const { documentId, locale } = ctx.params;
|
|
417
|
+
const result = await next();
|
|
418
|
+
if (!result) {
|
|
419
|
+
return result;
|
|
420
|
+
}
|
|
421
|
+
try {
|
|
422
|
+
deleteActionsAndUpdateReleaseStatus({
|
|
423
|
+
contentType,
|
|
424
|
+
entryDocumentId: documentId,
|
|
425
|
+
...locale !== "*" && { locale }
|
|
426
|
+
});
|
|
427
|
+
} catch (error) {
|
|
428
|
+
strapi.log.error("Error while deleting release actions after delete", {
|
|
429
|
+
error
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
return result;
|
|
433
|
+
};
|
|
434
|
+
const updateActionsOnUpdate = async (ctx, next) => {
|
|
435
|
+
if (ctx.action !== "update") {
|
|
436
|
+
return next();
|
|
437
|
+
}
|
|
438
|
+
if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
|
|
439
|
+
return next();
|
|
440
|
+
}
|
|
441
|
+
const contentType = ctx.contentType.uid;
|
|
442
|
+
const result = await next();
|
|
443
|
+
if (!result) {
|
|
444
|
+
return result;
|
|
445
|
+
}
|
|
446
|
+
try {
|
|
447
|
+
updateActionsStatusAndUpdateReleaseStatus(contentType, result);
|
|
448
|
+
} catch (error) {
|
|
449
|
+
strapi.log.error("Error while updating release actions after update", {
|
|
450
|
+
error
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
return result;
|
|
454
|
+
};
|
|
455
|
+
const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
|
|
456
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
457
|
+
where: {
|
|
458
|
+
actions: params
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
462
|
+
where: params
|
|
463
|
+
});
|
|
464
|
+
for (const release2 of releases) {
|
|
465
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
216
466
|
}
|
|
217
467
|
};
|
|
218
468
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
219
469
|
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
220
|
-
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes)
|
|
470
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
471
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
472
|
+
);
|
|
221
473
|
strapi2.db.lifecycles.subscribe({
|
|
222
474
|
models: contentTypesWithDraftAndPublish,
|
|
223
|
-
async afterDelete(event) {
|
|
224
|
-
try {
|
|
225
|
-
const { model, result } = event;
|
|
226
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
227
|
-
const { id } = result;
|
|
228
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
229
|
-
where: {
|
|
230
|
-
actions: {
|
|
231
|
-
target_type: model.uid,
|
|
232
|
-
target_id: id
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
237
|
-
where: {
|
|
238
|
-
target_type: model.uid,
|
|
239
|
-
target_id: id
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
for (const release2 of releases) {
|
|
243
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
} catch (error) {
|
|
247
|
-
strapi2.log.error("Error while deleting release actions after entry delete", { error });
|
|
248
|
-
}
|
|
249
|
-
},
|
|
250
475
|
/**
|
|
251
|
-
* deleteMany
|
|
252
|
-
* so we need to fetch them before deleting the entries to save the ids on our state
|
|
253
|
-
*/
|
|
254
|
-
async beforeDeleteMany(event) {
|
|
255
|
-
const { model, params } = event;
|
|
256
|
-
if (model.kind === "collectionType") {
|
|
257
|
-
const { where } = params;
|
|
258
|
-
const entriesToDelete = await strapi2.db.query(model.uid).findMany({ select: ["id"], where });
|
|
259
|
-
event.state.entriesToDelete = entriesToDelete;
|
|
260
|
-
}
|
|
261
|
-
},
|
|
262
|
-
/**
|
|
263
|
-
* We delete the release actions related to deleted entries
|
|
264
|
-
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
476
|
+
* deleteMany is still used outside documents service, for example when deleting a locale
|
|
265
477
|
*/
|
|
266
478
|
async afterDeleteMany(event) {
|
|
267
479
|
try {
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
target_id: {
|
|
276
|
-
$in: entriesToDelete.map(
|
|
277
|
-
(entry) => entry.id
|
|
278
|
-
)
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
284
|
-
where: {
|
|
285
|
-
target_type: model.uid,
|
|
286
|
-
target_id: {
|
|
287
|
-
$in: entriesToDelete.map((entry) => entry.id)
|
|
288
|
-
}
|
|
289
|
-
}
|
|
480
|
+
const model = strapi2.getModel(event.model.uid);
|
|
481
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
482
|
+
const { where } = event.params;
|
|
483
|
+
deleteReleasesActionsAndUpdateReleaseStatus({
|
|
484
|
+
contentType: model.uid,
|
|
485
|
+
locale: where.locale ?? null,
|
|
486
|
+
...where.documentId && { entryDocumentId: where.documentId }
|
|
290
487
|
});
|
|
291
|
-
for (const release2 of releases) {
|
|
292
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
293
|
-
}
|
|
294
488
|
}
|
|
295
489
|
} catch (error) {
|
|
296
490
|
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
297
491
|
error
|
|
298
492
|
});
|
|
299
493
|
}
|
|
300
|
-
},
|
|
301
|
-
async afterUpdate(event) {
|
|
302
|
-
try {
|
|
303
|
-
const { model, result } = event;
|
|
304
|
-
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
305
|
-
const isEntryValid = await getEntryValidStatus(
|
|
306
|
-
model.uid,
|
|
307
|
-
result,
|
|
308
|
-
{
|
|
309
|
-
strapi: strapi2
|
|
310
|
-
}
|
|
311
|
-
);
|
|
312
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
313
|
-
where: {
|
|
314
|
-
target_type: model.uid,
|
|
315
|
-
target_id: result.id
|
|
316
|
-
},
|
|
317
|
-
data: {
|
|
318
|
-
isEntryValid
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
322
|
-
where: {
|
|
323
|
-
actions: {
|
|
324
|
-
target_type: model.uid,
|
|
325
|
-
target_id: result.id
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
});
|
|
329
|
-
for (const release2 of releases) {
|
|
330
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
} catch (error) {
|
|
334
|
-
strapi2.log.error("Error while updating release actions after entry update", { error });
|
|
335
|
-
}
|
|
336
494
|
}
|
|
337
495
|
});
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
496
|
+
strapi2.documents.use(deleteActionsOnDelete);
|
|
497
|
+
strapi2.documents.use(updateActionsOnUpdate);
|
|
498
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
499
|
+
strapi2.log.error(
|
|
500
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
501
|
+
);
|
|
502
|
+
throw err;
|
|
503
|
+
});
|
|
504
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
505
|
+
strapi2.get("webhookStore").addAllowedEvent(key, value);
|
|
506
|
+
});
|
|
349
507
|
}
|
|
350
508
|
};
|
|
351
509
|
const destroy = async ({ strapi: strapi2 }) => {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
job.cancel();
|
|
358
|
-
}
|
|
510
|
+
const scheduledJobs = getService("scheduling", {
|
|
511
|
+
strapi: strapi2
|
|
512
|
+
}).getAll();
|
|
513
|
+
for (const [, job] of scheduledJobs) {
|
|
514
|
+
job.cancel();
|
|
359
515
|
}
|
|
360
516
|
};
|
|
361
517
|
const schema$1 = {
|
|
@@ -430,15 +586,13 @@ const schema = {
|
|
|
430
586
|
enum: ["publish", "unpublish"],
|
|
431
587
|
required: true
|
|
432
588
|
},
|
|
433
|
-
entry: {
|
|
434
|
-
type: "relation",
|
|
435
|
-
relation: "morphToOne",
|
|
436
|
-
configurable: false
|
|
437
|
-
},
|
|
438
589
|
contentType: {
|
|
439
590
|
type: "string",
|
|
440
591
|
required: true
|
|
441
592
|
},
|
|
593
|
+
entryDocumentId: {
|
|
594
|
+
type: "string"
|
|
595
|
+
},
|
|
442
596
|
locale: {
|
|
443
597
|
type: "string"
|
|
444
598
|
},
|
|
@@ -460,18 +614,6 @@ const contentTypes = {
|
|
|
460
614
|
release: release$1,
|
|
461
615
|
"release-action": releaseAction$1
|
|
462
616
|
};
|
|
463
|
-
const getGroupName = (queryValue) => {
|
|
464
|
-
switch (queryValue) {
|
|
465
|
-
case "contentType":
|
|
466
|
-
return "contentType.displayName";
|
|
467
|
-
case "action":
|
|
468
|
-
return "type";
|
|
469
|
-
case "locale":
|
|
470
|
-
return ___default.default.getOr("No locale", "locale.name");
|
|
471
|
-
default:
|
|
472
|
-
return "contentType.displayName";
|
|
473
|
-
}
|
|
474
|
-
};
|
|
475
617
|
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
476
618
|
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
477
619
|
strapi2.eventHub.emit(event, {
|
|
@@ -480,6 +622,33 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
480
622
|
release: release2
|
|
481
623
|
});
|
|
482
624
|
};
|
|
625
|
+
const getFormattedActions = async (releaseId) => {
|
|
626
|
+
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
627
|
+
where: {
|
|
628
|
+
release: {
|
|
629
|
+
id: releaseId
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
if (actions.length === 0) {
|
|
634
|
+
throw new utils.errors.ValidationError("No entries to publish");
|
|
635
|
+
}
|
|
636
|
+
const formattedActions = {};
|
|
637
|
+
for (const action of actions) {
|
|
638
|
+
const contentTypeUid = action.contentType;
|
|
639
|
+
if (!formattedActions[contentTypeUid]) {
|
|
640
|
+
formattedActions[contentTypeUid] = {
|
|
641
|
+
publish: [],
|
|
642
|
+
unpublish: []
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
formattedActions[contentTypeUid][action.type].push({
|
|
646
|
+
documentId: action.entryDocumentId,
|
|
647
|
+
locale: action.locale
|
|
648
|
+
});
|
|
649
|
+
}
|
|
650
|
+
return formattedActions;
|
|
651
|
+
};
|
|
483
652
|
return {
|
|
484
653
|
async create(releaseData, { user }) {
|
|
485
654
|
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
@@ -493,13 +662,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
493
662
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
494
663
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
495
664
|
]);
|
|
496
|
-
const release2 = await strapi2.
|
|
665
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
|
|
497
666
|
data: {
|
|
498
667
|
...releaseWithCreatorFields,
|
|
499
668
|
status: "empty"
|
|
500
669
|
}
|
|
501
670
|
});
|
|
502
|
-
if (
|
|
671
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
503
672
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
504
673
|
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
505
674
|
}
|
|
@@ -507,94 +676,28 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
507
676
|
return release2;
|
|
508
677
|
},
|
|
509
678
|
async findOne(id, query = {}) {
|
|
510
|
-
const
|
|
511
|
-
|
|
679
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query);
|
|
680
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
681
|
+
...dbQuery,
|
|
682
|
+
where: { id }
|
|
512
683
|
});
|
|
513
684
|
return release2;
|
|
514
685
|
},
|
|
515
686
|
findPage(query) {
|
|
516
|
-
|
|
517
|
-
|
|
687
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
688
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findPage({
|
|
689
|
+
...dbQuery,
|
|
518
690
|
populate: {
|
|
519
691
|
actions: {
|
|
520
|
-
// @ts-expect-error Ignore missing properties
|
|
521
692
|
count: true
|
|
522
693
|
}
|
|
523
694
|
}
|
|
524
695
|
});
|
|
525
696
|
},
|
|
526
|
-
|
|
527
|
-
const
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
target_type: contentTypeUid,
|
|
531
|
-
target_id: entryId
|
|
532
|
-
},
|
|
533
|
-
releasedAt: {
|
|
534
|
-
$null: true
|
|
535
|
-
}
|
|
536
|
-
},
|
|
537
|
-
populate: {
|
|
538
|
-
// Filter the action to get only the content type entry
|
|
539
|
-
actions: {
|
|
540
|
-
where: {
|
|
541
|
-
target_type: contentTypeUid,
|
|
542
|
-
target_id: entryId
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
});
|
|
547
|
-
return releases.map((release2) => {
|
|
548
|
-
if (release2.actions?.length) {
|
|
549
|
-
const [actionForEntry] = release2.actions;
|
|
550
|
-
delete release2.actions;
|
|
551
|
-
return {
|
|
552
|
-
...release2,
|
|
553
|
-
action: actionForEntry
|
|
554
|
-
};
|
|
555
|
-
}
|
|
556
|
-
return release2;
|
|
557
|
-
});
|
|
558
|
-
},
|
|
559
|
-
async findManyWithoutContentTypeEntryAttached(contentTypeUid, entryId) {
|
|
560
|
-
const releasesRelated = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
561
|
-
where: {
|
|
562
|
-
releasedAt: {
|
|
563
|
-
$null: true
|
|
564
|
-
},
|
|
565
|
-
actions: {
|
|
566
|
-
target_type: contentTypeUid,
|
|
567
|
-
target_id: entryId
|
|
568
|
-
}
|
|
569
|
-
}
|
|
570
|
-
});
|
|
571
|
-
const releases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
572
|
-
where: {
|
|
573
|
-
$or: [
|
|
574
|
-
{
|
|
575
|
-
id: {
|
|
576
|
-
$notIn: releasesRelated.map((release2) => release2.id)
|
|
577
|
-
}
|
|
578
|
-
},
|
|
579
|
-
{
|
|
580
|
-
actions: null
|
|
581
|
-
}
|
|
582
|
-
],
|
|
583
|
-
releasedAt: {
|
|
584
|
-
$null: true
|
|
585
|
-
}
|
|
586
|
-
}
|
|
587
|
-
});
|
|
588
|
-
return releases.map((release2) => {
|
|
589
|
-
if (release2.actions?.length) {
|
|
590
|
-
const [actionForEntry] = release2.actions;
|
|
591
|
-
delete release2.actions;
|
|
592
|
-
return {
|
|
593
|
-
...release2,
|
|
594
|
-
action: actionForEntry
|
|
595
|
-
};
|
|
596
|
-
}
|
|
597
|
-
return release2;
|
|
697
|
+
findMany(query) {
|
|
698
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
699
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
700
|
+
...dbQuery
|
|
598
701
|
});
|
|
599
702
|
},
|
|
600
703
|
async update(id, releaseData, { user }) {
|
|
@@ -609,90 +712,307 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
609
712
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
610
713
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
611
714
|
]);
|
|
612
|
-
const release2 = await strapi2.
|
|
715
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
|
|
613
716
|
if (!release2) {
|
|
614
717
|
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
615
718
|
}
|
|
616
719
|
if (release2.releasedAt) {
|
|
617
720
|
throw new utils.errors.ValidationError("Release already published");
|
|
618
721
|
}
|
|
619
|
-
const updatedRelease = await strapi2.
|
|
620
|
-
|
|
621
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">>
|
|
622
|
-
* is not compatible with the type we are passing here: UpdateRelease.Request['body']
|
|
623
|
-
*/
|
|
624
|
-
// @ts-expect-error see above
|
|
722
|
+
const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
723
|
+
where: { id },
|
|
625
724
|
data: releaseWithCreatorFields
|
|
626
725
|
});
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
schedulingService.cancel(id);
|
|
633
|
-
}
|
|
726
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
727
|
+
if (releaseData.scheduledAt) {
|
|
728
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
729
|
+
} else if (release2.scheduledAt) {
|
|
730
|
+
schedulingService.cancel(id);
|
|
634
731
|
}
|
|
635
732
|
this.updateReleaseStatus(id);
|
|
636
733
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
637
734
|
return updatedRelease;
|
|
638
735
|
},
|
|
639
|
-
async
|
|
640
|
-
const
|
|
736
|
+
async getAllComponents() {
|
|
737
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
738
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
739
|
+
const componentsMap = components.reduce(
|
|
740
|
+
(acc, component) => {
|
|
741
|
+
acc[component.uid] = component;
|
|
742
|
+
return acc;
|
|
743
|
+
},
|
|
744
|
+
{}
|
|
745
|
+
);
|
|
746
|
+
return componentsMap;
|
|
747
|
+
},
|
|
748
|
+
async delete(releaseId) {
|
|
749
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
750
|
+
where: { id: releaseId },
|
|
751
|
+
populate: {
|
|
752
|
+
actions: {
|
|
753
|
+
select: ["id"]
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
});
|
|
757
|
+
if (!release2) {
|
|
758
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
759
|
+
}
|
|
760
|
+
if (release2.releasedAt) {
|
|
761
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
762
|
+
}
|
|
763
|
+
await strapi2.db.transaction(async () => {
|
|
764
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
765
|
+
where: {
|
|
766
|
+
id: {
|
|
767
|
+
$in: release2.actions.map((action) => action.id)
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
772
|
+
where: {
|
|
773
|
+
id: releaseId
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
});
|
|
777
|
+
if (release2.scheduledAt) {
|
|
778
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
779
|
+
await schedulingService.cancel(release2.id);
|
|
780
|
+
}
|
|
781
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
782
|
+
return release2;
|
|
783
|
+
},
|
|
784
|
+
async publish(releaseId) {
|
|
785
|
+
const {
|
|
786
|
+
release: release2,
|
|
787
|
+
error
|
|
788
|
+
} = await strapi2.db.transaction(async ({ trx }) => {
|
|
789
|
+
const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
|
|
790
|
+
if (!lockedRelease) {
|
|
791
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
792
|
+
}
|
|
793
|
+
if (lockedRelease.releasedAt) {
|
|
794
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
795
|
+
}
|
|
796
|
+
if (lockedRelease.status === "failed") {
|
|
797
|
+
throw new utils.errors.ValidationError("Release failed to publish");
|
|
798
|
+
}
|
|
799
|
+
try {
|
|
800
|
+
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
801
|
+
const formattedActions = await getFormattedActions(releaseId);
|
|
802
|
+
await strapi2.db.transaction(
|
|
803
|
+
async () => Promise.all(
|
|
804
|
+
Object.keys(formattedActions).map(async (contentTypeUid) => {
|
|
805
|
+
const contentType = contentTypeUid;
|
|
806
|
+
const { publish, unpublish } = formattedActions[contentType];
|
|
807
|
+
return Promise.all([
|
|
808
|
+
...publish.map((params) => strapi2.documents(contentType).publish(params)),
|
|
809
|
+
...unpublish.map((params) => strapi2.documents(contentType).unpublish(params))
|
|
810
|
+
]);
|
|
811
|
+
})
|
|
812
|
+
)
|
|
813
|
+
);
|
|
814
|
+
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
815
|
+
where: {
|
|
816
|
+
id: releaseId
|
|
817
|
+
},
|
|
818
|
+
data: {
|
|
819
|
+
status: "done",
|
|
820
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
824
|
+
isPublished: true,
|
|
825
|
+
release: release22
|
|
826
|
+
});
|
|
827
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
828
|
+
return { release: release22, error: null };
|
|
829
|
+
} catch (error2) {
|
|
830
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
831
|
+
isPublished: false,
|
|
832
|
+
error: error2
|
|
833
|
+
});
|
|
834
|
+
await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
|
|
835
|
+
status: "failed"
|
|
836
|
+
}).transacting(trx).execute();
|
|
837
|
+
return {
|
|
838
|
+
release: null,
|
|
839
|
+
error: error2
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
});
|
|
843
|
+
if (error instanceof Error) {
|
|
844
|
+
throw error;
|
|
845
|
+
}
|
|
846
|
+
return release2;
|
|
847
|
+
},
|
|
848
|
+
async updateReleaseStatus(releaseId) {
|
|
849
|
+
const releaseActionService = getService("release-action", { strapi: strapi2 });
|
|
850
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
851
|
+
releaseActionService.countActions({
|
|
852
|
+
filters: {
|
|
853
|
+
release: releaseId
|
|
854
|
+
}
|
|
855
|
+
}),
|
|
856
|
+
releaseActionService.countActions({
|
|
857
|
+
filters: {
|
|
858
|
+
release: releaseId,
|
|
859
|
+
isEntryValid: false
|
|
860
|
+
}
|
|
861
|
+
})
|
|
862
|
+
]);
|
|
863
|
+
if (totalActions > 0) {
|
|
864
|
+
if (invalidActions > 0) {
|
|
865
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
866
|
+
where: {
|
|
867
|
+
id: releaseId
|
|
868
|
+
},
|
|
869
|
+
data: {
|
|
870
|
+
status: "blocked"
|
|
871
|
+
}
|
|
872
|
+
});
|
|
873
|
+
}
|
|
874
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
875
|
+
where: {
|
|
876
|
+
id: releaseId
|
|
877
|
+
},
|
|
878
|
+
data: {
|
|
879
|
+
status: "ready"
|
|
880
|
+
}
|
|
881
|
+
});
|
|
882
|
+
}
|
|
883
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
884
|
+
where: {
|
|
885
|
+
id: releaseId
|
|
886
|
+
},
|
|
887
|
+
data: {
|
|
888
|
+
status: "empty"
|
|
889
|
+
}
|
|
890
|
+
});
|
|
891
|
+
}
|
|
892
|
+
};
|
|
893
|
+
};
|
|
894
|
+
const getGroupName = (queryValue) => {
|
|
895
|
+
switch (queryValue) {
|
|
896
|
+
case "contentType":
|
|
897
|
+
return "contentType.displayName";
|
|
898
|
+
case "type":
|
|
899
|
+
return "type";
|
|
900
|
+
case "locale":
|
|
901
|
+
return ___default.default.getOr("No locale", "locale.name");
|
|
902
|
+
default:
|
|
903
|
+
return "contentType.displayName";
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
const createReleaseActionService = ({ strapi: strapi2 }) => {
|
|
907
|
+
const getLocalesDataForActions = async () => {
|
|
908
|
+
if (!strapi2.plugin("i18n")) {
|
|
909
|
+
return {};
|
|
910
|
+
}
|
|
911
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
912
|
+
return allLocales.reduce((acc, locale) => {
|
|
913
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
914
|
+
return acc;
|
|
915
|
+
}, {});
|
|
916
|
+
};
|
|
917
|
+
const getContentTypesDataForActions = async (contentTypesUids) => {
|
|
918
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
919
|
+
const contentTypesData = {};
|
|
920
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
921
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
922
|
+
uid: contentTypeUid
|
|
923
|
+
});
|
|
924
|
+
contentTypesData[contentTypeUid] = {
|
|
925
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
926
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
return contentTypesData;
|
|
930
|
+
};
|
|
931
|
+
return {
|
|
932
|
+
async create(releaseId, action, { disableUpdateReleaseStatus = false } = {}) {
|
|
933
|
+
const { validateEntryData, validateUniqueEntry } = getService("release-validation", {
|
|
641
934
|
strapi: strapi2
|
|
642
935
|
});
|
|
643
936
|
await Promise.all([
|
|
644
|
-
|
|
937
|
+
validateEntryData(action.contentType, action.entryDocumentId),
|
|
645
938
|
validateUniqueEntry(releaseId, action)
|
|
646
939
|
]);
|
|
647
|
-
const
|
|
940
|
+
const model = strapi2.contentType(action.contentType);
|
|
941
|
+
if (model.kind === "singleType") {
|
|
942
|
+
const document = await strapi2.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
943
|
+
if (!document) {
|
|
944
|
+
throw new utils.errors.NotFoundError(`No entry found for contentType ${action.contentType}`);
|
|
945
|
+
}
|
|
946
|
+
action.entryDocumentId = document.documentId;
|
|
947
|
+
}
|
|
948
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
|
|
648
949
|
if (!release2) {
|
|
649
950
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
650
951
|
}
|
|
651
952
|
if (release2.releasedAt) {
|
|
652
953
|
throw new utils.errors.ValidationError("Release already published");
|
|
653
954
|
}
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
955
|
+
const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
|
|
956
|
+
{
|
|
957
|
+
contentType: action.contentType,
|
|
958
|
+
documentId: action.entryDocumentId,
|
|
959
|
+
locale: action.locale
|
|
960
|
+
},
|
|
961
|
+
{
|
|
962
|
+
strapi: strapi2
|
|
963
|
+
}
|
|
964
|
+
) : true;
|
|
965
|
+
const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
658
966
|
data: {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
isEntryValid,
|
|
663
|
-
entry: {
|
|
664
|
-
id: entry.id,
|
|
665
|
-
__type: entry.contentType,
|
|
666
|
-
__pivot: { field: "entry" }
|
|
667
|
-
},
|
|
668
|
-
release: releaseId
|
|
967
|
+
...action,
|
|
968
|
+
release: release2.id,
|
|
969
|
+
isEntryValid: actionStatus
|
|
669
970
|
},
|
|
670
|
-
populate: { release: {
|
|
971
|
+
populate: { release: { select: ["id"] } }
|
|
671
972
|
});
|
|
672
|
-
|
|
973
|
+
if (!disableUpdateReleaseStatus) {
|
|
974
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
975
|
+
}
|
|
673
976
|
return releaseAction2;
|
|
674
977
|
},
|
|
675
|
-
async
|
|
676
|
-
const release2 = await strapi2.
|
|
677
|
-
|
|
978
|
+
async findPage(releaseId, query) {
|
|
979
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
980
|
+
where: { id: releaseId },
|
|
981
|
+
select: ["id"]
|
|
678
982
|
});
|
|
679
983
|
if (!release2) {
|
|
680
984
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
681
985
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
populate: "*"
|
|
687
|
-
}
|
|
688
|
-
},
|
|
689
|
-
filters: {
|
|
986
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
987
|
+
const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
988
|
+
...dbQuery,
|
|
989
|
+
where: {
|
|
690
990
|
release: releaseId
|
|
691
991
|
}
|
|
692
992
|
});
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
993
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
994
|
+
const actionsWithEntry = await utils.async.map(actions, async (action) => {
|
|
995
|
+
const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
|
|
996
|
+
const entry = await getEntry(
|
|
997
|
+
{
|
|
998
|
+
contentType: action.contentType,
|
|
999
|
+
documentId: action.entryDocumentId,
|
|
1000
|
+
locale: action.locale,
|
|
1001
|
+
populate,
|
|
1002
|
+
status: action.type === "publish" ? "draft" : "published"
|
|
1003
|
+
},
|
|
1004
|
+
{ strapi: strapi2 }
|
|
1005
|
+
);
|
|
1006
|
+
return {
|
|
1007
|
+
...action,
|
|
1008
|
+
entry,
|
|
1009
|
+
status: entry ? await getEntryStatus(action.contentType, entry) : null
|
|
1010
|
+
};
|
|
1011
|
+
});
|
|
1012
|
+
return {
|
|
1013
|
+
results: actionsWithEntry,
|
|
1014
|
+
pagination
|
|
1015
|
+
};
|
|
696
1016
|
},
|
|
697
1017
|
async groupActions(actions, groupBy) {
|
|
698
1018
|
const contentTypeUids = actions.reduce((acc, action) => {
|
|
@@ -701,10 +1021,8 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
701
1021
|
}
|
|
702
1022
|
return acc;
|
|
703
1023
|
}, []);
|
|
704
|
-
const allReleaseContentTypesDictionary = await
|
|
705
|
-
|
|
706
|
-
);
|
|
707
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
1024
|
+
const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
|
|
1025
|
+
const allLocalesDictionary = await getLocalesDataForActions();
|
|
708
1026
|
const formattedData = actions.map((action) => {
|
|
709
1027
|
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
710
1028
|
return {
|
|
@@ -720,30 +1038,6 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
720
1038
|
const groupName = getGroupName(groupBy);
|
|
721
1039
|
return ___default.default.groupBy(groupName)(formattedData);
|
|
722
1040
|
},
|
|
723
|
-
async getLocalesDataForActions() {
|
|
724
|
-
if (!strapi2.plugin("i18n")) {
|
|
725
|
-
return {};
|
|
726
|
-
}
|
|
727
|
-
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
728
|
-
return allLocales.reduce((acc, locale) => {
|
|
729
|
-
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
730
|
-
return acc;
|
|
731
|
-
}, {});
|
|
732
|
-
},
|
|
733
|
-
async getContentTypesDataForActions(contentTypesUids) {
|
|
734
|
-
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
735
|
-
const contentTypesData = {};
|
|
736
|
-
for (const contentTypeUid of contentTypesUids) {
|
|
737
|
-
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
738
|
-
uid: contentTypeUid
|
|
739
|
-
});
|
|
740
|
-
contentTypesData[contentTypeUid] = {
|
|
741
|
-
mainField: contentTypeConfig.settings.mainField,
|
|
742
|
-
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
return contentTypesData;
|
|
746
|
-
},
|
|
747
1041
|
getContentTypeModelsFromActions(actions) {
|
|
748
1042
|
const contentTypeUids = actions.reduce((acc, action) => {
|
|
749
1043
|
if (!acc.includes(action.contentType)) {
|
|
@@ -760,191 +1054,37 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
760
1054
|
);
|
|
761
1055
|
return contentTypeModelsMap;
|
|
762
1056
|
},
|
|
763
|
-
async
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
const componentsMap = components.reduce(
|
|
767
|
-
(acc, component) => {
|
|
768
|
-
acc[component.uid] = component;
|
|
769
|
-
return acc;
|
|
770
|
-
},
|
|
771
|
-
{}
|
|
772
|
-
);
|
|
773
|
-
return componentsMap;
|
|
774
|
-
},
|
|
775
|
-
async delete(releaseId) {
|
|
776
|
-
const release2 = await strapi2.entityService.findOne(RELEASE_MODEL_UID, releaseId, {
|
|
777
|
-
populate: {
|
|
778
|
-
actions: {
|
|
779
|
-
fields: ["id"]
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
});
|
|
783
|
-
if (!release2) {
|
|
784
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
785
|
-
}
|
|
786
|
-
if (release2.releasedAt) {
|
|
787
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
788
|
-
}
|
|
789
|
-
await strapi2.db.transaction(async () => {
|
|
790
|
-
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
791
|
-
where: {
|
|
792
|
-
id: {
|
|
793
|
-
$in: release2.actions.map((action) => action.id)
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
});
|
|
797
|
-
await strapi2.entityService.delete(RELEASE_MODEL_UID, releaseId);
|
|
798
|
-
});
|
|
799
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling") && release2.scheduledAt) {
|
|
800
|
-
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
801
|
-
await schedulingService.cancel(release2.id);
|
|
802
|
-
}
|
|
803
|
-
strapi2.telemetry.send("didDeleteContentRelease");
|
|
804
|
-
return release2;
|
|
1057
|
+
async countActions(query) {
|
|
1058
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
1059
|
+
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
805
1060
|
},
|
|
806
|
-
async
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
populate: {
|
|
815
|
-
entry: {
|
|
816
|
-
fields: ["id"]
|
|
817
|
-
}
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
);
|
|
823
|
-
if (!releaseWithPopulatedActionEntries) {
|
|
824
|
-
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
825
|
-
}
|
|
826
|
-
if (releaseWithPopulatedActionEntries.releasedAt) {
|
|
827
|
-
throw new utils.errors.ValidationError("Release already published");
|
|
828
|
-
}
|
|
829
|
-
if (releaseWithPopulatedActionEntries.actions.length === 0) {
|
|
830
|
-
throw new utils.errors.ValidationError("No entries to publish");
|
|
831
|
-
}
|
|
832
|
-
const collectionTypeActions = {};
|
|
833
|
-
const singleTypeActions = [];
|
|
834
|
-
for (const action of releaseWithPopulatedActionEntries.actions) {
|
|
835
|
-
const contentTypeUid = action.contentType;
|
|
836
|
-
if (strapi2.contentTypes[contentTypeUid].kind === "collectionType") {
|
|
837
|
-
if (!collectionTypeActions[contentTypeUid]) {
|
|
838
|
-
collectionTypeActions[contentTypeUid] = {
|
|
839
|
-
entriestoPublishIds: [],
|
|
840
|
-
entriesToUnpublishIds: []
|
|
841
|
-
};
|
|
842
|
-
}
|
|
843
|
-
if (action.type === "publish") {
|
|
844
|
-
collectionTypeActions[contentTypeUid].entriestoPublishIds.push(action.entry.id);
|
|
845
|
-
} else {
|
|
846
|
-
collectionTypeActions[contentTypeUid].entriesToUnpublishIds.push(action.entry.id);
|
|
847
|
-
}
|
|
848
|
-
} else {
|
|
849
|
-
singleTypeActions.push({
|
|
850
|
-
uid: contentTypeUid,
|
|
851
|
-
action: action.type,
|
|
852
|
-
id: action.entry.id
|
|
853
|
-
});
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
const entityManagerService = strapi2.plugin("content-manager").service("entity-manager");
|
|
857
|
-
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
858
|
-
await strapi2.db.transaction(async () => {
|
|
859
|
-
for (const { uid, action, id } of singleTypeActions) {
|
|
860
|
-
const populate = await populateBuilderService(uid).populateDeep(Infinity).build();
|
|
861
|
-
const entry = await strapi2.entityService.findOne(uid, id, { populate });
|
|
862
|
-
try {
|
|
863
|
-
if (action === "publish") {
|
|
864
|
-
await entityManagerService.publish(entry, uid);
|
|
865
|
-
} else {
|
|
866
|
-
await entityManagerService.unpublish(entry, uid);
|
|
867
|
-
}
|
|
868
|
-
} catch (error) {
|
|
869
|
-
if (error instanceof utils.errors.ApplicationError && (error.message === "already.published" || error.message === "already.draft")) {
|
|
870
|
-
} else {
|
|
871
|
-
throw error;
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
for (const contentTypeUid of Object.keys(collectionTypeActions)) {
|
|
876
|
-
const populate = await populateBuilderService(contentTypeUid).populateDeep(Infinity).build();
|
|
877
|
-
const { entriestoPublishIds, entriesToUnpublishIds } = collectionTypeActions[contentTypeUid];
|
|
878
|
-
const entriesToPublish = await strapi2.entityService.findMany(
|
|
879
|
-
contentTypeUid,
|
|
880
|
-
{
|
|
881
|
-
filters: {
|
|
882
|
-
id: {
|
|
883
|
-
$in: entriestoPublishIds
|
|
884
|
-
}
|
|
885
|
-
},
|
|
886
|
-
populate
|
|
887
|
-
}
|
|
888
|
-
);
|
|
889
|
-
const entriesToUnpublish = await strapi2.entityService.findMany(
|
|
890
|
-
contentTypeUid,
|
|
891
|
-
{
|
|
892
|
-
filters: {
|
|
893
|
-
id: {
|
|
894
|
-
$in: entriesToUnpublishIds
|
|
895
|
-
}
|
|
896
|
-
},
|
|
897
|
-
populate
|
|
898
|
-
}
|
|
899
|
-
);
|
|
900
|
-
if (entriesToPublish.length > 0) {
|
|
901
|
-
await entityManagerService.publishMany(entriesToPublish, contentTypeUid);
|
|
902
|
-
}
|
|
903
|
-
if (entriesToUnpublish.length > 0) {
|
|
904
|
-
await entityManagerService.unpublishMany(entriesToUnpublish, contentTypeUid);
|
|
905
|
-
}
|
|
906
|
-
}
|
|
907
|
-
});
|
|
908
|
-
const release2 = await strapi2.entityService.update(RELEASE_MODEL_UID, releaseId, {
|
|
909
|
-
data: {
|
|
910
|
-
/*
|
|
911
|
-
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
|
|
912
|
-
*/
|
|
913
|
-
// @ts-expect-error see above
|
|
914
|
-
releasedAt: /* @__PURE__ */ new Date()
|
|
915
|
-
},
|
|
916
|
-
populate: {
|
|
917
|
-
actions: {
|
|
918
|
-
// @ts-expect-error is not expecting count but it is working
|
|
919
|
-
count: true
|
|
1061
|
+
async update(actionId, releaseId, update) {
|
|
1062
|
+
const action = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findOne({
|
|
1063
|
+
where: {
|
|
1064
|
+
id: actionId,
|
|
1065
|
+
release: {
|
|
1066
|
+
id: releaseId,
|
|
1067
|
+
releasedAt: {
|
|
1068
|
+
$null: true
|
|
920
1069
|
}
|
|
921
1070
|
}
|
|
922
|
-
});
|
|
923
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
924
|
-
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
925
|
-
isPublished: true,
|
|
926
|
-
release: release2
|
|
927
|
-
});
|
|
928
|
-
}
|
|
929
|
-
strapi2.telemetry.send("didPublishContentRelease");
|
|
930
|
-
return release2;
|
|
931
|
-
} catch (error) {
|
|
932
|
-
if (strapi2.features.future.isEnabled("contentReleasesScheduling")) {
|
|
933
|
-
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
934
|
-
isPublished: false,
|
|
935
|
-
error
|
|
936
|
-
});
|
|
937
1071
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
});
|
|
944
|
-
throw error;
|
|
1072
|
+
});
|
|
1073
|
+
if (!action) {
|
|
1074
|
+
throw new utils.errors.NotFoundError(
|
|
1075
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1076
|
+
);
|
|
945
1077
|
}
|
|
946
|
-
|
|
947
|
-
|
|
1078
|
+
const actionStatus = update.type === "publish" ? await getDraftEntryValidStatus(
|
|
1079
|
+
{
|
|
1080
|
+
contentType: action.contentType,
|
|
1081
|
+
documentId: action.entryDocumentId,
|
|
1082
|
+
locale: action.locale
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
strapi: strapi2
|
|
1086
|
+
}
|
|
1087
|
+
) : true;
|
|
948
1088
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
949
1089
|
where: {
|
|
950
1090
|
id: actionId,
|
|
@@ -955,16 +1095,15 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
955
1095
|
}
|
|
956
1096
|
}
|
|
957
1097
|
},
|
|
958
|
-
data:
|
|
1098
|
+
data: {
|
|
1099
|
+
...update,
|
|
1100
|
+
isEntryValid: actionStatus
|
|
1101
|
+
}
|
|
959
1102
|
});
|
|
960
|
-
|
|
961
|
-
throw new utils.errors.NotFoundError(
|
|
962
|
-
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
963
|
-
);
|
|
964
|
-
}
|
|
1103
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
965
1104
|
return updatedAction;
|
|
966
1105
|
},
|
|
967
|
-
async
|
|
1106
|
+
async delete(actionId, releaseId) {
|
|
968
1107
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
969
1108
|
where: {
|
|
970
1109
|
id: actionId,
|
|
@@ -981,79 +1120,56 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
981
1120
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
982
1121
|
);
|
|
983
1122
|
}
|
|
984
|
-
|
|
1123
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
985
1124
|
return deletedAction;
|
|
986
|
-
},
|
|
987
|
-
async updateReleaseStatus(releaseId) {
|
|
988
|
-
const [totalActions, invalidActions] = await Promise.all([
|
|
989
|
-
this.countActions({
|
|
990
|
-
filters: {
|
|
991
|
-
release: releaseId
|
|
992
|
-
}
|
|
993
|
-
}),
|
|
994
|
-
this.countActions({
|
|
995
|
-
filters: {
|
|
996
|
-
release: releaseId,
|
|
997
|
-
isEntryValid: false
|
|
998
|
-
}
|
|
999
|
-
})
|
|
1000
|
-
]);
|
|
1001
|
-
if (totalActions > 0) {
|
|
1002
|
-
if (invalidActions > 0) {
|
|
1003
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1004
|
-
where: {
|
|
1005
|
-
id: releaseId
|
|
1006
|
-
},
|
|
1007
|
-
data: {
|
|
1008
|
-
status: "blocked"
|
|
1009
|
-
}
|
|
1010
|
-
});
|
|
1011
|
-
}
|
|
1012
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1013
|
-
where: {
|
|
1014
|
-
id: releaseId
|
|
1015
|
-
},
|
|
1016
|
-
data: {
|
|
1017
|
-
status: "ready"
|
|
1018
|
-
}
|
|
1019
|
-
});
|
|
1020
|
-
}
|
|
1021
|
-
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
1022
|
-
where: {
|
|
1023
|
-
id: releaseId
|
|
1024
|
-
},
|
|
1025
|
-
data: {
|
|
1026
|
-
status: "empty"
|
|
1027
|
-
}
|
|
1028
|
-
});
|
|
1029
1125
|
}
|
|
1030
1126
|
};
|
|
1031
1127
|
};
|
|
1128
|
+
class AlreadyOnReleaseError extends utils.errors.ApplicationError {
|
|
1129
|
+
constructor(message) {
|
|
1130
|
+
super(message);
|
|
1131
|
+
this.name = "AlreadyOnReleaseError";
|
|
1132
|
+
}
|
|
1133
|
+
}
|
|
1032
1134
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
1033
1135
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
1034
|
-
const release2 = await strapi2.
|
|
1035
|
-
|
|
1136
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1137
|
+
where: {
|
|
1138
|
+
id: releaseId
|
|
1139
|
+
},
|
|
1140
|
+
populate: {
|
|
1141
|
+
actions: true
|
|
1142
|
+
}
|
|
1036
1143
|
});
|
|
1037
1144
|
if (!release2) {
|
|
1038
1145
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1039
1146
|
}
|
|
1040
1147
|
const isEntryInRelease = release2.actions.some(
|
|
1041
|
-
(action) =>
|
|
1148
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
1042
1149
|
);
|
|
1043
1150
|
if (isEntryInRelease) {
|
|
1044
|
-
throw new
|
|
1045
|
-
`Entry with
|
|
1151
|
+
throw new AlreadyOnReleaseError(
|
|
1152
|
+
`Entry with documentId ${releaseActionArgs.entryDocumentId}${releaseActionArgs.locale ? `( ${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
|
|
1046
1153
|
);
|
|
1047
1154
|
}
|
|
1048
1155
|
},
|
|
1049
|
-
|
|
1156
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
1050
1157
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
1051
1158
|
if (!contentType) {
|
|
1052
1159
|
throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
1053
1160
|
}
|
|
1161
|
+
if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
|
|
1162
|
+
throw new utils.errors.ValidationError(
|
|
1163
|
+
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
1164
|
+
);
|
|
1165
|
+
}
|
|
1166
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1167
|
+
throw new utils.errors.ValidationError("Document id is required for collection type");
|
|
1168
|
+
}
|
|
1054
1169
|
},
|
|
1055
1170
|
async validatePendingReleasesLimit() {
|
|
1056
|
-
const
|
|
1171
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1172
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
1057
1173
|
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
1058
1174
|
filters: {
|
|
1059
1175
|
releasedAt: {
|
|
@@ -1066,8 +1182,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
1066
1182
|
}
|
|
1067
1183
|
},
|
|
1068
1184
|
async validateUniqueNameForPendingRelease(name, id) {
|
|
1069
|
-
const pendingReleases = await strapi2.
|
|
1070
|
-
|
|
1185
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1186
|
+
where: {
|
|
1071
1187
|
releasedAt: {
|
|
1072
1188
|
$null: true
|
|
1073
1189
|
},
|
|
@@ -1096,7 +1212,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1096
1212
|
}
|
|
1097
1213
|
const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
|
|
1098
1214
|
try {
|
|
1099
|
-
await getService("release").publish(releaseId);
|
|
1215
|
+
await getService("release", { strapi: strapi2 }).publish(releaseId);
|
|
1100
1216
|
} catch (error) {
|
|
1101
1217
|
}
|
|
1102
1218
|
this.cancel(releaseId);
|
|
@@ -1138,85 +1254,172 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1138
1254
|
}
|
|
1139
1255
|
};
|
|
1140
1256
|
};
|
|
1257
|
+
const DEFAULT_SETTINGS = {
|
|
1258
|
+
defaultTimezone: null
|
|
1259
|
+
};
|
|
1260
|
+
const createSettingsService = ({ strapi: strapi2 }) => {
|
|
1261
|
+
const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
|
|
1262
|
+
return {
|
|
1263
|
+
async update({ settings: settings2 }) {
|
|
1264
|
+
const store = await getStore();
|
|
1265
|
+
store.set({ key: "settings", value: settings2 });
|
|
1266
|
+
return settings2;
|
|
1267
|
+
},
|
|
1268
|
+
async find() {
|
|
1269
|
+
const store = await getStore();
|
|
1270
|
+
const settings2 = await store.get({ key: "settings" });
|
|
1271
|
+
return {
|
|
1272
|
+
...DEFAULT_SETTINGS,
|
|
1273
|
+
...settings2 || {}
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
};
|
|
1141
1278
|
const services = {
|
|
1142
1279
|
release: createReleaseService,
|
|
1280
|
+
"release-action": createReleaseActionService,
|
|
1143
1281
|
"release-validation": createReleaseValidationService,
|
|
1144
|
-
|
|
1282
|
+
scheduling: createSchedulingService,
|
|
1283
|
+
settings: createSettingsService
|
|
1145
1284
|
};
|
|
1146
|
-
const RELEASE_SCHEMA =
|
|
1147
|
-
name:
|
|
1148
|
-
scheduledAt:
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
otherwise: yup__namespace.string().nullable()
|
|
1154
|
-
}),
|
|
1155
|
-
timezone: yup__namespace.string().when("isScheduled", {
|
|
1156
|
-
is: true,
|
|
1157
|
-
then: yup__namespace.string().required().nullable(),
|
|
1158
|
-
otherwise: yup__namespace.string().nullable()
|
|
1159
|
-
}),
|
|
1160
|
-
date: yup__namespace.string().when("isScheduled", {
|
|
1161
|
-
is: true,
|
|
1162
|
-
then: yup__namespace.string().required().nullable(),
|
|
1163
|
-
otherwise: yup__namespace.string().nullable()
|
|
1285
|
+
const RELEASE_SCHEMA = utils.yup.object().shape({
|
|
1286
|
+
name: utils.yup.string().trim().required(),
|
|
1287
|
+
scheduledAt: utils.yup.string().nullable(),
|
|
1288
|
+
timezone: utils.yup.string().when("scheduledAt", {
|
|
1289
|
+
is: (value) => value !== null && value !== void 0,
|
|
1290
|
+
then: utils.yup.string().required(),
|
|
1291
|
+
otherwise: utils.yup.string().nullable()
|
|
1164
1292
|
})
|
|
1165
1293
|
}).required().noUnknown();
|
|
1294
|
+
const FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA = utils.yup.object().shape({
|
|
1295
|
+
contentType: utils.yup.string().required(),
|
|
1296
|
+
entryDocumentId: utils.yup.string().nullable(),
|
|
1297
|
+
hasEntryAttached: utils.yup.string().nullable(),
|
|
1298
|
+
locale: utils.yup.string().nullable()
|
|
1299
|
+
}).required().noUnknown();
|
|
1166
1300
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
1301
|
+
const validatefindByDocumentAttachedParams = utils.validateYupSchema(
|
|
1302
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1303
|
+
);
|
|
1167
1304
|
const releaseController = {
|
|
1168
|
-
|
|
1169
|
-
|
|
1305
|
+
/**
|
|
1306
|
+
* Find releases based on documents attached or not to the release.
|
|
1307
|
+
* If `hasEntryAttached` is true, it will return all releases that have the entry attached.
|
|
1308
|
+
* If `hasEntryAttached` is false, it will return all releases that don't have the entry attached.
|
|
1309
|
+
*/
|
|
1310
|
+
async findByDocumentAttached(ctx) {
|
|
1311
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1170
1312
|
ability: ctx.state.userAbility,
|
|
1171
1313
|
model: RELEASE_MODEL_UID
|
|
1172
1314
|
});
|
|
1173
1315
|
await permissionsManager.validateQuery(ctx.query);
|
|
1174
1316
|
const releaseService = getService("release", { strapi });
|
|
1175
|
-
const
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1317
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1318
|
+
await validatefindByDocumentAttachedParams(query);
|
|
1319
|
+
const model = strapi.getModel(query.contentType);
|
|
1320
|
+
if (model.kind && model.kind === "singleType") {
|
|
1321
|
+
const document = await strapi.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
1322
|
+
if (!document) {
|
|
1323
|
+
throw new utils.errors.NotFoundError(`No entry found for contentType ${query.contentType}`);
|
|
1324
|
+
}
|
|
1325
|
+
query.entryDocumentId = document.documentId;
|
|
1326
|
+
}
|
|
1327
|
+
const { contentType, hasEntryAttached, entryDocumentId, locale } = query;
|
|
1328
|
+
const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
|
|
1329
|
+
if (isEntryAttached) {
|
|
1330
|
+
const releases = await releaseService.findMany({
|
|
1331
|
+
where: {
|
|
1332
|
+
releasedAt: null,
|
|
1190
1333
|
actions: {
|
|
1191
|
-
|
|
1192
|
-
|
|
1334
|
+
contentType,
|
|
1335
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1336
|
+
locale: locale ?? null
|
|
1337
|
+
}
|
|
1338
|
+
},
|
|
1339
|
+
populate: {
|
|
1340
|
+
actions: {
|
|
1341
|
+
fields: ["type"],
|
|
1342
|
+
filters: {
|
|
1343
|
+
contentType,
|
|
1344
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1345
|
+
locale: locale ?? null
|
|
1193
1346
|
}
|
|
1194
1347
|
}
|
|
1195
|
-
}
|
|
1348
|
+
}
|
|
1349
|
+
});
|
|
1350
|
+
ctx.body = { data: releases };
|
|
1351
|
+
} else {
|
|
1352
|
+
const relatedReleases = await releaseService.findMany({
|
|
1353
|
+
where: {
|
|
1354
|
+
releasedAt: null,
|
|
1355
|
+
actions: {
|
|
1356
|
+
contentType,
|
|
1357
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1358
|
+
locale: locale ?? null
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1196
1361
|
});
|
|
1197
|
-
const
|
|
1362
|
+
const releases = await releaseService.findMany({
|
|
1198
1363
|
where: {
|
|
1364
|
+
$or: [
|
|
1365
|
+
{
|
|
1366
|
+
id: {
|
|
1367
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1368
|
+
}
|
|
1369
|
+
},
|
|
1370
|
+
{
|
|
1371
|
+
actions: null
|
|
1372
|
+
}
|
|
1373
|
+
],
|
|
1199
1374
|
releasedAt: null
|
|
1200
1375
|
}
|
|
1201
1376
|
});
|
|
1202
|
-
ctx.body = { data
|
|
1377
|
+
ctx.body = { data: releases };
|
|
1203
1378
|
}
|
|
1204
1379
|
},
|
|
1380
|
+
async findPage(ctx) {
|
|
1381
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1382
|
+
ability: ctx.state.userAbility,
|
|
1383
|
+
model: RELEASE_MODEL_UID
|
|
1384
|
+
});
|
|
1385
|
+
await permissionsManager.validateQuery(ctx.query);
|
|
1386
|
+
const releaseService = getService("release", { strapi });
|
|
1387
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1388
|
+
const { results, pagination } = await releaseService.findPage(query);
|
|
1389
|
+
const data = results.map((release2) => {
|
|
1390
|
+
const { actions, ...releaseData } = release2;
|
|
1391
|
+
return {
|
|
1392
|
+
...releaseData,
|
|
1393
|
+
actions: {
|
|
1394
|
+
meta: {
|
|
1395
|
+
count: actions.count
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
};
|
|
1399
|
+
});
|
|
1400
|
+
const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
|
|
1401
|
+
where: {
|
|
1402
|
+
releasedAt: null
|
|
1403
|
+
}
|
|
1404
|
+
});
|
|
1405
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
1406
|
+
},
|
|
1205
1407
|
async findOne(ctx) {
|
|
1206
1408
|
const id = ctx.params.id;
|
|
1207
1409
|
const releaseService = getService("release", { strapi });
|
|
1410
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1208
1411
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
1209
1412
|
if (!release2) {
|
|
1210
1413
|
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1211
1414
|
}
|
|
1212
|
-
const count = await
|
|
1415
|
+
const count = await releaseActionService.countActions({
|
|
1213
1416
|
filters: {
|
|
1214
1417
|
release: id
|
|
1215
1418
|
}
|
|
1216
1419
|
});
|
|
1217
1420
|
const sanitizedRelease = {
|
|
1218
1421
|
...release2,
|
|
1219
|
-
createdBy: release2.createdBy ? strapi.admin
|
|
1422
|
+
createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
|
|
1220
1423
|
};
|
|
1221
1424
|
const data = {
|
|
1222
1425
|
...sanitizedRelease,
|
|
@@ -1228,19 +1431,63 @@ const releaseController = {
|
|
|
1228
1431
|
};
|
|
1229
1432
|
ctx.body = { data };
|
|
1230
1433
|
},
|
|
1434
|
+
async mapEntriesToReleases(ctx) {
|
|
1435
|
+
const { contentTypeUid, documentIds, locale } = ctx.query;
|
|
1436
|
+
if (!contentTypeUid || !documentIds) {
|
|
1437
|
+
throw new utils.errors.ValidationError("Missing required query parameters");
|
|
1438
|
+
}
|
|
1439
|
+
const releaseService = getService("release", { strapi });
|
|
1440
|
+
const releasesWithActions = await releaseService.findMany({
|
|
1441
|
+
where: {
|
|
1442
|
+
releasedAt: null,
|
|
1443
|
+
actions: {
|
|
1444
|
+
contentType: contentTypeUid,
|
|
1445
|
+
entryDocumentId: {
|
|
1446
|
+
$in: documentIds
|
|
1447
|
+
},
|
|
1448
|
+
locale
|
|
1449
|
+
}
|
|
1450
|
+
},
|
|
1451
|
+
populate: {
|
|
1452
|
+
actions: true
|
|
1453
|
+
}
|
|
1454
|
+
});
|
|
1455
|
+
const mappedEntriesInReleases = releasesWithActions.reduce(
|
|
1456
|
+
(acc, release2) => {
|
|
1457
|
+
release2.actions.forEach((action) => {
|
|
1458
|
+
if (action.contentType !== contentTypeUid) {
|
|
1459
|
+
return;
|
|
1460
|
+
}
|
|
1461
|
+
if (locale && action.locale !== locale) {
|
|
1462
|
+
return;
|
|
1463
|
+
}
|
|
1464
|
+
if (!acc[action.entryDocumentId]) {
|
|
1465
|
+
acc[action.entryDocumentId] = [{ id: release2.id, name: release2.name }];
|
|
1466
|
+
} else {
|
|
1467
|
+
acc[action.entryDocumentId].push({ id: release2.id, name: release2.name });
|
|
1468
|
+
}
|
|
1469
|
+
});
|
|
1470
|
+
return acc;
|
|
1471
|
+
},
|
|
1472
|
+
{}
|
|
1473
|
+
);
|
|
1474
|
+
ctx.body = {
|
|
1475
|
+
data: mappedEntriesInReleases
|
|
1476
|
+
};
|
|
1477
|
+
},
|
|
1231
1478
|
async create(ctx) {
|
|
1232
1479
|
const user = ctx.state.user;
|
|
1233
1480
|
const releaseArgs = ctx.request.body;
|
|
1234
1481
|
await validateRelease(releaseArgs);
|
|
1235
1482
|
const releaseService = getService("release", { strapi });
|
|
1236
1483
|
const release2 = await releaseService.create(releaseArgs, { user });
|
|
1237
|
-
const permissionsManager = strapi.admin
|
|
1484
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1238
1485
|
ability: ctx.state.userAbility,
|
|
1239
1486
|
model: RELEASE_MODEL_UID
|
|
1240
1487
|
});
|
|
1241
|
-
ctx.
|
|
1488
|
+
ctx.created({
|
|
1242
1489
|
data: await permissionsManager.sanitizeOutput(release2)
|
|
1243
|
-
};
|
|
1490
|
+
});
|
|
1244
1491
|
},
|
|
1245
1492
|
async update(ctx) {
|
|
1246
1493
|
const user = ctx.state.user;
|
|
@@ -1249,7 +1496,7 @@ const releaseController = {
|
|
|
1249
1496
|
await validateRelease(releaseArgs);
|
|
1250
1497
|
const releaseService = getService("release", { strapi });
|
|
1251
1498
|
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
1252
|
-
const permissionsManager = strapi.admin
|
|
1499
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1253
1500
|
ability: ctx.state.userAbility,
|
|
1254
1501
|
model: RELEASE_MODEL_UID
|
|
1255
1502
|
});
|
|
@@ -1266,18 +1513,18 @@ const releaseController = {
|
|
|
1266
1513
|
};
|
|
1267
1514
|
},
|
|
1268
1515
|
async publish(ctx) {
|
|
1269
|
-
const user = ctx.state.user;
|
|
1270
1516
|
const id = ctx.params.id;
|
|
1271
1517
|
const releaseService = getService("release", { strapi });
|
|
1272
|
-
const
|
|
1518
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1519
|
+
const release2 = await releaseService.publish(id);
|
|
1273
1520
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1274
|
-
|
|
1521
|
+
releaseActionService.countActions({
|
|
1275
1522
|
filters: {
|
|
1276
1523
|
release: id,
|
|
1277
1524
|
type: "publish"
|
|
1278
1525
|
}
|
|
1279
1526
|
}),
|
|
1280
|
-
|
|
1527
|
+
releaseActionService.countActions({
|
|
1281
1528
|
filters: {
|
|
1282
1529
|
release: id,
|
|
1283
1530
|
type: "unpublish"
|
|
@@ -1295,45 +1542,93 @@ const releaseController = {
|
|
|
1295
1542
|
}
|
|
1296
1543
|
};
|
|
1297
1544
|
const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
}).required(),
|
|
1545
|
+
contentType: utils.yup.string().required(),
|
|
1546
|
+
entryDocumentId: utils.yup.strapiID(),
|
|
1547
|
+
locale: utils.yup.string(),
|
|
1302
1548
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
1303
1549
|
});
|
|
1304
1550
|
const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
|
|
1305
1551
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
1306
1552
|
});
|
|
1553
|
+
const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
|
|
1554
|
+
groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
|
|
1555
|
+
});
|
|
1307
1556
|
const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1308
1557
|
const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1558
|
+
const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1309
1559
|
const releaseActionController = {
|
|
1310
1560
|
async create(ctx) {
|
|
1311
1561
|
const releaseId = ctx.params.releaseId;
|
|
1312
1562
|
const releaseActionArgs = ctx.request.body;
|
|
1313
1563
|
await validateReleaseAction(releaseActionArgs);
|
|
1314
|
-
const
|
|
1315
|
-
const releaseAction2 = await
|
|
1316
|
-
ctx.
|
|
1564
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1565
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1566
|
+
ctx.created({
|
|
1317
1567
|
data: releaseAction2
|
|
1318
|
-
};
|
|
1568
|
+
});
|
|
1569
|
+
},
|
|
1570
|
+
async createMany(ctx) {
|
|
1571
|
+
const releaseId = ctx.params.releaseId;
|
|
1572
|
+
const releaseActionsArgs = ctx.request.body;
|
|
1573
|
+
await Promise.all(
|
|
1574
|
+
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1575
|
+
);
|
|
1576
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1577
|
+
const releaseService = getService("release", { strapi });
|
|
1578
|
+
const releaseActions = await strapi.db.transaction(async () => {
|
|
1579
|
+
const releaseActions2 = await Promise.all(
|
|
1580
|
+
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1581
|
+
try {
|
|
1582
|
+
const action = await releaseActionService.create(releaseId, releaseActionArgs, {
|
|
1583
|
+
disableUpdateReleaseStatus: true
|
|
1584
|
+
});
|
|
1585
|
+
return action;
|
|
1586
|
+
} catch (error) {
|
|
1587
|
+
if (error instanceof AlreadyOnReleaseError) {
|
|
1588
|
+
return null;
|
|
1589
|
+
}
|
|
1590
|
+
throw error;
|
|
1591
|
+
}
|
|
1592
|
+
})
|
|
1593
|
+
);
|
|
1594
|
+
return releaseActions2;
|
|
1595
|
+
});
|
|
1596
|
+
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1597
|
+
if (newReleaseActions.length > 0) {
|
|
1598
|
+
releaseService.updateReleaseStatus(releaseId);
|
|
1599
|
+
}
|
|
1600
|
+
ctx.created({
|
|
1601
|
+
data: newReleaseActions,
|
|
1602
|
+
meta: {
|
|
1603
|
+
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1604
|
+
totalEntries: releaseActions.length
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1319
1607
|
},
|
|
1320
1608
|
async findMany(ctx) {
|
|
1321
1609
|
const releaseId = ctx.params.releaseId;
|
|
1322
|
-
const permissionsManager = strapi.admin
|
|
1610
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1323
1611
|
ability: ctx.state.userAbility,
|
|
1324
1612
|
model: RELEASE_ACTION_MODEL_UID
|
|
1325
1613
|
});
|
|
1614
|
+
await validateFindManyActionsParams(ctx.query);
|
|
1615
|
+
if (ctx.query.groupBy) {
|
|
1616
|
+
if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
|
|
1617
|
+
ctx.badRequest("Invalid groupBy parameter");
|
|
1618
|
+
}
|
|
1619
|
+
}
|
|
1620
|
+
ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
|
|
1621
|
+
delete ctx.query.groupBy;
|
|
1326
1622
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1327
|
-
const
|
|
1328
|
-
const { results, pagination } = await
|
|
1329
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1623
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1624
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1330
1625
|
...query
|
|
1331
1626
|
});
|
|
1332
1627
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
1333
1628
|
if (acc[action.contentType]) {
|
|
1334
1629
|
return acc;
|
|
1335
1630
|
}
|
|
1336
|
-
const contentTypePermissionsManager = strapi.admin
|
|
1631
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1337
1632
|
ability: ctx.state.userAbility,
|
|
1338
1633
|
model: action.contentType
|
|
1339
1634
|
});
|
|
@@ -1342,10 +1637,11 @@ const releaseActionController = {
|
|
|
1342
1637
|
}, {});
|
|
1343
1638
|
const sanitizedResults = await utils.async.map(results, async (action) => ({
|
|
1344
1639
|
...action,
|
|
1345
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1640
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1346
1641
|
}));
|
|
1347
|
-
const groupedData = await
|
|
1348
|
-
const contentTypes2 =
|
|
1642
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1643
|
+
const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
|
|
1644
|
+
const releaseService = getService("release", { strapi });
|
|
1349
1645
|
const components = await releaseService.getAllComponents();
|
|
1350
1646
|
ctx.body = {
|
|
1351
1647
|
data: groupedData,
|
|
@@ -1361,8 +1657,8 @@ const releaseActionController = {
|
|
|
1361
1657
|
const releaseId = ctx.params.releaseId;
|
|
1362
1658
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1363
1659
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1364
|
-
const
|
|
1365
|
-
const updatedAction = await
|
|
1660
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1661
|
+
const updatedAction = await releaseActionService.update(
|
|
1366
1662
|
actionId,
|
|
1367
1663
|
releaseId,
|
|
1368
1664
|
releaseActionUpdateArgs
|
|
@@ -1374,17 +1670,71 @@ const releaseActionController = {
|
|
|
1374
1670
|
async delete(ctx) {
|
|
1375
1671
|
const actionId = ctx.params.actionId;
|
|
1376
1672
|
const releaseId = ctx.params.releaseId;
|
|
1377
|
-
const
|
|
1378
|
-
const deletedReleaseAction = await
|
|
1673
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1674
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1379
1675
|
ctx.body = {
|
|
1380
1676
|
data: deletedReleaseAction
|
|
1381
1677
|
};
|
|
1382
1678
|
}
|
|
1383
1679
|
};
|
|
1384
|
-
const
|
|
1680
|
+
const SETTINGS_SCHEMA = yup__namespace.object().shape({
|
|
1681
|
+
defaultTimezone: yup__namespace.string().nullable().default(null)
|
|
1682
|
+
}).required().noUnknown();
|
|
1683
|
+
const validateSettings = utils.validateYupSchema(SETTINGS_SCHEMA);
|
|
1684
|
+
const settingsController = {
|
|
1685
|
+
async find(ctx) {
|
|
1686
|
+
const settingsService = getService("settings", { strapi });
|
|
1687
|
+
const settings2 = await settingsService.find();
|
|
1688
|
+
ctx.body = { data: settings2 };
|
|
1689
|
+
},
|
|
1690
|
+
async update(ctx) {
|
|
1691
|
+
const settingsBody = ctx.request.body;
|
|
1692
|
+
const settings2 = await validateSettings(settingsBody);
|
|
1693
|
+
const settingsService = getService("settings", { strapi });
|
|
1694
|
+
const updatedSettings = await settingsService.update({ settings: settings2 });
|
|
1695
|
+
ctx.body = { data: updatedSettings };
|
|
1696
|
+
}
|
|
1697
|
+
};
|
|
1698
|
+
const controllers = {
|
|
1699
|
+
release: releaseController,
|
|
1700
|
+
"release-action": releaseActionController,
|
|
1701
|
+
settings: settingsController
|
|
1702
|
+
};
|
|
1385
1703
|
const release = {
|
|
1386
1704
|
type: "admin",
|
|
1387
1705
|
routes: [
|
|
1706
|
+
{
|
|
1707
|
+
method: "GET",
|
|
1708
|
+
path: "/mapEntriesToReleases",
|
|
1709
|
+
handler: "release.mapEntriesToReleases",
|
|
1710
|
+
config: {
|
|
1711
|
+
policies: [
|
|
1712
|
+
"admin::isAuthenticatedAdmin",
|
|
1713
|
+
{
|
|
1714
|
+
name: "admin::hasPermissions",
|
|
1715
|
+
config: {
|
|
1716
|
+
actions: ["plugin::content-releases.read"]
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
]
|
|
1720
|
+
}
|
|
1721
|
+
},
|
|
1722
|
+
{
|
|
1723
|
+
method: "GET",
|
|
1724
|
+
path: "/getByDocumentAttached",
|
|
1725
|
+
handler: "release.findByDocumentAttached",
|
|
1726
|
+
config: {
|
|
1727
|
+
policies: [
|
|
1728
|
+
"admin::isAuthenticatedAdmin",
|
|
1729
|
+
{
|
|
1730
|
+
name: "admin::hasPermissions",
|
|
1731
|
+
config: {
|
|
1732
|
+
actions: ["plugin::content-releases.read"]
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
]
|
|
1736
|
+
}
|
|
1737
|
+
},
|
|
1388
1738
|
{
|
|
1389
1739
|
method: "POST",
|
|
1390
1740
|
path: "/",
|
|
@@ -1404,7 +1754,7 @@ const release = {
|
|
|
1404
1754
|
{
|
|
1405
1755
|
method: "GET",
|
|
1406
1756
|
path: "/",
|
|
1407
|
-
handler: "release.
|
|
1757
|
+
handler: "release.findPage",
|
|
1408
1758
|
config: {
|
|
1409
1759
|
policies: [
|
|
1410
1760
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1502,6 +1852,22 @@ const releaseAction = {
|
|
|
1502
1852
|
]
|
|
1503
1853
|
}
|
|
1504
1854
|
},
|
|
1855
|
+
{
|
|
1856
|
+
method: "POST",
|
|
1857
|
+
path: "/:releaseId/actions/bulk",
|
|
1858
|
+
handler: "release-action.createMany",
|
|
1859
|
+
config: {
|
|
1860
|
+
policies: [
|
|
1861
|
+
"admin::isAuthenticatedAdmin",
|
|
1862
|
+
{
|
|
1863
|
+
name: "admin::hasPermissions",
|
|
1864
|
+
config: {
|
|
1865
|
+
actions: ["plugin::content-releases.create-action"]
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
]
|
|
1869
|
+
}
|
|
1870
|
+
},
|
|
1505
1871
|
{
|
|
1506
1872
|
method: "GET",
|
|
1507
1873
|
path: "/:releaseId/actions",
|
|
@@ -1552,7 +1918,45 @@ const releaseAction = {
|
|
|
1552
1918
|
}
|
|
1553
1919
|
]
|
|
1554
1920
|
};
|
|
1921
|
+
const settings = {
|
|
1922
|
+
type: "admin",
|
|
1923
|
+
routes: [
|
|
1924
|
+
{
|
|
1925
|
+
method: "GET",
|
|
1926
|
+
path: "/settings",
|
|
1927
|
+
handler: "settings.find",
|
|
1928
|
+
config: {
|
|
1929
|
+
policies: [
|
|
1930
|
+
"admin::isAuthenticatedAdmin",
|
|
1931
|
+
{
|
|
1932
|
+
name: "admin::hasPermissions",
|
|
1933
|
+
config: {
|
|
1934
|
+
actions: ["plugin::content-releases.settings.read"]
|
|
1935
|
+
}
|
|
1936
|
+
}
|
|
1937
|
+
]
|
|
1938
|
+
}
|
|
1939
|
+
},
|
|
1940
|
+
{
|
|
1941
|
+
method: "PUT",
|
|
1942
|
+
path: "/settings",
|
|
1943
|
+
handler: "settings.update",
|
|
1944
|
+
config: {
|
|
1945
|
+
policies: [
|
|
1946
|
+
"admin::isAuthenticatedAdmin",
|
|
1947
|
+
{
|
|
1948
|
+
name: "admin::hasPermissions",
|
|
1949
|
+
config: {
|
|
1950
|
+
actions: ["plugin::content-releases.settings.update"]
|
|
1951
|
+
}
|
|
1952
|
+
}
|
|
1953
|
+
]
|
|
1954
|
+
}
|
|
1955
|
+
}
|
|
1956
|
+
]
|
|
1957
|
+
};
|
|
1555
1958
|
const routes = {
|
|
1959
|
+
settings,
|
|
1556
1960
|
release,
|
|
1557
1961
|
"release-action": releaseAction
|
|
1558
1962
|
};
|
|
@@ -1569,6 +1973,9 @@ const getPlugin = () => {
|
|
|
1569
1973
|
};
|
|
1570
1974
|
}
|
|
1571
1975
|
return {
|
|
1976
|
+
// Always return register, it handles its own feature check
|
|
1977
|
+
register,
|
|
1978
|
+
// Always return contentTypes to avoid losing data when the feature is disabled
|
|
1572
1979
|
contentTypes
|
|
1573
1980
|
};
|
|
1574
1981
|
};
|