Merge branch 'alpha.6' of https://github.com/strapi/strapi into improvements/filters

This commit is contained in:
Jim Laurie 2017-09-29 13:11:14 +02:00
commit c97893a700
23 changed files with 392 additions and 37 deletions

View File

@ -1,11 +1,23 @@
![Logo](https://cldup.com/7umchwdUBh.png)
<p align="center"><img src="https://cldup.com/7umchwdUBh.png" /></p>
<h3 align="center">API creation made simple, secure and fast.</h3>
<p align="center">The most advanced open-source Content Management Framework to build powerful API with no effort.</p>
<br />
<p align="center">
<a href="https://www.npmjs.org/package/strapi">
<img src="https://img.shields.io/npm/v/strapi.svg" alt="Dependency Status" />
</a>
<a href="https://www.npmjs.org/package/strapi">
<img src="https://img.shields.io/npm/dm/strapi.svg" alt="Dependency Status" />
</a>
<a href="https://travis-ci.org/strapi/strapi">
<img src="https://travis-ci.org/strapi/strapi.svg?branch=master" alt="Dependency Status" />
</a>
<a href="http://slack.strapi.io">
<img src="http://strapi-slack.herokuapp.com/badge.svg" alt="Dependency Status" />
</a>
</p>
[![npm version](https://img.shields.io/npm/v/strapi.svg)](https://www.npmjs.org/package/strapi)
[![npm downloads](https://img.shields.io/npm/dm/strapi.svg)](https://www.npmjs.org/package/strapi)
[![Build status](https://travis-ci.org/strapi/strapi.svg?branch=master)](https://travis-ci.org/strapi/strapi)
[![Slack status](http://strapi-slack.herokuapp.com/badge.svg)](http://slack.strapi.io)
[Strapi](http://strapi.io) is an open source solution to create, deploy and manage your own API. It provides a powerful dashboard and features to make your life easier.
<br>
## v3.0.0 coming soon...
We've been working on a major update to Strapi for several months now, rewriting the core framework and the dashboard. Some parts of this work have been merged into our master branch. Currently, this is not stable and ready.

View File

@ -83,13 +83,31 @@ const registerPlugin = (plugin) => {
plugin.leftMenuSections = plugin.leftMenuSections || [];
// Execute bootstrap function.
if (isFunction(plugin.bootstrap)) {
plugin.bootstrap(plugin).then(plugin => {
switch (true) {
// Execute bootstrap function and check if plugin can be rendered
case isFunction(plugin.bootstrap) && isFunction(plugin.pluginRequirements):
plugin.pluginRequirements(plugin)
.then(plugin => {
return plugin.bootstrap(plugin);
})
.then(plugin => {
store.dispatch(pluginLoaded(plugin));
});
break;
// Check if plugin can be rendered
case isFunction(plugin.pluginRequirements):
plugin.pluginRequirements(plugin).then(plugin => {
store.dispatch(pluginLoaded(plugin));
})
break;
// Execute bootstrap function
case isFunction(plugin.bootstrap):
plugin.bootstrap(plugin).then(plugin => {
store.dispatch(pluginLoaded(plugin));
});
break;
default:
store.dispatch(pluginLoaded(plugin));
});
} else {
store.dispatch(pluginLoaded(plugin));
}
};

View File

@ -25,7 +25,8 @@ export class PluginPage extends React.Component { // eslint-disable-line react/p
if (plugin.id === pluginId) {
pluginName = plugin.name;
const Elem = plugin.mainComponent;
const Elem = plugin.preventComponentRendering ? plugin.blockerComponent : plugin.mainComponent;
return <Elem key={plugin.id} {...this.props} />;
}
});

View File

@ -10,5 +10,10 @@
"app.components.LeftMenuLinkContainer.noPluginsInstalled": "No plugins installed yet",
"app.components.LeftMenuLinkContainer.plugins": "Plugins",
"app.components.NotFoundPage.description": "Not Found",
"app.components.NotFoundPage.back": "Back to homepage"
"app.components.NotFoundPage.back": "Back to homepage",
"components.AutoReloadBlocker.header": "Reload feature is required for this plugin.",
"components.AutoReloadBlocker.description": "Open the following file and enable the feature.",
"components.ProductionBlocker.header": "This plugin is only available in development.",
"components.ProductionBlocker.description": "For safety we have to disable this plugin in other environments."
}

View File

@ -10,5 +10,9 @@
"app.components.LeftMenuLinkContainer.noPluginsInstalled": "Aucun plugin installé",
"app.components.LeftMenuLinkContainer.plugins": "Plugins",
"app.components.NotFoundPage.description": "Page introuvable",
"app.components.NotFoundPage.back": "Retourner à la page d'accueil"
"app.components.NotFoundPage.back": "Retourner à la page d'accueil",
"components.AutoReloadBlocker.header": "L'autoReload doit être activé pour ce plugin.",
"components.AutoReloadBlocker.description": "Ouvrez le fichier suivant pour activer cette fonctionnalité.",
"components.ProductionBlocker.header": "Ce plugin est disponible uniquement en développement.",
"components.ProductionBlocker.description": "Pour des raisons de sécurité il est désactivé dans les autres environnements."
}

View File

@ -1,6 +1,3 @@
{
"languages": ["en", "fr"],
"plugins": {
"ports": [3000]
}
"languages": ["en", "fr"]
}

View File

@ -44,7 +44,7 @@
"html-loader": "^0.4.3",
"html-webpack-plugin": "^2.22.0",
"plop": "^1.8.1",
"prettier": "^1.7.0",
"prettier": "^1.7.2",
"rimraf": "^2.6.2",
"strapi-helper-plugin": "3.0.0-alpha.5.5",
"webpack": "^3.6.0"
@ -66,4 +66,4 @@
"npm": ">= 3.0.0"
},
"license": "MIT"
}
}

View File

@ -8,11 +8,24 @@
import React from 'react';
import { Provider } from 'react-redux';
import App, { bootstrap } from 'containers/App'; // eslint-disable-line
import App from 'containers/App'; // eslint-disable-line
import configureStore from './store';
import { translationMessages } from './i18n';
const tryRequire = (bootstrap = false) => {
try {
const config = bootstrap ? require('bootstrap').default : require('requirements').default;
return config;
} catch(err) {
return null;
}
};
const bootstrap = tryRequire(true);
const pluginRequirements = tryRequire();
// Plugin identifier based on the package.json `name` value
const pluginPkg = require('../../../../package.json');
const pluginId = pluginPkg.name.replace(
@ -61,6 +74,9 @@ window.Strapi.registerPlugin({
mainComponent: Comp,
translationMessages,
bootstrap,
pluginRequirements,
preventComponentRendering: false,
blockerComponent: null,
});
// Export store

View File

@ -0,0 +1,61 @@
/**
*
* AutoReloadBlocker
*
*/
import React from 'react';
import { FormattedMessage } from 'react-intl';
import styles from './styles.scss';
function AutoReloadBlocker() {
return (
<div className={styles.autoReloadBlocker}>
<div className={styles.header}>
<div>
<h4>
<FormattedMessage id="components.AutoReloadBlocker.header" />
</h4>
<p>
<FormattedMessage id="components.AutoReloadBlocker.description" />
</p>
<div className={styles.ide}>
<p>./config/environments/development/server.json</p>
<div>
<pre style={{ whiteSpace: 'pre-wrap'}}>
<code>
&#123;
<br />
&nbsp;"host": "localhost",
<br />
&nbsp;"port": 1337,
<br />
<span style={{ color: '#006EE7'}}>
&nbsp;"autoReload": true,
</span>
<br />
&nbsp;"proxi": &#123;
<br />
&nbsp;&nbsp;"enabled": true
<br />
&nbsp;&#125;,
<br />
&nbsp;"cron": &#123;
<br />
&nbsp;&nbsp;"enabled": false
<br />
&nbsp;&#125;
<br />
&#125;
</code>
</pre>
</div>
</div>
</div>
</div>
</div>
);
}
export default AutoReloadBlocker;

View File

@ -0,0 +1,53 @@
.autoReloadBlocker {
padding-top: 5.5rem;
}
.header {
display: flex;
justify-content: center;
font-family: Lato;
> div {
padding-top: 2.5rem;
> h4 {
font-size: 24px;
font-weight: 700;
line-height: 24px;
margin-bottom: 0;
}
> p {
margin-top: -1px;
font-size: 14px;
color: #919BAE;
}
}
&:before{
content: '\f021';
font-family: 'FontAwesome';
font-size: 4.2rem;
color: #323740;
margin-right: 20px;
line-height: 9.3rem;
}
}
.ide {
padding-top: 2.3rem;
> p {
color: #006EE7;
font-size: 14px;
}
> div {
width: 455px;
background: #FFFFFF;
color: #787E8F;
font-size: 12px;
> ul {
padding-left: 20px;
list-style: none;
> li {
list-style: none;
}
}
}
}

View File

@ -0,0 +1,32 @@
/**
*
* ProductionBlocker
*
*/
import React from 'react';
import cn from 'classnames';
import { FormattedMessage } from 'react-intl';
import styles from './styles.scss';
function ProductionBlocker() {
return (
<div className={styles.productionBlocker}>
<div className={styles.header}>
<div>
<h4>
<FormattedMessage id="components.ProductionBlocker.header" />
</h4>
<p>
<FormattedMessage id="components.ProductionBlocker.description" />
</p>
<div className={styles.buttonContainer}>
<a className={cn(styles.primary, 'btn')} href="http://strapi.io" target="_blank">Read the documentation</a>
</div>
</div>
</div>
</div>
);
}
export default ProductionBlocker;

View File

@ -0,0 +1,84 @@
.productionBlocker {
padding-top: 5.5rem;
}
.header {
display: flex;
justify-content: center;
font-family: Lato;
> div {
padding-top: 2.5rem;
> h4 {
font-size: 24px;
font-weight: 700;
line-height: 24px;
margin-bottom: 0;
}
> p {
margin-top: -1px;
font-size: 14px;
color: #919BAE;
}
}
&:before{
content: '\f05e';
font-family: 'FontAwesome';
font-size: 4.2rem;
color: #323740;
margin-right: 20px;
line-height: 9.3rem;
}
}
.buttonContainer {
padding-top: 2rem;
}
.primary {
border-radius: 0.3rem;
border: none;
font-weight: 500;
min-width: 15rem;
background: linear-gradient(315deg, #0097F6 0%, #005EEA 100%);
-webkit-font-smoothing: antialiased;
color: white;
&:active {
box-shadow: inset 1px 1px 3px rgba(0,0,0,.15);
}
padding-top: 4px;
padding-left: 1.6rem;
padding-right: 1.6rem;
&:before {
content: '\f02d';
font-family: 'FontAwesome';
font-weight: 600;
font-size: 1.3rem;
margin-right: 13px;
}
cursor: pointer;
font-family: Lato;
-webkit-font-smoothing: antialiased;
&:focus {
outline: 0;
}
> i {
margin-right: 1.3rem;
font-weight: 600;
padding-top: 1px;
}
&:hover {
color: white;
&::after {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
border-radius: 0.3rem;
content: '';
opacity: 0.1;
background: #FFFFFF;
}
}
}

View File

@ -0,0 +1,14 @@
import { generateMenu } from 'containers/App/sagas';
// This method is executed before the load of the plugin
const bootstrap = (plugin) => new Promise((resolve, reject) => {
generateMenu()
.then(menu => {
plugin.leftMenuSections = menu;
resolve(plugin);
})
.catch(e => reject(e));
});
export default bootstrap;

View File

@ -22,7 +22,7 @@ import List from 'containers/List';
import { loadModels, updateSchema } from './actions';
import { makeSelectLoading } from './selectors';
import saga, { generateMenu } from './sagas';
import saga from './sagas';
const tryRequire = (path) => {
try {
@ -32,17 +32,6 @@ const tryRequire = (path) => {
}
};
// This method is executed before the load of the plugin.
export const bootstrap = (plugin) => new Promise((resolve, reject) => {
generateMenu()
.then(menu => {
plugin.leftMenuSections = menu;
resolve(plugin);
})
.catch(e => reject(e));
});
class App extends React.Component {
componentDidMount() {
const config = tryRequire('../../../../config/admin.json');

View File

@ -0,0 +1,3 @@
const shouldRenderCompo = (plugin) => Promise.resolve(plugin);
export default shouldRenderCompo;

View File

@ -23,7 +23,7 @@ class ContentHeader extends React.Component { // eslint-disable-line react/prefe
if (this.props.isLoading) {
return (
<div className={styles.buttonContainer}>
<Button type="submit" lg primary loader />
<Button type="submit" primary loader />
</div>
);
}

View File

@ -0,0 +1,16 @@
import AutoReloadBlocker from 'components/AutoReloadBlocker';
import request from 'utils/request';
const shouldRenderCompo = (plugin) => new Promise((resolve, reject) => {
request('/content-type-builder/autoReload')
.then(response => {
plugin.preventComponentRendering = !response.autoReload;
plugin.blockerComponent = AutoReloadBlocker;
return resolve(plugin);
})
.catch(err => reject(err));
});
export default shouldRenderCompo;

View File

@ -55,6 +55,14 @@
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/autoReload",
"handler": "ContentTypeBuilder.autoReload",
"config": {
"policies": []
}
}
]
}

View File

@ -191,5 +191,11 @@ module.exports = {
} catch (err) {
ctx.body = ctx.notFound();
}
},
autoReload: async ctx => {
ctx.send({
autoReload: _.get(strapi.config.environments, 'development.server.autoReload', false),
});
}
};

View File

@ -322,7 +322,7 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
renderPopUpFormLanguage = (section) => (
map(section.items, (item) => {
const value = this.props.home.modifiedData[item.target] || this.props.home.selectOptions.options[0].value;
return (
<div className={`col-md-6`}>
<div className={styles.modalLanguageLabel}>

View File

@ -0,0 +1,21 @@
import AutoReloadBlocker from 'components/AutoReloadBlocker';
import ProductionBlocker from 'components/ProductionBlocker';
import request from 'utils/request';
const shouldRenderCompo = (plugin) => new Promise((resolve, reject) => {
request('/settings-manager/autoReload')
.then(response => {
plugin.preventComponentRendering = !response.autoReload;
plugin.blockerComponent = AutoReloadBlocker;
if (response.environment !== 'development') {
plugin.preventComponentRendering = true;
plugin.blockerComponent = ProductionBlocker;
}
return resolve(plugin);
})
.catch(err => reject(err));
});
export default shouldRenderCompo;

View File

@ -111,6 +111,14 @@
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/autoReload",
"handler": "SettingsManager.autoReload",
"config": {
"policies": []
}
}
]
}

View File

@ -336,5 +336,12 @@ module.exports = {
} catch (err) {
ctx.body = ctx.notFound();
}
},
autoReload: async ctx => {
ctx.send({
autoReload: _.get(strapi.config.environments, 'development.server.autoReload', false),
environment: strapi.config.environment,
});
}
};