2019-04-02 21:23:42 +02:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* Admin
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import React from 'react';
|
2019-04-03 13:05:18 +02:00
|
|
|
import ReactGA from 'react-ga';
|
|
|
|
|
import PropTypes from 'prop-types';
|
2019-04-02 21:23:42 +02:00
|
|
|
import { connect } from 'react-redux';
|
|
|
|
|
import { createStructuredSelector } from 'reselect';
|
|
|
|
|
import { bindActionCreators, compose } from 'redux';
|
2019-04-03 13:05:18 +02:00
|
|
|
import { Switch, Route } from 'react-router-dom';
|
2019-04-02 21:23:42 +02:00
|
|
|
|
2019-04-03 13:05:18 +02:00
|
|
|
// Actions from strapi-helper-plugin
|
|
|
|
|
// Actions required for disabling and enabling the OverlayBlocker
|
|
|
|
|
import {
|
|
|
|
|
disableGlobalOverlayBlocker,
|
|
|
|
|
enableGlobalOverlayBlocker,
|
|
|
|
|
} from 'actions/overlayBlocker';
|
|
|
|
|
|
|
|
|
|
// Components from strapi-helper-plugin
|
|
|
|
|
import LoadingIndicatorPage from 'components/LoadingIndicatorPage';
|
|
|
|
|
import OverlayBlocker from 'components/OverlayBlocker';
|
|
|
|
|
|
|
|
|
|
import injectHooks from 'utils/injectHooks';
|
|
|
|
|
|
|
|
|
|
import Header from '../../components/Header/index';
|
|
|
|
|
import Logout from '../../components/Logout';
|
|
|
|
|
|
|
|
|
|
import ComingSoonPage from '../ComingSoonPage';
|
|
|
|
|
import LeftMenu from '../LeftMenu';
|
|
|
|
|
import LocaleToggle from '../LocaleToggle';
|
|
|
|
|
import HomePage from '../HomePage/Loadable';
|
|
|
|
|
import Marketplace from '../Marketplace/Loadable';
|
|
|
|
|
import ListPluginsPage from '../ListPluginsPage/Loadable';
|
|
|
|
|
import NotFoundPage from '../NotFoundPage/Loadable';
|
|
|
|
|
import PluginDispatcher from '../PluginDispatcher';
|
|
|
|
|
|
|
|
|
|
import { updatePlugin } from '../App/actions';
|
|
|
|
|
import makeSelecApp from '../App/selectors';
|
|
|
|
|
|
|
|
|
|
import injectSaga from '../../utils/injectSaga';
|
|
|
|
|
import injectReducer from '../../utils/injectReducer';
|
|
|
|
|
|
|
|
|
|
import localeToggleReducer from '../LocaleToggle/reducer';
|
|
|
|
|
import {
|
|
|
|
|
resetLocaleDefaultClassName,
|
|
|
|
|
setLocaleCustomClassName,
|
|
|
|
|
} from '../LocaleToggle/actions';
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
emitEvent,
|
|
|
|
|
getInitData,
|
|
|
|
|
getSecuredData,
|
|
|
|
|
hideLeftMenu,
|
|
|
|
|
hideLogout,
|
|
|
|
|
setAppError,
|
|
|
|
|
setAppSecured,
|
|
|
|
|
showLeftMenu,
|
|
|
|
|
showLogout,
|
|
|
|
|
unsetAppSecured,
|
|
|
|
|
} from './actions';
|
2019-04-02 21:23:42 +02:00
|
|
|
import makeSelectAdmin from './selectors';
|
|
|
|
|
import reducer from './reducer';
|
|
|
|
|
import saga from './saga';
|
|
|
|
|
|
2019-04-03 13:05:18 +02:00
|
|
|
import NavTopRightWrapper from './NavTopRightWrapper';
|
|
|
|
|
|
|
|
|
|
import styles from './styles.scss';
|
|
|
|
|
|
2019-04-02 21:23:42 +02:00
|
|
|
export class Admin extends React.Component {
|
|
|
|
|
// eslint-disable-line react/prefer-stateless-function
|
2019-04-03 13:05:18 +02:00
|
|
|
state = { shouldSecureAfterAllPluginsAreMounted: true };
|
|
|
|
|
|
|
|
|
|
getChildContext = () => ({
|
|
|
|
|
emitEvent: this.props.emitEvent,
|
|
|
|
|
currentEnvironment: this.props.admin.currentEnvironment,
|
|
|
|
|
disableGlobalOverlayBlocker: this.props.disableGlobalOverlayBlocker,
|
|
|
|
|
enableGlobalOverlayBlocker: this.props.enableGlobalOverlayBlocker,
|
|
|
|
|
plugins: this.props.global.plugins,
|
|
|
|
|
updatePlugin: this.props.updatePlugin,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
// Initialize Google Analytics
|
|
|
|
|
// Refer to ../../../doc/disable-tracking.md for more informations
|
|
|
|
|
/* istanbul ignore next */
|
|
|
|
|
ReactGA.initialize('UA-54313258-9', {
|
|
|
|
|
testMode: process.env.NODE_ENV === 'test',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Retrieve the main settings of the application
|
|
|
|
|
this.props.getInitData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
componentDidUpdate(prevProps) {
|
|
|
|
|
const {
|
|
|
|
|
admin: { didGetSecuredData, isLoading, isSecured },
|
|
|
|
|
location: { pathname },
|
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
|
|
if (!isLoading && this.state.shouldSecureAfterAllPluginsAreMounted) {
|
|
|
|
|
if (!this.hasApluginNotReady(this.props)) {
|
|
|
|
|
this.props.getHook('willSecure');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (prevProps.location.pathname !== pathname) {
|
|
|
|
|
this.props.getHook('willSecure');
|
|
|
|
|
|
|
|
|
|
/* istanbul ignore if */
|
|
|
|
|
if (this.isAcceptingTracking()) {
|
|
|
|
|
ReactGA.pageview(pathname, {
|
|
|
|
|
testMode: process.env.NODE_ENV === 'test',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (prevProps.admin.isSecured !== isSecured && isSecured) {
|
|
|
|
|
this.props.getSecuredData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (prevProps.admin.didGetSecuredData !== didGetSecuredData) {
|
|
|
|
|
this.props.getHook('didGetSecuredData');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* istanbul ignore next */
|
|
|
|
|
componentDidCatch(error, info) {
|
|
|
|
|
/* eslint-disable */
|
|
|
|
|
console.log('An error has occured');
|
|
|
|
|
console.log('--------------------');
|
|
|
|
|
console.log(error);
|
|
|
|
|
console.log('Here is some infos');
|
|
|
|
|
console.log(info);
|
|
|
|
|
/* eslint-enable */
|
|
|
|
|
|
|
|
|
|
// Display the error log component which is not designed yet
|
|
|
|
|
this.props.setAppError();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
getContentWrapperStyle = () => {
|
|
|
|
|
const {
|
|
|
|
|
admin: { showMenu },
|
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
|
|
return showMenu
|
|
|
|
|
? { main: {}, sub: styles.content }
|
|
|
|
|
: { main: { width: '100%' }, sub: styles.wrapper };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
hasApluginNotReady = props => {
|
|
|
|
|
const {
|
|
|
|
|
global: { plugins },
|
|
|
|
|
} = props;
|
|
|
|
|
|
|
|
|
|
return !Object.keys(plugins).every(
|
|
|
|
|
plugin => plugins[plugin].isReady === true,
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
helpers = {
|
|
|
|
|
hideLeftMenu: this.props.hideLeftMenu,
|
|
|
|
|
hideLogout: this.props.hideLogout,
|
|
|
|
|
setAppSecured: this.props.setAppSecured,
|
|
|
|
|
showLeftMenu: this.props.showLeftMenu,
|
|
|
|
|
showLogout: this.props.showLogout,
|
|
|
|
|
unsetAppSecured: this.props.unsetAppSecured,
|
|
|
|
|
updatePlugin: this.props.updatePlugin,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
isAcceptingTracking = () => {
|
|
|
|
|
const {
|
|
|
|
|
admin: { uuid },
|
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
|
|
return !!uuid;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Display the app loader until the app is ready
|
|
|
|
|
* @returns {Boolean}
|
|
|
|
|
*/
|
|
|
|
|
showLoader = () => {
|
|
|
|
|
const {
|
|
|
|
|
admin: { isLoading },
|
|
|
|
|
global: { isAppLoading },
|
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
|
|
if (isAppLoading) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this.hasApluginNotReady(this.props);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
renderInitializers = () => {
|
|
|
|
|
const {
|
|
|
|
|
global: { plugins },
|
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
|
|
return Object.keys(plugins).reduce((acc, current) => {
|
|
|
|
|
const InitializerComponent = plugins[current].initializer;
|
|
|
|
|
const key = plugins[current].id;
|
|
|
|
|
|
|
|
|
|
if (InitializerComponent) {
|
|
|
|
|
// We don't check if the initializer is correct because there's a fallback in cdc
|
|
|
|
|
acc.push(
|
|
|
|
|
<InitializerComponent key={key} {...this.props} {...this.helpers} />,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return acc;
|
|
|
|
|
}, []);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
renderMarketPlace = props => <Marketplace {...props} {...this.props} />;
|
|
|
|
|
|
|
|
|
|
renderPluginDispatcher = props => {
|
|
|
|
|
// NOTE: Send the needed props instead of everything...
|
|
|
|
|
|
|
|
|
|
return <PluginDispatcher {...this.props} {...props} {...this.helpers} />;
|
|
|
|
|
};
|
|
|
|
|
|
2019-04-02 21:23:42 +02:00
|
|
|
render() {
|
2019-04-03 13:05:18 +02:00
|
|
|
const {
|
|
|
|
|
admin: {
|
|
|
|
|
appError,
|
|
|
|
|
isLoading,
|
|
|
|
|
showLogoutComponent,
|
|
|
|
|
showMenu,
|
|
|
|
|
strapiVersion,
|
|
|
|
|
},
|
|
|
|
|
global: { blockApp, overlayBlockerData, plugins, showGlobalAppBlocker },
|
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
|
|
if (appError) {
|
|
|
|
|
return <div>An error has occured please check your logs</div>;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
|
return <LoadingIndicatorPage />;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We need the admin data in order to make the initializers work
|
|
|
|
|
if (this.showLoader()) {
|
|
|
|
|
return (
|
|
|
|
|
<React.Fragment>
|
|
|
|
|
{this.renderInitializers()}
|
|
|
|
|
<LoadingIndicatorPage />
|
|
|
|
|
</React.Fragment>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className={styles.adminPage}>
|
|
|
|
|
{showMenu && <LeftMenu version={strapiVersion} plugins={plugins} />}
|
|
|
|
|
<NavTopRightWrapper>
|
|
|
|
|
{/* Injection zone not ready yet */}
|
|
|
|
|
{showLogoutComponent && <Logout />}
|
|
|
|
|
<LocaleToggle isLogged />
|
|
|
|
|
</NavTopRightWrapper>
|
|
|
|
|
<div
|
|
|
|
|
className={styles.adminPageRightWrapper}
|
|
|
|
|
style={this.getContentWrapperStyle().main}
|
|
|
|
|
>
|
|
|
|
|
{showMenu ? <Header /> : ''}
|
|
|
|
|
<div className={this.getContentWrapperStyle().sub}>
|
|
|
|
|
<Switch>
|
|
|
|
|
<Route path='/' component={HomePage} exact />
|
|
|
|
|
<Route
|
|
|
|
|
path='/plugins/:pluginId'
|
|
|
|
|
render={this.renderPluginDispatcher}
|
|
|
|
|
/>
|
|
|
|
|
<Route path='/plugins' component={ComingSoonPage} />
|
|
|
|
|
<Route path='/list-plugins' component={ListPluginsPage} exact />
|
|
|
|
|
<Route
|
|
|
|
|
path='/marketplace'
|
|
|
|
|
render={this.renderMarketPlace}
|
|
|
|
|
exact
|
|
|
|
|
/>
|
|
|
|
|
<Route path='/configuration' component={ComingSoonPage} exact />
|
|
|
|
|
<Route key='7' path='' component={NotFoundPage} />
|
|
|
|
|
<Route key='8' path='404' component={NotFoundPage} />
|
|
|
|
|
</Switch>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<OverlayBlocker
|
|
|
|
|
key='overlayBlocker'
|
|
|
|
|
isOpen={blockApp && showGlobalAppBlocker}
|
|
|
|
|
{...overlayBlockerData}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
2019-04-02 21:23:42 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-03 13:05:18 +02:00
|
|
|
Admin.childContextTypes = {
|
|
|
|
|
emitEvent: PropTypes.func,
|
|
|
|
|
currentEnvironment: PropTypes.string,
|
|
|
|
|
disableGlobalOverlayBlocker: PropTypes.func,
|
|
|
|
|
enableGlobalOverlayBlocker: PropTypes.func,
|
|
|
|
|
plugins: PropTypes.object,
|
|
|
|
|
updatePlugin: PropTypes.func,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Admin.propTypes = {
|
|
|
|
|
admin: PropTypes.shape({
|
|
|
|
|
autoReload: PropTypes.bool,
|
|
|
|
|
appError: PropTypes.bool,
|
|
|
|
|
currentEnvironment: PropTypes.string,
|
|
|
|
|
didGetSecuredData: PropTypes.bool,
|
|
|
|
|
isLoading: PropTypes.bool,
|
|
|
|
|
isSecured: PropTypes.bool,
|
|
|
|
|
layout: PropTypes.object,
|
|
|
|
|
showLogoutComponent: PropTypes.bool,
|
|
|
|
|
showMenu: PropTypes.bool,
|
|
|
|
|
strapiVersion: PropTypes.string,
|
|
|
|
|
uuid: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
|
|
|
|
|
}).isRequired,
|
|
|
|
|
disableGlobalOverlayBlocker: PropTypes.func.isRequired,
|
|
|
|
|
emitEvent: PropTypes.func.isRequired,
|
|
|
|
|
enableGlobalOverlayBlocker: PropTypes.func.isRequired,
|
|
|
|
|
getHook: PropTypes.func.isRequired,
|
|
|
|
|
getInitData: PropTypes.func.isRequired,
|
|
|
|
|
getSecuredData: PropTypes.func.isRequired,
|
|
|
|
|
global: PropTypes.shape({
|
|
|
|
|
appPlugins: PropTypes.array,
|
|
|
|
|
blockApp: PropTypes.bool,
|
|
|
|
|
overlayBlockerData: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
|
|
|
|
|
isAppLoading: PropTypes.bool,
|
|
|
|
|
plugins: PropTypes.object,
|
|
|
|
|
showGlobalAppBlocker: PropTypes.bool,
|
|
|
|
|
}).isRequired,
|
|
|
|
|
hideLeftMenu: PropTypes.func.isRequired,
|
|
|
|
|
hideLogout: PropTypes.func.isRequired,
|
|
|
|
|
location: PropTypes.object.isRequired,
|
|
|
|
|
resetLocaleDefaultClassName: PropTypes.func.isRequired,
|
|
|
|
|
setAppError: PropTypes.func.isRequired,
|
|
|
|
|
setAppSecured: PropTypes.func.isRequired,
|
|
|
|
|
showLeftMenu: PropTypes.func.isRequired,
|
|
|
|
|
showLogout: PropTypes.func.isRequired,
|
|
|
|
|
unsetAppSecured: PropTypes.func.isRequired,
|
|
|
|
|
updatePlugin: PropTypes.func.isRequired,
|
|
|
|
|
};
|
2019-04-02 21:23:42 +02:00
|
|
|
|
|
|
|
|
const mapStateToProps = createStructuredSelector({
|
|
|
|
|
admin: makeSelectAdmin(),
|
2019-04-03 13:05:18 +02:00
|
|
|
global: makeSelecApp(),
|
2019-04-02 21:23:42 +02:00
|
|
|
});
|
|
|
|
|
|
2019-04-03 13:05:18 +02:00
|
|
|
export function mapDispatchToProps(dispatch) {
|
|
|
|
|
return bindActionCreators(
|
|
|
|
|
{
|
|
|
|
|
disableGlobalOverlayBlocker,
|
|
|
|
|
emitEvent,
|
|
|
|
|
enableGlobalOverlayBlocker,
|
|
|
|
|
getInitData,
|
|
|
|
|
getSecuredData,
|
|
|
|
|
hideLeftMenu,
|
|
|
|
|
hideLogout,
|
|
|
|
|
resetLocaleDefaultClassName,
|
|
|
|
|
setAppError,
|
|
|
|
|
setAppSecured,
|
|
|
|
|
setLocaleCustomClassName,
|
|
|
|
|
showLeftMenu,
|
|
|
|
|
showLogout,
|
|
|
|
|
unsetAppSecured,
|
|
|
|
|
updatePlugin,
|
|
|
|
|
},
|
|
|
|
|
dispatch,
|
|
|
|
|
);
|
2019-04-02 21:23:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const withConnect = connect(
|
|
|
|
|
mapStateToProps,
|
|
|
|
|
mapDispatchToProps,
|
|
|
|
|
);
|
2019-04-03 13:05:18 +02:00
|
|
|
const withReducer = injectReducer({ key: 'admin', reducer });
|
|
|
|
|
const withSaga = injectSaga({ key: 'admin', saga });
|
|
|
|
|
const withLocaleToggleReducer = injectReducer({
|
|
|
|
|
key: 'localeToggle',
|
|
|
|
|
reducer: localeToggleReducer,
|
|
|
|
|
});
|
|
|
|
|
const withHooks = injectHooks({ key: 'admin' });
|
2019-04-02 21:23:42 +02:00
|
|
|
|
|
|
|
|
export default compose(
|
|
|
|
|
withReducer,
|
2019-04-03 13:05:18 +02:00
|
|
|
withLocaleToggleReducer,
|
2019-04-02 21:23:42 +02:00
|
|
|
withSaga,
|
|
|
|
|
withConnect,
|
2019-04-03 13:05:18 +02:00
|
|
|
withHooks,
|
2019-04-02 21:23:42 +02:00
|
|
|
)(Admin);
|