diff --git a/packages/core/admin/ee/admin/constants.js b/packages/core/admin/ee/admin/constants.js index adef94a42c..058bea99bc 100644 --- a/packages/core/admin/ee/admin/constants.js +++ b/packages/core/admin/ee/admin/constants.js @@ -6,6 +6,9 @@ export const ADMIN_PERMISSIONS_EE = { }, 'review-workflows': { main: [{ action: 'admin::review-workflows.read', subject: null }], + create: [{ action: 'admin::review-workflows.create', subject: null }], + delete: [{ action: 'admin::review-workflows.delete', subject: null }], + update: [{ action: 'admin::review-workflows.update', subject: null }], }, sso: { main: [{ action: 'admin::provider-login.read', subject: null }], diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/ProtectedPage/ProtectedPage.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/ProtectedPage/ProtectedPage.js deleted file mode 100644 index 632c6a38c5..0000000000 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/ProtectedPage/ProtectedPage.js +++ /dev/null @@ -1,21 +0,0 @@ -import React from 'react'; - -import { CheckPagePermissions } from '@strapi/helper-plugin'; -import PropTypes from 'prop-types'; -import { useSelector } from 'react-redux'; - -import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors'; - -export function ProtectedPage({ children }) { - const permissions = useSelector(selectAdminPermissions); - - return ( - - {children} - - ); -} - -ProtectedPage.propTypes = { - children: PropTypes.node.isRequired, -}; diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/ProtectedPage/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/ProtectedPage/index.js deleted file mode 100644 index fa65144a38..0000000000 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/ProtectedPage/index.js +++ /dev/null @@ -1 +0,0 @@ -export * from './ProtectedPage'; diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js index 87f82885da..9b584d0dd8 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stage/Stage.js @@ -50,6 +50,7 @@ export function Stage({ index, canDelete, canReorder, + canUpdate, isOpen: isOpenDefault = false, stagesCount, }) { @@ -216,6 +217,7 @@ export function Stage({ { expect(getByRole('textbox').value).toBe('something'); }); + + it('disables all input fields, if canUpdate = false', async () => { + const { container, getByRole } = setup({ canUpdate: false }); + + await user.click(container.querySelector('button[aria-expanded]')); + + expect(getByRole('textbox')).toHaveAttribute('disabled'); + expect(getByRole('combobox')).toHaveAttribute('data-disabled'); + }); }); diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stages.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stages.js index da7cfffb02..215d20cf79 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stages.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/Stages.js @@ -12,26 +12,27 @@ import { AddStage } from '../AddStage'; import { Stage } from './Stage'; -const StagesContainer = styled(Box)` - position: relative; -`; - const Background = styled(Box)` - left: 50%; - position: absolute; - top: 0; transform: translateX(-50%); `; -function Stages({ stages }) { +export function Stages({ canDelete, canUpdate, stages }) { const { formatMessage } = useIntl(); const dispatch = useDispatch(); const { trackUsage } = useTracking(); return ( - - + + 1} isOpen={!stage.id} + canDelete={stages.length > 1 && canDelete} canReorder={stages.length > 1} + canUpdate={canUpdate} stagesCount={stages.length} /> ); })} - + - + {canUpdate && ( { @@ -73,18 +75,20 @@ function Stages({ stages }) { defaultMessage: 'Add new stage', })} - + )} ); } -export { Stages }; - Stages.defaultProps = { + canDelete: true, + canUpdate: true, stages: [], }; Stages.propTypes = { + canDelete: PropTypes.bool, + canUpdate: PropTypes.bool, stages: PropTypes.arrayOf( PropTypes.shape({ id: PropTypes.number, diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/tests/Stages.test.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/tests/Stages.test.js index 01f9fc1045..07f6286491 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/tests/Stages.test.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/Stages/tests/Stages.test.js @@ -122,4 +122,10 @@ describe('Admin | Settings | Review Workflow | Stages', () => { name: 'New name', }); }); + + it('should not render the "add stage" button if canUpdate = false', () => { + const { queryByText } = setup({ canUpdate: false }); + + expect(queryByText('Add new stage')).not.toBeInTheDocument(); + }); }); diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js index 4bfa1358b1..bd01101b51 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/components/WorkflowAttributes/WorkflowAttributes.js @@ -9,7 +9,7 @@ import { useDispatch } from 'react-redux'; import { updateWorkflow } from '../../actions'; -export function WorkflowAttributes({ contentTypes: { collectionTypes, singleTypes } }) { +export function WorkflowAttributes({ canUpdate, contentTypes: { collectionTypes, singleTypes } }) { const { formatMessage, locale } = useIntl(); const dispatch = useDispatch(); const [nameField, nameMeta, nameHelper] = useField('name'); @@ -24,6 +24,7 @@ export function WorkflowAttributes({ contentTypes: { collectionTypes, singleType { expect(getByRole('textbox')).toHaveValue('workflow name'); expect(getByText(/2 content types selected/i)).toBeInTheDocument(); + expect(getByRole('textbox')).not.toHaveAttribute('disabled'); + expect(getByRole('combobox', { name: /associated to/i })).not.toHaveAttribute('data-disabled'); + await user.click(contentTypesSelect); await waitFor(() => { @@ -81,4 +84,13 @@ describe('Admin | Settings | Review Workflow | WorkflowAttributes', () => { expect(getByRole('option', { name: /content type 2/i })).toBeInTheDocument(); }); }); + + it('should disabled fields if canUpdate = false', async () => { + const { getByRole } = setup({ canUpdate: false }); + + await waitFor(() => { + expect(getByRole('textbox')).toHaveAttribute('disabled'); + expect(getByRole('combobox', { name: /associated to/i })).toHaveAttribute('data-disabled'); + }); + }); }); diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js index 64d917aa6c..1de06b6946 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/CreateView.js @@ -1,7 +1,12 @@ import * as React from 'react'; import { Button, Flex, Loader } from '@strapi/design-system'; -import { useAPIErrorHandler, useFetchClient, useNotification } from '@strapi/helper-plugin'; +import { + useAPIErrorHandler, + useFetchClient, + useNotification, + useRBAC, +} from '@strapi/helper-plugin'; import { Check } from '@strapi/icons'; import { useFormik, Form, FormikProvider } from 'formik'; import set from 'lodash/set'; @@ -12,6 +17,7 @@ import { useHistory } from 'react-router-dom'; import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes'; import { useInjectReducer } from '../../../../../../../../admin/src/hooks/useInjectReducer'; +import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors'; import { useLicenseLimits } from '../../../../../../hooks'; import { resetWorkflow } from '../../actions'; import * as Layout from '../../components/Layout'; @@ -29,6 +35,7 @@ export function ReviewWorkflowsCreateView() { const { push } = useHistory(); const { formatAPIError } = useAPIErrorHandler(); const dispatch = useDispatch(); + const permissions = useSelector(selectAdminPermissions); const toggleNotification = useNotification(); const { collectionTypes, singleTypes, isLoading: isLoadingModels } = useContentTypes(); const { @@ -36,6 +43,9 @@ export function ReviewWorkflowsCreateView() { currentWorkflow: { data: currentWorkflow, isDirty: currentWorkflowIsDirty }, }, } = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState); + const { + allowedActions: { canCreate }, + } = useRBAC(permissions.settings['review-workflows']); const [showLimitModal, setShowLimitModal] = React.useState(false); const { isLoading: isLicenseLoading, getFeature } = useLicenseLimits(); const { meta, isLoading: isWorkflowLoading } = useReviewWorkflows(); @@ -191,7 +201,7 @@ export function ReviewWorkflowsCreateView() { startIcon={} type="submit" size="M" - disabled={!currentWorkflowIsDirty} + disabled={!currentWorkflowIsDirty || !canCreate} isLoading={isLoading} > {formatMessage({ diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/index.js index 1568576e19..1be523f91c 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/index.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/CreateView/index.js @@ -1,13 +1,18 @@ import React from 'react'; -import { ProtectedPage } from '../../components/ProtectedPage'; +import { CheckPagePermissions } from '@strapi/helper-plugin'; +import { useSelector } from 'react-redux'; + +import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors'; import { ReviewWorkflowsCreateView } from './CreateView'; export default function () { + const permissions = useSelector(selectAdminPermissions); + return ( - + - + ); } diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js index 5f8b2681bb..63b9509995 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/EditView.js @@ -6,6 +6,7 @@ import { useAPIErrorHandler, useFetchClient, useNotification, + useRBAC, } from '@strapi/helper-plugin'; import { Check } from '@strapi/icons'; import { useFormik, Form, FormikProvider } from 'formik'; @@ -17,6 +18,7 @@ import { useParams } from 'react-router-dom'; import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes'; import { useInjectReducer } from '../../../../../../../../admin/src/hooks/useInjectReducer'; +import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors'; import { useLicenseLimits } from '../../../../../../hooks'; import { setWorkflow } from '../../actions'; import * as Layout from '../../components/Layout'; @@ -30,6 +32,7 @@ import { validateWorkflow } from '../../utils/validateWorkflow'; export function ReviewWorkflowsEditView() { const { workflowId } = useParams(); + const permissions = useSelector(selectAdminPermissions); const { formatMessage } = useIntl(); const dispatch = useDispatch(); const { put } = useFetchClient(); @@ -53,6 +56,9 @@ export function ReviewWorkflowsEditView() { }, }, } = useSelector((state) => state?.[REDUX_NAMESPACE] ?? initialState); + const { + allowedActions: { canDelete, canUpdate }, + } = useRBAC(permissions.settings['review-workflows']); const [isConfirmDeleteDialogOpen, setIsConfirmDeleteDialogOpen] = React.useState(false); const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits(); const [showLimitModal, setShowLimitModal] = React.useState(false); @@ -225,7 +231,7 @@ export function ReviewWorkflowsEditView() { startIcon={} type="submit" size="M" - disabled={!currentWorkflowIsDirty} + disabled={!currentWorkflowIsDirty || !canUpdate} // if the confirm dialog is open the loading state is on // the confirm button already loading={!isConfirmDeleteDialogOpen && isLoading} @@ -256,8 +262,15 @@ export function ReviewWorkflowsEditView() { ) : ( - - + + )} diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/index.js index 25ac6151a4..60749ae8c8 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/index.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/EditView/index.js @@ -1,13 +1,18 @@ import React from 'react'; -import { ProtectedPage } from '../../components/ProtectedPage'; +import { CheckPagePermissions } from '@strapi/helper-plugin'; +import { useSelector } from 'react-redux'; + +import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors'; import { ReviewWorkflowsEditView } from './EditView'; export default function () { + const permissions = useSelector(selectAdminPermissions); + return ( - + - + ); } diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js index 467db14acf..1c619bf32e 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/ListView.js @@ -23,15 +23,18 @@ import { useAPIErrorHandler, useFetchClient, useNotification, + useRBAC, useTracking, } from '@strapi/helper-plugin'; import { Pencil, Plus, Trash } from '@strapi/icons'; import { useIntl } from 'react-intl'; import { useMutation } from 'react-query'; +import { useSelector } from 'react-redux'; import { useHistory } from 'react-router-dom'; import styled from 'styled-components'; import { useContentTypes } from '../../../../../../../../admin/src/hooks/useContentTypes'; +import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors'; import { useLicenseLimits } from '../../../../../../hooks'; import * as Layout from '../../components/Layout'; import * as LimitsModal from '../../components/LimitsModal'; @@ -76,6 +79,10 @@ export function ReviewWorkflowsListView() { const toggleNotification = useNotification(); const { getFeature, isLoading: isLicenseLoading } = useLicenseLimits(); const { trackUsage } = useTracking(); + const permissions = useSelector(selectAdminPermissions); + const { + allowedActions: { canCreate, canDelete }, + } = useRBAC(permissions.settings['review-workflows']); const limits = getFeature('review-workflows'); @@ -166,6 +173,7 @@ export function ReviewWorkflowsListView() { } size="S" to="/settings/review-workflows/create" @@ -218,31 +226,36 @@ export function ReviewWorkflowsListView() { colCount={3} footer={ // TODO: we should be able to use a link here instead of an (inaccessible onClick) handler - } - onClick={() => { - /** - * If the current license has a workflow limit: - * check if the total count of workflows exceeds that limit - * - * If the current license does not have a limit (e.g. offline license): - * allow the user to navigate to the create-view. In case they exceed the - * current hard-limit of 200 they will see an error thrown by the API. - */ + canCreate && ( + } + onClick={() => { + /** + * If the current license has a workflow limit: + * check if the total count of workflows exceeds that limit + * + * If the current license does not have a limit (e.g. offline license): + * allow the user to navigate to the create-view. In case they exceed the + * current hard-limit of 200 they will see an error thrown by the API. + */ - if (limits?.workflows && meta?.workflowCount >= parseInt(limits.workflows, 10)) { - setShowLimitModal(true); - } else { - push('/settings/review-workflows/create'); - trackUsage('willCreateWorkflow'); - } - }} - > - {formatMessage({ - id: 'Settings.review-workflows.list.page.create', - defaultMessage: 'Create new workflow', - })} - + if ( + limits?.workflows && + meta?.workflowCount >= parseInt(limits.workflows, 10) + ) { + setShowLimitModal(true); + } else { + push('/settings/review-workflows/create'); + trackUsage('willCreateWorkflow'); + } + }} + > + {formatMessage({ + id: 'Settings.review-workflows.list.page.create', + defaultMessage: 'Create new workflow', + })} + + ) } rowCount={1} > @@ -334,7 +347,7 @@ export function ReviewWorkflowsListView() { }, { name: 'Default workflow' } )} - disabled={workflows.length === 1} + disabled={workflows.length === 1 || !canDelete} icon={} noBorder onClick={() => { diff --git a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/index.js b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/index.js index 64f1cacc8d..a5c55f2eac 100644 --- a/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/index.js +++ b/packages/core/admin/ee/admin/pages/SettingsPage/pages/ReviewWorkflows/pages/ListView/index.js @@ -1,13 +1,18 @@ import React from 'react'; -import { ProtectedPage } from '../../components/ProtectedPage'; +import { CheckPagePermissions } from '@strapi/helper-plugin'; +import { useSelector } from 'react-redux'; + +import { selectAdminPermissions } from '../../../../../../../../admin/src/pages/App/selectors'; import { ReviewWorkflowsListView } from './ListView'; export default function () { + const permissions = useSelector(selectAdminPermissions); + return ( - + - + ); }