Added MJML/HTML codeeditor with a preview for template design.
This commit is contained in:
parent
c7d7b1fe0c
commit
7e52000219
24 changed files with 887 additions and 279 deletions
|
@ -25,6 +25,7 @@ const subscription = require('./routes/subscription');
|
||||||
const sandboxedMosaico = require('./routes/sandboxed-mosaico');
|
const sandboxedMosaico = require('./routes/sandboxed-mosaico');
|
||||||
const sandboxedCKEditor = require('./routes/sandboxed-ckeditor');
|
const sandboxedCKEditor = require('./routes/sandboxed-ckeditor');
|
||||||
const sandboxedGrapesJS = require('./routes/sandboxed-grapesjs');
|
const sandboxedGrapesJS = require('./routes/sandboxed-grapesjs');
|
||||||
|
const sandboxedCodeEditor = require('./routes/sandboxed-codeeditor');
|
||||||
const files = require('./routes/files');
|
const files = require('./routes/files');
|
||||||
const links = require('./routes/links');
|
const links = require('./routes/links');
|
||||||
const archive = require('./routes/archive');
|
const archive = require('./routes/archive');
|
||||||
|
@ -226,6 +227,7 @@ function createApp(appType) {
|
||||||
useWith404Fallback('/mosaico', sandboxedMosaico.getRouter(appType));
|
useWith404Fallback('/mosaico', sandboxedMosaico.getRouter(appType));
|
||||||
useWith404Fallback('/ckeditor', sandboxedCKEditor.getRouter(appType));
|
useWith404Fallback('/ckeditor', sandboxedCKEditor.getRouter(appType));
|
||||||
useWith404Fallback('/grapesjs', sandboxedGrapesJS.getRouter(appType));
|
useWith404Fallback('/grapesjs', sandboxedGrapesJS.getRouter(appType));
|
||||||
|
useWith404Fallback('/codeeditor', sandboxedCodeEditor.getRouter(appType));
|
||||||
|
|
||||||
if (appType === AppType.TRUSTED || appType === AppType.SANDBOXED) {
|
if (appType === AppType.TRUSTED || appType === AppType.SANDBOXED) {
|
||||||
if (config.reports && config.reports.enabled === true) {
|
if (config.reports && config.reports.enabled === true) {
|
||||||
|
|
332
client/package-lock.json
generated
332
client/package-lock.json
generated
|
@ -2180,11 +2180,6 @@
|
||||||
"safe-buffer": "5.1.1"
|
"safe-buffer": "5.1.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ckeditor": {
|
|
||||||
"version": "4.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ckeditor/-/ckeditor-4.11.1.tgz",
|
|
||||||
"integrity": "sha512-UhHe02cc/wWJquDQZysEgh0ohLMEMU56zDx+s8prDdjylY/aBDY2xdIiIpbgCBTXdjhrEPIAPyiDS9g3RxYXig=="
|
|
||||||
},
|
|
||||||
"ckeditor5": {
|
"ckeditor5": {
|
||||||
"version": "11.1.1",
|
"version": "11.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/ckeditor5/-/ckeditor5-11.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/ckeditor5/-/ckeditor5-11.1.1.tgz",
|
||||||
|
@ -3413,9 +3408,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"deep-extend": {
|
"deep-extend": {
|
||||||
"version": "0.5.1",
|
"version": "0.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
|
||||||
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w=="
|
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
|
||||||
},
|
},
|
||||||
"deep-is": {
|
"deep-is": {
|
||||||
"version": "0.1.3",
|
"version": "0.1.3",
|
||||||
|
@ -4624,6 +4619,16 @@
|
||||||
"mjml": "3.3.5"
|
"mjml": "3.3.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "4.1.1",
|
||||||
|
"shebang-command": "1.2.0",
|
||||||
|
"which": "1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
@ -4632,6 +4637,11 @@
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
|
||||||
|
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w=="
|
||||||
|
},
|
||||||
"hoist-non-react-statics": {
|
"hoist-non-react-statics": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
||||||
|
@ -4748,6 +4758,27 @@
|
||||||
"react": "15.6.2",
|
"react": "15.6.2",
|
||||||
"react-dom": "15.6.2",
|
"react-dom": "15.6.2",
|
||||||
"warning": "3.0.0"
|
"warning": "3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
||||||
|
},
|
||||||
|
"juice": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/juice/-/juice-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-3Qym/RnFoCGa9qrDz6xn4zRnohgI6G87xKWZV+/seF3dYpaVqNS1HijsDef+elGhytRY79RIboOzk0hucLtx6g==",
|
||||||
|
"requires": {
|
||||||
|
"cheerio": "0.22.0",
|
||||||
|
"commander": "2.19.0",
|
||||||
|
"cross-spawn": "5.1.0",
|
||||||
|
"deep-extend": "0.5.1",
|
||||||
|
"mensch": "0.3.3",
|
||||||
|
"slick": "1.12.2",
|
||||||
|
"web-resource-inliner": "4.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mjml-divider": {
|
"mjml-divider": {
|
||||||
|
@ -4914,17 +4945,48 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"grapesjs-plugin-ckeditor": {
|
|
||||||
"version": "0.0.9",
|
|
||||||
"resolved": "https://registry.npmjs.org/grapesjs-plugin-ckeditor/-/grapesjs-plugin-ckeditor-0.0.9.tgz",
|
|
||||||
"integrity": "sha512-QXyAcSwgi09pzigGVS/NsHag5Skuw4zTkVGmEiBN/Qi8KU12/cQBG/OjAcjAB3/ZpToyPoglI33Ydjgj2nJuxQ=="
|
|
||||||
},
|
|
||||||
"grapesjs-preset-newsletter": {
|
"grapesjs-preset-newsletter": {
|
||||||
"version": "0.2.20",
|
"version": "0.2.20",
|
||||||
"resolved": "https://registry.npmjs.org/grapesjs-preset-newsletter/-/grapesjs-preset-newsletter-0.2.20.tgz",
|
"resolved": "https://registry.npmjs.org/grapesjs-preset-newsletter/-/grapesjs-preset-newsletter-0.2.20.tgz",
|
||||||
"integrity": "sha512-rffUeuznf9Saig+kIUddmGfhWwbLjxdaqAYf6Hoge4b0sfT8knOS4mQXJBdRsSROfzuRhFe6ybRHm4yC32lHxA==",
|
"integrity": "sha512-rffUeuznf9Saig+kIUddmGfhWwbLjxdaqAYf6Hoge4b0sfT8knOS4mQXJBdRsSROfzuRhFe6ybRHm4yC32lHxA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"juice": "4.3.2"
|
"juice": "4.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
||||||
|
},
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "4.1.1",
|
||||||
|
"shebang-command": "1.2.0",
|
||||||
|
"which": "1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
|
||||||
|
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w=="
|
||||||
|
},
|
||||||
|
"juice": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/juice/-/juice-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-3Qym/RnFoCGa9qrDz6xn4zRnohgI6G87xKWZV+/seF3dYpaVqNS1HijsDef+elGhytRY79RIboOzk0hucLtx6g==",
|
||||||
|
"requires": {
|
||||||
|
"cheerio": "0.22.0",
|
||||||
|
"commander": "2.19.0",
|
||||||
|
"cross-spawn": "5.1.0",
|
||||||
|
"deep-extend": "0.5.1",
|
||||||
|
"mensch": "0.3.3",
|
||||||
|
"slick": "1.12.2",
|
||||||
|
"web-resource-inliner": "4.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"har-validator": {
|
"har-validator": {
|
||||||
|
@ -5819,14 +5881,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"juice": {
|
"juice": {
|
||||||
"version": "4.3.2",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/juice/-/juice-4.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/juice/-/juice-5.0.1.tgz",
|
||||||
"integrity": "sha512-3Qym/RnFoCGa9qrDz6xn4zRnohgI6G87xKWZV+/seF3dYpaVqNS1HijsDef+elGhytRY79RIboOzk0hucLtx6g==",
|
"integrity": "sha512-3XJgQxfXo4uHGbCCI6hKwlVtovj0IM+2BVAUCUfWlIiOn1Mljsm4+pYLatOyzY6SF0ks7eT2MSUmOBvue/39sQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"cheerio": "0.22.0",
|
"cheerio": "0.22.0",
|
||||||
"commander": "2.19.0",
|
"commander": "2.19.0",
|
||||||
"cross-spawn": "5.1.0",
|
"cross-spawn": "6.0.5",
|
||||||
"deep-extend": "0.5.1",
|
"deep-extend": "0.6.0",
|
||||||
"mensch": "0.3.3",
|
"mensch": "0.3.3",
|
||||||
"slick": "1.12.2",
|
"slick": "1.12.2",
|
||||||
"web-resource-inliner": "4.2.1"
|
"web-resource-inliner": "4.2.1"
|
||||||
|
@ -5838,11 +5900,13 @@
|
||||||
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
||||||
},
|
},
|
||||||
"cross-spawn": {
|
"cross-spawn": {
|
||||||
"version": "5.1.0",
|
"version": "6.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
|
||||||
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
"integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"lru-cache": "4.1.1",
|
"nice-try": "1.0.5",
|
||||||
|
"path-key": "2.0.1",
|
||||||
|
"semver": "5.5.0",
|
||||||
"shebang-command": "1.2.0",
|
"shebang-command": "1.2.0",
|
||||||
"which": "1.3.0"
|
"which": "1.3.0"
|
||||||
}
|
}
|
||||||
|
@ -6313,6 +6377,21 @@
|
||||||
"react": "15.6.2"
|
"react": "15.6.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
||||||
|
},
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "4.1.1",
|
||||||
|
"shebang-command": "1.2.0",
|
||||||
|
"which": "1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
@ -6321,6 +6400,11 @@
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
|
||||||
|
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w=="
|
||||||
|
},
|
||||||
"hoist-non-react-statics": {
|
"hoist-non-react-statics": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
||||||
|
@ -6346,6 +6430,22 @@
|
||||||
"react": "15.6.2",
|
"react": "15.6.2",
|
||||||
"react-dom": "15.6.2",
|
"react-dom": "15.6.2",
|
||||||
"warning": "3.0.0"
|
"warning": "3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"juice": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/juice/-/juice-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-3Qym/RnFoCGa9qrDz6xn4zRnohgI6G87xKWZV+/seF3dYpaVqNS1HijsDef+elGhytRY79RIboOzk0hucLtx6g==",
|
||||||
|
"requires": {
|
||||||
|
"cheerio": "0.22.0",
|
||||||
|
"commander": "2.19.0",
|
||||||
|
"cross-spawn": "5.1.0",
|
||||||
|
"deep-extend": "0.5.1",
|
||||||
|
"mensch": "0.3.3",
|
||||||
|
"slick": "1.12.2",
|
||||||
|
"web-resource-inliner": "4.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mjml-validator": {
|
"mjml-validator": {
|
||||||
|
@ -6369,6 +6469,21 @@
|
||||||
"react": "15.6.2"
|
"react": "15.6.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
||||||
|
},
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "4.1.1",
|
||||||
|
"shebang-command": "1.2.0",
|
||||||
|
"which": "1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
@ -6377,6 +6492,11 @@
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
|
||||||
|
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w=="
|
||||||
|
},
|
||||||
"hoist-non-react-statics": {
|
"hoist-non-react-statics": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
||||||
|
@ -6402,6 +6522,22 @@
|
||||||
"react": "15.6.2",
|
"react": "15.6.2",
|
||||||
"react-dom": "15.6.2",
|
"react-dom": "15.6.2",
|
||||||
"warning": "3.0.0"
|
"warning": "3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"juice": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/juice/-/juice-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-3Qym/RnFoCGa9qrDz6xn4zRnohgI6G87xKWZV+/seF3dYpaVqNS1HijsDef+elGhytRY79RIboOzk0hucLtx6g==",
|
||||||
|
"requires": {
|
||||||
|
"cheerio": "0.22.0",
|
||||||
|
"commander": "2.19.0",
|
||||||
|
"cross-spawn": "5.1.0",
|
||||||
|
"deep-extend": "0.5.1",
|
||||||
|
"mensch": "0.3.3",
|
||||||
|
"slick": "1.12.2",
|
||||||
|
"web-resource-inliner": "4.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mjml-validator": {
|
"mjml-validator": {
|
||||||
|
@ -6428,6 +6564,21 @@
|
||||||
"react": "15.6.2"
|
"react": "15.6.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
||||||
|
},
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "4.1.1",
|
||||||
|
"shebang-command": "1.2.0",
|
||||||
|
"which": "1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
@ -6436,6 +6587,11 @@
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
|
||||||
|
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w=="
|
||||||
|
},
|
||||||
"hoist-non-react-statics": {
|
"hoist-non-react-statics": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
||||||
|
@ -6461,6 +6617,22 @@
|
||||||
"react": "15.6.2",
|
"react": "15.6.2",
|
||||||
"react-dom": "15.6.2",
|
"react-dom": "15.6.2",
|
||||||
"warning": "3.0.0"
|
"warning": "3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"juice": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/juice/-/juice-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-3Qym/RnFoCGa9qrDz6xn4zRnohgI6G87xKWZV+/seF3dYpaVqNS1HijsDef+elGhytRY79RIboOzk0hucLtx6g==",
|
||||||
|
"requires": {
|
||||||
|
"cheerio": "0.22.0",
|
||||||
|
"commander": "2.19.0",
|
||||||
|
"cross-spawn": "5.1.0",
|
||||||
|
"deep-extend": "0.5.1",
|
||||||
|
"mensch": "0.3.3",
|
||||||
|
"slick": "1.12.2",
|
||||||
|
"web-resource-inliner": "4.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mjml-table": {
|
"mjml-table": {
|
||||||
|
@ -6493,6 +6665,21 @@
|
||||||
"react": "15.6.2"
|
"react": "15.6.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
||||||
|
},
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "4.1.1",
|
||||||
|
"shebang-command": "1.2.0",
|
||||||
|
"which": "1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
@ -6501,6 +6688,11 @@
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
|
||||||
|
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w=="
|
||||||
|
},
|
||||||
"hoist-non-react-statics": {
|
"hoist-non-react-statics": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
||||||
|
@ -6526,6 +6718,22 @@
|
||||||
"react": "15.6.2",
|
"react": "15.6.2",
|
||||||
"react-dom": "15.6.2",
|
"react-dom": "15.6.2",
|
||||||
"warning": "3.0.0"
|
"warning": "3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"juice": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/juice/-/juice-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-3Qym/RnFoCGa9qrDz6xn4zRnohgI6G87xKWZV+/seF3dYpaVqNS1HijsDef+elGhytRY79RIboOzk0hucLtx6g==",
|
||||||
|
"requires": {
|
||||||
|
"cheerio": "0.22.0",
|
||||||
|
"commander": "2.19.0",
|
||||||
|
"cross-spawn": "5.1.0",
|
||||||
|
"deep-extend": "0.5.1",
|
||||||
|
"mensch": "0.3.3",
|
||||||
|
"slick": "1.12.2",
|
||||||
|
"web-resource-inliner": "4.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mjml-validator": {
|
"mjml-validator": {
|
||||||
|
@ -6551,6 +6759,21 @@
|
||||||
"react": "15.6.2"
|
"react": "15.6.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
||||||
|
},
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "4.1.1",
|
||||||
|
"shebang-command": "1.2.0",
|
||||||
|
"which": "1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"debug": {
|
"debug": {
|
||||||
"version": "2.6.9",
|
"version": "2.6.9",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
@ -6559,6 +6782,11 @@
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
|
||||||
|
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w=="
|
||||||
|
},
|
||||||
"hoist-non-react-statics": {
|
"hoist-non-react-statics": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.0",
|
||||||
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
"resolved": "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz",
|
||||||
|
@ -6584,6 +6812,22 @@
|
||||||
"react": "15.6.2",
|
"react": "15.6.2",
|
||||||
"react-dom": "15.6.2",
|
"react-dom": "15.6.2",
|
||||||
"warning": "3.0.0"
|
"warning": "3.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"juice": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/juice/-/juice-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-3Qym/RnFoCGa9qrDz6xn4zRnohgI6G87xKWZV+/seF3dYpaVqNS1HijsDef+elGhytRY79RIboOzk0hucLtx6g==",
|
||||||
|
"requires": {
|
||||||
|
"cheerio": "0.22.0",
|
||||||
|
"commander": "2.19.0",
|
||||||
|
"cross-spawn": "5.1.0",
|
||||||
|
"deep-extend": "0.5.1",
|
||||||
|
"mensch": "0.3.3",
|
||||||
|
"slick": "1.12.2",
|
||||||
|
"web-resource-inliner": "4.2.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"mjml-image": {
|
"mjml-image": {
|
||||||
|
@ -6631,6 +6875,40 @@
|
||||||
"warning": "4.0.2"
|
"warning": "4.0.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "2.19.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz",
|
||||||
|
"integrity": "sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg=="
|
||||||
|
},
|
||||||
|
"cross-spawn": {
|
||||||
|
"version": "5.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
|
||||||
|
"integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=",
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "4.1.1",
|
||||||
|
"shebang-command": "1.2.0",
|
||||||
|
"which": "1.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deep-extend": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.5.1.tgz",
|
||||||
|
"integrity": "sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w=="
|
||||||
|
},
|
||||||
|
"juice": {
|
||||||
|
"version": "4.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/juice/-/juice-4.3.2.tgz",
|
||||||
|
"integrity": "sha512-3Qym/RnFoCGa9qrDz6xn4zRnohgI6G87xKWZV+/seF3dYpaVqNS1HijsDef+elGhytRY79RIboOzk0hucLtx6g==",
|
||||||
|
"requires": {
|
||||||
|
"cheerio": "0.22.0",
|
||||||
|
"commander": "2.19.0",
|
||||||
|
"cross-spawn": "5.1.0",
|
||||||
|
"deep-extend": "0.5.1",
|
||||||
|
"mensch": "0.3.3",
|
||||||
|
"slick": "1.12.2",
|
||||||
|
"web-resource-inliner": "4.2.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"warning": {
|
"warning": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/warning/-/warning-4.0.2.tgz",
|
||||||
|
@ -6683,6 +6961,11 @@
|
||||||
"integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=",
|
"integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"nice-try": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ=="
|
||||||
|
},
|
||||||
"no-case": {
|
"no-case": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz",
|
||||||
|
@ -7111,6 +7394,11 @@
|
||||||
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
|
"integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"path-key": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
|
||||||
|
"integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A="
|
||||||
|
},
|
||||||
"path-parse": {
|
"path-parse": {
|
||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||||
|
|
|
@ -43,6 +43,7 @@
|
||||||
"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",
|
||||||
|
"juice": "^5.0.1",
|
||||||
"mjml4-in-browser": "^1.0.1",
|
"mjml4-in-browser": "^1.0.1",
|
||||||
"moment": "^2.18.1",
|
"moment": "^2.18.1",
|
||||||
"moment-timezone": "^0.5.13",
|
"moment-timezone": "^0.5.13",
|
||||||
|
|
71
client/src/lib/sandbox-common.scss
Normal file
71
client/src/lib/sandbox-common.scss
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
$navbarHeight: 34px;
|
||||||
|
$editorNormalHeight: 800px !default;
|
||||||
|
|
||||||
|
.editor {
|
||||||
|
.host {
|
||||||
|
@if $editorNormalHeight {
|
||||||
|
height: $editorNormalHeight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.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;
|
||||||
|
}
|
|
@ -1,72 +1,7 @@
|
||||||
$navbarHeight: 34px;
|
$editorNormalHeight: false;
|
||||||
|
@import "sandbox-common";
|
||||||
.editor {
|
|
||||||
.host {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sandbox {
|
.sandbox {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
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;
|
|
||||||
}
|
|
166
client/src/lib/sandboxed-codeeditor-root.js
Normal file
166
client/src/lib/sandboxed-codeeditor-root.js
Normal file
|
@ -0,0 +1,166 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import './public-path';
|
||||||
|
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import ReactDOM
|
||||||
|
from 'react-dom';
|
||||||
|
import {
|
||||||
|
I18nextProvider,
|
||||||
|
translate,
|
||||||
|
} from 'react-i18next';
|
||||||
|
import i18n
|
||||||
|
from './i18n';
|
||||||
|
import {
|
||||||
|
parentRPC,
|
||||||
|
UntrustedContentRoot
|
||||||
|
} from './untrusted';
|
||||||
|
import PropTypes
|
||||||
|
from "prop-types";
|
||||||
|
import styles
|
||||||
|
from "./sandboxed-codeeditor.scss";
|
||||||
|
import {
|
||||||
|
getPublicUrl,
|
||||||
|
getSandboxUrl,
|
||||||
|
getTrustedUrl
|
||||||
|
} from "./urls";
|
||||||
|
import {
|
||||||
|
base,
|
||||||
|
unbase
|
||||||
|
} from "../../../shared/templates";
|
||||||
|
|
||||||
|
import brace from 'brace';
|
||||||
|
import ACEEditorRaw from 'react-ace';
|
||||||
|
import 'brace/theme/github';
|
||||||
|
import 'brace/ext/searchbox';
|
||||||
|
import 'brace/mode/html';
|
||||||
|
import {CodeEditorSourceType} from "./sandboxed-codeeditor-shared";
|
||||||
|
|
||||||
|
import mjml2html from "mjml4-in-browser";
|
||||||
|
import juice from "juice";
|
||||||
|
|
||||||
|
@translate(null, { withRef: true })
|
||||||
|
class CodeEditorSandbox extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
let defaultSource;
|
||||||
|
|
||||||
|
if (props.sourceType === CodeEditorSourceType.MJML) {
|
||||||
|
defaultSource =
|
||||||
|
'<mjml>\n' +
|
||||||
|
' <mj-body>\n' +
|
||||||
|
' <mj-section>\n' +
|
||||||
|
' <mj-column>\n' +
|
||||||
|
' <!-- First column content -->\n' +
|
||||||
|
' </mj-column>\n' +
|
||||||
|
' <mj-column>\n' +
|
||||||
|
' <!-- Second column content -->\n' +
|
||||||
|
' </mj-column>\n' +
|
||||||
|
' </mj-section>\n' +
|
||||||
|
' </mj-body>\n' +
|
||||||
|
'</mjml>';
|
||||||
|
|
||||||
|
} else if (props.sourceType === CodeEditorSourceType.HTML) {
|
||||||
|
defaultSource =
|
||||||
|
'<!DOCTYPE html>\n' +
|
||||||
|
'<html>\n' +
|
||||||
|
'<head>\n' +
|
||||||
|
' <meta charset="UTF-8">\n' +
|
||||||
|
' <title>Title of the document</title>\n' +
|
||||||
|
'</head>\n' +
|
||||||
|
'<body>\n' +
|
||||||
|
' Content of the document......\n' +
|
||||||
|
'</body>\n' +
|
||||||
|
'</html>';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const trustedUrlBase = getTrustedUrl();
|
||||||
|
const sandboxUrlBase = getSandboxUrl();
|
||||||
|
const publicUrlBase = getPublicUrl();
|
||||||
|
const source = this.props.initialSource ? base(this.props.initialSource, trustedUrlBase, sandboxUrlBase, publicUrlBase) : defaultSource;
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
source,
|
||||||
|
preview: props.initialPreview
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
entityTypeId: PropTypes.string,
|
||||||
|
entityId: PropTypes.number,
|
||||||
|
initialSource: PropTypes.string,
|
||||||
|
sourceType: PropTypes.string,
|
||||||
|
initialPreview: PropTypes.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
async exportState(method, params) {
|
||||||
|
const trustedUrlBase = getTrustedUrl();
|
||||||
|
const sandboxUrlBase = getSandboxUrl();
|
||||||
|
const publicUrlBase = getPublicUrl();
|
||||||
|
return {
|
||||||
|
source: unbase(this.state.source, trustedUrlBase, sandboxUrlBase, publicUrlBase, true)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async setPreview(method, preview) {
|
||||||
|
this.setState({
|
||||||
|
preview
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
parentRPC.setMethodHandler('exportState', ::this.exportState);
|
||||||
|
parentRPC.setMethodHandler('setPreview', ::this.setPreview);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let previewContents;
|
||||||
|
|
||||||
|
if (this.props.sourceType === CodeEditorSourceType.MJML) {
|
||||||
|
const res = mjml2html(this.state.source);
|
||||||
|
previewContents = res.html;
|
||||||
|
} else if (this.props.sourceType === CodeEditorSourceType.HTML) {
|
||||||
|
previewContents = juice(this.state.source);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.sandbox}>
|
||||||
|
<div className={this.state.preview ? styles.aceEditorWithPreview : styles.aceEditorWithoutPreview}>
|
||||||
|
<ACEEditorRaw
|
||||||
|
mode="html"
|
||||||
|
theme="github"
|
||||||
|
width="100%"
|
||||||
|
height="100%"
|
||||||
|
onChange={data => this.setState({source: data})}
|
||||||
|
fontSize={12}
|
||||||
|
showPrintMargin={false}
|
||||||
|
value={this.state.source}
|
||||||
|
tabSize={2}
|
||||||
|
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(previewContents)}></iframe>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
parentRPC.init();
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<I18nextProvider i18n={ i18n }>
|
||||||
|
<UntrustedContentRoot render={props => <CodeEditorSandbox {...props} />} />
|
||||||
|
</I18nextProvider>,
|
||||||
|
document.getElementById('root')
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
11
client/src/lib/sandboxed-codeeditor-shared.js
Normal file
11
client/src/lib/sandboxed-codeeditor-shared.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
export const CodeEditorSourceType = {
|
||||||
|
MJML: 'mjml',
|
||||||
|
HTML: 'html'
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getCodeEditorSourceTypeOptions = t => [
|
||||||
|
{key: CodeEditorSourceType.MJML, label: t('MJML')},
|
||||||
|
{key: CodeEditorSourceType.HTML, label: t('HTML')}
|
||||||
|
];
|
86
client/src/lib/sandboxed-codeeditor.js
Normal file
86
client/src/lib/sandboxed-codeeditor.js
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
import React, {Component} from 'react';
|
||||||
|
import {translate} from 'react-i18next';
|
||||||
|
import PropTypes
|
||||||
|
from "prop-types";
|
||||||
|
import styles
|
||||||
|
from "./sandboxed-codeeditor.scss";
|
||||||
|
|
||||||
|
import {UntrustedContentHost} from './untrusted';
|
||||||
|
import {Icon} from "./bootstrap-components";
|
||||||
|
import {getTrustedUrl} from "./urls";
|
||||||
|
|
||||||
|
@translate(null, { withRef: true })
|
||||||
|
export class CodeEditorHost extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
fullscreen: false,
|
||||||
|
preview: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
entityTypeId: PropTypes.string,
|
||||||
|
entity: PropTypes.object,
|
||||||
|
initialSource: PropTypes.string,
|
||||||
|
sourceType: PropTypes.string,
|
||||||
|
title: PropTypes.string,
|
||||||
|
onFullscreenAsync: PropTypes.func
|
||||||
|
}
|
||||||
|
|
||||||
|
async toggleFullscreenAsync() {
|
||||||
|
const fullscreen = !this.state.fullscreen;
|
||||||
|
this.setState({
|
||||||
|
fullscreen
|
||||||
|
});
|
||||||
|
await this.props.onFullscreenAsync(fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
async togglePreviewAsync() {
|
||||||
|
const preview = !this.state.preview;
|
||||||
|
this.setState({
|
||||||
|
preview
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.contentNode.ask('setPreview', preview);
|
||||||
|
}
|
||||||
|
async exportState() {
|
||||||
|
return await this.contentNode.ask('exportState');
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const t = this.props.t;
|
||||||
|
|
||||||
|
const editorData = {
|
||||||
|
entityTypeId: this.props.entityTypeId,
|
||||||
|
entityId: this.props.entity.id,
|
||||||
|
initialSource: this.props.initialSource,
|
||||||
|
sourceType: this.props.sourceType,
|
||||||
|
initialPreview: this.state.preview
|
||||||
|
};
|
||||||
|
|
||||||
|
const tokenData = {
|
||||||
|
entityTypeId: this.props.entityTypeId,
|
||||||
|
entityId: this.props.entity.id
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<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>
|
||||||
|
<a className={styles.btn} onClick={::this.togglePreviewAsync}><Icon icon={this.state.preview ? 'eye-close': 'eye-open'}/></a>
|
||||||
|
</div>
|
||||||
|
<UntrustedContentHost ref={node => this.contentNode = node} className={styles.host} singleToken={true} contentProps={editorData} contentSrc="codeeditor/editor" tokenMethod="codeeditor" tokenParams={tokenData}/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeEditorHost.prototype.exportState = async function() {
|
||||||
|
return await this.getWrappedInstance().exportState();
|
||||||
|
};
|
35
client/src/lib/sandboxed-codeeditor.scss
Normal file
35
client/src/lib/sandboxed-codeeditor.scss
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
@import "sandbox-common";
|
||||||
|
|
||||||
|
.sandbox {
|
||||||
|
}
|
||||||
|
|
||||||
|
.aceEditorWithPreview, .aceEditorWithoutPreview, .preview {
|
||||||
|
position: absolute;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aceEditorWithPreview {
|
||||||
|
border-right: #e8e8e8 solid 2px;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aceEditorWithoutPreview {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview {
|
||||||
|
border-left: #e8e8e8 solid 2px;
|
||||||
|
width: 50%;
|
||||||
|
left: 50%;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: 0px none;
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,3 +4,8 @@ export const GrapesJSSourceType = {
|
||||||
MJML: 'mjml',
|
MJML: 'mjml',
|
||||||
HTML: 'html'
|
HTML: 'html'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getGrapesJSSourceTypeOptions = t => [
|
||||||
|
{key: GrapesJSSourceType.MJML, label: t('MJML')},
|
||||||
|
{key: GrapesJSSourceType.HTML, label: t('HTML')}
|
||||||
|
];
|
||||||
|
|
|
@ -10,7 +10,6 @@ import styles
|
||||||
import {UntrustedContentHost} from './untrusted';
|
import {UntrustedContentHost} from './untrusted';
|
||||||
import {Icon} from "./bootstrap-components";
|
import {Icon} from "./bootstrap-components";
|
||||||
import {getTrustedUrl} from "./urls";
|
import {getTrustedUrl} from "./urls";
|
||||||
import {GrapesJSSourceType} from "./sandboxed-grapesjs-shared";
|
|
||||||
|
|
||||||
@translate(null, { withRef: true })
|
@translate(null, { withRef: true })
|
||||||
export class GrapesJSHost extends Component {
|
export class GrapesJSHost extends Component {
|
||||||
|
|
|
@ -1,72 +1,4 @@
|
||||||
$navbarHeight: 34px;
|
@import "sandbox-common";
|
||||||
|
|
||||||
.editor {
|
|
||||||
.host {
|
|
||||||
height: 800px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
:global .grapesjs-body {
|
:global .grapesjs-body {
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
|
|
|
@ -1,29 +1,4 @@
|
||||||
$navbarHeight: 34px;
|
@import "sandbox-common";
|
||||||
|
|
||||||
.editor {
|
|
||||||
.host {
|
|
||||||
height: 800px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.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%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
:global .mo-standalone {
|
:global .mo-standalone {
|
||||||
top: 0px;
|
top: 0px;
|
||||||
|
@ -31,45 +6,3 @@ $navbarHeight: 34px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import React from "react";
|
||||||
import {
|
import {
|
||||||
ACEEditor,
|
ACEEditor,
|
||||||
AlignedRow,
|
AlignedRow,
|
||||||
|
CheckBox,
|
||||||
CKEditor,
|
CKEditor,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
StaticField,
|
StaticField,
|
||||||
|
@ -15,6 +16,17 @@ import 'brace/mode/html';
|
||||||
import { MosaicoHost } from "../lib/sandboxed-mosaico";
|
import { MosaicoHost } from "../lib/sandboxed-mosaico";
|
||||||
import { CKEditorHost } from "../lib/sandboxed-ckeditor";
|
import { CKEditorHost } from "../lib/sandboxed-ckeditor";
|
||||||
import { GrapesJSHost } from "../lib/sandboxed-grapesjs";
|
import { GrapesJSHost } from "../lib/sandboxed-grapesjs";
|
||||||
|
import { CodeEditorHost } from "../lib/sandboxed-codeeditor";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getGrapesJSSourceTypeOptions,
|
||||||
|
GrapesJSSourceType
|
||||||
|
} from "../lib/sandboxed-grapesjs-shared";
|
||||||
|
|
||||||
|
import {
|
||||||
|
getCodeEditorSourceTypeOptions,
|
||||||
|
CodeEditorSourceType
|
||||||
|
} from "../lib/sandboxed-codeeditor-shared";
|
||||||
|
|
||||||
import {getTemplateTypes as getMosaicoTemplateTypes} from './mosaico/helpers';
|
import {getTemplateTypes as getMosaicoTemplateTypes} from './mosaico/helpers';
|
||||||
import {getSandboxUrl} from "../lib/urls";
|
import {getSandboxUrl} from "../lib/urls";
|
||||||
|
@ -26,7 +38,6 @@ import {
|
||||||
import {Trans} from "react-i18next";
|
import {Trans} from "react-i18next";
|
||||||
|
|
||||||
import styles from "../lib/styles.scss";
|
import styles from "../lib/styles.scss";
|
||||||
import {GrapesJSSourceType} from "../lib/sandboxed-grapesjs-shared";
|
|
||||||
|
|
||||||
export const ResourceType = {
|
export const ResourceType = {
|
||||||
TEMPLATE: 'template',
|
TEMPLATE: 'template',
|
||||||
|
@ -177,21 +188,17 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const grapesJSSourceTypes = [
|
const grapesJSSourceTypes = getGrapesJSSourceTypeOptions(t);
|
||||||
{key: GrapesJSSourceType.MJML, label: t('MJML')},
|
|
||||||
{key: GrapesJSSourceType.HTML, label: t('HTML')}
|
|
||||||
];
|
|
||||||
|
|
||||||
const grapesJSSourceTypeLabels = {};
|
const grapesJSSourceTypeLabels = {};
|
||||||
for ({key, label} of grapesJSSourceTypes) {
|
for ({key, label} of grapesJSSourceTypes) {
|
||||||
grapesJSSourceTypeLabels[key] = label;
|
grapesJSSourceTypeLabels[key] = label;
|
||||||
}
|
}
|
||||||
|
|
||||||
templateTypes.grapesjs = {
|
templateTypes.grapesjs = {
|
||||||
typeName: t('GrapeJS'),
|
typeName: t('GrapesJS'),
|
||||||
getTypeForm: (owner, isEdit) => {
|
getTypeForm: (owner, isEdit) => {
|
||||||
if (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 {
|
} else {
|
||||||
return <Dropdown id={prefix + 'grapesJSSourceType'} label={t('Type')} options={grapesJSSourceTypes}/>;
|
return <Dropdown id={prefix + 'grapesJSSourceType'} label={t('Type')} options={grapesJSSourceTypes}/>;
|
||||||
}
|
}
|
||||||
|
@ -283,30 +290,62 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
||||||
validate: state => {}
|
validate: state => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const codeEditorSourceTypes = getCodeEditorSourceTypeOptions(t);
|
||||||
|
const codeEditorSourceTypeLabels = {};
|
||||||
|
for ({key, label} of codeEditorSourceTypes) {
|
||||||
|
codeEditorSourceTypeLabels[key] = label;
|
||||||
|
}
|
||||||
|
|
||||||
templateTypes.codeeditor = {
|
templateTypes.codeeditor = {
|
||||||
typeName: t('Code Editor'),
|
typeName: t('Code Editor'),
|
||||||
getTypeForm: (owner, isEdit) => null,
|
getTypeForm: (owner, isEdit) => {
|
||||||
getHTMLEditor: owner => <ACEEditor id={prefix + 'html'} height="600px" mode="html" label={t('Template content (HTML)')}/>,
|
const sourceType = owner.getFormValue(prefix + 'codeEditorSourceType');
|
||||||
exportHTMLEditorData: async owner => {},
|
if (isEdit) {
|
||||||
initData: () => ({}),
|
return <StaticField id={prefix + 'codeEditorSourceType'} className={styles.formDisabled} label={t('Type')}>{codeEditorSourceTypeLabels[sourceType]}</StaticField>;
|
||||||
afterLoad: data => {},
|
} else {
|
||||||
beforeSave: data => {
|
return <Dropdown id={prefix + 'codeEditorSourceType'} label={t('Type')} options={codeEditorSourceTypes}/>;
|
||||||
clearBeforeSave(data);
|
}
|
||||||
},
|
},
|
||||||
afterTypeChange: mutState => {},
|
getHTMLEditor: owner =>
|
||||||
validate: state => {}
|
<AlignedRow label={t('Template content (HTML)')}>
|
||||||
};
|
<CodeEditorHost
|
||||||
|
ref={node => owner.editorNode = node}
|
||||||
|
entity={owner.props.entity}
|
||||||
|
entityTypeId={entityTypeId}
|
||||||
|
initialSource={owner.getFormValue(prefix + 'codeEditorData').source}
|
||||||
|
sourceType={owner.getFormValue(prefix + 'codeEditorSourceType')}
|
||||||
|
title={t('Code Editor Template Designer')}
|
||||||
|
onFullscreenAsync={::owner.setElementInFullscreen}
|
||||||
|
/>
|
||||||
|
</AlignedRow>,
|
||||||
|
exportHTMLEditorData: async owner => {
|
||||||
|
const {html, source} = await owner.editorNode.exportState();
|
||||||
|
owner.updateFormValue(prefix + 'html', html);
|
||||||
|
owner.updateFormValue(prefix + 'codeEditorData', {
|
||||||
|
source
|
||||||
|
});
|
||||||
|
},
|
||||||
|
initData: () => ({
|
||||||
|
[prefix + 'codeEditorSourceType']: CodeEditorSourceType.HTML,
|
||||||
|
[prefix + 'codeEditorData']: {}
|
||||||
|
}),
|
||||||
|
afterLoad: data => {
|
||||||
|
data[prefix + 'codeEditorSourceType'] = data[prefix + 'data'].sourceType;
|
||||||
|
data[prefix + 'codeEditorData'] = {
|
||||||
|
source: data[prefix + 'data'].source
|
||||||
|
};
|
||||||
|
},
|
||||||
|
beforeSave: data => {
|
||||||
|
data[prefix + 'data'] = {
|
||||||
|
sourceType: data[prefix + 'codeEditorSourceType'],
|
||||||
|
source: data[prefix + 'codeEditorData'].source
|
||||||
|
};
|
||||||
|
|
||||||
templateTypes.mjml = { // FIXME
|
|
||||||
getTypeForm: (owner, isEdit) => null,
|
|
||||||
getHTMLEditor: owner => null,
|
|
||||||
exportHTMLEditorData: async owner => {},
|
|
||||||
initData: () => ({}),
|
|
||||||
afterLoad: data => {},
|
|
||||||
beforeSave: data => {
|
|
||||||
clearBeforeSave(data);
|
clearBeforeSave(data);
|
||||||
},
|
},
|
||||||
afterTypeChange: mutState => {},
|
afterTypeChange: mutState => {
|
||||||
|
initFieldsIfMissing(mutState, 'codeeditor');
|
||||||
|
},
|
||||||
validate: state => {}
|
validate: state => {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@ import 'brace/mode/html'
|
||||||
import 'brace/mode/xml'
|
import 'brace/mode/xml'
|
||||||
|
|
||||||
export function getTemplateTypesOrder() {
|
export function getTemplateTypesOrder() {
|
||||||
return ['mjml', 'html'];
|
return [/* 'mjml' , */ 'html'];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getTemplateTypes(t) {
|
export function getTemplateTypes(t) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ module.exports = {
|
||||||
"mosaico-root": ['babel-polyfill', './src/lib/sandboxed-mosaico-root.js'],
|
"mosaico-root": ['babel-polyfill', './src/lib/sandboxed-mosaico-root.js'],
|
||||||
"ckeditor-root": ['babel-polyfill', './src/lib/sandboxed-ckeditor-root.js'],
|
"ckeditor-root": ['babel-polyfill', './src/lib/sandboxed-ckeditor-root.js'],
|
||||||
"grapesjs-root": ['babel-polyfill', './src/lib/sandboxed-grapesjs-root.js'],
|
"grapesjs-root": ['babel-polyfill', './src/lib/sandboxed-grapesjs-root.js'],
|
||||||
|
"codeeditor-root": ['babel-polyfill', './src/lib/sandboxed-codeeditor-root.js'],
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
library: 'MailtrainReactBody',
|
library: 'MailtrainReactBody',
|
||||||
|
|
|
@ -16,12 +16,12 @@ title: mailtrain
|
||||||
|
|
||||||
# Enabled HTML editors
|
# Enabled HTML editors
|
||||||
editors:
|
editors:
|
||||||
- ckeditor4
|
- grapesjs
|
||||||
- ckeditor5
|
|
||||||
- codeeditor
|
|
||||||
- mosaico
|
- mosaico
|
||||||
- mosaicoWithFsTemplate
|
- mosaicoWithFsTemplate
|
||||||
- grapesjs
|
- ckeditor5
|
||||||
|
- ckeditor4
|
||||||
|
- codeeditor
|
||||||
|
|
||||||
# Default language to use
|
# Default language to use
|
||||||
language: en
|
language: en
|
||||||
|
|
|
@ -150,7 +150,7 @@ async function prepareHtml(html) {
|
||||||
FetchExternalResources: false, // disables resource loading over HTTP / filesystem
|
FetchExternalResources: false, // disables resource loading over HTTP / filesystem
|
||||||
ProcessExternalResources: false // do not execute JS within script blocks
|
ProcessExternalResources: false // do not execute JS within script blocks
|
||||||
}
|
}
|
||||||
});
|
});pre
|
||||||
|
|
||||||
const head = win.document.querySelector('head');
|
const head = win.document.querySelector('head');
|
||||||
let hasCharsetTag = false;
|
let hasCharsetTag = false;
|
||||||
|
|
|
@ -20,7 +20,7 @@ users.registerRestrictedAccessTokenMethod('ckeditor', async ({entityTypeId, enti
|
||||||
if (entityTypeId === 'template') {
|
if (entityTypeId === 'template') {
|
||||||
const tmpl = await templates.getById(contextHelpers.getAdminContext(), entityId, false);
|
const tmpl = await templates.getById(contextHelpers.getAdminContext(), entityId, false);
|
||||||
|
|
||||||
if (tmpl.type === 'ckeditor') {
|
if (tmpl.type === 'ckeditor4') {
|
||||||
return {
|
return {
|
||||||
permissions: {
|
permissions: {
|
||||||
'template': {
|
'template': {
|
||||||
|
|
59
routes/sandboxed-codeeditor.js
Normal file
59
routes/sandboxed-codeeditor.js
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const routerFactory = require('../lib/router-async');
|
||||||
|
const passport = require('../lib/passport');
|
||||||
|
const clientHelpers = require('../lib/client-helpers');
|
||||||
|
const users = require('../models/users');
|
||||||
|
|
||||||
|
const files = require('../models/files');
|
||||||
|
const fileHelpers = require('../lib/file-helpers');
|
||||||
|
|
||||||
|
const templates = require('../models/templates');
|
||||||
|
|
||||||
|
const contextHelpers = require('../lib/context-helpers');
|
||||||
|
|
||||||
|
const { getTrustedUrl, getSandboxUrl, getPublicUrl } = require('../lib/urls');
|
||||||
|
const { AppType } = require('../shared/app');
|
||||||
|
|
||||||
|
|
||||||
|
users.registerRestrictedAccessTokenMethod('codeeditor', async ({entityTypeId, entityId}) => {
|
||||||
|
if (entityTypeId === 'template') {
|
||||||
|
const tmpl = await templates.getById(contextHelpers.getAdminContext(), entityId, false);
|
||||||
|
|
||||||
|
if (tmpl.type === 'codeeditor') {
|
||||||
|
return {
|
||||||
|
permissions: {
|
||||||
|
'template': {
|
||||||
|
[entityId]: new Set(['manageFiles', 'view'])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function getRouter(appType) {
|
||||||
|
const router = routerFactory.create();
|
||||||
|
|
||||||
|
if (appType === AppType.SANDBOXED) {
|
||||||
|
router.getAsync('/editor', passport.csrfProtection, async (req, res) => {
|
||||||
|
const mailtrainConfig = await clientHelpers.getAnonymousConfig(req.context, appType);
|
||||||
|
|
||||||
|
res.render('ckeditor/root', {
|
||||||
|
layout: 'ckeditor/layout',
|
||||||
|
reactCsrfToken: req.csrfToken(),
|
||||||
|
mailtrainConfig: JSON.stringify(mailtrainConfig),
|
||||||
|
scriptFiles: [
|
||||||
|
getSandboxUrl('mailtrain/common.js'),
|
||||||
|
getSandboxUrl('mailtrain/codeeditor-root.js')
|
||||||
|
],
|
||||||
|
publicPath: getSandboxUrl()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return router;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.getRouter = getRouter;
|
35
views/codeeditor/layout.hbs
Normal file
35
views/codeeditor/layout.hbs
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<meta name="description" content="{{#translate}}Self hosted email newsletter app{{/translate}}">
|
||||||
|
<link rel="shortcut icon" href="{{publicPath}}favicon.ico" type="image/x-icon" />
|
||||||
|
<link rel="icon" href="{{publicPath}}static/favicon.ico">
|
||||||
|
|
||||||
|
<title>Mailtrain</title>
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="{{publicPath}}static/bootstrap/themes/united.min.css">
|
||||||
|
<script src="{{publicPath}}static/jquery-2.2.1.min.js"></script>
|
||||||
|
<script src="{{publicPath}}static/bootstrap/js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
{{#if mailtrainConfig}}
|
||||||
|
<script>
|
||||||
|
{{#if reactCsrfToken}}window.csfrToken = '{{reactCsrfToken}}';{{/if}}
|
||||||
|
window.mailtrainConfig = {{{mailtrainConfig}}};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{{#each scriptFiles}}
|
||||||
|
<script src="{{this}}"></script>
|
||||||
|
{{/each}}
|
||||||
|
{{/if}}
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
{{{body}}}
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
6
views/codeeditor/root.hbs
Normal file
6
views/codeeditor/root.hbs
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<div id="root"></div>
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
MailtrainReactBody.default();
|
||||||
|
});
|
||||||
|
</script>
|
|
@ -1,18 +0,0 @@
|
||||||
# Process title visible in monitoring logs and process listing
|
|
||||||
title="mailtrain"
|
|
||||||
|
|
||||||
# Default language to use
|
|
||||||
language="en"
|
|
||||||
|
|
||||||
[log]
|
|
||||||
# silly|verbose|info|http|warn|error|silent
|
|
||||||
level="verbose"
|
|
||||||
|
|
||||||
[mysql]
|
|
||||||
host="localhost"
|
|
||||||
user="mailtrain"
|
|
||||||
password="mailtrain"
|
|
||||||
database="mailtrain"
|
|
||||||
port=3306
|
|
||||||
charset="utf8mb4"
|
|
||||||
timezone="local"
|
|
22
workers/reports/config/default.yaml
Normal file
22
workers/reports/config/default.yaml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
# Process title visible in monitoring logs and process listing
|
||||||
|
title: mailtrain
|
||||||
|
|
||||||
|
# Default language to use
|
||||||
|
language: en
|
||||||
|
|
||||||
|
log:
|
||||||
|
# silly|verbose|info|http|warn|error|silent
|
||||||
|
level: verbose
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
host: localhost
|
||||||
|
user: mailtrain
|
||||||
|
password: mailtrain
|
||||||
|
database: mailtrain
|
||||||
|
# Some installations, eg. MAMP can use a different port (8889)
|
||||||
|
# MAMP users should also turn on Allow network access to MySQL otherwise MySQL might not be accessible
|
||||||
|
port: 3306
|
||||||
|
charset: utf8mb4
|
||||||
|
# The timezone configured on the MySQL server. This can be 'local', 'Z', or an offset in the form +HH:MM or -HH:MM
|
||||||
|
timezone: local
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue