From 7e3465f37a008ebb2f12d2689aff2d70431f6deb Mon Sep 17 00:00:00 2001 From: wesleybl <wesleybl@gmail.com> Date: Fri, 4 Oct 2024 14:46:19 -0300 Subject: [PATCH 1/3] Pass the `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings --- docs/source/recipes/how-to-restrict-blocks.md | 8 +++++-- news/6264.feature | 1 + .../volto-slate/src/blocks/Text/SlashMenu.jsx | 16 ++++++++++++- .../manage/BlockChooser/BlockChooser.jsx | 10 +++++++- .../manage/BlockChooser/BlockChooser.test.jsx | 4 ++++ src/hooks/index.js | 1 + src/hooks/user/useUser.js | 23 +++++++++++++++++++ 7 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 news/6264.feature create mode 100644 src/hooks/user/useUser.js diff --git a/docs/source/recipes/how-to-restrict-blocks.md b/docs/source/recipes/how-to-restrict-blocks.md index b757215766..179323ef2c 100644 --- a/docs/source/recipes/how-to-restrict-blocks.md +++ b/docs/source/recipes/how-to-restrict-blocks.md @@ -22,12 +22,16 @@ The function has this signature: block: BlockConfigBase; navRoot: Content; contentType: string; + user: Object }) => boolean; } ``` -Where `properties` is the current object data and `block` is the block being evaluated in `BlockChooser`. -`navRoot` is the nearest navigation root object and `contentType` is the current content type. +`properties` is the current object data. +`block` is the block being evaluated in `BlockChooser`. +`navRoot` is the nearest navigation root object. +`contentType` is the current content type. +`user` is an object that represents the currently authenticated user. In the following configuration example, you can restrict a block so that it cannot be added unless the content type is `News Item` or the content item is in a specific path in the content tree (`/folder`): diff --git a/news/6264.feature b/news/6264.feature new file mode 100644 index 0000000000..e8a31aaf20 --- /dev/null +++ b/news/6264.feature @@ -0,0 +1 @@ +Pass the `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings. @wesleybl diff --git a/packages/volto-slate/src/blocks/Text/SlashMenu.jsx b/packages/volto-slate/src/blocks/Text/SlashMenu.jsx index de312e3d89..947a1f3cda 100644 --- a/packages/volto-slate/src/blocks/Text/SlashMenu.jsx +++ b/packages/volto-slate/src/blocks/Text/SlashMenu.jsx @@ -4,6 +4,7 @@ import { filter, isEmpty } from 'lodash'; import { Menu } from 'semantic-ui-react'; import { useIntl, FormattedMessage } from 'react-intl'; import { Icon } from '@plone/volto/components'; +import { useUser } from '@plone/volto/hooks'; const emptySlateBlock = () => ({ value: [ @@ -105,9 +106,13 @@ const PersistentSlashMenu = ({ editor }) => { selected, allowedBlocks, detached, + navRoot, + contentType, } = props; const disableNewBlocks = data?.disableNewBlocks || detached; + const user = useUser(); + const [slashMenuSelected, setSlashMenuSelected] = React.useState(0); const hasAllowedBlocks = !isEmpty(allowedBlocks); @@ -122,7 +127,13 @@ const PersistentSlashMenu = ({ editor }) => { hasAllowedBlocks ? allowedBlocks.includes(item.id) : typeof item.restricted === 'function' - ? !item.restricted({ properties, block: item }) + ? !item.restricted({ + properties, + block: item, + navRoot, + contentType, + user, + }) : !item.restricted, ) .filter((block) => Boolean(block.title && block.id)) @@ -152,6 +163,9 @@ const PersistentSlashMenu = ({ editor }) => { properties, slashCommand, hasAllowedBlocks, + navRoot, + contentType, + user, ], ); diff --git a/src/components/manage/BlockChooser/BlockChooser.jsx b/src/components/manage/BlockChooser/BlockChooser.jsx index 69b3d87c38..2ae878e5ba 100644 --- a/src/components/manage/BlockChooser/BlockChooser.jsx +++ b/src/components/manage/BlockChooser/BlockChooser.jsx @@ -1,4 +1,5 @@ import React from 'react'; +import { useUser } from '@plone/volto/hooks'; import PropTypes from 'prop-types'; import { filter, map, groupBy, isEmpty } from 'lodash'; import { Accordion, Button } from 'semantic-ui-react'; @@ -35,6 +36,7 @@ const BlockChooser = ({ contentType, }) => { const intl = useIntl(); + const user = useUser(); const hasAllowedBlocks = !isEmpty(allowedBlocks); const filteredBlocksConfig = filter(blocksConfig, (item) => { @@ -57,7 +59,13 @@ const BlockChooser = ({ // depending on this function, given properties (current present blocks) and the // block being evaluated return typeof item.restricted === 'function' - ? !item.restricted({ properties, block: item, navRoot, contentType }) + ? !item.restricted({ + properties, + block: item, + navRoot, + contentType, + user, + }) : !item.restricted; } } diff --git a/src/components/manage/BlockChooser/BlockChooser.test.jsx b/src/components/manage/BlockChooser/BlockChooser.test.jsx index 22fd5b5f48..bb9dd2b4e7 100644 --- a/src/components/manage/BlockChooser/BlockChooser.test.jsx +++ b/src/components/manage/BlockChooser/BlockChooser.test.jsx @@ -5,6 +5,7 @@ import { Provider } from 'react-intl-redux'; import configureStore from 'redux-mock-store'; import BlockChooser from './BlockChooser'; import config from '@plone/volto/registry'; +import jwt from 'jsonwebtoken'; const blockSVG = {}; @@ -122,6 +123,9 @@ const store = mockStore({ locale: 'en', messages: {}, }, + userSession: { + token: jwt.sign({ fullname: 'John Doe' }, 'secret'), + }, }); describe('BlocksChooser', () => { diff --git a/src/hooks/index.js b/src/hooks/index.js index a955aa8285..f8e327c0e6 100644 --- a/src/hooks/index.js +++ b/src/hooks/index.js @@ -1,2 +1,3 @@ export { default as useClipboard } from '@plone/volto/hooks/clipboard/useClipboard'; export { useClient } from '@plone/volto/hooks/client/useClient'; +export { default as useUser } from '@plone/volto/hooks/user/useUser'; diff --git a/src/hooks/user/useUser.js b/src/hooks/user/useUser.js new file mode 100644 index 0000000000..3232ed0a8e --- /dev/null +++ b/src/hooks/user/useUser.js @@ -0,0 +1,23 @@ +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import jwtDecode from 'jwt-decode'; +import { getUser } from '@plone/volto/actions'; + +const useUser = () => { + const users = useSelector((state) => state.users); + const user = users?.user; + const userId = useSelector((state) => + state.userSession.token ? jwtDecode(state.userSession.token).sub : '', + ); + const dispatch = useDispatch(); + + useEffect(() => { + if (!user?.id && users?.get.loading === false) { + dispatch(getUser(userId)); + } + }, [dispatch, userId, user, users?.get.loading]); + + return user; +}; + +export default useUser; From 3d0a09845772e806e4c97dc2e837c312725525c0 Mon Sep 17 00:00:00 2001 From: Steve Piercy <web@stevepiercy.com> Date: Fri, 4 Oct 2024 14:35:46 -0700 Subject: [PATCH 2/3] Convert wall of text to a definition list to improve readability --- docs/source/recipes/how-to-restrict-blocks.md | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/docs/source/recipes/how-to-restrict-blocks.md b/docs/source/recipes/how-to-restrict-blocks.md index 179323ef2c..16ef380c10 100644 --- a/docs/source/recipes/how-to-restrict-blocks.md +++ b/docs/source/recipes/how-to-restrict-blocks.md @@ -27,11 +27,20 @@ The function has this signature: } ``` -`properties` is the current object data. -`block` is the block being evaluated in `BlockChooser`. -`navRoot` is the nearest navigation root object. -`contentType` is the current content type. -`user` is an object that represents the currently authenticated user. +`properties` +: The current object data. + +`block` +: The block being evaluated in `BlockChooser`. + +`navRoot` +: The nearest navigation root object. + +`contentType` +: The current content type. + +`user` +: An object that represents the currently authenticated user. In the following configuration example, you can restrict a block so that it cannot be added unless the content type is `News Item` or the content item is in a specific path in the content tree (`/folder`): From c4d4cd06af530df6e5466ab6b37b509d59757e61 Mon Sep 17 00:00:00 2001 From: Steve Piercy <web@stevepiercy.com> Date: Fri, 4 Oct 2024 14:36:19 -0700 Subject: [PATCH 3/3] Update news/6264.feature Oxford comma --- news/6264.feature | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/news/6264.feature b/news/6264.feature index e8a31aaf20..505e64c3b6 100644 --- a/news/6264.feature +++ b/news/6264.feature @@ -1 +1 @@ -Pass the `user`, `navRoot` and `contentType` objects to the `restricted` function of the block settings. @wesleybl +Pass the `user`, `navRoot`, and `contentType` objects to the `restricted` function of the block settings. @wesleybl