'use strict';
import './public-path';
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {TranslationRoot, withTranslation} from './i18n';
import {parentRPC, UntrustedContentRoot} from './untrusted';
import PropTypes from "prop-types";
import {getPublicUrl, getSandboxUrl, getTrustedUrl} from "./urls";
import {base, unbase} from "../../../shared/templates";
import mjml2html from "./mjml";
import 'grapesjs/dist/css/grapes.min.css';
import grapesjs from 'grapesjs';
import 'grapesjs-mjml';
import 'grapesjs-preset-newsletter';
import 'grapesjs-preset-newsletter/dist/grapesjs-preset-newsletter.css';
import "./sandboxed-grapesjs.scss";
import axios from './axios';
import {GrapesJSSourceType} from "./sandboxed-grapesjs-shared";
import {withComponentMixins} from "./decorator-helpers";
grapesjs.plugins.add('mailtrain-remove-buttons', (editor, opts = {}) => {
// This needs to be done in on-load and after gjs plugin because grapesjs-preset-newsletter tries to set titles to all buttons (including those we remove)
// see https://github.com/artf/grapesjs-preset-newsletter/blob/e0a91636973a5a1481e9d7929e57a8869b1db72e/src/index.js#L248
editor.on('load', () => {
const panelManager = editor.Panels;
panelManager.removeButton('options','fullscreen');
panelManager.removeButton('options','export-template');
});
});
@withComponentMixins([
withTranslation
])
export class GrapesJSSandbox extends Component {
constructor(props) {
super(props);
this.initialized = false;
this.state = {
assets: null
};
}
static propTypes = {
entityTypeId: PropTypes.string,
entityId: PropTypes.number,
tagLanguage: PropTypes.string,
initialSource: PropTypes.string,
initialStyle: PropTypes.string,
sourceType: PropTypes.string
}
async exportState(method, params) {
const props = this.props;
const editor = this.editor;
// If exportState comes during text editing (via RichTextEditor), we need to cancel the editing, so that the
// text being edited is stored in the model
const sel = editor.getSelected();
if (sel && sel.view && sel.view.disableEditing) {
sel.view.disableEditing();
}
const trustedUrlBase = getTrustedUrl();
const sandboxUrlBase = getSandboxUrl();
const publicUrlBase = getPublicUrl();
const source = unbase(editor.getHtml(), this.props.tagLanguage, trustedUrlBase, sandboxUrlBase, publicUrlBase, true);
const style = unbase(editor.getCss(), this.props.tagLanguage, trustedUrlBase, sandboxUrlBase, publicUrlBase, true);
let html;
if (props.sourceType === GrapesJSSourceType.MJML) {
const preMjml = '';
const postMjml = '';
const mjml = preMjml + source + postMjml;
const mjmlRes = mjml2html(mjml);
html = mjmlRes.html;
} else if (props.sourceType === GrapesJSSourceType.HTML) {
const commandManager = editor.Commands;
const cmdGetCode = commandManager.get('gjs-get-inlined-html');
const htmlBody = cmdGetCode.run(editor);
const preHtml = '
';
const postHtml = '';
html = preHtml + unbase(htmlBody, this.props.tagLanguage, trustedUrlBase, sandboxUrlBase, publicUrlBase, true) + postHtml;
}
return {
html,
style: style,
source: source
};
}
async fetchAssets() {
const props = this.props;
const resp = await axios.get(getSandboxUrl(`rest/files-list/${props.entityTypeId}/file/${props.entityId}`));
this.setState({
assets: resp.data.map( f => ({type: 'image', src: getPublicUrl(`files/${props.entityTypeId}/file/${props.entityId}/${f.filename}`)}) )
});
}
componentDidMount() {
// noinspection JSIgnoredPromiseFromCall
this.fetchAssets();
}
componentDidUpdate() {
if (!this.initialized && this.state.assets !== null) {
this.initGrapesJs();
this.initialized = true;
}
}
initGrapesJs() {
const props = this.props;
parentRPC.setMethodHandler('exportState', ::this.exportState);
const trustedUrlBase = getTrustedUrl();
const sandboxUrlBase = getSandboxUrl();
const publicUrlBase = getPublicUrl();
const config = {
noticeOnUnload: false,
container: this.canvasNode,
height: '100%',
width: '100%',
storageManager:{
type: 'none'
},
assetManager: {
assets: this.state.assets,
upload: getSandboxUrl(`grapesjs/upload/${this.props.entityTypeId}/${this.props.entityId}`),
uploadText: 'Drop images here or click to upload',
headers: {
'X-CSRF-TOKEN': '{{csrfToken}}',
},
autoAdd: true
},
styleManager: {
clearProperties: true,
},
fromElement: false,
components: '',
style: '',
plugins: [
],
pluginsOpts: {
}
};
let defaultSource, defaultStyle;
if (props.sourceType === GrapesJSSourceType.MJML) {
defaultSource =
'\n' +
' \n' +
' \n' +
' Lorem Ipsum...\n' +
' \n' +
' \n' +
'';
defaultStyle = '';
config.plugins.push('gjs-mjml');
config.pluginsOpts['gjs-mjml'] = {
preMjml: '',
postMjml: ''
};
} else if (props.sourceType === GrapesJSSourceType.HTML) {
defaultSource =
'\n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' | \n' +
' View in browser\n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' GrapesJS Newsletter Builder\n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' Build your newsletters faster than ever\n' +
' \n' +
' \n' +
' Import, build, test and export responsive newsletter templates faster than ever using the GrapesJS Newsletter Builder.\n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' Built-in Blocks\n' +
' \n' +
' Drag and drop built-in blocks from the right panel and style them in a matter of seconds\n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' Toggle images\n' +
' \n' +
' Build a good looking newsletter even without images enabled by the email clients\n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' Test it\n' +
' \n' +
' You can send email tests directly from the editor and check how are looking on your email clients\n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' \n' +
' Responsive\n' +
' \n' +
' Using the device manager you\'ll always send a fully responsive contents\n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' \n' +
' | \n' +
' \n' +
' \n' +
' | \n' +
'
\n' +
'
';
defaultStyle =
'.link {\n' +
' color: rgb(217, 131, 166);\n' +
' }\n' +
' .row{\n' +
' vertical-align:top;\n' +
' }\n' +
' .main-body{\n' +
' min-height:150px;\n' +
' padding: 5px;\n' +
' width:100%;\n' +
' height:100%;\n' +
' background-color:rgb(234, 236, 237);\n' +
' }\n' +
' .c926{\n' +
' color:rgb(158, 83, 129);\n' +
' width:100%;\n' +
' font-size:50px;\n' +
' }\n' +
' .cell.c849{\n' +
' width:11%;\n' +
' }\n' +
' .c1144{\n' +
' padding: 10px;\n' +
' font-size:17px;\n' +
' font-weight: 300;\n' +
' }\n' +
' .card{\n' +
' min-height:150px;\n' +
' padding: 5px;\n' +
' margin-bottom:20px;\n' +
' height:0px;\n' +
' }\n' +
' .card-cell{\n' +
' background-color:rgb(255, 255, 255);\n' +
' overflow:hidden;\n' +
' border-radius: 3px;\n' +
' padding: 0;\n' +
' text-align:center;\n' +
' }\n' +
' .card.sector{\n' +
' background-color:rgb(255, 255, 255);\n' +
' border-radius: 3px;\n' +
' border-collapse:separate;\n' +
' }\n' +
' .c1271{\n' +
' width:100%;\n' +
' margin: 0 0 15px 0;\n' +
' font-size:50px;\n' +
' color:rgb(120, 197, 214);\n' +
' line-height:250px;\n' +
' text-align:center;\n' +
' }\n' +
' .table100{\n' +
' width:100%;\n' +
' }\n' +
' .c1357{\n' +
' min-height:150px;\n' +
' padding: 5px;\n' +
' margin: auto;\n' +
' height:0px;\n' +
' }\n' +
' .darkerfont{\n' +
' color:rgb(65, 69, 72);\n' +
' }\n' +
' .button{\n' +
' font-size:12px;\n' +
' padding: 10px 20px;\n' +
' background-color:rgb(217, 131, 166);\n' +
' color:rgb(255, 255, 255);\n' +
' text-align:center;\n' +
' border-radius: 3px;\n' +
' font-weight:300;\n' +
' }\n' +
' .table100.c1437{\n' +
' text-align:left;\n' +
' }\n' +
' .cell.cell-bottom{\n' +
' text-align:center;\n' +
' height:51px;\n' +
' }\n' +
' .card-title{\n' +
' font-size:25px;\n' +
' font-weight:300;\n' +
' color:rgb(68, 68, 68);\n' +
' }\n' +
' .card-content{\n' +
' font-size:13px;\n' +
' line-height:20px;\n' +
' color:rgb(111, 119, 125);\n' +
' padding: 10px 20px 0 20px;\n' +
' vertical-align:top;\n' +
' }\n' +
' .container{\n' +
' font-family: Helvetica, serif;\n' +
' min-height:150px;\n' +
' padding: 5px;\n' +
' margin:auto;\n' +
' height:0px;\n' +
' width:90%;\n' +
' max-width:550px;\n' +
' }\n' +
' .cell.c856{\n' +
' vertical-align:middle;\n' +
' }\n' +
' .container-cell{\n' +
' vertical-align:top;\n' +
' font-size:medium;\n' +
' padding-bottom:50px;\n' +
' }\n' +
' .c1790{\n' +
' min-height:150px;\n' +
' padding: 5px;\n' +
' margin:auto;\n' +
' height:0px;\n' +
' }\n' +
' .table100.c1790{\n' +
' min-height:30px;\n' +
' border-collapse:separate;\n' +
' margin: 0 0 10px 0;\n' +
' }\n' +
' .browser-link{\n' +
' font-size:12px;\n' +
' }\n' +
' .top-cell{\n' +
' text-align:right;\n' +
' color:rgb(152, 156, 165);\n' +
' }\n' +
' .table100.c1357{\n' +
' margin: 0;\n' +
' border-collapse:collapse;\n' +
' }\n' +
' .c1769{\n' +
' width:30%;\n' +
' }\n' +
' .c1776{\n' +
' width:70%;\n' +
' }\n' +
' .c1766{\n' +
' margin: 0 auto 10px 0;\n' +
' padding: 5px;\n' +
' width:100%;\n' +
' min-height:30px;\n' +
' }\n' +
' .cell.c1769{\n' +
' width:11%;\n' +
' }\n' +
' .cell.c1776{\n' +
' vertical-align:middle;\n' +
' }\n' +
' .c1542{\n' +
' margin: 0 auto 10px auto;\n' +
' padding:5px;\n' +
' width:100%;\n' +
' }\n' +
' .card-footer{\n' +
' padding: 20px 0;\n' +
' text-align:center;\n' +
' }\n' +
' .c2280{\n' +
' height:150px;\n' +
' margin:0 auto 10px auto;\n' +
' padding:5px 5px 5px 5px;\n' +
' width:100%;\n' +
' }\n' +
' .c2421{\n' +
' padding:10px;\n' +
' }\n' +
' .c2577{\n' +
' padding:10px;\n' +
' }\n' +
' .footer{\n' +
' margin-top: 50px;\n' +
' color:rgb(152, 156, 165);\n' +
' text-align:center;\n' +
' font-size:11px;\n' +
' padding: 5px;\n' +
' }\n' +
' .quote {\n' +
' font-style: italic;\n' +
' }\n' +
' .list-item{\n' +
' height:auto;\n' +
' width:100%;\n' +
' margin: 0 auto 10px auto;\n' +
' padding: 5px;\n' +
' }\n' +
' .list-item-cell{\n' +
' background-color:rgb(255, 255, 255);\n' +
' border-radius: 3px;\n' +
' overflow: hidden;\n' +
' padding: 0;\n' +
' }\n' +
' .list-cell-left{\n' +
' width:30%;\n' +
' padding: 0;\n' +
' }\n' +
' .list-cell-right{\n' +
' width:70%;\n' +
' color:rgb(111, 119, 125);\n' +
' font-size:13px;\n' +
' line-height:20px;\n' +
' padding: 10px 20px 0px 20px;\n' +
' }\n' +
' .list-item-content{\n' +
' border-collapse: collapse;\n' +
' margin: 0 auto;\n' +
' padding: 5px;\n' +
' height:150px;\n' +
' width:100%;\n' +
' }\n' +
' .list-item-image{\n' +
' color:rgb(217, 131, 166);\n' +
' font-size:45px;\n' +
' width: 100%;\n' +
' }\n' +
' .grid-item-image{\n' +
' line-height:150px;\n' +
' font-size:50px;\n' +
' color:rgb(120, 197, 214);\n' +
' margin-bottom:15px;\n' +
' width:100%;\n' +
' }\n' +
' .grid-item-row {\n' +
' margin: 0 auto 10px;\n' +
' padding: 5px 0;\n' +
' width: 100%;\n' +
' }\n' +
' .grid-item-card {\n' +
' width:100%;\n' +
' padding: 5px 0;\n' +
' margin-bottom: 10px;\n' +
' }\n' +
' .grid-item-card-cell{\n' +
' background-color:rgb(255, 255, 255);\n' +
' overflow: hidden;\n' +
' border-radius: 3px;\n' +
' text-align:center;\n' +
' padding: 0;\n' +
' }\n' +
' .grid-item-card-content{\n' +
' font-size:13px;\n' +
' color:rgb(111, 119, 125);\n' +
' padding: 0 10px 20px 10px;\n' +
' width:100%;\n' +
' line-height:20px;\n' +
' }\n' +
' .grid-item-cell2-l{\n' +
' vertical-align:top;\n' +
' padding-right:10px;\n' +
' width:50%;\n' +
' }\n' +
' .grid-item-cell2-r{\n' +
' vertical-align:top;\n' +
' padding-left:10px;\n' +
' width:50%;\n' +
' }';
config.plugins.push('gjs-preset-newsletter');
}
config.components = props.initialSource ? base(props.initialSource, this.props.tagLanguage, trustedUrlBase, sandboxUrlBase, publicUrlBase) : defaultSource;
config.style = props.initialStyle ? base(props.initialStyle, this.props.tagLanguage, trustedUrlBase, sandboxUrlBase, publicUrlBase) : defaultStyle;
config.plugins.push('mailtrain-remove-buttons');
this.editor = grapesjs.init(config);
}
render() {
return (
this.canvasNode = node}/>
);
}
}
export default function() {
parentRPC.init();
ReactDOM.render(
} />
,
document.getElementById('root')
);
};