@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.mjs
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1,
|
2
|
-
import { pick, omit, difference, intersection, pipe, propOr, isEqual, isEmpty, set, isNil as isNil$1, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, getOr, propEq, merge, groupBy
|
1
|
+
import strapiUtils, { validateYupSchema, errors, async, contentTypes as contentTypes$1, yup as yup$1, validateYupSchemaSync, policy, traverse, setCreatorFields, isOperatorOfType, relations as relations$1, traverseEntity, pagination } from "@strapi/utils";
|
2
|
+
import { pick, omit, difference, castArray, intersection, pipe, propOr, isEqual, isEmpty, set, isNil as isNil$1, has, prop, assoc, mapValues, flow, uniq, uniqBy, concat, getOr, propEq, merge, groupBy } from "lodash/fp";
|
3
3
|
import "@strapi/types";
|
4
4
|
import * as yup from "yup";
|
5
5
|
import { scheduleJob } from "node-schedule";
|
@@ -7,10 +7,10 @@ import isNil from "lodash/isNil";
|
|
7
7
|
import _, { intersection as intersection$1, difference as difference$1 } from "lodash";
|
8
8
|
import qs from "qs";
|
9
9
|
import slugify from "@sindresorhus/slugify";
|
10
|
-
const getService$
|
10
|
+
const getService$2 = (name) => {
|
11
11
|
return strapi.plugin("content-manager").service(name);
|
12
12
|
};
|
13
|
-
function getService(strapi2, name) {
|
13
|
+
function getService$1(strapi2, name) {
|
14
14
|
return strapi2.service(`plugin::content-manager.${name}`);
|
15
15
|
}
|
16
16
|
const historyRestoreVersionSchema = yup.object().shape({
|
@@ -46,7 +46,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
|
|
46
46
|
if (!isSingleType && (!contentTypeUid || !ctx.query.documentId)) {
|
47
47
|
throw new errors.ForbiddenError("contentType and documentId are required");
|
48
48
|
}
|
49
|
-
const permissionChecker2 = getService$
|
49
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
50
50
|
userAbility: ctx.state.userAbility,
|
51
51
|
model: ctx.query.contentType
|
52
52
|
});
|
@@ -54,7 +54,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
|
|
54
54
|
return ctx.forbidden();
|
55
55
|
}
|
56
56
|
const query = await permissionChecker2.sanitizeQuery(ctx.query);
|
57
|
-
const { results, pagination } = await getService(strapi2, "history").findVersionsPage({
|
57
|
+
const { results, pagination: pagination2 } = await getService$1(strapi2, "history").findVersionsPage({
|
58
58
|
query: {
|
59
59
|
...query,
|
60
60
|
...getValidPagination({ page: query.page, pageSize: query.pageSize })
|
@@ -73,20 +73,20 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
|
|
73
73
|
);
|
74
74
|
return {
|
75
75
|
data: sanitizedResults,
|
76
|
-
meta: { pagination }
|
76
|
+
meta: { pagination: pagination2 }
|
77
77
|
};
|
78
78
|
},
|
79
79
|
async restoreVersion(ctx) {
|
80
80
|
const request = ctx.request;
|
81
81
|
await validateRestoreVersion(request.body, "contentType is required");
|
82
|
-
const permissionChecker2 = getService$
|
82
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
83
83
|
userAbility: ctx.state.userAbility,
|
84
84
|
model: request.body.contentType
|
85
85
|
});
|
86
86
|
if (permissionChecker2.cannot.update()) {
|
87
87
|
throw new errors.ForbiddenError();
|
88
88
|
}
|
89
|
-
const restoredDocument = await getService(strapi2, "history").restoreVersion(
|
89
|
+
const restoredDocument = await getService$1(strapi2, "history").restoreVersion(
|
90
90
|
request.params.versionId
|
91
91
|
);
|
92
92
|
return {
|
@@ -95,7 +95,7 @@ const createHistoryVersionController = ({ strapi: strapi2 }) => {
|
|
95
95
|
}
|
96
96
|
};
|
97
97
|
};
|
98
|
-
const controllers$
|
98
|
+
const controllers$2 = {
|
99
99
|
"history-version": createHistoryVersionController
|
100
100
|
/**
|
101
101
|
* Casting is needed because the types aren't aware that Strapi supports
|
@@ -112,43 +112,70 @@ const FIELDS_TO_IGNORE = [
|
|
112
112
|
"strapi_stage",
|
113
113
|
"strapi_assignee"
|
114
114
|
];
|
115
|
-
const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
|
116
|
-
const sanitizedContentTypeSchemaAttributes = omit(FIELDS_TO_IGNORE, contentTypeSchemaAttributes);
|
117
|
-
const reduceDifferenceToAttributesObject = (diffKeys, source) => {
|
118
|
-
return diffKeys.reduce((previousAttributesObject, diffKey) => {
|
119
|
-
previousAttributesObject[diffKey] = source[diffKey];
|
120
|
-
return previousAttributesObject;
|
121
|
-
}, {});
|
122
|
-
};
|
123
|
-
const versionSchemaKeys = Object.keys(versionSchemaAttributes);
|
124
|
-
const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
|
125
|
-
const uniqueToContentType = difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
|
126
|
-
const added = reduceDifferenceToAttributesObject(
|
127
|
-
uniqueToContentType,
|
128
|
-
sanitizedContentTypeSchemaAttributes
|
129
|
-
);
|
130
|
-
const uniqueToVersion = difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
|
131
|
-
const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
|
132
|
-
return { added, removed };
|
133
|
-
};
|
134
115
|
const DEFAULT_RETENTION_DAYS = 90;
|
135
|
-
const
|
136
|
-
const
|
137
|
-
|
138
|
-
|
116
|
+
const createServiceUtils = ({ strapi: strapi2 }) => {
|
117
|
+
const getSchemaAttributesDiff = (versionSchemaAttributes, contentTypeSchemaAttributes) => {
|
118
|
+
const sanitizedContentTypeSchemaAttributes = omit(
|
119
|
+
FIELDS_TO_IGNORE,
|
120
|
+
contentTypeSchemaAttributes
|
121
|
+
);
|
122
|
+
const reduceDifferenceToAttributesObject = (diffKeys, source) => {
|
123
|
+
return diffKeys.reduce(
|
124
|
+
(previousAttributesObject, diffKey) => {
|
125
|
+
previousAttributesObject[diffKey] = source[diffKey];
|
126
|
+
return previousAttributesObject;
|
127
|
+
},
|
128
|
+
{}
|
129
|
+
);
|
130
|
+
};
|
131
|
+
const versionSchemaKeys = Object.keys(versionSchemaAttributes);
|
132
|
+
const contentTypeSchemaAttributesKeys = Object.keys(sanitizedContentTypeSchemaAttributes);
|
133
|
+
const uniqueToContentType = difference(contentTypeSchemaAttributesKeys, versionSchemaKeys);
|
134
|
+
const added = reduceDifferenceToAttributesObject(
|
135
|
+
uniqueToContentType,
|
136
|
+
sanitizedContentTypeSchemaAttributes
|
137
|
+
);
|
138
|
+
const uniqueToVersion = difference(versionSchemaKeys, contentTypeSchemaAttributesKeys);
|
139
|
+
const removed = reduceDifferenceToAttributesObject(uniqueToVersion, versionSchemaAttributes);
|
140
|
+
return { added, removed };
|
139
141
|
};
|
140
|
-
const
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
142
|
+
const getRelationRestoreValue = async (versionRelationData, attribute) => {
|
143
|
+
if (Array.isArray(versionRelationData)) {
|
144
|
+
if (versionRelationData.length === 0)
|
145
|
+
return versionRelationData;
|
146
|
+
const existingAndMissingRelations = await Promise.all(
|
147
|
+
versionRelationData.map((relation) => {
|
148
|
+
return strapi2.documents(attribute.target).findOne({
|
149
|
+
documentId: relation.documentId,
|
150
|
+
locale: relation.locale || void 0
|
151
|
+
});
|
152
|
+
})
|
153
|
+
);
|
154
|
+
return existingAndMissingRelations.filter(
|
155
|
+
(relation) => relation !== null
|
156
|
+
);
|
147
157
|
}
|
148
|
-
return
|
158
|
+
return strapi2.documents(attribute.target).findOne({
|
159
|
+
documentId: versionRelationData.documentId,
|
160
|
+
locale: versionRelationData.locale || void 0
|
161
|
+
});
|
162
|
+
};
|
163
|
+
const getMediaRestoreValue = async (versionRelationData, attribute) => {
|
164
|
+
if (attribute.multiple) {
|
165
|
+
const existingAndMissingMedias = await Promise.all(
|
166
|
+
// @ts-expect-error Fix the type definitions so this isn't any
|
167
|
+
versionRelationData.map((media) => {
|
168
|
+
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
|
169
|
+
})
|
170
|
+
);
|
171
|
+
return existingAndMissingMedias.filter((media) => media != null);
|
172
|
+
}
|
173
|
+
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: versionRelationData.id } });
|
149
174
|
};
|
150
175
|
const localesService = strapi2.plugin("i18n")?.service("locales");
|
176
|
+
const i18nContentTypeService = strapi2.plugin("i18n")?.service("content-types");
|
151
177
|
const getDefaultLocale = async () => localesService ? localesService.getDefaultLocale() : null;
|
178
|
+
const isLocalizedContentType = (model) => i18nContentTypeService ? i18nContentTypeService.isLocalizedContentType(model) : false;
|
152
179
|
const getLocaleDictionary = async () => {
|
153
180
|
if (!localesService)
|
154
181
|
return {};
|
@@ -161,36 +188,67 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
161
188
|
{}
|
162
189
|
);
|
163
190
|
};
|
191
|
+
const getRetentionDays = () => {
|
192
|
+
const featureConfig = strapi2.ee.features.get("cms-content-history");
|
193
|
+
const licenseRetentionDays = typeof featureConfig === "object" && featureConfig?.options.retentionDays;
|
194
|
+
const userRetentionDays = strapi2.config.get("admin.history.retentionDays");
|
195
|
+
if (userRetentionDays && userRetentionDays < licenseRetentionDays) {
|
196
|
+
return userRetentionDays;
|
197
|
+
}
|
198
|
+
return Math.min(licenseRetentionDays, DEFAULT_RETENTION_DAYS);
|
199
|
+
};
|
164
200
|
const getVersionStatus = async (contentTypeUid, document) => {
|
165
201
|
const documentMetadataService = strapi2.plugin("content-manager").service("document-metadata");
|
166
202
|
const meta = await documentMetadataService.getMetadata(contentTypeUid, document);
|
167
203
|
return documentMetadataService.getStatus(document, meta.availableStatus);
|
168
204
|
};
|
169
|
-
const
|
205
|
+
const getComponentFields = (componentUID) => {
|
206
|
+
return Object.entries(strapi2.getModel(componentUID).attributes).reduce(
|
207
|
+
(fieldsAcc, [key, attribute]) => {
|
208
|
+
if (!["relation", "media", "component", "dynamiczone"].includes(attribute.type)) {
|
209
|
+
fieldsAcc.push(key);
|
210
|
+
}
|
211
|
+
return fieldsAcc;
|
212
|
+
},
|
213
|
+
[]
|
214
|
+
);
|
215
|
+
};
|
216
|
+
const getDeepPopulate2 = (uid2, useDatabaseSyntax = false) => {
|
170
217
|
const model = strapi2.getModel(uid2);
|
171
218
|
const attributes = Object.entries(model.attributes);
|
219
|
+
const fieldSelector = useDatabaseSyntax ? "select" : "fields";
|
172
220
|
return attributes.reduce((acc, [attributeName, attribute]) => {
|
173
221
|
switch (attribute.type) {
|
174
222
|
case "relation": {
|
223
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
224
|
+
if (isMorphRelation) {
|
225
|
+
break;
|
226
|
+
}
|
175
227
|
const isVisible2 = contentTypes$1.isVisibleAttribute(model, attributeName);
|
176
228
|
if (isVisible2) {
|
177
|
-
acc[attributeName] = {
|
229
|
+
acc[attributeName] = { [fieldSelector]: ["documentId", "locale", "publishedAt"] };
|
178
230
|
}
|
179
231
|
break;
|
180
232
|
}
|
181
233
|
case "media": {
|
182
|
-
acc[attributeName] = {
|
234
|
+
acc[attributeName] = { [fieldSelector]: ["id"] };
|
183
235
|
break;
|
184
236
|
}
|
185
237
|
case "component": {
|
186
238
|
const populate = getDeepPopulate2(attribute.component);
|
187
|
-
acc[attributeName] = {
|
239
|
+
acc[attributeName] = {
|
240
|
+
populate,
|
241
|
+
[fieldSelector]: getComponentFields(attribute.component)
|
242
|
+
};
|
188
243
|
break;
|
189
244
|
}
|
190
245
|
case "dynamiczone": {
|
191
246
|
const populatedComponents = (attribute.components || []).reduce(
|
192
247
|
(acc2, componentUID) => {
|
193
|
-
acc2[componentUID] = {
|
248
|
+
acc2[componentUID] = {
|
249
|
+
populate: getDeepPopulate2(componentUID),
|
250
|
+
[fieldSelector]: getComponentFields(componentUID)
|
251
|
+
};
|
194
252
|
return acc2;
|
195
253
|
},
|
196
254
|
{}
|
@@ -202,80 +260,69 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
202
260
|
return acc;
|
203
261
|
}, {});
|
204
262
|
};
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
if (!strapi2.requestContext.get()?.request.url.startsWith("/content-manager")) {
|
212
|
-
return next();
|
263
|
+
const buildMediaResponse = async (values) => {
|
264
|
+
return values.slice(0, 25).reduce(
|
265
|
+
async (currentRelationDataPromise, entry) => {
|
266
|
+
const currentRelationData = await currentRelationDataPromise;
|
267
|
+
if (!entry) {
|
268
|
+
return currentRelationData;
|
213
269
|
}
|
214
|
-
|
215
|
-
|
270
|
+
const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
|
271
|
+
if (relatedEntry) {
|
272
|
+
currentRelationData.results.push(relatedEntry);
|
273
|
+
} else {
|
274
|
+
currentRelationData.meta.missingCount += 1;
|
216
275
|
}
|
217
|
-
|
218
|
-
|
219
|
-
|
276
|
+
return currentRelationData;
|
277
|
+
},
|
278
|
+
Promise.resolve({
|
279
|
+
results: [],
|
280
|
+
meta: { missingCount: 0 }
|
281
|
+
})
|
282
|
+
);
|
283
|
+
};
|
284
|
+
const buildRelationReponse = async (values, attributeSchema) => {
|
285
|
+
return values.slice(0, 25).reduce(
|
286
|
+
async (currentRelationDataPromise, entry) => {
|
287
|
+
const currentRelationData = await currentRelationDataPromise;
|
288
|
+
if (!entry) {
|
289
|
+
return currentRelationData;
|
220
290
|
}
|
221
|
-
const
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
documentId: documentContext.documentId,
|
227
|
-
locale,
|
228
|
-
populate: getDeepPopulate2(contentTypeUid)
|
229
|
-
});
|
230
|
-
const status = await getVersionStatus(contentTypeUid, document);
|
231
|
-
const attributesSchema = strapi2.getModel(contentTypeUid).attributes;
|
232
|
-
const componentsSchemas = Object.keys(
|
233
|
-
attributesSchema
|
234
|
-
).reduce((currentComponentSchemas, key) => {
|
235
|
-
const fieldSchema = attributesSchema[key];
|
236
|
-
if (fieldSchema.type === "component") {
|
237
|
-
const componentSchema = strapi2.getModel(fieldSchema.component).attributes;
|
238
|
-
return {
|
239
|
-
...currentComponentSchemas,
|
240
|
-
[fieldSchema.component]: componentSchema
|
241
|
-
};
|
242
|
-
}
|
243
|
-
return currentComponentSchemas;
|
244
|
-
}, {});
|
245
|
-
await strapi2.db.transaction(async ({ onCommit }) => {
|
246
|
-
onCommit(() => {
|
247
|
-
this.createVersion({
|
248
|
-
contentType: contentTypeUid,
|
249
|
-
data: omit(FIELDS_TO_IGNORE, document),
|
250
|
-
schema: omit(FIELDS_TO_IGNORE, attributesSchema),
|
251
|
-
componentsSchemas,
|
252
|
-
relatedDocumentId: documentContext.documentId,
|
253
|
-
locale,
|
254
|
-
status
|
255
|
-
});
|
291
|
+
const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
|
292
|
+
if (relatedEntry) {
|
293
|
+
currentRelationData.results.push({
|
294
|
+
...relatedEntry,
|
295
|
+
status: await getVersionStatus(attributeSchema.target, relatedEntry)
|
256
296
|
});
|
257
|
-
}
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
297
|
+
} else {
|
298
|
+
currentRelationData.meta.missingCount += 1;
|
299
|
+
}
|
300
|
+
return currentRelationData;
|
301
|
+
},
|
302
|
+
Promise.resolve({
|
303
|
+
results: [],
|
304
|
+
meta: { missingCount: 0 }
|
305
|
+
})
|
306
|
+
);
|
307
|
+
};
|
308
|
+
return {
|
309
|
+
getSchemaAttributesDiff,
|
310
|
+
getRelationRestoreValue,
|
311
|
+
getMediaRestoreValue,
|
312
|
+
getDefaultLocale,
|
313
|
+
isLocalizedContentType,
|
314
|
+
getLocaleDictionary,
|
315
|
+
getRetentionDays,
|
316
|
+
getVersionStatus,
|
317
|
+
getDeepPopulate: getDeepPopulate2,
|
318
|
+
buildMediaResponse,
|
319
|
+
buildRelationReponse
|
320
|
+
};
|
321
|
+
};
|
322
|
+
const createHistoryService = ({ strapi: strapi2 }) => {
|
323
|
+
const query = strapi2.db.query(HISTORY_VERSION_UID);
|
324
|
+
const serviceUtils = createServiceUtils({ strapi: strapi2 });
|
325
|
+
return {
|
279
326
|
async createVersion(historyVersionData) {
|
280
327
|
await query.create({
|
281
328
|
data: {
|
@@ -286,8 +333,14 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
286
333
|
});
|
287
334
|
},
|
288
335
|
async findVersionsPage(params) {
|
289
|
-
const
|
290
|
-
const
|
336
|
+
const model = strapi2.getModel(params.query.contentType);
|
337
|
+
const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
|
338
|
+
const defaultLocale = await serviceUtils.getDefaultLocale();
|
339
|
+
let locale = null;
|
340
|
+
if (isLocalizedContentType) {
|
341
|
+
locale = params.query.locale || defaultLocale;
|
342
|
+
}
|
343
|
+
const [{ results, pagination: pagination2 }, localeDictionary] = await Promise.all([
|
291
344
|
query.findPage({
|
292
345
|
...params.query,
|
293
346
|
where: {
|
@@ -300,82 +353,43 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
300
353
|
populate: ["createdBy"],
|
301
354
|
orderBy: [{ createdAt: "desc" }]
|
302
355
|
}),
|
303
|
-
getLocaleDictionary()
|
356
|
+
serviceUtils.getLocaleDictionary()
|
304
357
|
]);
|
305
|
-
const buildRelationReponse = async (values, attributeSchema) => {
|
306
|
-
return values.slice(0, 25).reduce(
|
307
|
-
async (currentRelationDataPromise, entry) => {
|
308
|
-
const currentRelationData = await currentRelationDataPromise;
|
309
|
-
if (!entry) {
|
310
|
-
return currentRelationData;
|
311
|
-
}
|
312
|
-
const relatedEntry = await strapi2.documents(attributeSchema.target).findOne({ documentId: entry.documentId, locale: entry.locale || void 0 });
|
313
|
-
const permissionChecker2 = getService$1("permission-checker").create({
|
314
|
-
userAbility: params.state.userAbility,
|
315
|
-
model: attributeSchema.target
|
316
|
-
});
|
317
|
-
const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
|
318
|
-
if (sanitizedEntry) {
|
319
|
-
currentRelationData.results.push({
|
320
|
-
...sanitizedEntry,
|
321
|
-
status: await getVersionStatus(attributeSchema.target, sanitizedEntry)
|
322
|
-
});
|
323
|
-
} else {
|
324
|
-
currentRelationData.meta.missingCount += 1;
|
325
|
-
}
|
326
|
-
return currentRelationData;
|
327
|
-
},
|
328
|
-
Promise.resolve({
|
329
|
-
results: [],
|
330
|
-
meta: { missingCount: 0 }
|
331
|
-
})
|
332
|
-
);
|
333
|
-
};
|
334
|
-
const buildMediaResponse = async (values) => {
|
335
|
-
return values.slice(0, 25).reduce(
|
336
|
-
async (currentRelationDataPromise, entry) => {
|
337
|
-
const currentRelationData = await currentRelationDataPromise;
|
338
|
-
if (!entry) {
|
339
|
-
return currentRelationData;
|
340
|
-
}
|
341
|
-
const permissionChecker2 = getService$1("permission-checker").create({
|
342
|
-
userAbility: params.state.userAbility,
|
343
|
-
model: "plugin::upload.file"
|
344
|
-
});
|
345
|
-
const relatedEntry = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: entry.id } });
|
346
|
-
const sanitizedEntry = await permissionChecker2.sanitizeOutput(relatedEntry);
|
347
|
-
if (sanitizedEntry) {
|
348
|
-
currentRelationData.results.push(sanitizedEntry);
|
349
|
-
} else {
|
350
|
-
currentRelationData.meta.missingCount += 1;
|
351
|
-
}
|
352
|
-
return currentRelationData;
|
353
|
-
},
|
354
|
-
Promise.resolve({
|
355
|
-
results: [],
|
356
|
-
meta: { missingCount: 0 }
|
357
|
-
})
|
358
|
-
);
|
359
|
-
};
|
360
358
|
const populateEntryRelations = async (entry) => {
|
361
359
|
const entryWithRelations = await Object.entries(entry.schema).reduce(
|
362
360
|
async (currentDataWithRelations, [attributeKey, attributeSchema]) => {
|
363
361
|
const attributeValue = entry.data[attributeKey];
|
364
362
|
const attributeValues = Array.isArray(attributeValue) ? attributeValue : [attributeValue];
|
365
363
|
if (attributeSchema.type === "media") {
|
364
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
365
|
+
userAbility: params.state.userAbility,
|
366
|
+
model: "plugin::upload.file"
|
367
|
+
});
|
368
|
+
const response = await serviceUtils.buildMediaResponse(attributeValues);
|
369
|
+
const sanitizedResults = await Promise.all(
|
370
|
+
response.results.map((media) => permissionChecker2.sanitizeOutput(media))
|
371
|
+
);
|
366
372
|
return {
|
367
373
|
...await currentDataWithRelations,
|
368
|
-
[attributeKey]:
|
374
|
+
[attributeKey]: {
|
375
|
+
results: sanitizedResults,
|
376
|
+
meta: response.meta
|
377
|
+
}
|
369
378
|
};
|
370
379
|
}
|
371
380
|
if (attributeSchema.type === "relation" && attributeSchema.relation !== "morphToOne" && attributeSchema.relation !== "morphToMany") {
|
372
381
|
if (attributeSchema.target === "admin::user") {
|
373
382
|
const adminUsers = await Promise.all(
|
374
|
-
attributeValues.map(
|
383
|
+
attributeValues.map((userToPopulate) => {
|
375
384
|
if (userToPopulate == null) {
|
376
385
|
return null;
|
377
386
|
}
|
378
|
-
return strapi2.query("admin::user").findOne({
|
387
|
+
return strapi2.query("admin::user").findOne({
|
388
|
+
where: {
|
389
|
+
...userToPopulate.id ? { id: userToPopulate.id } : {},
|
390
|
+
...userToPopulate.documentId ? { documentId: userToPopulate.documentId } : {}
|
391
|
+
}
|
392
|
+
});
|
379
393
|
})
|
380
394
|
);
|
381
395
|
return {
|
@@ -388,9 +402,23 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
388
402
|
[attributeKey]: adminUsers
|
389
403
|
};
|
390
404
|
}
|
405
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
406
|
+
userAbility: params.state.userAbility,
|
407
|
+
model: attributeSchema.target
|
408
|
+
});
|
409
|
+
const response = await serviceUtils.buildRelationReponse(
|
410
|
+
attributeValues,
|
411
|
+
attributeSchema
|
412
|
+
);
|
413
|
+
const sanitizedResults = await Promise.all(
|
414
|
+
response.results.map((media) => permissionChecker2.sanitizeOutput(media))
|
415
|
+
);
|
391
416
|
return {
|
392
417
|
...await currentDataWithRelations,
|
393
|
-
[attributeKey]:
|
418
|
+
[attributeKey]: {
|
419
|
+
results: sanitizedResults,
|
420
|
+
meta: response.meta
|
421
|
+
}
|
394
422
|
};
|
395
423
|
}
|
396
424
|
return currentDataWithRelations;
|
@@ -405,7 +433,7 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
405
433
|
...result,
|
406
434
|
data: await populateEntryRelations(result),
|
407
435
|
meta: {
|
408
|
-
unknownAttributes: getSchemaAttributesDiff(
|
436
|
+
unknownAttributes: serviceUtils.getSchemaAttributesDiff(
|
409
437
|
result.schema,
|
410
438
|
strapi2.getModel(params.query.contentType).attributes
|
411
439
|
)
|
@@ -416,13 +444,16 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
416
444
|
);
|
417
445
|
return {
|
418
446
|
results: formattedResults,
|
419
|
-
pagination
|
447
|
+
pagination: pagination2
|
420
448
|
};
|
421
449
|
},
|
422
450
|
async restoreVersion(versionId) {
|
423
451
|
const version = await query.findOne({ where: { id: versionId } });
|
424
452
|
const contentTypeSchemaAttributes = strapi2.getModel(version.contentType).attributes;
|
425
|
-
const schemaDiff = getSchemaAttributesDiff(
|
453
|
+
const schemaDiff = serviceUtils.getSchemaAttributesDiff(
|
454
|
+
version.schema,
|
455
|
+
contentTypeSchemaAttributes
|
456
|
+
);
|
426
457
|
const dataWithoutAddedAttributes = Object.keys(schemaDiff.added).reduce(
|
427
458
|
(currentData, addedKey) => {
|
428
459
|
currentData[addedKey] = null;
|
@@ -435,61 +466,26 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
435
466
|
FIELDS_TO_IGNORE,
|
436
467
|
contentTypeSchemaAttributes
|
437
468
|
);
|
438
|
-
const
|
439
|
-
|
440
|
-
|
441
|
-
const
|
442
|
-
if (
|
469
|
+
const reducer = async.reduce(Object.entries(sanitizedSchemaAttributes));
|
470
|
+
const dataWithoutMissingRelations = await reducer(
|
471
|
+
async (previousRelationAttributes, [name, attribute]) => {
|
472
|
+
const versionRelationData = version.data[name];
|
473
|
+
if (!versionRelationData) {
|
443
474
|
return previousRelationAttributes;
|
444
475
|
}
|
445
476
|
if (attribute.type === "relation" && // TODO: handle polymorphic relations
|
446
477
|
attribute.relation !== "morphToOne" && attribute.relation !== "morphToMany") {
|
447
|
-
|
448
|
-
|
449
|
-
return previousRelationAttributes;
|
450
|
-
const existingAndMissingRelations = await Promise.all(
|
451
|
-
relationData.map((relation) => {
|
452
|
-
return strapi2.documents(attribute.target).findOne({
|
453
|
-
documentId: relation.documentId,
|
454
|
-
locale: relation.locale || void 0
|
455
|
-
});
|
456
|
-
})
|
457
|
-
);
|
458
|
-
const existingRelations = existingAndMissingRelations.filter(
|
459
|
-
(relation) => relation !== null
|
460
|
-
);
|
461
|
-
previousRelationAttributes[name] = existingRelations;
|
462
|
-
} else {
|
463
|
-
const existingRelation = await strapi2.documents(attribute.target).findOne({
|
464
|
-
documentId: relationData.documentId,
|
465
|
-
locale: relationData.locale || void 0
|
466
|
-
});
|
467
|
-
if (!existingRelation) {
|
468
|
-
previousRelationAttributes[name] = null;
|
469
|
-
}
|
470
|
-
}
|
478
|
+
const data2 = await serviceUtils.getRelationRestoreValue(versionRelationData, attribute);
|
479
|
+
previousRelationAttributes[name] = data2;
|
471
480
|
}
|
472
481
|
if (attribute.type === "media") {
|
473
|
-
|
474
|
-
|
475
|
-
// @ts-expect-error Fix the type definitions so this isn't any
|
476
|
-
relationData.map((media) => {
|
477
|
-
return strapi2.db.query("plugin::upload.file").findOne({ where: { id: media.id } });
|
478
|
-
})
|
479
|
-
);
|
480
|
-
const existingMedias = existingAndMissingMedias.filter((media) => media != null);
|
481
|
-
previousRelationAttributes[name] = existingMedias;
|
482
|
-
} else {
|
483
|
-
const existingMedia = await strapi2.db.query("plugin::upload.file").findOne({ where: { id: version.data[name].id } });
|
484
|
-
if (!existingMedia) {
|
485
|
-
previousRelationAttributes[name] = null;
|
486
|
-
}
|
487
|
-
}
|
482
|
+
const data2 = await serviceUtils.getMediaRestoreValue(versionRelationData, attribute);
|
483
|
+
previousRelationAttributes[name] = data2;
|
488
484
|
}
|
489
485
|
return previousRelationAttributes;
|
490
486
|
},
|
491
487
|
// Clone to avoid mutating the original version data
|
492
|
-
|
488
|
+
structuredClone(dataWithoutAddedAttributes)
|
493
489
|
);
|
494
490
|
const data = omit(["id", ...Object.keys(schemaDiff.removed)], dataWithoutMissingRelations);
|
495
491
|
const restoredDocument = await strapi2.documents(version.contentType).update({
|
@@ -504,16 +500,132 @@ const createHistoryService = ({ strapi: strapi2 }) => {
|
|
504
500
|
}
|
505
501
|
};
|
506
502
|
};
|
507
|
-
const
|
508
|
-
|
503
|
+
const shouldCreateHistoryVersion = (context) => {
|
504
|
+
if (!strapi.requestContext.get()?.request.url.startsWith("/content-manager")) {
|
505
|
+
return false;
|
506
|
+
}
|
507
|
+
if (context.action !== "create" && context.action !== "update" && context.action !== "clone" && context.action !== "publish" && context.action !== "unpublish" && context.action !== "discardDraft") {
|
508
|
+
return false;
|
509
|
+
}
|
510
|
+
if (context.action === "update" && strapi.requestContext.get()?.request.url.endsWith("/actions/publish")) {
|
511
|
+
return false;
|
512
|
+
}
|
513
|
+
if (!context.contentType.uid.startsWith("api::")) {
|
514
|
+
return false;
|
515
|
+
}
|
516
|
+
return true;
|
509
517
|
};
|
510
|
-
const
|
518
|
+
const getSchemas = (uid2) => {
|
519
|
+
const attributesSchema = strapi.getModel(uid2).attributes;
|
520
|
+
const componentsSchemas = Object.keys(attributesSchema).reduce(
|
521
|
+
(currentComponentSchemas, key) => {
|
522
|
+
const fieldSchema = attributesSchema[key];
|
523
|
+
if (fieldSchema.type === "component") {
|
524
|
+
const componentSchema = strapi.getModel(fieldSchema.component).attributes;
|
525
|
+
return {
|
526
|
+
...currentComponentSchemas,
|
527
|
+
[fieldSchema.component]: componentSchema
|
528
|
+
};
|
529
|
+
}
|
530
|
+
return currentComponentSchemas;
|
531
|
+
},
|
532
|
+
{}
|
533
|
+
);
|
534
|
+
return {
|
535
|
+
schema: omit(FIELDS_TO_IGNORE, attributesSchema),
|
536
|
+
componentsSchemas
|
537
|
+
};
|
538
|
+
};
|
539
|
+
const createLifecyclesService = ({ strapi: strapi2 }) => {
|
540
|
+
const state = {
|
541
|
+
deleteExpiredJob: null,
|
542
|
+
isInitialized: false
|
543
|
+
};
|
544
|
+
const serviceUtils = createServiceUtils({ strapi: strapi2 });
|
545
|
+
return {
|
546
|
+
async bootstrap() {
|
547
|
+
if (state.isInitialized) {
|
548
|
+
return;
|
549
|
+
}
|
550
|
+
strapi2.documents.use(async (context, next) => {
|
551
|
+
const result = await next();
|
552
|
+
if (!shouldCreateHistoryVersion(context)) {
|
553
|
+
return result;
|
554
|
+
}
|
555
|
+
const documentId = context.action === "create" || context.action === "clone" ? result.documentId : context.params.documentId;
|
556
|
+
const defaultLocale = await serviceUtils.getDefaultLocale();
|
557
|
+
const locales = castArray(context.params?.locale || defaultLocale);
|
558
|
+
if (!locales.length) {
|
559
|
+
return result;
|
560
|
+
}
|
561
|
+
const uid2 = context.contentType.uid;
|
562
|
+
const schemas = getSchemas(uid2);
|
563
|
+
const model = strapi2.getModel(uid2);
|
564
|
+
const isLocalizedContentType = serviceUtils.isLocalizedContentType(model);
|
565
|
+
const localeEntries = await strapi2.db.query(uid2).findMany({
|
566
|
+
where: {
|
567
|
+
documentId,
|
568
|
+
...isLocalizedContentType ? { locale: { $in: locales } } : {},
|
569
|
+
...contentTypes$1.hasDraftAndPublish(strapi2.contentTypes[uid2]) ? { publishedAt: null } : {}
|
570
|
+
},
|
571
|
+
populate: serviceUtils.getDeepPopulate(
|
572
|
+
uid2,
|
573
|
+
true
|
574
|
+
/* use database syntax */
|
575
|
+
)
|
576
|
+
});
|
577
|
+
await strapi2.db.transaction(async ({ onCommit }) => {
|
578
|
+
onCommit(async () => {
|
579
|
+
for (const entry of localeEntries) {
|
580
|
+
const status = await serviceUtils.getVersionStatus(uid2, entry);
|
581
|
+
await getService$1(strapi2, "history").createVersion({
|
582
|
+
contentType: uid2,
|
583
|
+
data: omit(FIELDS_TO_IGNORE, entry),
|
584
|
+
relatedDocumentId: documentId,
|
585
|
+
locale: entry.locale,
|
586
|
+
status,
|
587
|
+
...schemas
|
588
|
+
});
|
589
|
+
}
|
590
|
+
});
|
591
|
+
});
|
592
|
+
return result;
|
593
|
+
});
|
594
|
+
state.deleteExpiredJob = scheduleJob("historyDaily", "0 0 * * *", () => {
|
595
|
+
const retentionDaysInMilliseconds = serviceUtils.getRetentionDays() * 24 * 60 * 60 * 1e3;
|
596
|
+
const expirationDate = new Date(Date.now() - retentionDaysInMilliseconds);
|
597
|
+
strapi2.db.query(HISTORY_VERSION_UID).deleteMany({
|
598
|
+
where: {
|
599
|
+
created_at: {
|
600
|
+
$lt: expirationDate
|
601
|
+
}
|
602
|
+
}
|
603
|
+
}).catch((error) => {
|
604
|
+
if (error instanceof Error) {
|
605
|
+
strapi2.log.error("Error deleting expired history versions", error.message);
|
606
|
+
}
|
607
|
+
});
|
608
|
+
});
|
609
|
+
state.isInitialized = true;
|
610
|
+
},
|
611
|
+
async destroy() {
|
612
|
+
if (state.deleteExpiredJob) {
|
613
|
+
state.deleteExpiredJob.cancel();
|
614
|
+
}
|
615
|
+
}
|
616
|
+
};
|
617
|
+
};
|
618
|
+
const services$2 = {
|
619
|
+
history: createHistoryService,
|
620
|
+
lifecycles: createLifecyclesService
|
621
|
+
};
|
622
|
+
const info$1 = { pluginName: "content-manager", type: "admin" };
|
511
623
|
const historyVersionRouter = {
|
512
624
|
type: "admin",
|
513
625
|
routes: [
|
514
626
|
{
|
515
627
|
method: "GET",
|
516
|
-
info,
|
628
|
+
info: info$1,
|
517
629
|
path: "/history-versions",
|
518
630
|
handler: "history-version.findMany",
|
519
631
|
config: {
|
@@ -522,7 +634,7 @@ const historyVersionRouter = {
|
|
522
634
|
},
|
523
635
|
{
|
524
636
|
method: "PUT",
|
525
|
-
info,
|
637
|
+
info: info$1,
|
526
638
|
path: "/history-versions/:versionId/restore",
|
527
639
|
handler: "history-version.restoreVersion",
|
528
640
|
config: {
|
@@ -531,7 +643,7 @@ const historyVersionRouter = {
|
|
531
643
|
}
|
532
644
|
]
|
533
645
|
};
|
534
|
-
const routes$
|
646
|
+
const routes$2 = {
|
535
647
|
"history-version": historyVersionRouter
|
536
648
|
};
|
537
649
|
const historyVersion = {
|
@@ -578,21 +690,21 @@ const historyVersion = {
|
|
578
690
|
}
|
579
691
|
}
|
580
692
|
};
|
581
|
-
const getFeature = () => {
|
693
|
+
const getFeature$1 = () => {
|
582
694
|
if (strapi.ee.features.isEnabled("cms-content-history")) {
|
583
695
|
return {
|
584
696
|
register({ strapi: strapi2 }) {
|
585
697
|
strapi2.get("models").add(historyVersion);
|
586
698
|
},
|
587
699
|
bootstrap({ strapi: strapi2 }) {
|
588
|
-
getService(strapi2, "
|
700
|
+
getService$1(strapi2, "lifecycles").bootstrap();
|
589
701
|
},
|
590
702
|
destroy({ strapi: strapi2 }) {
|
591
|
-
getService(strapi2, "
|
703
|
+
getService$1(strapi2, "lifecycles").destroy();
|
592
704
|
},
|
593
|
-
controllers: controllers$
|
594
|
-
services: services$
|
595
|
-
routes: routes$
|
705
|
+
controllers: controllers$2,
|
706
|
+
services: services$2,
|
707
|
+
routes: routes$2
|
596
708
|
};
|
597
709
|
}
|
598
710
|
return {
|
@@ -601,7 +713,7 @@ const getFeature = () => {
|
|
601
713
|
}
|
602
714
|
};
|
603
715
|
};
|
604
|
-
const history = getFeature();
|
716
|
+
const history = getFeature$1();
|
605
717
|
const register = async ({ strapi: strapi2 }) => {
|
606
718
|
await history.register?.({ strapi: strapi2 });
|
607
719
|
};
|
@@ -609,15 +721,165 @@ const ALLOWED_WEBHOOK_EVENTS = {
|
|
609
721
|
ENTRY_PUBLISH: "entry.publish",
|
610
722
|
ENTRY_UNPUBLISH: "entry.unpublish"
|
611
723
|
};
|
724
|
+
const FEATURE_ID = "preview";
|
725
|
+
const info = { pluginName: "content-manager", type: "admin" };
|
726
|
+
const previewRouter = {
|
727
|
+
type: "admin",
|
728
|
+
routes: [
|
729
|
+
{
|
730
|
+
method: "GET",
|
731
|
+
info,
|
732
|
+
path: "/preview/url/:contentType",
|
733
|
+
handler: "preview.getPreviewUrl",
|
734
|
+
config: {
|
735
|
+
policies: ["admin::isAuthenticatedAdmin"]
|
736
|
+
}
|
737
|
+
}
|
738
|
+
]
|
739
|
+
};
|
740
|
+
const routes$1 = {
|
741
|
+
preview: previewRouter
|
742
|
+
};
|
743
|
+
function getService(strapi2, name) {
|
744
|
+
return strapi2.service(`plugin::content-manager.${name}`);
|
745
|
+
}
|
746
|
+
const getPreviewUrlSchema = yup.object().shape({
|
747
|
+
// Will be undefined for single types
|
748
|
+
documentId: yup.string(),
|
749
|
+
locale: yup.string().nullable(),
|
750
|
+
status: yup.string()
|
751
|
+
}).required();
|
752
|
+
const validatePreviewUrl = async (strapi2, uid2, params) => {
|
753
|
+
await validateYupSchema(getPreviewUrlSchema)(params);
|
754
|
+
const newParams = pick(["documentId", "locale", "status"], params);
|
755
|
+
const model = strapi2.getModel(uid2);
|
756
|
+
if (!model || model.modelType !== "contentType") {
|
757
|
+
throw new errors.ValidationError("Invalid content type");
|
758
|
+
}
|
759
|
+
const isSingleType = model?.kind === "singleType";
|
760
|
+
if (!isSingleType && !params.documentId) {
|
761
|
+
throw new errors.ValidationError("documentId is required for Collection Types");
|
762
|
+
}
|
763
|
+
if (isSingleType) {
|
764
|
+
const doc = await strapi2.documents(uid2).findFirst();
|
765
|
+
if (!doc) {
|
766
|
+
throw new errors.NotFoundError("Document not found");
|
767
|
+
}
|
768
|
+
newParams.documentId = doc?.documentId;
|
769
|
+
}
|
770
|
+
return newParams;
|
771
|
+
};
|
772
|
+
const createPreviewController = () => {
|
773
|
+
return {
|
774
|
+
/**
|
775
|
+
* Transforms an entry into a preview URL, so that it can be previewed
|
776
|
+
* in the Content Manager.
|
777
|
+
*/
|
778
|
+
async getPreviewUrl(ctx) {
|
779
|
+
const uid2 = ctx.params.contentType;
|
780
|
+
const query = ctx.request.query;
|
781
|
+
const params = await validatePreviewUrl(strapi, uid2, query);
|
782
|
+
const previewService = getService(strapi, "preview");
|
783
|
+
const url = await previewService.getPreviewUrl(uid2, params);
|
784
|
+
if (!url) {
|
785
|
+
ctx.status = 204;
|
786
|
+
}
|
787
|
+
return {
|
788
|
+
data: { url }
|
789
|
+
};
|
790
|
+
}
|
791
|
+
};
|
792
|
+
};
|
793
|
+
const controllers$1 = {
|
794
|
+
preview: createPreviewController
|
795
|
+
/**
|
796
|
+
* Casting is needed because the types aren't aware that Strapi supports
|
797
|
+
* passing a controller factory as the value, instead of a controller object directly
|
798
|
+
*/
|
799
|
+
};
|
800
|
+
const createPreviewService = ({ strapi: strapi2 }) => {
|
801
|
+
const config = getService(strapi2, "preview-config");
|
802
|
+
return {
|
803
|
+
async getPreviewUrl(uid2, params) {
|
804
|
+
const handler = config.getPreviewHandler();
|
805
|
+
try {
|
806
|
+
return handler(uid2, params);
|
807
|
+
} catch (error) {
|
808
|
+
strapi2.log.error(`Failed to get preview URL: ${error}`);
|
809
|
+
throw new errors.ApplicationError("Failed to get preview URL");
|
810
|
+
}
|
811
|
+
return;
|
812
|
+
}
|
813
|
+
};
|
814
|
+
};
|
815
|
+
const createPreviewConfigService = ({ strapi: strapi2 }) => {
|
816
|
+
return {
|
817
|
+
isEnabled() {
|
818
|
+
const config = strapi2.config.get("admin.preview");
|
819
|
+
if (!config) {
|
820
|
+
return false;
|
821
|
+
}
|
822
|
+
return config?.enabled ?? true;
|
823
|
+
},
|
824
|
+
/**
|
825
|
+
* Validate if the configuration is valid
|
826
|
+
*/
|
827
|
+
validate() {
|
828
|
+
if (!this.isEnabled()) {
|
829
|
+
return;
|
830
|
+
}
|
831
|
+
const handler = this.getPreviewHandler();
|
832
|
+
if (typeof handler !== "function") {
|
833
|
+
throw new errors.ValidationError(
|
834
|
+
"Preview configuration is invalid. Handler must be a function"
|
835
|
+
);
|
836
|
+
}
|
837
|
+
},
|
838
|
+
/**
|
839
|
+
* Utility to get the preview handler from the configuration
|
840
|
+
*/
|
841
|
+
getPreviewHandler() {
|
842
|
+
const config = strapi2.config.get("admin.preview");
|
843
|
+
const emptyHandler = () => {
|
844
|
+
return void 0;
|
845
|
+
};
|
846
|
+
if (!this.isEnabled()) {
|
847
|
+
return emptyHandler;
|
848
|
+
}
|
849
|
+
return config?.config?.handler || emptyHandler;
|
850
|
+
}
|
851
|
+
};
|
852
|
+
};
|
853
|
+
const services$1 = {
|
854
|
+
preview: createPreviewService,
|
855
|
+
"preview-config": createPreviewConfigService
|
856
|
+
};
|
857
|
+
const getFeature = () => {
|
858
|
+
if (!strapi.features.future.isEnabled(FEATURE_ID)) {
|
859
|
+
return {};
|
860
|
+
}
|
861
|
+
return {
|
862
|
+
bootstrap() {
|
863
|
+
console.log("Bootstrapping preview server");
|
864
|
+
const config = getService(strapi, "preview-config");
|
865
|
+
config.validate();
|
866
|
+
},
|
867
|
+
routes: routes$1,
|
868
|
+
controllers: controllers$1,
|
869
|
+
services: services$1
|
870
|
+
};
|
871
|
+
};
|
872
|
+
const preview = getFeature();
|
612
873
|
const bootstrap = async () => {
|
613
874
|
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
614
875
|
strapi.get("webhookStore").addAllowedEvent(key, value);
|
615
876
|
});
|
616
|
-
getService$
|
617
|
-
await getService$
|
618
|
-
await getService$
|
619
|
-
await getService$
|
877
|
+
getService$2("field-sizes").setCustomFieldInputSizes();
|
878
|
+
await getService$2("components").syncConfigurations();
|
879
|
+
await getService$2("content-types").syncConfigurations();
|
880
|
+
await getService$2("permission").registerPermissions();
|
620
881
|
await history.bootstrap?.({ strapi });
|
882
|
+
await preview.bootstrap?.({ strapi });
|
621
883
|
};
|
622
884
|
const destroy = async ({ strapi: strapi2 }) => {
|
623
885
|
await history.destroy?.({ strapi: strapi2 });
|
@@ -1107,7 +1369,8 @@ const admin = {
|
|
1107
1369
|
};
|
1108
1370
|
const routes = {
|
1109
1371
|
admin,
|
1110
|
-
...history.routes ? history.routes : {}
|
1372
|
+
...history.routes ? history.routes : {},
|
1373
|
+
...preview.routes ? preview.routes : {}
|
1111
1374
|
};
|
1112
1375
|
const hasPermissionsSchema = yup$1.object({
|
1113
1376
|
actions: yup$1.array().of(yup$1.string()),
|
@@ -1118,6 +1381,11 @@ const { createPolicy } = policy;
|
|
1118
1381
|
const hasPermissions = createPolicy({
|
1119
1382
|
name: "plugin::content-manager.hasPermissions",
|
1120
1383
|
validator: validateHasPermissionsInput,
|
1384
|
+
/**
|
1385
|
+
* NOTE: Action aliases are currently not checked at this level (policy).
|
1386
|
+
* This is currently the intended behavior to avoid changing the behavior of API related permissions.
|
1387
|
+
* If you want to add support for it, please create a dedicated RFC with a list of potential side effect this could have.
|
1388
|
+
*/
|
1121
1389
|
handler(ctx, config = {}) {
|
1122
1390
|
const { actions = [], hasAtLeastOne = false } = config;
|
1123
1391
|
const { userAbility } = ctx.state;
|
@@ -1359,7 +1627,7 @@ const createMetadasSchema = (schema) => {
|
|
1359
1627
|
if (!value) {
|
1360
1628
|
return yup$1.string();
|
1361
1629
|
}
|
1362
|
-
const targetSchema = getService$
|
1630
|
+
const targetSchema = getService$2("content-types").findContentType(
|
1363
1631
|
schema.attributes[key].targetModel
|
1364
1632
|
);
|
1365
1633
|
if (!targetSchema) {
|
@@ -1407,7 +1675,7 @@ const { PaginationError, ValidationError } = errors;
|
|
1407
1675
|
const TYPES = ["singleType", "collectionType"];
|
1408
1676
|
const kindSchema = yup$1.string().oneOf(TYPES).nullable();
|
1409
1677
|
const bulkActionInputSchema = yup$1.object({
|
1410
|
-
|
1678
|
+
documentIds: yup$1.array().of(yup$1.strapiID()).min(1).required()
|
1411
1679
|
}).required();
|
1412
1680
|
const generateUIDInputSchema = yup$1.object({
|
1413
1681
|
contentTypeUID: yup$1.string().required(),
|
@@ -1506,22 +1774,56 @@ const excludeNotCreatableFields = (uid2, permissionChecker2) => (body, path = []
|
|
1506
1774
|
}
|
1507
1775
|
}, body);
|
1508
1776
|
};
|
1509
|
-
const
|
1510
|
-
|
1511
|
-
|
1512
|
-
|
1513
|
-
|
1514
|
-
|
1515
|
-
|
1777
|
+
const singleLocaleSchema = yup$1.string().nullable();
|
1778
|
+
const multipleLocaleSchema = yup$1.lazy(
|
1779
|
+
(value) => Array.isArray(value) ? yup$1.array().of(singleLocaleSchema.required()) : singleLocaleSchema
|
1780
|
+
);
|
1781
|
+
const statusSchema = yup$1.mixed().oneOf(["draft", "published"], "Invalid status");
|
1782
|
+
const getDocumentLocaleAndStatus = async (request, model, opts = { allowMultipleLocales: false }) => {
|
1783
|
+
const { allowMultipleLocales } = opts;
|
1784
|
+
const { locale, status: providedStatus, ...rest } = request || {};
|
1785
|
+
const defaultStatus = contentTypes$1.hasDraftAndPublish(strapi.getModel(model)) ? void 0 : "published";
|
1786
|
+
const status = providedStatus !== void 0 ? providedStatus : defaultStatus;
|
1787
|
+
const schema = yup$1.object().shape({
|
1788
|
+
locale: allowMultipleLocales ? multipleLocaleSchema : singleLocaleSchema,
|
1789
|
+
status: statusSchema
|
1790
|
+
});
|
1791
|
+
try {
|
1792
|
+
await validateYupSchema(schema, { strict: true, abortEarly: false })(request);
|
1793
|
+
return { locale, status, ...rest };
|
1794
|
+
} catch (error) {
|
1795
|
+
throw new errors.ValidationError(`Validation error: ${error.message}`);
|
1516
1796
|
}
|
1517
|
-
|
1797
|
+
};
|
1798
|
+
const formatDocumentWithMetadata = async (permissionChecker2, uid2, document, opts = {}) => {
|
1799
|
+
const documentMetadata2 = getService$2("document-metadata");
|
1800
|
+
const serviceOutput = await documentMetadata2.formatDocumentWithMetadata(uid2, document, opts);
|
1801
|
+
let {
|
1802
|
+
meta: { availableLocales, availableStatus }
|
1803
|
+
} = serviceOutput;
|
1804
|
+
const metadataSanitizer = permissionChecker2.sanitizeOutput;
|
1805
|
+
availableLocales = await async.map(
|
1806
|
+
availableLocales,
|
1807
|
+
async (localeDocument) => metadataSanitizer(localeDocument)
|
1808
|
+
);
|
1809
|
+
availableStatus = await async.map(
|
1810
|
+
availableStatus,
|
1811
|
+
async (statusDocument) => metadataSanitizer(statusDocument)
|
1812
|
+
);
|
1813
|
+
return {
|
1814
|
+
...serviceOutput,
|
1815
|
+
meta: {
|
1816
|
+
availableLocales,
|
1817
|
+
availableStatus
|
1818
|
+
}
|
1819
|
+
};
|
1518
1820
|
};
|
1519
1821
|
const createDocument = async (ctx, opts) => {
|
1520
1822
|
const { userAbility, user } = ctx.state;
|
1521
1823
|
const { model } = ctx.params;
|
1522
1824
|
const { body } = ctx.request;
|
1523
|
-
const documentManager2 = getService$
|
1524
|
-
const permissionChecker2 = getService$
|
1825
|
+
const documentManager2 = getService$2("document-manager");
|
1826
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1525
1827
|
if (permissionChecker2.cannot.create()) {
|
1526
1828
|
throw new errors.ForbiddenError();
|
1527
1829
|
}
|
@@ -1529,7 +1831,7 @@ const createDocument = async (ctx, opts) => {
|
|
1529
1831
|
const setCreator = setCreatorFields({ user });
|
1530
1832
|
const sanitizeFn = async.pipe(pickPermittedFields, setCreator);
|
1531
1833
|
const sanitizedBody = await sanitizeFn(body);
|
1532
|
-
const { locale, status
|
1834
|
+
const { locale, status } = await getDocumentLocaleAndStatus(body, model);
|
1533
1835
|
return documentManager2.create(model, {
|
1534
1836
|
data: sanitizedBody,
|
1535
1837
|
locale,
|
@@ -1541,14 +1843,14 @@ const updateDocument = async (ctx, opts) => {
|
|
1541
1843
|
const { userAbility, user } = ctx.state;
|
1542
1844
|
const { id, model } = ctx.params;
|
1543
1845
|
const { body } = ctx.request;
|
1544
|
-
const documentManager2 = getService$
|
1545
|
-
const permissionChecker2 = getService$
|
1846
|
+
const documentManager2 = getService$2("document-manager");
|
1847
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1546
1848
|
if (permissionChecker2.cannot.update()) {
|
1547
1849
|
throw new errors.ForbiddenError();
|
1548
1850
|
}
|
1549
1851
|
const permissionQuery = await permissionChecker2.sanitizedQuery.update(ctx.query);
|
1550
|
-
const populate = await getService$
|
1551
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
1852
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1853
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1552
1854
|
const [documentVersion, documentExists] = await Promise.all([
|
1553
1855
|
documentManager2.findOne(id, model, { populate, locale, status: "draft" }),
|
1554
1856
|
documentManager2.exists(model, id)
|
@@ -1564,7 +1866,7 @@ const updateDocument = async (ctx, opts) => {
|
|
1564
1866
|
throw new errors.ForbiddenError();
|
1565
1867
|
}
|
1566
1868
|
const pickPermittedFields = documentVersion ? permissionChecker2.sanitizeUpdateInput(documentVersion) : permissionChecker2.sanitizeCreateInput;
|
1567
|
-
const setCreator = setCreatorFields({ user, isEdition: true });
|
1869
|
+
const setCreator = documentVersion ? setCreatorFields({ user, isEdition: true }) : setCreatorFields({ user });
|
1568
1870
|
const sanitizeFn = async.pipe(pickPermittedFields, setCreator);
|
1569
1871
|
const sanitizedBody = await sanitizeFn(body);
|
1570
1872
|
return documentManager2.update(documentVersion?.documentId || id, model, {
|
@@ -1578,16 +1880,16 @@ const collectionTypes = {
|
|
1578
1880
|
const { userAbility } = ctx.state;
|
1579
1881
|
const { model } = ctx.params;
|
1580
1882
|
const { query } = ctx.request;
|
1581
|
-
const documentMetadata2 = getService$
|
1582
|
-
const documentManager2 = getService$
|
1583
|
-
const permissionChecker2 = getService$
|
1883
|
+
const documentMetadata2 = getService$2("document-metadata");
|
1884
|
+
const documentManager2 = getService$2("document-manager");
|
1885
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1584
1886
|
if (permissionChecker2.cannot.read()) {
|
1585
1887
|
return ctx.forbidden();
|
1586
1888
|
}
|
1587
1889
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
|
1588
|
-
const populate = await getService$
|
1589
|
-
const { locale, status } = getDocumentLocaleAndStatus(query);
|
1590
|
-
const { results: documents, pagination } = await documentManager2.findPage(
|
1890
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(1).countRelations({ toOne: false, toMany: true }).build();
|
1891
|
+
const { locale, status } = await getDocumentLocaleAndStatus(query, model);
|
1892
|
+
const { results: documents, pagination: pagination2 } = await documentManager2.findPage(
|
1591
1893
|
{ ...permissionQuery, populate, locale, status },
|
1592
1894
|
model
|
1593
1895
|
);
|
@@ -1608,21 +1910,20 @@ const collectionTypes = {
|
|
1608
1910
|
);
|
1609
1911
|
ctx.body = {
|
1610
1912
|
results,
|
1611
|
-
pagination
|
1913
|
+
pagination: pagination2
|
1612
1914
|
};
|
1613
1915
|
},
|
1614
1916
|
async findOne(ctx) {
|
1615
1917
|
const { userAbility } = ctx.state;
|
1616
1918
|
const { model, id } = ctx.params;
|
1617
|
-
const documentManager2 = getService$
|
1618
|
-
const
|
1619
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1919
|
+
const documentManager2 = getService$2("document-manager");
|
1920
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1620
1921
|
if (permissionChecker2.cannot.read()) {
|
1621
1922
|
return ctx.forbidden();
|
1622
1923
|
}
|
1623
1924
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
1624
|
-
const populate = await getService$
|
1625
|
-
const { locale, status
|
1925
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
1926
|
+
const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1626
1927
|
const version = await documentManager2.findOne(id, model, {
|
1627
1928
|
populate,
|
1628
1929
|
locale,
|
@@ -1633,9 +1934,11 @@ const collectionTypes = {
|
|
1633
1934
|
if (!exists) {
|
1634
1935
|
return ctx.notFound();
|
1635
1936
|
}
|
1636
|
-
const { meta } = await
|
1937
|
+
const { meta } = await formatDocumentWithMetadata(
|
1938
|
+
permissionChecker2,
|
1637
1939
|
model,
|
1638
|
-
|
1940
|
+
// @ts-expect-error TODO: fix
|
1941
|
+
{ documentId: id, locale, publishedAt: null },
|
1639
1942
|
{ availableLocales: true, availableStatus: false }
|
1640
1943
|
);
|
1641
1944
|
ctx.body = { data: {}, meta };
|
@@ -1645,20 +1948,19 @@ const collectionTypes = {
|
|
1645
1948
|
return ctx.forbidden();
|
1646
1949
|
}
|
1647
1950
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
|
1648
|
-
ctx.body = await
|
1951
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
1649
1952
|
},
|
1650
1953
|
async create(ctx) {
|
1651
1954
|
const { userAbility } = ctx.state;
|
1652
1955
|
const { model } = ctx.params;
|
1653
|
-
const
|
1654
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1956
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1655
1957
|
const [totalEntries, document] = await Promise.all([
|
1656
1958
|
strapi.db.query(model).count(),
|
1657
1959
|
createDocument(ctx)
|
1658
1960
|
]);
|
1659
1961
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
|
1660
1962
|
ctx.status = 201;
|
1661
|
-
ctx.body = await
|
1963
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
|
1662
1964
|
// Empty metadata as it's not relevant for a new document
|
1663
1965
|
availableLocales: false,
|
1664
1966
|
availableStatus: false
|
@@ -1672,25 +1974,23 @@ const collectionTypes = {
|
|
1672
1974
|
async update(ctx) {
|
1673
1975
|
const { userAbility } = ctx.state;
|
1674
1976
|
const { model } = ctx.params;
|
1675
|
-
const
|
1676
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1977
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1677
1978
|
const updatedVersion = await updateDocument(ctx);
|
1678
1979
|
const sanitizedVersion = await permissionChecker2.sanitizeOutput(updatedVersion);
|
1679
|
-
ctx.body = await
|
1980
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedVersion);
|
1680
1981
|
},
|
1681
1982
|
async clone(ctx) {
|
1682
1983
|
const { userAbility, user } = ctx.state;
|
1683
1984
|
const { model, sourceId: id } = ctx.params;
|
1684
1985
|
const { body } = ctx.request;
|
1685
|
-
const documentManager2 = getService$
|
1686
|
-
const
|
1687
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
1986
|
+
const documentManager2 = getService$2("document-manager");
|
1987
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1688
1988
|
if (permissionChecker2.cannot.create()) {
|
1689
1989
|
return ctx.forbidden();
|
1690
1990
|
}
|
1691
1991
|
const permissionQuery = await permissionChecker2.sanitizedQuery.create(ctx.query);
|
1692
|
-
const populate = await getService$
|
1693
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
1992
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
1993
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1694
1994
|
const document = await documentManager2.findOne(id, model, {
|
1695
1995
|
populate,
|
1696
1996
|
locale,
|
@@ -1706,7 +2006,7 @@ const collectionTypes = {
|
|
1706
2006
|
const sanitizedBody = await sanitizeFn(body);
|
1707
2007
|
const clonedDocument = await documentManager2.clone(document.documentId, sanitizedBody, model);
|
1708
2008
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(clonedDocument);
|
1709
|
-
ctx.body = await
|
2009
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument, {
|
1710
2010
|
// Empty metadata as it's not relevant for a new document
|
1711
2011
|
availableLocales: false,
|
1712
2012
|
availableStatus: false
|
@@ -1728,14 +2028,14 @@ const collectionTypes = {
|
|
1728
2028
|
async delete(ctx) {
|
1729
2029
|
const { userAbility } = ctx.state;
|
1730
2030
|
const { id, model } = ctx.params;
|
1731
|
-
const documentManager2 = getService$
|
1732
|
-
const permissionChecker2 = getService$
|
2031
|
+
const documentManager2 = getService$2("document-manager");
|
2032
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1733
2033
|
if (permissionChecker2.cannot.delete()) {
|
1734
2034
|
return ctx.forbidden();
|
1735
2035
|
}
|
1736
2036
|
const permissionQuery = await permissionChecker2.sanitizedQuery.delete(ctx.query);
|
1737
|
-
const populate = await getService$
|
1738
|
-
const { locale } = getDocumentLocaleAndStatus(ctx.query);
|
2037
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2038
|
+
const { locale } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1739
2039
|
const documentLocales = await documentManager2.findLocales(id, model, { populate, locale });
|
1740
2040
|
if (documentLocales.length === 0) {
|
1741
2041
|
return ctx.notFound();
|
@@ -1756,44 +2056,75 @@ const collectionTypes = {
|
|
1756
2056
|
const { userAbility } = ctx.state;
|
1757
2057
|
const { id, model } = ctx.params;
|
1758
2058
|
const { body } = ctx.request;
|
1759
|
-
const documentManager2 = getService$
|
1760
|
-
const
|
1761
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2059
|
+
const documentManager2 = getService$2("document-manager");
|
2060
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1762
2061
|
if (permissionChecker2.cannot.publish()) {
|
1763
2062
|
return ctx.forbidden();
|
1764
2063
|
}
|
1765
2064
|
const publishedDocument = await strapi.db.transaction(async () => {
|
1766
2065
|
const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
|
1767
|
-
const populate = await getService$
|
1768
|
-
|
2066
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
2067
|
+
let document;
|
2068
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2069
|
+
const isCreate = isNil$1(id);
|
2070
|
+
if (isCreate) {
|
2071
|
+
if (permissionChecker2.cannot.create()) {
|
2072
|
+
throw new errors.ForbiddenError();
|
2073
|
+
}
|
2074
|
+
document = await createDocument(ctx, { populate });
|
2075
|
+
}
|
2076
|
+
const isUpdate = !isCreate;
|
2077
|
+
if (isUpdate) {
|
2078
|
+
const documentExists = documentManager2.exists(model, id);
|
2079
|
+
if (!documentExists) {
|
2080
|
+
throw new errors.NotFoundError("Document not found");
|
2081
|
+
}
|
2082
|
+
document = await documentManager2.findOne(id, model, { populate, locale });
|
2083
|
+
if (!document) {
|
2084
|
+
if (permissionChecker2.cannot.create({ locale }) || permissionChecker2.cannot.publish({ locale })) {
|
2085
|
+
throw new errors.ForbiddenError();
|
2086
|
+
}
|
2087
|
+
document = await updateDocument(ctx);
|
2088
|
+
} else if (permissionChecker2.can.update(document)) {
|
2089
|
+
await updateDocument(ctx);
|
2090
|
+
}
|
2091
|
+
}
|
1769
2092
|
if (permissionChecker2.cannot.publish(document)) {
|
1770
2093
|
throw new errors.ForbiddenError();
|
1771
2094
|
}
|
1772
|
-
const
|
1773
|
-
return documentManager2.publish(document.documentId, model, {
|
2095
|
+
const publishResult = await documentManager2.publish(document.documentId, model, {
|
1774
2096
|
locale
|
1775
2097
|
// TODO: Allow setting creator fields on publish
|
1776
2098
|
// data: setCreatorFields({ user, isEdition: true })({}),
|
1777
2099
|
});
|
2100
|
+
if (!publishResult || publishResult.length === 0) {
|
2101
|
+
throw new errors.NotFoundError("Document not found or already published.");
|
2102
|
+
}
|
2103
|
+
return publishResult[0];
|
1778
2104
|
});
|
1779
2105
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
|
1780
|
-
ctx.body = await
|
2106
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
1781
2107
|
},
|
1782
2108
|
async bulkPublish(ctx) {
|
1783
2109
|
const { userAbility } = ctx.state;
|
1784
2110
|
const { model } = ctx.params;
|
1785
2111
|
const { body } = ctx.request;
|
1786
|
-
const {
|
2112
|
+
const { documentIds } = body;
|
1787
2113
|
await validateBulkActionInput(body);
|
1788
|
-
const documentManager2 = getService$
|
1789
|
-
const permissionChecker2 = getService$
|
2114
|
+
const documentManager2 = getService$2("document-manager");
|
2115
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1790
2116
|
if (permissionChecker2.cannot.publish()) {
|
1791
2117
|
return ctx.forbidden();
|
1792
2118
|
}
|
1793
2119
|
const permissionQuery = await permissionChecker2.sanitizedQuery.publish(ctx.query);
|
1794
|
-
const populate = await getService$
|
1795
|
-
const
|
1796
|
-
|
2120
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).populateDeep(Infinity).countRelations().build();
|
2121
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model, {
|
2122
|
+
allowMultipleLocales: true
|
2123
|
+
});
|
2124
|
+
const entityPromises = documentIds.map(
|
2125
|
+
(documentId) => documentManager2.findLocales(documentId, model, { populate, locale, isPublished: false })
|
2126
|
+
);
|
2127
|
+
const entities = (await Promise.all(entityPromises)).flat();
|
1797
2128
|
for (const entity of entities) {
|
1798
2129
|
if (!entity) {
|
1799
2130
|
return ctx.notFound();
|
@@ -1802,24 +2133,27 @@ const collectionTypes = {
|
|
1802
2133
|
return ctx.forbidden();
|
1803
2134
|
}
|
1804
2135
|
}
|
1805
|
-
const
|
2136
|
+
const count = await documentManager2.publishMany(model, documentIds, locale);
|
1806
2137
|
ctx.body = { count };
|
1807
2138
|
},
|
1808
2139
|
async bulkUnpublish(ctx) {
|
1809
2140
|
const { userAbility } = ctx.state;
|
1810
2141
|
const { model } = ctx.params;
|
1811
2142
|
const { body } = ctx.request;
|
1812
|
-
const {
|
2143
|
+
const { documentIds } = body;
|
1813
2144
|
await validateBulkActionInput(body);
|
1814
|
-
const documentManager2 = getService$
|
1815
|
-
const permissionChecker2 = getService$
|
2145
|
+
const documentManager2 = getService$2("document-manager");
|
2146
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1816
2147
|
if (permissionChecker2.cannot.unpublish()) {
|
1817
2148
|
return ctx.forbidden();
|
1818
2149
|
}
|
1819
|
-
const
|
1820
|
-
|
1821
|
-
|
1822
|
-
const
|
2150
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model, {
|
2151
|
+
allowMultipleLocales: true
|
2152
|
+
});
|
2153
|
+
const entityPromises = documentIds.map(
|
2154
|
+
(documentId) => documentManager2.findLocales(documentId, model, { locale, isPublished: true })
|
2155
|
+
);
|
2156
|
+
const entities = (await Promise.all(entityPromises)).flat();
|
1823
2157
|
for (const entity of entities) {
|
1824
2158
|
if (!entity) {
|
1825
2159
|
return ctx.notFound();
|
@@ -1828,7 +2162,8 @@ const collectionTypes = {
|
|
1828
2162
|
return ctx.forbidden();
|
1829
2163
|
}
|
1830
2164
|
}
|
1831
|
-
const
|
2165
|
+
const entitiesIds = entities.map((document) => document.documentId);
|
2166
|
+
const { count } = await documentManager2.unpublishMany(entitiesIds, model, { locale });
|
1832
2167
|
ctx.body = { count };
|
1833
2168
|
},
|
1834
2169
|
async unpublish(ctx) {
|
@@ -1837,9 +2172,8 @@ const collectionTypes = {
|
|
1837
2172
|
const {
|
1838
2173
|
body: { discardDraft, ...body }
|
1839
2174
|
} = ctx.request;
|
1840
|
-
const documentManager2 = getService$
|
1841
|
-
const
|
1842
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2175
|
+
const documentManager2 = getService$2("document-manager");
|
2176
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1843
2177
|
if (permissionChecker2.cannot.unpublish()) {
|
1844
2178
|
return ctx.forbidden();
|
1845
2179
|
}
|
@@ -1847,8 +2181,8 @@ const collectionTypes = {
|
|
1847
2181
|
return ctx.forbidden();
|
1848
2182
|
}
|
1849
2183
|
const permissionQuery = await permissionChecker2.sanitizedQuery.unpublish(ctx.query);
|
1850
|
-
const populate = await getService$
|
1851
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2184
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2185
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1852
2186
|
const document = await documentManager2.findOne(id, model, {
|
1853
2187
|
populate,
|
1854
2188
|
locale,
|
@@ -1870,7 +2204,7 @@ const collectionTypes = {
|
|
1870
2204
|
ctx.body = await async.pipe(
|
1871
2205
|
(document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
|
1872
2206
|
permissionChecker2.sanitizeOutput,
|
1873
|
-
(document2) =>
|
2207
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
1874
2208
|
)(document);
|
1875
2209
|
});
|
1876
2210
|
},
|
@@ -1878,15 +2212,14 @@ const collectionTypes = {
|
|
1878
2212
|
const { userAbility } = ctx.state;
|
1879
2213
|
const { id, model } = ctx.params;
|
1880
2214
|
const { body } = ctx.request;
|
1881
|
-
const documentManager2 = getService$
|
1882
|
-
const
|
1883
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2215
|
+
const documentManager2 = getService$2("document-manager");
|
2216
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1884
2217
|
if (permissionChecker2.cannot.discard()) {
|
1885
2218
|
return ctx.forbidden();
|
1886
2219
|
}
|
1887
2220
|
const permissionQuery = await permissionChecker2.sanitizedQuery.discard(ctx.query);
|
1888
|
-
const populate = await getService$
|
1889
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2221
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2222
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
1890
2223
|
const document = await documentManager2.findOne(id, model, {
|
1891
2224
|
populate,
|
1892
2225
|
locale,
|
@@ -1901,42 +2234,50 @@ const collectionTypes = {
|
|
1901
2234
|
ctx.body = await async.pipe(
|
1902
2235
|
(document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
|
1903
2236
|
permissionChecker2.sanitizeOutput,
|
1904
|
-
(document2) =>
|
2237
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
1905
2238
|
)(document);
|
1906
2239
|
},
|
1907
2240
|
async bulkDelete(ctx) {
|
1908
2241
|
const { userAbility } = ctx.state;
|
1909
2242
|
const { model } = ctx.params;
|
1910
2243
|
const { query, body } = ctx.request;
|
1911
|
-
const {
|
2244
|
+
const { documentIds } = body;
|
1912
2245
|
await validateBulkActionInput(body);
|
1913
|
-
const documentManager2 = getService$
|
1914
|
-
const permissionChecker2 = getService$
|
2246
|
+
const documentManager2 = getService$2("document-manager");
|
2247
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1915
2248
|
if (permissionChecker2.cannot.delete()) {
|
1916
2249
|
return ctx.forbidden();
|
1917
2250
|
}
|
1918
2251
|
const permissionQuery = await permissionChecker2.sanitizedQuery.delete(query);
|
1919
|
-
const
|
1920
|
-
const
|
1921
|
-
|
1922
|
-
|
1923
|
-
|
2252
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2253
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2254
|
+
const documentLocales = await documentManager2.findLocales(documentIds, model, {
|
2255
|
+
populate,
|
2256
|
+
locale
|
2257
|
+
});
|
2258
|
+
if (documentLocales.length === 0) {
|
2259
|
+
return ctx.notFound();
|
2260
|
+
}
|
2261
|
+
for (const document of documentLocales) {
|
2262
|
+
if (permissionChecker2.cannot.delete(document)) {
|
2263
|
+
return ctx.forbidden();
|
1924
2264
|
}
|
1925
|
-
}
|
1926
|
-
const
|
2265
|
+
}
|
2266
|
+
const localeDocumentsIds = documentLocales.map((document) => document.documentId);
|
2267
|
+
const { count } = await documentManager2.deleteMany(localeDocumentsIds, model, { locale });
|
1927
2268
|
ctx.body = { count };
|
1928
2269
|
},
|
1929
2270
|
async countDraftRelations(ctx) {
|
1930
2271
|
const { userAbility } = ctx.state;
|
1931
2272
|
const { model, id } = ctx.params;
|
1932
|
-
const documentManager2 = getService$
|
1933
|
-
const permissionChecker2 = getService$
|
2273
|
+
const documentManager2 = getService$2("document-manager");
|
2274
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1934
2275
|
if (permissionChecker2.cannot.read()) {
|
1935
2276
|
return ctx.forbidden();
|
1936
2277
|
}
|
1937
2278
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
1938
|
-
const populate = await getService$
|
1939
|
-
const { locale, status
|
2279
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2280
|
+
const { locale, status } = await getDocumentLocaleAndStatus(ctx.query, model);
|
1940
2281
|
const entity = await documentManager2.findOne(id, model, { populate, locale, status });
|
1941
2282
|
if (!entity) {
|
1942
2283
|
return ctx.notFound();
|
@@ -1951,24 +2292,24 @@ const collectionTypes = {
|
|
1951
2292
|
},
|
1952
2293
|
async countManyEntriesDraftRelations(ctx) {
|
1953
2294
|
const { userAbility } = ctx.state;
|
1954
|
-
const ids = ctx.request.query.
|
2295
|
+
const ids = ctx.request.query.documentIds;
|
1955
2296
|
const locale = ctx.request.query.locale;
|
1956
2297
|
const { model } = ctx.params;
|
1957
|
-
const documentManager2 = getService$
|
1958
|
-
const permissionChecker2 = getService$
|
2298
|
+
const documentManager2 = getService$2("document-manager");
|
2299
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
1959
2300
|
if (permissionChecker2.cannot.read()) {
|
1960
2301
|
return ctx.forbidden();
|
1961
2302
|
}
|
1962
|
-
const
|
2303
|
+
const documents = await documentManager2.findMany(
|
1963
2304
|
{
|
1964
2305
|
filters: {
|
1965
|
-
|
2306
|
+
documentId: ids
|
1966
2307
|
},
|
1967
2308
|
locale
|
1968
2309
|
},
|
1969
2310
|
model
|
1970
2311
|
);
|
1971
|
-
if (!
|
2312
|
+
if (!documents) {
|
1972
2313
|
return ctx.notFound();
|
1973
2314
|
}
|
1974
2315
|
const number = await documentManager2.countManyEntriesDraftRelations(ids, model, locale);
|
@@ -1979,13 +2320,13 @@ const collectionTypes = {
|
|
1979
2320
|
};
|
1980
2321
|
const components$1 = {
|
1981
2322
|
findComponents(ctx) {
|
1982
|
-
const components2 = getService$
|
1983
|
-
const { toDto } = getService$
|
2323
|
+
const components2 = getService$2("components").findAllComponents();
|
2324
|
+
const { toDto } = getService$2("data-mapper");
|
1984
2325
|
ctx.body = { data: components2.map(toDto) };
|
1985
2326
|
},
|
1986
2327
|
async findComponentConfiguration(ctx) {
|
1987
2328
|
const { uid: uid2 } = ctx.params;
|
1988
|
-
const componentService = getService$
|
2329
|
+
const componentService = getService$2("components");
|
1989
2330
|
const component = componentService.findComponent(uid2);
|
1990
2331
|
if (!component) {
|
1991
2332
|
return ctx.notFound("component.notFound");
|
@@ -2002,7 +2343,7 @@ const components$1 = {
|
|
2002
2343
|
async updateComponentConfiguration(ctx) {
|
2003
2344
|
const { uid: uid2 } = ctx.params;
|
2004
2345
|
const { body } = ctx.request;
|
2005
|
-
const componentService = getService$
|
2346
|
+
const componentService = getService$2("components");
|
2006
2347
|
const component = componentService.findComponent(uid2);
|
2007
2348
|
if (!component) {
|
2008
2349
|
return ctx.notFound("component.notFound");
|
@@ -2036,12 +2377,12 @@ const contentTypes = {
|
|
2036
2377
|
} catch (error) {
|
2037
2378
|
return ctx.send({ error }, 400);
|
2038
2379
|
}
|
2039
|
-
const contentTypes2 = getService$
|
2040
|
-
const { toDto } = getService$
|
2380
|
+
const contentTypes2 = getService$2("content-types").findContentTypesByKind(kind);
|
2381
|
+
const { toDto } = getService$2("data-mapper");
|
2041
2382
|
ctx.body = { data: contentTypes2.map(toDto) };
|
2042
2383
|
},
|
2043
2384
|
async findContentTypesSettings(ctx) {
|
2044
|
-
const { findAllContentTypes, findConfiguration } = getService$
|
2385
|
+
const { findAllContentTypes, findConfiguration } = getService$2("content-types");
|
2045
2386
|
const contentTypes2 = await findAllContentTypes();
|
2046
2387
|
const configurations = await Promise.all(
|
2047
2388
|
contentTypes2.map(async (contentType) => {
|
@@ -2055,7 +2396,7 @@ const contentTypes = {
|
|
2055
2396
|
},
|
2056
2397
|
async findContentTypeConfiguration(ctx) {
|
2057
2398
|
const { uid: uid2 } = ctx.params;
|
2058
|
-
const contentTypeService = getService$
|
2399
|
+
const contentTypeService = getService$2("content-types");
|
2059
2400
|
const contentType = await contentTypeService.findContentType(uid2);
|
2060
2401
|
if (!contentType) {
|
2061
2402
|
return ctx.notFound("contentType.notFound");
|
@@ -2077,13 +2418,13 @@ const contentTypes = {
|
|
2077
2418
|
const { userAbility } = ctx.state;
|
2078
2419
|
const { uid: uid2 } = ctx.params;
|
2079
2420
|
const { body } = ctx.request;
|
2080
|
-
const contentTypeService = getService$
|
2081
|
-
const metricsService = getService$
|
2421
|
+
const contentTypeService = getService$2("content-types");
|
2422
|
+
const metricsService = getService$2("metrics");
|
2082
2423
|
const contentType = await contentTypeService.findContentType(uid2);
|
2083
2424
|
if (!contentType) {
|
2084
2425
|
return ctx.notFound("contentType.notFound");
|
2085
2426
|
}
|
2086
|
-
if (!getService$
|
2427
|
+
if (!getService$2("permission").canConfigureContentType({ userAbility, contentType })) {
|
2087
2428
|
return ctx.forbidden();
|
2088
2429
|
}
|
2089
2430
|
let input;
|
@@ -2116,10 +2457,10 @@ const contentTypes = {
|
|
2116
2457
|
};
|
2117
2458
|
const init = {
|
2118
2459
|
getInitData(ctx) {
|
2119
|
-
const { toDto } = getService$
|
2120
|
-
const { findAllComponents } = getService$
|
2121
|
-
const { getAllFieldSizes } = getService$
|
2122
|
-
const { findAllContentTypes } = getService$
|
2460
|
+
const { toDto } = getService$2("data-mapper");
|
2461
|
+
const { findAllComponents } = getService$2("components");
|
2462
|
+
const { getAllFieldSizes } = getService$2("field-sizes");
|
2463
|
+
const { findAllContentTypes } = getService$2("content-types");
|
2123
2464
|
ctx.body = {
|
2124
2465
|
data: {
|
2125
2466
|
fieldSizes: getAllFieldSizes(),
|
@@ -2155,36 +2496,41 @@ const addFiltersClause = (params, filtersClause) => {
|
|
2155
2496
|
params.filters.$and.push(filtersClause);
|
2156
2497
|
};
|
2157
2498
|
const sanitizeMainField = (model, mainField, userAbility) => {
|
2158
|
-
const permissionChecker2 = getService$
|
2499
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
2159
2500
|
userAbility,
|
2160
2501
|
model: model.uid
|
2161
2502
|
});
|
2162
|
-
|
2503
|
+
const isMainFieldListable = isListable(model, mainField);
|
2504
|
+
const canReadMainField = permissionChecker2.can.read(null, mainField);
|
2505
|
+
if (!isMainFieldListable || !canReadMainField) {
|
2163
2506
|
return "id";
|
2164
2507
|
}
|
2165
|
-
if (
|
2166
|
-
|
2167
|
-
const userPermissionChecker = getService$1("permission-checker").create({
|
2168
|
-
userAbility,
|
2169
|
-
model: "plugin::users-permissions.user"
|
2170
|
-
});
|
2171
|
-
if (userPermissionChecker.can.read()) {
|
2172
|
-
return "name";
|
2173
|
-
}
|
2174
|
-
}
|
2175
|
-
return "id";
|
2508
|
+
if (model.uid === "plugin::users-permissions.role") {
|
2509
|
+
return "name";
|
2176
2510
|
}
|
2177
2511
|
return mainField;
|
2178
2512
|
};
|
2179
|
-
const addStatusToRelations = async (
|
2180
|
-
if (!contentTypes$1.hasDraftAndPublish(strapi.
|
2513
|
+
const addStatusToRelations = async (targetUid, relations2) => {
|
2514
|
+
if (!contentTypes$1.hasDraftAndPublish(strapi.getModel(targetUid))) {
|
2515
|
+
return relations2;
|
2516
|
+
}
|
2517
|
+
const documentMetadata2 = getService$2("document-metadata");
|
2518
|
+
if (!relations2.length) {
|
2181
2519
|
return relations2;
|
2182
2520
|
}
|
2183
|
-
const
|
2184
|
-
const
|
2521
|
+
const firstRelation = relations2[0];
|
2522
|
+
const filters = {
|
2523
|
+
documentId: { $in: relations2.map((r) => r.documentId) },
|
2524
|
+
// NOTE: find the "opposite" status
|
2525
|
+
publishedAt: firstRelation.publishedAt !== null ? { $null: true } : { $notNull: true }
|
2526
|
+
};
|
2527
|
+
const availableStatus = await strapi.query(targetUid).findMany({
|
2528
|
+
select: ["id", "documentId", "locale", "updatedAt", "createdAt", "publishedAt"],
|
2529
|
+
filters
|
2530
|
+
});
|
2185
2531
|
return relations2.map((relation) => {
|
2186
|
-
const availableStatuses =
|
2187
|
-
(availableDocument) => availableDocument.documentId === relation.documentId
|
2532
|
+
const availableStatuses = availableStatus.filter(
|
2533
|
+
(availableDocument) => availableDocument.documentId === relation.documentId && (relation.locale ? availableDocument.locale === relation.locale : true)
|
2188
2534
|
);
|
2189
2535
|
return {
|
2190
2536
|
...relation,
|
@@ -2205,11 +2551,8 @@ const validateLocale = (sourceUid, targetUid, locale) => {
|
|
2205
2551
|
const isLocalized = strapi.plugin("i18n").service("content-types").isLocalizedContentType;
|
2206
2552
|
const isSourceLocalized = isLocalized(sourceModel);
|
2207
2553
|
const isTargetLocalized = isLocalized(targetModel);
|
2208
|
-
let validatedLocale = locale;
|
2209
|
-
if (!targetModel || !isTargetLocalized)
|
2210
|
-
validatedLocale = void 0;
|
2211
2554
|
return {
|
2212
|
-
locale
|
2555
|
+
locale,
|
2213
2556
|
isSourceLocalized,
|
2214
2557
|
isTargetLocalized
|
2215
2558
|
};
|
@@ -2249,7 +2592,7 @@ const relations = {
|
|
2249
2592
|
ctx.request?.query?.locale
|
2250
2593
|
);
|
2251
2594
|
const { status } = validateStatus(sourceUid, ctx.request?.query?.status);
|
2252
|
-
const permissionChecker2 = getService$
|
2595
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
2253
2596
|
userAbility,
|
2254
2597
|
model
|
2255
2598
|
});
|
@@ -2274,7 +2617,7 @@ const relations = {
|
|
2274
2617
|
where.id = id;
|
2275
2618
|
}
|
2276
2619
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(ctx.query);
|
2277
|
-
const populate = await getService$
|
2620
|
+
const populate = await getService$2("populate-builder")(model).populateFromQuery(permissionQuery).build();
|
2278
2621
|
const currentEntity = await strapi.db.query(model).findOne({
|
2279
2622
|
where,
|
2280
2623
|
populate
|
@@ -2289,7 +2632,7 @@ const relations = {
|
|
2289
2632
|
}
|
2290
2633
|
entryId = currentEntity.id;
|
2291
2634
|
}
|
2292
|
-
const modelConfig = isComponent2 ? await getService$
|
2635
|
+
const modelConfig = isComponent2 ? await getService$2("components").findConfiguration(sourceSchema) : await getService$2("content-types").findConfiguration(sourceSchema);
|
2293
2636
|
const targetSchema = strapi.getModel(targetUid);
|
2294
2637
|
const mainField = flow(
|
2295
2638
|
prop(`metadatas.${targetField}.edit.mainField`),
|
@@ -2312,7 +2655,7 @@ const relations = {
|
|
2312
2655
|
attribute,
|
2313
2656
|
fieldsToSelect,
|
2314
2657
|
mainField,
|
2315
|
-
source: { schema: sourceSchema },
|
2658
|
+
source: { schema: sourceSchema, isLocalized: isSourceLocalized },
|
2316
2659
|
target: { schema: targetSchema, isLocalized: isTargetLocalized },
|
2317
2660
|
sourceSchema,
|
2318
2661
|
targetSchema,
|
@@ -2334,7 +2677,8 @@ const relations = {
|
|
2334
2677
|
fieldsToSelect,
|
2335
2678
|
mainField,
|
2336
2679
|
source: {
|
2337
|
-
schema: { uid: sourceUid, modelType: sourceModelType }
|
2680
|
+
schema: { uid: sourceUid, modelType: sourceModelType },
|
2681
|
+
isLocalized: isSourceLocalized
|
2338
2682
|
},
|
2339
2683
|
target: {
|
2340
2684
|
schema: { uid: targetUid },
|
@@ -2342,7 +2686,7 @@ const relations = {
|
|
2342
2686
|
}
|
2343
2687
|
} = await this.extractAndValidateRequestInfo(ctx, id);
|
2344
2688
|
const { idsToOmit, idsToInclude, _q, ...query } = ctx.request.query;
|
2345
|
-
const permissionChecker2 = getService$
|
2689
|
+
const permissionChecker2 = getService$2("permission-checker").create({
|
2346
2690
|
userAbility: ctx.state.userAbility,
|
2347
2691
|
model: targetUid
|
2348
2692
|
});
|
@@ -2372,12 +2716,16 @@ const relations = {
|
|
2372
2716
|
} else {
|
2373
2717
|
where.id = id;
|
2374
2718
|
}
|
2375
|
-
|
2376
|
-
|
2719
|
+
const publishedAt = getPublishedAtClause(status, targetUid);
|
2720
|
+
if (!isEmpty(publishedAt)) {
|
2721
|
+
where[`${alias}.published_at`] = publishedAt;
|
2377
2722
|
}
|
2378
|
-
if (
|
2723
|
+
if (isTargetLocalized && locale) {
|
2379
2724
|
where[`${alias}.locale`] = locale;
|
2380
2725
|
}
|
2726
|
+
if (isSourceLocalized && locale) {
|
2727
|
+
where.locale = locale;
|
2728
|
+
}
|
2381
2729
|
if ((idsToInclude?.length ?? 0) !== 0) {
|
2382
2730
|
where[`${alias}.id`].$notIn = idsToInclude;
|
2383
2731
|
}
|
@@ -2395,7 +2743,8 @@ const relations = {
|
|
2395
2743
|
id: { $notIn: uniq(idsToOmit) }
|
2396
2744
|
});
|
2397
2745
|
}
|
2398
|
-
const
|
2746
|
+
const dbQuery = strapi.get("query-params").transform(targetUid, queryParams);
|
2747
|
+
const res = await strapi.db.query(targetUid).findPage(dbQuery);
|
2399
2748
|
ctx.body = {
|
2400
2749
|
...res,
|
2401
2750
|
results: await addStatusToRelations(targetUid, res.results)
|
@@ -2410,29 +2759,39 @@ const relations = {
|
|
2410
2759
|
attribute,
|
2411
2760
|
targetField,
|
2412
2761
|
fieldsToSelect,
|
2413
|
-
|
2414
|
-
|
2415
|
-
}
|
2416
|
-
target: {
|
2417
|
-
schema: { uid: targetUid }
|
2418
|
-
}
|
2762
|
+
status,
|
2763
|
+
source: { schema: sourceSchema },
|
2764
|
+
target: { schema: targetSchema }
|
2419
2765
|
} = await this.extractAndValidateRequestInfo(ctx, id);
|
2420
|
-
const
|
2766
|
+
const { uid: sourceUid } = sourceSchema;
|
2767
|
+
const { uid: targetUid } = targetSchema;
|
2768
|
+
const permissionQuery = await getService$2("permission-checker").create({ userAbility, model: targetUid }).sanitizedQuery.read({ fields: fieldsToSelect });
|
2421
2769
|
const dbQuery = strapi.db.query(sourceUid);
|
2422
2770
|
const loadRelations = relations$1.isAnyToMany(attribute) ? (...args) => dbQuery.loadPages(...args) : (...args) => dbQuery.load(...args).then((res2) => ({ results: res2 ? [res2] : [] }));
|
2771
|
+
const filters = {};
|
2772
|
+
if (sourceSchema?.options?.draftAndPublish) {
|
2773
|
+
if (targetSchema?.options?.draftAndPublish) {
|
2774
|
+
if (status === "published") {
|
2775
|
+
filters.publishedAt = { $notNull: true };
|
2776
|
+
} else {
|
2777
|
+
filters.publishedAt = { $null: true };
|
2778
|
+
}
|
2779
|
+
}
|
2780
|
+
} else if (targetSchema?.options?.draftAndPublish) {
|
2781
|
+
filters.publishedAt = { $null: true };
|
2782
|
+
}
|
2423
2783
|
const res = await loadRelations({ id: entryId }, targetField, {
|
2424
|
-
select: ["id", "documentId", "locale", "publishedAt"],
|
2784
|
+
select: ["id", "documentId", "locale", "publishedAt", "updatedAt"],
|
2425
2785
|
ordering: "desc",
|
2426
2786
|
page: ctx.request.query.page,
|
2427
|
-
pageSize: ctx.request.query.pageSize
|
2787
|
+
pageSize: ctx.request.query.pageSize,
|
2788
|
+
filters
|
2428
2789
|
});
|
2429
2790
|
const loadedIds = res.results.map((item) => item.id);
|
2430
2791
|
addFiltersClause(permissionQuery, { id: { $in: loadedIds } });
|
2431
2792
|
const sanitizedRes = await loadRelations({ id: entryId }, targetField, {
|
2432
2793
|
...strapi.get("query-params").transform(targetUid, permissionQuery),
|
2433
|
-
ordering: "desc"
|
2434
|
-
page: ctx.request.query.page,
|
2435
|
-
pageSize: ctx.request.query.pageSize
|
2794
|
+
ordering: "desc"
|
2436
2795
|
});
|
2437
2796
|
const relationsUnion = uniqBy("id", concat(sanitizedRes.results, res.results));
|
2438
2797
|
ctx.body = {
|
@@ -2447,10 +2806,10 @@ const relations = {
|
|
2447
2806
|
}
|
2448
2807
|
};
|
2449
2808
|
const buildPopulateFromQuery = async (query, model) => {
|
2450
|
-
return getService$
|
2809
|
+
return getService$2("populate-builder")(model).populateFromQuery(query).populateDeep(Infinity).countRelations().build();
|
2451
2810
|
};
|
2452
2811
|
const findDocument = async (query, uid2, opts = {}) => {
|
2453
|
-
const documentManager2 = getService$
|
2812
|
+
const documentManager2 = getService$2("document-manager");
|
2454
2813
|
const populate = await buildPopulateFromQuery(query, uid2);
|
2455
2814
|
return documentManager2.findMany({ ...opts, populate }, uid2).then((documents) => documents[0]);
|
2456
2815
|
};
|
@@ -2458,13 +2817,13 @@ const createOrUpdateDocument = async (ctx, opts) => {
|
|
2458
2817
|
const { user, userAbility } = ctx.state;
|
2459
2818
|
const { model } = ctx.params;
|
2460
2819
|
const { body, query } = ctx.request;
|
2461
|
-
const documentManager2 = getService$
|
2462
|
-
const permissionChecker2 = getService$
|
2820
|
+
const documentManager2 = getService$2("document-manager");
|
2821
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2463
2822
|
if (permissionChecker2.cannot.create() && permissionChecker2.cannot.update()) {
|
2464
2823
|
throw new errors.ForbiddenError();
|
2465
2824
|
}
|
2466
2825
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.update(query);
|
2467
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2826
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2468
2827
|
const [documentVersion, otherDocumentVersion] = await Promise.all([
|
2469
2828
|
findDocument(sanitizedQuery, model, { locale, status: "draft" }),
|
2470
2829
|
// Find the first document to check if it exists
|
@@ -2500,13 +2859,12 @@ const singleTypes = {
|
|
2500
2859
|
const { userAbility } = ctx.state;
|
2501
2860
|
const { model } = ctx.params;
|
2502
2861
|
const { query = {} } = ctx.request;
|
2503
|
-
const permissionChecker2 = getService$
|
2504
|
-
const documentMetadata2 = getService$1("document-metadata");
|
2862
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2505
2863
|
if (permissionChecker2.cannot.read()) {
|
2506
2864
|
return ctx.forbidden();
|
2507
2865
|
}
|
2508
2866
|
const permissionQuery = await permissionChecker2.sanitizedQuery.read(query);
|
2509
|
-
const { locale, status } = getDocumentLocaleAndStatus(query);
|
2867
|
+
const { locale, status } = await getDocumentLocaleAndStatus(query, model);
|
2510
2868
|
const version = await findDocument(permissionQuery, model, { locale, status });
|
2511
2869
|
if (!version) {
|
2512
2870
|
if (permissionChecker2.cannot.create()) {
|
@@ -2516,9 +2874,11 @@ const singleTypes = {
|
|
2516
2874
|
if (!document) {
|
2517
2875
|
return ctx.notFound();
|
2518
2876
|
}
|
2519
|
-
const { meta } = await
|
2877
|
+
const { meta } = await formatDocumentWithMetadata(
|
2878
|
+
permissionChecker2,
|
2520
2879
|
model,
|
2521
|
-
|
2880
|
+
// @ts-expect-error - fix types
|
2881
|
+
{ documentId: document.documentId, locale, publishedAt: null },
|
2522
2882
|
{ availableLocales: true, availableStatus: false }
|
2523
2883
|
);
|
2524
2884
|
ctx.body = { data: {}, meta };
|
@@ -2528,29 +2888,28 @@ const singleTypes = {
|
|
2528
2888
|
return ctx.forbidden();
|
2529
2889
|
}
|
2530
2890
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(version);
|
2531
|
-
ctx.body = await
|
2891
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2532
2892
|
},
|
2533
2893
|
async createOrUpdate(ctx) {
|
2534
2894
|
const { userAbility } = ctx.state;
|
2535
2895
|
const { model } = ctx.params;
|
2536
|
-
const
|
2537
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2896
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2538
2897
|
const document = await createOrUpdateDocument(ctx);
|
2539
2898
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(document);
|
2540
|
-
ctx.body = await
|
2899
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2541
2900
|
},
|
2542
2901
|
async delete(ctx) {
|
2543
2902
|
const { userAbility } = ctx.state;
|
2544
2903
|
const { model } = ctx.params;
|
2545
2904
|
const { query = {} } = ctx.request;
|
2546
|
-
const documentManager2 = getService$
|
2547
|
-
const permissionChecker2 = getService$
|
2905
|
+
const documentManager2 = getService$2("document-manager");
|
2906
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2548
2907
|
if (permissionChecker2.cannot.delete()) {
|
2549
2908
|
return ctx.forbidden();
|
2550
2909
|
}
|
2551
2910
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.delete(query);
|
2552
2911
|
const populate = await buildPopulateFromQuery(sanitizedQuery, model);
|
2553
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
2912
|
+
const { locale } = await getDocumentLocaleAndStatus(query, model);
|
2554
2913
|
const documentLocales = await documentManager2.findLocales(void 0, model, {
|
2555
2914
|
populate,
|
2556
2915
|
locale
|
@@ -2572,9 +2931,8 @@ const singleTypes = {
|
|
2572
2931
|
const { userAbility } = ctx.state;
|
2573
2932
|
const { model } = ctx.params;
|
2574
2933
|
const { query = {} } = ctx.request;
|
2575
|
-
const documentManager2 = getService$
|
2576
|
-
const
|
2577
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2934
|
+
const documentManager2 = getService$2("document-manager");
|
2935
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2578
2936
|
if (permissionChecker2.cannot.publish()) {
|
2579
2937
|
return ctx.forbidden();
|
2580
2938
|
}
|
@@ -2588,11 +2946,12 @@ const singleTypes = {
|
|
2588
2946
|
if (permissionChecker2.cannot.publish(document)) {
|
2589
2947
|
throw new errors.ForbiddenError();
|
2590
2948
|
}
|
2591
|
-
const { locale } = getDocumentLocaleAndStatus(document);
|
2592
|
-
|
2949
|
+
const { locale } = await getDocumentLocaleAndStatus(document, model);
|
2950
|
+
const publishResult = await documentManager2.publish(document.documentId, model, { locale });
|
2951
|
+
return publishResult.at(0);
|
2593
2952
|
});
|
2594
2953
|
const sanitizedDocument = await permissionChecker2.sanitizeOutput(publishedDocument);
|
2595
|
-
ctx.body = await
|
2954
|
+
ctx.body = await formatDocumentWithMetadata(permissionChecker2, model, sanitizedDocument);
|
2596
2955
|
},
|
2597
2956
|
async unpublish(ctx) {
|
2598
2957
|
const { userAbility } = ctx.state;
|
@@ -2601,9 +2960,8 @@ const singleTypes = {
|
|
2601
2960
|
body: { discardDraft, ...body },
|
2602
2961
|
query = {}
|
2603
2962
|
} = ctx.request;
|
2604
|
-
const documentManager2 = getService$
|
2605
|
-
const
|
2606
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2963
|
+
const documentManager2 = getService$2("document-manager");
|
2964
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2607
2965
|
if (permissionChecker2.cannot.unpublish()) {
|
2608
2966
|
return ctx.forbidden();
|
2609
2967
|
}
|
@@ -2611,7 +2969,7 @@ const singleTypes = {
|
|
2611
2969
|
return ctx.forbidden();
|
2612
2970
|
}
|
2613
2971
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.unpublish(query);
|
2614
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
2972
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2615
2973
|
const document = await findDocument(sanitizedQuery, model, { locale });
|
2616
2974
|
if (!document) {
|
2617
2975
|
return ctx.notFound();
|
@@ -2629,7 +2987,7 @@ const singleTypes = {
|
|
2629
2987
|
ctx.body = await async.pipe(
|
2630
2988
|
(document2) => documentManager2.unpublish(document2.documentId, model, { locale }),
|
2631
2989
|
permissionChecker2.sanitizeOutput,
|
2632
|
-
(document2) =>
|
2990
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
2633
2991
|
)(document);
|
2634
2992
|
});
|
2635
2993
|
},
|
@@ -2637,14 +2995,13 @@ const singleTypes = {
|
|
2637
2995
|
const { userAbility } = ctx.state;
|
2638
2996
|
const { model } = ctx.params;
|
2639
2997
|
const { body, query = {} } = ctx.request;
|
2640
|
-
const documentManager2 = getService$
|
2641
|
-
const
|
2642
|
-
const permissionChecker2 = getService$1("permission-checker").create({ userAbility, model });
|
2998
|
+
const documentManager2 = getService$2("document-manager");
|
2999
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
2643
3000
|
if (permissionChecker2.cannot.discard()) {
|
2644
3001
|
return ctx.forbidden();
|
2645
3002
|
}
|
2646
3003
|
const sanitizedQuery = await permissionChecker2.sanitizedQuery.discard(query);
|
2647
|
-
const { locale } = getDocumentLocaleAndStatus(body);
|
3004
|
+
const { locale } = await getDocumentLocaleAndStatus(body, model);
|
2648
3005
|
const document = await findDocument(sanitizedQuery, model, { locale, status: "published" });
|
2649
3006
|
if (!document) {
|
2650
3007
|
return ctx.notFound();
|
@@ -2655,16 +3012,16 @@ const singleTypes = {
|
|
2655
3012
|
ctx.body = await async.pipe(
|
2656
3013
|
(document2) => documentManager2.discardDraft(document2.documentId, model, { locale }),
|
2657
3014
|
permissionChecker2.sanitizeOutput,
|
2658
|
-
(document2) =>
|
3015
|
+
(document2) => formatDocumentWithMetadata(permissionChecker2, model, document2)
|
2659
3016
|
)(document);
|
2660
3017
|
},
|
2661
3018
|
async countDraftRelations(ctx) {
|
2662
3019
|
const { userAbility } = ctx.state;
|
2663
3020
|
const { model } = ctx.params;
|
2664
3021
|
const { query } = ctx.request;
|
2665
|
-
const documentManager2 = getService$
|
2666
|
-
const permissionChecker2 = getService$
|
2667
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
3022
|
+
const documentManager2 = getService$2("document-manager");
|
3023
|
+
const permissionChecker2 = getService$2("permission-checker").create({ userAbility, model });
|
3024
|
+
const { locale } = await getDocumentLocaleAndStatus(query, model);
|
2668
3025
|
if (permissionChecker2.cannot.read()) {
|
2669
3026
|
return ctx.forbidden();
|
2670
3027
|
}
|
@@ -2685,9 +3042,9 @@ const uid$1 = {
|
|
2685
3042
|
async generateUID(ctx) {
|
2686
3043
|
const { contentTypeUID, field, data } = await validateGenerateUIDInput(ctx.request.body);
|
2687
3044
|
const { query = {} } = ctx.request;
|
2688
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
3045
|
+
const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
|
2689
3046
|
await validateUIDField(contentTypeUID, field);
|
2690
|
-
const uidService = getService$
|
3047
|
+
const uidService = getService$2("uid");
|
2691
3048
|
ctx.body = {
|
2692
3049
|
data: await uidService.generateUIDField({ contentTypeUID, field, data, locale })
|
2693
3050
|
};
|
@@ -2697,9 +3054,9 @@ const uid$1 = {
|
|
2697
3054
|
ctx.request.body
|
2698
3055
|
);
|
2699
3056
|
const { query = {} } = ctx.request;
|
2700
|
-
const { locale } = getDocumentLocaleAndStatus(query);
|
3057
|
+
const { locale } = await getDocumentLocaleAndStatus(query, contentTypeUID);
|
2701
3058
|
await validateUIDField(contentTypeUID, field);
|
2702
|
-
const uidService = getService$
|
3059
|
+
const uidService = getService$2("uid");
|
2703
3060
|
const isAvailable = await uidService.checkUIDAvailability({
|
2704
3061
|
contentTypeUID,
|
2705
3062
|
field,
|
@@ -2720,7 +3077,8 @@ const controllers = {
|
|
2720
3077
|
relations,
|
2721
3078
|
"single-types": singleTypes,
|
2722
3079
|
uid: uid$1,
|
2723
|
-
...history.controllers ? history.controllers : {}
|
3080
|
+
...history.controllers ? history.controllers : {},
|
3081
|
+
...preview.controllers ? preview.controllers : {}
|
2724
3082
|
};
|
2725
3083
|
const keys = {
|
2726
3084
|
CONFIGURATION: "configuration"
|
@@ -2871,12 +3229,12 @@ async function syncMetadatas(configuration, schema) {
|
|
2871
3229
|
return _.assign(metasWithDefaults, updatedMetas);
|
2872
3230
|
}
|
2873
3231
|
const getTargetSchema = (targetModel) => {
|
2874
|
-
return getService$
|
3232
|
+
return getService$2("content-types").findContentType(targetModel);
|
2875
3233
|
};
|
2876
3234
|
const DEFAULT_LIST_LENGTH = 4;
|
2877
3235
|
const MAX_ROW_SIZE = 12;
|
2878
3236
|
const isAllowedFieldSize = (type, size) => {
|
2879
|
-
const { getFieldSize } = getService$
|
3237
|
+
const { getFieldSize } = getService$2("field-sizes");
|
2880
3238
|
const fieldSize = getFieldSize(type);
|
2881
3239
|
if (!fieldSize.isResizable && size !== fieldSize.default) {
|
2882
3240
|
return false;
|
@@ -2884,7 +3242,7 @@ const isAllowedFieldSize = (type, size) => {
|
|
2884
3242
|
return size <= MAX_ROW_SIZE;
|
2885
3243
|
};
|
2886
3244
|
const getDefaultFieldSize = (attribute) => {
|
2887
|
-
const { hasFieldSize, getFieldSize } = getService$
|
3245
|
+
const { hasFieldSize, getFieldSize } = getService$2("field-sizes");
|
2888
3246
|
return getFieldSize(hasFieldSize(attribute.customField) ? attribute.customField : attribute.type).default;
|
2889
3247
|
};
|
2890
3248
|
async function createDefaultLayouts(schema) {
|
@@ -2919,7 +3277,7 @@ function syncLayouts(configuration, schema) {
|
|
2919
3277
|
for (const el of row) {
|
2920
3278
|
if (!hasEditableAttribute(schema, el.name))
|
2921
3279
|
continue;
|
2922
|
-
const { hasFieldSize } = getService$
|
3280
|
+
const { hasFieldSize } = getService$2("field-sizes");
|
2923
3281
|
const fieldType = hasFieldSize(schema.attributes[el.name].customField) ? schema.attributes[el.name].customField : schema.attributes[el.name].type;
|
2924
3282
|
if (!isAllowedFieldSize(fieldType, el.size)) {
|
2925
3283
|
elementsToReAppend.push(el.name);
|
@@ -3059,17 +3417,17 @@ const configurationService$1 = createConfigurationService({
|
|
3059
3417
|
isComponent: true,
|
3060
3418
|
prefix: STORE_KEY_PREFIX,
|
3061
3419
|
getModels() {
|
3062
|
-
const { toContentManagerModel } = getService$
|
3420
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3063
3421
|
return mapValues(toContentManagerModel, strapi.components);
|
3064
3422
|
}
|
3065
3423
|
});
|
3066
3424
|
const components = ({ strapi: strapi2 }) => ({
|
3067
3425
|
findAllComponents() {
|
3068
|
-
const { toContentManagerModel } = getService$
|
3426
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3069
3427
|
return Object.values(strapi2.components).map(toContentManagerModel);
|
3070
3428
|
},
|
3071
3429
|
findComponent(uid2) {
|
3072
|
-
const { toContentManagerModel } = getService$
|
3430
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3073
3431
|
const component = strapi2.components[uid2];
|
3074
3432
|
return isNil$1(component) ? component : toContentManagerModel(component);
|
3075
3433
|
},
|
@@ -3120,17 +3478,17 @@ const configurationService = createConfigurationService({
|
|
3120
3478
|
storeUtils,
|
3121
3479
|
prefix: "content_types",
|
3122
3480
|
getModels() {
|
3123
|
-
const { toContentManagerModel } = getService$
|
3481
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3124
3482
|
return mapValues(toContentManagerModel, strapi.contentTypes);
|
3125
3483
|
}
|
3126
3484
|
});
|
3127
3485
|
const service = ({ strapi: strapi2 }) => ({
|
3128
3486
|
findAllContentTypes() {
|
3129
|
-
const { toContentManagerModel } = getService$
|
3487
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3130
3488
|
return Object.values(strapi2.contentTypes).map(toContentManagerModel);
|
3131
3489
|
},
|
3132
3490
|
findContentType(uid2) {
|
3133
|
-
const { toContentManagerModel } = getService$
|
3491
|
+
const { toContentManagerModel } = getService$2("data-mapper");
|
3134
3492
|
const contentType = strapi2.contentTypes[uid2];
|
3135
3493
|
return isNil$1(contentType) ? contentType : toContentManagerModel(contentType);
|
3136
3494
|
},
|
@@ -3159,7 +3517,7 @@ const service = ({ strapi: strapi2 }) => ({
|
|
3159
3517
|
return this.findConfiguration(contentType);
|
3160
3518
|
},
|
3161
3519
|
findComponentsConfigurations(contentType) {
|
3162
|
-
return getService$
|
3520
|
+
return getService$2("components").findComponentsConfigurations(contentType);
|
3163
3521
|
},
|
3164
3522
|
syncConfigurations() {
|
3165
3523
|
return configurationService.syncConfigurations();
|
@@ -3340,12 +3698,27 @@ const createPermissionChecker = (strapi2) => ({ userAbility, model }) => {
|
|
3340
3698
|
ability: userAbility,
|
3341
3699
|
model
|
3342
3700
|
});
|
3343
|
-
const
|
3701
|
+
const { actionProvider } = strapi2.service("admin::permission");
|
3702
|
+
const toSubject = (entity) => {
|
3703
|
+
return entity ? permissionsManager.toSubject(entity, model) : model;
|
3704
|
+
};
|
3344
3705
|
const can = (action, entity, field) => {
|
3345
|
-
|
3706
|
+
const subject = toSubject(entity);
|
3707
|
+
const aliases = actionProvider.unstable_aliases(action, model);
|
3708
|
+
return (
|
3709
|
+
// Test the original action to see if it passes
|
3710
|
+
userAbility.can(action, subject, field) || // Else try every known alias if at least one of them succeed, then the user "can"
|
3711
|
+
aliases.some((alias) => userAbility.can(alias, subject, field))
|
3712
|
+
);
|
3346
3713
|
};
|
3347
3714
|
const cannot = (action, entity, field) => {
|
3348
|
-
|
3715
|
+
const subject = toSubject(entity);
|
3716
|
+
const aliases = actionProvider.unstable_aliases(action, model);
|
3717
|
+
return (
|
3718
|
+
// Test both the original action
|
3719
|
+
userAbility.cannot(action, subject, field) && // and every known alias, if all of them fail (cannot), then the user truly "cannot"
|
3720
|
+
aliases.every((alias) => userAbility.cannot(alias, subject, field))
|
3721
|
+
);
|
3349
3722
|
};
|
3350
3723
|
const sanitizeOutput = (data, { action = ACTIONS.read } = {}) => {
|
3351
3724
|
return permissionsManager.sanitizeOutput(data, { subject: toSubject(data), action });
|
@@ -3416,7 +3789,7 @@ const permission = ({ strapi: strapi2 }) => ({
|
|
3416
3789
|
return userAbility.can(action);
|
3417
3790
|
},
|
3418
3791
|
async registerPermissions() {
|
3419
|
-
const displayedContentTypes = getService$
|
3792
|
+
const displayedContentTypes = getService$2("content-types").findDisplayedContentTypes();
|
3420
3793
|
const contentTypesUids = displayedContentTypes.map(prop("uid"));
|
3421
3794
|
const actions = [
|
3422
3795
|
{
|
@@ -3488,7 +3861,7 @@ const permission = ({ strapi: strapi2 }) => ({
|
|
3488
3861
|
await strapi2.service("admin::permission").actionProvider.registerMany(actions);
|
3489
3862
|
}
|
3490
3863
|
});
|
3491
|
-
const { isVisibleAttribute: isVisibleAttribute$1 } = strapiUtils.contentTypes;
|
3864
|
+
const { isVisibleAttribute: isVisibleAttribute$1, isScalarAttribute, getDoesAttributeRequireValidation } = strapiUtils.contentTypes;
|
3492
3865
|
const { isAnyToMany } = strapiUtils.relations;
|
3493
3866
|
const { PUBLISHED_AT_ATTRIBUTE: PUBLISHED_AT_ATTRIBUTE$1 } = strapiUtils.contentTypes.constants;
|
3494
3867
|
const isMorphToRelation = (attribute) => isRelation(attribute) && attribute.relation.includes("morphTo");
|
@@ -3579,6 +3952,42 @@ const getDeepPopulate = (uid2, {
|
|
3579
3952
|
{}
|
3580
3953
|
);
|
3581
3954
|
};
|
3955
|
+
const getValidatableFieldsPopulate = (uid2, {
|
3956
|
+
initialPopulate = {},
|
3957
|
+
countMany = false,
|
3958
|
+
countOne = false,
|
3959
|
+
maxLevel = Infinity
|
3960
|
+
} = {}, level = 1) => {
|
3961
|
+
if (level > maxLevel) {
|
3962
|
+
return {};
|
3963
|
+
}
|
3964
|
+
const model = strapi.getModel(uid2);
|
3965
|
+
return Object.entries(model.attributes).reduce((populateAcc, [attributeName, attribute]) => {
|
3966
|
+
if (!getDoesAttributeRequireValidation(attribute)) {
|
3967
|
+
return populateAcc;
|
3968
|
+
}
|
3969
|
+
if (isScalarAttribute(attribute)) {
|
3970
|
+
return merge(populateAcc, {
|
3971
|
+
[attributeName]: true
|
3972
|
+
});
|
3973
|
+
}
|
3974
|
+
return merge(
|
3975
|
+
populateAcc,
|
3976
|
+
getPopulateFor(
|
3977
|
+
attributeName,
|
3978
|
+
model,
|
3979
|
+
{
|
3980
|
+
// @ts-expect-error - improve types
|
3981
|
+
initialPopulate: initialPopulate?.[attributeName],
|
3982
|
+
countMany,
|
3983
|
+
countOne,
|
3984
|
+
maxLevel
|
3985
|
+
},
|
3986
|
+
level
|
3987
|
+
)
|
3988
|
+
);
|
3989
|
+
}, {});
|
3990
|
+
};
|
3582
3991
|
const getDeepPopulateDraftCount = (uid2) => {
|
3583
3992
|
const model = strapi.getModel(uid2);
|
3584
3993
|
let hasRelations = false;
|
@@ -3586,6 +3995,10 @@ const getDeepPopulateDraftCount = (uid2) => {
|
|
3586
3995
|
const attribute = model.attributes[attributeName];
|
3587
3996
|
switch (attribute.type) {
|
3588
3997
|
case "relation": {
|
3998
|
+
const isMorphRelation = attribute.relation.toLowerCase().startsWith("morph");
|
3999
|
+
if (isMorphRelation) {
|
4000
|
+
break;
|
4001
|
+
}
|
3589
4002
|
if (isVisibleAttribute$1(model, attributeName)) {
|
3590
4003
|
populateAcc[attributeName] = {
|
3591
4004
|
count: true,
|
@@ -3600,22 +4013,24 @@ const getDeepPopulateDraftCount = (uid2) => {
|
|
3600
4013
|
attribute.component
|
3601
4014
|
);
|
3602
4015
|
if (childHasRelations) {
|
3603
|
-
populateAcc[attributeName] = {
|
4016
|
+
populateAcc[attributeName] = {
|
4017
|
+
populate: populate2
|
4018
|
+
};
|
3604
4019
|
hasRelations = true;
|
3605
4020
|
}
|
3606
4021
|
break;
|
3607
4022
|
}
|
3608
4023
|
case "dynamiczone": {
|
3609
|
-
const
|
3610
|
-
const { populate:
|
3611
|
-
if (
|
4024
|
+
const dzPopulateFragment = attribute.components?.reduce((acc, componentUID) => {
|
4025
|
+
const { populate: componentPopulate, hasRelations: componentHasRelations } = getDeepPopulateDraftCount(componentUID);
|
4026
|
+
if (componentHasRelations) {
|
3612
4027
|
hasRelations = true;
|
3613
|
-
return
|
4028
|
+
return { ...acc, [componentUID]: { populate: componentPopulate } };
|
3614
4029
|
}
|
3615
4030
|
return acc;
|
3616
4031
|
}, {});
|
3617
|
-
if (!isEmpty(
|
3618
|
-
populateAcc[attributeName] = {
|
4032
|
+
if (!isEmpty(dzPopulateFragment)) {
|
4033
|
+
populateAcc[attributeName] = { on: dzPopulateFragment };
|
3619
4034
|
}
|
3620
4035
|
break;
|
3621
4036
|
}
|
@@ -3650,7 +4065,7 @@ const getQueryPopulate = async (uid2, query) => {
|
|
3650
4065
|
return populateQuery;
|
3651
4066
|
};
|
3652
4067
|
const buildDeepPopulate = (uid2) => {
|
3653
|
-
return getService$
|
4068
|
+
return getService$2("populate-builder")(uid2).populateDeep(Infinity).countRelations().build();
|
3654
4069
|
};
|
3655
4070
|
const populateBuilder = (uid2) => {
|
3656
4071
|
let getInitialPopulate = async () => {
|
@@ -3807,41 +4222,72 @@ const AVAILABLE_STATUS_FIELDS = [
|
|
3807
4222
|
"updatedBy",
|
3808
4223
|
"status"
|
3809
4224
|
];
|
3810
|
-
const AVAILABLE_LOCALES_FIELDS = [
|
4225
|
+
const AVAILABLE_LOCALES_FIELDS = [
|
4226
|
+
"id",
|
4227
|
+
"locale",
|
4228
|
+
"updatedAt",
|
4229
|
+
"createdAt",
|
4230
|
+
"status",
|
4231
|
+
"publishedAt",
|
4232
|
+
"documentId"
|
4233
|
+
];
|
3811
4234
|
const CONTENT_MANAGER_STATUS = {
|
3812
4235
|
PUBLISHED: "published",
|
3813
4236
|
DRAFT: "draft",
|
3814
4237
|
MODIFIED: "modified"
|
3815
4238
|
};
|
3816
|
-
const
|
3817
|
-
if (!
|
4239
|
+
const getIsVersionLatestModification = (version, otherVersion) => {
|
4240
|
+
if (!version || !version.updatedAt) {
|
3818
4241
|
return false;
|
3819
4242
|
}
|
3820
|
-
const
|
3821
|
-
const
|
3822
|
-
|
3823
|
-
return difference2 <= threshold;
|
4243
|
+
const versionUpdatedAt = version?.updatedAt ? new Date(version.updatedAt).getTime() : 0;
|
4244
|
+
const otherUpdatedAt = otherVersion?.updatedAt ? new Date(otherVersion.updatedAt).getTime() : 0;
|
4245
|
+
return versionUpdatedAt > otherUpdatedAt;
|
3824
4246
|
};
|
3825
4247
|
const documentMetadata = ({ strapi: strapi2 }) => ({
|
3826
4248
|
/**
|
3827
4249
|
* Returns available locales of a document for the current status
|
3828
4250
|
*/
|
3829
|
-
getAvailableLocales(uid2, version, allVersions) {
|
4251
|
+
async getAvailableLocales(uid2, version, allVersions, validatableFields = []) {
|
3830
4252
|
const versionsByLocale = groupBy("locale", allVersions);
|
3831
|
-
|
3832
|
-
|
3833
|
-
|
3834
|
-
|
4253
|
+
if (version.locale) {
|
4254
|
+
delete versionsByLocale[version.locale];
|
4255
|
+
}
|
4256
|
+
const model = strapi2.getModel(uid2);
|
4257
|
+
const keysToKeep = [...AVAILABLE_LOCALES_FIELDS, ...validatableFields];
|
4258
|
+
const traversalFunction = async (localeVersion) => traverseEntity(
|
4259
|
+
({ key }, { remove }) => {
|
4260
|
+
if (keysToKeep.includes(key)) {
|
4261
|
+
return;
|
4262
|
+
}
|
4263
|
+
remove(key);
|
4264
|
+
},
|
4265
|
+
{ schema: model, getModel: strapi2.getModel.bind(strapi2) },
|
4266
|
+
// @ts-expect-error fix types DocumentVersion incompatible with Data
|
4267
|
+
localeVersion
|
4268
|
+
);
|
4269
|
+
const mappingResult = await async.map(
|
4270
|
+
Object.values(versionsByLocale),
|
4271
|
+
async (localeVersions) => {
|
4272
|
+
const mappedLocaleVersions = await async.map(
|
4273
|
+
localeVersions,
|
4274
|
+
traversalFunction
|
4275
|
+
);
|
4276
|
+
if (!contentTypes$1.hasDraftAndPublish(model)) {
|
4277
|
+
return mappedLocaleVersions[0];
|
4278
|
+
}
|
4279
|
+
const draftVersion = mappedLocaleVersions.find((v) => v.publishedAt === null);
|
4280
|
+
const otherVersions = mappedLocaleVersions.filter((v) => v.id !== draftVersion?.id);
|
4281
|
+
if (!draftVersion) {
|
4282
|
+
return;
|
4283
|
+
}
|
4284
|
+
return {
|
4285
|
+
...draftVersion,
|
4286
|
+
status: this.getStatus(draftVersion, otherVersions)
|
4287
|
+
};
|
3835
4288
|
}
|
3836
|
-
|
3837
|
-
|
3838
|
-
if (!draftVersion)
|
3839
|
-
return;
|
3840
|
-
return {
|
3841
|
-
...pick(AVAILABLE_LOCALES_FIELDS, draftVersion),
|
3842
|
-
status: this.getStatus(draftVersion, otherVersions)
|
3843
|
-
};
|
3844
|
-
}).filter(Boolean);
|
4289
|
+
);
|
4290
|
+
return mappingResult.filter(Boolean);
|
3845
4291
|
},
|
3846
4292
|
/**
|
3847
4293
|
* Returns available status of a document for the current locale
|
@@ -3879,26 +4325,37 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3879
4325
|
});
|
3880
4326
|
},
|
3881
4327
|
getStatus(version, otherDocumentStatuses) {
|
3882
|
-
|
3883
|
-
|
3884
|
-
|
4328
|
+
let draftVersion;
|
4329
|
+
let publishedVersion;
|
4330
|
+
if (version.publishedAt) {
|
4331
|
+
publishedVersion = version;
|
4332
|
+
} else {
|
4333
|
+
draftVersion = version;
|
3885
4334
|
}
|
3886
|
-
|
3887
|
-
|
3888
|
-
|
3889
|
-
|
3890
|
-
|
4335
|
+
const otherVersion = otherDocumentStatuses?.at(0);
|
4336
|
+
if (otherVersion?.publishedAt) {
|
4337
|
+
publishedVersion = otherVersion;
|
4338
|
+
} else if (otherVersion) {
|
4339
|
+
draftVersion = otherVersion;
|
3891
4340
|
}
|
3892
|
-
if (
|
4341
|
+
if (!draftVersion)
|
3893
4342
|
return CONTENT_MANAGER_STATUS.PUBLISHED;
|
3894
|
-
|
3895
|
-
|
4343
|
+
if (!publishedVersion)
|
4344
|
+
return CONTENT_MANAGER_STATUS.DRAFT;
|
4345
|
+
const isDraftModified = getIsVersionLatestModification(draftVersion, publishedVersion);
|
4346
|
+
return isDraftModified ? CONTENT_MANAGER_STATUS.MODIFIED : CONTENT_MANAGER_STATUS.PUBLISHED;
|
3896
4347
|
},
|
4348
|
+
// TODO is it necessary to return metadata on every page of the CM
|
4349
|
+
// We could refactor this so the locales are only loaded when they're
|
4350
|
+
// needed. e.g. in the bulk locale action modal.
|
3897
4351
|
async getMetadata(uid2, version, { availableLocales = true, availableStatus = true } = {}) {
|
4352
|
+
const populate = getValidatableFieldsPopulate(uid2);
|
3898
4353
|
const versions = await strapi2.db.query(uid2).findMany({
|
3899
4354
|
where: { documentId: version.documentId },
|
3900
|
-
select: ["createdAt", "updatedAt", "locale", "publishedAt", "documentId"],
|
3901
4355
|
populate: {
|
4356
|
+
// Populate only fields that require validation for bulk locale actions
|
4357
|
+
...populate,
|
4358
|
+
// NOTE: creator fields are selected in this way to avoid exposing sensitive data
|
3902
4359
|
createdBy: {
|
3903
4360
|
select: ["id", "firstname", "lastname", "email"]
|
3904
4361
|
},
|
@@ -3907,7 +4364,7 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3907
4364
|
}
|
3908
4365
|
}
|
3909
4366
|
});
|
3910
|
-
const availableLocalesResult = availableLocales ? this.getAvailableLocales(uid2, version, versions) : [];
|
4367
|
+
const availableLocalesResult = availableLocales ? await this.getAvailableLocales(uid2, version, versions, Object.keys(populate)) : [];
|
3911
4368
|
const availableStatusResult = availableStatus ? this.getAvailableStatus(version, versions) : null;
|
3912
4369
|
return {
|
3913
4370
|
availableLocales: availableLocalesResult,
|
@@ -3920,8 +4377,15 @@ const documentMetadata = ({ strapi: strapi2 }) => ({
|
|
3920
4377
|
* - Available status of the document for the current locale
|
3921
4378
|
*/
|
3922
4379
|
async formatDocumentWithMetadata(uid2, document, opts = {}) {
|
3923
|
-
if (!document)
|
3924
|
-
return
|
4380
|
+
if (!document) {
|
4381
|
+
return {
|
4382
|
+
data: document,
|
4383
|
+
meta: {
|
4384
|
+
availableLocales: [],
|
4385
|
+
availableStatus: []
|
4386
|
+
}
|
4387
|
+
};
|
4388
|
+
}
|
3925
4389
|
const hasDraftAndPublish = contentTypes$1.hasDraftAndPublish(strapi2.getModel(uid2));
|
3926
4390
|
if (!hasDraftAndPublish) {
|
3927
4391
|
opts.availableStatus = false;
|
@@ -3971,26 +4435,9 @@ const sumDraftCounts = (entity, uid2) => {
|
|
3971
4435
|
}, 0);
|
3972
4436
|
};
|
3973
4437
|
const { ApplicationError } = errors;
|
3974
|
-
const { ENTRY_PUBLISH, ENTRY_UNPUBLISH } = ALLOWED_WEBHOOK_EVENTS;
|
3975
4438
|
const { PUBLISHED_AT_ATTRIBUTE } = contentTypes$1.constants;
|
3976
4439
|
const omitPublishedAtField = omit(PUBLISHED_AT_ATTRIBUTE);
|
3977
4440
|
const omitIdField = omit("id");
|
3978
|
-
const emitEvent = async (uid2, event, document) => {
|
3979
|
-
const modelDef = strapi.getModel(uid2);
|
3980
|
-
const sanitizedDocument = await sanitize.sanitizers.defaultSanitizeOutput(
|
3981
|
-
{
|
3982
|
-
schema: modelDef,
|
3983
|
-
getModel(uid22) {
|
3984
|
-
return strapi.getModel(uid22);
|
3985
|
-
}
|
3986
|
-
},
|
3987
|
-
document
|
3988
|
-
);
|
3989
|
-
strapi.eventHub.emit(event, {
|
3990
|
-
model: modelDef.modelName,
|
3991
|
-
entry: sanitizedDocument
|
3992
|
-
});
|
3993
|
-
};
|
3994
4441
|
const documentManager = ({ strapi: strapi2 }) => {
|
3995
4442
|
return {
|
3996
4443
|
async findOne(id, uid2, opts = {}) {
|
@@ -4009,6 +4456,9 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4009
4456
|
} else if (opts.locale && opts.locale !== "*") {
|
4010
4457
|
where.locale = opts.locale;
|
4011
4458
|
}
|
4459
|
+
if (typeof opts.isPublished === "boolean") {
|
4460
|
+
where.publishedAt = { $notNull: opts.isPublished };
|
4461
|
+
}
|
4012
4462
|
return strapi2.db.query(uid2).findMany({ populate: opts.populate, where });
|
4013
4463
|
},
|
4014
4464
|
async findMany(opts, uid2) {
|
@@ -4016,20 +4466,16 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4016
4466
|
return strapi2.documents(uid2).findMany(params);
|
4017
4467
|
},
|
4018
4468
|
async findPage(opts, uid2) {
|
4019
|
-
const
|
4020
|
-
|
4469
|
+
const params = pagination.withDefaultPagination(opts || {}, {
|
4470
|
+
maxLimit: 1e3
|
4471
|
+
});
|
4021
4472
|
const [documents, total = 0] = await Promise.all([
|
4022
|
-
strapi2.documents(uid2).findMany(
|
4023
|
-
strapi2.documents(uid2).count(
|
4473
|
+
strapi2.documents(uid2).findMany(params),
|
4474
|
+
strapi2.documents(uid2).count(params)
|
4024
4475
|
]);
|
4025
4476
|
return {
|
4026
4477
|
results: documents,
|
4027
|
-
pagination:
|
4028
|
-
page,
|
4029
|
-
pageSize,
|
4030
|
-
pageCount: Math.ceil(total / pageSize),
|
4031
|
-
total
|
4032
|
-
}
|
4478
|
+
pagination: pagination.transformPagedPaginationInfo(params, total)
|
4033
4479
|
};
|
4034
4480
|
},
|
4035
4481
|
async create(uid2, opts = {}) {
|
@@ -4046,10 +4492,7 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4046
4492
|
async clone(id, body, uid2) {
|
4047
4493
|
const populate = await buildDeepPopulate(uid2);
|
4048
4494
|
const params = {
|
4049
|
-
data:
|
4050
|
-
...omitIdField(body),
|
4051
|
-
[PUBLISHED_AT_ATTRIBUTE]: null
|
4052
|
-
},
|
4495
|
+
data: omitIdField(body),
|
4053
4496
|
populate
|
4054
4497
|
};
|
4055
4498
|
return strapi2.documents(uid2).clone({ ...params, documentId: id }).then((result) => result?.entries.at(0));
|
@@ -4075,70 +4518,36 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4075
4518
|
return {};
|
4076
4519
|
},
|
4077
4520
|
// FIXME: handle relations
|
4078
|
-
async deleteMany(
|
4079
|
-
const
|
4080
|
-
|
4081
|
-
|
4082
|
-
}
|
4083
|
-
return { count: docs.length };
|
4521
|
+
async deleteMany(documentIds, uid2, opts = {}) {
|
4522
|
+
const deletedEntries = await strapi2.db.transaction(async () => {
|
4523
|
+
return Promise.all(documentIds.map(async (id) => this.delete(id, uid2, opts)));
|
4524
|
+
});
|
4525
|
+
return { count: deletedEntries.length };
|
4084
4526
|
},
|
4085
4527
|
async publish(id, uid2, opts = {}) {
|
4086
4528
|
const populate = await buildDeepPopulate(uid2);
|
4087
4529
|
const params = { ...opts, populate };
|
4088
|
-
return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries
|
4530
|
+
return strapi2.documents(uid2).publish({ ...params, documentId: id }).then((result) => result?.entries);
|
4089
4531
|
},
|
4090
|
-
async publishMany(
|
4091
|
-
|
4092
|
-
|
4093
|
-
|
4094
|
-
|
4095
|
-
|
4096
|
-
|
4097
|
-
strapi2.getModel(uid2),
|
4098
|
-
document,
|
4099
|
-
void 0,
|
4100
|
-
// @ts-expect-error - FIXME: entity here is unnecessary
|
4101
|
-
document
|
4102
|
-
);
|
4103
|
-
})
|
4104
|
-
);
|
4105
|
-
const entitiesToPublish = entities.filter((doc) => !doc[PUBLISHED_AT_ATTRIBUTE]).map((doc) => doc.id);
|
4106
|
-
const filters = { id: { $in: entitiesToPublish } };
|
4107
|
-
const data = { [PUBLISHED_AT_ATTRIBUTE]: /* @__PURE__ */ new Date() };
|
4108
|
-
const populate = await buildDeepPopulate(uid2);
|
4109
|
-
const publishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
|
4110
|
-
where: filters,
|
4111
|
-
data
|
4112
|
-
});
|
4113
|
-
const publishedEntities = await strapi2.db.query(uid2).findMany({
|
4114
|
-
where: filters,
|
4115
|
-
populate
|
4532
|
+
async publishMany(uid2, documentIds, locale) {
|
4533
|
+
return strapi2.db.transaction(async () => {
|
4534
|
+
const results = await Promise.all(
|
4535
|
+
documentIds.map((documentId) => this.publish(documentId, uid2, { locale }))
|
4536
|
+
);
|
4537
|
+
const publishedEntitiesCount = results.flat().filter(Boolean).length;
|
4538
|
+
return publishedEntitiesCount;
|
4116
4539
|
});
|
4117
|
-
await Promise.all(
|
4118
|
-
publishedEntities.map((doc) => emitEvent(uid2, ENTRY_PUBLISH, doc))
|
4119
|
-
);
|
4120
|
-
return publishedEntitiesCount;
|
4121
4540
|
},
|
4122
|
-
async unpublishMany(
|
4123
|
-
|
4124
|
-
return
|
4125
|
-
|
4126
|
-
|
4127
|
-
|
4128
|
-
|
4129
|
-
const populate = await buildDeepPopulate(uid2);
|
4130
|
-
const unpublishedEntitiesCount = await strapi2.db.query(uid2).updateMany({
|
4131
|
-
where: filters,
|
4132
|
-
data
|
4133
|
-
});
|
4134
|
-
const unpublishedEntities = await strapi2.db.query(uid2).findMany({
|
4135
|
-
where: filters,
|
4136
|
-
populate
|
4541
|
+
async unpublishMany(documentIds, uid2, opts = {}) {
|
4542
|
+
const unpublishedEntries = await strapi2.db.transaction(async () => {
|
4543
|
+
return Promise.all(
|
4544
|
+
documentIds.map(
|
4545
|
+
(id) => strapi2.documents(uid2).unpublish({ ...opts, documentId: id }).then((result) => result?.entries)
|
4546
|
+
)
|
4547
|
+
);
|
4137
4548
|
});
|
4138
|
-
|
4139
|
-
|
4140
|
-
);
|
4141
|
-
return unpublishedEntitiesCount;
|
4549
|
+
const unpublishedEntitiesCount = unpublishedEntries.flat().filter(Boolean).length;
|
4550
|
+
return { count: unpublishedEntitiesCount };
|
4142
4551
|
},
|
4143
4552
|
async unpublish(id, uid2, opts = {}) {
|
4144
4553
|
const populate = await buildDeepPopulate(uid2);
|
@@ -4163,16 +4572,20 @@ const documentManager = ({ strapi: strapi2 }) => {
|
|
4163
4572
|
}
|
4164
4573
|
return sumDraftCounts(document, uid2);
|
4165
4574
|
},
|
4166
|
-
async countManyEntriesDraftRelations(
|
4575
|
+
async countManyEntriesDraftRelations(documentIds, uid2, locale) {
|
4167
4576
|
const { populate, hasRelations } = getDeepPopulateDraftCount(uid2);
|
4168
4577
|
if (!hasRelations) {
|
4169
4578
|
return 0;
|
4170
4579
|
}
|
4580
|
+
let localeFilter = {};
|
4581
|
+
if (locale) {
|
4582
|
+
localeFilter = Array.isArray(locale) ? { locale: { $in: locale } } : { locale };
|
4583
|
+
}
|
4171
4584
|
const entities = await strapi2.db.query(uid2).findMany({
|
4172
4585
|
populate,
|
4173
4586
|
where: {
|
4174
|
-
|
4175
|
-
...
|
4587
|
+
documentId: { $in: documentIds },
|
4588
|
+
...localeFilter
|
4176
4589
|
}
|
4177
4590
|
});
|
4178
4591
|
const totalNumberDraftRelations = entities.reduce(
|
@@ -4195,7 +4608,8 @@ const services = {
|
|
4195
4608
|
permission,
|
4196
4609
|
"populate-builder": populateBuilder$1,
|
4197
4610
|
uid,
|
4198
|
-
...history.services ? history.services : {}
|
4611
|
+
...history.services ? history.services : {},
|
4612
|
+
...preview.services ? preview.services : {}
|
4199
4613
|
};
|
4200
4614
|
const index = () => {
|
4201
4615
|
return {
|