diff --git a/.travis.yml b/.travis.yml index b88bf9ac6b..1c49cb3da5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ before_script: script: - npm run lint - npm run doc + - npm run test:front cache: directories: diff --git a/packages/strapi-admin/admin/src/components/NotificationsContainer/styles.scss b/packages/strapi-admin/admin/src/components/NotificationsContainer/styles.scss index 3f1e7b5041..5234d3d63d 100644 --- a/packages/strapi-admin/admin/src/components/NotificationsContainer/styles.scss +++ b/packages/strapi-admin/admin/src/components/NotificationsContainer/styles.scss @@ -2,7 +2,7 @@ @import '../../styles/variables/variables'; .notificationsContainer { /* stylelint-disable */ - position: absolute; + position: fixed; top: 72px; left: 240px; right: 0; diff --git a/packages/strapi-admin/admin/src/containers/AdminPage/index.js b/packages/strapi-admin/admin/src/containers/AdminPage/index.js index 11129c6e3a..e97749581a 100644 --- a/packages/strapi-admin/admin/src/containers/AdminPage/index.js +++ b/packages/strapi-admin/admin/src/containers/AdminPage/index.js @@ -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, }; diff --git a/packages/strapi-admin/admin/src/translations/en.json b/packages/strapi-admin/admin/src/translations/en.json index a8b948af6b..d797736785 100644 --- a/packages/strapi-admin/admin/src/translations/en.json +++ b/packages/strapi-admin/admin/src/translations/en.json @@ -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.", diff --git a/packages/strapi-admin/admin/src/translations/fr.json b/packages/strapi-admin/admin/src/translations/fr.json index 111f504adb..da9a869f29 100644 --- a/packages/strapi-admin/admin/src/translations/fr.json +++ b/packages/strapi-admin/admin/src/translations/fr.json @@ -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.", diff --git a/packages/strapi-admin/package.json b/packages/strapi-admin/package.json index ff118fbbb2..bac4f7f828 100644 --- a/packages/strapi-admin/package.json +++ b/packages/strapi-admin/package.json @@ -55,4 +55,4 @@ "npm": ">= 6.0.0" }, "license": "MIT" -} \ No newline at end of file +} diff --git a/packages/strapi-generate-api/templates/mongoose/service.template b/packages/strapi-generate-api/templates/mongoose/service.template index afe2c1dffd..b664c76140 100644 --- a/packages/strapi-generate-api/templates/mongoose/service.template +++ b/packages/strapi-generate-api/templates/mongoose/service.template @@ -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; } diff --git a/packages/strapi-helper-plugin/lib/src/components/InputNumber/index.js b/packages/strapi-helper-plugin/lib/src/components/InputNumber/index.js index c5fa4dce0e..5aafe09b9b 100644 --- a/packages/strapi-helper-plugin/lib/src/components/InputNumber/index.js +++ b/packages/strapi-helper-plugin/lib/src/components/InputNumber/index.js @@ -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([ diff --git a/packages/strapi-helper-plugin/lib/src/components/InputNumberWithErrors/index.js b/packages/strapi-helper-plugin/lib/src/components/InputNumberWithErrors/index.js index 3598200ad4..1208407f52 100644 --- a/packages/strapi-helper-plugin/lib/src/components/InputNumberWithErrors/index.js +++ b/packages/strapi-helper-plugin/lib/src/components/InputNumberWithErrors/index.js @@ -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} /> { + 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 = ( +
+ Read the documentation +
+ ); + + 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 ) : (
-
+
@@ -42,9 +93,7 @@ class OverlayBlocker extends React.Component {

- + {button}
); @@ -65,7 +114,7 @@ class OverlayBlocker extends React.Component { } OverlayBlocker.defaultProps = { - children: '', + children: null, description: 'components.OverlayBlocker.description', icon: 'fa fa-refresh', isOpen: false, diff --git a/packages/strapi-helper-plugin/lib/src/components/OverlayBlocker/styles.scss b/packages/strapi-helper-plugin/lib/src/components/OverlayBlocker/styles.scss index e365f100cf..5cbda16d99 100644 --- a/packages/strapi-helper-plugin/lib/src/components/OverlayBlocker/styles.scss +++ b/packages/strapi-helper-plugin/lib/src/components/OverlayBlocker/styles.scss @@ -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; diff --git a/packages/strapi-helper-plugin/lib/src/components/PageFooter/index.js b/packages/strapi-helper-plugin/lib/src/components/PageFooter/index.js index 56e5df63a1..99a46577a9 100644 --- a/packages/strapi-helper-plugin/lib/src/components/PageFooter/index.js +++ b/packages/strapi-helper-plugin/lib/src/components/PageFooter/index.js @@ -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, diff --git a/packages/strapi-helper-plugin/lib/src/utils/Manager.js b/packages/strapi-helper-plugin/lib/src/utils/Manager.js index 8dfb050258..203529d566 100644 --- a/packages/strapi-helper-plugin/lib/src/utils/Manager.js +++ b/packages/strapi-helper-plugin/lib/src/utils/Manager.js @@ -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; \ No newline at end of file +module.exports = Manager; diff --git a/packages/strapi-hook-bookshelf/lib/index.js b/packages/strapi-hook-bookshelf/lib/index.js index 5b9c8231bc..a4ae66f601 100644 --- a/packages/strapi-hook-bookshelf/lib/index.js +++ b/packages/strapi-hook-bookshelf/lib/index.js @@ -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; diff --git a/packages/strapi-plugin-content-manager/admin/src/components/Edit/index.js b/packages/strapi-plugin-content-manager/admin/src/components/Edit/index.js index 7ff1746da6..592ccf6194 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/Edit/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/Edit/index.js @@ -27,7 +27,7 @@ const getInputType = (type = '') => { switch (type.toLowerCase()) { case 'boolean': return 'toggle'; - case 'bigint': + case 'biginteger': case 'decimal': case 'float': case 'integer': diff --git a/packages/strapi-plugin-content-manager/admin/src/components/FilterOptions/InputWithAutoFocus.js b/packages/strapi-plugin-content-manager/admin/src/components/FilterOptions/InputWithAutoFocus.js index 352eb7c074..286cdd873e 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/FilterOptions/InputWithAutoFocus.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/FilterOptions/InputWithAutoFocus.js @@ -20,7 +20,7 @@ const getInputType = (attrType) => { case 'datetime': return InputDate; case 'integer': - case 'bigint': + case 'biginteger': case 'decimal': case 'float': return InputNumber; diff --git a/packages/strapi-plugin-content-manager/admin/src/components/FilterOptions/filterTypes.js b/packages/strapi-plugin-content-manager/admin/src/components/FilterOptions/filterTypes.js index 50c0239173..880f7b50b8 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/FilterOptions/filterTypes.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/FilterOptions/filterTypes.js @@ -47,6 +47,7 @@ const getFilters = (type) => { }, ]; case 'integer': + case 'biginteger': case 'float': case 'decimal': case 'date': diff --git a/packages/strapi-plugin-content-manager/admin/src/components/FiltersPickWrapper/index.js b/packages/strapi-plugin-content-manager/admin/src/components/FiltersPickWrapper/index.js index 046954b0c1..247b762ad2 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/FiltersPickWrapper/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/FiltersPickWrapper/index.js @@ -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, diff --git a/packages/strapi-plugin-content-manager/admin/src/components/TableRow/index.js b/packages/strapi-plugin-content-manager/admin/src/components/TableRow/index.js index aca98a62cc..5bfe3a7bce 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/TableRow/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/TableRow/index.js @@ -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, }; diff --git a/packages/strapi-plugin-content-manager/admin/src/components/VariableDraggableAttr/index.js b/packages/strapi-plugin-content-manager/admin/src/components/VariableDraggableAttr/index.js index c9a2a836cb..4eb41b51be 100644 --- a/packages/strapi-plugin-content-manager/admin/src/components/VariableDraggableAttr/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/components/VariableDraggableAttr/index.js @@ -33,7 +33,7 @@ const getBootstrapClass = attrType => { case 'boolean': case 'toggle': case 'date': - case 'bigint': + case 'biginteger': case 'decimal': case 'float': case 'integer': diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/App/actions.js b/packages/strapi-plugin-content-manager/admin/src/containers/App/actions.js index b2b058b9a7..1634a7816b 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/App/actions.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/App/actions.js @@ -183,9 +183,10 @@ export function onReset() { }; } -export function onSubmit() { +export function onSubmit(context) { return { type: ON_SUBMIT, + context, }; } diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/App/sagas.js b/packages/strapi-plugin-content-manager/admin/src/containers/App/sagas.js index 5d40628432..7eb6098f2e 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/App/sagas.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/App/sagas.js @@ -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 diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/actions.js b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/actions.js index f2f60e0977..e6cdd68460 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/actions.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/actions.js @@ -154,9 +154,10 @@ export function setLoader() { }; } -export function submit() { +export function submit(context) { return { type: SUBMIT, + context, }; } diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/index.js b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/index.js index ac9f874c40..20e419d506 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/index.js @@ -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 ( -
  • +
  • this.context.emitEvent('willEditContentTypeLayoutFromEditView')}>
  • ); @@ -506,7 +506,7 @@ export class EditPage extends React.Component { const Component = compo.component; return ( -
  • +
  • this.context.emitEvent('willEditContentTypeFromEditView')}>
  • ); @@ -704,6 +704,7 @@ export class EditPage extends React.Component { } EditPage.contextTypes = { + emitEvent: PropTypes.func, currentEnvironment: PropTypes.string, plugins: PropTypes.object, }; diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/saga.js b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/saga.js index 8255fedc6f..a041abb301 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/saga.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/EditPage/saga.js @@ -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) => { diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/actions.js b/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/actions.js index 3728270107..7ccad68752 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/actions.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/actions.js @@ -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, }; } diff --git a/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/index.js b/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/index.js index 45853dcd3d..dcf7fa59be 100644 --- a/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/index.js +++ b/packages/strapi-plugin-content-manager/admin/src/containers/ListPage/index.js @@ -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} >
    - + this.context.emitEvent('willFilterEntries') && onToggleFilters(e)} showHideText={showFilter} id="addFilterCTA" /> {filters.map(this.renderFilter)}
    @@ -613,6 +623,7 @@ export class ListPage extends React.Component { /> {this.renderPopUpWarningDeleteAll()} { + 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)} /> 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)) diff --git a/packages/strapi-plugin-content-manager/config/queries/mongoose.js b/packages/strapi-plugin-content-manager/config/queries/mongoose.js index a3dabbdb35..f8099f6cef 100644 --- a/packages/strapi-plugin-content-manager/config/queries/mongoose.js +++ b/packages/strapi-plugin-content-manager/config/queries/mongoose.js @@ -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))) { diff --git a/packages/strapi-plugin-content-manager/package.json b/packages/strapi-plugin-content-manager/package.json index 87205bf477..e1c72123b6 100644 --- a/packages/strapi-plugin-content-manager/package.json +++ b/packages/strapi-plugin-content-manager/package.json @@ -52,4 +52,4 @@ "npm": ">= 6.0.0" }, "license": "MIT" -} \ No newline at end of file +} diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/AttributeRow/index.js b/packages/strapi-plugin-content-type-builder/admin/src/components/AttributeRow/index.js index ac8030a6fd..f976e79077 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/components/AttributeRow/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/AttributeRow/index.js @@ -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, diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/ContentHeader/index.js b/packages/strapi-plugin-content-type-builder/admin/src/components/ContentHeader/index.js index 06f882e914..10f2a8502c 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/components/ContentHeader/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/ContentHeader/index.js @@ -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) ? '' : ; const buttons = this.props.addButtons ? this.renderButtonContainer() : ''; + return (
    @@ -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, diff --git a/packages/strapi-plugin-content-type-builder/admin/src/components/PopUpHeaderNavLink/index.js b/packages/strapi-plugin-content-type-builder/admin/src/components/PopUpHeaderNavLink/index.js index c90fd23db0..bf6a75c717 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/components/PopUpHeaderNavLink/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/components/PopUpHeaderNavLink/index.js @@ -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, diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/App/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/App/index.js index a90c2fec77..20cc1ed60b 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/App/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/App/index.js @@ -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`, 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 38d1808991..e526d2b283 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 @@ -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" } ], diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/index.js index 7dbfd6e0ef..6cbf959ad4 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/index.js @@ -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, }; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/sagas.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/sagas.js index 4870e3655e..d07a1af61c 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/sagas.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/Form/sagas.js @@ -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()); diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/HomePage/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/HomePage/index.js index 9b3e534ceb..e6bd575c39 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/HomePage/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/HomePage/index.js @@ -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 ? {}} /> : ( 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, }; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/ModelPageOld/sagas.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/ModelPageOld/sagas.js index cee2a0c40a..e2af546545 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/ModelPageOld/sagas.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/ModelPageOld/sagas.js @@ -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()); diff --git a/packages/strapi-plugin-content-type-builder/admin/src/translations/en.json b/packages/strapi-plugin-content-type-builder/admin/src/translations/en.json index 9f18df4e65..2239d410f4 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/translations/en.json +++ b/packages/strapi-plugin-content-type-builder/admin/src/translations/en.json @@ -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", diff --git a/packages/strapi-plugin-content-type-builder/controllers/ContentTypeBuilder.js b/packages/strapi-plugin-content-type-builder/controllers/ContentTypeBuilder.js index e92d7be291..8c97fa36b9 100644 --- a/packages/strapi-plugin-content-type-builder/controllers/ContentTypeBuilder.js +++ b/packages/strapi-plugin-content-type-builder/controllers/ContentTypeBuilder.js @@ -89,6 +89,8 @@ module.exports = { if (_.isEmpty(strapi.api)) { strapi.emit('didCreateFirstContentType'); + } else { + strapi.emit('didCreateContentType'); } ctx.send({ ok: true }); diff --git a/packages/strapi-plugin-content-type-builder/package.json b/packages/strapi-plugin-content-type-builder/package.json index 8efdd3a40a..fcffcf23aa 100644 --- a/packages/strapi-plugin-content-type-builder/package.json +++ b/packages/strapi-plugin-content-type-builder/package.json @@ -51,4 +51,4 @@ "npm": ">= 6.0.0" }, "license": "MIT" -} \ No newline at end of file +} diff --git a/packages/strapi-plugin-content-type-builder/utils/Manager.js b/packages/strapi-plugin-content-type-builder/utils/Manager.js index 662d0e1d85..99e85579d0 100644 --- a/packages/strapi-plugin-content-type-builder/utils/Manager.js +++ b/packages/strapi-plugin-content-type-builder/utils/Manager.js @@ -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; \ No newline at end of file +module.exports = Manager; diff --git a/packages/strapi-plugin-documentation/services/Documentation.js b/packages/strapi-plugin-documentation/services/Documentation.js index 2291b70533..189aef6baa 100755 --- a/packages/strapi-plugin-documentation/services/Documentation.js +++ b/packages/strapi-plugin-documentation/services/Documentation.js @@ -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: diff --git a/packages/strapi-plugin-email/config/functions/bootstrap.js b/packages/strapi-plugin-email/config/functions/bootstrap.js index fa2b34617b..7fb453ac7d 100644 --- a/packages/strapi-plugin-email/config/functions/bootstrap.js +++ b/packages/strapi-plugin-email/config/functions/bootstrap.js @@ -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( diff --git a/packages/strapi-plugin-email/package.json b/packages/strapi-plugin-email/package.json index fd1f722587..ccefcf32f1 100644 --- a/packages/strapi-plugin-email/package.json +++ b/packages/strapi-plugin-email/package.json @@ -50,4 +50,4 @@ "npm": ">= 6.0.0" }, "license": "MIT" -} \ No newline at end of file +} diff --git a/packages/strapi-plugin-graphql/package.json b/packages/strapi-plugin-graphql/package.json index ddc2dca46a..5729cb670f 100644 --- a/packages/strapi-plugin-graphql/package.json +++ b/packages/strapi-plugin-graphql/package.json @@ -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" -} \ No newline at end of file +} diff --git a/packages/strapi-plugin-graphql/services/Query.js b/packages/strapi-plugin-graphql/services/Query.js index 21a3e93471..48332cbbe9 100644 --- a/packages/strapi-plugin-graphql/services/Query.js +++ b/packages/strapi-plugin-graphql/services/Query.js @@ -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] }); }, {}); }, diff --git a/packages/strapi-plugin-graphql/services/Types.js b/packages/strapi-plugin-graphql/services/Types.js index 2e1e5f9834..7ccfdc2abf 100644 --- a/packages/strapi-plugin-graphql/services/Types.js +++ b/packages/strapi-plugin-graphql/services/Types.js @@ -41,6 +41,7 @@ module.exports = { type = 'Boolean'; break; case 'integer': + case 'biginteger': type = 'Int'; break; case 'decimal': diff --git a/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/actions.js b/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/actions.js index 7341d351e7..6e81eab68d 100644 --- a/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/actions.js +++ b/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/actions.js @@ -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 }; } diff --git a/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/index.js b/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/index.js index 801dc6f58b..47bf915d96 100644 --- a/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/index.js +++ b/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/index.js @@ -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, diff --git a/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/sagas.js b/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/sagas.js index ee1d6b7d66..b85c39bb82 100644 --- a/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/sagas.js +++ b/packages/strapi-plugin-settings-manager/admin/src/containers/HomePage/sagas.js @@ -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()); diff --git a/packages/strapi-plugin-settings-manager/package.json b/packages/strapi-plugin-settings-manager/package.json index 7894dc912b..22c22f3674 100644 --- a/packages/strapi-plugin-settings-manager/package.json +++ b/packages/strapi-plugin-settings-manager/package.json @@ -53,4 +53,4 @@ "npm": ">= 6.0.0" }, "license": "MIT" -} \ No newline at end of file +} diff --git a/packages/strapi-plugin-upload/admin/src/components/EditForm/index.js b/packages/strapi-plugin-upload/admin/src/components/EditForm/index.js index 36277ff464..e996694374 100644 --- a/packages/strapi-plugin-upload/admin/src/components/EditForm/index.js +++ b/packages/strapi-plugin-upload/admin/src/components/EditForm/index.js @@ -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} />
    diff --git a/packages/strapi-plugin-upload/admin/src/containers/ConfigPage/actions.js b/packages/strapi-plugin-upload/admin/src/containers/ConfigPage/actions.js index ae3839a3da..e13fef19b2 100644 --- a/packages/strapi-plugin-upload/admin/src/containers/ConfigPage/actions.js +++ b/packages/strapi-plugin-upload/admin/src/containers/ConfigPage/actions.js @@ -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, diff --git a/packages/strapi-plugin-upload/admin/src/containers/HomePage/saga.js b/packages/strapi-plugin-upload/admin/src/containers/HomePage/saga.js index 76b4e67d88..374136cbe0 100644 --- a/packages/strapi-plugin-upload/admin/src/containers/HomePage/saga.js +++ b/packages/strapi-plugin-upload/admin/src/containers/HomePage/saga.js @@ -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()); } diff --git a/packages/strapi-plugin-upload/admin/src/translations/ar.json b/packages/strapi-plugin-upload/admin/src/translations/ar.json index 7b75b8be10..9bd4f7d527 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/ar.json +++ b/packages/strapi-plugin-upload/admin/src/translations/ar.json @@ -24,5 +24,7 @@ "notification.config.success": "تم تحديث الإعدادات", "notification.delete.success": "تم حذف الملف", "notification.dropFile.success": "تم تحميل ملفك", - "notification.dropFiles.success": "{number} ملفات تم تحميلها" -} \ No newline at end of file + "notification.dropFiles.success": "{number} ملفات تم تحميلها", + "Upload.status.sizeLimit": "{file} أكبر من حجم الحد الذي تمت تهيئته", + "Upload.status.disabled" : "تم تعطيل تحميل الملف" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/de.json b/packages/strapi-plugin-upload/admin/src/translations/de.json index 62508a5e84..773bdfc2d2 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/de.json +++ b/packages/strapi-plugin-upload/admin/src/translations/de.json @@ -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" -} \ No newline at end of file + "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" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/en.json b/packages/strapi-plugin-upload/admin/src/translations/en.json index 3abf220a52..862c7d0e83 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/en.json +++ b/packages/strapi-plugin-upload/admin/src/translations/en.json @@ -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" -} \ No newline at end of file + "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" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/es.json b/packages/strapi-plugin-upload/admin/src/translations/es.json index 9207269afb..2be4ac783e 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/es.json +++ b/packages/strapi-plugin-upload/admin/src/translations/es.json @@ -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" -} \ No newline at end of file + "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" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/fr.json b/packages/strapi-plugin-upload/admin/src/translations/fr.json index b0e876ab16..00012dc6f0 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/fr.json +++ b/packages/strapi-plugin-upload/admin/src/translations/fr.json @@ -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é" } diff --git a/packages/strapi-plugin-upload/admin/src/translations/it.json b/packages/strapi-plugin-upload/admin/src/translations/it.json index abffc11ad4..2ed81f53bf 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/it.json +++ b/packages/strapi-plugin-upload/admin/src/translations/it.json @@ -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" -} \ No newline at end of file + "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" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/ja.json b/packages/strapi-plugin-upload/admin/src/translations/ja.json index df8467fc0e..a7f14a98b3 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/ja.json +++ b/packages/strapi-plugin-upload/admin/src/translations/ja.json @@ -24,5 +24,7 @@ "notification.config.success": "設定が更新されました", "notification.delete.success": "ファイルが削除されました", "notification.dropFile.success": "ファイルがアップロードされました", - "notification.dropFiles.success": "{number}個のファイルがアップロードされました" -} \ No newline at end of file + "notification.dropFiles.success": "{number}個のファイルがアップロードされました", + "Upload.status.sizeLimit": "{file}は設定された制限サイズよりも大きいです", + "Upload.status.disabled" : "ファイルのアップロードが無効になっています" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/ko.json b/packages/strapi-plugin-upload/admin/src/translations/ko.json index 46f4f64663..9324c23c88 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/ko.json +++ b/packages/strapi-plugin-upload/admin/src/translations/ko.json @@ -24,5 +24,7 @@ "notification.config.success": "설정을 업데이트했습니다.", "notification.delete.success": "파일을 삭제했습니다.", "notification.dropFile.success": "파일을 업로드했습니다.", - "notification.dropFiles.success": "{number}개의 파일을 업로드 했습니다." -} \ No newline at end of file + "notification.dropFiles.success": "{number}개의 파일을 업로드 했습니다.", + "Upload.status.sizeLimit": "{file}이 (가) 구성된 제한 크기보다 큽니다.", + "Upload.status.disabled" : "파일 업로드가 사용 중지되었습니다." +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/nl.json b/packages/strapi-plugin-upload/admin/src/translations/nl.json index a77ca9f3b8..ab93b6c898 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/nl.json +++ b/packages/strapi-plugin-upload/admin/src/translations/nl.json @@ -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" -} \ No newline at end of file + "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" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/pl.json b/packages/strapi-plugin-upload/admin/src/translations/pl.json index 50ef7699a9..33f3d9017b 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/pl.json +++ b/packages/strapi-plugin-upload/admin/src/translations/pl.json @@ -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" -} \ No newline at end of file + "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" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/pt-BR.json b/packages/strapi-plugin-upload/admin/src/translations/pt-BR.json index 2a5ea2b05e..9b9a886e8c 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/pt-BR.json +++ b/packages/strapi-plugin-upload/admin/src/translations/pt-BR.json @@ -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" -} \ No newline at end of file +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/pt.json b/packages/strapi-plugin-upload/admin/src/translations/pt.json index 11f0aa6c17..a8522c1b25 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/pt.json +++ b/packages/strapi-plugin-upload/admin/src/translations/pt.json @@ -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" -} \ No newline at end of file + "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" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/ru.json b/packages/strapi-plugin-upload/admin/src/translations/ru.json index 542d2e9d16..66e63b0e06 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/ru.json +++ b/packages/strapi-plugin-upload/admin/src/translations/ru.json @@ -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" : "Загрузка файла отключена" } diff --git a/packages/strapi-plugin-upload/admin/src/translations/tr.json b/packages/strapi-plugin-upload/admin/src/translations/tr.json index c9316bafdb..6c426edab4 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/tr.json +++ b/packages/strapi-plugin-upload/admin/src/translations/tr.json @@ -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" -} \ No newline at end of file + "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ışı" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/zh-Hans.json b/packages/strapi-plugin-upload/admin/src/translations/zh-Hans.json index 7e6ece88b2..74e9bae7d2 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/zh-Hans.json +++ b/packages/strapi-plugin-upload/admin/src/translations/zh-Hans.json @@ -24,5 +24,7 @@ "notification.config.success": "设置已更新", "notification.delete.success": "文件已被删除", "notification.dropFile.success": "您的文件已上传", - "notification.dropFiles.success": "{number} 个文件已上传" -} \ No newline at end of file + "notification.dropFiles.success": "{number} 个文件已上传", + "Upload.status.sizeLimit": "{file}大于配置的限制大小", + "Upload.status.disabled" : "文件上传已禁用" +} diff --git a/packages/strapi-plugin-upload/admin/src/translations/zh.json b/packages/strapi-plugin-upload/admin/src/translations/zh.json index bc62ae9489..e864eca7ab 100644 --- a/packages/strapi-plugin-upload/admin/src/translations/zh.json +++ b/packages/strapi-plugin-upload/admin/src/translations/zh.json @@ -24,5 +24,7 @@ "notification.config.success": "設定已更新", "notification.delete.success": "檔案已刪除", "notification.dropFile.success": "您的檔案已上傳", - "notification.dropFiles.success": "{number} 個檔案已上傳" -} \ No newline at end of file + "notification.dropFiles.success": "{number} 個檔案已上傳", + "Upload.status.sizeLimit": "{file}大於配置的限制大小", + "Upload.status.disabled" : "文件上傳已禁用" +} diff --git a/packages/strapi-plugin-upload/config/functions/bootstrap.js b/packages/strapi-plugin-upload/config/functions/bootstrap.js index d437af25e2..fc8451c011 100644 --- a/packages/strapi-plugin-upload/config/functions/bootstrap.js +++ b/packages/strapi-plugin-upload/config/functions/bootstrap.js @@ -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( diff --git a/packages/strapi-plugin-upload/controllers/Upload.js b/packages/strapi-plugin-upload/controllers/Upload.js index d811b5f510..f9bd395786 100644 --- a/packages/strapi-plugin-upload/controllers/Upload.js +++ b/packages/strapi-plugin-upload/controllers/Upload.js @@ -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 diff --git a/packages/strapi-plugin-upload/package.json b/packages/strapi-plugin-upload/package.json index f256d568f3..1be374e166 100644 --- a/packages/strapi-plugin-upload/package.json +++ b/packages/strapi-plugin-upload/package.json @@ -46,4 +46,4 @@ "npm": ">= 6.0.0" }, "license": "MIT" -} \ No newline at end of file +} diff --git a/packages/strapi-plugin-users-permissions/admin/src/components/ListRow/index.js b/packages/strapi-plugin-users-permissions/admin/src/components/ListRow/index.js index 3e83c9b0c1..d5f6d4ecfd 100644 --- a/packages/strapi-plugin-users-permissions/admin/src/components/ListRow/index.js +++ b/packages/strapi-plugin-users-permissions/admin/src/components/ListRow/index.js @@ -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, }; diff --git a/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/actions.js b/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/actions.js index ed710a9d29..1e2137c4dc 100644 --- a/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/actions.js +++ b/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/actions.js @@ -218,9 +218,10 @@ export function setShouldDisplayPolicieshint() { }; } -export function submit() { +export function submit(context) { return { type: SUBMIT, + context, }; } diff --git a/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/index.js b/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/index.js index 024a44ec0b..21f3e27d81 100644 --- a/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/index.js +++ b/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/index.js @@ -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, diff --git a/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/saga.js b/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/saga.js index 5bb5105867..513a3f4d7e 100644 --- a/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/saga.js +++ b/packages/strapi-plugin-users-permissions/admin/src/containers/EditPage/saga.js @@ -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()); } diff --git a/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/actions.js b/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/actions.js index fa38c74375..ef994762c8 100644 --- a/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/actions.js +++ b/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/actions.js @@ -107,10 +107,11 @@ export function setFormErrors(formErrors) { }; } -export function submit(endPoint) { +export function submit(endPoint, context) { return { type: SUBMIT, endPoint, + context, }; } diff --git a/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/index.js b/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/index.js index b965c52062..ea8693b995 100644 --- a/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/index.js +++ b/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/index.js @@ -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 (
    e.preventDefault()}> @@ -232,6 +233,10 @@ HomePage.childContextTypes = { unsetDataToEdit: PropTypes.func, }; +HomePage.contextTypes = { + emitEvent: PropTypes.func, +}; + HomePage.defaultProps = {}; HomePage.propTypes = { diff --git a/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/saga.js b/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/saga.js index 35656cd206..1ad51f0c28 100644 --- a/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/saga.js +++ b/packages/strapi-plugin-users-permissions/admin/src/containers/HomePage/saga.js @@ -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) { diff --git a/packages/strapi-plugin-users-permissions/controllers/Auth.js b/packages/strapi-plugin-users-permissions/controllers/Auth.js index 9053eadc59..c5c61134a3 100644 --- a/packages/strapi-plugin-users-permissions/controllers/Auth.js +++ b/packages/strapi-plugin-users-permissions/controllers/Auth.js @@ -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}); diff --git a/packages/strapi-plugin-users-permissions/package.json b/packages/strapi-plugin-users-permissions/package.json index 0101ada403..c47eac6e33 100644 --- a/packages/strapi-plugin-users-permissions/package.json +++ b/packages/strapi-plugin-users-permissions/package.json @@ -56,4 +56,4 @@ "npm": ">= 6.0.0" }, "license": "MIT" -} \ No newline at end of file +}