Add permissions on the configure button in the ctm

Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
soupette 2020-06-10 15:52:10 +02:00 committed by Alexandre Bodin
parent 3c8c15efca
commit 67c8cbd1d7
12 changed files with 234 additions and 115 deletions

View File

@ -341,6 +341,21 @@ const data = {
subject: 'application::homepage.homepage',
conditions: [],
},
{
action: 'plugins::content-manager.single-types.configure-view',
subject: null,
conditions: [],
},
{
action: 'plugins::content-manager.collection-types.configure-view',
subject: null,
conditions: [],
},
{
action: 'plugins::content-manager.components.configure-layout',
subject: null,
conditions: [],
},
// Content type builder
{

View File

@ -1,4 +1,4 @@
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import { Redirect } from 'react-router-dom';
import PropTypes from 'prop-types';
import useUser from '../../hooks/useUser';
@ -8,22 +8,32 @@ import LoadingIndicatorPage from '../LoadingIndicatorPage';
const WithPagePermissions = ({ permissions, children }) => {
const userPermissions = useUser();
const [state, setState] = useState({ isLoading: true, canAccess: false });
const isMounted = useRef(true);
useEffect(() => {
const checkPermission = async () => {
try {
const canAccess = await hasPermissions(userPermissions, permissions);
setState({ isLoading: false, canAccess });
if (isMounted.current) {
setState({ isLoading: false, canAccess });
}
} catch (err) {
console.error(err);
strapi.notification.error('notification.error');
if (isMounted.current) {
console.error(err);
setState({ isLoading: false });
strapi.notification.error('notification.error');
setState({ isLoading: false });
}
}
};
checkPermission();
return () => {
isMounted.current = false;
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import PropTypes from 'prop-types';
import useUser from '../../hooks/useUser';
@ -10,23 +10,32 @@ import hasPermissions from '../../utils/hasPermissions';
const WithPermissions = ({ permissions, children }) => {
const userPermissions = useUser();
const [state, setState] = useState({ isLoading: true, canAccess: false });
const isMounted = useRef(true);
useEffect(() => {
const checkPermission = async () => {
try {
const canAccess = await hasPermissions(userPermissions, permissions);
setState({ isLoading: false, canAccess });
if (isMounted.current) {
setState({ isLoading: false, canAccess });
}
} catch (err) {
console.error(err);
strapi.notification.error('notification.error');
if (isMounted.current) {
console.error(err);
strapi.notification.error('notification.error');
setState({ isLoading: false });
setState({ isLoading: false });
}
}
};
checkPermission();
// eslint-disable-next-line react-hooks/exhaustive-deps
return () => {
isMounted.current = false;
};
}, []);
if (state.isLoading) {

View File

@ -4,7 +4,9 @@ import { isEmpty } from 'lodash';
import { FormattedMessage } from 'react-intl';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Grab, GrabLarge, Pencil } from '@buffetjs/icons';
import { WithPermissions } from 'strapi-helper-plugin';
import pluginId from '../../pluginId';
import pluginPermissions from '../../permissions';
import useLayoutDnd from '../../hooks/useLayoutDnd';
import GrabWrapper from './GrabWrapper';
import Link from './Link';
@ -122,9 +124,7 @@ const DraggedField = forwardRef(
onMouseLeave={() => setIsOverRemove(false)}
>
{isOverRemove && !isSelected && <Close />}
{((showEditBlockOverState && !isOverRemove) || isSelected) && (
<Pencil />
)}
{((showEditBlockOverState && !isOverRemove) || isSelected) && <Pencil />}
{!showEditBlockOverState && !isOverRemove && !isSelected && (
<Close width="10px" height="10px" />
)}
@ -132,24 +132,24 @@ const DraggedField = forwardRef(
</SubWrapper>
)}
{type === 'component' && (
<FormattedMessage
id={`${pluginId}.components.FieldItem.linkToComponentLayout`}
>
{msg => (
<Link
onClick={e => {
e.stopPropagation();
<WithPermissions permissions={pluginPermissions.componentsConfigurations}>
<FormattedMessage id={`${pluginId}.components.FieldItem.linkToComponentLayout`}>
{msg => (
<Link
onClick={e => {
e.stopPropagation();
goTo(
`/plugins/${pluginId}/ctm-configurations/edit-settings/components/${componentUid}/`
);
}}
>
<FontAwesomeIcon icon="cog" />
{msg}
</Link>
)}
</FormattedMessage>
goTo(
`/plugins/${pluginId}/ctm-configurations/edit-settings/components/${componentUid}/`
);
}}
>
<FontAwesomeIcon icon="cog" />
{msg}
</Link>
)}
</FormattedMessage>
</WithPermissions>
)}
</Wrapper>
);

View File

@ -1,22 +1,39 @@
import React, { useState } from 'react';
import React, { useEffect, useState } from 'react';
import { useHistory } from 'react-router-dom';
import PropTypes from 'prop-types';
import { hasPermissions, useUser } from 'strapi-helper-plugin';
import pluginId from '../../pluginId';
import pluginPermissions from '../../permissions';
import DynamicComponentCard from '../DynamicComponentCard';
import Tooltip from './Tooltip';
const DynamicComponent = ({
componentUid,
friendlyName,
icon,
setIsOverDynamicZone,
}) => {
const [state, setState] = useState(false);
const DynamicComponent = ({ componentUid, friendlyName, icon, setIsOverDynamicZone }) => {
const [isOver, setIsOver] = useState(false);
const [{ isLoading, canAccess }, setState] = useState({ isLoading: true, canAccess: false });
const { push } = useHistory();
const userPermissions = useUser();
useEffect(() => {
const checkPermission = async () => {
try {
const canAccess = await hasPermissions(
userPermissions,
pluginPermissions.componentsConfigurations
);
setState({ isLoading: false, canAccess });
} catch (err) {
setState({ isLoading: false });
}
};
checkPermission();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const handleMouseEvent = () => {
setIsOverDynamicZone(v => !v);
setState(v => !v);
setIsOver(v => !v);
};
return (
@ -24,16 +41,16 @@ const DynamicComponent = ({
componentUid={componentUid}
friendlyName={friendlyName}
icon={icon}
isOver={state}
isOver={isOver}
onClick={() => {
push(
`/plugins/${pluginId}/ctm-configurations/edit-settings/components/${componentUid}/`
);
if (!isLoading && canAccess) {
push(`/plugins/${pluginId}/ctm-configurations/edit-settings/components/${componentUid}/`);
}
}}
onMouseEvent={handleMouseEvent}
tradId="components.DraggableAttr.edit"
>
<Tooltip isOver={state}>{componentUid}</Tooltip>
<Tooltip isOver={isOver}>{componentUid}</Tooltip>
</DynamicComponentCard>
);
};

View File

@ -38,15 +38,7 @@ const DraggedFieldWithPreview = forwardRef(
const isFullSize = size === 12;
const display = isFullSize && dragStart ? 'none' : '';
const width = isFullSize && dragStart ? 0 : '100%';
const higherFields = [
'json',
'text',
'file',
'media',
'component',
'richtext',
'dynamiczone',
];
const higherFields = ['json', 'text', 'file', 'media', 'component', 'richtext', 'dynamiczone'];
const withLongerHeight = higherFields.includes(type) && !dragStart;
const getCompoInfos = uid =>
get(componentLayouts, [uid, 'schema', 'info'], { name: '', icon: '' });
@ -69,14 +61,8 @@ const DraggedFieldWithPreview = forwardRef(
}
}}
>
<Wrapper
ref={refs.dropRef}
withLongerHeight={withLongerHeight}
style={style}
>
{dragStart && isFullSize && (
<PreviewCarret style={{ marginRight: '-10px' }} />
)}
<Wrapper ref={refs.dropRef} withLongerHeight={withLongerHeight} style={style}>
{dragStart && isFullSize && <PreviewCarret style={{ marginRight: '-10px' }} />}
<>
{showLeftCarret && <Carret />}
@ -98,8 +84,7 @@ const DraggedFieldWithPreview = forwardRef(
>
{type === 'component' &&
componentLayout.map((row, i) => {
const marginBottom =
i === componentLayout.length - 1 ? '29px' : '';
const marginBottom = i === componentLayout.length - 1 ? '29px' : '';
const marginTop = i === 0 ? '5px' : '';
return (
@ -135,9 +120,7 @@ const DraggedFieldWithPreview = forwardRef(
label={label}
name={field.name}
isSub
withLongerHeight={higherFields.includes(
fieldType
)}
withLongerHeight={higherFields.includes(fieldType)}
/>
</div>
);

View File

@ -1,6 +1,7 @@
import React, { Suspense, lazy } from 'react';
import { Switch, Route, useRouteMatch, useParams } from 'react-router-dom';
import { LoadingIndicatorPage } from 'strapi-helper-plugin';
import { LoadingIndicatorPage, WithPagePermissions } from 'strapi-helper-plugin';
import pluginPermissions from '../../permissions';
const EditView = lazy(() => import('../EditView'));
const EditSettingsView = lazy(() => import('../EditSettingsView'));
@ -14,8 +15,15 @@ const CollectionTypeRecursivePath = props => {
const renderRoute = (routeProps, Component) => {
return <Component {...props} {...routeProps} slug={slug} />;
};
const renderPermissionsRoute = (routeProps, Component) => {
return (
<WithPagePermissions permissions={pluginPermissions.collectionTypesConfigurations}>
<Component {...props} {...routeProps} slug={slug} />
</WithPagePermissions>
);
};
const routes = [
const settingsRoutes = [
{
path: 'ctm-configurations/list-settings',
comp: ListSettingsView,
@ -24,6 +32,15 @@ const CollectionTypeRecursivePath = props => {
path: 'ctm-configurations/edit-settings/:type',
comp: EditSettingsView,
},
].map(({ path, comp }) => (
<Route
key={path}
path={`${url}/${path}`}
render={props => renderPermissionsRoute(props, comp)}
/>
));
const routes = [
{ path: ':id', comp: EditView },
{ path: '', comp: ListView },
].map(({ path, comp }) => (
@ -32,7 +49,10 @@ const CollectionTypeRecursivePath = props => {
return (
<Suspense fallback={<LoadingIndicatorPage />}>
<Switch>{routes}</Switch>
<Switch>
{settingsRoutes}
{routes}
</Switch>
</Suspense>
);
};

View File

@ -2,8 +2,9 @@ import React, { memo, useCallback, useMemo, useEffect, useReducer, useRef } from
import PropTypes from 'prop-types';
import { get } from 'lodash';
import { useHistory, useLocation, useRouteMatch } from 'react-router-dom';
import { BackHeader, LiLink } from 'strapi-helper-plugin';
import { BackHeader, LiLink, WithPermissions } from 'strapi-helper-plugin';
import pluginId from '../../pluginId';
import pluginPermissions from '../../permissions';
import Container from '../../components/Container';
import DynamicZone from '../../components/DynamicZone';
import FormWrapper from '../../components/FormWrapper';
@ -231,19 +232,27 @@ const EditView = ({ components, currentEnvironment, deleteLayout, layouts, plugi
)}
<LinkWrapper>
<ul>
<LiLink
message={{
id: 'app.links.configure-view',
}}
icon="layout"
key={`${pluginId}.link`}
url={`${
isSingleType ? `${pathname}/` : ''
}ctm-configurations/edit-settings/content-types`}
onClick={() => {
// emitEvent('willEditContentTypeLayoutFromEditView');
}}
/>
<WithPermissions
permissions={
isSingleType
? pluginPermissions.singleTypesConfigurations
: pluginPermissions.collectionTypesConfigurations
}
>
<LiLink
message={{
id: 'app.links.configure-view',
}}
icon="layout"
key={`${pluginId}.link`}
url={`${
isSingleType ? `${pathname}/` : ''
}ctm-configurations/edit-settings/content-types`}
onClick={() => {
// emitEvent('willEditContentTypeLayoutFromEditView');
}}
/>
</WithPermissions>
{getInjectedComponents(
'editView',
'right.links',

View File

@ -13,9 +13,11 @@ import {
getQueryParameters,
useGlobalContext,
request,
WithPermissions,
} from 'strapi-helper-plugin';
import pluginId from '../../pluginId';
import pluginPermissions from '../../permissions';
import DisplayedFieldsDropdown from '../../components/DisplayedFieldsDropdown';
import Container from '../../components/Container';
import CustomTable from '../../components/CustomTable';
@ -385,16 +387,18 @@ function ListView({
</div>
</div>
<div className="col-2">
<DisplayedFieldsDropdown
isOpen={isLabelPickerOpen}
items={getAllLabels()}
onChange={handleChangeListLabels}
onClickReset={() => {
resetListLabels(slug);
}}
slug={slug}
toggle={toggleLabelPickerState}
/>
<WithPermissions permissions={pluginPermissions.collectionTypesConfigurations}>
<DisplayedFieldsDropdown
isOpen={isLabelPickerOpen}
items={getAllLabels()}
onChange={handleChangeListLabels}
onClickReset={() => {
resetListLabels(slug);
}}
slug={slug}
toggle={toggleLabelPickerState}
/>
</WithPermissions>
</div>
</div>
<div className="row" style={{ paddingTop: '12px' }}>

View File

@ -3,10 +3,16 @@ import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux';
import { Switch, Route, useRouteMatch } from 'react-router-dom';
import { LoadingIndicatorPage, useGlobalContext, request } from 'strapi-helper-plugin';
import {
LoadingIndicatorPage,
useGlobalContext,
request,
WithPagePermissions,
} from 'strapi-helper-plugin';
import { DndProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import pluginId from '../../pluginId';
import pluginPermissions from '../../permissions';
import DragLayer from '../../components/DragLayer';
import getRequestUrl from '../../utils/getRequestUrl';
import createPossibleMainFieldsForModelsAndComponents from './utils/createPossibleMainFieldsForModelsAndComponents';
@ -110,10 +116,6 @@ function Main({
/>
);
const routes = [
{
path: 'ctm-configurations/edit-settings/:type/:componentSlug',
comp: EditSettingsView,
},
{ path: 'singleType/:slug', comp: SingleTypeRecursivePath },
{ path: 'collectionType/:slug', comp: CollectionTypeRecursivePath },
].map(({ path, comp }) => (
@ -128,7 +130,30 @@ function Main({
<DndProvider backend={HTML5Backend}>
<DragLayer />
<Suspense fallback={<LoadingIndicatorPage />}>
<Switch>{routes}</Switch>
<Switch>
<Route
path={`/plugins/${pluginId}/ctm-configurations/edit-settings/:type/:componentSlug`}
render={routeProps => (
<WithPagePermissions permissions={pluginPermissions.componentsConfigurations}>
<EditSettingsView
currentEnvironment={currentEnvironment}
deleteLayout={deleteLayout}
deleteLayouts={deleteLayouts}
emitEvent={emitEvent}
components={components}
componentsAndModelsMainPossibleMainFields={
componentsAndModelsMainPossibleMainFields
}
layouts={layouts}
models={models}
plugins={plugins}
{...routeProps}
/>
</WithPagePermissions>
)}
/>
{routes}
</Switch>
</Suspense>
</DndProvider>
);

View File

@ -1,6 +1,7 @@
import React, { Suspense, lazy } from 'react';
import { Switch, Route, useRouteMatch, useParams } from 'react-router-dom';
import { LoadingIndicatorPage } from 'strapi-helper-plugin';
import { LoadingIndicatorPage, WithPagePermissions } from 'strapi-helper-plugin';
import pluginPermissions from '../../permissions';
const EditView = lazy(() => import('../EditView'));
const EditSettingsView = lazy(() => import('../EditSettingsView'));
@ -9,23 +10,22 @@ const SingleTypeRecursivePath = props => {
const { url } = useRouteMatch();
const { slug } = useParams();
const renderRoute = (routeProps, Component) => {
return <Component {...props} {...routeProps} slug={slug} />;
};
const routes = [
{
path: 'ctm-configurations/edit-settings/:type',
comp: EditSettingsView,
},
{ path: '', comp: EditView },
].map(({ path, comp }) => (
<Route key={path} path={`${url}/${path}`} render={props => renderRoute(props, comp)} />
));
return (
<Suspense fallback={<LoadingIndicatorPage />}>
<Switch>{routes}</Switch>
<Switch>
<Route
path={`${url}/ctm-configurations/edit-settings/:type`}
render={routeProps => (
<WithPagePermissions permissions={pluginPermissions.singleTypesConfigurations}>
<EditSettingsView {...props} {...routeProps} slug={slug} />
</WithPagePermissions>
)}
/>
<Route
path={`${url}`}
render={routeProps => <EditView {...props} {...routeProps} slug={slug} />}
/>
</Switch>
</Suspense>
);
};

View File

@ -0,0 +1,27 @@
const pluginPermissions = {
// This permission regards the main component (App) and is used to tell
// If the plugin link should be displayed in the menu
// And also if the plugin is accessible. This use case is found when a user types the url of the
// plugin directly in the browser
main: [],
collectionTypesConfigurations: [
{
action: 'plugins::content-manager.collection-types.configure-view',
subject: null,
},
],
componentsConfigurations: [
{
action: 'plugins::content-manager.components.configure-layout',
subject: null,
},
],
singleTypesConfigurations: [
{
action: 'plugins::content-manager.single-types.configure-view',
subject: null,
},
],
};
export default pluginPermissions;