mirror of
https://github.com/strapi/strapi.git
synced 2025-11-02 19:04:38 +00:00
Fix conflicts
This commit is contained in:
commit
bac6dad0a6
@ -28,6 +28,7 @@ before_script:
|
||||
script:
|
||||
- npm run lint
|
||||
- npm run doc
|
||||
- npm run test:front
|
||||
|
||||
cache:
|
||||
directories:
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
@import '../../styles/variables/variables';
|
||||
|
||||
.notificationsContainer { /* stylelint-disable */
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
top: 72px;
|
||||
left: 240px;
|
||||
right: 0;
|
||||
|
||||
@ -296,8 +296,8 @@ export class AdminPage extends React.Component {
|
||||
}
|
||||
|
||||
AdminPage.childContextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
currentEnvironment: PropTypes.string.isRequired,
|
||||
emitEvent: PropTypes.func,
|
||||
disableGlobalOverlayBlocker: PropTypes.func,
|
||||
enableGlobalOverlayBlocker: PropTypes.func,
|
||||
plugins: PropTypes.object,
|
||||
@ -305,6 +305,7 @@ AdminPage.childContextTypes = {
|
||||
};
|
||||
|
||||
AdminPage.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
router: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@ -121,7 +121,9 @@
|
||||
"components.Input.error.validation.required": "This value is required.",
|
||||
"components.ListRow.empty": "There is no data to be shown.",
|
||||
"components.OverlayBlocker.description": "You're using a feature that needs the server to restart. Please wait until the server is up.",
|
||||
"components.OverlayBlocker.description.serverError": "The server should have restarted, please check your logs in the terminal.",
|
||||
"components.OverlayBlocker.title": "Waiting for restart...",
|
||||
"components.OverlayBlocker.title.serverError": "The restart takes longer than expected",
|
||||
"components.PageFooter.select": "entries per page",
|
||||
"components.ProductionBlocker.description": "For safety purposes we have to disable this plugin in other environments.",
|
||||
"components.ProductionBlocker.header": "This plugin is only available in development.",
|
||||
|
||||
@ -122,7 +122,9 @@
|
||||
"components.Input.error.validation.required": "Ce champ est obligatoire.",
|
||||
"components.ListRow.empty": "Il n'y a pas de données à afficher.",
|
||||
"components.OverlayBlocker.description": "Vous utilisez une fonctionnalité qui nécessite le redémarrage du server. Merci d'attendre que celui-ci ait redémarré.",
|
||||
"components.OverlayBlocker.description.serverError": "Le serveur aurait déjà du redémarrer, vous devriez regarder les messages dans le terminal.",
|
||||
"components.OverlayBlocker.title": "Le serveur est en train de redémarrer",
|
||||
"components.OverlayBlocker.title.serverError": "Le serveur aurait déjà du redémarrer",
|
||||
"components.PageFooter.select": "entrées par page",
|
||||
"components.ProductionBlocker.description": "Pour des raisons de sécurité il est désactivé dans les autres environnements.",
|
||||
"components.ProductionBlocker.header": "Ce plugin est disponible uniquement en développement.",
|
||||
|
||||
@ -55,4 +55,4 @@
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@ -129,7 +129,7 @@ module.exports = {
|
||||
|
||||
await Promise.all(
|
||||
<%= globalID %>.associations.map(async association => {
|
||||
if (!association.via || !data._id) {
|
||||
if (!association.via || !data._id || association.dominant) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@ -29,6 +29,7 @@ function InputNumber(props) {
|
||||
onFocus={props.onFocus}
|
||||
placeholder={message}
|
||||
ref={props.inputRef}
|
||||
step={!Number.isNaN(Number(props.step)) ? Number(props.step) : 1}
|
||||
style={props.style}
|
||||
tabIndex={props.tabIndex}
|
||||
type="number"
|
||||
@ -49,6 +50,7 @@ InputNumber.defaultProps = {
|
||||
onBlur: () => {},
|
||||
onFocus: () => {},
|
||||
placeholder: 'app.utils.placeholder.defaultMessage',
|
||||
step: 1,
|
||||
style: {},
|
||||
tabIndex: '0',
|
||||
};
|
||||
@ -65,6 +67,7 @@ InputNumber.propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onFocus: PropTypes.func,
|
||||
placeholder: PropTypes.string,
|
||||
step: PropTypes.number,
|
||||
style: PropTypes.object,
|
||||
tabIndex: PropTypes.string,
|
||||
value: PropTypes.oneOfType([
|
||||
|
||||
@ -83,6 +83,7 @@ class InputNumberWithErrors extends React.Component { // eslint-disable-line rea
|
||||
placeholder,
|
||||
style,
|
||||
tabIndex,
|
||||
step,
|
||||
value,
|
||||
} = this.props;
|
||||
const handleBlur = isFunction(onBlur) ? onBlur : this.handleBlur;
|
||||
@ -121,6 +122,7 @@ class InputNumberWithErrors extends React.Component { // eslint-disable-line rea
|
||||
placeholder={placeholder}
|
||||
style={inputStyle}
|
||||
tabIndex={tabIndex}
|
||||
step={step}
|
||||
value={value}
|
||||
/>
|
||||
<InputDescription
|
||||
@ -162,6 +164,7 @@ InputNumberWithErrors.defaultProps = {
|
||||
labelStyle: {},
|
||||
noErrorsDescription: false,
|
||||
placeholder: 'app.utils.placeholder.defaultMessage',
|
||||
step: 1,
|
||||
style: {},
|
||||
tabIndex: '0',
|
||||
validations: {},
|
||||
@ -209,6 +212,7 @@ InputNumberWithErrors.propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
onFocus: PropTypes.func,
|
||||
placeholder: PropTypes.string,
|
||||
step: PropTypes.number,
|
||||
style: PropTypes.object,
|
||||
tabIndex: PropTypes.string,
|
||||
validations: PropTypes.object,
|
||||
|
||||
@ -12,27 +12,78 @@ import cn from 'classnames';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
const DELAY = 1000;
|
||||
|
||||
class OverlayBlocker extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = { elapsed: 0, start: 0 };
|
||||
this.overlayContainer = document.createElement('div');
|
||||
|
||||
document.body.appendChild(this.overlayContainer);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { isOpen } = this.props;
|
||||
|
||||
if (prevProps.isOpen !== this.props.isOpen && isOpen) {
|
||||
this.startTimer();
|
||||
}
|
||||
|
||||
if (prevProps.isOpen !== isOpen && !isOpen) {
|
||||
this.stopTimer();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
document.body.removeChild(this.overlayContainer);
|
||||
}
|
||||
|
||||
tick = () => {
|
||||
const { elapsed } = this.state;
|
||||
|
||||
if (elapsed > 15) {
|
||||
clearInterval(this.timer);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState(prevState => ({ elapsed: (Math.round(Date.now() - prevState.start) / 1000) }));
|
||||
}
|
||||
|
||||
startTimer = () => {
|
||||
this.setState({ start: Date.now() });
|
||||
this.timer = setInterval(this.tick, DELAY);
|
||||
}
|
||||
|
||||
stopTimer = () => {
|
||||
this.setState({ elapsed: 0 });
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { title, description, icon } = this.props;
|
||||
let { title, description, icon } = this.props;
|
||||
const { elapsed } = this.state;
|
||||
|
||||
let button = (
|
||||
<div className={styles.buttonContainer}>
|
||||
<a className={cn(styles.primary, 'btn')} href="https://strapi.io/documentation/configurations/configurations.html#server" target="_blank">Read the documentation</a>
|
||||
</div>
|
||||
);
|
||||
|
||||
if (elapsed > 15) {
|
||||
button = null;
|
||||
icon = 'fa fa-clock-o';
|
||||
description = 'components.OverlayBlocker.description.serverError';
|
||||
title = 'components.OverlayBlocker.title.serverError';
|
||||
}
|
||||
|
||||
const content = this.props.children ? (
|
||||
this.props.children
|
||||
) : (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.icoContainer}>
|
||||
<div className={cn(styles.icoContainer, elapsed < 15 && styles.spin)}>
|
||||
<i className={icon} />
|
||||
</div>
|
||||
<div>
|
||||
@ -42,9 +93,7 @@ class OverlayBlocker extends React.Component {
|
||||
<p>
|
||||
<FormattedMessage id={description} />
|
||||
</p>
|
||||
<div className={styles.buttonContainer}>
|
||||
<a className={cn(styles.primary, 'btn')} href="https://strapi.io/documentation/configurations/configurations.html#server" target="_blank">Read the documentation</a>
|
||||
</div>
|
||||
{button}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -65,7 +114,7 @@ class OverlayBlocker extends React.Component {
|
||||
}
|
||||
|
||||
OverlayBlocker.defaultProps = {
|
||||
children: '',
|
||||
children: null,
|
||||
description: 'components.OverlayBlocker.description',
|
||||
icon: 'fa fa-refresh',
|
||||
isOpen: false,
|
||||
|
||||
@ -23,6 +23,9 @@
|
||||
color: #323740;
|
||||
margin-right: 20px;
|
||||
line-height: 9.3rem;
|
||||
}
|
||||
|
||||
.spin {
|
||||
> i {
|
||||
-webkit-animation:spin 4s linear infinite;
|
||||
-moz-animation:spin 4s linear infinite;
|
||||
|
||||
@ -30,6 +30,7 @@ function PageFooter(props) {
|
||||
name: 'params._limit',
|
||||
value: parseInt(e.target.value, 10),
|
||||
};
|
||||
props.context.emitEvent('willChangeNumberOfEntriesPerPage');
|
||||
props.onChangeParams({ target });
|
||||
}}
|
||||
value={get(props, ['params', '_limit'], 10)}
|
||||
@ -54,6 +55,7 @@ function PageFooter(props) {
|
||||
}
|
||||
|
||||
PageFooter.defaultProps = {
|
||||
context: {},
|
||||
count: 1,
|
||||
onChangeParams: () => {},
|
||||
params: {
|
||||
@ -64,6 +66,7 @@ PageFooter.defaultProps = {
|
||||
};
|
||||
|
||||
PageFooter.propTypes = {
|
||||
context: PropTypes.object,
|
||||
count: PropTypes.number,
|
||||
onChangeParams: PropTypes.func,
|
||||
params: PropTypes.object,
|
||||
|
||||
@ -14,7 +14,7 @@ class Manager {
|
||||
|
||||
/**
|
||||
* Retrieve the bootstrap col index, name and type of a field
|
||||
* @param {Number} index
|
||||
* @param {Number} index
|
||||
* @returns {Object}
|
||||
*/
|
||||
getAttrInfos(index) {
|
||||
@ -72,7 +72,7 @@ class Manager {
|
||||
/**
|
||||
* Retrieve a field default bootstrap col
|
||||
* NOTE: will change if we add the customisation of an input's width
|
||||
* @param {String} type
|
||||
* @param {String} type
|
||||
* @returns {Number}
|
||||
*/
|
||||
getBootStrapCol(type) {
|
||||
@ -80,7 +80,7 @@ class Manager {
|
||||
case 'checkbox':
|
||||
case 'boolean':
|
||||
case 'date':
|
||||
case 'bigint':
|
||||
case 'biginteger':
|
||||
case 'decimal':
|
||||
case 'float':
|
||||
case 'integer':
|
||||
@ -109,7 +109,7 @@ class Manager {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Retrieve the last element of each line of a bootstrap grid and push it into an array
|
||||
* @returns {Array}
|
||||
*/
|
||||
@ -138,7 +138,7 @@ class Manager {
|
||||
|
||||
if (i < this.list.size - 1) {
|
||||
let { bootstrapCol: nextBootstrapCol, name: nextName, type: nextType } = this.getAttrInfos(i + 1);
|
||||
|
||||
|
||||
if (!nextType && nextName.includes('__col')) {
|
||||
nextBootstrapCol = parseInt(nextName.split('__')[1].split('-')[2], 10);
|
||||
}
|
||||
@ -155,9 +155,9 @@ class Manager {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Retrieve the field's type depending on its name
|
||||
* @param {String} itemName
|
||||
* @param {String} itemName
|
||||
* @returns {String}
|
||||
*/
|
||||
getType(itemName) {
|
||||
@ -177,8 +177,8 @@ class Manager {
|
||||
|
||||
/**
|
||||
* Retrieve the line bootstrap col sum
|
||||
* @param {Number} leftBound
|
||||
* @param {Number} rightBound
|
||||
* @param {Number} leftBound
|
||||
* @param {Number} rightBound
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
@ -237,7 +237,7 @@ class Manager {
|
||||
if (lineSize < 10 && i < this.arrayOfEndLineElements.length - 1) {
|
||||
const colsToAdd = this.getColsToAdd(12 - lineSize);
|
||||
newList = newList.insert(lastLineItem + sum, colsToAdd[0]);
|
||||
|
||||
|
||||
if (colsToAdd.length > 1) {
|
||||
newList = newList.insert(lastLineItem + sum, colsToAdd[1]);
|
||||
}
|
||||
@ -249,4 +249,4 @@ class Manager {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Manager;
|
||||
module.exports = Manager;
|
||||
|
||||
@ -422,12 +422,12 @@ module.exports = function(strapi) {
|
||||
case 'email':
|
||||
type = 'varchar(255)';
|
||||
break;
|
||||
case 'biginteger':
|
||||
type = definition.client === 'pg' ? 'bigint' : 'bigint(53)';
|
||||
break;
|
||||
case 'integer':
|
||||
type = definition.client === 'pg' ? 'integer' : 'int';
|
||||
break;
|
||||
case 'biginteger':
|
||||
type = definition.client === 'pg' ? 'bigint' : 'bigint(53)';
|
||||
break;
|
||||
case 'float':
|
||||
type = definition.client === 'pg' ? 'double precision' : 'double';
|
||||
break;
|
||||
|
||||
@ -27,7 +27,7 @@ const getInputType = (type = '') => {
|
||||
switch (type.toLowerCase()) {
|
||||
case 'boolean':
|
||||
return 'toggle';
|
||||
case 'bigint':
|
||||
case 'biginteger':
|
||||
case 'decimal':
|
||||
case 'float':
|
||||
case 'integer':
|
||||
|
||||
@ -20,7 +20,7 @@ const getInputType = (attrType) => {
|
||||
case 'datetime':
|
||||
return InputDate;
|
||||
case 'integer':
|
||||
case 'bigint':
|
||||
case 'biginteger':
|
||||
case 'decimal':
|
||||
case 'float':
|
||||
return InputNumber;
|
||||
|
||||
@ -47,6 +47,7 @@ const getFilters = (type) => {
|
||||
},
|
||||
];
|
||||
case 'integer':
|
||||
case 'biginteger':
|
||||
case 'float':
|
||||
case 'decimal':
|
||||
case 'date':
|
||||
|
||||
@ -73,7 +73,10 @@ class FiltersPickWrapper extends React.PureComponent {
|
||||
label: 'content-manager.components.FiltersPickWrapper.PluginHeader.actions.apply',
|
||||
kind: 'primary',
|
||||
type: 'submit',
|
||||
onClick: this.props.onSubmit,
|
||||
onClick: (e) => {
|
||||
this.context.emitEvent('didFilterEntries');
|
||||
this.props.onSubmit(e);
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
@ -179,6 +182,10 @@ class FiltersPickWrapper extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
FiltersPickWrapper.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
FiltersPickWrapper.defaultProps = {
|
||||
appliedFilters: [],
|
||||
filterToFocus: null,
|
||||
|
||||
@ -68,6 +68,7 @@ class TableRow extends React.Component {
|
||||
|
||||
// Redirect to the edit page
|
||||
handleClick() {
|
||||
this.context.emitEvent('willEditEntry');
|
||||
this.context.router.history.push(`${this.props.destination}${this.props.redirectUrl}`);
|
||||
}
|
||||
|
||||
@ -128,6 +129,7 @@ class TableRow extends React.Component {
|
||||
}
|
||||
|
||||
TableRow.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
router: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ const getBootstrapClass = attrType => {
|
||||
case 'boolean':
|
||||
case 'toggle':
|
||||
case 'date':
|
||||
case 'bigint':
|
||||
case 'biginteger':
|
||||
case 'decimal':
|
||||
case 'float':
|
||||
case 'integer':
|
||||
|
||||
@ -183,9 +183,10 @@ export function onReset() {
|
||||
};
|
||||
}
|
||||
|
||||
export function onSubmit() {
|
||||
export function onSubmit(context) {
|
||||
return {
|
||||
type: ON_SUBMIT,
|
||||
context,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -28,11 +28,13 @@ export function* getModels() {
|
||||
}
|
||||
}
|
||||
|
||||
export function* submit() {
|
||||
export function* submit(action) {
|
||||
try {
|
||||
const schema = yield select(makeSelectModifiedSchema());
|
||||
yield call(request, '/content-manager/models', { method: 'PUT', body: { schema } });
|
||||
|
||||
|
||||
action.context.emitEvent('didSaveContentTypeLayout');
|
||||
|
||||
yield put(submitSucceeded());
|
||||
} catch(err) {
|
||||
// Silent
|
||||
|
||||
@ -154,9 +154,10 @@ export function setLoader() {
|
||||
};
|
||||
}
|
||||
|
||||
export function submit() {
|
||||
export function submit(context) {
|
||||
return {
|
||||
type: SUBMIT,
|
||||
context,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -380,7 +380,7 @@ export class EditPage extends React.Component {
|
||||
);
|
||||
|
||||
if (isEmpty(formErrors)) {
|
||||
this.props.submit();
|
||||
this.props.submit(this.context);
|
||||
}
|
||||
|
||||
this.props.setFormErrors(formErrors);
|
||||
@ -436,7 +436,7 @@ export class EditPage extends React.Component {
|
||||
};
|
||||
|
||||
return (
|
||||
<li key={`${pluginId}.link`}>
|
||||
<li key={`${pluginId}.link`} onClick={() => this.context.emitEvent('willEditContentTypeLayoutFromEditView')}>
|
||||
<NavLink {...message} url={url} />
|
||||
</li>
|
||||
);
|
||||
@ -506,7 +506,7 @@ export class EditPage extends React.Component {
|
||||
const Component = compo.component;
|
||||
|
||||
return (
|
||||
<li key={compo.key}>
|
||||
<li key={compo.key} onClick={() => this.context.emitEvent('willEditContentTypeFromEditView')}>
|
||||
<Component {...this} {...compo.props} />
|
||||
</li>
|
||||
);
|
||||
@ -704,6 +704,7 @@ export class EditPage extends React.Component {
|
||||
}
|
||||
|
||||
EditPage.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
currentEnvironment: PropTypes.string,
|
||||
plugins: PropTypes.object,
|
||||
};
|
||||
|
||||
@ -68,7 +68,7 @@ function* deleteData() {
|
||||
}
|
||||
}
|
||||
|
||||
export function* submit() {
|
||||
export function* submit(action) {
|
||||
const currentModelName = yield select(makeSelectModelName());
|
||||
const fileRelations = yield select(makeSelectFileRelations());
|
||||
const isCreating = yield select(makeSelectIsCreating());
|
||||
@ -140,6 +140,8 @@ export function* submit() {
|
||||
|
||||
const requestUrl = `/content-manager/explorer/${currentModelName}/${id}`;
|
||||
|
||||
action.context.emitEvent('willSaveEntry');
|
||||
|
||||
// Call our request helper (see 'utils/request')
|
||||
// Pass false and false as arguments so the request helper doesn't stringify
|
||||
// the body and doesn't watch for the server to restart
|
||||
@ -150,11 +152,14 @@ export function* submit() {
|
||||
params,
|
||||
}, false, false);
|
||||
|
||||
action.context.emitEvent('didSaveEntry');
|
||||
|
||||
strapi.notification.success('content-manager.success.record.save');
|
||||
// Redirect the user to the ListPage container
|
||||
yield put(submitSuccess());
|
||||
|
||||
} catch(err) {
|
||||
action.context.emitEvent('didNotSaveEntry', { error: err });
|
||||
if (isArray(get(err, 'response.payload.message'))) {
|
||||
const errors = err.response.payload.message.reduce((acc, current) => {
|
||||
const error = current.messages.reduce((acc, current) => {
|
||||
|
||||
@ -53,12 +53,13 @@ export function changeParams({ target }) {
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteData(id, modelName, source) {
|
||||
export function deleteData(id, modelName, source, context) {
|
||||
return {
|
||||
type: DELETE_DATA,
|
||||
id,
|
||||
modelName,
|
||||
source,
|
||||
context,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -192,7 +192,7 @@ export class ListPage extends React.Component {
|
||||
* Retrieve the model's schema
|
||||
* @return {Object} Fields
|
||||
*/
|
||||
getCurrentSchema = () =>
|
||||
getCurrentSchema = () =>
|
||||
get(this.props.schema, ['models', this.getCurrentModelName(), 'fields']) ||
|
||||
get(this.props.schema, ['models', 'plugins', this.getSource(), this.getCurrentModelName(), 'fields']);
|
||||
|
||||
@ -257,6 +257,8 @@ export class ListPage extends React.Component {
|
||||
const attrIndex = this.findAttrIndex(target.name);
|
||||
const defaultSettingsAttrIndex = findIndex(defaultSettingsDisplay, ['name', target.name]);
|
||||
|
||||
this.context.emitEvent('didChangeDisplayedFields');
|
||||
|
||||
if (attrIndex !== -1) {
|
||||
if (get(this.props.listPage, 'displayedFields', []).length === 1) {
|
||||
strapi.notification.error('content-manager.notification.error.displayedFields');
|
||||
@ -282,10 +284,10 @@ export class ListPage extends React.Component {
|
||||
} else {
|
||||
const attributes = this.getCurrentModelAttributes();
|
||||
const searchable = attributes[target.name].type !== 'json' && attributes[target.name] !== 'array';
|
||||
const attrToAdd = defaultSettingsAttrIndex !== -1
|
||||
const attrToAdd = defaultSettingsAttrIndex !== -1
|
||||
? get(defaultSettingsDisplay, [defaultSettingsAttrIndex], {})
|
||||
: Object.assign(attributes[target.name], { name: target.name, label: upperFirst(target.name), searchable, sortable: searchable });
|
||||
|
||||
|
||||
this.props.addAttr(attrToAdd, defaultSettingsAttrIndex);
|
||||
}
|
||||
}
|
||||
@ -332,7 +334,7 @@ export class ListPage extends React.Component {
|
||||
handleDelete = e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.props.deleteData(this.state.target, this.getCurrentModelName(), this.getSource());
|
||||
this.props.deleteData(this.state.target, this.getCurrentModelName(), this.getSource(), this.context);
|
||||
this.setState({ showWarning: false });
|
||||
};
|
||||
|
||||
@ -379,7 +381,13 @@ export class ListPage extends React.Component {
|
||||
|
||||
showBulkActions = () => get(this.getCurrentModel(), ['bulkActions']);
|
||||
|
||||
toggle = () => this.setState(prevState => ({ isOpen: !prevState.isOpen }));
|
||||
toggle = () => {
|
||||
if (!this.state.isOpen) {
|
||||
this.context.emitEvent('willChangeDisplayedFields');
|
||||
}
|
||||
|
||||
this.setState(prevState => ({ isOpen: !prevState.isOpen }));
|
||||
}
|
||||
|
||||
toggleModalWarning = e => {
|
||||
if (!isUndefined(e)) {
|
||||
@ -446,11 +454,13 @@ export class ListPage extends React.Component {
|
||||
entity: capitalize(this.props.match.params.slug) || 'Content Manager',
|
||||
},
|
||||
kind: 'primaryAddShape',
|
||||
onClick: () =>
|
||||
onClick: () => {
|
||||
this.context.emitEvent('willCreateEntry');
|
||||
this.props.history.push({
|
||||
pathname: `${this.props.location.pathname}/create`,
|
||||
search: this.generateRedirectURI(),
|
||||
}),
|
||||
});
|
||||
},
|
||||
},
|
||||
];
|
||||
const { listPage: { count } } = this.props;
|
||||
@ -555,7 +565,7 @@ export class ListPage extends React.Component {
|
||||
decreaseMarginBottom={filters.length > 0}
|
||||
>
|
||||
<div className="row">
|
||||
<AddFilterCTA onClick={onToggleFilters} showHideText={showFilter} id="addFilterCTA" />
|
||||
<AddFilterCTA onClick={(e) => this.context.emitEvent('willFilterEntries') && onToggleFilters(e)} showHideText={showFilter} id="addFilterCTA" />
|
||||
{filters.map(this.renderFilter)}
|
||||
</div>
|
||||
</Div>
|
||||
@ -613,6 +623,7 @@ export class ListPage extends React.Component {
|
||||
/>
|
||||
{this.renderPopUpWarningDeleteAll()}
|
||||
<PageFooter
|
||||
context={this.context}
|
||||
count={get(count, this.getCurrentModelName(), 0)}
|
||||
onChangeParams={this.handleChangeParams}
|
||||
params={listPage.params}
|
||||
@ -627,6 +638,10 @@ export class ListPage extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
ListPage.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
ListPage.propTypes = {
|
||||
addAttr: PropTypes.func.isRequired,
|
||||
addFilter: PropTypes.func.isRequired,
|
||||
|
||||
@ -73,7 +73,7 @@ export function* dataGet(action) {
|
||||
}
|
||||
}
|
||||
|
||||
export function* dataDelete({ id, modelName, source }) {
|
||||
export function* dataDelete({ id, modelName, source, context }) {
|
||||
try {
|
||||
const requestUrl = `/content-manager/explorer/${modelName}/${id}`;
|
||||
const params = {};
|
||||
@ -82,6 +82,8 @@ export function* dataDelete({ id, modelName, source }) {
|
||||
params.source = source;
|
||||
}
|
||||
|
||||
context.emitEvent('willDeleteEntry');
|
||||
|
||||
yield call(request, requestUrl, {
|
||||
method: 'DELETE',
|
||||
params,
|
||||
@ -90,6 +92,8 @@ export function* dataDelete({ id, modelName, source }) {
|
||||
strapi.notification.success('content-manager.success.record.delete');
|
||||
|
||||
yield put(deleteDataSuccess(id));
|
||||
|
||||
context.emitEvent('didDeleteEntry');
|
||||
} catch(err) {
|
||||
strapi.notification.error('content-manager.error.record.delete');
|
||||
}
|
||||
|
||||
@ -318,7 +318,10 @@ class SettingPage extends React.PureComponent {
|
||||
{
|
||||
kind: 'primary',
|
||||
label: 'content-manager.containers.Edit.submit',
|
||||
onClick: this.handleSubmit,
|
||||
onClick: (e) => {
|
||||
this.context.emitEvent('willSaveContentTypeLayout');
|
||||
this.handleSubmit(e);
|
||||
},
|
||||
type: 'submit',
|
||||
},
|
||||
];
|
||||
@ -1060,7 +1063,7 @@ class SettingPage extends React.PureComponent {
|
||||
confirm: 'content-manager.popUpWarning.button.confirm',
|
||||
}}
|
||||
popUpWarningType="danger"
|
||||
onConfirm={onSubmit}
|
||||
onConfirm={() => onSubmit(this.context)}
|
||||
/>
|
||||
<PopUpWarning
|
||||
isOpen={showWarningCancel}
|
||||
@ -1085,6 +1088,10 @@ class SettingPage extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
SettingPage.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
SettingPage.defaultProps = {
|
||||
draggedItemName: null,
|
||||
grid: [],
|
||||
|
||||
@ -58,7 +58,7 @@ module.exports = {
|
||||
|
||||
const searchInt = Object.keys(this._attributes)
|
||||
.filter(attribute => attribute !== this.primaryKey && !associations.includes(attribute))
|
||||
.filter(attribute => ['integer', 'decimal', 'float'].includes(this._attributes[attribute].type));
|
||||
.filter(attribute => ['integer','biginteger', 'decimal', 'float'].includes(this._attributes[attribute].type));
|
||||
|
||||
const searchBool = Object.keys(this._attributes)
|
||||
.filter(attribute => attribute !== this.primaryKey && !associations.includes(attribute))
|
||||
@ -129,11 +129,11 @@ module.exports = {
|
||||
|
||||
const searchNoText = Object.keys(this._attributes)
|
||||
.filter(attribute => attribute !== this.primaryKey && !associations.includes(attribute))
|
||||
.filter(attribute => !['string', 'text', 'boolean', 'integer', 'decimal', 'float'].includes(this._attributes[attribute].type));
|
||||
.filter(attribute => !['string', 'text', 'boolean', 'integer', 'biginteger', 'decimal', 'float'].includes(this._attributes[attribute].type));
|
||||
|
||||
const searchInt = Object.keys(this._attributes)
|
||||
.filter(attribute => attribute !== this.primaryKey && !associations.includes(attribute))
|
||||
.filter(attribute => ['integer', 'decimal', 'float'].includes(this._attributes[attribute].type));
|
||||
.filter(attribute => ['integer', 'biginteger', 'decimal', 'float'].includes(this._attributes[attribute].type));
|
||||
|
||||
const searchBool = Object.keys(this._attributes)
|
||||
.filter(attribute => attribute !== this.primaryKey && !associations.includes(attribute))
|
||||
|
||||
@ -22,6 +22,7 @@ module.exports = {
|
||||
const $or = Object.keys(this.attributes).reduce((acc, curr) => {
|
||||
switch (this.attributes[curr].type) {
|
||||
case 'integer':
|
||||
case 'biginteger':
|
||||
case 'float':
|
||||
case 'decimal':
|
||||
if (!_.isNaN(_.toNumber(params.search))) {
|
||||
@ -57,6 +58,7 @@ module.exports = {
|
||||
const $or = Object.keys(this.attributes).reduce((acc, curr) => {
|
||||
switch (this.attributes[curr].type) {
|
||||
case 'integer':
|
||||
case 'biginteger':
|
||||
case 'float':
|
||||
case 'decimal':
|
||||
if (!_.isNaN(_.toNumber(params.search))) {
|
||||
|
||||
@ -52,4 +52,4 @@
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,6 +42,7 @@ class AttributeRow extends React.Component {
|
||||
string: IcoString,
|
||||
text: IcoText,
|
||||
integer: IcoNumber,
|
||||
biginteger: IcoNumber,
|
||||
float: IcoNumber,
|
||||
decimal: IcoNumber,
|
||||
email: IcoEmail,
|
||||
@ -56,6 +57,7 @@ class AttributeRow extends React.Component {
|
||||
handleEdit = () => this.props.onEditAttribute(this.props.row.name);
|
||||
|
||||
handleDelete = () => {
|
||||
this.context.emitEvent('willDeleteFieldOfContentType');
|
||||
this.props.onDelete(this.props.row.name);
|
||||
this.setState({ showWarning: false });
|
||||
};
|
||||
@ -136,6 +138,10 @@ class AttributeRow extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
AttributeRow.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
AttributeRow.propTypes = {
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
onEditAttribute: PropTypes.func.isRequired,
|
||||
|
||||
@ -17,6 +17,9 @@ import styles from './styles.scss';
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
class ContentHeader extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
||||
handleEdit = () => {
|
||||
// Send event.
|
||||
this.context.emitEvent('willEditNameOfContentType');
|
||||
// Open modal.
|
||||
router.push(this.props.editPath);
|
||||
}
|
||||
|
||||
@ -41,6 +44,7 @@ class ContentHeader extends React.Component { // eslint-disable-line react/prefe
|
||||
renderContentHeader = () => {
|
||||
const description = isEmpty(this.props.description) ? '' : <FormattedMessage id={this.props.description} defaultMessage='{description}' values={{ description: this.props.description}} />;
|
||||
const buttons = this.props.addButtons ? this.renderButtonContainer() : '';
|
||||
|
||||
return (
|
||||
<div className={styles.contentHeader} style={this.props.styles}>
|
||||
<div>
|
||||
@ -74,6 +78,10 @@ class ContentHeader extends React.Component { // eslint-disable-line react/prefe
|
||||
}
|
||||
}
|
||||
|
||||
ContentHeader.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
ContentHeader.propTypes = {
|
||||
addButtons: PropTypes.bool,
|
||||
buttonsContent: PropTypes.array,
|
||||
|
||||
@ -15,6 +15,12 @@ import styles from './styles.scss';
|
||||
|
||||
class PopUpHeaderNavLink extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
||||
handleGoTo = () => {
|
||||
if (this.props.routePath.indexOf('#create::contentType') !== -1 && this.props.name === 'advancedSettings') {
|
||||
this.context.emitEvent('didSelectContentTypeSettings');
|
||||
} else if (this.props.routePath.indexOf('#create') !== -1 && this.props.routePath.indexOf('::attribute') !== -1 && this.props.name === 'advancedSettings') {
|
||||
this.context.emitEvent('didSelectContentTypeFieldSettings');
|
||||
}
|
||||
|
||||
router.push(replace(this.props.routePath, this.props.nameToReplace, this.props.name));
|
||||
}
|
||||
|
||||
@ -29,6 +35,10 @@ class PopUpHeaderNavLink extends React.Component { // eslint-disable-line react/
|
||||
}
|
||||
}
|
||||
|
||||
PopUpHeaderNavLink.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
PopUpHeaderNavLink.propTypes = {
|
||||
message: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
|
||||
@ -12,7 +12,7 @@ import { Switch, Route } from 'react-router-dom';
|
||||
|
||||
import pluginId from '../../pluginId';
|
||||
|
||||
// import HomePage from '../HomePage';
|
||||
import HomePage from '../HomePage';
|
||||
import ModelPage from '../ModelPage';
|
||||
import NotFoundPage from '../NotFoundPage';
|
||||
|
||||
@ -27,10 +27,10 @@ import makeSelectApp from './selectors';
|
||||
import styles from './styles.scss';
|
||||
|
||||
const ROUTES = [
|
||||
// {
|
||||
// component: HomePage,
|
||||
// to: `/plugins/${pluginId}`,
|
||||
// },
|
||||
{
|
||||
component: HomePage,
|
||||
to: `/plugins/${pluginId}`,
|
||||
},
|
||||
{
|
||||
component: ModelPage,
|
||||
to: `/plugins/${pluginId}/models/:modelName`,
|
||||
|
||||
@ -259,6 +259,7 @@
|
||||
"value": "integer",
|
||||
"items": [
|
||||
{ "name": "content-type-builder.form.attribute.item.number.type.integer", "value": "integer" },
|
||||
{ "name": "content-type-builder.form.attribute.item.number.type.biginteger", "value": "biginteger" },
|
||||
{ "name": "content-type-builder.form.attribute.item.number.type.float", "value": "float" },
|
||||
{ "name": "content-type-builder.form.attribute.item.number.type.decimal", "value": "decimal" }
|
||||
],
|
||||
|
||||
@ -373,6 +373,7 @@ export class Form extends React.Component { // eslint-disable-line react/prefer-
|
||||
}
|
||||
|
||||
goToAttributeTypeView = (attributeType) => {
|
||||
this.context.emitEvent('didSelectContentTypeFieldType', { type: attributeType });
|
||||
const settings = attributeType === 'relation' ? 'defineRelation' : 'baseSettings';
|
||||
router.push(`${this.props.routePath}#create${this.props.modelName}::attribute${attributeType}::${settings}`);
|
||||
}
|
||||
@ -461,6 +462,12 @@ export class Form extends React.Component { // eslint-disable-line react/prefer-
|
||||
let dataSucces = null;
|
||||
let cbFail;
|
||||
|
||||
if (redirectToChoose) {
|
||||
this.context.emitEvent('willAddMoreFieldToContentType');
|
||||
} else if (this.props.hash.indexOf('#edit') !== -1 && this.props.hash.indexOf('::attribute') !== -1) {
|
||||
this.context.emitEvent('willEditFieldOfContentType');
|
||||
}
|
||||
|
||||
switch (true) {
|
||||
case includes(hashArray[0], '#edit'): {
|
||||
// Check if the user is editing the attribute
|
||||
@ -682,6 +689,7 @@ export class Form extends React.Component { // eslint-disable-line react/prefer-
|
||||
}
|
||||
|
||||
Form.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
plugins: PropTypes.object,
|
||||
updatePlugin: PropTypes.func,
|
||||
};
|
||||
|
||||
@ -39,6 +39,8 @@ export function* editContentType(action) {
|
||||
const response = yield call(request, requestUrl, opts, true);
|
||||
|
||||
if (response.ok) {
|
||||
action.context.emitEvent('didEditNameOfContentType');
|
||||
|
||||
yield put(contentTypeActionSucceeded());
|
||||
yield put(unsetButtonLoading());
|
||||
|
||||
|
||||
@ -35,7 +35,7 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
|
||||
const title = availableNumber > 1 ? `${pluginId}.table.contentType.title.plural`
|
||||
: `${pluginId}.table.contentType.title.singular`;
|
||||
|
||||
const renderViewContent = availableNumber === 0 ?
|
||||
const renderViewContent = availableNumber === 0 ?
|
||||
<EmptyContentTypeView handleButtonClick={() => {}} />
|
||||
: (
|
||||
<TableList
|
||||
|
||||
@ -46,6 +46,9 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
|
||||
if (storeData.getIsModelTemporary()) {
|
||||
strapi.notification.info('content-type-builder.notification.info.contentType.creating.notSaved');
|
||||
} else {
|
||||
// Send event.
|
||||
this.context.emitEvent('willCreateContentType');
|
||||
// Open CT modal.
|
||||
this.toggleModal();
|
||||
}
|
||||
}
|
||||
@ -108,6 +111,7 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
|
||||
}
|
||||
|
||||
HomePage.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
plugins: PropTypes.object,
|
||||
updatePlugin: PropTypes.func,
|
||||
};
|
||||
|
||||
@ -46,7 +46,7 @@ import styles from './styles.scss';
|
||||
|
||||
// Array of attributes that the ctb can handle at the moment
|
||||
const availableAttributes = Object.keys(forms.attribute);
|
||||
availableAttributes.push('integer', 'decimal', 'float');
|
||||
availableAttributes.push('integer', 'biginteger', 'decimal', 'float');
|
||||
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable react/jsx-wrap-multilines */
|
||||
@ -149,7 +149,7 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
const attributeToRemove = get(model, ['attributes', index]);
|
||||
const parallelAttributeIndex = attributeToRemove.name === attributeToRemove.params.key ?
|
||||
-1 : findIndex(model.attributes, (attr) => attr.params.key === attributeName);
|
||||
|
||||
|
||||
this.props.deleteAttribute(index, this.props.match.params.modelName, parallelAttributeIndex !== -1);
|
||||
}
|
||||
|
||||
@ -169,6 +169,7 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
|
||||
switch (attribute.params.type) {
|
||||
case 'integer':
|
||||
case 'biginteger':
|
||||
case 'float':
|
||||
case 'decimal':
|
||||
attributeType = 'number';
|
||||
@ -321,6 +322,7 @@ export class ModelPage extends React.Component { // eslint-disable-line react/pr
|
||||
}
|
||||
|
||||
ModelPage.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
plugins: PropTypes.object,
|
||||
updatePlugin: PropTypes.func,
|
||||
};
|
||||
|
||||
@ -107,15 +107,22 @@ export function* submitChanges(action) {
|
||||
set(body, 'plugin', pluginModel);
|
||||
}
|
||||
|
||||
const { emitEvent } = action.context;
|
||||
const method = modelName === body.name ? 'POST' : 'PUT';
|
||||
const baseUrl = '/content-type-builder/models/';
|
||||
const requestUrl = method === 'POST' ? baseUrl : `${baseUrl}${body.name}`;
|
||||
const opts = { method, body };
|
||||
|
||||
// Send event.
|
||||
yield put(emitEvent('willSaveContentType'));
|
||||
|
||||
// Send request to save the content type.
|
||||
const response = yield call(request, requestUrl, opts, true);
|
||||
|
||||
if (response.ok) {
|
||||
if (method === 'POST') {
|
||||
storeData.clearAppStorage();
|
||||
yield put(emitEvent('didSaveContentType'));
|
||||
yield put(temporaryContentTypePosted(size(get(body, 'attributes'))));
|
||||
yield put(postContentTypeSucceeded());
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
"attribute.enumeration": "Enumeration",
|
||||
"attribute.float": "Float",
|
||||
"attribute.integer": "integer",
|
||||
"attribute.biginteger": "big integer",
|
||||
"attribute.json": "JSON",
|
||||
"attribute.media": "Media",
|
||||
"attribute.password": "Password",
|
||||
@ -54,6 +55,7 @@
|
||||
"form.attribute.item.number.type.decimal": "decimal (ex: 2.22)",
|
||||
"form.attribute.item.number.type.float": "float (ex: 3.33333333)",
|
||||
"form.attribute.item.number.type.integer": "integer (ex: 10)",
|
||||
"form.attribute.item.number.type.biginteger": "big integer (ex: 123456789)",
|
||||
"form.attribute.item.requiredField": "Required field",
|
||||
"form.attribute.item.requiredField.description": "You won't be able to create an entry if this field is empty",
|
||||
"form.attribute.item.settings.name": "Settings",
|
||||
|
||||
@ -89,6 +89,8 @@ module.exports = {
|
||||
|
||||
if (_.isEmpty(strapi.api)) {
|
||||
strapi.emit('didCreateFirstContentType');
|
||||
} else {
|
||||
strapi.emit('didCreateContentType');
|
||||
}
|
||||
|
||||
ctx.send({ ok: true });
|
||||
|
||||
@ -51,4 +51,4 @@
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@ -88,10 +88,10 @@ class Manager {
|
||||
case 'checkbox':
|
||||
case 'boolean':
|
||||
case 'date':
|
||||
case 'bigint':
|
||||
case 'decimal':
|
||||
case 'float':
|
||||
case 'integer':
|
||||
case 'biginteger':
|
||||
case 'number':
|
||||
return 4;
|
||||
case 'json':
|
||||
@ -250,4 +250,4 @@ class Manager {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Manager;
|
||||
module.exports = Manager;
|
||||
|
||||
@ -514,7 +514,7 @@ module.exports = {
|
||||
const getter =
|
||||
currentAssociation.plugin !== undefined
|
||||
? ['plugins', currentAssociation.plugin, 'models', name, 'attributes']
|
||||
: ['models', name, 'attributes'];
|
||||
: ['models', name.toLowerCase(), 'attributes'];
|
||||
const associationAttributes = _.get(strapi, getter);
|
||||
const associationSchema = this.generateAssociationSchema(associationAttributes, getter);
|
||||
|
||||
@ -1313,6 +1313,7 @@ module.exports = {
|
||||
case 'double':
|
||||
return 'number';
|
||||
case 'integer':
|
||||
case 'biginteger':
|
||||
case 'long':
|
||||
return 'integer';
|
||||
default:
|
||||
|
||||
@ -30,6 +30,20 @@ module.exports = async cb => {
|
||||
return _.startsWith(node_module, 'strapi-provider-email') || _.startsWith(node_module, 'strapi-email');
|
||||
});
|
||||
|
||||
node_modules.filter((node_module) => {
|
||||
return node_module.startsWith('@');
|
||||
})
|
||||
.forEach((orga) => {
|
||||
const node_modules = fs.readdirSync(path.join(basePath, 'node_modules', orga));
|
||||
|
||||
node_modules.forEach((node_module) => {
|
||||
// DEPRECATED strapi-email-* will be remove in next version
|
||||
if (_.startsWith(node_module, 'strapi-provider-email') || _.startsWith(node_module, 'strapi-email')) {
|
||||
emails.push(`${orga}/${node_module}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// mount all providers to get configs
|
||||
_.forEach(emails, (node_module) => {
|
||||
strapi.plugins.email.config.providers.push(
|
||||
|
||||
@ -50,4 +50,4 @@
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
"apollo-server-koa": "^2.0.7",
|
||||
"dataloader": "^1.4.0",
|
||||
"glob": "^7.1.3",
|
||||
"graphql": "^14.0.2",
|
||||
"graphql": "^14.1.0",
|
||||
"graphql-depth-limit": "^1.1.0",
|
||||
"graphql-playground-middleware-koa": "^1.6.4",
|
||||
"graphql-tools": "^3.1.1",
|
||||
@ -54,4 +54,4 @@
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,9 @@ module.exports = {
|
||||
convertToParams: (params, primaryKey) => {
|
||||
return Object.keys(params).reduce((acc, current) => {
|
||||
return Object.assign(acc, {
|
||||
[`${primaryKey === current ? '' : '_'}${current}`]: params[current],
|
||||
[`${
|
||||
primaryKey === current || "id" === current ? "" : "_"
|
||||
}${current}`]: params[current]
|
||||
});
|
||||
}, {});
|
||||
},
|
||||
|
||||
@ -41,6 +41,7 @@ module.exports = {
|
||||
type = 'Boolean';
|
||||
break;
|
||||
case 'integer':
|
||||
case 'biginteger':
|
||||
type = 'Int';
|
||||
break;
|
||||
case 'decimal':
|
||||
|
||||
@ -124,11 +124,12 @@ export function languagesFetchSucceeded(appLanguages, listLanguages) {
|
||||
}
|
||||
|
||||
|
||||
export function editSettings(newSettings, endPoint) {
|
||||
export function editSettings(newSettings, endPoint, context) {
|
||||
return {
|
||||
type: EDIT_SETTINGS,
|
||||
newSettings,
|
||||
endPoint,
|
||||
context
|
||||
};
|
||||
}
|
||||
|
||||
@ -230,11 +231,12 @@ export function databasesFetchSucceeded(listDatabases, availableDatabases) {
|
||||
};
|
||||
}
|
||||
|
||||
export function newDatabasePost(endPoint, data) {
|
||||
export function newDatabasePost(endPoint, data, context) {
|
||||
return {
|
||||
type: NEW_DATABASE_POST,
|
||||
endPoint,
|
||||
data,
|
||||
context
|
||||
};
|
||||
}
|
||||
|
||||
@ -284,11 +286,12 @@ export function specificDatabaseFetchSucceeded(db) {
|
||||
};
|
||||
}
|
||||
|
||||
export function databaseEdit(data, apiUrl) {
|
||||
export function databaseEdit(data, apiUrl, context) {
|
||||
return {
|
||||
type: DATABASE_EDIT,
|
||||
data,
|
||||
apiUrl,
|
||||
context
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -147,7 +147,7 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
|
||||
|
||||
if (isEmpty(formErrors)) {
|
||||
// this.props.setErrors([]);
|
||||
this.props.newDatabasePost(this.props.match.params.env, newData);
|
||||
this.props.newDatabasePost(this.props.match.params.env, newData, this.context);
|
||||
} else {
|
||||
this.props.setErrors(formErrors);
|
||||
}
|
||||
@ -192,7 +192,7 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
|
||||
const defaultLanguageArray = formatLanguageLocale(target.id);
|
||||
|
||||
// Edit the new config
|
||||
this.props.editSettings({ 'language.defaultLocale': join(defaultLanguageArray, '_') }, 'i18n');
|
||||
this.props.editSettings({ 'language.defaultLocale': join(defaultLanguageArray, '_') }, 'i18n', this.context);
|
||||
}
|
||||
|
||||
handleFetch(props) {
|
||||
@ -260,7 +260,7 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
|
||||
|
||||
if (isEmpty(body)) return strapi.notification.info('settings-manager.strapi.notification.info.settingsEqual');
|
||||
if (isEmpty(formErrors)) {
|
||||
this.props.editSettings(body, apiUrl);
|
||||
this.props.editSettings(body, apiUrl, this.context);
|
||||
} else {
|
||||
this.props.setErrors(formErrors);
|
||||
}
|
||||
@ -278,7 +278,7 @@ export class HomePage extends React.Component { // eslint-disable-line react/pre
|
||||
|
||||
|
||||
if (isEmpty(formErrors)) {
|
||||
this.props.databaseEdit(body, apiUrl);
|
||||
this.props.databaseEdit(body, apiUrl, this.context);
|
||||
} else {
|
||||
this.props.setErrors(formErrors);
|
||||
}
|
||||
@ -535,6 +535,10 @@ function mapDispatchToProps(dispatch) {
|
||||
);
|
||||
}
|
||||
|
||||
HomePage.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
HomePage.propTypes = {
|
||||
cancelChanges: PropTypes.func.isRequired,
|
||||
changeDefaultLanguage: PropTypes.func.isRequired,
|
||||
|
||||
@ -54,14 +54,19 @@ export function* editDatabase(action) {
|
||||
body,
|
||||
};
|
||||
const requestUrl = `/settings-manager/configurations/databases/${action.apiUrl}`;
|
||||
|
||||
|
||||
action.context.emitEvent('willEditDatabaseSettings');
|
||||
|
||||
const resp = yield call(request, requestUrl, opts, true);
|
||||
|
||||
if (resp.ok) {
|
||||
action.context.emitEvent('didEditDatabaseSettings');
|
||||
|
||||
strapi.notification.success('settings-manager.strapi.notification.success.databaseEdit');
|
||||
yield put(databaseActionSucceeded());
|
||||
}
|
||||
} catch(error) {
|
||||
action.context.emitEvent('didNotEditDatabaseSettings');
|
||||
const formErrors = map(error.response.payload.message, err => ({ target: err.target, errors: map(err.messages, mess => ({ id: `settings-manager.${mess.id}`})) }));
|
||||
|
||||
yield put(databaseActionError(formErrors));
|
||||
@ -190,14 +195,17 @@ export function* postDatabase(action) {
|
||||
method: 'POST',
|
||||
body,
|
||||
};
|
||||
action.context.emitEvent('willAddDatabaseSettings');
|
||||
const requestUrl = `/settings-manager/configurations/databases/${action.endPoint}`;
|
||||
const resp = yield call(request, requestUrl, opts, true);
|
||||
|
||||
if (resp.ok) {
|
||||
action.context.emitEvent('didAddDatabaseSettings');
|
||||
yield put(databaseActionSucceeded());
|
||||
strapi.notification.success('settings-manager.strapi.notification.success.databaseAdd');
|
||||
}
|
||||
} catch(error) {
|
||||
action.context.emitEvent('didNotAddDatabaseSettings')
|
||||
const formErrors = map(error.response.payload.message, (err) => {
|
||||
const target = err.target ? replace(err.target, err.target.split('.')[2], '${name}') : 'database.connections.${name}.name';
|
||||
return (
|
||||
@ -219,14 +227,19 @@ export function* settingsEdit(action) {
|
||||
body: action.newSettings,
|
||||
method: 'PUT',
|
||||
};
|
||||
|
||||
action.context.emitEvent('willEditSettings', { category : action.endPoint });
|
||||
|
||||
const requestUrl = `/settings-manager/configurations/${action.endPoint}`;
|
||||
const resp = yield call(request, requestUrl, opts, true);
|
||||
|
||||
if (resp.ok) {
|
||||
action.context.emitEvent('didEditSettings', { category : action.endPoint });
|
||||
yield put(editSettingsSucceeded());
|
||||
strapi.notification.success('settings-manager.strapi.notification.success.settingsEdit');
|
||||
}
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
action.context.emitEvent('didNotEditSettings', { error });
|
||||
strapi.notification.error('settings-manager.strapi.notification.error');
|
||||
} finally {
|
||||
yield put(unsetLoader());
|
||||
|
||||
@ -53,4 +53,4 @@
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +72,7 @@ class EditForm extends React.Component {
|
||||
name="sizeLimit"
|
||||
onChange={this.props.onChange}
|
||||
type="number"
|
||||
step={0.01}
|
||||
value={get(this.props.modifiedData, 'sizeLimit', 1) / 1000}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -40,7 +40,7 @@ export function onCancel() {
|
||||
|
||||
export function onChange({ target }) {
|
||||
const keys = ['modifiedData'].concat(target.name.split('.'));
|
||||
const value = target.name === 'sizeLimit' ? parseInt(target.value, 10) * 1000 : target.value;
|
||||
const value = target.name === 'sizeLimit' ? Number(target.value) * 1000 : target.value;
|
||||
|
||||
return {
|
||||
type: ON_CHANGE,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
// import { LOCATION_CHANGE } from 'react-router-redux';
|
||||
import { Map } from 'immutable';
|
||||
import { isEmpty } from 'lodash';
|
||||
import { isEmpty, get, isObject } from 'lodash';
|
||||
import {
|
||||
all,
|
||||
call,
|
||||
@ -13,6 +13,9 @@ import {
|
||||
} from 'redux-saga/effects';
|
||||
import request from 'utils/request';
|
||||
|
||||
import pluginId from '../../pluginId';
|
||||
|
||||
|
||||
import {
|
||||
deleteSuccess,
|
||||
dropSuccess,
|
||||
@ -81,8 +84,11 @@ function* uploadFiles(action) {
|
||||
strapi.notification.success({ id: 'upload.notification.dropFiles.success', values: { number: newFiles.length } });
|
||||
}
|
||||
|
||||
} catch(err) {
|
||||
strapi.notification.error('notification.error');
|
||||
} catch(error) {
|
||||
let message = get(error, ['response', 'payload', 'message', '0', 'messages', '0']);
|
||||
if (isObject(message)) message = {...message, id: `${pluginId}.${message.id}`};
|
||||
|
||||
strapi.notification.error(message || 'notification.error');
|
||||
} finally {
|
||||
yield put(unsetLoading());
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "تم تحديث الإعدادات",
|
||||
"notification.delete.success": "تم حذف الملف",
|
||||
"notification.dropFile.success": "تم تحميل ملفك",
|
||||
"notification.dropFiles.success": "{number} ملفات تم تحميلها"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} ملفات تم تحميلها",
|
||||
"Upload.status.sizeLimit": "{file} أكبر من حجم الحد الذي تمت تهيئته",
|
||||
"Upload.status.disabled" : "تم تعطيل تحميل الملف"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "Die Einstellungen wurden aktualisiert",
|
||||
"notification.delete.success": "Die Datei wurde gelöscht",
|
||||
"notification.dropFile.success": "Deine Datei wurde hochgeladen",
|
||||
"notification.dropFiles.success": "{number} Dateien wurden hochgeladen"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} Dateien wurden hochgeladen",
|
||||
"Upload.status.sizeLimit": "{file} ist größer als die konfigurierte Begrenzungsgröße",
|
||||
"Upload.status.disabled" : "Das Hochladen von Dateien ist deaktiviert"
|
||||
}
|
||||
|
||||
@ -21,8 +21,13 @@
|
||||
"PluginInputFile.link": "browse",
|
||||
"PluginInputFile.loading": "Your files are being uploaded...",
|
||||
"PluginInputFile.text": "Drag & drop your files into this area or {link} from a file to upload",
|
||||
"Upload.status.empty": "Files are empty",
|
||||
"Upload.status.disabled": "File upload is disabled",
|
||||
"Upload.status.sizeLimit": "{file} file is bigger than limit size!",
|
||||
"notification.config.success": "The settings has been updated",
|
||||
"notification.delete.success": "The file has been deleted",
|
||||
"notification.dropFile.success": "Your file has been uploaded",
|
||||
"notification.dropFiles.success": "{number} files have been uploaded"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} files have been uploaded",
|
||||
"Upload.status.sizeLimit": "{file} is bigger than configured limit size",
|
||||
"Upload.status.disabled" : "File upload is disabled"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "Se ha actualizado la configuración",
|
||||
"notification.delete.success": "El archivo ha sido borrado",
|
||||
"notification.dropFile.success": "Su archivo esta cargado",
|
||||
"notification.dropFiles.success": "{number} archivos han sido cargados"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} archivos han sido cargados",
|
||||
"Upload.status.sizeLimit": "{archivo} es más grande que el tamaño límite configurado",
|
||||
"Upload.status.disabled" : "La carga de archivos está deshabilitada"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "Les paramètres ont été mis à jour.",
|
||||
"notification.delete.success": "Le fichier a bien été supprimé",
|
||||
"notification.dropFile.success": "Votre fichier a été téléchargé",
|
||||
"notification.dropFiles.success": "{number} fichiers ont été téléchargées"
|
||||
"notification.dropFiles.success": "{number} fichiers ont été téléchargées",
|
||||
"Upload.status.sizeLimit": "{file} est plus grand que la taille limite configurée",
|
||||
"Upload.status.disabled" : "Le téléchargement de fichier est désactivé"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "Le impostazioni è stato aggiornato",
|
||||
"notification.delete.success": "Il file è stato cancellato",
|
||||
"notification.dropFile.success": "Il file è stato caricato",
|
||||
"notification.dropFiles.success": "{number} file sono stati caricati"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} file sono stati caricati",
|
||||
"Upload.status.sizeLimit": "{file} è più grande della dimensione limite configurata",
|
||||
"Upload.status.disabled" : "Il caricamento del file è disabilitato"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "設定が更新されました",
|
||||
"notification.delete.success": "ファイルが削除されました",
|
||||
"notification.dropFile.success": "ファイルがアップロードされました",
|
||||
"notification.dropFiles.success": "{number}個のファイルがアップロードされました"
|
||||
}
|
||||
"notification.dropFiles.success": "{number}個のファイルがアップロードされました",
|
||||
"Upload.status.sizeLimit": "{file}は設定された制限サイズよりも大きいです",
|
||||
"Upload.status.disabled" : "ファイルのアップロードが無効になっています"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "설정을 업데이트했습니다.",
|
||||
"notification.delete.success": "파일을 삭제했습니다.",
|
||||
"notification.dropFile.success": "파일을 업로드했습니다.",
|
||||
"notification.dropFiles.success": "{number}개의 파일을 업로드 했습니다."
|
||||
}
|
||||
"notification.dropFiles.success": "{number}개의 파일을 업로드 했습니다.",
|
||||
"Upload.status.sizeLimit": "{file}이 (가) 구성된 제한 크기보다 큽니다.",
|
||||
"Upload.status.disabled" : "파일 업로드가 사용 중지되었습니다."
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "De instellingen zijn geüpdatet",
|
||||
"notification.delete.success": "Het bestand is verwijderd",
|
||||
"notification.dropFile.success": "Je bestand is geüpload",
|
||||
"notification.dropFiles.success": "{number} bestanden zijn geüpload"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} bestanden zijn geüpload",
|
||||
"Upload.status.sizeLimit": "{file} is groter dan de geconfigureerde limietgrootte",
|
||||
"Upload.status.disabled" : "Bestand uploaden is uitgeschakeld"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "Ustawienia zostały zaaktualizowane",
|
||||
"notification.delete.success": "Plik został usunięty",
|
||||
"notification.dropFile.success": "Plik został przesłany",
|
||||
"notification.dropFiles.success": "{number} plików zostało przesłanych"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} plików zostało przesłanych",
|
||||
"Upload.status.sizeLimit": "{plik} jest większy niż skonfigurowany rozmiar limitu",
|
||||
"Upload.status.disabled" : "Przesyłanie plików jest wyłączone"
|
||||
}
|
||||
|
||||
@ -25,4 +25,4 @@
|
||||
"notification.delete.success": "O arquivo foi removido",
|
||||
"notification.dropFile.success": "Seu arquivo foi enviado com sucesso",
|
||||
"notification.dropFiles.success": "{number} arquivos foram enviados com sucesso"
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "As configurações foram actualizadas",
|
||||
"notification.delete.success": "O arquivo foi apagado",
|
||||
"notification.dropFile.success": "Seu arquivo foi transferido com sucesso",
|
||||
"notification.dropFiles.success": "{number} arquivos foram transferidos com sucesso"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} arquivos foram transferidos com sucesso",
|
||||
"Upload.status.sizeLimit": "{file} é maior que o tamanho limite configurado",
|
||||
"Upload.status.disabled" : "O upload de arquivos está desativado"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "Настройки обновлены",
|
||||
"notification.delete.success": "Файл удален",
|
||||
"notification.dropFile.success": "Ваш файл загружен",
|
||||
"notification.dropFiles.success": "Файлов загружено: {number}"
|
||||
"notification.dropFiles.success": "Файлов загружено: {number}",
|
||||
"Upload.status.sizeLimit": "{file} больше настроенного предельного размера",
|
||||
"Upload.status.disabled" : "Загрузка файла отключена"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "Ayarlar güncellendi.",
|
||||
"notification.delete.success": "Dosya silindi",
|
||||
"notification.dropFile.success": "Dosyanız yüklendi",
|
||||
"notification.dropFiles.success": "{number} dosyalar yüklendi"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} dosyalar yüklendi",
|
||||
"Upload.status.sizeLimit": "{file} yapılandırılmış sınır boyutundan daha büyük",
|
||||
"Upload.status.disabled" : "Dosya yükleme devre dışı"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "设置已更新",
|
||||
"notification.delete.success": "文件已被删除",
|
||||
"notification.dropFile.success": "您的文件已上传",
|
||||
"notification.dropFiles.success": "{number} 个文件已上传"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} 个文件已上传",
|
||||
"Upload.status.sizeLimit": "{file}大于配置的限制大小",
|
||||
"Upload.status.disabled" : "文件上传已禁用"
|
||||
}
|
||||
|
||||
@ -24,5 +24,7 @@
|
||||
"notification.config.success": "設定已更新",
|
||||
"notification.delete.success": "檔案已刪除",
|
||||
"notification.dropFile.success": "您的檔案已上傳",
|
||||
"notification.dropFiles.success": "{number} 個檔案已上傳"
|
||||
}
|
||||
"notification.dropFiles.success": "{number} 個檔案已上傳",
|
||||
"Upload.status.sizeLimit": "{file}大於配置的限制大小",
|
||||
"Upload.status.disabled" : "文件上傳已禁用"
|
||||
}
|
||||
|
||||
@ -30,6 +30,20 @@ module.exports = async cb => {
|
||||
return _.startsWith(node_module, 'strapi-provider-upload') || _.startsWith(node_module, 'strapi-upload');
|
||||
});
|
||||
|
||||
node_modules.filter((node_module) => {
|
||||
return node_module.startsWith('@');
|
||||
})
|
||||
.forEach((orga) => {
|
||||
const node_modules = fs.readdirSync(path.join(basePath, 'node_modules', orga));
|
||||
|
||||
node_modules.forEach((node_module) => {
|
||||
// DEPRECATED strapi-email-* will be remove in next version
|
||||
if (_.startsWith(node_module, 'strapi-provider-upload') || _.startsWith(node_module, 'strapi-upload')) {
|
||||
uploads.push(`${orga}/${node_module}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// mount all providers to get configs
|
||||
_.forEach(uploads, (node_module) => {
|
||||
strapi.plugins.upload.config.providers.push(
|
||||
|
||||
@ -19,7 +19,6 @@ module.exports = {
|
||||
|
||||
// Verify if the file upload is enable.
|
||||
if (config.enabled === false) {
|
||||
strapi.log.error('File upload is disabled');
|
||||
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Upload.status.disabled' }] }] : 'File upload is disabled');
|
||||
}
|
||||
|
||||
@ -28,7 +27,7 @@ module.exports = {
|
||||
const { files = {} } = ctx.request.body.files;
|
||||
|
||||
if (_.isEmpty(files)) {
|
||||
return ctx.send(true);
|
||||
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Upload.status.empty' }] }] : 'Files are empty');
|
||||
}
|
||||
|
||||
// Transform stream files to buffer
|
||||
|
||||
@ -46,4 +46,4 @@
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,7 +136,12 @@ class ListRow extends React.Component { // eslint-disable-line react/prefer-stat
|
||||
return;
|
||||
}
|
||||
case 'providers':
|
||||
this.context.emitEvent('willEditAuthenticationProvider');
|
||||
|
||||
return this.context.setDataToEdit(this.props.item.name);
|
||||
case 'email-templates':
|
||||
this.context.emitEvent('willEditEmailTemplates');
|
||||
|
||||
return this.context.setDataToEdit(this.props.item.name);
|
||||
default:
|
||||
return;
|
||||
@ -165,6 +170,7 @@ class ListRow extends React.Component { // eslint-disable-line react/prefer-stat
|
||||
}
|
||||
|
||||
ListRow.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
setDataToEdit: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
|
||||
@ -218,9 +218,10 @@ export function setShouldDisplayPolicieshint() {
|
||||
};
|
||||
}
|
||||
|
||||
export function submit() {
|
||||
export function submit(context) {
|
||||
return {
|
||||
type: SUBMIT,
|
||||
context,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -103,7 +103,7 @@ export class EditPage extends React.Component { // eslint-disable-line react/pre
|
||||
return this.props.setErrors([{ name: 'name', errors: [{ id: 'users-permissions.EditPage.form.roles.name.error' }] }]);
|
||||
}
|
||||
|
||||
this.props.submit();
|
||||
this.props.submit(this.context);
|
||||
}
|
||||
|
||||
showLoaderForm = () => {
|
||||
@ -159,7 +159,10 @@ export class EditPage extends React.Component { // eslint-disable-line react/pre
|
||||
number: size(get(this.props.editPage, ['modifiedData', 'users'])),
|
||||
},
|
||||
}}
|
||||
onClickAdd={this.props.onClickAdd}
|
||||
onClickAdd={() => {
|
||||
this.context.emitEvent('didAssociateUserToRole');
|
||||
this.props.onClickAdd();
|
||||
}}
|
||||
onClickDelete={this.props.onClickDelete}
|
||||
name="users"
|
||||
type="text"
|
||||
@ -268,6 +271,10 @@ EditPage.childContextTypes = {
|
||||
resetShouldDisplayPoliciesHint: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
EditPage.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
EditPage.propTypes = {
|
||||
addUser: PropTypes.func.isRequired,
|
||||
editPage: PropTypes.object.isRequired,
|
||||
|
||||
@ -85,7 +85,7 @@ export function* roleGet(action) {
|
||||
}
|
||||
}
|
||||
|
||||
export function* submit() {
|
||||
export function* submit(action) {
|
||||
try {
|
||||
const actionType = yield select(makeSelectActionType());
|
||||
const body = yield select(makeSelectModifiedData());
|
||||
@ -97,7 +97,11 @@ export function* submit() {
|
||||
|
||||
const requestURL = actionType === 'POST' ? '/users-permissions/roles' : `/users-permissions/roles/${roleId}`;
|
||||
const response = yield call(request, requestURL, opts);
|
||||
|
||||
|
||||
if (actionType === 'POST') {
|
||||
action.context.emitEvent('didCreateRole');
|
||||
}
|
||||
|
||||
if (response.ok) {
|
||||
yield put(submitSucceeded());
|
||||
}
|
||||
|
||||
@ -107,10 +107,11 @@ export function setFormErrors(formErrors) {
|
||||
};
|
||||
}
|
||||
|
||||
export function submit(endPoint) {
|
||||
export function submit(endPoint, context) {
|
||||
return {
|
||||
type: SUBMIT,
|
||||
endPoint,
|
||||
context,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -115,6 +115,7 @@ export class HomePage extends React.Component {
|
||||
handleButtonClick = () => {
|
||||
// TODO change open modal URL
|
||||
if (this.props.match.params.settingType === 'roles') {
|
||||
this.context.emitEvent('willCreateRole');
|
||||
this.props.history.push(`${this.props.location.pathname}/create`);
|
||||
} else if (this.props.match.params.settingType === 'providers') {
|
||||
this.props.history.push(`${this.props.location.pathname}#add::${this.props.match.params.settingType}`);
|
||||
@ -133,7 +134,7 @@ export class HomePage extends React.Component {
|
||||
|
||||
if (isEmpty(formErrors)) {
|
||||
this.setState({ showModalEdit: false });
|
||||
this.props.submit(this.props.match.params.settingType);
|
||||
this.props.submit(this.props.match.params.settingType, this.context);
|
||||
} else {
|
||||
this.props.setFormErrors(formErrors);
|
||||
}
|
||||
@ -176,7 +177,7 @@ export class HomePage extends React.Component {
|
||||
showLoaders = () => {
|
||||
const { data, isLoading, modifiedData } = this.props;
|
||||
const isAdvanded = this.getEndPoint() === 'advanced';
|
||||
|
||||
|
||||
return isLoading && get(data, this.getEndPoint()) === undefined && !isAdvanded || isLoading && isAdvanded && get(modifiedData, this.getEndPoint()) === undefined;
|
||||
}
|
||||
|
||||
@ -197,7 +198,7 @@ export class HomePage extends React.Component {
|
||||
values={get(modifiedData, this.getEndPoint(), {})}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={(e) => e.preventDefault()}>
|
||||
@ -232,6 +233,10 @@ HomePage.childContextTypes = {
|
||||
unsetDataToEdit: PropTypes.func,
|
||||
};
|
||||
|
||||
HomePage.contextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
};
|
||||
|
||||
HomePage.defaultProps = {};
|
||||
|
||||
HomePage.propTypes = {
|
||||
|
||||
@ -75,6 +75,13 @@ export function* submitData(action) {
|
||||
const opts = { method: 'PUT', body: (action.endPoint === 'advanced') ? get(body, ['advanced', 'settings'], {}) : body };
|
||||
|
||||
yield call(request, `/users-permissions/${action.endPoint}`, opts);
|
||||
|
||||
if (action.endPoint === 'email-templates') {
|
||||
action.context.emitEvent('didEditEmailTemplates');
|
||||
} else if (action.endPoint === 'providers') {
|
||||
action.context.emitEvent('didEditAuthenticationProvider');
|
||||
}
|
||||
|
||||
yield put(submitSucceeded());
|
||||
strapi.notification.success('users-permissions.notification.success.submit');
|
||||
} catch(error) {
|
||||
|
||||
@ -56,7 +56,7 @@ module.exports = {
|
||||
if (!user) {
|
||||
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.invalid' }] }] : 'Identifier or password invalid.');
|
||||
}
|
||||
|
||||
|
||||
if (_.get(await store.get({key: 'advanced'}), 'email_confirmation') && !user.confirmed) {
|
||||
return ctx.badRequest(null, ctx.request.admin ? [{ messages: [{ id: 'Auth.form.error.confirmed' }] }] : 'Your account email is not confirmed.');
|
||||
}
|
||||
@ -196,7 +196,7 @@ module.exports = {
|
||||
settings.object = await strapi.plugins['users-permissions'].services.userspermissions.template(settings.object, {
|
||||
USER: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken', 'role', 'provider'])
|
||||
});
|
||||
|
||||
|
||||
try {
|
||||
// Send an email to the user.
|
||||
await strapi.plugins['email'].services.email.send({
|
||||
@ -332,7 +332,7 @@ module.exports = {
|
||||
}
|
||||
|
||||
ctx.send({
|
||||
jwt,
|
||||
jwt: !settings.email_confirmation ? jwt : undefined,
|
||||
user: _.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken'])
|
||||
});
|
||||
} catch(err) {
|
||||
@ -345,7 +345,12 @@ module.exports = {
|
||||
emailConfirmation: async (ctx) => {
|
||||
const params = ctx.query;
|
||||
|
||||
const user = await strapi.plugins['users-permissions'].services.jwt.verify(params.confirmation);
|
||||
let user;
|
||||
try {
|
||||
user = await strapi.plugins['users-permissions'].services.jwt.verify(params.confirmation);
|
||||
} catch (err) {
|
||||
return ctx.badRequest(null, 'This confirmation token is invalid.');
|
||||
}
|
||||
|
||||
await strapi.plugins['users-permissions'].services.user.edit(_.pick(user, ['_id', 'id']), {confirmed: true});
|
||||
|
||||
|
||||
@ -56,4 +56,4 @@
|
||||
"npm": ">= 6.0.0"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user