Merge branch 'master' into master

This commit is contained in:
Jim LAURIE 2018-04-24 11:29:39 +02:00 committed by GitHub
commit f2cb6d7af8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 302 additions and 129 deletions

View File

@ -13,6 +13,12 @@ This will create two files located at `./api/user/models`:
> Note: when you create a new API using the CLI (`strapi generate:api <name>`), a model is automatically created. > Note: when you create a new API using the CLI (`strapi generate:api <name>`), a model is automatically created.
## Model Information
The info key on the model-json states information about the model. This information is used in the admin interface, when showing the model.
- `name`: The name of the model, as shown in admin interface.
- `description`: The description of the model.
- `mainField`: Determines which model-attribute is shown when displaying the model.
## Define the attributes ## Define the attributes
The following types are currently available: The following types are currently available:
@ -59,7 +65,8 @@ To improve the Developer eXperience when developing or using the administration
"connection": "default", "connection": "default",
"info": { "info": {
"name": "user", "name": "user",
"description": "This represents the User Model" "description": "This represents the User Model",
"mainField": "email"
}, },
"attributes": { "attributes": {
"firstname": { "firstname": {

View File

@ -10,6 +10,8 @@ To setup the development environment please **follow the instructions below:**
2. Clone it to your computer `git clone git@github.com:strapi/strapi.git`. 2. Clone it to your computer `git clone git@github.com:strapi/strapi.git`.
3. Run `npm run setup` at the root of the directory. 3. Run `npm run setup` at the root of the directory.
> You can run `npm run setup:build` to build the plugins' admin (the setup time will be longer)
> Note: If the installation failed, please remove the global packages related to Strapi. The command `npm ls strapi` will help you to find where your packages are installed globally. > Note: If the installation failed, please remove the global packages related to Strapi. The command `npm ls strapi` will help you to find where your packages are installed globally.
## Plugin development Setup ## Plugin development Setup

View File

@ -24,6 +24,7 @@
"release": "npm run clean:all && npm install && npm run createsymlinkdependencies && lerna exec --concurrency 1 -- npm install && npm run removesymlinkdependencies && node ./scripts/publish.js $TAG", "release": "npm run clean:all && npm install && npm run createsymlinkdependencies && lerna exec --concurrency 1 -- npm install && npm run removesymlinkdependencies && node ./scripts/publish.js $TAG",
"createsymlinkdependencies": "node ./scripts/createSymlinkDependencies.js", "createsymlinkdependencies": "node ./scripts/createSymlinkDependencies.js",
"removesymlinkdependencies": "node ./scripts/removeSymlinkDependencies.js", "removesymlinkdependencies": "node ./scripts/removeSymlinkDependencies.js",
"setup:build": "npm run setup --build",
"setup": "npm run clean:all && npm install && node ./scripts/setup.js && npm run clean", "setup": "npm run clean:all && npm install && node ./scripts/setup.js && npm run clean",
"test": "make test" "test": "make test"
}, },

View File

@ -55,16 +55,6 @@ const FIRST_BLOCK = [
}, },
]; ];
const WELCOME_AGAIN_BLOCK = [
{
title: {
id: 'app.components.HomePage.welcome.again',
},
name: upperFirst(`${get(auth.getUserInfo(), 'username')}!`),
content: () => <WelcomeContent hasContent />,
},
];
const FIRST_BLOCK_LINKS = [ const FIRST_BLOCK_LINKS = [
{ {
link: 'https://strapi.io/documentation/', link: 'https://strapi.io/documentation/',
@ -169,6 +159,15 @@ export class HomePage extends React.PureComponent {
render() { render() {
const { homePage: { articles, body } } = this.props; const { homePage: { articles, body } } = this.props;
const WELCOME_AGAIN_BLOCK = [
{
title: {
id: 'app.components.HomePage.welcome.again',
},
name: upperFirst(`${get(auth.getUserInfo(), 'username')}!`),
content: () => <WelcomeContent hasContent />,
},
];
return ( return (
<div className={cn('container-fluid', styles.containerFluid)}> <div className={cn('container-fluid', styles.containerFluid)}>

View File

@ -46,7 +46,7 @@
], ],
"engines": { "engines": {
"node": ">= 9.0.0", "node": ">= 9.0.0",
"npm": ">= 3.0.0" "npm": ">= 5.0.0"
}, },
"license": "MIT" "license": "MIT"
} }

View File

@ -709,6 +709,10 @@ module.exports = function(strapi) {
acc[current] = params.values[current]; acc[current] = params.values[current];
} else { } else {
switch (association.nature) { switch (association.nature) {
case 'oneWay':
acc[current] = _.get(params.values[current], this.primaryKey, params.values[current]) || null;
break;
case 'oneToOne': case 'oneToOne':
if (response[current] !== params.values[current]) { if (response[current] !== params.values[current]) {
const value = _.isNull(params.values[current]) ? response[current] : params.values; const value = _.isNull(params.values[current]) ? response[current] : params.values;

View File

@ -48,9 +48,12 @@ module.exports = {
*/ */
add: async (values) => { add: async (values) => {
const data = await <%= globalID %>.create(_.omit(values, _.keys(_.groupBy(strapi.models.<%= id %>.associations, 'alias')))); const query = await <%= globalID %>.create(_.omit(values, _.keys(_.groupBy(strapi.models.<%= id %>.associations, 'alias'))));
await strapi.hook.mongoose.manageRelations('<%= id %>', _.merge(_.clone(data), { values })); const data = query.toJSON ? query.toJSON() : query;
return data;
await strapi.hook.mongoose.manageRelations('<%= id %>', _.merge(data, { values }));
return query;
}, },
/** /**

View File

@ -71,8 +71,8 @@ module.exports = scope => {
'uuid': uuid() 'uuid': uuid()
}, },
'engines': { 'engines': {
'node': '>= 7.0.0', 'node': '>= 9.0.0',
'npm': '>= 3.0.0' 'npm': '>= 5.0.0'
}, },
'license': scope.license || 'MIT' 'license': scope.license || 'MIT'
}); });

View File

@ -29,7 +29,7 @@ module.exports = scope => {
'analyze:clean': 'node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json', 'analyze:clean': 'node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json',
'preanalyze': 'npm run analyze:clean', 'preanalyze': 'npm run analyze:clean',
'analyze': 'node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js', 'analyze': 'node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js',
'prebuild': 'npm run build:clean && npm run test', 'prebuild': 'npm run build:clean',
'build:dev': 'node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress', 'build:dev': 'node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress',
'build': 'node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress', 'build': 'node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress',
'build:clean': 'node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build', 'build:clean': 'node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build',

View File

@ -116,6 +116,11 @@ module.exports = function (strapi) {
[`${association.via}.${association.filter}`]: association.alias, [`${association.via}.${association.filter}`]: association.alias,
[`${association.via}.kind`]: definition.globalId [`${association.via}.kind`]: definition.globalId
} }
// Select last related to an entity.
this._mongooseOptions.populate[association.alias].options = {
sort: '-createdAt'
}
} else { } else {
this._mongooseOptions.populate[association.alias].path = `${association.alias}.ref`; this._mongooseOptions.populate[association.alias].path = `${association.alias}.ref`;
} }
@ -144,6 +149,8 @@ module.exports = function (strapi) {
save: 'afterSave' save: 'afterSave'
}; };
// Mongoose doesn't allow post 'remove' event on model.
// See https://github.com/Automattic/mongoose/issues/3054
_.forEach(postLifecycle, (fn, key) => { _.forEach(postLifecycle, (fn, key) => {
if (_.isFunction(target[model.toLowerCase()][fn])) { if (_.isFunction(target[model.toLowerCase()][fn])) {
collection.schema.post(key, function (doc, next) { collection.schema.post(key, function (doc, next) {
@ -178,6 +185,7 @@ module.exports = function (strapi) {
break; break;
case 'manyMorphToMany': case 'manyMorphToMany':
case 'manyMorphToOne': case 'manyMorphToOne':
returned[association.alias] = returned[association.alias].map(obj => obj.ref); returned[association.alias] = returned[association.alias].map(obj => obj.ref);
break; break;
default: default:
@ -465,7 +473,10 @@ module.exports = function (strapi) {
break; break;
case '_contains': case '_contains':
result.key = `where.${key}`; result.key = `where.${key}`;
result.value = new RegExp('\\b' + value + '\\b', 'i'); result.value = {
$regex: value,
$options: 'i',
};
break; break;
case '_containss': case '_containss':
result.key = `where.${key}.$regex`; result.key = `where.${key}.$regex`;
@ -502,10 +513,13 @@ module.exports = function (strapi) {
acc[current] = params.values[current]; acc[current] = params.values[current];
} else { } else {
switch (association.nature) { switch (association.nature) {
case 'oneWay':
acc[current] = _.get(params.values[current], this.primaryKey, params.values[current]) || null;
break;
case 'oneToOne': case 'oneToOne':
if (response[current] !== params.values[current]) { if (response[current] !== params.values[current]) {
const value = _.isNull(params.values[current]) ? response[current] : params.values; const value = _.isNull(params.values[current]) ? response[current] : params.values;
const recordId = _.isNull(params.values[current]) ? value[Model.primaryKey] || value.id || value._id : value[current]; const recordId = _.isNull(params.values[current]) ? value[Model.primaryKey] || value.id || value._id : value[current];
if (response[current] && _.isObject(response[current]) && response[current][Model.primaryKey] !== value[current]) { if (response[current] && _.isObject(response[current]) && response[current][Model.primaryKey] !== value[current]) {

View File

@ -16,7 +16,7 @@
"main": "./lib", "main": "./lib",
"dependencies": { "dependencies": {
"lodash": "^4.17.4", "lodash": "^4.17.4",
"mongoose": "^5.0.4", "mongoose": "^5.0.15",
"mongoose-float": "^1.0.2", "mongoose-float": "^1.0.2",
"pluralize": "^6.0.0", "pluralize": "^6.0.0",
"strapi-utils": "3.0.0-alpha.12" "strapi-utils": "3.0.0-alpha.12"

View File

@ -8,39 +8,51 @@ import React from 'react';
import Select from 'react-select'; import Select from 'react-select';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import 'react-select/dist/react-select.css'; import 'react-select/dist/react-select.css';
import { isArray, isNull, isUndefined, get, findIndex } from 'lodash'; import { cloneDeep, isArray, isNull, isUndefined, get, findIndex, includes } from 'lodash';
import request from 'utils/request'; import request from 'utils/request';
import templateObject from 'utils/templateObject'; import templateObject from 'utils/templateObject';
import styles from './styles.scss'; import styles from './styles.scss';
class SelectMany extends React.Component { // eslint-disable-line react/prefer-stateless-function class SelectMany extends React.Component {
// eslint-disable-line react/prefer-stateless-function
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
isLoading: true, isLoading: true,
options: [],
toSkip: 0,
}; };
} }
getOptions = (query) => { componentDidMount() {
this.getOptions('');
}
componentDidUpdate(prevProps, prevState) {
if (prevState.toSkip !== this.state.toSkip) {
this.getOptions('');
}
}
getOptions = query => {
const params = { const params = {
limit: 20, limit: 20,
skip: this.state.toSkip,
source: this.props.relation.plugin || 'content-manager', source: this.props.relation.plugin || 'content-manager',
}; };
// Set `query` parameter if necessary // Set `query` parameter if necessary
if (query) { if (query) {
params.query = query; delete params.limit,
params.queryAttribute = this.props.relation.displayedAttribute; delete params.skip,
params[`${this.props.relation.displayedAttribute}_contains`] = query;
} }
// Request URL // Request URL
const requestUrlSuffix = query && this.props.record.get(this.props.relation.alias) ? this.props.record.get(this.props.relation.alias) : ''; const requestUrl = `/content-manager/explorer/${this.props.relation.model ||
// NOTE: keep this line if we rollback to the old container this.props.relation.collection}`;
// const requestUrlSuffix = query && this.props.record.get(this.props.relation.alias).toJS() ? this.props.record.get(this.props.relation.alias).toJS() : '';
const requestUrl = `/content-manager/explorer/${this.props.relation.model || this.props.relation.collection}/${requestUrlSuffix}`;
// Call our request helper (see 'utils/request') // Call our request helper (see 'utils/request')
return request(requestUrl, { return request(requestUrl, {
@ -48,25 +60,41 @@ class SelectMany extends React.Component { // eslint-disable-line react/prefer-s
params, params,
}) })
.then(response => { .then(response => {
const options = isArray(response) ? const options = isArray(response)
response.map(item => ({ ? response.map(item => ({
value: item, value: item,
label: templateObject({ mainField: this.props.relation.displayedAttribute }, item).mainField, label: templateObject({ mainField: this.props.relation.displayedAttribute }, item)
})) : .mainField,
[{ }))
: [
{
value: response, value: response,
label: response[this.props.relation.displayedAttribute], label: response[this.props.relation.displayedAttribute],
}]; },
];
return { options }; const newOptions = cloneDeep(this.state.options);
options.map(option => {
// Don't add the values when searching
if (findIndex(newOptions, o => o.value.id === option.value.id) === -1) {
return newOptions.push(option);
}
});
return this.setState({
options: newOptions,
isLoading: false,
});
}) })
.catch(() => { .catch(() => {
strapi.notification.error('content-manager.notification.error.relationship.fetch'); strapi.notification.error('content-manager.notification.error.relationship.fetch');
}); });
} };
handleChange = (value) => { handleChange = value => {
const filteredValue = value.filter((data, index ) => findIndex(value, (o) => o.value.id === data.value.id) === index); const filteredValue = value.filter(
(data, index) => findIndex(value, o => o.value.id === data.value.id) === index
);
const target = { const target = {
name: `record.${this.props.relation.alias}`, name: `record.${this.props.relation.alias}`,
type: 'select', type: 'select',
@ -74,37 +102,62 @@ class SelectMany extends React.Component { // eslint-disable-line react/prefer-s
}; };
this.props.setRecordAttribute({ target }); this.props.setRecordAttribute({ target });
// NOTE: keep this line if we rollback to the old container };
// this.props.setRecordAttribute(this.props.relation.alias, filteredValue);
handleBottomScroll = () => {
this.setState(prevState => {
return {
toSkip: prevState.toSkip + 20,
};
});
}
handleInputChange = (value) => {
const clonedOptions = this.state.options;
const filteredValues = clonedOptions.filter(data => includes(data.label, value));
if (filteredValues.length === 0) {
return this.getOptions(value);
}
} }
render() { render() {
const description = this.props.relation.description const description = this.props.relation.description ? (
? <p>{this.props.relation.description}</p> <p>{this.props.relation.description}</p>
: ''; ) : (
''
);
const value = get(this.props.record, this.props.relation.alias); const value = get(this.props.record, this.props.relation.alias);
// NOTE: keep this line if we rollback to the old container
// const value = this.props.record.get(this.props.relation.alias);
/* eslint-disable jsx-a11y/label-has-for */ /* eslint-disable jsx-a11y/label-has-for */
return ( return (
<div className={`form-group ${styles.selectMany}`}> <div className={`form-group ${styles.selectMany}`}>
<label htmlFor={this.props.relation.alias}>{this.props.relation.alias}</label> <label htmlFor={this.props.relation.alias}>{this.props.relation.alias}</label>
{description} {description}
<Select.Async <Select
onChange={this.handleChange} onChange={this.handleChange}
loadOptions={this.getOptions} options={this.state.options}
id={this.props.relation.alias} id={this.props.relation.alias}
isLoading={this.state.isLoading}
onMenuScrollToBottom={this.handleBottomScroll}
onInputChange={this.handleInputChange}
multi multi
value={isNull(value) || isUndefined(value) || value.size === 0 ? null : value.map(item => { value={
isNull(value) || isUndefined(value) || value.size === 0
? null
: value.map(item => {
if (item) { if (item) {
return { return {
value: get(item, 'value') || item, value: get(item, 'value') || item,
label: get(item, 'label') || templateObject({ mainField: this.props.relation.displayedAttribute }, item).mainField || item.value.id, label:
get(item, 'label') ||
templateObject({ mainField: this.props.relation.displayedAttribute }, item)
.mainField ||
item.value.id,
}; };
} }
})} })
}
/> />
</div> </div>
); );
@ -113,10 +166,7 @@ class SelectMany extends React.Component { // eslint-disable-line react/prefer-s
} }
SelectMany.propTypes = { SelectMany.propTypes = {
record: PropTypes.oneOfType([ record: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]).isRequired,
PropTypes.object,
PropTypes.bool,
]).isRequired,
relation: PropTypes.object.isRequired, relation: PropTypes.object.isRequired,
setRecordAttribute: PropTypes.func.isRequired, setRecordAttribute: PropTypes.func.isRequired,
}; };

View File

@ -8,7 +8,7 @@ import React from 'react';
import Select from 'react-select'; import Select from 'react-select';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import 'react-select/dist/react-select.css'; import 'react-select/dist/react-select.css';
import { map, isArray, isNull, isUndefined, isFunction, get } from 'lodash'; import { cloneDeep, map, includes, isArray, isNull, isUndefined, isFunction, get, findIndex } from 'lodash';
import request from 'utils/request'; import request from 'utils/request';
import templateObject from 'utils/templateObject'; import templateObject from 'utils/templateObject';
@ -21,23 +21,37 @@ class SelectOne extends React.Component { // eslint-disable-line react/prefer-st
this.state = { this.state = {
isLoading: true, isLoading: true,
options: [],
toSkip: 0,
}; };
} }
componentDidMount() {
this.getOptions('');
}
componentDidUpdate(prevProps, prevState) {
if (prevState.toSkip !== this.state.toSkip) {
this.getOptions('');
}
}
getOptions = (query) => { getOptions = (query) => {
const params = { const params = {
limit: 20, limit: 20,
skip: this.state.toSkip,
source: this.props.relation.plugin || 'content-manager', source: this.props.relation.plugin || 'content-manager',
}; };
// Set `query` parameter if necessary // Set `query` parameter if necessary
if (query) { if (query) {
params.query = query; delete params.limit,
params.queryAttribute = this.props.relation.displayedAttribute; delete params.skip,
params[`${this.props.relation.displayedAttribute}_contains`] = query;
} }
// Request URL // Request URL
const requestUrlSuffix = query && this.props.record.get(this.props.relation.alias) ? this.props.record.get(this.props.relation.alias) : ''; const requestUrlSuffix = query && get(this.props.record, [this.props.relation.alias]) ? get(this.props.record, [this.props.relation.alias]) : '';
const requestUrl = `/content-manager/explorer/${this.props.relation.model || this.props.relation.collection}/${requestUrlSuffix}`; const requestUrl = `/content-manager/explorer/${this.props.relation.model || this.props.relation.collection}/${requestUrlSuffix}`;
// Call our request helper (see 'utils/request') // Call our request helper (see 'utils/request')
@ -56,7 +70,18 @@ class SelectOne extends React.Component { // eslint-disable-line react/prefer-st
label: templateObject({ mainField: this.props.relation.displayedAttribute }, response).mainField, label: templateObject({ mainField: this.props.relation.displayedAttribute }, response).mainField,
}]; }];
return {options}; const newOptions = cloneDeep(this.state.options);
options.map(option => {
// Don't add the values when searching
if (findIndex(newOptions, o => o.value.id === option.value.id) === -1) {
return newOptions.push(option);
}
});
return this.setState({
options: newOptions,
isLoading: false,
});
}) })
.catch(() => { .catch(() => {
strapi.notification.error('content-manager.notification.relationship.fetch'); strapi.notification.error('content-manager.notification.relationship.fetch');
@ -71,8 +96,23 @@ class SelectOne extends React.Component { // eslint-disable-line react/prefer-st
}; };
this.props.setRecordAttribute({ target }); this.props.setRecordAttribute({ target });
// NOTE: keep this line if we rollback to the old container }
// this.props.setRecordAttribute(this.props.relation.alias, value);
handleBottomScroll = () => {
this.setState(prevState => {
return {
toSkip: prevState.toSkip + 20,
};
});
}
handleInputChange = (value) => {
const clonedOptions = this.state.options;
const filteredValues = clonedOptions.filter(data => includes(data.label, value));
if (filteredValues.length === 0) {
return this.getOptions(value);
}
} }
render() { render() {
@ -81,17 +121,18 @@ class SelectOne extends React.Component { // eslint-disable-line react/prefer-st
: ''; : '';
const value = get(this.props.record, this.props.relation.alias); const value = get(this.props.record, this.props.relation.alias);
// NOTE: keep this line if we rollback to the old container
// const value = this.props.record.get(this.props.relation.alias);
/* eslint-disable jsx-a11y/label-has-for */ /* eslint-disable jsx-a11y/label-has-for */
return ( return (
<div className={`form-group ${styles.selectOne}`}> <div className={`form-group ${styles.selectOne}`}>
<label htmlFor={this.props.relation.alias}>{this.props.relation.alias}</label> <label htmlFor={this.props.relation.alias}>{this.props.relation.alias}</label>
{description} {description}
<Select.Async <Select
onChange={this.handleChange} onChange={this.handleChange}
loadOptions={this.getOptions} options={this.state.options}
isLoading={this.state.isLoading}
onMenuScrollToBottom={this.handleBottomScroll}
onInputChange={this.handleInputChange}
simpleValue simpleValue
value={isNull(value) || isUndefined(value) ? null : { value={isNull(value) || isUndefined(value) ? null : {
value: isFunction(value.toJS) ? value.toJS() : value, value: isFunction(value.toJS) ? value.toJS() : value,

View File

@ -235,7 +235,7 @@ module.exports = {
}) })
} }
if (association.type === 'model') { if (association.type === 'model' || (association.type === 'collection' && _.isObject(array))) {
return _.isEmpty(array) ? [] : transformToArrayID([array]); return _.isEmpty(array) ? [] : transformToArrayID([array]);
} }

View File

@ -220,7 +220,7 @@ module.exports = {
}) })
} }
if (association.type === 'model') { if (association.type === 'model' || (association.type === 'collection' && _.isObject(array))) {
return _.isEmpty(array) ? [] : transformToArrayID([array]); return _.isEmpty(array) ? [] : transformToArrayID([array]);
} }

View File

@ -11,7 +11,7 @@
"analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json", "analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json",
"preanalyze": "npm run analyze:clean", "preanalyze": "npm run analyze:clean",
"analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js", "analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js",
"prebuild": "npm run build:clean && npm run test", "prebuild": "npm run build:clean",
"build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build", "build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build",
@ -44,7 +44,7 @@
}, },
"engines": { "engines": {
"node": ">= 9.0.0", "node": ">= 9.0.0",
"npm": ">= 3.0.0" "npm": ">= 5.0.0"
}, },
"license": "MIT" "license": "MIT"
} }

View File

@ -11,7 +11,7 @@
"analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json", "analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json",
"preanalyze": "npm run analyze:clean", "preanalyze": "npm run analyze:clean",
"analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js", "analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js",
"prebuild": "npm run build:clean && npm run test", "prebuild": "npm run build:clean",
"build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build", "build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build",
@ -48,7 +48,7 @@
}, },
"engines": { "engines": {
"node": ">= 9.0.0", "node": ">= 9.0.0",
"npm": ">= 3.0.0" "npm": ">= 5.0.0"
}, },
"license": "MIT" "license": "MIT"
} }

View File

@ -11,7 +11,7 @@
"analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json", "analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json",
"preanalyze": "npm run analyze:clean", "preanalyze": "npm run analyze:clean",
"analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js", "analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js",
"prebuild": "npm run build:clean && npm run test", "prebuild": "npm run build:clean",
"build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build", "build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build",
@ -45,8 +45,8 @@
"url": "git://github.com/strapi/strapi.git" "url": "git://github.com/strapi/strapi.git"
}, },
"engines": { "engines": {
"node": ">= 7.0.0", "node": ">= 9.0.0",
"npm": ">= 3.0.0" "npm": ">= 5.0.0"
}, },
"license": "MIT" "license": "MIT"
} }

View File

@ -11,7 +11,7 @@
"analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json", "analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json",
"preanalyze": "npm run analyze:clean", "preanalyze": "npm run analyze:clean",
"analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js", "analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js",
"prebuild": "npm run build:clean && npm run test", "prebuild": "npm run build:clean",
"build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build", "build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build",
@ -45,7 +45,7 @@
}, },
"engines": { "engines": {
"node": ">= 9.0.0", "node": ">= 9.0.0",
"npm": ">= 3.0.0" "npm": ">= 5.0.0"
}, },
"license": "MIT" "license": "MIT"
} }

View File

@ -34,8 +34,8 @@ module.exports = async cb => {
size text, size text,
url text, url text,
provider text, provider text,
updated_at ${Model.client === 'pg' ? 'timestamp with time zone' : 'timestamp'}, updated_at ${Model.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'},
created_at ${Model.client === 'pg' ? 'timestamp with time zone' : 'timestamp'} created_at ${Model.client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP'}
); );
CREATE TABLE ${quote}upload_file_morph${quote} ( CREATE TABLE ${quote}upload_file_morph${quote} (

View File

@ -11,7 +11,7 @@
"analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json", "analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json",
"preanalyze": "npm run analyze:clean", "preanalyze": "npm run analyze:clean",
"analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js", "analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js",
"prebuild": "npm run build:clean && npm run test", "prebuild": "npm run build:clean",
"build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build", "build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build",

View File

@ -11,7 +11,7 @@
"analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json", "analyze:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf stats.json",
"preanalyze": "npm run analyze:clean", "preanalyze": "npm run analyze:clean",
"analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js", "analyze": "node ./node_modules/strapi-helper-plugin/lib/internals/scripts/analyze.js",
"prebuild": "npm run build:clean && npm run test", "prebuild": "npm run build:clean",
"build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build:dev": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=development node ./node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress", "build": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/cross-env NODE_ENV=production node node_modules/strapi-helper-plugin/node_modules/.bin/webpack --config node_modules/strapi-helper-plugin/lib/internals/webpack/webpack.prod.babel.js --color -p --progress",
"build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build", "build:clean": "node ./node_modules/strapi-helper-plugin/node_modules/.bin/rimraf admin/build",
@ -51,8 +51,8 @@
"url": "git://github.com/strapi/strapi.git" "url": "git://github.com/strapi/strapi.git"
}, },
"engines": { "engines": {
"node": ">= 7.0.0", "node": ">= 9.0.0",
"npm": ">= 3.0.0" "npm": ">= 5.0.0"
}, },
"license": "MIT" "license": "MIT"
} }

View File

@ -460,8 +460,8 @@ CREATE TABLE ${quote}${details[currentModel].tableName}${quote} (
role ${details[currentModel].client === 'pg' ? 'integer' : 'int'}, role ${details[currentModel].client === 'pg' ? 'integer' : 'int'},
${quote}resetPasswordToken${quote} text, ${quote}resetPasswordToken${quote} text,
password text, password text,
updated_at ${details[currentModel].client === 'pg' ? 'timestamp with time zone' : 'timestamp'}, updated_at ${details[currentModel].client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'},
created_at ${details[currentModel].client === 'pg' ? 'timestamp with time zone' : 'timestamp'} created_at ${details[currentModel].client === 'pg' ? 'timestamp with time zone' : 'timestamp DEFAULT CURRENT_TIMESTAMP'}
);`); );`);
break; break;
case 'role': case 'role':

View File

@ -1,8 +1,21 @@
const shell = require('shelljs'); const shell = require('shelljs');
// Check npm version
const npm = shell.exec('npm -v').stdout;
if (parseFloat(npm) < 5) {
throw new Error('[ERROR: Strapi] You need npm version @>=5');
}
const nodeVersion = shell.exec('node -v').stdout.replace('v', '');
if (parseFloat(nodeVersion) < 8.6) {
throw new Error('[ERROR: Strapi] You need to use node version @>=9');
}
// Store installation start date. // Store installation start date.
const silent = process.env.npm_config_debug !== 'true'; const silent = process.env.npm_config_debug !== 'true';
const installationStartDate = new Date(); const installationStartDate = new Date();
const watcher = (label, cmd, withSuccess = true) => { const watcher = (label, cmd, withSuccess = true) => {
if (label.length > 0) { if (label.length > 0) {
shell.echo(label); shell.echo(label);
@ -21,6 +34,22 @@ const watcher = (label, cmd, withSuccess = true) => {
shell.echo('✅ Success'); shell.echo('✅ Success');
shell.echo(''); shell.echo('');
} }
};
const asyncWatcher = (label, cmd, withSuccess = true, resolve) => {
if (label.length > 0) {
shell.echo(label);
}
return shell.exec(cmd, { silent, async: true }, (code, stdout, stderr) => {
if (stderr && code !== 0) {
console.error(stderr);
process.exit(1);
}
return resolve();
});
}; };
shell.echo(''); shell.echo('');
@ -57,7 +86,6 @@ if (shell.test('-e', 'admin/src/config/plugins.json') === false) {
} }
watcher('📦 Linking strapi-admin', 'npm link --no-optional', false); watcher('📦 Linking strapi-admin', 'npm link --no-optional', false);
watcher('🏗 Building...', 'npm run build');
shell.cd('../strapi-generate-admin'); shell.cd('../strapi-generate-admin');
watcher('', 'npm install ../strapi-admin'); watcher('', 'npm install ../strapi-admin');
@ -83,6 +111,14 @@ shell.cd('../strapi');
watcher('', 'npm install ../strapi-generate ../strapi-generate-admin ../strapi-generate-api ../strapi-generate-new ../strapi-generate-plugin ../strapi-generate-policy ../strapi-generate-service ../strapi-utils'); watcher('', 'npm install ../strapi-generate ../strapi-generate-admin ../strapi-generate-api ../strapi-generate-new ../strapi-generate-plugin ../strapi-generate-policy ../strapi-generate-service ../strapi-utils');
watcher('📦 Linking strapi...', 'npm link'); watcher('📦 Linking strapi...', 'npm link');
// Upload plugins
shell.cd('../strapi-upload-local');
watcher('📦 Linking strapi-upload-local...', 'npm link --no-optional', false);
shell.cd('../strapi-upload-aws-s3');
watcher('📦 Linking strapi-upload-aws-s3...', 'npm link --no-optional', false);
// Plugins with admin
shell.cd('../strapi-plugin-email'); shell.cd('../strapi-plugin-email');
shell.rm('-f', 'package-lock.json'); shell.rm('-f', 'package-lock.json');
watcher('📦 Linking strapi-plugin-email...', 'npm link --no-optional', false); watcher('📦 Linking strapi-plugin-email...', 'npm link --no-optional', false);
@ -91,32 +127,24 @@ shell.cd('../strapi-plugin-users-permissions');
watcher('', 'npm install ../strapi-helper-plugin --no-optional'); watcher('', 'npm install ../strapi-helper-plugin --no-optional');
shell.rm('-f', 'package-lock.json'); shell.rm('-f', 'package-lock.json');
watcher('📦 Linking strapi-plugin-users-permissions...', 'npm link --no-optional', false); watcher('📦 Linking strapi-plugin-users-permissions...', 'npm link --no-optional', false);
watcher('🏗 Building...', 'npm run build');
shell.cd('../strapi-plugin-content-manager'); shell.cd('../strapi-plugin-content-manager');
watcher('', 'npm install ../strapi-helper-plugin --no-optional'); watcher('', 'npm install ../strapi-helper-plugin --no-optional');
shell.rm('-f', 'package-lock.json'); shell.rm('-f', 'package-lock.json');
watcher('📦 Linking strapi-plugin-content-manager...', 'npm link --no-optional', false); watcher('📦 Linking strapi-plugin-content-manager...', 'npm link --no-optional', false);
watcher('🏗 Building...', 'npm run build');
shell.cd('../strapi-plugin-settings-manager'); shell.cd('../strapi-plugin-settings-manager');
watcher('', 'npm install ../strapi-helper-plugin --no-optional'); watcher('', 'npm install ../strapi-helper-plugin --no-optional');
shell.rm('-f', 'package-lock.json'); shell.rm('-f', 'package-lock.json');
watcher('📦 Linking strapi-plugin-settings-manager...', 'npm link --no-optional', false); watcher('📦 Linking strapi-plugin-settings-manager...', 'npm link --no-optional', false);
watcher('🏗 Building...', 'npm run build');
shell.cd('../strapi-upload-local'); // Plugins with admin and other plugin's dependencies
watcher('📦 Linking strapi-upload-local...', 'npm link --no-optional', false);
shell.cd('../strapi-upload-aws-s3');
watcher('📦 Linking strapi-upload-aws-s3...', 'npm link --no-optional', false);
shell.cd('../strapi-plugin-upload'); shell.cd('../strapi-plugin-upload');
watcher('', 'npm install ../strapi-helper-plugin --no-optional'); watcher('', 'npm install ../strapi-helper-plugin --no-optional');
watcher('', 'npm install ../strapi-upload-local --no-optional'); watcher('', 'npm install ../strapi-upload-local --no-optional');
shell.rm('-f', 'package-lock.json'); shell.rm('-f', 'package-lock.json');
watcher('📦 Linking strapi-plugin-upload...', 'npm link --no-optional', false); watcher('📦 Linking strapi-plugin-upload...', 'npm link --no-optional', false);
watcher('🏗 Building...', 'npm run build');
shell.cd('../strapi-plugin-content-type-builder'); shell.cd('../strapi-plugin-content-type-builder');
watcher('', 'npm install ../strapi-helper-plugin --no-optional'); watcher('', 'npm install ../strapi-helper-plugin --no-optional');
@ -124,15 +152,39 @@ watcher('', 'npm install ../strapi-generate --no-optional');
watcher('', 'npm install ../strapi-generate-api --no-optional'); watcher('', 'npm install ../strapi-generate-api --no-optional');
shell.rm('-f', 'package-lock.json'); shell.rm('-f', 'package-lock.json');
watcher('📦 Linking strapi-plugin-content-type-builder...', 'npm link --no-optional', false); watcher('📦 Linking strapi-plugin-content-type-builder...', 'npm link --no-optional', false);
watcher('🏗 Building...', 'npm run build');
shell.cd('../strapi-plugin-graphql'); const pluginsToBuild = ['admin', 'content-manager', 'content-type-builder', 'upload', 'users-permissions', 'settings-manager'];
watcher('', 'npm install ../strapi-utils --no-optional');
shell.rm('-f', 'package-lock.json');
watcher('📦 Linking strapi-plugin-graphql...', 'npm link --no-optional', false);
// Log installation duration. const buildPlugins = async () => {
const installationEndDate = new Date(); const build = (pckgName) => {
const duration = (installationEndDate.getTime() - installationStartDate.getTime()) / 1000; return new Promise(resolve => {
shell.echo('✅ Strapi has been succesfully installed.'); const name = pckgName === 'admin' ? pckgName: `plugin-${pckgName}`;
shell.echo(`⏳ The installation took ${Math.floor(duration / 60) > 0 ? `${Math.floor(duration / 60)} minutes and ` : ''}${Math.floor(duration % 60)} seconds.`); asyncWatcher(`🏗 Building ${name}...`, `cd ../strapi-${name} && npm run build`, false, resolve);
});
};
return Promise.all(pluginsToBuild.map(plugin => build(plugin)));
};
const setup = async () => {
if (process.env.npm_config_build) {
if (process.platform === 'darwin') { // Allow async build for darwin platform
await buildPlugins();
} else {
pluginsToBuild.map(name => {
const pluginName = name === 'admin' ? name : `plugin-${name}`;
shell.cd(`../strapi-${pluginName}`);
return watcher(`🏗 Building ${pluginName}...`, 'npm run build');
});
}
}
// Log installation duration.
const installationEndDate = new Date();
const duration = (installationEndDate.getTime() - installationStartDate.getTime()) / 1000;
shell.echo('✅ Strapi has been succesfully installed.');
shell.echo(`⏳ The installation took ${Math.floor(duration / 60) > 0 ? `${Math.floor(duration / 60)} minutes and ` : ''}${Math.floor(duration % 60)} seconds.`);
};
setup();