mirror of
https://github.com/vale981/Vulcan
synced 2025-03-05 09:31:43 -05:00
Merge branch 'devel' of https://github.com/VulcanJS/Vulcan into formTest
This commit is contained in:
commit
37e9937129
16 changed files with 202 additions and 76 deletions
27
packages/vulcan-admin/lib/components/AdminLayout.jsx
Normal file
27
packages/vulcan-admin/lib/components/AdminLayout.jsx
Normal file
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* @Author: Apollinaire Lecocq <apollinaire>
|
||||
* @Date: 08-01-19
|
||||
* @Last modified by: apollinaire
|
||||
* @Last modified time: 10-01-19
|
||||
*/
|
||||
import React from 'react';
|
||||
import {registerComponent, Components, withAccess, Dummy} from 'meteor/vulcan:core';
|
||||
|
||||
const RestrictToAdmins = withAccess({groups: ['admins']})(Dummy);
|
||||
|
||||
/**
|
||||
* A simple component that renders the existing layout and checks wether the currentUser is an admin or not.
|
||||
*/
|
||||
|
||||
function AdminLayout({children}) {
|
||||
return (
|
||||
<Components.Layout>
|
||||
<RestrictToAdmins>{children}</RestrictToAdmins>
|
||||
</Components.Layout>
|
||||
);
|
||||
}
|
||||
|
||||
registerComponent({
|
||||
name: 'AdminLayout',
|
||||
component: AdminLayout,
|
||||
});
|
|
@ -1,3 +1,4 @@
|
|||
import './fragments.js';
|
||||
import './routes.js';
|
||||
import './i18n.js';
|
||||
import '../components/AdminLayout';
|
||||
|
|
|
@ -1,5 +1,14 @@
|
|||
import { addRoute, getDynamicComponent } from 'meteor/vulcan:core';
|
||||
import {addRoute, getDynamicComponent} from 'meteor/vulcan:core';
|
||||
import React from 'react';
|
||||
|
||||
addRoute({ name: 'admin', path: '/admin', component: () => getDynamicComponent(import('../components/AdminHome.jsx'))});
|
||||
addRoute({ name: 'admin2', path: '/admin/users', component: () => getDynamicComponent(import('../components/AdminHome.jsx'))});
|
||||
addRoute({
|
||||
name: 'admin',
|
||||
path: '/admin',
|
||||
component: () => getDynamicComponent(import('../components/AdminHome.jsx')),
|
||||
layoutName: 'AdminLayout',
|
||||
});
|
||||
addRoute({
|
||||
name: 'admin2',
|
||||
path: '/admin/users',
|
||||
component: () => getDynamicComponent(import('../components/AdminHome.jsx')),
|
||||
});
|
||||
|
|
|
@ -17,8 +17,6 @@ import { withApollo } from 'react-apollo';
|
|||
import { withCookies } from 'react-cookie';
|
||||
import moment from 'moment';
|
||||
|
||||
const DummyErrorCatcher = ({ children }) => children;
|
||||
|
||||
class App extends PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
@ -114,7 +112,7 @@ class App extends PureComponent {
|
|||
const LayoutComponent = currentRoute.layoutName ? Components[currentRoute.layoutName] : Components.Layout;
|
||||
|
||||
// if defined, use ErrorCatcher component to wrap layout contents
|
||||
const ErrorCatcher = Components.ErrorCatcher ? Components.ErrorCatcher : DummyErrorCatcher;
|
||||
const ErrorCatcher = Components.ErrorCatcher ? Components.ErrorCatcher : Components.Dummy;
|
||||
|
||||
return (
|
||||
<IntlProvider locale={this.getLocale()} key={this.getLocale()} messages={Strings[this.getLocale()]}>
|
||||
|
@ -125,7 +123,7 @@ class App extends PureComponent {
|
|||
{this.props.currentUserLoading ? (
|
||||
<Components.Loading />
|
||||
) : this.props.children ? (
|
||||
<ErrorCatcher siteData={this.props.siteData}>{this.props.children}</ErrorCatcher>
|
||||
<ErrorCatcher>{this.props.children}</ErrorCatcher>
|
||||
) : (
|
||||
<Components.Welcome />
|
||||
)}
|
||||
|
|
10
packages/vulcan-core/lib/modules/components/Dummy.jsx
Normal file
10
packages/vulcan-core/lib/modules/components/Dummy.jsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
import React from 'react';
|
||||
import {registerComponent} from 'meteor/vulcan:lib';
|
||||
|
||||
function Dummy({children}) {
|
||||
return children;
|
||||
}
|
||||
Dummy.displayName = 'Dummy';
|
||||
|
||||
registerComponent({name: 'Dummy', component: Dummy});
|
||||
export default Dummy;
|
|
@ -1,31 +1,63 @@
|
|||
import React, { PureComponent } from 'react';
|
||||
import { withCurrentUser } from 'meteor/vulcan:core';
|
||||
import { withRouter } from 'react-router';
|
||||
import React, {PureComponent} from 'react';
|
||||
import {Components} from 'meteor/vulcan:lib';
|
||||
import withCurrentUser from './withCurrentUser';
|
||||
import {withRouter} from 'react-router';
|
||||
import Users from 'meteor/vulcan:users';
|
||||
|
||||
export default function withAccess (options) {
|
||||
/**
|
||||
* withAccess - description
|
||||
*
|
||||
* @param {Object} options the options that define the hoc
|
||||
* @param {string[]} options.groups the groups that have access to this component
|
||||
* @param {string} options.redirect the link to redirect to in case the access is not granted (optional)
|
||||
* @param {string} options.failureComponentName the name of a component to display if access is not granted (optional)
|
||||
* @param {Component} options.failureComponent the component to display if access is not granted (optional)
|
||||
* @return {PureComponent} a React component that will display only if the acces is granted
|
||||
*/
|
||||
|
||||
const { groups, redirect } = options;
|
||||
export default function withAccess(options) {
|
||||
const {
|
||||
groups = [],
|
||||
redirect = null,
|
||||
failureComponent = null,
|
||||
failureComponentName = null,
|
||||
} = options;
|
||||
|
||||
// we return a function that takes a component and itself returns a component
|
||||
return WrappedComponent => {
|
||||
class AccessComponent extends PureComponent {
|
||||
|
||||
// if there are any groups defined check if user belongs, else just check if user exists
|
||||
canAccess = currentUser => {
|
||||
return groups ? Users.isMemberOf(currentUser, groups) : currentUser;
|
||||
}
|
||||
};
|
||||
|
||||
// redirect on constructor if user cannot access
|
||||
constructor(props) {
|
||||
super(props);
|
||||
if(!this.canAccess(props.currentUser) && typeof redirect === 'string') {
|
||||
if (
|
||||
!this.canAccess(props.currentUser) &&
|
||||
typeof redirect === 'string'
|
||||
) {
|
||||
props.router.push(redirect);
|
||||
}
|
||||
}
|
||||
|
||||
renderFailureComponent() {
|
||||
if (failureComponentName) {
|
||||
const FailureComponent = Components[failureComponentName];
|
||||
return <FailureComponent {...this.props} />;
|
||||
} else if (failureComponent) {
|
||||
const FailureComponent = failureComponent; // necesary because jsx components must be uppercase
|
||||
return <FailureComponent {...this.props} />;
|
||||
} else return null;
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.canAccess(this.props.currentUser) ? <WrappedComponent {...this.props}/> : null;
|
||||
return this.canAccess(this.props.currentUser) ? (
|
||||
<WrappedComponent {...this.props} />
|
||||
) : (
|
||||
this.renderFailureComponent()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -207,11 +207,12 @@ const registerCollectionCallbacks = (typeName, options) => {
|
|||
if (options.create) {
|
||||
registerCallback({
|
||||
name: `${typeName}.create.validate`,
|
||||
iterator: { document: 'The document being inserted' },
|
||||
iterator: { validationErrors: 'An array that can be used to accumulate validation errors' },
|
||||
properties: [
|
||||
{ document: 'The document being inserted' },
|
||||
{ currentUser: 'The current user' },
|
||||
{ validationErrors: 'An object that can be used to accumulate validation errors' },
|
||||
{ collection: 'The collection the document belongs to' },
|
||||
{ context: 'The context of the mutation'},
|
||||
],
|
||||
runs: 'sync',
|
||||
returns: 'document',
|
||||
|
@ -248,11 +249,13 @@ const registerCollectionCallbacks = (typeName, options) => {
|
|||
if (options.update) {
|
||||
registerCallback({
|
||||
name: `${typeName}.update.validate`,
|
||||
iterator: { data: 'The client data' },
|
||||
iterator: { validationErrors: 'An object that can be used to accumulate validation errors' },
|
||||
properties: [
|
||||
{ document: 'The document being edited' },
|
||||
{ data: 'The client data' },
|
||||
{ currentUser: 'The current user' },
|
||||
{ validationErrors: 'An object that can be used to accumulate validation errors' },
|
||||
{ collection: 'The collection the document belongs to' },
|
||||
{ context: 'The context of the mutation'},
|
||||
],
|
||||
runs: 'sync',
|
||||
returns: 'modifier',
|
||||
|
@ -296,10 +299,12 @@ const registerCollectionCallbacks = (typeName, options) => {
|
|||
if (options.delete) {
|
||||
registerCallback({
|
||||
name: `${typeName}.delete.validate`,
|
||||
iterator: { document: 'The document being removed' },
|
||||
iterator: { validationErrors: 'An object that can be used to accumulate validation errors' },
|
||||
properties: [
|
||||
{ currentUser: 'The current user' },
|
||||
{ validationErrors: 'An object that can be used to accumulate validation errors' },
|
||||
{ document: 'The document being removed' },
|
||||
{ collection: 'The collection the document belongs to'},
|
||||
{ context: 'The context of this mutation'}
|
||||
],
|
||||
runs: 'sync',
|
||||
returns: 'document',
|
||||
|
|
|
@ -24,6 +24,7 @@ export { default as Flash } from './components/Flash.jsx';
|
|||
export { default as HelloWorld } from './components/HelloWorld.jsx';
|
||||
export { default as Welcome } from './components/Welcome.jsx';
|
||||
export { default as RouterHook } from './components/RouterHook.jsx';
|
||||
export { default as Dummy } from './components/Dummy.jsx';
|
||||
|
||||
export { default as withAccess } from './containers/withAccess.js';
|
||||
export { default as withMessages } from './containers/withMessages.js';
|
||||
|
@ -35,6 +36,7 @@ export { default as withDelete } from './containers/withDelete.js';
|
|||
export { default as withCurrentUser } from './containers/withCurrentUser.js';
|
||||
export { default as withMutation } from './containers/withMutation.js';
|
||||
export { default as withUpsert } from './containers/withUpsert.js';
|
||||
export { default as withSiteData } from './containers/withSiteData.js';
|
||||
|
||||
export { default as withComponents } from './containers/withComponents';
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
import React from 'react';
|
||||
import { Components, registerComponent } from 'meteor/vulcan:lib';
|
||||
|
||||
const adminStyles = {
|
||||
padding: '20px'
|
||||
};
|
||||
|
||||
const AdminLayout = props => <div className="admin-layout" style={adminStyles}>{props.children}</div>;
|
||||
|
||||
registerComponent('AdminLayout', AdminLayout);
|
10
packages/vulcan-debug/lib/components/DebugLayout.jsx
Normal file
10
packages/vulcan-debug/lib/components/DebugLayout.jsx
Normal file
|
@ -0,0 +1,10 @@
|
|||
import React from 'react';
|
||||
import { Components, registerComponent } from 'meteor/vulcan:lib';
|
||||
|
||||
const debugStyles = {
|
||||
padding: '20px'
|
||||
};
|
||||
|
||||
const DebugLayout = props => <div className="debug-layout" style={debugStyles}>{props.children}</div>;
|
||||
|
||||
registerComponent('DebugLayout', DebugLayout);
|
|
@ -1,26 +1,31 @@
|
|||
import React from 'react';
|
||||
import { registerComponent, Components, Routes } from 'meteor/vulcan:lib';
|
||||
import { Link } from 'react-router';
|
||||
import {registerComponent, Components, Routes} from 'meteor/vulcan:lib';
|
||||
import {Link} from 'react-router';
|
||||
|
||||
const RoutePath = ({ document }) =>
|
||||
<Link to={document.path}>{document.path}</Link>;
|
||||
const RoutePath = ({document}) => (
|
||||
<Link to={document.path}>{document.path}</Link>
|
||||
);
|
||||
|
||||
const RoutesDashboard = props =>
|
||||
<div className="routes">
|
||||
<Components.Datatable
|
||||
showSearch={false}
|
||||
showNew={false}
|
||||
showEdit={false}
|
||||
data={Object.values(Routes)}
|
||||
columns={[
|
||||
'name',
|
||||
{
|
||||
name: 'path',
|
||||
component: RoutePath
|
||||
},
|
||||
'componentName',
|
||||
]}
|
||||
/>
|
||||
</div>;
|
||||
const RoutesDashboard = props => {
|
||||
return (
|
||||
<div className="routes">
|
||||
<Components.Datatable
|
||||
showSearch={false}
|
||||
showNew={false}
|
||||
showEdit={false}
|
||||
data={Object.values(Routes)}
|
||||
columns={[
|
||||
'name',
|
||||
{
|
||||
name: 'path',
|
||||
component: RoutePath,
|
||||
},
|
||||
'componentName',
|
||||
'layoutName'
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
registerComponent('Routes', RoutesDashboard);
|
||||
registerComponent('Routes', RoutesDashboard);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import '../components/AdminLayout.jsx';
|
||||
import '../components/DebugLayout.jsx';
|
||||
|
||||
import '../components/Emails.jsx';
|
||||
import '../components/Groups.jsx';
|
||||
|
|
|
@ -1,14 +1,54 @@
|
|||
import { addRoute, getDynamicComponent } from 'meteor/vulcan:lib';
|
||||
import {addRoute, getDynamicComponent} from 'meteor/vulcan:lib';
|
||||
|
||||
addRoute([
|
||||
// {name: 'cheatsheet', path: '/cheatsheet', component: import('./components/Cheatsheet.jsx')},
|
||||
{ name: 'debug', path: '/debug', componentName: 'DebugDashboard', layoutName: 'AdminLayout' },
|
||||
{ name: 'debugGroups', path: '/debug/groups', component: () => getDynamicComponent(import('../components/Groups.jsx')), layoutName: 'AdminLayout' },
|
||||
{ name: 'debugSettings', path: '/debug/settings', componentName: 'Settings', layoutName: 'AdminLayout' },
|
||||
{ name: 'debugCallbacks', path: '/debug/callbacks', componentName: 'Callbacks', layoutName: 'AdminLayout' },
|
||||
{
|
||||
name: 'debug',
|
||||
path: '/debug',
|
||||
componentName: 'DebugDashboard',
|
||||
layoutName: 'DebugLayout',
|
||||
},
|
||||
{
|
||||
name: 'debugGroups',
|
||||
path: '/debug/groups',
|
||||
component: () => getDynamicComponent(import('../components/Groups.jsx')),
|
||||
layoutName: 'DebugLayout',
|
||||
},
|
||||
{
|
||||
name: 'debugSettings',
|
||||
path: '/debug/settings',
|
||||
componentName: 'Settings',
|
||||
layoutName: 'DebugLayout',
|
||||
},
|
||||
{
|
||||
name: 'debugCallbacks',
|
||||
path: '/debug/callbacks',
|
||||
componentName: 'Callbacks',
|
||||
layoutName: 'DebugLayout',
|
||||
},
|
||||
// {name: 'emails', path: '/emails', component: () => getDynamicComponent(import('./components/Emails.jsx'))},
|
||||
{ name: 'debugEmails', path: '/debug/emails', componentName: 'Emails', layoutName: 'AdminLayout' },
|
||||
{ name: 'debugRoutes', path: '/debug/routes', componentName: 'Routes', layoutName: 'AdminLayout' },
|
||||
{ name: 'debugComponents', path: '/debug/components', componentName: 'Components', layoutName: 'AdminLayout' },
|
||||
{ name: 'debugI18n', path: '/debug/i18n', componentName: 'I18n', layoutName: 'AdminLayout' },
|
||||
{
|
||||
name: 'debugEmails',
|
||||
path: '/debug/emails',
|
||||
componentName: 'Emails',
|
||||
layoutName: 'DebugLayout',
|
||||
},
|
||||
{
|
||||
name: 'debugRoutes',
|
||||
path: '/debug/routes',
|
||||
componentName: 'Routes',
|
||||
layoutName: 'DebugLayout',
|
||||
},
|
||||
{
|
||||
name: 'debugComponents',
|
||||
path: '/debug/components',
|
||||
componentName: 'Components',
|
||||
layoutName: 'DebugLayout',
|
||||
},
|
||||
{
|
||||
name: 'debugI18n',
|
||||
path: '/debug/i18n',
|
||||
componentName: 'I18n',
|
||||
layoutName: 'DebugLayout',
|
||||
},
|
||||
]);
|
||||
|
|
|
@ -10,7 +10,7 @@ Usage:
|
|||
|
||||
*/
|
||||
|
||||
import { Components, registerComponent, withCurrentUser } from 'meteor/vulcan:core';
|
||||
import { Components, registerComponent, withCurrentUser, withSiteData } from 'meteor/vulcan:core';
|
||||
import React, { Component } from 'react';
|
||||
import { Errors } from '../modules/errors.js';
|
||||
|
||||
|
@ -43,4 +43,4 @@ class ErrorCatcher extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
registerComponent('ErrorCatcher', ErrorCatcher, withCurrentUser);
|
||||
registerComponent('ErrorCatcher', ErrorCatcher, withCurrentUser, withSiteData);
|
||||
|
|
|
@ -97,15 +97,12 @@ class FormNestedArray extends PureComponent {
|
|||
)
|
||||
),
|
||||
(!maxCount || arrayLength < maxCount) && (
|
||||
<Components.Button
|
||||
<Components.FormNestedFoot
|
||||
key="add-button"
|
||||
size="small"
|
||||
variant="success"
|
||||
onClick={this.addItem}
|
||||
className="form-nested-button"
|
||||
>
|
||||
<Components.IconAdd height={12} width={12} />
|
||||
</Components.Button>
|
||||
addItem={this.addItem}
|
||||
label={this.props.label}
|
||||
className="form-nested-foot"
|
||||
/>
|
||||
),
|
||||
hasErrors ? (
|
||||
<FormComponents.FieldErrors
|
||||
|
|
|
@ -2,8 +2,8 @@ import React from 'react';
|
|||
import PropTypes from 'prop-types';
|
||||
import { Components, registerComponent } from 'meteor/vulcan:core';
|
||||
|
||||
const FormNestedFoot = ({ label, addItem }) => (
|
||||
<Components.Button size="small" variant="success" iconButton onClick={addItem} className="form-nested-button">
|
||||
const FormNestedFoot = ({ addItem }) => (
|
||||
<Components.Button size="small" variant="success" onClick={addItem} className="form-nested-button">
|
||||
<Components.IconAdd height={12} width={12} />
|
||||
</Components.Button>
|
||||
);
|
||||
|
|
Loading…
Add table
Reference in a new issue