Add permissions to ML

Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
soupette 2020-06-11 16:26:48 +02:00 committed by Alexandre Bodin
parent 170b8a7d18
commit 012941f9ad
13 changed files with 240 additions and 128 deletions

View File

@ -386,36 +386,36 @@ const data = {
},
// Upload plugin
{
action: 'plugins::upload.read',
subject: null,
fields: null,
conditions: [],
},
{
action: 'plugins::upload.assets.create',
subject: null,
fields: null,
conditions: [],
},
// {
// action: 'plugins::upload.assets.update',
// action: 'plugins::upload.read',
// subject: null,
// fields: null,
// conditions: [],
// },
// {
// action: 'plugins::upload.assets.create',
// subject: null,
// fields: null,
// conditions: [],
// },
{
action: 'plugins::upload.assets.dowload',
subject: null,
fields: null,
conditions: [],
},
{
action: 'plugins::upload.assets.copy-link',
action: 'plugins::upload.assets.update',
subject: null,
fields: null,
conditions: [],
},
// {
// action: 'plugins::upload.assets.dowload',
// subject: null,
// fields: null,
// conditions: [],
// },
// {
// action: 'plugins::upload.assets.copy-link',
// subject: null,
// fields: null,
// conditions: [],
// },
{
action: 'plugins::upload.settings.read',
subject: null,

View File

@ -15,6 +15,7 @@ import CardControl from '../CardControl';
const BrowseAssets = () => {
const {
allowedActions,
allowedTypes,
count,
files,
@ -80,18 +81,24 @@ const BrowseAssets = () => {
/* eslint-disable react/jsx-indent */
const renderCardControl = noNavigation
? null
: id => (
<CardControl
small
title="edit"
color="#9EA7B8"
type="pencil"
onClick={e => {
e.stopPropagation();
handleGoToEditFile(id);
}}
/>
);
: id => {
if (!allowedActions.canUpdate) {
return null;
}
return (
<CardControl
small
title="edit"
color="#9EA7B8"
type="pencil"
onClick={e => {
e.stopPropagation();
handleGoToEditFile(id);
}}
/>
);
};
/* eslint-enable indent */
/* eslint-enable react/jsx-indent */
@ -113,57 +120,62 @@ const BrowseAssets = () => {
const areResultsEmptyWithSearchOrFilters = isEmpty(files) && (hasSearch || hasFilters);
return (
<Wrapper>
<Padded top>
<Flex flexWrap="wrap">
{multiple && (
<Padded right size="sm">
<SelectAll
checked={areAllCheckboxesSelected}
onChange={handleAllFilesSelection}
someChecked={hasSomeCheckboxSelected && !areAllCheckboxesSelected}
<>
<Wrapper>
<Padded top>
{allowedActions.canRead && (
<Flex flexWrap="wrap">
{multiple && (
<Padded right size="sm">
<SelectAll
checked={areAllCheckboxesSelected}
onChange={handleAllFilesSelection}
someChecked={hasSomeCheckboxSelected && !areAllCheckboxesSelected}
/>
</Padded>
)}
<SortPicker onChange={handleChangeParams} value={params._sort} />
<Padded left size="sm" />
<Filters
filters={params.filters}
onChange={handleChangeParams}
onClick={handleDeleteFilter}
/>
</Padded>
</Flex>
)}
<SortPicker onChange={handleChangeParams} value={params._sort} />
<Padded left size="sm" />
<Filters
filters={params.filters}
onChange={handleChangeParams}
onClick={handleDeleteFilter}
</Padded>
{!files || files.length === 0 ? (
<ListEmpty
canCreate={allowedActions.canCreate}
numberOfRows={2}
onClick={handleGoToUpload}
hasSearchApplied={areResultsEmptyWithSearchOrFilters}
/>
</Flex>
</Padded>
{!files || files.length === 0 ? (
<ListEmpty
numberOfRows={2}
onClick={handleGoToUpload}
hasSearchApplied={areResultsEmptyWithSearchOrFilters}
/>
) : (
<>
<List
data={files}
onChange={handleCheckboxChange}
selectedItems={selectedFiles}
onCardClick={handleListCardClick}
allowedTypes={allowedTypes}
smallCards
renderCardControl={renderCardControl}
/>
<Padded left right>
<Padded left right size="xs">
<PageFooter
context={{ emitEvent: () => {} }}
count={count}
onChangeParams={handleChangeListParams}
params={paginationParams}
/>
) : (
<>
<List
data={files}
onChange={handleCheckboxChange}
selectedItems={selectedFiles}
onCardClick={handleListCardClick}
allowedTypes={allowedTypes}
smallCards
renderCardControl={renderCardControl}
/>
<Padded left right>
<Padded left right size="xs">
<PageFooter
context={{ emitEvent: () => {} }}
count={count}
onChangeParams={handleChangeListParams}
params={paginationParams}
/>
</Padded>
</Padded>
</Padded>
</>
)}
</Wrapper>
</>
)}
</Wrapper>
</>
);
};

View File

@ -38,6 +38,8 @@ import isVideoType from './utils/isVideoType';
const EditForm = forwardRef(
(
{
canCopyLink,
canDownload,
components,
fileToEdit,
isEditingUploadedFile,
@ -236,12 +238,14 @@ const EditForm = forwardRef(
/>
{fileURL && (
<>
<CardControl
color="#9EA7B8"
onClick={handleClickDownload}
type="download"
title="download"
/>
{canDownload && (
<CardControl
color="#9EA7B8"
onClick={handleClickDownload}
type="download"
title="download"
/>
)}
<a
title={fileToEdit.fileInfo.name}
style={{ display: 'none' }}
@ -249,13 +253,14 @@ const EditForm = forwardRef(
>
hidden
</a>
<CopyToClipboard
onCopy={handleCopy}
text={prefixFileUrlWithBackendUrl(fileURL)}
>
<CardControl color="#9EA7B8" type="link" title="copy-link" />
</CopyToClipboard>
{canCopyLink && (
<CopyToClipboard
onCopy={handleCopy}
text={prefixFileUrlWithBackendUrl(fileURL)}
>
<CardControl color="#9EA7B8" type="link" title="copy-link" />
</CopyToClipboard>
)}
</>
)}
{canCrop && (
@ -377,6 +382,8 @@ const EditForm = forwardRef(
);
EditForm.defaultProps = {
canCopyLink: true,
canDownload: true,
components: {
CheckControl: CardControl,
},
@ -392,6 +399,8 @@ EditForm.defaultProps = {
};
EditForm.propTypes = {
canCopyLink: PropTypes.bool,
canDownload: PropTypes.bool,
onAbortUpload: PropTypes.func,
components: PropTypes.object,
fileToEdit: PropTypes.object,

View File

@ -20,6 +20,7 @@ const List = ({
smallCards,
canSelect,
renderCardControl,
showCheckbox,
}) => {
const selectedAssets = selectedItems.length;
@ -57,7 +58,7 @@ const List = ({
>
{(checked || canSelect) && (
<>
{(checked || isAllowed) && (
{(checked || isAllowed) && showCheckbox && (
<CardControlsWrapper leftAlign className="card-control-wrapper">
<Checkbox
name={`${id}`}
@ -92,6 +93,7 @@ List.defaultProps = {
renderCardControl: null,
selectedItems: [],
smallCards: false,
showCheckbox: true,
};
List.propTypes = {
@ -103,6 +105,7 @@ List.propTypes = {
renderCardControl: PropTypes.func,
selectedItems: PropTypes.array,
smallCards: PropTypes.bool,
showCheckbox: PropTypes.bool,
};
export default List;

View File

@ -10,7 +10,7 @@ import CardEmpty from '../CardEmpty';
import Wrapper from './Wrapper';
import IntlText from '../IntlText';
const ListEmpty = ({ hasSearchApplied, onClick, numberOfRows }) => {
const ListEmpty = ({ canCreate, hasSearchApplied, onClick, numberOfRows }) => {
const rows = generateRows(numberOfRows);
const titleId = hasSearchApplied
? 'list.assets-empty.title-withSearch'
@ -34,30 +34,34 @@ const ListEmpty = ({ hasSearchApplied, onClick, numberOfRows }) => {
</div>
);
})}
<div className="btn-wrapper">
<IntlText id={prefixedTitleId} fontSize="lg" fontWeight="semiBold" />
{canCreate && (
<div className="btn-wrapper">
<IntlText id={prefixedTitleId} fontSize="lg" fontWeight="semiBold" />
{!hasSearchApplied && (
<>
<IntlText id={prefixedSubtitleId} fontSize="md" lineHeight="19px" />
<div style={{ paddingBottom: '1.1rem' }} />
<Button color="primary" onClick={onClick} type="button">
<FormattedMessage id={getTrad('header.actions.upload-assets')} />
</Button>
</>
)}
</div>
{!hasSearchApplied && (
<>
<IntlText id={prefixedSubtitleId} fontSize="md" lineHeight="19px" />
<div style={{ paddingBottom: '1.1rem' }} />
<Button color="primary" onClick={onClick} type="button">
<FormattedMessage id={getTrad('header.actions.upload-assets')} />
</Button>
</>
)}
</div>
)}
</Wrapper>
);
};
ListEmpty.defaultProps = {
canCreate: true,
hasSearchApplied: false,
onClick: () => {},
numberOfRows: 3,
};
ListEmpty.propTypes = {
canCreate: PropTypes.bool,
hasSearchApplied: PropTypes.bool,
onClick: PropTypes.func,
numberOfRows: PropTypes.number,

View File

@ -1,8 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Button } from '@buffetjs/core';
import { CheckPermissions } from 'strapi-helper-plugin';
import useModalContext from '../../hooks/useModalContext';
import { getTrad } from '../../utils';
import pluginPermissions from '../../permissions';
import BrowseAssets from '../BrowseAssets';
import ModalNavWrapper from '../ModalNavWrapper';
import ModalSection from '../ModalSection';
@ -23,13 +25,15 @@ const ListModal = ({ noNavigation }) => {
const renderUploadModalButton = () => (
<BaselineAlignmentWrapper>
<Button type="button" color="primary" onClick={handleGoToUpload}>
<IntlText
id={getTrad('modal.upload-list.sub-header.button')}
fontWeight="bold"
color="white"
/>
</Button>
<CheckPermissions permissions={pluginPermissions.create}>
<Button type="button" color="primary" onClick={handleGoToUpload}>
<IntlText
id={getTrad('modal.upload-list.sub-header.button')}
fontWeight="bold"
color="white"
/>
</Button>
</CheckPermissions>
</BaselineAlignmentWrapper>
);

View File

@ -2,8 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { PageFooter, useQuery } from 'strapi-helper-plugin';
import { generatePageFromStart, generateStartFromPage } from '../../../utils';
// TODO
// import { useAppContext } from '../../../hooks';
import { useAppContext } from '../../../hooks';
import List from '../../../components/List';
import ListEmpty from '../../../components/ListEmpty';
import Padded from '../../../components/Padded';
@ -19,8 +18,9 @@ const HomePageList = ({
onClick,
}) => {
const query = useQuery();
// TODO
// const { allowedActions } = useAppContext();
const {
allowedActions: { canCreate, canUpdate },
} = useAppContext();
const limit = parseInt(query.get('_limit'), 10) || 10;
const start = parseInt(query.get('_start'), 10) || 0;
@ -48,8 +48,7 @@ const HomePageList = ({
onChange={onCardCheck}
onCardClick={onCardClick}
selectedItems={dataToDelete}
// TODO
// showCheckbox={allowedActions.canUpdate}
showCheckbox={canUpdate}
/>
<Padded left right size="sm">
<Padded left right size="xs">
@ -65,7 +64,13 @@ const HomePageList = ({
);
}
return <ListEmpty onClick={onClick} hasSearchApplied={areResultsEmptyWithSettings} />;
return (
<ListEmpty
canCreate={canCreate}
onClick={onClick}
hasSearchApplied={areResultsEmptyWithSettings}
/>
);
};
HomePageList.defaultProps = {

View File

@ -27,7 +27,6 @@ import reducer, { initialState } from './reducer';
const HomePage = () => {
const { allowedActions } = useAppContext();
const { canRead } = allowedActions;
console.log({ allowedActions });
const { formatMessage, plugins } = useGlobalContext();
const [, updated_at] = getFileModelTimestamps(plugins);
const [reducerState, dispatch] = useReducer(reducer, initialState, () =>

View File

@ -9,7 +9,13 @@ import pluginId from '../../pluginId';
import stepper from './stepper';
import useModalContext from '../../hooks/useModalContext';
const InputModalStepper = ({ isOpen, onToggle, noNavigation, onInputMediaChange }) => {
const InputModalStepper = ({
allowedActions,
isOpen,
onToggle,
noNavigation,
onInputMediaChange,
}) => {
const { emitEvent, formatMessage } = useGlobalContext();
const [shouldDeleteFile, setShouldDeleteFile] = useState(false);
const [displayNextButton, setDisplayNextButton] = useState(false);
@ -308,6 +314,7 @@ const InputModalStepper = ({ isOpen, onToggle, noNavigation, onInputMediaChange
{/* body of the modal */}
{Component && (
<Component
{...allowedActions}
addFilesToUpload={addFilesToUploadList}
components={components}
filesToDownload={filesToDownload}
@ -418,11 +425,29 @@ const InputModalStepper = ({ isOpen, onToggle, noNavigation, onInputMediaChange
};
InputModalStepper.defaultProps = {
allowedActions: PropTypes.shape({
canCopyLink: PropTypes.bool,
canCreate: PropTypes.bool,
canDownload: PropTypes.bool,
canMain: PropTypes.bool,
canRead: PropTypes.bool,
canSettings: PropTypes.bool,
canUpdate: PropTypes.bool,
}),
noNavigation: false,
onToggle: () => {},
};
InputModalStepper.propTypes = {
allowedActions: {
canCopyLink: true,
canCreate: true,
canDownload: true,
canMain: true,
canRead: true,
canSettings: true,
canUpdate: true,
},
isOpen: PropTypes.bool.isRequired,
noNavigation: PropTypes.bool,
onInputMediaChange: PropTypes.func.isRequired,

View File

@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { DndProvider } from 'react-dnd';
import HTML5Backend from 'react-dnd-html5-backend';
import DragLayer from '../../components/DragLayer';
import { useUserPermissions } from '../../hooks';
import InputModalStepper from './InputModalStepper';
import InputModalStepperProvider from '../InputModalStepperProvider';
@ -20,11 +21,17 @@ const InputModal = ({
step,
}) => {
const singularTypes = allowedTypes.map(type => type.substring(0, type.length - 1));
const { allowedActions, isLoading } = useUserPermissions();
if (isLoading) {
return null;
}
return (
<DndProvider backend={HTML5Backend}>
<DragLayer />
<InputModalStepperProvider
allowedActions={allowedActions}
onClosed={onClosed}
initialFilesToUpload={filesToUpload}
initialFileToEdit={fileToEdit}
@ -36,6 +43,7 @@ const InputModal = ({
allowedTypes={singularTypes}
>
<InputModalStepper
allowedActions={allowedActions}
isOpen={isOpen}
noNavigation={noNavigation}
onToggle={onToggle}

View File

@ -15,12 +15,14 @@ import {
formatFilters,
} from '../../utils';
import InputModalStepperContext from '../../contexts/InputModal/InputModalDataManager';
import init from './init';
import reducer, { initialState } from './reducer';
/* eslint-disable indent */
const InputModalStepperProvider = ({
allowedActions,
allowedTypes,
children,
initialFilesToUpload,
@ -34,6 +36,7 @@ const InputModalStepperProvider = ({
step,
}) => {
const [formErrors, setFormErrors] = useState(null);
const { emitEvent, plugins } = useGlobalContext();
const [, updated_at] = getFileModelTimestamps(plugins);
const [reducerState, dispatch] = useReducer(reducer, initialState, state =>
@ -335,12 +338,14 @@ const InputModalStepperProvider = ({
};
const fetchMediaLib = async () => {
const [files, count] = await Promise.all([fetchMediaLibFiles(), fetchMediaLibFilesCount()]);
dispatch({
type: 'GET_DATA_SUCCEEDED',
files,
countData: count,
});
if (allowedActions.canRead) {
const [files, count] = await Promise.all([fetchMediaLibFiles(), fetchMediaLibFilesCount()]);
dispatch({
type: 'GET_DATA_SUCCEEDED',
files,
countData: count,
});
}
};
const fetchMediaLibFiles = async () => {
@ -455,6 +460,7 @@ const InputModalStepperProvider = ({
<InputModalStepperContext.Provider
value={{
...reducerState,
allowedActions,
addFilesToUpload,
downloadFiles,
fetchMediaLib,
@ -495,6 +501,15 @@ const InputModalStepperProvider = ({
};
InputModalStepperProvider.propTypes = {
allowedActions: PropTypes.shape({
canCopyLink: PropTypes.bool,
canCreate: PropTypes.bool,
canDownload: PropTypes.bool,
canMain: PropTypes.bool,
canRead: PropTypes.bool,
canSettings: PropTypes.bool,
canUpdate: PropTypes.bool,
}),
allowedTypes: PropTypes.arrayOf(PropTypes.string),
children: PropTypes.node.isRequired,
initialFilesToUpload: PropTypes.object,
@ -509,6 +524,15 @@ InputModalStepperProvider.propTypes = {
};
InputModalStepperProvider.defaultProps = {
allowedActions: {
canCopyLink: true,
canCreate: true,
canDownload: true,
canMain: true,
canRead: true,
canSettings: true,
canUpdate: true,
},
initialFileToEdit: null,
initialFilesToUpload: null,
isOpen: false,

View File

@ -13,6 +13,7 @@ import {
import { Button } from '@buffetjs/core';
import pluginId from '../../pluginId';
import { getFilesToDownload, getTrad, getYupError, urlSchema } from '../../utils';
import { useAppContext } from '../../hooks';
import ModalHeader from '../../components/ModalHeader';
import stepper from './stepper';
import init from './init';
@ -26,6 +27,7 @@ const ModalStepper = ({
onDeleteMedia,
onToggle,
}) => {
const { allowedActions } = useAppContext();
const { emitEvent, formatMessage } = useGlobalContext();
const [isWarningDeleteOpen, setIsWarningDeleteOpen] = useState(false);
const [shouldDeleteFile, setShouldDeleteFile] = useState(false);
@ -479,6 +481,7 @@ const ModalStepper = ({
{/* body of the modal */}
{Component && (
<Component
{...allowedActions}
onAbortUpload={handleAbortUpload}
addFilesToUpload={addFilesToUpload}
fileToEdit={fileToEdit}

View File

@ -18,6 +18,14 @@ const pluginPermissions = {
conditions: null,
},
],
copyLink: [
{
action: 'plugins::upload.assets.copy-link',
subject: null,
fields: null,
conditions: null,
},
],
create: [
{
action: 'plugins::upload.assets.create',
@ -26,6 +34,14 @@ const pluginPermissions = {
conditions: null,
},
],
download: [
{
action: 'plugins::upload.assets.download',
subject: null,
fields: null,
conditions: null,
},
],
read: [
{ action: 'plugins::upload.read', subject: null },