Merge branch 'development-upstream' into Override-config
# Conflicts: # docker-entrypoint.sh
This commit is contained in:
commit
94d3f4d725
11 changed files with 188 additions and 69 deletions
|
@ -222,6 +222,7 @@ The instructions above use an automatically built Docker image on DockerHub (htt
|
|||
| LDAP_FILTER | LDAP filter |
|
||||
| LDAP_BASEDN | LDAP base DN |
|
||||
| LDAP_UIDTAG | LDAP UID tag (e.g. uid/cn/username) |
|
||||
| POOL_NAME | sets builtin Zone-MTA pool name (default: os.hostname()) |
|
||||
|
||||
If you are using docker-compose to run Mailtrain in production and need to pass your own overrides of these env-vars in a custom override like `docker-compose.override.yml`:
|
||||
|
||||
|
|
|
@ -6,22 +6,22 @@ export function getFieldTypes(t) {
|
|||
|
||||
const fieldTypes = {
|
||||
text: {
|
||||
label: t('text'),
|
||||
label: t('text')
|
||||
},
|
||||
website: {
|
||||
label: t('website'),
|
||||
label: t('website')
|
||||
},
|
||||
longtext: {
|
||||
label: t('multilineText'),
|
||||
label: t('multilineText')
|
||||
},
|
||||
gpg: {
|
||||
label: t('gpgPublicKey'),
|
||||
label: t('gpgPublicKey')
|
||||
},
|
||||
number: {
|
||||
label: t('number'),
|
||||
label: t('number')
|
||||
},
|
||||
'checkbox-grouped': {
|
||||
label: t('checkboxesFromOptionFields'),
|
||||
label: t('checkboxesFromOptionFields')
|
||||
},
|
||||
'radio-grouped': {
|
||||
label: t('radioButtonsFromOptionFields')
|
||||
|
|
|
@ -90,8 +90,8 @@ export default class CUD extends Component {
|
|||
|
||||
const tree = [];
|
||||
for (const rule of rules) {
|
||||
const ruleTypeSettings = ruleHelpers.getRuleTypeSettings(rule);
|
||||
const title = ruleTypeSettings ? ruleTypeSettings.treeLabel(rule) : this.props.t('newRule');
|
||||
const ruleTreeLabel = ruleHelpers.getTreeLabel(rule);
|
||||
const title = ruleTreeLabel || this.props.t('newRule');
|
||||
|
||||
tree.push({
|
||||
rule,
|
||||
|
|
|
@ -25,7 +25,7 @@ export default class RuleSettingsPane extends PureComponent {
|
|||
|
||||
const t = props.t;
|
||||
this.ruleHelpers = getRuleHelpers(t, props.fields);
|
||||
this.fieldTypes = getFieldTypes(t);
|
||||
this.fieldTypes = { ...getFieldTypes(t), ...this.ruleHelpers.extraFieldTypes };
|
||||
|
||||
this.state = {};
|
||||
|
||||
|
@ -54,9 +54,11 @@ export default class RuleSettingsPane extends PureComponent {
|
|||
if (!ruleHelpers.isCompositeRuleType(rule.type)) { // rule.type === null signifies primitive rule where the type has not been determined yet
|
||||
data = ruleHelpers.primitiveRuleTypesFormDataDefaults;
|
||||
|
||||
const settings = ruleHelpers.getRuleTypeSettings(rule);
|
||||
if (settings) {
|
||||
Object.assign(data, settings.getFormData(rule));
|
||||
const colDef = ruleHelpers.getColumnDef(rule.column);
|
||||
if (colDef) {
|
||||
const colType = colDef.type;
|
||||
const settings = ruleHelpers.primitiveRuleTypes[colType][rule.type];
|
||||
Object.assign(data, settings.getFormData(rule, colDef));
|
||||
}
|
||||
|
||||
data.type = rule.type || ''; // On '', we display label "--SELECT--" in the type dropdown. Null would not be accepted by React.
|
||||
|
@ -92,8 +94,10 @@ export default class RuleSettingsPane extends PureComponent {
|
|||
if (!ruleHelpers.isCompositeRuleType(rule.type)) {
|
||||
rule.column = this.getFormValue('column');
|
||||
|
||||
const settings = this.ruleHelpers.getRuleTypeSettings(rule);
|
||||
settings.assignRuleSettings(rule, key => this.getFormValue(key));
|
||||
const colDef = ruleHelpers.getColumnDef(rule.column);
|
||||
const colType = colDef.type;
|
||||
const settings = ruleHelpers.primitiveRuleTypes[colType][rule.type];
|
||||
settings.assignRuleSettings(rule, key => this.getFormValue(key), colDef);
|
||||
}
|
||||
|
||||
this.props.onChange(false);
|
||||
|
@ -118,11 +122,12 @@ export default class RuleSettingsPane extends PureComponent {
|
|||
|
||||
const column = state.getIn(['column', 'value']);
|
||||
if (column) {
|
||||
const colType = ruleHelpers.getColumnType(column);
|
||||
const colDef = ruleHelpers.getColumnDef(column);
|
||||
|
||||
if (ruleType) {
|
||||
const colType = colDef.type;
|
||||
const settings = ruleHelpers.primitiveRuleTypes[colType][ruleType];
|
||||
settings.validate(state);
|
||||
settings.validate(state, colDef);
|
||||
}
|
||||
} else {
|
||||
state.setIn(['column', 'error'], t('fieldMustBeSelected'));
|
||||
|
@ -138,9 +143,10 @@ export default class RuleSettingsPane extends PureComponent {
|
|||
const column = mutStateData.getIn(['column', 'value']);
|
||||
|
||||
if (column) {
|
||||
const colType = ruleHelpers.getColumnType(column);
|
||||
const colDef = ruleHelpers.getColumnDef(column);
|
||||
|
||||
if (type) {
|
||||
const colType = colDef.type;
|
||||
const settings = ruleHelpers.primitiveRuleTypes[colType][type];
|
||||
if (!settings) {
|
||||
// The existing rule type does not fit the newly changed column. This resets the rule type chooser to "--- Select ---"
|
||||
|
@ -187,8 +193,9 @@ export default class RuleSettingsPane extends PureComponent {
|
|||
|
||||
const ruleColumn = this.getFormValue('column');
|
||||
if (ruleColumn) {
|
||||
const colType = ruleHelpers.getColumnType(ruleColumn);
|
||||
if (colType) {
|
||||
const colDef = ruleHelpers.getColumnDef(ruleColumn);
|
||||
if (colDef) {
|
||||
const colType = colDef.type;
|
||||
const ruleTypeOptions = ruleHelpers.getPrimitiveRuleTypeOptions(colType);
|
||||
ruleTypeOptions.unshift({ key: '', label: t('select-1')});
|
||||
|
||||
|
@ -197,7 +204,7 @@ export default class RuleSettingsPane extends PureComponent {
|
|||
|
||||
const ruleType = this.getFormValue('type');
|
||||
if (ruleType) {
|
||||
ruleSettings = ruleHelpers.primitiveRuleTypes[colType][ruleType].getForm();
|
||||
ruleSettings = ruleHelpers.primitiveRuleTypes[colType][ruleType].getForm(colDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -214,16 +214,23 @@ export function getRuleHelpers(t, fields) {
|
|||
}
|
||||
};
|
||||
|
||||
ruleHelpers.primitiveRuleTypes['dropdown-static'] = {
|
||||
eq: {
|
||||
dropdownLabel: t('equalTo'),
|
||||
treeLabel: rule => t('valueInColumnColNameIsEqualToValue', {colName: ruleHelpers.getColumnName(rule.column), value: rule.value}),
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const stringValueSettings = allowEmpty => ({
|
||||
getForm: () => <InputField id="value" label={t('value')} />,
|
||||
getFormData: rule => ({
|
||||
getForm: fldDef => <InputField id="value" label={t('value')} />,
|
||||
getFormData: (rule, fldDef) => ({
|
||||
value: rule.value
|
||||
}),
|
||||
assignRuleSettings: (rule, getter) => {
|
||||
assignRuleSettings: (rule, getter, fldDef) => {
|
||||
rule.value = getter('value');
|
||||
},
|
||||
validate: state => {
|
||||
validate: (state, fldDef) => {
|
||||
if (!allowEmpty && !state.getIn(['value', 'value'])) {
|
||||
state.setIn(['value', 'error'], t('valueMustNotBeEmpty'));
|
||||
} else {
|
||||
|
@ -233,14 +240,14 @@ export function getRuleHelpers(t, fields) {
|
|||
});
|
||||
|
||||
const numberValueSettings = {
|
||||
getForm: () => <InputField id="value" label={t('value')} />,
|
||||
getFormData: rule => ({
|
||||
getForm: fldDef => <InputField id="value" label={t('value')} />,
|
||||
getFormData: (rule, fldDef) => ({
|
||||
value: rule.value.toString()
|
||||
}),
|
||||
assignRuleSettings: (rule, getter) => {
|
||||
assignRuleSettings: (rule, getter, fldDef) => {
|
||||
rule.value = parseInt(getter('value'));
|
||||
},
|
||||
validate: state => {
|
||||
validate: (state, fldDef) => {
|
||||
const value = state.getIn(['value', 'value']).trim();
|
||||
if (value === '') {
|
||||
state.setIn(['value', 'error'], t('valueMustNotBeEmpty'));
|
||||
|
@ -253,14 +260,14 @@ export function getRuleHelpers(t, fields) {
|
|||
};
|
||||
|
||||
const birthdayValueSettings = {
|
||||
getForm: () => <DatePicker id="birthday" label={t('date')} birthday />,
|
||||
getFormData: rule => ({
|
||||
getForm: fldDef => <DatePicker id="birthday" label={t('date')} birthday />,
|
||||
getFormData: (rule, fldDef) => ({
|
||||
birthday: formatBirthday(DateFormat.INTL, rule.value)
|
||||
}),
|
||||
assignRuleSettings: (rule, getter) => {
|
||||
assignRuleSettings: (rule, getter, fldDef) => {
|
||||
rule.value = parseBirthday(DateFormat.INTL, getter('birthday')).toISOString();
|
||||
},
|
||||
validate: state => {
|
||||
validate: (state, fldDef) => {
|
||||
const value = state.getIn(['birthday', 'value']);
|
||||
const date = parseBirthday(DateFormat.INTL, value);
|
||||
if (!value) {
|
||||
|
@ -274,14 +281,14 @@ export function getRuleHelpers(t, fields) {
|
|||
};
|
||||
|
||||
const dateValueSettings = {
|
||||
getForm: () => <DatePicker id="date" label={t('date')} />,
|
||||
getFormData: rule => ({
|
||||
getForm: fldDef => <DatePicker id="date" label={t('date')} />,
|
||||
getFormData: (rule, fldDef) => ({
|
||||
date: formatDate(DateFormat.INTL, rule.value)
|
||||
}),
|
||||
assignRuleSettings: (rule, getter) => {
|
||||
assignRuleSettings: (rule, getter, fldDef) => {
|
||||
rule.value = parseDate(DateFormat.INTL, getter('date')).toISOString();
|
||||
},
|
||||
validate: state => {
|
||||
validate: (state, fldDef) => {
|
||||
const value = state.getIn(['date', 'value']);
|
||||
const date = parseDate(DateFormat.INTL, value);
|
||||
if (!value) {
|
||||
|
@ -295,7 +302,7 @@ export function getRuleHelpers(t, fields) {
|
|||
};
|
||||
|
||||
const dateRelativeValueSettings = {
|
||||
getForm: () =>
|
||||
getForm: fldDef =>
|
||||
<div>
|
||||
<InputField id="daysValue" label={t('numberOfDays')}/>
|
||||
<Dropdown id="direction" label={t('beforeAfter')} options={[
|
||||
|
@ -303,15 +310,15 @@ export function getRuleHelpers(t, fields) {
|
|||
{ key: 'after', label: t('afterCurrentDate') }
|
||||
]}/>
|
||||
</div>,
|
||||
getFormData: rule => ({
|
||||
getFormData: (rule, fldDef) => ({
|
||||
daysValue: Math.abs(rule.value).toString(),
|
||||
direction: rule.value >= 0 ? 'after' : 'before'
|
||||
}),
|
||||
assignRuleSettings: (rule, getter) => {
|
||||
assignRuleSettings: (rule, getter, fldDef) => {
|
||||
const direction = getter('direction');
|
||||
rule.value = parseInt(getter('daysValue')) * (direction === 'before' ? -1 : 1);
|
||||
},
|
||||
validate: state => {
|
||||
validate: (state, fldDef) => {
|
||||
const value = state.getIn(['daysValue', 'value']);
|
||||
if (!value) {
|
||||
state.setIn(['daysValue', 'error'], t('numberOfDaysMustNotBeEmpty'));
|
||||
|
@ -324,12 +331,45 @@ export function getRuleHelpers(t, fields) {
|
|||
};
|
||||
|
||||
const optionValueSettings = {
|
||||
getForm: () => null,
|
||||
getFormData: rule => ({}),
|
||||
assignRuleSettings: (rule, getter) => {},
|
||||
getForm: fldDef => null,
|
||||
getFormData: (rule, fldDef) => ({}),
|
||||
assignRuleSettings: (rule, getter, fldDef) => {},
|
||||
validate: state => {}
|
||||
};
|
||||
|
||||
const staticEnumValueSettings = {
|
||||
getForm: fldDef => {
|
||||
const opts = [];
|
||||
for (const opt in fldDef.options) {
|
||||
opts.push({key: opt, label: fldDef.options[opt]});
|
||||
}
|
||||
|
||||
return <Dropdown id="value" label={t('value')} options={opts}/>;
|
||||
},
|
||||
getFormData: (rule, fldDef) => {
|
||||
let value;
|
||||
if (rule.value in fldDef.options) {
|
||||
value = rule.value;
|
||||
} else {
|
||||
value = fldDef.default
|
||||
}
|
||||
|
||||
return {
|
||||
value
|
||||
};
|
||||
},
|
||||
assignRuleSettings: (rule, getter, fldDef) => {
|
||||
let value = getter('value');
|
||||
if (!(value in fldDef.options)) {
|
||||
value = fldDef.default
|
||||
}
|
||||
|
||||
rule.value = value;
|
||||
},
|
||||
validate: (state, fldDef) => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function assignSettingsToRuleTypes(ruleTypes, keys, settings) {
|
||||
for (const key of keys) {
|
||||
|
@ -349,6 +389,7 @@ export function getRuleHelpers(t, fields) {
|
|||
assignSettingsToRuleTypes(ruleHelpers.primitiveRuleTypes['dropdown-enum'], ['lt', 'le', 'gt', 'ge'], stringValueSettings(false));
|
||||
assignSettingsToRuleTypes(ruleHelpers.primitiveRuleTypes['radio-enum'], ['eq', 'like', 're'], stringValueSettings(true));
|
||||
assignSettingsToRuleTypes(ruleHelpers.primitiveRuleTypes['radio-enum'], ['lt', 'le', 'gt', 'ge'], stringValueSettings(false));
|
||||
assignSettingsToRuleTypes(ruleHelpers.primitiveRuleTypes['dropdown-static'], ['eq'], staticEnumValueSettings);
|
||||
|
||||
ruleHelpers.primitiveRuleTypesFormDataDefaults = {
|
||||
value: '',
|
||||
|
@ -374,7 +415,8 @@ export function getRuleHelpers(t, fields) {
|
|||
date: ['eq', 'lt', 'le', 'gt', 'ge', 'eqTodayPlusDays', 'ltTodayPlusDays', 'leTodayPlusDays', 'gtTodayPlusDays', 'geTodayPlusDays'],
|
||||
option: ['isTrue', 'isFalse'],
|
||||
'dropdown-enum': ['eq', 'like', 're', 'lt', 'le', 'gt', 'ge'],
|
||||
'radio-enum': ['eq', 'like', 're', 'lt', 'le', 'gt', 'ge']
|
||||
'radio-enum': ['eq', 'like', 're', 'lt', 'le', 'gt', 'ge'],
|
||||
'dropdown-static': ['eq'],
|
||||
};
|
||||
|
||||
return order[columnType].map(key => ({ key, label: ruleHelpers.primitiveRuleTypes[columnType][key].dropdownLabel }));
|
||||
|
@ -411,7 +453,20 @@ export function getRuleHelpers(t, fields) {
|
|||
column: 'is_test',
|
||||
name: t('testUser'),
|
||||
type: 'option'
|
||||
},
|
||||
{
|
||||
column: 'status',
|
||||
name: t('Status'),
|
||||
type: 'dropdown-static',
|
||||
options: {
|
||||
subscribed: t('Subscribed'),
|
||||
unsubscribed: t('Unsubscribed'),
|
||||
bounced: t('Bounced'),
|
||||
complained: t('Complained')
|
||||
},
|
||||
default: 'subscribed'
|
||||
}
|
||||
|
||||
];
|
||||
|
||||
ruleHelpers.fields = [
|
||||
|
@ -424,10 +479,10 @@ export function getRuleHelpers(t, fields) {
|
|||
ruleHelpers.fieldsByColumn[fld.column] = fld;
|
||||
}
|
||||
|
||||
ruleHelpers.getColumnType = column => {
|
||||
ruleHelpers.getColumnDef = column => {
|
||||
const field = ruleHelpers.fieldsByColumn[column];
|
||||
if (field) {
|
||||
return field.type;
|
||||
return field;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -438,21 +493,29 @@ export function getRuleHelpers(t, fields) {
|
|||
}
|
||||
};
|
||||
|
||||
ruleHelpers.getRuleTypeSettings = rule => {
|
||||
if (ruleHelpers.isCompositeRuleType(rule.type)) {
|
||||
return ruleHelpers.compositeRuleTypes[rule.type];
|
||||
} else {
|
||||
const colType = ruleHelpers.getColumnType(rule.column);
|
||||
ruleHelpers.isCompositeRuleType = ruleType => ruleType in ruleHelpers.compositeRuleTypes;
|
||||
|
||||
if (colType) {
|
||||
ruleHelpers.getTreeLabel = rule => {
|
||||
if (ruleHelpers.isCompositeRuleType(rule.type)) {
|
||||
return ruleHelpers.compositeRuleTypes[rule.type].treeLabel(rule);
|
||||
} else {
|
||||
const colDef = ruleHelpers.getColumnDef(rule.column);
|
||||
|
||||
if (colDef) {
|
||||
const colType = colDef.type;
|
||||
if (rule.type in ruleHelpers.primitiveRuleTypes[colType]) {
|
||||
return ruleHelpers.primitiveRuleTypes[colType][rule.type];
|
||||
return ruleHelpers.primitiveRuleTypes[colType][rule.type].treeLabel(rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ruleHelpers.isCompositeRuleType = ruleType => ruleType in ruleHelpers.compositeRuleTypes;
|
||||
|
||||
ruleHelpers.extraFieldTypes = {
|
||||
'dropdown-static': {
|
||||
label: t('Dropdown')
|
||||
}
|
||||
};
|
||||
|
||||
return ruleHelpers;
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ MYSQL_HOST=${MYSQL_HOST:-'mysql'}
|
|||
MYSQL_DATABASE=${MYSQL_DATABASE:-'mailtrain'}
|
||||
MYSQL_USER=${MYSQL_USER:-'mailtrain'}
|
||||
MYSQL_PASSWORD=${MYSQL_PASSWORD:-'mailtrain'}
|
||||
POOL_NAME=${POOL_NAME:-$(hostname)}
|
||||
|
||||
# Warning for users that already rely on the MAILTRAIN_SETTING variable
|
||||
# Can probably be removed in the future.
|
||||
|
@ -61,6 +62,7 @@ builtinZoneMTA:
|
|||
level: warn
|
||||
mongo: mongodb://${MONGO_HOST}:27017/zone-mta
|
||||
redis: redis://${REDIS_HOST}:6379/2
|
||||
poolName: $POOL_NAME
|
||||
|
||||
queue:
|
||||
processes: 5
|
||||
|
|
|
@ -116,6 +116,8 @@ If you do not use VERP with ZoneMTA then you should get notified most of the bou
|
|||
|
||||
If you are using the bundled ZoneMTA then you should make sure you are using a proper PTR record for your server. For example if you use DigitalOcean then PTR is set automatically (it's the droplet name, so make sure your droplet name is the same as the domain name you are running Mailtrain from). If you use AWS then you can request setting up PTR records using [this form](https://portal.aws.amazon.com/gp/aws/html-forms-controller/contactus/ec2-email-limit-rdns-request) (requires authentication). Otherwise you would have to check from your service provider, hot to get the PTR record changed. Everything should work without the PTR record but setting it up correctly improves the deliverability a lot.
|
||||
|
||||
If you are using the builtin Zone-MTA, make sure the configured pool name matches the PTR record.
|
||||
|
||||
##### 7. Ready to send!
|
||||
|
||||
With proper SPF, DKIM and PTR records (DMARC wouldn't hurt either) I got perfect 10/10 score out from [MailTester](https://www.mail-tester.com/) when sending a campaign message to a MailTester test address. I did not have VERP turned on, so the sender address matched return path address.
|
||||
|
|
|
@ -257,6 +257,7 @@ builtinZoneMTA:
|
|||
level: warn
|
||||
processes: 2
|
||||
connections: 5
|
||||
# poolName: 'mail.example.com' # defaults to os.hostname()
|
||||
|
||||
seleniumWebDriver:
|
||||
browser: phantomjs
|
||||
|
|
|
@ -7,6 +7,7 @@ const path = require('path');
|
|||
const fs = require('fs-extra');
|
||||
const crypto = require('crypto');
|
||||
const bluebird = require('bluebird');
|
||||
const os = require('os');
|
||||
|
||||
let zoneMtaProcess = null;
|
||||
|
||||
|
@ -119,6 +120,13 @@ async function createConfig() {
|
|||
}
|
||||
},
|
||||
|
||||
pools: {
|
||||
default: {
|
||||
address: '0.0.0.0',
|
||||
name: config.builtinZoneMTA.poolName || os.hostname()
|
||||
}
|
||||
},
|
||||
|
||||
zones: {
|
||||
default: {
|
||||
preferIPv6: false,
|
||||
|
|
|
@ -12,6 +12,7 @@ const subscriptions = require('./subscriptions');
|
|||
const dependencyHelpers = require('../lib/dependency-helpers');
|
||||
const {ListActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
const {SubscriptionStatus} = require('../../shared/lists');
|
||||
|
||||
const allowedKeys = new Set(['name', 'settings']);
|
||||
|
||||
|
@ -41,6 +42,16 @@ const predefColumns = [
|
|||
{
|
||||
column: 'is_test',
|
||||
type: 'option'
|
||||
},
|
||||
{
|
||||
column: 'status',
|
||||
type: 'dropdown-static',
|
||||
options: {
|
||||
subscribed: 1,
|
||||
unsubscribed: 2,
|
||||
bounced: 3,
|
||||
complained: 4
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -85,36 +96,37 @@ const primitiveRuleTypes = {
|
|||
birthday: {},
|
||||
option: {},
|
||||
'dropdown-enum': {},
|
||||
'radio-enum': {}
|
||||
'radio-enum': {},
|
||||
'dropdown-static': {}
|
||||
};
|
||||
|
||||
|
||||
function stringValueSettings(sqlOperator, allowEmpty) {
|
||||
return {
|
||||
validate: rule => {
|
||||
validate: (rule, fldDef) => {
|
||||
enforce(typeof rule.value === 'string', 'Invalid value type in rule');
|
||||
enforce(allowEmpty || rule.value, 'Value in rule must not be empty');
|
||||
},
|
||||
addQuery: (subsTableName, query, rule) => query.where(subsTableName + '. ' + rule.column, sqlOperator, rule.value)
|
||||
addQuery: (subsTableName, query, rule, fldDef) => query.where(subsTableName + '. ' + rule.column, sqlOperator, rule.value)
|
||||
};
|
||||
}
|
||||
|
||||
function numberValueSettings(sqlOperator) {
|
||||
return {
|
||||
validate: rule => {
|
||||
validate: (rule, fldDef) => {
|
||||
enforce(typeof rule.value === 'number', 'Invalid value type in rule');
|
||||
},
|
||||
addQuery: (subsTableName, query, rule) => query.where(subsTableName + '. ' + rule.column, sqlOperator, rule.value)
|
||||
addQuery: (subsTableName, query, rule, fldDef) => query.where(subsTableName + '. ' + rule.column, sqlOperator, rule.value)
|
||||
};
|
||||
}
|
||||
|
||||
function dateValueSettings(thisDaySqlOperator, nextDaySqlOperator) {
|
||||
return {
|
||||
validate: rule => {
|
||||
validate: (rule, fldDef) => {
|
||||
const date = moment.utc(rule.value);
|
||||
enforce(date.isValid(), 'Invalid date value');
|
||||
},
|
||||
addQuery: (subsTableName, query, rule) => {
|
||||
addQuery: (subsTableName, query, rule, fldDef) => {
|
||||
const thisDay = moment.utc(rule.value).startOf('day');
|
||||
const nextDay = moment(thisDay).add(1, 'days');
|
||||
|
||||
|
@ -131,10 +143,10 @@ function dateValueSettings(thisDaySqlOperator, nextDaySqlOperator) {
|
|||
|
||||
function dateRelativeValueSettings(todaySqlOperator, tomorrowSqlOperator) {
|
||||
return {
|
||||
validate: rule => {
|
||||
validate: (rule, fldDef) => {
|
||||
enforce(typeof rule.value === 'number', 'Invalid value type in rule');
|
||||
},
|
||||
addQuery: (subsTableName, query, rule) => {
|
||||
addQuery: (subsTableName, query, rule, fldDef) => {
|
||||
const todayWithOffset = moment.utc().startOf('day').add(rule.value, 'days');
|
||||
const tomorrowWithOffset = moment(todayWithOffset).add(1, 'days');
|
||||
|
||||
|
@ -151,8 +163,18 @@ function dateRelativeValueSettings(todaySqlOperator, tomorrowSqlOperator) {
|
|||
|
||||
function optionValueSettings(value) {
|
||||
return {
|
||||
validate: rule => {},
|
||||
addQuery: (subsTableName, query, rule) => query.where(subsTableName + '. ' + rule.column, value)
|
||||
validate: (rule, fldDef) => {},
|
||||
addQuery: (subsTableName, query, rule, fldDef) => query.where(subsTableName + '. ' + rule.column, value)
|
||||
};
|
||||
}
|
||||
|
||||
function staticEnumValueSettings(sqlOperator) {
|
||||
return {
|
||||
validate: (rule, fldDef) => {
|
||||
enforce(rule.value, 'Value in rule must not be empty');
|
||||
enforce(rule.value in fldDef.options, 'Value is not permitted')
|
||||
},
|
||||
addQuery: (subsTableName, query, rule, fldDef) => query.where(subsTableName + '. ' + rule.column, sqlOperator, fldDef.options[rule.value])
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -212,6 +234,15 @@ primitiveRuleTypes['radio-enum'].le = stringValueSettings('<=', false);
|
|||
primitiveRuleTypes['radio-enum'].gt = stringValueSettings('>', false);
|
||||
primitiveRuleTypes['radio-enum'].ge = stringValueSettings('>=', false);
|
||||
|
||||
primitiveRuleTypes['radio-enum'].eq = stringValueSettings('=', true);
|
||||
primitiveRuleTypes['radio-enum'].like = stringValueSettings('LIKE', true);
|
||||
primitiveRuleTypes['radio-enum'].re = stringValueSettings('REGEXP', true);
|
||||
primitiveRuleTypes['radio-enum'].lt = stringValueSettings('<', false);
|
||||
primitiveRuleTypes['radio-enum'].le = stringValueSettings('<=', false);
|
||||
primitiveRuleTypes['radio-enum'].gt = stringValueSettings('>', false);
|
||||
primitiveRuleTypes['radio-enum'].ge = stringValueSettings('>=', false);
|
||||
|
||||
primitiveRuleTypes['dropdown-static'].eq = staticEnumValueSettings('=', true);
|
||||
|
||||
|
||||
function hash(entity) {
|
||||
|
@ -283,8 +314,9 @@ async function _validateAndPreprocess(tx, listId, entity, isCreate) {
|
|||
validateRule(childRule);
|
||||
}
|
||||
} else {
|
||||
const colType = fieldsByColumn[rule.column].type;
|
||||
primitiveRuleTypes[colType][rule.type].validate(rule);
|
||||
const fldDef = fieldsByColumn[rule.column];
|
||||
const colType = fldDef.type;
|
||||
primitiveRuleTypes[colType][rule.type].validate(rule, fldDef);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -421,8 +453,9 @@ async function getQueryGeneratorTx(tx, listId, id) {
|
|||
processRule(subQuery, childRule);
|
||||
});
|
||||
} else {
|
||||
const colType = fieldsByColumn[rule.column].type;
|
||||
primitiveRuleTypes[colType][rule.type].addQuery(subsTableName, query, rule);
|
||||
const fldDef = fieldsByColumn[rule.column];
|
||||
const colType = fldDef.type;
|
||||
primitiveRuleTypes[colType][rule.type].addQuery(subsTableName, query, rule, fldDef);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ const mailers = require('../lib/mailers');
|
|||
const messageSender = require('../lib/message-sender');
|
||||
const {CampaignTrackerActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
|
||||
// TODO - use extension manager to add check to cleanExit (in fork) that waits for sendRegularCampaignMessage or sendQueuedMessage to finish
|
||||
require('../lib/fork');
|
||||
|
||||
const MessageType = messageSender.MessageType;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue