diff --git a/packages/strapi-generate-new/lib/before.js b/packages/strapi-generate-new/lib/before.js
index 00be647da5..1f6381aac8 100755
--- a/packages/strapi-generate-new/lib/before.js
+++ b/packages/strapi-generate-new/lib/before.js
@@ -152,7 +152,7 @@ module.exports = (scope, cb) => {
when: !hasDatabaseConfig,
type: 'input',
name: 'port',
- message: 'Port (It will be ignored if you enable +srv):',
+ message: `Port${scope.client.database === 'mongo' ? ' (It will be ignored if you enable +srv)' : ''}:`,
default: (answers) => { // eslint-disable-line no-unused-vars
if (_.get(scope.database, 'port')) {
return scope.database.port;
@@ -186,7 +186,7 @@ module.exports = (scope, cb) => {
when: !hasDatabaseConfig && scope.client.database === 'mongo',
type: 'input',
name: 'authenticationDatabase',
- message: 'Authentication database:',
+ message: 'Authentication database (Maybe "admin" or blank):',
default: _.get(scope.database, 'authenticationDatabase', undefined)
},
{
@@ -203,7 +203,7 @@ module.exports = (scope, cb) => {
}
scope.database.settings.host = answers.host;
- scope.database.settings.srv = answers.srv;
+ scope.database.settings.srv = _.toString(answers.srv) === 'true';
scope.database.settings.port = answers.port;
scope.database.settings.database = answers.database;
scope.database.settings.username = answers.username;
diff --git a/packages/strapi-helper-plugin/lib/src/components/InputFile/index.js b/packages/strapi-helper-plugin/lib/src/components/InputFile/index.js
index 8763ba4ae6..c118a3396d 100644
--- a/packages/strapi-helper-plugin/lib/src/components/InputFile/index.js
+++ b/packages/strapi-helper-plugin/lib/src/components/InputFile/index.js
@@ -8,7 +8,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
-import { cloneDeep } from 'lodash';
+import { cloneDeep, isArray, isObject } from 'lodash';
+import cn from 'classnames';
import ImgPreview from 'components/ImgPreview';
import InputFileDetails from 'components/InputFileDetails';
@@ -59,7 +60,8 @@ class InputFile extends React.Component {
type: 'file',
value,
};
-
+
+ this.inputFile.value = '';
this.setState({ isUploading: !this.state.isUploading });
this.props.onChange({ target });
}
@@ -75,11 +77,12 @@ class InputFile extends React.Component {
if (this.props.multiple) {
value.splice(this.state.position, 1);
}
+
// Update the parent's props
const target = {
name: this.props.name,
type: 'file',
- value,
+ value: Object.keys(value).length === 0 ? '' : value,
};
this.props.onChange({ target });
@@ -98,6 +101,19 @@ class InputFile extends React.Component {
this.setState({ position: newPosition });
}
+ isVisibleDetails = () => {
+ const {value} = this.props;
+
+ if (!value ||
+ (isArray(value) && value.length === 0) ||
+ (isObject(value) && Object.keys(value).length === 0)
+ ) {
+ return false;
+ }
+
+ return true;
+ }
+
render() {
const {
multiple,
@@ -108,39 +124,43 @@ class InputFile extends React.Component {
return (
+ {this.isVisibleDetails() && (
+
+ )}
);
}
@@ -150,9 +170,12 @@ InputFile.defaultProps = {
multiple: false,
setLabel: () => {},
value: [],
+ error: false,
+
};
InputFile.propTypes = {
+ error: PropTypes.bool,
multiple: PropTypes.bool,
name: PropTypes.string.isRequired,
onChange: PropTypes.func.isRequired,
diff --git a/packages/strapi-helper-plugin/lib/src/components/InputFile/styles.scss b/packages/strapi-helper-plugin/lib/src/components/InputFile/styles.scss
index c6c81e3692..26799e7784 100644
--- a/packages/strapi-helper-plugin/lib/src/components/InputFile/styles.scss
+++ b/packages/strapi-helper-plugin/lib/src/components/InputFile/styles.scss
@@ -32,3 +32,7 @@
.copy {
cursor: copy !important;
}
+
+.inputFileControlForm {
+ padding: 0;
+}
\ No newline at end of file
diff --git a/packages/strapi-helper-plugin/lib/src/components/InputFileWithErrors/index.js b/packages/strapi-helper-plugin/lib/src/components/InputFileWithErrors/index.js
index 1167cb4b1b..ed08566151 100644
--- a/packages/strapi-helper-plugin/lib/src/components/InputFileWithErrors/index.js
+++ b/packages/strapi-helper-plugin/lib/src/components/InputFileWithErrors/index.js
@@ -14,16 +14,27 @@ import Label from 'components/Label';
import InputDescription from 'components/InputDescription';
import InputFile from 'components/InputFile';
import InputSpacer from 'components/InputSpacer';
+import InputErrors from 'components/InputErrors';
+// Styles
import styles from './styles.scss';
class InputFileWithErrors extends React.PureComponent {
- state = { label: null, hasValue: false };
+ state = { errors: [], label: null, hasValue: false };
componentDidMount() {
+ const { errors } = this.props;
+ let newState = Object.assign({}, this.state);
+
if (this.props.multiple && !isEmpty(this.props.value)) {
- this.setState({ label: 1, hasValue: true });
+ newState = Object.assign({}, newState, { label: 1, hasValue: true });
}
+
+ if (!isEmpty(errors)) {
+ newState = Object.assign({}, newState, { errors });
+ }
+
+ this.setState(newState);
}
componentDidUpdate(prevProps) {
@@ -32,6 +43,12 @@ class InputFileWithErrors extends React.PureComponent {
} else if(isEmpty(this.props.value)) {
this.updateState({ label: null });
}
+ // Check if errors have been updated during validations
+ if (nextProps.didCheckErrors !== this.props.didCheckErrors) {
+ // Remove from the state the errors that have already been set
+ const errors = isEmpty(nextProps.errors) ? [] : nextProps.errors;
+ this.setState({ errors });
+ }
}
setLabel = (label) => {
@@ -47,6 +64,9 @@ class InputFileWithErrors extends React.PureComponent {
const {
className,
customBootstrapClass,
+ errorsClassName,
+ errorsStyle,
+ noErrorsDescription,
inputDescription,
inputDescriptionClassName,
inputDescriptionStyle,
@@ -83,6 +103,7 @@ class InputFileWithErrors extends React.PureComponent {
)}
+
{spacer}
);
@@ -100,8 +126,12 @@ class InputFileWithErrors extends React.PureComponent {
}
InputFileWithErrors.defaultProps = {
+ errors: [],
+ errorsClassName: '',
+ errorsStyle: {},
className: '',
customBootstrapClass: 'col-md-6',
+ didCheckErrors: false,
inputDescription: '',
inputDescriptionClassName: '',
inputDescriptionStyle: {},
@@ -109,6 +139,7 @@ InputFileWithErrors.defaultProps = {
labelClassName: '',
labelStyle: {},
multiple: false,
+ noErrorsDescription: false,
style: {},
value: [],
};
@@ -116,6 +147,10 @@ InputFileWithErrors.defaultProps = {
InputFileWithErrors.propTypes = {
className: PropTypes.string,
customBootstrapClass: PropTypes.string,
+ didCheckErrors: PropTypes.bool,
+ errors: PropTypes.array,
+ errorsClassName: PropTypes.string,
+ errorsStyle: PropTypes.object,
inputDescription: PropTypes.oneOfType([
PropTypes.string,
PropTypes.func,
@@ -138,6 +173,7 @@ InputFileWithErrors.propTypes = {
labelStyle: PropTypes.object,
multiple: PropTypes.bool,
name: PropTypes.string.isRequired,
+ noErrorsDescription: PropTypes.bool,
onChange: PropTypes.func.isRequired,
style: PropTypes.object,
value: PropTypes.oneOfType([
diff --git a/packages/strapi-hook-bookshelf/lib/relations.js b/packages/strapi-hook-bookshelf/lib/relations.js
index 09a6a26291..e22b8418d1 100644
--- a/packages/strapi-hook-bookshelf/lib/relations.js
+++ b/packages/strapi-hook-bookshelf/lib/relations.js
@@ -97,7 +97,7 @@ module.exports = {
module.exports.findOne
.call(model, { [model.primaryKey]: recordId }, [details.via])
.then(record => {
- if (record && _.isObject(record[details.via])) {
+ if (record && _.isObject(record[details.via]) && record.id !== record[details.via][current]) {
return module.exports.update.call(this, {
id: getValuePrimaryKey(record[details.via], model.primaryKey),
values: {
diff --git a/packages/strapi-hook-mongoose/lib/index.js b/packages/strapi-hook-mongoose/lib/index.js
index a0c3e5e9d4..2ad6a41574 100755
--- a/packages/strapi-hook-mongoose/lib/index.js
+++ b/packages/strapi-hook-mongoose/lib/index.js
@@ -51,9 +51,10 @@ module.exports = function (strapi) {
initialize: cb => {
_.forEach(_.pickBy(strapi.config.connections, {connector: 'strapi-hook-mongoose'}), (connection, connectionName) => {
const instance = new Mongoose();
- const { uri, host, port, username, password, database } = _.defaults(connection.settings, strapi.config.hook.settings.mongoose);
+ const { uri, host, port, username, password, database, srv } = _.defaults(connection.settings, strapi.config.hook.settings.mongoose);
const uriOptions = uri ? url.parse(uri, true).query : {};
const { authenticationDatabase, ssl, debug } = _.defaults(connection.options, uriOptions, strapi.config.hook.settings.mongoose);
+ const isSrv = srv === true || srv === 'true';
// Connect to mongo database
const connectOptions = {};
@@ -73,10 +74,16 @@ module.exports = function (strapi) {
connectOptions.ssl = ssl === true || ssl === 'true';
connectOptions.useNewUrlParser = true;
+ connectOptions.dbName = database;
options.debug = debug === true || debug === 'true';
- instance.connect(uri || `mongodb://${host}:${port}/${database}`, connectOptions);
+ /* FIXME: for now, mongoose doesn't support srv auth except the way including user/pass in URI.
+ * https://github.com/Automattic/mongoose/issues/6881 */
+ instance.connect(uri ||
+ `mongodb${isSrv ? '+srv' : ''}://${username}:${password}@${host}${ !isSrv ? ':' + port : '' }/`,
+ connectOptions
+ );
for (let key in options) {
instance.set(key, options[key]);
diff --git a/packages/strapi-hook-mongoose/lib/relations.js b/packages/strapi-hook-mongoose/lib/relations.js
index 1fbe976cac..0dcf8d35b7 100644
--- a/packages/strapi-hook-mongoose/lib/relations.js
+++ b/packages/strapi-hook-mongoose/lib/relations.js
@@ -50,7 +50,7 @@ module.exports = {
.findOne({ [model.primaryKey]: value[current] })
.populate(details.via)
.then(record => {
- if (record && _.isObject(record[details.via])) {
+ if (record && _.isObject(record[details.via]) && record._id.toString() !== record[details.via][current].toString()) {
return module.exports.update.call(this, {
id: getValuePrimaryKey(record[details.via], model.primaryKey),
values: {
diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/saga.js b/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/saga.js
index 32497c9870..cf2db9228e 100644
--- a/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/saga.js
+++ b/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/saga.js
@@ -107,6 +107,7 @@ export function* dataDeleteAll({ entriesToDelete, model, source }) {
yield put(deleteSeveralDataSuccess());
yield call(dataGet, { currentModel: model, source });
+ strapi.notification.success('content-manager.success.record.delete');
} catch(err) {
strapi.notification.error('content-manager.error.record.delete');
}
diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/forms.json b/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/forms.json
index af00853d4e..38d1808991 100644
--- a/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/forms.json
+++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/forms.json
@@ -926,7 +926,10 @@
},
"name": "name",
"type": "string",
- "value": ""
+ "value": "",
+ "validations": {
+ "required": true
+ }
},
{
"label": {
diff --git a/packages/strapi-plugin-content-type-builder/services/ContentTypeBuilder.js b/packages/strapi-plugin-content-type-builder/services/ContentTypeBuilder.js
index 2170bfec9b..6910be9dc8 100755
--- a/packages/strapi-plugin-content-type-builder/services/ContentTypeBuilder.js
+++ b/packages/strapi-plugin-content-type-builder/services/ContentTypeBuilder.js
@@ -178,7 +178,8 @@ module.exports = {
if (params.plugin === 'upload' && relation.model || relation.collection === 'file') {
params = {
type: 'media',
- multiple: params.collection ? true : false
+ multiple: params.collection ? true : false,
+ required: params.required
};
} else {
params = _.omit(params, ['collection', 'model', 'via']);
@@ -288,7 +289,8 @@ module.exports = {
attrs[attribute.name] = {
[attribute.params.multiple ? 'collection' : 'model']: 'file',
via,
- plugin: 'upload'
+ plugin: 'upload',
+ required: attribute.params.required === true ? true : false
};
}
} else if (_.has(attribute, 'params.target')) {
diff --git a/packages/strapi-plugin-users-permissions/controllers/Auth.js b/packages/strapi-plugin-users-permissions/controllers/Auth.js
index 72ba075327..25f608040e 100644
--- a/packages/strapi-plugin-users-permissions/controllers/Auth.js
+++ b/packages/strapi-plugin-users-permissions/controllers/Auth.js
@@ -56,14 +56,14 @@ module.exports = {
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.confirmed' }] }] : 'Your account email is not confirmed.');
}
- if (user.blocked === true) {
- return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.blocked' }] }] : 'Your account has been blocked by the administrator.');
- }
-
if (!user) {
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.invalid' }] }] : 'Identifier or password invalid.');
}
+ if (user.blocked === true) {
+ return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.blocked' }] }] : 'Your account has been blocked by the administrator.');
+ }
+
if (user.role.type !== 'root' && ctx.request.admin) {
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.noAdminAccess' }] }] : `You're not an administrator.`);
}
@@ -285,7 +285,7 @@ module.exports = {
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.email.taken' }] }] : 'Email is already taken.');
}
- if (user && user.provider !== params.provider && strapi.plugins['users-permissions'].config.advanced.unique_email) {
+ if (user && user.provider !== params.provider && settings.unique_email) {
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.email.taken' }] }] : 'Email is already taken.');
}