@strapi/content-releases 0.0.0-experimental.fc1ac2acd58c8a5a858679956b6d102ac5ee4011 → 0.0.0-experimental.fd379e4937e431407d784eaa5fe7f93cf2a53386
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 +1057 -646
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1058 -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,242 @@ 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 hasTable = await trx.schema.hasTable("strapi_release_actions");
|
|
333
|
+
if (!hasTable) {
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
const hasPolymorphicColumn = await trx.schema.hasColumn("strapi_release_actions", "target_id");
|
|
337
|
+
if (hasPolymorphicColumn) {
|
|
338
|
+
const hasEntryDocumentIdColumn = await trx.schema.hasColumn(
|
|
339
|
+
"strapi_release_actions",
|
|
340
|
+
"entry_document_id"
|
|
341
|
+
);
|
|
342
|
+
if (!hasEntryDocumentIdColumn) {
|
|
343
|
+
await trx.schema.alterTable("strapi_release_actions", (table) => {
|
|
344
|
+
table.string("entry_document_id");
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
const releaseActions = await trx.select("*").from("strapi_release_actions");
|
|
348
|
+
utils.async.map(releaseActions, async (action) => {
|
|
349
|
+
const { target_type, target_id } = action;
|
|
350
|
+
const entry = await db.query(target_type).findOne({ where: { id: target_id } });
|
|
351
|
+
if (entry) {
|
|
352
|
+
await trx("strapi_release_actions").update({ entry_document_id: entry.documentId }).where("id", action.id);
|
|
353
|
+
}
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
async down() {
|
|
358
|
+
throw new Error("not implemented");
|
|
359
|
+
}
|
|
360
|
+
};
|
|
212
361
|
const register = async ({ strapi: strapi2 }) => {
|
|
213
362
|
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
214
|
-
await strapi2.admin
|
|
215
|
-
strapi2.
|
|
363
|
+
await strapi2.service("admin::permission").actionProvider.registerMany(ACTIONS);
|
|
364
|
+
strapi2.db.migrations.providers.internal.register(addEntryDocumentToReleaseActions);
|
|
365
|
+
strapi2.hook("strapi::content-types.beforeSync").register(disableContentTypeLocalized).register(deleteActionsOnDisableDraftAndPublish);
|
|
366
|
+
strapi2.hook("strapi::content-types.afterSync").register(deleteActionsOnDeleteContentType).register(enableContentTypeLocalized).register(revalidateChangedContentTypes).register(migrateIsValidAndStatusReleases);
|
|
367
|
+
}
|
|
368
|
+
if (strapi2.plugin("graphql")) {
|
|
369
|
+
const graphqlExtensionService = strapi2.plugin("graphql").service("extension");
|
|
370
|
+
graphqlExtensionService.shadowCRUD(RELEASE_MODEL_UID).disable();
|
|
371
|
+
graphqlExtensionService.shadowCRUD(RELEASE_ACTION_MODEL_UID).disable();
|
|
372
|
+
}
|
|
373
|
+
};
|
|
374
|
+
const updateActionsStatusAndUpdateReleaseStatus = async (contentType, entry) => {
|
|
375
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
376
|
+
where: {
|
|
377
|
+
actions: {
|
|
378
|
+
contentType,
|
|
379
|
+
entryDocumentId: entry.documentId,
|
|
380
|
+
locale: entry.locale
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
const entryStatus = await isEntryValid(contentType, entry, { strapi });
|
|
385
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
386
|
+
where: {
|
|
387
|
+
contentType,
|
|
388
|
+
entryDocumentId: entry.documentId,
|
|
389
|
+
locale: entry.locale
|
|
390
|
+
},
|
|
391
|
+
data: {
|
|
392
|
+
isEntryValid: entryStatus
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
for (const release2 of releases) {
|
|
396
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
397
|
+
}
|
|
398
|
+
};
|
|
399
|
+
const deleteActionsAndUpdateReleaseStatus = async (params) => {
|
|
400
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
401
|
+
where: {
|
|
402
|
+
actions: params
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
406
|
+
where: params
|
|
407
|
+
});
|
|
408
|
+
for (const release2 of releases) {
|
|
409
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
const deleteActionsOnDelete = async (ctx, next) => {
|
|
413
|
+
if (ctx.action !== "delete") {
|
|
414
|
+
return next();
|
|
415
|
+
}
|
|
416
|
+
if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
|
|
417
|
+
return next();
|
|
418
|
+
}
|
|
419
|
+
const contentType = ctx.contentType.uid;
|
|
420
|
+
const { documentId, locale } = ctx.params;
|
|
421
|
+
const result = await next();
|
|
422
|
+
if (!result) {
|
|
423
|
+
return result;
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
deleteActionsAndUpdateReleaseStatus({
|
|
427
|
+
contentType,
|
|
428
|
+
entryDocumentId: documentId,
|
|
429
|
+
...locale !== "*" && { locale }
|
|
430
|
+
});
|
|
431
|
+
} catch (error) {
|
|
432
|
+
strapi.log.error("Error while deleting release actions after delete", {
|
|
433
|
+
error
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
return result;
|
|
437
|
+
};
|
|
438
|
+
const updateActionsOnUpdate = async (ctx, next) => {
|
|
439
|
+
if (ctx.action !== "update") {
|
|
440
|
+
return next();
|
|
441
|
+
}
|
|
442
|
+
if (!utils.contentTypes.hasDraftAndPublish(ctx.contentType)) {
|
|
443
|
+
return next();
|
|
444
|
+
}
|
|
445
|
+
const contentType = ctx.contentType.uid;
|
|
446
|
+
const result = await next();
|
|
447
|
+
if (!result) {
|
|
448
|
+
return result;
|
|
449
|
+
}
|
|
450
|
+
try {
|
|
451
|
+
updateActionsStatusAndUpdateReleaseStatus(contentType, result);
|
|
452
|
+
} catch (error) {
|
|
453
|
+
strapi.log.error("Error while updating release actions after update", {
|
|
454
|
+
error
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
return result;
|
|
458
|
+
};
|
|
459
|
+
const deleteReleasesActionsAndUpdateReleaseStatus = async (params) => {
|
|
460
|
+
const releases = await strapi.db.query(RELEASE_MODEL_UID).findMany({
|
|
461
|
+
where: {
|
|
462
|
+
actions: params
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
await strapi.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
466
|
+
where: params
|
|
467
|
+
});
|
|
468
|
+
for (const release2 of releases) {
|
|
469
|
+
getService("release", { strapi }).updateReleaseStatus(release2.id);
|
|
216
470
|
}
|
|
217
471
|
};
|
|
218
472
|
const bootstrap = async ({ strapi: strapi2 }) => {
|
|
219
473
|
if (strapi2.ee.features.isEnabled("cms-content-releases")) {
|
|
220
|
-
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes)
|
|
474
|
+
const contentTypesWithDraftAndPublish = Object.keys(strapi2.contentTypes).filter(
|
|
475
|
+
(uid) => strapi2.contentTypes[uid]?.options?.draftAndPublish
|
|
476
|
+
);
|
|
221
477
|
strapi2.db.lifecycles.subscribe({
|
|
222
478
|
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
|
-
/**
|
|
251
|
-
* deleteMany hook doesn't return the deleted entries ids
|
|
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
479
|
/**
|
|
263
|
-
*
|
|
264
|
-
* We make this only after deleteMany is succesfully executed to avoid errors
|
|
480
|
+
* deleteMany is still used outside documents service, for example when deleting a locale
|
|
265
481
|
*/
|
|
266
482
|
async afterDeleteMany(event) {
|
|
267
483
|
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
|
-
}
|
|
484
|
+
const model = strapi2.getModel(event.model.uid);
|
|
485
|
+
if (model.kind === "collectionType" && model.options?.draftAndPublish) {
|
|
486
|
+
const { where } = event.params;
|
|
487
|
+
deleteReleasesActionsAndUpdateReleaseStatus({
|
|
488
|
+
contentType: model.uid,
|
|
489
|
+
locale: where.locale ?? null,
|
|
490
|
+
...where.documentId && { entryDocumentId: where.documentId }
|
|
290
491
|
});
|
|
291
|
-
for (const release2 of releases) {
|
|
292
|
-
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
293
|
-
}
|
|
294
492
|
}
|
|
295
493
|
} catch (error) {
|
|
296
494
|
strapi2.log.error("Error while deleting release actions after entry deleteMany", {
|
|
297
495
|
error
|
|
298
496
|
});
|
|
299
497
|
}
|
|
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
498
|
}
|
|
337
499
|
});
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
}
|
|
500
|
+
strapi2.documents.use(deleteActionsOnDelete);
|
|
501
|
+
strapi2.documents.use(updateActionsOnUpdate);
|
|
502
|
+
getService("scheduling", { strapi: strapi2 }).syncFromDatabase().catch((err) => {
|
|
503
|
+
strapi2.log.error(
|
|
504
|
+
"Error while syncing scheduled jobs from the database in the content-releases plugin. This could lead to errors in the releases scheduling."
|
|
505
|
+
);
|
|
506
|
+
throw err;
|
|
507
|
+
});
|
|
508
|
+
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
|
509
|
+
strapi2.get("webhookStore").addAllowedEvent(key, value);
|
|
510
|
+
});
|
|
349
511
|
}
|
|
350
512
|
};
|
|
351
513
|
const destroy = async ({ strapi: strapi2 }) => {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
job.cancel();
|
|
358
|
-
}
|
|
514
|
+
const scheduledJobs = getService("scheduling", {
|
|
515
|
+
strapi: strapi2
|
|
516
|
+
}).getAll();
|
|
517
|
+
for (const [, job] of scheduledJobs) {
|
|
518
|
+
job.cancel();
|
|
359
519
|
}
|
|
360
520
|
};
|
|
361
521
|
const schema$1 = {
|
|
@@ -430,15 +590,13 @@ const schema = {
|
|
|
430
590
|
enum: ["publish", "unpublish"],
|
|
431
591
|
required: true
|
|
432
592
|
},
|
|
433
|
-
entry: {
|
|
434
|
-
type: "relation",
|
|
435
|
-
relation: "morphToOne",
|
|
436
|
-
configurable: false
|
|
437
|
-
},
|
|
438
593
|
contentType: {
|
|
439
594
|
type: "string",
|
|
440
595
|
required: true
|
|
441
596
|
},
|
|
597
|
+
entryDocumentId: {
|
|
598
|
+
type: "string"
|
|
599
|
+
},
|
|
442
600
|
locale: {
|
|
443
601
|
type: "string"
|
|
444
602
|
},
|
|
@@ -460,18 +618,6 @@ const contentTypes = {
|
|
|
460
618
|
release: release$1,
|
|
461
619
|
"release-action": releaseAction$1
|
|
462
620
|
};
|
|
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
621
|
const createReleaseService = ({ strapi: strapi2 }) => {
|
|
476
622
|
const dispatchWebhook = (event, { isPublished, release: release2, error }) => {
|
|
477
623
|
strapi2.eventHub.emit(event, {
|
|
@@ -480,6 +626,33 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
480
626
|
release: release2
|
|
481
627
|
});
|
|
482
628
|
};
|
|
629
|
+
const getFormattedActions = async (releaseId) => {
|
|
630
|
+
const actions = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findMany({
|
|
631
|
+
where: {
|
|
632
|
+
release: {
|
|
633
|
+
id: releaseId
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
});
|
|
637
|
+
if (actions.length === 0) {
|
|
638
|
+
throw new utils.errors.ValidationError("No entries to publish");
|
|
639
|
+
}
|
|
640
|
+
const formattedActions = {};
|
|
641
|
+
for (const action of actions) {
|
|
642
|
+
const contentTypeUid = action.contentType;
|
|
643
|
+
if (!formattedActions[contentTypeUid]) {
|
|
644
|
+
formattedActions[contentTypeUid] = {
|
|
645
|
+
publish: [],
|
|
646
|
+
unpublish: []
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
formattedActions[contentTypeUid][action.type].push({
|
|
650
|
+
documentId: action.entryDocumentId,
|
|
651
|
+
locale: action.locale
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
return formattedActions;
|
|
655
|
+
};
|
|
483
656
|
return {
|
|
484
657
|
async create(releaseData, { user }) {
|
|
485
658
|
const releaseWithCreatorFields = await utils.setCreatorFields({ user })(releaseData);
|
|
@@ -493,13 +666,13 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
493
666
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name),
|
|
494
667
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
495
668
|
]);
|
|
496
|
-
const release2 = await strapi2.
|
|
669
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).create({
|
|
497
670
|
data: {
|
|
498
671
|
...releaseWithCreatorFields,
|
|
499
672
|
status: "empty"
|
|
500
673
|
}
|
|
501
674
|
});
|
|
502
|
-
if (
|
|
675
|
+
if (releaseWithCreatorFields.scheduledAt) {
|
|
503
676
|
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
504
677
|
await schedulingService.set(release2.id, release2.scheduledAt);
|
|
505
678
|
}
|
|
@@ -507,94 +680,28 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
507
680
|
return release2;
|
|
508
681
|
},
|
|
509
682
|
async findOne(id, query = {}) {
|
|
510
|
-
const
|
|
511
|
-
|
|
683
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query);
|
|
684
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
685
|
+
...dbQuery,
|
|
686
|
+
where: { id }
|
|
512
687
|
});
|
|
513
688
|
return release2;
|
|
514
689
|
},
|
|
515
690
|
findPage(query) {
|
|
516
|
-
|
|
517
|
-
|
|
691
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
692
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findPage({
|
|
693
|
+
...dbQuery,
|
|
518
694
|
populate: {
|
|
519
695
|
actions: {
|
|
520
|
-
// @ts-expect-error Ignore missing properties
|
|
521
696
|
count: true
|
|
522
697
|
}
|
|
523
698
|
}
|
|
524
699
|
});
|
|
525
700
|
},
|
|
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;
|
|
701
|
+
findMany(query) {
|
|
702
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_MODEL_UID, query ?? {});
|
|
703
|
+
return strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
704
|
+
...dbQuery
|
|
598
705
|
});
|
|
599
706
|
},
|
|
600
707
|
async update(id, releaseData, { user }) {
|
|
@@ -609,90 +716,307 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
609
716
|
validateUniqueNameForPendingRelease(releaseWithCreatorFields.name, id),
|
|
610
717
|
validateScheduledAtIsLaterThanNow(releaseWithCreatorFields.scheduledAt)
|
|
611
718
|
]);
|
|
612
|
-
const release2 = await strapi2.
|
|
719
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id } });
|
|
613
720
|
if (!release2) {
|
|
614
721
|
throw new utils.errors.NotFoundError(`No release found for id ${id}`);
|
|
615
722
|
}
|
|
616
723
|
if (release2.releasedAt) {
|
|
617
724
|
throw new utils.errors.ValidationError("Release already published");
|
|
618
725
|
}
|
|
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
|
|
726
|
+
const updatedRelease = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
727
|
+
where: { id },
|
|
625
728
|
data: releaseWithCreatorFields
|
|
626
729
|
});
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
schedulingService.cancel(id);
|
|
633
|
-
}
|
|
730
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
731
|
+
if (releaseData.scheduledAt) {
|
|
732
|
+
await schedulingService.set(id, releaseData.scheduledAt);
|
|
733
|
+
} else if (release2.scheduledAt) {
|
|
734
|
+
schedulingService.cancel(id);
|
|
634
735
|
}
|
|
635
736
|
this.updateReleaseStatus(id);
|
|
636
737
|
strapi2.telemetry.send("didUpdateContentRelease");
|
|
637
738
|
return updatedRelease;
|
|
638
739
|
},
|
|
639
|
-
async
|
|
640
|
-
const
|
|
740
|
+
async getAllComponents() {
|
|
741
|
+
const contentManagerComponentsService = strapi2.plugin("content-manager").service("components");
|
|
742
|
+
const components = await contentManagerComponentsService.findAllComponents();
|
|
743
|
+
const componentsMap = components.reduce(
|
|
744
|
+
(acc, component) => {
|
|
745
|
+
acc[component.uid] = component;
|
|
746
|
+
return acc;
|
|
747
|
+
},
|
|
748
|
+
{}
|
|
749
|
+
);
|
|
750
|
+
return componentsMap;
|
|
751
|
+
},
|
|
752
|
+
async delete(releaseId) {
|
|
753
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
754
|
+
where: { id: releaseId },
|
|
755
|
+
populate: {
|
|
756
|
+
actions: {
|
|
757
|
+
select: ["id"]
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
});
|
|
761
|
+
if (!release2) {
|
|
762
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
763
|
+
}
|
|
764
|
+
if (release2.releasedAt) {
|
|
765
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
766
|
+
}
|
|
767
|
+
await strapi2.db.transaction(async () => {
|
|
768
|
+
await strapi2.db.query(RELEASE_ACTION_MODEL_UID).deleteMany({
|
|
769
|
+
where: {
|
|
770
|
+
id: {
|
|
771
|
+
$in: release2.actions.map((action) => action.id)
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
});
|
|
775
|
+
await strapi2.db.query(RELEASE_MODEL_UID).delete({
|
|
776
|
+
where: {
|
|
777
|
+
id: releaseId
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
});
|
|
781
|
+
if (release2.scheduledAt) {
|
|
782
|
+
const schedulingService = getService("scheduling", { strapi: strapi2 });
|
|
783
|
+
await schedulingService.cancel(release2.id);
|
|
784
|
+
}
|
|
785
|
+
strapi2.telemetry.send("didDeleteContentRelease");
|
|
786
|
+
return release2;
|
|
787
|
+
},
|
|
788
|
+
async publish(releaseId) {
|
|
789
|
+
const {
|
|
790
|
+
release: release2,
|
|
791
|
+
error
|
|
792
|
+
} = await strapi2.db.transaction(async ({ trx }) => {
|
|
793
|
+
const lockedRelease = await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).select(["id", "name", "releasedAt", "status"]).first().transacting(trx).forUpdate().execute();
|
|
794
|
+
if (!lockedRelease) {
|
|
795
|
+
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
796
|
+
}
|
|
797
|
+
if (lockedRelease.releasedAt) {
|
|
798
|
+
throw new utils.errors.ValidationError("Release already published");
|
|
799
|
+
}
|
|
800
|
+
if (lockedRelease.status === "failed") {
|
|
801
|
+
throw new utils.errors.ValidationError("Release failed to publish");
|
|
802
|
+
}
|
|
803
|
+
try {
|
|
804
|
+
strapi2.log.info(`[Content Releases] Starting to publish release ${lockedRelease.name}`);
|
|
805
|
+
const formattedActions = await getFormattedActions(releaseId);
|
|
806
|
+
await strapi2.db.transaction(
|
|
807
|
+
async () => Promise.all(
|
|
808
|
+
Object.keys(formattedActions).map(async (contentTypeUid) => {
|
|
809
|
+
const contentType = contentTypeUid;
|
|
810
|
+
const { publish, unpublish } = formattedActions[contentType];
|
|
811
|
+
return Promise.all([
|
|
812
|
+
...publish.map((params) => strapi2.documents(contentType).publish(params)),
|
|
813
|
+
...unpublish.map((params) => strapi2.documents(contentType).unpublish(params))
|
|
814
|
+
]);
|
|
815
|
+
})
|
|
816
|
+
)
|
|
817
|
+
);
|
|
818
|
+
const release22 = await strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
819
|
+
where: {
|
|
820
|
+
id: releaseId
|
|
821
|
+
},
|
|
822
|
+
data: {
|
|
823
|
+
status: "done",
|
|
824
|
+
releasedAt: /* @__PURE__ */ new Date()
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
828
|
+
isPublished: true,
|
|
829
|
+
release: release22
|
|
830
|
+
});
|
|
831
|
+
strapi2.telemetry.send("didPublishContentRelease");
|
|
832
|
+
return { release: release22, error: null };
|
|
833
|
+
} catch (error2) {
|
|
834
|
+
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
|
835
|
+
isPublished: false,
|
|
836
|
+
error: error2
|
|
837
|
+
});
|
|
838
|
+
await strapi2.db?.queryBuilder(RELEASE_MODEL_UID).where({ id: releaseId }).update({
|
|
839
|
+
status: "failed"
|
|
840
|
+
}).transacting(trx).execute();
|
|
841
|
+
return {
|
|
842
|
+
release: null,
|
|
843
|
+
error: error2
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
if (error instanceof Error) {
|
|
848
|
+
throw error;
|
|
849
|
+
}
|
|
850
|
+
return release2;
|
|
851
|
+
},
|
|
852
|
+
async updateReleaseStatus(releaseId) {
|
|
853
|
+
const releaseActionService = getService("release-action", { strapi: strapi2 });
|
|
854
|
+
const [totalActions, invalidActions] = await Promise.all([
|
|
855
|
+
releaseActionService.countActions({
|
|
856
|
+
filters: {
|
|
857
|
+
release: releaseId
|
|
858
|
+
}
|
|
859
|
+
}),
|
|
860
|
+
releaseActionService.countActions({
|
|
861
|
+
filters: {
|
|
862
|
+
release: releaseId,
|
|
863
|
+
isEntryValid: false
|
|
864
|
+
}
|
|
865
|
+
})
|
|
866
|
+
]);
|
|
867
|
+
if (totalActions > 0) {
|
|
868
|
+
if (invalidActions > 0) {
|
|
869
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
870
|
+
where: {
|
|
871
|
+
id: releaseId
|
|
872
|
+
},
|
|
873
|
+
data: {
|
|
874
|
+
status: "blocked"
|
|
875
|
+
}
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
879
|
+
where: {
|
|
880
|
+
id: releaseId
|
|
881
|
+
},
|
|
882
|
+
data: {
|
|
883
|
+
status: "ready"
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
return strapi2.db.query(RELEASE_MODEL_UID).update({
|
|
888
|
+
where: {
|
|
889
|
+
id: releaseId
|
|
890
|
+
},
|
|
891
|
+
data: {
|
|
892
|
+
status: "empty"
|
|
893
|
+
}
|
|
894
|
+
});
|
|
895
|
+
}
|
|
896
|
+
};
|
|
897
|
+
};
|
|
898
|
+
const getGroupName = (queryValue) => {
|
|
899
|
+
switch (queryValue) {
|
|
900
|
+
case "contentType":
|
|
901
|
+
return "contentType.displayName";
|
|
902
|
+
case "type":
|
|
903
|
+
return "type";
|
|
904
|
+
case "locale":
|
|
905
|
+
return ___default.default.getOr("No locale", "locale.name");
|
|
906
|
+
default:
|
|
907
|
+
return "contentType.displayName";
|
|
908
|
+
}
|
|
909
|
+
};
|
|
910
|
+
const createReleaseActionService = ({ strapi: strapi2 }) => {
|
|
911
|
+
const getLocalesDataForActions = async () => {
|
|
912
|
+
if (!strapi2.plugin("i18n")) {
|
|
913
|
+
return {};
|
|
914
|
+
}
|
|
915
|
+
const allLocales = await strapi2.plugin("i18n").service("locales").find() || [];
|
|
916
|
+
return allLocales.reduce((acc, locale) => {
|
|
917
|
+
acc[locale.code] = { name: locale.name, code: locale.code };
|
|
918
|
+
return acc;
|
|
919
|
+
}, {});
|
|
920
|
+
};
|
|
921
|
+
const getContentTypesDataForActions = async (contentTypesUids) => {
|
|
922
|
+
const contentManagerContentTypeService = strapi2.plugin("content-manager").service("content-types");
|
|
923
|
+
const contentTypesData = {};
|
|
924
|
+
for (const contentTypeUid of contentTypesUids) {
|
|
925
|
+
const contentTypeConfig = await contentManagerContentTypeService.findConfiguration({
|
|
926
|
+
uid: contentTypeUid
|
|
927
|
+
});
|
|
928
|
+
contentTypesData[contentTypeUid] = {
|
|
929
|
+
mainField: contentTypeConfig.settings.mainField,
|
|
930
|
+
displayName: strapi2.getModel(contentTypeUid).info.displayName
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
return contentTypesData;
|
|
934
|
+
};
|
|
935
|
+
return {
|
|
936
|
+
async create(releaseId, action, { disableUpdateReleaseStatus = false } = {}) {
|
|
937
|
+
const { validateEntryData, validateUniqueEntry } = getService("release-validation", {
|
|
641
938
|
strapi: strapi2
|
|
642
939
|
});
|
|
643
940
|
await Promise.all([
|
|
644
|
-
|
|
941
|
+
validateEntryData(action.contentType, action.entryDocumentId),
|
|
645
942
|
validateUniqueEntry(releaseId, action)
|
|
646
943
|
]);
|
|
647
|
-
const
|
|
944
|
+
const model = strapi2.contentType(action.contentType);
|
|
945
|
+
if (model.kind === "singleType") {
|
|
946
|
+
const document = await strapi2.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
947
|
+
if (!document) {
|
|
948
|
+
throw new utils.errors.NotFoundError(`No entry found for contentType ${action.contentType}`);
|
|
949
|
+
}
|
|
950
|
+
action.entryDocumentId = document.documentId;
|
|
951
|
+
}
|
|
952
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({ where: { id: releaseId } });
|
|
648
953
|
if (!release2) {
|
|
649
954
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
650
955
|
}
|
|
651
956
|
if (release2.releasedAt) {
|
|
652
957
|
throw new utils.errors.ValidationError("Release already published");
|
|
653
958
|
}
|
|
654
|
-
const
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
959
|
+
const actionStatus = action.type === "publish" ? await getDraftEntryValidStatus(
|
|
960
|
+
{
|
|
961
|
+
contentType: action.contentType,
|
|
962
|
+
documentId: action.entryDocumentId,
|
|
963
|
+
locale: action.locale
|
|
964
|
+
},
|
|
965
|
+
{
|
|
966
|
+
strapi: strapi2
|
|
967
|
+
}
|
|
968
|
+
) : true;
|
|
969
|
+
const releaseAction2 = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).create({
|
|
658
970
|
data: {
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
isEntryValid,
|
|
663
|
-
entry: {
|
|
664
|
-
id: entry.id,
|
|
665
|
-
__type: entry.contentType,
|
|
666
|
-
__pivot: { field: "entry" }
|
|
667
|
-
},
|
|
668
|
-
release: releaseId
|
|
971
|
+
...action,
|
|
972
|
+
release: release2.id,
|
|
973
|
+
isEntryValid: actionStatus
|
|
669
974
|
},
|
|
670
|
-
populate: { release: {
|
|
975
|
+
populate: { release: { select: ["id"] } }
|
|
671
976
|
});
|
|
672
|
-
|
|
977
|
+
if (!disableUpdateReleaseStatus) {
|
|
978
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(release2.id);
|
|
979
|
+
}
|
|
673
980
|
return releaseAction2;
|
|
674
981
|
},
|
|
675
|
-
async
|
|
676
|
-
const release2 = await strapi2.
|
|
677
|
-
|
|
982
|
+
async findPage(releaseId, query) {
|
|
983
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
984
|
+
where: { id: releaseId },
|
|
985
|
+
select: ["id"]
|
|
678
986
|
});
|
|
679
987
|
if (!release2) {
|
|
680
988
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
681
989
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
populate: "*"
|
|
687
|
-
}
|
|
688
|
-
},
|
|
689
|
-
filters: {
|
|
990
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
991
|
+
const { results: actions, pagination } = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findPage({
|
|
992
|
+
...dbQuery,
|
|
993
|
+
where: {
|
|
690
994
|
release: releaseId
|
|
691
995
|
}
|
|
692
996
|
});
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
997
|
+
const populateBuilderService = strapi2.plugin("content-manager").service("populate-builder");
|
|
998
|
+
const actionsWithEntry = await utils.async.map(actions, async (action) => {
|
|
999
|
+
const populate = await populateBuilderService(action.contentType).populateDeep(Infinity).build();
|
|
1000
|
+
const entry = await getEntry(
|
|
1001
|
+
{
|
|
1002
|
+
contentType: action.contentType,
|
|
1003
|
+
documentId: action.entryDocumentId,
|
|
1004
|
+
locale: action.locale,
|
|
1005
|
+
populate,
|
|
1006
|
+
status: action.type === "publish" ? "draft" : "published"
|
|
1007
|
+
},
|
|
1008
|
+
{ strapi: strapi2 }
|
|
1009
|
+
);
|
|
1010
|
+
return {
|
|
1011
|
+
...action,
|
|
1012
|
+
entry,
|
|
1013
|
+
status: entry ? await getEntryStatus(action.contentType, entry) : null
|
|
1014
|
+
};
|
|
1015
|
+
});
|
|
1016
|
+
return {
|
|
1017
|
+
results: actionsWithEntry,
|
|
1018
|
+
pagination
|
|
1019
|
+
};
|
|
696
1020
|
},
|
|
697
1021
|
async groupActions(actions, groupBy) {
|
|
698
1022
|
const contentTypeUids = actions.reduce((acc, action) => {
|
|
@@ -701,10 +1025,8 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
701
1025
|
}
|
|
702
1026
|
return acc;
|
|
703
1027
|
}, []);
|
|
704
|
-
const allReleaseContentTypesDictionary = await
|
|
705
|
-
|
|
706
|
-
);
|
|
707
|
-
const allLocalesDictionary = await this.getLocalesDataForActions();
|
|
1028
|
+
const allReleaseContentTypesDictionary = await getContentTypesDataForActions(contentTypeUids);
|
|
1029
|
+
const allLocalesDictionary = await getLocalesDataForActions();
|
|
708
1030
|
const formattedData = actions.map((action) => {
|
|
709
1031
|
const { mainField, displayName } = allReleaseContentTypesDictionary[action.contentType];
|
|
710
1032
|
return {
|
|
@@ -720,30 +1042,6 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
720
1042
|
const groupName = getGroupName(groupBy);
|
|
721
1043
|
return ___default.default.groupBy(groupName)(formattedData);
|
|
722
1044
|
},
|
|
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
1045
|
getContentTypeModelsFromActions(actions) {
|
|
748
1046
|
const contentTypeUids = actions.reduce((acc, action) => {
|
|
749
1047
|
if (!acc.includes(action.contentType)) {
|
|
@@ -760,191 +1058,37 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
760
1058
|
);
|
|
761
1059
|
return contentTypeModelsMap;
|
|
762
1060
|
},
|
|
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;
|
|
1061
|
+
async countActions(query) {
|
|
1062
|
+
const dbQuery = strapi2.get("query-params").transform(RELEASE_ACTION_MODEL_UID, query ?? {});
|
|
1063
|
+
return strapi2.db.query(RELEASE_ACTION_MODEL_UID).count(dbQuery);
|
|
805
1064
|
},
|
|
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
|
|
1065
|
+
async update(actionId, releaseId, update) {
|
|
1066
|
+
const action = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).findOne({
|
|
1067
|
+
where: {
|
|
1068
|
+
id: actionId,
|
|
1069
|
+
release: {
|
|
1070
|
+
id: releaseId,
|
|
1071
|
+
releasedAt: {
|
|
1072
|
+
$null: true
|
|
920
1073
|
}
|
|
921
1074
|
}
|
|
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
1075
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
});
|
|
944
|
-
throw error;
|
|
1076
|
+
});
|
|
1077
|
+
if (!action) {
|
|
1078
|
+
throw new utils.errors.NotFoundError(
|
|
1079
|
+
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
1080
|
+
);
|
|
945
1081
|
}
|
|
946
|
-
|
|
947
|
-
|
|
1082
|
+
const actionStatus = update.type === "publish" ? await getDraftEntryValidStatus(
|
|
1083
|
+
{
|
|
1084
|
+
contentType: action.contentType,
|
|
1085
|
+
documentId: action.entryDocumentId,
|
|
1086
|
+
locale: action.locale
|
|
1087
|
+
},
|
|
1088
|
+
{
|
|
1089
|
+
strapi: strapi2
|
|
1090
|
+
}
|
|
1091
|
+
) : true;
|
|
948
1092
|
const updatedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).update({
|
|
949
1093
|
where: {
|
|
950
1094
|
id: actionId,
|
|
@@ -955,16 +1099,15 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
955
1099
|
}
|
|
956
1100
|
}
|
|
957
1101
|
},
|
|
958
|
-
data:
|
|
1102
|
+
data: {
|
|
1103
|
+
...update,
|
|
1104
|
+
isEntryValid: actionStatus
|
|
1105
|
+
}
|
|
959
1106
|
});
|
|
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
|
-
}
|
|
1107
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
965
1108
|
return updatedAction;
|
|
966
1109
|
},
|
|
967
|
-
async
|
|
1110
|
+
async delete(actionId, releaseId) {
|
|
968
1111
|
const deletedAction = await strapi2.db.query(RELEASE_ACTION_MODEL_UID).delete({
|
|
969
1112
|
where: {
|
|
970
1113
|
id: actionId,
|
|
@@ -981,79 +1124,56 @@ const createReleaseService = ({ strapi: strapi2 }) => {
|
|
|
981
1124
|
`Action with id ${actionId} not found in release with id ${releaseId} or it is already published`
|
|
982
1125
|
);
|
|
983
1126
|
}
|
|
984
|
-
|
|
1127
|
+
getService("release", { strapi: strapi2 }).updateReleaseStatus(releaseId);
|
|
985
1128
|
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
1129
|
}
|
|
1030
1130
|
};
|
|
1031
1131
|
};
|
|
1132
|
+
class AlreadyOnReleaseError extends utils.errors.ApplicationError {
|
|
1133
|
+
constructor(message) {
|
|
1134
|
+
super(message);
|
|
1135
|
+
this.name = "AlreadyOnReleaseError";
|
|
1136
|
+
}
|
|
1137
|
+
}
|
|
1032
1138
|
const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
1033
1139
|
async validateUniqueEntry(releaseId, releaseActionArgs) {
|
|
1034
|
-
const release2 = await strapi2.
|
|
1035
|
-
|
|
1140
|
+
const release2 = await strapi2.db.query(RELEASE_MODEL_UID).findOne({
|
|
1141
|
+
where: {
|
|
1142
|
+
id: releaseId
|
|
1143
|
+
},
|
|
1144
|
+
populate: {
|
|
1145
|
+
actions: true
|
|
1146
|
+
}
|
|
1036
1147
|
});
|
|
1037
1148
|
if (!release2) {
|
|
1038
1149
|
throw new utils.errors.NotFoundError(`No release found for id ${releaseId}`);
|
|
1039
1150
|
}
|
|
1040
1151
|
const isEntryInRelease = release2.actions.some(
|
|
1041
|
-
(action) =>
|
|
1152
|
+
(action) => action.entryDocumentId === releaseActionArgs.entryDocumentId && action.contentType === releaseActionArgs.contentType && (releaseActionArgs.locale ? action.locale === releaseActionArgs.locale : true)
|
|
1042
1153
|
);
|
|
1043
1154
|
if (isEntryInRelease) {
|
|
1044
|
-
throw new
|
|
1045
|
-
`Entry with
|
|
1155
|
+
throw new AlreadyOnReleaseError(
|
|
1156
|
+
`Entry with documentId ${releaseActionArgs.entryDocumentId}${releaseActionArgs.locale ? `( ${releaseActionArgs.locale})` : ""} and contentType ${releaseActionArgs.contentType} already exists in release with id ${releaseId}`
|
|
1046
1157
|
);
|
|
1047
1158
|
}
|
|
1048
1159
|
},
|
|
1049
|
-
|
|
1160
|
+
validateEntryData(contentTypeUid, entryDocumentId) {
|
|
1050
1161
|
const contentType = strapi2.contentType(contentTypeUid);
|
|
1051
1162
|
if (!contentType) {
|
|
1052
1163
|
throw new utils.errors.NotFoundError(`No content type found for uid ${contentTypeUid}`);
|
|
1053
1164
|
}
|
|
1165
|
+
if (!utils.contentTypes.hasDraftAndPublish(contentType)) {
|
|
1166
|
+
throw new utils.errors.ValidationError(
|
|
1167
|
+
`Content type with uid ${contentTypeUid} does not have draftAndPublish enabled`
|
|
1168
|
+
);
|
|
1169
|
+
}
|
|
1170
|
+
if (contentType.kind === "collectionType" && !entryDocumentId) {
|
|
1171
|
+
throw new utils.errors.ValidationError("Document id is required for collection type");
|
|
1172
|
+
}
|
|
1054
1173
|
},
|
|
1055
1174
|
async validatePendingReleasesLimit() {
|
|
1056
|
-
const
|
|
1175
|
+
const featureCfg = strapi2.ee.features.get("cms-content-releases");
|
|
1176
|
+
const maximumPendingReleases = typeof featureCfg === "object" && featureCfg?.options?.maximumReleases || 3;
|
|
1057
1177
|
const [, pendingReleasesCount] = await strapi2.db.query(RELEASE_MODEL_UID).findWithCount({
|
|
1058
1178
|
filters: {
|
|
1059
1179
|
releasedAt: {
|
|
@@ -1066,8 +1186,8 @@ const createReleaseValidationService = ({ strapi: strapi2 }) => ({
|
|
|
1066
1186
|
}
|
|
1067
1187
|
},
|
|
1068
1188
|
async validateUniqueNameForPendingRelease(name, id) {
|
|
1069
|
-
const pendingReleases = await strapi2.
|
|
1070
|
-
|
|
1189
|
+
const pendingReleases = await strapi2.db.query(RELEASE_MODEL_UID).findMany({
|
|
1190
|
+
where: {
|
|
1071
1191
|
releasedAt: {
|
|
1072
1192
|
$null: true
|
|
1073
1193
|
},
|
|
@@ -1096,7 +1216,7 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1096
1216
|
}
|
|
1097
1217
|
const job = nodeSchedule.scheduleJob(scheduleDate, async () => {
|
|
1098
1218
|
try {
|
|
1099
|
-
await getService("release").publish(releaseId);
|
|
1219
|
+
await getService("release", { strapi: strapi2 }).publish(releaseId);
|
|
1100
1220
|
} catch (error) {
|
|
1101
1221
|
}
|
|
1102
1222
|
this.cancel(releaseId);
|
|
@@ -1138,85 +1258,172 @@ const createSchedulingService = ({ strapi: strapi2 }) => {
|
|
|
1138
1258
|
}
|
|
1139
1259
|
};
|
|
1140
1260
|
};
|
|
1261
|
+
const DEFAULT_SETTINGS = {
|
|
1262
|
+
defaultTimezone: null
|
|
1263
|
+
};
|
|
1264
|
+
const createSettingsService = ({ strapi: strapi2 }) => {
|
|
1265
|
+
const getStore = async () => strapi2.store({ type: "core", name: "content-releases" });
|
|
1266
|
+
return {
|
|
1267
|
+
async update({ settings: settings2 }) {
|
|
1268
|
+
const store = await getStore();
|
|
1269
|
+
store.set({ key: "settings", value: settings2 });
|
|
1270
|
+
return settings2;
|
|
1271
|
+
},
|
|
1272
|
+
async find() {
|
|
1273
|
+
const store = await getStore();
|
|
1274
|
+
const settings2 = await store.get({ key: "settings" });
|
|
1275
|
+
return {
|
|
1276
|
+
...DEFAULT_SETTINGS,
|
|
1277
|
+
...settings2 || {}
|
|
1278
|
+
};
|
|
1279
|
+
}
|
|
1280
|
+
};
|
|
1281
|
+
};
|
|
1141
1282
|
const services = {
|
|
1142
1283
|
release: createReleaseService,
|
|
1284
|
+
"release-action": createReleaseActionService,
|
|
1143
1285
|
"release-validation": createReleaseValidationService,
|
|
1144
|
-
|
|
1286
|
+
scheduling: createSchedulingService,
|
|
1287
|
+
settings: createSettingsService
|
|
1145
1288
|
};
|
|
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()
|
|
1289
|
+
const RELEASE_SCHEMA = utils.yup.object().shape({
|
|
1290
|
+
name: utils.yup.string().trim().required(),
|
|
1291
|
+
scheduledAt: utils.yup.string().nullable(),
|
|
1292
|
+
timezone: utils.yup.string().when("scheduledAt", {
|
|
1293
|
+
is: (value) => value !== null && value !== void 0,
|
|
1294
|
+
then: utils.yup.string().required(),
|
|
1295
|
+
otherwise: utils.yup.string().nullable()
|
|
1164
1296
|
})
|
|
1165
1297
|
}).required().noUnknown();
|
|
1298
|
+
const FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA = utils.yup.object().shape({
|
|
1299
|
+
contentType: utils.yup.string().required(),
|
|
1300
|
+
entryDocumentId: utils.yup.string().nullable(),
|
|
1301
|
+
hasEntryAttached: utils.yup.string().nullable(),
|
|
1302
|
+
locale: utils.yup.string().nullable()
|
|
1303
|
+
}).required().noUnknown();
|
|
1166
1304
|
const validateRelease = utils.validateYupSchema(RELEASE_SCHEMA);
|
|
1305
|
+
const validatefindByDocumentAttachedParams = utils.validateYupSchema(
|
|
1306
|
+
FIND_BY_DOCUMENT_ATTACHED_PARAMS_SCHEMA
|
|
1307
|
+
);
|
|
1167
1308
|
const releaseController = {
|
|
1168
|
-
|
|
1169
|
-
|
|
1309
|
+
/**
|
|
1310
|
+
* Find releases based on documents attached or not to the release.
|
|
1311
|
+
* If `hasEntryAttached` is true, it will return all releases that have the entry attached.
|
|
1312
|
+
* If `hasEntryAttached` is false, it will return all releases that don't have the entry attached.
|
|
1313
|
+
*/
|
|
1314
|
+
async findByDocumentAttached(ctx) {
|
|
1315
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1170
1316
|
ability: ctx.state.userAbility,
|
|
1171
1317
|
model: RELEASE_MODEL_UID
|
|
1172
1318
|
});
|
|
1173
1319
|
await permissionsManager.validateQuery(ctx.query);
|
|
1174
1320
|
const releaseService = getService("release", { strapi });
|
|
1175
|
-
const
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
const
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1321
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1322
|
+
await validatefindByDocumentAttachedParams(query);
|
|
1323
|
+
const model = strapi.getModel(query.contentType);
|
|
1324
|
+
if (model.kind && model.kind === "singleType") {
|
|
1325
|
+
const document = await strapi.db.query(model.uid).findOne({ select: ["documentId"] });
|
|
1326
|
+
if (!document) {
|
|
1327
|
+
throw new utils.errors.NotFoundError(`No entry found for contentType ${query.contentType}`);
|
|
1328
|
+
}
|
|
1329
|
+
query.entryDocumentId = document.documentId;
|
|
1330
|
+
}
|
|
1331
|
+
const { contentType, hasEntryAttached, entryDocumentId, locale } = query;
|
|
1332
|
+
const isEntryAttached = typeof hasEntryAttached === "string" ? Boolean(JSON.parse(hasEntryAttached)) : false;
|
|
1333
|
+
if (isEntryAttached) {
|
|
1334
|
+
const releases = await releaseService.findMany({
|
|
1335
|
+
where: {
|
|
1336
|
+
releasedAt: null,
|
|
1190
1337
|
actions: {
|
|
1191
|
-
|
|
1192
|
-
|
|
1338
|
+
contentType,
|
|
1339
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1340
|
+
locale: locale ?? null
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
populate: {
|
|
1344
|
+
actions: {
|
|
1345
|
+
fields: ["type"],
|
|
1346
|
+
filters: {
|
|
1347
|
+
contentType,
|
|
1348
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1349
|
+
locale: locale ?? null
|
|
1193
1350
|
}
|
|
1194
1351
|
}
|
|
1195
|
-
}
|
|
1352
|
+
}
|
|
1353
|
+
});
|
|
1354
|
+
ctx.body = { data: releases };
|
|
1355
|
+
} else {
|
|
1356
|
+
const relatedReleases = await releaseService.findMany({
|
|
1357
|
+
where: {
|
|
1358
|
+
releasedAt: null,
|
|
1359
|
+
actions: {
|
|
1360
|
+
contentType,
|
|
1361
|
+
entryDocumentId: entryDocumentId ?? null,
|
|
1362
|
+
locale: locale ?? null
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1196
1365
|
});
|
|
1197
|
-
const
|
|
1366
|
+
const releases = await releaseService.findMany({
|
|
1198
1367
|
where: {
|
|
1368
|
+
$or: [
|
|
1369
|
+
{
|
|
1370
|
+
id: {
|
|
1371
|
+
$notIn: relatedReleases.map((release2) => release2.id)
|
|
1372
|
+
}
|
|
1373
|
+
},
|
|
1374
|
+
{
|
|
1375
|
+
actions: null
|
|
1376
|
+
}
|
|
1377
|
+
],
|
|
1199
1378
|
releasedAt: null
|
|
1200
1379
|
}
|
|
1201
1380
|
});
|
|
1202
|
-
ctx.body = { data
|
|
1381
|
+
ctx.body = { data: releases };
|
|
1203
1382
|
}
|
|
1204
1383
|
},
|
|
1384
|
+
async findPage(ctx) {
|
|
1385
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1386
|
+
ability: ctx.state.userAbility,
|
|
1387
|
+
model: RELEASE_MODEL_UID
|
|
1388
|
+
});
|
|
1389
|
+
await permissionsManager.validateQuery(ctx.query);
|
|
1390
|
+
const releaseService = getService("release", { strapi });
|
|
1391
|
+
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1392
|
+
const { results, pagination } = await releaseService.findPage(query);
|
|
1393
|
+
const data = results.map((release2) => {
|
|
1394
|
+
const { actions, ...releaseData } = release2;
|
|
1395
|
+
return {
|
|
1396
|
+
...releaseData,
|
|
1397
|
+
actions: {
|
|
1398
|
+
meta: {
|
|
1399
|
+
count: actions.count
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
});
|
|
1404
|
+
const pendingReleasesCount = await strapi.db.query(RELEASE_MODEL_UID).count({
|
|
1405
|
+
where: {
|
|
1406
|
+
releasedAt: null
|
|
1407
|
+
}
|
|
1408
|
+
});
|
|
1409
|
+
ctx.body = { data, meta: { pagination, pendingReleasesCount } };
|
|
1410
|
+
},
|
|
1205
1411
|
async findOne(ctx) {
|
|
1206
1412
|
const id = ctx.params.id;
|
|
1207
1413
|
const releaseService = getService("release", { strapi });
|
|
1414
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1208
1415
|
const release2 = await releaseService.findOne(id, { populate: ["createdBy"] });
|
|
1209
1416
|
if (!release2) {
|
|
1210
1417
|
throw new utils.errors.NotFoundError(`Release not found for id: ${id}`);
|
|
1211
1418
|
}
|
|
1212
|
-
const count = await
|
|
1419
|
+
const count = await releaseActionService.countActions({
|
|
1213
1420
|
filters: {
|
|
1214
1421
|
release: id
|
|
1215
1422
|
}
|
|
1216
1423
|
});
|
|
1217
1424
|
const sanitizedRelease = {
|
|
1218
1425
|
...release2,
|
|
1219
|
-
createdBy: release2.createdBy ? strapi.admin
|
|
1426
|
+
createdBy: release2.createdBy ? strapi.service("admin::user").sanitizeUser(release2.createdBy) : null
|
|
1220
1427
|
};
|
|
1221
1428
|
const data = {
|
|
1222
1429
|
...sanitizedRelease,
|
|
@@ -1228,19 +1435,63 @@ const releaseController = {
|
|
|
1228
1435
|
};
|
|
1229
1436
|
ctx.body = { data };
|
|
1230
1437
|
},
|
|
1438
|
+
async mapEntriesToReleases(ctx) {
|
|
1439
|
+
const { contentTypeUid, documentIds, locale } = ctx.query;
|
|
1440
|
+
if (!contentTypeUid || !documentIds) {
|
|
1441
|
+
throw new utils.errors.ValidationError("Missing required query parameters");
|
|
1442
|
+
}
|
|
1443
|
+
const releaseService = getService("release", { strapi });
|
|
1444
|
+
const releasesWithActions = await releaseService.findMany({
|
|
1445
|
+
where: {
|
|
1446
|
+
releasedAt: null,
|
|
1447
|
+
actions: {
|
|
1448
|
+
contentType: contentTypeUid,
|
|
1449
|
+
entryDocumentId: {
|
|
1450
|
+
$in: documentIds
|
|
1451
|
+
},
|
|
1452
|
+
locale
|
|
1453
|
+
}
|
|
1454
|
+
},
|
|
1455
|
+
populate: {
|
|
1456
|
+
actions: true
|
|
1457
|
+
}
|
|
1458
|
+
});
|
|
1459
|
+
const mappedEntriesInReleases = releasesWithActions.reduce(
|
|
1460
|
+
(acc, release2) => {
|
|
1461
|
+
release2.actions.forEach((action) => {
|
|
1462
|
+
if (action.contentType !== contentTypeUid) {
|
|
1463
|
+
return;
|
|
1464
|
+
}
|
|
1465
|
+
if (locale && action.locale !== locale) {
|
|
1466
|
+
return;
|
|
1467
|
+
}
|
|
1468
|
+
if (!acc[action.entryDocumentId]) {
|
|
1469
|
+
acc[action.entryDocumentId] = [{ id: release2.id, name: release2.name }];
|
|
1470
|
+
} else {
|
|
1471
|
+
acc[action.entryDocumentId].push({ id: release2.id, name: release2.name });
|
|
1472
|
+
}
|
|
1473
|
+
});
|
|
1474
|
+
return acc;
|
|
1475
|
+
},
|
|
1476
|
+
{}
|
|
1477
|
+
);
|
|
1478
|
+
ctx.body = {
|
|
1479
|
+
data: mappedEntriesInReleases
|
|
1480
|
+
};
|
|
1481
|
+
},
|
|
1231
1482
|
async create(ctx) {
|
|
1232
1483
|
const user = ctx.state.user;
|
|
1233
1484
|
const releaseArgs = ctx.request.body;
|
|
1234
1485
|
await validateRelease(releaseArgs);
|
|
1235
1486
|
const releaseService = getService("release", { strapi });
|
|
1236
1487
|
const release2 = await releaseService.create(releaseArgs, { user });
|
|
1237
|
-
const permissionsManager = strapi.admin
|
|
1488
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1238
1489
|
ability: ctx.state.userAbility,
|
|
1239
1490
|
model: RELEASE_MODEL_UID
|
|
1240
1491
|
});
|
|
1241
|
-
ctx.
|
|
1492
|
+
ctx.created({
|
|
1242
1493
|
data: await permissionsManager.sanitizeOutput(release2)
|
|
1243
|
-
};
|
|
1494
|
+
});
|
|
1244
1495
|
},
|
|
1245
1496
|
async update(ctx) {
|
|
1246
1497
|
const user = ctx.state.user;
|
|
@@ -1249,7 +1500,7 @@ const releaseController = {
|
|
|
1249
1500
|
await validateRelease(releaseArgs);
|
|
1250
1501
|
const releaseService = getService("release", { strapi });
|
|
1251
1502
|
const release2 = await releaseService.update(id, releaseArgs, { user });
|
|
1252
|
-
const permissionsManager = strapi.admin
|
|
1503
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1253
1504
|
ability: ctx.state.userAbility,
|
|
1254
1505
|
model: RELEASE_MODEL_UID
|
|
1255
1506
|
});
|
|
@@ -1266,18 +1517,18 @@ const releaseController = {
|
|
|
1266
1517
|
};
|
|
1267
1518
|
},
|
|
1268
1519
|
async publish(ctx) {
|
|
1269
|
-
const user = ctx.state.user;
|
|
1270
1520
|
const id = ctx.params.id;
|
|
1271
1521
|
const releaseService = getService("release", { strapi });
|
|
1272
|
-
const
|
|
1522
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1523
|
+
const release2 = await releaseService.publish(id);
|
|
1273
1524
|
const [countPublishActions, countUnpublishActions] = await Promise.all([
|
|
1274
|
-
|
|
1525
|
+
releaseActionService.countActions({
|
|
1275
1526
|
filters: {
|
|
1276
1527
|
release: id,
|
|
1277
1528
|
type: "publish"
|
|
1278
1529
|
}
|
|
1279
1530
|
}),
|
|
1280
|
-
|
|
1531
|
+
releaseActionService.countActions({
|
|
1281
1532
|
filters: {
|
|
1282
1533
|
release: id,
|
|
1283
1534
|
type: "unpublish"
|
|
@@ -1295,45 +1546,93 @@ const releaseController = {
|
|
|
1295
1546
|
}
|
|
1296
1547
|
};
|
|
1297
1548
|
const RELEASE_ACTION_SCHEMA = utils.yup.object().shape({
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
}).required(),
|
|
1549
|
+
contentType: utils.yup.string().required(),
|
|
1550
|
+
entryDocumentId: utils.yup.strapiID(),
|
|
1551
|
+
locale: utils.yup.string(),
|
|
1302
1552
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
1303
1553
|
});
|
|
1304
1554
|
const RELEASE_ACTION_UPDATE_SCHEMA = utils.yup.object().shape({
|
|
1305
1555
|
type: utils.yup.string().oneOf(["publish", "unpublish"]).required()
|
|
1306
1556
|
});
|
|
1557
|
+
const FIND_MANY_ACTIONS_PARAMS = utils.yup.object().shape({
|
|
1558
|
+
groupBy: utils.yup.string().oneOf(["action", "contentType", "locale"])
|
|
1559
|
+
});
|
|
1307
1560
|
const validateReleaseAction = utils.validateYupSchema(RELEASE_ACTION_SCHEMA);
|
|
1308
1561
|
const validateReleaseActionUpdateSchema = utils.validateYupSchema(RELEASE_ACTION_UPDATE_SCHEMA);
|
|
1562
|
+
const validateFindManyActionsParams = utils.validateYupSchema(FIND_MANY_ACTIONS_PARAMS);
|
|
1309
1563
|
const releaseActionController = {
|
|
1310
1564
|
async create(ctx) {
|
|
1311
1565
|
const releaseId = ctx.params.releaseId;
|
|
1312
1566
|
const releaseActionArgs = ctx.request.body;
|
|
1313
1567
|
await validateReleaseAction(releaseActionArgs);
|
|
1314
|
-
const
|
|
1315
|
-
const releaseAction2 = await
|
|
1316
|
-
ctx.
|
|
1568
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1569
|
+
const releaseAction2 = await releaseActionService.create(releaseId, releaseActionArgs);
|
|
1570
|
+
ctx.created({
|
|
1317
1571
|
data: releaseAction2
|
|
1318
|
-
};
|
|
1572
|
+
});
|
|
1573
|
+
},
|
|
1574
|
+
async createMany(ctx) {
|
|
1575
|
+
const releaseId = ctx.params.releaseId;
|
|
1576
|
+
const releaseActionsArgs = ctx.request.body;
|
|
1577
|
+
await Promise.all(
|
|
1578
|
+
releaseActionsArgs.map((releaseActionArgs) => validateReleaseAction(releaseActionArgs))
|
|
1579
|
+
);
|
|
1580
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1581
|
+
const releaseService = getService("release", { strapi });
|
|
1582
|
+
const releaseActions = await strapi.db.transaction(async () => {
|
|
1583
|
+
const releaseActions2 = await Promise.all(
|
|
1584
|
+
releaseActionsArgs.map(async (releaseActionArgs) => {
|
|
1585
|
+
try {
|
|
1586
|
+
const action = await releaseActionService.create(releaseId, releaseActionArgs, {
|
|
1587
|
+
disableUpdateReleaseStatus: true
|
|
1588
|
+
});
|
|
1589
|
+
return action;
|
|
1590
|
+
} catch (error) {
|
|
1591
|
+
if (error instanceof AlreadyOnReleaseError) {
|
|
1592
|
+
return null;
|
|
1593
|
+
}
|
|
1594
|
+
throw error;
|
|
1595
|
+
}
|
|
1596
|
+
})
|
|
1597
|
+
);
|
|
1598
|
+
return releaseActions2;
|
|
1599
|
+
});
|
|
1600
|
+
const newReleaseActions = releaseActions.filter((action) => action !== null);
|
|
1601
|
+
if (newReleaseActions.length > 0) {
|
|
1602
|
+
releaseService.updateReleaseStatus(releaseId);
|
|
1603
|
+
}
|
|
1604
|
+
ctx.created({
|
|
1605
|
+
data: newReleaseActions,
|
|
1606
|
+
meta: {
|
|
1607
|
+
entriesAlreadyInRelease: releaseActions.length - newReleaseActions.length,
|
|
1608
|
+
totalEntries: releaseActions.length
|
|
1609
|
+
}
|
|
1610
|
+
});
|
|
1319
1611
|
},
|
|
1320
1612
|
async findMany(ctx) {
|
|
1321
1613
|
const releaseId = ctx.params.releaseId;
|
|
1322
|
-
const permissionsManager = strapi.admin
|
|
1614
|
+
const permissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1323
1615
|
ability: ctx.state.userAbility,
|
|
1324
1616
|
model: RELEASE_ACTION_MODEL_UID
|
|
1325
1617
|
});
|
|
1618
|
+
await validateFindManyActionsParams(ctx.query);
|
|
1619
|
+
if (ctx.query.groupBy) {
|
|
1620
|
+
if (!["action", "contentType", "locale"].includes(ctx.query.groupBy)) {
|
|
1621
|
+
ctx.badRequest("Invalid groupBy parameter");
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
ctx.query.sort = ctx.query.groupBy === "action" ? "type" : ctx.query.groupBy;
|
|
1625
|
+
delete ctx.query.groupBy;
|
|
1326
1626
|
const query = await permissionsManager.sanitizeQuery(ctx.query);
|
|
1327
|
-
const
|
|
1328
|
-
const { results, pagination } = await
|
|
1329
|
-
sort: query.groupBy === "action" ? "type" : query.groupBy,
|
|
1627
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1628
|
+
const { results, pagination } = await releaseActionService.findPage(releaseId, {
|
|
1330
1629
|
...query
|
|
1331
1630
|
});
|
|
1332
1631
|
const contentTypeOutputSanitizers = results.reduce((acc, action) => {
|
|
1333
1632
|
if (acc[action.contentType]) {
|
|
1334
1633
|
return acc;
|
|
1335
1634
|
}
|
|
1336
|
-
const contentTypePermissionsManager = strapi.admin
|
|
1635
|
+
const contentTypePermissionsManager = strapi.service("admin::permission").createPermissionsManager({
|
|
1337
1636
|
ability: ctx.state.userAbility,
|
|
1338
1637
|
model: action.contentType
|
|
1339
1638
|
});
|
|
@@ -1342,10 +1641,11 @@ const releaseActionController = {
|
|
|
1342
1641
|
}, {});
|
|
1343
1642
|
const sanitizedResults = await utils.async.map(results, async (action) => ({
|
|
1344
1643
|
...action,
|
|
1345
|
-
entry: await contentTypeOutputSanitizers[action.contentType](action.entry)
|
|
1644
|
+
entry: action.entry ? await contentTypeOutputSanitizers[action.contentType](action.entry) : {}
|
|
1346
1645
|
}));
|
|
1347
|
-
const groupedData = await
|
|
1348
|
-
const contentTypes2 =
|
|
1646
|
+
const groupedData = await releaseActionService.groupActions(sanitizedResults, query.sort);
|
|
1647
|
+
const contentTypes2 = releaseActionService.getContentTypeModelsFromActions(results);
|
|
1648
|
+
const releaseService = getService("release", { strapi });
|
|
1349
1649
|
const components = await releaseService.getAllComponents();
|
|
1350
1650
|
ctx.body = {
|
|
1351
1651
|
data: groupedData,
|
|
@@ -1361,8 +1661,8 @@ const releaseActionController = {
|
|
|
1361
1661
|
const releaseId = ctx.params.releaseId;
|
|
1362
1662
|
const releaseActionUpdateArgs = ctx.request.body;
|
|
1363
1663
|
await validateReleaseActionUpdateSchema(releaseActionUpdateArgs);
|
|
1364
|
-
const
|
|
1365
|
-
const updatedAction = await
|
|
1664
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1665
|
+
const updatedAction = await releaseActionService.update(
|
|
1366
1666
|
actionId,
|
|
1367
1667
|
releaseId,
|
|
1368
1668
|
releaseActionUpdateArgs
|
|
@@ -1374,17 +1674,71 @@ const releaseActionController = {
|
|
|
1374
1674
|
async delete(ctx) {
|
|
1375
1675
|
const actionId = ctx.params.actionId;
|
|
1376
1676
|
const releaseId = ctx.params.releaseId;
|
|
1377
|
-
const
|
|
1378
|
-
const deletedReleaseAction = await
|
|
1677
|
+
const releaseActionService = getService("release-action", { strapi });
|
|
1678
|
+
const deletedReleaseAction = await releaseActionService.delete(actionId, releaseId);
|
|
1379
1679
|
ctx.body = {
|
|
1380
1680
|
data: deletedReleaseAction
|
|
1381
1681
|
};
|
|
1382
1682
|
}
|
|
1383
1683
|
};
|
|
1384
|
-
const
|
|
1684
|
+
const SETTINGS_SCHEMA = yup__namespace.object().shape({
|
|
1685
|
+
defaultTimezone: yup__namespace.string().nullable().default(null)
|
|
1686
|
+
}).required().noUnknown();
|
|
1687
|
+
const validateSettings = utils.validateYupSchema(SETTINGS_SCHEMA);
|
|
1688
|
+
const settingsController = {
|
|
1689
|
+
async find(ctx) {
|
|
1690
|
+
const settingsService = getService("settings", { strapi });
|
|
1691
|
+
const settings2 = await settingsService.find();
|
|
1692
|
+
ctx.body = { data: settings2 };
|
|
1693
|
+
},
|
|
1694
|
+
async update(ctx) {
|
|
1695
|
+
const settingsBody = ctx.request.body;
|
|
1696
|
+
const settings2 = await validateSettings(settingsBody);
|
|
1697
|
+
const settingsService = getService("settings", { strapi });
|
|
1698
|
+
const updatedSettings = await settingsService.update({ settings: settings2 });
|
|
1699
|
+
ctx.body = { data: updatedSettings };
|
|
1700
|
+
}
|
|
1701
|
+
};
|
|
1702
|
+
const controllers = {
|
|
1703
|
+
release: releaseController,
|
|
1704
|
+
"release-action": releaseActionController,
|
|
1705
|
+
settings: settingsController
|
|
1706
|
+
};
|
|
1385
1707
|
const release = {
|
|
1386
1708
|
type: "admin",
|
|
1387
1709
|
routes: [
|
|
1710
|
+
{
|
|
1711
|
+
method: "GET",
|
|
1712
|
+
path: "/mapEntriesToReleases",
|
|
1713
|
+
handler: "release.mapEntriesToReleases",
|
|
1714
|
+
config: {
|
|
1715
|
+
policies: [
|
|
1716
|
+
"admin::isAuthenticatedAdmin",
|
|
1717
|
+
{
|
|
1718
|
+
name: "admin::hasPermissions",
|
|
1719
|
+
config: {
|
|
1720
|
+
actions: ["plugin::content-releases.read"]
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
]
|
|
1724
|
+
}
|
|
1725
|
+
},
|
|
1726
|
+
{
|
|
1727
|
+
method: "GET",
|
|
1728
|
+
path: "/getByDocumentAttached",
|
|
1729
|
+
handler: "release.findByDocumentAttached",
|
|
1730
|
+
config: {
|
|
1731
|
+
policies: [
|
|
1732
|
+
"admin::isAuthenticatedAdmin",
|
|
1733
|
+
{
|
|
1734
|
+
name: "admin::hasPermissions",
|
|
1735
|
+
config: {
|
|
1736
|
+
actions: ["plugin::content-releases.read"]
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
]
|
|
1740
|
+
}
|
|
1741
|
+
},
|
|
1388
1742
|
{
|
|
1389
1743
|
method: "POST",
|
|
1390
1744
|
path: "/",
|
|
@@ -1404,7 +1758,7 @@ const release = {
|
|
|
1404
1758
|
{
|
|
1405
1759
|
method: "GET",
|
|
1406
1760
|
path: "/",
|
|
1407
|
-
handler: "release.
|
|
1761
|
+
handler: "release.findPage",
|
|
1408
1762
|
config: {
|
|
1409
1763
|
policies: [
|
|
1410
1764
|
"admin::isAuthenticatedAdmin",
|
|
@@ -1502,6 +1856,22 @@ const releaseAction = {
|
|
|
1502
1856
|
]
|
|
1503
1857
|
}
|
|
1504
1858
|
},
|
|
1859
|
+
{
|
|
1860
|
+
method: "POST",
|
|
1861
|
+
path: "/:releaseId/actions/bulk",
|
|
1862
|
+
handler: "release-action.createMany",
|
|
1863
|
+
config: {
|
|
1864
|
+
policies: [
|
|
1865
|
+
"admin::isAuthenticatedAdmin",
|
|
1866
|
+
{
|
|
1867
|
+
name: "admin::hasPermissions",
|
|
1868
|
+
config: {
|
|
1869
|
+
actions: ["plugin::content-releases.create-action"]
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
]
|
|
1873
|
+
}
|
|
1874
|
+
},
|
|
1505
1875
|
{
|
|
1506
1876
|
method: "GET",
|
|
1507
1877
|
path: "/:releaseId/actions",
|
|
@@ -1552,7 +1922,45 @@ const releaseAction = {
|
|
|
1552
1922
|
}
|
|
1553
1923
|
]
|
|
1554
1924
|
};
|
|
1925
|
+
const settings = {
|
|
1926
|
+
type: "admin",
|
|
1927
|
+
routes: [
|
|
1928
|
+
{
|
|
1929
|
+
method: "GET",
|
|
1930
|
+
path: "/settings",
|
|
1931
|
+
handler: "settings.find",
|
|
1932
|
+
config: {
|
|
1933
|
+
policies: [
|
|
1934
|
+
"admin::isAuthenticatedAdmin",
|
|
1935
|
+
{
|
|
1936
|
+
name: "admin::hasPermissions",
|
|
1937
|
+
config: {
|
|
1938
|
+
actions: ["plugin::content-releases.settings.read"]
|
|
1939
|
+
}
|
|
1940
|
+
}
|
|
1941
|
+
]
|
|
1942
|
+
}
|
|
1943
|
+
},
|
|
1944
|
+
{
|
|
1945
|
+
method: "PUT",
|
|
1946
|
+
path: "/settings",
|
|
1947
|
+
handler: "settings.update",
|
|
1948
|
+
config: {
|
|
1949
|
+
policies: [
|
|
1950
|
+
"admin::isAuthenticatedAdmin",
|
|
1951
|
+
{
|
|
1952
|
+
name: "admin::hasPermissions",
|
|
1953
|
+
config: {
|
|
1954
|
+
actions: ["plugin::content-releases.settings.update"]
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
]
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
]
|
|
1961
|
+
};
|
|
1555
1962
|
const routes = {
|
|
1963
|
+
settings,
|
|
1556
1964
|
release,
|
|
1557
1965
|
"release-action": releaseAction
|
|
1558
1966
|
};
|
|
@@ -1569,6 +1977,9 @@ const getPlugin = () => {
|
|
|
1569
1977
|
};
|
|
1570
1978
|
}
|
|
1571
1979
|
return {
|
|
1980
|
+
// Always return register, it handles its own feature check
|
|
1981
|
+
register,
|
|
1982
|
+
// Always return contentTypes to avoid losing data when the feature is disabled
|
|
1572
1983
|
contentTypes
|
|
1573
1984
|
};
|
|
1574
1985
|
};
|