Implemented basic support for GDPR
This commit is contained in:
parent
9f9cbc4c2b
commit
92ca1c0f28
21 changed files with 271 additions and 105 deletions
|
@ -69,7 +69,8 @@ export default class CUD extends Component {
|
|||
'web_unsubscribed_notice',
|
||||
'mail_unsubscription_confirmed_html',
|
||||
'mail_unsubscription_confirmed_text',
|
||||
'web_manual_unsubscribe_notice'
|
||||
'web_manual_unsubscribe_notice',
|
||||
'web_privacy_policy_notice'
|
||||
];
|
||||
|
||||
this.initForm({
|
||||
|
@ -206,6 +207,11 @@ export default class CUD extends Component {
|
|||
label: t('webManualUnsubscribeNotice'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
},
|
||||
web_privacy_policy_notice: {
|
||||
label: t('Privacy policy'),
|
||||
mode: 'html',
|
||||
help: helpMjmlGeneral
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -254,6 +260,12 @@ export default class CUD extends Component {
|
|||
'web_manual_unsubscribe_notice'
|
||||
]
|
||||
},
|
||||
gdpr: {
|
||||
label: t('Data protection'),
|
||||
options: [
|
||||
'web_privacy_policy_notice'
|
||||
]
|
||||
},
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ import axios from "../../lib/axios";
|
|||
import {getUrl} from "../../lib/urls";
|
||||
import listStyles from "../styles.scss";
|
||||
import styles from "../../lib/styles.scss";
|
||||
import interoperableErrors
|
||||
from "../../../../shared/interoperable-errors";
|
||||
|
||||
|
||||
function truncate(str, len, ending = '...') {
|
||||
|
@ -185,6 +187,10 @@ export default class CUD extends Component {
|
|||
}
|
||||
|
||||
async submitHandler() {
|
||||
await this.save();
|
||||
}
|
||||
|
||||
async save(runAfterSave) {
|
||||
const t = this.props.t;
|
||||
const isEdit = !!this.props.entity;
|
||||
|
||||
|
@ -275,6 +281,16 @@ export default class CUD extends Component {
|
|||
if (!isEdit) {
|
||||
this.navigateTo(`/lists/${this.props.list.id}/imports/${submitResponse}/edit`);
|
||||
} else {
|
||||
try {
|
||||
await axios.post(getUrl(`rest/import-start/${this.props.list.id}/${this.props.entity.id}`));
|
||||
} catch (err) {
|
||||
if (err instanceof interoperableErrors.InvalidStateError) {
|
||||
// Just mask the fact that it's not possible to start anything and refresh instead.
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
this.navigateToWithFlashMessage(`/lists/${this.props.list.id}/imports/${this.props.entity.id}/status`, 'success', t('importSaved'));
|
||||
}
|
||||
|
||||
|
@ -389,11 +405,12 @@ export default class CUD extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
let saveButtonLabel;
|
||||
const saveButtons = []
|
||||
if (!isEdit) {
|
||||
saveButtonLabel = t('saveAndEditSettings');
|
||||
saveButtons.push(<Button key="default" type="submit" className="btn-primary" icon="ok" label={t('saveAndEditSettings')}/>);
|
||||
} else {
|
||||
saveButtonLabel = t('save');
|
||||
saveButtons.push(<Button key="default" type="submit" className="btn-primary" icon="ok" label={t('save')}/>);
|
||||
saveButtons.push(<Button key="saveAndRun" className="btn-primary" icon="ok" label={t('Save and Run')} onClickAsync={async () => await this.save(true)}/>);
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -427,7 +444,7 @@ export default class CUD extends Component {
|
|||
|
||||
|
||||
<ButtonRow>
|
||||
<Button type="submit" className="btn-primary" icon="ok" label={saveButtonLabel}/>
|
||||
{saveButtons}
|
||||
{isEdit && <NavButton className="btn-danger" icon="remove" label={t('delete')} linkTo={`/lists/${this.props.list.id}/imports/${this.props.entity.id}/delete`}/>}
|
||||
</ButtonRow>
|
||||
</Form>
|
||||
|
|
|
@ -96,13 +96,13 @@ export function getRuleHelpers(t, fields) {
|
|||
};
|
||||
|
||||
// FXIME - the localization here is still wrong
|
||||
function getRelativeDateTreeLabel(rule, textFragment) {
|
||||
function getRelativeDateTreeLabel(rule, variants) {
|
||||
if (rule.value === 0) {
|
||||
return t(/*ignore*/'Date in column ' + textFragment + ' the current date', {colName: ruleHelpers.getColumnName(rule.column)})
|
||||
return t(variants[0], {colName: ruleHelpers.getColumnName(rule.column)})
|
||||
} else if (rule.value > 0) {
|
||||
return t(/*ignore*/'Date in column ' + textFragment + ' {{value}}-th day after the current date', {colName: ruleHelpers.getColumnName(rule.column), value: rule.value});
|
||||
return t(variants[1], {colName: ruleHelpers.getColumnName(rule.column), value: rule.value});
|
||||
} else {
|
||||
return t(/*ignore*/'Date in column ' + textFragment + ' {{value}}-th day before the current date', {colName: ruleHelpers.getColumnName(rule.column), value: rule.value});
|
||||
return t(variants[2], {colName: ruleHelpers.getColumnName(rule.column), value: rule.value});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,48 +129,23 @@ export function getRuleHelpers(t, fields) {
|
|||
},
|
||||
eqTodayPlusDays: {
|
||||
dropdownLabel: t('onXthDayBeforeafterCurrentDate'),
|
||||
/*
|
||||
tMark('dateInColumnIsTheCurrentDate')
|
||||
tMark('dateInColumnIsValuethDayAfterTheCurrent')
|
||||
tMark('dateInColumnIsValuethDayBeforeTheCurrent')
|
||||
*/
|
||||
treeLabel: rule => getRelativeDateTreeLabel(rule, 'is'),
|
||||
treeLabel: rule => getRelativeDateTreeLabel(rule, 'is', [tMark('dateInColumnIsTheCurrentDate'), tMark('dateInColumnIsValuethDayAfterTheCurrent'), tMark('dateInColumnIsValuethDayBeforeTheCurrent')]),
|
||||
},
|
||||
ltTodayPlusDays: {
|
||||
dropdownLabel: t('beforeXthDayBeforeafterCurrentDate'),
|
||||
/*
|
||||
tMark('dateInColumnIsBeforeTheCurrentDate')
|
||||
tMark('dateInColumnIsBeforeValuethDayAfterThe')
|
||||
tMark('dateInColumnIsBeforeValuethDayBeforeThe')
|
||||
*/
|
||||
treeLabel: rule => getRelativeDateTreeLabel(rule, 'is before'),
|
||||
treeLabel: rule => getRelativeDateTreeLabel(rule, [tMark('dateInColumnIsBeforeTheCurrentDate'), tMark('dateInColumnIsBeforeValuethDayAfterThe'), tMark('dateInColumnIsBeforeValuethDayBeforeThe')]),
|
||||
},
|
||||
leTodayPlusDays: {
|
||||
dropdownLabel: t('beforeOrOnXthDayBeforeafterCurrentDate'),
|
||||
/*
|
||||
tMark('dateInColumnIsBeforeOrOnTheCurrentDate')
|
||||
tMark('dateInColumnIsBeforeOrOnValuethDayAfter')
|
||||
tMark('dateInColumnIsBeforeOrOnValuethDayBefore')
|
||||
*/
|
||||
treeLabel: rule => getRelativeDateTreeLabel(rule, 'is before or on'),
|
||||
treeLabel: rule => getRelativeDateTreeLabel(rule, [tMark('dateInColumnIsBeforeOrOnTheCurrentDate'), tMark('dateInColumnIsBeforeOrOnValuethDayAfter'), tMark('dateInColumnIsBeforeOrOnValuethDayBefore')]),
|
||||
},
|
||||
gtTodayPlusDays: {
|
||||
dropdownLabel: t('afterXthDayBeforeafterCurrentDate'),
|
||||
/*
|
||||
tMark('dateInColumnIsAfterTheCurrentDate')
|
||||
tMark('dateInColumnIsAfterValuethDayAfterThe')
|
||||
tMark('dateInColumnIsAfterValuethDayAfterThe')
|
||||
*/
|
||||
treeLabel: rule => getRelativeDateTreeLabel(rule, 'is after'),
|
||||
treeLabel: rule => getRelativeDateTreeLabel(rule, [tMark('dateInColumnIsAfterTheCurrentDate'), tMark('dateInColumnIsAfterValuethDayAfterThe'), tMark('dateInColumnIsAfterValuethDayAfterThe')]),
|
||||
},
|
||||
geTodayPlusDays: {
|
||||
dropdownLabel: t('afterOrOnXthDayBeforeafterCurrentDate'),
|
||||
/*
|
||||
tMark('dateInColumnIsAfterOrOnTheCurrentDate')
|
||||
tMark('dateInColumnIsAfterOrOnValuethDayAfter')
|
||||
tMark('dateInColumnIsAfterOrOnValuethDayAfter')
|
||||
*/
|
||||
treeLabel: rule => getRelativeDateTreeLabel(rule, 'is after or on'),
|
||||
treeLabel: rule => getRelativeDateTreeLabel(rule, [tMark('dateInColumnIsAfterOrOnTheCurrentDate'), tMark('dateInColumnIsAfterOrOnValuethDayAfter'), tMark('dateInColumnIsAfterOrOnValuethDayAfter')]),
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
.mapping {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.erased {
|
||||
color: #808080;
|
||||
}
|
|
@ -13,14 +13,18 @@ import {
|
|||
withForm
|
||||
} from '../../lib/form';
|
||||
import {Icon, Button} from "../../lib/bootstrap-components";
|
||||
import axios from '../../lib/axios';
|
||||
import axios, {HTTPMethod} from '../../lib/axios';
|
||||
import {getFieldTypes, getSubscriptionStatusLabels} from './helpers';
|
||||
import {getUrl, getPublicUrl} from "../../lib/urls";
|
||||
import {
|
||||
DeleteModalDialog,
|
||||
RestActionModalDialog,
|
||||
tableDeleteDialogAddDeleteButton,
|
||||
tableDeleteDialogInit,
|
||||
tableDeleteDialogRender
|
||||
} from "../../lib/modals";
|
||||
import listStyles from "../styles.scss";
|
||||
import styles from '../../lib/styles.scss';
|
||||
|
||||
@withTranslation()
|
||||
@withForm
|
||||
|
@ -87,7 +91,7 @@ export default class List extends Component {
|
|||
|
||||
const columns = [
|
||||
{ data: 1, title: t('id'), render: data => <code>{data}</code> },
|
||||
{ data: 2, title: t('email') },
|
||||
{ data: 2, title: t('email'), render: data => data === null ? <span className={listStyles.erased}>{t('[ERASED]')}</span> : data },
|
||||
{ data: 3, title: t('status'), render: (data, display, rowData) => this.subscriptionStatusLabels[data] + (rowData[5] ? ', ' + t('blacklisted') : '') },
|
||||
{ data: 4, title: t('created'), render: data => data ? moment(data).fromNow() : '' }
|
||||
];
|
||||
|
@ -112,27 +116,30 @@ export default class List extends Component {
|
|||
columns.push({
|
||||
actions: data => {
|
||||
const actions = [];
|
||||
const id = data[0];
|
||||
const email = data[2];
|
||||
const status = data[3];
|
||||
|
||||
actions.push({
|
||||
label: <Icon icon="edit" title={t('edit')}/>,
|
||||
link: `/lists/${this.props.list.id}/subscriptions/${data[0]}/edit`
|
||||
link: `/lists/${this.props.list.id}/subscriptions/${id}/edit`
|
||||
});
|
||||
|
||||
if (data[3] === SubscriptionStatus.SUBSCRIBED) {
|
||||
if (email && status === SubscriptionStatus.SUBSCRIBED) {
|
||||
actions.push({
|
||||
label: <Icon icon="off" title={t('unsubscribe')}/>,
|
||||
action: () => this.unsubscribeSubscription(data[0])
|
||||
action: () => this.unsubscribeSubscription(id)
|
||||
});
|
||||
}
|
||||
|
||||
if (!data[5]) {
|
||||
if (email && !data[5]) {
|
||||
actions.push({
|
||||
label: <Icon icon="ban-circle" title={t('blacklist')}/>,
|
||||
action: () => this.blacklistSubscription(data[2])
|
||||
action: () => this.blacklistSubscription(email)
|
||||
});
|
||||
}
|
||||
|
||||
tableDeleteDialogAddDeleteButton(actions, this, null, data[0], data[2]);
|
||||
tableDeleteDialogAddDeleteButton(actions, this, null, id, email);
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
|
|
@ -49,7 +49,11 @@ import axios
|
|||
import {getUrl} from "./lib/urls";
|
||||
import {langCodes} from "../../shared/langs";
|
||||
|
||||
const topLevelMenuKeys = ['lists', 'templates', 'campaigns', 'reports'];
|
||||
const topLevelMenuKeys = ['lists', 'templates', 'campaigns'];
|
||||
|
||||
if (mailtrainConfig.reportsEnabmed) {
|
||||
topLevelMenuKeys.push('reports');
|
||||
}
|
||||
|
||||
@withTranslation()
|
||||
class Root extends Component {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue