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:
Tomas Bures 2018-11-22 15:21:15 +01:00
parent a993c06aaf
commit 3bb235a585
12 changed files with 417 additions and 82 deletions

View file

@ -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) {

View file

@ -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')}

View file

@ -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;
}

View file

@ -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>

View file

@ -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>

View file

@ -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);
}

View file

@ -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)
}
};

View file

@ -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);

View file

@ -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>;
}

View file

@ -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'
]

View file

@ -33,7 +33,6 @@ editors:
- grapesjs
- mosaico
- mosaicoWithFsTemplate
- ckeditor5
- ckeditor4
- codeeditor

View file

@ -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;