Some bugfixes. The configuration management should be now OK.

This commit is contained in:
Tomas Bures 2018-04-22 20:29:35 +02:00
parent c12efeb97f
commit e97415c237
9 changed files with 209 additions and 142 deletions

View file

@ -3,18 +3,35 @@
import React, {Component} from "react";
import PropTypes from "prop-types";
import {translate} from "react-i18next";
import {NavButton, requiresAuthenticatedUser, Title, Toolbar, withPageHelpers} from "../../lib/page";
import {Button as FormButton, ButtonRow, Dropdown, Form, FormSendMethod, InputField, withForm} from "../../lib/form";
import {withAsyncErrorHandler, withErrorHandling} from "../../lib/error-handling";
import {
NavButton,
requiresAuthenticatedUser,
Title,
Toolbar,
withPageHelpers
} from "../../lib/page";
import {
Button as FormButton,
ButtonRow,
Dropdown,
Form,
FormSendMethod,
InputField,
withForm
} from "../../lib/form";
import {withErrorHandling} from "../../lib/error-handling";
import {DeleteModalDialog} from "../../lib/modals";
import interoperableErrors from "../../../../shared/interoperable-errors";
import styles from "./CUD.scss";
import {DragDropContext} from "react-dnd";
import HTML5Backend from "react-dnd-html5-backend";
import TouchBackend from "react-dnd-touch-backend";
import SortableTree from "react-sortable-tree";
import {ActionLink, Button, Icon} from "../../lib/bootstrap-components";
import {
ActionLink,
Button,
Icon
} from "../../lib/bootstrap-components";
import {getRuleHelpers} from "./helpers";
import RuleSettingsPane from "./RuleSettingsPane";
@ -88,18 +105,6 @@ export default class CUD extends Component {
return tree;
}
@withAsyncErrorHandler
async loadFormValues() {
await this.getFormValuesFromURL(`/rest/segments/${this.props.list.id}/${this.props.entity.id}`, data => {
data.rootRuleType = data.settings.rootRule.type;
data.selectedRule = null; // Validation errors of the selected rule are attached to this which makes sure we don't submit the segment if the opened rule has errors
this.setState({
rulesTree: this.getTreeFromRules(data.settings.rootRule.rules)
});
});
}
componentDidMount() {
if (this.props.entity) {
this.setState({
@ -167,7 +172,14 @@ export default class CUD extends Component {
if (submitSuccessful) {
if (stay) {
await this.loadFormValues();
await this.getFormValuesFromURL(`/rest/segments/${this.props.list.id}/${this.props.entity.id}`, data => {
data.rootRuleType = data.settings.rootRule.type;
data.selectedRule = null; // Validation errors of the selected rule are attached to this which makes sure we don't submit the segment if the opened rule has errors
this.setState({
rulesTree: this.getTreeFromRules(data.settings.rootRule.rules)
});
});
this.enableForm();
this.setFormStatusMessage('success', t('Segment saved'));
} else {

View file

@ -1,12 +1,33 @@
'use strict';
import React, { Component } from 'react';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { translate, Trans } from 'react-i18next';
import {requiresAuthenticatedUser, withPageHelpers, Title, NavButton} from '../../lib/page'
import { withForm, Form, FormSendMethod, InputField, TextArea, Dropdown, ACEEditor, ButtonRow, Button } from '../../lib/form';
import { withErrorHandling, withAsyncErrorHandler } from '../../lib/error-handling';
import { validateNamespace, NamespaceSelect } from '../../lib/namespace';
import {
Trans,
translate
} from 'react-i18next';
import {
NavButton,
requiresAuthenticatedUser,
Title,
withPageHelpers
} from '../../lib/page'
import {
ACEEditor,
Button,
ButtonRow,
Dropdown,
Form,
FormSendMethod,
InputField,
TextArea,
withForm
} from '../../lib/form';
import {withErrorHandling} from '../../lib/error-handling';
import {
NamespaceSelect,
validateNamespace
} from '../../lib/namespace';
import {DeleteModalDialog} from "../../lib/modals";
import mailtrainConfig from 'mailtrainConfig';
import 'brace/mode/javascript';
@ -33,11 +54,6 @@ export default class CUD extends Component {
entity: PropTypes.object
}
@withAsyncErrorHandler
async loadFormValues() {
await this.getFormValuesFromURL(`/rest/report-templates/${this.props.entity.id}`);
}
componentDidMount() {
if (this.props.entity) {
this.getFormValuesFromEntity(this.props.entity);
@ -259,7 +275,7 @@ export default class CUD extends Component {
if (submitSuccessful) {
if (stay) {
await this.loadFormValues();
await this.getFormValuesFromURL(`/rest/report-templates/${this.props.entity.id}`);
this.enableForm();
this.setFormStatusMessage('success', t('Report template saved'));
} else {

View file

@ -99,13 +99,15 @@ export default class CUD extends Component {
localValidateFormValues(state) {
const t = this.props.t;
const typeKey = state.getIn(['mailer_type', 'value']);
if (!state.getIn(['name', 'value'])) {
state.setIn(['name', 'error'], t('Name must not be empty'));
} else {
state.setIn(['name', 'error'], null);
}
if (!state.getIn(['mailer_type', 'value'])) {
if (!typeKey) {
state.setIn(['mailer_type', 'error'], t('Mailer type must be selected'));
} else {
state.setIn(['mailer_type', 'error'], null);
@ -117,8 +119,11 @@ export default class CUD extends Component {
state.setIn(['verp_hostname', 'error'], null);
}
validateNamespace(t, state);
if (typeKey) {
this.mailerTypes[typeKey].validate(state);
}
}
async submitHandler() {
@ -157,7 +162,6 @@ export default class CUD extends Component {
const canDelete = isEdit && this.props.entity.permissions.includes('delete') && this.props.entity.id !== getSystemSendConfigurationId();
const typeKey = this.getFormValue('mailer_type');
console.log(typeKey);
let mailerForm = null;
if (typeKey) {
mailerForm = this.mailerTypes[typeKey].getForm(this);
@ -210,12 +214,6 @@ export default class CUD extends Component {
:
<Trans><p>VERP bounce handling server is not enabled. Modify your server configuration file and restart server to enable it.</p></Trans>
}
<InputField id="from_email" label={t('Default "from" email')}/>
<CheckBox id="from_email_overridable" text={t('Overridable')}/>
<InputField id="from_name" label={t('Default "from" name')}/>
<CheckBox id="from_name_overridable" text={t('Overridable')}/>
<InputField id="subject" label={t('Subject')}/>
<CheckBox id="subject_overridable" text={t('Overridable')}/>
</Fieldset>
<ButtonRow>

View file

@ -18,65 +18,6 @@ export const mailerTypesOrder = [
MailerType.AWS_SES
];
function getInitCommon() {
return {
maxConnections: '5',
throttling: '',
logTransactions: false
};
}
function getInitGenericSMTP() {
return {
...getInitCommon(),
smtpHostname: '',
smtpPort: '',
smtpEncryption: 'NONE',
smtpUseAuth: false,
smtpUser: '',
smtpPassword: '',
smtpAllowSelfSigned: false,
smtpMaxMessages: '100'
};
}
function afterLoadCommon(data) {
data.maxConnections = data.mailer_settings.maxConnections;
data.throttling = data.mailer_settings.throttling || '';
data.logTransaction = data.mailer_settings.logTransactions;
}
function afterLoadGenericSMTP(data) {
afterLoadCommon(data);
data.smtpHostname = data.mailer_settings.hostname;
data.smtpPort = data.mailer_settings.port || '';
data.smtpEncryption = data.mailer_settings.encryption;
data.smtpUseAuth = data.mailer_settings.useAuth;
data.smtpUser = data.mailer_settings.user;
data.smtpPassword = data.mailer_settings.password;
data.smtpAllowSelfSigned = data.mailer_settings.allowSelfSigned;
data.smtpMaxMessages = data.mailer_settings.maxMessages;
}
function beforeSaveCommon(data) {
data.mailer_settings = {};
data.mailer_settings.maxConnections = Number(data.maxConnections);
data.mailer_settings.throttling = Number(data.throttling);
data.mailer_settings.logTransactions = data.logTransaction;
}
function beforeSaveGenericSMTP(data) {
beforeSaveCommon(data);
data.mailer_settings.hostname = data.smtpHostname;
data.mailer_settings.port = Number(data.smtpPort);
data.mailer_settings.encryption = data.smtpEncryption;
data.mailer_settings.useAuth = data.smtpUseAuth;
data.mailer_settings.user = data.smtpUser;
data.mailer_settings.password = data.smtpPassword;
data.mailer_settings.allowSelfSigned = data.smtpAllowSelfSigned;
data.mailer_settings.maxMessages = Number(data.smtpMaxMessages);
}
export function getMailerTypes(t) {
const mailerTypes = {};
@ -99,6 +40,88 @@ export function getMailerTypes(t) {
}
}
function validateNumber(state, field, label, emptyAllowed = false) {
const value = state.getIn([field, 'value']);
if (typeof value === 'string' && value.trim() === '' && !emptyAllowed) { // After load, the numerical values can be still numbers
state.setIn([field, 'error'], t(`${label} must not be empty`));
} else if (isNaN(value)) {
state.setIn([field, 'error'], t(`${label} must be a number`));
} else {
state.setIn([field, 'error'], null);
}
}
function getInitCommon() {
return {
maxConnections: '5',
throttling: '',
logTransactions: false
};
}
function getInitGenericSMTP() {
return {
...getInitCommon(),
smtpHostname: '',
smtpPort: '',
smtpEncryption: 'NONE',
smtpUseAuth: false,
smtpUser: '',
smtpPassword: '',
smtpAllowSelfSigned: false,
smtpMaxMessages: '100'
};
}
function afterLoadCommon(data) {
data.maxConnections = data.mailer_settings.maxConnections;
data.throttling = data.mailer_settings.throttling || '';
data.logTransactions = data.mailer_settings.logTransactions;
}
function afterLoadGenericSMTP(data) {
afterLoadCommon(data);
data.smtpHostname = data.mailer_settings.hostname;
data.smtpPort = data.mailer_settings.port || '';
data.smtpEncryption = data.mailer_settings.encryption;
data.smtpUseAuth = data.mailer_settings.useAuth;
data.smtpUser = data.mailer_settings.user;
data.smtpPassword = data.mailer_settings.password;
data.smtpAllowSelfSigned = data.mailer_settings.allowSelfSigned;
data.smtpMaxMessages = data.mailer_settings.maxMessages;
}
function beforeSaveCommon(data) {
data.mailer_settings = {};
data.mailer_settings.maxConnections = Number(data.maxConnections);
data.mailer_settings.throttling = Number(data.throttling);
data.mailer_settings.logTransactions = data.logTransactions;
}
function beforeSaveGenericSMTP(data) {
beforeSaveCommon(data);
data.mailer_settings.hostname = data.smtpHostname;
data.mailer_settings.port = Number(data.smtpPort);
data.mailer_settings.encryption = data.smtpEncryption;
data.mailer_settings.useAuth = data.smtpUseAuth;
data.mailer_settings.user = data.smtpUser;
data.mailer_settings.password = data.smtpPassword;
data.mailer_settings.allowSelfSigned = data.smtpAllowSelfSigned;
data.mailer_settings.maxMessages = Number(data.smtpMaxMessages);
}
function validateCommon(state) {
validateNumber(state, 'maxConnections', 'Max connections');
validateNumber(state, 'throttling', 'Throttling', true);
}
function validateGenericSMTP(state) {
validateCommon(state);
validateNumber(state, 'smtpPort', 'Port', true);
validateNumber(state, 'smtpMaxMessages', 'Max messages');
}
const typeOptions = [
{ key: MailerType.GENERIC_SMTP, label: t('Generic SMTP')},
{ key: MailerType.ZONE_MTA, label: t('Zone MTA')},
@ -153,6 +176,9 @@ export function getMailerTypes(t) {
},
afterTypeChange: mutState => {
initFieldsIfMissing(mutState, MailerType.GENERIC_SMTP);
},
validate: state => {
validateGenericSMTP(state);
}
};
@ -212,6 +238,9 @@ export function getMailerTypes(t) {
},
afterTypeChange: mutState => {
initFieldsIfMissing(mutState, MailerType.ZONE_MTA);
},
validate: state => {
validateGenericSMTP(state);
}
};
@ -251,6 +280,9 @@ export function getMailerTypes(t) {
},
afterTypeChange: mutState => {
initFieldsIfMissing(mutState, MailerType.AWS_SES);
},
validate: state => {
validateCommon(state);
}
};

View file

@ -1,29 +1,27 @@
'use strict';
import React, { Component } from 'react';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { translate, Trans } from 'react-i18next';
import {requiresAuthenticatedUser, withPageHelpers, Title, NavButton} from '../lib/page';
import {
withForm,
Trans,
translate
} from 'react-i18next';
import {
requiresAuthenticatedUser,
Title,
withPageHelpers
} from '../lib/page';
import {
Button,
ButtonRow,
Fieldset,
Form,
FormSendMethod,
InputField,
TextArea,
TableSelect,
ButtonRow,
Button,
Dropdown,
StaticField,
CheckBox,
Fieldset
withForm
} from '../lib/form';
import { withErrorHandling } from '../lib/error-handling';
import { DeleteModalDialog } from '../lib/modals';
import { validateNamespace, NamespaceSelect } from '../lib/namespace';
import { UnsubscriptionMode } from '../../../shared/lists';
import styles from "../lib/styles.scss";
import mailtrainConfig from 'mailtrainConfig';
import {withErrorHandling} from '../lib/error-handling';
@translate()
@withForm
@ -42,7 +40,7 @@ export default class Update extends Component {
static propTypes = {
entity: PropTypes.object
}
componentDidMount() {
this.getFormValuesFromEntity(this.props.entity);
}
@ -60,7 +58,9 @@ export default class Update extends Component {
const submitSuccessful = await this.validateAndSendFormValuesToURL(FormSendMethod.PUT, '/rest/settings');
if (submitSuccessful) {
this.navigateToWithFlashMessage('/settings', 'success', t('Global settings saved'));
await this.getFormValuesFromURL('/rest/settings');
this.enableForm();
this.setFormStatusMessage('success', t('Global settings saved'));
} else {
this.enableForm();
this.setFormStatusMessage('warning', t('There are errors in the form. Please fix them and submit again.'));

View file

@ -52,11 +52,6 @@ export default class CUD extends Component {
entity: PropTypes.object
}
@withAsyncErrorHandler
async loadFormValues() {
await this.getFormValuesFromURL(`/rest/templates/${this.props.entity.id}`);
}
componentDidMount() {
if (this.props.entity) {
this.getFormValuesFromEntity(this.props.entity);

View file

@ -1,16 +1,36 @@
'use strict';
import React, { Component } from 'react';
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { translate, Trans } from 'react-i18next';
import {requiresAuthenticatedUser, withPageHelpers, Title, NavButton} from '../../lib/page'
import { withForm, Form, FormSendMethod, InputField, TextArea, Dropdown, ButtonRow, Button } from '../../lib/form';
import { withErrorHandling, withAsyncErrorHandler } from '../../lib/error-handling';
import { validateNamespace, NamespaceSelect } from '../../lib/namespace';
import {translate} from 'react-i18next';
import {
NavButton,
requiresAuthenticatedUser,
Title,
withPageHelpers
} from '../../lib/page'
import {
Button,
ButtonRow,
Dropdown,
Form,
FormSendMethod,
InputField,
TextArea,
withForm
} from '../../lib/form';
import {withErrorHandling} from '../../lib/error-handling';
import {
NamespaceSelect,
validateNamespace
} from '../../lib/namespace';
import {DeleteModalDialog} from "../../lib/modals";
import { versafix } from "../../../../shared/mosaico-templates";
import { getTemplateTypes, getTemplateTypesOrder } from "./helpers";
import {versafix} from "../../../../shared/mosaico-templates";
import {
getTemplateTypes,
getTemplateTypesOrder
} from "./helpers";
@translate()
@withForm
@ -42,13 +62,6 @@ export default class CUD extends Component {
entity: PropTypes.object
}
@withAsyncErrorHandler
async loadFormValues() {
await this.getFormValuesFromURL(`/rest/mosaico-templates/${this.props.entity.id}`, data => {
this.templateTypes[data.type].afterLoad(data);
});
}
componentDidMount() {
if (this.props.entity) {
this.getFormValuesFromEntity(this.props.entity, data => {
@ -126,7 +139,9 @@ export default class CUD extends Component {
if (submitSuccessful) {
if (stay) {
await this.loadFormValues();
await this.getFormValuesFromURL(`/rest/mosaico-templates/${this.props.entity.id}`, data => {
this.templateTypes[data.type].afterLoad(data);
});
this.enableForm();
this.setFormStatusMessage('success', t('Mosaico template saved'));
} else {

View file

@ -11,12 +11,12 @@ const {MailerType, getSystemSendConfigurationId} = require('../shared/send-confi
const allowedKeys = new Set(['name', 'description', 'from_email', 'from_email_overridable', 'from_name', 'from_name_overridable', 'subject', 'subject_overridable', 'verp_hostname', 'mailer_type', 'mailer_settings', 'namespace']);
const allowedMailerTypes = new Set(Object.values(MailerType));
function hash(entity) {
return hasher.hash(filterObject(entity, allowedKeys));
}
async function listDTAjax(context, params) {
return await dtHelpers.ajaxListWithPermissions(
context,
@ -47,7 +47,7 @@ async function getById(context, id, withPermissions = true) {
async function _validateAndPreprocess(tx, entity, isCreate) {
await namespaceHelpers.validateEntity(tx, entity);
enforce(entity.mailer_type >= 0 && entity.mailer_type < MailerType.MAX, 'Unknown mailer type');
enforce(allowedMailerTypes.has(entity.mailer_type), 'Unknown mailer type');
entity.mailer_settings = JSON.stringify(entity.mailer_settings);
}

View file

@ -3,8 +3,7 @@
const MailerType = {
GENERIC_SMTP: 'generic_smtp',
ZONE_MTA: 'zone_mta',
AWS_SES: 'aws_ses',
MAX: 3
AWS_SES: 'aws_ses'
};
function getSystemSendConfigurationId() {