@strapi/content-manager 0.0.0-experimental.a65a85fdea97faae8679d3ffc5f9d79af61abd26 → 0.0.0-experimental.abc
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 +18 -3
- package/dist/_chunks/{CardDragPreview-DSVYodBX.js → CardDragPreview-C0QyJgRA.js} +10 -14
- package/dist/_chunks/CardDragPreview-C0QyJgRA.js.map +1 -0
- package/dist/_chunks/{CardDragPreview-ikSG4M46.mjs → CardDragPreview-DOxamsuj.mjs} +7 -9
- package/dist/_chunks/CardDragPreview-DOxamsuj.mjs.map +1 -0
- package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs → ComponentConfigurationPage-B3yDbeU1.mjs} +3 -3
- package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs.map → ComponentConfigurationPage-B3yDbeU1.mjs.map} +1 -1
- package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js → ComponentConfigurationPage-KXSuLnQD.js} +3 -3
- package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js.map → ComponentConfigurationPage-KXSuLnQD.js.map} +1 -1
- package/dist/_chunks/{ComponentIcon-BBQsYCVn.js → ComponentIcon-BXdiCGQp.js} +8 -2
- package/dist/_chunks/ComponentIcon-BXdiCGQp.js.map +1 -0
- package/dist/_chunks/{ComponentIcon-BOFnK76n.mjs → ComponentIcon-u4bIXTFY.mjs} +9 -3
- package/dist/_chunks/ComponentIcon-u4bIXTFY.mjs.map +1 -0
- package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js → EditConfigurationPage-BQ17--5R.js} +3 -3
- package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js.map → EditConfigurationPage-BQ17--5R.js.map} +1 -1
- package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs → EditConfigurationPage-D7PrLO8j.mjs} +3 -3
- package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs.map → EditConfigurationPage-D7PrLO8j.mjs.map} +1 -1
- package/dist/_chunks/{EditViewPage-Bm8lgcm6.mjs → EditViewPage-B7VgwJaG.mjs} +58 -47
- package/dist/_chunks/EditViewPage-B7VgwJaG.mjs.map +1 -0
- package/dist/_chunks/{EditViewPage-CzOT5Kpj.js → EditViewPage-BgjdnGz2.js} +57 -48
- package/dist/_chunks/EditViewPage-BgjdnGz2.js.map +1 -0
- package/dist/_chunks/{Field-Caef4JjM.js → Field-CdK7ZLmv.js} +1030 -800
- package/dist/_chunks/Field-CdK7ZLmv.js.map +1 -0
- package/dist/_chunks/{Field-Dlh0uGnL.mjs → Field-tHCw4lGA.mjs} +981 -750
- package/dist/_chunks/Field-tHCw4lGA.mjs.map +1 -0
- package/dist/_chunks/{Form-EnaQL_6L.mjs → Form-BJxdTv3Q.mjs} +56 -43
- package/dist/_chunks/Form-BJxdTv3Q.mjs.map +1 -0
- package/dist/_chunks/{Form-BzuAjtRq.js → Form-C_0KTVvV.js} +55 -43
- package/dist/_chunks/Form-C_0KTVvV.js.map +1 -0
- package/dist/_chunks/{History-D6sbCJvo.mjs → History-DR2txJLE.mjs} +151 -57
- package/dist/_chunks/History-DR2txJLE.mjs.map +1 -0
- package/dist/_chunks/{History-C17LiyRg.js → History-nuEzM5qm.js} +151 -58
- package/dist/_chunks/History-nuEzM5qm.js.map +1 -0
- package/dist/_chunks/{ListConfigurationPage-Dks5SX6f.js → ListConfigurationPage-CnB86Psm.js} +70 -61
- package/dist/_chunks/ListConfigurationPage-CnB86Psm.js.map +1 -0
- package/dist/_chunks/{ListConfigurationPage-Ce4qs7qE.mjs → ListConfigurationPage-voFVtXu6.mjs} +67 -57
- package/dist/_chunks/ListConfigurationPage-voFVtXu6.mjs.map +1 -0
- package/dist/_chunks/{ListViewPage-Be7S5aKL.mjs → ListViewPage-B_GaWgRH.mjs} +95 -106
- package/dist/_chunks/ListViewPage-B_GaWgRH.mjs.map +1 -0
- package/dist/_chunks/{ListViewPage-BwrZrPsh.js → ListViewPage-SXIXm-RM.js} +100 -111
- package/dist/_chunks/ListViewPage-SXIXm-RM.js.map +1 -0
- package/dist/_chunks/{NoContentTypePage-Cu5r1-JT.js → NoContentTypePage-BzsQ3hLZ.js} +5 -5
- package/dist/_chunks/NoContentTypePage-BzsQ3hLZ.js.map +1 -0
- package/dist/_chunks/{NoContentTypePage-CIPmYQMm.mjs → NoContentTypePage-CYiGpsbj.mjs} +7 -7
- package/dist/_chunks/NoContentTypePage-CYiGpsbj.mjs.map +1 -0
- package/dist/_chunks/{NoPermissionsPage-DhJ7LYrr.mjs → NoPermissionsPage-B5baIHal.mjs} +5 -6
- package/dist/_chunks/NoPermissionsPage-B5baIHal.mjs.map +1 -0
- package/dist/_chunks/{NoPermissionsPage-C-j6TEUF.js → NoPermissionsPage-IGkId4C5.js} +4 -5
- package/dist/_chunks/NoPermissionsPage-IGkId4C5.js.map +1 -0
- package/dist/_chunks/{Relations-CY7AtkDA.mjs → Relations-CIYDdKU-.mjs} +67 -57
- package/dist/_chunks/Relations-CIYDdKU-.mjs.map +1 -0
- package/dist/_chunks/{Relations-Czs-uZ-s.js → Relations-Dhuurpx2.js} +71 -62
- package/dist/_chunks/Relations-Dhuurpx2.js.map +1 -0
- package/dist/_chunks/{en-MBPul9Su.mjs → en-BrCTWlZv.mjs} +11 -4
- package/dist/_chunks/{en-MBPul9Su.mjs.map → en-BrCTWlZv.mjs.map} +1 -1
- package/dist/_chunks/{en-C-V1_90f.js → en-uOUIxfcQ.js} +11 -4
- package/dist/_chunks/{en-C-V1_90f.js.map → en-uOUIxfcQ.js.map} +1 -1
- package/dist/_chunks/{index-DNVx8ssZ.mjs → index-C9TJPyni.mjs} +1696 -912
- package/dist/_chunks/index-C9TJPyni.mjs.map +1 -0
- package/dist/_chunks/{index-X_2tafck.js → index-CdT0kHZ8.js} +1626 -843
- package/dist/_chunks/index-CdT0kHZ8.js.map +1 -0
- package/dist/_chunks/{layout-Dnh0PNp9.mjs → layout-BNqvLR_b.mjs} +45 -28
- package/dist/_chunks/layout-BNqvLR_b.mjs.map +1 -0
- package/dist/_chunks/{layout-dBc7wN7L.js → layout-C6dxWYT7.js} +45 -30
- package/dist/_chunks/layout-C6dxWYT7.js.map +1 -0
- package/dist/_chunks/{relations-Dx7tMKJN.mjs → relations-CkKqKw65.mjs} +2 -2
- package/dist/_chunks/{relations-Dx7tMKJN.mjs.map → relations-CkKqKw65.mjs.map} +1 -1
- package/dist/_chunks/{relations-4pHtBrHJ.js → relations-DtFaDnP1.js} +2 -2
- package/dist/_chunks/{relations-4pHtBrHJ.js.map → relations-DtFaDnP1.js.map} +1 -1
- package/dist/_chunks/useDragAndDrop-DdHgKsqq.mjs.map +1 -1
- package/dist/_chunks/useDragAndDrop-J0TUUbR6.js.map +1 -1
- package/dist/_chunks/usePrev-B9w_-eYc.js +15 -0
- package/dist/_chunks/usePrev-B9w_-eYc.js.map +1 -0
- package/dist/_chunks/usePrev-DH6iah0A.mjs +16 -0
- package/dist/_chunks/usePrev-DH6iah0A.mjs.map +1 -0
- package/dist/admin/index.js +2 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +5 -4
- package/dist/admin/src/components/ComponentIcon.d.ts +6 -3
- package/dist/admin/src/content-manager.d.ts +3 -3
- package/dist/admin/src/exports.d.ts +1 -0
- package/dist/admin/src/history/components/VersionInputRenderer.d.ts +1 -1
- package/dist/admin/src/history/index.d.ts +3 -0
- package/dist/admin/src/history/services/historyVersion.d.ts +1 -1
- package/dist/admin/src/hooks/useDocument.d.ts +5 -8
- package/dist/admin/src/hooks/useDocumentActions.d.ts +24 -3
- package/dist/admin/src/hooks/useDocumentLayout.d.ts +2 -2
- package/dist/admin/src/hooks/useDragAndDrop.d.ts +4 -4
- package/dist/admin/src/hooks/useKeyboardDragAndDrop.d.ts +1 -1
- package/dist/admin/src/index.d.ts +1 -0
- package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +11 -4
- package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/BlocksInput.d.ts +3 -3
- package/dist/admin/src/pages/EditView/components/FormInputs/BlocksInput/utils/constants.d.ts +4 -0
- package/dist/admin/src/pages/EditView/components/FormInputs/Component/Input.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/ComponentCategory.d.ts +3 -5
- package/dist/admin/src/pages/EditView/components/FormInputs/DynamicZone/Field.d.ts +1 -1
- package/dist/admin/src/pages/EditView/components/FormInputs/Relations.d.ts +30 -18
- package/dist/admin/src/pages/EditView/components/FormInputs/UID.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/EditorLayout.d.ts +3 -49
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/Field.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygFooter.d.ts +2 -2
- package/dist/admin/src/pages/EditView/components/FormInputs/Wysiwyg/WysiwygStyles.d.ts +48 -53
- package/dist/admin/src/pages/EditView/components/InputRenderer.d.ts +2 -10
- package/dist/admin/src/pages/ListView/components/BulkActions/Actions.d.ts +3 -30
- package/dist/admin/src/pages/ListView/components/BulkActions/ConfirmBulkActionDialog.d.ts +2 -2
- package/dist/admin/src/pages/ListView/components/BulkActions/PublishAction.d.ts +9 -26
- package/dist/admin/src/services/api.d.ts +2 -3
- package/dist/admin/src/services/components.d.ts +2 -2
- package/dist/admin/src/services/contentTypes.d.ts +5 -5
- package/dist/admin/src/services/documents.d.ts +29 -17
- package/dist/admin/src/services/init.d.ts +2 -2
- package/dist/admin/src/services/relations.d.ts +3 -3
- package/dist/admin/src/services/uid.d.ts +3 -3
- package/dist/admin/src/utils/api.d.ts +4 -18
- package/dist/admin/src/utils/validation.d.ts +1 -6
- package/dist/server/index.js +602 -426
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +610 -434
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
- package/dist/server/src/controllers/single-types.d.ts.map +1 -1
- package/dist/server/src/controllers/uid.d.ts.map +1 -1
- package/dist/server/src/controllers/utils/metadata.d.ts +8 -0
- package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/dimensions.d.ts +11 -0
- package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/index.d.ts +1 -1
- package/dist/server/src/history/services/history.d.ts +2 -4
- package/dist/server/src/history/services/history.d.ts.map +1 -1
- package/dist/server/src/history/services/index.d.ts +6 -2
- package/dist/server/src/history/services/index.d.ts.map +1 -1
- package/dist/server/src/history/services/lifecycles.d.ts +9 -0
- package/dist/server/src/history/services/lifecycles.d.ts.map +1 -0
- package/dist/server/src/history/services/utils.d.ts +41 -9
- package/dist/server/src/history/services/utils.d.ts.map +1 -1
- package/dist/server/src/history/utils.d.ts +6 -2
- package/dist/server/src/history/utils.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +18 -39
- package/dist/server/src/index.d.ts.map +1 -1
- package/dist/server/src/services/document-manager.d.ts +13 -12
- package/dist/server/src/services/document-manager.d.ts.map +1 -1
- package/dist/server/src/services/document-metadata.d.ts +8 -29
- package/dist/server/src/services/document-metadata.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +18 -39
- package/dist/server/src/services/index.d.ts.map +1 -1
- package/dist/server/src/services/utils/populate.d.ts +8 -1
- package/dist/server/src/services/utils/populate.d.ts.map +1 -1
- package/dist/shared/contracts/collection-types.d.ts +14 -6
- package/dist/shared/contracts/collection-types.d.ts.map +1 -1
- package/dist/shared/contracts/relations.d.ts +2 -2
- package/dist/shared/contracts/relations.d.ts.map +1 -1
- package/package.json +13 -14
- package/dist/_chunks/CardDragPreview-DSVYodBX.js.map +0 -1
- package/dist/_chunks/CardDragPreview-ikSG4M46.mjs.map +0 -1
- package/dist/_chunks/ComponentIcon-BBQsYCVn.js.map +0 -1
- package/dist/_chunks/ComponentIcon-BOFnK76n.mjs.map +0 -1
- package/dist/_chunks/EditViewPage-Bm8lgcm6.mjs.map +0 -1
- package/dist/_chunks/EditViewPage-CzOT5Kpj.js.map +0 -1
- package/dist/_chunks/Field-Caef4JjM.js.map +0 -1
- package/dist/_chunks/Field-Dlh0uGnL.mjs.map +0 -1
- package/dist/_chunks/Form-BzuAjtRq.js.map +0 -1
- package/dist/_chunks/Form-EnaQL_6L.mjs.map +0 -1
- package/dist/_chunks/History-C17LiyRg.js.map +0 -1
- package/dist/_chunks/History-D6sbCJvo.mjs.map +0 -1
- package/dist/_chunks/ListConfigurationPage-Ce4qs7qE.mjs.map +0 -1
- package/dist/_chunks/ListConfigurationPage-Dks5SX6f.js.map +0 -1
- package/dist/_chunks/ListViewPage-Be7S5aKL.mjs.map +0 -1
- package/dist/_chunks/ListViewPage-BwrZrPsh.js.map +0 -1
- package/dist/_chunks/NoContentTypePage-CIPmYQMm.mjs.map +0 -1
- package/dist/_chunks/NoContentTypePage-Cu5r1-JT.js.map +0 -1
- package/dist/_chunks/NoPermissionsPage-C-j6TEUF.js.map +0 -1
- package/dist/_chunks/NoPermissionsPage-DhJ7LYrr.mjs.map +0 -1
- package/dist/_chunks/Relations-CY7AtkDA.mjs.map +0 -1
- package/dist/_chunks/Relations-Czs-uZ-s.js.map +0 -1
- package/dist/_chunks/index-DNVx8ssZ.mjs.map +0 -1
- package/dist/_chunks/index-X_2tafck.js.map +0 -1
- package/dist/_chunks/layout-Dnh0PNp9.mjs.map +0 -1
- package/dist/_chunks/layout-dBc7wN7L.js.map +0 -1
- package/dist/_chunks/urls-CbOsUOoW.mjs +0 -7
- package/dist/_chunks/urls-CbOsUOoW.mjs.map +0 -1
- package/dist/_chunks/urls-DzZya_gm.js +0 -6
- package/dist/_chunks/urls-DzZya_gm.js.map +0 -1
- package/dist/server/src/controllers/utils/dimensions.d.ts +0 -5
- package/dist/server/src/controllers/utils/dimensions.d.ts.map +0 -1
package/dist/server/index.mjs
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1,
|
2
|
-
import { pick, omit, difference, intersection, pipe, propOr, isEqual, isEmpty, set, isNil as isNil$1, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, getOr, propEq, merge, groupBy
|
1
|
+
import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1, traverseEntity, pagination } from "@strapi/utils";
|
2
|
+
import { pick, omit, difference, castArray, intersection, pipe, propOr, isEqual, isEmpty, set, isNil as isNil$1, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, getOr, propEq, merge, groupBy } from "lodash/fp";
|
3
3
|
import "@strapi/types";
|
4
4
|
import * as yup from "yup";
|
5
5
|
import { scheduleJob } from "node-schedule";
|
@@ -54,7 +54,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
|
|
54
54
|
return ctx.forbidden();
|
55
55
|
}
|
56
56
|
const query = await permissionChecker2.sanitizeQuery(ctx.query);
|
57
|
-
const { results, pagination } = await getService(strapi2, "history").findVersionsPage({
|
57
|
+
const { results, pagination: pagination2 } = await getService(strapi2, "history").findVersionsPage({
|
58
58
|
query: {
|
59
59
|
...query,
|
60
60
|
...getValidPagination({ page: query.page, pageSize: query.pageSize })
|
@@ -73,7 +73,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
|
|
73
73
|
);
|
74
74
|
return {
|
75
75
|
data: sanitizedResults,
|
76
|
-
meta: { pagination }
|
76
|
+
meta: { pagination: pagination2 }
|
77
77
|
};
|
78
78
|
},
|
79
79
|
async restoreVersion(ctx) {
|
@@ -112,40 +112,65 @@ const FIELDS_TO_IGNORE = [
|
|
112
112
|
"strapi_stage",
|
113
113
|
"strapi_assignee"
|
114
114
|
];
|
115
|
-
const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
|
116
|
-
const sanitizedContentTypeSchemaAttributes = omit(FIELDS_TO_IGNORE, contentTypeSchemaAttributes);
|
117
|
-
const reduceDifferenceToAttributesObject = (diffKeys, source) => {
|
118
|
-
return diffKeys.reduce((previousAttributesObject, diffKey) => {
|
119
|
-
previousAttributesObject[diffKey] = source[diffKey];
|
120
|
-
return previousAttributesObject;
|
121
|
-
}, {});
|
122
|
-
};
|
123
|
-
const versionSchemaKeys = Object.keys(versionSchemaAttributes);
|
124
|
-
const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
|
125
|
-
const uniqueToContentType = difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
|
126
|
-
const added = reduceDifferenceToAttributesObject(
|
127
|
-
uniqueToContentType,
|
128
|
-
sanitizedContentTypeSchemaAttributes
|
129
|
-
);
|
130
|
-
const uniqueToVersion = difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
|
131
|
-
const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
|
132
|
-
return { added, removed };
|
133
|
-
};
|
134
115
|
const DEFAULT_RETENTION_DAYS = 90;
|
135
|
-
const
|
136
|
-
const
|
137
|
-
|
138
|
-
|
116
|
+
const createServiceUtils = ({ strapi: strapi2 }) => {
|
117
|
+
const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
|
118
|
+
const sanitizedContentTypeSchemaAttributes = omit(
|
119
|
+
FIELDS_TO_IGNORE,
|
120
|
+
contentTypeSchemaAttributes
|
121
|
+
);
|
122
|
+
const reduceDifferenceToAttributesObject = (diffKeys, source) => {
|
123
|
+
return diffKeys.reduce(
|
124
|
+
(previousAttributesObject, diffKey) => {
|
125
|
+
previousAttributesObject[diffKey] = source[diffKey];
|
126
|
+
return previousAttributesObject;
|
127
|
+
},
|
128
|
+
{}
|
129
|
+
);
|
130
|
+
};
|
131
|
+
const versionSchemaKeys = Object.keys(versionSchemaAttributes);
|
132
|
+
const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
|
133
|
+
const uniqueToContentType = difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
|
134
|
+
const added = reduceDifferenceToAttributesObject(
|
135
|
+
uniqueToContentType,
|
136
|
+
sanitizedContentTypeSchemaAttributes
|
137
|
+
);
|
138
|
+
const uniqueToVersion = difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
|
139
|
+
const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
|
140
|
+
return { added, removed };
|
139
141
|
};
|
140
|
-
const
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
142
|
+
const getRelationRestoreValue = async (versionRelationData, attribute) => {
|
143
|
+
if (Array.isArray(versionRelationData)) {
|
144
|
+
if (versionRelationData.length === 0)
|
145
|
+
return versionRelationData;
|
146
|
+
const existingAndMissingRelations = await Promise.all(
|
147
|
+
versionRelationData.map((relation) => {
|
148
|
+
return strapi2.documents(attribute.target).findOne({
|
149
|
+
documentId: relation.documentId,
|
150
|
+
locale: relation.locale || void 0
|
151
|
+
});
|
152
|
+
})
|
153
|
+
);
|
154
|
+
return existingAndMissingRelations.filter(
|
155
|
+
(relation) => relation !== null
|
156
|
+
);
|
147
157
|
}
|
148
|
-
return
|
158
|
+
return strapi2.documents(attribute.target).findOne({
|
159
|
+
documentId: versionRelationData.documentId,
|
160
|
+
locale: versionRelationData.locale || void 0
|
161
|
+
});
|
162
|
+
};
|
163
|
+
const getMediaRestoreValue = async (versionRelationData, attribute) => {
|
164
|
+
if (attribute.multiple) {
|
165
|
+
const existingAndMissingMedias = await Promise.all(
|
166
|
+
// @ts-expect-error Fix the type definitions so this isn't any
|
167
|
+
versionRelationData.map((media) => {
|
168
|
+
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
|
169
|
+
})
|
170
|
+
);
|
171
|
+
return existingAndMissingMedias.filter((media) => media != null);
|
172
|
+
}
|
173
|
+
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
|
149
174
|
};
|
150
175
|
const localesService = strapi2.plugin("i18n")?.service("locales");
|
151
176
|
const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
|
@@ -161,25 +186,39 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
161
186
|
{}
|
162
187
|
);
|
163
188
|
};
|
189
|
+
const getRetentionDays = () => {
|
190
|
+
const featureConfig = strapi2.ee.features.get("cms-content-history");
|
191
|
+
const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
|
192
|
+
const userRetentionDays = strapi2.config.get("admin.history.retentionDays");
|
193
|
+
if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
|
194
|
+
return userRetentionDays;
|
195
|
+
}
|
196
|
+
return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
|
197
|
+
};
|
164
198
|
const getVersionStatus = async (contentTypeUid, document) => {
|
165
199
|
const documentMetadataService = strapi2.plugin("content-manager").service("document-metadata");
|
166
200
|
const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
|
167
201
|
return documentMetadataService.getStatus(document, meta.availableStatus);
|
168
202
|
};
|
169
|
-
const getDeepPopulate2 = (uid2) => {
|
203
|
+
const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
|
170
204
|
const model = strapi2.getModel(uid2);
|
171
205
|
const attributes = Object.entries(model.attributes);
|
206
|
+
const fieldSelector = useDatabaseSyntax ? "select" : "fields";
|
172
207
|
return attributes.reduce((acc, [attributeName, attribute]) => {
|
173
208
|
switch (attribute.type) {
|
174
209
|
case "relation": {
|
210
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
211
|
+
if (isMorphRelation) {
|
212
|
+
break;
|
213
|
+
}
|
175
214
|
const isVisible2 = contentTypes$1.isVisibleAttribute(model, attributeName);
|
176
215
|
if (isVisible2) {
|
177
|
-
acc[attributeName] = {
|
216
|
+
acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
|
178
217
|
}
|
179
218
|
break;
|
180
219
|
}
|
181
220
|
case "media": {
|
182
|
-
acc[attributeName] = {
|
221
|
+
acc[attributeName] = { [fieldSelector]: ["id"] };
|
183
222
|
break;
|
184
223
|
}
|
185
224
|
case "component": {
|
@@ -202,80 +241,68 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
202
241
|
return acc;
|
203
242
|
}, {});
|
204
243
|
};
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
|
212
|
-
return next();
|
244
|
+
const buildMediaResponse = async (values) => {
|
245
|
+
return values.slice(0, 25).reduce(
|
246
|
+
async (currentRelationDataPromise, entry) => {
|
247
|
+
const currentRelationData = await currentRelationDataPromise;
|
248
|
+
if (!entry) {
|
249
|
+
return currentRelationData;
|
213
250
|
}
|
214
|
-
|
215
|
-
|
251
|
+
const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
|
252
|
+
if (relatedEntry) {
|
253
|
+
currentRelationData.results.push(relatedEntry);
|
254
|
+
} else {
|
255
|
+
currentRelationData.meta.missingCount += 1;
|
216
256
|
}
|
217
|
-
|
218
|
-
|
219
|
-
|
257
|
+
return currentRelationData;
|
258
|
+
},
|
259
|
+
Promise.resolve({
|
260
|
+
results: [],
|
261
|
+
meta: { missingCount: 0 }
|
262
|
+
})
|
263
|
+
);
|
264
|
+
};
|
265
|
+
const buildRelationReponse = async (values, attributeSchema) => {
|
266
|
+
return values.slice(0, 25).reduce(
|
267
|
+
async (currentRelationDataPromise, entry) => {
|
268
|
+
const currentRelationData = await currentRelationDataPromise;
|
269
|
+
if (!entry) {
|
270
|
+
return currentRelationData;
|
220
271
|
}
|
221
|
-
const
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
documentId: documentContext.documentId,
|
227
|
-
locale,
|
228
|
-
populate: getDeepPopulate2(contentTypeUid)
|
229
|
-
});
|
230
|
-
const status = await getVersionStatus(contentTypeUid, document);
|
231
|
-
const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
|
232
|
-
const componentsSchemas = Object.keys(
|
233
|
-
attributesSchema
|
234
|
-
).reduce((currentComponentSchemas, key) => {
|
235
|
-
const fieldSchema = attributesSchema[key];
|
236
|
-
if (fieldSchema.type === "component") {
|
237
|
-
const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
|
238
|
-
return {
|
239
|
-
...currentComponentSchemas,
|
240
|
-
[fieldSchema.component]: componentSchema
|
241
|
-
};
|
242
|
-
}
|
243
|
-
return currentComponentSchemas;
|
244
|
-
}, {});
|
245
|
-
await strapi2.db.transaction(async ({ onCommit }) => {
|
246
|
-
onCommit(() => {
|
247
|
-
this.createVersion({
|
248
|
-
contentType: contentTypeUid,
|
249
|
-
data: omit(FIELDS_TO_IGNORE, document),
|
250
|
-
schema: omit(FIELDS_TO_IGNORE, attributesSchema),
|
251
|
-
componentsSchemas,
|
252
|
-
relatedDocumentId: documentContext.documentId,
|
253
|
-
locale,
|
254
|
-
status
|
255
|
-
});
|
272
|
+
const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
|
273
|
+
if (relatedEntry) {
|
274
|
+
currentRelationData.results.push({
|
275
|
+
...relatedEntry,
|
276
|
+
status: await getVersionStatus(attributeSchema.target, relatedEntry)
|
256
277
|
});
|
257
|
-
}
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
278
|
+
} else {
|
279
|
+
currentRelationData.meta.missingCount += 1;
|
280
|
+
}
|
281
|
+
return currentRelationData;
|
282
|
+
},
|
283
|
+
Promise.resolve({
|
284
|
+
results: [],
|
285
|
+
meta: { missingCount: 0 }
|
286
|
+
})
|
287
|
+
);
|
288
|
+
};
|
289
|
+
return {
|
290
|
+
getSchemaAttributesDiff,
|
291
|
+
getRelationRestoreValue,
|
292
|
+
getMediaRestoreValue,
|
293
|
+
getDefaultLocale,
|
294
|
+
getLocaleDictionary,
|
295
|
+
getRetentionDays,
|
296
|
+
getVersionStatus,
|
297
|
+
getDeepPopulate: getDeepPopulate2,
|
298
|
+
buildMediaResponse,
|
299
|
+
buildRelationReponse
|
300
|
+
};
|
301
|
+
};
|
302
|
+
const createHistoryService = ({ strapi: strapi2 }) => {
|
303
|
+
const query = strapi2.db.query(HISTORY_VERSION_UID);
|
304
|
+
const serviceUtils = createServiceUtils({ strapi: strapi2 });
|
305
|
+
return {
|
279
306
|
async createVersion(historyVersionData) {
|
280
307
|
await query.create({
|
281
308
|
data: {
|
@@ -286,8 +313,8 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
286
313
|
});
|
287
314
|
},
|
288
315
|
async findVersionsPage(params) {
|
289
|
-
const locale = params.query.locale || await getDefaultLocale();
|
290
|
-
const [{ results, pagination }, localeDictionary] = await Promise.all([
|
316
|
+
const locale = params.query.locale || await serviceUtils.getDefaultLocale();
|
317
|
+
const [{ results, pagination: pagination2 }, localeDictionary] = await Promise.all([
|
291
318
|
query.findPage({
|
292
319
|
...params.query,
|
293
320
|
where: {
|
@@ -300,78 +327,34 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
300
327
|
populate: ["createdBy"],
|
301
328
|
orderBy: [{ createdAt: "desc" }]
|
302
329
|
}),
|
303
|
-
getLocaleDictionary()
|
330
|
+
serviceUtils.getLocaleDictionary()
|
304
331
|
]);
|
305
|
-
const buildRelationReponse = async (values, attributeSchema) => {
|
306
|
-
return values.slice(0, 25).reduce(
|
307
|
-
async (currentRelationDataPromise, entry) => {
|
308
|
-
const currentRelationData = await currentRelationDataPromise;
|
309
|
-
if (!entry) {
|
310
|
-
return currentRelationData;
|
311
|
-
}
|
312
|
-
const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
|
313
|
-
const permissionChecker2 = getService$1("permission-checker").create({
|
314
|
-
userAbility: params.state.userAbility,
|
315
|
-
model: attributeSchema.target
|
316
|
-
});
|
317
|
-
const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
|
318
|
-
if (sanitizedEntry) {
|
319
|
-
currentRelationData.results.push({
|
320
|
-
...sanitizedEntry,
|
321
|
-
status: await getVersionStatus(attributeSchema.target, sanitizedEntry)
|
322
|
-
});
|
323
|
-
} else {
|
324
|
-
currentRelationData.meta.missingCount += 1;
|
325
|
-
}
|
326
|
-
return currentRelationData;
|
327
|
-
},
|
328
|
-
Promise.resolve({
|
329
|
-
results: [],
|
330
|
-
meta: { missingCount: 0 }
|
331
|
-
})
|
332
|
-
);
|
333
|
-
};
|
334
|
-
const buildMediaResponse = async (values) => {
|
335
|
-
return values.slice(0, 25).reduce(
|
336
|
-
async (currentRelationDataPromise, entry) => {
|
337
|
-
const currentRelationData = await currentRelationDataPromise;
|
338
|
-
if (!entry) {
|
339
|
-
return currentRelationData;
|
340
|
-
}
|
341
|
-
const permissionChecker2 = getService$1("permission-checker").create({
|
342
|
-
userAbility: params.state.userAbility,
|
343
|
-
model: "plugin::upload.file"
|
344
|
-
});
|
345
|
-
const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
|
346
|
-
const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
|
347
|
-
if (sanitizedEntry) {
|
348
|
-
currentRelationData.results.push(sanitizedEntry);
|
349
|
-
} else {
|
350
|
-
currentRelationData.meta.missingCount += 1;
|
351
|
-
}
|
352
|
-
return currentRelationData;
|
353
|
-
},
|
354
|
-
Promise.resolve({
|
355
|
-
results: [],
|
356
|
-
meta: { missingCount: 0 }
|
357
|
-
})
|
358
|
-
);
|
359
|
-
};
|
360
332
|
const populateEntryRelations = async (entry) => {
|
361
333
|
const entryWithRelations = await Object.entries(entry.schema).reduce(
|
362
334
|
async (currentDataWithRelations, [attributeKey, attributeSchema]) => {
|
363
335
|
const attributeValue = entry.data[attributeKey];
|
364
336
|
const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
|
365
337
|
if (attributeSchema.type === "media") {
|
338
|
+
const permissionChecker2 = getService$1("permission-checker").create({
|
339
|
+
userAbility: params.state.userAbility,
|
340
|
+
model: "plugin::upload.file"
|
341
|
+
});
|
342
|
+
const response = await serviceUtils.buildMediaResponse(attributeValues);
|
343
|
+
const sanitizedResults = await Promise.all(
|
344
|
+
response.results.map((media) => permissionChecker2.sanitizeOutput(media))
|
345
|
+
);
|
366
346
|
return {
|
367
347
|
...await currentDataWithRelations,
|
368
|
-
[attributeKey]:
|
348
|
+
[attributeKey]: {
|
349
|
+
results: sanitizedResults,
|
350
|
+
meta: response.meta
|
351
|
+
}
|
369
352
|
};
|
370
353
|
}
|
371
354
|
if (attributeSchema.type === "relation" && attributeSchema.relation !== "morphToOne" && attributeSchema.relation !== "morphToMany") {
|
372
355
|
if (attributeSchema.target === "admin::user") {
|
373
356
|
const adminUsers = await Promise.all(
|
374
|
-
attributeValues.map(
|
357
|
+
attributeValues.map((userToPopulate) => {
|
375
358
|
if (userToPopulate == null) {
|
376
359
|
return null;
|
377
360
|
}
|
@@ -388,9 +371,23 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
388
371
|
[attributeKey]: adminUsers
|
389
372
|
};
|
390
373
|
}
|
374
|
+
const permissionChecker2 = getService$1("permission-checker").create({
|
375
|
+
userAbility: params.state.userAbility,
|
376
|
+
model: attributeSchema.target
|
377
|
+
});
|
378
|
+
const response = await serviceUtils.buildRelationReponse(
|
379
|
+
attributeValues,
|
380
|
+
attributeSchema
|
381
|
+
);
|
382
|
+
const sanitizedResults = await Promise.all(
|
383
|
+
response.results.map((media) => permissionChecker2.sanitizeOutput(media))
|
384
|
+
);
|
391
385
|
return {
|
392
386
|
...await currentDataWithRelations,
|
393
|
-
[attributeKey]:
|
387
|
+
[attributeKey]: {
|
388
|
+
results: sanitizedResults,
|
389
|
+
meta: response.meta
|
390
|
+
}
|
394
391
|
};
|
395
392
|
}
|
396
393
|
return currentDataWithRelations;
|
@@ -405,7 +402,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
405
402
|
...result,
|
406
403
|
data: await populateEntryRelations(result),
|
407
404
|
meta: {
|
408
|
-
unknownAttributes: getSchemaAttributesDiff(
|
405
|
+
unknownAttributes: serviceUtils.getSchemaAttributesDiff(
|
409
406
|
result.schema,
|
410
407
|
strapi2.getModel(params.query.contentType).attributes
|
411
408
|
)
|
@@ -416,13 +413,16 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
416
413
|
);
|
417
414
|
return {
|
418
415
|
results: formattedResults,
|
419
|
-
pagination
|
416
|
+
pagination: pagination2
|
420
417
|
};
|
421
418
|
},
|
422
419
|
async restoreVersion(versionId) {
|
423
420
|
const version = await query.findOne({ where: { id: versionId } });
|
424
421
|
const contentTypeSchemaAttributes = strapi2.getModel(version.contentType).attributes;
|
425
|
-
const schemaDiff = getSchemaAttributesDiff(
|
422
|
+
const schemaDiff = serviceUtils.getSchemaAttributesDiff(
|
423
|
+
version.schema,
|
424
|
+
contentTypeSchemaAttributes
|
425
|
+
);
|
426
426
|
const dataWithoutAddedAttributes = Object.keys(schemaDiff.added).reduce(
|
427
427
|
(currentData, addedKey) => {
|
428
428
|
currentData[addedKey] = null;
|
@@ -435,61 +435,26 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
435
435
|
FIELDS_TO_IGNORE,
|
436
436
|
contentTypeSchemaAttributes
|
437
437
|
);
|
438
|
-
const
|
439
|
-
|
440
|
-
|
441
|
-
const
|
442
|
-
if (
|
438
|
+
const reducer = async.reduce(Object.entries(sanitizedSchemaAttributes));
|
439
|
+
const dataWithoutMissingRelations = await reducer(
|
440
|
+
async (previousRelationAttributes, [name, attribute]) => {
|
441
|
+
const versionRelationData = version.data[name];
|
442
|
+
if (!versionRelationData) {
|
443
443
|
return previousRelationAttributes;
|
444
444
|
}
|
445
445
|
if (attribute.type === "relation" && // TODO: handle polymorphic relations
|
446
446
|
attribute.relation !== "morphToOne" && attribute.relation !== "morphToMany") {
|
447
|
-
|
448
|
-
|
449
|
-
return previousRelationAttributes;
|
450
|
-
const existingAndMissingRelations = await Promise.all(
|
451
|
-
relationData.map((relation) => {
|
452
|
-
return strapi2.documents(attribute.target).findOne({
|
453
|
-
documentId: relation.documentId,
|
454
|
-
locale: relation.locale || void 0
|
455
|
-
});
|
456
|
-
})
|
457
|
-
);
|
458
|
-
const existingRelations = existingAndMissingRelations.filter(
|
459
|
-
(relation) => relation !== null
|
460
|
-
);
|
461
|
-
previousRelationAttributes[name] = existingRelations;
|
462
|
-
} else {
|
463
|
-
const existingRelation = await strapi2.documents(attribute.target).findOne({
|
464
|
-
documentId: relationData.documentId,
|
465
|
-
locale: relationData.locale || void 0
|
466
|
-
});
|
467
|
-
if (!existingRelation) {
|
468
|
-
previousRelationAttributes[name] = null;
|
469
|
-
}
|
470
|
-
}
|
447
|
+
const data2 = await serviceUtils.getRelationRestoreValue(versionRelationData, attribute);
|
448
|
+
previousRelationAttributes[name] = data2;
|
471
449
|
}
|
472
450
|
if (attribute.type === "media") {
|
473
|
-
|
474
|
-
|
475
|
-
// @ts-expect-error Fix the type definitions so this isn't any
|
476
|
-
relationData.map((media) => {
|
477
|
-
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
|
478
|
-
})
|
479
|
-
);
|
480
|
-
const existingMedias = existingAndMissingMedias.filter((media) => media != null);
|
481
|
-
previousRelationAttributes[name] = existingMedias;
|
482
|
-
} else {
|
483
|
-
const existingMedia = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: version.data[name].id } });
|
484
|
-
if (!existingMedia) {
|
485
|
-
previousRelationAttributes[name] = null;
|
486
|
-
}
|
487
|
-
}
|
451
|
+
const data2 = await serviceUtils.getMediaRestoreValue(versionRelationData, attribute);
|
452
|
+
previousRelationAttributes[name] = data2;
|
488
453
|
}
|
489
454
|
return previousRelationAttributes;
|
490
455
|
},
|
491
456
|
// Clone to avoid mutating the original version data
|
492
|
-
|
457
|
+
structuredClone(dataWithoutAddedAttributes)
|
493
458
|
);
|
494
459
|
const data = omit(["id", ...Object.keys(schemaDiff.removed)], dataWithoutMissingRelations);
|
495
460
|
const restoredDocument = await strapi2.documents(version.contentType).update({
|
@@ -504,8 +469,118 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
504
469
|
}
|
505
470
|
};
|
506
471
|
};
|
472
|
+
const shouldCreateHistoryVersion = (context) => {
|
473
|
+
if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
|
474
|
+
return false;
|
475
|
+
}
|
476
|
+
if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
|
477
|
+
return false;
|
478
|
+
}
|
479
|
+
if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
|
480
|
+
return false;
|
481
|
+
}
|
482
|
+
if (!context.contentType.uid.startsWith("api::")) {
|
483
|
+
return false;
|
484
|
+
}
|
485
|
+
return true;
|
486
|
+
};
|
487
|
+
const getSchemas = (uid2) => {
|
488
|
+
const attributesSchema = strapi.getModel(uid2).attributes;
|
489
|
+
const componentsSchemas = Object.keys(attributesSchema).reduce(
|
490
|
+
(currentComponentSchemas, key) => {
|
491
|
+
const fieldSchema = attributesSchema[key];
|
492
|
+
if (fieldSchema.type === "component") {
|
493
|
+
const componentSchema = strapi.getModel(fieldSchema.component).attributes;
|
494
|
+
return {
|
495
|
+
...currentComponentSchemas,
|
496
|
+
[fieldSchema.component]: componentSchema
|
497
|
+
};
|
498
|
+
}
|
499
|
+
return currentComponentSchemas;
|
500
|
+
},
|
501
|
+
{}
|
502
|
+
);
|
503
|
+
return {
|
504
|
+
schema: omit(FIELDS_TO_IGNORE, attributesSchema),
|
505
|
+
componentsSchemas
|
506
|
+
};
|
507
|
+
};
|
508
|
+
const createLifecyclesService = ({ strapi: strapi2 }) => {
|
509
|
+
const state = {
|
510
|
+
deleteExpiredJob: null,
|
511
|
+
isInitialized: false
|
512
|
+
};
|
513
|
+
const serviceUtils = createServiceUtils({ strapi: strapi2 });
|
514
|
+
return {
|
515
|
+
async bootstrap() {
|
516
|
+
if (state.isInitialized) {
|
517
|
+
return;
|
518
|
+
}
|
519
|
+
strapi2.documents.use(async (context, next) => {
|
520
|
+
const result = await next();
|
521
|
+
if (!shouldCreateHistoryVersion(context)) {
|
522
|
+
return result;
|
523
|
+
}
|
524
|
+
const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
|
525
|
+
const defaultLocale = await serviceUtils.getDefaultLocale();
|
526
|
+
const locales = castArray(context.params?.locale || defaultLocale);
|
527
|
+
if (!locales.length) {
|
528
|
+
return result;
|
529
|
+
}
|
530
|
+
const uid2 = context.contentType.uid;
|
531
|
+
const schemas = getSchemas(uid2);
|
532
|
+
const localeEntries = await strapi2.db.query(uid2).findMany({
|
533
|
+
where: {
|
534
|
+
documentId,
|
535
|
+
locale: { $in: locales },
|
536
|
+
publishedAt: null
|
537
|
+
},
|
538
|
+
populate: serviceUtils.getDeepPopulate(
|
539
|
+
uid2,
|
540
|
+
true
|
541
|
+
/* use database syntax */
|
542
|
+
)
|
543
|
+
});
|
544
|
+
await strapi2.db.transaction(async ({ onCommit }) => {
|
545
|
+
onCommit(async () => {
|
546
|
+
for (const entry of localeEntries) {
|
547
|
+
const status = await serviceUtils.getVersionStatus(uid2, entry);
|
548
|
+
await getService(strapi2, "history").createVersion({
|
549
|
+
contentType: uid2,
|
550
|
+
data: omit(FIELDS_TO_IGNORE, entry),
|
551
|
+
relatedDocumentId: documentId,
|
552
|
+
locale: entry.locale,
|
553
|
+
status,
|
554
|
+
...schemas
|
555
|
+
});
|
556
|
+
}
|
557
|
+
});
|
558
|
+
});
|
559
|
+
return result;
|
560
|
+
});
|
561
|
+
state.deleteExpiredJob = scheduleJob("0 0 * * *", () => {
|
562
|
+
const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
|
563
|
+
const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
|
564
|
+
strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
|
565
|
+
where: {
|
566
|
+
created_at: {
|
567
|
+
$lt: expirationDate.toISOString()
|
568
|
+
}
|
569
|
+
}
|
570
|
+
});
|
571
|
+
});
|
572
|
+
state.isInitialized = true;
|
573
|
+
},
|
574
|
+
async destroy() {
|
575
|
+
if (state.deleteExpiredJob) {
|
576
|
+
state.deleteExpiredJob.cancel();
|
577
|
+
}
|
578
|
+
}
|
579
|
+
};
|
580
|
+
};
|
507
581
|
const services$1 = {
|
508
|
-
history: createHistoryService
|
582
|
+
history: createHistoryService,
|
583
|
+
lifecycles: createLifecyclesService
|
509
584
|
};
|
510
585
|
const info = { pluginName: "content-manager", type: "admin" };
|
511
586
|
const historyVersionRouter = {
|
@@ -585,10 +660,10 @@ const getFeature = () => {
|
|
585
660
|
strapi2.get("models").add(historyVersion);
|
586
661
|
},
|
587
662
|
bootstrap({ strapi: strapi2 }) {
|
588
|
-
getService(strapi2, "
|
663
|
+
getService(strapi2, "lifecycles").bootstrap();
|
589
664
|
},
|
590
665
|
destroy({ strapi: strapi2 }) {
|
591
|
-
getService(strapi2, "
|
666
|
+
getService(strapi2, "lifecycles").destroy();
|
592
667
|
},
|
593
668
|
controllers: controllers$1,
|
594
669
|
services: services$1,
|
@@ -1407,7 +1482,7 @@ const { PaginationError, ValidationError } = errors;
|
|
1407
1482
|
const TYPES = ["singleType", "collectionType"];
|
1408
1483
|
const kindSchema = yup$1.string().oneOf(TYPES).nullable();
|
1409
1484
|
const bulkActionInputSchema = yup$1.object({
|
1410
|
-
|
1485
|
+
documentIds: yup$1.array().of(yup$1.strapiID()).min(1).required()
|
1411
1486
|
}).required();
|
1412
1487
|
const generateUIDInputSchema = yup$1.object({
|
1413
1488
|
contentTypeUID: yup$1.string().required(),
|
@@ -1506,15 +1581,49 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
|
|
1506
1581
|
}
|
1507
1582
|
}, body);
|
1508
1583
|
};
|
1509
|
-
const
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1584
|
+
const singleLocaleSchema = yup$1.string().nullable();
|
1585
|
+
const multipleLocaleSchema = yup$1.lazy(
|
1586
|
+
(value) => Array.isArray(value) ? yup$1.array().of(singleLocaleSchema.required()) : singleLocaleSchema
|
1587
|
+
);
|
1588
|
+
const statusSchema = yup$1.mixed().oneOf(["draft", "published"], "Invalid status");
|
1589
|
+
const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
|
1590
|
+
const { allowMultipleLocales } = opts;
|
1591
|
+
const { locale, status: providedStatus, ...rest } = request || {};
|
1592
|
+
const defaultStatus = contentTypes$1.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
|
1593
|
+
const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
|
1594
|
+
const schema = yup$1.object().shape({
|
1595
|
+
locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
|
1596
|
+
status: statusSchema
|
1597
|
+
});
|
1598
|
+
try {
|
1599
|
+
await validateYupSchema(schema, { strict: true, abortEarly: false })(request);
|
1600
|
+
return { locale, status, ...rest };
|
1601
|
+
} catch (error) {
|
1602
|
+
throw new errors.ValidationError(`Validation error: ${error.message}`);
|
1516
1603
|
}
|
1517
|
-
|
1604
|
+
};
|
1605
|
+
const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
|
1606
|
+
const documentMetadata2 = getService$1("document-metadata");
|
1607
|
+
const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
|
1608
|
+
let {
|
1609
|
+
meta: { availableLocales, availableStatus }
|
1610
|
+
} = serviceOutput;
|
1611
|
+
const metadataSanitizer = permissionChecker2.sanitizeOutput;
|
1612
|
+
availableLocales = await async.map(
|
1613
|
+
availableLocales,
|
1614
|
+
async (localeDocument) => metadataSanitizer(localeDocument)
|
1615
|
+
);
|
1616
|
+
availableStatus = await async.map(
|
1617
|
+
availableStatus,
|
1618
|
+
async (statusDocument) => metadataSanitizer(statusDocument)
|
1619
|
+
);
|
1620
|
+
return {
|
1621
|
+
...serviceOutput,
|
1622
|
+
meta: {
|
1623
|
+
availableLocales,
|
1624
|
+
availableStatus
|
1625
|
+
}
|
1626
|
+
};
|
1518
1627
|
};
|
1519
1628
|
const createDocument = async (ctx, opts) => {
|
1520
1629
|
const { userAbility, user } = ctx.state;
|
@@ -1529,7 +1638,7 @@ const createDocument = async (ctx, opts) => {
|
|
1529
1638
|
const setCreator = setCreatorFields({ user });
|
1530
1639
|
const sanitizeFn = async.pipe(pickPermittedFields, setCreator);
|
1531
1640
|
const sanitizedBody = await sanitizeFn(body);
|
1532
|
-
const { locale, status
|
1641
|
+
const { locale, status } = await getDocumentLocaleAndStatus(body, model);
|
1533
1642
|
return documentManager2.create(model, {
|
1534
1643
|
data: sanitizedBody,
|
1535
1644
|
locale,
|
@@ -1548,7 +1657,7 @@ const updateDocument = async (ctx, opts) => {
|
|
1548
1657
|
}
|
1549
1658
|
const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
|
1550
1659
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1551
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
1660
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1552
1661
|
const [documentVersion, documentExists] = await Promise.all([
|
1553
1662
|
documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
|
1554
1663
|
documentManager2.exists(model, id)
|
@@ -1586,8 +1695,8 @@ const collectionTypes = {
|
|
1586
1695
|
}
|
1587
1696
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
|
1588
1697
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
|
1589
|
-
const { locale, status } = getDocumentLocaleAndStatus(query);
|
1590
|
-
const { results: documents, pagination } = await documentManager2.findPage(
|
1698
|
+
const { locale, status } = await getDocumentLocaleAndStatus(query, model);
|
1699
|
+
const { results: documents, pagination: pagination2 } = await documentManager2.findPage(
|
1591
1700
|
{ ...permissionQuery, populate, locale, status },
|
1592
1701
|
model
|
1593
1702
|
);
|
@@ -1608,21 +1717,20 @@ const collectionTypes = {
|
|
1608
1717
|
);
|
1609
1718
|
ctx.body = {
|
1610
1719
|
results,
|
1611
|
-
pagination
|
1720
|
+
pagination: pagination2
|
1612
1721
|
};
|
1613
1722
|
},
|
1614
1723
|
async findOne(ctx) {
|
1615
1724
|
const { userAbility } = ctx.state;
|
1616
1725
|
const { model, id } = ctx.params;
|
1617
1726
|
const documentManager2 = getService$1("document-manager");
|
1618
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1619
1727
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1620
1728
|
if (permissionChecker2.cannot.read()) {
|
1621
1729
|
return ctx.forbidden();
|
1622
1730
|
}
|
1623
1731
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
1624
1732
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1625
|
-
const { locale, status
|
1733
|
+
const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1626
1734
|
const version = await documentManager2.findOne(id, model, {
|
1627
1735
|
populate,
|
1628
1736
|
locale,
|
@@ -1633,8 +1741,10 @@ const collectionTypes = {
|
|
1633
1741
|
if (!exists) {
|
1634
1742
|
return ctx.notFound();
|
1635
1743
|
}
|
1636
|
-
const { meta } = await
|
1744
|
+
const { meta } = await formatDocumentWithMetadata(
|
1745
|
+
permissionChecker2,
|
1637
1746
|
model,
|
1747
|
+
// @ts-expect-error TODO: fix
|
1638
1748
|
{ id, locale, publishedAt: null },
|
1639
1749
|
{ availableLocales: true, availableStatus: false }
|
1640
1750
|
);
|
@@ -1645,12 +1755,11 @@ const collectionTypes = {
|
|
1645
1755
|
return ctx.forbidden();
|
1646
1756
|
}
|
1647
1757
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
|
1648
|
-
ctx.body = await
|
1758
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
1649
1759
|
},
|
1650
1760
|
async create(ctx) {
|
1651
1761
|
const { userAbility } = ctx.state;
|
1652
1762
|
const { model } = ctx.params;
|
1653
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1654
1763
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1655
1764
|
const [totalEntries, document] = await Promise.all([
|
1656
1765
|
strapi.db.query(model).count(),
|
@@ -1658,7 +1767,7 @@ const collectionTypes = {
|
|
1658
1767
|
]);
|
1659
1768
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
|
1660
1769
|
ctx.status = 201;
|
1661
|
-
ctx.body = await
|
1770
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
|
1662
1771
|
// Empty metadata as it's not relevant for a new document
|
1663
1772
|
availableLocales: false,
|
1664
1773
|
availableStatus: false
|
@@ -1672,25 +1781,23 @@ const collectionTypes = {
|
|
1672
1781
|
async update(ctx) {
|
1673
1782
|
const { userAbility } = ctx.state;
|
1674
1783
|
const { model } = ctx.params;
|
1675
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1676
1784
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1677
1785
|
const updatedVersion = await updateDocument(ctx);
|
1678
1786
|
const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
|
1679
|
-
ctx.body = await
|
1787
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
|
1680
1788
|
},
|
1681
1789
|
async clone(ctx) {
|
1682
1790
|
const { userAbility, user } = ctx.state;
|
1683
1791
|
const { model, sourceId: id } = ctx.params;
|
1684
1792
|
const { body } = ctx.request;
|
1685
1793
|
const documentManager2 = getService$1("document-manager");
|
1686
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1687
1794
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1688
1795
|
if (permissionChecker2.cannot.create()) {
|
1689
1796
|
return ctx.forbidden();
|
1690
1797
|
}
|
1691
1798
|
const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
|
1692
1799
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1693
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
1800
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1694
1801
|
const document = await documentManager2.findOne(id, model, {
|
1695
1802
|
populate,
|
1696
1803
|
locale,
|
@@ -1706,7 +1813,7 @@ const collectionTypes = {
|
|
1706
1813
|
const sanitizedBody = await sanitizeFn(body);
|
1707
1814
|
const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
|
1708
1815
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
|
1709
|
-
ctx.body = await
|
1816
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
|
1710
1817
|
// Empty metadata as it's not relevant for a new document
|
1711
1818
|
availableLocales: false,
|
1712
1819
|
availableStatus: false
|
@@ -1735,7 +1842,7 @@ const collectionTypes = {
|
|
1735
1842
|
}
|
1736
1843
|
const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
|
1737
1844
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1738
|
-
const { locale } = getDocumentLocaleAndStatus(ctx.query);
|
1845
|
+
const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1739
1846
|
const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
|
1740
1847
|
if (documentLocales.length === 0) {
|
1741
1848
|
return ctx.notFound();
|
@@ -1757,7 +1864,6 @@ const collectionTypes = {
|
|
1757
1864
|
const { id, model } = ctx.params;
|
1758
1865
|
const { body } = ctx.request;
|
1759
1866
|
const documentManager2 = getService$1("document-manager");
|
1760
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1761
1867
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1762
1868
|
if (permissionChecker2.cannot.publish()) {
|
1763
1869
|
return ctx.forbidden();
|
@@ -1765,25 +1871,46 @@ const collectionTypes = {
|
|
1765
1871
|
const publishedDocument = await strapi.db.transaction(async () => {
|
1766
1872
|
const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
|
1767
1873
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1768
|
-
|
1874
|
+
let document;
|
1875
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1876
|
+
const isCreate = isNil$1(id);
|
1877
|
+
if (isCreate) {
|
1878
|
+
if (permissionChecker2.cannot.create()) {
|
1879
|
+
throw new errors.ForbiddenError();
|
1880
|
+
}
|
1881
|
+
document = await createDocument(ctx, { populate });
|
1882
|
+
}
|
1883
|
+
const isUpdate = !isCreate;
|
1884
|
+
if (isUpdate) {
|
1885
|
+
document = await documentManager2.findOne(id, model, { populate, locale });
|
1886
|
+
if (!document) {
|
1887
|
+
throw new errors.NotFoundError("Document not found");
|
1888
|
+
}
|
1889
|
+
if (permissionChecker2.can.update(document)) {
|
1890
|
+
await updateDocument(ctx);
|
1891
|
+
}
|
1892
|
+
}
|
1769
1893
|
if (permissionChecker2.cannot.publish(document)) {
|
1770
1894
|
throw new errors.ForbiddenError();
|
1771
1895
|
}
|
1772
|
-
const
|
1773
|
-
return documentManager2.publish(document.documentId, model, {
|
1896
|
+
const publishResult = await documentManager2.publish(document.documentId, model, {
|
1774
1897
|
locale
|
1775
1898
|
// TODO: Allow setting creator fields on publish
|
1776
1899
|
// data: setCreatorFields({ user, isEdition: true })({}),
|
1777
1900
|
});
|
1901
|
+
if (!publishResult || publishResult.length === 0) {
|
1902
|
+
throw new errors.NotFoundError("Document not found or already published.");
|
1903
|
+
}
|
1904
|
+
return publishResult[0];
|
1778
1905
|
});
|
1779
1906
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
|
1780
|
-
ctx.body = await
|
1907
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
1781
1908
|
},
|
1782
1909
|
async bulkPublish(ctx) {
|
1783
1910
|
const { userAbility } = ctx.state;
|
1784
1911
|
const { model } = ctx.params;
|
1785
1912
|
const { body } = ctx.request;
|
1786
|
-
const {
|
1913
|
+
const { documentIds } = body;
|
1787
1914
|
await validateBulkActionInput(body);
|
1788
1915
|
const documentManager2 = getService$1("document-manager");
|
1789
1916
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
@@ -1792,8 +1919,13 @@ const collectionTypes = {
|
|
1792
1919
|
}
|
1793
1920
|
const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
|
1794
1921
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1795
|
-
const
|
1796
|
-
|
1922
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model, {
|
1923
|
+
allowMultipleLocales: true
|
1924
|
+
});
|
1925
|
+
const entityPromises = documentIds.map(
|
1926
|
+
(documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
|
1927
|
+
);
|
1928
|
+
const entities = (await Promise.all(entityPromises)).flat();
|
1797
1929
|
for (const entity of entities) {
|
1798
1930
|
if (!entity) {
|
1799
1931
|
return ctx.notFound();
|
@@ -1802,24 +1934,25 @@ const collectionTypes = {
|
|
1802
1934
|
return ctx.forbidden();
|
1803
1935
|
}
|
1804
1936
|
}
|
1805
|
-
const
|
1937
|
+
const count = await documentManager2.publishMany(model, documentIds, locale);
|
1806
1938
|
ctx.body = { count };
|
1807
1939
|
},
|
1808
1940
|
async bulkUnpublish(ctx) {
|
1809
1941
|
const { userAbility } = ctx.state;
|
1810
1942
|
const { model } = ctx.params;
|
1811
1943
|
const { body } = ctx.request;
|
1812
|
-
const {
|
1944
|
+
const { documentIds } = body;
|
1813
1945
|
await validateBulkActionInput(body);
|
1814
1946
|
const documentManager2 = getService$1("document-manager");
|
1815
1947
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1816
1948
|
if (permissionChecker2.cannot.unpublish()) {
|
1817
1949
|
return ctx.forbidden();
|
1818
1950
|
}
|
1819
|
-
const
|
1820
|
-
const
|
1821
|
-
|
1822
|
-
|
1951
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1952
|
+
const entityPromises = documentIds.map(
|
1953
|
+
(documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
|
1954
|
+
);
|
1955
|
+
const entities = (await Promise.all(entityPromises)).flat();
|
1823
1956
|
for (const entity of entities) {
|
1824
1957
|
if (!entity) {
|
1825
1958
|
return ctx.notFound();
|
@@ -1828,7 +1961,8 @@ const collectionTypes = {
|
|
1828
1961
|
return ctx.forbidden();
|
1829
1962
|
}
|
1830
1963
|
}
|
1831
|
-
const
|
1964
|
+
const entitiesIds = entities.map((document) => document.documentId);
|
1965
|
+
const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
|
1832
1966
|
ctx.body = { count };
|
1833
1967
|
},
|
1834
1968
|
async unpublish(ctx) {
|
@@ -1838,7 +1972,6 @@ const collectionTypes = {
|
|
1838
1972
|
body: { discardDraft, ...body }
|
1839
1973
|
} = ctx.request;
|
1840
1974
|
const documentManager2 = getService$1("document-manager");
|
1841
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1842
1975
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1843
1976
|
if (permissionChecker2.cannot.unpublish()) {
|
1844
1977
|
return ctx.forbidden();
|
@@ -1848,7 +1981,7 @@ const collectionTypes = {
|
|
1848
1981
|
}
|
1849
1982
|
const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
|
1850
1983
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1851
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
1984
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1852
1985
|
const document = await documentManager2.findOne(id, model, {
|
1853
1986
|
populate,
|
1854
1987
|
locale,
|
@@ -1870,7 +2003,7 @@ const collectionTypes = {
|
|
1870
2003
|
ctx.body = await async.pipe(
|
1871
2004
|
(document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
|
1872
2005
|
permissionChecker2.sanitizeOutput,
|
1873
|
-
(document2) =>
|
2006
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
1874
2007
|
)(document);
|
1875
2008
|
});
|
1876
2009
|
},
|
@@ -1879,14 +2012,13 @@ const collectionTypes = {
|
|
1879
2012
|
const { id, model } = ctx.params;
|
1880
2013
|
const { body } = ctx.request;
|
1881
2014
|
const documentManager2 = getService$1("document-manager");
|
1882
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1883
2015
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1884
2016
|
if (permissionChecker2.cannot.discard()) {
|
1885
2017
|
return ctx.forbidden();
|
1886
2018
|
}
|
1887
2019
|
const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
|
1888
2020
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1889
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2021
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1890
2022
|
const document = await documentManager2.findOne(id, model, {
|
1891
2023
|
populate,
|
1892
2024
|
locale,
|
@@ -1901,14 +2033,14 @@ const collectionTypes = {
|
|
1901
2033
|
ctx.body = await async.pipe(
|
1902
2034
|
(document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
|
1903
2035
|
permissionChecker2.sanitizeOutput,
|
1904
|
-
(document2) =>
|
2036
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
1905
2037
|
)(document);
|
1906
2038
|
},
|
1907
2039
|
async bulkDelete(ctx) {
|
1908
2040
|
const { userAbility } = ctx.state;
|
1909
2041
|
const { model } = ctx.params;
|
1910
2042
|
const { query, body } = ctx.request;
|
1911
|
-
const {
|
2043
|
+
const { documentIds } = body;
|
1912
2044
|
await validateBulkActionInput(body);
|
1913
2045
|
const documentManager2 = getService$1("document-manager");
|
1914
2046
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
@@ -1916,14 +2048,22 @@ const collectionTypes = {
|
|
1916
2048
|
return ctx.forbidden();
|
1917
2049
|
}
|
1918
2050
|
const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
|
1919
|
-
const
|
1920
|
-
const
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
2051
|
+
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2052
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2053
|
+
const documentLocales = await documentManager2.findLocales(documentIds, model, {
|
2054
|
+
populate,
|
2055
|
+
locale
|
2056
|
+
});
|
2057
|
+
if (documentLocales.length === 0) {
|
2058
|
+
return ctx.notFound();
|
2059
|
+
}
|
2060
|
+
for (const document of documentLocales) {
|
2061
|
+
if (permissionChecker2.cannot.delete(document)) {
|
2062
|
+
return ctx.forbidden();
|
1924
2063
|
}
|
1925
|
-
}
|
1926
|
-
const
|
2064
|
+
}
|
2065
|
+
const localeDocumentsIds = documentLocales.map((document) => document.documentId);
|
2066
|
+
const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
|
1927
2067
|
ctx.body = { count };
|
1928
2068
|
},
|
1929
2069
|
async countDraftRelations(ctx) {
|
@@ -1936,7 +2076,7 @@ const collectionTypes = {
|
|
1936
2076
|
}
|
1937
2077
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
1938
2078
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1939
|
-
const { locale, status
|
2079
|
+
const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1940
2080
|
const entity = await documentManager2.findOne(id, model, { populate, locale, status });
|
1941
2081
|
if (!entity) {
|
1942
2082
|
return ctx.notFound();
|
@@ -1951,7 +2091,7 @@ const collectionTypes = {
|
|
1951
2091
|
},
|
1952
2092
|
async countManyEntriesDraftRelations(ctx) {
|
1953
2093
|
const { userAbility } = ctx.state;
|
1954
|
-
const ids = ctx.request.query.
|
2094
|
+
const ids = ctx.request.query.documentIds;
|
1955
2095
|
const locale = ctx.request.query.locale;
|
1956
2096
|
const { model } = ctx.params;
|
1957
2097
|
const documentManager2 = getService$1("document-manager");
|
@@ -1959,16 +2099,16 @@ const collectionTypes = {
|
|
1959
2099
|
if (permissionChecker2.cannot.read()) {
|
1960
2100
|
return ctx.forbidden();
|
1961
2101
|
}
|
1962
|
-
const
|
2102
|
+
const documents = await documentManager2.findMany(
|
1963
2103
|
{
|
1964
2104
|
filters: {
|
1965
|
-
|
2105
|
+
documentId: ids
|
1966
2106
|
},
|
1967
2107
|
locale
|
1968
2108
|
},
|
1969
2109
|
model
|
1970
2110
|
);
|
1971
|
-
if (!
|
2111
|
+
if (!documents) {
|
1972
2112
|
return ctx.notFound();
|
1973
2113
|
}
|
1974
2114
|
const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
|
@@ -2464,7 +2604,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
|
|
2464
2604
|
throw new errors.ForbiddenError();
|
2465
2605
|
}
|
2466
2606
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
|
2467
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2607
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2468
2608
|
const [documentVersion, otherDocumentVersion] = await Promise.all([
|
2469
2609
|
findDocument(sanitizedQuery, model, { locale, status: "draft" }),
|
2470
2610
|
// Find the first document to check if it exists
|
@@ -2501,12 +2641,11 @@ const singleTypes = {
|
|
2501
2641
|
const { model } = ctx.params;
|
2502
2642
|
const { query = {} } = ctx.request;
|
2503
2643
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2504
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2505
2644
|
if (permissionChecker2.cannot.read()) {
|
2506
2645
|
return ctx.forbidden();
|
2507
2646
|
}
|
2508
2647
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
|
2509
|
-
const { locale, status } = getDocumentLocaleAndStatus(query);
|
2648
|
+
const { locale, status } = await getDocumentLocaleAndStatus(query, model);
|
2510
2649
|
const version = await findDocument(permissionQuery, model, { locale, status });
|
2511
2650
|
if (!version) {
|
2512
2651
|
if (permissionChecker2.cannot.create()) {
|
@@ -2516,8 +2655,10 @@ const singleTypes = {
|
|
2516
2655
|
if (!document) {
|
2517
2656
|
return ctx.notFound();
|
2518
2657
|
}
|
2519
|
-
const { meta } = await
|
2658
|
+
const { meta } = await formatDocumentWithMetadata(
|
2659
|
+
permissionChecker2,
|
2520
2660
|
model,
|
2661
|
+
// @ts-expect-error - fix types
|
2521
2662
|
{ id: document.documentId, locale, publishedAt: null },
|
2522
2663
|
{ availableLocales: true, availableStatus: false }
|
2523
2664
|
);
|
@@ -2528,16 +2669,15 @@ const singleTypes = {
|
|
2528
2669
|
return ctx.forbidden();
|
2529
2670
|
}
|
2530
2671
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
|
2531
|
-
ctx.body = await
|
2672
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2532
2673
|
},
|
2533
2674
|
async createOrUpdate(ctx) {
|
2534
2675
|
const { userAbility } = ctx.state;
|
2535
2676
|
const { model } = ctx.params;
|
2536
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2537
2677
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2538
2678
|
const document = await createOrUpdateDocument(ctx);
|
2539
2679
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
|
2540
|
-
ctx.body = await
|
2680
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2541
2681
|
},
|
2542
2682
|
async delete(ctx) {
|
2543
2683
|
const { userAbility } = ctx.state;
|
@@ -2550,7 +2690,7 @@ const singleTypes = {
|
|
2550
2690
|
}
|
2551
2691
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
|
2552
2692
|
const populate = await buildPopulateFromQuery(sanitizedQuery, model);
|
2553
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2693
|
+
const { locale } = await getDocumentLocaleAndStatus(query, model);
|
2554
2694
|
const documentLocales = await documentManager2.findLocales(void 0, model, {
|
2555
2695
|
populate,
|
2556
2696
|
locale
|
@@ -2573,7 +2713,6 @@ const singleTypes = {
|
|
2573
2713
|
const { model } = ctx.params;
|
2574
2714
|
const { query = {} } = ctx.request;
|
2575
2715
|
const documentManager2 = getService$1("document-manager");
|
2576
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2577
2716
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2578
2717
|
if (permissionChecker2.cannot.publish()) {
|
2579
2718
|
return ctx.forbidden();
|
@@ -2588,11 +2727,12 @@ const singleTypes = {
|
|
2588
2727
|
if (permissionChecker2.cannot.publish(document)) {
|
2589
2728
|
throw new errors.ForbiddenError();
|
2590
2729
|
}
|
2591
|
-
const { locale } = getDocumentLocaleAndStatus(document);
|
2592
|
-
|
2730
|
+
const { locale } = await getDocumentLocaleAndStatus(document, model);
|
2731
|
+
const publishResult = await documentManager2.publish(document.documentId, model, { locale });
|
2732
|
+
return publishResult.at(0);
|
2593
2733
|
});
|
2594
2734
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
|
2595
|
-
ctx.body = await
|
2735
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2596
2736
|
},
|
2597
2737
|
async unpublish(ctx) {
|
2598
2738
|
const { userAbility } = ctx.state;
|
@@ -2602,7 +2742,6 @@ const singleTypes = {
|
|
2602
2742
|
query = {}
|
2603
2743
|
} = ctx.request;
|
2604
2744
|
const documentManager2 = getService$1("document-manager");
|
2605
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2606
2745
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2607
2746
|
if (permissionChecker2.cannot.unpublish()) {
|
2608
2747
|
return ctx.forbidden();
|
@@ -2611,7 +2750,7 @@ const singleTypes = {
|
|
2611
2750
|
return ctx.forbidden();
|
2612
2751
|
}
|
2613
2752
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
|
2614
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2753
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2615
2754
|
const document = await findDocument(sanitizedQuery, model, { locale });
|
2616
2755
|
if (!document) {
|
2617
2756
|
return ctx.notFound();
|
@@ -2629,7 +2768,7 @@ const singleTypes = {
|
|
2629
2768
|
ctx.body = await async.pipe(
|
2630
2769
|
(document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
|
2631
2770
|
permissionChecker2.sanitizeOutput,
|
2632
|
-
(document2) =>
|
2771
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
2633
2772
|
)(document);
|
2634
2773
|
});
|
2635
2774
|
},
|
@@ -2638,13 +2777,12 @@ const singleTypes = {
|
|
2638
2777
|
const { model } = ctx.params;
|
2639
2778
|
const { body, query = {} } = ctx.request;
|
2640
2779
|
const documentManager2 = getService$1("document-manager");
|
2641
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2642
2780
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2643
2781
|
if (permissionChecker2.cannot.discard()) {
|
2644
2782
|
return ctx.forbidden();
|
2645
2783
|
}
|
2646
2784
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
|
2647
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2785
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2648
2786
|
const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
|
2649
2787
|
if (!document) {
|
2650
2788
|
return ctx.notFound();
|
@@ -2655,7 +2793,7 @@ const singleTypes = {
|
|
2655
2793
|
ctx.body = await async.pipe(
|
2656
2794
|
(document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
|
2657
2795
|
permissionChecker2.sanitizeOutput,
|
2658
|
-
(document2) =>
|
2796
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
2659
2797
|
)(document);
|
2660
2798
|
},
|
2661
2799
|
async countDraftRelations(ctx) {
|
@@ -2664,7 +2802,7 @@ const singleTypes = {
|
|
2664
2802
|
const { query } = ctx.request;
|
2665
2803
|
const documentManager2 = getService$1("document-manager");
|
2666
2804
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2667
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2805
|
+
const { locale } = await getDocumentLocaleAndStatus(query, model);
|
2668
2806
|
if (permissionChecker2.cannot.read()) {
|
2669
2807
|
return ctx.forbidden();
|
2670
2808
|
}
|
@@ -2685,7 +2823,7 @@ const uid$1 = {
|
|
2685
2823
|
async generateUID(ctx) {
|
2686
2824
|
const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
|
2687
2825
|
const { query = {} } = ctx.request;
|
2688
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2826
|
+
const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
|
2689
2827
|
await validateUIDField(contentTypeUID, field);
|
2690
2828
|
const uidService = getService$1("uid");
|
2691
2829
|
ctx.body = {
|
@@ -2697,7 +2835,7 @@ const uid$1 = {
|
|
2697
2835
|
ctx.request.body
|
2698
2836
|
);
|
2699
2837
|
const { query = {} } = ctx.request;
|
2700
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2838
|
+
const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
|
2701
2839
|
await validateUIDField(contentTypeUID, field);
|
2702
2840
|
const uidService = getService$1("uid");
|
2703
2841
|
const isAvailable = await uidService.checkUIDAvailability({
|
@@ -3488,7 +3626,7 @@ const permission = ({ strapi: strapi2 }) => ({
|
|
3488
3626
|
await strapi2.service("admin::permission").actionProvider.registerMany(actions);
|
3489
3627
|
}
|
3490
3628
|
});
|
3491
|
-
const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils.contentTypes;
|
3629
|
+
const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils.contentTypes;
|
3492
3630
|
const { isAnyToMany } = strapiUtils.relations;
|
3493
3631
|
const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils.contentTypes.constants;
|
3494
3632
|
const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
|
@@ -3579,6 +3717,42 @@ const getDeepPopulate = (uid2, {
|
|
3579
3717
|
{}
|
3580
3718
|
);
|
3581
3719
|
};
|
3720
|
+
const getValidatableFieldsPopulate = (uid2, {
|
3721
|
+
initialPopulate = {},
|
3722
|
+
countMany = false,
|
3723
|
+
countOne = false,
|
3724
|
+
maxLevel = Infinity
|
3725
|
+
} = {}, level = 1) => {
|
3726
|
+
if (level > maxLevel) {
|
3727
|
+
return {};
|
3728
|
+
}
|
3729
|
+
const model = strapi.getModel(uid2);
|
3730
|
+
return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
|
3731
|
+
if (!getDoesAttributeRequireValidation(attribute)) {
|
3732
|
+
return populateAcc;
|
3733
|
+
}
|
3734
|
+
if (isScalarAttribute(attribute)) {
|
3735
|
+
return merge(populateAcc, {
|
3736
|
+
[attributeName]: true
|
3737
|
+
});
|
3738
|
+
}
|
3739
|
+
return merge(
|
3740
|
+
populateAcc,
|
3741
|
+
getPopulateFor(
|
3742
|
+
attributeName,
|
3743
|
+
model,
|
3744
|
+
{
|
3745
|
+
// @ts-expect-error - improve types
|
3746
|
+
initialPopulate: initialPopulate?.[attributeName],
|
3747
|
+
countMany,
|
3748
|
+
countOne,
|
3749
|
+
maxLevel
|
3750
|
+
},
|
3751
|
+
level
|
3752
|
+
)
|
3753
|
+
);
|
3754
|
+
}, {});
|
3755
|
+
};
|
3582
3756
|
const getDeepPopulateDraftCount = (uid2) => {
|
3583
3757
|
const model = strapi.getModel(uid2);
|
3584
3758
|
let hasRelations = false;
|
@@ -3586,6 +3760,10 @@ const getDeepPopulateDraftCount = (uid2) => {
|
|
3586
3760
|
const attribute = model.attributes[attributeName];
|
3587
3761
|
switch (attribute.type) {
|
3588
3762
|
case "relation": {
|
3763
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
3764
|
+
if (isMorphRelation) {
|
3765
|
+
break;
|
3766
|
+
}
|
3589
3767
|
if (isVisibleAttribute$1(model, attributeName)) {
|
3590
3768
|
populateAcc[attributeName] = {
|
3591
3769
|
count: true,
|
@@ -3600,22 +3778,24 @@ const getDeepPopulateDraftCount = (uid2) => {
|
|
3600
3778
|
attribute.component
|
3601
3779
|
);
|
3602
3780
|
if (childHasRelations) {
|
3603
|
-
populateAcc[attributeName] = {
|
3781
|
+
populateAcc[attributeName] = {
|
3782
|
+
populate: populate2
|
3783
|
+
};
|
3604
3784
|
hasRelations = true;
|
3605
3785
|
}
|
3606
3786
|
break;
|
3607
3787
|
}
|
3608
3788
|
case "dynamiczone": {
|
3609
|
-
const
|
3610
|
-
const { populate:
|
3611
|
-
if (
|
3789
|
+
const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
|
3790
|
+
const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
|
3791
|
+
if (componentHasRelations) {
|
3612
3792
|
hasRelations = true;
|
3613
|
-
return
|
3793
|
+
return { ...acc, [componentUID]: { populate: componentPopulate } };
|
3614
3794
|
}
|
3615
3795
|
return acc;
|
3616
3796
|
}, {});
|
3617
|
-
if (!isEmpty(
|
3618
|
-
populateAcc[attributeName] = {
|
3797
|
+
if (!isEmpty(dzPopulateFragment)) {
|
3798
|
+
populateAcc[attributeName] = { on: dzPopulateFragment };
|
3619
3799
|
}
|
3620
3800
|
break;
|
3621
3801
|
}
|
@@ -3807,41 +3987,70 @@ const AVAILABLE_STATUS_FIELDS = [
|
|
3807
3987
|
"updatedBy",
|
3808
3988
|
"status"
|
3809
3989
|
];
|
3810
|
-
const AVAILABLE_LOCALES_FIELDS = [
|
3990
|
+
const AVAILABLE_LOCALES_FIELDS = [
|
3991
|
+
"id",
|
3992
|
+
"locale",
|
3993
|
+
"updatedAt",
|
3994
|
+
"createdAt",
|
3995
|
+
"status",
|
3996
|
+
"publishedAt",
|
3997
|
+
"documentId"
|
3998
|
+
];
|
3811
3999
|
const CONTENT_MANAGER_STATUS = {
|
3812
4000
|
PUBLISHED: "published",
|
3813
4001
|
DRAFT: "draft",
|
3814
4002
|
MODIFIED: "modified"
|
3815
4003
|
};
|
3816
|
-
const
|
3817
|
-
if (!
|
4004
|
+
const getIsVersionLatestModification = (version, otherVersion) => {
|
4005
|
+
if (!version || !version.updatedAt) {
|
3818
4006
|
return false;
|
3819
4007
|
}
|
3820
|
-
const
|
3821
|
-
const
|
3822
|
-
|
3823
|
-
return difference2 <= threshold;
|
4008
|
+
const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
|
4009
|
+
const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
|
4010
|
+
return versionUpdatedAt > otherUpdatedAt;
|
3824
4011
|
};
|
3825
4012
|
const documentMetadata = ({ strapi: strapi2 }) => ({
|
3826
4013
|
/**
|
3827
4014
|
* Returns available locales of a document for the current status
|
3828
4015
|
*/
|
3829
|
-
getAvailableLocales(uid2, version, allVersions) {
|
4016
|
+
async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
|
3830
4017
|
const versionsByLocale = groupBy("locale", allVersions);
|
3831
4018
|
delete versionsByLocale[version.locale];
|
3832
|
-
|
3833
|
-
|
3834
|
-
|
4019
|
+
const model = strapi2.getModel(uid2);
|
4020
|
+
const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
|
4021
|
+
const traversalFunction = async (localeVersion) => traverseEntity(
|
4022
|
+
({ key }, { remove }) => {
|
4023
|
+
if (keysToKeep.includes(key)) {
|
4024
|
+
return;
|
4025
|
+
}
|
4026
|
+
remove(key);
|
4027
|
+
},
|
4028
|
+
{ schema: model, getModel: strapi2.getModel.bind(strapi2) },
|
4029
|
+
// @ts-expect-error fix types DocumentVersion incompatible with Data
|
4030
|
+
localeVersion
|
4031
|
+
);
|
4032
|
+
const mappingResult = await async.map(
|
4033
|
+
Object.values(versionsByLocale),
|
4034
|
+
async (localeVersions) => {
|
4035
|
+
const mappedLocaleVersions = await async.map(
|
4036
|
+
localeVersions,
|
4037
|
+
traversalFunction
|
4038
|
+
);
|
4039
|
+
if (!contentTypes$1.hasDraftAndPublish(model)) {
|
4040
|
+
return mappedLocaleVersions[0];
|
4041
|
+
}
|
4042
|
+
const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
|
4043
|
+
const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
|
4044
|
+
if (!draftVersion) {
|
4045
|
+
return;
|
4046
|
+
}
|
4047
|
+
return {
|
4048
|
+
...draftVersion,
|
4049
|
+
status: this.getStatus(draftVersion, otherVersions)
|
4050
|
+
};
|
3835
4051
|
}
|
3836
|
-
|
3837
|
-
|
3838
|
-
if (!draftVersion)
|
3839
|
-
return;
|
3840
|
-
return {
|
3841
|
-
...pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
|
3842
|
-
status: this.getStatus(draftVersion, otherVersions)
|
3843
|
-
};
|
3844
|
-
}).filter(Boolean);
|
4052
|
+
);
|
4053
|
+
return mappingResult.filter(Boolean);
|
3845
4054
|
},
|
3846
4055
|
/**
|
3847
4056
|
* Returns available status of a document for the current locale
|
@@ -3879,26 +4088,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3879
4088
|
});
|
3880
4089
|
},
|
3881
4090
|
getStatus(version, otherDocumentStatuses) {
|
3882
|
-
|
3883
|
-
|
3884
|
-
|
4091
|
+
let draftVersion;
|
4092
|
+
let publishedVersion;
|
4093
|
+
if (version.publishedAt) {
|
4094
|
+
publishedVersion = version;
|
4095
|
+
} else {
|
4096
|
+
draftVersion = version;
|
3885
4097
|
}
|
3886
|
-
|
3887
|
-
|
3888
|
-
|
3889
|
-
|
3890
|
-
|
4098
|
+
const otherVersion = otherDocumentStatuses?.at(0);
|
4099
|
+
if (otherVersion?.publishedAt) {
|
4100
|
+
publishedVersion = otherVersion;
|
4101
|
+
} else if (otherVersion) {
|
4102
|
+
draftVersion = otherVersion;
|
3891
4103
|
}
|
3892
|
-
if (
|
4104
|
+
if (!draftVersion)
|
3893
4105
|
return CONTENT_MANAGER_STATUS.PUBLISHED;
|
3894
|
-
|
3895
|
-
|
4106
|
+
if (!publishedVersion)
|
4107
|
+
return CONTENT_MANAGER_STATUS.DRAFT;
|
4108
|
+
const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
|
4109
|
+
return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
|
3896
4110
|
},
|
4111
|
+
// TODO is it necessary to return metadata on every page of the CM
|
4112
|
+
// We could refactor this so the locales are only loaded when they're
|
4113
|
+
// needed. e.g. in the bulk locale action modal.
|
3897
4114
|
async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
|
4115
|
+
const populate = getValidatableFieldsPopulate(uid2);
|
3898
4116
|
const versions = await strapi2.db.query(uid2).findMany({
|
3899
4117
|
where: { documentId: version.documentId },
|
3900
|
-
select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
|
3901
4118
|
populate: {
|
4119
|
+
// Populate only fields that require validation for bulk locale actions
|
4120
|
+
...populate,
|
4121
|
+
// NOTE: creator fields are selected in this way to avoid exposing sensitive data
|
3902
4122
|
createdBy: {
|
3903
4123
|
select: ["id", "firstname", "lastname", "email"]
|
3904
4124
|
},
|
@@ -3907,7 +4127,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3907
4127
|
}
|
3908
4128
|
}
|
3909
4129
|
});
|
3910
|
-
const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
|
4130
|
+
const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
|
3911
4131
|
const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
|
3912
4132
|
return {
|
3913
4133
|
availableLocales: availableLocalesResult,
|
@@ -3920,8 +4140,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3920
4140
|
* - Available status of the document for the current locale
|
3921
4141
|
*/
|
3922
4142
|
async formatDocumentWithMetadata(uid2, document, opts = {}) {
|
3923
|
-
if (!document)
|
3924
|
-
return
|
4143
|
+
if (!document) {
|
4144
|
+
return {
|
4145
|
+
data: document,
|
4146
|
+
meta: {
|
4147
|
+
availableLocales: [],
|
4148
|
+
availableStatus: []
|
4149
|
+
}
|
4150
|
+
};
|
4151
|
+
}
|
3925
4152
|
const hasDraftAndPublish = contentTypes$1.hasDraftAndPublish(strapi2.getModel(uid2));
|
3926
4153
|
if (!hasDraftAndPublish) {
|
3927
4154
|
opts.availableStatus = false;
|
@@ -3971,26 +4198,9 @@ const sumDraftCounts = (entity, uid2) => {
|
|
3971
4198
|
}, 0);
|
3972
4199
|
};
|
3973
4200
|
const { ApplicationError } = errors;
|
3974
|
-
const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
|
3975
4201
|
const { PUBLISHED_AT_ATTRIBUTE } = contentTypes$1.constants;
|
3976
4202
|
const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE);
|
3977
4203
|
const omitIdField = omit("id");
|
3978
|
-
const emitEvent = async (uid2, event, document) => {
|
3979
|
-
const modelDef = strapi.getModel(uid2);
|
3980
|
-
const sanitizedDocument = await sanitize.sanitizers.defaultSanitizeOutput(
|
3981
|
-
{
|
3982
|
-
schema: modelDef,
|
3983
|
-
getModel(uid22) {
|
3984
|
-
return strapi.getModel(uid22);
|
3985
|
-
}
|
3986
|
-
},
|
3987
|
-
document
|
3988
|
-
);
|
3989
|
-
strapi.eventHub.emit(event, {
|
3990
|
-
model: modelDef.modelName,
|
3991
|
-
entry: sanitizedDocument
|
3992
|
-
});
|
3993
|
-
};
|
3994
4204
|
const documentManager = ({ strapi: strapi2 }) => {
|
3995
4205
|
return {
|
3996
4206
|
async findOne(id, uid2, opts = {}) {
|
@@ -4009,6 +4219,9 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4009
4219
|
} else if (opts.locale && opts.locale !== "*") {
|
4010
4220
|
where.locale = opts.locale;
|
4011
4221
|
}
|
4222
|
+
if (typeof opts.isPublished === "boolean") {
|
4223
|
+
where.publishedAt = { $notNull: opts.isPublished };
|
4224
|
+
}
|
4012
4225
|
return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
|
4013
4226
|
},
|
4014
4227
|
async findMany(opts, uid2) {
|
@@ -4016,20 +4229,16 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4016
4229
|
return strapi2.documents(uid2).findMany(params);
|
4017
4230
|
},
|
4018
4231
|
async findPage(opts, uid2) {
|
4019
|
-
const
|
4020
|
-
|
4232
|
+
const params = pagination.withDefaultPagination(opts || {}, {
|
4233
|
+
maxLimit: 1e3
|
4234
|
+
});
|
4021
4235
|
const [documents, total = 0] = await Promise.all([
|
4022
|
-
strapi2.documents(uid2).findMany(
|
4023
|
-
strapi2.documents(uid2).count(
|
4236
|
+
strapi2.documents(uid2).findMany(params),
|
4237
|
+
strapi2.documents(uid2).count(params)
|
4024
4238
|
]);
|
4025
4239
|
return {
|
4026
4240
|
results: documents,
|
4027
|
-
pagination:
|
4028
|
-
page,
|
4029
|
-
pageSize,
|
4030
|
-
pageCount: Math.ceil(total / pageSize),
|
4031
|
-
total
|
4032
|
-
}
|
4241
|
+
pagination: pagination.transformPagedPaginationInfo(params, total)
|
4033
4242
|
};
|
4034
4243
|
},
|
4035
4244
|
async create(uid2, opts = {}) {
|
@@ -4046,10 +4255,7 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4046
4255
|
async clone(id, body, uid2) {
|
4047
4256
|
const populate = await buildDeepPopulate(uid2);
|
4048
4257
|
const params = {
|
4049
|
-
data:
|
4050
|
-
...omitIdField(body),
|
4051
|
-
[PUBLISHED_AT_ATTRIBUTE]: null
|
4052
|
-
},
|
4258
|
+
data: omitIdField(body),
|
4053
4259
|
populate
|
4054
4260
|
};
|
4055
4261
|
return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
|
@@ -4075,70 +4281,36 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4075
4281
|
return {};
|
4076
4282
|
},
|
4077
4283
|
// FIXME: handle relations
|
4078
|
-
async deleteMany(
|
4079
|
-
const
|
4080
|
-
|
4081
|
-
|
4082
|
-
}
|
4083
|
-
return { count: docs.length };
|
4284
|
+
async deleteMany(documentIds, uid2, opts = {}) {
|
4285
|
+
const deletedEntries = await strapi2.db.transaction(async () => {
|
4286
|
+
return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
|
4287
|
+
});
|
4288
|
+
return { count: deletedEntries.length };
|
4084
4289
|
},
|
4085
4290
|
async publish(id, uid2, opts = {}) {
|
4086
4291
|
const populate = await buildDeepPopulate(uid2);
|
4087
4292
|
const params = { ...opts, populate };
|
4088
|
-
return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries
|
4293
|
+
return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
|
4089
4294
|
},
|
4090
|
-
async publishMany(
|
4091
|
-
|
4092
|
-
|
4093
|
-
|
4094
|
-
|
4095
|
-
|
4096
|
-
|
4097
|
-
strapi2.getModel(uid2),
|
4098
|
-
document,
|
4099
|
-
void 0,
|
4100
|
-
// @ts-expect-error - FIXME: entity here is unnecessary
|
4101
|
-
document
|
4102
|
-
);
|
4103
|
-
})
|
4104
|
-
);
|
4105
|
-
const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
|
4106
|
-
const filters = { id: { $in: entitiesToPublish } };
|
4107
|
-
const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
|
4108
|
-
const populate = await buildDeepPopulate(uid2);
|
4109
|
-
const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
|
4110
|
-
where: filters,
|
4111
|
-
data
|
4112
|
-
});
|
4113
|
-
const publishedEntities = await strapi2.db.query(uid2).findMany({
|
4114
|
-
where: filters,
|
4115
|
-
populate
|
4295
|
+
async publishMany(uid2, documentIds, locale) {
|
4296
|
+
return strapi2.db.transaction(async () => {
|
4297
|
+
const results = await Promise.all(
|
4298
|
+
documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
|
4299
|
+
);
|
4300
|
+
const publishedEntitiesCount = results.flat().filter(Boolean).length;
|
4301
|
+
return publishedEntitiesCount;
|
4116
4302
|
});
|
4117
|
-
await Promise.all(
|
4118
|
-
publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
|
4119
|
-
);
|
4120
|
-
return publishedEntitiesCount;
|
4121
4303
|
},
|
4122
|
-
async unpublishMany(
|
4123
|
-
|
4124
|
-
return
|
4125
|
-
|
4126
|
-
|
4127
|
-
|
4128
|
-
|
4129
|
-
const populate = await buildDeepPopulate(uid2);
|
4130
|
-
const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
|
4131
|
-
where: filters,
|
4132
|
-
data
|
4133
|
-
});
|
4134
|
-
const unpublishedEntities = await strapi2.db.query(uid2).findMany({
|
4135
|
-
where: filters,
|
4136
|
-
populate
|
4304
|
+
async unpublishMany(documentIds, uid2, opts = {}) {
|
4305
|
+
const unpublishedEntries = await strapi2.db.transaction(async () => {
|
4306
|
+
return Promise.all(
|
4307
|
+
documentIds.map(
|
4308
|
+
(id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
|
4309
|
+
)
|
4310
|
+
);
|
4137
4311
|
});
|
4138
|
-
|
4139
|
-
|
4140
|
-
);
|
4141
|
-
return unpublishedEntitiesCount;
|
4312
|
+
const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
|
4313
|
+
return { count: unpublishedEntitiesCount };
|
4142
4314
|
},
|
4143
4315
|
async unpublish(id, uid2, opts = {}) {
|
4144
4316
|
const populate = await buildDeepPopulate(uid2);
|
@@ -4163,16 +4335,20 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4163
4335
|
}
|
4164
4336
|
return sumDraftCounts(document, uid2);
|
4165
4337
|
},
|
4166
|
-
async countManyEntriesDraftRelations(
|
4338
|
+
async countManyEntriesDraftRelations(documentIds, uid2, locale) {
|
4167
4339
|
const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
|
4168
4340
|
if (!hasRelations) {
|
4169
4341
|
return 0;
|
4170
4342
|
}
|
4343
|
+
let localeFilter = {};
|
4344
|
+
if (locale) {
|
4345
|
+
localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
|
4346
|
+
}
|
4171
4347
|
const entities = await strapi2.db.query(uid2).findMany({
|
4172
4348
|
populate,
|
4173
4349
|
where: {
|
4174
|
-
|
4175
|
-
...
|
4350
|
+
documentId: { $in: documentIds },
|
4351
|
+
...localeFilter
|
4176
4352
|
}
|
4177
4353
|
});
|
4178
4354
|
const totalNumberDraftRelations = entities.reduce(
|