@strapi/content-manager 0.0.0-experimental.a65a85fdea97faae8679d3ffc5f9d79af61abd26 → 0.0.0-experimental.a687a6977f91492ccfc6771bf398a5236ece3c94
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-43KmCNQE.js → ComponentConfigurationPage-BebDdCkl.js} +4 -4
- package/dist/_chunks/{ComponentConfigurationPage-43KmCNQE.js.map → ComponentConfigurationPage-BebDdCkl.js.map} +1 -1
- package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs → ComponentConfigurationPage-XdGcgtZh.mjs} +4 -4
- package/dist/_chunks/{ComponentConfigurationPage--2aLCv-G.mjs.map → ComponentConfigurationPage-XdGcgtZh.mjs.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-DaNf9MoK.mjs} +4 -4
- package/dist/_chunks/{EditConfigurationPage-CUcGHHvQ.mjs.map → EditConfigurationPage-DaNf9MoK.mjs.map} +1 -1
- package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js → EditConfigurationPage-sdGi-bna.js} +4 -4
- package/dist/_chunks/{EditConfigurationPage-BfFzJ4Br.js.map → EditConfigurationPage-sdGi-bna.js.map} +1 -1
- package/dist/_chunks/{EditViewPage-CzOT5Kpj.js → EditViewPage-BRA-JSnw.js} +101 -52
- package/dist/_chunks/EditViewPage-BRA-JSnw.js.map +1 -0
- package/dist/_chunks/EditViewPage-DtbTsNeX.mjs +254 -0
- package/dist/_chunks/EditViewPage-DtbTsNeX.mjs.map +1 -0
- package/dist/_chunks/{Field-Dlh0uGnL.mjs → Field-CDmB9zqh.mjs} +1075 -812
- package/dist/_chunks/Field-CDmB9zqh.mjs.map +1 -0
- package/dist/_chunks/{Field-Caef4JjM.js → Field-DoVRA4co.js} +1120 -858
- package/dist/_chunks/Field-DoVRA4co.js.map +1 -0
- package/dist/_chunks/{Form-BzuAjtRq.js → Form-BEh514bg.js} +69 -49
- package/dist/_chunks/Form-BEh514bg.js.map +1 -0
- package/dist/_chunks/{Form-EnaQL_6L.mjs → Form-Cle2X-4n.mjs} +70 -49
- package/dist/_chunks/Form-Cle2X-4n.mjs.map +1 -0
- package/dist/_chunks/{History-C17LiyRg.js → History-BsPXl1XH.js} +182 -146
- package/dist/_chunks/History-BsPXl1XH.js.map +1 -0
- package/dist/_chunks/{History-D6sbCJvo.mjs → History-ktAPcvHJ.mjs} +182 -145
- package/dist/_chunks/History-ktAPcvHJ.mjs.map +1 -0
- package/dist/_chunks/{ListConfigurationPage-Ce4qs7qE.mjs → ListConfigurationPage-BNAS_v2s.mjs} +71 -60
- package/dist/_chunks/ListConfigurationPage-BNAS_v2s.mjs.map +1 -0
- package/dist/_chunks/{ListConfigurationPage-Dks5SX6f.js → ListConfigurationPage-CN1-7Pgi.js} +73 -63
- package/dist/_chunks/ListConfigurationPage-CN1-7Pgi.js.map +1 -0
- package/dist/_chunks/{ListViewPage-Be7S5aKL.mjs → ListViewPage-B15c_0Vt.mjs} +143 -139
- package/dist/_chunks/ListViewPage-B15c_0Vt.mjs.map +1 -0
- package/dist/_chunks/{ListViewPage-BwrZrPsh.js → ListViewPage-BUKFOJuS.js} +146 -142
- package/dist/_chunks/ListViewPage-BUKFOJuS.js.map +1 -0
- package/dist/_chunks/{NoContentTypePage-CIPmYQMm.mjs → NoContentTypePage-CAgdmhlo.mjs} +7 -7
- package/dist/_chunks/NoContentTypePage-CAgdmhlo.mjs.map +1 -0
- package/dist/_chunks/{NoContentTypePage-Cu5r1-JT.js → NoContentTypePage-Cs7Kk9EW.js} +5 -5
- package/dist/_chunks/NoContentTypePage-Cs7Kk9EW.js.map +1 -0
- package/dist/_chunks/{NoPermissionsPage-C-j6TEUF.js → NoPermissionsPage-DRPnEq9t.js} +4 -5
- package/dist/_chunks/NoPermissionsPage-DRPnEq9t.js.map +1 -0
- package/dist/_chunks/{NoPermissionsPage-DhJ7LYrr.mjs → NoPermissionsPage-JxX4Y4RJ.mjs} +5 -6
- package/dist/_chunks/NoPermissionsPage-JxX4Y4RJ.mjs.map +1 -0
- package/dist/_chunks/Preview-BKuVG1dV.js +286 -0
- package/dist/_chunks/Preview-BKuVG1dV.js.map +1 -0
- package/dist/_chunks/Preview-CQlTtbLc.mjs +267 -0
- package/dist/_chunks/Preview-CQlTtbLc.mjs.map +1 -0
- package/dist/_chunks/{Relations-CY7AtkDA.mjs → Relations-DMQ93R3L.mjs} +135 -89
- package/dist/_chunks/Relations-DMQ93R3L.mjs.map +1 -0
- package/dist/_chunks/{Relations-Czs-uZ-s.js → Relations-DTEGSaM_.js} +138 -93
- package/dist/_chunks/Relations-DTEGSaM_.js.map +1 -0
- package/dist/_chunks/{en-MBPul9Su.mjs → en-CfIXaZf9.mjs} +36 -17
- package/dist/_chunks/{en-MBPul9Su.mjs.map → en-CfIXaZf9.mjs.map} +1 -1
- package/dist/_chunks/{en-C-V1_90f.js → en-DTWPCdTS.js} +36 -17
- package/dist/_chunks/{en-C-V1_90f.js.map → en-DTWPCdTS.js.map} +1 -1
- package/dist/_chunks/{es-EUonQTon.js → es-9K52xZIr.js} +2 -2
- package/dist/_chunks/{ja-CcFe8diO.js.map → es-9K52xZIr.js.map} +1 -1
- package/dist/_chunks/{es-CeXiYflN.mjs → es-D34tqjMw.mjs} +2 -2
- package/dist/_chunks/{es-CeXiYflN.mjs.map → es-D34tqjMw.mjs.map} +1 -1
- package/dist/_chunks/{fr-CD9VFbPM.mjs → fr--pg5jUbt.mjs} +13 -3
- package/dist/_chunks/{fr-CD9VFbPM.mjs.map → fr--pg5jUbt.mjs.map} +1 -1
- package/dist/_chunks/{fr-B7kGGg3E.js → fr-B2Kyv8Z9.js} +13 -3
- package/dist/_chunks/{fr-B7kGGg3E.js.map → fr-B2Kyv8Z9.js.map} +1 -1
- package/dist/_chunks/{index-DNVx8ssZ.mjs → index-BdUq-Dtg.mjs} +1839 -831
- package/dist/_chunks/index-BdUq-Dtg.mjs.map +1 -0
- package/dist/_chunks/{index-X_2tafck.js → index-DjV7tyGu.js} +1826 -818
- package/dist/_chunks/index-DjV7tyGu.js.map +1 -0
- package/dist/_chunks/{ja-CcFe8diO.js → ja-7sfIbjxE.js} +2 -2
- package/dist/_chunks/{es-EUonQTon.js.map → ja-7sfIbjxE.js.map} +1 -1
- package/dist/_chunks/{ja-CtsUxOvk.mjs → ja-BHqhDq4V.mjs} +2 -2
- package/dist/_chunks/{ja-CtsUxOvk.mjs.map → ja-BHqhDq4V.mjs.map} +1 -1
- package/dist/_chunks/{layout-Dnh0PNp9.mjs → layout-BJ8CpEJu.mjs} +47 -29
- package/dist/_chunks/layout-BJ8CpEJu.mjs.map +1 -0
- package/dist/_chunks/{layout-dBc7wN7L.js → layout-Cx9LHtH5.js} +47 -31
- package/dist/_chunks/layout-Cx9LHtH5.js.map +1 -0
- package/dist/_chunks/{objects-gigeqt7s.js → objects-BcXOv6_9.js} +2 -4
- package/dist/_chunks/{objects-gigeqt7s.js.map → objects-BcXOv6_9.js.map} +1 -1
- package/dist/_chunks/{objects-mKMAmfec.mjs → objects-D6yBsdmx.mjs} +2 -4
- package/dist/_chunks/{objects-mKMAmfec.mjs.map → objects-D6yBsdmx.mjs.map} +1 -1
- package/dist/_chunks/{relations-4pHtBrHJ.js → relations-5bc76OJz.js} +6 -7
- package/dist/_chunks/relations-5bc76OJz.js.map +1 -0
- package/dist/_chunks/{relations-Dx7tMKJN.mjs → relations-BeezTo41.mjs} +6 -7
- package/dist/_chunks/relations-BeezTo41.mjs.map +1 -0
- 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 +37 -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/EditViewPage.d.ts +9 -1
- package/dist/admin/src/pages/EditView/components/DocumentActions.d.ts +11 -4
- package/dist/admin/src/pages/EditView/components/DocumentStatus.d.ts +2 -2
- 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 +11 -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/preview/components/PreviewContent.d.ts +2 -0
- package/dist/admin/src/preview/components/PreviewHeader.d.ts +2 -0
- package/dist/admin/src/preview/components/PreviewSidePanel.d.ts +3 -0
- package/dist/admin/src/preview/constants.d.ts +1 -0
- package/dist/admin/src/preview/index.d.ts +4 -0
- package/dist/admin/src/preview/pages/Preview.d.ts +11 -0
- package/dist/admin/src/preview/routes.d.ts +3 -0
- package/dist/admin/src/preview/services/preview.d.ts +3 -0
- package/dist/admin/src/router.d.ts +1 -1
- 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 -20
- 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 +1008 -594
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.mjs +1014 -600
- package/dist/server/index.mjs.map +1 -1
- package/dist/server/src/bootstrap.d.ts.map +1 -1
- package/dist/server/src/controllers/collection-types.d.ts.map +1 -1
- package/dist/server/src/controllers/index.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 +22 -0
- package/dist/server/src/controllers/utils/metadata.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/dimensions.d.ts +11 -0
- package/dist/server/src/controllers/validation/dimensions.d.ts.map +1 -0
- package/dist/server/src/controllers/validation/index.d.ts +1 -1
- package/dist/server/src/history/services/history.d.ts +2 -4
- package/dist/server/src/history/services/history.d.ts.map +1 -1
- package/dist/server/src/history/services/index.d.ts +6 -2
- package/dist/server/src/history/services/index.d.ts.map +1 -1
- package/dist/server/src/history/services/lifecycles.d.ts +9 -0
- package/dist/server/src/history/services/lifecycles.d.ts.map +1 -0
- package/dist/server/src/history/services/utils.d.ts +41 -9
- package/dist/server/src/history/services/utils.d.ts.map +1 -1
- package/dist/server/src/history/utils.d.ts +6 -2
- package/dist/server/src/history/utils.d.ts.map +1 -1
- package/dist/server/src/index.d.ts +21 -42
- 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/preview/constants.d.ts +2 -0
- package/dist/server/src/preview/constants.d.ts.map +1 -0
- package/dist/server/src/preview/controllers/index.d.ts +2 -0
- package/dist/server/src/preview/controllers/index.d.ts.map +1 -0
- package/dist/server/src/preview/controllers/preview.d.ts +13 -0
- package/dist/server/src/preview/controllers/preview.d.ts.map +1 -0
- package/dist/server/src/preview/controllers/validation/preview.d.ts +6 -0
- package/dist/server/src/preview/controllers/validation/preview.d.ts.map +1 -0
- package/dist/server/src/preview/index.d.ts +4 -0
- package/dist/server/src/preview/index.d.ts.map +1 -0
- package/dist/server/src/preview/routes/index.d.ts +8 -0
- package/dist/server/src/preview/routes/index.d.ts.map +1 -0
- package/dist/server/src/preview/routes/preview.d.ts +4 -0
- package/dist/server/src/preview/routes/preview.d.ts.map +1 -0
- package/dist/server/src/preview/services/index.d.ts +15 -0
- package/dist/server/src/preview/services/index.d.ts.map +1 -0
- package/dist/server/src/preview/services/preview-config.d.ts +30 -0
- package/dist/server/src/preview/services/preview-config.d.ts.map +1 -0
- package/dist/server/src/preview/services/preview.d.ts +12 -0
- package/dist/server/src/preview/services/preview.d.ts.map +1 -0
- package/dist/server/src/preview/utils.d.ts +18 -0
- package/dist/server/src/preview/utils.d.ts.map +1 -0
- package/dist/server/src/routes/index.d.ts.map +1 -1
- package/dist/server/src/services/document-manager.d.ts +13 -12
- package/dist/server/src/services/document-manager.d.ts.map +1 -1
- package/dist/server/src/services/document-metadata.d.ts +14 -35
- package/dist/server/src/services/document-metadata.d.ts.map +1 -1
- package/dist/server/src/services/index.d.ts +21 -42
- 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/configuration/index.d.ts +2 -2
- package/dist/server/src/services/utils/configuration/layouts.d.ts +2 -2
- 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/server/src/utils/index.d.ts +2 -0
- package/dist/server/src/utils/index.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/index.d.ts +1 -0
- package/dist/shared/contracts/index.d.ts.map +1 -1
- package/dist/shared/contracts/preview.d.ts +27 -0
- package/dist/shared/contracts/preview.d.ts.map +1 -0
- package/dist/shared/contracts/relations.d.ts +2 -2
- package/dist/shared/contracts/relations.d.ts.map +1 -1
- package/dist/shared/index.js +4 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/index.mjs +4 -0
- package/dist/shared/index.mjs.map +1 -1
- package/package.json +19 -20
- 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 +0 -203
- 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/relations-4pHtBrHJ.js.map +0 -1
- package/dist/_chunks/relations-Dx7tMKJN.mjs.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.js
CHANGED
@@ -33,10 +33,10 @@ const isNil__default = /* @__PURE__ */ _interopDefault(isNil);
|
|
33
33
|
const ___default = /* @__PURE__ */ _interopDefault(_);
|
34
34
|
const qs__default = /* @__PURE__ */ _interopDefault(qs);
|
35
35
|
const slugify__default = /* @__PURE__ */ _interopDefault(slugify);
|
36
|
-
const getService$
|
36
|
+
const getService$2 = (name) => {
|
37
37
|
return strapi.plugin("content-manager").service(name);
|
38
38
|
};
|
39
|
-
function getService(strapi2, name) {
|
39
|
+
function getService$1(strapi2, name) {
|
40
40
|
return strapi2.service(`plugin::content-manager.${name}`);
|
41
41
|
}
|
42
42
|
const historyRestoreVersionSchema = yup__namespace.object().shape({
|
@@ -72,7 +72,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
|
|
72
72
|
if (!isSingleType && (!contentTypeUid || !ctx.query.documentId)) {
|
73
73
|
throw new strapiUtils.errors.ForbiddenError("contentType and documentId are required");
|
74
74
|
}
|
75
|
-
const permissionChecker2 = getService$
|
75
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
76
76
|
userAbility: ctx.state.userAbility,
|
77
77
|
model: ctx.query.contentType
|
78
78
|
});
|
@@ -80,7 +80,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
|
|
80
80
|
return ctx.forbidden();
|
81
81
|
}
|
82
82
|
const query = await permissionChecker2.sanitizeQuery(ctx.query);
|
83
|
-
const { results, pagination } = await getService(strapi2, "history").findVersionsPage({
|
83
|
+
const { results, pagination } = await getService$1(strapi2, "history").findVersionsPage({
|
84
84
|
query: {
|
85
85
|
...query,
|
86
86
|
...getValidPagination({ page: query.page, pageSize: query.pageSize })
|
@@ -105,14 +105,14 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
|
|
105
105
|
async restoreVersion(ctx) {
|
106
106
|
const request = ctx.request;
|
107
107
|
await validateRestoreVersion(request.body, "contentType is required");
|
108
|
-
const permissionChecker2 = getService$
|
108
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
109
109
|
userAbility: ctx.state.userAbility,
|
110
110
|
model: request.body.contentType
|
111
111
|
});
|
112
112
|
if (permissionChecker2.cannot.update()) {
|
113
113
|
throw new strapiUtils.errors.ForbiddenError();
|
114
114
|
}
|
115
|
-
const restoredDocument = await getService(strapi2, "history").restoreVersion(
|
115
|
+
const restoredDocument = await getService$1(strapi2, "history").restoreVersion(
|
116
116
|
request.params.versionId
|
117
117
|
);
|
118
118
|
return {
|
@@ -121,7 +121,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
|
|
121
121
|
}
|
122
122
|
};
|
123
123
|
};
|
124
|
-
const controllers$
|
124
|
+
const controllers$2 = {
|
125
125
|
"history-version": createHistoryVersionController
|
126
126
|
/**
|
127
127
|
* Casting is needed because the types aren't aware that Strapi supports
|
@@ -138,43 +138,70 @@ const FIELDS_TO_IGNORE = [
|
|
138
138
|
"strapi_stage",
|
139
139
|
"strapi_assignee"
|
140
140
|
];
|
141
|
-
const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
|
142
|
-
const sanitizedContentTypeSchemaAttributes = fp.omit(FIELDS_TO_IGNORE, contentTypeSchemaAttributes);
|
143
|
-
const reduceDifferenceToAttributesObject = (diffKeys, source) => {
|
144
|
-
return diffKeys.reduce((previousAttributesObject, diffKey) => {
|
145
|
-
previousAttributesObject[diffKey] = source[diffKey];
|
146
|
-
return previousAttributesObject;
|
147
|
-
}, {});
|
148
|
-
};
|
149
|
-
const versionSchemaKeys = Object.keys(versionSchemaAttributes);
|
150
|
-
const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
|
151
|
-
const uniqueToContentType = fp.difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
|
152
|
-
const added = reduceDifferenceToAttributesObject(
|
153
|
-
uniqueToContentType,
|
154
|
-
sanitizedContentTypeSchemaAttributes
|
155
|
-
);
|
156
|
-
const uniqueToVersion = fp.difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
|
157
|
-
const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
|
158
|
-
return { added, removed };
|
159
|
-
};
|
160
141
|
const DEFAULT_RETENTION_DAYS = 90;
|
161
|
-
const
|
162
|
-
const
|
163
|
-
|
164
|
-
|
142
|
+
const createServiceUtils = ({ strapi: strapi2 }) => {
|
143
|
+
const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
|
144
|
+
const sanitizedContentTypeSchemaAttributes = fp.omit(
|
145
|
+
FIELDS_TO_IGNORE,
|
146
|
+
contentTypeSchemaAttributes
|
147
|
+
);
|
148
|
+
const reduceDifferenceToAttributesObject = (diffKeys, source) => {
|
149
|
+
return diffKeys.reduce(
|
150
|
+
(previousAttributesObject, diffKey) => {
|
151
|
+
previousAttributesObject[diffKey] = source[diffKey];
|
152
|
+
return previousAttributesObject;
|
153
|
+
},
|
154
|
+
{}
|
155
|
+
);
|
156
|
+
};
|
157
|
+
const versionSchemaKeys = Object.keys(versionSchemaAttributes);
|
158
|
+
const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
|
159
|
+
const uniqueToContentType = fp.difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
|
160
|
+
const added = reduceDifferenceToAttributesObject(
|
161
|
+
uniqueToContentType,
|
162
|
+
sanitizedContentTypeSchemaAttributes
|
163
|
+
);
|
164
|
+
const uniqueToVersion = fp.difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
|
165
|
+
const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
|
166
|
+
return { added, removed };
|
165
167
|
};
|
166
|
-
const
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
168
|
+
const getRelationRestoreValue = async (versionRelationData, attribute) => {
|
169
|
+
if (Array.isArray(versionRelationData)) {
|
170
|
+
if (versionRelationData.length === 0)
|
171
|
+
return versionRelationData;
|
172
|
+
const existingAndMissingRelations = await Promise.all(
|
173
|
+
versionRelationData.map((relation) => {
|
174
|
+
return strapi2.documents(attribute.target).findOne({
|
175
|
+
documentId: relation.documentId,
|
176
|
+
locale: relation.locale || void 0
|
177
|
+
});
|
178
|
+
})
|
179
|
+
);
|
180
|
+
return existingAndMissingRelations.filter(
|
181
|
+
(relation) => relation !== null
|
182
|
+
);
|
173
183
|
}
|
174
|
-
return
|
184
|
+
return strapi2.documents(attribute.target).findOne({
|
185
|
+
documentId: versionRelationData.documentId,
|
186
|
+
locale: versionRelationData.locale || void 0
|
187
|
+
});
|
188
|
+
};
|
189
|
+
const getMediaRestoreValue = async (versionRelationData, attribute) => {
|
190
|
+
if (attribute.multiple) {
|
191
|
+
const existingAndMissingMedias = await Promise.all(
|
192
|
+
// @ts-expect-error Fix the type definitions so this isn't any
|
193
|
+
versionRelationData.map((media) => {
|
194
|
+
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
|
195
|
+
})
|
196
|
+
);
|
197
|
+
return existingAndMissingMedias.filter((media) => media != null);
|
198
|
+
}
|
199
|
+
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
|
175
200
|
};
|
176
201
|
const localesService = strapi2.plugin("i18n")?.service("locales");
|
202
|
+
const i18nContentTypeService = strapi2.plugin("i18n")?.service("content-types");
|
177
203
|
const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
|
204
|
+
const isLocalizedContentType = (model) => i18nContentTypeService ? i18nContentTypeService.isLocalizedContentType(model) : false;
|
178
205
|
const getLocaleDictionary = async () => {
|
179
206
|
if (!localesService)
|
180
207
|
return {};
|
@@ -187,36 +214,67 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
187
214
|
{}
|
188
215
|
);
|
189
216
|
};
|
217
|
+
const getRetentionDays = () => {
|
218
|
+
const featureConfig = strapi2.ee.features.get("cms-content-history");
|
219
|
+
const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
|
220
|
+
const userRetentionDays = strapi2.config.get("admin.history.retentionDays");
|
221
|
+
if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
|
222
|
+
return userRetentionDays;
|
223
|
+
}
|
224
|
+
return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
|
225
|
+
};
|
190
226
|
const getVersionStatus = async (contentTypeUid, document) => {
|
191
227
|
const documentMetadataService = strapi2.plugin("content-manager").service("document-metadata");
|
192
228
|
const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
|
193
229
|
return documentMetadataService.getStatus(document, meta.availableStatus);
|
194
230
|
};
|
195
|
-
const
|
231
|
+
const getComponentFields = (componentUID) => {
|
232
|
+
return Object.entries(strapi2.getModel(componentUID).attributes).reduce(
|
233
|
+
(fieldsAcc, [key, attribute]) => {
|
234
|
+
if (!["relation", "media", "component", "dynamiczone"].includes(attribute.type)) {
|
235
|
+
fieldsAcc.push(key);
|
236
|
+
}
|
237
|
+
return fieldsAcc;
|
238
|
+
},
|
239
|
+
[]
|
240
|
+
);
|
241
|
+
};
|
242
|
+
const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
|
196
243
|
const model = strapi2.getModel(uid2);
|
197
244
|
const attributes = Object.entries(model.attributes);
|
245
|
+
const fieldSelector = useDatabaseSyntax ? "select" : "fields";
|
198
246
|
return attributes.reduce((acc, [attributeName, attribute]) => {
|
199
247
|
switch (attribute.type) {
|
200
248
|
case "relation": {
|
249
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
250
|
+
if (isMorphRelation) {
|
251
|
+
break;
|
252
|
+
}
|
201
253
|
const isVisible2 = strapiUtils.contentTypes.isVisibleAttribute(model, attributeName);
|
202
254
|
if (isVisible2) {
|
203
|
-
acc[attributeName] = {
|
255
|
+
acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
|
204
256
|
}
|
205
257
|
break;
|
206
258
|
}
|
207
259
|
case "media": {
|
208
|
-
acc[attributeName] = {
|
260
|
+
acc[attributeName] = { [fieldSelector]: ["id"] };
|
209
261
|
break;
|
210
262
|
}
|
211
263
|
case "component": {
|
212
264
|
const populate = getDeepPopulate2(attribute.component);
|
213
|
-
acc[attributeName] = {
|
265
|
+
acc[attributeName] = {
|
266
|
+
populate,
|
267
|
+
[fieldSelector]: getComponentFields(attribute.component)
|
268
|
+
};
|
214
269
|
break;
|
215
270
|
}
|
216
271
|
case "dynamiczone": {
|
217
272
|
const populatedComponents = (attribute.components || []).reduce(
|
218
273
|
(acc2, componentUID) => {
|
219
|
-
acc2[componentUID] = {
|
274
|
+
acc2[componentUID] = {
|
275
|
+
populate: getDeepPopulate2(componentUID),
|
276
|
+
[fieldSelector]: getComponentFields(componentUID)
|
277
|
+
};
|
220
278
|
return acc2;
|
221
279
|
},
|
222
280
|
{}
|
@@ -228,80 +286,69 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
228
286
|
return acc;
|
229
287
|
}, {});
|
230
288
|
};
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
|
238
|
-
return next();
|
289
|
+
const buildMediaResponse = async (values) => {
|
290
|
+
return values.slice(0, 25).reduce(
|
291
|
+
async (currentRelationDataPromise, entry) => {
|
292
|
+
const currentRelationData = await currentRelationDataPromise;
|
293
|
+
if (!entry) {
|
294
|
+
return currentRelationData;
|
239
295
|
}
|
240
|
-
|
241
|
-
|
296
|
+
const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
|
297
|
+
if (relatedEntry) {
|
298
|
+
currentRelationData.results.push(relatedEntry);
|
299
|
+
} else {
|
300
|
+
currentRelationData.meta.missingCount += 1;
|
242
301
|
}
|
243
|
-
|
244
|
-
|
245
|
-
|
302
|
+
return currentRelationData;
|
303
|
+
},
|
304
|
+
Promise.resolve({
|
305
|
+
results: [],
|
306
|
+
meta: { missingCount: 0 }
|
307
|
+
})
|
308
|
+
);
|
309
|
+
};
|
310
|
+
const buildRelationReponse = async (values, attributeSchema) => {
|
311
|
+
return values.slice(0, 25).reduce(
|
312
|
+
async (currentRelationDataPromise, entry) => {
|
313
|
+
const currentRelationData = await currentRelationDataPromise;
|
314
|
+
if (!entry) {
|
315
|
+
return currentRelationData;
|
246
316
|
}
|
247
|
-
const
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
documentId: documentContext.documentId,
|
253
|
-
locale,
|
254
|
-
populate: getDeepPopulate2(contentTypeUid)
|
255
|
-
});
|
256
|
-
const status = await getVersionStatus(contentTypeUid, document);
|
257
|
-
const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
|
258
|
-
const componentsSchemas = Object.keys(
|
259
|
-
attributesSchema
|
260
|
-
).reduce((currentComponentSchemas, key) => {
|
261
|
-
const fieldSchema = attributesSchema[key];
|
262
|
-
if (fieldSchema.type === "component") {
|
263
|
-
const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
|
264
|
-
return {
|
265
|
-
...currentComponentSchemas,
|
266
|
-
[fieldSchema.component]: componentSchema
|
267
|
-
};
|
268
|
-
}
|
269
|
-
return currentComponentSchemas;
|
270
|
-
}, {});
|
271
|
-
await strapi2.db.transaction(async ({ onCommit }) => {
|
272
|
-
onCommit(() => {
|
273
|
-
this.createVersion({
|
274
|
-
contentType: contentTypeUid,
|
275
|
-
data: fp.omit(FIELDS_TO_IGNORE, document),
|
276
|
-
schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
|
277
|
-
componentsSchemas,
|
278
|
-
relatedDocumentId: documentContext.documentId,
|
279
|
-
locale,
|
280
|
-
status
|
281
|
-
});
|
317
|
+
const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
|
318
|
+
if (relatedEntry) {
|
319
|
+
currentRelationData.results.push({
|
320
|
+
...relatedEntry,
|
321
|
+
status: await getVersionStatus(attributeSchema.target, relatedEntry)
|
282
322
|
});
|
283
|
-
}
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
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
|
+
return {
|
335
|
+
getSchemaAttributesDiff,
|
336
|
+
getRelationRestoreValue,
|
337
|
+
getMediaRestoreValue,
|
338
|
+
getDefaultLocale,
|
339
|
+
isLocalizedContentType,
|
340
|
+
getLocaleDictionary,
|
341
|
+
getRetentionDays,
|
342
|
+
getVersionStatus,
|
343
|
+
getDeepPopulate: getDeepPopulate2,
|
344
|
+
buildMediaResponse,
|
345
|
+
buildRelationReponse
|
346
|
+
};
|
347
|
+
};
|
348
|
+
const createHistoryService = ({ strapi: strapi2 }) => {
|
349
|
+
const query = strapi2.db.query(HISTORY_VERSION_UID);
|
350
|
+
const serviceUtils = createServiceUtils({ strapi: strapi2 });
|
351
|
+
return {
|
305
352
|
async createVersion(historyVersionData) {
|
306
353
|
await query.create({
|
307
354
|
data: {
|
@@ -312,7 +359,13 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
312
359
|
});
|
313
360
|
},
|
314
361
|
async findVersionsPage(params) {
|
315
|
-
const
|
362
|
+
const model = strapi2.getModel(params.query.contentType);
|
363
|
+
const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
|
364
|
+
const defaultLocale = await serviceUtils.getDefaultLocale();
|
365
|
+
let locale = null;
|
366
|
+
if (isLocalizedContentType) {
|
367
|
+
locale = params.query.locale || defaultLocale;
|
368
|
+
}
|
316
369
|
const [{ results, pagination }, localeDictionary] = await Promise.all([
|
317
370
|
query.findPage({
|
318
371
|
...params.query,
|
@@ -326,82 +379,43 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
326
379
|
populate: ["createdBy"],
|
327
380
|
orderBy: [{ createdAt: "desc" }]
|
328
381
|
}),
|
329
|
-
getLocaleDictionary()
|
382
|
+
serviceUtils.getLocaleDictionary()
|
330
383
|
]);
|
331
|
-
const buildRelationReponse = async (values, attributeSchema) => {
|
332
|
-
return values.slice(0, 25).reduce(
|
333
|
-
async (currentRelationDataPromise, entry) => {
|
334
|
-
const currentRelationData = await currentRelationDataPromise;
|
335
|
-
if (!entry) {
|
336
|
-
return currentRelationData;
|
337
|
-
}
|
338
|
-
const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
|
339
|
-
const permissionChecker2 = getService$1("permission-checker").create({
|
340
|
-
userAbility: params.state.userAbility,
|
341
|
-
model: attributeSchema.target
|
342
|
-
});
|
343
|
-
const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
|
344
|
-
if (sanitizedEntry) {
|
345
|
-
currentRelationData.results.push({
|
346
|
-
...sanitizedEntry,
|
347
|
-
status: await getVersionStatus(attributeSchema.target, sanitizedEntry)
|
348
|
-
});
|
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
|
-
const buildMediaResponse = async (values) => {
|
361
|
-
return values.slice(0, 25).reduce(
|
362
|
-
async (currentRelationDataPromise, entry) => {
|
363
|
-
const currentRelationData = await currentRelationDataPromise;
|
364
|
-
if (!entry) {
|
365
|
-
return currentRelationData;
|
366
|
-
}
|
367
|
-
const permissionChecker2 = getService$1("permission-checker").create({
|
368
|
-
userAbility: params.state.userAbility,
|
369
|
-
model: "plugin::upload.file"
|
370
|
-
});
|
371
|
-
const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
|
372
|
-
const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
|
373
|
-
if (sanitizedEntry) {
|
374
|
-
currentRelationData.results.push(sanitizedEntry);
|
375
|
-
} else {
|
376
|
-
currentRelationData.meta.missingCount += 1;
|
377
|
-
}
|
378
|
-
return currentRelationData;
|
379
|
-
},
|
380
|
-
Promise.resolve({
|
381
|
-
results: [],
|
382
|
-
meta: { missingCount: 0 }
|
383
|
-
})
|
384
|
-
);
|
385
|
-
};
|
386
384
|
const populateEntryRelations = async (entry) => {
|
387
385
|
const entryWithRelations = await Object.entries(entry.schema).reduce(
|
388
386
|
async (currentDataWithRelations, [attributeKey, attributeSchema]) => {
|
389
387
|
const attributeValue = entry.data[attributeKey];
|
390
388
|
const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
|
391
389
|
if (attributeSchema.type === "media") {
|
390
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
391
|
+
userAbility: params.state.userAbility,
|
392
|
+
model: "plugin::upload.file"
|
393
|
+
});
|
394
|
+
const response = await serviceUtils.buildMediaResponse(attributeValues);
|
395
|
+
const sanitizedResults = await Promise.all(
|
396
|
+
response.results.map((media) => permissionChecker2.sanitizeOutput(media))
|
397
|
+
);
|
392
398
|
return {
|
393
399
|
...await currentDataWithRelations,
|
394
|
-
[attributeKey]:
|
400
|
+
[attributeKey]: {
|
401
|
+
results: sanitizedResults,
|
402
|
+
meta: response.meta
|
403
|
+
}
|
395
404
|
};
|
396
405
|
}
|
397
406
|
if (attributeSchema.type === "relation" && attributeSchema.relation !== "morphToOne" && attributeSchema.relation !== "morphToMany") {
|
398
407
|
if (attributeSchema.target === "admin::user") {
|
399
408
|
const adminUsers = await Promise.all(
|
400
|
-
attributeValues.map(
|
409
|
+
attributeValues.map((userToPopulate) => {
|
401
410
|
if (userToPopulate == null) {
|
402
411
|
return null;
|
403
412
|
}
|
404
|
-
return strapi2.query("admin::user").findOne({
|
413
|
+
return strapi2.query("admin::user").findOne({
|
414
|
+
where: {
|
415
|
+
...userToPopulate.id ? { id: userToPopulate.id } : {},
|
416
|
+
...userToPopulate.documentId ? { documentId: userToPopulate.documentId } : {}
|
417
|
+
}
|
418
|
+
});
|
405
419
|
})
|
406
420
|
);
|
407
421
|
return {
|
@@ -414,9 +428,23 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
414
428
|
[attributeKey]: adminUsers
|
415
429
|
};
|
416
430
|
}
|
431
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
432
|
+
userAbility: params.state.userAbility,
|
433
|
+
model: attributeSchema.target
|
434
|
+
});
|
435
|
+
const response = await serviceUtils.buildRelationReponse(
|
436
|
+
attributeValues,
|
437
|
+
attributeSchema
|
438
|
+
);
|
439
|
+
const sanitizedResults = await Promise.all(
|
440
|
+
response.results.map((media) => permissionChecker2.sanitizeOutput(media))
|
441
|
+
);
|
417
442
|
return {
|
418
443
|
...await currentDataWithRelations,
|
419
|
-
[attributeKey]:
|
444
|
+
[attributeKey]: {
|
445
|
+
results: sanitizedResults,
|
446
|
+
meta: response.meta
|
447
|
+
}
|
420
448
|
};
|
421
449
|
}
|
422
450
|
return currentDataWithRelations;
|
@@ -431,7 +459,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
431
459
|
...result,
|
432
460
|
data: await populateEntryRelations(result),
|
433
461
|
meta: {
|
434
|
-
unknownAttributes: getSchemaAttributesDiff(
|
462
|
+
unknownAttributes: serviceUtils.getSchemaAttributesDiff(
|
435
463
|
result.schema,
|
436
464
|
strapi2.getModel(params.query.contentType).attributes
|
437
465
|
)
|
@@ -448,7 +476,10 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
448
476
|
async restoreVersion(versionId) {
|
449
477
|
const version = await query.findOne({ where: { id: versionId } });
|
450
478
|
const contentTypeSchemaAttributes = strapi2.getModel(version.contentType).attributes;
|
451
|
-
const schemaDiff = getSchemaAttributesDiff(
|
479
|
+
const schemaDiff = serviceUtils.getSchemaAttributesDiff(
|
480
|
+
version.schema,
|
481
|
+
contentTypeSchemaAttributes
|
482
|
+
);
|
452
483
|
const dataWithoutAddedAttributes = Object.keys(schemaDiff.added).reduce(
|
453
484
|
(currentData, addedKey) => {
|
454
485
|
currentData[addedKey] = null;
|
@@ -461,61 +492,26 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
461
492
|
FIELDS_TO_IGNORE,
|
462
493
|
contentTypeSchemaAttributes
|
463
494
|
);
|
464
|
-
const
|
465
|
-
|
466
|
-
|
467
|
-
const
|
468
|
-
if (
|
495
|
+
const reducer = strapiUtils.async.reduce(Object.entries(sanitizedSchemaAttributes));
|
496
|
+
const dataWithoutMissingRelations = await reducer(
|
497
|
+
async (previousRelationAttributes, [name, attribute]) => {
|
498
|
+
const versionRelationData = version.data[name];
|
499
|
+
if (!versionRelationData) {
|
469
500
|
return previousRelationAttributes;
|
470
501
|
}
|
471
502
|
if (attribute.type === "relation" && // TODO: handle polymorphic relations
|
472
503
|
attribute.relation !== "morphToOne" && attribute.relation !== "morphToMany") {
|
473
|
-
|
474
|
-
|
475
|
-
return previousRelationAttributes;
|
476
|
-
const existingAndMissingRelations = await Promise.all(
|
477
|
-
relationData.map((relation) => {
|
478
|
-
return strapi2.documents(attribute.target).findOne({
|
479
|
-
documentId: relation.documentId,
|
480
|
-
locale: relation.locale || void 0
|
481
|
-
});
|
482
|
-
})
|
483
|
-
);
|
484
|
-
const existingRelations = existingAndMissingRelations.filter(
|
485
|
-
(relation) => relation !== null
|
486
|
-
);
|
487
|
-
previousRelationAttributes[name] = existingRelations;
|
488
|
-
} else {
|
489
|
-
const existingRelation = await strapi2.documents(attribute.target).findOne({
|
490
|
-
documentId: relationData.documentId,
|
491
|
-
locale: relationData.locale || void 0
|
492
|
-
});
|
493
|
-
if (!existingRelation) {
|
494
|
-
previousRelationAttributes[name] = null;
|
495
|
-
}
|
496
|
-
}
|
504
|
+
const data2 = await serviceUtils.getRelationRestoreValue(versionRelationData, attribute);
|
505
|
+
previousRelationAttributes[name] = data2;
|
497
506
|
}
|
498
507
|
if (attribute.type === "media") {
|
499
|
-
|
500
|
-
|
501
|
-
// @ts-expect-error Fix the type definitions so this isn't any
|
502
|
-
relationData.map((media) => {
|
503
|
-
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
|
504
|
-
})
|
505
|
-
);
|
506
|
-
const existingMedias = existingAndMissingMedias.filter((media) => media != null);
|
507
|
-
previousRelationAttributes[name] = existingMedias;
|
508
|
-
} else {
|
509
|
-
const existingMedia = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: version.data[name].id } });
|
510
|
-
if (!existingMedia) {
|
511
|
-
previousRelationAttributes[name] = null;
|
512
|
-
}
|
513
|
-
}
|
508
|
+
const data2 = await serviceUtils.getMediaRestoreValue(versionRelationData, attribute);
|
509
|
+
previousRelationAttributes[name] = data2;
|
514
510
|
}
|
515
511
|
return previousRelationAttributes;
|
516
512
|
},
|
517
513
|
// Clone to avoid mutating the original version data
|
518
|
-
|
514
|
+
structuredClone(dataWithoutAddedAttributes)
|
519
515
|
);
|
520
516
|
const data = fp.omit(["id", ...Object.keys(schemaDiff.removed)], dataWithoutMissingRelations);
|
521
517
|
const restoredDocument = await strapi2.documents(version.contentType).update({
|
@@ -530,16 +526,132 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
530
526
|
}
|
531
527
|
};
|
532
528
|
};
|
533
|
-
const
|
534
|
-
|
529
|
+
const shouldCreateHistoryVersion = (context) => {
|
530
|
+
if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
|
531
|
+
return false;
|
532
|
+
}
|
533
|
+
if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
|
534
|
+
return false;
|
535
|
+
}
|
536
|
+
if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
|
537
|
+
return false;
|
538
|
+
}
|
539
|
+
if (!context.contentType.uid.startsWith("api::")) {
|
540
|
+
return false;
|
541
|
+
}
|
542
|
+
return true;
|
535
543
|
};
|
536
|
-
const
|
544
|
+
const getSchemas = (uid2) => {
|
545
|
+
const attributesSchema = strapi.getModel(uid2).attributes;
|
546
|
+
const componentsSchemas = Object.keys(attributesSchema).reduce(
|
547
|
+
(currentComponentSchemas, key) => {
|
548
|
+
const fieldSchema = attributesSchema[key];
|
549
|
+
if (fieldSchema.type === "component") {
|
550
|
+
const componentSchema = strapi.getModel(fieldSchema.component).attributes;
|
551
|
+
return {
|
552
|
+
...currentComponentSchemas,
|
553
|
+
[fieldSchema.component]: componentSchema
|
554
|
+
};
|
555
|
+
}
|
556
|
+
return currentComponentSchemas;
|
557
|
+
},
|
558
|
+
{}
|
559
|
+
);
|
560
|
+
return {
|
561
|
+
schema: fp.omit(FIELDS_TO_IGNORE, attributesSchema),
|
562
|
+
componentsSchemas
|
563
|
+
};
|
564
|
+
};
|
565
|
+
const createLifecyclesService = ({ strapi: strapi2 }) => {
|
566
|
+
const state = {
|
567
|
+
deleteExpiredJob: null,
|
568
|
+
isInitialized: false
|
569
|
+
};
|
570
|
+
const serviceUtils = createServiceUtils({ strapi: strapi2 });
|
571
|
+
return {
|
572
|
+
async bootstrap() {
|
573
|
+
if (state.isInitialized) {
|
574
|
+
return;
|
575
|
+
}
|
576
|
+
strapi2.documents.use(async (context, next) => {
|
577
|
+
const result = await next();
|
578
|
+
if (!shouldCreateHistoryVersion(context)) {
|
579
|
+
return result;
|
580
|
+
}
|
581
|
+
const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
|
582
|
+
const defaultLocale = await serviceUtils.getDefaultLocale();
|
583
|
+
const locales = fp.castArray(context.params?.locale || defaultLocale);
|
584
|
+
if (!locales.length) {
|
585
|
+
return result;
|
586
|
+
}
|
587
|
+
const uid2 = context.contentType.uid;
|
588
|
+
const schemas = getSchemas(uid2);
|
589
|
+
const model = strapi2.getModel(uid2);
|
590
|
+
const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
|
591
|
+
const localeEntries = await strapi2.db.query(uid2).findMany({
|
592
|
+
where: {
|
593
|
+
documentId,
|
594
|
+
...isLocalizedContentType ? { locale: { $in: locales } } : {},
|
595
|
+
...strapiUtils.contentTypes.hasDraftAndPublish(strapi2.contentTypes[uid2]) ? { publishedAt: null } : {}
|
596
|
+
},
|
597
|
+
populate: serviceUtils.getDeepPopulate(
|
598
|
+
uid2,
|
599
|
+
true
|
600
|
+
/* use database syntax */
|
601
|
+
)
|
602
|
+
});
|
603
|
+
await strapi2.db.transaction(async ({ onCommit }) => {
|
604
|
+
onCommit(async () => {
|
605
|
+
for (const entry of localeEntries) {
|
606
|
+
const status = await serviceUtils.getVersionStatus(uid2, entry);
|
607
|
+
await getService$1(strapi2, "history").createVersion({
|
608
|
+
contentType: uid2,
|
609
|
+
data: fp.omit(FIELDS_TO_IGNORE, entry),
|
610
|
+
relatedDocumentId: documentId,
|
611
|
+
locale: entry.locale,
|
612
|
+
status,
|
613
|
+
...schemas
|
614
|
+
});
|
615
|
+
}
|
616
|
+
});
|
617
|
+
});
|
618
|
+
return result;
|
619
|
+
});
|
620
|
+
state.deleteExpiredJob = nodeSchedule.scheduleJob("historyDaily", "0 0 * * *", () => {
|
621
|
+
const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
|
622
|
+
const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
|
623
|
+
strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
|
624
|
+
where: {
|
625
|
+
created_at: {
|
626
|
+
$lt: expirationDate
|
627
|
+
}
|
628
|
+
}
|
629
|
+
}).catch((error) => {
|
630
|
+
if (error instanceof Error) {
|
631
|
+
strapi2.log.error("Error deleting expired history versions", error.message);
|
632
|
+
}
|
633
|
+
});
|
634
|
+
});
|
635
|
+
state.isInitialized = true;
|
636
|
+
},
|
637
|
+
async destroy() {
|
638
|
+
if (state.deleteExpiredJob) {
|
639
|
+
state.deleteExpiredJob.cancel();
|
640
|
+
}
|
641
|
+
}
|
642
|
+
};
|
643
|
+
};
|
644
|
+
const services$2 = {
|
645
|
+
history: createHistoryService,
|
646
|
+
lifecycles: createLifecyclesService
|
647
|
+
};
|
648
|
+
const info$1 = { pluginName: "content-manager", type: "admin" };
|
537
649
|
const historyVersionRouter = {
|
538
650
|
type: "admin",
|
539
651
|
routes: [
|
540
652
|
{
|
541
653
|
method: "GET",
|
542
|
-
info,
|
654
|
+
info: info$1,
|
543
655
|
path: "/history-versions",
|
544
656
|
handler: "history-version.findMany",
|
545
657
|
config: {
|
@@ -548,7 +660,7 @@ const historyVersionRouter = {
|
|
548
660
|
},
|
549
661
|
{
|
550
662
|
method: "PUT",
|
551
|
-
info,
|
663
|
+
info: info$1,
|
552
664
|
path: "/history-versions/:versionId/restore",
|
553
665
|
handler: "history-version.restoreVersion",
|
554
666
|
config: {
|
@@ -557,7 +669,7 @@ const historyVersionRouter = {
|
|
557
669
|
}
|
558
670
|
]
|
559
671
|
};
|
560
|
-
const routes$
|
672
|
+
const routes$2 = {
|
561
673
|
"history-version": historyVersionRouter
|
562
674
|
};
|
563
675
|
const historyVersion = {
|
@@ -604,21 +716,21 @@ const historyVersion = {
|
|
604
716
|
}
|
605
717
|
}
|
606
718
|
};
|
607
|
-
const getFeature = () => {
|
719
|
+
const getFeature$1 = () => {
|
608
720
|
if (strapi.ee.features.isEnabled("cms-content-history")) {
|
609
721
|
return {
|
610
722
|
register({ strapi: strapi2 }) {
|
611
723
|
strapi2.get("models").add(historyVersion);
|
612
724
|
},
|
613
725
|
bootstrap({ strapi: strapi2 }) {
|
614
|
-
getService(strapi2, "
|
726
|
+
getService$1(strapi2, "lifecycles").bootstrap();
|
615
727
|
},
|
616
728
|
destroy({ strapi: strapi2 }) {
|
617
|
-
getService(strapi2, "
|
729
|
+
getService$1(strapi2, "lifecycles").destroy();
|
618
730
|
},
|
619
|
-
controllers: controllers$
|
620
|
-
services: services$
|
621
|
-
routes: routes$
|
731
|
+
controllers: controllers$2,
|
732
|
+
services: services$2,
|
733
|
+
routes: routes$2
|
622
734
|
};
|
623
735
|
}
|
624
736
|
return {
|
@@ -627,7 +739,7 @@ const getFeature = () => {
|
|
627
739
|
}
|
628
740
|
};
|
629
741
|
};
|
630
|
-
const history = getFeature();
|
742
|
+
const history = getFeature$1();
|
631
743
|
const register = async ({ strapi: strapi2 }) => {
|
632
744
|
await history.register?.({ strapi: strapi2 });
|
633
745
|
};
|
@@ -635,15 +747,165 @@ const ALLOWED_WEBHOOK_EVENTS = {
|
|
635
747
|
ENTRY_PUBLISH: "entry.publish",
|
636
748
|
ENTRY_UNPUBLISH: "entry.unpublish"
|
637
749
|
};
|
750
|
+
const FEATURE_ID = "preview";
|
751
|
+
const info = { pluginName: "content-manager", type: "admin" };
|
752
|
+
const previewRouter = {
|
753
|
+
type: "admin",
|
754
|
+
routes: [
|
755
|
+
{
|
756
|
+
method: "GET",
|
757
|
+
info,
|
758
|
+
path: "/preview/url/:contentType",
|
759
|
+
handler: "preview.getPreviewUrl",
|
760
|
+
config: {
|
761
|
+
policies: ["admin::isAuthenticatedAdmin"]
|
762
|
+
}
|
763
|
+
}
|
764
|
+
]
|
765
|
+
};
|
766
|
+
const routes$1 = {
|
767
|
+
preview: previewRouter
|
768
|
+
};
|
769
|
+
function getService(strapi2, name) {
|
770
|
+
return strapi2.service(`plugin::content-manager.${name}`);
|
771
|
+
}
|
772
|
+
const getPreviewUrlSchema = yup__namespace.object().shape({
|
773
|
+
// Will be undefined for single types
|
774
|
+
documentId: yup__namespace.string(),
|
775
|
+
locale: yup__namespace.string().nullable(),
|
776
|
+
status: yup__namespace.string()
|
777
|
+
}).required();
|
778
|
+
const validatePreviewUrl = async (strapi2, uid2, params) => {
|
779
|
+
await strapiUtils.validateYupSchema(getPreviewUrlSchema)(params);
|
780
|
+
const newParams = fp.pick(["documentId", "locale", "status"], params);
|
781
|
+
const model = strapi2.getModel(uid2);
|
782
|
+
if (!model || model.modelType !== "contentType") {
|
783
|
+
throw new strapiUtils.errors.ValidationError("Invalid content type");
|
784
|
+
}
|
785
|
+
const isSingleType = model?.kind === "singleType";
|
786
|
+
if (!isSingleType && !params.documentId) {
|
787
|
+
throw new strapiUtils.errors.ValidationError("documentId is required for Collection Types");
|
788
|
+
}
|
789
|
+
if (isSingleType) {
|
790
|
+
const doc = await strapi2.documents(uid2).findFirst();
|
791
|
+
if (!doc) {
|
792
|
+
throw new strapiUtils.errors.NotFoundError("Document not found");
|
793
|
+
}
|
794
|
+
newParams.documentId = doc?.documentId;
|
795
|
+
}
|
796
|
+
return newParams;
|
797
|
+
};
|
798
|
+
const createPreviewController = () => {
|
799
|
+
return {
|
800
|
+
/**
|
801
|
+
* Transforms an entry into a preview URL, so that it can be previewed
|
802
|
+
* in the Content Manager.
|
803
|
+
*/
|
804
|
+
async getPreviewUrl(ctx) {
|
805
|
+
const uid2 = ctx.params.contentType;
|
806
|
+
const query = ctx.request.query;
|
807
|
+
const params = await validatePreviewUrl(strapi, uid2, query);
|
808
|
+
const previewService = getService(strapi, "preview");
|
809
|
+
const url = await previewService.getPreviewUrl(uid2, params);
|
810
|
+
if (!url) {
|
811
|
+
ctx.status = 204;
|
812
|
+
}
|
813
|
+
return {
|
814
|
+
data: { url }
|
815
|
+
};
|
816
|
+
}
|
817
|
+
};
|
818
|
+
};
|
819
|
+
const controllers$1 = {
|
820
|
+
preview: createPreviewController
|
821
|
+
/**
|
822
|
+
* Casting is needed because the types aren't aware that Strapi supports
|
823
|
+
* passing a controller factory as the value, instead of a controller object directly
|
824
|
+
*/
|
825
|
+
};
|
826
|
+
const createPreviewService = ({ strapi: strapi2 }) => {
|
827
|
+
const config = getService(strapi2, "preview-config");
|
828
|
+
return {
|
829
|
+
async getPreviewUrl(uid2, params) {
|
830
|
+
const handler = config.getPreviewHandler();
|
831
|
+
try {
|
832
|
+
return handler(uid2, params);
|
833
|
+
} catch (error) {
|
834
|
+
strapi2.log.error(`Failed to get preview URL: ${error}`);
|
835
|
+
throw new strapiUtils.errors.ApplicationError("Failed to get preview URL");
|
836
|
+
}
|
837
|
+
return;
|
838
|
+
}
|
839
|
+
};
|
840
|
+
};
|
841
|
+
const createPreviewConfigService = ({ strapi: strapi2 }) => {
|
842
|
+
return {
|
843
|
+
isEnabled() {
|
844
|
+
const config = strapi2.config.get("admin.preview");
|
845
|
+
if (!config) {
|
846
|
+
return false;
|
847
|
+
}
|
848
|
+
return config?.enabled ?? true;
|
849
|
+
},
|
850
|
+
/**
|
851
|
+
* Validate if the configuration is valid
|
852
|
+
*/
|
853
|
+
validate() {
|
854
|
+
if (!this.isEnabled()) {
|
855
|
+
return;
|
856
|
+
}
|
857
|
+
const handler = this.getPreviewHandler();
|
858
|
+
if (typeof handler !== "function") {
|
859
|
+
throw new strapiUtils.errors.ValidationError(
|
860
|
+
"Preview configuration is invalid. Handler must be a function"
|
861
|
+
);
|
862
|
+
}
|
863
|
+
},
|
864
|
+
/**
|
865
|
+
* Utility to get the preview handler from the configuration
|
866
|
+
*/
|
867
|
+
getPreviewHandler() {
|
868
|
+
const config = strapi2.config.get("admin.preview");
|
869
|
+
const emptyHandler = () => {
|
870
|
+
return void 0;
|
871
|
+
};
|
872
|
+
if (!this.isEnabled()) {
|
873
|
+
return emptyHandler;
|
874
|
+
}
|
875
|
+
return config?.config?.handler || emptyHandler;
|
876
|
+
}
|
877
|
+
};
|
878
|
+
};
|
879
|
+
const services$1 = {
|
880
|
+
preview: createPreviewService,
|
881
|
+
"preview-config": createPreviewConfigService
|
882
|
+
};
|
883
|
+
const getFeature = () => {
|
884
|
+
if (!strapi.features.future.isEnabled(FEATURE_ID)) {
|
885
|
+
return {};
|
886
|
+
}
|
887
|
+
return {
|
888
|
+
bootstrap() {
|
889
|
+
console.log("Bootstrapping preview server");
|
890
|
+
const config = getService(strapi, "preview-config");
|
891
|
+
config.validate();
|
892
|
+
},
|
893
|
+
routes: routes$1,
|
894
|
+
controllers: controllers$1,
|
895
|
+
services: services$1
|
896
|
+
};
|
897
|
+
};
|
898
|
+
const preview = getFeature();
|
638
899
|
const bootstrap = async () => {
|
639
900
|
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
640
901
|
strapi.get("webhookStore").addAllowedEvent(key, value);
|
641
902
|
});
|
642
|
-
getService$
|
643
|
-
await getService$
|
644
|
-
await getService$
|
645
|
-
await getService$
|
903
|
+
getService$2("field-sizes").setCustomFieldInputSizes();
|
904
|
+
await getService$2("components").syncConfigurations();
|
905
|
+
await getService$2("content-types").syncConfigurations();
|
906
|
+
await getService$2("permission").registerPermissions();
|
646
907
|
await history.bootstrap?.({ strapi });
|
908
|
+
await preview.bootstrap?.({ strapi });
|
647
909
|
};
|
648
910
|
const destroy = async ({ strapi: strapi2 }) => {
|
649
911
|
await history.destroy?.({ strapi: strapi2 });
|
@@ -1133,7 +1395,8 @@ const admin = {
|
|
1133
1395
|
};
|
1134
1396
|
const routes = {
|
1135
1397
|
admin,
|
1136
|
-
...history.routes ? history.routes : {}
|
1398
|
+
...history.routes ? history.routes : {},
|
1399
|
+
...preview.routes ? preview.routes : {}
|
1137
1400
|
};
|
1138
1401
|
const hasPermissionsSchema = strapiUtils.yup.object({
|
1139
1402
|
actions: strapiUtils.yup.array().of(strapiUtils.yup.string()),
|
@@ -1144,6 +1407,11 @@ const { createPolicy } = strapiUtils.policy;
|
|
1144
1407
|
const hasPermissions = createPolicy({
|
1145
1408
|
name: "plugin::content-manager.hasPermissions",
|
1146
1409
|
validator: validateHasPermissionsInput,
|
1410
|
+
/**
|
1411
|
+
* NOTE: Action aliases are currently not checked at this level (policy).
|
1412
|
+
* This is currently the intended behavior to avoid changing the behavior of API related permissions.
|
1413
|
+
* If you want to add support for it, please create a dedicated RFC with a list of potential side effect this could have.
|
1414
|
+
*/
|
1147
1415
|
handler(ctx, config = {}) {
|
1148
1416
|
const { actions = [], hasAtLeastOne = false } = config;
|
1149
1417
|
const { userAbility } = ctx.state;
|
@@ -1385,7 +1653,7 @@ const createMetadasSchema = (schema) => {
|
|
1385
1653
|
if (!value) {
|
1386
1654
|
return strapiUtils.yup.string();
|
1387
1655
|
}
|
1388
|
-
const targetSchema = getService$
|
1656
|
+
const targetSchema = getService$2("content-types").findContentType(
|
1389
1657
|
schema.attributes[key].targetModel
|
1390
1658
|
);
|
1391
1659
|
if (!targetSchema) {
|
@@ -1433,7 +1701,7 @@ const { PaginationError, ValidationError } = strapiUtils.errors;
|
|
1433
1701
|
const TYPES = ["singleType", "collectionType"];
|
1434
1702
|
const kindSchema = strapiUtils.yup.string().oneOf(TYPES).nullable();
|
1435
1703
|
const bulkActionInputSchema = strapiUtils.yup.object({
|
1436
|
-
|
1704
|
+
documentIds: strapiUtils.yup.array().of(strapiUtils.yup.strapiID()).min(1).required()
|
1437
1705
|
}).required();
|
1438
1706
|
const generateUIDInputSchema = strapiUtils.yup.object({
|
1439
1707
|
contentTypeUID: strapiUtils.yup.string().required(),
|
@@ -1532,22 +1800,56 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
|
|
1532
1800
|
}
|
1533
1801
|
}, body);
|
1534
1802
|
};
|
1535
|
-
const
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1540
|
-
|
1541
|
-
|
1803
|
+
const singleLocaleSchema = strapiUtils.yup.string().nullable();
|
1804
|
+
const multipleLocaleSchema = strapiUtils.yup.lazy(
|
1805
|
+
(value) => Array.isArray(value) ? strapiUtils.yup.array().of(singleLocaleSchema.required()) : singleLocaleSchema
|
1806
|
+
);
|
1807
|
+
const statusSchema = strapiUtils.yup.mixed().oneOf(["draft", "published"], "Invalid status");
|
1808
|
+
const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
|
1809
|
+
const { allowMultipleLocales } = opts;
|
1810
|
+
const { locale, status: providedStatus, ...rest } = request || {};
|
1811
|
+
const defaultStatus = strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
|
1812
|
+
const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
|
1813
|
+
const schema = strapiUtils.yup.object().shape({
|
1814
|
+
locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
|
1815
|
+
status: statusSchema
|
1816
|
+
});
|
1817
|
+
try {
|
1818
|
+
await strapiUtils.validateYupSchema(schema, { strict: true, abortEarly: false })(request);
|
1819
|
+
return { locale, status, ...rest };
|
1820
|
+
} catch (error) {
|
1821
|
+
throw new strapiUtils.errors.ValidationError(`Validation error: ${error.message}`);
|
1542
1822
|
}
|
1543
|
-
|
1823
|
+
};
|
1824
|
+
const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
|
1825
|
+
const documentMetadata2 = getService$2("document-metadata");
|
1826
|
+
const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
|
1827
|
+
let {
|
1828
|
+
meta: { availableLocales, availableStatus }
|
1829
|
+
} = serviceOutput;
|
1830
|
+
const metadataSanitizer = permissionChecker2.sanitizeOutput;
|
1831
|
+
availableLocales = await strapiUtils.async.map(
|
1832
|
+
availableLocales,
|
1833
|
+
async (localeDocument) => metadataSanitizer(localeDocument)
|
1834
|
+
);
|
1835
|
+
availableStatus = await strapiUtils.async.map(
|
1836
|
+
availableStatus,
|
1837
|
+
async (statusDocument) => metadataSanitizer(statusDocument)
|
1838
|
+
);
|
1839
|
+
return {
|
1840
|
+
...serviceOutput,
|
1841
|
+
meta: {
|
1842
|
+
availableLocales,
|
1843
|
+
availableStatus
|
1844
|
+
}
|
1845
|
+
};
|
1544
1846
|
};
|
1545
1847
|
const createDocument = async (ctx, opts) => {
|
1546
1848
|
const { userAbility, user } = ctx.state;
|
1547
1849
|
const { model } = ctx.params;
|
1548
1850
|
const { body } = ctx.request;
|
1549
|
-
const documentManager2 = getService$
|
1550
|
-
const permissionChecker2 = getService$
|
1851
|
+
const documentManager2 = getService$2("document-manager");
|
1852
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1551
1853
|
if (permissionChecker2.cannot.create()) {
|
1552
1854
|
throw new strapiUtils.errors.ForbiddenError();
|
1553
1855
|
}
|
@@ -1555,7 +1857,7 @@ const createDocument = async (ctx, opts) => {
|
|
1555
1857
|
const setCreator = strapiUtils.setCreatorFields({ user });
|
1556
1858
|
const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
|
1557
1859
|
const sanitizedBody = await sanitizeFn(body);
|
1558
|
-
const { locale, status
|
1860
|
+
const { locale, status } = await getDocumentLocaleAndStatus(body, model);
|
1559
1861
|
return documentManager2.create(model, {
|
1560
1862
|
data: sanitizedBody,
|
1561
1863
|
locale,
|
@@ -1567,14 +1869,14 @@ const updateDocument = async (ctx, opts) => {
|
|
1567
1869
|
const { userAbility, user } = ctx.state;
|
1568
1870
|
const { id, model } = ctx.params;
|
1569
1871
|
const { body } = ctx.request;
|
1570
|
-
const documentManager2 = getService$
|
1571
|
-
const permissionChecker2 = getService$
|
1872
|
+
const documentManager2 = getService$2("document-manager");
|
1873
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1572
1874
|
if (permissionChecker2.cannot.update()) {
|
1573
1875
|
throw new strapiUtils.errors.ForbiddenError();
|
1574
1876
|
}
|
1575
1877
|
const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
|
1576
|
-
const populate = await getService$
|
1577
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
1878
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1879
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1578
1880
|
const [documentVersion, documentExists] = await Promise.all([
|
1579
1881
|
documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
|
1580
1882
|
documentManager2.exists(model, id)
|
@@ -1590,7 +1892,7 @@ const updateDocument = async (ctx, opts) => {
|
|
1590
1892
|
throw new strapiUtils.errors.ForbiddenError();
|
1591
1893
|
}
|
1592
1894
|
const pickPermittedFields = documentVersion ? permissionChecker2.sanitizeUpdateInput(documentVersion) : permissionChecker2.sanitizeCreateInput;
|
1593
|
-
const setCreator = strapiUtils.setCreatorFields({ user, isEdition: true });
|
1895
|
+
const setCreator = documentVersion ? strapiUtils.setCreatorFields({ user, isEdition: true }) : strapiUtils.setCreatorFields({ user });
|
1594
1896
|
const sanitizeFn = strapiUtils.async.pipe(pickPermittedFields, setCreator);
|
1595
1897
|
const sanitizedBody = await sanitizeFn(body);
|
1596
1898
|
return documentManager2.update(documentVersion?.documentId || id, model, {
|
@@ -1604,15 +1906,15 @@ const collectionTypes = {
|
|
1604
1906
|
const { userAbility } = ctx.state;
|
1605
1907
|
const { model } = ctx.params;
|
1606
1908
|
const { query } = ctx.request;
|
1607
|
-
const documentMetadata2 = getService$
|
1608
|
-
const documentManager2 = getService$
|
1609
|
-
const permissionChecker2 = getService$
|
1909
|
+
const documentMetadata2 = getService$2("document-metadata");
|
1910
|
+
const documentManager2 = getService$2("document-manager");
|
1911
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1610
1912
|
if (permissionChecker2.cannot.read()) {
|
1611
1913
|
return ctx.forbidden();
|
1612
1914
|
}
|
1613
1915
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
|
1614
|
-
const populate = await getService$
|
1615
|
-
const { locale, status } = getDocumentLocaleAndStatus(query);
|
1916
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
|
1917
|
+
const { locale, status } = await getDocumentLocaleAndStatus(query, model);
|
1616
1918
|
const { results: documents, pagination } = await documentManager2.findPage(
|
1617
1919
|
{ ...permissionQuery, populate, locale, status },
|
1618
1920
|
model
|
@@ -1640,15 +1942,14 @@ const collectionTypes = {
|
|
1640
1942
|
async findOne(ctx) {
|
1641
1943
|
const { userAbility } = ctx.state;
|
1642
1944
|
const { model, id } = ctx.params;
|
1643
|
-
const documentManager2 = getService$
|
1644
|
-
const
|
1645
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1945
|
+
const documentManager2 = getService$2("document-manager");
|
1946
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1646
1947
|
if (permissionChecker2.cannot.read()) {
|
1647
1948
|
return ctx.forbidden();
|
1648
1949
|
}
|
1649
1950
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
1650
|
-
const populate = await getService$
|
1651
|
-
const { locale, status
|
1951
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1952
|
+
const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1652
1953
|
const version = await documentManager2.findOne(id, model, {
|
1653
1954
|
populate,
|
1654
1955
|
locale,
|
@@ -1659,9 +1960,11 @@ const collectionTypes = {
|
|
1659
1960
|
if (!exists) {
|
1660
1961
|
return ctx.notFound();
|
1661
1962
|
}
|
1662
|
-
const { meta } = await
|
1963
|
+
const { meta } = await formatDocumentWithMetadata(
|
1964
|
+
permissionChecker2,
|
1663
1965
|
model,
|
1664
|
-
|
1966
|
+
// @ts-expect-error TODO: fix
|
1967
|
+
{ documentId: id, locale, publishedAt: null },
|
1665
1968
|
{ availableLocales: true, availableStatus: false }
|
1666
1969
|
);
|
1667
1970
|
ctx.body = { data: {}, meta };
|
@@ -1671,20 +1974,19 @@ const collectionTypes = {
|
|
1671
1974
|
return ctx.forbidden();
|
1672
1975
|
}
|
1673
1976
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
|
1674
|
-
ctx.body = await
|
1977
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
1675
1978
|
},
|
1676
1979
|
async create(ctx) {
|
1677
1980
|
const { userAbility } = ctx.state;
|
1678
1981
|
const { model } = ctx.params;
|
1679
|
-
const
|
1680
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1982
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1681
1983
|
const [totalEntries, document] = await Promise.all([
|
1682
1984
|
strapi.db.query(model).count(),
|
1683
1985
|
createDocument(ctx)
|
1684
1986
|
]);
|
1685
1987
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
|
1686
1988
|
ctx.status = 201;
|
1687
|
-
ctx.body = await
|
1989
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
|
1688
1990
|
// Empty metadata as it's not relevant for a new document
|
1689
1991
|
availableLocales: false,
|
1690
1992
|
availableStatus: false
|
@@ -1698,25 +2000,23 @@ const collectionTypes = {
|
|
1698
2000
|
async update(ctx) {
|
1699
2001
|
const { userAbility } = ctx.state;
|
1700
2002
|
const { model } = ctx.params;
|
1701
|
-
const
|
1702
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2003
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1703
2004
|
const updatedVersion = await updateDocument(ctx);
|
1704
2005
|
const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
|
1705
|
-
ctx.body = await
|
2006
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
|
1706
2007
|
},
|
1707
2008
|
async clone(ctx) {
|
1708
2009
|
const { userAbility, user } = ctx.state;
|
1709
2010
|
const { model, sourceId: id } = ctx.params;
|
1710
2011
|
const { body } = ctx.request;
|
1711
|
-
const documentManager2 = getService$
|
1712
|
-
const
|
1713
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2012
|
+
const documentManager2 = getService$2("document-manager");
|
2013
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1714
2014
|
if (permissionChecker2.cannot.create()) {
|
1715
2015
|
return ctx.forbidden();
|
1716
2016
|
}
|
1717
2017
|
const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
|
1718
|
-
const populate = await getService$
|
1719
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2018
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2019
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1720
2020
|
const document = await documentManager2.findOne(id, model, {
|
1721
2021
|
populate,
|
1722
2022
|
locale,
|
@@ -1732,7 +2032,7 @@ const collectionTypes = {
|
|
1732
2032
|
const sanitizedBody = await sanitizeFn(body);
|
1733
2033
|
const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
|
1734
2034
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
|
1735
|
-
ctx.body = await
|
2035
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
|
1736
2036
|
// Empty metadata as it's not relevant for a new document
|
1737
2037
|
availableLocales: false,
|
1738
2038
|
availableStatus: false
|
@@ -1754,14 +2054,14 @@ const collectionTypes = {
|
|
1754
2054
|
async delete(ctx) {
|
1755
2055
|
const { userAbility } = ctx.state;
|
1756
2056
|
const { id, model } = ctx.params;
|
1757
|
-
const documentManager2 = getService$
|
1758
|
-
const permissionChecker2 = getService$
|
2057
|
+
const documentManager2 = getService$2("document-manager");
|
2058
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1759
2059
|
if (permissionChecker2.cannot.delete()) {
|
1760
2060
|
return ctx.forbidden();
|
1761
2061
|
}
|
1762
2062
|
const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
|
1763
|
-
const populate = await getService$
|
1764
|
-
const { locale } = getDocumentLocaleAndStatus(ctx.query);
|
2063
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2064
|
+
const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1765
2065
|
const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
|
1766
2066
|
if (documentLocales.length === 0) {
|
1767
2067
|
return ctx.notFound();
|
@@ -1782,44 +2082,75 @@ const collectionTypes = {
|
|
1782
2082
|
const { userAbility } = ctx.state;
|
1783
2083
|
const { id, model } = ctx.params;
|
1784
2084
|
const { body } = ctx.request;
|
1785
|
-
const documentManager2 = getService$
|
1786
|
-
const
|
1787
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2085
|
+
const documentManager2 = getService$2("document-manager");
|
2086
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1788
2087
|
if (permissionChecker2.cannot.publish()) {
|
1789
2088
|
return ctx.forbidden();
|
1790
2089
|
}
|
1791
2090
|
const publishedDocument = await strapi.db.transaction(async () => {
|
1792
2091
|
const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
|
1793
|
-
const populate = await getService$
|
1794
|
-
|
2092
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
2093
|
+
let document;
|
2094
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2095
|
+
const isCreate = fp.isNil(id);
|
2096
|
+
if (isCreate) {
|
2097
|
+
if (permissionChecker2.cannot.create()) {
|
2098
|
+
throw new strapiUtils.errors.ForbiddenError();
|
2099
|
+
}
|
2100
|
+
document = await createDocument(ctx, { populate });
|
2101
|
+
}
|
2102
|
+
const isUpdate = !isCreate;
|
2103
|
+
if (isUpdate) {
|
2104
|
+
const documentExists = documentManager2.exists(model, id);
|
2105
|
+
if (!documentExists) {
|
2106
|
+
throw new strapiUtils.errors.NotFoundError("Document not found");
|
2107
|
+
}
|
2108
|
+
document = await documentManager2.findOne(id, model, { populate, locale });
|
2109
|
+
if (!document) {
|
2110
|
+
if (permissionChecker2.cannot.create({ locale }) || permissionChecker2.cannot.publish({ locale })) {
|
2111
|
+
throw new strapiUtils.errors.ForbiddenError();
|
2112
|
+
}
|
2113
|
+
document = await updateDocument(ctx);
|
2114
|
+
} else if (permissionChecker2.can.update(document)) {
|
2115
|
+
await updateDocument(ctx);
|
2116
|
+
}
|
2117
|
+
}
|
1795
2118
|
if (permissionChecker2.cannot.publish(document)) {
|
1796
2119
|
throw new strapiUtils.errors.ForbiddenError();
|
1797
2120
|
}
|
1798
|
-
const
|
1799
|
-
return documentManager2.publish(document.documentId, model, {
|
2121
|
+
const publishResult = await documentManager2.publish(document.documentId, model, {
|
1800
2122
|
locale
|
1801
2123
|
// TODO: Allow setting creator fields on publish
|
1802
2124
|
// data: setCreatorFields({ user, isEdition: true })({}),
|
1803
2125
|
});
|
2126
|
+
if (!publishResult || publishResult.length === 0) {
|
2127
|
+
throw new strapiUtils.errors.NotFoundError("Document not found or already published.");
|
2128
|
+
}
|
2129
|
+
return publishResult[0];
|
1804
2130
|
});
|
1805
2131
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
|
1806
|
-
ctx.body = await
|
2132
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
1807
2133
|
},
|
1808
2134
|
async bulkPublish(ctx) {
|
1809
2135
|
const { userAbility } = ctx.state;
|
1810
2136
|
const { model } = ctx.params;
|
1811
2137
|
const { body } = ctx.request;
|
1812
|
-
const {
|
2138
|
+
const { documentIds } = body;
|
1813
2139
|
await validateBulkActionInput(body);
|
1814
|
-
const documentManager2 = getService$
|
1815
|
-
const permissionChecker2 = getService$
|
2140
|
+
const documentManager2 = getService$2("document-manager");
|
2141
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1816
2142
|
if (permissionChecker2.cannot.publish()) {
|
1817
2143
|
return ctx.forbidden();
|
1818
2144
|
}
|
1819
2145
|
const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
|
1820
|
-
const populate = await getService$
|
1821
|
-
const
|
1822
|
-
|
2146
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
2147
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model, {
|
2148
|
+
allowMultipleLocales: true
|
2149
|
+
});
|
2150
|
+
const entityPromises = documentIds.map(
|
2151
|
+
(documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
|
2152
|
+
);
|
2153
|
+
const entities = (await Promise.all(entityPromises)).flat();
|
1823
2154
|
for (const entity of entities) {
|
1824
2155
|
if (!entity) {
|
1825
2156
|
return ctx.notFound();
|
@@ -1828,24 +2159,27 @@ const collectionTypes = {
|
|
1828
2159
|
return ctx.forbidden();
|
1829
2160
|
}
|
1830
2161
|
}
|
1831
|
-
const
|
2162
|
+
const count = await documentManager2.publishMany(model, documentIds, locale);
|
1832
2163
|
ctx.body = { count };
|
1833
2164
|
},
|
1834
2165
|
async bulkUnpublish(ctx) {
|
1835
2166
|
const { userAbility } = ctx.state;
|
1836
2167
|
const { model } = ctx.params;
|
1837
2168
|
const { body } = ctx.request;
|
1838
|
-
const {
|
2169
|
+
const { documentIds } = body;
|
1839
2170
|
await validateBulkActionInput(body);
|
1840
|
-
const documentManager2 = getService$
|
1841
|
-
const permissionChecker2 = getService$
|
2171
|
+
const documentManager2 = getService$2("document-manager");
|
2172
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1842
2173
|
if (permissionChecker2.cannot.unpublish()) {
|
1843
2174
|
return ctx.forbidden();
|
1844
2175
|
}
|
1845
|
-
const
|
1846
|
-
|
1847
|
-
|
1848
|
-
const
|
2176
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model, {
|
2177
|
+
allowMultipleLocales: true
|
2178
|
+
});
|
2179
|
+
const entityPromises = documentIds.map(
|
2180
|
+
(documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
|
2181
|
+
);
|
2182
|
+
const entities = (await Promise.all(entityPromises)).flat();
|
1849
2183
|
for (const entity of entities) {
|
1850
2184
|
if (!entity) {
|
1851
2185
|
return ctx.notFound();
|
@@ -1854,7 +2188,8 @@ const collectionTypes = {
|
|
1854
2188
|
return ctx.forbidden();
|
1855
2189
|
}
|
1856
2190
|
}
|
1857
|
-
const
|
2191
|
+
const entitiesIds = entities.map((document) => document.documentId);
|
2192
|
+
const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
|
1858
2193
|
ctx.body = { count };
|
1859
2194
|
},
|
1860
2195
|
async unpublish(ctx) {
|
@@ -1863,9 +2198,8 @@ const collectionTypes = {
|
|
1863
2198
|
const {
|
1864
2199
|
body: { discardDraft, ...body }
|
1865
2200
|
} = ctx.request;
|
1866
|
-
const documentManager2 = getService$
|
1867
|
-
const
|
1868
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2201
|
+
const documentManager2 = getService$2("document-manager");
|
2202
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1869
2203
|
if (permissionChecker2.cannot.unpublish()) {
|
1870
2204
|
return ctx.forbidden();
|
1871
2205
|
}
|
@@ -1873,8 +2207,8 @@ const collectionTypes = {
|
|
1873
2207
|
return ctx.forbidden();
|
1874
2208
|
}
|
1875
2209
|
const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
|
1876
|
-
const populate = await getService$
|
1877
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2210
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2211
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1878
2212
|
const document = await documentManager2.findOne(id, model, {
|
1879
2213
|
populate,
|
1880
2214
|
locale,
|
@@ -1896,7 +2230,7 @@ const collectionTypes = {
|
|
1896
2230
|
ctx.body = await strapiUtils.async.pipe(
|
1897
2231
|
(document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
|
1898
2232
|
permissionChecker2.sanitizeOutput,
|
1899
|
-
(document2) =>
|
2233
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
1900
2234
|
)(document);
|
1901
2235
|
});
|
1902
2236
|
},
|
@@ -1904,15 +2238,14 @@ const collectionTypes = {
|
|
1904
2238
|
const { userAbility } = ctx.state;
|
1905
2239
|
const { id, model } = ctx.params;
|
1906
2240
|
const { body } = ctx.request;
|
1907
|
-
const documentManager2 = getService$
|
1908
|
-
const
|
1909
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2241
|
+
const documentManager2 = getService$2("document-manager");
|
2242
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1910
2243
|
if (permissionChecker2.cannot.discard()) {
|
1911
2244
|
return ctx.forbidden();
|
1912
2245
|
}
|
1913
2246
|
const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
|
1914
|
-
const populate = await getService$
|
1915
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2247
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2248
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1916
2249
|
const document = await documentManager2.findOne(id, model, {
|
1917
2250
|
populate,
|
1918
2251
|
locale,
|
@@ -1927,42 +2260,50 @@ const collectionTypes = {
|
|
1927
2260
|
ctx.body = await strapiUtils.async.pipe(
|
1928
2261
|
(document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
|
1929
2262
|
permissionChecker2.sanitizeOutput,
|
1930
|
-
(document2) =>
|
2263
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
1931
2264
|
)(document);
|
1932
2265
|
},
|
1933
2266
|
async bulkDelete(ctx) {
|
1934
2267
|
const { userAbility } = ctx.state;
|
1935
2268
|
const { model } = ctx.params;
|
1936
2269
|
const { query, body } = ctx.request;
|
1937
|
-
const {
|
2270
|
+
const { documentIds } = body;
|
1938
2271
|
await validateBulkActionInput(body);
|
1939
|
-
const documentManager2 = getService$
|
1940
|
-
const permissionChecker2 = getService$
|
2272
|
+
const documentManager2 = getService$2("document-manager");
|
2273
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1941
2274
|
if (permissionChecker2.cannot.delete()) {
|
1942
2275
|
return ctx.forbidden();
|
1943
2276
|
}
|
1944
2277
|
const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
|
1945
|
-
const
|
1946
|
-
const
|
1947
|
-
|
1948
|
-
|
1949
|
-
|
2278
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2279
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2280
|
+
const documentLocales = await documentManager2.findLocales(documentIds, model, {
|
2281
|
+
populate,
|
2282
|
+
locale
|
2283
|
+
});
|
2284
|
+
if (documentLocales.length === 0) {
|
2285
|
+
return ctx.notFound();
|
2286
|
+
}
|
2287
|
+
for (const document of documentLocales) {
|
2288
|
+
if (permissionChecker2.cannot.delete(document)) {
|
2289
|
+
return ctx.forbidden();
|
1950
2290
|
}
|
1951
|
-
}
|
1952
|
-
const
|
2291
|
+
}
|
2292
|
+
const localeDocumentsIds = documentLocales.map((document) => document.documentId);
|
2293
|
+
const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
|
1953
2294
|
ctx.body = { count };
|
1954
2295
|
},
|
1955
2296
|
async countDraftRelations(ctx) {
|
1956
2297
|
const { userAbility } = ctx.state;
|
1957
2298
|
const { model, id } = ctx.params;
|
1958
|
-
const documentManager2 = getService$
|
1959
|
-
const permissionChecker2 = getService$
|
2299
|
+
const documentManager2 = getService$2("document-manager");
|
2300
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1960
2301
|
if (permissionChecker2.cannot.read()) {
|
1961
2302
|
return ctx.forbidden();
|
1962
2303
|
}
|
1963
2304
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
1964
|
-
const populate = await getService$
|
1965
|
-
const { locale, status
|
2305
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2306
|
+
const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1966
2307
|
const entity = await documentManager2.findOne(id, model, { populate, locale, status });
|
1967
2308
|
if (!entity) {
|
1968
2309
|
return ctx.notFound();
|
@@ -1977,24 +2318,24 @@ const collectionTypes = {
|
|
1977
2318
|
},
|
1978
2319
|
async countManyEntriesDraftRelations(ctx) {
|
1979
2320
|
const { userAbility } = ctx.state;
|
1980
|
-
const ids = ctx.request.query.
|
2321
|
+
const ids = ctx.request.query.documentIds;
|
1981
2322
|
const locale = ctx.request.query.locale;
|
1982
2323
|
const { model } = ctx.params;
|
1983
|
-
const documentManager2 = getService$
|
1984
|
-
const permissionChecker2 = getService$
|
2324
|
+
const documentManager2 = getService$2("document-manager");
|
2325
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1985
2326
|
if (permissionChecker2.cannot.read()) {
|
1986
2327
|
return ctx.forbidden();
|
1987
2328
|
}
|
1988
|
-
const
|
2329
|
+
const documents = await documentManager2.findMany(
|
1989
2330
|
{
|
1990
2331
|
filters: {
|
1991
|
-
|
2332
|
+
documentId: ids
|
1992
2333
|
},
|
1993
2334
|
locale
|
1994
2335
|
},
|
1995
2336
|
model
|
1996
2337
|
);
|
1997
|
-
if (!
|
2338
|
+
if (!documents) {
|
1998
2339
|
return ctx.notFound();
|
1999
2340
|
}
|
2000
2341
|
const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
|
@@ -2005,13 +2346,13 @@ const collectionTypes = {
|
|
2005
2346
|
};
|
2006
2347
|
const components$1 = {
|
2007
2348
|
findComponents(ctx) {
|
2008
|
-
const components2 = getService$
|
2009
|
-
const { toDto } = getService$
|
2349
|
+
const components2 = getService$2("components").findAllComponents();
|
2350
|
+
const { toDto } = getService$2("data-mapper");
|
2010
2351
|
ctx.body = { data: components2.map(toDto) };
|
2011
2352
|
},
|
2012
2353
|
async findComponentConfiguration(ctx) {
|
2013
2354
|
const { uid: uid2 } = ctx.params;
|
2014
|
-
const componentService = getService$
|
2355
|
+
const componentService = getService$2("components");
|
2015
2356
|
const component = componentService.findComponent(uid2);
|
2016
2357
|
if (!component) {
|
2017
2358
|
return ctx.notFound("component.notFound");
|
@@ -2028,7 +2369,7 @@ const components$1 = {
|
|
2028
2369
|
async updateComponentConfiguration(ctx) {
|
2029
2370
|
const { uid: uid2 } = ctx.params;
|
2030
2371
|
const { body } = ctx.request;
|
2031
|
-
const componentService = getService$
|
2372
|
+
const componentService = getService$2("components");
|
2032
2373
|
const component = componentService.findComponent(uid2);
|
2033
2374
|
if (!component) {
|
2034
2375
|
return ctx.notFound("component.notFound");
|
@@ -2062,12 +2403,12 @@ const contentTypes = {
|
|
2062
2403
|
} catch (error) {
|
2063
2404
|
return ctx.send({ error }, 400);
|
2064
2405
|
}
|
2065
|
-
const contentTypes2 = getService$
|
2066
|
-
const { toDto } = getService$
|
2406
|
+
const contentTypes2 = getService$2("content-types").findContentTypesByKind(kind);
|
2407
|
+
const { toDto } = getService$2("data-mapper");
|
2067
2408
|
ctx.body = { data: contentTypes2.map(toDto) };
|
2068
2409
|
},
|
2069
2410
|
async findContentTypesSettings(ctx) {
|
2070
|
-
const { findAllContentTypes, findConfiguration } = getService$
|
2411
|
+
const { findAllContentTypes, findConfiguration } = getService$2("content-types");
|
2071
2412
|
const contentTypes2 = await findAllContentTypes();
|
2072
2413
|
const configurations = await Promise.all(
|
2073
2414
|
contentTypes2.map(async (contentType) => {
|
@@ -2081,7 +2422,7 @@ const contentTypes = {
|
|
2081
2422
|
},
|
2082
2423
|
async findContentTypeConfiguration(ctx) {
|
2083
2424
|
const { uid: uid2 } = ctx.params;
|
2084
|
-
const contentTypeService = getService$
|
2425
|
+
const contentTypeService = getService$2("content-types");
|
2085
2426
|
const contentType = await contentTypeService.findContentType(uid2);
|
2086
2427
|
if (!contentType) {
|
2087
2428
|
return ctx.notFound("contentType.notFound");
|
@@ -2103,13 +2444,13 @@ const contentTypes = {
|
|
2103
2444
|
const { userAbility } = ctx.state;
|
2104
2445
|
const { uid: uid2 } = ctx.params;
|
2105
2446
|
const { body } = ctx.request;
|
2106
|
-
const contentTypeService = getService$
|
2107
|
-
const metricsService = getService$
|
2447
|
+
const contentTypeService = getService$2("content-types");
|
2448
|
+
const metricsService = getService$2("metrics");
|
2108
2449
|
const contentType = await contentTypeService.findContentType(uid2);
|
2109
2450
|
if (!contentType) {
|
2110
2451
|
return ctx.notFound("contentType.notFound");
|
2111
2452
|
}
|
2112
|
-
if (!getService$
|
2453
|
+
if (!getService$2("permission").canConfigureContentType({ userAbility, contentType })) {
|
2113
2454
|
return ctx.forbidden();
|
2114
2455
|
}
|
2115
2456
|
let input;
|
@@ -2142,10 +2483,10 @@ const contentTypes = {
|
|
2142
2483
|
};
|
2143
2484
|
const init = {
|
2144
2485
|
getInitData(ctx) {
|
2145
|
-
const { toDto } = getService$
|
2146
|
-
const { findAllComponents } = getService$
|
2147
|
-
const { getAllFieldSizes } = getService$
|
2148
|
-
const { findAllContentTypes } = getService$
|
2486
|
+
const { toDto } = getService$2("data-mapper");
|
2487
|
+
const { findAllComponents } = getService$2("components");
|
2488
|
+
const { getAllFieldSizes } = getService$2("field-sizes");
|
2489
|
+
const { findAllContentTypes } = getService$2("content-types");
|
2149
2490
|
ctx.body = {
|
2150
2491
|
data: {
|
2151
2492
|
fieldSizes: getAllFieldSizes(),
|
@@ -2181,36 +2522,41 @@ const addFiltersClause = (params, filtersClause) => {
|
|
2181
2522
|
params.filters.$and.push(filtersClause);
|
2182
2523
|
};
|
2183
2524
|
const sanitizeMainField = (model, mainField, userAbility) => {
|
2184
|
-
const permissionChecker2 = getService$
|
2525
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
2185
2526
|
userAbility,
|
2186
2527
|
model: model.uid
|
2187
2528
|
});
|
2188
|
-
|
2529
|
+
const isMainFieldListable = isListable(model, mainField);
|
2530
|
+
const canReadMainField = permissionChecker2.can.read(null, mainField);
|
2531
|
+
if (!isMainFieldListable || !canReadMainField) {
|
2189
2532
|
return "id";
|
2190
2533
|
}
|
2191
|
-
if (
|
2192
|
-
|
2193
|
-
const userPermissionChecker = getService$1("permission-checker").create({
|
2194
|
-
userAbility,
|
2195
|
-
model: "plugin::users-permissions.user"
|
2196
|
-
});
|
2197
|
-
if (userPermissionChecker.can.read()) {
|
2198
|
-
return "name";
|
2199
|
-
}
|
2200
|
-
}
|
2201
|
-
return "id";
|
2534
|
+
if (model.uid === "plugin::users-permissions.role") {
|
2535
|
+
return "name";
|
2202
2536
|
}
|
2203
2537
|
return mainField;
|
2204
2538
|
};
|
2205
|
-
const addStatusToRelations = async (
|
2206
|
-
if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi.
|
2539
|
+
const addStatusToRelations = async (targetUid, relations2) => {
|
2540
|
+
if (!strapiUtils.contentTypes.hasDraftAndPublish(strapi.getModel(targetUid))) {
|
2207
2541
|
return relations2;
|
2208
2542
|
}
|
2209
|
-
const documentMetadata2 = getService$
|
2210
|
-
|
2543
|
+
const documentMetadata2 = getService$2("document-metadata");
|
2544
|
+
if (!relations2.length) {
|
2545
|
+
return relations2;
|
2546
|
+
}
|
2547
|
+
const firstRelation = relations2[0];
|
2548
|
+
const filters = {
|
2549
|
+
documentId: { $in: relations2.map((r) => r.documentId) },
|
2550
|
+
// NOTE: find the "opposite" status
|
2551
|
+
publishedAt: firstRelation.publishedAt !== null ? { $null: true } : { $notNull: true }
|
2552
|
+
};
|
2553
|
+
const availableStatus = await strapi.query(targetUid).findMany({
|
2554
|
+
select: ["id", "documentId", "locale", "updatedAt", "createdAt", "publishedAt"],
|
2555
|
+
filters
|
2556
|
+
});
|
2211
2557
|
return relations2.map((relation) => {
|
2212
|
-
const availableStatuses =
|
2213
|
-
(availableDocument) => availableDocument.documentId === relation.documentId
|
2558
|
+
const availableStatuses = availableStatus.filter(
|
2559
|
+
(availableDocument) => availableDocument.documentId === relation.documentId && (relation.locale ? availableDocument.locale === relation.locale : true)
|
2214
2560
|
);
|
2215
2561
|
return {
|
2216
2562
|
...relation,
|
@@ -2231,11 +2577,8 @@ const validateLocale = (sourceUid, targetUid, locale) => {
|
|
2231
2577
|
const isLocalized = strapi.plugin("i18n").service("content-types").isLocalizedContentType;
|
2232
2578
|
const isSourceLocalized = isLocalized(sourceModel);
|
2233
2579
|
const isTargetLocalized = isLocalized(targetModel);
|
2234
|
-
let validatedLocale = locale;
|
2235
|
-
if (!targetModel || !isTargetLocalized)
|
2236
|
-
validatedLocale = void 0;
|
2237
2580
|
return {
|
2238
|
-
locale
|
2581
|
+
locale,
|
2239
2582
|
isSourceLocalized,
|
2240
2583
|
isTargetLocalized
|
2241
2584
|
};
|
@@ -2275,7 +2618,7 @@ const relations = {
|
|
2275
2618
|
ctx.request?.query?.locale
|
2276
2619
|
);
|
2277
2620
|
const { status } = validateStatus(sourceUid, ctx.request?.query?.status);
|
2278
|
-
const permissionChecker2 = getService$
|
2621
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
2279
2622
|
userAbility,
|
2280
2623
|
model
|
2281
2624
|
});
|
@@ -2300,7 +2643,7 @@ const relations = {
|
|
2300
2643
|
where.id = id;
|
2301
2644
|
}
|
2302
2645
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
2303
|
-
const populate = await getService$
|
2646
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2304
2647
|
const currentEntity = await strapi.db.query(model).findOne({
|
2305
2648
|
where,
|
2306
2649
|
populate
|
@@ -2315,7 +2658,7 @@ const relations = {
|
|
2315
2658
|
}
|
2316
2659
|
entryId = currentEntity.id;
|
2317
2660
|
}
|
2318
|
-
const modelConfig = isComponent2 ? await getService$
|
2661
|
+
const modelConfig = isComponent2 ? await getService$2("components").findConfiguration(sourceSchema) : await getService$2("content-types").findConfiguration(sourceSchema);
|
2319
2662
|
const targetSchema = strapi.getModel(targetUid);
|
2320
2663
|
const mainField = fp.flow(
|
2321
2664
|
fp.prop(`metadatas.${targetField}.edit.mainField`),
|
@@ -2338,7 +2681,7 @@ const relations = {
|
|
2338
2681
|
attribute,
|
2339
2682
|
fieldsToSelect,
|
2340
2683
|
mainField,
|
2341
|
-
source: { schema: sourceSchema },
|
2684
|
+
source: { schema: sourceSchema, isLocalized: isSourceLocalized },
|
2342
2685
|
target: { schema: targetSchema, isLocalized: isTargetLocalized },
|
2343
2686
|
sourceSchema,
|
2344
2687
|
targetSchema,
|
@@ -2360,7 +2703,8 @@ const relations = {
|
|
2360
2703
|
fieldsToSelect,
|
2361
2704
|
mainField,
|
2362
2705
|
source: {
|
2363
|
-
schema: { uid: sourceUid, modelType: sourceModelType }
|
2706
|
+
schema: { uid: sourceUid, modelType: sourceModelType },
|
2707
|
+
isLocalized: isSourceLocalized
|
2364
2708
|
},
|
2365
2709
|
target: {
|
2366
2710
|
schema: { uid: targetUid },
|
@@ -2368,7 +2712,7 @@ const relations = {
|
|
2368
2712
|
}
|
2369
2713
|
} = await this.extractAndValidateRequestInfo(ctx, id);
|
2370
2714
|
const { idsToOmit, idsToInclude, _q, ...query } = ctx.request.query;
|
2371
|
-
const permissionChecker2 = getService$
|
2715
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
2372
2716
|
userAbility: ctx.state.userAbility,
|
2373
2717
|
model: targetUid
|
2374
2718
|
});
|
@@ -2398,12 +2742,16 @@ const relations = {
|
|
2398
2742
|
} else {
|
2399
2743
|
where.id = id;
|
2400
2744
|
}
|
2401
|
-
|
2402
|
-
|
2745
|
+
const publishedAt = getPublishedAtClause(status, targetUid);
|
2746
|
+
if (!fp.isEmpty(publishedAt)) {
|
2747
|
+
where[`${alias}.published_at`] = publishedAt;
|
2403
2748
|
}
|
2404
|
-
if (
|
2749
|
+
if (isTargetLocalized && locale) {
|
2405
2750
|
where[`${alias}.locale`] = locale;
|
2406
2751
|
}
|
2752
|
+
if (isSourceLocalized && locale) {
|
2753
|
+
where.locale = locale;
|
2754
|
+
}
|
2407
2755
|
if ((idsToInclude?.length ?? 0) !== 0) {
|
2408
2756
|
where[`${alias}.id`].$notIn = idsToInclude;
|
2409
2757
|
}
|
@@ -2421,7 +2769,8 @@ const relations = {
|
|
2421
2769
|
id: { $notIn: fp.uniq(idsToOmit) }
|
2422
2770
|
});
|
2423
2771
|
}
|
2424
|
-
const
|
2772
|
+
const dbQuery = strapi.get("query-params").transform(targetUid, queryParams);
|
2773
|
+
const res = await strapi.db.query(targetUid).findPage(dbQuery);
|
2425
2774
|
ctx.body = {
|
2426
2775
|
...res,
|
2427
2776
|
results: await addStatusToRelations(targetUid, res.results)
|
@@ -2436,29 +2785,39 @@ const relations = {
|
|
2436
2785
|
attribute,
|
2437
2786
|
targetField,
|
2438
2787
|
fieldsToSelect,
|
2439
|
-
|
2440
|
-
|
2441
|
-
}
|
2442
|
-
target: {
|
2443
|
-
schema: { uid: targetUid }
|
2444
|
-
}
|
2788
|
+
status,
|
2789
|
+
source: { schema: sourceSchema },
|
2790
|
+
target: { schema: targetSchema }
|
2445
2791
|
} = await this.extractAndValidateRequestInfo(ctx, id);
|
2446
|
-
const
|
2792
|
+
const { uid: sourceUid } = sourceSchema;
|
2793
|
+
const { uid: targetUid } = targetSchema;
|
2794
|
+
const permissionQuery = await getService$2("permission-checker").create({ userAbility, model: targetUid }).sanitizedQuery.read({ fields: fieldsToSelect });
|
2447
2795
|
const dbQuery = strapi.db.query(sourceUid);
|
2448
2796
|
const loadRelations = strapiUtils.relations.isAnyToMany(attribute) ? (...args) => dbQuery.loadPages(...args) : (...args) => dbQuery.load(...args).then((res2) => ({ results: res2 ? [res2] : [] }));
|
2797
|
+
const filters = {};
|
2798
|
+
if (sourceSchema?.options?.draftAndPublish) {
|
2799
|
+
if (targetSchema?.options?.draftAndPublish) {
|
2800
|
+
if (status === "published") {
|
2801
|
+
filters.publishedAt = { $notNull: true };
|
2802
|
+
} else {
|
2803
|
+
filters.publishedAt = { $null: true };
|
2804
|
+
}
|
2805
|
+
}
|
2806
|
+
} else if (targetSchema?.options?.draftAndPublish) {
|
2807
|
+
filters.publishedAt = { $null: true };
|
2808
|
+
}
|
2449
2809
|
const res = await loadRelations({ id: entryId }, targetField, {
|
2450
|
-
select: ["id", "documentId", "locale", "publishedAt"],
|
2810
|
+
select: ["id", "documentId", "locale", "publishedAt", "updatedAt"],
|
2451
2811
|
ordering: "desc",
|
2452
2812
|
page: ctx.request.query.page,
|
2453
|
-
pageSize: ctx.request.query.pageSize
|
2813
|
+
pageSize: ctx.request.query.pageSize,
|
2814
|
+
filters
|
2454
2815
|
});
|
2455
2816
|
const loadedIds = res.results.map((item) => item.id);
|
2456
2817
|
addFiltersClause(permissionQuery, { id: { $in: loadedIds } });
|
2457
2818
|
const sanitizedRes = await loadRelations({ id: entryId }, targetField, {
|
2458
2819
|
...strapi.get("query-params").transform(targetUid, permissionQuery),
|
2459
|
-
ordering: "desc"
|
2460
|
-
page: ctx.request.query.page,
|
2461
|
-
pageSize: ctx.request.query.pageSize
|
2820
|
+
ordering: "desc"
|
2462
2821
|
});
|
2463
2822
|
const relationsUnion = fp.uniqBy("id", fp.concat(sanitizedRes.results, res.results));
|
2464
2823
|
ctx.body = {
|
@@ -2473,10 +2832,10 @@ const relations = {
|
|
2473
2832
|
}
|
2474
2833
|
};
|
2475
2834
|
const buildPopulateFromQuery = async (query, model) => {
|
2476
|
-
return getService$
|
2835
|
+
return getService$2("populate-builder")(model).populateFromQuery(query).populateDeep(Infinity).countRelations().build();
|
2477
2836
|
};
|
2478
2837
|
const findDocument = async (query, uid2, opts = {}) => {
|
2479
|
-
const documentManager2 = getService$
|
2838
|
+
const documentManager2 = getService$2("document-manager");
|
2480
2839
|
const populate = await buildPopulateFromQuery(query, uid2);
|
2481
2840
|
return documentManager2.findMany({ ...opts, populate }, uid2).then((documents) => documents[0]);
|
2482
2841
|
};
|
@@ -2484,13 +2843,13 @@ const createOrUpdateDocument = async (ctx, opts) => {
|
|
2484
2843
|
const { user, userAbility } = ctx.state;
|
2485
2844
|
const { model } = ctx.params;
|
2486
2845
|
const { body, query } = ctx.request;
|
2487
|
-
const documentManager2 = getService$
|
2488
|
-
const permissionChecker2 = getService$
|
2846
|
+
const documentManager2 = getService$2("document-manager");
|
2847
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2489
2848
|
if (permissionChecker2.cannot.create() && permissionChecker2.cannot.update()) {
|
2490
2849
|
throw new strapiUtils.errors.ForbiddenError();
|
2491
2850
|
}
|
2492
2851
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
|
2493
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2852
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2494
2853
|
const [documentVersion, otherDocumentVersion] = await Promise.all([
|
2495
2854
|
findDocument(sanitizedQuery, model, { locale, status: "draft" }),
|
2496
2855
|
// Find the first document to check if it exists
|
@@ -2526,13 +2885,12 @@ const singleTypes = {
|
|
2526
2885
|
const { userAbility } = ctx.state;
|
2527
2886
|
const { model } = ctx.params;
|
2528
2887
|
const { query = {} } = ctx.request;
|
2529
|
-
const permissionChecker2 = getService$
|
2530
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2888
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2531
2889
|
if (permissionChecker2.cannot.read()) {
|
2532
2890
|
return ctx.forbidden();
|
2533
2891
|
}
|
2534
2892
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
|
2535
|
-
const { locale, status } = getDocumentLocaleAndStatus(query);
|
2893
|
+
const { locale, status } = await getDocumentLocaleAndStatus(query, model);
|
2536
2894
|
const version = await findDocument(permissionQuery, model, { locale, status });
|
2537
2895
|
if (!version) {
|
2538
2896
|
if (permissionChecker2.cannot.create()) {
|
@@ -2542,9 +2900,11 @@ const singleTypes = {
|
|
2542
2900
|
if (!document) {
|
2543
2901
|
return ctx.notFound();
|
2544
2902
|
}
|
2545
|
-
const { meta } = await
|
2903
|
+
const { meta } = await formatDocumentWithMetadata(
|
2904
|
+
permissionChecker2,
|
2546
2905
|
model,
|
2547
|
-
|
2906
|
+
// @ts-expect-error - fix types
|
2907
|
+
{ documentId: document.documentId, locale, publishedAt: null },
|
2548
2908
|
{ availableLocales: true, availableStatus: false }
|
2549
2909
|
);
|
2550
2910
|
ctx.body = { data: {}, meta };
|
@@ -2554,29 +2914,28 @@ const singleTypes = {
|
|
2554
2914
|
return ctx.forbidden();
|
2555
2915
|
}
|
2556
2916
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
|
2557
|
-
ctx.body = await
|
2917
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2558
2918
|
},
|
2559
2919
|
async createOrUpdate(ctx) {
|
2560
2920
|
const { userAbility } = ctx.state;
|
2561
2921
|
const { model } = ctx.params;
|
2562
|
-
const
|
2563
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2922
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2564
2923
|
const document = await createOrUpdateDocument(ctx);
|
2565
2924
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
|
2566
|
-
ctx.body = await
|
2925
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2567
2926
|
},
|
2568
2927
|
async delete(ctx) {
|
2569
2928
|
const { userAbility } = ctx.state;
|
2570
2929
|
const { model } = ctx.params;
|
2571
2930
|
const { query = {} } = ctx.request;
|
2572
|
-
const documentManager2 = getService$
|
2573
|
-
const permissionChecker2 = getService$
|
2931
|
+
const documentManager2 = getService$2("document-manager");
|
2932
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2574
2933
|
if (permissionChecker2.cannot.delete()) {
|
2575
2934
|
return ctx.forbidden();
|
2576
2935
|
}
|
2577
2936
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
|
2578
2937
|
const populate = await buildPopulateFromQuery(sanitizedQuery, model);
|
2579
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2938
|
+
const { locale } = await getDocumentLocaleAndStatus(query, model);
|
2580
2939
|
const documentLocales = await documentManager2.findLocales(void 0, model, {
|
2581
2940
|
populate,
|
2582
2941
|
locale
|
@@ -2598,9 +2957,8 @@ const singleTypes = {
|
|
2598
2957
|
const { userAbility } = ctx.state;
|
2599
2958
|
const { model } = ctx.params;
|
2600
2959
|
const { query = {} } = ctx.request;
|
2601
|
-
const documentManager2 = getService$
|
2602
|
-
const
|
2603
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2960
|
+
const documentManager2 = getService$2("document-manager");
|
2961
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2604
2962
|
if (permissionChecker2.cannot.publish()) {
|
2605
2963
|
return ctx.forbidden();
|
2606
2964
|
}
|
@@ -2614,11 +2972,12 @@ const singleTypes = {
|
|
2614
2972
|
if (permissionChecker2.cannot.publish(document)) {
|
2615
2973
|
throw new strapiUtils.errors.ForbiddenError();
|
2616
2974
|
}
|
2617
|
-
const { locale } = getDocumentLocaleAndStatus(document);
|
2618
|
-
|
2975
|
+
const { locale } = await getDocumentLocaleAndStatus(document, model);
|
2976
|
+
const publishResult = await documentManager2.publish(document.documentId, model, { locale });
|
2977
|
+
return publishResult.at(0);
|
2619
2978
|
});
|
2620
2979
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
|
2621
|
-
ctx.body = await
|
2980
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2622
2981
|
},
|
2623
2982
|
async unpublish(ctx) {
|
2624
2983
|
const { userAbility } = ctx.state;
|
@@ -2627,9 +2986,8 @@ const singleTypes = {
|
|
2627
2986
|
body: { discardDraft, ...body },
|
2628
2987
|
query = {}
|
2629
2988
|
} = ctx.request;
|
2630
|
-
const documentManager2 = getService$
|
2631
|
-
const
|
2632
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2989
|
+
const documentManager2 = getService$2("document-manager");
|
2990
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2633
2991
|
if (permissionChecker2.cannot.unpublish()) {
|
2634
2992
|
return ctx.forbidden();
|
2635
2993
|
}
|
@@ -2637,7 +2995,7 @@ const singleTypes = {
|
|
2637
2995
|
return ctx.forbidden();
|
2638
2996
|
}
|
2639
2997
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
|
2640
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2998
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2641
2999
|
const document = await findDocument(sanitizedQuery, model, { locale });
|
2642
3000
|
if (!document) {
|
2643
3001
|
return ctx.notFound();
|
@@ -2655,7 +3013,7 @@ const singleTypes = {
|
|
2655
3013
|
ctx.body = await strapiUtils.async.pipe(
|
2656
3014
|
(document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
|
2657
3015
|
permissionChecker2.sanitizeOutput,
|
2658
|
-
(document2) =>
|
3016
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
2659
3017
|
)(document);
|
2660
3018
|
});
|
2661
3019
|
},
|
@@ -2663,14 +3021,13 @@ const singleTypes = {
|
|
2663
3021
|
const { userAbility } = ctx.state;
|
2664
3022
|
const { model } = ctx.params;
|
2665
3023
|
const { body, query = {} } = ctx.request;
|
2666
|
-
const documentManager2 = getService$
|
2667
|
-
const
|
2668
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
3024
|
+
const documentManager2 = getService$2("document-manager");
|
3025
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2669
3026
|
if (permissionChecker2.cannot.discard()) {
|
2670
3027
|
return ctx.forbidden();
|
2671
3028
|
}
|
2672
3029
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
|
2673
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
3030
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2674
3031
|
const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
|
2675
3032
|
if (!document) {
|
2676
3033
|
return ctx.notFound();
|
@@ -2681,16 +3038,16 @@ const singleTypes = {
|
|
2681
3038
|
ctx.body = await strapiUtils.async.pipe(
|
2682
3039
|
(document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
|
2683
3040
|
permissionChecker2.sanitizeOutput,
|
2684
|
-
(document2) =>
|
3041
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
2685
3042
|
)(document);
|
2686
3043
|
},
|
2687
3044
|
async countDraftRelations(ctx) {
|
2688
3045
|
const { userAbility } = ctx.state;
|
2689
3046
|
const { model } = ctx.params;
|
2690
3047
|
const { query } = ctx.request;
|
2691
|
-
const documentManager2 = getService$
|
2692
|
-
const permissionChecker2 = getService$
|
2693
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
3048
|
+
const documentManager2 = getService$2("document-manager");
|
3049
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
3050
|
+
const { locale } = await getDocumentLocaleAndStatus(query, model);
|
2694
3051
|
if (permissionChecker2.cannot.read()) {
|
2695
3052
|
return ctx.forbidden();
|
2696
3053
|
}
|
@@ -2711,9 +3068,9 @@ const uid$1 = {
|
|
2711
3068
|
async generateUID(ctx) {
|
2712
3069
|
const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
|
2713
3070
|
const { query = {} } = ctx.request;
|
2714
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
3071
|
+
const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
|
2715
3072
|
await validateUIDField(contentTypeUID, field);
|
2716
|
-
const uidService = getService$
|
3073
|
+
const uidService = getService$2("uid");
|
2717
3074
|
ctx.body = {
|
2718
3075
|
data: await uidService.generateUIDField({ contentTypeUID, field, data, locale })
|
2719
3076
|
};
|
@@ -2723,9 +3080,9 @@ const uid$1 = {
|
|
2723
3080
|
ctx.request.body
|
2724
3081
|
);
|
2725
3082
|
const { query = {} } = ctx.request;
|
2726
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
3083
|
+
const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
|
2727
3084
|
await validateUIDField(contentTypeUID, field);
|
2728
|
-
const uidService = getService$
|
3085
|
+
const uidService = getService$2("uid");
|
2729
3086
|
const isAvailable = await uidService.checkUIDAvailability({
|
2730
3087
|
contentTypeUID,
|
2731
3088
|
field,
|
@@ -2746,7 +3103,8 @@ const controllers = {
|
|
2746
3103
|
relations,
|
2747
3104
|
"single-types": singleTypes,
|
2748
3105
|
uid: uid$1,
|
2749
|
-
...history.controllers ? history.controllers : {}
|
3106
|
+
...history.controllers ? history.controllers : {},
|
3107
|
+
...preview.controllers ? preview.controllers : {}
|
2750
3108
|
};
|
2751
3109
|
const keys = {
|
2752
3110
|
CONFIGURATION: "configuration"
|
@@ -2897,12 +3255,12 @@ async function syncMetadatas(configuration, schema) {
|
|
2897
3255
|
return ___default.default.assign(metasWithDefaults, updatedMetas);
|
2898
3256
|
}
|
2899
3257
|
const getTargetSchema = (targetModel) => {
|
2900
|
-
return getService$
|
3258
|
+
return getService$2("content-types").findContentType(targetModel);
|
2901
3259
|
};
|
2902
3260
|
const DEFAULT_LIST_LENGTH = 4;
|
2903
3261
|
const MAX_ROW_SIZE = 12;
|
2904
3262
|
const isAllowedFieldSize = (type, size) => {
|
2905
|
-
const { getFieldSize } = getService$
|
3263
|
+
const { getFieldSize } = getService$2("field-sizes");
|
2906
3264
|
const fieldSize = getFieldSize(type);
|
2907
3265
|
if (!fieldSize.isResizable && size !== fieldSize.default) {
|
2908
3266
|
return false;
|
@@ -2910,7 +3268,7 @@ const isAllowedFieldSize = (type, size) => {
|
|
2910
3268
|
return size <= MAX_ROW_SIZE;
|
2911
3269
|
};
|
2912
3270
|
const getDefaultFieldSize = (attribute) => {
|
2913
|
-
const { hasFieldSize, getFieldSize } = getService$
|
3271
|
+
const { hasFieldSize, getFieldSize } = getService$2("field-sizes");
|
2914
3272
|
return getFieldSize(hasFieldSize(attribute.customField) ? attribute.customField : attribute.type).default;
|
2915
3273
|
};
|
2916
3274
|
async function createDefaultLayouts(schema) {
|
@@ -2945,7 +3303,7 @@ function syncLayouts(configuration, schema) {
|
|
2945
3303
|
for (const el of row) {
|
2946
3304
|
if (!hasEditableAttribute(schema, el.name))
|
2947
3305
|
continue;
|
2948
|
-
const { hasFieldSize } = getService$
|
3306
|
+
const { hasFieldSize } = getService$2("field-sizes");
|
2949
3307
|
const fieldType = hasFieldSize(schema.attributes[el.name].customField) ? schema.attributes[el.name].customField : schema.attributes[el.name].type;
|
2950
3308
|
if (!isAllowedFieldSize(fieldType, el.size)) {
|
2951
3309
|
elementsToReAppend.push(el.name);
|
@@ -3085,17 +3443,17 @@ const configurationService$1 = createConfigurationService({
|
|
3085
3443
|
isComponent: true,
|
3086
3444
|
prefix: STORE_KEY_PREFIX,
|
3087
3445
|
getModels() {
|
3088
|
-
const { toContentManagerModel } = getService$
|
3446
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3089
3447
|
return fp.mapValues(toContentManagerModel, strapi.components);
|
3090
3448
|
}
|
3091
3449
|
});
|
3092
3450
|
const components = ({ strapi: strapi2 }) => ({
|
3093
3451
|
findAllComponents() {
|
3094
|
-
const { toContentManagerModel } = getService$
|
3452
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3095
3453
|
return Object.values(strapi2.components).map(toContentManagerModel);
|
3096
3454
|
},
|
3097
3455
|
findComponent(uid2) {
|
3098
|
-
const { toContentManagerModel } = getService$
|
3456
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3099
3457
|
const component = strapi2.components[uid2];
|
3100
3458
|
return fp.isNil(component) ? component : toContentManagerModel(component);
|
3101
3459
|
},
|
@@ -3146,17 +3504,17 @@ const configurationService = createConfigurationService({
|
|
3146
3504
|
storeUtils,
|
3147
3505
|
prefix: "content_types",
|
3148
3506
|
getModels() {
|
3149
|
-
const { toContentManagerModel } = getService$
|
3507
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3150
3508
|
return fp.mapValues(toContentManagerModel, strapi.contentTypes);
|
3151
3509
|
}
|
3152
3510
|
});
|
3153
3511
|
const service = ({ strapi: strapi2 }) => ({
|
3154
3512
|
findAllContentTypes() {
|
3155
|
-
const { toContentManagerModel } = getService$
|
3513
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3156
3514
|
return Object.values(strapi2.contentTypes).map(toContentManagerModel);
|
3157
3515
|
},
|
3158
3516
|
findContentType(uid2) {
|
3159
|
-
const { toContentManagerModel } = getService$
|
3517
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3160
3518
|
const contentType = strapi2.contentTypes[uid2];
|
3161
3519
|
return fp.isNil(contentType) ? contentType : toContentManagerModel(contentType);
|
3162
3520
|
},
|
@@ -3185,7 +3543,7 @@ const service = ({ strapi: strapi2 }) => ({
|
|
3185
3543
|
return this.findConfiguration(contentType);
|
3186
3544
|
},
|
3187
3545
|
findComponentsConfigurations(contentType) {
|
3188
|
-
return getService$
|
3546
|
+
return getService$2("components").findComponentsConfigurations(contentType);
|
3189
3547
|
},
|
3190
3548
|
syncConfigurations() {
|
3191
3549
|
return configurationService.syncConfigurations();
|
@@ -3366,12 +3724,27 @@ const createPermissionChecker = (strapi2) => ({ userAbility, model }) => {
|
|
3366
3724
|
ability: userAbility,
|
3367
3725
|
model
|
3368
3726
|
});
|
3369
|
-
const
|
3727
|
+
const { actionProvider } = strapi2.service("admin::permission");
|
3728
|
+
const toSubject = (entity) => {
|
3729
|
+
return entity ? permissionsManager.toSubject(entity, model) : model;
|
3730
|
+
};
|
3370
3731
|
const can = (action, entity, field) => {
|
3371
|
-
|
3732
|
+
const subject = toSubject(entity);
|
3733
|
+
const aliases = actionProvider.unstable_aliases(action, model);
|
3734
|
+
return (
|
3735
|
+
// Test the original action to see if it passes
|
3736
|
+
userAbility.can(action, subject, field) || // Else try every known alias if at least one of them succeed, then the user "can"
|
3737
|
+
aliases.some((alias) => userAbility.can(alias, subject, field))
|
3738
|
+
);
|
3372
3739
|
};
|
3373
3740
|
const cannot = (action, entity, field) => {
|
3374
|
-
|
3741
|
+
const subject = toSubject(entity);
|
3742
|
+
const aliases = actionProvider.unstable_aliases(action, model);
|
3743
|
+
return (
|
3744
|
+
// Test both the original action
|
3745
|
+
userAbility.cannot(action, subject, field) && // and every known alias, if all of them fail (cannot), then the user truly "cannot"
|
3746
|
+
aliases.every((alias) => userAbility.cannot(alias, subject, field))
|
3747
|
+
);
|
3375
3748
|
};
|
3376
3749
|
const sanitizeOutput = (data, { action = ACTIONS.read } = {}) => {
|
3377
3750
|
return permissionsManager.sanitizeOutput(data, { subject: toSubject(data), action });
|
@@ -3442,7 +3815,7 @@ const permission = ({ strapi: strapi2 }) => ({
|
|
3442
3815
|
return userAbility.can(action);
|
3443
3816
|
},
|
3444
3817
|
async registerPermissions() {
|
3445
|
-
const displayedContentTypes = getService$
|
3818
|
+
const displayedContentTypes = getService$2("content-types").findDisplayedContentTypes();
|
3446
3819
|
const contentTypesUids = displayedContentTypes.map(fp.prop("uid"));
|
3447
3820
|
const actions = [
|
3448
3821
|
{
|
@@ -3514,7 +3887,7 @@ const permission = ({ strapi: strapi2 }) => ({
|
|
3514
3887
|
await strapi2.service("admin::permission").actionProvider.registerMany(actions);
|
3515
3888
|
}
|
3516
3889
|
});
|
3517
|
-
const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils__default.default.contentTypes;
|
3890
|
+
const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils__default.default.contentTypes;
|
3518
3891
|
const { isAnyToMany } = strapiUtils__default.default.relations;
|
3519
3892
|
const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils__default.default.contentTypes.constants;
|
3520
3893
|
const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
|
@@ -3605,6 +3978,42 @@ const getDeepPopulate = (uid2, {
|
|
3605
3978
|
{}
|
3606
3979
|
);
|
3607
3980
|
};
|
3981
|
+
const getValidatableFieldsPopulate = (uid2, {
|
3982
|
+
initialPopulate = {},
|
3983
|
+
countMany = false,
|
3984
|
+
countOne = false,
|
3985
|
+
maxLevel = Infinity
|
3986
|
+
} = {}, level = 1) => {
|
3987
|
+
if (level > maxLevel) {
|
3988
|
+
return {};
|
3989
|
+
}
|
3990
|
+
const model = strapi.getModel(uid2);
|
3991
|
+
return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
|
3992
|
+
if (!getDoesAttributeRequireValidation(attribute)) {
|
3993
|
+
return populateAcc;
|
3994
|
+
}
|
3995
|
+
if (isScalarAttribute(attribute)) {
|
3996
|
+
return fp.merge(populateAcc, {
|
3997
|
+
[attributeName]: true
|
3998
|
+
});
|
3999
|
+
}
|
4000
|
+
return fp.merge(
|
4001
|
+
populateAcc,
|
4002
|
+
getPopulateFor(
|
4003
|
+
attributeName,
|
4004
|
+
model,
|
4005
|
+
{
|
4006
|
+
// @ts-expect-error - improve types
|
4007
|
+
initialPopulate: initialPopulate?.[attributeName],
|
4008
|
+
countMany,
|
4009
|
+
countOne,
|
4010
|
+
maxLevel
|
4011
|
+
},
|
4012
|
+
level
|
4013
|
+
)
|
4014
|
+
);
|
4015
|
+
}, {});
|
4016
|
+
};
|
3608
4017
|
const getDeepPopulateDraftCount = (uid2) => {
|
3609
4018
|
const model = strapi.getModel(uid2);
|
3610
4019
|
let hasRelations = false;
|
@@ -3612,6 +4021,10 @@ const getDeepPopulateDraftCount = (uid2) => {
|
|
3612
4021
|
const attribute = model.attributes[attributeName];
|
3613
4022
|
switch (attribute.type) {
|
3614
4023
|
case "relation": {
|
4024
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
4025
|
+
if (isMorphRelation) {
|
4026
|
+
break;
|
4027
|
+
}
|
3615
4028
|
if (isVisibleAttribute$1(model, attributeName)) {
|
3616
4029
|
populateAcc[attributeName] = {
|
3617
4030
|
count: true,
|
@@ -3626,22 +4039,24 @@ const getDeepPopulateDraftCount = (uid2) => {
|
|
3626
4039
|
attribute.component
|
3627
4040
|
);
|
3628
4041
|
if (childHasRelations) {
|
3629
|
-
populateAcc[attributeName] = {
|
4042
|
+
populateAcc[attributeName] = {
|
4043
|
+
populate: populate2
|
4044
|
+
};
|
3630
4045
|
hasRelations = true;
|
3631
4046
|
}
|
3632
4047
|
break;
|
3633
4048
|
}
|
3634
4049
|
case "dynamiczone": {
|
3635
|
-
const
|
3636
|
-
const { populate:
|
3637
|
-
if (
|
4050
|
+
const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
|
4051
|
+
const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
|
4052
|
+
if (componentHasRelations) {
|
3638
4053
|
hasRelations = true;
|
3639
|
-
return
|
4054
|
+
return { ...acc, [componentUID]: { populate: componentPopulate } };
|
3640
4055
|
}
|
3641
4056
|
return acc;
|
3642
4057
|
}, {});
|
3643
|
-
if (!fp.isEmpty(
|
3644
|
-
populateAcc[attributeName] = {
|
4058
|
+
if (!fp.isEmpty(dzPopulateFragment)) {
|
4059
|
+
populateAcc[attributeName] = { on: dzPopulateFragment };
|
3645
4060
|
}
|
3646
4061
|
break;
|
3647
4062
|
}
|
@@ -3676,7 +4091,7 @@ const getQueryPopulate = async (uid2, query) => {
|
|
3676
4091
|
return populateQuery;
|
3677
4092
|
};
|
3678
4093
|
const buildDeepPopulate = (uid2) => {
|
3679
|
-
return getService$
|
4094
|
+
return getService$2("populate-builder")(uid2).populateDeep(Infinity).countRelations().build();
|
3680
4095
|
};
|
3681
4096
|
const populateBuilder = (uid2) => {
|
3682
4097
|
let getInitialPopulate = async () => {
|
@@ -3833,41 +4248,72 @@ const AVAILABLE_STATUS_FIELDS = [
|
|
3833
4248
|
"updatedBy",
|
3834
4249
|
"status"
|
3835
4250
|
];
|
3836
|
-
const AVAILABLE_LOCALES_FIELDS = [
|
4251
|
+
const AVAILABLE_LOCALES_FIELDS = [
|
4252
|
+
"id",
|
4253
|
+
"locale",
|
4254
|
+
"updatedAt",
|
4255
|
+
"createdAt",
|
4256
|
+
"status",
|
4257
|
+
"publishedAt",
|
4258
|
+
"documentId"
|
4259
|
+
];
|
3837
4260
|
const CONTENT_MANAGER_STATUS = {
|
3838
4261
|
PUBLISHED: "published",
|
3839
4262
|
DRAFT: "draft",
|
3840
4263
|
MODIFIED: "modified"
|
3841
4264
|
};
|
3842
|
-
const
|
3843
|
-
if (!
|
4265
|
+
const getIsVersionLatestModification = (version, otherVersion) => {
|
4266
|
+
if (!version || !version.updatedAt) {
|
3844
4267
|
return false;
|
3845
4268
|
}
|
3846
|
-
const
|
3847
|
-
const
|
3848
|
-
|
3849
|
-
return difference <= threshold;
|
4269
|
+
const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
|
4270
|
+
const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
|
4271
|
+
return versionUpdatedAt > otherUpdatedAt;
|
3850
4272
|
};
|
3851
4273
|
const documentMetadata = ({ strapi: strapi2 }) => ({
|
3852
4274
|
/**
|
3853
4275
|
* Returns available locales of a document for the current status
|
3854
4276
|
*/
|
3855
|
-
getAvailableLocales(uid2, version, allVersions) {
|
4277
|
+
async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
|
3856
4278
|
const versionsByLocale = fp.groupBy("locale", allVersions);
|
3857
|
-
|
3858
|
-
|
3859
|
-
|
3860
|
-
|
4279
|
+
if (version.locale) {
|
4280
|
+
delete versionsByLocale[version.locale];
|
4281
|
+
}
|
4282
|
+
const model = strapi2.getModel(uid2);
|
4283
|
+
const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
|
4284
|
+
const traversalFunction = async (localeVersion) => strapiUtils.traverseEntity(
|
4285
|
+
({ key }, { remove }) => {
|
4286
|
+
if (keysToKeep.includes(key)) {
|
4287
|
+
return;
|
4288
|
+
}
|
4289
|
+
remove(key);
|
4290
|
+
},
|
4291
|
+
{ schema: model, getModel: strapi2.getModel.bind(strapi2) },
|
4292
|
+
// @ts-expect-error fix types DocumentVersion incompatible with Data
|
4293
|
+
localeVersion
|
4294
|
+
);
|
4295
|
+
const mappingResult = await strapiUtils.async.map(
|
4296
|
+
Object.values(versionsByLocale),
|
4297
|
+
async (localeVersions) => {
|
4298
|
+
const mappedLocaleVersions = await strapiUtils.async.map(
|
4299
|
+
localeVersions,
|
4300
|
+
traversalFunction
|
4301
|
+
);
|
4302
|
+
if (!strapiUtils.contentTypes.hasDraftAndPublish(model)) {
|
4303
|
+
return mappedLocaleVersions[0];
|
4304
|
+
}
|
4305
|
+
const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
|
4306
|
+
const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
|
4307
|
+
if (!draftVersion) {
|
4308
|
+
return;
|
4309
|
+
}
|
4310
|
+
return {
|
4311
|
+
...draftVersion,
|
4312
|
+
status: this.getStatus(draftVersion, otherVersions)
|
4313
|
+
};
|
3861
4314
|
}
|
3862
|
-
|
3863
|
-
|
3864
|
-
if (!draftVersion)
|
3865
|
-
return;
|
3866
|
-
return {
|
3867
|
-
...fp.pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
|
3868
|
-
status: this.getStatus(draftVersion, otherVersions)
|
3869
|
-
};
|
3870
|
-
}).filter(Boolean);
|
4315
|
+
);
|
4316
|
+
return mappingResult.filter(Boolean);
|
3871
4317
|
},
|
3872
4318
|
/**
|
3873
4319
|
* Returns available status of a document for the current locale
|
@@ -3905,26 +4351,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3905
4351
|
});
|
3906
4352
|
},
|
3907
4353
|
getStatus(version, otherDocumentStatuses) {
|
3908
|
-
|
3909
|
-
|
3910
|
-
|
3911
|
-
|
3912
|
-
|
3913
|
-
|
3914
|
-
if (!publishedVersion) {
|
3915
|
-
return CONTENT_MANAGER_STATUS.DRAFT;
|
3916
|
-
}
|
4354
|
+
let draftVersion;
|
4355
|
+
let publishedVersion;
|
4356
|
+
if (version.publishedAt) {
|
4357
|
+
publishedVersion = version;
|
4358
|
+
} else {
|
4359
|
+
draftVersion = version;
|
3917
4360
|
}
|
3918
|
-
|
3919
|
-
|
4361
|
+
const otherVersion = otherDocumentStatuses?.at(0);
|
4362
|
+
if (otherVersion?.publishedAt) {
|
4363
|
+
publishedVersion = otherVersion;
|
4364
|
+
} else if (otherVersion) {
|
4365
|
+
draftVersion = otherVersion;
|
3920
4366
|
}
|
3921
|
-
|
4367
|
+
if (!draftVersion)
|
4368
|
+
return CONTENT_MANAGER_STATUS.PUBLISHED;
|
4369
|
+
if (!publishedVersion)
|
4370
|
+
return CONTENT_MANAGER_STATUS.DRAFT;
|
4371
|
+
const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
|
4372
|
+
return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
|
3922
4373
|
},
|
4374
|
+
// TODO is it necessary to return metadata on every page of the CM
|
4375
|
+
// We could refactor this so the locales are only loaded when they're
|
4376
|
+
// needed. e.g. in the bulk locale action modal.
|
3923
4377
|
async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
|
4378
|
+
const populate = getValidatableFieldsPopulate(uid2);
|
3924
4379
|
const versions = await strapi2.db.query(uid2).findMany({
|
3925
4380
|
where: { documentId: version.documentId },
|
3926
|
-
select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
|
3927
4381
|
populate: {
|
4382
|
+
// Populate only fields that require validation for bulk locale actions
|
4383
|
+
...populate,
|
4384
|
+
// NOTE: creator fields are selected in this way to avoid exposing sensitive data
|
3928
4385
|
createdBy: {
|
3929
4386
|
select: ["id", "firstname", "lastname", "email"]
|
3930
4387
|
},
|
@@ -3933,7 +4390,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3933
4390
|
}
|
3934
4391
|
}
|
3935
4392
|
});
|
3936
|
-
const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
|
4393
|
+
const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
|
3937
4394
|
const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
|
3938
4395
|
return {
|
3939
4396
|
availableLocales: availableLocalesResult,
|
@@ -3946,8 +4403,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3946
4403
|
* - Available status of the document for the current locale
|
3947
4404
|
*/
|
3948
4405
|
async formatDocumentWithMetadata(uid2, document, opts = {}) {
|
3949
|
-
if (!document)
|
3950
|
-
return
|
4406
|
+
if (!document) {
|
4407
|
+
return {
|
4408
|
+
data: document,
|
4409
|
+
meta: {
|
4410
|
+
availableLocales: [],
|
4411
|
+
availableStatus: []
|
4412
|
+
}
|
4413
|
+
};
|
4414
|
+
}
|
3951
4415
|
const hasDraftAndPublish = strapiUtils.contentTypes.hasDraftAndPublish(strapi2.getModel(uid2));
|
3952
4416
|
if (!hasDraftAndPublish) {
|
3953
4417
|
opts.availableStatus = false;
|
@@ -3997,26 +4461,9 @@ const sumDraftCounts = (entity, uid2) => {
|
|
3997
4461
|
}, 0);
|
3998
4462
|
};
|
3999
4463
|
const { ApplicationError } = strapiUtils.errors;
|
4000
|
-
const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
|
4001
4464
|
const { PUBLISHED_AT_ATTRIBUTE } = strapiUtils.contentTypes.constants;
|
4002
4465
|
const omitPublishedAtField = fp.omit(PUBLISHED_AT_ATTRIBUTE);
|
4003
4466
|
const omitIdField = fp.omit("id");
|
4004
|
-
const emitEvent = async (uid2, event, document) => {
|
4005
|
-
const modelDef = strapi.getModel(uid2);
|
4006
|
-
const sanitizedDocument = await strapiUtils.sanitize.sanitizers.defaultSanitizeOutput(
|
4007
|
-
{
|
4008
|
-
schema: modelDef,
|
4009
|
-
getModel(uid22) {
|
4010
|
-
return strapi.getModel(uid22);
|
4011
|
-
}
|
4012
|
-
},
|
4013
|
-
document
|
4014
|
-
);
|
4015
|
-
strapi.eventHub.emit(event, {
|
4016
|
-
model: modelDef.modelName,
|
4017
|
-
entry: sanitizedDocument
|
4018
|
-
});
|
4019
|
-
};
|
4020
4467
|
const documentManager = ({ strapi: strapi2 }) => {
|
4021
4468
|
return {
|
4022
4469
|
async findOne(id, uid2, opts = {}) {
|
@@ -4035,6 +4482,9 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4035
4482
|
} else if (opts.locale && opts.locale !== "*") {
|
4036
4483
|
where.locale = opts.locale;
|
4037
4484
|
}
|
4485
|
+
if (typeof opts.isPublished === "boolean") {
|
4486
|
+
where.publishedAt = { $notNull: opts.isPublished };
|
4487
|
+
}
|
4038
4488
|
return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
|
4039
4489
|
},
|
4040
4490
|
async findMany(opts, uid2) {
|
@@ -4042,20 +4492,16 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4042
4492
|
return strapi2.documents(uid2).findMany(params);
|
4043
4493
|
},
|
4044
4494
|
async findPage(opts, uid2) {
|
4045
|
-
const
|
4046
|
-
|
4495
|
+
const params = strapiUtils.pagination.withDefaultPagination(opts || {}, {
|
4496
|
+
maxLimit: 1e3
|
4497
|
+
});
|
4047
4498
|
const [documents, total = 0] = await Promise.all([
|
4048
|
-
strapi2.documents(uid2).findMany(
|
4049
|
-
strapi2.documents(uid2).count(
|
4499
|
+
strapi2.documents(uid2).findMany(params),
|
4500
|
+
strapi2.documents(uid2).count(params)
|
4050
4501
|
]);
|
4051
4502
|
return {
|
4052
4503
|
results: documents,
|
4053
|
-
pagination:
|
4054
|
-
page,
|
4055
|
-
pageSize,
|
4056
|
-
pageCount: Math.ceil(total / pageSize),
|
4057
|
-
total
|
4058
|
-
}
|
4504
|
+
pagination: strapiUtils.pagination.transformPagedPaginationInfo(params, total)
|
4059
4505
|
};
|
4060
4506
|
},
|
4061
4507
|
async create(uid2, opts = {}) {
|
@@ -4072,10 +4518,7 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4072
4518
|
async clone(id, body, uid2) {
|
4073
4519
|
const populate = await buildDeepPopulate(uid2);
|
4074
4520
|
const params = {
|
4075
|
-
data:
|
4076
|
-
...omitIdField(body),
|
4077
|
-
[PUBLISHED_AT_ATTRIBUTE]: null
|
4078
|
-
},
|
4521
|
+
data: omitIdField(body),
|
4079
4522
|
populate
|
4080
4523
|
};
|
4081
4524
|
return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
|
@@ -4101,70 +4544,36 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4101
4544
|
return {};
|
4102
4545
|
},
|
4103
4546
|
// FIXME: handle relations
|
4104
|
-
async deleteMany(
|
4105
|
-
const
|
4106
|
-
|
4107
|
-
|
4108
|
-
}
|
4109
|
-
return { count: docs.length };
|
4547
|
+
async deleteMany(documentIds, uid2, opts = {}) {
|
4548
|
+
const deletedEntries = await strapi2.db.transaction(async () => {
|
4549
|
+
return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
|
4550
|
+
});
|
4551
|
+
return { count: deletedEntries.length };
|
4110
4552
|
},
|
4111
4553
|
async publish(id, uid2, opts = {}) {
|
4112
4554
|
const populate = await buildDeepPopulate(uid2);
|
4113
4555
|
const params = { ...opts, populate };
|
4114
|
-
return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries
|
4556
|
+
return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
|
4115
4557
|
},
|
4116
|
-
async publishMany(
|
4117
|
-
|
4118
|
-
|
4119
|
-
|
4120
|
-
|
4121
|
-
|
4122
|
-
|
4123
|
-
strapi2.getModel(uid2),
|
4124
|
-
document,
|
4125
|
-
void 0,
|
4126
|
-
// @ts-expect-error - FIXME: entity here is unnecessary
|
4127
|
-
document
|
4128
|
-
);
|
4129
|
-
})
|
4130
|
-
);
|
4131
|
-
const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
|
4132
|
-
const filters = { id: { $in: entitiesToPublish } };
|
4133
|
-
const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
|
4134
|
-
const populate = await buildDeepPopulate(uid2);
|
4135
|
-
const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
|
4136
|
-
where: filters,
|
4137
|
-
data
|
4138
|
-
});
|
4139
|
-
const publishedEntities = await strapi2.db.query(uid2).findMany({
|
4140
|
-
where: filters,
|
4141
|
-
populate
|
4558
|
+
async publishMany(uid2, documentIds, locale) {
|
4559
|
+
return strapi2.db.transaction(async () => {
|
4560
|
+
const results = await Promise.all(
|
4561
|
+
documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
|
4562
|
+
);
|
4563
|
+
const publishedEntitiesCount = results.flat().filter(Boolean).length;
|
4564
|
+
return publishedEntitiesCount;
|
4142
4565
|
});
|
4143
|
-
await Promise.all(
|
4144
|
-
publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
|
4145
|
-
);
|
4146
|
-
return publishedEntitiesCount;
|
4147
4566
|
},
|
4148
|
-
async unpublishMany(
|
4149
|
-
|
4150
|
-
return
|
4151
|
-
|
4152
|
-
|
4153
|
-
|
4154
|
-
|
4155
|
-
const populate = await buildDeepPopulate(uid2);
|
4156
|
-
const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
|
4157
|
-
where: filters,
|
4158
|
-
data
|
4159
|
-
});
|
4160
|
-
const unpublishedEntities = await strapi2.db.query(uid2).findMany({
|
4161
|
-
where: filters,
|
4162
|
-
populate
|
4567
|
+
async unpublishMany(documentIds, uid2, opts = {}) {
|
4568
|
+
const unpublishedEntries = await strapi2.db.transaction(async () => {
|
4569
|
+
return Promise.all(
|
4570
|
+
documentIds.map(
|
4571
|
+
(id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
|
4572
|
+
)
|
4573
|
+
);
|
4163
4574
|
});
|
4164
|
-
|
4165
|
-
|
4166
|
-
);
|
4167
|
-
return unpublishedEntitiesCount;
|
4575
|
+
const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
|
4576
|
+
return { count: unpublishedEntitiesCount };
|
4168
4577
|
},
|
4169
4578
|
async unpublish(id, uid2, opts = {}) {
|
4170
4579
|
const populate = await buildDeepPopulate(uid2);
|
@@ -4189,16 +4598,20 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4189
4598
|
}
|
4190
4599
|
return sumDraftCounts(document, uid2);
|
4191
4600
|
},
|
4192
|
-
async countManyEntriesDraftRelations(
|
4601
|
+
async countManyEntriesDraftRelations(documentIds, uid2, locale) {
|
4193
4602
|
const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
|
4194
4603
|
if (!hasRelations) {
|
4195
4604
|
return 0;
|
4196
4605
|
}
|
4606
|
+
let localeFilter = {};
|
4607
|
+
if (locale) {
|
4608
|
+
localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
|
4609
|
+
}
|
4197
4610
|
const entities = await strapi2.db.query(uid2).findMany({
|
4198
4611
|
populate,
|
4199
4612
|
where: {
|
4200
|
-
|
4201
|
-
...
|
4613
|
+
documentId: { $in: documentIds },
|
4614
|
+
...localeFilter
|
4202
4615
|
}
|
4203
4616
|
});
|
4204
4617
|
const totalNumberDraftRelations = entities.reduce(
|
@@ -4221,7 +4634,8 @@ const services = {
|
|
4221
4634
|
permission,
|
4222
4635
|
"populate-builder": populateBuilder$1,
|
4223
4636
|
uid,
|
4224
|
-
...history.services ? history.services : {}
|
4637
|
+
...history.services ? history.services : {},
|
4638
|
+
...preview.services ? preview.services : {}
|
4225
4639
|
};
|
4226
4640
|
const index = () => {
|
4227
4641
|
return {
|