Added sandboxed CKEditor 4 as a template editor
This commit is contained in:
parent
eacdc74c29
commit
02a7275ae4
23 changed files with 1299 additions and 377 deletions
|
@ -10,7 +10,7 @@
|
||||||
* Fixed security issue where description tags were able to include script tags. Reported by Andreas Lindh. Fixed with [ae6affda](https://github.com/Mailtrain-org/mailtrain/commit/ae6affda8193f034e06f7e095ee23821a83d5190)
|
* Fixed security issue where description tags were able to include script tags. Reported by Andreas Lindh. Fixed with [ae6affda](https://github.com/Mailtrain-org/mailtrain/commit/ae6affda8193f034e06f7e095ee23821a83d5190)
|
||||||
* Fixed security issue where templates that looked like file paths loaded content from arbitrary files. Reported by Andreas Lindh. Fixed with [0879fa41](https://github.com/Mailtrain-org/mailtrain/commit/0879fa412a2d4a417aeca5cd5092a8f86531e7ef)
|
* Fixed security issue where templates that looked like file paths loaded content from arbitrary files. Reported by Andreas Lindh. Fixed with [0879fa41](https://github.com/Mailtrain-org/mailtrain/commit/0879fa412a2d4a417aeca5cd5092a8f86531e7ef)
|
||||||
* Fixed security issue where users were able to use html tags in subscription values. Reported by Andreas Lindh. Fixed with [9d5fb816](https://github.com/Mailtrain-org/mailtrain/commit/9d5fb816c937114966d4f589e1ad4e164ff3a187)
|
* Fixed security issue where users were able to use html tags in subscription values. Reported by Andreas Lindh. Fixed with [9d5fb816](https://github.com/Mailtrain-org/mailtrain/commit/9d5fb816c937114966d4f589e1ad4e164ff3a187)
|
||||||
* Support for multiple HTML editors (Mosaico, GrapeJS, Summernote, HTML code)
|
* Support for multiple HTML editors (Mosaico, Grapesjs, Summernote, HTML code)
|
||||||
|
|
||||||
## 1.22.0 2017-03-02
|
## 1.22.0 2017-03-02
|
||||||
|
|
||||||
|
|
2
TODO.md
2
TODO.md
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
### Templates
|
### Templates
|
||||||
- Add MJML template editor
|
- Add MJML template editor
|
||||||
- Include GrapeJS with MJML support
|
- Include Grapesjs with MJML support
|
||||||
- CKEditor to sandbox
|
- CKEditor to sandbox
|
||||||
- Add Files support to CKEditor
|
- Add Files support to CKEditor
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ The migration should happen almost automatically. There are however the followin
|
||||||
1. Structure of config files (under `config`) has changed at many places. Revisit the default config (`config/default.toml`)
|
1. Structure of config files (under `config`) has changed at many places. Revisit the default config (`config/default.toml`)
|
||||||
and update your configs accordingly.
|
and update your configs accordingly.
|
||||||
|
|
||||||
2. Images uploaded in a template editor (Mosaico, GrapeJS, etc.) need to be manually moved to a new destination (under `client`).
|
2. Images uploaded in a template editor (Mosaico, Grapesjs, etc.) need to be manually moved to a new destination (under `client`).
|
||||||
For Mosaico, this means to move folders named by a number from `public/mosaico` to `client/static/mosaico`.
|
For Mosaico, this means to move folders named by a number from `public/mosaico` to `client/static/mosaico`.
|
||||||
|
|
||||||
3. Directory for custom Mosaico templates has changed from `public/mosaico/templates` to `client/static/mosaico/templates`.
|
3. Directory for custom Mosaico templates has changed from `public/mosaico/templates` to `client/static/mosaico/templates`.
|
||||||
|
|
1360
client/package-lock.json
generated
1360
client/package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -37,6 +37,8 @@
|
||||||
"axios": "^0.16.2",
|
"axios": "^0.16.2",
|
||||||
"datatables.net": "^1.10.15",
|
"datatables.net": "^1.10.15",
|
||||||
"datatables.net-bs": "^1.10.15",
|
"datatables.net-bs": "^1.10.15",
|
||||||
|
"grapesjs": "^0.14.40",
|
||||||
|
"grapesjs-mjml": "0.0.27",
|
||||||
"i18next": "^8.4.3",
|
"i18next": "^8.4.3",
|
||||||
"i18next-xhr-backend": "^1.4.2",
|
"i18next-xhr-backend": "^1.4.2",
|
||||||
"immutable": "^3.8.1",
|
"immutable": "^3.8.1",
|
||||||
|
@ -46,6 +48,7 @@
|
||||||
"querystringify": "^1.0.0",
|
"querystringify": "^1.0.0",
|
||||||
"react": "^15.6.1",
|
"react": "^15.6.1",
|
||||||
"react-ace": "^5.1.0",
|
"react-ace": "^5.1.0",
|
||||||
|
"react-ckeditor-component": "^1.1.0",
|
||||||
"react-day-picker": "^6.1.0",
|
"react-day-picker": "^6.1.0",
|
||||||
"react-dnd-html5-backend": "^2.4.1",
|
"react-dnd-html5-backend": "^2.4.1",
|
||||||
"react-dnd-touch-backend": "^0.3.13",
|
"react-dnd-touch-backend": "^0.3.13",
|
||||||
|
|
|
@ -17,7 +17,7 @@ import ImagePlugin from '@ckeditor/ckeditor5-image/src/image';
|
||||||
import ImageCaptionPlugin from '@ckeditor/ckeditor5-image/src/imagecaption';
|
import ImageCaptionPlugin from '@ckeditor/ckeditor5-image/src/imagecaption';
|
||||||
import ImageStylePlugin from '@ckeditor/ckeditor5-image/src/imagestyle';
|
import ImageStylePlugin from '@ckeditor/ckeditor5-image/src/imagestyle';
|
||||||
import ImageToolbarPlugin from '@ckeditor/ckeditor5-image/src/imagetoolbar';
|
import ImageToolbarPlugin from '@ckeditor/ckeditor5-image/src/imagetoolbar';
|
||||||
import ImageUploadPlugin from '@ckeditor/ckeditor5-image/src/imageupload';
|
//import ImageUploadPlugin from '@ckeditor/ckeditor5-image/src/imageupload';
|
||||||
import LinkPlugin from '@ckeditor/ckeditor5-link/src/link';
|
import LinkPlugin from '@ckeditor/ckeditor5-link/src/link';
|
||||||
import ListPlugin from '@ckeditor/ckeditor5-list/src/list';
|
import ListPlugin from '@ckeditor/ckeditor5-list/src/list';
|
||||||
import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph';
|
import ParagraphPlugin from '@ckeditor/ckeditor5-paragraph/src/paragraph';
|
||||||
|
@ -75,6 +75,8 @@ class InsertImage extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Upload through CKEditor is disable because files can be managed by Files tab
|
||||||
|
|
||||||
class UploadAdapter {
|
class UploadAdapter {
|
||||||
constructor(loader, url, t) {
|
constructor(loader, url, t) {
|
||||||
|
@ -105,6 +107,7 @@ class MailtrainUploadAdapter extends Plugin {
|
||||||
this.editor.plugins.get(FileRepository).createUploadAdapter = loader => new UploadAdapter(loader, this.editor.t);
|
this.editor.plugins.get(FileRepository).createUploadAdapter = loader => new UploadAdapter(loader, this.editor.t);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -123,14 +126,14 @@ ClassicEditor.builtinPlugins = [
|
||||||
ImageCaptionPlugin,
|
ImageCaptionPlugin,
|
||||||
ImageStylePlugin,
|
ImageStylePlugin,
|
||||||
ImageToolbarPlugin,
|
ImageToolbarPlugin,
|
||||||
ImageUploadPlugin,
|
//ImageUploadPlugin,
|
||||||
LinkPlugin,
|
LinkPlugin,
|
||||||
ListPlugin,
|
ListPlugin,
|
||||||
ParagraphPlugin,
|
ParagraphPlugin,
|
||||||
AlignmentPlugin,
|
AlignmentPlugin,
|
||||||
TablePlugin,
|
TablePlugin,
|
||||||
TableToolbarPlugin,
|
TableToolbarPlugin,
|
||||||
MailtrainUploadAdapter,
|
//MailtrainUploadAdapter,
|
||||||
InsertImage
|
InsertImage
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -152,7 +155,7 @@ ClassicEditor.defaultConfig = {
|
||||||
'numberedList',
|
'numberedList',
|
||||||
'|',
|
'|',
|
||||||
'insertImage',
|
'insertImage',
|
||||||
'imageUpload',
|
// 'imageUpload',
|
||||||
'blockQuote',
|
'blockQuote',
|
||||||
'|',
|
'|',
|
||||||
'insertTable',
|
'insertTable',
|
|
@ -17,7 +17,7 @@ import ACEEditorRaw from 'react-ace';
|
||||||
import 'brace/theme/github';
|
import 'brace/theme/github';
|
||||||
import 'brace/ext/searchbox';
|
import 'brace/ext/searchbox';
|
||||||
|
|
||||||
import CKEditorRaw from './ckeditor';
|
import CKEditorRaw from './ckeditor5';
|
||||||
|
|
||||||
import DayPicker from 'react-day-picker';
|
import DayPicker from 'react-day-picker';
|
||||||
import 'react-day-picker/lib/style.css';
|
import 'react-day-picker/lib/style.css';
|
||||||
|
|
48
client/src/lib/grapesjs.js
Normal file
48
client/src/lib/grapesjs.js
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
|
||||||
|
import 'grapesjs/dist/css/grapes.min.css';
|
||||||
|
import grapesjs from 'grapesjs';
|
||||||
|
import grapesjsMjml from 'grapesjs-mjml';
|
||||||
|
|
||||||
|
export default class GrapesJs extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const editor = grapesjs.init({
|
||||||
|
container: this.canvasNode,
|
||||||
|
height: '100%',
|
||||||
|
width: 'auto',
|
||||||
|
noticeOnUnload: 0,
|
||||||
|
storageManager:{autoload: 0},
|
||||||
|
fromElement: false,
|
||||||
|
components:
|
||||||
|
' <mj-container>\n' +
|
||||||
|
' <mj-section>\n' +
|
||||||
|
' <mj-column>\n' +
|
||||||
|
' <mj-text>My Company</mj-text>\n' +
|
||||||
|
' </mj-column>\n' +
|
||||||
|
' </mj-section>\n' +
|
||||||
|
' <mj-container>',
|
||||||
|
plugins: ['gjs-mjml'],
|
||||||
|
pluginsOpts: {
|
||||||
|
'gjs-mjml': {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div ref={node => this.canvasNode = node}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
5
client/src/lib/grapesjs.scss
Normal file
5
client/src/lib/grapesjs.scss
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
.gjs-cv-canvas {
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
|
@ -16,26 +16,66 @@ import {
|
||||||
base,
|
base,
|
||||||
unbase
|
unbase
|
||||||
} from "../../../shared/templates";
|
} from "../../../shared/templates";
|
||||||
import CKEditor from './ckeditor';
|
import CKEditor from "react-ckeditor-component";
|
||||||
|
|
||||||
|
const initialHeight = 600;
|
||||||
|
const navbarHeight = 34; // Sync this with navbarheight in sandboxed-ckeditor.scss
|
||||||
|
|
||||||
@translate(null, { withRef: true })
|
@translate(null, { withRef: true })
|
||||||
export class CKEditorHost extends Component {
|
export class CKEditorHost extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {}
|
this.state = {
|
||||||
|
fullscreen: false
|
||||||
|
}
|
||||||
|
|
||||||
|
this.onWindowResizeHandler = ::this.onWindowResize;
|
||||||
}
|
}
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
entityTypeId: PropTypes.string,
|
entityTypeId: PropTypes.string,
|
||||||
entity: PropTypes.object,
|
entity: PropTypes.object,
|
||||||
initialHtml: PropTypes.string
|
initialHtml: PropTypes.string,
|
||||||
|
title: PropTypes.string,
|
||||||
|
onFullscreenAsync: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleFullscreenAsync() {
|
||||||
|
const fullscreen = !this.state.fullscreen;
|
||||||
|
this.setState({
|
||||||
|
fullscreen
|
||||||
|
});
|
||||||
|
await this.props.onFullscreenAsync(fullscreen);
|
||||||
|
|
||||||
|
let newHeight;
|
||||||
|
if (fullscreen) {
|
||||||
|
newHeight = window.innerHeight - navbarHeight;
|
||||||
|
} else {
|
||||||
|
newHeight = initialHeight;
|
||||||
|
}
|
||||||
|
await this.contentNode.ask('setHeight', newHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
async exportState() {
|
async exportState() {
|
||||||
return await this.contentNode.ask('exportState');
|
return await this.contentNode.ask('exportState');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onWindowResize() {
|
||||||
|
if (this.state.fullscreen) {
|
||||||
|
const newHeight = window.innerHeight - navbarHeight;
|
||||||
|
this.contentNode.ask('setHeight', newHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
window.addEventListener('resize', this.onWindowResizeHandler, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
window.removeEventListener('resize', this.onWindowResizeHandler, false);
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const t = this.props.t;
|
const t = this.props.t;
|
||||||
|
|
||||||
|
@ -51,7 +91,12 @@ export class CKEditorHost extends Component {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.editor}>
|
<div className={this.state.fullscreen ? styles.editorFullscreen : styles.editor}>
|
||||||
|
<div className={styles.navbar}>
|
||||||
|
{this.state.fullscreen && <img className={styles.logo} src={getTrustedUrl('static/mailtrain-notext.png')}/>}
|
||||||
|
<div className={styles.title}>{this.props.title}</div>
|
||||||
|
<a className={styles.btn} onClick={::this.toggleFullscreenAsync}><Icon icon="fullscreen"/></a>
|
||||||
|
</div>
|
||||||
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="ckeditor/editor" tokenMethod="ckeditor" tokenParams={editorData}/>
|
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="ckeditor/editor" tokenMethod="ckeditor" tokenParams={editorData}/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -93,16 +138,30 @@ export class CKEditorSandbox extends Component {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setHeight(methods, params) {
|
||||||
|
this.node.editorInstance.resize('100%', params);
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
parentRPC.setMethodHandler('exportState', ::this.exportState);
|
parentRPC.setMethodHandler('exportState', ::this.exportState);
|
||||||
|
parentRPC.setMethodHandler('setHeight', ::this.setHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const config = {
|
||||||
|
removeButtons: 'Underline,Subscript,Superscript,Maximize',
|
||||||
|
resize_enabled: false,
|
||||||
|
height: initialHeight
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.sandbox}>
|
<div className={styles.sandbox}>
|
||||||
<CKEditor
|
<CKEditor ref={node => this.node = node}
|
||||||
onChange={(event, editor) => this.setState({html: editor.getData()})}
|
content={this.state.html}
|
||||||
data={this.state.html}
|
events={{
|
||||||
|
change: evt => this.setState({html: evt.editor.getData()}),
|
||||||
|
}}
|
||||||
|
config={config}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,10 +1,72 @@
|
||||||
|
$navbarHeight: 34px;
|
||||||
|
|
||||||
.editor {
|
.editor {
|
||||||
.host {
|
.host {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.sandbox {
|
.sandbox {
|
||||||
:global .ck-editor__editable {
|
height: 100%;
|
||||||
height: 500px;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editorFullscreen {
|
||||||
|
position: fixed;
|
||||||
|
top: 0px;
|
||||||
|
bottom: 0px;
|
||||||
|
left: 0px;
|
||||||
|
right: 0px;
|
||||||
|
z-index: 1000;
|
||||||
|
background: white;
|
||||||
|
margin-top: $navbarHeight;
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
margin-top: -$navbarHeight;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
.host {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
background: #DE4320;
|
||||||
|
width: 100%;
|
||||||
|
height: $navbarHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo {
|
||||||
|
float: left;
|
||||||
|
height: $navbarHeight;
|
||||||
|
padding: 5px 0 5px 10px;
|
||||||
|
filter: brightness(0) invert(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding: 5px 0 5px 10px;
|
||||||
|
font-size: 18px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-family: "Ubuntu",Tahoma,"Helvetica Neue",Helvetica,Arial,sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
float: left;
|
||||||
|
color: white;
|
||||||
|
height: $navbarHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: block;
|
||||||
|
float: right;
|
||||||
|
padding: 0px 15px;
|
||||||
|
line-height: $navbarHeight;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
font-family: sans-serif;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
background-color: #b1381e;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import 'brace/mode/html';
|
||||||
|
|
||||||
import { MosaicoEditorHost } from "../lib/sandboxed-mosaico";
|
import { MosaicoEditorHost } from "../lib/sandboxed-mosaico";
|
||||||
import { CKEditorHost } from "../lib/sandboxed-ckeditor";
|
import { CKEditorHost } from "../lib/sandboxed-ckeditor";
|
||||||
|
import GrapesJS from "../lib/grapesjs";
|
||||||
|
|
||||||
import {getTemplateTypes as getMosaicoTemplateTypes} from './mosaico/helpers';
|
import {getTemplateTypes as getMosaicoTemplateTypes} from './mosaico/helpers';
|
||||||
import {getSandboxUrl} from "../lib/urls";
|
import {getSandboxUrl} from "../lib/urls";
|
||||||
|
@ -174,7 +175,7 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
validate: state => {}
|
validate: state => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
templateTypes.grapejs = { // FIXME
|
templateTypes.xgrapesjs = { // FIXME
|
||||||
typeName: t('GrapeJS'),
|
typeName: t('GrapeJS'),
|
||||||
getTypeForm: (owner, isEdit) => null,
|
getTypeForm: (owner, isEdit) => null,
|
||||||
getHTMLEditor: owner => null,
|
getHTMLEditor: owner => null,
|
||||||
|
@ -188,12 +189,12 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
validate: state => {}
|
validate: state => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
templateTypes.xckeditor = {
|
templateTypes.grapesjs = {
|
||||||
typeName: t('CKEditor'),
|
typeName: t('GrapeJS (fake)'),
|
||||||
getTypeForm: (owner, isEdit) => null,
|
getTypeForm: (owner, isEdit) => null,
|
||||||
getHTMLEditor: owner =>
|
getHTMLEditor: owner =>
|
||||||
<AlignedRow label={t('Template content (HTML)')}>
|
<AlignedRow label={t('Template content (HTML)')}>
|
||||||
<CKEditorHost
|
<GrapesJS
|
||||||
ref={node => owner.editorNode = node}
|
ref={node => owner.editorNode = node}
|
||||||
entity={owner.props.entity}
|
entity={owner.props.entity}
|
||||||
initialHtml={owner.getFormValue(prefix + 'html')}
|
initialHtml={owner.getFormValue(prefix + 'html')}
|
||||||
|
@ -212,10 +213,37 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
validate: state => {}
|
validate: state => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
templateTypes.ckeditor = {
|
templateTypes.ckeditor4 = {
|
||||||
typeName: t('CKEditor'),
|
typeName: t('CKEditor 4'),
|
||||||
getTypeForm: (owner, isEdit) => null,
|
getTypeForm: (owner, isEdit) => null,
|
||||||
getHTMLEditor: owner => <CKEditor id={prefix + 'html'} height="600px" mode="html" label={t('Template content (HTML)')}/>,
|
getHTMLEditor: owner =>
|
||||||
|
<AlignedRow label={t('Template content')}>
|
||||||
|
<CKEditorHost
|
||||||
|
ref={node => owner.editorNode = node}
|
||||||
|
entity={owner.props.entity}
|
||||||
|
initialHtml={owner.getFormValue(prefix + 'html')}
|
||||||
|
entityTypeId={entityTypeId}
|
||||||
|
title={t('CKEditor 4 Template Designer')}
|
||||||
|
onFullscreenAsync={::owner.setElementInFullscreen}
|
||||||
|
/>
|
||||||
|
</AlignedRow>,
|
||||||
|
exportHTMLEditorData: async owner => {
|
||||||
|
const {html} = await owner.editorNode.exportState();
|
||||||
|
owner.updateFormValue(prefix + 'html', html);
|
||||||
|
},
|
||||||
|
initData: () => ({}),
|
||||||
|
afterLoad: data => {},
|
||||||
|
beforeSave: data => {
|
||||||
|
clearBeforeSave(data);
|
||||||
|
},
|
||||||
|
afterTypeChange: mutState => {},
|
||||||
|
validate: state => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
templateTypes.ckeditor5 = {
|
||||||
|
typeName: t('CKEditor 5'),
|
||||||
|
getTypeForm: (owner, isEdit) => null,
|
||||||
|
getHTMLEditor: owner => <CKEditor id={prefix + 'html'} height="600px" mode="html" label={t('Template content')}/>,
|
||||||
exportHTMLEditorData: async owner => {},
|
exportHTMLEditorData: async owner => {},
|
||||||
initData: () => ({}),
|
initData: () => ({}),
|
||||||
afterLoad: data => {},
|
afterLoad: data => {},
|
||||||
|
|
|
@ -13,9 +13,9 @@ module.exports = {
|
||||||
} )
|
} )
|
||||||
],
|
],
|
||||||
entry: {
|
entry: {
|
||||||
root: ['babel-polyfill', './src/root.js'],
|
"root": ['babel-polyfill', './src/root.js'],
|
||||||
mosaico: ['babel-polyfill', './src/lib/sandboxed-mosaico-root.js'],
|
"mosaico-root": ['babel-polyfill', './src/lib/sandboxed-mosaico-root.js'],
|
||||||
ckeditor: ['babel-polyfill', './src/lib/sandboxed-ckeditor-root.js'],
|
"ckeditor-root": ['babel-polyfill', './src/lib/sandboxed-ckeditor-root.js'],
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
library: 'MailtrainReactBody',
|
library: 'MailtrainReactBody',
|
||||||
|
@ -46,8 +46,6 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
// exclude: /(disposables|react-dnd-touch-backend|attr-accept)/ /* https://github.com/react-dnd/react-dnd/issues/407 */,
|
|
||||||
// use: [ 'babel-loader' ]
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.css$/,
|
test: /\.css$/,
|
||||||
|
@ -69,7 +67,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
minify: false
|
minify: false
|
||||||
} )
|
} )
|
||||||
},
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -84,10 +82,7 @@ module.exports = {
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// Or /ckeditor5-[^/]+\/theme\/icons\/[^/]+\.svg$/ if you want to limit this loader
|
|
||||||
// to CKEditor 5 icons only.
|
|
||||||
test: /\.svg$/,
|
test: /\.svg$/,
|
||||||
|
|
||||||
use: [ 'raw-loader' ]
|
use: [ 'raw-loader' ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -105,8 +100,8 @@ module.exports = {
|
||||||
'sass-loader' ]
|
'sass-loader' ]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
test: /\.(woff|ttf|eot)$/,
|
test: /\.(otf|woff2|woff|ttf|eot)$/,
|
||||||
use: [ 'url-loader' ]
|
use: [ 'raw-loader' ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
|
@ -16,10 +16,12 @@ title: mailtrain
|
||||||
|
|
||||||
# Enabled HTML editors
|
# Enabled HTML editors
|
||||||
editors:
|
editors:
|
||||||
- ckeditor
|
- ckeditor4
|
||||||
|
- ckeditor5
|
||||||
- codeeditor
|
- codeeditor
|
||||||
- mosaico
|
- mosaico
|
||||||
- mosaicoWithFsTemplate
|
- mosaicoWithFsTemplate
|
||||||
|
- grapesjs
|
||||||
|
|
||||||
# Default language to use
|
# Default language to use
|
||||||
language: en
|
language: en
|
||||||
|
@ -149,7 +151,7 @@ mosaico:
|
||||||
# customscripts:
|
# customscripts:
|
||||||
# - /mosaico/custom/my-mosaico-plugin.js
|
# - /mosaico/custom/my-mosaico-plugin.js
|
||||||
|
|
||||||
grapejs:
|
grapesjs:
|
||||||
# Installed templates
|
# Installed templates
|
||||||
templates:
|
templates:
|
||||||
- key: demo
|
- key: demo
|
||||||
|
|
Binary file not shown.
|
@ -974,11 +974,11 @@ msgstr "Vorlagen Editoren"
|
||||||
|
|
||||||
#: views/index.hbs:21
|
#: views/index.hbs:21
|
||||||
msgid ""
|
msgid ""
|
||||||
"Mailtrain ships with GrapeJS and Mosaico built in, two advanced template "
|
"Mailtrain ships with Grapesjs and Mosaico built in, two advanced template "
|
||||||
"editors. Mailtrain also offers a code editor if you prefer to handcraft the "
|
"editors. Mailtrain also offers a code editor if you prefer to handcraft the "
|
||||||
"HTML yourself."
|
"HTML yourself."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Mailtrain beinhaltet GrapeJS und Mosaico, zwei Programme zum Bearbeiten der "
|
"Mailtrain beinhaltet Grapesjs und Mosaico, zwei Programme zum Bearbeiten der "
|
||||||
"E-Mail-Vorlagen. Natürlich bietet Mailtrain auch einen Code-Editor, sofern "
|
"E-Mail-Vorlagen. Natürlich bietet Mailtrain auch einen Code-Editor, sofern "
|
||||||
"Sie den HTML Code selber erstellen möchten."
|
"Sie den HTML Code selber erstellen möchten."
|
||||||
|
|
||||||
|
@ -1913,8 +1913,8 @@ msgid "CLOSE"
|
||||||
msgstr "SCHLIESSEN"
|
msgstr "SCHLIESSEN"
|
||||||
|
|
||||||
#: views/partials/grapejs.hbs:2
|
#: views/partials/grapejs.hbs:2
|
||||||
msgid "Open GrapeJS"
|
msgid "Open Grapesjs"
|
||||||
msgstr "GrapeJS öffnen"
|
msgstr "Grapesjs öffnen"
|
||||||
|
|
||||||
#: views/partials/html-preview.hbs:1
|
#: views/partials/html-preview.hbs:1
|
||||||
msgid "Toggle HTML preview"
|
msgid "Toggle HTML preview"
|
||||||
|
@ -3699,7 +3699,7 @@ msgid "Attachment not found"
|
||||||
msgstr "Anhangs-Datei nicht gefunden"
|
msgstr "Anhangs-Datei nicht gefunden"
|
||||||
|
|
||||||
#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:35
|
#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:35
|
||||||
#: routes/fields.js:13 routes/forms.js:16 routes/grapejs.js:13
|
#: routes/fields.js:13 routes/forms.js:16 routes/grapesjs.js:13
|
||||||
#: routes/lists.js:50 routes/mosaico.js:14 routes/report-templates.js:20
|
#: routes/lists.js:50 routes/mosaico.js:14 routes/report-templates.js:20
|
||||||
#: routes/reports.js:22 routes/segments.js:13 routes/settings.js:23
|
#: routes/reports.js:22 routes/segments.js:13 routes/settings.js:23
|
||||||
#: routes/templates.js:18 routes/triggers.js:18 routes/users.js:75
|
#: routes/templates.js:18 routes/triggers.js:18 routes/users.js:75
|
||||||
|
|
Binary file not shown.
|
@ -979,11 +979,11 @@ msgstr "Editores plantilla"
|
||||||
|
|
||||||
#: views/index.hbs:21
|
#: views/index.hbs:21
|
||||||
msgid ""
|
msgid ""
|
||||||
"Mailtrain ships with GrapeJS and Mosaico built in, two advanced template "
|
"Mailtrain ships with Grapesjs and Mosaico built in, two advanced template "
|
||||||
"editors. Mailtrain also offers a code editor if you prefer to handcraft the "
|
"editors. Mailtrain also offers a code editor if you prefer to handcraft the "
|
||||||
"HTML yourself."
|
"HTML yourself."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Mailtrain envía con los incorporados GrapeJS y Mosaico, dos editores "
|
"Mailtrain envía con los incorporados Grapesjs y Mosaico, dos editores "
|
||||||
"avanzados de plantillas . Mailtrain también ofrece un editor de código, por "
|
"avanzados de plantillas . Mailtrain también ofrece un editor de código, por "
|
||||||
"si prefieres modificar HTML."
|
"si prefieres modificar HTML."
|
||||||
|
|
||||||
|
@ -1946,8 +1946,8 @@ msgid "CLOSE"
|
||||||
msgstr "CERRAR"
|
msgstr "CERRAR"
|
||||||
|
|
||||||
#: views/partials/grapejs.hbs:2
|
#: views/partials/grapejs.hbs:2
|
||||||
msgid "Open GrapeJS"
|
msgid "Open Grapesjs"
|
||||||
msgstr "Abrir GrapeJS"
|
msgstr "Abrir Grapesjs"
|
||||||
|
|
||||||
#: views/partials/html-preview.hbs:1
|
#: views/partials/html-preview.hbs:1
|
||||||
msgid "Toggle HTML preview"
|
msgid "Toggle HTML preview"
|
||||||
|
@ -3831,7 +3831,7 @@ msgid "Attachment not found"
|
||||||
msgstr "Adjunto no encontrado"
|
msgstr "Adjunto no encontrado"
|
||||||
|
|
||||||
#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:35
|
#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:35
|
||||||
#: routes/fields.js:13 routes/forms.js:16 routes/grapejs.js:14
|
#: routes/fields.js:13 routes/forms.js:16 routes/grapesjs.js:14
|
||||||
#: routes/lists.js:50 routes/mosaico.js:14 routes/report-templates.js:20
|
#: routes/lists.js:50 routes/mosaico.js:14 routes/report-templates.js:20
|
||||||
#: routes/reports.js:22 routes/segments.js:13 routes/settings.js:23
|
#: routes/reports.js:22 routes/segments.js:13 routes/settings.js:23
|
||||||
#: routes/templates.js:18 routes/triggers.js:18 routes/users.js:75
|
#: routes/templates.js:18 routes/triggers.js:18 routes/users.js:75
|
||||||
|
|
Binary file not shown.
|
@ -977,11 +977,11 @@ msgstr "Editors dei templates"
|
||||||
|
|
||||||
#: views/index.hbs:21
|
#: views/index.hbs:21
|
||||||
msgid ""
|
msgid ""
|
||||||
"Mailtrain ships with GrapeJS and Mosaico built in, two advanced template "
|
"Mailtrain ships with Grapesjs and Mosaico built in, two advanced template "
|
||||||
"editors. Mailtrain also offers a code editor if you prefer to handcraft the "
|
"editors. Mailtrain also offers a code editor if you prefer to handcraft the "
|
||||||
"HTML yourself."
|
"HTML yourself."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Mailtrain include GrapeJS e Mosaico, due editors di template avanzati. "
|
"Mailtrain include Grapesjs e Mosaico, due editors di template avanzati. "
|
||||||
"Mailtrain offre inoltre un editor di codice se preferisci scrivere tu l'HTML."
|
"Mailtrain offre inoltre un editor di codice se preferisci scrivere tu l'HTML."
|
||||||
|
|
||||||
#: views/index.hbs:23
|
#: views/index.hbs:23
|
||||||
|
@ -1913,8 +1913,8 @@ msgid "CLOSE"
|
||||||
msgstr "CHIUDI"
|
msgstr "CHIUDI"
|
||||||
|
|
||||||
#: views/partials/grapejs.hbs:2
|
#: views/partials/grapejs.hbs:2
|
||||||
msgid "Open GrapeJS"
|
msgid "Open Grapesjs"
|
||||||
msgstr "Apri GrapeJS"
|
msgstr "Apri Grapesjs"
|
||||||
|
|
||||||
#: views/partials/html-preview.hbs:1
|
#: views/partials/html-preview.hbs:1
|
||||||
msgid "Toggle HTML preview"
|
msgid "Toggle HTML preview"
|
||||||
|
@ -3809,7 +3809,7 @@ msgid "Attachment not found"
|
||||||
msgstr "Allegato non trovato"
|
msgstr "Allegato non trovato"
|
||||||
|
|
||||||
#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:32
|
#: routes/blacklist.js:13 routes/campaigns.js:26 routes/editorapi.js:32
|
||||||
#: routes/fields.js:13 routes/forms.js:16 routes/grapejs.js:15
|
#: routes/fields.js:13 routes/forms.js:16 routes/grapesjs.js:15
|
||||||
#: routes/lists.js:50 routes/mosaico.js:14 routes/report-templates.js:20
|
#: routes/lists.js:50 routes/mosaico.js:14 routes/report-templates.js:20
|
||||||
#: routes/reports.js:22 routes/segments.js:13 routes/settings.js:23
|
#: routes/reports.js:22 routes/segments.js:13 routes/settings.js:23
|
||||||
#: routes/templates.js:18 routes/triggers.js:18 routes/users.js:75
|
#: routes/templates.js:18 routes/triggers.js:18 routes/users.js:75
|
||||||
|
|
|
@ -48,7 +48,7 @@ function getRouter(appType) {
|
||||||
mailtrainConfig: JSON.stringify(mailtrainConfig),
|
mailtrainConfig: JSON.stringify(mailtrainConfig),
|
||||||
scriptFiles: [
|
scriptFiles: [
|
||||||
getSandboxUrl('mailtrain/common.js'),
|
getSandboxUrl('mailtrain/common.js'),
|
||||||
getSandboxUrl('mailtrain/ckeditor.js')
|
getSandboxUrl('mailtrain/ckeditor-root.js')
|
||||||
],
|
],
|
||||||
publicPath: getSandboxUrl()
|
publicPath: getSandboxUrl()
|
||||||
});
|
});
|
||||||
|
|
|
@ -199,7 +199,7 @@ function getRouter(appType) {
|
||||||
mailtrainConfig: JSON.stringify(mailtrainConfig),
|
mailtrainConfig: JSON.stringify(mailtrainConfig),
|
||||||
scriptFiles: [
|
scriptFiles: [
|
||||||
getSandboxUrl('mailtrain/common.js'),
|
getSandboxUrl('mailtrain/common.js'),
|
||||||
getSandboxUrl('mailtrain/mosaico.js')
|
getSandboxUrl('mailtrain/mosaico-root.js')
|
||||||
],
|
],
|
||||||
publicPath: getSandboxUrl()
|
publicPath: getSandboxUrl()
|
||||||
});
|
});
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
<script src="/static/jquery-2.2.1.min.js"></script>
|
<script src="/static/jquery-2.2.1.min.js"></script>
|
||||||
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
|
<script src="/static/bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
|
||||||
{{#if mailtrainConfig}}
|
{{#if mailtrainConfig}}
|
||||||
<script>
|
<script>
|
||||||
{{#if reactCsrfToken}}window.csfrToken = '{{reactCsrfToken}}';{{/if}}
|
{{#if reactCsrfToken}}window.csfrToken = '{{reactCsrfToken}}';{{/if}}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue