Removed CKEditor 5 because it was of little use and doubled the code size of root.js
Word wrap and Save to code editor.
This commit is contained in:
parent
a993c06aaf
commit
3bb235a585
12 changed files with 417 additions and 82 deletions
|
@ -17,7 +17,7 @@ import ACEEditorRaw from 'react-ace';
|
|||
import 'brace/theme/github';
|
||||
import 'brace/ext/searchbox';
|
||||
|
||||
import CKEditorRaw from './ckeditor5';
|
||||
//import CKEditorRaw from './ckeditor5';
|
||||
|
||||
import DayPicker from 'react-day-picker';
|
||||
import 'react-day-picker/lib/style.css';
|
||||
|
@ -863,6 +863,7 @@ class ACEEditor extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
/* Excluded. It's not very useful and just eats a lot of space in the resulting JS.
|
||||
|
||||
class CKEditor extends Component {
|
||||
static propTypes = {
|
||||
|
@ -893,6 +894,7 @@ class CKEditor extends Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
function withForm(target) {
|
||||
|
|
|
@ -167,7 +167,7 @@ export class DeleteModalDialog extends Component {
|
|||
render() {
|
||||
const t = this.props.t;
|
||||
const owner = this.props.stateOwner;
|
||||
const name = owner.getFormValue('name');
|
||||
const name = owner.getFormValue('name') || '';
|
||||
|
||||
return <RestActionModalDialog
|
||||
title={t('confirmDeletion')}
|
||||
|
|
|
@ -52,7 +52,7 @@ $editorNormalHeight: 800px !default;
|
|||
height: $navbarHeight;
|
||||
}
|
||||
|
||||
.btn {
|
||||
.btn, .btnDisabled {
|
||||
display: block;
|
||||
float: right;
|
||||
padding: 0px 15px;
|
||||
|
@ -68,4 +68,10 @@ $editorNormalHeight: 800px !default;
|
|||
.btn:hover {
|
||||
background-color: #b1381e;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btnDisabled {
|
||||
color: #581c00;
|
||||
cursor: default;
|
||||
}
|
||||
|
|
|
@ -82,7 +82,8 @@ class CodeEditorSandbox extends Component {
|
|||
|
||||
this.state = {
|
||||
source,
|
||||
preview: props.initialPreview
|
||||
preview: props.initialPreview,
|
||||
wrapEnabled: props.initialWrap
|
||||
};
|
||||
this.state.previewContents = this.getHtml();
|
||||
|
||||
|
@ -97,7 +98,8 @@ class CodeEditorSandbox extends Component {
|
|||
entityId: PropTypes.number,
|
||||
initialSource: PropTypes.string,
|
||||
sourceType: PropTypes.string,
|
||||
initialPreview: PropTypes.bool
|
||||
initialPreview: PropTypes.bool,
|
||||
initialWrap: PropTypes.bool
|
||||
}
|
||||
|
||||
async exportState(method, params) {
|
||||
|
@ -116,9 +118,16 @@ class CodeEditorSandbox extends Component {
|
|||
});
|
||||
}
|
||||
|
||||
async setWrap(method, wrap) {
|
||||
this.setState({
|
||||
wrapEnabled: wrap
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
parentRPC.setMethodHandler('exportState', ::this.exportState);
|
||||
parentRPC.setMethodHandler('setPreview', ::this.setPreview);
|
||||
parentRPC.setMethodHandler('setWrap', ::this.setWrap);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -169,13 +178,14 @@ class CodeEditorSandbox extends Component {
|
|||
showPrintMargin={false}
|
||||
value={this.state.source}
|
||||
tabSize={2}
|
||||
wrapEnabled={this.state.wrapEnabled}
|
||||
setOptions={{useWorker: false}} // This disables syntax check because it does not always work well (e.g. in case of JS code in report templates)
|
||||
/>
|
||||
</div>
|
||||
{
|
||||
this.state.preview &&
|
||||
<div className={styles.preview}>
|
||||
<iframe src={"data:text/html;charset=utf-8," + escape(this.state.previewContents)}></iframe>
|
||||
<iframe ref={node => this.previewNode = node} src={"data:text/html;charset=utf-8," + escape(this.state.previewContents)}></iframe>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
|
|
@ -18,7 +18,8 @@ export class CodeEditorHost extends Component {
|
|||
|
||||
this.state = {
|
||||
fullscreen: false,
|
||||
preview: true
|
||||
preview: true,
|
||||
wrap: true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,6 +30,7 @@ export class CodeEditorHost extends Component {
|
|||
sourceType: PropTypes.string,
|
||||
title: PropTypes.string,
|
||||
onTestSend: PropTypes.func,
|
||||
onSave: PropTypes.func,
|
||||
onFullscreenAsync: PropTypes.func
|
||||
}
|
||||
|
||||
|
@ -49,6 +51,15 @@ export class CodeEditorHost extends Component {
|
|||
await this.contentNode.ask('setPreview', preview);
|
||||
}
|
||||
|
||||
async toggleWrapAsync() {
|
||||
const wrap = !this.state.wrap;
|
||||
this.setState({
|
||||
wrap
|
||||
});
|
||||
|
||||
await this.contentNode.ask('setWrap', wrap);
|
||||
}
|
||||
|
||||
async exportState() {
|
||||
return await this.contentNode.ask('exportState');
|
||||
}
|
||||
|
@ -61,7 +72,8 @@ export class CodeEditorHost extends Component {
|
|||
entityId: this.props.entity.id,
|
||||
initialSource: this.props.initialSource,
|
||||
sourceType: this.props.sourceType,
|
||||
initialPreview: this.state.preview
|
||||
initialPreview: this.state.preview,
|
||||
initialWrap: this.state.wrap
|
||||
};
|
||||
|
||||
const tokenData = {
|
||||
|
@ -77,6 +89,8 @@ export class CodeEditorHost extends Component {
|
|||
<a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a>
|
||||
<a className={styles.btn} onClick={this.props.onTestSend}><Icon icon="send"/></a>
|
||||
<a className={styles.btn} onClick={::this.togglePreviewAsync}><Icon icon={this.state.preview ? 'eye-close': 'eye-open'}/></a>
|
||||
{this.props.canSave ? <a className={styles.btn} onClick={this.props.onSave}><Icon icon="floppy-disk"/></a> : <span className={styles.btnDisabled}><Icon icon="floppy-disk"/></span>}
|
||||
<a className={styles.btn} onClick={::this.toggleWrapAsync}>{this.state.wrap ? 'WRAP': 'NOWRAP'}</a>
|
||||
</div>
|
||||
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="codeeditor/editor" tokenMethod="codeeditor" tokenParams={tokenData}/>
|
||||
</div>
|
||||
|
|
|
@ -59,6 +59,9 @@ export class UntrustedContentHost extends Component {
|
|||
const msg = evt.data;
|
||||
|
||||
if (msg.type === 'initNeeded') {
|
||||
// It seems that sometime the message that the content node does not arrive. However if the content root notifies us, we just proceed
|
||||
this.contentNodeIsLoaded = true;
|
||||
|
||||
if (this.isInitialized()) {
|
||||
this.sendMessage('init', {
|
||||
accessToken: this.accessToken,
|
||||
|
@ -181,7 +184,7 @@ export class UntrustedContentRoot extends Component {
|
|||
|
||||
this.receiveMessageHandler = ::this.receiveMessage;
|
||||
|
||||
this.periodicTimeoutHandler = ::this.periodicTimeoutHandler;
|
||||
this.periodicTimeoutHandler = ::this.onPeriodicTimeout;
|
||||
this.periodicTimeoutId = 0;
|
||||
|
||||
this.clientHeight = 0;
|
||||
|
@ -192,13 +195,13 @@ export class UntrustedContentRoot extends Component {
|
|||
}
|
||||
|
||||
|
||||
async periodicTimeoutHandler() {
|
||||
onPeriodicTimeout() {
|
||||
const newHeight = document.body.clientHeight;
|
||||
if (this.clientHeight !== newHeight) {
|
||||
this.clientHeight = newHeight;
|
||||
this.sendMessage('clientHeight', newHeight);
|
||||
}
|
||||
this.periodicTimeoutId = setTimeout(this.periodicTimeoutHandler, 250);
|
||||
//this.periodicTimeoutId = setTimeout(this.periodicTimeoutHandler, 250);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@ import ReactDOM
|
|||
from 'react-dom';
|
||||
import {I18nextProvider} from 'react-i18next';
|
||||
import i18n, {withTranslation} from './lib/i18n';
|
||||
|
||||
import account
|
||||
from './account/root';
|
||||
import blacklist
|
||||
|
@ -55,6 +54,7 @@ if (mailtrainConfig.reportsEnabmed) {
|
|||
topLevelMenuKeys.push('reports');
|
||||
}
|
||||
|
||||
|
||||
@withTranslation()
|
||||
class Root extends Component {
|
||||
constructor(props) {
|
||||
|
@ -166,20 +166,20 @@ class Root extends Component {
|
|||
|
||||
structure[''] ={
|
||||
title: t('home'),
|
||||
link: '/',
|
||||
panelComponent: Home,
|
||||
primaryMenuComponent: MainMenu,
|
||||
children: {
|
||||
...lists.getMenus(t),
|
||||
...reports.getMenus(t),
|
||||
...templates.getMenus(t),
|
||||
...namespaces.getMenus(t),
|
||||
...users.getMenus(t),
|
||||
...blacklist.getMenus(t),
|
||||
...account.getMenus(t),
|
||||
...settings.getMenus(t),
|
||||
...sendConfigurations.getMenus(t),
|
||||
...campaigns.getMenus(t)
|
||||
link: '/',
|
||||
panelComponent: Home,
|
||||
primaryMenuComponent: MainMenu,
|
||||
children: {
|
||||
...lists.getMenus(t),
|
||||
...reports.getMenus(t),
|
||||
...templates.getMenus(t),
|
||||
...namespaces.getMenus(t),
|
||||
...users.getMenus(t),
|
||||
...blacklist.getMenus(t),
|
||||
...account.getMenus(t),
|
||||
...settings.getMenus(t),
|
||||
...sendConfigurations.getMenus(t),
|
||||
...campaigns.getMenus(t)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -118,7 +118,7 @@ export default class CUD extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
async submitHandler() {
|
||||
async doSave(stayOnPage) {
|
||||
const t = this.props.t;
|
||||
|
||||
let exportedData = {};
|
||||
|
@ -145,8 +145,14 @@ export default class CUD extends Component {
|
|||
});
|
||||
|
||||
if (submitResponse) {
|
||||
if (this.props.entity) {
|
||||
if (stayOnPage) {
|
||||
this.enableForm();
|
||||
this.clearFormStatusMessage();
|
||||
this.setFlashMessage('success', t('templateSaved'));
|
||||
|
||||
} else if (this.props.entity) {
|
||||
this.navigateToWithFlashMessage('/templates', 'success', t('templateSaved'));
|
||||
|
||||
} else {
|
||||
this.navigateToWithFlashMessage(`/templates/${submitResponse}/edit`, 'success', t('templateSaved'));
|
||||
}
|
||||
|
@ -156,6 +162,14 @@ export default class CUD extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
await this.doSave(true);
|
||||
}
|
||||
|
||||
async submitHandler() {
|
||||
await this.doSave(false);
|
||||
}
|
||||
|
||||
async extractPlainText() {
|
||||
const typeKey = this.getFormValue('type');
|
||||
const exportedData = await this.templateTypes[typeKey].exportHTMLEditorData(this);
|
||||
|
|
|
@ -5,7 +5,6 @@ import React
|
|||
import {
|
||||
ACEEditor,
|
||||
AlignedRow,
|
||||
CKEditor,
|
||||
Dropdown,
|
||||
StaticField,
|
||||
TableSelect
|
||||
|
@ -80,18 +79,40 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
|
||||
const mosaicoTemplateTypes = getMosaicoTemplateTypes(t);
|
||||
const mosaicoTemplatesColumns = [
|
||||
{ data: 1, title: t('name') },
|
||||
{ data: 2, title: t('description') },
|
||||
{ data: 3, title: t('type'), render: data => mosaicoTemplateTypes[data].typeName },
|
||||
{ data: 5, title: t('namespace') },
|
||||
{
|
||||
data: 1,
|
||||
title: t('name')
|
||||
},
|
||||
{
|
||||
data: 2,
|
||||
title: t('description')
|
||||
},
|
||||
{
|
||||
data: 3,
|
||||
title: t('type'),
|
||||
render: data => mosaicoTemplateTypes[data].typeName
|
||||
},
|
||||
{
|
||||
data: 5,
|
||||
title: t('namespace')
|
||||
},
|
||||
];
|
||||
|
||||
templateTypes.mosaico = {
|
||||
typeName: t('mosaico'),
|
||||
getTypeForm: (owner, isEdit) =>
|
||||
<TableSelect id={prefix + 'mosaicoTemplate'} label={t('mosaicoTemplate')} withHeader dropdown dataUrl='rest/mosaico-templates-table' columns={mosaicoTemplatesColumns} selectionLabelIndex={1} disabled={isEdit} />,
|
||||
<TableSelect
|
||||
id={prefix + 'mosaicoTemplate'}
|
||||
label={t('mosaicoTemplate')}
|
||||
withHeader
|
||||
dropdown
|
||||
dataUrl='rest/mosaico-templates-table'
|
||||
columns={mosaicoTemplatesColumns}
|
||||
selectionLabelIndex={1}
|
||||
disabled={isEdit}/>,
|
||||
getHTMLEditor: owner =>
|
||||
<AlignedRow label={t('templateContentHtml')}>
|
||||
<AlignedRow
|
||||
label={t('templateContentHtml')}>
|
||||
<MosaicoHost
|
||||
ref={node => owner.editorNode = node}
|
||||
entity={owner.props.entity}
|
||||
|
@ -151,13 +172,20 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
typeName: t('mosaicoWithPredefinedTemplates'),
|
||||
getTypeForm: (owner, isEdit) => {
|
||||
if (isEdit) {
|
||||
return <StaticField id={prefix + 'mosaicoFsTemplate'} className={styles.formDisabled} label={t('mosaicoTemplate-1')}>{mosaicoFsTemplatesLabels.get(owner.getFormValue(prefix + 'mosaicoFsTemplate'))}</StaticField>;
|
||||
return <StaticField
|
||||
id={prefix + 'mosaicoFsTemplate'}
|
||||
className={styles.formDisabled}
|
||||
label={t('mosaicoTemplate-1')}>{mosaicoFsTemplatesLabels.get(owner.getFormValue(prefix + 'mosaicoFsTemplate'))}</StaticField>;
|
||||
} else {
|
||||
return <Dropdown id={prefix + 'mosaicoFsTemplate'} label={t('mosaicoTemplate-1')} options={mosaicoFsTemplatesOptions}/>;
|
||||
return <Dropdown
|
||||
id={prefix + 'mosaicoFsTemplate'}
|
||||
label={t('mosaicoTemplate-1')}
|
||||
options={mosaicoFsTemplatesOptions}/>;
|
||||
}
|
||||
},
|
||||
getHTMLEditor: owner =>
|
||||
<AlignedRow label={t('templateContentHtml')}>
|
||||
<AlignedRow
|
||||
label={t('templateContentHtml')}>
|
||||
<MosaicoHost
|
||||
ref={node => owner.editorNode = node}
|
||||
entity={owner.props.entity}
|
||||
|
@ -202,13 +230,17 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
afterTypeChange: mutState => {
|
||||
initFieldsIfMissing(mutState, 'mosaicoWithFsTemplate');
|
||||
},
|
||||
validate: state => {}
|
||||
validate: state => {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const grapesJSSourceTypes = getGrapesJSSourceTypeOptions(t);
|
||||
const grapesJSSourceTypeLabels = {};
|
||||
for ({key, label} of grapesJSSourceTypes) {
|
||||
for ({
|
||||
key,
|
||||
label
|
||||
} of grapesJSSourceTypes) {
|
||||
grapesJSSourceTypeLabels[key] = label;
|
||||
}
|
||||
|
||||
|
@ -216,13 +248,20 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
typeName: t('grapesJs'),
|
||||
getTypeForm: (owner, isEdit) => {
|
||||
if (isEdit) {
|
||||
return <StaticField id={prefix + 'grapesJSSourceType'} className={styles.formDisabled} label={t('type')}>{grapesJSSourceTypeLabels[owner.getFormValue(prefix + 'grapesJSSourceType')]}</StaticField>;
|
||||
return <StaticField
|
||||
id={prefix + 'grapesJSSourceType'}
|
||||
className={styles.formDisabled}
|
||||
label={t('type')}>{grapesJSSourceTypeLabels[owner.getFormValue(prefix + 'grapesJSSourceType')]}</StaticField>;
|
||||
} else {
|
||||
return <Dropdown id={prefix + 'grapesJSSourceType'} label={t('type')} options={grapesJSSourceTypes}/>;
|
||||
return <Dropdown
|
||||
id={prefix + 'grapesJSSourceType'}
|
||||
label={t('type')}
|
||||
options={grapesJSSourceTypes}/>;
|
||||
}
|
||||
},
|
||||
getHTMLEditor: owner =>
|
||||
<AlignedRow label={t('templateContentHtml')}>
|
||||
<AlignedRow
|
||||
label={t('templateContentHtml')}>
|
||||
<GrapesJSHost
|
||||
ref={node => owner.editorNode = node}
|
||||
entity={owner.props.entity}
|
||||
|
@ -267,14 +306,16 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
afterTypeChange: mutState => {
|
||||
initFieldsIfMissing(mutState, 'grapesjs');
|
||||
},
|
||||
validate: state => {}
|
||||
validate: state => {
|
||||
}
|
||||
};
|
||||
|
||||
templateTypes.ckeditor4 = {
|
||||
typeName: t('ckEditor4'),
|
||||
getTypeForm: (owner, isEdit) => null,
|
||||
getHTMLEditor: owner =>
|
||||
<AlignedRow label={t('templateContentHtml')}>
|
||||
<AlignedRow
|
||||
label={t('templateContentHtml')}>
|
||||
<CKEditorHost
|
||||
ref={node => owner.editorNode = node}
|
||||
entity={owner.props.entity}
|
||||
|
@ -311,13 +352,21 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
afterTypeChange: mutState => {
|
||||
initFieldsIfMissing(mutState, 'ckeditor4');
|
||||
},
|
||||
validate: state => {}
|
||||
validate: state => {
|
||||
}
|
||||
};
|
||||
|
||||
/* Excluded. It's not very useful and just eats a lot of space in the resulting JS.
|
||||
|
||||
templateTypes.ckeditor5 = {
|
||||
typeName: t('ckEditor5'),
|
||||
getTypeForm: (owner, isEdit) => null,
|
||||
getHTMLEditor: owner => <CKEditor id={prefix + 'ckeditor5Source'} height="600px" mode="html" label={t('templateContentHtml')}/>,
|
||||
getHTMLEditor: owner =>
|
||||
<CKEditor
|
||||
id={prefix + 'ckeditor5Source'}
|
||||
height="600px"
|
||||
mode="html"
|
||||
label={t('templateContentHtml')}/>,
|
||||
exportHTMLEditorData: async owner => {
|
||||
const preHtml = '<!doctype html><html><head><meta charset="utf-8"><title></title></head><body>';
|
||||
const postHtml = '</body></html>';
|
||||
|
@ -354,12 +403,17 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
afterTypeChange: mutState => {
|
||||
initFieldsIfMissing(mutState, 'ckeditor5');
|
||||
},
|
||||
validate: state => {}
|
||||
validate: state => {
|
||||
}
|
||||
};
|
||||
*/
|
||||
|
||||
const codeEditorSourceTypes = getCodeEditorSourceTypeOptions(t);
|
||||
const codeEditorSourceTypeLabels = {};
|
||||
for ({key, label} of codeEditorSourceTypes) {
|
||||
for ({
|
||||
key,
|
||||
label
|
||||
} of codeEditorSourceTypes) {
|
||||
codeEditorSourceTypeLabels[key] = label;
|
||||
}
|
||||
|
||||
|
@ -368,13 +422,20 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
getTypeForm: (owner, isEdit) => {
|
||||
const sourceType = owner.getFormValue(prefix + 'codeEditorSourceType');
|
||||
if (isEdit) {
|
||||
return <StaticField id={prefix + 'codeEditorSourceType'} className={styles.formDisabled} label={t('type')}>{codeEditorSourceTypeLabels[sourceType]}</StaticField>;
|
||||
return <StaticField
|
||||
id={prefix + 'codeEditorSourceType'}
|
||||
className={styles.formDisabled}
|
||||
label={t('type')}>{codeEditorSourceTypeLabels[sourceType]}</StaticField>;
|
||||
} else {
|
||||
return <Dropdown id={prefix + 'codeEditorSourceType'} label={t('type')} options={codeEditorSourceTypes}/>;
|
||||
return <Dropdown
|
||||
id={prefix + 'codeEditorSourceType'}
|
||||
label={t('type')}
|
||||
options={codeEditorSourceTypes}/>;
|
||||
}
|
||||
},
|
||||
getHTMLEditor: owner =>
|
||||
<AlignedRow label={t('templateContentHtml')}>
|
||||
<AlignedRow
|
||||
label={t('templateContentHtml')}>
|
||||
<CodeEditorHost
|
||||
ref={node => owner.editorNode = node}
|
||||
entity={owner.props.entity}
|
||||
|
@ -382,6 +443,8 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
initialSource={owner.getFormValue(prefix + 'codeEditorData').source}
|
||||
sourceType={owner.getFormValue(prefix + 'codeEditorSourceType')}
|
||||
title={t('codeEditorTemplateDesigner')}
|
||||
onSave={::owner.save}
|
||||
canSave={owner.isFormWithoutErrors()}
|
||||
onTestSend={::owner.showTestSendModal}
|
||||
onFullscreenAsync={::owner.setElementInFullscreen}
|
||||
/>
|
||||
|
@ -416,7 +479,8 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
afterTypeChange: mutState => {
|
||||
initFieldsIfMissing(mutState, 'codeeditor');
|
||||
},
|
||||
validate: state => {}
|
||||
validate: state => {
|
||||
}
|
||||
};
|
||||
|
||||
return templateTypes;
|
||||
|
@ -428,19 +492,94 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
|
||||
return <div>
|
||||
<AlignedRow>
|
||||
<Button className="btn-default" onClickAsync={::owner.toggleMergeTagReference} label={t('mergeTagReference')}/>
|
||||
<Button
|
||||
className="btn-default"
|
||||
onClickAsync={::owner.toggleMergeTagReference}
|
||||
label={t('mergeTagReference')}/>
|
||||
{owner.state.showMergeTagReference &&
|
||||
<div style={{marginTop: '15px'}}>
|
||||
<Trans i18nKey="mergeTagsAreTagsThatAreReplacedBefore"><p>Merge tags are tags that are replaced before sending out the message. The format of the merge tag is the following: <code>[TAG_NAME]</code> or <code>[TAG_NAME/fallback]</code> where <code>fallback</code> is an optional text value used when <code>TAG_NAME</code> is empty.</p></Trans>
|
||||
<Trans i18nKey="youCanUseAnyOfTheStandardMergeTagsBelow"><p>You can use any of the standard merge tags below. In addition to that every custom field has its own merge tag. Check the fields of the list you are going to send to.</p></Trans>
|
||||
<table className="table table-bordered table-condensed table-striped">
|
||||
<div
|
||||
style={{marginTop: '15px'}}>
|
||||
<Trans
|
||||
i18nKey="mergeTagsAreTagsThatAreReplacedBefore">
|
||||
<p>Merge
|
||||
tags
|
||||
are
|
||||
tags
|
||||
that
|
||||
are
|
||||
replaced
|
||||
before
|
||||
sending
|
||||
out
|
||||
the
|
||||
message.
|
||||
The
|
||||
format
|
||||
of
|
||||
the
|
||||
merge
|
||||
tag
|
||||
is
|
||||
the
|
||||
following: <code>[TAG_NAME]</code> or <code>[TAG_NAME/fallback]</code> where <code>fallback</code> is
|
||||
an
|
||||
optional
|
||||
text
|
||||
value
|
||||
used
|
||||
when <code>TAG_NAME</code> is
|
||||
empty.
|
||||
</p>
|
||||
</Trans>
|
||||
<Trans
|
||||
i18nKey="youCanUseAnyOfTheStandardMergeTagsBelow">
|
||||
<p>You
|
||||
can
|
||||
use
|
||||
any
|
||||
of
|
||||
the
|
||||
standard
|
||||
merge
|
||||
tags
|
||||
below.
|
||||
In
|
||||
addition
|
||||
to
|
||||
that
|
||||
every
|
||||
custom
|
||||
field
|
||||
has
|
||||
its
|
||||
own
|
||||
merge
|
||||
tag.
|
||||
Check
|
||||
the
|
||||
fields
|
||||
of
|
||||
the
|
||||
list
|
||||
you
|
||||
are
|
||||
going
|
||||
to
|
||||
send
|
||||
to.</p>
|
||||
</Trans>
|
||||
<table
|
||||
className="table table-bordered table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<Trans i18nKey="mergeTag-1">Merge tag</Trans>
|
||||
<Trans
|
||||
i18nKey="mergeTag-1">Merge
|
||||
tag</Trans>
|
||||
</th>
|
||||
<th>
|
||||
<Trans i18nKey="description">Description</Trans>
|
||||
<Trans
|
||||
i18nKey="description">Description</Trans>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -450,7 +589,14 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[LINK_UNSUBSCRIBE]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="urlThatPointsToTheUnsubscribePage">URL that points to the unsubscribe page</Trans>
|
||||
<Trans
|
||||
i18nKey="urlThatPointsToTheUnsubscribePage">URL
|
||||
that
|
||||
points
|
||||
to
|
||||
the
|
||||
unsubscribe
|
||||
page</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -458,7 +604,17 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[LINK_PREFERENCES]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="urlThatPointsToThePreferencesPageOfThe">URL that points to the preferences page of the subscriber</Trans>
|
||||
<Trans
|
||||
i18nKey="urlThatPointsToThePreferencesPageOfThe">URL
|
||||
that
|
||||
points
|
||||
to
|
||||
the
|
||||
preferences
|
||||
page
|
||||
of
|
||||
the
|
||||
subscriber</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -466,7 +622,15 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[LINK_BROWSER]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="urlToPreviewTheMessageInABrowser">URL to preview the message in a browser</Trans>
|
||||
<Trans
|
||||
i18nKey="urlToPreviewTheMessageInABrowser">URL
|
||||
to
|
||||
preview
|
||||
the
|
||||
message
|
||||
in
|
||||
a
|
||||
browser</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -474,7 +638,9 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[EMAIL]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="emailAddress-1">Email address</Trans>
|
||||
<Trans
|
||||
i18nKey="emailAddress-1">Email
|
||||
address</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -482,7 +648,16 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[TO_NAME]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="recipientNameAsItAppearsInEmailsToHeader">Recipient name as it appears in email's 'To' header</Trans>
|
||||
<Trans
|
||||
i18nKey="recipientNameAsItAppearsInEmailsToHeader">Recipient
|
||||
name
|
||||
as
|
||||
it
|
||||
appears
|
||||
in
|
||||
email's
|
||||
'To'
|
||||
header</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -490,7 +665,13 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[SUBSCRIPTION_ID]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="uniqueIdThatIdentifiesTheRecipient">Unique ID that identifies the recipient</Trans>
|
||||
<Trans
|
||||
i18nKey="uniqueIdThatIdentifiesTheRecipient">Unique
|
||||
ID
|
||||
that
|
||||
identifies
|
||||
the
|
||||
recipient</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -498,7 +679,17 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[LIST_ID]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="uniqueIdThatIdentifiesTheListUsedForThis">Unique ID that identifies the list used for this campaign</Trans>
|
||||
<Trans
|
||||
i18nKey="uniqueIdThatIdentifiesTheListUsedForThis">Unique
|
||||
ID
|
||||
that
|
||||
identifies
|
||||
the
|
||||
list
|
||||
used
|
||||
for
|
||||
this
|
||||
campaign</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -506,20 +697,42 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[CAMPAIGN_ID]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="uniqueIdThatIdentifiesCurrentCampaign">Unique ID that identifies current campaign</Trans>
|
||||
<Trans
|
||||
i18nKey="uniqueIdThatIdentifiesCurrentCampaign">Unique
|
||||
ID
|
||||
that
|
||||
identifies
|
||||
current
|
||||
campaign</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<Trans i18nKey="forRssCampaignsTheFollowingFurtherTags"><p>For RSS campaigns, the following further tags can be used.</p></Trans>
|
||||
<table className="table table-bordered table-condensed table-striped">
|
||||
<Trans
|
||||
i18nKey="forRssCampaignsTheFollowingFurtherTags">
|
||||
<p>For
|
||||
RSS
|
||||
campaigns,
|
||||
the
|
||||
following
|
||||
further
|
||||
tags
|
||||
can
|
||||
be
|
||||
used.</p>
|
||||
</Trans>
|
||||
<table
|
||||
className="table table-bordered table-condensed table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<Trans i18nKey="mergeTag-1">Merge tag</Trans>
|
||||
<Trans
|
||||
i18nKey="mergeTag-1">Merge
|
||||
tag</Trans>
|
||||
</th>
|
||||
<th>
|
||||
<Trans i18nKey="description">Description</Trans>
|
||||
<Trans
|
||||
i18nKey="description">Description</Trans>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -529,7 +742,10 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[RSS_ENTRY_TITLE]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="rssEntryTitle">RSS entry title</Trans>
|
||||
<Trans
|
||||
i18nKey="rssEntryTitle">RSS
|
||||
entry
|
||||
title</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -537,7 +753,10 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[RSS_ENTRY_DATE]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="rssEntryDate">RSS entry date</Trans>
|
||||
<Trans
|
||||
i18nKey="rssEntryDate">RSS
|
||||
entry
|
||||
date</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -545,7 +764,10 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[RSS_ENTRY_LINK]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="rssEntryLink">RSS entry link</Trans>
|
||||
<Trans
|
||||
i18nKey="rssEntryLink">RSS
|
||||
entry
|
||||
link</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -553,7 +775,12 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[RSS_ENTRY_CONTENT]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="contentOfAnRssEntry">Content of an RSS entry</Trans>
|
||||
<Trans
|
||||
i18nKey="contentOfAnRssEntry">Content
|
||||
of
|
||||
an
|
||||
RSS
|
||||
entry</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -561,7 +788,10 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[RSS_ENTRY_SUMMARY]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="rssEntrySummary">RSS entry summary</Trans>
|
||||
<Trans
|
||||
i18nKey="rssEntrySummary">RSS
|
||||
entry
|
||||
summary</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
@ -569,7 +799,11 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
[RSS_ENTRY_IMAGE_URL]
|
||||
</th>
|
||||
<td>
|
||||
<Trans i18nKey="rssEntryImageUrl">RSS entry image URL</Trans>
|
||||
<Trans
|
||||
i18nKey="rssEntryImageUrl">RSS
|
||||
entry
|
||||
image
|
||||
URL</Trans>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -579,7 +813,52 @@ export function getEditForm(owner, typeKey, prefix = '') {
|
|||
|
||||
{owner.templateTypes[typeKey].getHTMLEditor(owner)}
|
||||
|
||||
<ACEEditor id={prefix + 'text'} height="400px" mode="text" label={t('templateContentPlainText')} help={<Trans i18nKey="toExtractTheTextFromHtmlClickHerePlease">To extract the text from HTML click <ActionLink onClickAsync={::owner.extractPlainText}>here</ActionLink>. Please note that your existing plaintext in the field above will be overwritten. This feature uses the <a href="http://premailer.dialect.ca/api">Premailer API</a>, a third party service. Their Terms of Service and Privacy Policy apply.</Trans>}/>
|
||||
<ACEEditor
|
||||
id={prefix + 'text'}
|
||||
height="400px"
|
||||
mode="text"
|
||||
label={t('templateContentPlainText')}
|
||||
help={
|
||||
<Trans
|
||||
i18nKey="toExtractTheTextFromHtmlClickHerePlease">To
|
||||
extract
|
||||
the
|
||||
text
|
||||
from
|
||||
HTML
|
||||
click <ActionLink
|
||||
onClickAsync={::owner.extractPlainText}>here</ActionLink>.
|
||||
Please
|
||||
note
|
||||
that
|
||||
your
|
||||
existing
|
||||
plaintext
|
||||
in
|
||||
the
|
||||
field
|
||||
above
|
||||
will
|
||||
be
|
||||
overwritten.
|
||||
This
|
||||
feature
|
||||
uses
|
||||
the <a
|
||||
href="http://premailer.dialect.ca/api">Premailer
|
||||
API</a>,
|
||||
a
|
||||
third
|
||||
party
|
||||
service.
|
||||
Their
|
||||
Terms
|
||||
of
|
||||
Service
|
||||
and
|
||||
Privacy
|
||||
Policy
|
||||
apply.</Trans>}/>
|
||||
</div>;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
|
||||
/* Excluded. It's not very useful and just eats a lot of space in the resulting JS.
|
||||
// The CKEditor part comes from https://ckeditor.com/docs/ckeditor5/latest/builds/guides/integration/advanced-setup.html
|
||||
const CKEditorWebpackPlugin = require( '@ckeditor/ckeditor5-dev-webpack-plugin' );
|
||||
const { styles } = require( '@ckeditor/ckeditor5-dev-utils' );
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
plugins: [
|
||||
/*
|
||||
new CKEditorWebpackPlugin( {
|
||||
// See https://ckeditor.com/docs/ckeditor5/latest/features/ui-language.html
|
||||
language: 'en'
|
||||
} )
|
||||
*/
|
||||
],
|
||||
entry: {
|
||||
"root": ['./src/root.js'],
|
||||
|
@ -65,6 +69,7 @@ module.exports = {
|
|||
}
|
||||
]
|
||||
},
|
||||
/*
|
||||
{
|
||||
test: /ckeditor5-[^/]+\/theme\/[\w-/]+\.css$/,
|
||||
use: [
|
||||
|
@ -79,6 +84,7 @@ module.exports = {
|
|||
}
|
||||
]
|
||||
},
|
||||
*/
|
||||
{
|
||||
test: /\.(png|jpg|gif)$/,
|
||||
use: [
|
||||
|
@ -105,15 +111,17 @@ module.exports = {
|
|||
'sass-loader'
|
||||
]
|
||||
},
|
||||
/*
|
||||
{
|
||||
test: /ckeditor5-[^/]+\/theme\/icons\/[^/]+\.svg|ckeditor-insert-image\.svg$/,
|
||||
use: [
|
||||
'raw-loader'
|
||||
]
|
||||
},
|
||||
*/
|
||||
{
|
||||
test: /\.(svg|otf|woff2|woff|ttf|eot)$/,
|
||||
exclude: /ckeditor5-[^/]+\/theme\/icons\/[^/]+\.svg|ckeditor-insert-image\.svg$/,
|
||||
// exclude: /ckeditor5-[^/]+\/theme\/icons\/[^/]+\.svg|ckeditor-insert-image\.svg$/,
|
||||
use: [
|
||||
'url-loader'
|
||||
]
|
||||
|
|
|
@ -33,7 +33,6 @@ editors:
|
|||
- grapesjs
|
||||
- mosaico
|
||||
- mosaicoWithFsTemplate
|
||||
- ckeditor5
|
||||
- ckeditor4
|
||||
- codeeditor
|
||||
|
||||
|
|
|
@ -61,7 +61,7 @@ const langCodes = {
|
|||
}
|
||||
}
|
||||
|
||||
langCodes.en = langCodes.en_US;
|
||||
langCodes.en = langCodes['en-US'] = langCodes.en_US;
|
||||
|
||||
module.exports.convertToFake = convertToFake;
|
||||
module.exports.langCodes = langCodes;
|
Loading…
Reference in a new issue