mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 07:03:38 +00:00
Add permissions to plugins links in the menu only
Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
parent
d282c7d5d6
commit
b68245b8b1
@ -22,34 +22,29 @@ const LinkLabel = styled.span`
|
||||
padding-left: 2.5rem;
|
||||
`;
|
||||
|
||||
const LeftMenuLinkContent = ({
|
||||
destination,
|
||||
iconName,
|
||||
label,
|
||||
location,
|
||||
source,
|
||||
suffixUrlToReplaceForLeftMenuHighlight,
|
||||
}) => {
|
||||
// TODO: refacto this file
|
||||
const LeftMenuLinkContent = ({ destination, iconName, label, location }) => {
|
||||
const isLinkActive = startsWith(
|
||||
location.pathname.replace('/admin', '').concat('/'),
|
||||
|
||||
destination.replace(suffixUrlToReplaceForLeftMenuHighlight, '').concat('/')
|
||||
destination.concat('/')
|
||||
);
|
||||
|
||||
// Check if messageId exists in en locale to prevent warning messages
|
||||
const content = en[label] ? (
|
||||
<FormattedMessage
|
||||
id={label}
|
||||
defaultMessage="{label}"
|
||||
values={{
|
||||
label: `${label}`,
|
||||
}}
|
||||
>
|
||||
{message => <LinkLabel>{message}</LinkLabel>}
|
||||
</FormattedMessage>
|
||||
) : (
|
||||
<LinkLabel>{label}</LinkLabel>
|
||||
);
|
||||
const labelId = label.id || label;
|
||||
const content =
|
||||
en[labelId] || label.defaultMessage ? (
|
||||
<FormattedMessage
|
||||
id={labelId}
|
||||
defaultMessage={label.defaultMessage || '{label}'}
|
||||
values={{
|
||||
label: `${label.id || label}`,
|
||||
}}
|
||||
>
|
||||
{message => <LinkLabel>{message}</LinkLabel>}
|
||||
</FormattedMessage>
|
||||
) : (
|
||||
<LinkLabel>{labelId}</LinkLabel>
|
||||
);
|
||||
|
||||
// Create external or internal link.
|
||||
return destination.includes('http') ? (
|
||||
@ -68,7 +63,6 @@ const LeftMenuLinkContent = ({
|
||||
className={isLinkActive ? 'linkActive' : ''}
|
||||
to={{
|
||||
pathname: destination,
|
||||
search: source ? `?source=${source}` : '',
|
||||
}}
|
||||
>
|
||||
<LeftMenuIcon icon={iconName} />
|
||||
@ -80,17 +74,10 @@ const LeftMenuLinkContent = ({
|
||||
LeftMenuLinkContent.propTypes = {
|
||||
destination: PropTypes.string.isRequired,
|
||||
iconName: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
label: PropTypes.oneOfType([PropTypes.object, PropTypes.string]).isRequired,
|
||||
location: PropTypes.shape({
|
||||
pathname: PropTypes.string,
|
||||
}).isRequired,
|
||||
source: PropTypes.string,
|
||||
suffixUrlToReplaceForLeftMenuHighlight: PropTypes.string,
|
||||
};
|
||||
|
||||
LeftMenuLinkContent.defaultProps = {
|
||||
source: '',
|
||||
suffixUrlToReplaceForLeftMenuHighlight: '',
|
||||
};
|
||||
|
||||
export default withRouter(LeftMenuLinkContent);
|
||||
|
||||
@ -1,33 +0,0 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const Plugin = styled.div`
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: calc(100% - 4px);
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
transition: right 1s ease-in-out;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
padding: 0 14px 0 10px;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
background: #0097f7;
|
||||
border-radius: 3px;
|
||||
transition: transform 0.3s ease-in-out;
|
||||
white-space: pre;
|
||||
|
||||
&:hover {
|
||||
transform: translateX(calc(-100% + 9px));
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default Plugin;
|
||||
@ -5,29 +5,11 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { upperFirst } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import LeftMenuLinkContent from './LeftMenuLinkContent';
|
||||
import Plugin from './Plugin';
|
||||
|
||||
const LeftMenuLink = ({
|
||||
destination,
|
||||
iconName,
|
||||
label,
|
||||
location,
|
||||
source,
|
||||
suffixUrlToReplaceForLeftMenuHighlight,
|
||||
}) => {
|
||||
const plugin =
|
||||
source !== 'content-manager' && source !== '' ? (
|
||||
<Plugin>
|
||||
<span>{upperFirst(source.split('-').join(' '))}</span>
|
||||
</Plugin>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
|
||||
const LeftMenuLink = ({ destination, iconName, label, location }) => {
|
||||
return (
|
||||
<>
|
||||
<LeftMenuLinkContent
|
||||
@ -35,10 +17,7 @@ const LeftMenuLink = ({
|
||||
iconName={iconName}
|
||||
label={label}
|
||||
location={location}
|
||||
source={source}
|
||||
suffixUrlToReplaceForLeftMenuHighlight={suffixUrlToReplaceForLeftMenuHighlight}
|
||||
/>
|
||||
{plugin}
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -46,18 +25,14 @@ const LeftMenuLink = ({
|
||||
LeftMenuLink.propTypes = {
|
||||
destination: PropTypes.string.isRequired,
|
||||
iconName: PropTypes.string,
|
||||
label: PropTypes.string.isRequired,
|
||||
label: PropTypes.oneOfType([PropTypes.object, PropTypes.string]).isRequired,
|
||||
location: PropTypes.shape({
|
||||
pathname: PropTypes.string,
|
||||
}).isRequired,
|
||||
source: PropTypes.string,
|
||||
suffixUrlToReplaceForLeftMenuHighlight: PropTypes.string,
|
||||
};
|
||||
|
||||
LeftMenuLink.defaultProps = {
|
||||
iconName: 'circle',
|
||||
source: '',
|
||||
suffixUrlToReplaceForLeftMenuHighlight: '',
|
||||
};
|
||||
|
||||
export default LeftMenuLink;
|
||||
|
||||
@ -4,8 +4,6 @@ import { useLocation } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { get, snakeCase, isEmpty } from 'lodash';
|
||||
|
||||
import messages from './messages.json';
|
||||
|
||||
import LeftMenuLinkSection from '../LeftMenuLinkSection';
|
||||
|
||||
const LeftMenuLinkContainer = ({ plugins }) => {
|
||||
@ -34,29 +32,8 @@ const LeftMenuLinkContainer = ({ plugins }) => {
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
// Generate the list of plugin links (plugins without a mainComponent should not appear in the left menu)
|
||||
const pluginsLinks = Object.values(plugins)
|
||||
.filter(
|
||||
plugin => plugin.id !== 'email' && plugin.id !== 'content-manager' && !!plugin.mainComponent
|
||||
)
|
||||
.map(plugin => {
|
||||
const pluginSuffixUrl = plugin.suffixUrl ? plugin.suffixUrl(plugins) : '';
|
||||
|
||||
return {
|
||||
icon: get(plugin, 'icon') || 'plug',
|
||||
label: get(plugin, 'name'),
|
||||
destination: `/plugins/${get(plugin, 'id')}${pluginSuffixUrl}`,
|
||||
};
|
||||
});
|
||||
|
||||
const menu = {
|
||||
...contentTypesSections,
|
||||
plugins: {
|
||||
searchable: false,
|
||||
name: 'plugins',
|
||||
emptyLinksListMessage: messages.noPluginsInstalled.id,
|
||||
links: pluginsLinks,
|
||||
},
|
||||
};
|
||||
|
||||
return Object.keys(menu).map(current => (
|
||||
|
||||
@ -214,7 +214,7 @@ export class Admin extends React.Component {
|
||||
exact
|
||||
/>
|
||||
<Route key="7" path="" component={NotFoundPage} />
|
||||
<Route key="8" path="404" component={NotFoundPage} />
|
||||
<Route key="8" path="/404" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</Content>
|
||||
</div>
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import React, { useContext, useEffect, useReducer } from 'react';
|
||||
import React, { useContext, useEffect, useMemo, useReducer } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { UserContext, hasPermissions } from 'strapi-helper-plugin';
|
||||
@ -15,6 +15,7 @@ import {
|
||||
LeftMenuLinkContainer,
|
||||
LinksContainer,
|
||||
} from '../../components/LeftMenu';
|
||||
import init from './init';
|
||||
import reducer, { initialState } from './reducer';
|
||||
import Loader from './Loader';
|
||||
import Wrapper from './Wrapper';
|
||||
@ -22,8 +23,21 @@ import Wrapper from './Wrapper';
|
||||
const LeftMenu = ({ version, plugins }) => {
|
||||
const location = useLocation();
|
||||
const permissions = useContext(UserContext);
|
||||
const [{ generalSectionLinks, isLoading }, dispatch] = useReducer(reducer, initialState);
|
||||
const filteredLinks = generalSectionLinks.filter(link => link.isDisplayed);
|
||||
const [{ generalSectionLinks, isLoading, pluginsSectionLinks }, dispatch] = useReducer(
|
||||
reducer,
|
||||
initialState,
|
||||
() => init(initialState, plugins)
|
||||
);
|
||||
const generalSectionLinksFiltered = useMemo(
|
||||
() => generalSectionLinks.filter(link => link.isDisplayed),
|
||||
[generalSectionLinks]
|
||||
);
|
||||
const pluginsSectionLinksFiltered = useMemo(
|
||||
() => pluginsSectionLinks.filter(link => link.isDisplayed),
|
||||
[pluginsSectionLinks]
|
||||
);
|
||||
|
||||
console.log(pluginsSectionLinks);
|
||||
|
||||
useEffect(() => {
|
||||
const getLinksPermissions = async () => {
|
||||
@ -33,15 +47,21 @@ const LeftMenu = ({ version, plugins }) => {
|
||||
return { index, hasPermission };
|
||||
};
|
||||
|
||||
const generalSectionLinksArrayOfPromises = generalSectionLinks.map((_, index) =>
|
||||
checkPermissions(index, generalSectionLinks[index].permissions)
|
||||
);
|
||||
const generateArrayOfPromises = array =>
|
||||
array.map((_, index) => checkPermissions(index, array[index].permissions));
|
||||
|
||||
const results = await Promise.all(generalSectionLinksArrayOfPromises);
|
||||
const generalSectionLinksArrayOfPromises = generateArrayOfPromises(generalSectionLinks);
|
||||
const pluginsSectionLinksArrayOfPromises = generateArrayOfPromises(pluginsSectionLinks);
|
||||
|
||||
const generalSectionResults = await Promise.all(generalSectionLinksArrayOfPromises);
|
||||
const pluginsSectionResults = await Promise.all(pluginsSectionLinksArrayOfPromises);
|
||||
|
||||
dispatch({
|
||||
type: 'SET_LINK_PERMISSIONS',
|
||||
results,
|
||||
data: {
|
||||
generalSectionLinks: generalSectionResults,
|
||||
pluginsSectionLinks: pluginsSectionResults,
|
||||
},
|
||||
});
|
||||
|
||||
dispatch({
|
||||
@ -59,11 +79,19 @@ const LeftMenu = ({ version, plugins }) => {
|
||||
<LeftMenuHeader />
|
||||
<LinksContainer>
|
||||
<LeftMenuLinkContainer plugins={plugins} />
|
||||
{filteredLinks.length && (
|
||||
<LeftMenuLinksSection
|
||||
section="plugins"
|
||||
name="plugins"
|
||||
links={pluginsSectionLinksFiltered}
|
||||
location={location}
|
||||
searchable={false}
|
||||
emptyLinksListMessage="app.components.LeftMenuLinkContainer.noPluginsInstalled"
|
||||
/>
|
||||
{generalSectionLinksFiltered.length && (
|
||||
<LeftMenuLinksSection
|
||||
section="general"
|
||||
name="general"
|
||||
links={filteredLinks}
|
||||
links={generalSectionLinksFiltered}
|
||||
location={location}
|
||||
searchable={false}
|
||||
/>
|
||||
|
||||
23
packages/strapi-admin/admin/src/containers/LeftMenu/init.js
Normal file
23
packages/strapi-admin/admin/src/containers/LeftMenu/init.js
Normal file
@ -0,0 +1,23 @@
|
||||
import { get, omit, set, sortBy } from 'lodash';
|
||||
|
||||
const sortLinks = links => sortBy(links, object => object.name);
|
||||
|
||||
const init = (initialState, plugins = {}) => {
|
||||
const pluginsLinks = Object.values(plugins).reduce((acc, current) => {
|
||||
const pluginsSectionLinks = get(current, 'menu.pluginsSectionLinks', []);
|
||||
|
||||
return [...acc, ...pluginsSectionLinks];
|
||||
}, []);
|
||||
const sortedLinks = sortLinks(pluginsLinks).map(link => {
|
||||
return { ...omit(link, 'name'), isDisplayed: false };
|
||||
});
|
||||
|
||||
if (sortedLinks.length) {
|
||||
set(initialState, 'pluginsSectionLinks', sortedLinks);
|
||||
}
|
||||
|
||||
return initialState;
|
||||
};
|
||||
|
||||
export default init;
|
||||
export { sortLinks };
|
||||
@ -46,11 +46,14 @@ const initialState = {
|
||||
{ action: 'admin::roles.update', subject: null },
|
||||
{ action: 'admin::roles.read', subject: null },
|
||||
{ action: 'admin::roles.delete', subject: null },
|
||||
|
||||
// TODO this should be set by the plugin directly
|
||||
// media library
|
||||
{ action: 'plugins::upload.settings.read', subject: null },
|
||||
],
|
||||
},
|
||||
],
|
||||
pluginsSectionLinks: [],
|
||||
isLoading: true,
|
||||
};
|
||||
|
||||
@ -58,12 +61,12 @@ const reducer = (state, action) =>
|
||||
produce(state, draftState => {
|
||||
switch (action.type) {
|
||||
case 'SET_LINK_PERMISSIONS': {
|
||||
action.results.forEach(result => {
|
||||
set(
|
||||
draftState,
|
||||
['generalSectionLinks', result.index, 'isDisplayed'],
|
||||
result.hasPermission
|
||||
);
|
||||
Object.keys(action.data).forEach(sectionName => {
|
||||
const sectionData = action.data[sectionName];
|
||||
|
||||
sectionData.forEach(result => {
|
||||
set(draftState, [sectionName, result.index, 'isDisplayed'], result.hasPermission);
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
@ -0,0 +1,139 @@
|
||||
import init, { sortLinks } from '../init';
|
||||
|
||||
describe('ADMIN | LeftMenu | init', () => {
|
||||
describe('init', () => {
|
||||
it('should return the initialState if the plugins are empty', () => {
|
||||
const initialState = {
|
||||
ok: true,
|
||||
};
|
||||
|
||||
expect(init(initialState)).toEqual({ ok: true });
|
||||
});
|
||||
|
||||
it('should create the pluginsSectionLinks correctly', () => {
|
||||
const plugins = {
|
||||
documentation: {
|
||||
menu: {
|
||||
pluginsSectionLinks: [
|
||||
{
|
||||
destination: '/plugins/documentation',
|
||||
icon: 'doc',
|
||||
label: {
|
||||
id: 'documentation.plugin.name',
|
||||
defaultMessage: 'Documentation',
|
||||
},
|
||||
name: 'documentation',
|
||||
permissions: [{ action: 'plugins::documentation.read', subject: null }],
|
||||
},
|
||||
{
|
||||
destination: '/plugins/documentation/test',
|
||||
icon: 'doc',
|
||||
label: {
|
||||
id: 'documentation.plugin.name.test',
|
||||
defaultMessage: 'Documentation Test',
|
||||
},
|
||||
name: 'documentation test',
|
||||
permissions: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
test: {},
|
||||
'content-type-builder': {
|
||||
menu: {
|
||||
pluginsSectionLinks: [
|
||||
{
|
||||
destination: '/plugins/content-type-builder',
|
||||
icon: 'plug',
|
||||
label: {
|
||||
id: 'content-type-builder.plugin.name',
|
||||
defaultMessage: 'content-type-builder',
|
||||
},
|
||||
name: 'content-type-builder',
|
||||
permissions: [{ action: 'plugins::content-type-builder.read', subject: null }],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
const initialState = {
|
||||
generalSectionLinks: [],
|
||||
pluginsSectionLinks: [],
|
||||
isLoading: true,
|
||||
};
|
||||
const expected = {
|
||||
generalSectionLinks: [],
|
||||
pluginsSectionLinks: [
|
||||
{
|
||||
destination: '/plugins/content-type-builder',
|
||||
icon: 'plug',
|
||||
label: {
|
||||
id: 'content-type-builder.plugin.name',
|
||||
defaultMessage: 'content-type-builder',
|
||||
},
|
||||
isDisplayed: false,
|
||||
permissions: [{ action: 'plugins::content-type-builder.read', subject: null }],
|
||||
},
|
||||
{
|
||||
destination: '/plugins/documentation',
|
||||
icon: 'doc',
|
||||
label: {
|
||||
id: 'documentation.plugin.name',
|
||||
defaultMessage: 'Documentation',
|
||||
},
|
||||
isDisplayed: false,
|
||||
permissions: [{ action: 'plugins::documentation.read', subject: null }],
|
||||
},
|
||||
{
|
||||
destination: '/plugins/documentation/test',
|
||||
icon: 'doc',
|
||||
label: {
|
||||
id: 'documentation.plugin.name.test',
|
||||
defaultMessage: 'Documentation Test',
|
||||
},
|
||||
isDisplayed: false,
|
||||
permissions: [],
|
||||
},
|
||||
],
|
||||
isLoading: true,
|
||||
};
|
||||
|
||||
expect(init(initialState, plugins)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sortLinks', () => {
|
||||
it('should return an empty array', () => {
|
||||
expect(sortLinks([])).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return a sorted array', () => {
|
||||
const data = [
|
||||
{
|
||||
name: 'un',
|
||||
},
|
||||
{ name: 'deux' },
|
||||
{ name: 'un-un' },
|
||||
{ name: 'un-deux' },
|
||||
{ name: 'un un' },
|
||||
];
|
||||
const expected = [
|
||||
{
|
||||
name: 'deux',
|
||||
},
|
||||
{
|
||||
name: 'un',
|
||||
},
|
||||
{ name: 'un un' },
|
||||
{
|
||||
name: 'un-deux',
|
||||
},
|
||||
{
|
||||
name: 'un-un',
|
||||
},
|
||||
];
|
||||
|
||||
expect(sortLinks(data)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -62,23 +62,59 @@ describe('ADMIN | LeftMenu | reducer', () => {
|
||||
permissions: [],
|
||||
},
|
||||
],
|
||||
pluginsSectionLinks: [
|
||||
{
|
||||
destination: '/plugins/content-type-builder',
|
||||
icon: 'paint-brush',
|
||||
isDisplayed: false,
|
||||
label: {
|
||||
id: 'content-type-builder.plugin.name',
|
||||
defaultMessage: 'Content-Types Builder',
|
||||
},
|
||||
permissions: [
|
||||
{
|
||||
action: 'plugins::content-type-builder.read',
|
||||
subject: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
destination: '/plugins/documentation',
|
||||
icon: 'book',
|
||||
isDisplayed: false,
|
||||
label: { id: 'documentation.plugin.name', defaultMessage: 'Documentation' },
|
||||
permissions: [
|
||||
{ action: 'plugins::documentation.read', subject: null },
|
||||
{ action: 'plugins::documentation.regenerate', subject: null },
|
||||
{ action: 'plugins::documentation.update', subject: null },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
const action = {
|
||||
type: 'SET_LINK_PERMISSIONS',
|
||||
results: [
|
||||
{
|
||||
index: 1,
|
||||
hasPermission: true,
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
hasPermission: false,
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
hasPermission: true,
|
||||
},
|
||||
],
|
||||
data: {
|
||||
generalSectionLinks: [
|
||||
{
|
||||
index: 1,
|
||||
hasPermission: true,
|
||||
},
|
||||
{
|
||||
index: 0,
|
||||
hasPermission: false,
|
||||
},
|
||||
{
|
||||
index: 2,
|
||||
hasPermission: true,
|
||||
},
|
||||
],
|
||||
pluginsSectionLinks: [
|
||||
{
|
||||
index: 0,
|
||||
hasPermission: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const expected = {
|
||||
@ -112,6 +148,34 @@ describe('ADMIN | LeftMenu | reducer', () => {
|
||||
permissions: [],
|
||||
},
|
||||
],
|
||||
pluginsSectionLinks: [
|
||||
{
|
||||
destination: '/plugins/content-type-builder',
|
||||
icon: 'paint-brush',
|
||||
isDisplayed: true,
|
||||
label: {
|
||||
id: 'content-type-builder.plugin.name',
|
||||
defaultMessage: 'Content-Types Builder',
|
||||
},
|
||||
permissions: [
|
||||
{
|
||||
action: 'plugins::content-type-builder.read',
|
||||
subject: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
destination: '/plugins/documentation',
|
||||
icon: 'book',
|
||||
isDisplayed: false,
|
||||
label: { id: 'documentation.plugin.name', defaultMessage: 'Documentation' },
|
||||
permissions: [
|
||||
{ action: 'plugins::documentation.read', subject: null },
|
||||
{ action: 'plugins::documentation.regenerate', subject: null },
|
||||
{ action: 'plugins::documentation.update', subject: null },
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(reducer(state, action)).toEqual(expected);
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { get } from 'lodash';
|
||||
|
||||
import { BlockerComponent } from 'strapi-helper-plugin';
|
||||
@ -25,7 +26,7 @@ export function PluginDispatcher(props) {
|
||||
const pluginToRender = get(plugins, pluginId, null);
|
||||
|
||||
if (!pluginToRender) {
|
||||
return null;
|
||||
return <Redirect to="/404" />;
|
||||
}
|
||||
|
||||
const {
|
||||
@ -35,9 +36,7 @@ export function PluginDispatcher(props) {
|
||||
name,
|
||||
preventComponentRendering,
|
||||
} = pluginToRender;
|
||||
let PluginEntryComponent = preventComponentRendering
|
||||
? BlockerComponent
|
||||
: mainComponent;
|
||||
let PluginEntryComponent = preventComponentRendering ? BlockerComponent : mainComponent;
|
||||
|
||||
// Change the plugin's blockerComponent if the plugin uses a custom one.
|
||||
if (preventComponentRendering && blockerComponent) {
|
||||
|
||||
@ -23,14 +23,15 @@ window.strapi = Object.assign(window.strapi || {}, {
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
'strapi-plugin-documentation': require('../../../strapi-plugin-documentation/admin/src').default,
|
||||
'strapi-plugin-users-permissions': require('../../../strapi-plugin-users-permissions/admin/src')
|
||||
.default,
|
||||
'strapi-plugin-content-manager': require('../../../strapi-plugin-content-manager/admin/src')
|
||||
.default,
|
||||
'strapi-plugin-content-type-builder': require('../../../strapi-plugin-content-type-builder/admin/src')
|
||||
.default,
|
||||
'strapi-plugin-documentation': require('../../../strapi-plugin-documentation/admin/src').default,
|
||||
|
||||
'strapi-plugin-email': require('../../../strapi-plugin-email/admin/src').default,
|
||||
'strapi-plugin-upload': require('../../../strapi-plugin-upload/admin/src').default,
|
||||
// 'strapi-plugin-upload': require('../../../strapi-plugin-upload/admin/src').default,
|
||||
'strapi-plugin-graphql': require('../../../strapi-plugin-graphql/admin/src').default,
|
||||
};
|
||||
|
||||
@ -354,12 +354,12 @@ const data = {
|
||||
// },
|
||||
|
||||
// Upload plugin
|
||||
{
|
||||
action: 'plugins::upload.read',
|
||||
subject: null,
|
||||
fields: null,
|
||||
conditions: [],
|
||||
},
|
||||
// {
|
||||
// action: 'plugins::upload.read',
|
||||
// subject: null,
|
||||
// fields: null,
|
||||
// conditions: [],
|
||||
// },
|
||||
{
|
||||
action: 'plugins::upload.assets.create',
|
||||
subject: null,
|
||||
|
||||
@ -7,12 +7,14 @@ import trads from './translations';
|
||||
|
||||
export default strapi => {
|
||||
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
|
||||
const icon = pluginPkg.strapi.icon;
|
||||
const name = pluginPkg.strapi.name;
|
||||
|
||||
const plugin = {
|
||||
blockerComponent: null,
|
||||
blockerComponentProps: {},
|
||||
description: pluginDescription,
|
||||
icon: pluginPkg.strapi.icon,
|
||||
icon,
|
||||
id: pluginId,
|
||||
initializer: Initializer,
|
||||
injectedComponents: [],
|
||||
@ -23,9 +25,29 @@ export default strapi => {
|
||||
leftMenuLinks: [],
|
||||
leftMenuSections: [],
|
||||
mainComponent: App,
|
||||
name: pluginPkg.strapi.name,
|
||||
name,
|
||||
preventComponentRendering: false,
|
||||
trads,
|
||||
menu: {
|
||||
pluginsSection: [
|
||||
{
|
||||
destination: `/plugins/${pluginId}`,
|
||||
icon,
|
||||
label: {
|
||||
id: `${pluginId}.plugin.name`,
|
||||
defaultMessage: name,
|
||||
},
|
||||
name,
|
||||
permissions: [
|
||||
// Uncomment to set the permissions of the plugin here
|
||||
// {
|
||||
// action: '', // the action name should be plugins::plugin-name.actionType
|
||||
// subject: null,
|
||||
// },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return strapi.registerPlugin(plugin);
|
||||
|
||||
@ -43,8 +43,6 @@ export default strapi => {
|
||||
pluginLogo,
|
||||
preventComponentRendering: false,
|
||||
reducers,
|
||||
suffixUrl: () => '/ctm-configurations/models',
|
||||
suffixUrlToReplaceForLeftMenuHighlight: '/models',
|
||||
trads,
|
||||
};
|
||||
|
||||
|
||||
@ -17,11 +17,13 @@ import pluginId from './pluginId';
|
||||
|
||||
export default strapi => {
|
||||
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
|
||||
const icon = pluginPkg.strapi.icon;
|
||||
const name = pluginPkg.strapi.name;
|
||||
const plugin = {
|
||||
blockerComponent: null,
|
||||
blockerComponentProps: {},
|
||||
description: pluginDescription,
|
||||
icon: pluginPkg.strapi.icon,
|
||||
icon,
|
||||
id: pluginId,
|
||||
initializer: Initializer,
|
||||
injectedComponents: [
|
||||
@ -50,10 +52,24 @@ export default strapi => {
|
||||
leftMenuLinks: [],
|
||||
leftMenuSections: [],
|
||||
mainComponent: App,
|
||||
name: pluginPkg.strapi.name,
|
||||
name,
|
||||
pluginLogo,
|
||||
preventComponentRendering: false,
|
||||
trads,
|
||||
menu: {
|
||||
pluginsSectionLinks: [
|
||||
{
|
||||
destination: `/plugins/${pluginId}`,
|
||||
icon,
|
||||
label: {
|
||||
id: `${pluginId}.plugin.name`,
|
||||
defaultMessage: 'Content-Types Builder',
|
||||
},
|
||||
name,
|
||||
permissions: [{ action: 'plugins::content-type-builder.read', subject: null }],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return strapi.registerPlugin(plugin);
|
||||
|
||||
@ -176,5 +176,6 @@
|
||||
"relation.oneToOne": "has and belongs to one",
|
||||
"relation.oneWay": "has one",
|
||||
"table.attributes.title.plural": "{number} fields",
|
||||
"table.attributes.title.singular": "{number} field"
|
||||
"table.attributes.title.singular": "{number} field",
|
||||
"plugin.name": "Content-Types Builder"
|
||||
}
|
||||
|
||||
@ -16,11 +16,13 @@ import trads from './translations';
|
||||
|
||||
export default strapi => {
|
||||
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
|
||||
const icon = pluginPkg.strapi.icon;
|
||||
const name = pluginPkg.strapi.name;
|
||||
const plugin = {
|
||||
blockerComponent: null,
|
||||
blockerComponentProps: {},
|
||||
description: pluginDescription,
|
||||
icon: pluginPkg.strapi.icon,
|
||||
icon,
|
||||
id: pluginId,
|
||||
initializer: Initializer,
|
||||
injectedComponents: [],
|
||||
@ -31,11 +33,29 @@ export default strapi => {
|
||||
leftMenuLinks: [],
|
||||
leftMenuSections: [],
|
||||
mainComponent: App,
|
||||
name: pluginPkg.strapi.name,
|
||||
name,
|
||||
pluginLogo,
|
||||
preventComponentRendering: false,
|
||||
reducers,
|
||||
trads,
|
||||
menu: {
|
||||
pluginsSectionLinks: [
|
||||
{
|
||||
destination: `/plugins/${pluginId}`,
|
||||
icon,
|
||||
label: {
|
||||
id: `${pluginId}.plugin.name`,
|
||||
defaultMessage: 'Documentation',
|
||||
},
|
||||
name,
|
||||
permissions: [
|
||||
{ action: 'plugins::documentation.read', subject: null },
|
||||
{ action: 'plugins::documentation.regenerate', subject: null },
|
||||
{ action: 'plugins::documentation.update', subject: null },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return strapi.registerPlugin(plugin);
|
||||
|
||||
@ -25,5 +25,6 @@
|
||||
"error.noVersion": "A version is required",
|
||||
"error.regenerateDoc.versionMissing": "The version you are trying to generate doesn't exist",
|
||||
"error.deleteDoc.versionMissing": "The version you are trying to delete does not exist.",
|
||||
"notification.update.success": "Settings updated successfully"
|
||||
"notification.update.success": "Settings updated successfully",
|
||||
"plugin.name": "Documentation"
|
||||
}
|
||||
|
||||
@ -19,12 +19,14 @@ import { getTrad } from './utils';
|
||||
|
||||
export default strapi => {
|
||||
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
|
||||
const icon = pluginPkg.strapi.icon;
|
||||
const name = pluginPkg.strapi.name;
|
||||
const plugin = {
|
||||
blockerComponent: null,
|
||||
blockerComponentProps: {},
|
||||
description: pluginDescription,
|
||||
fileModel: null,
|
||||
icon: pluginPkg.strapi.icon,
|
||||
icon,
|
||||
id: pluginId,
|
||||
initializer: Initializer,
|
||||
injectedComponents: [],
|
||||
@ -35,7 +37,7 @@ export default strapi => {
|
||||
leftMenuLinks: [],
|
||||
leftMenuSections: [],
|
||||
mainComponent: App,
|
||||
name: pluginPkg.strapi.name,
|
||||
name,
|
||||
pluginLogo,
|
||||
preventComponentRendering: false,
|
||||
settings: {
|
||||
@ -52,6 +54,20 @@ export default strapi => {
|
||||
],
|
||||
},
|
||||
trads,
|
||||
menu: {
|
||||
pluginsSectionLinks: [
|
||||
{
|
||||
destination: `/plugins/${pluginId}`,
|
||||
icon,
|
||||
label: {
|
||||
id: `${pluginId}.plugin.name`,
|
||||
defaultMessage: 'Media Library',
|
||||
},
|
||||
name,
|
||||
permissions: [{ action: 'plugins::upload.read', subject: null }],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
strapi.registerComponent({ name: 'media-library', Component: InputModalStepper });
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
import { Switch, Redirect, Route, useRouteMatch } from 'react-router-dom';
|
||||
import { NotFound } from 'strapi-helper-plugin';
|
||||
import pluginId from '../../pluginId';
|
||||
|
||||
@ -14,6 +14,13 @@ import EditPage from '../EditPage';
|
||||
import HomePage from '../HomePage';
|
||||
|
||||
const App = () => {
|
||||
const settingType = useRouteMatch(`/plugins/${pluginId}/:settingType`);
|
||||
|
||||
// Todo check if the settingType is allowed
|
||||
if (!settingType) {
|
||||
return <Redirect to={`/plugins/${pluginId}/roles`} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={pluginId}>
|
||||
<Switch>
|
||||
@ -22,11 +29,7 @@ const App = () => {
|
||||
component={EditPage}
|
||||
exact
|
||||
/>
|
||||
<Route
|
||||
path={`/plugins/${pluginId}/:settingType`}
|
||||
component={HomePage}
|
||||
exact
|
||||
/>
|
||||
<Route path={`/plugins/${pluginId}/:settingType`} component={HomePage} exact />
|
||||
<Route component={NotFound} />
|
||||
</Switch>
|
||||
</div>
|
||||
|
||||
@ -17,12 +17,14 @@ import trads from './translations';
|
||||
|
||||
export default strapi => {
|
||||
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
|
||||
const icon = pluginPkg.strapi.icon;
|
||||
const name = pluginPkg.strapi.name;
|
||||
|
||||
const plugin = {
|
||||
blockerComponent: null,
|
||||
blockerComponentProps: {},
|
||||
description: pluginDescription,
|
||||
icon: pluginPkg.strapi.icon,
|
||||
icon,
|
||||
id: pluginId,
|
||||
initializer: Initializer,
|
||||
injectedComponents: [],
|
||||
@ -32,14 +34,35 @@ export default strapi => {
|
||||
leftMenuLinks: [],
|
||||
leftMenuSections: [],
|
||||
mainComponent: App,
|
||||
name: pluginPkg.strapi.name,
|
||||
name,
|
||||
pluginLogo,
|
||||
preventComponentRendering: false,
|
||||
reducers,
|
||||
settings: {},
|
||||
suffixUrl: () => '/roles',
|
||||
suffixUrlToReplaceForLeftMenuHighlight: '/roles',
|
||||
trads,
|
||||
menu: {
|
||||
pluginsSectionLinks: [
|
||||
{
|
||||
destination: `/plugins/${pluginId}`,
|
||||
icon,
|
||||
label: {
|
||||
id: `${pluginId}.plugin.name`,
|
||||
defaultMessage: 'Roles & Permissions',
|
||||
},
|
||||
name,
|
||||
permissions: [
|
||||
{ action: 'plugins::users-permissions.advanced-settings.read', subject: null },
|
||||
{ action: 'plugins::users-permissions.advanced-settings.update', subject: null },
|
||||
{ action: 'plugins::users-permissions.email-templates.read', subject: null },
|
||||
{ action: 'plugins::users-permissions.email-templates.update', subject: null },
|
||||
{ action: 'plugins::users-permissions.providers.read', subject: null },
|
||||
{ action: 'plugins::users-permissions.providers.update', subject: null },
|
||||
{ action: 'plugins::users-permissions.roles.create', subject: null },
|
||||
{ action: 'plugins::users-permissions.roles.read', subject: null },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
return strapi.registerPlugin(plugin);
|
||||
|
||||
@ -112,5 +112,6 @@
|
||||
"notification.success.delete": "The item has been deleted",
|
||||
"notification.success.submit": "Settings have been updated",
|
||||
"plugin.description.long": "Protect your API with a full authentication process based on JWT. This plugin comes also with an ACL strategy that allows you to manage the permissions between the groups of users.",
|
||||
"plugin.description.short": "Protect your API with a full authentication process based on JWT"
|
||||
"plugin.description.short": "Protect your API with a full authentication process based on JWT",
|
||||
"plugin.name": "Roles & Permission"
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user