Merge branch 'master' into patch-2

This commit is contained in:
Jim LAURIE 2019-04-03 15:39:33 +02:00 committed by GitHub
commit f28fed0e69
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 226 additions and 195 deletions

View File

@ -1,3 +1,10 @@
> ⚠️ We have stopped merging PRs for now to the Strapi core.<br><br>
> The reason is that we are developing new architecture for the admin panel and for the plugins.<br>
> This new architecture will provide stability of the Strapi core as we approach the release of Beta.<br>
> We appreciate and welcome all your contributions, but until further notice, please do not submit a PR as it will not be merged.<br>
> Furthermore, you will have to rewrite it based on the new architecture.
<!-- ⚠️ Your PR title will appear in the changelogs please make it short detailed and understandable for all. --> <!-- ⚠️ Your PR title will appear in the changelogs please make it short detailed and understandable for all. -->
<!-- Write a short description of what your PR does and link the concerned issues of your update. --> <!-- Write a short description of what your PR does and link the concerned issues of your update. -->

View File

@ -32,10 +32,14 @@ install:
jobs: jobs:
include: include:
- stage: test - stage: test
name: 'Lint / Snyk / Unit Tests ' name: Snyk
script: npm run -s test:snyk
if: fork = false
- stage: test
name: 'Lint / Unit Tests '
script: script:
- npm run -s lint - npm run -s lint
- npm run -s test:snyk
- npm run -s test:unit - npm run -s test:unit
- <<: *e2e_tests - <<: *e2e_tests

View File

@ -1,5 +1,11 @@
# Contribute to Strapi # Contribute to Strapi
> ⚠️ We have stopped merging PRs for now to the Strapi core.<br><br>
> The reason is that we are developing new architecture for the admin panel and for the plugins.<br>
> This new architecture will provide stability of the Strapi core as we approach the release of Beta.<br>
> We appreciate and welcome all your contributions, but until further notice, please do not submit a PR as it will not be merged.<br>
> Furthermore, you will have to rewrite it based on the new architecture.
First off, thanks for taking the time to contribute! 🎉👍 First off, thanks for taking the time to contribute! 🎉👍
The following is a set of guidelines for contributing to Strapi and its packages. The following is a set of guidelines for contributing to Strapi and its packages.
@ -206,7 +212,7 @@ Then run
# generate the test app # generate the test app
$ node test/createTestApp.js $ node test/createTestApp.js
# run the testApp in the background (️️️⚠️ be carefull to kill the process after the tests have run) # run the testApp in the background (️️️⚠️ be carefull to kill the process after the tests have run)
$ node test/startTestApp.js & $ node test/startTestApp.js &
# run cypress # run cypress
$ node test/cypress $ node test/cypress
``` ```

View File

@ -121,6 +121,12 @@ Be aware that one of the content type builder won't work due to the writing file
## Contributing ## Contributing
> ⚠️ We have stopped merging PRs for now to the Strapi core.<br><br>
> The reason is that we are developing new architecture for the admin panel and for the plugins.<br>
> This new architecture will provide stability of the Strapi core as we approach the release of Beta.<br>
> We appreciate and welcome all your contributions, but until further notice, please do not submit a PR as it will not be merged.<br>
> Furthermore, you will have to rewrite it based on the new architecture.
Please read our [Contributing Guide](./CONTRIBUTING.md) before submitting a Pull Request to the project. Please read our [Contributing Guide](./CONTRIBUTING.md) before submitting a Pull Request to the project.
## Support ## Support
@ -133,7 +139,7 @@ For general help using Strapi, please refer to [the official Strapi documentatio
- [StackOverflow](http://stackoverflow.com/questions/tagged/strapi) - [StackOverflow](http://stackoverflow.com/questions/tagged/strapi)
- [Slack](http://slack.strapi.io) (highly recommended for faster support) - [Slack](http://slack.strapi.io) (highly recommended for faster support)
- [GitHub](https://github.com/strapi/strapi) - [GitHub](https://github.com/strapi/strapi) (Bug reports and Feature requests only)
- [Twitter](https://twitter.com/strapijs) - [Twitter](https://twitter.com/strapijs)
- [Facebook](https://www.facebook.com/Strapi-616063331867161). - [Facebook](https://www.facebook.com/Strapi-616063331867161).

View File

@ -15,6 +15,6 @@ You are invited to get started using Strapi. You may explore Strapi by:
1. A [Quick Start Guide](../getting-started/quick-start.html) for more intermediate to advanced developers. 1. A [Quick Start Guide](../getting-started/quick-start.html) for more intermediate to advanced developers.
2. A [Tutorial](../getting-started/quick-start-tutorial.html) for those who prefer a step-by-step introduction. 2. A [Tutorial](../getting-started/quick-start-tutorial.html) for those who prefer a step-by-step introduction.
When you're done getting started, we invite you to join our [community](../community.html). When you're done getting started, we invite you to join our [community](https://strapi.io/community).

View File

@ -15,7 +15,14 @@ import injectReducer from 'utils/injectReducer';
import OnboardingVideo from 'components/OnboardingVideo'; import OnboardingVideo from 'components/OnboardingVideo';
import { getVideos, onClick, removeVideos, setVideoDuration, setVideoEnd, updateVideoStartTime } from './actions'; import {
getVideos,
onClick,
removeVideos,
setVideoDuration,
setVideoEnd,
updateVideoStartTime,
} from './actions';
import makeSelectOnboarding from './selectors'; import makeSelectOnboarding from './selectors';
import reducer from './reducer'; import reducer from './reducer';
import saga from './saga'; import saga from './saga';
@ -43,17 +50,17 @@ export class Onboarding extends React.Component {
setVideoEnd = () => { setVideoEnd = () => {
this.setVideoEnd(); this.setVideoEnd();
} };
didPlayVideo = (index, currTime) => { didPlayVideo = (index, currTime) => {
const eventName = `didPlay${index}GetStartedVideo`; const eventName = `didPlay${index}GetStartedVideo`;
this.context.emitEvent(eventName, {timestamp: currTime}); this.context.emitEvent(eventName, { timestamp: currTime });
} };
didStopVideo = (index, currTime) => { didStopVideo = (index, currTime) => {
const eventName = `didStop${index}Video`; const eventName = `didStop${index}Video`;
this.context.emitEvent(eventName, {timestamp: currTime}); this.context.emitEvent(eventName, { timestamp: currTime });
} };
handleOpenModal = () => this.setState({ showVideos: true }); handleOpenModal = () => this.setState({ showVideos: true });
@ -67,10 +74,9 @@ export class Onboarding extends React.Component {
}; };
updateCurrentTime = (index, current, duration) => { updateCurrentTime = (index, current, duration) => {
this.props.updateVideoStartTime(index, current); this.props.updateVideoStartTime(index, current);
const percent = current * 100 / duration; const percent = (current * 100) / duration;
const video = this.props.videos[index]; const video = this.props.videos[index];
if (percent >= 80) { if (percent >= 80) {
@ -80,21 +86,35 @@ export class Onboarding extends React.Component {
} }
}; };
updateEnd = (index) => { updateEnd = index => {
this.props.setVideoEnd(index, true); this.props.setVideoEnd(index, true);
}; };
// eslint-disable-line jsx-handler-names // eslint-disable-line jsx-handler-names
render() { render() {
const { videos, onClick, setVideoDuration } = this.props; const { videos, onClick, setVideoDuration } = this.props;
const { showVideos } = this.state;
const style = showVideos ? {} : { maxWidth: 0 };
return ( return (
<div className={cn(styles.videosWrapper, videos.length > 0 ? styles.visible : styles.hidden)}> <div
<div className={cn(styles.videosContent, this.state.showVideos ? styles.shown : styles.hide)}> style={style}
className={cn(styles.videosWrapper, videos.length > 0 ? styles.visible : styles.hidden)}
>
<div
style={style}
className={cn(styles.videosContent, this.state.showVideos ? styles.shown : styles.hide)}
>
<div className={styles.videosHeader}> <div className={styles.videosHeader}>
<p><FormattedMessage id="app.components.Onboarding.title" /></p> <p>
<FormattedMessage id="app.components.Onboarding.title" />
</p>
{videos.length && ( {videos.length && (
<p>{Math.floor((videos.filter(v => v.end).length)*100/videos.length)}<FormattedMessage id="app.components.Onboarding.label.completed" /></p> <p>
{Math.floor((videos.filter(v => v.end).length * 100) / videos.length)}
<FormattedMessage id="app.components.Onboarding.label.completed" />
</p>
)} )}
</div> </div>
<ul className={styles.onboardingList}> <ul className={styles.onboardingList}>
@ -116,10 +136,7 @@ export class Onboarding extends React.Component {
</div> </div>
<div className={styles.openBtn}> <div className={styles.openBtn}>
<button <button onClick={this.handleVideosToggle} className={this.state.showVideos ? styles.active : ''}>
onClick={this.handleVideosToggle}
className={this.state.showVideos ? styles.active : ''}
>
<i className="fa fa-question" /> <i className="fa fa-question" />
<i className="fa fa-times" /> <i className="fa fa-times" />
<span /> <span />
@ -157,7 +174,10 @@ Onboarding.propTypes = {
const mapStateToProps = makeSelectOnboarding(); const mapStateToProps = makeSelectOnboarding();
function mapDispatchToProps(dispatch) { function mapDispatchToProps(dispatch) {
return bindActionCreators({ getVideos, onClick, setVideoDuration, updateVideoStartTime, setVideoEnd, removeVideos }, dispatch); return bindActionCreators(
{ getVideos, onClick, setVideoDuration, updateVideoStartTime, setVideoEnd, removeVideos },
dispatch,
);
} }
const withConnect = connect( const withConnect = connect(

View File

@ -113,4 +113,4 @@
width: 0; width: 0;
height: 0; height: 0;
} }
} }

View File

@ -1,5 +1,7 @@
'use strict'; 'use strict';
/* global <%= globalID %> */
/** /**
* <%= filename %> service * <%= filename %> service
* *

View File

@ -5,6 +5,6 @@
* Direct selector to the list state domain * Direct selector to the list state domain
*/ */
// const selectGlobalDomain = () => state => state.get(`${pluginId}-global`); // const selectGlobalDomain = () => state => state.get(`${pluginId}_global`);
export {}; export {};

View File

@ -4,16 +4,22 @@ import pluginId from 'pluginId';
/** /**
* Direct selector to the examplePage state domain * Direct selector to the examplePage state domain
*/ */
const selectExamplePageDomain = () => state => state.get(`${pluginId}-examplePage`); const selectExamplePageDomain = () => state => state.get(`${pluginId}_examplePage`);
/** /**
* Default selector used by HomePage * Default selector used by HomePage
*/ */
const makeSelectLoading = () => const makeSelectLoading = () =>
createSelector(selectExamplePageDomain(), substate => substate.get('loading')); createSelector(
selectExamplePageDomain(),
substate => substate.get('loading'),
);
const makeSelectData = () => const makeSelectData = () =>
createSelector(selectExamplePageDomain(), substate => substate.get('data')); createSelector(
selectExamplePageDomain(),
substate => substate.get('data'),
);
export { makeSelectLoading, makeSelectData }; export { makeSelectLoading, makeSelectData };

View File

@ -3,15 +3,16 @@ import pluginId from 'pluginId';
/** /**
* Direct selector to the homePage state domain * Direct selector to the homePage state domain
*/ */
const selectHomePageDomain = () => state => state.get(`${pluginId}-homePage`); const selectHomePageDomain = () => state => state.get(`${pluginId}_homePage`);
/** /**
* Default selector used by HomePage * Default selector used by HomePage
*/ */
const selectHomePage = () => createSelector( const selectHomePage = () =>
selectHomePageDomain(), createSelector(
(substate) => substate.toJS(), selectHomePageDomain(),
); substate => substate.toJS(),
);
export default selectHomePage; export default selectHomePage;

View File

@ -39,10 +39,22 @@ function InputSelect(props) {
> >
{map(props.selectOptions, (option, key) => { {map(props.selectOptions, (option, key) => {
if (isObject(option)) { if (isObject(option)) {
return <SelectOption key={key} {...option} />; if (option.label) {
return (
<option key={option.value} value={option.value}>
{option.label}
</option>
);
} else {
return <SelectOption key={key} {...option} />;
}
} }
return <option key={key} value={option}>{option}</option>; return (
<option key={key} value={option}>
{option}
</option>
);
})} })}
</select> </select>
); );

View File

@ -77,16 +77,16 @@ module.exports = {
try { try {
fs.writeFileSync(modelFilePath, JSON.stringify(modelJSON, null, 2), 'utf8'); fs.writeFileSync(modelFilePath, JSON.stringify(modelJSON, null, 2), 'utf8');
if (_.isEmpty(strapi.api)) { if (_.isEmpty(strapi.api)) {
strapi.emit('didCreateFirstContentType'); strapi.emit('didCreateFirstContentType');
} else { } else {
strapi.emit('didCreateContentType'); strapi.emit('didCreateContentType');
} }
ctx.send({ ok: true }); ctx.send({ ok: true });
strapi.reload(); setImmediate(() => strapi.reload());
} catch (e) { } catch (e) {
strapi.emit('didNotCreateContentType', e); strapi.emit('didNotCreateContentType', e);
return ctx.badRequest(null, [{ messages: [{ id: 'request.error.model.write' }] }]); return ctx.badRequest(null, [{ messages: [{ id: 'request.error.model.write' }] }]);

View File

@ -1,50 +0,0 @@
/**
*
* SelectOption
*
*/
import React from 'react';
import PropTypes from 'prop-types';
import getFlag, { formatLanguageLocale } from '../../utils/getFlag';
import styles from './styles.scss';
/* eslint-disable react/require-default-props */
class SelectOptionLanguage extends React.Component { // eslint-disable-line react/prefer-stateless-function
/* eslint-disable jsx-a11y/no-static-element-interactions */
handleSelect = (event) => {
event.preventDefault();
event.stopPropagation();
this.props.onSelect(this.props.option, event);
}
handleMouseEnter = (event) => {
this.props.onFocus(this.props.option, event);
}
handleMouseMove = (event) => {
if (this.props.isFocused) return;
this.props.onFocus(this.props.option, event);
}
render() {
const flagName = formatLanguageLocale(this.props.option.value);
const flag = getFlag(flagName);
return (
<div className={styles.selectOption} onMouseEnter={this.handleMouseEnter} onMouseMove={this.handleMouseMove} onFocus={this.props.onFocus} onClick={this.handleSelect} id={this.props.option.value}>
<span className={`${styles.flagContainer} flag-icon flag-icon-${flag}`} />
<span className={styles.optionLabel}>{this.props.option.label}</span>
</div>
);
}
}
SelectOptionLanguage.propTypes = {
isFocused: PropTypes.bool,
onFocus: PropTypes.func,
onSelect: PropTypes.func,
option: PropTypes.object,
};
export default SelectOptionLanguage;

View File

@ -1,15 +0,0 @@
.selectOption { /* stylelint-disable */
margin: 1rem;
background-image: url('assets/images/unknow_flag.png');
background-size: 1.3333em auto;
background-position: left center;
}
.flagContainer {
// margin-left: 1rem;
}
.optionLabel {
margin-left: 1rem;
text-transform: capitalize;
}

View File

@ -11,7 +11,6 @@ import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect'; import { createStructuredSelector } from 'reselect';
import { bindActionCreators, compose } from 'redux'; import { bindActionCreators, compose } from 'redux';
import 'flag-icon-css/css/flag-icon.css'; import 'flag-icon-css/css/flag-icon.css';
import 'react-select/dist/react-select.css';
import { Switch, Route } from 'react-router-dom'; import { Switch, Route } from 'react-router-dom';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
@ -83,7 +82,7 @@ export function mapDispatchToProps(dispatch) {
menuFetch, menuFetch,
environmentsFetch, environmentsFetch,
}, },
dispatch dispatch,
); );
} }
@ -93,7 +92,10 @@ const mapStateToProps = createStructuredSelector({
}); });
// Wrap the component to inject dispatch and state into it // Wrap the component to inject dispatch and state into it
const withConnect = connect(mapStateToProps, mapDispatchToProps); const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
const withReducer = strapi.injectReducer({ key: 'global', reducer, pluginId }); const withReducer = strapi.injectReducer({ key: 'global', reducer, pluginId });
const withSaga = strapi.injectSaga({ key: 'global', saga, pluginId }); const withSaga = strapi.injectSaga({ key: 'global', saga, pluginId });

View File

@ -26,9 +26,10 @@ import {
} from 'lodash'; } from 'lodash';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import Helmet from 'react-helmet'; import Helmet from 'react-helmet';
import Select from 'react-select';
import { router } from 'app'; import { router } from 'app';
import InputSelect from 'strapi-helper-plugin/lib/src/components/InputSelect';
import pluginId from '../../pluginId'; import pluginId from '../../pluginId';
// design // design
import ContentHeader from '../../components/ContentHeader'; import ContentHeader from '../../components/ContentHeader';
@ -36,15 +37,11 @@ import EditForm from '../../components/EditForm';
import HeaderNav from '../../components/HeaderNav'; import HeaderNav from '../../components/HeaderNav';
import List from '../../components/List'; import List from '../../components/List';
import RowDatabase from '../../components/RowDatabase'; import RowDatabase from '../../components/RowDatabase';
import SelectOptionLanguage from '../../components/SelectOptionLanguage';
import RowLanguage from '../../components/RowLanguage'; import RowLanguage from '../../components/RowLanguage';
import PluginLeftMenu from '../../components/PluginLeftMenu'; import PluginLeftMenu from '../../components/PluginLeftMenu';
// utils
import unknowFlag from 'assets/images/unknow_flag.png';
import supportedFlags from 'utils/supportedFlags.json';
import { checkFormValidity, getRequiredInputsDb } from '../../utils/inputValidations'; import { checkFormValidity, getRequiredInputsDb } from '../../utils/inputValidations';
import getFlag, { formatLanguageLocale } from '../../utils/getFlag'; import { formatLanguageLocale } from '../../utils/getFlag';
import sendUpdatedParams from '../../utils/sendUpdatedParams'; import sendUpdatedParams from '../../utils/sendUpdatedParams';
// App selectors // App selectors
import { makeSelectSections, makeSelectEnvironments } from '../App/selectors'; import { makeSelectSections, makeSelectEnvironments } from '../App/selectors';
@ -74,7 +71,8 @@ import styles from './styles.scss';
import config from './config.json'; import config from './config.json';
/* eslint-disable react/require-default-props */ /* eslint-disable react/require-default-props */
export class HomePage extends React.Component { // eslint-disable-line react/prefer-stateless-function export class HomePage extends React.Component {
// eslint-disable-line react/prefer-stateless-function
constructor(props) { constructor(props) {
super(props); super(props);
this.customComponents = config.customComponents; this.customComponents = config.customComponents;
@ -98,7 +96,10 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
if (this.props.match.params.slug) { if (this.props.match.params.slug) {
this.handleFetch(this.props); this.handleFetch(this.props);
} else { } else {
router.push(`/plugins/settings-manager/${get(this.props.menuSections, ['0', 'items', '0', 'slug']) || 'application'}`); router.push(
`/plugins/settings-manager/${get(this.props.menuSections, ['0', 'items', '0', 'slug']) ||
'application'}`,
);
} }
} }
@ -112,7 +113,11 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
// redirect user if no params slug provided // redirect user if no params slug provided
router.push(`/plugins/settings-manager/${get(this.props.menuSections, ['0', 'items', '0', 'slug'])}`); router.push(`/plugins/settings-manager/${get(this.props.menuSections, ['0', 'items', '0', 'slug'])}`);
} }
} else if (this.props.match.params.env !== nextProps.match.params.env && nextProps.match.params.env && this.props.match.params.env) { } else if (
this.props.match.params.env !== nextProps.match.params.env &&
nextProps.match.params.env &&
this.props.match.params.env
) {
// get data if params env updated // get data if params env updated
this.handleFetch(nextProps); this.handleFetch(nextProps);
} }
@ -130,7 +135,7 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
/* eslint-disable react/sort-comp */ /* eslint-disable react/sort-comp */
/* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/no-static-element-interactions */
addConnection = (e) => { addConnection = e => {
e.preventDefault(); e.preventDefault();
const newData = {}; const newData = {};
/* eslint-disable no-template-curly-in-string */ /* eslint-disable no-template-curly-in-string */
@ -151,18 +156,18 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
} else { } else {
this.props.setErrors(formErrors); this.props.setErrors(formErrors);
} }
} };
emptyDbModifiedData = () => { emptyDbModifiedData = () => {
this.setState({ toggleDefaultConnection: false }); this.setState({ toggleDefaultConnection: false });
this.props.emptyDbModifiedData(); this.props.emptyDbModifiedData();
} };
getDatabase = (databaseName) => { getDatabase = databaseName => {
// allow state here just for modal purpose // allow state here just for modal purpose
this.props.specificDatabaseFetch(databaseName, this.props.match.params.env); this.props.specificDatabaseFetch(databaseName, this.props.match.params.env);
// this.setState({ modal: !this.state.modal }); // this.setState({ modal: !this.state.modal });
} };
handleDefaultLanguageChange = ({ target }) => { handleDefaultLanguageChange = ({ target }) => {
// create new object configsDisplay based on store property configsDisplay // create new object configsDisplay based on store property configsDisplay
@ -192,13 +197,19 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
const defaultLanguageArray = formatLanguageLocale(target.id); const defaultLanguageArray = formatLanguageLocale(target.id);
// Edit the new config // Edit the new config
this.props.editSettings({ 'language.defaultLocale': join(defaultLanguageArray, '_') }, 'i18n', this.context); this.props.editSettings(
} { 'language.defaultLocale': join(defaultLanguageArray, '_') },
'i18n',
this.context,
);
};
handleFetch(props) { handleFetch(props) {
const apiUrl = props.match.params.env ? `${props.match.params.slug}/${props.match.params.env}` : props.match.params.slug; const apiUrl = props.match.params.env
? `${props.match.params.slug}/${props.match.params.env}`
: props.match.params.slug;
switch(props.match.params.slug) { switch (props.match.params.slug) {
case 'languages': case 'languages':
return this.props.languagesFetch(); return this.props.languagesFetch();
case 'databases': case 'databases':
@ -222,21 +233,30 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
if (this.props.match.params.slug === 'databases') { if (this.props.match.params.slug === 'databases') {
if (name === this.props.home.dbNameTarget) { if (name === this.props.home.dbNameTarget) {
const formErrors = value === this.props.home.addDatabaseSection.sections[1].items[0].value ? const formErrors =
[{ target: name, errors: [{ id: 'settings-manager.request.error.database.exist' }] }] : []; value === this.props.home.addDatabaseSection.sections[1].items[0].value
? [{ target: name, errors: [{ id: 'settings-manager.request.error.database.exist' }] }]
: [];
this.props.setErrors(formErrors); this.props.setErrors(formErrors);
} else if (endsWith(name, '.settings.client')) { } else if (endsWith(name, '.settings.client')) {
const item = find(this.props.home.addDatabaseSection.sections[0].items[1].items, { value }); const item = find(this.props.home.addDatabaseSection.sections[0].items[1].items, { value });
this.props.changeInput('database.connections.${name}.settings.port', item.port); this.props.changeInput('database.connections.${name}.settings.port', item.port);
this.props.changeInput(`database.connections.${this.props.home.addDatabaseSection.sections[1].items[0].value}.settings.port`, item.port); this.props.changeInput(
`database.connections.${
this.props.home.addDatabaseSection.sections[1].items[0].value
}.settings.port`,
item.port,
);
} else { } else {
this.props.setErrors([]); this.props.setErrors([]);
} }
} }
this.props.changeInput(name, value); this.props.changeInput(name, value);
} };
handleChangeLanguage = (value) => this.props.changeInput('language.defaultLocale', value.value); handleChangeLanguage = ({ target: { value } }) => {
this.props.changeInput('language.defaultLocale', value);
};
handleCancel = () => this.props.cancelChanges(); handleCancel = () => this.props.cancelChanges();
@ -245,28 +265,33 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
? this.props.home.addDatabaseSection.sections[1].items[0].value ? this.props.home.addDatabaseSection.sections[1].items[0].value
: this.props.home.modifiedData[this.props.home.dbNameTarget]; : this.props.home.modifiedData[this.props.home.dbNameTarget];
const target = { name: 'database.defaultConnection', value }; const target = { name: 'database.defaultConnection', value };
this.handleChange({target}); this.handleChange({ target });
this.setState({ toggleDefaultConnection: !this.state.toggleDefaultConnection }); this.setState({ toggleDefaultConnection: !this.state.toggleDefaultConnection });
} };
handleSubmit = (e) => { // eslint-disable-line consistent-return handleSubmit = e => {
// eslint-disable-line consistent-return
e.preventDefault(); e.preventDefault();
const apiUrl = this.props.match.params.env ? `${this.props.match.params.slug}/${this.props.match.params.env}` : this.props.match.params.slug; const apiUrl = this.props.match.params.env
? `${this.props.match.params.slug}/${this.props.match.params.env}`
: this.props.match.params.slug;
const isCreatingNewFields = this.props.match.params.slug === 'security'; const isCreatingNewFields = this.props.match.params.slug === 'security';
// send only updated settings // send only updated settings
const body = this.sendUpdatedParams(isCreatingNewFields); const body = this.sendUpdatedParams(isCreatingNewFields);
const formErrors = checkFormValidity(body, this.props.home.formValidations); const formErrors = checkFormValidity(body, this.props.home.formValidations);
if (isEmpty(body)) return strapi.notification.info('settings-manager.strapi.notification.info.settingsEqual'); if (isEmpty(body))
return strapi.notification.info('settings-manager.strapi.notification.info.settingsEqual');
if (isEmpty(formErrors)) { if (isEmpty(formErrors)) {
this.props.editSettings(body, apiUrl, this.context); this.props.editSettings(body, apiUrl, this.context);
} else { } else {
this.props.setErrors(formErrors); this.props.setErrors(formErrors);
} }
} };
handleSubmitEditDatabase = (databaseName) => { // eslint-disable-line consistent-return handleSubmitEditDatabase = databaseName => {
// eslint-disable-line consistent-return
const body = this.sendUpdatedParams(); const body = this.sendUpdatedParams();
const apiUrl = `${databaseName}/${this.props.match.params.env}`; const apiUrl = `${databaseName}/${this.props.match.params.env}`;
const formErrors = checkFormValidity(body, this.props.home.formValidations, this.props.home.formErrors); const formErrors = checkFormValidity(body, this.props.home.formValidations, this.props.home.formErrors);
@ -276,25 +301,21 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
return strapi.notification.info('settings-manager.strapi.notification.info.settingsEqual'); return strapi.notification.info('settings-manager.strapi.notification.info.settingsEqual');
} }
if (isEmpty(formErrors)) { if (isEmpty(formErrors)) {
this.props.databaseEdit(body, apiUrl, this.context); this.props.databaseEdit(body, apiUrl, this.context);
} else { } else {
this.props.setErrors(formErrors); this.props.setErrors(formErrors);
} }
} };
// retrieve the language to delete using the target id // retrieve the language to delete using the target id
handleLanguageDelete = (languaToDelete) => this.props.languageDelete(languaToDelete); handleLanguageDelete = languaToDelete => this.props.languageDelete(languaToDelete);
handleDatabaseDelete = (dbName) => { handleDatabaseDelete = dbName => {
this.context.enableGlobalOverlayBlocker(); this.context.enableGlobalOverlayBlocker();
strapi.notification.success('settings-manager.strapi.notification.success.databaseDelete'); strapi.notification.success('settings-manager.strapi.notification.success.databaseDelete');
this.props.databaseDelete(dbName, this.props.match.params.env, this.context); this.props.databaseDelete(dbName, this.props.match.params.env, this.context);
} };
// function used for react-select option
optionComponent = (props) => <SelectOptionLanguage {...props} />;
// custom Row rendering for the component List with params slug === languages // custom Row rendering for the component List with params slug === languages
renderRowLanguage = (props, key, liStyles) => ( renderRowLanguage = (props, key, liStyles) => (
@ -306,22 +327,36 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
listLanguages={this.props.home.listLanguages} listLanguages={this.props.home.listLanguages}
onDefaultLanguageChange={this.handleDefaultLanguageChange} onDefaultLanguageChange={this.handleDefaultLanguageChange}
/> />
) );
renderListTitle = () => { renderListTitle = () => {
const availableContentNumber = size(this.props.home.configsDisplay.sections); const availableContentNumber = size(this.props.home.configsDisplay.sections);
const title = availableContentNumber > 1 ? `list.${this.props.match.params.slug}.title.plural` : `list.${this.props.match.params.slug}.title.singular`; const title =
availableContentNumber > 1
? `list.${this.props.match.params.slug}.title.plural`
: `list.${this.props.match.params.slug}.title.singular`;
const titleDisplay = title ? <FormattedMessage id={`settings-manager.${title}`} /> : ''; const titleDisplay = title ? <FormattedMessage id={`settings-manager.${title}`} /> : '';
return <span>{availableContentNumber}&nbsp;{titleDisplay}</span>; return (
} <span>
{availableContentNumber}&nbsp;{titleDisplay}
</span>
);
};
renderListButtonLabel = () => `list.${this.props.match.params.slug}.button.label`; renderListButtonLabel = () => `list.${this.props.match.params.slug}.button.label`;
renderPopUpFormDatabase = (section, props, popUpStyles) => ( renderPopUpFormDatabase = (section, props, popUpStyles) =>
map(section.items, (item, key) => { map(section.items, (item, key) => {
const isActive = props.values[this.props.home.dbNameTarget] === this.props.home.modifiedData['database.defaultConnection'] ? const isActive =
<div className={popUpStyles.rounded}><i className="fa fa-check" /></div> : ''; props.values[this.props.home.dbNameTarget] ===
this.props.home.modifiedData['database.defaultConnection'] ? (
<div className={popUpStyles.rounded}>
<i className="fa fa-check" />
</div>
) : (
''
);
if (item.name === 'form.database.item.default') { if (item.name === 'form.database.item.default') {
return ( return (
@ -331,39 +366,35 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
id={item.target} id={item.target}
onClick={this.handleSetDefaultConnectionDb} onClick={this.handleSetDefaultConnectionDb}
> >
<FormattedMessage id={`settings-manager.${item.name}`} />{isActive} <FormattedMessage id={`settings-manager.${item.name}`} />
{isActive}
</div> </div>
); );
} }
return ( return props.renderInput(item, key);
props.renderInput(item, key) });
);
})
)
renderPopUpFormLanguage = (section) => ( renderPopUpFormLanguage = section =>
map(section.items, (item) => { map(section.items, item => {
const value = this.props.home.modifiedData[item.target] || this.props.home.selectOptions.options[0].value; const value =
this.props.home.modifiedData[item.target] || this.props.home.selectOptions.options[0].value;
return ( return (
<div className={`col-md-6`} key={item.name}> <div className={`col-md-6`} key={item.name}>
<div className={styles.modalLanguageLabel}> <div className={styles.modalLanguageLabel}>
<FormattedMessage id={`settings-manager.${item.name}`} /> <FormattedMessage id={`settings-manager.${item.name}`} />
</div> </div>
<Select <InputSelect
name={item.target} name={item.target}
value={value} value={value}
options={this.props.home.selectOptions.options} selectOptions={this.props.home.selectOptions.options}
onChange={this.handleChangeLanguage} onChange={this.handleChangeLanguage}
valueComponent={this.valueComponent} validations={{}}
optionComponent={this.optionComponent}
clearable={false}
/> />
<div className={styles.popUpSpacer} /> <div className={styles.popUpSpacer} />
</div> </div>
); );
}) });
)
renderRowDatabase = (props, key) => ( renderRowDatabase = (props, key) => (
<RowDatabase <RowDatabase
@ -380,22 +411,30 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
error={this.props.home.error} error={this.props.home.error}
resetToggleDefaultConnection={this.resetToggleDefaultConnection} resetToggleDefaultConnection={this.resetToggleDefaultConnection}
/> />
) );
renderComponent = () => { renderComponent = () => {
// check if settingName (params.slug) has a custom view display // check if settingName (params.slug) has a custom view display
let specificComponent = findKey(this.customComponents, (value) => includes(value, this.props.match.params.slug)); let specificComponent = findKey(this.customComponents, value =>
includes(value, this.props.match.params.slug),
);
if (!specificComponent) { if (!specificComponent) {
// Check if params env : render HeaderNav component // Check if params env : render HeaderNav component
specificComponent = !this.props.match.params.env ? 'defaultComponent' : 'defaultComponentWithEnvironments'; specificComponent = !this.props.match.params.env
? 'defaultComponent'
: 'defaultComponentWithEnvironments';
} }
// if custom view display render specificComponent // if custom view display render specificComponent
const Component = this.components[specificComponent]; const Component = this.components[specificComponent];
const addRequiredInputDesign = this.props.match.params.slug === 'databases'; const addRequiredInputDesign = this.props.match.params.slug === 'databases';
const listTitle = ['languages', 'databases'].includes(this.props.match.params.slug) ? this.renderListTitle() : ''; const listTitle = ['languages', 'databases'].includes(this.props.match.params.slug)
const listButtonLabel = ['languages', 'databases'].includes(this.props.match.params.slug) ? this.renderListButtonLabel() : ''; ? this.renderListTitle()
: '';
const listButtonLabel = ['languages', 'databases'].includes(this.props.match.params.slug)
? this.renderListButtonLabel()
: '';
// check if HeaderNav component needs to render a form or a list // check if HeaderNav component needs to render a form or a list
const renderListComponent = this.props.match.params.slug === 'databases'; const renderListComponent = this.props.match.params.slug === 'databases';
@ -459,7 +498,7 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
showLoader={this.props.home.showLoader} showLoader={this.props.home.showLoader}
/> />
); );
} };
// Set the toggleDefaultConnection to false // Set the toggleDefaultConnection to false
resetToggleDefaultConnection = () => this.setState({ toggleDefaultConnection: false }); resetToggleDefaultConnection = () => this.setState({ toggleDefaultConnection: false });
@ -467,30 +506,19 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
// Hide database modal // Hide database modal
toggle = () => this.setState({ modal: !this.state.modal }); toggle = () => this.setState({ modal: !this.state.modal });
// function used for react-select
valueComponent = (props) => {
const flagName = formatLanguageLocale(props.value.value);
const flag = getFlag(flagName);
const spanStyle = includes(supportedFlags.flags, flag) ? {} : { backgroundImage: `url(${unknowFlag})` };
return (
<span className={`${styles.flagContainer} flag-icon-background flag-icon-${flag}`} style={spanStyle}>
<FormattedMessage id="settings-manager.selectValue" defaultMessage='{language}' values={{ language: props.value.label}} className={styles.marginLeft} />
</span>
);
}
render() { render() {
return ( return (
<div className="container-fluid"> <div className="container-fluid">
<div className="row"> <div className="row">
<PluginLeftMenu sections={this.props.menuSections} environments={this.props.environments} envParams={this.props.match.params.env} /> <PluginLeftMenu
sections={this.props.menuSections}
environments={this.props.environments}
envParams={this.props.match.params.env}
/>
<div className={`${styles.home} col-md-9`}> <div className={`${styles.home} col-md-9`}>
<Helmet <Helmet
title="Settings Manager" title="Settings Manager"
meta={[ meta={[{ name: 'Settings Manager Plugin', content: 'Modify your app settings' }]}
{ name: 'Settings Manager Plugin', content: 'Modify your app settings' },
]}
/> />
<ContentHeader <ContentHeader
name={this.props.home.configsDisplay.name} name={this.props.home.configsDisplay.name}
@ -531,7 +559,7 @@ function mapDispatchToProps(dispatch) {
setErrors, setErrors,
specificDatabaseFetch, specificDatabaseFetch,
}, },
dispatch dispatch,
); );
} }
@ -563,7 +591,10 @@ HomePage.propTypes = {
specificDatabaseFetch: PropTypes.func.isRequired, specificDatabaseFetch: PropTypes.func.isRequired,
}; };
const withConnect = connect(mapStateToProps, mapDispatchToProps); const withConnect = connect(
mapStateToProps,
mapDispatchToProps,
);
const withReducer = strapi.injectReducer({ key: 'homePage', reducer, pluginId }); const withReducer = strapi.injectReducer({ key: 'homePage', reducer, pluginId });
const withSaga = strapi.injectSaga({ key: 'homePage', saga, pluginId }); const withSaga = strapi.injectSaga({ key: 'homePage', saga, pluginId });

View File

@ -25,7 +25,6 @@
"devDependencies": { "devDependencies": {
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"flag-icon-css": "^2.8.0", "flag-icon-css": "^2.8.0",
"react-select": "^1.0.0-rc.5",
"rimraf": "^2.6.3", "rimraf": "^2.6.3",
"strapi-helper-plugin": "3.0.0-alpha.25.2" "strapi-helper-plugin": "3.0.0-alpha.25.2"
}, },