'use strict'; import React from "react"; import {ACEEditor, AlignedRow, Dropdown, StaticField, TableSelect} from "../lib/form"; import 'ace-builds/src-noconflict/mode-text'; import 'ace-builds/src-noconflict/mode-html'; import {MosaicoHost} from "../lib/sandboxed-mosaico"; import {CKEditorHost} from "../lib/sandboxed-ckeditor"; import {GrapesJSHost} from "../lib/sandboxed-grapesjs"; import {CodeEditorHost} from "../lib/sandboxed-codeeditor"; import {getGrapesJSSourceTypeOptions, GrapesJSSourceType} from "../lib/sandboxed-grapesjs-shared"; import {CodeEditorSourceType, getCodeEditorSourceTypeOptions} from "../lib/sandboxed-codeeditor-shared"; import {getTemplateTypes as getMosaicoTemplateTypes} from './mosaico/helpers'; import {getSandboxUrl} from "../lib/urls"; import mailtrainConfig from 'mailtrainConfig'; import {ActionLink, Button} from "../lib/bootstrap-components"; import {Trans} from "react-i18next"; import {TagLanguages, renderTag} from "../../../shared/templates"; import styles from "../lib/styles.scss"; export const ResourceType = { TEMPLATE: 'template', CAMPAIGN: 'campaign' }; export function getTagLanguages(t) { return { [TagLanguages.SIMPLE]: { name: t('Simple') }, [TagLanguages.HBS]: { name: t('Handlebars') } }; } export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEMPLATE, allowEmpty = false) { // The prefix is used to to enable use within other forms (i.e. campaign form) const templateTypes = {}; function initFieldsIfMissing(mutState, templateType) { const initVals = templateTypes[templateType].initData(); for (const key in initVals) { if (!mutState.hasIn([key])) { mutState.setIn([key, 'value'], initVals[key]); } } } function clearBeforeSave(data) { for (const templateKey in templateTypes) { const initVals = templateTypes[templateKey].initData(); for (const fieldKey in initVals) { delete data[fieldKey]; } } } const mosaicoTemplateTypes = getMosaicoTemplateTypes(t); const mosaicoTemplatesColumns = [ { data: 1, title: t('name') }, { data: 2, title: t('description') }, { data: 3, title: t('type'), render: data => mosaicoTemplateTypes[data].typeName }, { data: 6, title: t('namespace') }, ]; templateTypes.mosaico = { typeName: t('mosaico'), getTypeForm: (owner, isEdit) => { const tagLanguageKey = owner.getFormValue(prefix + 'tag_language'); if (tagLanguageKey) { return } else { return null; } }, getHTMLEditor: owner => , exportHTMLEditorData: async owner => { const state = await owner.editorNode.exportState(); // If the sandbox is still loading, the exportState returns null. if (state) { return { [prefix + 'html']: state.html, [prefix + 'mosaicoData']: { metadata: state.metadata, model: state.model } }; } else { return null; } }, exportContent: async (owner, contentType) => { const {html, metadata, model} = await owner.editorNode.exportState(); if (contentType === 'html') return html; return null; }, initData: () => ({ [prefix + 'mosaicoTemplate']: null, [prefix + 'mosaicoData']: {} }), afterLoad: data => { data[prefix + 'mosaicoTemplate'] = data[prefix + 'data'].mosaicoTemplate; data[prefix + 'mosaicoData'] = { metadata: data[prefix + 'data'].metadata, model: data[prefix + 'data'].model }; }, beforeSave: data => { data[prefix + 'data'] = { mosaicoTemplate: data[prefix + 'mosaicoTemplate'], metadata: data[prefix + 'mosaicoData'].metadata, model: data[prefix + 'mosaicoData'].model }; clearBeforeSave(data); }, afterTypeChange: mutState => { initFieldsIfMissing(mutState, 'mosaico'); }, afterTagLanguageChange: (mutState, isEdit) => { if (!isEdit) { mutState.setIn([prefix + 'mosaicoTemplate', 'value'], null); } }, validate: state => { const mosaicoTemplate = state.getIn([prefix + 'mosaicoTemplate', 'value']); if (!allowEmpty && !mosaicoTemplate) { state.setIn([prefix + 'mosaicoTemplate', 'error'], t('mosaicoTemplateMustBeSelected')); } } }; const mosaicoFsTemplatesOptions = mailtrainConfig.mosaico.fsTemplates; const mosaicoFsTemplatesLabels = new Map(mailtrainConfig.mosaico.fsTemplates.map(({key, label}) => ([key, label]))); templateTypes.mosaicoWithFsTemplate = { typeName: t('mosaicoWithPredefinedTemplates'), getTypeForm: (owner, isEdit) => { if (isEdit) { return {mosaicoFsTemplatesLabels.get(owner.getFormValue(prefix + 'mosaicoFsTemplate'))}; } else { return ; } }, getHTMLEditor: owner => , exportHTMLEditorData: async owner => { const state = await owner.editorNode.exportState(); // If the sandbox is still loading, the exportState returns null. if (state) { return { [prefix + 'html']: state.html, [prefix + 'mosaicoData']: { metadata: state.metadata, model: state.model } }; } else { return null; } }, exportContent: async (owner, contentType) => { const {html, metadata, model} = await owner.editorNode.exportState(); if (contentType === 'html') return html; return null; }, initData: () => ({ [prefix + 'mosaicoFsTemplate']: mailtrainConfig.mosaico.fsTemplates[0].key, [prefix + 'mosaicoData']: {} }), afterLoad: data => { data[prefix + 'mosaicoFsTemplate'] = data[prefix + 'data'].mosaicoFsTemplate; data[prefix + 'mosaicoData'] = { metadata: data[prefix + 'data'].metadata, model: data[prefix + 'data'].model }; }, beforeSave: data => { data[prefix + 'data'] = { mosaicoFsTemplate: data[prefix + 'mosaicoFsTemplate'], metadata: data[prefix + 'mosaicoData'].metadata, model: data[prefix + 'mosaicoData'].model }; clearBeforeSave(data); }, afterTypeChange: mutState => { initFieldsIfMissing(mutState, 'mosaicoWithFsTemplate'); }, afterTagLanguageChange: (mutState, isEdit) => { }, validate: state => { } }; const grapesJSSourceTypes = getGrapesJSSourceTypeOptions(t); const grapesJSSourceTypeLabels = {}; for (const srcType of grapesJSSourceTypes) { grapesJSSourceTypeLabels[srcType.key] = srcType.label; } templateTypes.grapesjs = { typeName: t('grapesJs'), getTypeForm: (owner, isEdit) => { if (isEdit) { return {grapesJSSourceTypeLabels[owner.getFormValue(prefix + 'grapesJSSourceType')]}; } else { return ; } }, getHTMLEditor: owner => , exportHTMLEditorData: async owner => { const state = await owner.editorNode.exportState(); // If the sandbox is still loading, the exportState returns null. if (state) { return { [prefix + 'html']: state.html, [prefix + 'grapesJSData']: { source: state.source, style: state.style } }; } else { return null; } }, exportContent: async (owner, contentType) => { const {html, source, style} = await owner.editorNode.exportState(); if (contentType === 'html') return html; if (contentType === 'mjml') return source; return null; }, initData: () => ({ [prefix + 'grapesJSSourceType']: GrapesJSSourceType.MJML, [prefix + 'grapesJSData']: {} }), afterLoad: data => { data[prefix + 'grapesJSSourceType'] = data[prefix + 'data'].sourceType; data[prefix + 'grapesJSData'] = { source: data[prefix + 'data'].source, style: data[prefix + 'data'].style }; }, beforeSave: data => { data[prefix + 'data'] = { sourceType: data[prefix + 'grapesJSSourceType'], source: data[prefix + 'grapesJSData'].source, style: data[prefix + 'grapesJSData'].style }; clearBeforeSave(data); }, afterTypeChange: mutState => { initFieldsIfMissing(mutState, 'grapesjs'); }, afterTagLanguageChange: (mutState, isEdit) => { }, validate: state => { } }; templateTypes.ckeditor4 = { typeName: t('ckEditor4'), getTypeForm: (owner, isEdit) => null, getHTMLEditor: owner => , exportHTMLEditorData: async owner => { const state = await owner.editorNode.exportState(); // If the sandbox is still loading, the exportState returns null. if (state) { return { [prefix + 'html']: state.html, [prefix + 'ckeditor4Data']: { source: state.source } }; } else { return null; } }, exportContent: async (owner, contentType) => { const {html, source} = await owner.editorNode.exportState(); if (contentType === 'html') return html; return null; }, initData: () => ({ [prefix + 'ckeditor4Data']: {} }), afterLoad: data => { data[prefix + 'ckeditor4Data'] = { source: data[prefix + 'data'].source }; }, beforeSave: data => { data[prefix + 'data'] = { source: data[prefix + 'ckeditor4Data'].source, }; clearBeforeSave(data); }, afterTypeChange: mutState => { initFieldsIfMissing(mutState, 'ckeditor4'); }, afterTagLanguageChange: (mutState, isEdit) => { }, validate: state => { } }; const codeEditorSourceTypes = getCodeEditorSourceTypeOptions(t); const codeEditorSourceTypeLabels = {}; for (const srcType of codeEditorSourceTypes) { codeEditorSourceTypeLabels[srcType.key] = srcType.label; } templateTypes.codeeditor = { typeName: t('codeEditor'), getTypeForm: (owner, isEdit) => { const sourceType = owner.getFormValue(prefix + 'codeEditorSourceType'); if (isEdit) { return {codeEditorSourceTypeLabels[sourceType]}; } else { return ; } }, getHTMLEditor: owner => , exportHTMLEditorData: async owner => { const state = await owner.editorNode.exportState(); // If the sandbox is still loading, the exportState returns null. if (state) { return { [prefix + 'html']: state.html, [prefix + 'codeEditorData']: { source: state.source } }; } else { return null; } }, exportContent: async (owner, contentType) => { const {html, source} = await owner.editorNode.exportState(); if (contentType === 'html') return html; return null; }, 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 }; clearBeforeSave(data); }, afterTypeChange: mutState => { initFieldsIfMissing(mutState, 'codeeditor'); }, afterTagLanguageChange: (mutState, isEdit) => { }, validate: state => { } }; return templateTypes; } export function getEditForm(owner, typeKey, prefix = '') { const t = owner.props.t; const tagLanguage = owner.getFormValue(prefix + 'tag_language'); const tg = tag => renderTag(tagLanguage, tag); let instructions = null; if (tagLanguage === TagLanguages.SIMPLE) { instructions = ( <>

Merge tags are tags that are replaced before sending out the message. The format of the merge tag is the following: {tg('TAG_NAME')} or [TAG_NAME/fallback] where fallback is an optional text value used when TAG_NAME is empty.

You can use any of the standard merge tags below. In addition to that every custom field has its own merge tag. Check the fields of the list you are going to send to.

); } else if (tagLanguage === TagLanguages.HBS) { instructions = ( <>

Merge tags are tags that are replaced before sending out the message. The format of the merge tag is the following: {tg('TAG_NAME')}.

You can use any of the standard merge tags below. In addition to that every custom field has its own merge tag. Check the fields of the list you are going to send to.

The whole message is interpreted as Handlebars template (see http://handlebarsjs.com/). You can use any Handlebars blocks and expressions in the template. The merge tags form the root context of the Handlebars template.

); } return (
); } export function getTypeForm(owner, typeKey, isEdit) { return owner.templateTypes[typeKey].getTypeForm(owner, isEdit); }