Work in progress on introducing tag language. Not tested yet.
This commit is contained in:
parent
450b930cc5
commit
00e328a914
21 changed files with 2154 additions and 1909 deletions
|
@ -25,7 +25,7 @@ import {withAsyncErrorHandler, withErrorHandling} from '../lib/error-handling';
|
||||||
import {NamespaceSelect, validateNamespace} from '../lib/namespace';
|
import {NamespaceSelect, validateNamespace} from '../lib/namespace';
|
||||||
import {DeleteModalDialog} from "../lib/modals";
|
import {DeleteModalDialog} from "../lib/modals";
|
||||||
import mailtrainConfig from 'mailtrainConfig';
|
import mailtrainConfig from 'mailtrainConfig';
|
||||||
import {getTemplateTypes, getTypeForm, ResourceType} from '../templates/helpers';
|
import {getTagLanguages, getTemplateTypes, getTypeForm, ResourceType} from '../templates/helpers';
|
||||||
import axios from '../lib/axios';
|
import axios from '../lib/axios';
|
||||||
import styles from "../lib/styles.scss";
|
import styles from "../lib/styles.scss";
|
||||||
import campaignsStyles from "./styles.scss";
|
import campaignsStyles from "./styles.scss";
|
||||||
|
@ -50,6 +50,8 @@ export default class CUD extends Component {
|
||||||
const t = props.t;
|
const t = props.t;
|
||||||
|
|
||||||
this.templateTypes = getTemplateTypes(props.t, 'data_sourceCustom_', ResourceType.CAMPAIGN);
|
this.templateTypes = getTemplateTypes(props.t, 'data_sourceCustom_', ResourceType.CAMPAIGN);
|
||||||
|
this.tagLanguages = getTagLanguages(props.t);
|
||||||
|
|
||||||
this.mailerTypes = getMailerTypes(props.t);
|
this.mailerTypes = getMailerTypes(props.t);
|
||||||
|
|
||||||
const { campaignTypeLabels } = getCampaignLabels(t);
|
const { campaignTypeLabels } = getCampaignLabels(t);
|
||||||
|
@ -85,6 +87,11 @@ export default class CUD extends Component {
|
||||||
this.customTemplateTypeOptions.push({key, label: this.templateTypes[key].typeName});
|
this.customTemplateTypeOptions.push({key, label: this.templateTypes[key].typeName});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.customTemplateTagLanguageOptions = [];
|
||||||
|
for (const key of mailtrainConfig.tagLanguages) {
|
||||||
|
this.customTemplateTagLanguageOptions.push({key, label: this.tagLanguages[key].name});
|
||||||
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
sendConfiguration: null
|
sendConfiguration: null
|
||||||
};
|
};
|
||||||
|
@ -120,6 +127,14 @@ export default class CUD extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (key === undefined || key === 'data_sourceCustom_tag_language') {
|
||||||
|
if (newValue) {
|
||||||
|
const isEdit = !!this.props.entity;
|
||||||
|
const type = mutStateData.getIn(['data_sourceCustom_tag_language', 'value']);
|
||||||
|
this.templateTypes[type].afterTagLanguageChange(mutStateData, isEdit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (key === undefined || (match = key.match(/^(lists_[0-9]+_)list$/))) {
|
if (key === undefined || (match = key.match(/^(lists_[0-9]+_)list$/))) {
|
||||||
const prefix = match[1];
|
const prefix = match[1];
|
||||||
mutStateData.setIn([prefix + 'segment', 'value'], null);
|
mutStateData.setIn([prefix + 'segment', 'value'], null);
|
||||||
|
@ -202,6 +217,7 @@ export default class CUD extends Component {
|
||||||
|
|
||||||
data.data.sourceCustom = {
|
data.data.sourceCustom = {
|
||||||
type: data.data_sourceCustom_type,
|
type: data.data_sourceCustom_type,
|
||||||
|
tag_language: data.data_sourceCustom_tag_language,
|
||||||
data: data.data_sourceCustom_data,
|
data: data.data_sourceCustom_data,
|
||||||
html: data.data_sourceCustom_html,
|
html: data.data_sourceCustom_html,
|
||||||
text: data.data_sourceCustom_text
|
text: data.data_sourceCustom_text
|
||||||
|
@ -301,6 +317,7 @@ export default class CUD extends Component {
|
||||||
|
|
||||||
// This is for CampaignSource.CUSTOM
|
// This is for CampaignSource.CUSTOM
|
||||||
data_sourceCustom_type: mailtrainConfig.editors[0],
|
data_sourceCustom_type: mailtrainConfig.editors[0],
|
||||||
|
data_sourceCustom_tag_language: mailtrainConfig.tagLanguages[0],
|
||||||
data_sourceCustom_data: {},
|
data_sourceCustom_data: {},
|
||||||
data_sourceCustom_html: '',
|
data_sourceCustom_html: '',
|
||||||
data_sourceCustom_text: '',
|
data_sourceCustom_text: '',
|
||||||
|
@ -362,6 +379,10 @@ export default class CUD extends Component {
|
||||||
state.setIn(['data_sourceCustom_type', 'error'], t('typeMustBeSelected'));
|
state.setIn(['data_sourceCustom_type', 'error'], t('typeMustBeSelected'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!state.getIn(['data_sourceCustom_tag_language', 'value'])) {
|
||||||
|
state.setIn(['data_sourceCustom_tag_language', 'error'], t('Tag language must be selected'));
|
||||||
|
}
|
||||||
|
|
||||||
if (customTemplateTypeKey) {
|
if (customTemplateTypeKey) {
|
||||||
this.templateTypes[customTemplateTypeKey].validate(state);
|
this.templateTypes[customTemplateTypeKey].validate(state);
|
||||||
}
|
}
|
||||||
|
@ -654,8 +675,8 @@ export default class CUD extends Component {
|
||||||
{ data: 1, title: t('name') },
|
{ data: 1, title: t('name') },
|
||||||
{ data: 2, title: t('description') },
|
{ data: 2, title: t('description') },
|
||||||
{ data: 3, title: t('type'), render: data => this.templateTypes[data].typeName },
|
{ data: 3, title: t('type'), render: data => this.templateTypes[data].typeName },
|
||||||
{ data: 4, title: t('created'), render: data => moment(data).fromNow() },
|
{ data: 5, title: t('created'), render: data => moment(data).fromNow() },
|
||||||
{ data: 5, title: t('namespace') },
|
{ data: 6, title: t('namespace') },
|
||||||
];
|
];
|
||||||
|
|
||||||
let help = null;
|
let help = null;
|
||||||
|
@ -690,6 +711,8 @@ export default class CUD extends Component {
|
||||||
|
|
||||||
templateEdit = <div>
|
templateEdit = <div>
|
||||||
<Dropdown id="data_sourceCustom_type" label={t('type')} options={this.customTemplateTypeOptions}/>
|
<Dropdown id="data_sourceCustom_type" label={t('type')} options={this.customTemplateTypeOptions}/>
|
||||||
|
<Dropdown id="data_sourceCustom_tag_language" label={t('Tag language')} options={this.customTemplateTagLanguageOptions} disabled={isEdit && (!customTemplateTypeKey || this.templateTypes[customTemplateTypeKey].isTagLanguageSelectorDisabledForEdit)}/>
|
||||||
|
|
||||||
{customTemplateTypeForm}
|
{customTemplateTypeForm}
|
||||||
</div>;
|
</div>;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {requiresAuthenticatedUser, Title, withPageHelpers} from '../lib/page'
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
ButtonRow,
|
ButtonRow,
|
||||||
|
Dropdown,
|
||||||
filterData,
|
filterData,
|
||||||
Form,
|
Form,
|
||||||
FormSendMethod,
|
FormSendMethod,
|
||||||
|
@ -16,7 +17,7 @@ import {
|
||||||
} from '../lib/form';
|
} from '../lib/form';
|
||||||
import {withErrorHandling} from '../lib/error-handling';
|
import {withErrorHandling} from '../lib/error-handling';
|
||||||
import mailtrainConfig from 'mailtrainConfig';
|
import mailtrainConfig from 'mailtrainConfig';
|
||||||
import {getEditForm, getTemplateTypes, getTypeForm, ResourceType} from '../templates/helpers';
|
import {getEditForm, getTagLanguages, getTemplateTypes, getTypeForm, ResourceType} from '../templates/helpers';
|
||||||
import axios from '../lib/axios';
|
import axios from '../lib/axios';
|
||||||
import styles from "../lib/styles.scss";
|
import styles from "../lib/styles.scss";
|
||||||
import {getUrl} from "../lib/urls";
|
import {getUrl} from "../lib/urls";
|
||||||
|
@ -39,12 +40,18 @@ export default class CustomContent extends Component {
|
||||||
const t = props.t;
|
const t = props.t;
|
||||||
|
|
||||||
this.templateTypes = getTemplateTypes(props.t, 'data_sourceCustom_', ResourceType.CAMPAIGN);
|
this.templateTypes = getTemplateTypes(props.t, 'data_sourceCustom_', ResourceType.CAMPAIGN);
|
||||||
|
this.tagLanguages = getTagLanguages(props.t);
|
||||||
|
|
||||||
this.customTemplateTypeOptions = [];
|
this.customTemplateTypeOptions = [];
|
||||||
for (const key of mailtrainConfig.editors) {
|
for (const key of mailtrainConfig.editors) {
|
||||||
this.customTemplateTypeOptions.push({key, label: this.templateTypes[key].typeName});
|
this.customTemplateTypeOptions.push({key, label: this.templateTypes[key].typeName});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.customTemplateTagLanguageOptions = [];
|
||||||
|
for (const key of mailtrainConfig.tagLanguages) {
|
||||||
|
this.customTemplateTagLanguageOptions.push({key, label: this.tagLanguages[key].name});
|
||||||
|
}
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
showMergeTagReference: false,
|
showMergeTagReference: false,
|
||||||
elementInFullscreen: false,
|
elementInFullscreen: false,
|
||||||
|
@ -56,6 +63,9 @@ export default class CustomContent extends Component {
|
||||||
|
|
||||||
this.initForm({
|
this.initForm({
|
||||||
getPreSubmitUpdater: ::this.getPreSubmitFormValuesUpdater,
|
getPreSubmitUpdater: ::this.getPreSubmitFormValuesUpdater,
|
||||||
|
onChangeBeforeValidation: {
|
||||||
|
data_sourceCustom_tag_language: ::this.onTagLanguageChanged
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.sendModalGetDataHandler = ::this.sendModalGetData;
|
this.sendModalGetDataHandler = ::this.sendModalGetData;
|
||||||
|
@ -71,9 +81,16 @@ export default class CustomContent extends Component {
|
||||||
setPanelInFullScreen: PropTypes.func
|
setPanelInFullScreen: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTagLanguageChanged(mutStateData, key, oldTagLanguage, tagLanguage) {
|
||||||
|
if (tagLanguage) {
|
||||||
|
const type = mutStateData.getIn(['data_sourceCustom_tag_language', 'value']);
|
||||||
|
this.tagLanguages[type].afterTagLanguageChange(mutStateData, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getFormValuesMutator(data) {
|
getFormValuesMutator(data) {
|
||||||
data.data_sourceCustom_type = data.data.sourceCustom.type;
|
data.data_sourceCustom_type = data.data.sourceCustom.type;
|
||||||
|
data.data_sourceCustom_tag_language = data.data.sourceCustom.tag_language;
|
||||||
data.data_sourceCustom_data = data.data.sourceCustom.data;
|
data.data_sourceCustom_data = data.data.sourceCustom.data;
|
||||||
data.data_sourceCustom_html = data.data.sourceCustom.html;
|
data.data_sourceCustom_html = data.data.sourceCustom.html;
|
||||||
data.data_sourceCustom_text = data.data.sourceCustom.text;
|
data.data_sourceCustom_text = data.data.sourceCustom.text;
|
||||||
|
@ -86,6 +103,7 @@ export default class CustomContent extends Component {
|
||||||
|
|
||||||
data.data.sourceCustom = {
|
data.data.sourceCustom = {
|
||||||
type: data.data_sourceCustom_type,
|
type: data.data_sourceCustom_type,
|
||||||
|
tag_language: data.data_sourceCustom_tag_language,
|
||||||
data: data.data_sourceCustom_data,
|
data: data.data_sourceCustom_data,
|
||||||
html: data.data_sourceCustom_html,
|
html: data.data_sourceCustom_html,
|
||||||
text: data.data_sourceCustom_text
|
text: data.data_sourceCustom_text
|
||||||
|
@ -112,6 +130,12 @@ export default class CustomContent extends Component {
|
||||||
localValidateFormValues(state) {
|
localValidateFormValues(state) {
|
||||||
const t = this.props.t;
|
const t = this.props.t;
|
||||||
|
|
||||||
|
if (!state.getIn(['data_sourceCustom_tag_language', 'value'])) {
|
||||||
|
state.setIn(['data_sourceCustom_tag_language', 'error'], t('Tag language must be selected'));
|
||||||
|
} else {
|
||||||
|
state.setIn(['data_sourceCustom_tag_language', 'error'], null);
|
||||||
|
}
|
||||||
|
|
||||||
const customTemplateTypeKey = state.getIn(['data_sourceCustom_type', 'value']);
|
const customTemplateTypeKey = state.getIn(['data_sourceCustom_type', 'value']);
|
||||||
|
|
||||||
if (customTemplateTypeKey) {
|
if (customTemplateTypeKey) {
|
||||||
|
@ -229,8 +253,6 @@ export default class CustomContent extends Component {
|
||||||
|
|
||||||
const customTemplateTypeKey = this.getFormValue('data_sourceCustom_type');
|
const customTemplateTypeKey = this.getFormValue('data_sourceCustom_type');
|
||||||
|
|
||||||
// FIXME - data_sourceCustom_type is initialized only after first render
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={this.state.elementInFullscreen ? styles.withElementInFullscreen : ''}>
|
<div className={this.state.elementInFullscreen ? styles.withElementInFullscreen : ''}>
|
||||||
<TestSendModalDialog
|
<TestSendModalDialog
|
||||||
|
@ -254,6 +276,8 @@ export default class CustomContent extends Component {
|
||||||
{customTemplateTypeKey && this.templateTypes[customTemplateTypeKey].typeName}
|
{customTemplateTypeKey && this.templateTypes[customTemplateTypeKey].typeName}
|
||||||
</StaticField>
|
</StaticField>
|
||||||
|
|
||||||
|
<Dropdown id="data_sourceCustom_tag_language" label={t('Tag language')} options={this.customTemplateTagLanguageOptions} disabled={!customTemplateTypeKey || this.templateTypes[customTemplateTypeKey].isTagLanguageSelectorDisabledForEdit}/>
|
||||||
|
|
||||||
{customTemplateTypeKey && getTypeForm(this, customTemplateTypeKey, true)}
|
{customTemplateTypeKey && getTypeForm(this, customTemplateTypeKey, true)}
|
||||||
|
|
||||||
{customTemplateTypeKey && getEditForm(this, customTemplateTypeKey, 'data_sourceCustom_')}
|
{customTemplateTypeKey && getEditForm(this, customTemplateTypeKey, 'data_sourceCustom_')}
|
||||||
|
|
|
@ -712,7 +712,8 @@ class Dropdown extends Component {
|
||||||
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||||
options: PropTypes.array,
|
options: PropTypes.array,
|
||||||
className: PropTypes.string,
|
className: PropTypes.string,
|
||||||
format: PropTypes.string
|
format: PropTypes.string,
|
||||||
|
disabled: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -740,7 +741,7 @@ class Dropdown extends Component {
|
||||||
const className = owner.addFormValidationClass('form-control ' + (props.className || '') , id);
|
const className = owner.addFormValidationClass('form-control ' + (props.className || '') , id);
|
||||||
|
|
||||||
return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
|
return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
|
||||||
<select id={htmlId} className={className} aria-describedby={htmlId + '_help'} value={owner.getFormValue(id)} onChange={evt => owner.updateFormValue(id, evt.target.value)}>
|
<select id={htmlId} className={className} aria-describedby={htmlId + '_help'} value={owner.getFormValue(id)} onChange={evt => owner.updateFormValue(id, evt.target.value)} disabled={props.disabled}>
|
||||||
{options}
|
{options}
|
||||||
</select>
|
</select>
|
||||||
);
|
);
|
||||||
|
|
|
@ -33,6 +33,8 @@ class TreeTable extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
this.isComponentMounted = false;
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
treeData: []
|
treeData: []
|
||||||
};
|
};
|
||||||
|
@ -68,10 +70,12 @@ class TreeTable extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.isComponentMounted) {
|
||||||
this.setState({
|
this.setState({
|
||||||
treeData
|
treeData
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
dataUrl: PropTypes.string,
|
dataUrl: PropTypes.string,
|
||||||
|
@ -109,6 +113,8 @@ class TreeTable extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
this.isComponentMounted = true;
|
||||||
|
|
||||||
if (!this.props.data && this.props.dataUrl) {
|
if (!this.props.data && this.props.dataUrl) {
|
||||||
// noinspection JSIgnoredPromiseFromCall
|
// noinspection JSIgnoredPromiseFromCall
|
||||||
this.loadData();
|
this.loadData();
|
||||||
|
@ -221,6 +227,10 @@ class TreeTable extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.isComponentMounted = false;
|
||||||
|
}
|
||||||
|
|
||||||
updateSelection() {
|
updateSelection() {
|
||||||
const tree = this.tree;
|
const tree = this.tree;
|
||||||
if (this.selectMode === TreeSelectMode.MULTI) {
|
if (this.selectMode === TreeSelectMode.MULTI) {
|
||||||
|
|
|
@ -23,7 +23,7 @@ import {withErrorHandling} from '../lib/error-handling';
|
||||||
import {NamespaceSelect, validateNamespace} from '../lib/namespace';
|
import {NamespaceSelect, validateNamespace} from '../lib/namespace';
|
||||||
import {ContentModalDialog, DeleteModalDialog} from "../lib/modals";
|
import {ContentModalDialog, DeleteModalDialog} from "../lib/modals";
|
||||||
import mailtrainConfig from 'mailtrainConfig';
|
import mailtrainConfig from 'mailtrainConfig';
|
||||||
import {getEditForm, getTemplateTypes, getTypeForm} from './helpers';
|
import {getEditForm, getTagLanguages, getTemplateTypes, getTypeForm} from './helpers';
|
||||||
import axios from '../lib/axios';
|
import axios from '../lib/axios';
|
||||||
import styles from "../lib/styles.scss";
|
import styles from "../lib/styles.scss";
|
||||||
import {getUrl} from "../lib/urls";
|
import {getUrl} from "../lib/urls";
|
||||||
|
@ -44,6 +44,7 @@ export default class CUD extends Component {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.templateTypes = getTemplateTypes(props.t);
|
this.templateTypes = getTemplateTypes(props.t);
|
||||||
|
this.tagLanguages = getTagLanguages(props.t);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
showMergeTagReference: false,
|
showMergeTagReference: false,
|
||||||
|
@ -57,7 +58,8 @@ export default class CUD extends Component {
|
||||||
this.initForm({
|
this.initForm({
|
||||||
getPreSubmitUpdater: ::this.getPreSubmitFormValuesUpdater,
|
getPreSubmitUpdater: ::this.getPreSubmitFormValuesUpdater,
|
||||||
onChangeBeforeValidation: {
|
onChangeBeforeValidation: {
|
||||||
type: ::this.onTypeChanged
|
type: ::this.onTypeChanged,
|
||||||
|
tag_language: ::this.onTagLanguageChanged
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -82,13 +84,21 @@ export default class CUD extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onTagLanguageChanged(mutStateData, key, oldTagLanguage, tagLanguage) {
|
||||||
|
if (tagLanguage) {
|
||||||
|
const isEdit = !!this.props.entity;
|
||||||
|
const type = mutStateData.getIn(['type', 'value']);
|
||||||
|
this.templateTypes[type].afterTagLanguageChange(mutStateData, isEdit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
getFormValuesMutator(data) {
|
getFormValuesMutator(data) {
|
||||||
this.templateTypes[data.type].afterLoad(data);
|
this.templateTypes[data.type].afterLoad(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
submitFormValuesMutator(data) {
|
submitFormValuesMutator(data) {
|
||||||
this.templateTypes[data.type].beforeSave(data);
|
this.templateTypes[data.type].beforeSave(data);
|
||||||
return filterData(data, ['name', 'description', 'type', 'data', 'html', 'text', 'namespace']);
|
return filterData(data, ['name', 'description', 'type', 'tag_language', 'data', 'html', 'text', 'namespace']);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getPreSubmitFormValuesUpdater() {
|
async getPreSubmitFormValuesUpdater() {
|
||||||
|
@ -115,6 +125,7 @@ export default class CUD extends Component {
|
||||||
description: '',
|
description: '',
|
||||||
namespace: mailtrainConfig.user.namespace,
|
namespace: mailtrainConfig.user.namespace,
|
||||||
type: mailtrainConfig.editors[0],
|
type: mailtrainConfig.editors[0],
|
||||||
|
tag_language: mailtrainConfig.tagLanguages[0],
|
||||||
|
|
||||||
fromSourceTemplate: false,
|
fromSourceTemplate: false,
|
||||||
sourceTemplate: null,
|
sourceTemplate: null,
|
||||||
|
@ -143,6 +154,10 @@ export default class CUD extends Component {
|
||||||
state.setIn(['type', 'error'], t('typeMustBeSelected'));
|
state.setIn(['type', 'error'], t('typeMustBeSelected'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!state.getIn(['tag_language', 'value'])) {
|
||||||
|
state.setIn(['tag_language', 'error'], t('Tag language must be selected'));
|
||||||
|
}
|
||||||
|
|
||||||
if (state.getIn(['fromSourceTemplate', 'value']) && !state.getIn(['sourceTemplate', 'value'])) {
|
if (state.getIn(['fromSourceTemplate', 'value']) && !state.getIn(['sourceTemplate', 'value'])) {
|
||||||
state.setIn(['sourceTemplate', 'error'], t('sourceTemplateMustNotBeEmpty'));
|
state.setIn(['sourceTemplate', 'error'], t('sourceTemplateMustNotBeEmpty'));
|
||||||
} else {
|
} else {
|
||||||
|
@ -274,6 +289,11 @@ export default class CUD extends Component {
|
||||||
typeOptions.push({key, label: this.templateTypes[key].typeName});
|
typeOptions.push({key, label: this.templateTypes[key].typeName});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tagLanguageOptions = [];
|
||||||
|
for (const key of mailtrainConfig.tagLanguages) {
|
||||||
|
tagLanguageOptions.push({key, label: this.tagLanguages[key].name});
|
||||||
|
}
|
||||||
|
|
||||||
const typeKey = this.getFormValue('type');
|
const typeKey = this.getFormValue('type');
|
||||||
|
|
||||||
let editForm = null;
|
let editForm = null;
|
||||||
|
@ -290,8 +310,9 @@ export default class CUD extends Component {
|
||||||
{ data: 1, title: t('name') },
|
{ data: 1, title: t('name') },
|
||||||
{ data: 2, title: t('description') },
|
{ data: 2, title: t('description') },
|
||||||
{ data: 3, title: t('type'), render: data => this.templateTypes[data].typeName },
|
{ data: 3, title: t('type'), render: data => this.templateTypes[data].typeName },
|
||||||
{ data: 4, title: t('created'), render: data => moment(data).fromNow() },
|
{ data: 4, title: t('Tag language'), render: data => this.tagLanguages[data].name },
|
||||||
{ data: 5, title: t('namespace') },
|
{ data: 5, title: t('created'), render: data => moment(data).fromNow() },
|
||||||
|
{ data: 6, title: t('namespace') },
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -347,6 +368,8 @@ export default class CUD extends Component {
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<Dropdown id="tag_language" label={t('Tag language')} options={tagLanguageOptions} disabled={isEdit && (!typeKey || this.templateTypes[typeKey].isTagLanguageSelectorDisabledForEdit)}/>
|
||||||
|
|
||||||
<NamespaceSelect/>
|
<NamespaceSelect/>
|
||||||
|
|
||||||
{editForm}
|
{editForm}
|
||||||
|
|
|
@ -7,11 +7,12 @@ import {LinkButton, requiresAuthenticatedUser, Title, Toolbar, withPageHelpers}
|
||||||
import {withAsyncErrorHandler, withErrorHandling} from '../lib/error-handling';
|
import {withAsyncErrorHandler, withErrorHandling} from '../lib/error-handling';
|
||||||
import {Table} from '../lib/table';
|
import {Table} from '../lib/table';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {getTemplateTypes} from './helpers';
|
import {getTemplateTypes, getTagLanguages} from './helpers';
|
||||||
import {checkPermissions} from "../lib/permissions";
|
import {checkPermissions} from "../lib/permissions";
|
||||||
import {tableAddDeleteButton, tableRestActionDialogInit, tableRestActionDialogRender} from "../lib/modals";
|
import {tableAddDeleteButton, tableRestActionDialogInit, tableRestActionDialogRender} from "../lib/modals";
|
||||||
import {withComponentMixins} from "../lib/decorator-helpers";
|
import {withComponentMixins} from "../lib/decorator-helpers";
|
||||||
|
|
||||||
|
|
||||||
@withComponentMixins([
|
@withComponentMixins([
|
||||||
withTranslation,
|
withTranslation,
|
||||||
withErrorHandling,
|
withErrorHandling,
|
||||||
|
@ -23,6 +24,7 @@ export default class List extends Component {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.templateTypes = getTemplateTypes(props.t);
|
this.templateTypes = getTemplateTypes(props.t);
|
||||||
|
this.tagLanguages = getTagLanguages(props.t);
|
||||||
|
|
||||||
this.state = {};
|
this.state = {};
|
||||||
tableRestActionDialogInit(this);
|
tableRestActionDialogInit(this);
|
||||||
|
@ -63,12 +65,13 @@ export default class List extends Component {
|
||||||
{ data: 1, title: t('name') },
|
{ data: 1, title: t('name') },
|
||||||
{ data: 2, title: t('description') },
|
{ data: 2, title: t('description') },
|
||||||
{ data: 3, title: t('type'), render: data => this.templateTypes[data].typeName },
|
{ data: 3, title: t('type'), render: data => this.templateTypes[data].typeName },
|
||||||
{ data: 4, title: t('created'), render: data => moment(data).fromNow() },
|
{ data: 4, title: t('Tag language'), render: data => this.tagLanguages[data].name },
|
||||||
{ data: 5, title: t('namespace') },
|
{ data: 5, title: t('created'), render: data => moment(data).fromNow() },
|
||||||
|
{ data: 6, title: t('namespace') },
|
||||||
{
|
{
|
||||||
actions: data => {
|
actions: data => {
|
||||||
const actions = [];
|
const actions = [];
|
||||||
const perms = data[6];
|
const perms = data[7];
|
||||||
|
|
||||||
if (perms.includes('edit')) {
|
if (perms.includes('edit')) {
|
||||||
actions.push({
|
actions.push({
|
||||||
|
|
|
@ -19,12 +19,24 @@ import {getSandboxUrl} from "../lib/urls";
|
||||||
import mailtrainConfig from 'mailtrainConfig';
|
import mailtrainConfig from 'mailtrainConfig';
|
||||||
import {ActionLink, Button} from "../lib/bootstrap-components";
|
import {ActionLink, Button} from "../lib/bootstrap-components";
|
||||||
import {Trans} from "react-i18next";
|
import {Trans} from "react-i18next";
|
||||||
|
import {TagLanguages} from "../../../shared/templates";
|
||||||
|
|
||||||
import styles from "../lib/styles.scss";
|
import styles from "../lib/styles.scss";
|
||||||
|
|
||||||
export const ResourceType = {
|
export const ResourceType = {
|
||||||
TEMPLATE: 'template',
|
TEMPLATE: 'template',
|
||||||
CAMPAIGN: 'campaign'
|
CAMPAIGN: 'campaign'
|
||||||
|
};
|
||||||
|
|
||||||
|
export function getTagLanguages(t) {
|
||||||
|
return {
|
||||||
|
[TagLanguages.SIMPLE]: {
|
||||||
|
name: t('Simple')
|
||||||
|
},
|
||||||
|
[TagLanguages.HBS]: {
|
||||||
|
name: t('Handlebars')
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEMPLATE) {
|
export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEMPLATE) {
|
||||||
|
@ -67,23 +79,29 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
render: data => mosaicoTemplateTypes[data].typeName
|
render: data => mosaicoTemplateTypes[data].typeName
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
data: 5,
|
data: 6,
|
||||||
title: t('namespace')
|
title: t('namespace')
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
templateTypes.mosaico = {
|
templateTypes.mosaico = {
|
||||||
typeName: t('mosaico'),
|
typeName: t('mosaico'),
|
||||||
getTypeForm: (owner, isEdit) =>
|
getTypeForm: (owner, isEdit) => {
|
||||||
<TableSelect
|
const tagLanguageKey = owner.getFormValue(prefix + 'tag_language');
|
||||||
|
if (tagLanguageKey) {
|
||||||
|
return <TableSelect
|
||||||
id={prefix + 'mosaicoTemplate'}
|
id={prefix + 'mosaicoTemplate'}
|
||||||
label={t('mosaicoTemplate')}
|
label={t('mosaicoTemplate')}
|
||||||
withHeader
|
withHeader
|
||||||
dropdown
|
dropdown
|
||||||
dataUrl='rest/mosaico-templates-table'
|
dataUrl={`rest/mosaico-templates-by-tag-language-table/${tagLanguageKey}`}
|
||||||
columns={mosaicoTemplatesColumns}
|
columns={mosaicoTemplatesColumns}
|
||||||
selectionLabelIndex={1}
|
selectionLabelIndex={1}
|
||||||
disabled={isEdit}/>,
|
disabled={isEdit}/>
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
},
|
||||||
getHTMLEditor: owner =>
|
getHTMLEditor: owner =>
|
||||||
<AlignedRow
|
<AlignedRow
|
||||||
label={t('templateContentHtml')}>
|
label={t('templateContentHtml')}>
|
||||||
|
@ -144,6 +162,12 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
afterTypeChange: mutState => {
|
afterTypeChange: mutState => {
|
||||||
initFieldsIfMissing(mutState, 'mosaico');
|
initFieldsIfMissing(mutState, 'mosaico');
|
||||||
},
|
},
|
||||||
|
afterTagLanguageChange: (mutState, isEdit) => {
|
||||||
|
if (!isEdit) {
|
||||||
|
mutState.setIn([prefix + 'mosaicoTemplate', 'value'], null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
isTagLanguageSelectorDisabledForEdit: true,
|
||||||
validate: state => {
|
validate: state => {
|
||||||
const mosaicoTemplate = state.getIn([prefix + 'mosaicoTemplate', 'value']);
|
const mosaicoTemplate = state.getIn([prefix + 'mosaicoTemplate', 'value']);
|
||||||
if (!mosaicoTemplate) {
|
if (!mosaicoTemplate) {
|
||||||
|
@ -230,6 +254,9 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
afterTypeChange: mutState => {
|
afterTypeChange: mutState => {
|
||||||
initFieldsIfMissing(mutState, 'mosaicoWithFsTemplate');
|
initFieldsIfMissing(mutState, 'mosaicoWithFsTemplate');
|
||||||
},
|
},
|
||||||
|
afterTagLanguageChange: (mutState, isEdit) => {
|
||||||
|
},
|
||||||
|
isTagLanguageSelectorDisabledForEdit: false,
|
||||||
validate: state => {
|
validate: state => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -317,6 +344,9 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
afterTypeChange: mutState => {
|
afterTypeChange: mutState => {
|
||||||
initFieldsIfMissing(mutState, 'grapesjs');
|
initFieldsIfMissing(mutState, 'grapesjs');
|
||||||
},
|
},
|
||||||
|
afterTagLanguageChange: (mutState, isEdit) => {
|
||||||
|
},
|
||||||
|
isTagLanguageSelectorDisabledForEdit: false,
|
||||||
validate: state => {
|
validate: state => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -376,6 +406,9 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
afterTypeChange: mutState => {
|
afterTypeChange: mutState => {
|
||||||
initFieldsIfMissing(mutState, 'ckeditor4');
|
initFieldsIfMissing(mutState, 'ckeditor4');
|
||||||
},
|
},
|
||||||
|
afterTagLanguageChange: (mutState, isEdit) => {
|
||||||
|
},
|
||||||
|
isTagLanguageSelectorDisabledForEdit: false,
|
||||||
validate: state => {
|
validate: state => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -460,6 +493,9 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
afterTypeChange: mutState => {
|
afterTypeChange: mutState => {
|
||||||
initFieldsIfMissing(mutState, 'codeeditor');
|
initFieldsIfMissing(mutState, 'codeeditor');
|
||||||
},
|
},
|
||||||
|
afterTagLanguageChange: (mutState, isEdit) => {
|
||||||
|
},
|
||||||
|
isTagLanguageSelectorDisabledForEdit: false,
|
||||||
validate: state => {
|
validate: state => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -20,11 +20,12 @@ import {
|
||||||
import {withErrorHandling} from '../../lib/error-handling';
|
import {withErrorHandling} from '../../lib/error-handling';
|
||||||
import {NamespaceSelect, validateNamespace} from '../../lib/namespace';
|
import {NamespaceSelect, validateNamespace} from '../../lib/namespace';
|
||||||
import {DeleteModalDialog} from "../../lib/modals";
|
import {DeleteModalDialog} from "../../lib/modals";
|
||||||
|
import mailtrainConfig from 'mailtrainConfig';
|
||||||
import {getMJMLSample, getVersafix} from "../../../../shared/mosaico-templates";
|
import {getMJMLSample, getVersafix} from "../../../../shared/mosaico-templates";
|
||||||
import {getTemplateTypes, getTemplateTypesOrder} from "./helpers";
|
import {getTemplateTypes, getTemplateTypesOrder} from "./helpers";
|
||||||
import {withComponentMixins} from "../../lib/decorator-helpers";
|
import {withComponentMixins} from "../../lib/decorator-helpers";
|
||||||
import styles from "../../lib/styles.scss";
|
import styles from "../../lib/styles.scss";
|
||||||
|
import {getTagLanguages} from "../helpers";
|
||||||
|
|
||||||
@withComponentMixins([
|
@withComponentMixins([
|
||||||
withTranslation,
|
withTranslation,
|
||||||
|
@ -38,6 +39,7 @@ export default class CUD extends Component {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.templateTypes = getTemplateTypes(props.t);
|
this.templateTypes = getTemplateTypes(props.t);
|
||||||
|
this.tagLanguages = getTagLanguages(props.t);
|
||||||
|
|
||||||
this.typeOptions = [];
|
this.typeOptions = [];
|
||||||
for (const type of getTemplateTypesOrder()) {
|
for (const type of getTemplateTypesOrder()) {
|
||||||
|
@ -63,8 +65,16 @@ export default class CUD extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
submitFormValuesMutator(data) {
|
submitFormValuesMutator(data) {
|
||||||
|
const wizard = this.props.wizard;
|
||||||
|
|
||||||
|
if (wizard === 'versafix') {
|
||||||
|
data.html = getVersafix(data.tag_language);
|
||||||
|
} else if (wizard === 'mjml-sample') {
|
||||||
|
data.mjml = getMJMLSample(data.tag_language);
|
||||||
|
}
|
||||||
|
|
||||||
this.templateTypes[data.type].beforeSave(this, data);
|
this.templateTypes[data.type].beforeSave(this, data);
|
||||||
return filterData(data, ['name', 'description', 'type', 'data', 'namespace']);
|
return filterData(data, ['name', 'description', 'type', 'tag_language', 'data', 'namespace']);
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
@ -80,7 +90,7 @@ export default class CUD extends Component {
|
||||||
description: '',
|
description: '',
|
||||||
namespace: mailtrainConfig.user.namespace,
|
namespace: mailtrainConfig.user.namespace,
|
||||||
type: 'html',
|
type: 'html',
|
||||||
html: getVersafix()
|
tag_language: mailtrainConfig.tagLanguages[0]
|
||||||
});
|
});
|
||||||
|
|
||||||
} else if (wizard === 'mjml-sample') {
|
} else if (wizard === 'mjml-sample') {
|
||||||
|
@ -89,7 +99,7 @@ export default class CUD extends Component {
|
||||||
description: '',
|
description: '',
|
||||||
namespace: mailtrainConfig.user.namespace,
|
namespace: mailtrainConfig.user.namespace,
|
||||||
type: 'mjml',
|
type: 'mjml',
|
||||||
mjml: getMJMLSample()
|
tag_language: mailtrainConfig.tagLanguages[0]
|
||||||
});
|
});
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -98,6 +108,7 @@ export default class CUD extends Component {
|
||||||
description: '',
|
description: '',
|
||||||
namespace: mailtrainConfig.user.namespace,
|
namespace: mailtrainConfig.user.namespace,
|
||||||
type: 'html',
|
type: 'html',
|
||||||
|
tag_language: mailtrainConfig.tagLanguages[0],
|
||||||
html: ''
|
html: ''
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -119,6 +130,12 @@ export default class CUD extends Component {
|
||||||
state.setIn(['type', 'error'], null);
|
state.setIn(['type', 'error'], null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!state.getIn(['tag_language', 'value'])) {
|
||||||
|
state.setIn(['tag_language', 'error'], t('Tag language must be selected'));
|
||||||
|
} else {
|
||||||
|
state.setIn(['tag_language', 'error'], null);
|
||||||
|
}
|
||||||
|
|
||||||
validateNamespace(t, state);
|
validateNamespace(t, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +186,11 @@ export default class CUD extends Component {
|
||||||
|
|
||||||
const typeKey = this.getFormValue('type');
|
const typeKey = this.getFormValue('type');
|
||||||
|
|
||||||
|
const tagLanguageOptions = [];
|
||||||
|
for (const key of mailtrainConfig.tagLanguages) {
|
||||||
|
tagLanguageOptions.push({key, label: this.tagLanguages[key].name});
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{canDelete &&
|
{canDelete &&
|
||||||
|
@ -194,6 +216,9 @@ export default class CUD extends Component {
|
||||||
:
|
:
|
||||||
<Dropdown id="type" label={t('type')} options={this.typeOptions}/>
|
<Dropdown id="type" label={t('type')} options={this.typeOptions}/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<Dropdown id="tag_language" label={t('Tag language')} options={tagLanguageOptions}/>
|
||||||
|
|
||||||
<NamespaceSelect/>
|
<NamespaceSelect/>
|
||||||
|
|
||||||
{isEdit && typeKey && this.templateTypes[typeKey].getForm(this)}
|
{isEdit && typeKey && this.templateTypes[typeKey].getForm(this)}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {withAsyncErrorHandler, withErrorHandling} from '../../lib/error-handling
|
||||||
import {Table} from '../../lib/table';
|
import {Table} from '../../lib/table';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import {getTemplateTypes} from './helpers';
|
import {getTemplateTypes} from './helpers';
|
||||||
|
import {getTagLanguages} from '../helpers';
|
||||||
import {checkPermissions} from "../../lib/permissions";
|
import {checkPermissions} from "../../lib/permissions";
|
||||||
import {tableAddDeleteButton, tableRestActionDialogInit, tableRestActionDialogRender} from "../../lib/modals";
|
import {tableAddDeleteButton, tableRestActionDialogInit, tableRestActionDialogRender} from "../../lib/modals";
|
||||||
import {withComponentMixins} from "../../lib/decorator-helpers";
|
import {withComponentMixins} from "../../lib/decorator-helpers";
|
||||||
|
@ -24,6 +25,7 @@ export default class List extends Component {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.templateTypes = getTemplateTypes(props.t);
|
this.templateTypes = getTemplateTypes(props.t);
|
||||||
|
this.tagLanguages = getTagLanguages(props.t);
|
||||||
|
|
||||||
this.state = {};
|
this.state = {};
|
||||||
tableRestActionDialogInit(this);
|
tableRestActionDialogInit(this);
|
||||||
|
@ -55,12 +57,13 @@ export default class List extends Component {
|
||||||
{ data: 1, title: t('name') },
|
{ data: 1, title: t('name') },
|
||||||
{ data: 2, title: t('description') },
|
{ data: 2, title: t('description') },
|
||||||
{ data: 3, title: t('type'), render: data => this.templateTypes[data].typeName },
|
{ data: 3, title: t('type'), render: data => this.templateTypes[data].typeName },
|
||||||
{ data: 4, title: t('created'), render: data => moment(data).fromNow() },
|
{ data: 4, title: t('Tag language'), render: data => this.tagLanguages[data].name },
|
||||||
{ data: 5, title: t('namespace') },
|
{ data: 5, title: t('created'), render: data => moment(data).fromNow() },
|
||||||
|
{ data: 6, title: t('namespace') },
|
||||||
{
|
{
|
||||||
actions: data => {
|
actions: data => {
|
||||||
const actions = [];
|
const actions = [];
|
||||||
const perms = data[6];
|
const perms = data[7];
|
||||||
|
|
||||||
if (perms.includes('edit')) {
|
if (perms.includes('edit')) {
|
||||||
actions.push({
|
actions.push({
|
||||||
|
|
|
@ -20,9 +20,7 @@ export function getTemplateTypes(t) {
|
||||||
return owner.getFormValue('mjml') || '';
|
return owner.getFormValue('mjml') || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateHtmlFromMjml(owner) {
|
function generateHtmlFromMjml(mjml) {
|
||||||
const mjml = getMjml(owner);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = mjml2html(mjml);
|
const res = mjml2html(mjml);
|
||||||
return res.html;
|
return res.html;
|
||||||
|
@ -88,7 +86,7 @@ export function getTemplateTypes(t) {
|
||||||
typeName: t('mjml'),
|
typeName: t('mjml'),
|
||||||
getForm: owner => (
|
getForm: owner => (
|
||||||
<>
|
<>
|
||||||
<ContentModalDialog visible={!!owner.state.exportModalVisible} title={t('html')} getContentAsync={async () => generateHtmlFromMjml(owner)} onHide={() => setExportModalVisibility(owner, false)}/>
|
<ContentModalDialog visible={!!owner.state.exportModalVisible} title={t('html')} getContentAsync={async () => generateHtmlFromMjml(getMjml(owner))} onHide={() => setExportModalVisibility(owner, false)}/>
|
||||||
<ACEEditor id="mjml" height="700px" mode="xml" label={t('templateContent')}/>
|
<ACEEditor id="mjml" height="700px" mode="xml" label={t('templateContent')}/>
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
|
@ -98,7 +96,7 @@ export function getTemplateTypes(t) {
|
||||||
beforeSave: (owner, data) => {
|
beforeSave: (owner, data) => {
|
||||||
data.data = {
|
data.data = {
|
||||||
mjml: data.mjml,
|
mjml: data.mjml,
|
||||||
html: generateHtmlFromMjml(owner)
|
html: generateHtmlFromMjml(data.mjml)
|
||||||
};
|
};
|
||||||
|
|
||||||
clearBeforeSend(data);
|
clearBeforeSend(data);
|
||||||
|
|
|
@ -36,6 +36,11 @@ editors:
|
||||||
- ckeditor4
|
- ckeditor4
|
||||||
- codeeditor
|
- codeeditor
|
||||||
|
|
||||||
|
# Enabled tag languages
|
||||||
|
tagLanguages:
|
||||||
|
- simple # e.g. [FIRST_NAME] - this the style of merge tags found in Mailtrain v1
|
||||||
|
- hbs # e.g. {{#if FIRST_NAME}}Hello {{firstName}}!{{else}}Hello!{{/if}} - this syntax uses Handlebars templating language (http://handlebarsjs.com)
|
||||||
|
|
||||||
# Default language to use
|
# Default language to use
|
||||||
defaultLanguage: en-US
|
defaultLanguage: en-US
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ async function getAuthenticatedConfig(context) {
|
||||||
},
|
},
|
||||||
globalPermissions,
|
globalPermissions,
|
||||||
editors: config.editors,
|
editors: config.editors,
|
||||||
|
tagLanguages: config.tagLanguages,
|
||||||
mosaico: config.mosaico,
|
mosaico: config.mosaico,
|
||||||
verpEnabled: config.verp.enabled,
|
verpEnabled: config.verp.enabled,
|
||||||
reportsEnabled: config.reports.enabled,
|
reportsEnabled: config.reports.enabled,
|
||||||
|
|
|
@ -10,6 +10,7 @@ const shares = require('./shares');
|
||||||
const namespaceHelpers = require('../lib/namespace-helpers');
|
const namespaceHelpers = require('../lib/namespace-helpers');
|
||||||
const files = require('./files');
|
const files = require('./files');
|
||||||
const templates = require('./templates');
|
const templates = require('./templates');
|
||||||
|
const { allTagLanguages } = require('../../shared/templates');
|
||||||
const { CampaignStatus, CampaignSource, CampaignType, getSendConfigurationPermissionRequiredForSend } = require('../../shared/campaigns');
|
const { CampaignStatus, CampaignSource, CampaignType, getSendConfigurationPermissionRequiredForSend } = require('../../shared/campaigns');
|
||||||
const sendConfigurations = require('./send-configurations');
|
const sendConfigurations = require('./send-configurations');
|
||||||
const triggers = require('./triggers');
|
const triggers = require('./triggers');
|
||||||
|
@ -445,6 +446,10 @@ async function _validateAndPreprocess(tx, context, entity, isCreate, content) {
|
||||||
|
|
||||||
await shares.enforceEntityPermissionTx(tx, context, 'sendConfiguration', entity.send_configuration, 'viewPublic');
|
await shares.enforceEntityPermissionTx(tx, context, 'sendConfiguration', entity.send_configuration, 'viewPublic');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((isCreate && entity.source === CampaignSource.CUSTOM) || (content === Content.ONLY_SOURCE_CUSTOM)) {
|
||||||
|
enforce(allTagLanguages.includes(entity.data.sourceCustom.tag_language), `Invalid tag language '${entity.data.sourceCustom.tag_language}'`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _createTx(tx, context, entity, content) {
|
async function _createTx(tx, context, entity, content) {
|
||||||
|
@ -462,6 +467,7 @@ async function _createTx(tx, context, entity, content) {
|
||||||
|
|
||||||
entity.data.sourceCustom = {
|
entity.data.sourceCustom = {
|
||||||
type: template.type,
|
type: template.type,
|
||||||
|
tag_language: template.tag_language,
|
||||||
data: template.data,
|
data: template.data,
|
||||||
html: template.html,
|
html: template.html,
|
||||||
text: template.text
|
text: template.text
|
||||||
|
|
|
@ -9,8 +9,9 @@ const namespaceHelpers = require('../lib/namespace-helpers');
|
||||||
const shares = require('./shares');
|
const shares = require('./shares');
|
||||||
const files = require('./files');
|
const files = require('./files');
|
||||||
const dependencyHelpers = require('../lib/dependency-helpers');
|
const dependencyHelpers = require('../lib/dependency-helpers');
|
||||||
|
const { allTagLanguages } = require('../../shared/templates');
|
||||||
|
|
||||||
const allowedKeys = new Set(['name', 'description', 'type', 'data', 'namespace']);
|
const allowedKeys = new Set(['name', 'description', 'type', 'tag_language', 'data', 'namespace']);
|
||||||
|
|
||||||
function hash(entity) {
|
function hash(entity) {
|
||||||
return hasher.hash(filterObject(entity, allowedKeys));
|
return hasher.hash(filterObject(entity, allowedKeys));
|
||||||
|
@ -32,11 +33,25 @@ async function listDTAjax(context, params) {
|
||||||
[{ entityTypeId: 'mosaicoTemplate', requiredOperations: ['view'] }],
|
[{ entityTypeId: 'mosaicoTemplate', requiredOperations: ['view'] }],
|
||||||
params,
|
params,
|
||||||
builder => builder.from('mosaico_templates').innerJoin('namespaces', 'namespaces.id', 'mosaico_templates.namespace'),
|
builder => builder.from('mosaico_templates').innerJoin('namespaces', 'namespaces.id', 'mosaico_templates.namespace'),
|
||||||
[ 'mosaico_templates.id', 'mosaico_templates.name', 'mosaico_templates.description', 'mosaico_templates.type', 'mosaico_templates.created', 'namespaces.name' ]
|
[ 'mosaico_templates.id', 'mosaico_templates.name', 'mosaico_templates.description', 'mosaico_templates.type', 'mosaico_templates.tag_language', 'mosaico_templates.created', 'namespaces.name' ]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function listByTagLanguageDTAjax(context, tagLanguage, params) {
|
||||||
|
return await dtHelpers.ajaxListWithPermissions(
|
||||||
|
context,
|
||||||
|
[{ entityTypeId: 'mosaicoTemplate', requiredOperations: ['view'] }],
|
||||||
|
params,
|
||||||
|
builder => builder.from('mosaico_templates')
|
||||||
|
.innerJoin('namespaces', 'namespaces.id', 'mosaico_templates.namespace')
|
||||||
|
.where('mosaico_templates.tag_language', tagLanguage),
|
||||||
|
[ 'mosaico_templates.id', 'mosaico_templates.name', 'mosaico_templates.description', 'mosaico_templates.type', 'mosaico_templates.tag_language', 'mosaico_templates.created', 'namespaces.name' ]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function _validateAndPreprocess(tx, entity) {
|
async function _validateAndPreprocess(tx, entity) {
|
||||||
|
enforce(allTagLanguages.includes(entity.tag_language), `Invalid tag language '${entity.tag_language}'`);
|
||||||
|
|
||||||
entity.data = JSON.stringify(entity.data);
|
entity.data = JSON.stringify(entity.data);
|
||||||
await namespaceHelpers.validateEntity(tx, entity);
|
await namespaceHelpers.validateEntity(tx, entity);
|
||||||
}
|
}
|
||||||
|
@ -119,6 +134,7 @@ async function remove(context, id) {
|
||||||
module.exports.hash = hash;
|
module.exports.hash = hash;
|
||||||
module.exports.getById = getById;
|
module.exports.getById = getById;
|
||||||
module.exports.listDTAjax = listDTAjax;
|
module.exports.listDTAjax = listDTAjax;
|
||||||
|
module.exports.listByTagLanguageDTAjax = listByTagLanguageDTAjax;
|
||||||
module.exports.create = create;
|
module.exports.create = create;
|
||||||
module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck;
|
module.exports.updateWithConsistencyCheck = updateWithConsistencyCheck;
|
||||||
module.exports.remove = remove;
|
module.exports.remove = remove;
|
||||||
|
|
|
@ -14,11 +14,11 @@ const {convertFileURLs} = require('../lib/campaign-content');
|
||||||
const mailers = require('../lib/mailers');
|
const mailers = require('../lib/mailers');
|
||||||
const tools = require('../lib/tools');
|
const tools = require('../lib/tools');
|
||||||
const sendConfigurations = require('./send-configurations');
|
const sendConfigurations = require('./send-configurations');
|
||||||
const { getMergeTagsForBases } = require('../../shared/templates');
|
const { getMergeTagsForBases, allTagLanguages } = require('../../shared/templates');
|
||||||
const { getTrustedUrl, getSandboxUrl, getPublicUrl } = require('../lib/urls');
|
const { getTrustedUrl, getSandboxUrl, getPublicUrl } = require('../lib/urls');
|
||||||
const htmlToText = require('html-to-text');
|
const htmlToText = require('html-to-text');
|
||||||
|
|
||||||
const allowedKeys = new Set(['name', 'description', 'type', 'data', 'html', 'text', 'namespace']);
|
const allowedKeys = new Set(['name', 'description', 'type', 'tag_language', 'data', 'html', 'text', 'namespace']);
|
||||||
|
|
||||||
function hash(entity) {
|
function hash(entity) {
|
||||||
return hasher.hash(filterObject(entity, allowedKeys));
|
return hasher.hash(filterObject(entity, allowedKeys));
|
||||||
|
@ -54,7 +54,7 @@ async function _listDTAjax(context, namespaceId, params) {
|
||||||
}
|
}
|
||||||
return builder;
|
return builder;
|
||||||
},
|
},
|
||||||
[ 'templates.id', 'templates.name', 'templates.description', 'templates.type', 'templates.created', 'namespaces.name' ]
|
[ 'templates.id', 'templates.name', 'templates.description', 'templates.type', 'templates.tag_language', 'templates.created', 'namespaces.name' ]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +69,8 @@ async function listByNamespaceDTAjax(context, namespaceId, params) {
|
||||||
async function _validateAndPreprocess(tx, entity) {
|
async function _validateAndPreprocess(tx, entity) {
|
||||||
await namespaceHelpers.validateEntity(tx, entity);
|
await namespaceHelpers.validateEntity(tx, entity);
|
||||||
|
|
||||||
|
enforce(allTagLanguages.includes(entity.tag_language), `Invalid tag language '${entity.tag_language}'`);
|
||||||
|
|
||||||
// We don't check contents of the "data" because it is processed solely on the client. The client generates the HTML code we use when sending out campaigns.
|
// We don't check contents of the "data" because it is processed solely on the client. The client generates the HTML code we use when sending out campaigns.
|
||||||
|
|
||||||
entity.data = JSON.stringify(entity.data);
|
entity.data = JSON.stringify(entity.data);
|
||||||
|
@ -82,6 +84,7 @@ async function create(context, entity) {
|
||||||
const template = await getByIdTx(tx, context, entity.sourceTemplate, false);
|
const template = await getByIdTx(tx, context, entity.sourceTemplate, false);
|
||||||
|
|
||||||
entity.type = template.type;
|
entity.type = template.type;
|
||||||
|
entity.tag_language = template.tag_language;
|
||||||
entity.data = template.data;
|
entity.data = template.data;
|
||||||
entity.html = template.html;
|
entity.html = template.html;
|
||||||
entity.text = template.text;
|
entity.text = template.text;
|
||||||
|
|
|
@ -33,4 +33,9 @@ router.postAsync('/mosaico-templates-table', passport.loggedIn, async (req, res)
|
||||||
return res.json(await mosaicoTemplates.listDTAjax(req.context, req.body));
|
return res.json(await mosaicoTemplates.listDTAjax(req.context, req.body));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.postAsync('/mosaico-templates-by-tag-language-table/:tagLanguage', passport.loggedIn, async (req, res) => {
|
||||||
|
return res.json(await mosaicoTemplates.listByTagLanguageDTAjax(req.context, req.params.tagLanguage, req.body));
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
module.exports = router;
|
module.exports = router;
|
|
@ -2,6 +2,7 @@ const { CampaignSource, CampaignType} = require('../../../../shared/campaigns');
|
||||||
const files = require('../../../models/files');
|
const files = require('../../../models/files');
|
||||||
const contextHelpers = require('../../../lib/context-helpers');
|
const contextHelpers = require('../../../lib/context-helpers');
|
||||||
const mosaicoTemplates = require('../../../../shared/mosaico-templates');
|
const mosaicoTemplates = require('../../../../shared/mosaico-templates');
|
||||||
|
const {TagLanguages} = require('../../../../shared/templates');
|
||||||
const {getGlobalNamespaceId} = require('../../../../shared/namespaces');
|
const {getGlobalNamespaceId} = require('../../../../shared/namespaces');
|
||||||
const {getAdminId} = require('../../../../shared/users');
|
const {getAdminId} = require('../../../../shared/users');
|
||||||
const { MailerType, ZoneMTAType, getSystemSendConfigurationId, getSystemSendConfigurationCid } = require('../../../../shared/send-configurations');
|
const { MailerType, ZoneMTAType, getSystemSendConfigurationId, getSystemSendConfigurationCid } = require('../../../../shared/send-configurations');
|
||||||
|
@ -905,7 +906,7 @@ async function addMosaicoTemplates(knex) {
|
||||||
type: 'html',
|
type: 'html',
|
||||||
namespace: 1,
|
namespace: 1,
|
||||||
data: JSON.stringify({
|
data: JSON.stringify({
|
||||||
html: mosaicoTemplates.getVersafix()
|
html: mosaicoTemplates.getVersafix(TagLanguages.SIMPLE)
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ exports.up = (knex, Promise) => (async() => {
|
||||||
data.listId = queuedEntry.list;
|
data.listId = queuedEntry.list;
|
||||||
data.subscriptionId = queuedEntry.subscription;
|
data.subscriptionId = queuedEntry.subscription;
|
||||||
|
|
||||||
knex('queued')
|
await knex('queued')
|
||||||
.where('id', queuedEntry.id)
|
.where('id', queuedEntry.id)
|
||||||
.update({
|
.update({
|
||||||
data: JSON.stringify(data)
|
data: JSON.stringify(data)
|
||||||
|
|
41
server/setup/knex/migrations/20190630210000_tag_language.js
Normal file
41
server/setup/knex/migrations/20190630210000_tag_language.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
const { CampaignSource } = require('../../../../shared/campaigns');
|
||||||
|
const { TagLanguages } = require('../../../../shared/templates');
|
||||||
|
|
||||||
|
exports.up = (knex, Promise) => (async() => {
|
||||||
|
await knex.schema.table('templates', table => {
|
||||||
|
table.string('tag_language', 48);
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex('templates').update({
|
||||||
|
tag_language: 'simple'
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.table('templates', table => {
|
||||||
|
table.string('tag_language', 48).notNullable().index().alter();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
await knex.schema.table('mosaico_templates', table => {
|
||||||
|
table.string('tag_language', 48);
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex('mosaico_templates').update({
|
||||||
|
tag_language: TagLanguages.SIMPLE
|
||||||
|
});
|
||||||
|
|
||||||
|
await knex.schema.table('mosaico_templates', table => {
|
||||||
|
table.string('tag_language', 48).notNullable().index().alter();
|
||||||
|
});
|
||||||
|
|
||||||
|
const rows = await knex('campaigns').whereIn('source', [CampaignSource.CUSTOM, CampaignSource.CUSTOM_FROM_CAMPAIGN, CampaignSource.CUSTOM_FROM_TEMPLATE]);
|
||||||
|
for (const row of rows) {
|
||||||
|
const data = JSON.parse(row.data);
|
||||||
|
|
||||||
|
data.sourceCustom.tag_language = TagLanguages.SIMPLE;
|
||||||
|
|
||||||
|
await knex('campaigns').where('id', row.id).update({data: JSON.stringify(data)});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
exports.down = (knex, Promise) => (async() => {
|
||||||
|
})();
|
|
@ -1,5 +1,18 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const {TagLanguages} = require('./templates');
|
||||||
|
|
||||||
|
function renderTag(tagLanguage, tag) {
|
||||||
|
if (tagLanguage === TagLanguages.SIMPLE) {
|
||||||
|
return `[${tag}]`;
|
||||||
|
} else if (tagLanguage === TagLanguages.HBS) {
|
||||||
|
return `{{${tag}}}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getVersafix(tagLanguage) {
|
||||||
|
const tg = tag => renderTag(tagLanguage, tag);
|
||||||
|
|
||||||
const versafix = '<!DOCTYPE html>\n' +
|
const versafix = '<!DOCTYPE html>\n' +
|
||||||
'<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">\n' +
|
'<html xmlns="http://www.w3.org/1999/xhtml" xmlns:o="urn:schemas-microsoft-com:office:office">\n' +
|
||||||
'<head>\n' +
|
'<head>\n' +
|
||||||
|
@ -1594,6 +1607,11 @@ const versafix = '<!DOCTYPE html>\n' +
|
||||||
' \n' +
|
' \n' +
|
||||||
'</center><!--[if !(gte mso 16)]--></body><!--<![endif]--></html>';
|
'</center><!--[if !(gte mso 16)]--></body><!--<![endif]--></html>';
|
||||||
|
|
||||||
|
return versafix;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMJMLSample(tagLanguage) {
|
||||||
|
const tg = tag => renderTag(tagLanguage, tag);
|
||||||
|
|
||||||
const mjmlSample = '<mjml>\n' +
|
const mjmlSample = '<mjml>\n' +
|
||||||
' <mj-head>\n' +
|
' <mj-head>\n' +
|
||||||
|
@ -1701,10 +1719,10 @@ const mjmlSample = '<mjml>\n' +
|
||||||
' \n' +
|
' \n' +
|
||||||
' <mj-section mj-class="header-section">\n' +
|
' <mj-section mj-class="header-section">\n' +
|
||||||
' <mj-column>\n' +
|
' <mj-column>\n' +
|
||||||
' <mj-text align="left"><a href="https://lists.example.org/subscription/[LIST_ID]?locale=en-US">Subscribe</a></mj-text>\n' +
|
' <mj-text align="left"><a href="https://lists.example.org/subscription/' + tg('LIST_ID') + '?locale=en-US">Subscribe</a></mj-text>\n' +
|
||||||
' </mj-column>\n' +
|
' </mj-column>\n' +
|
||||||
' <mj-column>\n' +
|
' <mj-column>\n' +
|
||||||
' <mj-text align="right"><a href="[LINK_BROWSER]">View in browser</a></mj-text>\n' +
|
' <mj-text align="right"><a href="' + tg('LINK_BROWSER') + '">View in browser</a></mj-text>\n' +
|
||||||
' </mj-column>\n' +
|
' </mj-column>\n' +
|
||||||
' </mj-section>\n' +
|
' </mj-section>\n' +
|
||||||
' \n' +
|
' \n' +
|
||||||
|
@ -1839,8 +1857,8 @@ const mjmlSample = '<mjml>\n' +
|
||||||
' <mj-section mj-class="links-section">\n' +
|
' <mj-section mj-class="links-section">\n' +
|
||||||
' <mj-column>\n' +
|
' <mj-column>\n' +
|
||||||
' <mj-social border-radius="5px">\n' +
|
' <mj-social border-radius="5px">\n' +
|
||||||
' <mj-social-element name="facebook" href="[LINK_BROWSER]">Share on Facebook</mj-social-element>\n' +
|
' <mj-social-element name="facebook" href="' + tg('LINK_BROWSER') + '">Share on Facebook</mj-social-element>\n' +
|
||||||
' <mj-social-element name="twitter" href="[LINK_BROWSER]">Tweet</mj-social-element>\n' +
|
' <mj-social-element name="twitter" href="' + tg('LINK_BROWSER') + '">Tweet</mj-social-element>\n' +
|
||||||
' </mj-social> \n' +
|
' </mj-social> \n' +
|
||||||
' </mj-column>\n' +
|
' </mj-column>\n' +
|
||||||
' </mj-section>\n' +
|
' </mj-section>\n' +
|
||||||
|
@ -1848,8 +1866,8 @@ const mjmlSample = '<mjml>\n' +
|
||||||
' <mj-section mj-class="footer-section">\n' +
|
' <mj-section mj-class="footer-section">\n' +
|
||||||
' <mj-column>\n' +
|
' <mj-column>\n' +
|
||||||
' <mj-text mj-class="footer-text">\n' +
|
' <mj-text mj-class="footer-text">\n' +
|
||||||
' <p>This email was sent to <a href="mailto:[EMAIL]">[EMAIL]</a><p>\n' +
|
' <p>This email was sent to <a href="mailto:' + tg('EMAIL') + '">' + tg('EMAIL') + '</a><p>\n' +
|
||||||
' <p> <a href="[LINK_UNSUBSCRIBE]">Unsubscribe from this list</a> <a href="[LINK_PREFERENCES]">Update subscription preferences</a> </p>\n' +
|
' <p> <a href="' + tg('LINK_UNSUBSCRIBE') + '">Unsubscribe from this list</a> <a href="' + tg('LINK_PREFERENCES') + '">Update subscription preferences</a> </p>\n' +
|
||||||
' <p>Your address XXXXXX</p>\n' +
|
' <p>Your address XXXXXX</p>\n' +
|
||||||
' </mj-text>\n' +
|
' </mj-text>\n' +
|
||||||
' </mj-column>\n' +
|
' </mj-column>\n' +
|
||||||
|
@ -1858,12 +1876,6 @@ const mjmlSample = '<mjml>\n' +
|
||||||
' </mj-body>\n' +
|
' </mj-body>\n' +
|
||||||
'</mjml>';
|
'</mjml>';
|
||||||
|
|
||||||
|
|
||||||
function getVersafix() {
|
|
||||||
return versafix;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMJMLSample() {
|
|
||||||
return mjmlSample;
|
return mjmlSample;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const TagLanguages = {
|
||||||
|
SIMPLE: 'simple',
|
||||||
|
HBS: 'hbs'
|
||||||
|
};
|
||||||
|
|
||||||
|
const allTagLanguages = [TagLanguages.SIMPLE, TagLanguages.HBS];
|
||||||
|
|
||||||
function _getBases(trustedBaseUrl, sandboxBaseUrl, publicBaseUrl) {
|
function _getBases(trustedBaseUrl, sandboxBaseUrl, publicBaseUrl) {
|
||||||
if (trustedBaseUrl.endsWith('/')) {
|
if (trustedBaseUrl.endsWith('/')) {
|
||||||
trustedBaseUrl = trustedBaseUrl.substring(0, trustedBaseUrl.length - 1);
|
trustedBaseUrl = trustedBaseUrl.substring(0, trustedBaseUrl.length - 1);
|
||||||
|
@ -58,5 +65,7 @@ function unbase(text, trustedBaseUrl, sandboxBaseUrl, publicBaseUrl, treatAllAsP
|
||||||
module.exports = {
|
module.exports = {
|
||||||
base,
|
base,
|
||||||
unbase,
|
unbase,
|
||||||
getMergeTagsForBases
|
getMergeTagsForBases,
|
||||||
|
TagLanguages,
|
||||||
|
allTagLanguages
|
||||||
};
|
};
|
Loading…
Add table
Add a link
Reference in a new issue