'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' + '
\n' + ' \n' + ' \n' + ' \n' + ' \n' + '
\n' + ' View in browser\n' + ' \n' + '
\n' + ' \n' + ' \n' + ' \n' + ' \n' + ' \n' + '
\n' + ' GrapesJS.\n' + ' \n' + '
GrapesJS Newsletter Builder\n' + '
\n' + '
\n' + '
\n' + ' \n' + ' \n' + ' \n' + ' \n' + '
\n' + ' Big image here\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' + ' Image1\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' + ' Image2\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' + ' Image1\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' + ' Image2\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' + '
'; 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') ); };