@strapi/content-manager 0.0.0-experimental.a65a85fdea97faae8679d3ffc5f9d79af61abd26 → 0.0.0-experimental.a6728ad43ac70ae19dabb624dbfca1f2d9610a86
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-DJ5voqEK.mjs} +3 -3
- package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs.map → ComponentConfigurationPage-DJ5voqEK.mjs.map} +1 -1
- package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js → ComponentConfigurationPage-_6osrv39.js} +3 -3
- package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js.map → ComponentConfigurationPage-_6osrv39.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-CUcGHHvQ.mjs → EditConfigurationPage-CZofxSLy.mjs} +3 -3
- package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs.map → EditConfigurationPage-CZofxSLy.mjs.map} +1 -1
- package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js → EditConfigurationPage-ZN3s568V.js} +3 -3
- package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js.map → EditConfigurationPage-ZN3s568V.js.map} +1 -1
- package/dist/_chunks/{EditViewPage-CzOT5Kpj.js → EditViewPage-Co2IKQZH.js} +58 -49
- package/dist/_chunks/EditViewPage-Co2IKQZH.js.map +1 -0
- package/dist/_chunks/{EditViewPage-Bm8lgcm6.mjs → EditViewPage-HYljoEY7.mjs} +59 -48
- package/dist/_chunks/EditViewPage-HYljoEY7.mjs.map +1 -0
- package/dist/_chunks/{Field-Dlh0uGnL.mjs → Field-BOPUMZ1u.mjs} +995 -795
- package/dist/_chunks/Field-BOPUMZ1u.mjs.map +1 -0
- package/dist/_chunks/{Field-Caef4JjM.js → Field-G9CkFUtP.js} +1041 -842
- package/dist/_chunks/Field-G9CkFUtP.js.map +1 -0
- package/dist/_chunks/{Form-EnaQL_6L.mjs → Form-CDwNp7pU.mjs} +69 -48
- package/dist/_chunks/Form-CDwNp7pU.mjs.map +1 -0
- package/dist/_chunks/{Form-BzuAjtRq.js → Form-crsbkGxI.js} +68 -48
- package/dist/_chunks/Form-crsbkGxI.js.map +1 -0
- package/dist/_chunks/{History-D6sbCJvo.mjs → History-BDZrgfZ3.mjs} +151 -57
- package/dist/_chunks/History-BDZrgfZ3.mjs.map +1 -0
- package/dist/_chunks/{History-C17LiyRg.js → History-CWcM9HnW.js} +151 -58
- package/dist/_chunks/History-CWcM9HnW.js.map +1 -0
- package/dist/_chunks/{ListConfigurationPage-Ce4qs7qE.mjs → ListConfigurationPage-BZ3ScUna.mjs} +67 -57
- package/dist/_chunks/ListConfigurationPage-BZ3ScUna.mjs.map +1 -0
- package/dist/_chunks/{ListConfigurationPage-Dks5SX6f.js → ListConfigurationPage-DGzoQD_I.js} +70 -61
- package/dist/_chunks/ListConfigurationPage-DGzoQD_I.js.map +1 -0
- package/dist/_chunks/{ListViewPage-BwrZrPsh.js → ListViewPage-BBAC9aPu.js} +132 -139
- package/dist/_chunks/ListViewPage-BBAC9aPu.js.map +1 -0
- package/dist/_chunks/{ListViewPage-Be7S5aKL.mjs → ListViewPage-CsX7tWx-.mjs} +129 -136
- package/dist/_chunks/ListViewPage-CsX7tWx-.mjs.map +1 -0
- package/dist/_chunks/{NoContentTypePage-Cu5r1-JT.js → NoContentTypePage-CwVDx_YC.js} +5 -5
- package/dist/_chunks/NoContentTypePage-CwVDx_YC.js.map +1 -0
- package/dist/_chunks/{NoContentTypePage-CIPmYQMm.mjs → NoContentTypePage-LClTUPWs.mjs} +7 -7
- package/dist/_chunks/NoContentTypePage-LClTUPWs.mjs.map +1 -0
- package/dist/_chunks/{NoPermissionsPage-C-j6TEUF.js → NoPermissionsPage-D2iWw-sn.js} +4 -5
- package/dist/_chunks/NoPermissionsPage-D2iWw-sn.js.map +1 -0
- package/dist/_chunks/{NoPermissionsPage-DhJ7LYrr.mjs → NoPermissionsPage-S4Re3FwO.mjs} +5 -6
- package/dist/_chunks/NoPermissionsPage-S4Re3FwO.mjs.map +1 -0
- package/dist/_chunks/{Relations-CY7AtkDA.mjs → Relations-Dmv0Tpe5.mjs} +67 -57
- package/dist/_chunks/Relations-Dmv0Tpe5.mjs.map +1 -0
- package/dist/_chunks/{Relations-Czs-uZ-s.js → Relations-jwuTFGOV.js} +71 -62
- package/dist/_chunks/Relations-jwuTFGOV.js.map +1 -0
- package/dist/_chunks/{en-C-V1_90f.js → en-BlhnxQfj.js} +17 -9
- package/dist/_chunks/{en-C-V1_90f.js.map → en-BlhnxQfj.js.map} +1 -1
- package/dist/_chunks/{en-MBPul9Su.mjs → en-C8YBvRrK.mjs} +17 -9
- package/dist/_chunks/{en-MBPul9Su.mjs.map → en-C8YBvRrK.mjs.map} +1 -1
- package/dist/_chunks/{index-DNVx8ssZ.mjs → index-BmUAydCA.mjs} +1715 -813
- package/dist/_chunks/index-BmUAydCA.mjs.map +1 -0
- package/dist/_chunks/{index-X_2tafck.js → index-CBX6KyXv.js} +1815 -914
- package/dist/_chunks/index-CBX6KyXv.js.map +1 -0
- package/dist/_chunks/{layout-Dnh0PNp9.mjs → layout-ClP-DC72.mjs} +47 -29
- package/dist/_chunks/layout-ClP-DC72.mjs.map +1 -0
- package/dist/_chunks/{layout-dBc7wN7L.js → layout-CxxkX9jY.js} +47 -31
- package/dist/_chunks/layout-CxxkX9jY.js.map +1 -0
- package/dist/_chunks/{relations-4pHtBrHJ.js → relations-DIjTADIu.js} +2 -2
- package/dist/_chunks/{relations-4pHtBrHJ.js.map → relations-DIjTADIu.js.map} +1 -1
- package/dist/_chunks/{relations-Dx7tMKJN.mjs → relations-op89RClB.mjs} +2 -2
- package/dist/_chunks/{relations-Dx7tMKJN.mjs.map → relations-op89RClB.mjs.map} +1 -1
- package/dist/_chunks/useDebounce-CtcjDB3L.js +28 -0
- package/dist/_chunks/useDebounce-CtcjDB3L.js.map +1 -0
- package/dist/_chunks/useDebounce-DmuSJIF3.mjs +29 -0
- package/dist/_chunks/useDebounce-DmuSJIF3.mjs.map +1 -0
- package/dist/_chunks/useDragAndDrop-DdHgKsqq.mjs.map +1 -1
- package/dist/_chunks/useDragAndDrop-J0TUUbR6.js.map +1 -1
- package/dist/admin/index.js +3 -1
- package/dist/admin/index.js.map +1 -1
- package/dist/admin/index.mjs +9 -7
- 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 +2 -1
- 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 +35 -9
- 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 +16 -53
- package/dist/admin/src/pages/EditView/components/Header.d.ts +10 -11
- 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 +31 -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 +5 -7
- package/dist/server/index.js +648 -447
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +656 -455
- 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/relations.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 +42 -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/policies/hasPermissions.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/permission-checker.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 +17 -7
- 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 +16 -17
- 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/strapi-server.js +0 -3
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,43 +112,70 @@ 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");
|
176
|
+
const i18nContentTypeService = strapi2.plugin("i18n")?.service("content-types");
|
151
177
|
const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
|
178
|
+
const isLocalizedContentType = (model) => i18nContentTypeService ? i18nContentTypeService.isLocalizedContentType(model) : false;
|
152
179
|
const getLocaleDictionary = async () => {
|
153
180
|
if (!localesService)
|
154
181
|
return {};
|
@@ -161,25 +188,39 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
161
188
|
{}
|
162
189
|
);
|
163
190
|
};
|
191
|
+
const getRetentionDays = () => {
|
192
|
+
const featureConfig = strapi2.ee.features.get("cms-content-history");
|
193
|
+
const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
|
194
|
+
const userRetentionDays = strapi2.config.get("admin.history.retentionDays");
|
195
|
+
if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
|
196
|
+
return userRetentionDays;
|
197
|
+
}
|
198
|
+
return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
|
199
|
+
};
|
164
200
|
const getVersionStatus = async (contentTypeUid, document) => {
|
165
201
|
const documentMetadataService = strapi2.plugin("content-manager").service("document-metadata");
|
166
202
|
const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
|
167
203
|
return documentMetadataService.getStatus(document, meta.availableStatus);
|
168
204
|
};
|
169
|
-
const getDeepPopulate2 = (uid2) => {
|
205
|
+
const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
|
170
206
|
const model = strapi2.getModel(uid2);
|
171
207
|
const attributes = Object.entries(model.attributes);
|
208
|
+
const fieldSelector = useDatabaseSyntax ? "select" : "fields";
|
172
209
|
return attributes.reduce((acc, [attributeName, attribute]) => {
|
173
210
|
switch (attribute.type) {
|
174
211
|
case "relation": {
|
212
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
213
|
+
if (isMorphRelation) {
|
214
|
+
break;
|
215
|
+
}
|
175
216
|
const isVisible2 = contentTypes$1.isVisibleAttribute(model, attributeName);
|
176
217
|
if (isVisible2) {
|
177
|
-
acc[attributeName] = {
|
218
|
+
acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
|
178
219
|
}
|
179
220
|
break;
|
180
221
|
}
|
181
222
|
case "media": {
|
182
|
-
acc[attributeName] = {
|
223
|
+
acc[attributeName] = { [fieldSelector]: ["id"] };
|
183
224
|
break;
|
184
225
|
}
|
185
226
|
case "component": {
|
@@ -202,80 +243,69 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
202
243
|
return acc;
|
203
244
|
}, {});
|
204
245
|
};
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
|
212
|
-
return next();
|
246
|
+
const buildMediaResponse = async (values) => {
|
247
|
+
return values.slice(0, 25).reduce(
|
248
|
+
async (currentRelationDataPromise, entry) => {
|
249
|
+
const currentRelationData = await currentRelationDataPromise;
|
250
|
+
if (!entry) {
|
251
|
+
return currentRelationData;
|
213
252
|
}
|
214
|
-
|
215
|
-
|
253
|
+
const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
|
254
|
+
if (relatedEntry) {
|
255
|
+
currentRelationData.results.push(relatedEntry);
|
256
|
+
} else {
|
257
|
+
currentRelationData.meta.missingCount += 1;
|
216
258
|
}
|
217
|
-
|
218
|
-
|
219
|
-
|
259
|
+
return currentRelationData;
|
260
|
+
},
|
261
|
+
Promise.resolve({
|
262
|
+
results: [],
|
263
|
+
meta: { missingCount: 0 }
|
264
|
+
})
|
265
|
+
);
|
266
|
+
};
|
267
|
+
const buildRelationReponse = async (values, attributeSchema) => {
|
268
|
+
return values.slice(0, 25).reduce(
|
269
|
+
async (currentRelationDataPromise, entry) => {
|
270
|
+
const currentRelationData = await currentRelationDataPromise;
|
271
|
+
if (!entry) {
|
272
|
+
return currentRelationData;
|
220
273
|
}
|
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
|
-
});
|
274
|
+
const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
|
275
|
+
if (relatedEntry) {
|
276
|
+
currentRelationData.results.push({
|
277
|
+
...relatedEntry,
|
278
|
+
status: await getVersionStatus(attributeSchema.target, relatedEntry)
|
256
279
|
});
|
257
|
-
}
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
280
|
+
} else {
|
281
|
+
currentRelationData.meta.missingCount += 1;
|
282
|
+
}
|
283
|
+
return currentRelationData;
|
284
|
+
},
|
285
|
+
Promise.resolve({
|
286
|
+
results: [],
|
287
|
+
meta: { missingCount: 0 }
|
288
|
+
})
|
289
|
+
);
|
290
|
+
};
|
291
|
+
return {
|
292
|
+
getSchemaAttributesDiff,
|
293
|
+
getRelationRestoreValue,
|
294
|
+
getMediaRestoreValue,
|
295
|
+
getDefaultLocale,
|
296
|
+
isLocalizedContentType,
|
297
|
+
getLocaleDictionary,
|
298
|
+
getRetentionDays,
|
299
|
+
getVersionStatus,
|
300
|
+
getDeepPopulate: getDeepPopulate2,
|
301
|
+
buildMediaResponse,
|
302
|
+
buildRelationReponse
|
303
|
+
};
|
304
|
+
};
|
305
|
+
const createHistoryService = ({ strapi: strapi2 }) => {
|
306
|
+
const query = strapi2.db.query(HISTORY_VERSION_UID);
|
307
|
+
const serviceUtils = createServiceUtils({ strapi: strapi2 });
|
308
|
+
return {
|
279
309
|
async createVersion(historyVersionData) {
|
280
310
|
await query.create({
|
281
311
|
data: {
|
@@ -286,8 +316,14 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
286
316
|
});
|
287
317
|
},
|
288
318
|
async findVersionsPage(params) {
|
289
|
-
const
|
290
|
-
const
|
319
|
+
const model = strapi2.getModel(params.query.contentType);
|
320
|
+
const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
|
321
|
+
const defaultLocale = await serviceUtils.getDefaultLocale();
|
322
|
+
let locale = null;
|
323
|
+
if (isLocalizedContentType) {
|
324
|
+
locale = params.query.locale || defaultLocale;
|
325
|
+
}
|
326
|
+
const [{ results, pagination: pagination2 }, localeDictionary] = await Promise.all([
|
291
327
|
query.findPage({
|
292
328
|
...params.query,
|
293
329
|
where: {
|
@@ -300,78 +336,34 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
300
336
|
populate: ["createdBy"],
|
301
337
|
orderBy: [{ createdAt: "desc" }]
|
302
338
|
}),
|
303
|
-
getLocaleDictionary()
|
339
|
+
serviceUtils.getLocaleDictionary()
|
304
340
|
]);
|
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
341
|
const populateEntryRelations = async (entry) => {
|
361
342
|
const entryWithRelations = await Object.entries(entry.schema).reduce(
|
362
343
|
async (currentDataWithRelations, [attributeKey, attributeSchema]) => {
|
363
344
|
const attributeValue = entry.data[attributeKey];
|
364
345
|
const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
|
365
346
|
if (attributeSchema.type === "media") {
|
347
|
+
const permissionChecker2 = getService$1("permission-checker").create({
|
348
|
+
userAbility: params.state.userAbility,
|
349
|
+
model: "plugin::upload.file"
|
350
|
+
});
|
351
|
+
const response = await serviceUtils.buildMediaResponse(attributeValues);
|
352
|
+
const sanitizedResults = await Promise.all(
|
353
|
+
response.results.map((media) => permissionChecker2.sanitizeOutput(media))
|
354
|
+
);
|
366
355
|
return {
|
367
356
|
...await currentDataWithRelations,
|
368
|
-
[attributeKey]:
|
357
|
+
[attributeKey]: {
|
358
|
+
results: sanitizedResults,
|
359
|
+
meta: response.meta
|
360
|
+
}
|
369
361
|
};
|
370
362
|
}
|
371
363
|
if (attributeSchema.type === "relation" && attributeSchema.relation !== "morphToOne" && attributeSchema.relation !== "morphToMany") {
|
372
364
|
if (attributeSchema.target === "admin::user") {
|
373
365
|
const adminUsers = await Promise.all(
|
374
|
-
attributeValues.map(
|
366
|
+
attributeValues.map((userToPopulate) => {
|
375
367
|
if (userToPopulate == null) {
|
376
368
|
return null;
|
377
369
|
}
|
@@ -388,9 +380,23 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
388
380
|
[attributeKey]: adminUsers
|
389
381
|
};
|
390
382
|
}
|
383
|
+
const permissionChecker2 = getService$1("permission-checker").create({
|
384
|
+
userAbility: params.state.userAbility,
|
385
|
+
model: attributeSchema.target
|
386
|
+
});
|
387
|
+
const response = await serviceUtils.buildRelationReponse(
|
388
|
+
attributeValues,
|
389
|
+
attributeSchema
|
390
|
+
);
|
391
|
+
const sanitizedResults = await Promise.all(
|
392
|
+
response.results.map((media) => permissionChecker2.sanitizeOutput(media))
|
393
|
+
);
|
391
394
|
return {
|
392
395
|
...await currentDataWithRelations,
|
393
|
-
[attributeKey]:
|
396
|
+
[attributeKey]: {
|
397
|
+
results: sanitizedResults,
|
398
|
+
meta: response.meta
|
399
|
+
}
|
394
400
|
};
|
395
401
|
}
|
396
402
|
return currentDataWithRelations;
|
@@ -405,7 +411,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
405
411
|
...result,
|
406
412
|
data: await populateEntryRelations(result),
|
407
413
|
meta: {
|
408
|
-
unknownAttributes: getSchemaAttributesDiff(
|
414
|
+
unknownAttributes: serviceUtils.getSchemaAttributesDiff(
|
409
415
|
result.schema,
|
410
416
|
strapi2.getModel(params.query.contentType).attributes
|
411
417
|
)
|
@@ -416,13 +422,16 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
416
422
|
);
|
417
423
|
return {
|
418
424
|
results: formattedResults,
|
419
|
-
pagination
|
425
|
+
pagination: pagination2
|
420
426
|
};
|
421
427
|
},
|
422
428
|
async restoreVersion(versionId) {
|
423
429
|
const version = await query.findOne({ where: { id: versionId } });
|
424
430
|
const contentTypeSchemaAttributes = strapi2.getModel(version.contentType).attributes;
|
425
|
-
const schemaDiff = getSchemaAttributesDiff(
|
431
|
+
const schemaDiff = serviceUtils.getSchemaAttributesDiff(
|
432
|
+
version.schema,
|
433
|
+
contentTypeSchemaAttributes
|
434
|
+
);
|
426
435
|
const dataWithoutAddedAttributes = Object.keys(schemaDiff.added).reduce(
|
427
436
|
(currentData, addedKey) => {
|
428
437
|
currentData[addedKey] = null;
|
@@ -435,61 +444,26 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
435
444
|
FIELDS_TO_IGNORE,
|
436
445
|
contentTypeSchemaAttributes
|
437
446
|
);
|
438
|
-
const
|
439
|
-
|
440
|
-
|
441
|
-
const
|
442
|
-
if (
|
447
|
+
const reducer = async.reduce(Object.entries(sanitizedSchemaAttributes));
|
448
|
+
const dataWithoutMissingRelations = await reducer(
|
449
|
+
async (previousRelationAttributes, [name, attribute]) => {
|
450
|
+
const versionRelationData = version.data[name];
|
451
|
+
if (!versionRelationData) {
|
443
452
|
return previousRelationAttributes;
|
444
453
|
}
|
445
454
|
if (attribute.type === "relation" && // TODO: handle polymorphic relations
|
446
455
|
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
|
-
}
|
456
|
+
const data2 = await serviceUtils.getRelationRestoreValue(versionRelationData, attribute);
|
457
|
+
previousRelationAttributes[name] = data2;
|
471
458
|
}
|
472
459
|
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
|
-
}
|
460
|
+
const data2 = await serviceUtils.getMediaRestoreValue(versionRelationData, attribute);
|
461
|
+
previousRelationAttributes[name] = data2;
|
488
462
|
}
|
489
463
|
return previousRelationAttributes;
|
490
464
|
},
|
491
465
|
// Clone to avoid mutating the original version data
|
492
|
-
|
466
|
+
structuredClone(dataWithoutAddedAttributes)
|
493
467
|
);
|
494
468
|
const data = omit(["id", ...Object.keys(schemaDiff.removed)], dataWithoutMissingRelations);
|
495
469
|
const restoredDocument = await strapi2.documents(version.contentType).update({
|
@@ -504,8 +478,120 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
504
478
|
}
|
505
479
|
};
|
506
480
|
};
|
481
|
+
const shouldCreateHistoryVersion = (context) => {
|
482
|
+
if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
|
483
|
+
return false;
|
484
|
+
}
|
485
|
+
if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
|
486
|
+
return false;
|
487
|
+
}
|
488
|
+
if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
|
489
|
+
return false;
|
490
|
+
}
|
491
|
+
if (!context.contentType.uid.startsWith("api::")) {
|
492
|
+
return false;
|
493
|
+
}
|
494
|
+
return true;
|
495
|
+
};
|
496
|
+
const getSchemas = (uid2) => {
|
497
|
+
const attributesSchema = strapi.getModel(uid2).attributes;
|
498
|
+
const componentsSchemas = Object.keys(attributesSchema).reduce(
|
499
|
+
(currentComponentSchemas, key) => {
|
500
|
+
const fieldSchema = attributesSchema[key];
|
501
|
+
if (fieldSchema.type === "component") {
|
502
|
+
const componentSchema = strapi.getModel(fieldSchema.component).attributes;
|
503
|
+
return {
|
504
|
+
...currentComponentSchemas,
|
505
|
+
[fieldSchema.component]: componentSchema
|
506
|
+
};
|
507
|
+
}
|
508
|
+
return currentComponentSchemas;
|
509
|
+
},
|
510
|
+
{}
|
511
|
+
);
|
512
|
+
return {
|
513
|
+
schema: omit(FIELDS_TO_IGNORE, attributesSchema),
|
514
|
+
componentsSchemas
|
515
|
+
};
|
516
|
+
};
|
517
|
+
const createLifecyclesService = ({ strapi: strapi2 }) => {
|
518
|
+
const state = {
|
519
|
+
deleteExpiredJob: null,
|
520
|
+
isInitialized: false
|
521
|
+
};
|
522
|
+
const serviceUtils = createServiceUtils({ strapi: strapi2 });
|
523
|
+
return {
|
524
|
+
async bootstrap() {
|
525
|
+
if (state.isInitialized) {
|
526
|
+
return;
|
527
|
+
}
|
528
|
+
strapi2.documents.use(async (context, next) => {
|
529
|
+
const result = await next();
|
530
|
+
if (!shouldCreateHistoryVersion(context)) {
|
531
|
+
return result;
|
532
|
+
}
|
533
|
+
const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
|
534
|
+
const defaultLocale = await serviceUtils.getDefaultLocale();
|
535
|
+
const locales = castArray(context.params?.locale || defaultLocale);
|
536
|
+
if (!locales.length) {
|
537
|
+
return result;
|
538
|
+
}
|
539
|
+
const uid2 = context.contentType.uid;
|
540
|
+
const schemas = getSchemas(uid2);
|
541
|
+
const model = strapi2.getModel(uid2);
|
542
|
+
const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
|
543
|
+
const localeEntries = await strapi2.db.query(uid2).findMany({
|
544
|
+
where: {
|
545
|
+
documentId,
|
546
|
+
...isLocalizedContentType ? { locale: { $in: locales } } : {},
|
547
|
+
...contentTypes$1.hasDraftAndPublish(strapi2.contentTypes[uid2]) ? { publishedAt: null } : {}
|
548
|
+
},
|
549
|
+
populate: serviceUtils.getDeepPopulate(
|
550
|
+
uid2,
|
551
|
+
true
|
552
|
+
/* use database syntax */
|
553
|
+
)
|
554
|
+
});
|
555
|
+
await strapi2.db.transaction(async ({ onCommit }) => {
|
556
|
+
onCommit(async () => {
|
557
|
+
for (const entry of localeEntries) {
|
558
|
+
const status = await serviceUtils.getVersionStatus(uid2, entry);
|
559
|
+
await getService(strapi2, "history").createVersion({
|
560
|
+
contentType: uid2,
|
561
|
+
data: omit(FIELDS_TO_IGNORE, entry),
|
562
|
+
relatedDocumentId: documentId,
|
563
|
+
locale: entry.locale,
|
564
|
+
status,
|
565
|
+
...schemas
|
566
|
+
});
|
567
|
+
}
|
568
|
+
});
|
569
|
+
});
|
570
|
+
return result;
|
571
|
+
});
|
572
|
+
state.deleteExpiredJob = scheduleJob("0 0 * * *", () => {
|
573
|
+
const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
|
574
|
+
const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
|
575
|
+
strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
|
576
|
+
where: {
|
577
|
+
created_at: {
|
578
|
+
$lt: expirationDate.toISOString()
|
579
|
+
}
|
580
|
+
}
|
581
|
+
});
|
582
|
+
});
|
583
|
+
state.isInitialized = true;
|
584
|
+
},
|
585
|
+
async destroy() {
|
586
|
+
if (state.deleteExpiredJob) {
|
587
|
+
state.deleteExpiredJob.cancel();
|
588
|
+
}
|
589
|
+
}
|
590
|
+
};
|
591
|
+
};
|
507
592
|
const services$1 = {
|
508
|
-
history: createHistoryService
|
593
|
+
history: createHistoryService,
|
594
|
+
lifecycles: createLifecyclesService
|
509
595
|
};
|
510
596
|
const info = { pluginName: "content-manager", type: "admin" };
|
511
597
|
const historyVersionRouter = {
|
@@ -585,10 +671,10 @@ const getFeature = () => {
|
|
585
671
|
strapi2.get("models").add(historyVersion);
|
586
672
|
},
|
587
673
|
bootstrap({ strapi: strapi2 }) {
|
588
|
-
getService(strapi2, "
|
674
|
+
getService(strapi2, "lifecycles").bootstrap();
|
589
675
|
},
|
590
676
|
destroy({ strapi: strapi2 }) {
|
591
|
-
getService(strapi2, "
|
677
|
+
getService(strapi2, "lifecycles").destroy();
|
592
678
|
},
|
593
679
|
controllers: controllers$1,
|
594
680
|
services: services$1,
|
@@ -1118,6 +1204,11 @@ const { createPolicy } = policy;
|
|
1118
1204
|
const hasPermissions = createPolicy({
|
1119
1205
|
name: "plugin::content-manager.hasPermissions",
|
1120
1206
|
validator: validateHasPermissionsInput,
|
1207
|
+
/**
|
1208
|
+
* NOTE: Action aliases are currently not checked at this level (policy).
|
1209
|
+
* This is currently the intended behavior to avoid changing the behavior of API related permissions.
|
1210
|
+
* If you want to add support for it, please create a dedicated RFC with a list of potential side effect this could have.
|
1211
|
+
*/
|
1121
1212
|
handler(ctx, config = {}) {
|
1122
1213
|
const { actions = [], hasAtLeastOne = false } = config;
|
1123
1214
|
const { userAbility } = ctx.state;
|
@@ -1407,7 +1498,7 @@ const { PaginationError, ValidationError } = errors;
|
|
1407
1498
|
const TYPES = ["singleType", "collectionType"];
|
1408
1499
|
const kindSchema = yup$1.string().oneOf(TYPES).nullable();
|
1409
1500
|
const bulkActionInputSchema = yup$1.object({
|
1410
|
-
|
1501
|
+
documentIds: yup$1.array().of(yup$1.strapiID()).min(1).required()
|
1411
1502
|
}).required();
|
1412
1503
|
const generateUIDInputSchema = yup$1.object({
|
1413
1504
|
contentTypeUID: yup$1.string().required(),
|
@@ -1506,15 +1597,49 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
|
|
1506
1597
|
}
|
1507
1598
|
}, body);
|
1508
1599
|
};
|
1509
|
-
const
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1600
|
+
const singleLocaleSchema = yup$1.string().nullable();
|
1601
|
+
const multipleLocaleSchema = yup$1.lazy(
|
1602
|
+
(value) => Array.isArray(value) ? yup$1.array().of(singleLocaleSchema.required()) : singleLocaleSchema
|
1603
|
+
);
|
1604
|
+
const statusSchema = yup$1.mixed().oneOf(["draft", "published"], "Invalid status");
|
1605
|
+
const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
|
1606
|
+
const { allowMultipleLocales } = opts;
|
1607
|
+
const { locale, status: providedStatus, ...rest } = request || {};
|
1608
|
+
const defaultStatus = contentTypes$1.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
|
1609
|
+
const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
|
1610
|
+
const schema = yup$1.object().shape({
|
1611
|
+
locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
|
1612
|
+
status: statusSchema
|
1613
|
+
});
|
1614
|
+
try {
|
1615
|
+
await validateYupSchema(schema, { strict: true, abortEarly: false })(request);
|
1616
|
+
return { locale, status, ...rest };
|
1617
|
+
} catch (error) {
|
1618
|
+
throw new errors.ValidationError(`Validation error: ${error.message}`);
|
1516
1619
|
}
|
1517
|
-
|
1620
|
+
};
|
1621
|
+
const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
|
1622
|
+
const documentMetadata2 = getService$1("document-metadata");
|
1623
|
+
const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
|
1624
|
+
let {
|
1625
|
+
meta: { availableLocales, availableStatus }
|
1626
|
+
} = serviceOutput;
|
1627
|
+
const metadataSanitizer = permissionChecker2.sanitizeOutput;
|
1628
|
+
availableLocales = await async.map(
|
1629
|
+
availableLocales,
|
1630
|
+
async (localeDocument) => metadataSanitizer(localeDocument)
|
1631
|
+
);
|
1632
|
+
availableStatus = await async.map(
|
1633
|
+
availableStatus,
|
1634
|
+
async (statusDocument) => metadataSanitizer(statusDocument)
|
1635
|
+
);
|
1636
|
+
return {
|
1637
|
+
...serviceOutput,
|
1638
|
+
meta: {
|
1639
|
+
availableLocales,
|
1640
|
+
availableStatus
|
1641
|
+
}
|
1642
|
+
};
|
1518
1643
|
};
|
1519
1644
|
const createDocument = async (ctx, opts) => {
|
1520
1645
|
const { userAbility, user } = ctx.state;
|
@@ -1529,7 +1654,7 @@ const createDocument = async (ctx, opts) => {
|
|
1529
1654
|
const setCreator = setCreatorFields({ user });
|
1530
1655
|
const sanitizeFn = async.pipe(pickPermittedFields, setCreator);
|
1531
1656
|
const sanitizedBody = await sanitizeFn(body);
|
1532
|
-
const { locale, status
|
1657
|
+
const { locale, status } = await getDocumentLocaleAndStatus(body, model);
|
1533
1658
|
return documentManager2.create(model, {
|
1534
1659
|
data: sanitizedBody,
|
1535
1660
|
locale,
|
@@ -1548,7 +1673,7 @@ const updateDocument = async (ctx, opts) => {
|
|
1548
1673
|
}
|
1549
1674
|
const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
|
1550
1675
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1551
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
1676
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1552
1677
|
const [documentVersion, documentExists] = await Promise.all([
|
1553
1678
|
documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
|
1554
1679
|
documentManager2.exists(model, id)
|
@@ -1586,8 +1711,8 @@ const collectionTypes = {
|
|
1586
1711
|
}
|
1587
1712
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
|
1588
1713
|
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(
|
1714
|
+
const { locale, status } = await getDocumentLocaleAndStatus(query, model);
|
1715
|
+
const { results: documents, pagination: pagination2 } = await documentManager2.findPage(
|
1591
1716
|
{ ...permissionQuery, populate, locale, status },
|
1592
1717
|
model
|
1593
1718
|
);
|
@@ -1608,21 +1733,20 @@ const collectionTypes = {
|
|
1608
1733
|
);
|
1609
1734
|
ctx.body = {
|
1610
1735
|
results,
|
1611
|
-
pagination
|
1736
|
+
pagination: pagination2
|
1612
1737
|
};
|
1613
1738
|
},
|
1614
1739
|
async findOne(ctx) {
|
1615
1740
|
const { userAbility } = ctx.state;
|
1616
1741
|
const { model, id } = ctx.params;
|
1617
1742
|
const documentManager2 = getService$1("document-manager");
|
1618
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1619
1743
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1620
1744
|
if (permissionChecker2.cannot.read()) {
|
1621
1745
|
return ctx.forbidden();
|
1622
1746
|
}
|
1623
1747
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
1624
1748
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1625
|
-
const { locale, status
|
1749
|
+
const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1626
1750
|
const version = await documentManager2.findOne(id, model, {
|
1627
1751
|
populate,
|
1628
1752
|
locale,
|
@@ -1633,9 +1757,11 @@ const collectionTypes = {
|
|
1633
1757
|
if (!exists) {
|
1634
1758
|
return ctx.notFound();
|
1635
1759
|
}
|
1636
|
-
const { meta } = await
|
1760
|
+
const { meta } = await formatDocumentWithMetadata(
|
1761
|
+
permissionChecker2,
|
1637
1762
|
model,
|
1638
|
-
|
1763
|
+
// @ts-expect-error TODO: fix
|
1764
|
+
{ documentId: id, locale, publishedAt: null },
|
1639
1765
|
{ availableLocales: true, availableStatus: false }
|
1640
1766
|
);
|
1641
1767
|
ctx.body = { data: {}, meta };
|
@@ -1645,12 +1771,11 @@ const collectionTypes = {
|
|
1645
1771
|
return ctx.forbidden();
|
1646
1772
|
}
|
1647
1773
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
|
1648
|
-
ctx.body = await
|
1774
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
1649
1775
|
},
|
1650
1776
|
async create(ctx) {
|
1651
1777
|
const { userAbility } = ctx.state;
|
1652
1778
|
const { model } = ctx.params;
|
1653
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1654
1779
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1655
1780
|
const [totalEntries, document] = await Promise.all([
|
1656
1781
|
strapi.db.query(model).count(),
|
@@ -1658,7 +1783,7 @@ const collectionTypes = {
|
|
1658
1783
|
]);
|
1659
1784
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
|
1660
1785
|
ctx.status = 201;
|
1661
|
-
ctx.body = await
|
1786
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
|
1662
1787
|
// Empty metadata as it's not relevant for a new document
|
1663
1788
|
availableLocales: false,
|
1664
1789
|
availableStatus: false
|
@@ -1672,25 +1797,23 @@ const collectionTypes = {
|
|
1672
1797
|
async update(ctx) {
|
1673
1798
|
const { userAbility } = ctx.state;
|
1674
1799
|
const { model } = ctx.params;
|
1675
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1676
1800
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1677
1801
|
const updatedVersion = await updateDocument(ctx);
|
1678
1802
|
const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
|
1679
|
-
ctx.body = await
|
1803
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
|
1680
1804
|
},
|
1681
1805
|
async clone(ctx) {
|
1682
1806
|
const { userAbility, user } = ctx.state;
|
1683
1807
|
const { model, sourceId: id } = ctx.params;
|
1684
1808
|
const { body } = ctx.request;
|
1685
1809
|
const documentManager2 = getService$1("document-manager");
|
1686
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1687
1810
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1688
1811
|
if (permissionChecker2.cannot.create()) {
|
1689
1812
|
return ctx.forbidden();
|
1690
1813
|
}
|
1691
1814
|
const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
|
1692
1815
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1693
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
1816
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1694
1817
|
const document = await documentManager2.findOne(id, model, {
|
1695
1818
|
populate,
|
1696
1819
|
locale,
|
@@ -1706,7 +1829,7 @@ const collectionTypes = {
|
|
1706
1829
|
const sanitizedBody = await sanitizeFn(body);
|
1707
1830
|
const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
|
1708
1831
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
|
1709
|
-
ctx.body = await
|
1832
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
|
1710
1833
|
// Empty metadata as it's not relevant for a new document
|
1711
1834
|
availableLocales: false,
|
1712
1835
|
availableStatus: false
|
@@ -1735,7 +1858,7 @@ const collectionTypes = {
|
|
1735
1858
|
}
|
1736
1859
|
const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
|
1737
1860
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1738
|
-
const { locale } = getDocumentLocaleAndStatus(ctx.query);
|
1861
|
+
const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1739
1862
|
const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
|
1740
1863
|
if (documentLocales.length === 0) {
|
1741
1864
|
return ctx.notFound();
|
@@ -1757,7 +1880,6 @@ const collectionTypes = {
|
|
1757
1880
|
const { id, model } = ctx.params;
|
1758
1881
|
const { body } = ctx.request;
|
1759
1882
|
const documentManager2 = getService$1("document-manager");
|
1760
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1761
1883
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1762
1884
|
if (permissionChecker2.cannot.publish()) {
|
1763
1885
|
return ctx.forbidden();
|
@@ -1765,25 +1887,46 @@ const collectionTypes = {
|
|
1765
1887
|
const publishedDocument = await strapi.db.transaction(async () => {
|
1766
1888
|
const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
|
1767
1889
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1768
|
-
|
1890
|
+
let document;
|
1891
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1892
|
+
const isCreate = isNil$1(id);
|
1893
|
+
if (isCreate) {
|
1894
|
+
if (permissionChecker2.cannot.create()) {
|
1895
|
+
throw new errors.ForbiddenError();
|
1896
|
+
}
|
1897
|
+
document = await createDocument(ctx, { populate });
|
1898
|
+
}
|
1899
|
+
const isUpdate = !isCreate;
|
1900
|
+
if (isUpdate) {
|
1901
|
+
document = await documentManager2.findOne(id, model, { populate, locale });
|
1902
|
+
if (!document) {
|
1903
|
+
throw new errors.NotFoundError("Document not found");
|
1904
|
+
}
|
1905
|
+
if (permissionChecker2.can.update(document)) {
|
1906
|
+
await updateDocument(ctx);
|
1907
|
+
}
|
1908
|
+
}
|
1769
1909
|
if (permissionChecker2.cannot.publish(document)) {
|
1770
1910
|
throw new errors.ForbiddenError();
|
1771
1911
|
}
|
1772
|
-
const
|
1773
|
-
return documentManager2.publish(document.documentId, model, {
|
1912
|
+
const publishResult = await documentManager2.publish(document.documentId, model, {
|
1774
1913
|
locale
|
1775
1914
|
// TODO: Allow setting creator fields on publish
|
1776
1915
|
// data: setCreatorFields({ user, isEdition: true })({}),
|
1777
1916
|
});
|
1917
|
+
if (!publishResult || publishResult.length === 0) {
|
1918
|
+
throw new errors.NotFoundError("Document not found or already published.");
|
1919
|
+
}
|
1920
|
+
return publishResult[0];
|
1778
1921
|
});
|
1779
1922
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
|
1780
|
-
ctx.body = await
|
1923
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
1781
1924
|
},
|
1782
1925
|
async bulkPublish(ctx) {
|
1783
1926
|
const { userAbility } = ctx.state;
|
1784
1927
|
const { model } = ctx.params;
|
1785
1928
|
const { body } = ctx.request;
|
1786
|
-
const {
|
1929
|
+
const { documentIds } = body;
|
1787
1930
|
await validateBulkActionInput(body);
|
1788
1931
|
const documentManager2 = getService$1("document-manager");
|
1789
1932
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
@@ -1792,8 +1935,13 @@ const collectionTypes = {
|
|
1792
1935
|
}
|
1793
1936
|
const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
|
1794
1937
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1795
|
-
const
|
1796
|
-
|
1938
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model, {
|
1939
|
+
allowMultipleLocales: true
|
1940
|
+
});
|
1941
|
+
const entityPromises = documentIds.map(
|
1942
|
+
(documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
|
1943
|
+
);
|
1944
|
+
const entities = (await Promise.all(entityPromises)).flat();
|
1797
1945
|
for (const entity of entities) {
|
1798
1946
|
if (!entity) {
|
1799
1947
|
return ctx.notFound();
|
@@ -1802,24 +1950,27 @@ const collectionTypes = {
|
|
1802
1950
|
return ctx.forbidden();
|
1803
1951
|
}
|
1804
1952
|
}
|
1805
|
-
const
|
1953
|
+
const count = await documentManager2.publishMany(model, documentIds, locale);
|
1806
1954
|
ctx.body = { count };
|
1807
1955
|
},
|
1808
1956
|
async bulkUnpublish(ctx) {
|
1809
1957
|
const { userAbility } = ctx.state;
|
1810
1958
|
const { model } = ctx.params;
|
1811
1959
|
const { body } = ctx.request;
|
1812
|
-
const {
|
1960
|
+
const { documentIds } = body;
|
1813
1961
|
await validateBulkActionInput(body);
|
1814
1962
|
const documentManager2 = getService$1("document-manager");
|
1815
1963
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1816
1964
|
if (permissionChecker2.cannot.unpublish()) {
|
1817
1965
|
return ctx.forbidden();
|
1818
1966
|
}
|
1819
|
-
const
|
1820
|
-
|
1821
|
-
|
1822
|
-
const
|
1967
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model, {
|
1968
|
+
allowMultipleLocales: true
|
1969
|
+
});
|
1970
|
+
const entityPromises = documentIds.map(
|
1971
|
+
(documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
|
1972
|
+
);
|
1973
|
+
const entities = (await Promise.all(entityPromises)).flat();
|
1823
1974
|
for (const entity of entities) {
|
1824
1975
|
if (!entity) {
|
1825
1976
|
return ctx.notFound();
|
@@ -1828,7 +1979,8 @@ const collectionTypes = {
|
|
1828
1979
|
return ctx.forbidden();
|
1829
1980
|
}
|
1830
1981
|
}
|
1831
|
-
const
|
1982
|
+
const entitiesIds = entities.map((document) => document.documentId);
|
1983
|
+
const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
|
1832
1984
|
ctx.body = { count };
|
1833
1985
|
},
|
1834
1986
|
async unpublish(ctx) {
|
@@ -1838,7 +1990,6 @@ const collectionTypes = {
|
|
1838
1990
|
body: { discardDraft, ...body }
|
1839
1991
|
} = ctx.request;
|
1840
1992
|
const documentManager2 = getService$1("document-manager");
|
1841
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1842
1993
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1843
1994
|
if (permissionChecker2.cannot.unpublish()) {
|
1844
1995
|
return ctx.forbidden();
|
@@ -1848,7 +1999,7 @@ const collectionTypes = {
|
|
1848
1999
|
}
|
1849
2000
|
const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
|
1850
2001
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1851
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2002
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1852
2003
|
const document = await documentManager2.findOne(id, model, {
|
1853
2004
|
populate,
|
1854
2005
|
locale,
|
@@ -1870,7 +2021,7 @@ const collectionTypes = {
|
|
1870
2021
|
ctx.body = await async.pipe(
|
1871
2022
|
(document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
|
1872
2023
|
permissionChecker2.sanitizeOutput,
|
1873
|
-
(document2) =>
|
2024
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
1874
2025
|
)(document);
|
1875
2026
|
});
|
1876
2027
|
},
|
@@ -1879,14 +2030,13 @@ const collectionTypes = {
|
|
1879
2030
|
const { id, model } = ctx.params;
|
1880
2031
|
const { body } = ctx.request;
|
1881
2032
|
const documentManager2 = getService$1("document-manager");
|
1882
|
-
const documentMetadata2 = getService$1("document-metadata");
|
1883
2033
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1884
2034
|
if (permissionChecker2.cannot.discard()) {
|
1885
2035
|
return ctx.forbidden();
|
1886
2036
|
}
|
1887
2037
|
const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
|
1888
2038
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1889
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2039
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1890
2040
|
const document = await documentManager2.findOne(id, model, {
|
1891
2041
|
populate,
|
1892
2042
|
locale,
|
@@ -1901,14 +2051,14 @@ const collectionTypes = {
|
|
1901
2051
|
ctx.body = await async.pipe(
|
1902
2052
|
(document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
|
1903
2053
|
permissionChecker2.sanitizeOutput,
|
1904
|
-
(document2) =>
|
2054
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
1905
2055
|
)(document);
|
1906
2056
|
},
|
1907
2057
|
async bulkDelete(ctx) {
|
1908
2058
|
const { userAbility } = ctx.state;
|
1909
2059
|
const { model } = ctx.params;
|
1910
2060
|
const { query, body } = ctx.request;
|
1911
|
-
const {
|
2061
|
+
const { documentIds } = body;
|
1912
2062
|
await validateBulkActionInput(body);
|
1913
2063
|
const documentManager2 = getService$1("document-manager");
|
1914
2064
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
@@ -1916,14 +2066,22 @@ const collectionTypes = {
|
|
1916
2066
|
return ctx.forbidden();
|
1917
2067
|
}
|
1918
2068
|
const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
|
1919
|
-
const
|
1920
|
-
const
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
2069
|
+
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2070
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2071
|
+
const documentLocales = await documentManager2.findLocales(documentIds, model, {
|
2072
|
+
populate,
|
2073
|
+
locale
|
2074
|
+
});
|
2075
|
+
if (documentLocales.length === 0) {
|
2076
|
+
return ctx.notFound();
|
2077
|
+
}
|
2078
|
+
for (const document of documentLocales) {
|
2079
|
+
if (permissionChecker2.cannot.delete(document)) {
|
2080
|
+
return ctx.forbidden();
|
1924
2081
|
}
|
1925
|
-
}
|
1926
|
-
const
|
2082
|
+
}
|
2083
|
+
const localeDocumentsIds = documentLocales.map((document) => document.documentId);
|
2084
|
+
const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
|
1927
2085
|
ctx.body = { count };
|
1928
2086
|
},
|
1929
2087
|
async countDraftRelations(ctx) {
|
@@ -1936,7 +2094,7 @@ const collectionTypes = {
|
|
1936
2094
|
}
|
1937
2095
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
1938
2096
|
const populate = await getService$1("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1939
|
-
const { locale, status
|
2097
|
+
const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1940
2098
|
const entity = await documentManager2.findOne(id, model, { populate, locale, status });
|
1941
2099
|
if (!entity) {
|
1942
2100
|
return ctx.notFound();
|
@@ -1951,7 +2109,7 @@ const collectionTypes = {
|
|
1951
2109
|
},
|
1952
2110
|
async countManyEntriesDraftRelations(ctx) {
|
1953
2111
|
const { userAbility } = ctx.state;
|
1954
|
-
const ids = ctx.request.query.
|
2112
|
+
const ids = ctx.request.query.documentIds;
|
1955
2113
|
const locale = ctx.request.query.locale;
|
1956
2114
|
const { model } = ctx.params;
|
1957
2115
|
const documentManager2 = getService$1("document-manager");
|
@@ -1959,16 +2117,16 @@ const collectionTypes = {
|
|
1959
2117
|
if (permissionChecker2.cannot.read()) {
|
1960
2118
|
return ctx.forbidden();
|
1961
2119
|
}
|
1962
|
-
const
|
2120
|
+
const documents = await documentManager2.findMany(
|
1963
2121
|
{
|
1964
2122
|
filters: {
|
1965
|
-
|
2123
|
+
documentId: ids
|
1966
2124
|
},
|
1967
2125
|
locale
|
1968
2126
|
},
|
1969
2127
|
model
|
1970
2128
|
);
|
1971
|
-
if (!
|
2129
|
+
if (!documents) {
|
1972
2130
|
return ctx.notFound();
|
1973
2131
|
}
|
1974
2132
|
const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
|
@@ -2159,20 +2317,13 @@ const sanitizeMainField = (model, mainField, userAbility) => {
|
|
2159
2317
|
userAbility,
|
2160
2318
|
model: model.uid
|
2161
2319
|
});
|
2162
|
-
|
2320
|
+
const isMainFieldListable = isListable(model, mainField);
|
2321
|
+
const canReadMainField = permissionChecker2.can.read(null, mainField);
|
2322
|
+
if (!isMainFieldListable || !canReadMainField) {
|
2163
2323
|
return "id";
|
2164
2324
|
}
|
2165
|
-
if (
|
2166
|
-
|
2167
|
-
const userPermissionChecker = getService$1("permission-checker").create({
|
2168
|
-
userAbility,
|
2169
|
-
model: "plugin::users-permissions.user"
|
2170
|
-
});
|
2171
|
-
if (userPermissionChecker.can.read()) {
|
2172
|
-
return "name";
|
2173
|
-
}
|
2174
|
-
}
|
2175
|
-
return "id";
|
2325
|
+
if (model.uid === "plugin::users-permissions.role") {
|
2326
|
+
return "name";
|
2176
2327
|
}
|
2177
2328
|
return mainField;
|
2178
2329
|
};
|
@@ -2372,8 +2523,9 @@ const relations = {
|
|
2372
2523
|
} else {
|
2373
2524
|
where.id = id;
|
2374
2525
|
}
|
2375
|
-
|
2376
|
-
|
2526
|
+
const publishedAt = getPublishedAtClause(status, targetUid);
|
2527
|
+
if (!isEmpty(publishedAt)) {
|
2528
|
+
where[`${alias}.published_at`] = publishedAt;
|
2377
2529
|
}
|
2378
2530
|
if (filterByLocale) {
|
2379
2531
|
where[`${alias}.locale`] = locale;
|
@@ -2430,9 +2582,7 @@ const relations = {
|
|
2430
2582
|
addFiltersClause(permissionQuery, { id: { $in: loadedIds } });
|
2431
2583
|
const sanitizedRes = await loadRelations({ id: entryId }, targetField, {
|
2432
2584
|
...strapi.get("query-params").transform(targetUid, permissionQuery),
|
2433
|
-
ordering: "desc"
|
2434
|
-
page: ctx.request.query.page,
|
2435
|
-
pageSize: ctx.request.query.pageSize
|
2585
|
+
ordering: "desc"
|
2436
2586
|
});
|
2437
2587
|
const relationsUnion = uniqBy("id", concat(sanitizedRes.results, res.results));
|
2438
2588
|
ctx.body = {
|
@@ -2464,7 +2614,7 @@ const createOrUpdateDocument = async (ctx, opts) => {
|
|
2464
2614
|
throw new errors.ForbiddenError();
|
2465
2615
|
}
|
2466
2616
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
|
2467
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2617
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2468
2618
|
const [documentVersion, otherDocumentVersion] = await Promise.all([
|
2469
2619
|
findDocument(sanitizedQuery, model, { locale, status: "draft" }),
|
2470
2620
|
// Find the first document to check if it exists
|
@@ -2501,12 +2651,11 @@ const singleTypes = {
|
|
2501
2651
|
const { model } = ctx.params;
|
2502
2652
|
const { query = {} } = ctx.request;
|
2503
2653
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2504
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2505
2654
|
if (permissionChecker2.cannot.read()) {
|
2506
2655
|
return ctx.forbidden();
|
2507
2656
|
}
|
2508
2657
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
|
2509
|
-
const { locale, status } = getDocumentLocaleAndStatus(query);
|
2658
|
+
const { locale, status } = await getDocumentLocaleAndStatus(query, model);
|
2510
2659
|
const version = await findDocument(permissionQuery, model, { locale, status });
|
2511
2660
|
if (!version) {
|
2512
2661
|
if (permissionChecker2.cannot.create()) {
|
@@ -2516,8 +2665,10 @@ const singleTypes = {
|
|
2516
2665
|
if (!document) {
|
2517
2666
|
return ctx.notFound();
|
2518
2667
|
}
|
2519
|
-
const { meta } = await
|
2668
|
+
const { meta } = await formatDocumentWithMetadata(
|
2669
|
+
permissionChecker2,
|
2520
2670
|
model,
|
2671
|
+
// @ts-expect-error - fix types
|
2521
2672
|
{ id: document.documentId, locale, publishedAt: null },
|
2522
2673
|
{ availableLocales: true, availableStatus: false }
|
2523
2674
|
);
|
@@ -2528,16 +2679,15 @@ const singleTypes = {
|
|
2528
2679
|
return ctx.forbidden();
|
2529
2680
|
}
|
2530
2681
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
|
2531
|
-
ctx.body = await
|
2682
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2532
2683
|
},
|
2533
2684
|
async createOrUpdate(ctx) {
|
2534
2685
|
const { userAbility } = ctx.state;
|
2535
2686
|
const { model } = ctx.params;
|
2536
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2537
2687
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2538
2688
|
const document = await createOrUpdateDocument(ctx);
|
2539
2689
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
|
2540
|
-
ctx.body = await
|
2690
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2541
2691
|
},
|
2542
2692
|
async delete(ctx) {
|
2543
2693
|
const { userAbility } = ctx.state;
|
@@ -2550,7 +2700,7 @@ const singleTypes = {
|
|
2550
2700
|
}
|
2551
2701
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
|
2552
2702
|
const populate = await buildPopulateFromQuery(sanitizedQuery, model);
|
2553
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2703
|
+
const { locale } = await getDocumentLocaleAndStatus(query, model);
|
2554
2704
|
const documentLocales = await documentManager2.findLocales(void 0, model, {
|
2555
2705
|
populate,
|
2556
2706
|
locale
|
@@ -2573,7 +2723,6 @@ const singleTypes = {
|
|
2573
2723
|
const { model } = ctx.params;
|
2574
2724
|
const { query = {} } = ctx.request;
|
2575
2725
|
const documentManager2 = getService$1("document-manager");
|
2576
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2577
2726
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2578
2727
|
if (permissionChecker2.cannot.publish()) {
|
2579
2728
|
return ctx.forbidden();
|
@@ -2588,11 +2737,12 @@ const singleTypes = {
|
|
2588
2737
|
if (permissionChecker2.cannot.publish(document)) {
|
2589
2738
|
throw new errors.ForbiddenError();
|
2590
2739
|
}
|
2591
|
-
const { locale } = getDocumentLocaleAndStatus(document);
|
2592
|
-
|
2740
|
+
const { locale } = await getDocumentLocaleAndStatus(document, model);
|
2741
|
+
const publishResult = await documentManager2.publish(document.documentId, model, { locale });
|
2742
|
+
return publishResult.at(0);
|
2593
2743
|
});
|
2594
2744
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
|
2595
|
-
ctx.body = await
|
2745
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2596
2746
|
},
|
2597
2747
|
async unpublish(ctx) {
|
2598
2748
|
const { userAbility } = ctx.state;
|
@@ -2602,7 +2752,6 @@ const singleTypes = {
|
|
2602
2752
|
query = {}
|
2603
2753
|
} = ctx.request;
|
2604
2754
|
const documentManager2 = getService$1("document-manager");
|
2605
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2606
2755
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2607
2756
|
if (permissionChecker2.cannot.unpublish()) {
|
2608
2757
|
return ctx.forbidden();
|
@@ -2611,7 +2760,7 @@ const singleTypes = {
|
|
2611
2760
|
return ctx.forbidden();
|
2612
2761
|
}
|
2613
2762
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
|
2614
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2763
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2615
2764
|
const document = await findDocument(sanitizedQuery, model, { locale });
|
2616
2765
|
if (!document) {
|
2617
2766
|
return ctx.notFound();
|
@@ -2629,7 +2778,7 @@ const singleTypes = {
|
|
2629
2778
|
ctx.body = await async.pipe(
|
2630
2779
|
(document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
|
2631
2780
|
permissionChecker2.sanitizeOutput,
|
2632
|
-
(document2) =>
|
2781
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
2633
2782
|
)(document);
|
2634
2783
|
});
|
2635
2784
|
},
|
@@ -2638,13 +2787,12 @@ const singleTypes = {
|
|
2638
2787
|
const { model } = ctx.params;
|
2639
2788
|
const { body, query = {} } = ctx.request;
|
2640
2789
|
const documentManager2 = getService$1("document-manager");
|
2641
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2642
2790
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2643
2791
|
if (permissionChecker2.cannot.discard()) {
|
2644
2792
|
return ctx.forbidden();
|
2645
2793
|
}
|
2646
2794
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
|
2647
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2795
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2648
2796
|
const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
|
2649
2797
|
if (!document) {
|
2650
2798
|
return ctx.notFound();
|
@@ -2655,7 +2803,7 @@ const singleTypes = {
|
|
2655
2803
|
ctx.body = await async.pipe(
|
2656
2804
|
(document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
|
2657
2805
|
permissionChecker2.sanitizeOutput,
|
2658
|
-
(document2) =>
|
2806
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
2659
2807
|
)(document);
|
2660
2808
|
},
|
2661
2809
|
async countDraftRelations(ctx) {
|
@@ -2664,7 +2812,7 @@ const singleTypes = {
|
|
2664
2812
|
const { query } = ctx.request;
|
2665
2813
|
const documentManager2 = getService$1("document-manager");
|
2666
2814
|
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2667
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2815
|
+
const { locale } = await getDocumentLocaleAndStatus(query, model);
|
2668
2816
|
if (permissionChecker2.cannot.read()) {
|
2669
2817
|
return ctx.forbidden();
|
2670
2818
|
}
|
@@ -2685,7 +2833,7 @@ const uid$1 = {
|
|
2685
2833
|
async generateUID(ctx) {
|
2686
2834
|
const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
|
2687
2835
|
const { query = {} } = ctx.request;
|
2688
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2836
|
+
const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
|
2689
2837
|
await validateUIDField(contentTypeUID, field);
|
2690
2838
|
const uidService = getService$1("uid");
|
2691
2839
|
ctx.body = {
|
@@ -2697,7 +2845,7 @@ const uid$1 = {
|
|
2697
2845
|
ctx.request.body
|
2698
2846
|
);
|
2699
2847
|
const { query = {} } = ctx.request;
|
2700
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2848
|
+
const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
|
2701
2849
|
await validateUIDField(contentTypeUID, field);
|
2702
2850
|
const uidService = getService$1("uid");
|
2703
2851
|
const isAvailable = await uidService.checkUIDAvailability({
|
@@ -3340,12 +3488,27 @@ const createPermissionChecker = (strapi2) => ({ userAbility, model }) => {
|
|
3340
3488
|
ability: userAbility,
|
3341
3489
|
model
|
3342
3490
|
});
|
3343
|
-
const
|
3491
|
+
const { actionProvider } = strapi2.service("admin::permission");
|
3492
|
+
const toSubject = (entity) => {
|
3493
|
+
return entity ? permissionsManager.toSubject(entity, model) : model;
|
3494
|
+
};
|
3344
3495
|
const can = (action, entity, field) => {
|
3345
|
-
|
3496
|
+
const subject = toSubject(entity);
|
3497
|
+
const aliases = actionProvider.unstable_aliases(action, model);
|
3498
|
+
return (
|
3499
|
+
// Test the original action to see if it passes
|
3500
|
+
userAbility.can(action, subject, field) || // Else try every known alias if at least one of them succeed, then the user "can"
|
3501
|
+
aliases.some((alias) => userAbility.can(alias, subject, field))
|
3502
|
+
);
|
3346
3503
|
};
|
3347
3504
|
const cannot = (action, entity, field) => {
|
3348
|
-
|
3505
|
+
const subject = toSubject(entity);
|
3506
|
+
const aliases = actionProvider.unstable_aliases(action, model);
|
3507
|
+
return (
|
3508
|
+
// Test both the original action
|
3509
|
+
userAbility.cannot(action, subject, field) && // and every known alias, if all of them fail (cannot), then the user truly "cannot"
|
3510
|
+
aliases.every((alias) => userAbility.cannot(alias, subject, field))
|
3511
|
+
);
|
3349
3512
|
};
|
3350
3513
|
const sanitizeOutput = (data, { action = ACTIONS.read } = {}) => {
|
3351
3514
|
return permissionsManager.sanitizeOutput(data, { subject: toSubject(data), action });
|
@@ -3488,7 +3651,7 @@ const permission = ({ strapi: strapi2 }) => ({
|
|
3488
3651
|
await strapi2.service("admin::permission").actionProvider.registerMany(actions);
|
3489
3652
|
}
|
3490
3653
|
});
|
3491
|
-
const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils.contentTypes;
|
3654
|
+
const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils.contentTypes;
|
3492
3655
|
const { isAnyToMany } = strapiUtils.relations;
|
3493
3656
|
const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils.contentTypes.constants;
|
3494
3657
|
const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
|
@@ -3579,6 +3742,42 @@ const getDeepPopulate = (uid2, {
|
|
3579
3742
|
{}
|
3580
3743
|
);
|
3581
3744
|
};
|
3745
|
+
const getValidatableFieldsPopulate = (uid2, {
|
3746
|
+
initialPopulate = {},
|
3747
|
+
countMany = false,
|
3748
|
+
countOne = false,
|
3749
|
+
maxLevel = Infinity
|
3750
|
+
} = {}, level = 1) => {
|
3751
|
+
if (level > maxLevel) {
|
3752
|
+
return {};
|
3753
|
+
}
|
3754
|
+
const model = strapi.getModel(uid2);
|
3755
|
+
return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
|
3756
|
+
if (!getDoesAttributeRequireValidation(attribute)) {
|
3757
|
+
return populateAcc;
|
3758
|
+
}
|
3759
|
+
if (isScalarAttribute(attribute)) {
|
3760
|
+
return merge(populateAcc, {
|
3761
|
+
[attributeName]: true
|
3762
|
+
});
|
3763
|
+
}
|
3764
|
+
return merge(
|
3765
|
+
populateAcc,
|
3766
|
+
getPopulateFor(
|
3767
|
+
attributeName,
|
3768
|
+
model,
|
3769
|
+
{
|
3770
|
+
// @ts-expect-error - improve types
|
3771
|
+
initialPopulate: initialPopulate?.[attributeName],
|
3772
|
+
countMany,
|
3773
|
+
countOne,
|
3774
|
+
maxLevel
|
3775
|
+
},
|
3776
|
+
level
|
3777
|
+
)
|
3778
|
+
);
|
3779
|
+
}, {});
|
3780
|
+
};
|
3582
3781
|
const getDeepPopulateDraftCount = (uid2) => {
|
3583
3782
|
const model = strapi.getModel(uid2);
|
3584
3783
|
let hasRelations = false;
|
@@ -3586,6 +3785,10 @@ const getDeepPopulateDraftCount = (uid2) => {
|
|
3586
3785
|
const attribute = model.attributes[attributeName];
|
3587
3786
|
switch (attribute.type) {
|
3588
3787
|
case "relation": {
|
3788
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
3789
|
+
if (isMorphRelation) {
|
3790
|
+
break;
|
3791
|
+
}
|
3589
3792
|
if (isVisibleAttribute$1(model, attributeName)) {
|
3590
3793
|
populateAcc[attributeName] = {
|
3591
3794
|
count: true,
|
@@ -3600,22 +3803,24 @@ const getDeepPopulateDraftCount = (uid2) => {
|
|
3600
3803
|
attribute.component
|
3601
3804
|
);
|
3602
3805
|
if (childHasRelations) {
|
3603
|
-
populateAcc[attributeName] = {
|
3806
|
+
populateAcc[attributeName] = {
|
3807
|
+
populate: populate2
|
3808
|
+
};
|
3604
3809
|
hasRelations = true;
|
3605
3810
|
}
|
3606
3811
|
break;
|
3607
3812
|
}
|
3608
3813
|
case "dynamiczone": {
|
3609
|
-
const
|
3610
|
-
const { populate:
|
3611
|
-
if (
|
3814
|
+
const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
|
3815
|
+
const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
|
3816
|
+
if (componentHasRelations) {
|
3612
3817
|
hasRelations = true;
|
3613
|
-
return
|
3818
|
+
return { ...acc, [componentUID]: { populate: componentPopulate } };
|
3614
3819
|
}
|
3615
3820
|
return acc;
|
3616
3821
|
}, {});
|
3617
|
-
if (!isEmpty(
|
3618
|
-
populateAcc[attributeName] = {
|
3822
|
+
if (!isEmpty(dzPopulateFragment)) {
|
3823
|
+
populateAcc[attributeName] = { on: dzPopulateFragment };
|
3619
3824
|
}
|
3620
3825
|
break;
|
3621
3826
|
}
|
@@ -3807,41 +4012,70 @@ const AVAILABLE_STATUS_FIELDS = [
|
|
3807
4012
|
"updatedBy",
|
3808
4013
|
"status"
|
3809
4014
|
];
|
3810
|
-
const AVAILABLE_LOCALES_FIELDS = [
|
4015
|
+
const AVAILABLE_LOCALES_FIELDS = [
|
4016
|
+
"id",
|
4017
|
+
"locale",
|
4018
|
+
"updatedAt",
|
4019
|
+
"createdAt",
|
4020
|
+
"status",
|
4021
|
+
"publishedAt",
|
4022
|
+
"documentId"
|
4023
|
+
];
|
3811
4024
|
const CONTENT_MANAGER_STATUS = {
|
3812
4025
|
PUBLISHED: "published",
|
3813
4026
|
DRAFT: "draft",
|
3814
4027
|
MODIFIED: "modified"
|
3815
4028
|
};
|
3816
|
-
const
|
3817
|
-
if (!
|
4029
|
+
const getIsVersionLatestModification = (version, otherVersion) => {
|
4030
|
+
if (!version || !version.updatedAt) {
|
3818
4031
|
return false;
|
3819
4032
|
}
|
3820
|
-
const
|
3821
|
-
const
|
3822
|
-
|
3823
|
-
return difference2 <= threshold;
|
4033
|
+
const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
|
4034
|
+
const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
|
4035
|
+
return versionUpdatedAt > otherUpdatedAt;
|
3824
4036
|
};
|
3825
4037
|
const documentMetadata = ({ strapi: strapi2 }) => ({
|
3826
4038
|
/**
|
3827
4039
|
* Returns available locales of a document for the current status
|
3828
4040
|
*/
|
3829
|
-
getAvailableLocales(uid2, version, allVersions) {
|
4041
|
+
async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
|
3830
4042
|
const versionsByLocale = groupBy("locale", allVersions);
|
3831
4043
|
delete versionsByLocale[version.locale];
|
3832
|
-
|
3833
|
-
|
3834
|
-
|
4044
|
+
const model = strapi2.getModel(uid2);
|
4045
|
+
const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
|
4046
|
+
const traversalFunction = async (localeVersion) => traverseEntity(
|
4047
|
+
({ key }, { remove }) => {
|
4048
|
+
if (keysToKeep.includes(key)) {
|
4049
|
+
return;
|
4050
|
+
}
|
4051
|
+
remove(key);
|
4052
|
+
},
|
4053
|
+
{ schema: model, getModel: strapi2.getModel.bind(strapi2) },
|
4054
|
+
// @ts-expect-error fix types DocumentVersion incompatible with Data
|
4055
|
+
localeVersion
|
4056
|
+
);
|
4057
|
+
const mappingResult = await async.map(
|
4058
|
+
Object.values(versionsByLocale),
|
4059
|
+
async (localeVersions) => {
|
4060
|
+
const mappedLocaleVersions = await async.map(
|
4061
|
+
localeVersions,
|
4062
|
+
traversalFunction
|
4063
|
+
);
|
4064
|
+
if (!contentTypes$1.hasDraftAndPublish(model)) {
|
4065
|
+
return mappedLocaleVersions[0];
|
4066
|
+
}
|
4067
|
+
const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
|
4068
|
+
const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
|
4069
|
+
if (!draftVersion) {
|
4070
|
+
return;
|
4071
|
+
}
|
4072
|
+
return {
|
4073
|
+
...draftVersion,
|
4074
|
+
status: this.getStatus(draftVersion, otherVersions)
|
4075
|
+
};
|
3835
4076
|
}
|
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);
|
4077
|
+
);
|
4078
|
+
return mappingResult.filter(Boolean);
|
3845
4079
|
},
|
3846
4080
|
/**
|
3847
4081
|
* Returns available status of a document for the current locale
|
@@ -3879,26 +4113,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3879
4113
|
});
|
3880
4114
|
},
|
3881
4115
|
getStatus(version, otherDocumentStatuses) {
|
3882
|
-
|
3883
|
-
|
3884
|
-
|
4116
|
+
let draftVersion;
|
4117
|
+
let publishedVersion;
|
4118
|
+
if (version.publishedAt) {
|
4119
|
+
publishedVersion = version;
|
4120
|
+
} else {
|
4121
|
+
draftVersion = version;
|
3885
4122
|
}
|
3886
|
-
|
3887
|
-
|
3888
|
-
|
3889
|
-
|
3890
|
-
|
4123
|
+
const otherVersion = otherDocumentStatuses?.at(0);
|
4124
|
+
if (otherVersion?.publishedAt) {
|
4125
|
+
publishedVersion = otherVersion;
|
4126
|
+
} else if (otherVersion) {
|
4127
|
+
draftVersion = otherVersion;
|
3891
4128
|
}
|
3892
|
-
if (
|
4129
|
+
if (!draftVersion)
|
3893
4130
|
return CONTENT_MANAGER_STATUS.PUBLISHED;
|
3894
|
-
|
3895
|
-
|
4131
|
+
if (!publishedVersion)
|
4132
|
+
return CONTENT_MANAGER_STATUS.DRAFT;
|
4133
|
+
const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
|
4134
|
+
return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
|
3896
4135
|
},
|
4136
|
+
// TODO is it necessary to return metadata on every page of the CM
|
4137
|
+
// We could refactor this so the locales are only loaded when they're
|
4138
|
+
// needed. e.g. in the bulk locale action modal.
|
3897
4139
|
async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
|
4140
|
+
const populate = getValidatableFieldsPopulate(uid2);
|
3898
4141
|
const versions = await strapi2.db.query(uid2).findMany({
|
3899
4142
|
where: { documentId: version.documentId },
|
3900
|
-
select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
|
3901
4143
|
populate: {
|
4144
|
+
// Populate only fields that require validation for bulk locale actions
|
4145
|
+
...populate,
|
4146
|
+
// NOTE: creator fields are selected in this way to avoid exposing sensitive data
|
3902
4147
|
createdBy: {
|
3903
4148
|
select: ["id", "firstname", "lastname", "email"]
|
3904
4149
|
},
|
@@ -3907,7 +4152,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3907
4152
|
}
|
3908
4153
|
}
|
3909
4154
|
});
|
3910
|
-
const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
|
4155
|
+
const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
|
3911
4156
|
const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
|
3912
4157
|
return {
|
3913
4158
|
availableLocales: availableLocalesResult,
|
@@ -3920,8 +4165,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3920
4165
|
* - Available status of the document for the current locale
|
3921
4166
|
*/
|
3922
4167
|
async formatDocumentWithMetadata(uid2, document, opts = {}) {
|
3923
|
-
if (!document)
|
3924
|
-
return
|
4168
|
+
if (!document) {
|
4169
|
+
return {
|
4170
|
+
data: document,
|
4171
|
+
meta: {
|
4172
|
+
availableLocales: [],
|
4173
|
+
availableStatus: []
|
4174
|
+
}
|
4175
|
+
};
|
4176
|
+
}
|
3925
4177
|
const hasDraftAndPublish = contentTypes$1.hasDraftAndPublish(strapi2.getModel(uid2));
|
3926
4178
|
if (!hasDraftAndPublish) {
|
3927
4179
|
opts.availableStatus = false;
|
@@ -3971,26 +4223,9 @@ const sumDraftCounts = (entity, uid2) => {
|
|
3971
4223
|
}, 0);
|
3972
4224
|
};
|
3973
4225
|
const { ApplicationError } = errors;
|
3974
|
-
const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
|
3975
4226
|
const { PUBLISHED_AT_ATTRIBUTE } = contentTypes$1.constants;
|
3976
4227
|
const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE);
|
3977
4228
|
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
4229
|
const documentManager = ({ strapi: strapi2 }) => {
|
3995
4230
|
return {
|
3996
4231
|
async findOne(id, uid2, opts = {}) {
|
@@ -4009,6 +4244,9 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4009
4244
|
} else if (opts.locale && opts.locale !== "*") {
|
4010
4245
|
where.locale = opts.locale;
|
4011
4246
|
}
|
4247
|
+
if (typeof opts.isPublished === "boolean") {
|
4248
|
+
where.publishedAt = { $notNull: opts.isPublished };
|
4249
|
+
}
|
4012
4250
|
return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
|
4013
4251
|
},
|
4014
4252
|
async findMany(opts, uid2) {
|
@@ -4016,20 +4254,16 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4016
4254
|
return strapi2.documents(uid2).findMany(params);
|
4017
4255
|
},
|
4018
4256
|
async findPage(opts, uid2) {
|
4019
|
-
const
|
4020
|
-
|
4257
|
+
const params = pagination.withDefaultPagination(opts || {}, {
|
4258
|
+
maxLimit: 1e3
|
4259
|
+
});
|
4021
4260
|
const [documents, total = 0] = await Promise.all([
|
4022
|
-
strapi2.documents(uid2).findMany(
|
4023
|
-
strapi2.documents(uid2).count(
|
4261
|
+
strapi2.documents(uid2).findMany(params),
|
4262
|
+
strapi2.documents(uid2).count(params)
|
4024
4263
|
]);
|
4025
4264
|
return {
|
4026
4265
|
results: documents,
|
4027
|
-
pagination:
|
4028
|
-
page,
|
4029
|
-
pageSize,
|
4030
|
-
pageCount: Math.ceil(total / pageSize),
|
4031
|
-
total
|
4032
|
-
}
|
4266
|
+
pagination: pagination.transformPagedPaginationInfo(params, total)
|
4033
4267
|
};
|
4034
4268
|
},
|
4035
4269
|
async create(uid2, opts = {}) {
|
@@ -4046,10 +4280,7 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4046
4280
|
async clone(id, body, uid2) {
|
4047
4281
|
const populate = await buildDeepPopulate(uid2);
|
4048
4282
|
const params = {
|
4049
|
-
data:
|
4050
|
-
...omitIdField(body),
|
4051
|
-
[PUBLISHED_AT_ATTRIBUTE]: null
|
4052
|
-
},
|
4283
|
+
data: omitIdField(body),
|
4053
4284
|
populate
|
4054
4285
|
};
|
4055
4286
|
return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
|
@@ -4075,70 +4306,36 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4075
4306
|
return {};
|
4076
4307
|
},
|
4077
4308
|
// FIXME: handle relations
|
4078
|
-
async deleteMany(
|
4079
|
-
const
|
4080
|
-
|
4081
|
-
|
4082
|
-
}
|
4083
|
-
return { count: docs.length };
|
4309
|
+
async deleteMany(documentIds, uid2, opts = {}) {
|
4310
|
+
const deletedEntries = await strapi2.db.transaction(async () => {
|
4311
|
+
return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
|
4312
|
+
});
|
4313
|
+
return { count: deletedEntries.length };
|
4084
4314
|
},
|
4085
4315
|
async publish(id, uid2, opts = {}) {
|
4086
4316
|
const populate = await buildDeepPopulate(uid2);
|
4087
4317
|
const params = { ...opts, populate };
|
4088
|
-
return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries
|
4318
|
+
return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
|
4089
4319
|
},
|
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
|
4320
|
+
async publishMany(uid2, documentIds, locale) {
|
4321
|
+
return strapi2.db.transaction(async () => {
|
4322
|
+
const results = await Promise.all(
|
4323
|
+
documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
|
4324
|
+
);
|
4325
|
+
const publishedEntitiesCount = results.flat().filter(Boolean).length;
|
4326
|
+
return publishedEntitiesCount;
|
4116
4327
|
});
|
4117
|
-
await Promise.all(
|
4118
|
-
publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
|
4119
|
-
);
|
4120
|
-
return publishedEntitiesCount;
|
4121
4328
|
},
|
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
|
4329
|
+
async unpublishMany(documentIds, uid2, opts = {}) {
|
4330
|
+
const unpublishedEntries = await strapi2.db.transaction(async () => {
|
4331
|
+
return Promise.all(
|
4332
|
+
documentIds.map(
|
4333
|
+
(id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
|
4334
|
+
)
|
4335
|
+
);
|
4137
4336
|
});
|
4138
|
-
|
4139
|
-
|
4140
|
-
);
|
4141
|
-
return unpublishedEntitiesCount;
|
4337
|
+
const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
|
4338
|
+
return { count: unpublishedEntitiesCount };
|
4142
4339
|
},
|
4143
4340
|
async unpublish(id, uid2, opts = {}) {
|
4144
4341
|
const populate = await buildDeepPopulate(uid2);
|
@@ -4163,16 +4360,20 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4163
4360
|
}
|
4164
4361
|
return sumDraftCounts(document, uid2);
|
4165
4362
|
},
|
4166
|
-
async countManyEntriesDraftRelations(
|
4363
|
+
async countManyEntriesDraftRelations(documentIds, uid2, locale) {
|
4167
4364
|
const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
|
4168
4365
|
if (!hasRelations) {
|
4169
4366
|
return 0;
|
4170
4367
|
}
|
4368
|
+
let localeFilter = {};
|
4369
|
+
if (locale) {
|
4370
|
+
localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
|
4371
|
+
}
|
4171
4372
|
const entities = await strapi2.db.query(uid2).findMany({
|
4172
4373
|
populate,
|
4173
4374
|
where: {
|
4174
|
-
|
4175
|
-
...
|
4375
|
+
documentId: { $in: documentIds },
|
4376
|
+
...localeFilter
|
4176
4377
|
}
|
4177
4378
|
});
|
4178
4379
|
const totalNumberDraftRelations = entities.reduce(
|