Forms preview

This commit is contained in:
Tomas Bures 2018-12-15 20:09:07 +01:00
parent 97bbac8698
commit ba996d845d
23 changed files with 206 additions and 99 deletions

View file

@ -111,7 +111,7 @@ export default class List extends Component {
if (status === CampaignStatus.SENDING || status === CampaignStatus.PAUSED || status === CampaignStatus.FINISHED) {
actions.push({
label: <Icon icon="signal" title={t('Statistics')}/>,
label: <Icon icon="signal" title={t('statistics')}/>,
link: `/campaigns/${data[0]}/statistics`
});
}

View file

@ -79,7 +79,7 @@ export default class Statistics extends Component {
return (
<div>
<Title>{t('Campaign Statistics')}</Title>
<Title>{t('campaignStatistics')}</Title>
<Chart
width="100%"

View file

@ -258,7 +258,7 @@ class SendControls extends Component {
:
<Button className="btn-primary" icon="send" label={t('send') + subscrInfo} onClickAsync={::this.startAsync}/>
}
{entity.status === CampaignStatus.PAUSED && <NavButton className="btn-default" icon="signal" label={t('View statistics')} linkTo={`/campaigns/${entity.id}/statistics`}/>}
{entity.status === CampaignStatus.PAUSED && <NavButton className="btn-default" icon="signal" label={t('viewStatistics')} linkTo={`/campaigns/${entity.id}/statistics`}/>}
</ButtonRow>
</div>
);
@ -271,7 +271,7 @@ class SendControls extends Component {
</AlignedRow>
<ButtonRow>
<Button className="btn-primary" icon="stop" label={t('stop')} onClickAsync={::this.stopAsync}/>
<NavButton className="btn-default" icon="signal" label={t('View statistics')} linkTo={`/campaigns/${entity.id}/statistics`}/>
<NavButton className="btn-default" icon="signal" label={t('viewStatistics')} linkTo={`/campaigns/${entity.id}/statistics`}/>
</ButtonRow>
</div>
);
@ -287,7 +287,7 @@ class SendControls extends Component {
<ButtonRow>
<Button className="btn-primary" icon="play" label={t('continue') + subscrInfo} onClickAsync={::this.startAsync}/>
<Button className="btn-primary" icon="refresh" label={t('reset')} onClickAsync={::this.resetAsync}/>
<NavButton className="btn-default" icon="signal" label={t('View statistics')} linkTo={`/campaigns/${entity.id}/statistics`}/>
<NavButton className="btn-default" icon="signal" label={t('viewStatistics')} linkTo={`/campaigns/${entity.id}/statistics`}/>
</ButtonRow>
</div>
);

View file

@ -39,7 +39,7 @@ function getMenus(t) {
panelRender: props => <Status entity={props.resolved.campaign} />
},
statistics: {
title: t('Statistics'),
title: t('statistics'),
link: params => `/campaigns/${params.campaignId}/statistics`,
visible: resolved => resolved.campaign.permissions.includes('viewStats') && (resolved.campaign.status === CampaignStatus.SENDING || resolved.campaign.status === CampaignStatus.PAUSED || resolved.campaign.status === CampaignStatus.FINISHED),
panelRender: props => <Statistics entity={props.resolved.campaign} />

View file

@ -29,8 +29,6 @@ for (const lng of mailtrainConfig.enabledLanguages) {
};
}
console.log(resources);
i18n
.use(LanguageDetector)
.init({

View file

@ -206,7 +206,7 @@ class CodeEditorSandbox extends Component {
{
this.state.preview &&
<div className={styles.preview}>
<iframe ref={node => this.previewNode = node} src={"data:text/html;charset=utf-8," + escape(previewContents)}></iframe>
<iframe ref={node => this.previewNode = node} src={"data:text/html;charset=utf-8," + encodeURIComponent(previewContents)}></iframe>
</div>
}
</div>

View file

@ -92,7 +92,7 @@ export default class CUD extends Component {
}
if (!state.getIn(['send_configuration', 'value'])) {
state.setIn(['send_configuration', 'error'], t('Send configuration must be selected'));
state.setIn(['send_configuration', 'error'], t('sendConfigurationMustBeSelected'));
} else {
state.setIn(['send_configuration', 'error'], null);
}

View file

@ -34,6 +34,21 @@ import {
import {DeleteModalDialog} from "../../lib/modals";
import mailtrainConfig
from 'mailtrainConfig';
import {
getTrustedUrl,
getUrl
} from "../../lib/urls";
import {
ActionLink,
Icon
} from "../../lib/bootstrap-components";
import styles
from "../../lib/styles.scss";
import formsStyles
from "./styles.scss";
import axios
from "../../lib/axios";
import {UntrustedContentHost} from "../../lib/untrusted";
@withTranslation()
@withForm
@ -44,7 +59,10 @@ export default class CUD extends Component {
constructor(props) {
super(props);
this.state = {};
this.state = {
previewContents: null,
previewFullscreen: false
};
this.serverValidatedFields = [
'layout',
@ -77,6 +95,13 @@ export default class CUD extends Component {
serverValidation: {
url: 'rest/forms-validate',
changed: this.serverValidatedFields
},
onChange: {
previewList: () => {
this.setState({
previewContents: null
});
}
}
});
@ -372,6 +397,23 @@ export default class CUD extends Component {
}
}
async preview(formKey) {
const data = {
formKey,
template: this.getFormValue(formKey),
layout: this.getFormValue('layout'),
formInputStyle: this.getFormValue('form_input_style'),
listId: this.getFormValue('previewList')
}
const response = await axios.post(getUrl('rest/forms-preview'), data);
this.setState({
previewContents: response.data.content,
previewLabel: this.templateSettings[formKey].label
});
}
render() {
const t = this.props.t;
const isEdit = !!this.props.entity;
@ -394,7 +436,7 @@ export default class CUD extends Component {
const listsColumns = [
{ data: 0, title: "#" },
{ data: 1, title: t('name') },
{ data: 2, title: t('id'), render: data => `<code>${data}</code>` },
{ data: 2, title: t('id'), render: data => <code>{data}</code> },
{ data: 5, title: t('namespace') }
];
@ -402,7 +444,7 @@ export default class CUD extends Component {
const selectedTemplate = this.getFormValue('selectedTemplate');
return (
<div>
<div className={this.state.previewFullscreen ? styles.withElementInFullscreen : ''}>
{canDelete &&
<DeleteModalDialog
stateOwner={this}
@ -427,34 +469,49 @@ export default class CUD extends Component {
<TableSelect id="previewList" label={t('listToPreviewOn')} withHeader dropdown dataUrl='rest/lists-table' columns={listsColumns} selectionLabelIndex={1} help={t('selectListWhoseFieldsWillBeUsedToPreview')}/>
{ previewListId &&
<AlignedRow>
<div className="help-block">
<small>
Note: These links are solely for a quick preview. If you submit a preview form you'll get redirected to the list's default form.
</small>
<div>
<AlignedRow>
<div className="help-block">
<small>
{t('Note: These links are solely for a quick preview. To get the address of the subscription form, go to the list\'s subscribers and click on "Subscription Form".')}
</small>
</div>
<p>
<ActionLink onClickAsync={async () => await this.preview('web_subscribe')}>Subscribe</ActionLink>
{' | '}
<ActionLink onClickAsync={async () => await this.preview('web_confirm_subscription_notice')}>Confirm Subscription Notice</ActionLink>
{' | '}
<ActionLink onClickAsync={async () => await this.preview('web_confirm_unsubscription_notice')}>Confirm Unsubscription Notice</ActionLink>
{' | '}
<ActionLink onClickAsync={async () => await this.preview('web_subscribed_notice')}>Subscribed Notice</ActionLink>
{' | '}
<ActionLink onClickAsync={async () => await this.preview('web_updated_notice')}>Updated Notice</ActionLink>
{' | '}
<ActionLink onClickAsync={async () => await this.preview('web_unsubscribed_notice')}>Unsubscribed Notice</ActionLink>
{' | '}
<ActionLink onClickAsync={async () => await this.preview('web_manual_unsubscribe_notice')}>Manual Unsubscribe Notice</ActionLink>
{' | '}
<ActionLink onClickAsync={async () => await this.preview('web_unsubscribe')}>Unsubscribe</ActionLink>
{' | '}
<ActionLink onClickAsync={async () => await this.preview('web_manage')}>Manage</ActionLink>
{' | '}
<ActionLink onClickAsync={async () => await this.preview('web_manage_address')}>Manage Address</ActionLink>
{' | '}
<ActionLink onClickAsync={async () => await this.preview('web_privacy_policy_notice')}>Privacy Policy</ActionLink>
</p>
</AlignedRow>
{this.state.previewContents &&
<div className={this.state.previewFullscreen ? formsStyles.editorFullscreen : formsStyles.editor}>
<div className={formsStyles.navbar}>
{this.state.fullscreen && <img className={formsStyles.logo} src={getTrustedUrl('static/mailtrain-notext.png')}/>}
<div className={formsStyles.title}>{t('Form preview:') + ' ' + this.state.previewLabel}</div>
<a className={formsStyles.btn} onClick={() => this.setState({previewContents: null, previewFullscreen: false})}><Icon icon="remove"/></a>
<a className={formsStyles.btn} onClick={() => this.setState({previewFullscreen: !this.state.previewFullscreen})}><Icon icon="fullscreen"/></a>
</div>
<iframe className={formsStyles.host} src={"data:text/html;charset=utf-8," + encodeURIComponent(this.state.previewContents)}></iframe>
</div>
<p>
<a href={`/lists/forms/preview/${previewListId}`} target="_blank">Subscribe</a>
|
<a href={`/lists/forms/preview/${previewListId}/confirm-subscription-notice`} target="_blank">Confirm Subscription Notice</a>
|
<a href={`/lists/forms/preview/${previewListId}/confirm-unsubscription-notice`} target="_blank">Confirm Unsubscription Notice</a>
|
<a href={`/lists/forms/preview/${previewListId}/subscribed-notice`} target="_blank">Subscribed Notice</a>
|
<a href={`/lists/forms/preview/${previewListId}/updated-notice`} target="_blank">Updated Notice</a>
|
<a href={`/lists/forms/preview/${previewListId}/unsubscribed-notice`} target="_blank">Unsubscribed Notice</a>
|
<a href={`/lists/forms/preview/${previewListId}/manual-unsubscribe-notice`} target="_blank">Manual Unsubscribe Notice</a>
|
<a href={`/lists/forms/preview/${previewListId}/unsubscribe`} target="_blank">Unsubscribe</a>
|
<a href={`/lists/forms/preview/${previewListId}/manage`} target="_blank">Manage</a>
|
<a href={`/lists/forms/preview/${previewListId}/manage-address`} target="_blank">Manage Address</a>
</p>
</AlignedRow>
}
</div>
}
</Fieldset>

View file

@ -0,0 +1,11 @@
$editorNormalHeight: 400px;
@import "../../lib/sandbox-common";
.editor {
margin-bottom: 15px;
}
.host {
border: none;
width: 100%;
}

View file

@ -190,6 +190,7 @@
"status": "Status",
"sendingScheduled": "Sending scheduled",
"sending": "Sending",
"statistics": "Statistics",
"edit": "Edit",
"content": "Content",
"files": "Files",
@ -201,6 +202,7 @@
"rss": "RSS",
"triggered": "Triggered",
"campaigns": "Campaigns",
"campaignStatistics": "Campaign Statistics",
"subscriptionHasToBeSelectedToShowThe": "Subscription has to be selected to show the campaign for a test user.",
"subscriptionId": "Subscription ID",
"listId": "List ID",
@ -223,6 +225,7 @@
"rescheduleSend": "Reschedule send",
"scheduleSend": "Schedule send",
"send": "Send",
"viewStatistics": "View statistics",
"campaignIsBeingSentOut": "Campaign is being sent out.",
"stop": "Stop",
"allMessagesSent!HitContinueIfYouYouWant": "All messages sent! Hit \"Continue\" if you you want to send this campaign to new subscribers.",
@ -827,28 +830,10 @@
"grapesJsTemplateDesigner": "GrapesJS Template Designer",
"ckEditor4": "CKEditor 4",
"ckEditor4TemplateDesigner": "CKEditor 4 Template Designer",
"ckEditor5": "CKEditor 5",
"codeEditor": "Code Editor",
"codeEditorTemplateDesigner": "Code Editor Template Designer",
"mergeTagReference": "Merge tag reference",
"templateContentPlainText": "Template content (plain text)",
"mergeTagsAreTagsThatAreReplacedBefore": "<0>Merge tags are tags that are replaced before sending out the message. The format of the merge tag is the following: <1>[TAG_NAME]</1> or <3>[TAG_NAME/fallback]</3> where <5>fallback</5> is an optional text value used when <7>TAG_NAME</7> is empty.</0>",
"youCanUseAnyOfTheStandardMergeTagsBelow": "<0>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.</0>",
"urlThatPointsToTheUnsubscribePage": "URL that points to the unsubscribe page",
"urlThatPointsToThePreferencesPageOfThe": "URL that points to the preferences page of the subscriber",
"urlToPreviewTheMessageInABrowser": "URL to preview the message in a browser",
"recipientNameAsItAppearsInEmailsToHeader": "Recipient name as it appears in email's 'To' header",
"uniqueIdThatIdentifiesTheRecipient": "Unique ID that identifies the recipient",
"uniqueIdThatIdentifiesTheListUsedForThis": "Unique ID that identifies the list used for this campaign",
"uniqueIdThatIdentifiesCurrentCampaign": "Unique ID that identifies current campaign",
"forRssCampaignsTheFollowingFurtherTags": "<0>For RSS campaigns, the following further tags can be used.</0>",
"rssEntryTitle": "RSS entry title",
"rssEntryDate": "RSS entry date",
"rssEntryLink": "RSS entry link",
"contentOfAnRssEntry": "Content of an RSS entry",
"rssEntrySummary": "RSS entry summary",
"rssEntryImageUrl": "RSS entry image URL",
"toExtractTheTextFromHtmlClickHerePlease": "To extract the text from HTML click <1>here</1>. Please note that your existing plaintext in the field above will be overwritten. This feature uses the <3>Premailer API</3>, a third party service. Their Terms of Service and Privacy Policy apply.",
"mosaicoTemplateSaved": "Mosaico template saved",
"deletingMosaicoTemplate": "Deleting Mosaico template ...",
"mosaicoTemplateDeleted": "Mosaico template deleted",
@ -884,6 +869,7 @@
"invalidEmailAddressEmailAddressDomain": "Invalid email address \"{{email}}\": Address domain name is required",
"invalidEmailGeneric": "invalidEmailGeneric",
"mailerPasswordChangeRequest": "Mailer password change request",
"mailtrain": "Mailtrain",
"emailAddressChanged": "Email address changed",
"emailAddressNotSet": "Email address not set",
"nothingSeemsToBeChanged": "Nothing seems to be changed",
@ -891,6 +877,19 @@
"foundAddedMessagesNewCampaignMessages": "Found {{addedMessages}} new campaign messages from feed {{campaignId}}",
"foundNothingNewFromTheFeed": "Found nothing new from the feed",
"missingEmail": "Missing email",
"emailAddress-2": "Email Address",
"wantToChangeIt?": "want to change it?",
"downloadSignatureVerificationKey": "Download signature verification key",
"beginsWithAnd#39BeginPgpPublicKeyBloc": "Begins with &#39;-----BEGIN PGP PUBLIC KEY BLOCK-----&#39;",
"insertYourGpgPublicKeyHereToEncrypt": "Insert your GPG public key here to encrypt messages sent to your address <em>(optional)</em>",
"warning!": "Warning!",
"javaScriptMustBeEnabledInOrderForThis": "JavaScript must be enabled in order for this form to work",
"existingEmailAddress": "Existing Email Address",
"newEmailAddress": "New Email Address",
"youWillReceiveAConfirmationRequestToYour": "You will receive a confirmation request to your new email address that you need to accept before your email is actually changed",
"updateEmailAddress": "Update Email Address",
"updateProfile": "Update Profile",
"subscribeToList": "Subscribe to list",
"thePasswordMustBeAtLeastMinLength": "The password must be at least {{ minLength }} characters long",
"thePasswordMustBeFewerThanMaxLength": "The password must be fewer than {{ maxLength }} characters",
"thePasswordMayNotContainSequencesOfThree": "The password may not contain sequences of three or more repeated characters",

View file

@ -14,7 +14,7 @@ const camelCase = require('camelcase');
const slugify = require('slugify');
const readline = require('readline');
const localeFile = 'en_US/common.json';
const localeFile = 'en-US/common.json';
const searchDirs = [
'../client/src',
'../server',
@ -149,7 +149,7 @@ function parseSpec(specStr) {
// see http://blog.stevenlevithan.com/archives/match-quoted-string
const tMatcher = /(^|[ {+(=.\[])((?:tUI|tLog|t|tMark)\s*\(\s*(?:\/\*(.*?)\*\/)?\s*)(["'])((?:(?!\4)[^\\]|\\.)*)(\4)/;
const jsxTransMatcher = /(\/\*(.*?)\*\/\s*)?(\<Trans[ >][\s\S]*?\<\/Trans\>)/;
const hbsTranslateMatcher = /(\{\{!--(.*?)--\}\}\s*)?\{\{#translate\}\}([\s\S]*?)\{\{\/translate\}\}/;
const hbsTranslateMatcher = /(\{\{!--(.*?)--\}\}\s*)?(\{\{#translate\}\})([\s\S]*?)(\{\{\/translate\}\})/;
const jsxParser = acorn.Parser.extend(acornJsx());
function parseJsxTrans(fragment) {
@ -224,7 +224,7 @@ function parseJsxTrans(fragment) {
function parseHbsTranslate(fragment) {
const match = fragment.match(hbsTranslateMatcher);
const spec = parseSpec(match[2]);
const originalKey = match[3];
const originalKey = match[4];
let value;
const originalValue = findInDict(originalResDict, originalKey);
@ -237,7 +237,7 @@ function parseHbsTranslate(fragment) {
const key = getKeyFromValue(spec, value);
const replacement = `${match[1] || ''}${key}`;
const replacement = `${match[1] || ''}${match[3]}${key}${match[5]}`;
return { key, originalKey, value, replacement };
}

View file

@ -22,3 +22,5 @@ nameTag="username"
passwordresetlink="xxx"
[reports]
enabled=true
[redis]
enabled=true

View file

@ -9,9 +9,7 @@ const {convertToFake, getLang} = require('../../shared/langs');
const resourcesCommon = {};
function loadLanguage(longCode) {
resourcesCommon[longCode] = {
common: JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'locales', longCode, 'common.json')))
};
resourcesCommon[longCode] = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'locales', longCode, 'common.json')));
}
loadLanguage('en-US');

View file

@ -316,7 +316,7 @@ async function sendPasswordReset(locale, usernameOrEmail) {
text: 'users/password-reset-text.hbs',
locale,
data: {
title: tUI('Mailtrain', locale),
title: tUI('mailtrain', locale),
username: user.username,
name: user.name,
confirmUrl: getTrustedUrl(`/account/reset/${encodeURIComponent(user.username)}/${encodeURIComponent(resetToken)}`)

View file

@ -11476,9 +11476,9 @@
}
},
"sprintf-js": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz",
"integrity": "sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw=",
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz",
"integrity": "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==",
"dev": true
},
"sqlstring": {
@ -11899,9 +11899,9 @@
"integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg=="
},
"underscore.string": {
"version": "3.3.4",
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.4.tgz",
"integrity": "sha1-LCo/n4PmR2L9xF5s6sZRQoZCE9s=",
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.5.tgz",
"integrity": "sha512-g+dpmgn+XBneLmXXo+sGlW5xQEt4ErkS3mgeN2GFbremYeMBSJKr9Wf2KJplQVaiPY/f7FN6atosWYNm9ovrYg==",
"dev": true,
"requires": {
"sprintf-js": "^1.0.3",

View file

@ -1,7 +1,12 @@
'use strict';
const passport = require('../../lib/passport');
const lists = require('../../models/lists');
const forms = require('../../models/forms');
const fields = require('../../models/fields');
const settings = require('../../models/settings');
const tools = require('../../lib/tools');
const contextHelpers = require('../../lib/context-helpers');
const router = require('../../lib/router-async').create();
const {castToInteger} = require('../../lib/helpers');
@ -38,5 +43,45 @@ router.postAsync('/forms-validate', passport.loggedIn, async (req, res) => {
return res.json(await forms.serverValidate(req.context, req.body));
});
router.postAsync('/forms-preview', passport.loggedIn, passport.csrfProtection, async (req, res) => {
function sortAndFilterCustomFieldsBy(key) {
data.customFields = data.customFields.filter(fld => fld[key] !== null);
data.customFields.sort((a, b) => a[key] - b[key]);
}
const formKey = req.body.formKey;
const listId = req.body.listId;
const data = {};
const list = await lists.getById(req.context, listId);
data.title = list.name;
data.cid = list.cid;
data.isWeb = true;
data.customFields = await fields.forHbs(req.context, listId, {});
const configItems = await settings.get(contextHelpers.getAdminContext(), ['pgpPrivateKey']);
data.hasPubkey = !!configItems.pgpPrivateKey;
data.formInputStyle = req.body.formInputStyle;
if (formKey === 'web_subscribe') {
sortAndFilterCustomFieldsBy('order_subscribe');
} else if (formKey === 'web_manage') {
sortAndFilterCustomFieldsBy('order_manage');
}
const tmpl = {
template: req.body.template,
layout: req.body.layout,
type: 'mjml'
};
const htmlRenderer = await tools.getTemplate(tmpl, req.locale);
return res.json({content: htmlRenderer(data)});
});
module.exports = router;

View file

@ -12,7 +12,7 @@ const settings = require('../models/settings');
const { tUI } = require('../lib/translate');
const contextHelpers = require('../lib/context-helpers');
const forms = require('../models/forms');
const {getTrustedUrl} = require('../lib/urls');
const {getTrustedUrl, getPublicUrl} = require('../lib/urls');
const bluebird = require('bluebird');
const { SubscriptionStatus, SubscriptionSource } = require('../../shared/lists');
@ -86,7 +86,7 @@ async function injectCustomFormData(customFormId, viewKey, data) {
data.template.template = form[viewKey] || data.template.template;
data.template.layout = form.layout || data.template.layout;
data.formInputStyle = form.formInputStyle || '@import url(/static/subscription/form-input-style.css);';
data.formInputStyle = form.formInputStyle || `@import url(${getPublicUrl('static/subscription/form-input-style.css')});`;
const configItems = await settings.get(contextHelpers.getAdminContext(), ['uaCode']);
@ -168,7 +168,6 @@ async function _renderSubscribe(req, res, list, subscription) {
data.csrfToken = req.csrfToken();
data.customFields = await fields.forHbs(contextHelpers.getAdminContext(), list.id, subscription);
data.useEditor = true;
const configItems = await settings.get(contextHelpers.getAdminContext(), ['pgpPrivateKey']);
data.hasPubkey = !!configItems.pgpPrivateKey;
@ -179,7 +178,7 @@ async function _renderSubscribe(req, res, list, subscription) {
type: 'mjml'
};
await injectCustomFormData(req.query.fid || list.default_form, 'subscription/web-subscribe', data);
await injectCustomFormData(req.query.fid || list.default_form, 'web_subscribe', data);
const htmlRenderer = await tools.getTemplate(data.template, req.locale);
@ -334,7 +333,7 @@ router.getAsync('/:cid/widget', cors(corsOptions), async (req, res) => {
layout: null,
};
await injectCustomFormData(req.query.fid || list.default_form, 'subscription/web-subscribe', data);
await injectCustomFormData(req.query.fid || list.default_form, 'web_subscribe', data);
const renderAsync = bluebird.promisify(res.render);
const html = await renderAsync('subscription/widget-subscribe', data);
@ -372,8 +371,6 @@ router.getAsync('/:lcid/manage/:ucid', passport.csrfProtection, async (req, res)
data.customFields = await fields.forHbs(contextHelpers.getAdminContext(), list.id, subscription);
data.useEditor = true;
const configItems = await settings.get(contextHelpers.getAdminContext(), ['pgpPrivateKey']);
data.hasPubkey = !!configItems.pgpPrivateKey;
@ -383,7 +380,7 @@ router.getAsync('/:lcid/manage/:ucid', passport.csrfProtection, async (req, res)
type: 'mjml'
};
await injectCustomFormData(req.query.fid || list.default_form, 'data/web-manage', data);
await injectCustomFormData(req.query.fid || list.default_form, 'web_manage', data);
const htmlRenderer = await tools.getTemplate(data.template, req.locale);
@ -433,7 +430,7 @@ router.getAsync('/:lcid/manage-address/:ucid', passport.csrfProtection, async (r
type: 'mjml'
};
await injectCustomFormData(req.query.fid || list.default_form, 'data/web-manage-address', data);
await injectCustomFormData(req.query.fid || list.default_form, 'web_manage_address', data);
const htmlRenderer = await tools.getTemplate(data.template, req.locale);
@ -533,7 +530,7 @@ router.getAsync('/:lcid/unsubscribe/:ucid', passport.csrfProtection, async (req,
type: 'mjml'
};
await injectCustomFormData(req.query.fid || list.default_form, 'subscription/web-unsubscribe', data);
await injectCustomFormData(req.query.fid || list.default_form, 'web_unsubscribe', data);
const htmlRenderer = await tools.getTemplate(data.template, req.locale);
@ -682,7 +679,7 @@ async function webNotice(type, req, res) {
}
};
await injectCustomFormData(req.query.fid || list.default_form, 'subscription/web-' + type + '-notice', data);
await injectCustomFormData(req.query.fid || list.default_form, 'web_' + type + '_notice', data);
const htmlRenderer = await tools.getTemplate(data.template, req.locale);

View file

@ -2,11 +2,11 @@
{{#if typeSubscriptionEmail}}
<div class="form-group email">
<label for="EMAIL">{{#translate}}Email Address{{/translate}}</label>
<label for="EMAIL">{{#translate}}emailAddress-2{{/translate}}</label>
{{#if ../isManagePreferences}}
<div class="input-group">
<input type="email" name="EMAIL" id="email" placeholder="" value="{{../email}}" readonly>
<div class="input-group-addon"><a href="/subscription/{{../lcid}}/manage-address/{{../cid}}">{{#translate}}want to change it?{{/translate}}</a></div>
<div class="input-group-addon"><a href="/subscription/{{../lcid}}/manage-address/{{../cid}}">{{#translate}}wantToChangeIt?{{/translate}}</a></div>
</div>
{{else}}
<input type="email" name="EMAIL" id="email" placeholder="" value="{{../email}}" required>
@ -53,11 +53,11 @@
<div class="form-group gpg {{key}}">
<label for="{{key}}">{{name}}</label>
{{#if ../hasPubkey}}
<button class="btn-download-pubkey" type="submit" form="download-pubkey">{{#translate}}Download signature verification key{{/translate}}</button>
<button class="btn-download-pubkey" type="submit" form="download-pubkey">{{#translate}}downloadSignatureVerificationKey{{/translate}}</button>
{{/if}}
<textarea class="form-control gpg-text" rows="4" name="{{key}}" placeholder="{{#translate}}Begins with &#39;-----BEGIN PGP PUBLIC KEY BLOCK-----&#39;{{/translate}}">{{value}}</textarea>
<textarea class="form-control gpg-text" rows="4" name="{{key}}" placeholder="{{#translate}}beginsWithAnd#39BeginPgpPublicKeyBloc{{/translate}}">{{value}}</textarea>
<span class="help-block">
{{#translate}}Insert your GPG public key here to encrypt messages sent to your address <em>(optional)</em>{{/translate}}
{{#translate}}insertYourGpgPublicKeyHereToEncrypt{{/translate}}
</span>
</div>
{{/if}}
@ -95,7 +95,7 @@
<label for="{{key}}">{{name}}</label>
<select name="{{key}}" class="form-control">
<option value="">
{{#translate}} Select {{/translate}}
{{#translate}}Select {{/translate}}
</option>
{{#each options}}
<option value="{{key}}" {{#if value}} selected {{/if}}>{{name}}</option>

View file

@ -2,7 +2,7 @@
{{#if needsJsWarning}}
<div class="alert alert-danger js-warning" role="alert">
<strong>{{#translate}}Warning!{{/translate}}</strong>
{{#translate}}JavaScript must be enabled in order for this form to work{{/translate}}
<strong>{{#translate}}warning!{{/translate}}</strong>
{{#translate}}javaScriptMustBeEnabledInOrderForThis{{/translate}}
</div>
{{/if}}

View file

@ -4,19 +4,19 @@
<input type="hidden" name="cid" value="{{cid}}">
<div class="form-group email">
<label for="EMAIL">{{#translate}}Existing Email Address{{/translate}}</label>
<label for="EMAIL">{{#translate}}existingEmailAddress{{/translate}}</label>
<input type="email" name="EMAIL" id="email" placeholder="" value="{{email}}" readonly>
</div>
<div class="form-group email">
<label for="EMAIL_NEW">{{#translate}}New Email Address{{/translate}}</label>
<label for="EMAIL_NEW">{{#translate}}newEmailAddress{{/translate}}</label>
<input type="email" name="EMAIL_NEW" id="email-new" placeholder="Your new email address" value="{{email}}">
</div>
<p>
{{#translate}}You will receive a confirmation request to your new email address that you need to accept before your email is actually changed{{/translate}}
{{#translate}}youWillReceiveAConfirmationRequestToYour{{/translate}}
</p>
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}Update Email Address{{/translate}}</button>
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}updateEmailAddress{{/translate}}</button>
</form>

View file

@ -12,7 +12,7 @@
{{> subscription_custom_fields}}
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}Update Profile{{/translate}}</button>
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}updateProfile{{/translate}}</button>
</form>
<script src="/moment/moment.min.js"></script>

View file

@ -14,7 +14,7 @@
{{> subscription_custom_fields}}
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}Subscribe to list{{/translate}}</button>
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}subscribeToList{{/translate}}</button>
</form>
<script>

View file

@ -4,10 +4,10 @@
<input type="hidden" name="ucid" value="{{ucid}}">
<div class="form-group">
<label for="email">{{#translate}}Email address{{/translate}}</label>
<label for="email">{{#translate}}emailAddress-1{{/translate}}</label>
<input type="email" name="email" id="email" placeholder="" value="{{email}}" readonly>
</div>
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}Unsubscribe{{/translate}}</button>
<button type="submit" style="position: absolute; top: -9999px; left: -9999px;">{{#translate}}unsubscribe{{/translate}}</button>
</form>