Added feature to create template from another template.
This commit is contained in:
parent
031b346440
commit
8d95f43dbc
5 changed files with 90 additions and 41 deletions
|
@ -12,12 +12,12 @@ import {
|
|||
} from '../lib/page'
|
||||
import {
|
||||
Button,
|
||||
ButtonRow,
|
||||
ButtonRow, CheckBox,
|
||||
Dropdown,
|
||||
Form,
|
||||
FormSendMethod,
|
||||
InputField,
|
||||
StaticField,
|
||||
StaticField, TableSelect,
|
||||
TextArea,
|
||||
withForm
|
||||
} from '../lib/form';
|
||||
|
@ -41,6 +41,8 @@ import styles
|
|||
import {getUrl} from "../lib/urls";
|
||||
import {TestSendModalDialog} from "./TestSendModalDialog";
|
||||
import {withComponentMixins} from "../lib/decorator-helpers";
|
||||
import moment
|
||||
from 'moment';
|
||||
|
||||
|
||||
@withComponentMixins([
|
||||
|
@ -98,6 +100,10 @@ console.log('constructor')
|
|||
description: '',
|
||||
namespace: mailtrainConfig.user.namespace,
|
||||
type: mailtrainConfig.editors[0],
|
||||
|
||||
fromSourceTemplate: false,
|
||||
sourceTemplate: null,
|
||||
|
||||
text: '',
|
||||
html: '',
|
||||
data: {},
|
||||
|
@ -122,6 +128,12 @@ console.log('constructor')
|
|||
state.setIn(['type', 'error'], t('typeMustBeSelected'));
|
||||
}
|
||||
|
||||
if (state.getIn(['fromSourceTemplate', 'value']) && !state.getIn(['sourceTemplate', 'value'])) {
|
||||
state.setIn(['sourceTemplate', 'error'], t('Source template must not be empty'));
|
||||
} else {
|
||||
state.setIn(['sourceTemplate', 'error'], null);
|
||||
}
|
||||
|
||||
validateNamespace(t, state);
|
||||
|
||||
if (typeKey) {
|
||||
|
@ -255,6 +267,13 @@ console.log('constructor')
|
|||
typeForm = getTypeForm(this, typeKey, isEdit);
|
||||
}
|
||||
|
||||
const templatesColumns = [
|
||||
{ data: 1, title: t('name') },
|
||||
{ data: 2, title: t('description') },
|
||||
{ data: 3, title: t('type'), render: data => this.templateTypes[data].typeName },
|
||||
{ data: 4, title: t('created'), render: data => moment(data).fromNow() },
|
||||
{ data: 5, title: t('namespace') },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={this.state.elementInFullscreen ? styles.withElementInFullscreen : ''}>
|
||||
|
@ -281,16 +300,25 @@ console.log('constructor')
|
|||
<InputField id="name" label={t('name')}/>
|
||||
<TextArea id="description" label={t('description')}/>
|
||||
|
||||
{isEdit
|
||||
?
|
||||
<StaticField id="type" className={styles.formDisabled} label={t('type')}>
|
||||
{typeKey && this.templateTypes[typeKey].typeName}
|
||||
</StaticField>
|
||||
:
|
||||
<Dropdown id="type" label={t('type')} options={typeOptions}/>
|
||||
{!isEdit &&
|
||||
<CheckBox id="fromSourceTemplate" label={t('template')} text={t('Clone from an existing template')}/>
|
||||
}
|
||||
|
||||
{typeForm}
|
||||
{this.getFormValue('fromSourceTemplate') ?
|
||||
<TableSelect key="templateSelect" id="sourceTemplate" withHeader dropdown dataUrl='rest/templates-table' columns={templatesColumns} selectionLabelIndex={1} />
|
||||
:
|
||||
<>
|
||||
{isEdit ?
|
||||
<StaticField id="type" className={styles.formDisabled} label={t('type')}>
|
||||
{typeKey && this.templateTypes[typeKey].typeName}
|
||||
</StaticField>
|
||||
:
|
||||
<Dropdown id="type" label={t('type')} options={typeOptions}/>
|
||||
}
|
||||
|
||||
{typeForm}
|
||||
</>
|
||||
}
|
||||
|
||||
<NamespaceSelect/>
|
||||
|
||||
|
|
|
@ -255,11 +255,11 @@ export function getTemplateTypes(t, prefix = '', entityTypeId = ResourceType.TEM
|
|||
return <StaticField
|
||||
id={prefix + 'grapesJSSourceType'}
|
||||
className={styles.formDisabled}
|
||||
label={t('type')}>{grapesJSSourceTypeLabels[owner.getFormValue(prefix + 'grapesJSSourceType')]}</StaticField>;
|
||||
label={t('Content')}>{grapesJSSourceTypeLabels[owner.getFormValue(prefix + 'grapesJSSourceType')]}</StaticField>;
|
||||
} else {
|
||||
return <Dropdown
|
||||
id={prefix + 'grapesJSSourceType'}
|
||||
label={t('type')}
|
||||
label={t('Content')}
|
||||
options={grapesJSSourceTypes}/>;
|
||||
}
|
||||
},
|
||||
|
|
32
server/lib/campaign-content.js
Normal file
32
server/lib/campaign-content.js
Normal file
|
@ -0,0 +1,32 @@
|
|||
'use strict';
|
||||
|
||||
function convertFileURLs(sourceCustom, fromEntityType, fromEntityId, toEntityType, toEntityId) {
|
||||
|
||||
function convertText(text) {
|
||||
if (text) {
|
||||
const fromUrl = `/files/${fromEntityType}/file/${fromEntityId}`;
|
||||
const toUrl = `/files/${toEntityType}/file/${toEntityId}`;
|
||||
|
||||
const encodedFromUrl = encodeURIComponent(fromUrl);
|
||||
const encodedToUrl = encodeURIComponent(toUrl);
|
||||
|
||||
text = text.split('[URL_BASE]' + fromUrl).join('[URL_BASE]' + toUrl);
|
||||
text = text.split('[SANDBOX_URL_BASE]' + fromUrl).join('[SANDBOX_URL_BASE]' + toUrl);
|
||||
text = text.split('[ENCODED_URL_BASE]' + encodedFromUrl).join('[ENCODED_URL_BASE]' + encodedToUrl);
|
||||
text = text.split('[ENCODED_SANDBOX_URL_BASE]' + encodedFromUrl).join('[ENCODED_SANDBOX_URL_BASE]' + encodedToUrl);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
sourceCustom.html = convertText(sourceCustom.html);
|
||||
sourceCustom.text = convertText(sourceCustom.text);
|
||||
|
||||
if (sourceCustom.type === 'mosaico' || sourceCustom.type === 'mosaicoWithFsTemplate') {
|
||||
sourceCustom.data.model = convertText(sourceCustom.data.model);
|
||||
sourceCustom.data.model = convertText(sourceCustom.data.model);
|
||||
sourceCustom.data.metadata = convertText(sourceCustom.data.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.convertFileURLs = convertFileURLs;
|
|
@ -20,6 +20,7 @@ const senders = require('../lib/senders');
|
|||
const {LinkId} = require('./links');
|
||||
const feedcheck = require('../lib/feedcheck');
|
||||
const contextHelpers = require('../lib/context-helpers');
|
||||
const {convertFileURLs} = require('../lib/campaign-content');
|
||||
|
||||
const {EntityActivityType, CampaignActivityType} = require('../../shared/activity-log');
|
||||
const activityLog = require('../lib/activity-log');
|
||||
|
@ -430,35 +431,6 @@ async function _validateAndPreprocess(tx, context, entity, isCreate, content) {
|
|||
}
|
||||
}
|
||||
|
||||
function convertFileURLs(sourceCustom, fromEntityType, fromEntityId, toEntityType, toEntityId) {
|
||||
|
||||
function convertText(text) {
|
||||
if (text) {
|
||||
const fromUrl = `/files/${fromEntityType}/file/${fromEntityId}`;
|
||||
const toUrl = `/files/${toEntityType}/file/${toEntityId}`;
|
||||
|
||||
const encodedFromUrl = encodeURIComponent(fromUrl);
|
||||
const encodedToUrl = encodeURIComponent(toUrl);
|
||||
|
||||
text = text.split('[URL_BASE]' + fromUrl).join('[URL_BASE]' + toUrl);
|
||||
text = text.split('[SANDBOX_URL_BASE]' + fromUrl).join('[SANDBOX_URL_BASE]' + toUrl);
|
||||
text = text.split('[ENCODED_URL_BASE]' + encodedFromUrl).join('[ENCODED_URL_BASE]' + encodedToUrl);
|
||||
text = text.split('[ENCODED_SANDBOX_URL_BASE]' + encodedFromUrl).join('[ENCODED_SANDBOX_URL_BASE]' + encodedToUrl);
|
||||
}
|
||||
|
||||
return text;
|
||||
}
|
||||
|
||||
sourceCustom.html = convertText(sourceCustom.html);
|
||||
sourceCustom.text = convertText(sourceCustom.text);
|
||||
|
||||
if (sourceCustom.type === 'mosaico' || sourceCustom.type === 'mosaicoWithFsTemplate') {
|
||||
sourceCustom.data.model = convertText(sourceCustom.data.model);
|
||||
sourceCustom.data.model = convertText(sourceCustom.data.model);
|
||||
sourceCustom.data.metadata = convertText(sourceCustom.data.metadata);
|
||||
}
|
||||
}
|
||||
|
||||
async function _createTx(tx, context, entity, content) {
|
||||
return await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'namespace', entity.namespace, 'createCampaign');
|
||||
|
|
|
@ -10,6 +10,7 @@ const shares = require('./shares');
|
|||
const reports = require('./reports');
|
||||
const files = require('./files');
|
||||
const dependencyHelpers = require('../lib/dependency-helpers');
|
||||
const {convertFileURLs} = require('../lib/campaign-content');
|
||||
|
||||
const allowedKeys = new Set(['name', 'description', 'type', 'data', 'html', 'text', 'namespace']);
|
||||
|
||||
|
@ -57,6 +58,15 @@ async function create(context, entity) {
|
|||
return await knex.transaction(async tx => {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'namespace', entity.namespace, 'createTemplate');
|
||||
|
||||
if (entity.fromSourceTemplate) {
|
||||
const template = await getByIdTx(tx, context, entity.sourceTemplate, false);
|
||||
|
||||
entity.type = template.type;
|
||||
entity.data = template.data;
|
||||
entity.html = template.html;
|
||||
entity.text = template.text;
|
||||
}
|
||||
|
||||
await _validateAndPreprocess(tx, entity);
|
||||
|
||||
const ids = await tx('templates').insert(filterObject(entity, allowedKeys));
|
||||
|
@ -64,6 +74,13 @@ async function create(context, entity) {
|
|||
|
||||
await shares.rebuildPermissionsTx(tx, { entityTypeId: 'template', entityId: id });
|
||||
|
||||
if (entity.fromSourceTemplate) {
|
||||
await files.copyAllTx(tx, context, 'template', 'file', entity.sourceTemplate, 'template', 'file', id);
|
||||
|
||||
convertFileURLs(entity, 'template', entity.sourceTemplate, 'template', id);
|
||||
await tx('templates').update(filterObject(entity, allowedKeys)).where('id', id);
|
||||
}
|
||||
|
||||
return id;
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue